I've also developed an extreme anti-fetish for trailing whitespace recently. I made vim highlight it, and that shit is fucking everywhere!
ZOMG
So, in a bid to eradicate both mixed indenting and trailing whitespace from the universe, I've made vim display some flags on my statusline whenever they are present, and whenever my
&expandtab
setting is wrong. Read this raving to find out how.Note: this raving assumes a fair amount of knowledge about statuslines in vim. If you aren't pro at statuslines then you should read this raving first.
Just so you know what I mean...
A buffer containing mixed indenting, and trailing whitespace (click to enlarge).
A buffer with &et set wrong, and trailing whitespace (click to enlarge).
The three statusline flags of interest in the above screenshots are:
[mixed-indenting]
: Appears when a buffer is indented with both tabs and spaces[&et]
: Appears when a buffer is indented consistently (i.e. using only spaces or only tabs) but the&expandtab
setting for the buffer is wrong.[\s]
: Appears when a buffer contains trailing whitespace
The code for the [mixed-indenting] and [&et] flags
This is snipped straight outta my vimrc:
1 "display a warning if &et is wrong, or we have mixed-indenting
2 set statusline+=%#error#
3 set statusline+=%{StatuslineTabWarning()}
4 set statusline+=%*
5
6 "recalculate the tab warning flag when idle and after writing
7 autocmd cursorhold,bufwritepost * unlet! b:statusline_tab_warning
8
9 "return '[&et]' if &et is set wrong
10 "return '[mixed-indenting]' if spaces and tabs are used to indent
11 "return an empty string if everything is fine
12 function! StatuslineTabWarning()
13 if !exists("b:statusline_tab_warning")
14 let tabs = search('^\t', 'nw') != 0
15 let spaces = search('^ ', 'nw') != 0
16
17 if tabs && spaces
18 let b:statusline_tab_warning = '[mixed-indenting]'
19 elseif (spaces && !&et) || (tabs && &et)
20 let b:statusline_tab_warning = '[&et]'
21 else
22 let b:statusline_tab_warning = ''
23 endif
24 endif
25 return b:statusline_tab_warning
26 endfunction
2 set statusline+=%#error#
3 set statusline+=%{StatuslineTabWarning()}
4 set statusline+=%*
5
6 "recalculate the tab warning flag when idle and after writing
7 autocmd cursorhold,bufwritepost * unlet! b:statusline_tab_warning
8
9 "return '[&et]' if &et is set wrong
10 "return '[mixed-indenting]' if spaces and tabs are used to indent
11 "return an empty string if everything is fine
12 function! StatuslineTabWarning()
13 if !exists("b:statusline_tab_warning")
14 let tabs = search('^\t', 'nw') != 0
15 let spaces = search('^ ', 'nw') != 0
16
17 if tabs && spaces
18 let b:statusline_tab_warning = '[mixed-indenting]'
19 elseif (spaces && !&et) || (tabs && &et)
20 let b:statusline_tab_warning = '[&et]'
21 else
22 let b:statusline_tab_warning = ''
23 endif
24 endif
25 return b:statusline_tab_warning
26 endfunction
There are three parts to this code. The function (lines 12–26) works out what should be displayed on the statusline and caches the result. An autocommand on line 7 clears this cache when the user is idle or saves the file. Lines 2–4 put the flag on the statusline.
Note that the caching is needed as the function is called every time the statusline is refreshed. Without caching this would be very costly since the function searches the entire buffer for a leading space and a leading tab.
The code for the [\s] flag
Also straight from my vimrc, this code functions very similarly to the previous code. Lines 8–17 calculate and cache what should be displayed on the statusline. Line 4 clears this cache when the user is idle, or saves the file. Line 1 puts the flag on the statusline.
1 set statusline+=%{StatuslineTrailingSpaceWarning()}
2
3 "recalculate the trailing whitespace warning when idle, and after saving
4 autocmd cursorhold,bufwritepost * unlet! b:statusline_trailing_space_warning
5
6 "return '[\s]' if trailing white space is detected
7 "return '' otherwise
8 function! StatuslineTrailingSpaceWarning()
9 if !exists("b:statusline_trailing_space_warning")
10 if search('\s\+$', 'nw') != 0
11 let b:statusline_trailing_space_warning = '[\s]'
12 else
13 let b:statusline_trailing_space_warning = ''
14 endif
15 endif
16 return b:statusline_trailing_space_warning
17 endfunction
2
3 "recalculate the trailing whitespace warning when idle, and after saving
4 autocmd cursorhold,bufwritepost * unlet! b:statusline_trailing_space_warning
5
6 "return '[\s]' if trailing white space is detected
7 "return '' otherwise
8 function! StatuslineTrailingSpaceWarning()
9 if !exists("b:statusline_trailing_space_warning")
10 if search('\s\+$', 'nw') != 0
11 let b:statusline_trailing_space_warning = '[\s]'
12 else
13 let b:statusline_trailing_space_warning = ''
14 endif
15 endif
16 return b:statusline_trailing_space_warning
17 endfunction
An aside: use the list
and listchars
settings!
I have these lines in my vimrc:
set list
set listchars=tab:▷⋅,trail:⋅,nbsp:⋅
This tells vim to display tab characters as little arrow things and trailing spaces as floating dots (as in the two screenshots at the top of this raving). Having tabs displayed is invaluable as it becomes obvious how a file is indented, and if you aren't following suit.
Join in the fight against bundy whitespace today!
I was eavesdropping on two catholic priests yesterday
On the other hand, studies show that people who indent code responsibly are far more likely to have sex with famous actresses than those who don't.
So what are you waiting for? Take every precaution to ensure you indent correctly! And fuck trailing whitespace!
I am a catholic priest - how dare you eavesdrop into my private conversations!!!1!one
ReplyDeleteAlso see :he hl-SpecialKey to make tabs and trailing spaces stand out even more. I use it with:
ReplyDeleteset listchars=tab:\ \ ,trail:·,extends:…,nbsp:‗
to see tabs without using strange looking symbols.
The following command will solve all of the problems for you by simply deleting them.
ReplyDelete:perldo s/\s+$//; s/^ (\s*) (?=\S) / s#[^\t]##g;$_ /xe;
A mate of mine asked for some functions to jump to erroneous ^\s and \s$.
ReplyDeleteI'm pretty amateur when it comes to Vim functions, but have at you:
function! SeekIndentWarningOccurrence()
if (!&et)
/^
elseif (&et)
/^\t
endif
exe "normal 0"
endfunction
function! SeekTrailingWhiteSpace()
let [nws_line, nws_col] = searchpos('\s\+$', 'nw')
if ( nws_line != 0 )
exe "normal ".nws_line."G"
" This would be nicer, but | doesn't seem to collapse \t in to 1 col?
" exe "normal ".nws_col."|"
" so i'll do this instead :( Might be a better way
exe "normal 0"
exe "normal ".(nws_col-1)."l"
endif
endfunction
Excuse the abuse of and-en-bee-ess-pees for indentation.