From 6fe9ecd99539523b3203aab7b0776e32e22cb3b4 Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Sun, 19 Apr 2026 15:26:48 +0200 Subject: [PATCH] neovim/queries: init and add an example injection --- docs/manual/configuring.md | 1 + docs/manual/configuring/queries.md | 46 +++++++++++++++ docs/manual/release-notes/rl-0.9.md | 5 ++ modules/plugins/languages/nix.nix | 70 ++++++++++++++++++++++- modules/plugins/treesitter/config.nix | 37 +++++++++++- modules/plugins/treesitter/treesitter.nix | 26 ++++++++- 6 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 docs/manual/configuring/queries.md diff --git a/docs/manual/configuring.md b/docs/manual/configuring.md index ce455ea7..00765d17 100644 --- a/docs/manual/configuring.md +++ b/docs/manual/configuring.md @@ -21,4 +21,5 @@ configuring/keybinds.md configuring/dags.md configuring/dag-entries.md configuring/autocmds.md +configuring/queries.md ``` diff --git a/docs/manual/configuring/queries.md b/docs/manual/configuring/queries.md new file mode 100644 index 00000000..e6e544f7 --- /dev/null +++ b/docs/manual/configuring/queries.md @@ -0,0 +1,46 @@ +# Queries (`vim.treesitter.queries`) + +Queries allow you to change Neovim's behavior based on Tree-sitter.\ +Read more about it in the +[neovim docs](https://neovim.io/doc/user/treesitter/#_treesitter-queries). + +**Example:** + +In the following example, we are creating a custom injection, to highlight the +Lua string after `mkLuaInline`. + +```nix +foo = mkLuaInline '' + function bar() + return 'foobar' + end +''; +``` + +```nix +{ + vim.treesitter.queries = [{ + type = "injections"; + filetypes = ["nix"]; + content = '' + ;; extends + + ((apply_expression + function: (variable_expression + name: (identifier) @_func + (#eq? @_func "mkLuaInline")) + + argument: (indented_string_expression + (string_fragment) @injection.content) + + (#set! injection.language "lua") + (#set! injection.combined))) + ''; + }]; +} +``` + +This will generate a `queries/nix/injections.scm` in a Neovim runtime directory. + +> [!NOTE] +> When multiple queries match the same `filetype` and `type`, they are merged. diff --git a/docs/manual/release-notes/rl-0.9.md b/docs/manual/release-notes/rl-0.9.md index 9c85f052..1975bb91 100644 --- a/docs/manual/release-notes/rl-0.9.md +++ b/docs/manual/release-notes/rl-0.9.md @@ -271,6 +271,11 @@ [Snoweuph](https://github.com/snoweuph) +- Added {option}`vim.treesitter.queries` to support adding custom queries. + +- Added injections for `vim.treesitter.queries.*.content` as `query` and + `mkLualine ""` as `lua`. + - Added `vim.lsp.presets.` to contain LSP configurations. This allows for more flexibility in nvf and reuse of LSPs across languages. Dropped `deprecatedSingleOrListOf` in favor of `listOf` for the affected LSP options. diff --git a/modules/plugins/languages/nix.nix b/modules/plugins/languages/nix.nix index eac9264e..3c4a05ed 100644 --- a/modules/plugins/languages/nix.nix +++ b/modules/plugins/languages/nix.nix @@ -128,8 +128,74 @@ in { } (mkIf cfg.treesitter.enable { - vim.treesitter.enable = true; - vim.treesitter.grammars = [cfg.treesitter.package]; + vim.treesitter = { + enable = true; + grammars = [cfg.treesitter.package]; + queries = [ + # vim.treesitter.queries.*.content + { + type = "injections"; + filetypes = ["nix"]; + content = '' + ;; extends + ( + (binding + attrpath: (attrpath + (identifier) @_a + (identifier) @_b + (identifier)? @_c) + (#eq? @_a "vim") + (#any-of? @_b "treesitter") + (#any-of? @_c "queries") + + expression: (attrset_expression + (binding_set + (binding + attrpath: (attrpath + (identifier) @_queries) + (#eq? @_queries "queries") + + expression: (list_expression + (attrset_expression + (binding_set + (binding + attrpath: (attrpath + (identifier) @_field) + (#eq? @_field "content") + + expression: [ + (string_expression + (string_fragment) @injection.content) + (indented_string_expression + (string_fragment) @injection.content) + ] + + (#set! injection.language "query") + (#set! injection.combined))))))))) + ) + ''; + } + # mkLuaInline = lua + { + type = "injections"; + filetypes = ["nix"]; + content = '' + ;; extends + + ((apply_expression + function: (variable_expression + name: (identifier) @_func + (#eq? @_func "mkLuaInline")) + + argument: (indented_string_expression + (string_fragment) @injection.content) + + (#set! injection.language "lua") + (#set! injection.combined))) + ''; + } + ]; + }; }) (mkIf cfg.lsp.enable { diff --git a/modules/plugins/treesitter/config.nix b/modules/plugins/treesitter/config.nix index e937ae32..b2b4cc99 100644 --- a/modules/plugins/treesitter/config.nix +++ b/modules/plugins/treesitter/config.nix @@ -1,9 +1,10 @@ { config, lib, + pkgs, ... }: let - inherit (lib.modules) mkIf; + inherit (lib) mkIf foldl' mapAttrsToList; inherit (lib.strings) optionalString; inherit (lib.lists) optionals; inherit (lib.nvim.dag) entryAfter; @@ -66,6 +67,40 @@ in { }) ''} ''; + + additionalRuntimePaths = mkIf (cfg.queries != []) [ + (let + grouped = + foldl' + ( + acc: query: + foldl' + ( + inner: filetype: let + path = "queries/${filetype}/${query.type}.scm"; + prev = inner.${path} or ""; + in + inner + // { + ${path} = prev + query.content; + } + ) + acc + query.filetypes + ) + {} + cfg.queries; + + files = + mapAttrsToList + (path: content: { + name = path; + path = pkgs.writeText path content; + }) + grouped; + in + pkgs.linkFarm "treesitter-queries" files) + ]; }; }; } diff --git a/modules/plugins/treesitter/treesitter.nix b/modules/plugins/treesitter/treesitter.nix index ba5b269e..d10a3eaf 100644 --- a/modules/plugins/treesitter/treesitter.nix +++ b/modules/plugins/treesitter/treesitter.nix @@ -4,7 +4,25 @@ ... }: let inherit (lib.options) mkOption mkEnableOption literalExpression; - inherit (lib.types) listOf nullOr package bool str oneOf; + inherit (lib.types) listOf nullOr package bool str lines enum submodule oneOf; + + queriesType = submodule { + options = { + type = mkOption { + type = enum ["injections" "highlights" "folds" "locals" "indents"]; + description = "The kind of query to register."; + }; + filetypes = mkOption { + type = listOf str; + default = []; + description = "The filetypes for which the query should be registered."; + }; + content = mkOption { + type = lines; + description = "The queries scm script."; + }; + }; + }; in { options.vim.treesitter = { enable = mkEnableOption "treesitter, also enabled automatically through language options"; @@ -87,5 +105,11 @@ in { }; highlight = {enable = mkEnableOption "highlighting with treesitter" // {default = true;};}; + + queries = mkOption { + type = listOf queriesType; + default = []; + description = "A list of Neovim treesitter queries to be registered."; + }; }; }