diff --git a/doc/cmp.txt b/doc/cmp.txt index da2a7038f..accf76eac 100644 --- a/doc/cmp.txt +++ b/doc/cmp.txt @@ -592,6 +592,12 @@ matching.disallow_symbol_nonprefix_matching `boolean` Whether to allow symbols in matches if the match is not a prefix match. + cmp-config.matching.disallow_case_insensetive_matching +matching.disallow_case_insensetive_matching + `boolean` + Whether to perform a case sensetive matching and return 0 score if the cases + don't match. + *cmp-config.sorting.priority_weight* sorting.priority_weight~ `number` diff --git a/lua/cmp/config/default.lua b/lua/cmp/config/default.lua index e1be158f6..bd4d4c84b 100644 --- a/lua/cmp/config/default.lua +++ b/lua/cmp/config/default.lua @@ -61,6 +61,7 @@ return function() disallow_partial_matching = false, disallow_prefix_unmatching = false, disallow_symbol_nonprefix_matching = true, + disallow_case_insensetive_matching = false }, sorting = { diff --git a/lua/cmp/entry.lua b/lua/cmp/entry.lua index cc89acb06..0d8c96d96 100644 --- a/lua/cmp/entry.lua +++ b/lua/cmp/entry.lua @@ -393,7 +393,17 @@ end entry.match = function(self, input, matching_config) -- https://www.lua.org/pil/11.6.html -- do not use '..' to allocate multiple strings - local cache_key = string.format('%s:%d:%d:%d:%d:%d:%d', 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, matching_config.disallow_partial_fuzzy_matching and 1 or 0, matching_config.disallow_symbol_nonprefix_matching and 1 or 0) + + local cache_key = string.format('%s:%d:%d:%d:%d:%d:%d:%d', + 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, + matching_config.disallow_partial_fuzzy_matching and 1 or 0, + matching_config.disallow_symbol_nonprefix_matching and 1 or 0, + matching_config.disallow_case_insensetive_matching and 1 or 0) + local matched = self.match_cache:get(cache_key) if matched then if self.match_view_args_ret and self.match_view_args_ret.input ~= input then @@ -417,6 +427,7 @@ entry._match = function(self, input, matching_config) disallow_partial_matching = matching_config.disallow_partial_matching, disallow_prefix_unmatching = matching_config.disallow_prefix_unmatching, disallow_symbol_nonprefix_matching = matching_config.disallow_symbol_nonprefix_matching, + disallow_case_insensentive_matching = matching_config.disallow_case_insensetive_matching, synonyms = { self.word, self.completion_item.label, diff --git a/lua/cmp/matcher.lua b/lua/cmp/matcher.lua index b5cbf4ca8..0ff62d027 100644 --- a/lua/cmp/matcher.lua +++ b/lua/cmp/matcher.lua @@ -81,7 +81,7 @@ end ---Match entry ---@param input string ---@param word string ----@param option { synonyms: string[], disallow_fullfuzzy_matching: boolean, disallow_fuzzy_matching: boolean, disallow_partial_fuzzy_matching: boolean, disallow_partial_matching: boolean, disallow_prefix_unmatching: boolean, disallow_symbol_nonprefix_matching: boolean } +---@param option { synonyms: string[], disallow_fullfuzzy_matching: boolean, disallow_fuzzy_matching: boolean, disallow_partial_fuzzy_matching: boolean, disallow_partial_matching: boolean, disallow_prefix_unmatching: boolean, disallow_symbol_nonprefix_matching: boolean, disallow_case_insensetive_matching: boolean} ---@return integer, table matcher.match = function(input, word, option) option = option or {} @@ -98,7 +98,7 @@ matcher.match = function(input, word, option) -- Check prefix matching. if option.disallow_prefix_unmatching then - if not char.match(string.byte(input, 1), string.byte(word, 1)) then + if not char.match(string.byte(input, 1), string.byte(word, 1), option.disallow_case_insensetive_matching) then return 0, {} end end @@ -111,7 +111,7 @@ matcher.match = function(input, word, option) local word_bound_index = 1 local no_symbol_match = false while input_end_index <= #input and word_index <= #word do - local m = matcher.find_match_region(input, input_start_index, input_end_index, word, word_index) + local m = matcher.find_match_region(input, input_start_index, input_end_index, word, word_index, option) if m and input_end_index <= m.input_match_end then m.index = word_bound_index input_start_index = m.input_match_start + 1 @@ -131,7 +131,9 @@ matcher.match = function(input, word, option) end if #matches == 0 then - if not option.disallow_fuzzy_matching and not option.disallow_prefix_unmatching and not option.disallow_partial_fuzzy_matching then + if not option.disallow_fuzzy_matching + and not option.disallow_prefix_unmatching + and not option.disallow_partial_fuzzy_matching then if matcher.fuzzy(input, word, matches, option) then return 1, matches end @@ -150,7 +152,8 @@ matcher.match = function(input, word, option) prefix = true local o = 1 for i = matches[1].input_match_start, matches[1].input_match_end do - if not char.match(string.byte(synonym, o), string.byte(input, i)) then + if not char.match(string.byte(synonym, o), string.byte(input, i), + option.disallow_case_insensetive_matching) then prefix = false break end @@ -212,7 +215,9 @@ matcher.fuzzy = function(input, word, matches, option) local word_offset = 0 local word_index = char.get_next_semantic_index(word, curr_match.word_match_end) while word_offset + word_index < next_match.word_match_start and input_index <= #input do - if char.match(string.byte(word, word_index + word_offset), string.byte(input, input_index)) then + if char.match(string.byte(word, word_index + word_offset), + string.byte(input, input_index), + option.disallow_case_insensetive_matching) then input_index = input_index + 1 word_offset = word_offset + 1 else @@ -233,7 +238,7 @@ matcher.fuzzy = function(input, word, matches, option) local match_count = 0 while word_offset + word_index <= #word and input_index <= #input do local c1, c2 = string.byte(word, word_index + word_offset), string.byte(input, input_index) - if char.match(c1, c2) then + if char.match(c1, c2, option.disallow_case_insensetive_matching ) then if not matched then input_match_start = input_index word_match_start = word_index + word_offset @@ -277,10 +282,10 @@ matcher.fuzzy = function(input, word, matches, option) end --- find_match_region -matcher.find_match_region = function(input, input_start_index, input_end_index, word, word_index) +matcher.find_match_region = function(input, input_start_index, input_end_index, word, word_index, option) -- determine input position ( woroff -> word_offset ) while input_start_index < input_end_index do - if char.match(string.byte(input, input_end_index), string.byte(word, word_index)) then + if char.match(string.byte(input, input_end_index), string.byte(word, word_index), option.disallow_case_insensetive_matching) then break end input_end_index = input_end_index - 1 @@ -300,7 +305,7 @@ matcher.find_match_region = function(input, input_start_index, input_end_index, while input_index <= #input and word_index + word_offset <= #word do local c1 = string.byte(input, input_index) local c2 = string.byte(word, word_index + word_offset) - if char.match(c1, c2) then + if char.match(c1, c2, option.disallow_case_insensetive_matching) then -- Match start. if input_match_start == -1 then input_match_start = input_index diff --git a/lua/cmp/matcher_spec.lua b/lua/cmp/matcher_spec.lua index e79715d48..a59313caf 100644 --- a/lua/cmp/matcher_spec.lua +++ b/lua/cmp/matcher_spec.lua @@ -52,6 +52,7 @@ describe('matcher', function() disallow_prefix_unmatching = false, disallow_partial_fuzzy_matching = false, disallow_symbol_nonprefix_matching = true, + disallow_case_insensetive_matching = false, }) assert.is.truthy(score >= 1) assert.equals(matches[1].word_match_start, 5) @@ -62,6 +63,7 @@ describe('matcher', function() disallow_prefix_unmatching = false, disallow_partial_fuzzy_matching = true, disallow_symbol_nonprefix_matching = true, + disallow_case_insensetive_matching = false, }) assert.is.truthy(score == 0) end) @@ -93,6 +95,11 @@ describe('matcher', function() assert.is.truthy(matcher.match('foo_', 'b foo_bar', { disallow_symbol_nonprefix_matching = false }) >= 1) end) + it('disallow_case_insensetive_matching', function() + assert.is.truthy(matcher.match('Test', 'test', { disallow_case_insensetive_matching = true }) == 0) + assert.is.truthy(matcher.match('Test', 'test', { disallow_case_insensetive_matching = false}) >= 1) + end) + it('debug', function() matcher.debug = function(...) print(vim.inspect({ ... })) diff --git a/lua/cmp/types/cmp.lua b/lua/cmp/types/cmp.lua index 80f6d9205..56ddc30c6 100644 --- a/lua/cmp/types/cmp.lua +++ b/lua/cmp/types/cmp.lua @@ -146,6 +146,7 @@ cmp.ItemField = { ---@field public disallow_partial_matching boolean ---@field public disallow_prefix_unmatching boolean ---@field public disallow_symbol_nonprefix_matching boolean +---@field public disallow_case_insensetive_matching boolean ---@class cmp.SortingConfig ---@field public priority_weight integer diff --git a/lua/cmp/utils/char.lua b/lua/cmp/utils/char.lua index c733bef31..0affaf17e 100644 --- a/lua/cmp/utils/char.lua +++ b/lua/cmp/utils/char.lua @@ -102,11 +102,16 @@ char.get_next_semantic_index = function(text, current_index) return #text + 1 end ----Ignore case match +---Ignore case match by default ---@param byte1 integer ---@param byte2 integer +---@param is_case_sensetive boolean? default:false whether the match should be case sensetive ---@return boolean -char.match = function(byte1, byte2) +char.match = function(byte1, byte2, is_case_sensetive) + if is_case_sensetive then + return byte1 == byte2 + end + if not char.is_alpha(byte1) or not char.is_alpha(byte2) then return byte1 == byte2 end