From 73b568b071ed723bae24fa3a4893e1579e5028fa Mon Sep 17 00:00:00 2001 From: w0rp Date: Tue, 18 Mar 2025 14:05:58 +0000 Subject: [PATCH] Supply language_id values to Neovim LSP API Change logic so ALE's LSP implementation and the Neovim LSP client retrieve the language_id for language clients at roughly the same time via the same means. This makes ALE inform the language server what the language for the language is for clients. --- autoload/ale/assert.vim | 5 +++- autoload/ale/linter.vim | 6 ----- autoload/ale/lsp.vim | 22 +++++++++++++--- autoload/ale/lsp_linter.vim | 17 +++++++++--- lua/ale/lsp.lua | 4 +++ .../test_lsp_completion_messages.vader | 2 +- test/lsp/test_closing_documents.vader | 26 +++++++++---------- test/lsp/test_did_save_event.vader | 2 +- test/lsp/test_lsp_custom_request.vader | 2 +- test/lsp/test_lsp_startup.vader | 2 +- ...st_other_initialize_message_handling.vader | 2 +- test/lsp/test_update_config.vader | 2 +- test/test_codefix.vader | 2 +- test/test_filerename.vader | 2 +- test/test_find_references.vader | 2 +- test/test_go_to_definition.vader | 2 +- test/test_organize_imports.vader | 2 +- test/test_rename.vader | 2 +- test/test_symbol_search.vader | 2 +- 19 files changed, 65 insertions(+), 41 deletions(-) diff --git a/autoload/ale/assert.vim b/autoload/ale/assert.vim index 141cd0f2..c5157dba 100644 --- a/autoload/ale/assert.vim +++ b/autoload/ale/assert.vim @@ -205,7 +205,10 @@ endfunction function! ale#assert#LSPLanguage(expected_language) abort let l:buffer = bufnr('') let l:linter = s:GetLinter() - let l:language = ale#linter#GetLanguage(l:buffer, l:linter) + let l:Language = l:linter.language + let l:language = type(l:Language) is v:t_func + \ ? l:Language(l:buffer) + \ : l:Language AssertEqual a:expected_language, l:language endfunction diff --git a/autoload/ale/linter.vim b/autoload/ale/linter.vim index 618557d7..021516c7 100644 --- a/autoload/ale/linter.vim +++ b/autoload/ale/linter.vim @@ -446,9 +446,3 @@ function! ale#linter#GetAddress(buffer, linter) abort return type(l:Address) is v:t_func ? l:Address(a:buffer) : l:Address endfunction - -function! ale#linter#GetLanguage(buffer, linter) abort - let l:Language = a:linter.language - - return type(l:Language) is v:t_func ? l:Language(a:buffer) : l:Language -endfunction diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim index 1206818e..fcc16069 100644 --- a/autoload/ale/lsp.vim +++ b/autoload/ale/lsp.vim @@ -5,9 +5,10 @@ let s:connections = get(s:, 'connections', {}) let g:ale_lsp_next_message_id = 1 -" Given an id, which can be an executable or address, and a project path, +" Given an id, which can be an executable or address, a project path, +" and a language string or (bufnr) -> string function " create a new connection if needed. Return a unique ID for the connection. -function! ale#lsp#Register(executable_or_address, project, init_options) abort +function! ale#lsp#Register(executable_or_address, project, language, init_options) abort let l:conn_id = a:executable_or_address . ':' . a:project if !has_key(s:connections, l:conn_id) @@ -28,6 +29,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort \ 'is_tsserver': 0, \ 'data': '', \ 'root': a:project, + \ 'language': a:language, \ 'open_documents': {}, \ 'initialized': 0, \ 'init_request_id': 0, @@ -677,9 +679,20 @@ function! ale#lsp#Send(conn_id, message) abort return l:id == 0 ? -1 : l:id endfunction +function! ale#lsp#GetLanguage(conn_id, buffer) abort + let l:conn = get(s:connections, a:conn_id, {}) + let l:Language = get(l:conn, 'language') + + if empty(l:Language) + return getbufvar(a:buffer, '&filetype') + endif + + return type(l:Language) is v:t_func ? l:Language(a:buffer) : l:Language +endfunction + " Notify LSP servers or tsserver if a document is opened, if needed. " If a document is opened, 1 will be returned, otherwise 0 will be returned. -function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort +function! ale#lsp#OpenDocument(conn_id, buffer) abort let l:conn = get(s:connections, a:conn_id, {}) let l:opened = 0 @@ -693,7 +706,8 @@ function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort \ 'client_id': l:conn.client_id, \}) else - let l:message = ale#lsp#message#DidOpen(a:buffer, a:language_id) + let l:language_id = ale#lsp#GetLanguage(a:conn_id, a:buffer) + let l:message = ale#lsp#message#DidOpen(a:buffer, l:language_id) call ale#lsp#Send(a:conn_id, l:message) endif diff --git a/autoload/ale/lsp_linter.vim b/autoload/ale/lsp_linter.vim index dcc028b4..ab45e411 100644 --- a/autoload/ale/lsp_linter.vim +++ b/autoload/ale/lsp_linter.vim @@ -306,11 +306,10 @@ function! ale#lsp_linter#OnInit(linter, details, Callback) abort let l:command = a:details.command let l:config = ale#lsp_linter#GetConfig(l:buffer, a:linter) - let l:language_id = ale#linter#GetLanguage(l:buffer, a:linter) call ale#lsp#UpdateConfig(l:conn_id, l:buffer, l:config) - if ale#lsp#OpenDocument(l:conn_id, l:buffer, l:language_id) + if ale#lsp#OpenDocument(l:conn_id, l:buffer) if g:ale_history_enabled && !empty(l:command) call ale#history#Add(l:buffer, 'started', l:conn_id, l:command) endif @@ -357,11 +356,21 @@ function! s:StartLSP(options, address, executable, command) abort let l:init_options = ale#lsp_linter#GetOptions(l:buffer, l:linter) if l:linter.lsp is# 'socket' - let l:conn_id = ale#lsp#Register(a:address, l:root, l:init_options) + let l:conn_id = ale#lsp#Register( + \ a:address, + \ l:root, + \ l:linter.language, + \ l:init_options + \) let l:ready = ale#lsp#ConnectToAddress(l:conn_id, a:address) let l:command = '' else - let l:conn_id = ale#lsp#Register(a:executable, l:root, l:init_options) + let l:conn_id = ale#lsp#Register( + \ a:executable, + \ l:root, + \ l:linter.language, + \ l:init_options + \) " tsserver behaves differently, so tell the LSP API that it is tsserver. if l:linter.lsp is# 'tsserver' diff --git a/lua/ale/lsp.lua b/lua/ale/lsp.lua index 2c58ec72..f45f4598 100644 --- a/lua/ale/lsp.lua +++ b/lua/ale/lsp.lua @@ -39,6 +39,10 @@ module.start = function(config) end, 0) end + config.get_language_id = function(bufnr, _) + return vim.fn["ale#lsp#GetLanguage"](config.name, bufnr) + end + return vim.lsp.start(config, { attach = false, silent = true, diff --git a/test/completion/test_lsp_completion_messages.vader b/test/completion/test_lsp_completion_messages.vader index f6aa332b..b0d0c120 100644 --- a/test/completion/test_lsp_completion_messages.vader +++ b/test/completion/test_lsp_completion_messages.vader @@ -19,7 +19,7 @@ Before: let g:init_callback_list = [] function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) let l:details = { diff --git a/test/lsp/test_closing_documents.vader b/test/lsp/test_closing_documents.vader index b9f2f824..29377768 100644 --- a/test/lsp/test_closing_documents.vader +++ b/test/lsp/test_closing_documents.vader @@ -37,24 +37,24 @@ After: runtime autoload/ale/lsp.vim Execute(No errors should be thrown if the connection is not initialized): - call ale#lsp#Register('command', '/foo', {}) + call ale#lsp#Register('command', '/foo', '', {}) call MarkDocumentOpened() call ale#engine#Cleanup(bufnr('')) AssertEqual [], g:message_list Execute(No messages should be sent if the document wasn't opened): - call ale#lsp#Register('command', '/foo', {}) + call ale#lsp#Register('command', '/foo', '', {}) call MarkAllConnectionsInitialized() call ale#engine#Cleanup(bufnr('')) AssertEqual [], g:message_list Execute(A message should be sent if the document was opened): - call ale#lsp#Register('command', '/foo', {}) + call ale#lsp#Register('command', '/foo', 'lang', {}) call MarkAllConnectionsInitialized() - call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang') + call ale#lsp#OpenDocument('command:/foo', bufnr('')) call ale#engine#Cleanup(bufnr('')) " We should only send the message once. call ale#engine#Cleanup(bufnr('')) @@ -78,10 +78,10 @@ Execute(A message should be sent if the document was opened): \ g:message_list Execute(A message should be sent if the document was opened for tsserver): - call ale#lsp#Register('command', '/foo', {}) + call ale#lsp#Register('command', '/foo', 'lang', {}) call ale#lsp#MarkConnectionAsTsserver('command:/foo') - call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang') + call ale#lsp#OpenDocument('command:/foo', bufnr('')) call ale#engine#Cleanup(bufnr('')) " We should only send the message once. call ale#engine#Cleanup(bufnr('')) @@ -94,12 +94,12 @@ Execute(A message should be sent if the document was opened for tsserver): \ g:message_list Execute(Re-opening and closing the documents should work): - call ale#lsp#Register('command', '/foo', {}) + call ale#lsp#Register('command', '/foo', 'lang', {}) call MarkAllConnectionsInitialized() - call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang') + call ale#lsp#OpenDocument('command:/foo', bufnr('')) call ale#engine#Cleanup(bufnr('')) - call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang') + call ale#lsp#OpenDocument('command:/foo', bufnr('')) call ale#engine#Cleanup(bufnr('')) AssertEqual @@ -134,12 +134,12 @@ Execute(Re-opening and closing the documents should work): \ g:message_list Execute(Messages for closing documents should be sent to each server): - call ale#lsp#Register('command', '/foo', {}) - call ale#lsp#Register('command', '/bar', {}) + call ale#lsp#Register('command', '/foo', 'lang', {}) + call ale#lsp#Register('command', '/bar', 'lang', {}) call MarkAllConnectionsInitialized() - call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang') - call ale#lsp#OpenDocument('command:/bar', bufnr(''), 'lang') + call ale#lsp#OpenDocument('command:/foo', bufnr('')) + call ale#lsp#OpenDocument('command:/bar', bufnr('')) call ale#engine#Cleanup(bufnr('')) " We should only send the message once. call ale#engine#Cleanup(bufnr('')) diff --git a/test/lsp/test_did_save_event.vader b/test/lsp/test_did_save_event.vader index fc73c4d6..13963451 100644 --- a/test/lsp/test_did_save_event.vader +++ b/test/lsp/test_did_save_event.vader @@ -39,7 +39,7 @@ Before: let g:ale_linters = {'foobar': ['dummy_linter']} function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', 'foobar', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) let l:details = { \ 'command': 'foobar', diff --git a/test/lsp/test_lsp_custom_request.vader b/test/lsp/test_lsp_custom_request.vader index c8767e59..6a864cef 100644 --- a/test/lsp/test_lsp_custom_request.vader +++ b/test/lsp/test_lsp_custom_request.vader @@ -39,7 +39,7 @@ Before: " Replace the StartLSP function to mock an LSP linter function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register(g:executable_or_address, g:project_root, {}) + let g:conn_id = ale#lsp#Register(g:executable_or_address, g:project_root, '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) call ale#lsp#HandleMessage(g:conn_id, Encode({'method': 'initialize'})) diff --git a/test/lsp/test_lsp_startup.vader b/test/lsp/test_lsp_startup.vader index 5dd6ee9a..f7407437 100644 --- a/test/lsp/test_lsp_startup.vader +++ b/test/lsp/test_lsp_startup.vader @@ -441,7 +441,7 @@ Execute(Deferred addresses should be handled correctly): call AssertInitSuccess('foo', 'localhost:1234', 'foobar', '/foo/bar', '', bufnr('')) Execute(Servers that have crashed should be restarted): - call ale#lsp#Register('foo', '/foo/bar', {}) + call ale#lsp#Register('foo', '/foo/bar', '', {}) call extend(ale#lsp#GetConnections()['foo:/foo/bar'], {'initialized': 1}) " Starting the program again should reset initialized to `0`. diff --git a/test/lsp/test_other_initialize_message_handling.vader b/test/lsp/test_other_initialize_message_handling.vader index 46352f5f..b86aa1fa 100644 --- a/test/lsp/test_other_initialize_message_handling.vader +++ b/test/lsp/test_other_initialize_message_handling.vader @@ -4,7 +4,7 @@ Before: let g:message_list = [] " Register a fake connection and get it for tests. - call ale#lsp#Register('ale-fake-lsp-server', '/code', {}) + call ale#lsp#Register('ale-fake-lsp-server', '/code', '', {}) let b:conn = ale#lsp#GetConnections()['ale-fake-lsp-server:/code'] function! ale#lsp#Send(conn_id, message) abort diff --git a/test/lsp/test_update_config.vader b/test/lsp/test_update_config.vader index 698477ec..2a2c85e6 100644 --- a/test/lsp/test_update_config.vader +++ b/test/lsp/test_update_config.vader @@ -1,7 +1,7 @@ Before: runtime autoload/ale/lsp.vim - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) " Stub out this function, so we test updating configs. function! ale#lsp#Send(conn_id, message) abort diff --git a/test/test_codefix.vader b/test/test_codefix.vader index 2d6d215b..b56f2381 100644 --- a/test/test_codefix.vader +++ b/test/test_codefix.vader @@ -23,7 +23,7 @@ Before: runtime autoload/ale/code_action.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_filerename.vader b/test/test_filerename.vader index c91b3556..1fc655c3 100644 --- a/test/test_filerename.vader +++ b/test/test_filerename.vader @@ -20,7 +20,7 @@ Before: runtime autoload/ale/code_action.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_find_references.vader b/test/test_find_references.vader index 01c15469..ad462394 100644 --- a/test/test_find_references.vader +++ b/test/test_find_references.vader @@ -22,7 +22,7 @@ Before: runtime autoload/ale/preview.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_go_to_definition.vader b/test/test_go_to_definition.vader index 2290054a..6a42bdcb 100644 --- a/test/test_go_to_definition.vader +++ b/test/test_go_to_definition.vader @@ -19,7 +19,7 @@ Before: runtime autoload/ale/util.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_organize_imports.vader b/test/test_organize_imports.vader index 87a7b7c1..64307029 100644 --- a/test/test_organize_imports.vader +++ b/test/test_organize_imports.vader @@ -20,7 +20,7 @@ Before: runtime autoload/ale/code_action.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_rename.vader b/test/test_rename.vader index a4d7e1b3..ec255bb3 100644 --- a/test/test_rename.vader +++ b/test/test_rename.vader @@ -20,7 +20,7 @@ Before: runtime autoload/ale/code_action.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) if a:linter.lsp is# 'tsserver' diff --git a/test/test_symbol_search.vader b/test/test_symbol_search.vader index 754826aa..6dc2fb3e 100644 --- a/test/test_symbol_search.vader +++ b/test/test_symbol_search.vader @@ -18,7 +18,7 @@ Before: runtime autoload/ale/preview.vim function! ale#lsp_linter#StartLSP(buffer, linter, Callback) abort - let g:conn_id = ale#lsp#Register('executable', '/foo/bar', {}) + let g:conn_id = ale#lsp#Register('executable', '/foo/bar', '', {}) call ale#lsp#MarkDocumentAsOpen(g:conn_id, a:buffer) let l:details = { \ 'buffer': a:buffer,