diff --git a/README.md b/README.md index 9167936..bac67f3 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,15 @@ other reference to the currently highlighted element. ### Jump to definition While the cursor is on any identifier call `LSClientGoToDefinition` (`` if -using the default mappings) to jump to the location of the definition. If the -cursor moves before the server responds the response will be ignored. +using the default mappings) to jump to the location of the definition. When +multiple definitions are available, the quickfix list will be opened. +If the cursor moves before the server responds the response will be ignored. + +### Jump to declaration + +While the cursor is on any identifier call `LSClientGoToDeclaration` (`gd` if +using the default mappings) to jump to the location of the declaration. +If the cursor moves before the server responds the response will be ignored. ### Find references diff --git a/autoload/lsc/config.vim b/autoload/lsc/config.vim index 52f12ef..3e32216 100644 --- a/autoload/lsc/config.vim +++ b/autoload/lsc/config.vim @@ -3,6 +3,8 @@ if !exists('s:initialized') let s:default_maps = { \ 'GoToDefinition': '', \ 'GoToDefinitionSplit': [']', ''], + \ 'GoToDeclaration': 'gd', + \ 'GoToDeclarationSplit': [']', 'gd'], \ 'FindReferences': 'gr', \ 'NextReference': '', \ 'PreviousReference': '', @@ -54,6 +56,8 @@ function! lsc#config#mapKeys() abort for l:command in [ \ 'GoToDefinition', \ 'GoToDefinitionSplit', + \ 'GoToDeclaration', + \ 'GoToDeclarationSplit', \ 'FindReferences', \ 'NextReference', \ 'PreviousReference', diff --git a/autoload/lsc/reference.vim b/autoload/lsc/reference.vim index 42ef669..060ec38 100644 --- a/autoload/lsc/reference.vim +++ b/autoload/lsc/reference.vim @@ -1,45 +1,58 @@ let s:popup_id = 0 +function! lsc#reference#goToDeclaration(mods, issplit) abort + call lsc#file#flushChanges() + call lsc#server#userCall('textDocument/declaration', + \ lsc#params#documentPosition(), + \ lsc#util#gateResult('GoTo', + \ function('GoTo', ['declaration', a:mods, a:issplit]))) +endfunction + function! lsc#reference#goToDefinition(mods, issplit) abort call lsc#file#flushChanges() call lsc#server#userCall('textDocument/definition', \ lsc#params#documentPosition(), - \ lsc#util#gateResult('GoToDefinition', - \ function('GoToDefinition', [a:mods, a:issplit]))) + \ lsc#util#gateResult('GoTo', + \ function('GoTo', ['definition', a:mods, a:issplit]))) endfunction -function! s:GoToDefinition(mods, issplit, result) abort +function! s:GoTo(label, mods, issplit, result) abort if type(a:result) == type(v:null) || \ (type(a:result) == type([]) && len(a:result) == 0) - call lsc#message#error('No definition found') + call lsc#message#error('No'. a:label .'found') return endif - if type(a:result) == type([]) + if type(a:result) == type([]) && (a:label ==# 'declaration' || len(a:result) == 1) let l:location = a:result[0] + elseif type(a:result) == type([]) && len(a:result) > 1 + call s:setQuickFixLocations('Definitions', a:result) + call copen() else let l:location = a:result endif - let l:file = lsc#uri#documentPath(l:location.uri) - let l:line = l:location.range.start.line + 1 - let l:character = l:location.range.start.character + 1 - let l:dotag = &tagstack && exists('*gettagstack') && exists('*settagstack') - if l:dotag - let l:from = [bufnr('%'), line('.'), col('.'), 0] - let l:tagname = expand('') - let l:stack = gettagstack() - if l:stack.curidx > 1 - let l:stack.items = l:stack.items[0:l:stack.curidx-2] - else - let l:stack.items = [] + if exists('l:location') + let l:file = lsc#uri#documentPath(l:location.uri) + let l:line = l:location.range.start.line + 1 + let l:character = l:location.range.start.character + 1 + let l:dotag = &tagstack && exists('*gettagstack') && exists('*settagstack') + if l:dotag + let l:from = [bufnr('%'), line('.'), col('.'), 0] + let l:tagname = expand('') + let l:stack = gettagstack() + if l:stack.curidx > 1 + let l:stack.items = l:stack.items[0:l:stack.curidx-2] + else + let l:stack.items = [] + endif + let l:stack.items += [{'from': l:from, 'tagname': l:tagname}] + let l:stack.curidx = len(l:stack.items) + call settagstack(win_getid(), l:stack) + endif + call s:goTo(l:file, l:line, l:character, a:mods, a:issplit) + if l:dotag + let l:curidx = gettagstack().curidx + 1 + call settagstack(win_getid(), {'curidx': l:curidx}) endif - let l:stack.items += [{'from': l:from, 'tagname': l:tagname}] - let l:stack.curidx = len(l:stack.items) - call settagstack(win_getid(), l:stack) - endif - call s:goTo(l:file, l:line, l:character, a:mods, a:issplit) - if l:dotag - let l:curidx = gettagstack().curidx + 1 - call settagstack(win_getid(), {'curidx': l:curidx}) endif endfunction diff --git a/doc/lsc.txt b/doc/lsc.txt index 03f41f3..d8f551b 100644 --- a/doc/lsc.txt +++ b/doc/lsc.txt @@ -50,9 +50,10 @@ COMMANDS *lsc-commands* *:LSClientGoToDefinition* Jump to the location defining the element under the cursor. Sends a "textDocument/definition" request with the location set to the cursor's -position. If the cursor is not in the same position when the server responds -the jump will be canceled. With |lsc-default-map| this command is bound to -``. +position. When multiple definitions are returned, the quickfix list will +be opened to prompt the user for choice. If the cursor is not in the same +position when the server responds the jump will be canceled. +With |lsc-default-map| this command is bound to ``. If this version of vim supports |settagstack| the tag stack will also be updated to include this jump. Jump back with `` or |:pop|, it is not @@ -75,6 +76,15 @@ will default to opening a vertical split, while > < will prefer a new tab. + *:LSClientGoToDeclaration* +Similiar to |LSClientGoToDefinition| but expects only a single result when +issuing the default binding `gd`. +This is not compliant to the current protocol since it accepts multiple +declaration results as well. + + *:LSClientGoToDeclarationSplit* +In accordance to |LSClientGoToDeclaration| and |LSClientGoToDefinitionSplit|. + *:LSClientFindReferences* Populate the |quickfix| with a list of location which reference the element under the cursor, including it's definition. Sends a "textDocument/references" diff --git a/plugin/lsc.vim b/plugin/lsc.vim index 445001c..b86ad36 100644 --- a/plugin/lsc.vim +++ b/plugin/lsc.vim @@ -25,6 +25,10 @@ command! LSClientGoToDefinitionSplit \ call lsc#reference#goToDefinition(, 1) command! LSClientGoToDefinition \ call lsc#reference#goToDefinition(, 0) +command! LSClientGoToDeclarationSplit + \ call lsc#reference#goToDeclaration(, 1) +command! LSClientGoToDeclaration + \ call lsc#reference#goToDeclaration(, 0) command! LSClientFindReferences call lsc#reference#findReferences() command! LSClientNextReference call lsc#reference#findNext(1) command! LSClientPreviousReference call lsc#reference#findNext(-1) diff --git a/test/integration/test/vim_test.dart b/test/integration/test/vim_test.dart index 9b69dec..7f81025 100644 --- a/test/integration/test/vim_test.dart +++ b/test/integration/test/vim_test.dart @@ -35,6 +35,11 @@ void main() { expect(result, '2'); }); + test('loads plugin', () async { + final result = await vim.expr('exists(\':LSClientGoToDeclaration\')'); + expect(result, '1'); + }); + test('opens files, has filetype detection', () async { await vim.edit('foo.txt'); expect(await vim.expr('&ft'), 'text');