Vim jumps to the wrong tag - vim

The title states the problem, so here's the context. I have a tiny C++ file
void f(
int x
) { }
void f(
) { }
On which I run ctags.
ctags --recurse --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ -f tags f.C
Examining the tags files shows two entries for f as expected, with correct signatures.
When I try to use this in Vim, Vim locates the function with ctrl-] but when I use :tnext and :tprev the message says tag 1 of 2 or tag 2 of 2 but the cursor doesn't move between them.

If you look at :help tags-file-format, Vim uses the third column (named {tagaddress}) as a (search) command (:help tag-search). In the generated tags file, it looks like this:
f foo.cpp /^void f($/;" f signature:( )
f foo.cpp /^void f($/;" f signature:( int x )
The search pattern for both overloads (/^void f($/) are identical; that's why every tag jump will locate the first instance! In other words, though it's nice that the tags program added the signature, Vim unfortunately doesn't consider it.
With that, the obvious way to work around the problem would be to reformat the source code so that (parts of) the signature are included on the same line. Then, there will be different patterns:
b bar.cpp /^void b()$/;" f signature:()
b bar.cpp /^void b(int x)$/;" f signature:(int x)
A more correct (but also way more complex) route for solving this would be to extend the ctags program to recognize these ambiguities, and then augment the pattern with positive lookahead to also consider the stuff in the following line.
f foo.cpp /^void f(\%(\n\s*int x\)\#=/;" f signature:( )
f foo.cpp /^void f(\n\s*)/;" f signature:( int x )
Unfortunately, Vim doesn't seem to understand this syntax (neither with nor without the lookahead); I just get E435: Couldn't find tag, just guessing!.

Based on Ingo Karkat's answer, here's a solution that may work for you. If you run ctags (at least, Exuberant Ctags) with --excmd=number, it will output line numbers instead of search commands for the tag location, which will then resolve the ambiguity.
ctags --recurse --sort=1 --c++-kinds=+p --fields=+iaS --extra=+q --language-force=C++ -f tags --excmd=number f.C
The downside to this is that once you start editing the file, the tags will be invalid until you run ctags again. Search patterns are less susceptible to this than line numbers would be.
There are some other answers (Vim auto-generate ctags is one) that cover automatically running ctags on changes; some combination of the two approaches may work for you.

Related

Creating ctags extension for markdown

I edit quite a few markdown files using Vim these days. One thing I'm missing is a map of the file like function list in C based on ctags. So I came up with the following .ctags file
--langdef=markdown
--langmap=markdown:.md
--regex-markdown=/^# ([a-zA-Z0-9]+)/\1/
It runs OK but generates no valid tags for my .md file. With verbose mode turned on I get the following:
Considering option file /home/wenliang/.ctags: reading...
Option: --langdef=markdown
Option: --langmap=markdown:.md
Setting markdown language map: .md
Option: --regex-markdown=/^# ([a-zA-Z0-9]+)/\1/
Considering option file ./.ctags: not found
What's wrong with what I did?
Your definition looks OK.
What command did you use to generate your tags file? $ ctags . won't index anything but $ ctags -R . will.
FWIW, here is a slightly modified version of your definition that provides meaningful tag names and kind informations:
--langdef=markdown
--langmap=markdown:.md
--regex-markdown=/^#[ \t](.*$)/\1/h,heading,headings/
As an alternative, you might be interested in these cheaper, built-in, solutions…
using the define option and :dlist:
:setlocal define=^#\\s*
:dli /<CR>
using :ilist and no setup:
:il /#<CR>
which both produce the same list, ready for you to type :126<CR>:
See :help :ilist, :help :dlist, :help 'define'.

Vim Latex Suite: Display inline-math delimiters ($$)

As the title suggests: The Latex Suite for VIM (VIMLatex) doesn't display inline-math delimiters ($$) sometimes as I'm typing in that particular line. However, I'd like to show them.
What I currently see when typing:
BLABLA: \omega = 2\, \pi\, f
What I'd like to have:
BLABLA: $ \omega = 2\, \pi\, f $
I already searched the documentation but could not find the setting…
It should display them while typing in the current line. Search your vimrc for a line that adjusts concealcursor (short: cocu) option, it should not contain i, also check verbose set cocu?.
You can disable concealing completely by adding
set conceallevel=0
to your vimrc.

Why cscope don't search definitions in header files?

I use the following command to generate my cscope database:
tmpfile=$(mktemp)
find dir1/ dir2/ dir3/ -type f -regex ".*\.\([chlysS]\(xx\|pp\)*\|cc\|hh\|inl\|inc\|ld\)$" -print > $tmpfile
cscope -q -b -U -i $tmpfile -f cscope.out
Into vim, a :cs f g myfunction only leads me to the definition in C file, and nether in header file.
Make sure you got the terminology right. In C, usually the function definitions are put in C files, whereas declarations go into header files.
The cscope command f g (find definition) should correctly take you to the function definition. In the case where you actually have definitions in a header file (for example inline functions) the find definition command takes you there as well. If this is not the case, you should file a bug report to the cscope team.
Cscope unfortunately does not provide functionality for showing only a declaration. You could use the find symbol command (f s) but this might show a lot of results if the function is called from many places in your code.
You can use ctags which usually lets you choose from the declaration or definition. I usually use a mix of cscope and ctags within my projects because neither of them provides all the functionality i want.

Efficient way to refactor a class/method/string within a directory using vim

So far, I have been manually refactoring code by using the find-and-replace operation
%s:/stringiwanttoreplace/newstring/g
in vim.
But this is a slow and laborious process if I have stringiwanttoreplace in many files inside a specific directory.
My current/typical slow and laborious process involves a grep:-
grep -rn "stringiwanttoreplace" .
in my terminal to reveal all the locations/filenames where stringiwanttoreplace are; and now that I know which files contain stringiwanttoreplace, I will open each file one-by-one to perform the find-and-replace operation in each file.
Is there a more efficient workflow (in vim) to get this done?
CLARIFICATION: I would prefer a vim-based solution instead of a bash script/one-liner.
Here's the full sequence of commands that I would use:
/stringiwanttoreplace
:vimgrep /<c-r>// **
:Qargs
:argdo %s//newstring/g
:argdo update
In the first line, we search for the target pattern. That populates the last search pattern register (:help quote/), which means that we won't have to type it out in full again.
The :vimgrep command searches the entire project for the specified pattern. Type <c-r>/ as ctlr+r followed by / - this inserts the contents of the last search pattern register onto the command line. The first and last / symbols are delimiters for the search field. The trailing ** tells Vim to look inside every file and directory below the current directory.
At this point, the quickfix list will be populated with search matches from all matching files. :Qargs is a custom command, which populates the argument list with all of the files listed in the quickfix list. Here's the implementation:
command! -nargs=0 -bar Qargs execute 'args ' . QuickfixFilenames()
function! QuickfixFilenames()
" Building a hash ensures we get each buffer only once
let buffer_numbers = {}
for quickfix_item in getqflist()
let buffer_numbers[quickfix_item['bufnr']] = bufname(quickfix_item['bufnr'])
endfor
return join(values(buffer_numbers))
endfunction
Add that to your vimrc file.
Having run :Qargs, our argument list should now contain all of the files that include our target string. So we can run the substitution command with :argdo, to execute the command in each file. We can leave the search field of the substitution command blank, and it will automatically use the most recent search pattern. If you want, you could include the c flag when you run the substitution command, then you'll be prompted for confirmation.
Finally, the :argdo update command saves each file that was changed.
As #Peter Rincker pointed out, you should ensure that Vim's 'hidden' option is enabled, otherwise it will raise an error when you try to switch to another buffer before writing any changes to the active buffer.
Also, note that the last 3 commands can be executed in a single command line, by separating them with a pipe character.
:Qargs | argdo %s//replacement/gc | update
The :Qargs command is pinched from this answer (by me), which in turn was inspired by this answer by DrAl. A very similar solution was posted by #ib, which suggests to me that Vim should really implement something like :quickfixdo natively.
If you really want to do it in Vim you can follow the suggestions here.
You can call this from within Vim (:!find ...) but you don't need to:
find . -type f | xargs sed -i 's/stringiwanttoreplace/newstring/g'
Fine-tune the file selection with the dozens of parameters described in
man find
(e.g., replace only in HTML files: -name \*.html)
This solution will try to attempt the replacement in all files. You can filter that through grep before, but that is just doing twice the work for no gain.
By the way: sed uses almost the same syntax for regular expressions as Vim (stemming from the same history).
You could open all the files and type
:bufdo :s/stringiwanttoreplace/newstring/g
It performs the search/replace in all your buffers.
You don't need vim to do this, you can use command line tools. Using sed in a loop on the list of files to do this for you automatically. Something like this:
for each in `grep -l "stringiwanttoreplace" *` ;
do
cat $each | sed -e "s/stringiwanttoreplace/newstring/g" > $each
; done
vim7 has recursive grep built-in
:vimgrep /pattern/[j][g] file file1 file2 ... fileN
the result will be shown in a quickfix-window (:help quickfix)
to do the search recursively use the **-wildcard like
**/*.c to search through the current folder and recursively through all subdirectories.

How can I replace a word all the files within a folder in Vim?

I know that by typing the following: :%s/iwanthis/replacedbythis/g will change all the matching words of the file. How can I do the same for all the files within a folder?
(actually replacing a lot of words like this: padding-bottom:5px;)
Open Vim with all the files loaded into buffers, and do the replace on all buffers at once with bufdo:
% vim *
... when vim has loaded:
:bufdo %s/iwanthis/replacedbythis/g | w
The | w will write each file back to disk.
you can try greplace.vim that can give you a buffer include all lines matching a given regex across multiple files, then you can modify things in the buffer, and then call another greplace command to make all the changes updated to all these files.
Hope this would be helpful for those who work without vim
find /your_path/to/folder -type f -exec sed -i 's/text_to_be_replaced/new_text/g' {} \;
This code replaces all the occurrences of the text in the specified path (/your_path/to/folder). Thought it might be helpful for someone.

Resources