How 99 Auto-Detects and Loads Language-Specific Configurations in Neovim
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 at line 91, the _99.State table defines a default array:
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 (lines 36-41). This function iterates over the languages array and dynamically requires each module:
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 at lines 20-22, this function queries Neovim's API:
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, the code performs:
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 (lines 47-52), the public API function forwards calls to the active language module:
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): Returnsvim.inspect(...)wrapping - Go (
lua/99/language/go.lua): Returnsfmt.Printf(...)statements - Java (
lua/99/language/java.lua): ReturnsSystem.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 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:
- Extend the language list in your setup configuration (or fork):
-- In your init.lua or 99 setup
require('99').setup({
languages = { "lua", "go", "java", "elixir", "cpp", "ruby", "python" }
})
- Create the language module at
lua/99/language/python.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
filetypeoption viavim.api.nvim_get_option_valueto determine the current buffer's language. - A lookup table maps filetypes to modules built during
setup()by iterating over thelanguagesarray in_99.State. - Language-specific behavior is delegated to modules like
lua/99/language/go.lua, which implement functions such aslog_item. - Extending support requires only adding a string to the
languageslist 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 (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 and lua/99/language/go.lua for complete implementation examples.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →