18
doc/cmp.txt
18
doc/cmp.txt
@@ -209,12 +209,11 @@ NOTE: You can call these functions in mapping via `<Cmd>lua require('cmp').compl
|
|||||||
>
|
>
|
||||||
cmp.setup {
|
cmp.setup {
|
||||||
mapping = {
|
mapping = {
|
||||||
['<C-n>'] = cmp.mapping(function(fallback)
|
['<C-l>'] = cmp.mapping(function(fallback)
|
||||||
if cmp.visible() then
|
if cmp.visible() then
|
||||||
if cmp.complete_common_string() then
|
if cmp.complete_common_string() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return cmp.select_next_item()
|
|
||||||
end
|
end
|
||||||
fallback()
|
fallback()
|
||||||
end, { 'i', 'c' }),
|
end, { 'i', 'c' }),
|
||||||
@@ -422,6 +421,21 @@ formatting.format~
|
|||||||
NOTE: The `vim.CompletedItem` can have special properties `abbr_hl_group`,
|
NOTE: The `vim.CompletedItem` can have special properties `abbr_hl_group`,
|
||||||
`kind_hl_group` and `menu_hl_group`.
|
`kind_hl_group` and `menu_hl_group`.
|
||||||
|
|
||||||
|
*cmp-config.matching.disallow_fuzzy_matching*
|
||||||
|
matching.disallow_fuzzy_matching~
|
||||||
|
`boolean`
|
||||||
|
Specify disallow or allow fuzzy matching.
|
||||||
|
|
||||||
|
*cmp-config.matching.disallow_partial_matching*
|
||||||
|
matching.disallow_partial_matching~
|
||||||
|
`boolean`
|
||||||
|
Specify disallow or allow partial matching.
|
||||||
|
|
||||||
|
*cmp-config.matching.disallow_prefix_unmatching*
|
||||||
|
matching.disallow_prefix_unmatching~
|
||||||
|
`boolean`
|
||||||
|
Specify disallow or allow prefix unmatching.
|
||||||
|
|
||||||
*cmp-config.sorting.priority_weight*
|
*cmp-config.sorting.priority_weight*
|
||||||
sorting.priority_weight~
|
sorting.priority_weight~
|
||||||
`number`
|
`number`
|
||||||
|
|||||||
@@ -88,6 +88,12 @@ return function()
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
matching = {
|
||||||
|
disallow_fuzzy_matching = false,
|
||||||
|
disallow_partial_matching = false,
|
||||||
|
disallow_prefix_unmatching = false,
|
||||||
|
},
|
||||||
|
|
||||||
sorting = {
|
sorting = {
|
||||||
priority_weight = 2,
|
priority_weight = 2,
|
||||||
comparators = {
|
comparators = {
|
||||||
|
|||||||
@@ -220,12 +220,24 @@ end
|
|||||||
|
|
||||||
---Complete common string for current completed entries.
|
---Complete common string for current completed entries.
|
||||||
core.complete_common_string = function(self)
|
core.complete_common_string = function(self)
|
||||||
if not self.view:visible() then
|
if not self.view:visible() or self.view:get_active_entry() then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config.set_onetime({
|
||||||
|
sources = config.get().sources,
|
||||||
|
matching = {
|
||||||
|
disallow_prefix_unmatching = true,
|
||||||
|
disallow_partial_matching = true,
|
||||||
|
disallow_fuzzy_matching = true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self:filter()
|
||||||
self.filter:sync(1000)
|
self.filter:sync(1000)
|
||||||
|
|
||||||
|
config.set_onetime({})
|
||||||
|
|
||||||
local cursor = api.get_cursor()
|
local cursor = api.get_cursor()
|
||||||
local offset = self.view:get_offset()
|
local offset = self.view:get_offset()
|
||||||
local common_string
|
local common_string
|
||||||
|
|||||||
@@ -337,13 +337,28 @@ end
|
|||||||
|
|
||||||
---Match line.
|
---Match line.
|
||||||
---@param input string
|
---@param input string
|
||||||
|
---@param matching_config cmp.MatchingConfig
|
||||||
---@return { score: number, matches: table[] }
|
---@return { score: number, matches: table[] }
|
||||||
entry.match = function(self, input)
|
entry.match = function(self, input, matching_config)
|
||||||
return self.match_cache:ensure({ input, self.resolved_completion_item and 1 or 0 }, function()
|
return self.match_cache:ensure({
|
||||||
local filter_text = self:get_filter_text()
|
input,
|
||||||
|
self.resolved_completion_item and 1 or 0,
|
||||||
|
matching_config.disallow_fuzzy_matching and 1 or 0,
|
||||||
|
matching_config.disallow_partial_matching and 1 or 0,
|
||||||
|
matching_config.disallow_prefix_unmatching and 1 or 0,
|
||||||
|
}, function()
|
||||||
|
local option = {
|
||||||
|
disallow_fuzzy_matching = matching_config.disallow_fuzzy_matching,
|
||||||
|
disallow_partial_matching = matching_config.disallow_partial_matching,
|
||||||
|
disallow_prefix_unmatching = matching_config.disallow_prefix_unmatching,
|
||||||
|
synonyms = {
|
||||||
|
self:get_word(),
|
||||||
|
self:get_completion_item().label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
local score, matches, _
|
local score, matches, _
|
||||||
score, matches = matcher.match(input, filter_text, { self:get_word(), self:get_completion_item().label })
|
score, matches = matcher.match(input, self:get_filter_text(), option)
|
||||||
|
|
||||||
-- Support the language server that doesn't respect VSCode's behaviors.
|
-- Support the language server that doesn't respect VSCode's behaviors.
|
||||||
if score == 0 then
|
if score == 0 then
|
||||||
@@ -355,13 +370,13 @@ entry.match = function(self, input)
|
|||||||
accept = accept or string.match(prefix, '^[^%a]+$')
|
accept = accept or string.match(prefix, '^[^%a]+$')
|
||||||
accept = accept or string.find(self:get_completion_item().textEdit.newText, prefix, 1, true)
|
accept = accept or string.find(self:get_completion_item().textEdit.newText, prefix, 1, true)
|
||||||
if accept then
|
if accept then
|
||||||
score, matches = matcher.match(input, prefix .. filter_text, { self:get_word(), self:get_completion_item().label })
|
score, matches = matcher.match(input, prefix .. self:get_filter_text(), option)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if filter_text ~= self:get_completion_item().label then
|
if self:get_filter_text() ~= self:get_completion_item().label then
|
||||||
_, matches = matcher.match(input, self:get_completion_item().label, { self:get_word() })
|
_, matches = matcher.match(input, self:get_completion_item().label, { self:get_word() })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,11 @@ end
|
|||||||
---Match entry
|
---Match entry
|
||||||
---@param input string
|
---@param input string
|
||||||
---@param word string
|
---@param word string
|
||||||
---@param words string[]
|
---@param option { synonyms: string[], disallow_fuzzy_matching: boolean, disallow_partial_matching: boolean, disallow_prefix_unmatching: boolean }
|
||||||
---@return number
|
---@return number
|
||||||
matcher.match = function(input, word, words)
|
matcher.match = function(input, word, option)
|
||||||
|
option = option or {}
|
||||||
|
|
||||||
-- Empty input
|
-- Empty input
|
||||||
if #input == 0 then
|
if #input == 0 then
|
||||||
return matcher.PREFIX_FACTOR + matcher.NOT_FUZZY_FACTOR, {}
|
return matcher.PREFIX_FACTOR + matcher.NOT_FUZZY_FACTOR, {}
|
||||||
@@ -85,7 +87,14 @@ matcher.match = function(input, word, words)
|
|||||||
return 0, {}
|
return 0, {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gather matched regions
|
-- Check prefix matching.
|
||||||
|
if option.disallow_prefix_unmatching then
|
||||||
|
if not char.match(string.byte(input, 1), string.byte(word, 1)) then
|
||||||
|
return 0, {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gather matched regions
|
||||||
local matches = {}
|
local matches = {}
|
||||||
local input_start_index = 1
|
local input_start_index = 1
|
||||||
local input_end_index = 1
|
local input_end_index = 1
|
||||||
@@ -105,6 +114,11 @@ matcher.match = function(input, word, words)
|
|||||||
word_bound_index = word_bound_index + 1
|
word_bound_index = word_bound_index + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Check partial matching.
|
||||||
|
if option.disallow_partial_matching and #matches > 1 then
|
||||||
|
return 0, {}
|
||||||
|
end
|
||||||
|
|
||||||
if #matches == 0 then
|
if #matches == 0 then
|
||||||
return 0, {}
|
return 0, {}
|
||||||
end
|
end
|
||||||
@@ -116,11 +130,11 @@ matcher.match = function(input, word, words)
|
|||||||
if matches[1].input_match_start == 1 and matches[1].word_match_start == 1 then
|
if matches[1].input_match_start == 1 and matches[1].word_match_start == 1 then
|
||||||
prefix = true
|
prefix = true
|
||||||
else
|
else
|
||||||
for _, w in ipairs(words or {}) do
|
for _, synonym in ipairs(option.synonyms or {}) do
|
||||||
prefix = true
|
prefix = true
|
||||||
local o = 1
|
local o = 1
|
||||||
for i = matches[1].input_match_start, matches[1].input_match_end do
|
for i = matches[1].input_match_start, matches[1].input_match_end do
|
||||||
if not char.match(string.byte(w, o), string.byte(input, i)) then
|
if not char.match(string.byte(synonym, o), string.byte(input, i)) then
|
||||||
prefix = false
|
prefix = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -152,9 +166,11 @@ matcher.match = function(input, word, words)
|
|||||||
|
|
||||||
-- Check remaining input as fuzzy
|
-- Check remaining input as fuzzy
|
||||||
if matches[#matches].input_match_end < #input then
|
if matches[#matches].input_match_end < #input then
|
||||||
|
if not option.disallow_fuzzy_matching then
|
||||||
if prefix and matcher.fuzzy(input, word, matches) then
|
if prefix and matcher.fuzzy(input, word, matches) then
|
||||||
return score, matches
|
return score, matches
|
||||||
end
|
end
|
||||||
|
end
|
||||||
return 0, {}
|
return 0, {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -16,20 +16,37 @@ describe('matcher', function()
|
|||||||
assert.is.truthy(matcher.match('woroff', 'word_offset') >= 1)
|
assert.is.truthy(matcher.match('woroff', 'word_offset') >= 1)
|
||||||
assert.is.truthy(matcher.match('call', 'call') > matcher.match('call', 'condition_all'))
|
assert.is.truthy(matcher.match('call', 'call') > matcher.match('call', 'condition_all'))
|
||||||
assert.is.truthy(matcher.match('Buffer', 'Buffer') > matcher.match('Buffer', 'buffer'))
|
assert.is.truthy(matcher.match('Buffer', 'Buffer') > matcher.match('Buffer', 'buffer'))
|
||||||
|
assert.is.truthy(matcher.match('luacon', 'lua_context') > matcher.match('luacon', 'LuaContext'))
|
||||||
assert.is.truthy(matcher.match('fmodify', 'fnamemodify') >= 1)
|
assert.is.truthy(matcher.match('fmodify', 'fnamemodify') >= 1)
|
||||||
assert.is.truthy(matcher.match('candlesingle', 'candle#accept#single') >= 1)
|
assert.is.truthy(matcher.match('candlesingle', 'candle#accept#single') >= 1)
|
||||||
assert.is.truthy(matcher.match('conso', 'console') > matcher.match('conso', 'ConstantSourceNode'))
|
|
||||||
assert.is.truthy(matcher.match('var_', 'var_dump') >= 1)
|
|
||||||
assert.is.truthy(matcher.match('my_', 'my_awesome_variable') > matcher.match('my_', 'completion_matching_strategy_list'))
|
|
||||||
assert.is.truthy(matcher.match('luacon', 'lua_context') > matcher.match('luacon', 'LuaContext'))
|
|
||||||
assert.is.truthy(matcher.match('call', 'calc') == 0)
|
|
||||||
|
|
||||||
assert.is.truthy(matcher.match('vi', 'void#') >= 1)
|
assert.is.truthy(matcher.match('vi', 'void#') >= 1)
|
||||||
assert.is.truthy(matcher.match('vo', 'void#') >= 1)
|
assert.is.truthy(matcher.match('vo', 'void#') >= 1)
|
||||||
|
assert.is.truthy(matcher.match('var_', 'var_dump') >= 1)
|
||||||
|
assert.is.truthy(matcher.match('conso', 'console') > matcher.match('conso', 'ConstantSourceNode'))
|
||||||
assert.is.truthy(matcher.match('usela', 'useLayoutEffect') > matcher.match('usela', 'useDataLayer'))
|
assert.is.truthy(matcher.match('usela', 'useLayoutEffect') > matcher.match('usela', 'useDataLayer'))
|
||||||
assert.is.truthy(matcher.match('true', 'v:true', { 'true' }) == matcher.match('true', 'true'))
|
assert.is.truthy(matcher.match('my_', 'my_awesome_variable') > matcher.match('my_', 'completion_matching_strategy_list'))
|
||||||
assert.is.truthy(matcher.match('g', 'get', { 'get' }) > matcher.match('g', 'dein#get', { 'dein#get' }))
|
|
||||||
assert.is.truthy(matcher.match('2', '[[2021') >= 1)
|
assert.is.truthy(matcher.match('2', '[[2021') >= 1)
|
||||||
|
|
||||||
|
assert.is.truthy(matcher.match('true', 'v:true', { synonyms = { 'true' } }) == matcher.match('true', 'true'))
|
||||||
|
assert.is.truthy(matcher.match('g', 'get', { synonyms = { 'get' } }) > matcher.match('g', 'dein#get', { 'dein#get' }))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('disallow_fuzzy_matching', function()
|
||||||
|
assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = true }) == 0)
|
||||||
|
assert.is.truthy(matcher.match('fmodify', 'fnamemodify', { disallow_fuzzy_matching = false }) >= 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('disallow_partial_matching', function()
|
||||||
|
assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = true }) == 0)
|
||||||
|
assert.is.truthy(matcher.match('fb', 'foo_bar', { disallow_partial_matching = false }) >= 1)
|
||||||
|
assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = true }) >= 1)
|
||||||
|
assert.is.truthy(matcher.match('fb', 'fboo_bar', { disallow_partial_matching = false }) >= 1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('disallow_prefix_unmatching', function()
|
||||||
|
assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = true }) == 0)
|
||||||
|
assert.is.truthy(matcher.match('bar', 'foo_bar', { disallow_prefix_unmatching = false }) >= 1)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('debug', function()
|
it('debug', function()
|
||||||
|
|||||||
@@ -61,12 +61,18 @@ source.reset = function(self)
|
|||||||
self.complete_dedup(function() end)
|
self.complete_dedup(function() end)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Return source option
|
---Return source config
|
||||||
---@return cmp.SourceConfig
|
---@return cmp.SourceConfig
|
||||||
source.get_config = function(self)
|
source.get_source_config = function(self)
|
||||||
return config.get_source_config(self.name) or {}
|
return config.get_source_config(self.name) or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Return matching config
|
||||||
|
---@return cmp.MatchingConfig
|
||||||
|
source.get_matching_config = function()
|
||||||
|
return config.get().matching
|
||||||
|
end
|
||||||
|
|
||||||
---Get fetching time
|
---Get fetching time
|
||||||
source.get_fetching_time = function(self)
|
source.get_fetching_time = function(self)
|
||||||
if self.status == source.SourceStatus.FETCHING then
|
if self.status == source.SourceStatus.FETCHING then
|
||||||
@@ -103,7 +109,7 @@ source.get_entries = function(self, ctx)
|
|||||||
inputs[o] = string.sub(ctx.cursor_before_line, o)
|
inputs[o] = string.sub(ctx.cursor_before_line, o)
|
||||||
end
|
end
|
||||||
|
|
||||||
local match = e:match(inputs[o])
|
local match = e:match(inputs[o], self:get_matching_config())
|
||||||
e.score = match.score
|
e.score = match.score
|
||||||
e.exact = false
|
e.exact = false
|
||||||
if e.score >= 1 then
|
if e.score >= 1 then
|
||||||
@@ -114,7 +120,7 @@ source.get_entries = function(self, ctx)
|
|||||||
end
|
end
|
||||||
self.cache:set({ 'get_entries', self.revision, ctx.cursor_before_line }, entries)
|
self.cache:set({ 'get_entries', self.revision, ctx.cursor_before_line }, entries)
|
||||||
|
|
||||||
local max_item_count = self:get_config().max_item_count or 200
|
local max_item_count = self:get_source_config().max_item_count or 200
|
||||||
local limited_entries = {}
|
local limited_entries = {}
|
||||||
for _, e in ipairs(entries) do
|
for _, e in ipairs(entries) do
|
||||||
table.insert(limited_entries, e)
|
table.insert(limited_entries, e)
|
||||||
@@ -188,7 +194,7 @@ end
|
|||||||
---Get trigger_characters
|
---Get trigger_characters
|
||||||
---@return string[]
|
---@return string[]
|
||||||
source.get_trigger_characters = function(self)
|
source.get_trigger_characters = function(self)
|
||||||
local c = self:get_config()
|
local c = self:get_source_config()
|
||||||
if c.trigger_characters then
|
if c.trigger_characters then
|
||||||
return c.trigger_characters
|
return c.trigger_characters
|
||||||
end
|
end
|
||||||
@@ -206,7 +212,7 @@ end
|
|||||||
---Get keyword_pattern
|
---Get keyword_pattern
|
||||||
---@return string
|
---@return string
|
||||||
source.get_keyword_pattern = function(self)
|
source.get_keyword_pattern = function(self)
|
||||||
local c = self:get_config()
|
local c = self:get_source_config()
|
||||||
if c.keyword_pattern then
|
if c.keyword_pattern then
|
||||||
return c.keyword_pattern
|
return c.keyword_pattern
|
||||||
end
|
end
|
||||||
@@ -219,7 +225,7 @@ end
|
|||||||
---Get keyword_length
|
---Get keyword_length
|
||||||
---@return number
|
---@return number
|
||||||
source.get_keyword_length = function(self)
|
source.get_keyword_length = function(self)
|
||||||
local c = self:get_config()
|
local c = self:get_source_config()
|
||||||
if c.keyword_length then
|
if c.keyword_length then
|
||||||
return c.keyword_length
|
return c.keyword_length
|
||||||
end
|
end
|
||||||
@@ -288,7 +294,7 @@ source.complete = function(self, ctx, callback)
|
|||||||
self.context = ctx
|
self.context = ctx
|
||||||
self.completion_context = completion_context
|
self.completion_context = completion_context
|
||||||
self.source:complete(
|
self.source:complete(
|
||||||
vim.tbl_extend('keep', misc.copy(self:get_config()), {
|
vim.tbl_extend('keep', misc.copy(self:get_source_config()), {
|
||||||
offset = self.offset,
|
offset = self.offset,
|
||||||
context = ctx,
|
context = ctx,
|
||||||
completion_context = completion_context,
|
completion_context = completion_context,
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ cmp.ItemField.Menu = 'menu'
|
|||||||
---@field public completion cmp.CompletionConfig
|
---@field public completion cmp.CompletionConfig
|
||||||
---@field public documentation cmp.DocumentationConfig|"false"
|
---@field public documentation cmp.DocumentationConfig|"false"
|
||||||
---@field public confirmation cmp.ConfirmationConfig
|
---@field public confirmation cmp.ConfirmationConfig
|
||||||
|
---@field public matching cmp.MatchingConfig
|
||||||
---@field public sorting cmp.SortingConfig
|
---@field public sorting cmp.SortingConfig
|
||||||
---@field public formatting cmp.FormattingConfig
|
---@field public formatting cmp.FormattingConfig
|
||||||
---@field public snippet cmp.SnippetConfig
|
---@field public snippet cmp.SnippetConfig
|
||||||
@@ -103,6 +104,11 @@ cmp.ItemField.Menu = 'menu'
|
|||||||
---@field public default_behavior cmp.ConfirmBehavior
|
---@field public default_behavior cmp.ConfirmBehavior
|
||||||
---@field public get_commit_characters fun(commit_characters: string[]): string[]
|
---@field public get_commit_characters fun(commit_characters: string[]): string[]
|
||||||
|
|
||||||
|
---@class cmp.MatchingConfig
|
||||||
|
---@field public disallow_fuzzy_matching boolean
|
||||||
|
---@field public disallow_partial_matching boolean
|
||||||
|
---@field public disallow_prefix_unmatching boolean
|
||||||
|
|
||||||
---@class cmp.SortingConfig
|
---@class cmp.SortingConfig
|
||||||
---@field public priority_weight number
|
---@field public priority_weight number
|
||||||
---@field public comparators function[]
|
---@field public comparators function[]
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ async.throttle = function(fn, timeout)
|
|||||||
timer:start(math.max(1, self.timeout - (vim.loop.now() - time)), 0, function()
|
timer:start(math.max(1, self.timeout - (vim.loop.now() - time)), 0, function()
|
||||||
vim.schedule(function()
|
vim.schedule(function()
|
||||||
time = nil
|
time = nil
|
||||||
self.running = false
|
|
||||||
fn(unpack(args))
|
fn(unpack(args))
|
||||||
|
self.running = false
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end,
|
end,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ end
|
|||||||
view.open = function(self, ctx, sources)
|
view.open = function(self, ctx, sources)
|
||||||
local source_group_map = {}
|
local source_group_map = {}
|
||||||
for _, s in ipairs(sources) do
|
for _, s in ipairs(sources) do
|
||||||
local group_index = s:get_config().group_index or 0
|
local group_index = s:get_source_config().group_index or 0
|
||||||
if not source_group_map[group_index] then
|
if not source_group_map[group_index] then
|
||||||
source_group_map[group_index] = {}
|
source_group_map[group_index] = {}
|
||||||
end
|
end
|
||||||
@@ -83,7 +83,7 @@ view.open = function(self, ctx, sources)
|
|||||||
if s.offset <= offset then
|
if s.offset <= offset then
|
||||||
if not has_triggered_by_symbol_source or s.is_triggered_by_symbol then
|
if not has_triggered_by_symbol_source or s.is_triggered_by_symbol then
|
||||||
-- source order priority bonus.
|
-- source order priority bonus.
|
||||||
local priority = s:get_config().priority or ((#source_group - (i - 1)) * config.get().sorting.priority_weight)
|
local priority = s:get_source_config().priority or ((#source_group - (i - 1)) * config.get().sorting.priority_weight)
|
||||||
|
|
||||||
for _, e in ipairs(s:get_entries(ctx)) do
|
for _, e in ipairs(s:get_entries(ctx)) do
|
||||||
e.score = e.score + priority
|
e.score = e.score + priority
|
||||||
|
|||||||
Reference in New Issue
Block a user