From e5ea809094fd1d521ac88516f5b4b6870e656f3a Mon Sep 17 00:00:00 2001 From: w0rp Date: Fri, 17 May 2019 00:57:52 +0100 Subject: [PATCH] Close #2285 - Add a function for use with omnifunc --- README.md | 7 ++ autoload/ale/completion.vim | 40 +++++++++-- doc/ale.txt | 13 ++++ test/completion/test_completion_events.vader | 14 ++-- .../completion/test_omnifunc_completion.vader | 70 +++++++++++++++++++ 5 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 test/completion/test_omnifunc_completion.vader diff --git a/README.md b/README.md index 277d8ee1..84439b71 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,13 @@ other plugins, and can be enabled by changing a setting before ALE is loaded. let g:ale_completion_enabled = 1 ``` +ALE provides an omni-completion function you can use for triggering +completion manually with ``. + +```vim +set omnifunc=ale#completion#OmniFunc +``` + See `:help ale-completion` for more information. diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim index 03cc6471..9a8b76c7 100644 --- a/autoload/ale/completion.vim +++ b/autoload/ale/completion.vim @@ -169,7 +169,7 @@ function! s:ReplaceCompletionOptions() abort let b:ale_old_omnifunc = &l:omnifunc endif - let &l:omnifunc = 'ale#completion#OmniFunc' + let &l:omnifunc = 'ale#completion#AutomaticOmniFunc' endif if l:source is# 'ale-automatic' @@ -235,7 +235,7 @@ function! ale#completion#GetCompletionResult() abort return v:null endfunction -function! ale#completion#OmniFunc(findstart, base) abort +function! ale#completion#AutomaticOmniFunc(findstart, base) abort if a:findstart return ale#completion#GetCompletionPosition() else @@ -279,6 +279,7 @@ function! s:CompletionStillValid(request_id) abort \&& ( \ b:ale_completion_info.column == l:column \ || b:ale_completion_info.source is# 'deoplete' + \ || b:ale_completion_info.source is# 'ale-omnifunc' \) endfunction @@ -570,7 +571,7 @@ function! ale#completion#GetCompletions(source) abort let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column) if a:source is# 'ale-automatic' && empty(l:prefix) - return + return 0 endif let l:line_length = len(getline('.')) @@ -584,16 +585,47 @@ function! ale#completion#GetCompletions(source) abort \ 'request_id': 0, \ 'source': a:source, \} + unlet! b:ale_completion_response + unlet! b:ale_completion_parser unlet! b:ale_completion_result let l:buffer = bufnr('') let l:Callback = function('s:OnReady') + let l:started = 0 + for l:linter in ale#linter#Get(&filetype) if !empty(l:linter.lsp) - call ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) + if ale#lsp_linter#StartLSP(l:buffer, l:linter, l:Callback) + let l:started = 1 + endif endif endfor + + return l:started +endfunction + +function! ale#completion#OmniFunc(findstart, base) abort + if a:findstart + let l:started = ale#completion#GetCompletions('ale-omnifunc') + + if !l:started + " This is the special value for cancelling completions silently. + " See :help complete-functions + return -3 + endif + + return ale#completion#GetCompletionPosition() + else + let l:result = ale#completion#GetCompletionResult() + + while l:result is v:null && !complete_check() + sleep 2ms + let l:result = ale#completion#GetCompletionResult() + endwhile + + return l:result isnot v:null ? l:result : [] + endif endfunction function! s:TimerHandler(...) abort diff --git a/doc/ale.txt b/doc/ale.txt index e445e914..7e6ac443 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -342,6 +342,12 @@ is loaded. The delay for completion can be configured with |g:ale_completion_delay|. This setting should not be enabled if you wish to use ALE as a completion source for other plugins. +ALE provides an 'omnifunc' function |ale#completion#OmniFunc| for triggering +completion manually with CTRL-X CTRL-O. |i_CTRL-X_CTRL-O| > + + " Use ALE's function for omnicompletion. + set omnifunc=ale#completion#OmniFunc +< ALE will only suggest so many possible matches for completion. The maximum number of items can be controlled with |g:ale_completion_max_suggestions|. @@ -2790,6 +2796,13 @@ ale#command#ManageFile(buffer, filename) *ale#command#ManageFile()* manages directories separately with the |ale#command#ManageDirectory| function. +ale#completion#OmniFunc(findstart, base) *ale#completion#OmniFunc()* + + A completion function to use with 'omnifunc'. + + See |ale-completion|. + + ale#engine#GetLoclist(buffer) *ale#engine#GetLoclist()* Given a buffer number, this function will return the list of problems diff --git a/test/completion/test_completion_events.vader b/test/completion/test_completion_events.vader index cdd3fea4..ebdcb5df 100644 --- a/test/completion/test_completion_events.vader +++ b/test/completion/test_completion_events.vader @@ -139,7 +139,7 @@ Execute(ale#completion#Show() should remember the omnifunc setting and replace i call ale#completion#Show('Response', 'Parser') AssertEqual 'FooBar', b:ale_old_omnifunc - AssertEqual 'ale#completion#OmniFunc', &l:omnifunc + AssertEqual 'ale#completion#AutomaticOmniFunc', &l:omnifunc AssertEqual [], g:feedkeys_calls sleep 1ms @@ -184,20 +184,20 @@ Execute(ale#completion#Show() should not replace the completeopt setting for man sleep 1ms AssertEqual [["\(ale_show_completion_menu)"]], g:feedkeys_calls -Execute(ale#completion#OmniFunc() should also remember the completeopt setting and replace it): +Execute(ale#completion#AutomaticOmniFunc() should also remember the completeopt setting and replace it): let &l:completeopt = 'menu' let b:ale_completion_info = {'source': 'ale-automatic'} - call ale#completion#OmniFunc(0, '') + call ale#completion#AutomaticOmniFunc(0, '') AssertEqual 'menu', b:ale_old_completeopt AssertEqual 'menu,menuone,noselect,noinsert', &l:completeopt -Execute(ale#completion#OmniFunc() should set the preview option if it's set): +Execute(ale#completion#AutomaticOmniFunc() should set the preview option if it's set): let &l:completeopt = 'menu,preview' let b:ale_completion_info = {'source': 'ale-automatic'} - call ale#completion#OmniFunc(0, '') + call ale#completion#AutomaticOmniFunc(0, '') AssertEqual 'menu,preview', b:ale_old_completeopt AssertEqual 'menu,menuone,preview,noselect,noinsert', &l:completeopt @@ -317,6 +317,8 @@ Execute(b:ale_completion_info should be set up correctly when requesting complet Execute(b:ale_completion_info should be set up correctly for other sources): let b:ale_completion_result = [] + let b:ale_completion_response = [] + let b:ale_completion_parser = 'type' call setpos('.', [bufnr(''), 3, 14, 0]) call ale#completion#GetCompletions('deoplete') @@ -332,6 +334,8 @@ Execute(b:ale_completion_info should be set up correctly for other sources): \ }, \ b:ale_completion_info Assert !exists('b:ale_completion_result') + Assert !exists('b:ale_completion_response') + Assert !exists('b:ale_completion_parser') Execute(The correct keybinds should be configured): redir => g:output diff --git a/test/completion/test_omnifunc_completion.vader b/test/completion/test_omnifunc_completion.vader new file mode 100644 index 00000000..221d337f --- /dev/null +++ b/test/completion/test_omnifunc_completion.vader @@ -0,0 +1,70 @@ +Before: + unlet! b:ale_completion_info + unlet! b:ale_completion_response + unlet! b:ale_completion_parser + unlet! b:ale_completion_result + + let b:lsp_started = 0 + + runtime autoload/ale/lsp_linter.vim + + function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort + return b:lsp_started + endfunction + + function! Identity(x) abort + return a:x + endfunction + + function! SetCompletionResult(...) abort + let b:ale_completion_result = ['foo'] + endfunction + + function! SetCompletionResponse(...) abort + let b:ale_completion_response = ['foo'] + let b:ale_completion_parser = 'Identity' + endfunction + +After: + unlet! b:ale_completion_info + unlet! b:ale_completion_response + unlet! b:ale_completion_parser + unlet! b:ale_completion_result + unlet! b:lsp_started + + delfunction Identity + delfunction SetCompletionResult + delfunction SetCompletionResponse + + runtime autoload/ale/lsp_linter.vim + + call ale#linter#Reset() + +Given typescript(): + let abc = y. + let foo = ab + let foo = (ab) + +Execute(-3 should be returned when completion results cannot be requested): + AssertEqual -3, ale#completion#OmniFunc(1, '') + +Execute(The start position should be returned when results can be requested): + let b:lsp_started = 1 + call setpos('.', [bufnr(''), 3, 14, 0]) + + AssertEqual 11, ale#completion#OmniFunc(1, '') + +Execute(The omnifunc function should return async results): + " Neovim 0.2.0 struggles at running these tests. + if !has('nvim') || has('nvim-0.3.0') + call timer_start(0, function('SetCompletionResult')) + + AssertEqual ['foo'], ale#completion#OmniFunc(0, '') + endif + +Execute(The omnifunc function should parse and return async responses): + if !has('nvim') || has('nvim-0.3.0') + call timer_start(0, function('SetCompletionResponse')) + + AssertEqual ['foo'], ale#completion#OmniFunc(0, '') + endif