Skip to content

Commit 9e518f3

Browse files
committed
feat(tag): let :CommandTTag open stuff in splits, tabs etc
As noted in the related issue, we can't use `:tag` if we want to be able to open stuff in splits. The naïve approach doesn't work: ```diff diff --git a/lua/wincent/commandt/init.lua b/lua/wincent/commandt/init.lua index 339edd4..d9f36c9 100644 --- a/lua/wincent/commandt/init.lua +++ b/lua/wincent/commandt/init.lua @@ -660,7 +660,16 @@ local default_options = { end end - vim.cmd('silent! tag ' .. tag_name .. ' | normal! zz') + local tag_command = 'tag ' .. tag_name .. ' | normal! zz' + if ex_command == 'edit' then + vim.cmd('silent! ' .. tag_command) + elseif ex_command == 'split' then + vim.cmd('silent! split new | ' .. tag_command) + elseif ex_command == 'tabedit' then + vim.cmd('silent! tabedit | ' .. tag_command) + elseif ex_command == 'vsplit' then + vim.cmd('silent! vsplit new | ' .. tag_command) + end end, options = force_dot_files, }, ``` Because: > It will work most of the time, except when it doesn't. > > Here's how to break it: > > 1. Run `:CommandTTag`. Because `:CommandTTag` uses `taglist('.')` > under the covers, it will search a set of files relevant to the > current buffer. In a project like Command-T, `:echo tagfiles()` > prints `['tags']`, which means that `taglist('.')` will find the > project-local tags list and use it. Splitting etc with the above patch > works. > 2. Open a help buffer (eg. `:help :help`, for example). > Now `:echo tagfiles()` prints a huge list corresponding to > all the help tags (eg. `['$HOME/.config/nvim/doc/tags', > '$HOME/.config/nvim/pack/bundle/opt/vim-abolish/doc /tags', > '$HOME/.config/nvim/pack/bundle/opt/undotree/doc/tags', ...]` etc). If > you run `:CommandTTag` and try to open in a split using one of those, > then the above patch will first run `:split new`, _causing the output > of `tagfiles()` to print something else, because you're no longer in a > help buffer_, which means the subsequent `:tag` command will fail. > > So, if you have `include_filenames` set to `true`, it will work > because we're doing it differently (jumping to the file first, then > searching for the right position in the file). I think the only way > we'll get this to work with `include_filenames` set to `false` is > going to be to not use the `:tag` command at all, which in turn means > that commands like `:tnext` won't work. So, unless I come up with a really clever idea, this commit means we've got opening-in-a-split, opening-in-a-tab etc working, but at the cost of breaking `:tnext`. Let's ship it and see if there are any complaints. Note that in order to get this to work in the test case mentioned above, I had to make the trailing `/` optional in the `match()` call. This is because lines in the help tags look like this: ``` split() builtin.txt /*split()* ``` (Note the absence of the trailing slash.) Compare that with something like `scanner_free` in Command-T: ``` scanner_free lua/wincent/commandt/lib/scanner.c /^void scanner_free(scanner_t *scanner) {$/;" f typeref:typename:void scanner_free lua/wincent/commandt/lib/scanner.h /^#define scanner_free commandt_scanner_free$/;" d ``` (That's generated with Universal Ctags.) Then there's what you get with Exuberant Ctags: ``` scanner_free lua/wincent/commandt/lib/scanner.c /^void scanner_free(scanner_t *scanner) {$/;" f scanner_free lua/wincent/commandt/lib/scanner.h /^#define scanner_free /;" d `` In all cases Neovim is going to slice the relevant pattern out of the tags file and pass it to us in the `cmd` value, so what we actually get will be a pattern like one of the following: - /*split()* - /^void scanner_free(scanner_t *scanner) {$/ - /^#define scanner_free commandt_scanner_free$/ So, to match all of those, we need to look for a `/` at the start of the line and capture everything up to the optional `/` at the end of the line. Closes: #439
1 parent 8172e97 commit 9e518f3

File tree

1 file changed

+8
-16
lines changed

1 file changed

+8
-16
lines changed

lua/wincent/commandt/init.lua

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -644,23 +644,15 @@ local default_options = {
644644
mode = 'virtual',
645645
open = function(item, ex_command, _directory, options, opener, context)
646646
local tag = context[item]
647-
local tag_name = tag.name
648-
if options.scanners.tag.include_filenames then
649-
if tag.filename and tag.cmd then
650-
opener(tag.filename, ex_command)
651-
652-
-- Strip leading and trailing slashes, and use \M ('nomagic'):
653-
-- ie. "/^int main()$/" → "\M^int main()$"
654-
local pattern = '\\M' .. tag.cmd:match('^/(.-)/$')
655-
local line, column = unpack(vim.fn.searchpos(pattern, 'w'))
656-
if line ~= 0 and column ~= 0 then
657-
vim.cmd('normal! zz')
658-
end
659-
return
660-
end
647+
opener(tag.filename, ex_command)
648+
649+
-- Strip leading and trailing slashes, and use \M ('nomagic'):
650+
-- ie. "/^int main()$/" → "\M^int main()$"
651+
local pattern = '\\M' .. tag.cmd:match('^/(.-)/?$')
652+
local line, column = unpack(vim.fn.searchpos(pattern, 'w'))
653+
if line ~= 0 and column ~= 0 then
654+
vim.cmd('normal! zz')
661655
end
662-
663-
vim.cmd('silent! tag ' .. tag_name .. ' | normal! zz')
664656
end,
665657
options = force_dot_files,
666658
},

0 commit comments

Comments
 (0)