I'm trying to write a snippet of VimL to allow the user to toggle the hilighting of unwanted trailing whitespace with a hotkey. (This is my first-ever Vim script, beyond copy-pasting things into my .vimrc, so … grain of salt :P)
I wish for the ‘are we currently hilighting trailing whitespace?’ to be buffer-specific state; but I'm having a lot of trouble figuring out how autocommands interact with buffers.
For instance, here's my first stab at an augroup of buffer-local autocmds:
augroup ExtraWhitespace
au!
au BufEnter <buffer=abuf> match ExtraWhitespace /\s\+$/
au InsertEnter <buffer=abuf> match ExtraWhitespace /\s\+\%#\#<!$/
au InsertLeave <buffer=abuf> match ExtraWhiteSpace /\s\+$/
augroup END
… unfortunately, this immediately trips up when invoked:
Error detected while processing function ToggleExtraWhitespace:
line 19:
E680: <buffer=0>: invalid buffer number
line 20:
E680: <buffer=0>: invalid buffer number
line 21:
E680: <buffer=0>: invalid buffer number
No matching autocommands
I don't understand why <abuf> is 0, when bufnr('%') is 1, or how to get the autocommands to execute for buffer 1 instead. (Of course 0 is invalid!)
For the moment, I've swapped out <buffer=abuf> for *; but this screws up the functionality of this function when there are multiple buffers loaded, and That Is Bad. So, any help figuring this out is welcome. /=
First, I don't know how <buffer=abuf> works. The documentation for it seems to be conflicting. It appears the the behavior for <buffer=abuf> was changed/fixed with patch 7.4.637, before It was causing problems even if used correctly. <buffer=abuf> must only be used when an autocmd is running. So your function would probably have worked if you called it in VimEnter or BufAdd.
The following a modified version of what you attempted which doesn't use <buffer=abuf>
augroup ExtraWhitespace
autocmd! * <buffer>
autocmd BufEnter <buffer> match ExtraWhitespace /\s\+$/
autocmd InsertEnter <buffer> match ExtraWhitespace /\s\+\%#\#<!$/
autocmd InsertLeave <buffer> match ExtraWhitespace /\s\+$/
augroup END
The first things you should notice is that au! has been replaced with autocmd! * <buffer>. au! shouldn't be there since this will remove all autocmd in the ExtraWhitespace group from all buffers. This means that you can only define it in one buffer. (autocmd! * <buffer> only deletes the autocmds in the current buffer)
The second thing you should notice is that <buffer> is used. This means that the autocmd will be created only for the current buffer when when the function is called. Buffer local autocmd must be called for each buffer that you want to define.
Other miscellaneous comments
You have
fun! HighlightExtraWhitespace()
if exists('b:ews') && b:ews == 1
"echom "-- Adding ExtraWhitespace hilighting"
highlight ExtraWhitespace ctermbg=red guibg=red
else
"echom "-- Removing ExtraWhitespace hilighting"
highlight clear ExtraWhitespace
endif
endfun
au ColorScheme * call HighlightExtraWhitespace()
Highlighting is global, so clearing it in one buffer is going to remove the highlight group everywhere. So its better to just leave the highlighting in place and redefine it every time the colorscheme changes.
autocmd ColorScheme * highlight ExtraWhitespace ctermbg=red guibg=red
It is recommended to use the long form of command names in scripts. (Short form used only for typing). The long form is more readable and easily identifiable, so au would be come autocmd.
Related
in my vimrc I have a script that transform a text in bold when banded between * * double stars * * (like it does in this editor actually), but i don't want it to apply on my js or c or any programming files of course, so i tried to make it run only when it's a .txt file :
if (&filetype=='text')
set concealcursor=n
set conceallevel=3
hi AsteriskBold ctermfg=Green cterm=bold
autocmd BufEnter * syn match Asterisks contained "**" conceal
autocmd BufEnter * syn match AsteriskBold "\*\*.*\*\*" contains=Asterisks
endif
but obviously the condition of the "if" doesn't work since this rules doesn't apply anymore in none of my file, text or not
EDIT => SOLUTION
after reading the answers I choose this solution, in my vimrc (even though it's not the best way vim works as explaind by ingo)
au BufEnter *.txt setf text "(set a filetype unless it already exist)
au filetype text set cocu=n cole=3
au filetype text hi AsteriskBold ctermfg=Green cterm=bold
au filetype text syn match Asterisks contained "**" conceal
au filetype text syn match AsteriskBold "\*\*.*\*\*" contains=Asterisks
Filetypes
Filetype-specific settings go into ~/.vim/after/ftplugin/text.vim. (This requires that you have :filetype plugin on; use of the after directory allows you to override any default filetype settings done by $VIMRUNTIME/ftplugin/text.vim.) Alternatively, you could define an :autocmd FileType text ... directly in your ~/.vimrc, but this tends to become unwieldy once you have many customizations.
Syntaxes
For :syntax commands, there's a corresponding directory ~/.vim/after/syntax/text.vim. (Vim currently doesn't ship with a dedicated text syntax; you could drop the after part, and make your syntax the main one.)
By syntax script convention, your syntax groups should be prefixed with the filetype; e.g. textAsterisks. The :hi group has to be renamed as well; however, usually syntax scripts use :hi def link to link the syntax group to a (more generic) highlight group: hi def link textAsteriskBold AsteriskBold. More information at the end of usr_44.txt.
Highlight groups
These are global, you can put your :hi command(s) directly into your ~/.vimrc and define it just once.
Conceal
The conceal settings are window-local, but filetypes and syntaxes apply to buffers. And by using :set (instead of :setlocal), those settings will be inherited by any new window opened from the one that shows a text file. Depending on your workflow (and whether other filetypes you edit use concealing at all), you may never notice this, and there's no good workaround (only a huge mess of :autocmd could try to adapt those). Just be aware of this.
You're looking for an augroup.
See :help augroup and :help filetype.
For instance:
augroup asteriskbold
au!
au BufNewFile,BufRead *.txt,*.md,*.mkd,*.markdown,*.mdwn set concealcursor=n conceallevel=3
au BufNewFile,BufRead *.txt,*.md,*.mkd,*.markdown,*.mdwn hi AsteriskBold ctermfg=Green cterm=bold
au BufNewFile,BufRead *.txt,*.md,*.mkd,*.markdown,*.mdwn syn match Asterisks contained "**" conceal
au BufNewFile,BufRead *.txt,*.md,*.mkd,*.markdown,*.mdwn syn match AsteriskBold "\*\*.*\*\*" contains=Asterisks
augroup end
I'm writing a small snippet for quickfix buffers. I need to add an autocmd for quickfix buffers for BufDelete event.
I have following in ~/.vim/ftplugin/qf.vim
augroup quickr_preview
autocmd!
autocmd BufDelete <buffer> echom "Hey"
augroup END
The autocmd is getting executed before the quickfix buffer is loaded. So the BufDelete autocmd gets set for the calling buffer and not the quickfix buffer.
I've also tried putting autocmd FileType qf autocmd BufDelete <buffer> echom "Hey" directly in my ~/.vimrc, but that has same effect.
How to go about this?
For now I'm going with following.
function! QuickFixBufDelete()
if &buftype == 'quickfix'
autocmd BufDelete <buffer> echom "Hey"
endif
endfunction
autocmd BufCreate * call QuickFixBufDelete()
This is not good as the autocmd gets invoked for all filetypes and then I check for the filetype in my function.
The current answer is no longer working for me. I am now trying the BufReadPost auto command, and it seems to be playing nicely ...
augroup quickr_preview_auto_cmds
autocmd!
autocmd BufReadPost quickfix
\ if !exists('b:quickr_preview_auto_cmds')
\ | exe 'autocmd BufDelete <buffer> pclose! | sign unplace 26'
\ | let b:quickr_preview_auto_cmds = 1
\ | endif
augroup END
This matches what is described in the vim help for qiuckfix-window ...
When the quickfix window has been filled, two autocommand events are
triggered. First the 'filetype' option is set to "qf", which triggers the
FileType event. Then the BufReadPost event is triggered, using "quickfix" for
the buffer name. This can be used to perform some action on the listed
errors.
In my vimrc file i have this option set cursorline. I want to hide this line if that window is not in focus . Is there an option in vim to do that?
See this
Essentially, it's just the following autocmds:
augroup CursorLine
au!
au VimEnter * setlocal cursorline
au WinEnter * setlocal cursorline
au BufWinEnter * setlocal cursorline
au WinLeave * setlocal nocursorline
augroup END
But occasionally, you may want to define exceptions (i.e. permanently on or off) for certain windows. That's where my CursorLineCurrentWindow plugin may be helpful.
It sounds like you want the cursorline on when entering a vim buffer and off when leaving it. These commands in the vimrc file will achieve this:
autocmd BufEnter * set cursorline
autocmd BufLeave * set nocursorline
I my ~/.vimrc I use this syn for long lines
augroup longLines
au!
au! filetype zsh,sh,python,vim,c,cpp
\ syn match ColorColumn /\%>80v.\+/ containedin=ALL
augroup END
but this overwrite other syn, with
without
Why the synoverwrite other highlight?
this is notorious in the last lines
sys.exit(1)
import settings
have different colors, with syn, the lines lost normal highlight
I use the following code:
highlight TooLongLine term=reverse ctermfg=Yellow ctermbg=Red
autocmd BufEnter,WinEnter * if &tw && !exists('b:DO_NOT_2MATCH') |
\ execute '2match TooLongLine /\S\%>'.(&tw+1).'v/' |
\ endif
autocmd BufLeave,WinLeave * 2match
command -nargs=0 -bar Dm let b:DO_NOT_2MATCH=1 | 2match
command -nargs=0 -bar Sm execute '2match TooLongLine /\S\%>'.(&tw+1).'v/' |
\ silent! unlet b:DO_NOT_2MATCH
If you don’t want to be able to remove this highlighting, depend on textwidth and insist on highlighting spaces that go beyond the limit, then you can truncate this to just
2match TooLongLine /.\%>80v/
This solution uses match-highlight that does not scrap syntax highlighting, but always overrides it.
I realize you asked this quite some time ago, but in case if other people ask too, perhaps you could try using the matchadd() function, instead, like this:
hi def longLine gui=reverse "or guibg=pink, or whatever you prefer
augroup longLines
au!
au! filetype zsh,sh,python,vim,c,cpp
\ call matchadd("longLine", "\\%>80v", 0, 9999)
augroup END
Most importantly, make sure that you do NOT set the guifg of whatever highlight group you decide to use. That would overwrite your syntax highlighting.
Another important part (for me, at least) is to use matchadd with 0 as the third parameter so that your Search highlighting remains effective and doesn't get overtaken by the longLine highlighting.
The fourth parameter can be omitted. It's just a constant so that you can :call matchdelete(9999) to easily remove the highlighting again later, if you want.
See :h matchadd and :h matchdelete
I am getting 'trailing whitespace' errors trying to commit some files in Git.
I want to remove these trailing whitespace characters automatically right before I save Python files.
Can you configure Vim to do this? If so, how?
I found the answer here.
Adding the following to my .vimrc file did the trick:
autocmd BufWritePre *.py :%s/\s\+$//e
The e flag at the end means that the command doesn't issue an error message if the search pattern fails. See :h :s_flags for more.
Compilation of above plus saving cursor position:
function! <SID>StripTrailingWhitespaces()
if !&binary && &filetype != 'diff'
let l:save = winsaveview()
keeppatterns %s/\s\+$//e
call winrestview(l:save)
endif
endfun
autocmd FileType c,cpp,java,php,ruby,python autocmd BufWritePre <buffer> :call <SID>StripTrailingWhitespaces()
If you want to apply this on save to any file, leave out the second autocmd and use a wildcard *:
autocmd BufWritePre,FileWritePre,FileAppendPre,FilterWritePre *
\ :call <SID>StripTrailingWhitespaces()
I also usually have a :
match Todo /\s\+$/
in my .vimrc file, so that end of line whitespace are hilighted.
Todo being a syntax hilighting group-name that is used for hilighting keywords like TODO, FIXME or XXX. It has an annoyingly ugly yellowish background color, and I find it's the best to hilight things you don't want in your code :-)
I both highlight existing trailing whitespace and also strip trailing whitespace.
I configure my editor (vim) to show white space at the end, e.g.
with this at the bottom of my .vimrc:
highlight ExtraWhitespace ctermbg=red guibg=red
match ExtraWhitespace /\s\+$/
autocmd BufWinEnter * match ExtraWhitespace /\s\+$/
autocmd InsertEnter * match ExtraWhitespace /\s\+\%#\#<!$/
autocmd InsertLeave * match ExtraWhitespace /\s\+$/
autocmd BufWinLeave * call clearmatches()
and I 'auto-strip' it from files when saving them, in my case *.rb for ruby files, again in my ~/.vimrc
function! TrimWhiteSpace()
%s/\s\+$//e
endfunction
autocmd BufWritePre *.rb :call TrimWhiteSpace()
Here's a way to filter by more than one FileType.
autocmd FileType c,cpp,python,ruby,java autocmd BufWritePre <buffer> :%s/\s\+$//e
I saw this solution in a comment at
VIM Wikia - Remove unwanted spaces
I really liked it. Adds a . on the unwanted white spaces.
Put this in your .vimrc
" Removes trailing spaces
function TrimWhiteSpace()
%s/\s*$//
''
endfunction
set list listchars=trail:.,extends:>
autocmd FileWritePre * call TrimWhiteSpace()
autocmd FileAppendPre * call TrimWhiteSpace()
autocmd FilterWritePre * call TrimWhiteSpace()
autocmd BufWritePre * call TrimWhiteSpace()
Copied and pasted from http://blog.kamil.dworakowski.name/2009/09/unobtrusive-highlighting-of-trailing.html (the link no longer works, but the bit you need is below)
"This has the advantage of not highlighting each space you type at the end of the line, only when you open a file or leave insert mode. Very neat."
highlight ExtraWhitespace ctermbg=red guibg=red
au ColorScheme * highlight ExtraWhitespace guibg=red
au BufEnter * match ExtraWhitespace /\s\+$/
au InsertEnter * match ExtraWhitespace /\s\+\%#\#<!$/
au InsertLeave * match ExtraWhiteSpace /\s\+$/
This is how I'm doing it. I can't remember where I stole it from tbh.
autocmd BufWritePre * :call <SID>StripWhite()
fun! <SID>StripWhite()
%s/[ \t]\+$//ge
%s!^\( \+\)\t!\=StrRepeat("\t", 1 + strlen(submatch(1)) / 8)!ge
endfun
A solution which simply strips trailing whitespace from the file is not acceptable in all circumstances. It will work in a project which has had this policy from the start, and so there are no such whitespace that you did not just add yourself in your upcoming commit.
Suppose you wish merely not to add new instances of trailing whitespace, without affecting existing whitespace in lines that you didn't edit, in order to keep your commit free of changes which are irrelevant to your work.
In that case, with git, you can can use a script like this:
#!/bin/sh
set -e # bail on errors
git stash save commit-cleanup
git stash show -p | sed '/^\+/s/ *$//' | git apply
git stash drop
That is to say, we stash the changes, and then filter all the + lines in the diff to remove their trailing whitespace as we re-apply the change to the working directory. If this command pipe is successful, we drop the stash.
The other approaches here somehow didn't work for me in MacVim when used in the .vimrc file. So here's one that does and highlights trailing spaces:
set encoding=utf-8
set listchars=trail:·
set list
For people who want to run it for specific file types (FileTypes are not always reliable):
autocmd BufWritePre *.c,*.cpp,*.cc,*.h,*.hpp,*.py,*.m,*.mm :%s/\s\+$//e
Or with vim7:
autocmd BufWritePre *.{c,cpp,cc,h,hpp,py,m,mm} :%s/\s\+$//e
If you trim whitespace, you should only do it on files that are already clean. "When in Rome...". This is good etiquette when working on codebases where spurious changes are unwelcome.
This function detects trailing whitespace and turns on trimming only if it was already clean.
The credit for this idea goes to a gem of a comment here: https://github.com/atom/whitespace/issues/10 (longest bug ticket comment stream ever)
autocmd BufNewFile,BufRead *.test call KarlDetectWhitespace()
fun! KarlDetectWhitespace()
python << endpython
import vim
nr_unclean = 0
for line in vim.current.buffer:
if line.rstrip() != line:
nr_unclean += 1
print "Unclean Lines: %d" % nr_unclean
print "Name: %s" % vim.current.buffer.name
cmd = "autocmd BufWritePre <buffer> call KarlStripTrailingWhitespace()"
if nr_unclean == 0:
print "Enabling Whitespace Trimming on Save"
vim.command(cmd)
else:
print "Whitespace Trimming Disabled"
endpython
endfun
fun! KarlStripTrailingWhitespace()
let l = line(".")
let c = col(".")
%s/\s\+$//e
call cursor(l, c)
endfun
autocmd BufWritePre *.py execute 'norm m`' | %s/\s\+$//e | norm g``
This will keep the cursor in the same position as it was just before saving
autocmd BufWritePre * :%s/\s\+$//<CR>:let #/=''<CR>