# `Minga.Language`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/language.ex#L1)

Unified per-language configuration struct.

Every supported language is described by a single `%Language{}` struct
that consolidates data previously spread across `Filetype`, `Comment`,
`Formatter`, `LSP.ServerRegistry`, `Highlight.Grammar`, `Devicon`,
`Modeline`, and `Project.Detector`.

Language definitions live in modules that export a `definition/0` function returning a `%Language{}` struct. Bundled definitions are grouped into language pack extensions under `Minga.Extensions.LanguagePacks`, and third-party packs register the same struct shape through `Minga.Language.Registry.register/2`.

## Adding a new language

Create a language module inside a pack, return a `%Language{}` from `definition/0`, and add that module to the pack's `language_modules/0` list. Removing the pack's registry source removes the whole language record, so the language name, filetypes, shebangs, devicon, grammar metadata, formatter, and LSP defaults go away together.

## Extension languages

Extensions register languages at runtime via `Minga.Language.Registry.register/2`, passing a `%Language{}` struct and a `{:extension, name}` source.

# `t`

```elixir
@type t() :: %Minga.Language{
  comment_token: String.t(),
  extensions: [String.t()],
  filenames: [String.t()],
  formatter: String.t() | nil,
  grammar: String.t() | nil,
  icon: String.t() | nil,
  icon_color: non_neg_integer() | nil,
  indent_with: :spaces | :tabs,
  label: String.t(),
  language_servers: [Minga.LSP.ServerConfig.t()],
  name: atom(),
  project_type: atom() | nil,
  root_markers: [String.t()],
  shebangs: [String.t()],
  tab_width: pos_integer()
}
```

A per-language configuration.

# `all`

```elixir
@spec all() :: [t()]
```

Returns all registered language definitions.

# `block_pairs`

```elixir
@spec block_pairs(atom()) :: [Minga.Language.BlockPair.t()]
```

Returns language-owned block auto-close metadata for a language name.

# `detect_filetype`

```elixir
@spec detect_filetype(String.t() | nil) :: atom()
```

Detects a file's language atom from its path.

# `detect_filetype_from_content`

```elixir
@spec detect_filetype_from_content(String.t(), String.t()) :: atom()
```

Detects filetype using both path and first line content (shebang).

# `get`

```elixir
@spec get(atom()) :: t() | nil
```

Returns the language definition for a name atom (e.g., `:elixir`), or nil.

# `register`

```elixir
@spec register(String.t(), String.t(), [Minga.Language.TreeSitter.register_opt()]) ::
  :ok | {:error, String.t()}
```

Registers a new language with Minga.

Compiles the tree-sitter grammar sources, loads them into the parser,
sends highlight/injection queries, and registers filetype mappings.
Compilation and setup failures are reported synchronously; the parser-side
load response is asynchronous, so callers decide whether to fail the
extension or continue without highlighting.

`name` is the language identifier (e.g., `"org"`).
`source_dir` is the path containing the grammar's `parser.c` and
optionally `scanner.c`.

## Options

- `:highlights` - path to a `highlights.scm` query file
- `:injections` - path to an `injections.scm` query file
- `:filetype_extensions` - list of file extensions to map (e.g., `[".org"]`)
- `:filetype_filenames` - list of exact filenames to map (e.g., `["Orgfile"]`)
- `:filetype_atom` - the filetype atom (e.g., `:org`)

## Example

    Minga.Language.register("org", "/path/to/tree-sitter-org/src",
      highlights: "/path/to/queries/org/highlights.scm",
      filetype_extensions: [".org"],
      filetype_atom: :org
    )

---

*Consult [api-reference.md](api-reference.md) for complete listing*
