Read trigger characters from LSP initialize responses (#5121)
CI / Build (push) Has been cancelled
CI / Neovim 0.10 Windows (push) Has been cancelled
CI / Neovim 0.12 Windows (push) Has been cancelled
CI / Vim 8.2 Windows (push) Has been cancelled
CI / Vim 9.2 Windows (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Lua (push) Has been cancelled
CI / Neovim 0.10 Linux (push) Has been cancelled
CI / Neovim 0.12 Linux (push) Has been cancelled
CI / Vim 8.2 Linux (push) Has been cancelled
CI / Vim 9.2 Linux (push) Has been cancelled

* Add ale#lsp#GetCompletionTriggerCharacters getter
* Add s:GetTriggerCharacters helper with LSP support
* Pass connection ID to GetTriggerCharacter in s:OnReady
* Use LSP trigger characters in Filter function
* Add tests for LSP completion trigger characters
* Check LSP trigger characters in GetPrefix
* Add tests for GetAllCompletionTriggerCharactersForBuffer

GetTriggerCharacter now accepts optional conn_id parameter to
prefer LSP-provided trigger characters over the hardcoded map.

GetPrefix now checks if the line ends with any trigger character
from LSPs active for the current buffer, enabling automatic
completion for LSP-provided triggers like > for PHP.
This commit is contained in:
Eric Stern
2026-05-31 17:59:23 +01:00
committed by GitHub
parent 7a7fc85e51
commit 36d541facb
4 changed files with 173 additions and 5 deletions
@@ -140,3 +140,61 @@ Execute(Filtering should respect filetype triggers):
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), '', b:suggestions, '.', 0)
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '.', 0)
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'rust', b:suggestions, '::', 0)
Execute(GetTriggerCharacter should return trigger characters from hardcoded map):
AssertEqual '.', ale#completion#GetTriggerCharacter('python', '.')
AssertEqual '::', ale#completion#GetTriggerCharacter('rust', '::')
AssertEqual '->', ale#completion#GetTriggerCharacter('c', '->')
AssertEqual '', ale#completion#GetTriggerCharacter('python', '@')
Execute(GetTriggerCharacter should return empty for empty prefix):
AssertEqual '', ale#completion#GetTriggerCharacter('python', '')
Execute(GetTriggerCharacter should use LSP triggers when conn_id provided):
call ale#lsp#Register('test-lsp', '/project', '', {})
let g:conn_id = 'test-lsp:/project'
call ale#lsp#UpdateCapabilities(g:conn_id, {
\ 'completionProvider': {'triggerCharacters': ['@', '#']},
\})
" '@' is in LSP triggers
AssertEqual '@', ale#completion#GetTriggerCharacter('python', '@', g:conn_id)
" '.' is NOT in LSP triggers (should not match even though it's in hardcoded)
AssertEqual '', ale#completion#GetTriggerCharacter('python', '.', g:conn_id)
" '#' is in LSP triggers
AssertEqual '#', ale#completion#GetTriggerCharacter('python', '#', g:conn_id)
call ale#lsp#RemoveConnectionWithID(g:conn_id)
unlet g:conn_id
Execute(GetTriggerCharacter should fall back to hardcoded when no LSP triggers):
call ale#lsp#Register('test-lsp-empty', '/project', '', {})
let g:conn_id = 'test-lsp-empty:/project'
call ale#lsp#UpdateCapabilities(g:conn_id, {})
" Falls back to hardcoded map
AssertEqual '.', ale#completion#GetTriggerCharacter('python', '.', g:conn_id)
AssertEqual '', ale#completion#GetTriggerCharacter('python', '@', g:conn_id)
call ale#lsp#RemoveConnectionWithID(g:conn_id)
unlet g:conn_id
Execute(Filtering should use LSP trigger characters):
call ale#lsp#Register('test-lsp-filter', '/project', '', {})
let g:conn_id = 'test-lsp-filter:/project'
call ale#lsp#UpdateCapabilities(g:conn_id, {
\ 'completionProvider': {'triggerCharacters': ['@']},
\})
" Set up completion info with conn_id
let b:ale_completion_info = {'conn_id': g:conn_id}
let b:suggestions = [{'word': 'foo'}, {'word': 'bar'}]
" '@' is LSP trigger - should return all suggestions
AssertEqual b:suggestions, ale#completion#Filter(bufnr(''), 'python', b:suggestions, '@', 0)
" '.' is NOT LSP trigger - should filter
AssertEqual [], ale#completion#Filter(bufnr(''), 'python', b:suggestions, '.', 0)
unlet b:ale_completion_info
call ale#lsp#RemoveConnectionWithID(g:conn_id)
unlet g:conn_id
@@ -343,3 +343,51 @@ Execute(Results that are not dictionaries should be handled correctly):
\ 'result': v:null,
\})
AssertEqual [], g:message_list
Execute(GetCompletionTriggerCharacters should return stored characters):
call ale#lsp#HandleInitResponse(b:conn, {
\ 'jsonrpc': '2.0',
\ 'id': 1,
\ 'result': {
\ 'capabilities': {
\ 'completionProvider': {
\ 'triggerCharacters': ['@', '#', '.'],
\ },
\ },
\ },
\})
AssertEqual ['@', '#', '.'], ale#lsp#GetCompletionTriggerCharacters(b:conn.id)
Execute(GetCompletionTriggerCharacters should return empty for missing connection):
AssertEqual [], ale#lsp#GetCompletionTriggerCharacters('nonexistent-connection')
Execute(GetCompletionTriggerCharacters should return empty when no triggers):
call ale#lsp#HandleInitResponse(b:conn, {
\ 'jsonrpc': '2.0',
\ 'id': 1,
\ 'result': {
\ 'capabilities': {},
\ },
\})
AssertEqual [], ale#lsp#GetCompletionTriggerCharacters(b:conn.id)
Execute(GetAllCompletionTriggerCharactersForBuffer should return triggers for open buffers):
call ale#lsp#HandleInitResponse(b:conn, {
\ 'jsonrpc': '2.0',
\ 'id': 1,
\ 'result': {
\ 'capabilities': {
\ 'completionProvider': {
\ 'triggerCharacters': ['>', '$'],
\ },
\ },
\ },
\})
call ale#lsp#MarkDocumentAsOpen(b:conn.id, 1)
AssertEqual sort(['>', '$']), sort(ale#lsp#GetAllCompletionTriggerCharactersForBuffer(1))
Execute(GetAllCompletionTriggerCharactersForBuffer should return empty for unknown buffer):
AssertEqual [], ale#lsp#GetAllCompletionTriggerCharactersForBuffer(99999)