From 24e511267d494f5a0753e2b95f599b0d3f662413 Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 2 Aug 2024 02:24:30 -0700 Subject: [PATCH 1/9] Disable compare.scopes if a buffer doesn't have a parser attached On nvim-treesitter `main` the methods used in compare.scopes seem to error instead of silently failing, this implements a rudimentary fix for those using `main` (which are guaranteed to be using nvim 0.10+). --- lua/cmp/config/compare.lua | 4 ++++ lua/cmp/utils/autocmd.lua | 9 +++++---- plugin/cmp.lua | 8 ++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index ec79b7336..9dbe4f96d 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -199,6 +199,7 @@ compare.locality = setmetatable({ ---@type cmp.ComparatorFunctor compare.scopes = setmetatable({ scopes_map = {}, + has_nvim_0_9_features = vim.fn.has('nvim-0.9') == 1, update = function(self) local config = require('cmp').get_config() if not vim.tbl_contains(config.sorting.comparators, compare.scopes) then @@ -209,6 +210,9 @@ compare.scopes = setmetatable({ if ok then local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf() local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1 + if self.has_nvim_0_9_features and not vim.b[buf].cmp_buf_has_treesitter then + return + end -- Cursor scope. local cursor_scope = nil diff --git a/lua/cmp/utils/autocmd.lua b/lua/cmp/utils/autocmd.lua index 438e23190..0d980a358 100644 --- a/lua/cmp/utils/autocmd.lua +++ b/lua/cmp/utils/autocmd.lua @@ -19,8 +19,8 @@ autocmd.subscribe = function(events, callback) vim.api.nvim_create_autocmd(event, { desc = ('nvim-cmp: autocmd: %s'):format(event), group = autocmd.group, - callback = function() - autocmd.emit(event) + callback = function(details) + autocmd.emit(event, details) end, }) end @@ -41,12 +41,13 @@ end ---Emit autocmd ---@param event string -autocmd.emit = function(event) +---@param details table|nil +autocmd.emit = function(event, details) debug.log(' ') debug.log(string.format('>>> %s', event)) autocmd.events[event] = autocmd.events[event] or {} for _, callback in ipairs(autocmd.events[event]) do - callback() + callback(details) end end diff --git a/plugin/cmp.lua b/plugin/cmp.lua index 611b5c96a..c6cd91c2e 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -53,6 +53,14 @@ if vim.on_key then end, vim.api.nvim_create_namespace('cmp.plugin')) end +-- see compare.scopes +if vim.fn.has('nvim-0.9') then + autocmd.subscribe({ 'FileType' }, function(details) + if vim.treesitter.language.get_lang(details.match) then + vim.b[details.buf].cmp_buf_has_treesitter = true + end + end) +end vim.api.nvim_create_user_command('CmpStatus', function() require('cmp').status() From 80235040840673961747aae28d844d4ce2a2e3cc Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 2 Aug 2024 02:38:46 -0700 Subject: [PATCH 2/9] add extra unload autocmd --- plugin/cmp.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugin/cmp.lua b/plugin/cmp.lua index c6cd91c2e..bb84319b2 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -60,6 +60,11 @@ if vim.fn.has('nvim-0.9') then vim.b[details.buf].cmp_buf_has_treesitter = true end end) + autocmd.subscribe({ 'BufUnload' }, function(details) + if vim.treesitter.language.get_lang(details.match) then + vim.b[details.buf].cmp_buf_has_treesitter = false + end + end) end vim.api.nvim_create_user_command('CmpStatus', function() From fdba1ccaaca9d1a52a6fdd80348040ad00c15412 Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 9 Aug 2024 16:04:50 -0700 Subject: [PATCH 3/9] Improve perf of compare.scopes --- lua/cmp/config/compare.lua | 66 +++++++++++++++----------------------- plugin/cmp.lua | 4 +-- 2 files changed, 27 insertions(+), 43 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index 9dbe4f96d..7b4da1814 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -202,6 +202,7 @@ compare.scopes = setmetatable({ has_nvim_0_9_features = vim.fn.has('nvim-0.9') == 1, update = function(self) local config = require('cmp').get_config() + self.definition_depths = {} if not vim.tbl_contains(config.sorting.comparators, compare.scopes) then return end @@ -210,65 +211,48 @@ compare.scopes = setmetatable({ if ok then local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf() local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1 - if self.has_nvim_0_9_features and not vim.b[buf].cmp_buf_has_treesitter then + if self.has_nvim_0_9_features and not vim.b[buf].cmp_buf_has_ts_parser then return end - -- Cursor scope. - local cursor_scope = nil - -- Prioritize the older get_scopes method from nvim-treesitter `master` over get from `main` - local scopes = locals.get_scopes and locals.get_scopes(buf) or select(3, locals.get(buf)) - for _, scope in ipairs(scopes) do - if scope:start() <= cursor_row and cursor_row <= scope:end_() then - if not cursor_scope then - cursor_scope = scope - else - if cursor_scope:start() <= scope:start() and scope:end_() <= cursor_scope:end_() then - cursor_scope = scope - end - end - elseif cursor_scope and cursor_scope:end_() <= scope:start() then - break - end + local get_cursor_node = vim.treesitter.get_node or require('nvim-treesitter.ts_utils').get_node_at_cursor + local cursor_node = get_cursor_node() + local scope_depths = {} + local depth = 0 + -- If there's no cursor node, no iterations are made. + ---@diagnostic disable-next-line: param-type-mismatch + for scope in locals.iter_scope_tree(cursor_node, buf) do + scope_depths[scope:id()] = depth + depth = depth + 1 end - -- Definitions. + -- Check definitions from smaller to larger scopes. local definitions = locals.get_definitions_lookup_table(buf) - - -- Narrow definitions. - local depth = 0 - for scope in locals.iter_scope_tree(cursor_scope, buf) do - local s, e = scope:start(), scope:end_() - - -- Check scope's direct child. - for _, definition in pairs(definitions) do - if s <= definition.node:start() and definition.node:end_() <= e then - if scope:id() == locals.containing_scope(definition.node, buf):id() then - local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text - local text = get_node_text(definition.node, buf) or '' - if not self.scopes_map[text] then - self.scopes_map[text] = depth - end - end + local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text + for _, definition in pairs(definitions) do + local definition_depth = scope_depths[locals.containing_scope(definition.node, buf):id()] + local def_text = get_node_text(definition.node, buf) or '' + if definition_depth then + if not self.definition_depths[def_text] or self.definition_depths[def_text] then + self.definition_depths[def_text] = definition_depth end end - depth = depth + 1 end end end, }, { ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil __call = function(self, entry1, entry2) - local local1 = self.scopes_map[entry1.word] - local local2 = self.scopes_map[entry2.word] - if local1 ~= local2 then - if local1 == nil then + local def_depth1 = self.definition_depths[entry1.word] + local def_depth2 = self.definition_depths[entry2.word] + if def_depth1 ~= def_depth2 then + if def_depth1 == nil then return false end - if local2 == nil then + if def_depth2 == nil then return true end - return local1 < local2 + return def_depth1 < def_depth2 end end, }) diff --git a/plugin/cmp.lua b/plugin/cmp.lua index bb84319b2..c9d2617f0 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -57,12 +57,12 @@ end if vim.fn.has('nvim-0.9') then autocmd.subscribe({ 'FileType' }, function(details) if vim.treesitter.language.get_lang(details.match) then - vim.b[details.buf].cmp_buf_has_treesitter = true + vim.b[details.buf].cmp_buf_has_ts_parser = true end end) autocmd.subscribe({ 'BufUnload' }, function(details) if vim.treesitter.language.get_lang(details.match) then - vim.b[details.buf].cmp_buf_has_treesitter = false + vim.b[details.buf].cmp_buf_has_ts_parser = false end end) end From 545d9498bc4b95183c2235e0723cff06101a7480 Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 9 Aug 2024 16:14:06 -0700 Subject: [PATCH 4/9] Adjust when to clear definition_depths --- lua/cmp/config/compare.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index 7b4da1814..9fb499f8c 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -198,17 +198,17 @@ compare.locality = setmetatable({ ---scopes: Entries defined in a closer scope will be ranked higher (e.g., prefer local variables to globals). ---@type cmp.ComparatorFunctor compare.scopes = setmetatable({ - scopes_map = {}, + definition_depths = {}, has_nvim_0_9_features = vim.fn.has('nvim-0.9') == 1, update = function(self) local config = require('cmp').get_config() - self.definition_depths = {} if not vim.tbl_contains(config.sorting.comparators, compare.scopes) then return end local ok, locals = pcall(require, 'nvim-treesitter.locals') if ok then + self.definition_depths = {} local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf() local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1 if self.has_nvim_0_9_features and not vim.b[buf].cmp_buf_has_ts_parser then From fd5dec99e0d8fc4bc0b97e67f5beaf4f06e7abdb Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 9 Aug 2024 16:30:28 -0700 Subject: [PATCH 5/9] Remove unused variables, add comments, fix check --- lua/cmp/config/compare.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index 9fb499f8c..ef975f391 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -209,8 +209,7 @@ compare.scopes = setmetatable({ local ok, locals = pcall(require, 'nvim-treesitter.locals') if ok then self.definition_depths = {} - local win, buf = vim.api.nvim_get_current_win(), vim.api.nvim_get_current_buf() - local cursor_row = vim.api.nvim_win_get_cursor(win)[1] - 1 + local buf = vim.api.nvim_get_current_buf() if self.has_nvim_0_9_features and not vim.b[buf].cmp_buf_has_ts_parser then return end @@ -226,14 +225,15 @@ compare.scopes = setmetatable({ depth = depth + 1 end - -- Check definitions from smaller to larger scopes. + -- Map definitions based on their scope relative to the cursor. local definitions = locals.get_definitions_lookup_table(buf) local get_node_text = vim.treesitter.get_node_text or vim.treesitter.query.get_node_text for _, definition in pairs(definitions) do local definition_depth = scope_depths[locals.containing_scope(definition.node, buf):id()] local def_text = get_node_text(definition.node, buf) or '' if definition_depth then - if not self.definition_depths[def_text] or self.definition_depths[def_text] then + -- Prefer the closest scoped definitions. + if not self.definition_depths[def_text] or self.definition_depths[def_text] > definition_depth then self.definition_depths[def_text] = definition_depth end end From cf10bf84b94fadc8b7c2879db4a40735a946206f Mon Sep 17 00:00:00 2001 From: pynappo Date: Fri, 9 Aug 2024 16:41:26 -0700 Subject: [PATCH 6/9] Fix unintended change --- lua/cmp/config/compare.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index ef975f391..2630ee552 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -243,8 +243,8 @@ compare.scopes = setmetatable({ }, { ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil __call = function(self, entry1, entry2) - local def_depth1 = self.definition_depths[entry1.word] - local def_depth2 = self.definition_depths[entry2.word] + local def_depth1 = self.definition_depths[entry1:get_word()] + local def_depth2 = self.definition_depths[entry2:get_word()] if def_depth1 ~= def_depth2 then if def_depth1 == nil then return false From be8b111f4dc63c53f0c54f56c3f416b05d031029 Mon Sep 17 00:00:00 2001 From: pynappo Date: Sun, 27 Oct 2024 03:51:44 -0700 Subject: [PATCH 7/9] disable scopes if final filetype doesn't have ts support --- plugin/cmp.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugin/cmp.lua b/plugin/cmp.lua index c9d2617f0..3e44d90d3 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -58,6 +58,8 @@ if vim.fn.has('nvim-0.9') then autocmd.subscribe({ 'FileType' }, function(details) if vim.treesitter.language.get_lang(details.match) then vim.b[details.buf].cmp_buf_has_ts_parser = true + else + vim.b[details.buf].cmp_buf_has_ts_parser = false end end) autocmd.subscribe({ 'BufUnload' }, function(details) From 51da174c599ff6ee686fa0fb528c1ea3c53d8ea9 Mon Sep 17 00:00:00 2001 From: pynappo Date: Sun, 27 Oct 2024 04:08:38 -0700 Subject: [PATCH 8/9] update ts parser detection --- plugin/cmp.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugin/cmp.lua b/plugin/cmp.lua index 3e44d90d3..110fe01a8 100644 --- a/plugin/cmp.lua +++ b/plugin/cmp.lua @@ -54,9 +54,18 @@ if vim.on_key then end -- see compare.scopes -if vim.fn.has('nvim-0.9') then +if vim.fn.has('nvim-0.9') == 1 then + local ts = vim.treesitter + local has_ts_parser = ts.language.get_lang + -- vim.treesitter.language.add is recommended for checking treesitter in 0.11 nightly + if vim.fn.has('nvim-0.11') then + has_ts_parser = function(filetype) + local lang = ts.language.get_lang(filetype) + return lang and ts.language.add(lang) + end + end autocmd.subscribe({ 'FileType' }, function(details) - if vim.treesitter.language.get_lang(details.match) then + if has_ts_parser(details.match) then vim.b[details.buf].cmp_buf_has_ts_parser = true else vim.b[details.buf].cmp_buf_has_ts_parser = false From 1e90708741c9ca3bbacc3f6b9d72329d45826e1f Mon Sep 17 00:00:00 2001 From: pynappo Date: Sun, 27 Oct 2024 04:28:13 -0700 Subject: [PATCH 9/9] Switch off of entry:get_word() --- lua/cmp/config/compare.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/cmp/config/compare.lua b/lua/cmp/config/compare.lua index 2630ee552..ef975f391 100644 --- a/lua/cmp/config/compare.lua +++ b/lua/cmp/config/compare.lua @@ -243,8 +243,8 @@ compare.scopes = setmetatable({ }, { ---@type fun(self: table, entry1: cmp.Entry, entry2: cmp.Entry): boolean|nil __call = function(self, entry1, entry2) - local def_depth1 = self.definition_depths[entry1:get_word()] - local def_depth2 = self.definition_depths[entry2:get_word()] + local def_depth1 = self.definition_depths[entry1.word] + local def_depth2 = self.definition_depths[entry2.word] if def_depth1 ~= def_depth2 then if def_depth1 == nil then return false