Run a command each time a file opens, or general syntax - vim

I have a syntax rule that highlights trailing whitespace:
highlight Badspace ctermfg=red ctermbg=red
match Badspace /\s\+$/
This is in my .vimrc. It works fine, but the problem is I use splits a lot, and it seems that the match is only run on the first file you open, as well it should because the .vimrc should only run once.
Anyway, how can I get the above syntax to match any file that is opened? Is there a "general" syntax file? Is there any other way to run match each time a file opens rather than just once? I'd like to know both because I could end up using either one in the future.

The :match command applies the highlighting to a window, so you can use the WinEnter event to define an :autocmd.
:autocmd WinEnter * match Badspace /\s\+$/
Note that there are already a number of plugins for this purpose, most based on this VimTip: http://vim.wikia.com/wiki/Highlight_unwanted_spaces
They handle all that for you, and turn off the highlighting in insert mode; some can also automatically delete the whitespace. In fact, I have written a set of plugins for that, too: ShowTrailingWhitespace plugin.

You could accomplish this by using an autocmd:
highlight Badspace ctermfg=red ctermbg=red
autocmd BufEnter * match Badspace /\s\+$/
However, there's another way to accomplish your specific goal of marking trailing whitespace. Vim has a built-in feature for highlighting "special" whitespace, which includes tabs (to differentiate from spaces), trailing whitespace, and non-breaking spaces (character 160, which looks like a normal space but isn't).
See :help list and :help listchars. Here's what I use:
set list listchars=tab:>·,trail:·,nbsp:·,extends:>
listchars has the benefit of working with any file type, and marking up multiple whitespace types that are of interest. It is also a lot faster (match will be noticeably slow on giant files) and built-in already.
(Note that those are funky non-ASCII dot characters, which should work fine for you if you cut-and-paste into a UTF8-capable Vim. If they don't work for you, you can use any characters you like there, such as periods or underscores).
Here's what it looks like for me:

The correct approach to this problem is actually to use :syntax to define a custom syn-match.
Try putting this in your vimrc:
augroup BadWhitespace
au!
au Syntax * syn match customBadWhitespace /\s\+$/ containedin=ALL | hi link customBadWhitespace Error
augroup END
Edit: It should also be noted that there is built-in support for highlighting trailing whitespace with the 'list' option; see :help 'listchars' and :h hl-SpecialKey (SpecialKey is the highlight group used to highlight trailing whitespace characters when 'list' is on).

This is accomplished using autocmd. The events you're looking for are BufWinEnter and VimEnter. From the Vim manual:
BufWinEnter
After a buffer is displayed in a window. This
can be when the buffer is loaded (after
processing the modelines) or when a hidden
buffer is displayed in a window (and is no
longer hidden).
Does not happen for |:split| without
arguments, since you keep editing the same
buffer, or ":split" with a file that's already
open in a window, because it re-uses an
existing buffer. But it does happen for a
":split" with the name of the current buffer,
since it reloads that buffer.
VimEnter
After doing all the startup stuff, including
loading .vimrc files, executing the "-c cmd"
arguments, creating all windows and loading
the buffers in them.
Try putting this in your vimrc:
augroup BadWhitespace
au!
au VimEnter,BufWinEnter * match Badspace /\s\+$/
augroup END
Do :help autocmd for more info.
This is completely wrong because :match is window-local, not buffer-local. Ingo Karkat has the right idea. Unfortunately, there is no good way to avoid triggering the autocmd every time you enter the window.
More to the point, though, this is a job for a custom syntax, not match.

Related

How do I autocomplete and format brackets in Vim?

I'm working my way through a Java book and as a result, I find myself typing in a lot of curly braces. Considering that I knew how powerful Vim can be, though being far from an expert, I've looked for ways to let it handle the braces for me. I came across the use of autocmds, and I put the following in my .vimrc:
autocmd FileType java inoremap <buffer> { {<CR><CR>}<Up><Tab>. I also tried just a normal inoremap, to no avail.
What I have now works for the highest level block. However, when I get to any block deeper, the <Tab> doesn't seem to execute. Everything else works as expected. But no matter how deep after that, but the cursor always ends up back at the beginning of the method level. I imagine that it's because I told it to add exactly one tab. How can I make the tab count depend on the depth?
Assuming you have some sort of autoindenting set up:
augroup Java
autocmd!
autocmd FileType java inoremap <buffer> { {<CR>}<C-c>O
augroup END
What your version does:
insert the opening brace,
insert a newline,
insert a newline,
insert the closing brace,
move the cursor up to column 1,
insert a tab.
You got it right: since you only insert a single tab this can only get you to the first level of indentation.
What my version does:
insert the opening brace,
insert a newline,
insert the closing brace,
exit insert mode without triggering autocommands,
open a new line above the current line.
:help O respects your autoindenting settings so it will enter insert mode at the right indentation level.
Alternatively, you could modify your version like this:
augroup Java
autocmd!
autocmd FileType java inoremap <buffer> { {<CR><CR>}<C-c>S
augroup END
Where you leave insert mode on the blank line between the braces and do :help S to enter insert mode at the right indentation level.
There are many ways to skin that cat.
I'd suggest letting Vim use its built-in Java mode. If you add
source $VIMRUNTIME/vimrc_example.vim
to your .vimrc, Vim will pick up a load of useful defaults, including automatic filetype detection and formatting rules. You'll find that opening a .java file will give you most of what you want.
And to anticipate your next question, if the tab width isn't what you want, add something like
autocmd FileType java setlocal shiftwidth=4
to the end of your .vimrc.

Display-only character substitution in VIM

Is there any way to set VIM that it substitutes characters in the displayed text without changing the underlying file?
To be specific, I would like to substitute SOH characters (displayed as ^A by default) for tab to make the FIX protocol log files more readable.
I'm partially fine with a workaround
:highlight SpecialKey guifg=#ffffff
what makes the ^A invisible on white background but the substitution for tab would be much better.
Update:
I've almost found the solution:
:syntax match SOH /^A/ conceal cchar=_
:set conceallevel=2
:hi conceal guibg=NONE
Unfortunately I haven't found the way how to escape tab to the cchar parameter (instead of the underscore).
I do face the problem while opening log files.
I use autocommands to do such substitutions and leave files without saving.
:au BufEnter *.log :%s/^M/\r/g
This autocommand will replace all ^M characters with newlines while opening a log file.
So, the text like ADM-DEVICE : logged in^MADM-DEVICE : in prompt will be shown correctly as
ADM-DEVICE : logged in
ADM-DEVICE : in prompt.
Similarly, other control characters can be replaced. One thing I would do is changing '^[' to ` for better understanding.
Log files should not be edited and saved manually. So, for that I will use another autocommand.
:au BufWrite *.log :q!
This autocommand will exit the file if I try to save it. Autocommands will be the best choice to do automatically what you wanted to do.

vim: Highlight quickfix selected line with color different than 'Search'

What is the highlight group for the currently selected line in the quickfix window?
The selected line in the quickfix window uses Search for highlighting. I'd like to continue using yellow for Search highlighting, but use blue for quickfix selected line.
Ingo Karkat's answer is right. It's indeed hard-coded in vim code.
I have created a patch - QuickFixCurrentLine.patch for vim8.
Patch is long enough to be posted here. Plus, it has mix of tabs and spaces. So, providing a link-only-answer.
EDIT:
The patch has got upstreamed in the latest vim code.
The name of the highlight has been changed to quickfixline instead of quickfixcurrentline.
The currently selected quickfix item is hard-coded to Search. You'd have to change the Vim source code and recompile to change this.
I see only limited ways to work around this with Vimscript. You could try to override the highlighting for the current line via :match / matchadd() (it has higher priority), but it would only cover the length of the text, not the entire line the original highlighting. Also, I think the currently selected item cannot be easily queried from Vim, so you'd have to hook into the quickfix-local <CR> mapping to update it, and stop using :cnext etc. to move to different errors.
:highlight BlueLine guibg=Blue
:autocmd BufReadPost quickfix match BlueLine /\%1l/
:autocmd BufReadPost quickfix nnoremap <buffer> <CR> :execute 'match BlueLine /\%' . line('.') . 'l/'<CR><CR>

No more messing up whitespace in VIM

I have an BufWritePre hook added to my .vimrc that trims trailing whitespace before a buffer is saved. This is very convenient for me when editing my own code or that of others who also have a policy to always remove trailing whitespace. However, this makes me sometimes mess up the whitespace of others, which doesn't look very nice in version control.
I have two ideas how this could be solved in general, both of which I have specific problems with:
Option 1
After opening a file (maybe using a BufReadPost hook), detect whether there is trailing whitespace in the file. If yes, set a buffer-local flag to signal this. If the flag is set, disable the trimming before a save.
The problem I have with this approach is that I don't seem to figure out how I can detect whether there is trailing whitespace in the buffer. I know about =~, but how do I get the buffer contents as a string? Or even better, I can do a search using /\s+$<cr>, but how can I check if the search was successful (if there are hits)?
Option 2 (more intelligent)
It would be even better if the whitespace trimming would only happen on the lines that were actually modified. This way I could have the benefit of not having to care about trailing whitespace in my code but still not messing up the rest of the file. This raises the question: can I somehow get the line numbers of the lines I added or modified?
I'm new to Vimscript, so I'd appreciate any hints or tips :)
UPDATE: I settled with option 1 now:
" configure list facility
highlight SpecialKey term=standout ctermbg=yellow guibg=yellow
set listchars=tab:>-,trail:~
" determine whether the current file has trailing whitespace
function! SetWhitespaceMode()
let b:has_trailing_whitespace=!!search('\v\s+$', 'cwn')
if b:has_trailing_whitespace
" if yes, we want to enable list for this file
set list
else
set nolist
endif
endfunction
" trim trailing whitespace in the current file
function! RTrim()
%s/\v\s+$//e
noh
endfunction
" trim trailing whitespace in the given range
function! RTrimRange() range
exec a:firstline.",".a:lastline."substitute /\\v\\s+$//e"
endfunction
" after opening and saving files, check the whitespace mode
autocmd BufReadPost * call SetWhitespaceMode()
autocmd BufWritePost * call SetWhitespaceMode()
" on save, remove trailing whitespace if there was already trailing whitespace
" in the file before
autocmd BufWritePre * if !b:has_trailing_whitespace | call RTrim() | endif
" strip whitespace manually
nmap <silent> <leader>W :call RTrim()<cr>
vmap <silent> <leader>W :call RTrimRange()<cr>
Option 1 can benefit from search() function, like so:
let b:has_trailing_spaces=!!search('\v\s+$', 'cwn')
search() function returns a number of matched line (they start from 1) or 0 if nothing was found, !! turns it to either 1 or 0, dropping information about on which line search() found trailing whitespace. Without n flag search() moves the cursor which is, I guess, undesired. Without w it may search only in the part of buffer that is after the cursor (really depends on 'wrapscan' option).
Proposed option 2 implementation is a hack that uses InsertLeave and '[, '] markers:
augroup CleanInsertedTrailingSpaces
autocmd!
autocmd InsertLeave * let wv=winsaveview() | keepjumps lockmarks '[,']s/\s\+$//e | call winrestview(wv)
augroup END
It assumes that you only add trailing whitespaces after typing. It will break if you move your cursor across the lines in insert mode. You can also try adding
autocmd CursorHold * if getpos("'.")[1]!=0 | let wv=winsaveview() | keepjumps lockmarks '.s/\s\+$//e | call winrestview(wv) | endif
, this should remove trailing spaces at the line of last change (only one line, '[ and '] can’t be used here because they point to first and last lines to often to be useful). Both autocommands should add information to undo tree.
There is a second option for the option 2: git annotate is able to annotate current state of the file thus you can use grep to filter out lines that have both trailing spaces and uncommitted changes and use a hook to purge unwanted spaces from them before commit. Sad, but hg annotate is not able to do so and thus you will have to write something more complex, possibly in python. I can’t say anything about other VC systems.
I guess it would be better if you used set list listchars+=trail:- to see such spaces and thus be able to remove them manually if they accidentally appear (personally I can’t remember myself constantly adding trailing spaces by accident, though in comments and documentation they are used by me intentionally to indicate that paragraph continues). What do you do so that this problem appears?
I tend not to let vim automatically trim anything. As you say, this can be a nightmare if dealing with other peoples code, and can lead to unnecessary conflicts. The approach I take, to keep my own code tidy is to make whitespace visible. With vim this can be achieved by adding the following to your ~/.vimrc file:
highlight SpecialKey ctermfg=DarkGray
set listchars=tab:>-,trail:~
set list
The result is to show whitespace like this:
This allows me to keep files clean whilst I write them. Most other (GUI) editors have the ability to show whitespace too.
" Show trailing whitepace and spaces before a tab:
:highlight ExtraWhitespace ctermbg=red guibg=red
:match ExtraWhitespace /\s\+$\| \+\ze\t/
:autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
This way any bad whitespace will glow in red. It's quite hard to miss.

Get vim to provide tab completion for CSS class and ID names

The one IDE feature that I always missed and invariably plug into vim is tab completion.
I'm a big fan of SuperTab, but one thing I can't stand is the fact that it treats the parts of CSS class names and IDs with dashes as individual words.
I've found a couple of possible solutions for camelCase and underscore_completion but I can't seem to find anything that supports plain-old-dashes.
This is not a CSS-specific problem: Vim uses the value of iskeyword to perform completion.
Type :set iskeyword? to see what characters are considered to be part of keywords. The default on a Mac is supposed to be #,48-57,_,192-255.
You can add the dash to the list with this command:
:set iskeyword+=-
Add this line to your ~/.vimrc to make this setting stick:
set iskeyword+=-
This seems to work for me:
autocmd FileType css,scss set iskeyword=#,48-57,_,-,?,!,192-255
Taken from here: VIM: How to autocomplete in a CSS file with tag ids and class names declared in HTML file
For future readers: if you want the benefits of dashes for edit/movement commands, but want full property autocompletion, try adding this to your .vimrc:
augroup css_dash_autocompletion
autocmd FileType scss,css autocmd! css_dash_autocompletion InsertEnter <buffer> set isk+=-
autocmd FileType scss,css autocmd css_dash_autocompletion InsertLeave <buffer> set isk-=-
augroup END
The first ! prevents duplicate event firing. Thanks to ZyX for the structure. If you re-source your .vimrc, you will need to :e any (S)CSS files you have open to pick up the change.

Resources