Skip to content

Elixir API Reference

Installation

Add to mix.exs:

def deps do
  [
    {:tree_sitter_language_pack, "~> 1.0"}
  ]
end
```text

Then run:

```bash
mix deps.get
```text

## Quick Start

```elixir
# Pre-download languages
TreeSitterLanguagePack.init(languages: ["python", "rust"])

# Get a language
{:ok, language} = TreeSitterLanguagePack.get_language("python")

# Get a pre-configured parser
{:ok, parser} = TreeSitterLanguagePack.get_parser("python")
tree = TreeSitter.Parser.parse(parser, "def hello(): pass")
IO.puts(TreeSitter.Tree.sexp(tree))

# Extract code intelligence
config = TreeSitterLanguagePack.ProcessConfig.new("python")
  |> TreeSitterLanguagePack.ProcessConfig.all()

{:ok, result} = TreeSitterLanguagePack.process("def hello(): pass", config)
IO.inspect(result["structure"])
```text

## Download Management

### `TreeSitterLanguagePack.init(options \\ [])`

Initialize the language pack with optional pre-downloads.

**Parameters:**

- `options` (keyword list):
    - `languages` (list[String]): Languages to download
    - `groups` (list[String]): Language groups to download
    - `cache_dir` (String): Custom cache directory

**Returns:** {:ok, nil} | {:error, reason}

**Example:**

```elixir
# Pre-download specific languages
TreeSitterLanguagePack.init(languages: ["python", "javascript", "rust"])

# Or download language groups
TreeSitterLanguagePack.init(groups: ["web", "data"])

# With custom cache directory
TreeSitterLanguagePack.init(
  languages: ["python"],
  cache_dir: "/opt/ts-pack"
)
```text

### `TreeSitterLanguagePack.configure(options \\ [])`

Apply configuration without downloading.

Use to set custom cache directory before first `get_language` call.

**Parameters:**

- `options` (keyword list):
    - `cache_dir` (String): Custom cache directory

**Returns:** {:ok, nil} | {:error, reason}

**Example:**

```elixir
TreeSitterLanguagePack.configure(cache_dir: "/data/ts-pack")

{:ok, language} = TreeSitterLanguagePack.get_language("python")
```text

### `TreeSitterLanguagePack.download(names)`

Download specific languages to cache.

**Parameters:**

- `names` (list[String]): Language names to download

**Returns:** {:ok, count} | {:error, reason} where count is Integer

**Example:**

```elixir
case TreeSitterLanguagePack.download(["python", "rust", "typescript"]) do
  {:ok, count} -> IO.puts("Downloaded #{count} new languages")
  {:error, reason} -> IO.puts("Error: #{reason}")
end
```text

### `TreeSitterLanguagePack.download_all()`

Download all available languages (170+).

**Returns:** {:ok, count} | {:error, reason}

**Example:**

```elixir
{:ok, count} = TreeSitterLanguagePack.download_all()
IO.puts("Downloaded #{count} languages total")
```text

### `TreeSitterLanguagePack.manifest_languages()`

Get all available languages from remote manifest.

**Returns:** {:ok, languages} | {:error, reason}

**Example:**

```elixir
case TreeSitterLanguagePack.manifest_languages() do
  {:ok, languages} ->
    IO.puts("Available: #{length(languages)} languages")
    IO.inspect(Enum.sort(languages))

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end
```text

### `TreeSitterLanguagePack.downloaded_languages()`

Get languages already cached locally.

Does not perform network requests.

**Returns:** list[String]

**Example:**

```elixir
cached = TreeSitterLanguagePack.downloaded_languages()
IO.inspect(cached)
```text

### `TreeSitterLanguagePack.clean_cache()`

Delete all cached parser shared libraries.

**Returns:** :ok | {:error, reason}

**Example:**

```elixir
TreeSitterLanguagePack.clean_cache()
IO.puts("Cache cleaned")
```text

### `TreeSitterLanguagePack.cache_dir()`

Get the current cache directory path.

**Returns:** String

**Example:**

```elixir
dir = TreeSitterLanguagePack.cache_dir()
IO.puts("Cache at: #{dir}")
```text

## Language Discovery

### `TreeSitterLanguagePack.get_language(name)`

Get a tree-sitter Language by name.

Resolves aliases (e.g., `"shell"`  `"bash"`). Auto-downloads if needed.

**Parameters:**

- `name` (String): Language name or alias

**Returns:** {:ok, language} | {:error, reason}

**Example:**

```elixir
case TreeSitterLanguagePack.get_language("python") do
  {:ok, language} ->
    {:ok, parser} = TreeSitter.Parser.new()
    TreeSitter.Parser.set_language(parser, language)
    tree = TreeSitter.Parser.parse(parser, "x = 1")
    IO.puts(tree.root_node.type)

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end
```text

### `TreeSitterLanguagePack.get_parser(name)`

Get a pre-configured Parser for a language.

**Parameters:**

- `name` (String): Language name or alias

**Returns:** {:ok, parser} | {:error, reason}

**Example:**

```elixir
case TreeSitterLanguagePack.get_parser("rust") do
  {:ok, parser} ->
    tree = TreeSitter.Parser.parse(parser, "fn main() {}")
    IO.puts(tree.root_node.has_error?)

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end
```text

### `TreeSitterLanguagePack.available_languages()`

List all available language names.

**Returns:** list[String]

**Example:**

```elixir
langs = TreeSitterLanguagePack.available_languages()
Enum.each(langs, &IO.puts/1)
```text

### `TreeSitterLanguagePack.has_language?(name)`

Check if a language is available.

**Parameters:**

- `name` (String): Language name or alias

**Returns:** boolean

**Example:**

```elixir
if TreeSitterLanguagePack.has_language?("python") do
  IO.puts("Python available")
end

unless TreeSitterLanguagePack.has_language?("shell") do
  raise "Shell not available"
end
```text

### `TreeSitterLanguagePack.language_count()`

Get total number of available languages.

**Returns:** integer

**Example:**

```elixir
count = TreeSitterLanguagePack.language_count()
IO.puts("#{count} languages available")
```text

## Parsing

### `TreeSitterLanguagePack.parse_string(source, language)`

Parse source code into a syntax tree.

**Parameters:**

- `source` (String | binary): Source code
- `language` (String): Language name

**Returns:** {:ok, tree} | {:error, reason}

**Example:**

```elixir
case TreeSitterLanguagePack.parse_string("def foo(): pass", "python") do
  {:ok, tree} ->
    IO.puts(TreeSitter.Tree.sexp(tree))

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end
```text

## Code Intelligence

### `TreeSitterLanguagePack.process(source, config)`

Extract code intelligence from source code.

**Parameters:**

- `source` (String): Source code
- `config` (ProcessConfig): Configuration

**Returns:** {:ok, result} | {:error, reason}

**Example:**

```elixir
config = TreeSitterLanguagePack.ProcessConfig.new("python")
  |> TreeSitterLanguagePack.ProcessConfig.all()

case TreeSitterLanguagePack.process("def hello(): pass", config) do
  {:ok, result} ->
    IO.puts("Functions: #{length(result["structure"])}")
    IO.puts("Lines: #{result["metrics"]["total_lines"]}")

  {:error, reason} ->
    IO.puts("Error: #{reason}")
end
```text

## Types

### `TreeSitterLanguagePack.ProcessConfig`

Configuration for code intelligence analysis.

Use with pipe operators for fluent API.

**Constructor:**

```elixir
config = TreeSitterLanguagePack.ProcessConfig.new("python")
```text

**Methods:**

#### `structure() :: ProcessConfig`

Enable structure extraction.

#### `import_exports() :: ProcessConfig`

Enable imports/exports extraction.

#### `comments() :: ProcessConfig`

Enable comment extraction.

#### `docstrings() :: ProcessConfig`

Enable docstring extraction.

#### `symbols() :: ProcessConfig`

Enable symbol extraction.

#### `metrics() :: ProcessConfig`

Enable metric extraction.

#### `diagnostics() :: ProcessConfig`

Enable diagnostic extraction.

#### `with_chunks(max_size :: integer, overlap :: integer) :: ProcessConfig`

Configure code chunking.

#### `all() :: ProcessConfig`

Enable all features.

**Example:**

```elixir
config = TreeSitterLanguagePack.ProcessConfig.new("python")
  |> TreeSitterLanguagePack.ProcessConfig.structure()
  |> TreeSitterLanguagePack.ProcessConfig.import_exports()
  |> TreeSitterLanguagePack.ProcessConfig.comments()
  |> TreeSitterLanguagePack.ProcessConfig.with_chunks(2000, 400)
```text

### Result Map

Result from `process` function.

**Keys:**

- `"language"` (String) - Language name
- `"metrics"` (Map) - File metrics
    - `"total_lines"` (integer)
    - `"code_lines"` (integer)
    - `"comment_lines"` (integer)
    - `"blank_lines"` (integer)
- `"structure"` (list) - Code structure items
    - Each item has `"kind"`, `"name"`, `"line"`, `"column"`, etc.
- `"imports"` (list) - Import statements
- `"exports"` (list) - Export statements
- `"comments"` (list) - Comments
- `"docstrings"` (list) - Docstrings
- `"symbols"` (list) - Symbols
- `"diagnostics"` (list) - Diagnostics
- `"chunks"` (list) - Code chunks
- `"parse_errors"` (integer) - Number of parse errors

**Example:**

```elixir
{:ok, result} = TreeSitterLanguagePack.process(source, config)

language = result["language"]
structure = result["structure"]

Enum.each(structure, fn item ->
  IO.puts("#{item["kind"]}: #{item["name"]}")
end)
```text

## Error Handling

Use pattern matching with case/with for error handling:

```elixir
case TreeSitterLanguagePack.get_language("python") do
  {:ok, language} ->
    IO.puts("Got Python")

  {:error, :language_not_found} ->
    IO.puts("Language not available")

  {:error, :download_failed} ->
    IO.puts("Download failed")

  {:error, reason} ->
    IO.puts("Error: #{inspect(reason)}")
end
```text

Or with `with` for multi-step operations:

```elixir
with {:ok, parser} <- TreeSitterLanguagePack.get_parser("python"),
     {:ok, tree} <- TreeSitter.Parser.parse(parser, source),
     {:ok, result} <- TreeSitterLanguagePack.process(source, config) do
  IO.inspect(result)
else
  {:error, reason} -> IO.puts("Error: #{reason}")
end
```text

## Usage Patterns

### Pre-download in Application Start

```elixir
# lib/my_app/application.ex
defmodule MyApp.Application do
  use Application

  @impl true
  def start(_type, _args) do
    TreeSitterLanguagePack.init(
      languages: ["python", "rust", "typescript", "javascript"]
    )

    children = [
      # ... other children
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
```text

### Custom Cache Directory

```elixir
# lib/my_app/config.ex
TreeSitterLanguagePack.configure(cache_dir: "/data/ts-pack-cache")
```text

### Process Batch Files

```elixir
defmodule MyApp.Analyzer do
  @spec analyze_files(String.t(), String.t()) :: :ok
  def analyze_files(dir, language) do
    config = TreeSitterLanguagePack.ProcessConfig.new(language)
      |> TreeSitterLanguagePack.ProcessConfig.all()

    dir
    |> File.ls!()
    |> Enum.filter(&String.ends_with?(&1, ".py"))
    |> Enum.each(fn file ->
      path = Path.join(dir, file)
      source = File.read!(path)

      case TreeSitterLanguagePack.process(source, config) do
        {:ok, result} ->
          IO.puts("#{file}: #{length(result["structure"])} items")

        {:error, reason} ->
          IO.puts("Error: #{reason}")
      end
    end)

    :ok
  end
end
```text

### Concurrent Processing with Tasks

```elixir
defmodule MyApp.ConcurrentAnalyzer do
  @spec analyze_files_async(list(String.t()), String.t()) :: list(map)
  def analyze_files_async(files, language) do
    config = TreeSitterLanguagePack.ProcessConfig.new(language)
      |> TreeSitterLanguagePack.ProcessConfig.all()

    files
    |> Task.async_stream(fn file ->
      case File.read(file) do
        {:ok, source} ->
          case TreeSitterLanguagePack.process(source, config) do
            {:ok, result} -> {:ok, result}
            {:error, reason} -> {:error, {file, reason}}
          end

        {:error, reason} ->
          {:error, {file, reason}}
      end
    end)
    |> Enum.map(&elem(&1, 1))
  end
end
```text

### Parse and Walk Tree

```elixir
defmodule MyApp.TreeWalker do
  @spec walk_tree(any(), non_neg_integer()) :: :ok
  def walk_tree(node, depth \\ 0) do
    indent = String.duplicate("  ", depth)
    type = TreeSitter.Node.type(node)
    IO.puts("#{indent}#{type}")

    node
    |> TreeSitter.Node.children()
    |> Enum.each(&walk_tree(&1, depth + 1))
  end
end
```text

### Extract Specific Patterns

```elixir
defmodule MyApp.FunctionFinder do
  @spec find_functions(String.t(), String.t()) :: list(map)
  def find_functions(source, language) do
    config = TreeSitterLanguagePack.ProcessConfig.new(language)
      |> TreeSitterLanguagePack.ProcessConfig.structure()

    case TreeSitterLanguagePack.process(source, config) do
      {:ok, result} ->
        result["structure"]
        |> Enum.filter(&(&1["kind"] == "function"))

      {:error, _reason} ->
        []
    end
  end
end
```text

### ExUnit Tests

```elixir
defmodule MyApp.AnalyzerTest do
  use ExUnit.Case

  setup do
    TreeSitterLanguagePack.init(languages: ["python"])
    :ok
  end

  test "analyzes python code" do
    source = """
    def hello():
        pass
    """

    config = TreeSitterLanguagePack.ProcessConfig.new("python")
      |> TreeSitterLanguagePack.ProcessConfig.all()

    {:ok, result} = TreeSitterLanguagePack.process(source, config)

    assert result["language"] == "python"
    assert length(result["structure"]) > 0
  end
end
```text

## Type Specifications

Define specs for type safety with Dialyzer:

```elixir
@spec analyze(String.t(), String.t()) :: {:ok, map()} | {:error, String.t()}
def analyze(source, language) do
  config = TreeSitterLanguagePack.ProcessConfig.new(language)
    |> TreeSitterLanguagePack.ProcessConfig.all()

  TreeSitterLanguagePack.process(source, config)
end