dense-analysis / ale

Check syntax in Vim/Neovim asynchronously and fix files, with Language Server Protocol (LSP) support
BSD 2-Clause "Simplified" License
13.55k stars 1.44k forks source link

Add a command for manually triggering completion #1830

Closed nerdrew closed 5 years ago

nerdrew commented 6 years ago

I can't find a way to manually trigger completion, i.e. via my own shortcut. I only see the timer based completions? If there isn't a way to manually trigger completion, can I request the feature?

I'm thinking of something like this commit which would allow me to use "tab" to trigger completion.

w0rp commented 6 years ago

Not at the moment. I might add a command for it eventually, or someone else could give it a go. It shouldn't be necessary for the g:ale_completion_enabled setting to be on.

etiennecrb commented 6 years ago

This feature would be amazing! Maybe another way of doing it is to expose a function that satisfies the complete-functions requirements (see help).

w0rp commented 6 years ago

We can't output a function to be used with omnifunc, as Vim only supports searching for completion results synchronously. Instead, we can do what the feature for searching for completion results as you type does now, which is make a request for completion results, get the response from the language server, temporarily replace omnifunc, show the results, and reset omnifunc after CompleteDone. The command will just call the same function that is called after a delay as you type.

w0rp commented 6 years ago

I recently realised that we may also be able to offer an omnifunc function that just returns nothing and executes the rest of the logic as usual, before being replaced with the temporary function. It might work, it might not.

nerdrew commented 6 years ago

I'm currently testing a branch that tries to do this. It doesn't give a temp omnifunc. It instead just skips the C-X C-O call and then waits for you to execute it manually. It is a little hacky, and the completions aren't always there... as I said, I'm trying it for a bit to see if it works for me.

phcerdan commented 6 years ago

Great, this is a must! There are servers/languages/codebases that are not as fast to allow the current "query as you type". I got slow/inconsistent/freezing behaviour when typing just a letter using the lsp cquery with a considerable indexed database.

w0rp commented 6 years ago

Another area where this is also useful is when you are using completion suggestions, but where the completions have to be triggered manually, and not by typing. For example:

const x: T = {
  // <- Cursor rests here
}

In a position like this, manually requesting completion will allow you to select a key to input in an object. As you input more keys in TypeScript, the completion results narrow to suggest only the remaining keys you have not provided values for.

dakom commented 5 years ago

As it is right now - how do you trigger the autocompletion in the foo.bar case where we just type foo.?

Currently I find myself needing to type b and only then does ALE kick in. Would be great if it gave suggestions immediately after .

w0rp commented 5 years ago

It'll depend on what the language server does. For tsserver just foo. works for me.

dakom commented 5 years ago

Hmmm I'm also trying in typescript. Here's my current ALE config: https://github.com/dakom/dotfiles/blob/5f23fd69220e5d230a5ebbc3f397249c074fec45/.config/nvim/init.vim#L209

dakom commented 5 years ago

Hmmm - actually I can get it to work, but then sometimes it doesn't. Here's a reproducable situation where it doesn't:

  1. foo. (let's assume it works)
  2. cancel the auto complete
  3. delete the . (now just foo)
  4. foo. - no longer works
w0rp commented 5 years ago

That's probably an oddity of how tsserver works, but might be a bug I don't know about. I haven't noticed many issues myself, in my regular usage of tsserver. You can try adjusting your g:ale_completion_delay value, but I have left mine at the default value of 100ms.

dakom commented 5 years ago

OK. Thanks for taking a look!

jeremija commented 5 years ago

I was able to create a hotkey to trigger completion manually without modifying Ale's source:

inoremap <silent><C-a> <C-\><C-O>:call ale#completion#GetCompletions()<CR>

It shouldn't be necessary for the g:ale_completion_enabled setting to be on.

FWIW, this seems to be working for me:

function! GetCompletions() abort
    let l:completion_enabled = get(g:, 'ale_completion_enabled', 0)
    if !l:completion_enabled
        call ale#completion#Enable()
    endif
    call ale#completion#GetCompletions()
    if !l:completion_enabled
        call ale#completion#Disable()
    endif
endfunction

inoremap <silent><C-a> <C-\><C-O>:call GetCompletions()<CR>
w0rp commented 5 years ago

@jeremija I wouldn't rely on that, as some of those functions aren't part of the public API, but someone will implement something for this eventually.

jeremija commented 5 years ago

@w0rp Thanks for the feedback! Unless somebody else is working on this, I'd like to contribute. I understand that calling ale#completion#Disable and ale#completion#Enable is probably not the best way to do it and was thinking of creating a new function that performs completion and does not check for g:ale_completion_enabled. What would be the preferred way of making that function a part of the public API? Create an :ALEComplete command?

EDIT There is no need to call ale#competion#Enable and ale#completion#Disable:

function! GetCompletions() abort
  let l:completion_enabled = get(g:, 'ale_completion_enabled', 0)
  if !l:completion_enabled
    let g:ale_completion_enabled = 1
  endif
  call ale#completion#GetCompletions()
  if !l:completion_enabled
    let g:ale_completion_enabled = 0
  endif
endfunction

inoremap <silent><C-a> <C-\><C-O>:call GetCompletions()<CR>

Alternatively, a function could be added which wouldn't check for g:ale_completion_enabled:

diff --git a/autoload/ale/completion.vim b/autoload/ale/completion.vim
index 9dd913f5..69f1e3bb 100644
--- a/autoload/ale/completion.vim
+++ b/autoload/ale/completion.vim
@@ -509,6 +509,10 @@ function! ale#completion#GetCompletions() abort
         return
     endif

+    call ale#completion#AlwaysGetCompletions()
+endfunction
+
+function ale#completion#AlwaysGetCompletions() abort
     let [l:line, l:column] = getcurpos()[1:2]

     let l:prefix = ale#completion#GetPrefix(&filetype, l:line, l:column)

The command mapping could then be:

inoremap <silent><C-Space> <C-\><C-O>:call ale#completion#AlwaysGetCompletions()<CR>
w0rp commented 5 years ago

Thanks. The public part of the API should be a command, and the function should be private and undocumented.