# How 99 Auto-Detects and Loads Language-Specific Configurations in Neovim

> Discover how 99 auto-detects and loads language-specific configurations in Neovim by mapping the filetype option to pre-loaded modules for tailored editor helpers. Learn more!

- Repository: [ThePrimeagen/99](https://github.com/theprimeagen/99)
- Tags: how-to-guide
- Published: 2026-02-19

---

**99 determines the language of the current buffer by reading Neovim's `filetype` option and mapping it to a pre-loaded language module that provides editor-specific helpers like `log_item`.**

ThePrimeagen's **99** plugin for Neovim eliminates manual configuration by automatically detecting which programming language you're editing and loading the appropriate behavior modules. This auto-detection and load language-specific configurations system works through a lookup table built during initialization, requiring zero user intervention when switching between supported filetypes like Lua, Go, Java, or Elixir.

## The Architecture Behind 99's Language Detection

The detection mechanism follows a five-step pipeline that bridges Neovim's buffer state with 99's modular language implementations.

### Step 1: Defining Supported Languages in State

The core plugin state declares which languages are available. In [`lua/99/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/init.lua) at line 91, the `_99.State` table defines a default array:

```lua
languages = { "lua", "go", "java", "elixir", "cpp", "ruby" }

```

This array controls which language modules 99 attempts to load during setup.

### Step 2: Building the Language Module Lookup Table

When you call `require('99').setup()`, the plugin invokes `Languages.initialize(_99_state)` from [`lua/99/language/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/init.lua) (lines 36-41). This function iterates over the `languages` array and dynamically requires each module:

```lua
for _, lang in ipairs(state.languages) do
    local ok, mod = pcall(require, "99.language." .. lang)
    if ok then
        M.languages[lang] = mod
    end
end

```

The resulting modules are stored in `M.languages`, a table that maps **filetype strings** to **language module objects**.

### Step 3: Detecting the Current Buffer's Filetype

Whenever 99 needs language-specific behavior, it calls `get_langauge(buf)` (note: the function name contains a typo in the source as `get_langauge`). Located in [`lua/99/language/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/init.lua) at lines 20-22, this function queries Neovim's API:

```lua
local function get_langauge(buffer)
    return vim.api.nvim_get_option_value("filetype", { buf = buffer })
end

```

This retrieves the exact `filetype` option that Neovim sets automatically when you open a file.

### Step 4: Resolving and Loading the Language Module

The detection pipeline completes by indexing into the pre-built lookup table. At lines 22-26 in [`lua/99/language/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/init.lua), the code performs:

```lua
local file_type = get_langauge(buffer)
local lang = M.languages[file_type]
if not lang then
    -- fatal log: language not supported
end

```

If the `filetype` exists in `M.languages`, the corresponding module is returned. If not, 99 emits a fatal error indicating the language lacks support.

## How 99 Uses Language-Specific Helpers

Once the language module is resolved, 99 delegates editor operations to language-specific implementations.

### The log_item Function Example

The `log_item` function demonstrates this delegation pattern. In [`lua/99/language/init.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/init.lua) (lines 47-52), the public API function forwards calls to the active language module:

```lua
function M.log_item(item_name)
    local lang = get_langauge(vim.api.nvim_get_current_buf())
    local mod = M.languages[lang]
    return mod.log_item(item_name)
end

```

Each language module implements its own `log_item`. For example:
- **Lua** ([`lua/99/language/lua.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/lua.lua)): Returns `vim.inspect(...)` wrapping
- **Go** ([`lua/99/language/go.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/go.lua)): Returns `fmt.Printf(...)` statements
- **Java** ([`lua/99/language/java.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/java.lua)): Returns `System.out.println(...)` equivalents

### Tree-sitter Integration

Language modules also export `names` tables that map semantic concepts to Tree-sitter node types. The [`lua/99/editor/treesitter.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/editor/treesitter.lua) file imports these definitions to construct language-aware queries for operations like "find function" or "find class."

## Extending 99 with Custom Language Support

The auto-detection system is fully extensible without modifying core files.

To add **Python** support, create two artifacts:

1. **Extend the language list** in your setup configuration (or fork):

```lua
-- In your init.lua or 99 setup
require('99').setup({
    languages = { "lua", "go", "java", "elixir", "cpp", "ruby", "python" }
})

```

2. **Create the language module** at [`lua/99/language/python.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/python.lua):

```lua
local M = {}

-- Optional: Tree-sitter node name mappings
M.names = {
    function_declaration = "function_definition",
    class_declaration = "class_definition",
}

-- Required: Generate language-specific log statement
function M.log_item(item_name)
    return string.format('print(f"{%s=}")', item_name)
end

return M

```

When you open a `.py` file, Neovim sets `filetype=python`, and 99 automatically resolves `M.languages["python"]` to your new module.

## Summary

- **99 reads Neovim's `filetype` option** via `vim.api.nvim_get_option_value` to determine the current buffer's language.
- **A lookup table maps filetypes to modules** built during `setup()` by iterating over the `languages` array in `_99.State`.
- **Language-specific behavior** is delegated to modules like [`lua/99/language/go.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/go.lua), which implement functions such as `log_item`.
- **Extending support** requires only adding a string to the `languages` list and creating a corresponding module file that exports the required interface.

## Frequently Asked Questions

### How does 99 handle unsupported filetypes?

If the current buffer's `filetype` is not present in the `M.languages` lookup table, 99 emits a fatal error message indicating that the language is not supported. The plugin does not fall back to a default language; it requires an exact match between Neovim's `filetype` value and a key in the language table.

### Can I override an existing language module without forking the repository?

Yes. Since 99 uses Lua's `require` system to load language modules from `lua/99/language/<name>.lua`, you can use Neovim's `runtimepath` precedence to shadow the built-in module. Place a file at [`lua/99/language/go.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/go.lua) (or any language) in your personal configuration directory (e.g., `~/.config/nvim/lua/99/language/go.lua`), and Neovim will load your version instead of the bundled one when 99 initializes.

### What functions must a custom language module implement?

At minimum, a language module must export a `log_item` function that accepts an `item_name` string and returns a string representing the language-specific logging statement. Optionally, the module can export a `names` table that maps abstract concepts like `function_declaration` to Tree-sitter node names used by 99's editor features. Refer to [`lua/99/language/lua.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/lua.lua) and [`lua/99/language/go.lua`](https://github.com/ThePrimeagen/99/blob/main/lua/99/language/go.lua) for complete implementation examples.