Add ALEGoToImplementation (#4160)

* Add go to implementation

* Add test cases for GoToImplementation

* Add documentation for GoToImplementation
This commit is contained in:
godbless 2022-04-30 08:58:26 +05:30 committed by GitHub
parent 57e16957e0
commit d484347fb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 229 additions and 8 deletions

View File

@ -121,6 +121,12 @@ function! s:OnReady(line, column, options, capability, linter, lsp_details) abor
\ a:line,
\ a:column
\)
elseif a:capability is# 'implementation'
let l:message = ale#lsp#tsserver_message#Implementation(
\ l:buffer,
\ a:line,
\ a:column
\)
endif
else
" Send a message saying the buffer has changed first, or the
@ -134,6 +140,8 @@ function! s:OnReady(line, column, options, capability, linter, lsp_details) abor
let l:message = ale#lsp#message#Definition(l:buffer, a:line, a:column)
elseif a:capability is# 'typeDefinition'
let l:message = ale#lsp#message#TypeDefinition(l:buffer, a:line, a:column)
elseif a:capability is# 'implementation'
let l:message = ale#lsp#message#Implementation(l:buffer, a:line, a:column)
else
" XXX: log here?
return
@ -175,6 +183,14 @@ function! ale#definition#GoToType(options) abort
endfor
endfunction
function! ale#definition#GoToImpl(options) abort
for l:linter in ale#linter#Get(&filetype)
if !empty(l:linter.lsp)
call s:GoToLSPDefinition(l:linter, a:options, 'implementation')
endif
endfor
endfunction
function! ale#definition#GoToCommandHandler(command, ...) abort
let l:options = {}
@ -200,6 +216,8 @@ function! ale#definition#GoToCommandHandler(command, ...) abort
if a:command is# 'type'
call ale#definition#GoToType(l:options)
elseif a:command is# 'implementation'
call ale#definition#GoToImpl(l:options)
else
call ale#definition#GoTo(l:options)
endif

View File

@ -44,6 +44,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
\ 'completion_trigger_characters': [],
\ 'definition': 0,
\ 'typeDefinition': 0,
\ 'implementation': 0,
\ 'symbol_search': 0,
\ 'code_actions': 0,
\ 'did_save': 0,
@ -259,6 +260,14 @@ function! s:UpdateCapabilities(conn, capabilities) abort
let a:conn.capabilities.typeDefinition = 1
endif
if get(a:capabilities, 'implementationProvider') is v:true
let a:conn.capabilities.implementation = 1
endif
if type(get(a:capabilities, 'implementationProvider')) is v:t_dict
let a:conn.capabilities.implementation = 1
endif
if get(a:capabilities, 'workspaceSymbolProvider') is v:true
let a:conn.capabilities.symbol_search = 1
endif
@ -379,6 +388,7 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn.capabilities.completion_trigger_characters = ['.']
let l:conn.capabilities.definition = 1
let l:conn.capabilities.typeDefinition = 1
let l:conn.capabilities.implementation = 1
let l:conn.capabilities.symbol_search = 1
let l:conn.capabilities.rename = 1
let l:conn.capabilities.filerename = 1
@ -438,6 +448,10 @@ function! s:SendInitMessage(conn) abort
\ 'typeDefinition': {
\ 'dynamicRegistration': v:false,
\ },
\ 'implementation': {
\ 'dynamicRegistration': v:false,
\ 'linkSupport': v:false,
\ },
\ 'publishDiagnostics': {
\ 'relatedInformation': v:true,
\ },

View File

@ -139,6 +139,15 @@ function! ale#lsp#message#TypeDefinition(buffer, line, column) abort
\}]
endfunction
function! ale#lsp#message#Implementation(buffer, line, column) abort
return [0, 'textDocument/implementation', {
\ 'textDocument': {
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
\ },
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
\}]
endfunction
function! ale#lsp#message#References(buffer, line, column) abort
return [0, 'textDocument/references', {
\ 'textDocument': {

View File

@ -72,6 +72,14 @@ function! ale#lsp#tsserver_message#TypeDefinition(buffer, line, column) abort
\}]
endfunction
function! ale#lsp#tsserver_message#Implementation(buffer, line, column) abort
return [0, 'ts@implementation', {
\ 'line': a:line,
\ 'offset': a:column,
\ 'file': expand('#' . a:buffer . ':p'),
\}]
endfunction
function! ale#lsp#tsserver_message#References(buffer, line, column) abort
return [0, 'ts@references', {
\ 'line': a:line,

View File

@ -17,10 +17,11 @@ CONTENTS *ale-contents*
5.1 Completion........................|ale-completion|
5.2 Go To Definition..................|ale-go-to-definition|
5.3 Go To Type Definition.............|ale-go-to-type-definition|
5.4 Find References...................|ale-find-references|
5.5 Hovering..........................|ale-hover|
5.6 Symbol Search.....................|ale-symbol-search|
5.7 Refactoring: Rename, Actions......|ale-refactor|
5.4 Go To Implementation..............|ale-go-to-type-implementation|
5.5 Find References...................|ale-find-references|
5.6 Hovering..........................|ale-hover|
5.7 Symbol Search.....................|ale-symbol-search|
5.8 Refactoring: Rename, Actions......|ale-refactor|
6. Global Options.......................|ale-options|
6.1 Highlights........................|ale-highlights|
7. Linter/Fixer Options.................|ale-integration-options|
@ -637,14 +638,23 @@ documentation for the command for configuring how the location will be
displayed.
-------------------------------------------------------------------------------
5.4 Find References *ale-find-references*
5.4 Go To Implementation *ale-go-to-implementation*
ALE supports jumping to the files and locations where symbols are implemented
through any enabled LSP linters. The locations ALE will jump to depend on the
information returned by LSP servers. The |ALEGoToImplementation| command will
jump to the implementation of symbols under the cursor. See the documentation
for the command for configuring how the location will be displayed.
-------------------------------------------------------------------------------
5.5 Find References *ale-find-references*
ALE supports finding references for symbols though any enabled LSP linters
with the |ALEFindReferences| command. See the documentation for the command
for a full list of options.
-------------------------------------------------------------------------------
5.5 Hovering *ale-hover*
5.6 Hovering *ale-hover*
ALE supports "hover" information for printing brief information about symbols
at the cursor taken from LSP linters. The following commands are supported:
@ -686,14 +696,14 @@ Documentation for symbols at the cursor can be retrieved using the
|ALEDocumentation| command. This command is only available for `tsserver`.
-------------------------------------------------------------------------------
5.6 Symbol Search *ale-symbol-search*
5.7 Symbol Search *ale-symbol-search*
ALE supports searching for workspace symbols via LSP linters with the
|ALESymbolSearch| command. See the documentation for the command
for a full list of options.
-------------------------------------------------------------------------------
5.7 Refactoring: Rename, Actions *ale-refactor*
5.8 Refactoring: Rename, Actions *ale-refactor*
ALE supports renaming symbols in code such as variables or class names with
the |ALERename| command.
@ -3410,6 +3420,33 @@ ALEGoToTypeDefinition *ALEGoToTypeDefinition*
`<Plug>(ale_go_to_type_definition_in_vsplit)` - `:ALEGoToTypeDefinition -vsplit`
ALEGoToImplementation *ALEGoToImplementation*
This works similar to |ALEGoToDefinition| but instead jumps to the
implementation of symbol under the cursor. ALE will jump to a definition if
an LSP server provides a location to jump to. Otherwise, ALE will do nothing.
The locations opened in different ways using the following variations.
`:ALEGoToImplementation -tab` - Open the location in a new tab.
`:ALEGoToImplementation -split` - Open the location in a horizontal split.
`:ALEGoToImplementation -vsplit` - Open the location in a vertical split.
The default method used for navigating to a new location can be changed
by modifying |g:ale_default_navigation|.
You can jump back to the position you were at before going to the definition
of something with jump motions like CTRL-O. See |jump-motions|.
The following Plug mappings are defined for this command, which correspond
to the following commands.
`<Plug>(ale_go_to_implementation)` - `:ALEGoToImplementation`
`<Plug>(ale_go_to_implementation_in_tab)` - `:ALEGoToImplementation -tab`
`<Plug>(ale_go_to_implementation_in_split)` - `:ALEGoToImplementation -split`
`<Plug>(ale_go_to_implementation_in_vsplit)` - `:ALEGoToImplementation -vsplit`
ALEHover *ALEHover*
Print brief information about the symbol under the cursor, taken from any

View File

@ -252,6 +252,9 @@ command! -bar -nargs=* ALEGoToDefinition :call ale#definition#GoToCommandHandler
" Go to type definition for tsserver and LSP
command! -bar -nargs=* ALEGoToTypeDefinition :call ale#definition#GoToCommandHandler('type', <f-args>)
" Go to implementation for tsserver and LSP
command! -bar -nargs=* ALEGoToImplementation :call ale#definition#GoToCommandHandler('implementation', <f-args>)
" Repeat a previous selection in the preview window
command! -bar ALERepeatSelection :call ale#preview#RepeatSelection()
@ -319,6 +322,9 @@ nnoremap <silent> <Plug>(ale_go_to_type_definition) :ALEGoToTypeDefinition<Retur
nnoremap <silent> <Plug>(ale_go_to_type_definition_in_tab) :ALEGoToTypeDefinition -tab<Return>
nnoremap <silent> <Plug>(ale_go_to_type_definition_in_split) :ALEGoToTypeDefinition -split<Return>
nnoremap <silent> <Plug>(ale_go_to_type_definition_in_vsplit) :ALEGoToTypeDefinitionIn -vsplit<Return>
nnoremap <silent> <Plug>(ale_go_to_implementation_in_tab) :ALEGoToImplementation -tab<Return>
nnoremap <silent> <Plug>(ale_go_to_implementation_in_split) :ALEGoToImplementation -split<Return>
nnoremap <silent> <Plug>(ale_go_to_implementation_in_vsplit) :ALEGoToImplementation -vsplit<Return>
nnoremap <silent> <Plug>(ale_find_references) :ALEFindReferences<Return>
nnoremap <silent> <Plug>(ale_hover) :ALEHover<Return>
nnoremap <silent> <Plug>(ale_documentation) :ALEDocumentation<Return>

View File

@ -175,6 +175,20 @@ Execute(ale#lsp#message#TypeDefinition() should return correct messages):
\ ],
\ ale#lsp#message#TypeDefinition(bufnr(''), 12, 34)
Execute(ale#lsp#message#Implementation() should return correct messages):
AssertEqual
\ [
\ 0,
\ 'textDocument/implementation',
\ {
\ 'textDocument': {
\ 'uri': ale#path#ToFileURI(g:dir . '/foo/bar.ts'),
\ },
\ 'position': {'line': 11, 'character': 33},
\ }
\ ],
\ ale#lsp#message#Implementation(bufnr(''), 12, 34)
Execute(ale#lsp#message#References() should return correct messages):
AssertEqual
\ [
@ -335,6 +349,19 @@ Execute(ale#lsp#tsserver_message#TypeDefinition() should return correct messages
\ ],
\ ale#lsp#tsserver_message#TypeDefinition(bufnr(''), 347, 12)
Execute(ale#lsp#tsserver_message#Implementation() should return correct messages):
AssertEqual
\ [
\ 0,
\ 'ts@implementation',
\ {
\ 'file': ale#path#Simplify(g:dir . '/foo/bar.ts'),
\ 'line': 347,
\ 'offset': 12,
\ }
\ ],
\ ale#lsp#tsserver_message#Implementation(bufnr(''), 347, 12)
Execute(ale#lsp#tsserver_message#References() should return correct messages):
AssertEqual
\ [

View File

@ -189,6 +189,10 @@ Before:
\ 'typeDefinition': {
\ 'dynamicRegistration': v:false,
\ },
\ 'implementation': {
\ 'dynamicRegistration': v:false,
\ 'linkSupport': v:false,
\ },
\ 'publishDiagnostics': {
\ 'relatedInformation': v:true,
\ },

View File

@ -182,6 +182,7 @@ Execute(Capabilities should be enabled when send as Dictionaries):
\ },
\ 'definitionProvider': {},
\ 'typeDefinitionProvider': {},
\ 'implementationProvider': {},
\ 'experimental': {},
\ 'documentHighlightProvider': v:true,
\ 'workspaceSymbolProvider': {}
@ -198,6 +199,7 @@ Execute(Capabilities should be enabled when send as Dictionaries):
\ 'hover': 1,
\ 'definition': 1,
\ 'typeDefinition': 1,
\ 'implementation': 1,
\ 'symbol_search': 1,
\ 'rename': 1,
\ 'code_actions': 1,

View File

@ -266,6 +266,30 @@ Execute(tsserver type definition requests should be sent):
\ g:message_list
AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
Execute(tsserver implementation requests should be sent):
runtime ale_linters/typescript/tsserver.vim
call setpos('.', [bufnr(''), 2, 5, 0])
ALEGoToImplementation
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual type(function('type')), type(g:InitCallback)
call g:InitCallback()
AssertEqual 'implementation', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleTSServerResponse'')',
\ string(g:Callback)
AssertEqual
\ [
\ ale#lsp#tsserver_message#Change(bufnr('')),
\ [0, 'ts@implementation', {'file': expand('%:p'), 'line': 2, 'offset': 5}]
\ ],
\ g:message_list
AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
Execute(tsserver tab definition requests should be sent):
runtime ale_linters/typescript/tsserver.vim
call setpos('.', [bufnr(''), 2, 5, 0])
@ -520,6 +544,42 @@ Execute(LSP type definition requests should be sent):
AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
Execute(LSP implementation requests should be sent):
runtime ale_linters/python/pylsp.vim
let b:ale_linters = ['pylsp']
call setpos('.', [bufnr(''), 1, 5, 0])
ALEGoToImplementation
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual type(function('type')), type(g:InitCallback)
call g:InitCallback()
AssertEqual 'implementation', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback)
AssertEqual
\ [
\ [1, 'textDocument/didChange', {
\ 'textDocument': {
\ 'uri': ale#path#ToFileURI(expand('%:p')),
\ 'version': g:ale_lsp_next_version_id - 1,
\ },
\ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}]
\ }],
\ [0, 'textDocument/implementation', {
\ 'textDocument': {'uri': ale#path#ToFileURI(expand('%:p'))},
\ 'position': {'line': 0, 'character': 2},
\ }],
\ ],
\ g:message_list
AssertEqual {'42': {'open_in': 'current-buffer'}}, ale#definition#GetMap()
Execute(LSP tab definition requests should be sent):
runtime ale_linters/python/pylsp.vim
let b:ale_linters = ['pylsp']
@ -591,3 +651,39 @@ Execute(LSP tab type definition requests should be sent):
\ g:message_list
AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()
Execute(LSP tab implementation requests should be sent):
runtime ale_linters/python/pylsp.vim
let b:ale_linters = ['pylsp']
call setpos('.', [bufnr(''), 1, 5, 0])
ALEGoToImplementation -tab
" We shouldn't register the callback yet.
AssertEqual '''''', string(g:Callback)
AssertEqual type(function('type')), type(g:InitCallback)
call g:InitCallback()
AssertEqual 'implementation', g:capability_checked
AssertEqual
\ 'function(''ale#definition#HandleLSPResponse'')',
\ string(g:Callback)
AssertEqual
\ [
\ [1, 'textDocument/didChange', {
\ 'textDocument': {
\ 'uri': ale#path#ToFileURI(expand('%:p')),
\ 'version': g:ale_lsp_next_version_id - 1,
\ },
\ 'contentChanges': [{'text': join(getline(1, '$'), "\n") . "\n"}]
\ }],
\ [0, 'textDocument/implementation', {
\ 'textDocument': {'uri': ale#path#ToFileURI(expand('%:p'))},
\ 'position': {'line': 0, 'character': 2},
\ }],
\ ],
\ g:message_list
AssertEqual {'42': {'open_in': 'tab'}}, ale#definition#GetMap()