Is there a simple way to setup a binding that will :vimgrep
up until a tags
file is found?
I'm curious because I can do set tags=tags;/
to do a similar search to define tags, but am not quite sure how to transfer this to a different use case.
Vi and Vim Stack Exchange is a question and answer site for people using the vi and Vim families of text editors. It only takes a minute to sign up.
Sign up to join this communityIs there a simple way to setup a binding that will :vimgrep
up until a tags
file is found?
I'm curious because I can do set tags=tags;/
to do a similar search to define tags, but am not quite sure how to transfer this to a different use case.
I believe you're looking for a function that will return the directory where the tags
file is located, by performing the same kind of lookup up the tree.
You can use findfile()
or finddir()
for this purpose, both will use a lookup method similar to the one used in the 'tags'
option. See :help file-searching
for more details on file searching syntax and a more complete list of functions and options that support that syntax.
For example, you can find the tags
file up the tree using:
:echo findfile('tags', '.;')
You can get the path to the directory where the tags
file using fnamemodify()
:
:echo fnamemodify(findfile('tags', '.;'), ':p:h')
And you can use that expression to execute a :vimgrep
starting on that directory. You'll need :execute
to build the command from a string:
:execute 'vimgrep /something/ '
\ . fnamemodify(findfile('tags', '.;'), ':p:h')
\ . '/**'
You can easily adapt that to write a function or user-defined command starting the search at the expected directory.
UPDATE: In order to bind a key combination to searching from the directory where your tags
file is, you can either use :execute
or create an <expr>
mapping.
For example:
nnoremap <Leader>s
\ :execute "vimgrep /"
\ . escape(input('/'), '/') . "/ "
\ . fnamemodify(findfile('tags', '.;'), ':p:h')
\ . "/**"<CR>
The final <CR>
executes the Ex command right away. Input is being escaped so that /
characters are allowed.
An alternative, using <expr>
, would be:
nnoremap <expr> <Leader>s
\ ":vimgrep /" . escape(input('/'), '/') . "/ "
\ . fnamemodify(findfile('tags', '.;'), ':p:h')
\ . "/**\<CR>"
This time we're using a string with :
so it enters Ex mode first. We're also including the final <CR>
inside the string with a "\<CR>"
(using "\r"
would also be possible, it's equivalent.)
Either approach would do what you want.
UPDATE 2: If you're interested in searching relative to where you found the tags
file in specific, then consider using the tagfiles()
function to get the path of the one you're using.
Something like this would work:
nnoremap <Leader>s
\ :execute "vimgrep /"
\ . escape(input('/'), '/') . "/ "
\ . fnamemodify(tagfiles()[0], ':p:h')
\ . "/**"<CR>
<expr>
to be able to map this to a key combination and input "something" in the middle, would I also have to use execute?
– ThoseKind
Oct 30 '19 at 13:48
vimgrep
on all the *.c
or *.h
files starting up the tree where the tags
file is found) then you might get a more concrete answer...
– filbranden♦
Oct 30 '19 at 14:13
nnoremap <expr> <Leader>s <esc>:exec ":vimgrep /" . input("") . "/ " . fnamemodify(findfile('tags', ';'), ':p:h') . "/**"
This works if I just put the :exec ...
part as a command, but I can't seem to get it to work for mapping to a key combination.
– ThoseKind
Oct 30 '19 at 14:18
:exec ":vimgrep /" . input("") . "/ " . fnamemodify(findfile('tags', ';'), ':p:h') . "/**"
works exactly as I want when input as a command. But I am having trouble mapping this to a key binding.
– ThoseKind
Oct 30 '19 at 14:27
:execute part
if I am mapping with <expr>
. So the resulting mapping is: noremap <expr> <Leader>s "<ESC>:vimgrep /" . input("") . "/ " . fnamemodify(findfile('tags', ';'), ':p:h') . "/**<ESC>"
. Thanks for your help!
– ThoseKind
Oct 30 '19 at 14:34