diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 29b1df1f..c15a4867 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -1,6 +1,8 @@ " Author: w0rp " Description: Completion support for LSP linters +call ale#Set('completion_excluded_words', []) + let s:timer_id = -1 let s:last_done_pos = [] @@ -76,33 +78,49 @@ function! ale#completion#GetTriggerCharacter(filetype, prefix) abort return '' endfunction -function! ale#completion#Filter(suggestions, prefix) abort +function! ale#completion#Filter(buffer, suggestions, prefix) abort + let l:excluded_words = ale#Var(a:buffer, 'completion_excluded_words') + " For completing... " foo. " ^ " We need to include all of the given suggestions. if a:prefix is# '.' - return a:suggestions + let l:filtered_suggestions = a:suggestions + else + let l:filtered_suggestions = [] + + " Filter suggestions down to those starting with the prefix we used for + " finding suggestions in the first place. + " + " Some completion tools will include suggestions which don't even start + " with the characters we have already typed. + for l:item in a:suggestions + " A List of String values or a List of completion item Dictionaries + " is accepted here. + let l:word = type(l:item) == type('') ? l:item : l:item.word + + " Add suggestions if the suggestion starts with a case-insensitive + " match for the prefix. + if l:word[: len(a:prefix) - 1] is? a:prefix + call add(l:filtered_suggestions, l:item) + endif + endfor endif - let l:filtered_suggestions = [] - - " Filter suggestions down to those starting with the prefix we used for - " finding suggestions in the first place. - " - " Some completion tools will include suggestions which don't even start - " with the characters we have already typed. - for l:item in a:suggestions - " A List of String values or a List of completion item Dictionaries - " is accepted here. - let l:word = type(l:item) == type('') ? l:item : l:item.word - - " Add suggestions if the suggestion starts with a case-insensitive - " match for the prefix. - if l:word[: len(a:prefix) - 1] is? a:prefix - call add(l:filtered_suggestions, l:item) + if !empty(l:excluded_words) + " Copy the List if needed. We don't want to modify the argument. + " We shouldn't make a copy if we don't need to. + if l:filtered_suggestions is a:suggestions + let l:filtered_suggestions = copy(a:suggestions) endif - endfor + + " Remove suggestions with words in the exclusion List. + call filter( + \ l:filtered_suggestions, + \ 'index(l:excluded_words, type(v:val) is type('''') ? v:val : v:val.word) < 0', + \) + endif return l:filtered_suggestions endfunction @@ -290,10 +308,12 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort return endif + let l:buffer = bufnr('') let l:command = get(a:response, 'command', '') if l:command is# 'completions' let l:names = ale#completion#Filter( + \ l:buffer, \ ale#completion#ParseTSServerCompletions(a:response), \ b:ale_completion_info.prefix, \)[: g:ale_completion_max_suggestions - 1] @@ -302,7 +322,7 @@ function! ale#completion#HandleTSServerResponse(conn_id, response) abort let b:ale_completion_info.request_id = ale#lsp#Send( \ b:ale_completion_info.conn_id, \ ale#lsp#tsserver_message#CompletionEntryDetails( - \ bufnr(''), + \ l:buffer, \ b:ale_completion_info.line, \ b:ale_completion_info.column, \ l:names, diff --git a/doc/ale.txt b/doc/ale.txt index f1e784ae..4a926386 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -631,6 +631,9 @@ delay for completion can be configured with |g:ale_completion_delay|. ALE will only suggest so many possible matches for completion. The maximum number of items can be controlled with |g:ale_completion_max_suggestions|. +If you don't like some of the suggestions you see, you can filter them out +with |g:ale_completion_excluded_words| or |b:ale_completion_excluded_words|. + ------------------------------------------------------------------------------- 5.2 Go To Definition *ale-go-to-definition* @@ -763,6 +766,24 @@ g:ale_completion_enabled *g:ale_completion_enabled* See |ale-completion| +g:ale_completion_excluded_words *g:ale_completion_excluded_words* + *b:ale_completion_excluded_words* + Type: |List| + Default: `[]` + + This option can be set to a list of |String| values for "words" to exclude + from completion results, as in the words for |complete-items|. The strings + will be matched exactly in a case-sensitive manner. (|==#|) + + This setting can be configured in ftplugin files with buffer variables, so + that different lists can be used for different filetypes. For example: > + + " In ~/.vim/ftplugin/typescript.vim + + " Don't suggest `it` or `describe` so we can use snippets for those words. + let b:ale_completion_excluded_words = ['it', 'describe'] +< + g:ale_completion_max_suggestions *g:ale_completion_max_suggestions* Type: |Number| diff --git a/test/completion/test_completion_filtering.vader b/test/completion/test_completion_filtering.vader index 3e461aef..ae91a952 100644 --- a/test/completion/test_completion_filtering.vader +++ b/test/completion/test_completion_filtering.vader @@ -1,15 +1,27 @@ +Before: + Save g:ale_completion_excluded_words + + let g:ale_completion_excluded_words = [] + +After: + Restore + + unlet! b:ale_completion_excluded_words + unlet! b:suggestions + Execute(Prefix filtering should work for Lists of strings): AssertEqual \ ['FooBar', 'foo'], - \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], 'foo') + \ ale#completion#Filter(bufnr(''), ['FooBar', 'FongBar', 'baz', 'foo'], 'foo') AssertEqual \ ['FooBar', 'FongBar', 'baz', 'foo'], - \ ale#completion#Filter(['FooBar', 'FongBar', 'baz', 'foo'], '.') + \ ale#completion#Filter(bufnr(''), ['FooBar', 'FongBar', 'baz', 'foo'], '.') Execute(Prefix filtering should work for completion items): AssertEqual \ [{'word': 'FooBar'}, {'word': 'foo'}], \ ale#completion#Filter( + \ bufnr(''), \ [ \ {'word': 'FooBar'}, \ {'word': 'FongBar'}, @@ -18,6 +30,7 @@ Execute(Prefix filtering should work for completion items): \ ], \ 'foo' \ ) + AssertEqual \ [ \ {'word': 'FooBar'}, @@ -26,6 +39,7 @@ Execute(Prefix filtering should work for completion items): \ {'word': 'foo'}, \ ], \ ale#completion#Filter( + \ bufnr(''), \ [ \ {'word': 'FooBar'}, \ {'word': 'FongBar'}, @@ -34,3 +48,61 @@ Execute(Prefix filtering should work for completion items): \ ], \ '.' \ ) + +Execute(Excluding words from completion results should work): + let b:ale_completion_excluded_words = ['it', 'describe'] + + AssertEqual + \ [{'word': 'Italian'}], + \ ale#completion#Filter( + \ bufnr(''), + \ [ + \ {'word': 'Italian'}, + \ {'word': 'it'}, + \ ], + \ 'it' + \ ) + + AssertEqual + \ [{'word': 'Deutsch'}], + \ ale#completion#Filter( + \ bufnr(''), + \ [ + \ {'word': 'describe'}, + \ {'word': 'Deutsch'}, + \ ], + \ 'de' + \ ) + + AssertEqual + \ [{'word': 'Deutsch'}], + \ ale#completion#Filter( + \ bufnr(''), + \ [ + \ {'word': 'describe'}, + \ {'word': 'Deutsch'}, + \ ], + \ '.' + \ ) + +Execute(Excluding words from completion results should work with lists of Strings): + let b:ale_completion_excluded_words = ['it', 'describe'] + + AssertEqual + \ ['Italian'], + \ ale#completion#Filter(bufnr(''), ['Italian', 'it'], 'it') + AssertEqual + \ ['Deutsch'], + \ ale#completion#Filter(bufnr(''), ['describe', 'Deutsch'], 'de') + AssertEqual + \ ['Deutsch'], + \ ale#completion#Filter(bufnr(''), ['describe', 'Deutsch'], '.') + +Execute(Filtering shouldn't modify the original list): + let b:ale_completion_excluded_words = ['it', 'describe'] + let b:suggestions = [{'word': 'describe'}] + + AssertEqual [], ale#completion#Filter(bufnr(''), b:suggestions, '.') + AssertEqual b:suggestions, [{'word': 'describe'}] + AssertEqual [], ale#completion#Filter(bufnr(''), b:suggestions, 'de') + AssertEqual b:suggestions, [{'word': 'describe'}]