mirror of
https://github.com/nvim-treesitter/nvim-treesitter.git
synced 2026-07-17 02:40:09 -04:00
feat(configs): dynamic module defintions
This commit is contained in:
parent
27d7a0ffff
commit
673c9d679a
3 changed files with 206 additions and 74 deletions
37
README.md
37
README.md
|
|
@ -35,7 +35,7 @@ $ git clone https://github.com/nvim-treesitter/nvim-treesitter.git
|
||||||
|
|
||||||
## Adding parsers
|
## Adding parsers
|
||||||
|
|
||||||
Treesitter is using a different _parser_ for every language. It can be quite a pain to install, but fortunately `nvim-treesitter`
|
Treesitter is using a different _parser_ for every language. It can be quite a pain to install, but fortunately `nvim-treesitter`
|
||||||
provides two command to tackle this issue:
|
provides two command to tackle this issue:
|
||||||
- `TSInstall` to install one or more parser. You can use `TSInstall all` to download all parsers.
|
- `TSInstall` to install one or more parser. You can use `TSInstall all` to download all parsers.
|
||||||
- `TSInstallInfo` to know which parser is installed.
|
- `TSInstallInfo` to know which parser is installed.
|
||||||
|
|
@ -159,6 +159,41 @@ The roadmap and all features of this plugin are open to change, and any suggesti
|
||||||
* List all definitions
|
* List all definitions
|
||||||
* Go to definition
|
* Go to definition
|
||||||
|
|
||||||
|
## Defining Modules
|
||||||
|
|
||||||
|
Users and plugin authors can take advantage of modules by creating their own. Modules provide:
|
||||||
|
|
||||||
|
- Treesitter language detection support
|
||||||
|
- Attach and detach to buffers
|
||||||
|
- Works with all nvim-treesitter commands
|
||||||
|
|
||||||
|
You can use the `define_modules` function to define one or more modules or module groups.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require 'nvim-treesitter'.define_modules {
|
||||||
|
my_cool_plugin = {
|
||||||
|
attach = function(bufnr, lang)
|
||||||
|
-- Do cool stuff here
|
||||||
|
end,
|
||||||
|
detach = function(bufnr)
|
||||||
|
-- Undo cool stuff here
|
||||||
|
end,
|
||||||
|
is_supported = function(lang)
|
||||||
|
-- Check if the language is supported
|
||||||
|
end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Modules can consist of the following properties:
|
||||||
|
|
||||||
|
- `module_path`: A require path (string) that exports a module with an `attach` and `detach` function. This is not required if the functions are on this definition.
|
||||||
|
- `enable`: Determines if the module is enabled by default. This is usually overridden by the user.
|
||||||
|
- `disable`: A list of languages that this module is disabled for. This is usually overridden by the user.
|
||||||
|
- `is_supported`: A function that takes a language and determines if this module supports that language.
|
||||||
|
- `attach`: A function that attachs to a buffer. This is required if `module_path` is not provided.
|
||||||
|
- `detach`: A function that detaches from a buffer. This is required if `module_path` is not provided.
|
||||||
|
|
||||||
## Utils
|
## Utils
|
||||||
|
|
||||||
you can get some utility functions with
|
you can get some utility functions with
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
local api = vim.api
|
|
||||||
|
|
||||||
local install = require'nvim-treesitter.install'
|
local install = require'nvim-treesitter.install'
|
||||||
local utils = require'nvim-treesitter.utils'
|
local utils = require'nvim-treesitter.utils'
|
||||||
local ts_utils = require'nvim-treesitter.ts_utils'
|
local ts_utils = require'nvim-treesitter.ts_utils'
|
||||||
|
|
@ -13,17 +11,11 @@ function M.setup()
|
||||||
utils.setup_commands('install', install.commands)
|
utils.setup_commands('install', install.commands)
|
||||||
utils.setup_commands('info', info.commands)
|
utils.setup_commands('info', info.commands)
|
||||||
utils.setup_commands('configs', configs.commands)
|
utils.setup_commands('configs', configs.commands)
|
||||||
|
configs.init()
|
||||||
|
end
|
||||||
|
|
||||||
for _, lang in pairs(parsers.available_parsers()) do
|
function M.define_modules(...)
|
||||||
for _, mod in pairs(configs.available_modules()) do
|
configs.define_modules(...)
|
||||||
if configs.is_enabled(mod, lang) then
|
|
||||||
local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
|
|
||||||
for _, ft in pairs(parsers.lang_to_ft(lang)) do
|
|
||||||
api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.statusline(indicator_size)
|
function M.statusline(indicator_size)
|
||||||
|
|
|
||||||
|
|
@ -4,76 +4,95 @@ local queries = require'nvim-treesitter.query'
|
||||||
local parsers = require'nvim-treesitter.parsers'
|
local parsers = require'nvim-treesitter.parsers'
|
||||||
local utils = require'nvim-treesitter.utils'
|
local utils = require'nvim-treesitter.utils'
|
||||||
|
|
||||||
-- @enable can be true or false
|
|
||||||
-- @disable is a list of languages, only relevant if enable is true
|
|
||||||
-- @keymaps list of user mappings for a given module if relevant
|
|
||||||
-- @is_supported function which, given a ft, will return true if the ft works on the module
|
|
||||||
local config = {
|
local config = {
|
||||||
modules = {
|
modules = {},
|
||||||
highlight = {
|
ensure_installed = nil
|
||||||
enable = false,
|
}
|
||||||
disable = {},
|
local user_setup_data = nil
|
||||||
is_supported = queries.has_highlights
|
-- List of modules that need to be setup on initialization.
|
||||||
|
local queued_modules_defs = {}
|
||||||
|
-- Whether we've initialized the plugin yet.
|
||||||
|
local is_initialized = false
|
||||||
|
local builtin_modules = {
|
||||||
|
highlight = {
|
||||||
|
module_path = 'nvim-treesitter.highlight',
|
||||||
|
enable = false,
|
||||||
|
disable = {},
|
||||||
|
is_supported = queries.has_highlights
|
||||||
|
},
|
||||||
|
incremental_selection = {
|
||||||
|
module_path = 'nvim-treesitter.incremental_selection',
|
||||||
|
enable = false,
|
||||||
|
disable = {},
|
||||||
|
keymaps = {
|
||||||
|
init_selection="gnn",
|
||||||
|
node_incremental="grn",
|
||||||
|
scope_incremental="grc",
|
||||||
|
node_decremental="grm"
|
||||||
},
|
},
|
||||||
incremental_selection = {
|
is_supported = queries.has_locals
|
||||||
|
},
|
||||||
|
refactor = {
|
||||||
|
highlight_definitions = {
|
||||||
|
module_path = 'nvim-treesitter.refactor.highlight_definitions',
|
||||||
enable = false,
|
enable = false,
|
||||||
disable = {},
|
disable = {},
|
||||||
keymaps = {
|
|
||||||
init_selection="gnn",
|
|
||||||
node_incremental="grn",
|
|
||||||
scope_incremental="grc",
|
|
||||||
node_decremental="grm"
|
|
||||||
},
|
|
||||||
is_supported = queries.has_locals
|
is_supported = queries.has_locals
|
||||||
},
|
},
|
||||||
refactor = {
|
smart_rename = {
|
||||||
highlight_definitions = {
|
module_path = 'nvim-treesitter.refactor.smart_rename',
|
||||||
enable = false,
|
enable = false,
|
||||||
disable = {},
|
disable = {},
|
||||||
is_supported = queries.has_locals
|
is_supported = queries.has_locals,
|
||||||
},
|
keymaps = {
|
||||||
smart_rename = {
|
smart_rename = "grr"
|
||||||
enable = false,
|
}
|
||||||
disable = {},
|
},
|
||||||
is_supported = queries.has_locals,
|
navigation = {
|
||||||
keymaps = {
|
module_path = 'nvim-treesitter.refactor.navigation',
|
||||||
smart_rename = "grr"
|
enable = false,
|
||||||
}
|
disable = {},
|
||||||
},
|
is_supported = queries.has_locals,
|
||||||
navigation = {
|
keymaps = {
|
||||||
enable = false,
|
goto_definition = "gnd",
|
||||||
disable = {},
|
list_definitions = "gnD"
|
||||||
is_supported = queries.has_locals,
|
|
||||||
keymaps = {
|
|
||||||
goto_definition = "gnd",
|
|
||||||
list_definitions = "gnD"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ensure_installed = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
|
-- Resolves a module by requiring the `module_path` or using the module definition.
|
||||||
|
local function resolve_module(mod_name)
|
||||||
|
local config_mod = M.get_module(mod_name)
|
||||||
|
|
||||||
|
if not config_mod then return end
|
||||||
|
|
||||||
|
if type(config_mod.attach) == 'function' and type(config_mod.detach) == 'function' then
|
||||||
|
return config_mod
|
||||||
|
elseif type(config_mod.module_path) == 'string' then
|
||||||
|
return require(config_mod.module_path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function enable_module(mod, bufnr, lang)
|
local function enable_module(mod, bufnr, lang)
|
||||||
local bufnr = bufnr or api.nvim_get_current_buf()
|
local bufnr = bufnr or api.nvim_get_current_buf()
|
||||||
local lang = lang or parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, 'ft'))
|
local lang = lang or parsers.ft_to_lang(api.nvim_buf_get_option(bufnr, 'ft'))
|
||||||
|
|
||||||
if not parsers.list[lang] or not M.get_module(mod) then
|
if not parsers.list[lang] then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
|
M.attach_module(mod, bufnr, lang)
|
||||||
loaded_mod.attach(bufnr, lang)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function enable_mod_conf_autocmd(mod, lang)
|
local function enable_mod_conf_autocmd(mod, lang)
|
||||||
local config_mod = M.get_module(mod)
|
local config_mod = M.get_module(mod)
|
||||||
|
|
||||||
if not config_mod or M.is_enabled(mod, lang) then return end
|
if not config_mod or not M.is_enabled(mod, lang) then return end
|
||||||
|
|
||||||
local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
|
local cmd = string.format("lua require'nvim-treesitter.configs'.attach_module('%s')", mod)
|
||||||
for _, ft in pairs(parsers.lang_to_ft(lang)) do
|
for _, ft in pairs(parsers.lang_to_ft(lang)) do
|
||||||
api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
|
api.nvim_command(string.format("autocmd NvimTreesitter FileType %s %s", ft, cmd))
|
||||||
end
|
end
|
||||||
|
|
@ -117,12 +136,11 @@ local function disable_module(mod, bufnr, lang)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not parsers.list[lang] or not M.get_module(mod) then
|
if not parsers.list[lang] then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local loaded_mod = require(string.format("nvim-treesitter.%s", mod))
|
M.detach_module(mod, bufnr)
|
||||||
loaded_mod.detach(bufnr)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disable_mod_conf_autocmd(mod, lang)
|
local function disable_mod_conf_autocmd(mod, lang)
|
||||||
|
|
@ -130,7 +148,6 @@ local function disable_mod_conf_autocmd(mod, lang)
|
||||||
|
|
||||||
if not config_mod or not M.is_enabled(mod, lang) then return end
|
if not config_mod or not M.is_enabled(mod, lang) then return end
|
||||||
|
|
||||||
--local cmd = string.format("lua require'nvim-treesitter.%s'.attach()", mod)
|
|
||||||
-- TODO(kyazdani): detach the correct autocmd... doesn't work when using %s, cmd
|
-- TODO(kyazdani): detach the correct autocmd... doesn't work when using %s, cmd
|
||||||
for _, ft in pairs(parsers.lang_to_ft(lang)) do
|
for _, ft in pairs(parsers.lang_to_ft(lang)) do
|
||||||
api.nvim_command(string.format("autocmd! NvimTreesitter FileType %s", ft))
|
api.nvim_command(string.format("autocmd! NvimTreesitter FileType %s", ft))
|
||||||
|
|
@ -160,7 +177,7 @@ local function disable_all(mod, lang)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Recurses trough all modules including submodules
|
-- Recurses through all modules including submodules
|
||||||
-- @param accumulator function called for each module
|
-- @param accumulator function called for each module
|
||||||
-- @param root root configuration table to start at
|
-- @param root root configuration table to start at
|
||||||
-- @param path prefix path
|
-- @param path prefix path
|
||||||
|
|
@ -171,7 +188,7 @@ local function recurse_modules(accumulator, root, path)
|
||||||
local new_path = path and (path..'.'..name) or name
|
local new_path = path and (path..'.'..name) or name
|
||||||
|
|
||||||
if M.is_module(module) then
|
if M.is_module(module) then
|
||||||
accumulator(name, module, new_path)
|
accumulator(name, module, new_path, root)
|
||||||
elseif type(module) == 'table' then
|
elseif type(module) == 'table' then
|
||||||
recurse_modules(accumulator, module, new_path)
|
recurse_modules(accumulator, module, new_path)
|
||||||
end
|
end
|
||||||
|
|
@ -230,10 +247,10 @@ function M.is_enabled(mod, lang)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Setup call for users to override module configurations.
|
||||||
|
-- @param user_data module overrides
|
||||||
function M.setup(user_data)
|
function M.setup(user_data)
|
||||||
if not user_data then return end
|
user_setup_data = user_data or {}
|
||||||
|
|
||||||
M.setup_module(config.modules, user_data)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sets up a single module or all submodules of a group.
|
-- Sets up a single module or all submodules of a group.
|
||||||
|
|
@ -245,7 +262,7 @@ function M.setup_module(mod, data, mod_name)
|
||||||
if mod_name == 'ensure_installed' then
|
if mod_name == 'ensure_installed' then
|
||||||
config.ensure_installed = data
|
config.ensure_installed = data
|
||||||
require'nvim-treesitter.install'.ensure_installed(data)
|
require'nvim-treesitter.install'.ensure_installed(data)
|
||||||
elseif M.is_module(mod) then
|
elseif M.is_module(mod) and type(data) == 'table' then
|
||||||
if type(data.enable) == 'boolean' then
|
if type(data.enable) == 'boolean' then
|
||||||
mod.enable = data.enable
|
mod.enable = data.enable
|
||||||
end
|
end
|
||||||
|
|
@ -260,18 +277,93 @@ function M.setup_module(mod, data, mod_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif type(data) == 'table' and type(mod) == 'table' then
|
elseif type(data) == 'table' and type(mod) == 'table' then
|
||||||
for key, value in pairs(data) do
|
for key, value in pairs(mod) do
|
||||||
M.setup_module(mod[key], value, key)
|
M.setup_module(value, data[key], key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.available_modules()
|
-- Defines a table of modules that can be attached/detached to buffers
|
||||||
|
-- based on language support. A module consist of the following properties:
|
||||||
|
-- * @enable Whether the modules is enabled. Can be true or false.
|
||||||
|
-- * @disable A list of languages to disable the module for. Only relevant if enable is true.
|
||||||
|
-- * @keymaps A list of user mappings for a given module if relevant.
|
||||||
|
-- * @is_supported A function which, given a ft, will return true if the ft works on the module.
|
||||||
|
-- * @module_path A string path to a module file using `require`. The exported module must contain
|
||||||
|
-- an `attach` and `detach` function. This path is not required if `attach` and `detach`
|
||||||
|
-- functions are provided directly on the module definition.
|
||||||
|
-- * @attach An attach function that is called for each buffer that the module is enabled for. This is required
|
||||||
|
-- if a `module_path` is not specified.
|
||||||
|
-- * @detach A detach function that is called for each buffer that the module is enabled for. This is required
|
||||||
|
-- if a `module_path` is not specified.
|
||||||
|
-- Modules are not setup until `init` is invoked by the plugin. This allows modules to be defined in any order
|
||||||
|
-- and can be loaded lazily.
|
||||||
|
-- @example
|
||||||
|
-- require"nvim-treesitter".define_modules {
|
||||||
|
-- my_cool_module = {
|
||||||
|
-- attach = function()
|
||||||
|
-- do_some_cool_setup()
|
||||||
|
-- end,
|
||||||
|
-- detach = function()
|
||||||
|
-- do_some_cool_teardown()
|
||||||
|
-- end
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
function M.define_modules(mod_defs)
|
||||||
|
if not is_initialized then
|
||||||
|
table.insert(queued_modules_defs, mod_defs)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
recurse_modules(function(key, mod, _, group)
|
||||||
|
group[key] = vim.tbl_extend("keep", mod, {
|
||||||
|
enable = false,
|
||||||
|
disable = {},
|
||||||
|
is_supported = function() return true end
|
||||||
|
})
|
||||||
|
end, mod_defs)
|
||||||
|
|
||||||
|
M.setup_module(mod_defs, user_setup_data)
|
||||||
|
config.modules = vim.tbl_extend("keep", config.modules, mod_defs)
|
||||||
|
|
||||||
|
for _, lang in pairs(parsers.available_parsers()) do
|
||||||
|
for _, mod in ipairs(M.available_modules(mod_defs)) do
|
||||||
|
enable_mod_conf_autocmd(mod, lang)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Attaches a module to a buffer
|
||||||
|
-- @param mod_name the module name
|
||||||
|
-- @param bufnr the bufnr
|
||||||
|
-- @param lang the language of the buffer
|
||||||
|
function M.attach_module(mod_name, bufnr, lang)
|
||||||
|
local resolved_mod = resolve_module(mod_name)
|
||||||
|
|
||||||
|
if resolved_mod then
|
||||||
|
resolved_mod.attach(bufnr, lang)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detaches a module to a buffer
|
||||||
|
-- @param mod_name the module name
|
||||||
|
-- @param bufnr the bufnr
|
||||||
|
function M.detach_module(mod_name, bufnr)
|
||||||
|
local resolved_mod = resolve_module(mod_name)
|
||||||
|
|
||||||
|
if resolved_mod then
|
||||||
|
resolved_mod.detach(bufnr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gets available modules
|
||||||
|
-- @param root root table to find modules
|
||||||
|
function M.available_modules(root)
|
||||||
local modules = {}
|
local modules = {}
|
||||||
|
|
||||||
recurse_modules(function(_, _, path)
|
recurse_modules(function(_, _, path)
|
||||||
table.insert(modules, path)
|
table.insert(modules, path)
|
||||||
end)
|
end, root)
|
||||||
|
|
||||||
return modules
|
return modules
|
||||||
end
|
end
|
||||||
|
|
@ -286,10 +378,23 @@ function M.get_module(mod_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Determines whether the provided table is a module.
|
-- Determines whether the provided table is a module.
|
||||||
-- A module should contain an 'is_supported' function.
|
-- A module should contain an attach and detach function.
|
||||||
-- @param mod the module table
|
-- @param mod the module table
|
||||||
function M.is_module(mod)
|
function M.is_module(mod)
|
||||||
return type(mod) == 'table' and type(mod.is_supported) == 'function'
|
return type(mod) == 'table'
|
||||||
|
and ((type(mod.attach) == 'function' and type(mod.detach) == 'function')
|
||||||
|
or type(mod.module_path) == 'string')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Initializes built-in modules and any queued modules
|
||||||
|
-- registered by plugins or the user.
|
||||||
|
function M.init()
|
||||||
|
is_initialized = true
|
||||||
|
M.define_modules(builtin_modules)
|
||||||
|
|
||||||
|
for _, mod_def in ipairs(queued_modules_defs) do
|
||||||
|
M.define_modules(mod_def)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue