From 3fd7c3bf598129b94353fe7906f6aa78cb88b85c Mon Sep 17 00:00:00 2001 From: Gaetan Lepage Date: Mon, 30 Dec 2024 23:55:58 +0100 Subject: [PATCH] plugins/blink-cmp: rework options --- .../dev/list-plugins/list-plugins.py | 1 + plugins/by-name/blink-cmp/default.nix | 158 +---- plugins/by-name/blink-cmp/provider-config.nix | 116 +++ .../by-name/blink-cmp/settings-options.nix | 669 ++++++++++++++++++ .../plugins/by-name/blink-cmp/default.nix | 439 +++++++++--- 5 files changed, 1158 insertions(+), 225 deletions(-) create mode 100644 plugins/by-name/blink-cmp/provider-config.nix create mode 100644 plugins/by-name/blink-cmp/settings-options.nix diff --git a/flake-modules/dev/list-plugins/list-plugins.py b/flake-modules/dev/list-plugins/list-plugins.py index 9d185696..dbf095e0 100755 --- a/flake-modules/dev/list-plugins/list-plugins.py +++ b/flake-modules/dev/list-plugins/list-plugins.py @@ -16,6 +16,7 @@ EXCLUDES: list[str] = [ "settings-options.nix", # Specific files "colorschemes/base16/theme-list.nix", + "plugins/by-name/blink-cmp/provider-config.nix", "plugins/by-name/dap/dapHelpers.nix", "plugins/by-name/efmls-configs/packages.nix", "plugins/by-name/gitsigns/options.nix", diff --git a/plugins/by-name/blink-cmp/default.nix b/plugins/by-name/blink-cmp/default.nix index 922e0181..be8904e4 100644 --- a/plugins/by-name/blink-cmp/default.nix +++ b/plugins/by-name/blink-cmp/default.nix @@ -2,10 +2,6 @@ lib, ... }: -let - inherit (lib) types; - inherit (lib.nixvim) defaultNullOpts; -in lib.nixvim.plugins.mkNeovimPlugin { name = "blink-cmp"; packPathName = "blink.cmp"; @@ -17,145 +13,31 @@ lib.nixvim.plugins.mkNeovimPlugin { Performant, batteries-included completion plugin for Neovim. ''; - settingsOptions = { - keymap = defaultNullOpts.mkNullableWithRaw' { - type = with types; either (attrsOf anything) (enum [ false ]); - pluginDefault = { - preset = "default"; - }; - example = { - "" = [ - "show" - "show_documentation" - "hide_documentation" - ]; - "" = [ "hide" ]; - "" = [ "select_and_accept" ]; - - "" = [ - "select_prev" - "fallback" - ]; - "" = [ - "select_next" - "fallback" - ]; - "" = [ - "select_prev" - "fallback" - ]; - "" = [ - "select_next" - "fallback" - ]; - - "" = [ - "scroll_documentation_up" - "fallback" - ]; - "" = [ - "scroll_documentation_down" - "fallback" - ]; - - "" = [ - "snippet_forward" - "fallback" - ]; - "" = [ - "snippet_backward" - "fallback" - ]; - - }; - description = '' - The keymap can be: - - A preset (`'default'` | `'super-tab'` | `'enter'`) - - A table of `keys => command[]` (optionally with a "preset" key to merge with a preset) - When specifying 'preset' in the keymap table, the custom key mappings are merged with the preset, - and any conflicting keys will overwrite the preset mappings. - ''; - }; - - highlight = { - use_nvim_cmp_as_default = defaultNullOpts.mkBool false '' - Sets the fallback highlight groups to nvim-cmp's highlight groups. - ''; - }; - - fuzzy = { - use_frecency = defaultNullOpts.mkBool true '' - Enable the `Frecency` integration to boost the score of the most recently/frequently used items. - ''; - use_proximity = defaultNullOpts.mkBool true '' - Enables the `Proximity` integration that boosts the score of items with a value in the buffer. - ''; - max_items = defaultNullOpts.mkUnsignedInt 200 '' - Maximum number of items shown. - ''; - }; - - windows = { - documentation = { - auto_show = defaultNullOpts.mkBool false '' - Enables automatically showing documentation when typing. - ''; - auto_show_delay_ms = defaultNullOpts.mkUnsignedInt 500 '' - Delay, in milliseconds, after which documentation is shown. - ''; - update_delay_ms = defaultNullOpts.mkUnsignedInt 50 '' - Delay, in milliseconds, after which documentation is updated. - ''; - }; - }; - - nerd_font_variant = - defaultNullOpts.mkEnumFirstDefault - [ - "normal" - "mono" - ] - '' - Set to `mono` for `Nerd Font Mono` or `normal` for `Nerd Font`. - Adjusts spacing to ensure icons are aligned. - ''; - - accept = { - auto_brackets = { - enabled = defaultNullOpts.mkBool false '' - Enable experimental auto-brackets support. - ''; - }; - }; - trigger = { - signature_help = { - enabled = defaultNullOpts.mkBool false '' - Enable experimental signature help support. - ''; - }; - }; - }; + settingsOptions = import ./settings-options.nix lib; settingsExample = { - keymap = { - preset = "default"; + keymap.preset = "super-tab"; + sources = { + providers = { + buffer.score_offset = -7; + lsp.fallbacks = [ ]; + }; + cmdline = [ ]; }; - highlight = { + completion = { + accept = { + auto_brackets = { + enabled = true; + semantic_token_resolution.enabled = false; + }; + }; + documentation.auto_show = true; + }; + appearance = { use_nvim_cmp_as_default = true; + nerd_font_variant = "normal"; }; - documentation = { - auto_show = false; - }; - accept = { - auto_brackets = { - enabled = false; - }; - }; - trigger = { - signature_help = { - enabled = true; - }; - }; + signature.enabled = true; }; extraConfig = cfg: { diff --git a/plugins/by-name/blink-cmp/provider-config.nix b/plugins/by-name/blink-cmp/provider-config.nix new file mode 100644 index 00000000..237eb7e2 --- /dev/null +++ b/plugins/by-name/blink-cmp/provider-config.nix @@ -0,0 +1,116 @@ +lib: +let + inherit (lib) types; + inherit (lib.nixvim) + mkNullOrOption + mkNullOrStr' + mkNullOrOption' + ; +in +types.submodule { + freeformType = with types; attrsOf anything; + options = { + name = mkNullOrStr' { + description = '' + The name of the source. + ''; + example = "LSP"; + }; + + module = mkNullOrStr' { + description = '' + The module name to load. + ''; + example = "blink.cmp.sources.lsp"; + }; + + enabled = mkNullOrOption' { + type = types.bool; + description = '' + Whether or not to enable the provider. + ''; + example.__raw = '' + function() + return true + end + ''; + }; + + opts = mkNullOrOption' { + type = with types; attrsOf anything; + description = '' + Options for this provider. + ''; + example = { }; + }; + + async = mkNullOrOption types.bool '' + Whether blink should wait for the source to return before showing the completions. + ''; + + timeout_ms = mkNullOrOption types.ints.unsigned '' + How long to wait for the provider to return before showing completions and treating it as + asynchronous. + ''; + + transform_items = mkNullOrOption' { + type = types.rawLua; + description = '' + Function to transform the items before they're returned. + ''; + example = '' + function(_, items) + -- demote snippets + for _, item in ipairs(items) do + if item.kind == require('blink.cmp.types').CompletionItemKind.Snippet then + item.score_offset = item.score_offset - 3 + end + end + + -- filter out text items, since we have the buffer source + return vim.tbl_filter( + function(item) return item.kind ~= require('blink.cmp.types').CompletionItemKind.Text end, + items + ) + end + ''; + }; + + should_show_items = mkNullOrOption types.bool '' + Whether or not to show the items. + ''; + + max_items = mkNullOrOption types.ints.unsigned '' + Maximum number of items to display in the menu. + ''; + + min_keyword_length = mkNullOrOption types.ints.unsigned '' + Minimum number of characters in the keyword to trigger the provider. + ''; + + fallbacks = mkNullOrOption' { + type = with types; listOf str; + description = '' + If this provider returns `0` items, it will fallback to these providers. + ''; + example = [ "buffer" ]; + }; + + score_offset = mkNullOrOption' { + type = types.int; + description = '' + Boost/penalize the score of the items. + ''; + example = 3; + }; + + deduplicate = mkNullOrOption types.anything '' + Warning: not yet implemented. + ''; + + # https://github.com/Saghen/blink.cmp/blob/main/lua/blink/cmp/sources/lib/types.lua#L22 + override = mkNullOrOption (with types; attrsOf anything) '' + Override source options. + ''; + }; +} diff --git a/plugins/by-name/blink-cmp/settings-options.nix b/plugins/by-name/blink-cmp/settings-options.nix new file mode 100644 index 00000000..b3f9b8d0 --- /dev/null +++ b/plugins/by-name/blink-cmp/settings-options.nix @@ -0,0 +1,669 @@ +lib: +let + inherit (lib) types; + inherit (lib.nixvim) + defaultNullOpts + literalLua + mkNullOrOption + mkNullOrOption' + ; +in +{ + enabled = defaultNullOpts.mkRaw' { + pluginDefault = '' + function() + return vim.bo.buftype ~= 'prompt' and vim.b.completion ~= false + end + ''; + example = '' + function() + return not vim.tbl_contains({ "lua", "markdown" }, vim.bo.filetype) + and vim.bo.buftype ~= "prompt" + and vim.b.completion ~= false + end + ''; + description = '' + A function that returns whether blink-cmp should be enabled or not for this buffer. + ''; + }; + + keymap = defaultNullOpts.mkNullableWithRaw' { + type = with types; either (attrsOf anything) (enum [ false ]); + pluginDefault = { + preset = "default"; + }; + example = { + "" = [ + "show" + "show_documentation" + "hide_documentation" + ]; + "" = [ "hide" ]; + "" = [ "select_and_accept" ]; + + "" = [ + "select_prev" + "fallback" + ]; + "" = [ + "select_next" + "fallback" + ]; + "" = [ + "select_prev" + "fallback" + ]; + "" = [ + "select_next" + "fallback" + ]; + + "" = [ + "scroll_documentation_up" + "fallback" + ]; + "" = [ + "scroll_documentation_down" + "fallback" + ]; + + "" = [ + "snippet_forward" + "fallback" + ]; + "" = [ + "snippet_backward" + "fallback" + ]; + + }; + description = '' + The keymap can be: + - A preset (`'default'` | `'super-tab'` | `'enter'`) + - A table of `keys => command[]` (optionally with a "preset" key to merge with a preset) + When specifying 'preset' in the keymap table, the custom key mappings are merged with the preset, + and any conflicting keys will overwrite the preset mappings. + ''; + }; + + completion = { + keyword = { + range = defaultNullOpts.mkEnumFirstDefault [ "prefix" "full" ] '' + - `"prefix"` will fuzzy match on the text before the cursor + - `"full"` will fuzzy match on the text before *and* after the cursor + + Example: `"foo_|_bar"` will match `"foo_"` for `"prefix"` and `"foo__bar"` for `"full"`. + ''; + + regex = defaultNullOpts.mkStr "[-_]\\|\\k" '' + Regex used to get the text when fuzzy matching. + ''; + + exclude_from_prefix_regex = defaultNullOpts.mkStr "-" '' + After matching with regex, any characters matching this regex at the prefix will be + excluded. + ''; + }; + + trigger = { + prefetch_on_insert = defaultNullOpts.mkBool false '' + When `true`, will prefetch the completion items when entering insert mode. + WARN: buggy, not recommended unless you'd like to help develop prefetching. + ''; + + show_in_snippet = defaultNullOpts.mkBool true '' + When `false`, will not show the completion window when in a snippet. + ''; + + show_on_keyword = defaultNullOpts.mkBool true '' + When `true`, will show the completion window after typing a character that matches the + `keyword.regex`. + ''; + + show_on_trigger_character = defaultNullOpts.mkBool true '' + When `true`, will show the completion window after typing a trigger character. + ''; + + show_on_blocked_trigger_characters = + defaultNullOpts.mkListOf types.str + (literalLua '' + function() + if vim.api.nvim_get_mode().mode == 'c' then return {} end + return { ' ', '\n', '\t' } + end + '') + '' + LSPs can indicate when to show the completion window via trigger characters. + + However, some LSPs (i.e. tsserver) return characters that would essentially always + show the window. + We block these by default. + ''; + + show_on_accept_on_trigger_character = defaultNullOpts.mkBool true '' + When both this and `show_on_trigger_character` are `true`, will show the completion window + when the cursor comes after a trigger character after accepting an item. + ''; + + show_on_insert_on_trigger_character = defaultNullOpts.mkBool true '' + When both this and `show_on_trigger_character` are `true`, will show the completion window + when the cursor comes after a trigger character when entering insert mode. + ''; + + show_on_x_blocked_trigger_characters = + defaultNullOpts.mkListOf types.str [ "'" ''"'' "(" "{" "[" ] + '' + List of trigger characters (on top of `show_on_blocked_trigger_characters`) that won't + trigger the completion window when the cursor comes after a trigger character when + entering insert mode/accepting an item + ''; + }; + + list = { + max_items = defaultNullOpts.mkUnsignedInt 200 '' + Maximum number of items to display. + ''; + + selection = defaultNullOpts.mkEnumFirstDefault [ "preselect" "manual" "autoinsert" ] '' + Controls if completion items will be selected automatically, and whether selection + automatically inserts. + ''; + + cycle = { + from_bottom = defaultNullOpts.mkBool true '' + When `true`, calling `select_next` at the **bottom** of the completion list will select + the **first** completion item. + ''; + + from_top = defaultNullOpts.mkBool true '' + When `true`, calling `select_prev` at the **top** of the completion list will select the + **last** completion item. + ''; + }; + }; + + accept = { + create_undo_point = defaultNullOpts.mkBool true '' + Create an undo point when accepting a completion item. + ''; + + auto_brackets = { + enabled = defaultNullOpts.mkBool true '' + Whether to auto-insert brackets for functions. + ''; + + default_brackets = defaultNullOpts.mkListOf types.str [ "(" ")" ] '' + Default brackets to use for unknown languages. + ''; + + override_brackets_for_filetypes = defaultNullOpts.mkAttrsOf (with types; listOf str) { } '' + Brackets override per filetype. + ''; + + force_allow_filetypes = defaultNullOpts.mkListOf types.str [ ] '' + Overrides the default blocked filetypes. + ''; + + blocked_filetypes = defaultNullOpts.mkListOf types.str [ ] '' + Blocked filetypes. + ''; + + kind_resolution = { + enabled = defaultNullOpts.mkBool true '' + Synchronously use the kind of the item to determine if brackets should be added. + ''; + + blocked_filetypes = + defaultNullOpts.mkListOf types.str + [ + "typescriptreact" + "javascriptreact" + "vue" + "rust" + ] + '' + Blocked filetypes. + ''; + }; + + semantic_token_resolution = { + enabled = defaultNullOpts.mkBool true '' + Asynchronously use semantic token to determine if brackets should be added + ''; + + blocked_filetypes = + defaultNullOpts.mkListOf types.str + [ + "java" + ] + '' + Blocked filetypes. + ''; + + timeout_ms = defaultNullOpts.mkUnsignedInt 400 '' + How long to wait for semantic tokens to return before assuming no brackets should be added. + ''; + }; + }; + }; + + menu = { + enabled = defaultNullOpts.mkBool true '' + Whether to enable the completion menu. + ''; + + min_width = defaultNullOpts.mkUnsignedInt 15 '' + Minimum width of the completion menu. + ''; + + max_height = defaultNullOpts.mkUnsignedInt 10 '' + Maximum width of the completion menu. + ''; + + border = defaultNullOpts.mkNullable types.anything "none" '' + Border settings. + ''; + + winblend = defaultNullOpts.mkUnsignedInt 0 '' + `winblend` value for the completion menu. + ''; + + winhighlight = defaultNullOpts.mkStr "Normal:BlinkCmpMenu,FloatBorder:BlinkCmpMenuBorder,CursorLine:BlinkCmpMenuSelection,Search:None" '' + Highlight groups for the completion menu. + ''; + + scrolloff = defaultNullOpts.mkUnsignedInt 2 '' + Keep the cursor X lines away from the top/bottom of the window. + ''; + + scrollbar = defaultNullOpts.mkBool true '' + Note that the gutter will be disabled when `border != "none"`. + ''; + + direction_priority = + defaultNullOpts.mkListOf + (types.enum [ + "n" + "s" + ]) + [ "s" "n" ] + '' + Which directions to show the window, falling back to the next direction when there's not + enough space. + ''; + + order = { + n = defaultNullOpts.mkEnum [ "top_down" "bottom_up" ] "bottom_up" '' + Warning: not yet implemented. + ''; + + s = defaultNullOpts.mkEnum [ "top_down" "bottom_up" ] "top_down" '' + Warning: not yet implemented. + ''; + }; + + auto_show = defaultNullOpts.mkBool true '' + Whether to automatically show the window when new completion items are available. + ''; + + cmdline_position = + defaultNullOpts.mkRaw + '' + function() + if vim.g.ui_cmdline_pos ~= nil then + local pos = vim.g.ui_cmdline_pos -- (1, 0)-indexed + return { pos[1] - 1, pos[2] } + end + local height = (vim.o.cmdheight == 0) and 1 or vim.o.cmdheight + return { vim.o.lines - height, 0 } + end + '' + '' + Screen coordinates (0-indexed) of the command line. + ''; + + draw = mkNullOrOption (with types; attrsOf anything) '' + Controls how the completion items are rendered on the popup window. + ''; + }; + + documentation = { + auto_show = defaultNullOpts.mkBool false '' + Controls whether the documentation window will automatically show when selecting a + completion item. + ''; + + auto_show_delay_ms = defaultNullOpts.mkUnsignedInt 500 '' + Delay before showing the documentation window. + ''; + + update_delay_ms = defaultNullOpts.mkUnsignedInt 50 '' + Delay before updating the documentation window when selecting a new item, while an existing + item is still visible. + ''; + + treesitter_highlighting = defaultNullOpts.mkBool true '' + Whether to use treesitter highlighting, disable if you run into performance issues. + ''; + + window = { + min_width = defaultNullOpts.mkUnsignedInt 10 '' + Minimum width of the documentation window. + ''; + + max_width = defaultNullOpts.mkUnsignedInt 80 '' + Maximum width of the documentation window. + ''; + + max_height = defaultNullOpts.mkUnsignedInt 20 '' + Maximum height of the documentation window. + ''; + + desired_min_width = defaultNullOpts.mkUnsignedInt 50 '' + Desired minimum width of the documentation window. + ''; + + desired_min_height = defaultNullOpts.mkUnsignedInt 10 '' + Desired minimum height of the documentation window. + ''; + + border = defaultNullOpts.mkNullable types.anything "padded" '' + Border settings for the documentation window. + ''; + + winblend = defaultNullOpts.mkUnsignedInt 0 '' + `winblend` value. + ''; + + winhighlight = defaultNullOpts.mkStr "Normal:BlinkCmpDoc,FloatBorder:BlinkCmpDocBorder,EndOfBuffer:BlinkCmpDoc" '' + Highlight groups for the documentation window. + ''; + + scrollbar = defaultNullOpts.mkBool true '' + Whether to enable `scrollbar` in the documentation window. + ''; + + direction_priority = + let + mkDirection = defaultNullOpts.mkListOf ( + types.enum [ + "n" + "s" + "e" + "w" + ] + ); + in + { + menu_north = mkDirection [ "e" "w" "n" "s" ] '' + Which directions to show the window, for the _north_ menu window direction, falling + back to the next direction when there's not enough space. + ''; + + menu_south = mkDirection [ "e" "w" "s" "n" ] '' + Which directions to show the window, for the _south_ menu window direction, falling + back to the next direction when there's not enough space. + ''; + }; + }; + }; + + ghost_text = { + enabled = defaultNullOpts.mkBool false '' + Displays a preview of the selected item on the current line. + ''; + }; + }; + + fuzzy = { + use_typo_resistance = defaultNullOpts.mkBool true '' + When enabled, allows for a number of typos relative to the length of the query. + Disabling this matches the behavior of fzf. + ''; + + use_frecency = defaultNullOpts.mkBool true '' + Tracks the most recently/frequently used items and boosts the score of the item. + ''; + + use_proximity = defaultNullOpts.mkBool true '' + Boosts the score of items matching nearby words. + ''; + + use_unsafe_no_lock = defaultNullOpts.mkBool false '' + UNSAFE!! When enabled, disables the lock and fsync when writing to the frecency database. + This should only be used on unsupported platforms (i.e. alpine termux). + ''; + + sorts = + defaultNullOpts.mkListOf + (types.enum [ + "label" + "sort_text" + "kind" + "score" + ]) + [ "score" "sort_text" ] + '' + Controls which sorts to use and in which order, these three are currently the only allowed options + ''; + + prebuilt_binaries = { + download = defaultNullOpts.mkBool true '' + Whenther or not to automatically download a prebuilt binary from github. + If this is set to `false` you will need to manually build the fuzzy binary dependencies by + running `cargo build --release`. + ''; + + ignore_version_mismatch = defaultNullOpts.mkBool false '' + Ignores mismatched version between the built binary and the current git sha, when building + locally. + ''; + + force_version = defaultNullOpts.mkStr null '' + When downloading a prebuilt binary, force the downloader to resolve this version. + + If this is unset then the downloader will attempt to infer the version from the checked out + git tag (if any). + + WARN: Beware that `main` may be incompatible with the version you select. + ''; + + force_system_triple = defaultNullOpts.mkStr null '' + When downloading a prebuilt binary, force the downloader to use this system triple. + + If this is unset then the downloader will attempt to infer the system triple from `jit.os` + and `jit.arch`. + Check the latest release for all available system triples. + + WARN: Beware that `main` may be incompatible with the version you select + ''; + + extra_curl_args = defaultNullOpts.mkListOf types.str [ ] '' + Extra arguments that will be passed to curl like + `[ "curl" ..extra_curl_args ..built_in_args ]`. + ''; + }; + }; + + sources = { + default = defaultNullOpts.mkListOf types.str [ "lsp" "path" "snippets" "buffer" ] '' + Default sources. + ''; + + per_filetype = defaultNullOpts.mkAttrsOf (with types; attrsOf str) { } '' + Sources per filetype. + ''; + + cmdline = + defaultNullOpts.mkListOf types.str + (literalLua '' + function() + local type = vim.fn.getcmdtype() + -- Search forward and backward + if type == '/' or type == '?' then return { 'buffer' } end + -- Commands + if type == ':' then return { 'cmdline' } end + return {} + end + '') + '' + `cmdline` or `buffer`. + ''; + + transform_items = defaultNullOpts.mkRaw "function(_, items) return items end" '' + Function to transform the items before they're returned. + ''; + + min_keyword_length = defaultNullOpts.mkUnsignedInt 0 '' + Minimum number of characters in the keyword to trigger. + ''; + + providers = mkNullOrOption' { + type = with types; attrsOf (import ./provider-config.nix lib); + description = '' + Definition of completion providers. + ''; + example = { + buffer.score_offset = -7; + lsp.fallbacks = [ ]; + }; + }; + }; + + signature = { + enabled = defaultNullOpts.mkBool false '' + Whether to enable experimental signature help support. + ''; + + trigger = { + enabled = defaultNullOpts.mkBool true '' + Whether to enable signature trigger. + ''; + + blocked_trigger_characters = defaultNullOpts.mkListOf types.str [ ] '' + Blocked trigger characters. + ''; + + blocked_retrigger_characters = defaultNullOpts.mkListOf types.str [ ] '' + Blocked retrigger characters. + ''; + + show_on_insert_on_trigger_character = defaultNullOpts.mkBool true '' + When `true`, will show the signature help window when the cursor comes after a trigger + character when entering insert mode. + ''; + }; + + window = { + min_width = defaultNullOpts.mkUnsignedInt 1 '' + Minimum width of the signature window. + ''; + + max_width = defaultNullOpts.mkUnsignedInt 100 '' + Maximum width of the signature window. + ''; + + max_height = defaultNullOpts.mkUnsignedInt 10 '' + Maximum height of the signature window. + ''; + + border = defaultNullOpts.mkNullable types.anything "padded" '' + Border settings for the signature window. + ''; + + winblend = defaultNullOpts.mkUnsignedInt 0 '' + `winblend` value for the signature window. + ''; + + winhighlight = defaultNullOpts.mkStr "Normal:BlinkCmpSignatureHelp,FloatBorder:BlinkCmpSignatureHelpBorder" '' + Highlight groups for the signature window. + ''; + + scrollbar = defaultNullOpts.mkBool false '' + Note that the gutter will be disabled when `border != "none"`. + ''; + + direction_priority = + defaultNullOpts.mkListOf + (types.enum [ + "n" + "s" + ]) + [ "n" "s" ] + '' + Which directions to show the window, falling back to the next direction when there's not + enough space, or another window is in the way. + ''; + + treesitter_highlighting = defaultNullOpts.mkBool true '' + Disable if you run into performance issues. + ''; + }; + }; + + snippets = { + expand = defaultNullOpts.mkRaw "function(snippet) vim.snippet.expand(snippet) end" '' + Function to use when expanding LSP provided snippets. + ''; + + active = defaultNullOpts.mkRaw "function(filter) return vim.snippet.active(filter) end" '' + Function to use when checking if a snippet is active. + ''; + + jump = defaultNullOpts.mkRaw "function(direction) vim.snippet.jump(direction) end" '' + Function to use when jumping between tab stops in a snippet, where direction can be negative + or positive. + ''; + }; + + appearance = { + highlight_ns = defaultNullOpts.mkUnsignedInt (literalLua "vim.api.nvim_create_namespace('blink_cmp')") '' + Highlight namespace. + ''; + + use_nvim_cmp_as_default = defaultNullOpts.mkBool false '' + Sets the fallback highlight groups to `nvim-cmp`'s highlight groups. + + Useful for when your theme doesn't support `blink.cmp`, will be removed in a future release. + ''; + + nerd_font_variant = defaultNullOpts.mkEnumFirstDefault [ "mono" "normal" ] '' + Set to `"mono"` for _Nerd Font Mono_ or `"normal"` for _Nerd Font_. + + Adjusts spacing to ensure icons are aligned. + ''; + + kind_icons = + defaultNullOpts.mkAttrsOf types.str + { + Text = "󰉿"; + Method = "󰊕"; + Function = "󰊕"; + Constructor = "󰒓"; + Field = "󰜢"; + Variable = "󰆦"; + Property = "󰖷"; + Class = "󱡠"; + Interface = "󱡠"; + Struct = "󱡠"; + Module = "󰅩"; + Unit = "󰪚"; + Value = "󰦨"; + Enum = "󰦨"; + EnumMember = "󰦨"; + Keyword = "󰻾"; + Constant = "󰏿"; + Snippet = "󱄽"; + Color = "󰏘"; + File = "󰈔"; + Reference = "󰬲"; + Folder = "󰉋"; + Event = "󱐋"; + Operator = "󰪚"; + TypeParameter = "󰬛"; + } + '' + Kind icons definitions. + ''; + }; +} diff --git a/tests/test-sources/plugins/by-name/blink-cmp/default.nix b/tests/test-sources/plugins/by-name/blink-cmp/default.nix index cf34db71..7392402f 100644 --- a/tests/test-sources/plugins/by-name/blink-cmp/default.nix +++ b/tests/test-sources/plugins/by-name/blink-cmp/default.nix @@ -4,52 +4,363 @@ plugins.blink-cmp.enable = true; }; - default = { + defaults = { test.runNvim = false; plugins.blink-cmp = { enable = true; + settings = { keymap = { preset = "default"; }; - highlight = { - use_nvim_cmp_as_default = false; - }; - nerd_font_variant = "normal"; - windows = { + completion = { + keyword = { + range = "prefix"; + regex = "[-_]\\|\\k"; + exclude_from_prefix_regex = "-"; + }; + trigger = { + prefetch_on_insert = false; + show_in_snippet = true; + show_on_keyword = true; + show_on_trigger_character = true; + show_on_blocked_trigger_characters.__raw = '' + function() + if vim.api.nvim_get_mode().mode == 'c' then return {} end + return { ' ', '\n', '\t' } + end + ''; + show_on_accept_on_trigger_character = true; + show_on_insert_on_trigger_character = true; + show_on_x_blocked_trigger_characters = [ + "'" + ''"'' + "(" + "{" + "[" + ]; + }; + list = { + max_items = 200; + selection = "preselect"; + cycle = { + from_bottom = true; + from_top = true; + }; + }; + accept = { + create_undo_point = true; + auto_brackets = { + enabled = true; + default_brackets = [ + "(" + ")" + ]; + override_brackets_for_filetypes = { }; + force_allow_filetypes = [ ]; + blocked_filetypes = [ ]; + kind_resolution = { + enabled = true; + blocked_filetypes = [ + "typescriptreact" + "javascriptreact" + "vue" + "rust" + ]; + }; + semantic_token_resolution = { + enabled = true; + blocked_filetypes = [ + "java" + ]; + timeout_ms = 400; + }; + }; + }; + menu = { + enabled = true; + min_width = 15; + max_height = 10; + border = "none"; + winblend = 0; + winhighlight = "Normal:BlinkCmpMenu,FloatBorder:BlinkCmpMenuBorder,CursorLine:BlinkCmpMenuSelection,Search:None"; + scrolloff = 2; + scrollbar = true; + direction_priority = [ + "s" + "n" + ]; + order = { + n = "bottom_up"; + s = "top_down"; + }; + auto_show = true; + cmdline_position.__raw = '' + function() + if vim.g.ui_cmdline_pos ~= nil then + local pos = vim.g.ui_cmdline_pos -- (1, 0)-indexed + return { pos[1] - 1, pos[2] } + end + local height = (vim.o.cmdheight == 0) and 1 or vim.o.cmdheight + return { vim.o.lines - height, 0 } + end + ''; + draw = { + align_to = "label"; + padding = 1; + gap = 1; + treesitter = { }; + columns = [ + [ "kind_icon" ] + { + __unkeyed-1 = "label"; + __unkeyed-2 = "label_description"; + gap = 1; + } + ]; + components = { + kind_icon = { + ellipsis = false; + text.__raw = "function(ctx) return ctx.kind_icon .. ctx.icon_gap end"; + highlight.__raw = '' + function(ctx) + return require('blink.cmp.completion.windows.render.tailwind').get_hl(ctx) or ('BlinkCmpKind' .. ctx.kind) + end + ''; + }; + kind = { + ellipsis = false; + width.fill = true; + text.__raw = "function(ctx) return ctx.kind end"; + highlight.__raw = '' + function(ctx) + return require('blink.cmp.completion.windows.render.tailwind').get_hl(ctx) or ('BlinkCmpKind' .. ctx.kind) + end + ''; + }; + label = { + width = { + fill = true; + max = 60; + }; + text.__raw = "function(ctx) return ctx.label .. ctx.label_detail end"; + highlight.__raw = '' + function(ctx) + -- label and label details + local label = ctx.label + local highlights = { + { 0, #label, group = ctx.deprecated and 'BlinkCmpLabelDeprecated' or 'BlinkCmpLabel' }, + } + if ctx.label_detail then + table.insert(highlights, { #label, #label + #ctx.label_detail, group = 'BlinkCmpLabelDetail' }) + end + + if vim.list_contains(ctx.self.treesitter, ctx.source_id) then + -- add treesitter highlights + vim.list_extend(highlights, require('blink.cmp.completion.windows.render.treesitter').highlight(ctx)) + end + + -- characters matched on the label by the fuzzy matcher + for _, idx in ipairs(ctx.label_matched_indices) do + table.insert(highlights, { idx, idx + 1, group = 'BlinkCmpLabelMatch' }) + end + + return highlights + end + ''; + }; + label_description = { + width.max = 30; + text.__raw = "function(ctx) return ctx.label_description end"; + highlight = "BlinkCmpLabelDescription"; + }; + source_name = { + width.max = 30; + text.__raw = "function(ctx) return ctx.source_name end"; + highlight = "BlinkCmpSource"; + }; + }; + }; + }; documentation = { auto_show = false; auto_show_delay_ms = 500; update_delay_ms = 50; + treesitter_highlighting = true; + window = { + min_width = 10; + max_width = 80; + max_height = 20; + desired_min_width = 50; + desired_min_height = 10; + border = "padded"; + winblend = 0; + winhighlight = "Normal:BlinkCmpDoc,FloatBorder:BlinkCmpDocBorder,EndOfBuffer:BlinkCmpDoc"; + scrollbar = true; + direction_priority = { + menu_north = [ + "e" + "w" + "n" + "s" + ]; + menu_south = [ + "e" + "w" + "s" + "n" + ]; + }; + }; }; - }; - accept = { - auto_brackets = { + ghost_text = { enabled = false; }; }; - trigger = { - signature_help = { - enabled = false; - show_on_insert_on_trigger_character = false; - }; - }; fuzzy = { + use_typo_resistance = true; use_frecency = true; use_proximity = true; - max_items = 200; - prebuiltBinaries = { - download = false; + use_unsafe_no_lock = false; + sorts = [ + "score" + "sort_text" + ]; + prebuilt_binaries = { + download = true; + ignore_version_mismatch = false; + force_version = null; + force_system_triple = null; + extra_curl_args = [ ]; }; }; sources = { - completion = { - enabled_providers = [ - "lsp" - "path" - "snippets" - "buffer" + default = [ + "lsp" + "path" + "snippets" + "buffer" + ]; + per_filetype = { }; + cmdline.__raw = '' + function() + local type = vim.fn.getcmdtype() + -- Search forward and backward + if type == '/' or type == '?' then return { 'buffer' } end + -- Commands + if type == ':' then return { 'cmdline' } end + return {} + end + ''; + transform_items.__raw = "function(_, items) return items end"; + min_keyword_length = 0; + providers = { + lsp = { + name = "LSP"; + module = "blink.cmp.sources.lsp"; + fallbacks = [ "buffer" ]; + transform_items.__raw = '' + function(_, items) + -- demote snippets + for _, item in ipairs(items) do + if item.kind == require('blink.cmp.types').CompletionItemKind.Snippet then + item.score_offset = item.score_offset - 3 + end + end + + -- filter out text items, since we have the buffer source + return vim.tbl_filter( + function(item) return item.kind ~= require('blink.cmp.types').CompletionItemKind.Text end, + items + ) + end + ''; + }; + path = { + name = "Path"; + module = "blink.cmp.sources.path"; + score_offset = 3; + fallbacks = [ "buffer" ]; + }; + snippets = { + name = "Snippets"; + module = "blink.cmp.sources.snippets"; + score_offset = -3; + }; + luasnip = { + name = "Luasnip"; + module = "blink.cmp.sources.luasnip"; + score_offset = -3; + }; + buffer = { + name = "Buffer"; + module = "blink.cmp.sources.buffer"; + score_offset = -3; + }; + cmdline = { + name = "cmdline"; + module = "blink.cmp.sources.cmdline"; + }; + }; + }; + signature = { + enabled = false; + trigger = { + enabled = true; + blocked_trigger_characters = [ ]; + blocked_retrigger_characters = [ ]; + show_on_insert_on_trigger_character = true; + }; + window = { + min_width = 1; + max_width = 100; + max_height = 10; + border = "padded"; + winblend = 0; + winhighlight = "Normal:BlinkCmpSignatureHelp,FloatBorder:BlinkCmpSignatureHelpBorder"; + scrollbar = false; + direction_priority = [ + "n" + "s" ]; + treesitter_highlighting = true; + }; + }; + snippets = { + expand.__raw = "function(snippet) vim.snippet.expand(snippet) end"; + active.__raw = "function(filter) return vim.snippet.active(filter) end"; + jump.__raw = "function(direction) vim.snippet.jump(direction) end"; + }; + appearance = { + highlight_ns.__raw = "vim.api.nvim_create_namespace('blink_cmp')"; + use_nvim_cmp_as_default = false; + nerd_font_variant = "mono"; + kind_icons = { + Text = "󰉿"; + Method = "󰊕"; + Function = "󰊕"; + Constructor = "󰒓"; + Field = "󰜢"; + Variable = "󰆦"; + Property = "󰖷"; + Class = "󱡠"; + Interface = "󱡠"; + Struct = "󱡠"; + Module = "󰅩"; + Unit = "󰪚"; + Value = "󰦨"; + Enum = "󰦨"; + EnumMember = "󰦨"; + Keyword = "󰻾"; + Constant = "󰏿"; + Snippet = "󱄽"; + Color = "󰏘"; + File = "󰈔"; + Reference = "󰬲"; + Folder = "󰉋"; + Event = "󱐋"; + Operator = "󰪚"; + TypeParameter = "󰬛"; }; }; }; @@ -60,75 +371,29 @@ plugins.blink-cmp = { enable = true; settings = { - fuzzy = { - prebuilt_binaries = { - download = false; + keymap.preset = "super-tab"; + sources = { + providers = { + buffer.score_offset = -7; + lsp.fallbacks = [ ]; }; + cmdline = [ ]; }; - keymap = { - "" = [ - "show" - "show_documentation" - "hide_documentation" - ]; - "" = [ "hide" ]; - "" = [ "select_and_accept" ]; - - "" = [ - "select_prev" - "fallback" - ]; - "" = [ - "select_next" - "fallback" - ]; - "" = [ - "select_prev" - "fallback" - ]; - "" = [ - "select_next" - "fallback" - ]; - - "" = [ - "scroll_documentation_up" - "fallback" - ]; - "" = [ - "scroll_documentation_down" - "fallback" - ]; - - "" = [ - "snippet_forward" - "fallback" - ]; - "" = [ - "snippet_backward" - "fallback" - ]; + completion = { + accept = { + auto_brackets = { + enabled = true; + semantic_token_resolution.enabled = false; + }; + }; + documentation.auto_show = true; }; - highlight = { + appearance = { use_nvim_cmp_as_default = true; + nerd_font_variant = "normal"; }; - windows = { - documentation = { - auto_show = true; - }; - }; - accept = { - auto_brackets = { - enabled = true; - }; - }; - trigger = { - signature_help = { - enabled = true; - }; - }; + signature.enabled = true; }; - }; }; }