Is there a way to configure vimdiff to ignore ALL whitespaces? - vim

I'm using vim -d file1 file2 in order to see the differences between them. This works fine, but I want to ignore whitespace changes - they are irrelevant for source code files.
Vim help states that the following command will do the magic:
set diffopt+=iwhite
But unfortunately, this command only adds -b to diff tool command line, and that only ignores trailing whitespaces. The correct command line key for diff should be -w, to ignore all whitespace changes. But I can't find how to modify the diff command line directly from Vim. Of course I can compile a custom diff, or replace diff with diff.sh, but that looks kinda ugly :(.
Is there a better way to modify how Vim interacts with the diff tool for displaying file differences?

This implements what you want (taken from the diffexpr docs with -b changed to -w):
set diffopt+=iwhite
set diffexpr=DiffW()
function DiffW()
let opt = ""
if &diffopt =~ "icase"
let opt = opt . "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt . "-w " " swapped vim's -b with -w
endif
silent execute "!diff -a --binary " . opt .
\ v:fname_in . " " . v:fname_new . " > " . v:fname_out
endfunction
... I'm still looking for a better diffexpr helper with respect to handling which lines map to which (GNU diff, even with -w instead of -b, is rather baffled by combining extra whitespace with minor edits like commented lines). Maybe diffchar?

Yes. Set the iwhite option as you did, but additionally, make diffexpr empty.
From the relevant section of the vim docs:
iwhite
Ignore changes in amount of white space. Adds
the "-b" flag to the "diff" command if
'diffexpr' is empty. Check the documentation
of the "diff" command for what this does
exactly. It should ignore adding trailing
white space, but not leading white space.
Note also that you can provide a custom diff command line by setting diffexpr. See the discussion on the vimdiff man page, in particular:
The 'diffexpr' option can be set to use something else than the standard
"diff" program to compare two files and find the differences.
When 'diffexpr' is empty, Vim uses this command to find the differences
between file1 and file2:
diff file1 file2 > outfile

Thanks ire, that helped me. I now only need to have this (simpler than what is proposed by Adam K) in my ~/.vimrc :
set diffopt+=iwhite
set diffexpr=""
And it does it... That is still the most powerfull diff tool I know of, far better than any other.

I know it's an antique question but for others like me who didn't know, this is now available:
:set diffopt+=iwhiteall
Adds the "-w" flag to the "diff" command if 'diffexpr' is empty.
See :h 'diffopt'

For those hitting "Invalid argument" doing set diffopt+=iwhite, try without the + like so:
set diffopt=iwhite
However, a more robust approach would be to set ignore whitespace while preserving existing options. Beware though, that the "Invalid argument" error is likely caused by one of those existing options not being supported. In my case it was the "internal" option therefore I needed to set options in the following order:
set diffopt-=internal
set diffopt+=iwhite
Or add the following to your .vimrc:
if &diff
set diffopt-=internal
set diffopt+=iwhite
endif
Credit to https://www.micahsmith.com/blog/2019/11/fixing-vim-invalid-argument-diffopt-iwhite/

Addressing an issue brought up in the comments of Adam Katz's solution:
Depending on the vim version and setup of the user, a silent command can neglect to redraw the screen after it is issued. I also encountered this problem, which arose whenever I executed :diffo after using the suggested diffexpr. My solution was to change the silent execute command to the following:
silent execute "!diff -a --binary " . opt .
\ v:fname_in . " " . v:fname_new . " > " . v:fname_out | redraw!
This forces a redraw after the command is issued.

Related

Skip showing external command to be run when invoking `:grep`

Let's say I have many files:
for i in $(seq 1 1000)
do
if [ $((1 + RANDOM % 100)) -lt 2 ]
then
echo "needle" > $(uuid)
else
echo "haystack" > $(uuid)
fi
done
which I then edit with vim:
find . -type f | xargs -o vi --clean
I can search in these files via:
:vimgrep needle ##
or:
:grep needle ##
However, when I use the latter, vim does two things that I would like it to skip:
shows me the external command that it will execute, in this case pages of:
:!grep -n needle ./499553b6-615d-11ed-9ef7-5f9f4b37d6da ./484c2354-615d-11ed-94ff-a3a9a2d52531 ./49492e82-615d-11ed-af5d-5363f17c2e2b ./4ab7cc42-615d-11ed-8d84-4fd2cdf1942
c ./4ad4a132-615d-11ed-8af6-2f5cbf24f5de ./4c1f26c0-615d-11ed-b630-6bcbd445b4a1 ./4de4b948-615d-11ed-8e82-4f0c16b0065e <snip>
asks me to Press ENTER or type command to continue before starting to edit the matching files
How can I achieve this?
If you don't depend on specific features of your :help 'grepprg', then :help :vimgrep is your best choice because the work is done internally.
If you do, then you will have to use a different command because :help :grep shells out and it can't really be made to work silently. You might find mappings online that press the dreaded <CR> for you but they don't really fix anything.
The :help :cexpr family of commands is a less noisy alternative that allows you to use :help system() and thus remove both the shelling-out and the <CR> problem:
:cexpr system('grep -n needle ' .. expand('##'))
That's a lot to type, though, so you might want to turn that into a proper command or mapping.
Welcome to this particular rabbit hole.

vimdiff is not working under cywin with mobaxterm (E97 cannot create diffs)

On my PC I am using mobaxterm and installed vim in its cygwin environment.
vim and gvim works fine with no issue.
Only vimdiff is not working as I get the error : "E97: Cannot create diffs"
I installed diffutils "GNU collection of diff" and checked it on 2
files and it works fine.
I have no disk full.
I run vim -d or vimdiff without using my .vimrc and it is the same.
So it is not coming from my vim configuration.
I am using last version of mobaxterm (10.4) and vim v8 but the issue
was the same with previous version (mobaxterm v9 and vim v7.X).
I run vimdiff with -V option and I get :
Calling shell to execute: "diff -a /tmp/vkjIw1o/0 /tmp/vkjIw1o/1 > /tmp/vkjIw1o/2"
/tmp/vkjIw1o/0 /tmp/vkjIw1o/1 > /tmp/vkjIw1o/2"
E97: Cannot create diffs
I can see that /tmp/vkjIw1o is created, no file inside.
This is same case as this very old post https://superuser.com/questions/455210/vimdiff-in-cygwin-is-not-working-properly .
It is annoying I still have this issue.
I hope someone solved this.
JP
More infos :
1) I modified one file (vimrc2). Running this command from vim works fine : :!diff ~/.vimrc ~/vimrc2
## -51,7 +51,7 ##
"
set showmode
set ruler
-set cursorline
+"set cursorline
set number
2) For temp dir, echo tempname() gives me /tmp/vmtID5W/1
Then I can save a file to /tmp with :w /tmp/test.txt
I solved the issue by adding this code in my .virmc file :
set diffexpr=MyDiff()
function MyDiff()
let opt = ""
if &diffopt =~ "icase"
let opt = opt . "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt . "-b "
endif
silent execute "!/bin/diff.exe -a --binary " . opt . v:fname_in . " " . v:fname_new . " > " . v:fname_out
endfunction
I took the idea from https://github.com/neovim/neovim/issues/1466
Note that I had to write /bin/diff.exe
Thanks for the help.
I was facing the same problem in MobaXTerm for a long time up to version 11.1. The reason (and solution) I finally found is twofold:
E810 is about Vim being unable to write and read temporary files. If you run fugitive you get E484 about the same.
E97 is about wrong output of the diff executable called by Vim.
Resolving E810 and E484
In my case it was about wrong set shellxquote=\", which I got during my searching for a solution. That setting is responsible for how Vim quotes filenames. Removing it, resulting in unsetting, resolved the error with writing and reading temporary files.
Resolving E97
This is where the diffexpr comes to help. But it might not be sufficient itself. In my case I had to install diffutils package too as the /bin/diff was pointing to busybox, which supports only the unified diff format, while Vim requires ed-style (i.e. normal) diff format.
Now my diff executables are:
lrwxrwxrwx 16 Jan 29 13:26 /bin/diff -> /bin/busybox.exe*
-rwxr-xr-x 201.5K Dec 6 2016 /bin/diff.exe*
-rwxr-xr-x 56.0K Dec 6 2016 /bin/diff3.exe*
The difference in output is:
$ /bin/diff one two
--- one
+++ two
## -1,3 +1,3 ##
no change
-file one
+file two
no change
$ /bin/diff.exe one two
2c2
< file one
---
> file two
All that needs to be done is to point to the proper one in the diffexpr function:
silent execute "!/bin/diff.exe " . opt . v:fname_in . " " . v:fname_new . " > " . v:fname_out
Even the -a --binary options are not required.

Vim, Ag and Quickfix window, limit output width and height

Is there a way to limit :Ag output so it always takes one line and doesn't blow up the quickfix window?
At the moment it looks like this and it's awful. I can't see filenames, everything is super slow and just sucks:
Update For the record, I scrolled Quickfix window a bit to illustrate the point better. And while it is usable via :cn :cp, I would like to be able to quickly glance over the results with j k.
Looking over the man page, there does not seem to be any way to limit the output built into Ag itself.
Is there another way of limiting the line length? Actually, you do have the built in "cut" command in Linux, e.g. using it on the shell:
ag --column foo | cut -c 1-80
Limit all lines to 80.
Now we have to make ag.vim execute our specially crafted command, for which the g:agprg exists. So the first thing I thought of is this:
let g:agprg='ag --column \| cut -c 1-80' " doesn't work
The problem with this is that the ag.vim plugin just appends extra arguments to the end, and thus you end up executing something like ag --column | cut -c 1-80 something-i-searched-for. Is there a way to directly "insert" the arguments before the |?
One trick is to use a temporary shell function, like this:
f() { ag --column "$#" | cut -c 1-80 }; f something-i-search-for
Unfortunately, we still can't use this. ag.vim checks whether or not the first word is an actual command. So it complains that no executable by the name of "f()" exists. So my final solution:
let g:agprg='true ; f(){ ag --column "$#" \| cut -c 1-80 }; f'
As true always exists and doesn't do anything except return true, this works!
To your actual screenwidth instead of 80, you could use:
let g:agprg='true ; f(){ ag --column "$#" \| cut -c 1-'.(&columns - 6).' }; f'
I added the magic - 6 here to account for the extra characters Vim itself adds.
ag now supports a --width switch. rg has a similar --max-columns switch.
Assuming you are using this plugin. You should add this to your ~/.vimrc as specified by :h g:ag_qhandler
let g:ag_qhandler = 'copen 1'
However you can probably just do let :g:ag_qhandler = 'cc'. This will print the results at the in the bottom. When you move through the quickfix list via :cnext or :cprev it will print the current result as well.
For more help see:
:h g:ag_qhandler
:h :cope
Changing the geometry of the quickfix window won't help you fix your problem: the window is unusable not because of its size but because your search results are polluted by superfluous matches in minimized files.
Minimized JavaScript or CSS is the frontend development's equivalent of a binary and that kind of file should be ignored by search tools, indexing tools, file navigation tools and even version control tools, sometimes, because they are generally irrelevant.
Adding these lines to your ~/.agignore will make Ag search only in actual source files:
*.min*
*-min*
*_min*
*.min.*
bundle
min
vendor
tags
cscope.*
Adjust that list to your liking.

Vim verbosefile with timestamps

I have a number of very long and complex Vim configuration files, but these seem to be causing some unpredictable performance slowdowns.
Is there a way of producing a logfile of Vim's internal activity (in the style of verbosefile) which also includes timestamps for each function call to allow me to debug these issues. Alternatively, are there any other tools to help me in this task?
One thing you can try is invoking vim with --startuptime some_file. This will dump the steps performed during startup, along with timestamps. Try :help --startuptime for some more information.
Unfortunately, this shows you timestamps for files, not functions. If you want to see how long a specific function takes, you could put a call to it in a file under the plugin directory, so it's executed on boot. This won't be possible in all cases and it won't give you a lot of granularity, but it might help you narrow down the problem. Someone else might have a better idea, but I'd start by trying something like this.
EDIT:
I just found two things that may help with this problem. One of them is reltime(). Here's an example from vim's help:
let start = reltime()
call MyFunction()
echo reltimestr(reltime(start))
All it does is show the time a function takes to execute. Combined with, for example, Decho, this could provide a simple log of said functions.
The second one should be a lot more powerfule, the :profile command. It lets you profile a piece of vimscript and place the results in a file. :help profile should tell you all you need to know. Unfortunately, it's only available with the +profile feature, which seems to be enabled only in the "huge" feature set. If your vim doesn't have it, you'll need to recompile. If that's a problem, or you don't want to bother, you might be able to achieve good results with reltime alone.
If --startuptime does not fit your needs, you may use the following command:
vim --cmd 'profile start profile.log' \
--cmd 'profile func *' \
--cmd 'profile file *' \
-c 'profile pause' \
-c 'qa!'
(replace --cmd with -c and add -c 'Command that causes slowdown' if problem occurs not at startup). You will get all timings in the profile.log. Table with function timings will be present at the end of the file, to get per-script timings, use the following
script:
" Open profile.log file in vim first
let timings=[]
g/^SCRIPT/call add(timings, [getline('.')[len('SCRIPT '):], matchstr(getline(line('.')+1), '^Sourced \zs\d\+')]+map(getline(line('.')+2, line('.')+3), 'matchstr(v:val, ''\d\+\.\d\+$'')'))
enew
call setline('.', ['count total (s) self (s) script']+map(copy(timings), 'printf("%5u %9s %8s %s", v:val[1], v:val[2], v:val[3], v:val[0])'))
This will open a new file containing just the same table as at the end of profile.log, but 1) for scripts, not for functions, 2) unsorted.
Better late than never? I was searching to see if I could get vim to include some timestamps in the &verbosefile, as it causes vim to append to the file with no indication of when the appending started.
I adapted the solution from ZyX's answer here and after trying some things out, found that a simple echo "foo\n" will append foo and a newline to the &verbosefile:
let s:VerboseLS = ' **** V'.'E'.'RB'.'OSE LO'.'GG'.'ING S'.'TA'.'RTED ****'
let s:VerboseLE = ' **** V'.'E'.'RB'.'OSE LO'.'GG'.'ING E'.'ND'.'ED ****'
function! ToggleVerbose()
let l:timestamp = strftime("%c")
if !&verbose
let l:msg = "\n\n" . l:timestamp . s:VerboseLS . "\n\n"
set verbosefile=~/.vim/log/verbose.log
set verbose=100
echo l:msg
else
let l:msg = "\n" . l:timestamp . s:VerboseLE . "\n"
echo l:msg
set verbose=0
set verbosefile=
endif
endfunction
nnoremap <silent> <Leader>vl :call ToggleVerbose()<CR>
Now I get nice banners in my log:
Sun Feb 17 14:18:02 2013 **** VERBOSE LOGGING STARTED ****
...[snip]...
Sun Feb 17 14:18:08 2013 **** VERBOSE LOGGING ENDED ****
I defined those banners the way I did so that if I source that script file while verbose logging is on so if I search the log for "VERBOSE LOGGING" or similar I won't match on them.
So, you could adapt this method to use reltime if your vim has('reltime') for sub-second precision and maybe make a timing wrapper to call that echos the start/end times for function calls (although you'd have to refactor when you call things inline without the call command).
Ooh, I just had an idea... I wonder if &verbosefile can be set to a named pipe that goes to util that prepends time stamps... Let's see...

How to do search & replace with ack in vim?

I am using the Ack plugin in Vim, which helps me to quickly search for strings in my project. However, sometimes I want to replace all or some occurrences of the found strings. You can do some kind of global search and replace using the Vim arglist like this (source)
:
:args app/views/*/*
:argdo %s/, :expire.*)/)/ge | update
But instead of using args, I would prefer to do a search via Ack and then do the replace in all files that have been found. Is there a way to do it similar to the argdo command?
I've decided to use ack and perl to solve this problem outside of Vim so I could use the more powerful Perl regular expressions instead of the GNU subset. You could map this to a key stroke in your .vimrc.
ack -l 'pattern' | xargs perl -pi -E 's/pattern/replacement/g'
Explanation
ack
ack is an awesome command line tool that is a mix of grep, find, and full Perl regular expressions (not just the GNU subset). It's written in pure Perl, it's fast, it has match highlighting, it works on Windows and it's friendlier to programmers than the traditional command line tools. Install it on Ubuntu with sudo apt-get install ack-grep.
xargs
xargs is an old Unix command line tool. It reads items from standard input and executes the command specified followed by the items read for standard input. So basically the list of files generated by ack are being appended to the end of the perl -pi -E 's/pattern/replacement/g' command.
perl -pi -E
Perl is a programming language.
The -p option causes Perl to create a loop around your program which iterates over filename arguments.
The -i option causes Perl to edit the file in place. You can modify this to create backups.
The -E option causes Perl to execute the one line of code specified as the program. In our case the program is just a Perl regex substitution.
For more information on Perl command line options, see perldoc perlrun. For more information on Perl, see http://www.perl.org/.
Now, Vim has this new command cdo that will run the given command to each line of the quickfix list.
So you can use
:Ack pattern
:cdo s/pattern/newpattern/g
I don't believe there's a built in way of doing this, but it should be easy to make one.
What you need to do is create a command that calls a custom function. The function should then use the getqflist() function to get all of the entries in the quickfix list and exe to do the dirty work. Be careful what you pass as an argument!
" Define a command to make it easier to use
command! -nargs=+ QFDo call QFDo(<q-args>)
" Function that does the work
function! QFDo(command)
" Create a dictionary so that we can
" get the list of buffers rather than the
" list of lines in buffers (easy way
" to get unique entries)
let buffer_numbers = {}
" For each entry, use the buffer number as
" a dictionary key (won't get repeats)
for fixlist_entry in getqflist()
let buffer_numbers[fixlist_entry['bufnr']] = 1
endfor
" Make it into a list as it seems cleaner
let buffer_number_list = keys(buffer_numbers)
" For each buffer
for num in buffer_number_list
" Select the buffer
exe 'buffer' num
" Run the command that's passed as an argument
exe a:command
" Save if necessary
update
endfor
endfunction
You could using ack by this way
:args `ack -l User app/`
:argdo %s/, :expire.*)/)/ge | update
Or use ag
:args `ag -l User app/`
:argdo %s/, :expire.*)/)/gec | w
I use MacVim (activated with mvim in a shell). I pipe the results of ack to mvim:
mvim -f $(ack -l $#)
Then in MacVim, I search/replace using bufdo:
:bufdo %s/SEARCH/REPLACE/gce | update
Omit the c option if confirmation is not needed.

Resources