Open dylan-chong opened 6 years ago
Adding this was enough in my case, maybe it helps you too:
nnoremap <silent> gd :normal! m'<CR>:call LanguageClient_textDocument_definition()<CR>
@balta2ar Does that just create a mark? Marks are local to the current buffer, so if you go to the definition to a different file (so 90% of the time) then you won't be able to jump back.
Looks like vim/neovim doesn't have plugin API for tagstack interaction. (https://github.com/neovim/neovim/issues/398)
All ctrl+] and ctrl+t stuff is hardcoded, and there is no way how to implement this feature except hooking these hotkeys and emulate tagstack behavior :-( .
It seems that vim now have api's for incrementing tagstack, see the reference implementation here https://github.com/natebosch/vim-lsc/blob/b8a04ab0721c7383290ee997dd3bd491b8f5f93c/autoload/lsc/reference.vim#L22-L28
it will be good to add now.
Thanks for @qrux0 and @amgoyal's comment. They give me the idea.
Maybe we don't have to rewrite <c-]>
and <c-t>
. We can add two function to push/pop the trace stack. I copy the concept and part of code from faith/vim-go and write the two proof-of-concept functions.
I have no idea about rust. If the idea is ok, I would try to learn rust and write rust version.
" ref: https://github.com/fatih/vim-go/blob/8b4b0e3cea0f41aeb6810fbc30d57e4a639d1ee6/autoload/go/def.vim
let s:lsp_stack = []
let s:lsp_stack_level = 0
function! MyGoToDefinition(...) abort
" Get the current position
let l:fname = expand('%:p')
let l:line = line(".")
let l:col = col(".")
" Call the original function to jump to the definition
let l:result = LanguageClient_runSync('LanguageClient#textDocument_definition', {
\ 'handle': v:true,
\ })
" Get the position of definition
let l:jump_fname = expand('%:p')
let l:jump_line = line(".")
let l:jump_col = col(".")
" If the position is the same as previous, ignore the jump action
if l:fname == l:jump_fname && l:line == l:jump_line
return
endif
" Remove anything newer than the current position, just like basic
" vim tag support
if s:lsp_stack_level == 0
let s:lsp_stack = []
else
let s:lsp_stack = s:lsp_stack[0:s:lsp_stack_level-1]
endif
" Push entry into stack
let s:lsp_stack_level += 1
let l:stack_entry = {'line': l:line, 'col': l:col, 'file': l:fname}
call add(s:lsp_stack, l:stack_entry)
endfunction
function! MyTagStackPop() abort
if s:lsp_stack_level == 0
echo "lsp stack empty!"
return
endif
" Get previous position
let l:curr_stack_level = s:lsp_stack_level - 1
let l:jump_entry = s:lsp_stack[l:curr_stack_level]
" Pop stack
let s:lsp_stack = s:lsp_stack[0:l:curr_stack_level]
let s:lsp_stack_level = s:lsp_stack_level - 1
" Jump to previous location
if &modified
exec 'hide edit' l:jump_entry['file']
else
exec 'edit' l:jump_entry['file']
endif
call cursor(l:jump_entry['line'], l:jump_entry['col'])
normal! zz
endfunction
nnoremap gd :call MyGoToDefinition()<cr>
nnoremap gt :<C-U>call MyTagStackPop()<cr>
"nnoremap <c-]> :call MyGoToDefinition()<cr>
"nnoremap <c-t> :<C-U>call MyTagStackPop()<cr>
The jedi-vim uses a trick to insert tags into vim's original stack. The benefit of the method is that the implementation does not need to manipulate a stack to memory user's behaviour.
But the function is still vim version. In next step, I would lean rust in my free time and try to write rust version.
function! MyGoToDefinition(...) abort
" ref: https://github.com/davidhalter/jedi-vim/blob/master/pythonx/jedi_vim.py#L329-L345
" Get the current position
let l:fname = expand('%:p')
let l:line = line(".")
let l:col = col(".")
let l:word = expand("<cword>")
" Call the original function to jump to the definition
let l:result = LanguageClient_runSync(
\ 'LanguageClient#textDocument_definition', {
\ 'handle': v:true,
\ })
" Get the position of definition
let l:jump_fname = expand('%:p')
let l:jump_line = line(".")
let l:jump_col = col(".")
" If the position is the same as previous, ignore the jump action
if l:fname == l:jump_fname && l:line == l:jump_line
return
endif
" Workaround: Jump to origial file. If the function is in rust, there is a
" way to ignore the behaviour
if &modified
exec 'hide edit' l:fname
else
exec 'edit' l:fname
endif
call cursor(l:line, l:col)
" Store the original setting
let l:ori_wildignore = &wildignore
let l:ori_tags = &tags
" Write a temp tags file
let l:temp_tags_fname = tempname()
let l:temp_tags_content = printf("%s\t%s\t%s", l:word, l:jump_fname,
\ printf("call cursor(%d, %d)", l:jump_line, l:jump_col+1))
call writefile([l:temp_tags_content], l:temp_tags_fname)
" Set temporary new setting
set wildignore=
let &tags = l:temp_tags_fname
" Add to new stack
execute ":tjump " . l:word
" Restore original setting
let &tags = l:ori_tags
let &wildignore = l:ori_wildignore
" Remove temporary file
if filereadable(l:temp_tags_fname)
call delete(l:temp_tags_fname, "rf")
endif
endfunction
nnoremap gd :call MyGoToDefinition()<cr>
" No need to remap <c-t> anymore
I slightly modified the script by @yen3 to make it asynchronous:
function! MyGoToDefinition(...) abort
" ref: https://github.com/davidhalter/jedi-vim/blob/master/pythonx/jedi_vim.py#L329-L345
" Get the current position
let pos = {
\ 'fname': expand('%:p'),
\ 'line': line("."),
\ 'col': col("."),
\ 'word': expand("<cword>")
\ }
call LanguageClient#textDocument_definition({'handle': v:true}, function('MyGoToDefinitionCallback', [pos]))
endfunction
function! MyGoToDefinitionCallback(pos, ...) abort
" Get the original position
let l:fname = a:pos['fname']
let l:line = a:pos['line']
let l:col = a:pos['col']
let l:word = a:pos['word']
" Get the position of definition
let l:jump_fname = expand('%:p')
let l:jump_line = line(".")
let l:jump_col = col(".")
" If the position is the same as previous, ignore the jump action
if l:fname == l:jump_fname && l:line == l:jump_line
return
endif
" Workaround: Jump to origial file. If the function is in rust, there is a
" way to ignore the behaviour
if &modified
exec 'hide edit' l:fname
else
exec 'edit' l:fname
endif
call cursor(l:line, l:col)
" Write a temp tags file
let l:temp_tags_fname = tempname()
let l:temp_tags_content = printf("%s\t%s\t%s", l:word, l:jump_fname,
\ printf("call cursor(%d, %d)", l:jump_line, l:jump_col))
call writefile([l:temp_tags_content], l:temp_tags_fname)
" Store the original setting
let l:ori_wildignore = &wildignore
let l:ori_tags = &tags
" Set temporary new setting
set wildignore=
let &tags = l:temp_tags_fname
" Add to new stack
execute ":tjump " . l:word
" Restore original setting
let &tags = l:ori_tags
let &wildignore = l:ori_wildignore
" Remove temporary file
if filereadable(l:temp_tags_fname)
call delete(l:temp_tags_fname, "rf")
endif
endfunction
This feature would be really useful for jumping back to where you came from, when you do multiple jumps and browse around in various files.
Just a note,
C-]
which pops the tag stack, is different toC-o
, which is not necessarily jump-to-definition-related. The stack forC-o
gets modified very often, for example when doinggg
, so simply using this key would not be ideal, as I often have to press the scheme the times to come back to where I was before using the jump to definition feature.