plugins/treesitter: switch to mkNeovimPlugin

This commit is contained in:
Austin Horstman 2024-07-01 23:00:34 -05:00
parent c0ea106b4b
commit 435ef287ab
No known key found for this signature in database
3 changed files with 386 additions and 193 deletions

View file

@ -0,0 +1,17 @@
;; extends
(binding
attrpath: (attrpath (identifier) @_path)
expression: [
(string_expression (string_fragment) @lua)
(indented_string_expression (string_fragment) @lua)
]
(#match? @_path "^extraConfigLua(Pre|Post)?$"))
(binding
attrpath: (attrpath (identifier) @_path)
expression: [
(string_expression (string_fragment) @vim)
(indented_string_expression (string_fragment) @vim)
]
(#match? @_path "^extraConfigVim(Pre|Post)?$"))

View file

@ -6,210 +6,309 @@
... ...
}: }:
with lib; with lib;
helpers.neovim-plugin.mkNeovimPlugin config {
name = "treesitter";
originalName = "nvim-treesitter";
luaName = "nvim-treesitter.configs";
defaultPackage = pkgs.vimPlugins.nvim-treesitter;
maintainers = [ lib.maintainers.khaneliman ];
# TODO introduced 2024-07-06: remove after 24.11
optionsRenamedToSettings = [
"ensureInstalled"
"ignoreInstall"
"parserInstallDir"
[
"incrementalSelection"
"enable"
]
[
"incrementalSelection"
"keymaps"
"initSelection"
"nodeDecremental"
]
[
"incrementalSelection"
"keymaps"
"initSelection"
"nodeIncremental"
]
[
"incrementalSelection"
"keymaps"
"initSelection"
"scopeIncremental"
]
];
imports =
let let
cfg = config.plugins.treesitter; basePluginPath = [
"plugins"
"treesitter"
];
settingsPath = basePluginPath ++ [ "settings" ];
in
[
(lib.mkRenamedOptionModule (basePluginPath ++ [ "moduleConfig" ]) settingsPath)
(lib.mkRenamedOptionModule (basePluginPath ++ [ "customCaptures" ]) (
settingsPath
++ [
"highlight"
"custom_captures"
]
))
(lib.mkRenamedOptionModule (basePluginPath ++ [ "disabledLanguages" ]) (
settingsPath
++ [
"highlight"
"disable"
]
))
(lib.mkRenamedOptionModule (basePluginPath ++ [ "indent" ]) (
settingsPath
++ [
"indent"
"enable"
]
))
];
settingsOptions = {
auto_install = helpers.defaultNullOpts.mkBool false ''
Whether to automatically install missing parsers when entering a buffer.
'';
highlight = {
additional_vim_regex_highlighting =
helpers.defaultNullOpts.mkNullableWithRaw
(with helpers.nixvimTypes; either bool (listOf (maybeRaw str)))
false
''
Setting this to true will run `syntax` and tree-sitter at the same time. \
Set this to `true` if you depend on 'syntax' being enabled (e.g. for indentation). \
See `:h syntax`.
Using this option may slow down your editor, and you may see some duplicate highlights. \
Instead of true, it can also be a list of languages.
'';
enable = helpers.defaultNullOpts.mkBool false ''
Whether to enable treesitter highlighting.
'';
disable =
helpers.defaultNullOpts.mkStrLuaFnOr (with helpers.nixvimTypes; listOf (maybeRaw str)) null
''
Can either be a list of the names of parsers you wish to disable or
a lua function that returns a boolean indicating the parser should be disabled.
'';
custom_captures = helpers.defaultNullOpts.mkAttrsOf types.str { } ''
Custom capture group highlighting.
'';
};
incremental_selection = {
enable = helpers.defaultNullOpts.mkBool false ''
Incremental selection based on the named nodes from the grammar.
'';
keymaps =
let
mkKeymap = default: helpers.defaultNullOpts.mkStr default "Key shortcut";
in in
{ {
options = { init_selection = mkKeymap "gnn";
plugins.treesitter = { node_incremental = mkKeymap "grn";
enable = mkEnableOption "tree-sitter syntax highlighting"; scope_incremental = mkKeymap "grc";
node_decremental = mkKeymap "grm";
package = mkOption { };
type = types.package;
default = pkgs.vimPlugins.nvim-treesitter;
description = "Plugin to use for nvim-treesitter. If using nixGrammars, it should include a `withPlugins` function";
}; };
nixGrammars = mkOption { indent = {
enable = helpers.defaultNullOpts.mkBool false ''
Whether to enable treesitter indentation.
'';
};
ensure_installed = helpers.defaultNullOpts.mkListOf types.str [ ] ''
Either "all" or a list of languages to ensure installing.
'';
ignore_install = helpers.defaultNullOpts.mkListOf types.str [ ] ''
List of parsers to ignore installing. Used when `ensure_installed` is set to "all".
'';
parser_install_dir = helpers.defaultNullOpts.mkStr "$XDG_DATA_HOME/nvim/treesitter" ''
Location of the parsers to be installed by the plugin (only needed when `nixGrammars` is disabled).
This default might not work on your own install, please make sure that `$XDG_DATA_HOME` is set if you want to use the default.
Otherwise, change it to something that will work for you!
'';
sync_install = helpers.defaultNullOpts.mkBool false ''
Install parsers synchronously (only applied to `ensure_installed`).
'';
};
settingsExample = {
additional_vim_regex_highlighting = true;
ensure_installed = true;
ignore_install = [ "rust" ];
indent = true;
parser_install_dir = "$XDG_DATA_HOME/nvim/treesitter";
highlight = {
enable = true;
disable = [ "rust" ];
custom_captures = { };
};
incremental_selection = {
enable = true;
keymaps = {
init_selection = "gnn";
node_incremental = "grn";
scope_incremental = "grc";
node_decremental = "grm";
};
};
};
extraOptions = {
folding = mkOption {
type = types.bool; type = types.bool;
default = true; default = true;
description = "Install grammars with Nix"; example = false;
}; description = "Whether to enable treesitter folding.";
ensureInstalled = mkOption {
type =
with types;
oneOf [
(enum [ "all" ])
(listOf str)
];
default = "all";
description = "Either \"all\" or a list of languages";
}; };
gccPackage = helpers.mkPackageOption { gccPackage = helpers.mkPackageOption {
default = if cfg.nixGrammars then null else pkgs.gcc; name = "gcc";
default = pkgs.gcc;
defaultText = literalExpression "pkgs.gcc";
example = literalExpression "pkgs.gcc14";
description = '' description = ''
Which package (if any) to be added as the GCC compiler. Which package (if any) to be added as the GCC compiler.
This is required to build grammars if you are not using `nixGrammars`. This is required to build grammars if you are not using `nixGrammars`.
To disable the installation of GCC, set this option to `null`. To disable the installation of GCC, set this option to `null`.
''; '';
}; };
parserInstallDir = mkOption { grammarPackages = mkOption {
type = types.nullOr types.str; type = with types; listOf package;
default = if cfg.nixGrammars then null else "$XDG_DATA_HOME/nvim/treesitter"; default = config.plugins.treesitter.package.passthru.allGrammars;
description = '' example = literalExpression "pkgs.vimPlugins.nvim-treesitter.passthru.allGrammars";
Location of the parsers to be installed by the plugin (only needed when nixGrammars is disabled). defaultText = literalExpression "config.plugins.treesitter.package.passthru.allGrammars";
This default might not work on your own install, please make sure that $XDG_DATA_HOME is set if you want to use the default. Otherwise, change it to something that will work for you! description = "Grammar packages to install";
'';
}; };
ignoreInstall = mkOption { # TODO: Implement rawLua support to be passed into extraConfigLua.
type = types.listOf types.str;
default = [ ];
description = "List of parsers to ignore installing (for \"all\")";
};
disabledLanguages = mkOption {
type = types.listOf types.str;
default = [ ];
description = "A list of languages to disable";
};
customCaptures = mkOption {
type = types.attrsOf types.str;
default = { };
description = "Custom capture group highlighting";
};
incrementalSelection =
let
keymap =
default:
mkOption {
type = types.str;
inherit default;
description = "Key shortcut";
};
in
{
enable = mkEnableOption "incremental selection based on the named nodes from the grammar";
keymaps = {
initSelection = keymap "gnn";
nodeIncremental = keymap "grn";
scopeIncremental = keymap "grc";
nodeDecremental = keymap "grm";
};
};
indent = mkEnableOption "tree-sitter based indentation";
folding = mkEnableOption "tree-sitter based folding";
languageRegister = mkOption { languageRegister = mkOption {
type = with types; attrsOf (either str (listOf str)); type = with types; attrsOf (coercedTo str toList (listOf str));
description = ''
This is a wrapping of the `vim.treesitter.language.register` function.
Register specific parsers to one or several filetypes.
The keys are the parser names and the values are either one or several filetypes.
'';
default = { }; default = { };
example = { example = {
cpp = "onelab"; cpp = "onelab";
python = [ python = [
"myFiletype" "foo"
"anotherFiletype" "bar"
]; ];
}; };
description = ''
This is a wrapping of the `vim.treesitter.language.register` function.
Register specific parsers to one or several filetypes.
The keys are the parser names and the values are either one or several filetypes.
'';
}; };
grammarPackages = mkOption { nixGrammars = mkOption {
type = with types; listOf package; type = types.bool;
default = cfg.package.passthru.allGrammars; default = true;
description = "Grammar packages to install"; example = false;
description = "Whether to install grammars defined in `grammarPackages`.";
}; };
moduleConfig = mkOption { nixvimInjections = mkOption {
type = types.attrsOf types.anything; type = types.bool;
default = { }; default = true;
description = "This is the configuration for extra modules. It should not be used directly"; example = false;
description = "Whether to enable Nixvim injections, e.g. highlighting `extraConfigLua` as lua.";
}; };
nixvimInjections = mkEnableOption "nixvim specific injections, like lua highlighting in extraConfigLua"; nodejsPackage = helpers.mkPackageOption {
name = "nodejs";
default = pkgs.nodejs;
defaultText = literalExpression "pkgs.nodejs";
example = literalExpression "pkgs.nodejs_22";
description = ''
Which package (if any) to be added as the nodejs package.
This is required to build grammars if you are not using `nixGrammars`.
To disable the installation of NodeJS, set this option to `null`.
'';
};
treesitterPackage = helpers.mkPackageOption {
name = "tree-sitter";
default = pkgs.tree-sitter;
defaultText = literalExpression "pkgs.tree-sitter";
description = ''
Which package (if any) to be added as the tree-sitter binary.
This is required to build grammars if you are not using `nixGrammars`.
To disable the installation of tree-sitter, set this option to `null`.
'';
}; };
}; };
config = # NOTE: We call setup manually below.
let callSetup = false;
tsOptions = { # NOTE: We install cfg.package manually so we can install grammars using it.
highlight = { installPackage = false;
inherit (cfg) enable;
disable = if (cfg.disabledLanguages != [ ]) then cfg.disabledLanguages else null;
custom_captures = if (cfg.customCaptures != { }) then cfg.customCaptures else null; extraConfig = cfg: {
};
incremental_selection =
if cfg.incrementalSelection.enable then
{
enable = true;
keymaps = {
init_selection = cfg.incrementalSelection.keymaps.initSelection;
node_incremental = cfg.incrementalSelection.keymaps.nodeIncremental;
scope_incremental = cfg.incrementalSelection.keymaps.scopeIncremental;
node_decremental = cfg.incrementalSelection.keymaps.nodeDecremental;
};
}
else
null;
indent = if cfg.indent then { enable = true; } else null;
ensure_installed = if cfg.nixGrammars then [ ] else cfg.ensureInstalled;
ignore_install = cfg.ignoreInstall;
parser_install_dir = cfg.parserInstallDir;
} // cfg.moduleConfig;
in
mkIf cfg.enable {
extraConfigLua = extraConfigLua =
(optionalString (cfg.parserInstallDir != null) '' # NOTE: Upstream state that the parser MUST be at the beginning of runtimepath.
vim.opt.runtimepath:append("${cfg.parserInstallDir}") # Otherwise the parsers from Neovim takes precedent, which may be incompatible with some queries.
(optionalString (cfg.settings.parser_install_dir != null) ''
vim.opt.runtimepath:prepend("${cfg.settings.parser_install_dir}")
'') '')
+ '' + ''
require('nvim-treesitter.configs').setup(${helpers.toLuaObject tsOptions}) require('nvim-treesitter.configs').setup(${helpers.toLuaObject cfg.settings})
'' ''
+ (optionalString (cfg.languageRegister != { }) '' + (optionalString (cfg.languageRegister != { }) ''
__parserFiletypeMappings = ${helpers.toLuaObject cfg.languageRegister} do
local __parserFiletypeMappings = ${helpers.toLuaObject cfg.languageRegister}
for parser_name, ft in pairs(__parserFiletypeMappings) do for parser_name, ft in pairs(__parserFiletypeMappings) do
require('vim.treesitter.language').register(parser_name, ft) require('vim.treesitter.language').register(parser_name, ft)
end end
end
''); '');
extraFiles = mkIf cfg.nixvimInjections { extraFiles = mkIf cfg.nixvimInjections { "queries/nix/injections.scm".source = ./injections.scm; };
"queries/nix/injections.scm".text = ''
;; extends
(binding extraPlugins = mkIf (cfg.package != null) [
attrpath: (attrpath (identifier) @_path) (mkIf cfg.nixGrammars (cfg.package.withPlugins (_: cfg.grammarPackages)))
expression: [ (mkIf (!cfg.nixGrammars) cfg.package)
(string_expression (string_fragment) @lua)
(indented_string_expression (string_fragment) @lua)
]
(#match? @_path "^extraConfigLua(Pre|Post)?$"))
(binding
attrpath: (attrpath (identifier) @_path)
expression: [
(string_expression (string_fragment) @vim)
(indented_string_expression (string_fragment) @vim)
]
(#match? @_path "^extraConfigVim(Pre|Post)?$"))
'';
};
extraPlugins =
if cfg.nixGrammars then [ (cfg.package.withPlugins (_: cfg.grammarPackages)) ] else [ cfg.package ];
extraPackages = with pkgs; [
tree-sitter
nodejs
cfg.gccPackage
]; ];
opts = mkIf cfg.folding { extraPackages = [
cfg.gccPackage
cfg.nodejsPackage
cfg.treesitterPackage
];
opts = mkIf cfg.folding (mkDefault {
foldmethod = "expr"; foldmethod = "expr";
foldexpr = "nvim_treesitter#foldexpr()"; foldexpr = "nvim_treesitter#foldexpr()";
}; });
}; };
} }

View file

@ -1,18 +1,70 @@
{ pkgs, ... }:
{ {
default = {
plugins.treesitter = {
enable = true;
settings = {
auto_install = false;
ensure_installed = [ ];
ignore_install = [ ];
parser_install_dir = null;
sync_install = false;
highlight = {
additional_vim_regex_highlighting = false;
enable = false;
custom_captures = { };
disable = null;
};
incremental_selection = {
enable = false;
keymaps = {
init_selection = "gnn";
node_incremental = "grn";
scope_incremental = "grc";
node_decremental = "grm";
};
};
indent = {
enable = false;
};
};
};
};
empty = { empty = {
plugins.treesitter.enable = true; plugins.treesitter.enable = true;
}; };
nonix = { empty-grammar-packages = {
# TODO: See if we can build parsers (legacy way)
tests.dontRun = true;
plugins.treesitter = { plugins.treesitter = {
enable = true; enable = true;
nixGrammars = false;
grammarPackages = [ ];
}; };
}; };
nixvimInjections = { highlight-disable-function = {
plugins.treesitter = {
enable = true;
settings = {
highlight = {
enable = true;
disable = ''
function(lang, bufnr)
return api.nvim_buf_line_count(bufnr) > 50000
end
'';
};
};
};
};
nixvim-injections = {
plugins.treesitter = { plugins.treesitter = {
enable = true; enable = true;
nixvimInjections = true; nixvimInjections = true;
@ -27,18 +79,43 @@
}; };
}; };
# This needs a custom input no-nix = {
# custom = { # TODO: See if we can build parsers (legacy way)
# plugins.treesitter = { tests.dontRun = true;
# enable = true; plugins.treesitter = {
# nixGrammars = true; enable = true;
# grammarPackages = [ nixGrammars = false;
# (build-ts.lib.buildGrammar pkgs { };
# language = "gleam"; };
# version = "0.25.0";
# source = gleam; specific-grammars = {
# }) plugins.treesitter = {
# ]; enable = true;
# };
# }; grammarPackages = with pkgs.vimPlugins.nvim-treesitter.builtGrammars; [
bash
git_config
git_rebase
gitattributes
gitcommit
gitignore
json
jsonc
lua
make
markdown
meson
ninja
nix
readline
regex
ssh-config
toml
vim
vimdoc
xml
yaml
];
};
};
} }