diff --git a/lua/cmp/entry.lua b/lua/cmp/entry.lua index a66a90c..07ceb52 100644 --- a/lua/cmp/entry.lua +++ b/lua/cmp/entry.lua @@ -109,14 +109,17 @@ entry.get_word = function(self) local word if misc.safe(self:get_completion_item().textEdit) then word = str.trim(self:get_completion_item().textEdit.newText) + if self:get_completion_item().insertTextFormat == types.lsp.InsertTextFormat.Snippet then + word = vim.lsp.util.parse_snippet(word) + end local overwrite = self:get_overwrite() if 0 < overwrite[2] or self:get_completion_item().insertTextFormat == types.lsp.InsertTextFormat.Snippet then - word = str.get_word(word, string.byte(self.context.cursor_after_line, 1)) + word = str.get_word(word, string.byte(self.context.cursor_after_line, 1), overwrite[1] or 0) end elseif misc.safe(self:get_completion_item().insertText) then word = str.trim(self:get_completion_item().insertText) if self:get_completion_item().insertTextFormat == types.lsp.InsertTextFormat.Snippet then - word = str.get_word(word) + word = str.get_word(vim.lsp.util.parse_snippet(word)) end else word = str.trim(self:get_completion_item().label) @@ -151,20 +154,6 @@ entry.get_filter_text = function(self) else word = str.trim(self:get_completion_item().label) end - - -- @see https://github.com/clangd/clangd/issues/815 - if misc.safe(self:get_completion_item().textEdit) then - local diff = self.source_offset - self:get_offset() - if diff > 0 then - if char.is_symbol(string.byte(self.context.cursor_line, self:get_offset())) then - local prefix = string.sub(self.context.cursor_line, self:get_offset(), self:get_offset() + diff) - if string.find(word, prefix, 1, true) ~= 1 then - word = prefix .. word - end - end - end - end - return word end) end @@ -351,11 +340,31 @@ end ---@return { score: number, matches: table[] } entry.match = function(self, input) return self.match_cache:ensure({ input, self.resolved_completion_item and 1 or 0 }, function() - local score, matches, _ - score, matches = matcher.match(input, self:get_filter_text(), { self:get_word(), self:get_completion_item().label }) - if self:get_filter_text() ~= self:get_completion_item().label then + local filter_text = self:get_filter_text() + + local score, matches + score, matches = matcher.match(input, filter_text, { self:get_word(), self:get_completion_item().label }) + + -- Support the language server that doesn't respect VSCode's behaviors. + if score == 0 then + if misc.safe(self:get_completion_item().textEdit) then + local diff = self.source_offset - self:get_offset() + if diff > 0 then + local prefix = string.sub(self.context.cursor_line, self:get_offset(), self:get_offset() + diff) + local accept = false + accept = accept or string.match(prefix, '^[^%a]+$') + accept = accept or string.find(self:get_completion_item().textEdit.newText, prefix, 1, true) + if accept then + score, matches = matcher.match(input, prefix .. filter_text, { self:get_word(), self:get_completion_item().label }) + end + end + end + end + + if filter_text ~= self:get_completion_item().label then _, matches = matcher.match(input, self:get_completion_item().label, { self:get_word() }) end + return { score = score, matches = matches } end) end diff --git a/lua/cmp/entry_spec.lua b/lua/cmp/entry_spec.lua index 08223b4..61f6fb8 100644 --- a/lua/cmp/entry_spec.lua +++ b/lua/cmp/entry_spec.lua @@ -101,7 +101,7 @@ describe('entry', function() }, }) assert.are.equal(e:get_vim_item(4).word, '->foo') - assert.are.equal(e:get_filter_text(), '.foo') + assert.are.equal(e:get_filter_text(), 'foo') end) it('[typescript-language-server] 1', function() diff --git a/lua/cmp/utils/str.lua b/lua/cmp/utils/str.lua index d4457d2..cbdcbf4 100644 --- a/lua/cmp/utils/str.lua +++ b/lua/cmp/utils/str.lua @@ -106,33 +106,36 @@ end ---get_word ---@param text string +---@param stop_char number +---@param min_length number ---@return string -str.get_word = function(text, stop_char) +str.get_word = function(text, stop_char, min_length) + min_length = min_length or 0 + local has_alnum = false local stack = {} local word = {} + local add = function(c) + table.insert(word, string.char(c)) + if stack[#stack] == c then + table.remove(stack, #stack) + else + if PAIRS[c] then + table.insert(stack, c) + end + end + end for i = 1, #text do local c = string.byte(text, i, i) - if not INVALIDS[c] then - if PAIRS[c] then - table.insert(stack, c) - end + if #word < min_length then table.insert(word, string.char(c)) + elseif not INVALIDS[c] then + add(c) has_alnum = has_alnum or char.is_alnum(c) elseif not has_alnum then - if PAIRS[c] then - table.insert(stack, c) - end - table.insert(word, string.char(c)) + add(c) elseif #stack ~= 0 then - table.insert(word, string.char(c)) - if stack[#stack] == c then - table.remove(stack, #stack) - else - if PAIRS[c] then - table.insert(stack, c) - end - end + add(c) if has_alnum and #stack == 0 then break end diff --git a/lua/cmp/utils/str_spec.lua b/lua/cmp/utils/str_spec.lua index 8f7462e..541414f 100644 --- a/lua/cmp/utils/str_spec.lua +++ b/lua/cmp/utils/str_spec.lua @@ -9,6 +9,7 @@ describe('utils.str', function() assert.are.equal(str.get_word('"devDependencies":', string.byte('"')), '"devDependencies') assert.are.equal(str.get_word('"devDependencies": ${1},', string.byte('"')), '"devDependencies') assert.are.equal(str.get_word('#[cfg(test)]'), '#[cfg(test)]') + assert.are.equal(str.get_word('import { GetStaticProps$1 } from "next";', nil, 9), 'import { GetStaticProps') end) it('strikethrough', function()