Syntax highlighting conflict in Vim (when using keyword) - vim

I am quite new to Vim and have the following problem:
In my .gvimrc I defined
syn keyword Todo PMID
to highlight every PMID in the files I edit. This works quite well as long as no general syntax highlighting is applied to the file (via setf or autocmd BufRead,BufNewFile …)
My question: How do I maintain a list of individual keywords which are highlighted no matter what file I edit and which syntax highlight I use for that file?

In this case matches come handy:
let s:kwreg='\v<%(PMID|OTHER|OTHER2)>'
let s:kwsyn='Todo'
augroup MyKeywords
autocmd!
autocmd WinEnter * if !exists('w:my_keyword_mnr') |
\ let w:my_keyword_mnr=matchadd(s:kwsyn, s:kwreg) |
\ endif
augroup END
let s:curtab=tabpagenr()
for s:tab in range(1, tabpagenr('$'))
execute 'tabnext' s:tab
let s:curwin=winnr()
for s:win in range(1, winnr('$'))
execute s:win.'wincmd w'
let w:my_keyword_mnr=matchadd(s:kwsyn, s:kwreg)
endfor
execute s:curwin.'wincmd w'
endfor
execute 'tabnext' s:curtab
unlet s:curtab s:curwin s:tab s:win

If you want that highlighted everywhere (as a kind of "overlay"), use matchadd(), as shown by ZyX's answer.
If this is a syntax extension for one / a couple of filetypes (e.g. only Java and Python files), put your :syntax definition into ~/.vim/after/syntax/<filetype>.vim. You have to study the original syntax a bit, and possibly add containedin=<groupNames> clauses, so that your items properly integrate.

You could use something like this:
syntax match pmidTODO /TODO\|PMID/
hi link pmidTODO Todo
hi Todo guibg=yellow guifg=black
This should work even when Todo is redefined by Vim syntax files.
Edit: As ZyX pointed out, this does not survive syntax changes.

Related

autocmd setting signs is breaking syntax highlighting

I currently have an autocmd for BufAdd that will add marks (signs) to certain lines of the loaded file. I have defined custom highlights for each sign, along the lines of:
highlight Custom1 ctermfg=black ctermbg=red
highlight Custom2 ctermfg=black ctermbg=green
sign define custom_sign1 text=>> texthl=Custom1
sign define custom_sign2 text=<< texthl=Custom2
Signs are create with:
sign place ID line=LINE name=custom_sign1 file=FILENAME
where ID is a unique id for the sign in the file, LINE is the line number for the sign, and FILENAME is the name of the file.
The signs all get created correctly, but it kills the syntax highlighting. I then have to do syntax on again, but then the custom colors for the signs don't show, at which point I have to reload the vimscript that defines those colors.
How do I make this not break syntax highlighting?
Also, this is done from an autocmd that uses the BufAdd and VimEnter events.
Edit: wrote the wrong BufXXX event at the end of the post - should be BufAdd
Update:
To duplicate my problem, source the .vim file below, and then open a new buffer that is normally syntax-highlighted. The first line will have a red >> sign, but the file will not be syntax highlighted. Note that it must be a new buffer (not already in the buffer list):
highlight Custom1 ctermfg=black ctermbg=red
highlight Custom2 ctermfg=black ctermbg=green
sign define custom_sign1 text=>> texthl=Custom1
sign define custom_sign2 text=<< texthl=Custom2
function! SignLine()
execute "sign place 1 line=1 name=custom_sign1 file=" . expand("<afile>")
endfunction
autocmd!
autocmd BufAdd * call SignLine()
augroup END
I created a simple file called test.c. After opening vim with the above .vim script as the initial file, sourcing it (with :so %), and then opening test.c, this is what I see:
After turning the syntax back on with :syntax on, it now looks like this (custom sign colors are now broken):
To get the custom sign colors working again, I have to re-source the vimscript AGAIN (:so test.vim), after which both the custom signs and the syntax highlighting work:
I shouldn't have to do this. Why is this happening?
When opening your script in Vim, the syntax highlighting already gives a big clue:
That last line isn't highlighted correctly because there's no :augroup command that corresponds to augroup END! You need to define it like that (or combine the two inner lines to autocmd! BufAdd ...):
augroup SignLine
autocmd!
autocmd BufAdd * call SignLine()
augroup END
The initial :autocmd! removed all defined autocmds, also the ones responsible for loading the syntax. That explains the broken behavior.

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

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.

Vim : Highlight the word TODO for every filetype

I would like to highlight the word TODO in vim, no matter what file is edited (code or normal text). It currently works for many different languages (for example, TODO is highlighted in C/Java comments by default), but I use vim for non-code text files and I would like to see the TODOs highlighted for them too.
What is the easiest way to do that ?
Highlighting TODO for every filetype and without possibly breaking other syntax rules can be done only by using :match/matchadd():
augroup HiglightTODO
autocmd!
autocmd WinEnter,VimEnter * :silent! call matchadd('Todo', 'TODO', -1)
augroup END

Insert vim variables into text for comment shortcut

I have a simple goal: Map Ctrl-C, a command I don't think I've ever used to kill vim, to automatically insert at the beginning of a line the correct character(s) to comment out that line according to the file's filetype.
I figured I could use an autocommand the recognize the file type and set a vim variable to the correct comment character when the file is open. So I tried something like:
" Control C, which is NEVER used. Now comments out lines!
autocmd BufNewFile,BufRead *.c let CommentChar = "//"
autocmd BufNewFile,BufRead *.py let CommentChar = "#"
map <C-C> mwI:echo &CommentChar<Esc>`wll
That map marks my current location, goes to the beginning of the line in insert mode, echoes the Comment Character(s) at that point, enters command mode, goes back to the set mark, and goes two characters right to make up for the inserted comment characters (assuming C style comment).
The italicized portion is the part I'm having trouble with; it is only there as a place holder to represent what I want to do. Can you help me figure out how to achieve this? Bonus points if you use strlen(CommentChar) to step the correct number of spaces to the right! Extra bonus points for the vim-master that includes how to do block-style comments if you are in visual mode!!
I'm still fairly new at vim scripting; my .vimrc is a measly 98 lines long, so if you could please help me by explaining any answers you provide! Thanks.
You can use <C-r> here:
noremap <C-c> mwI<C-r>=g:CommentChar<CR><Esc>`wll
see :h i_CTRL-R.
Also look at NERDCommenter plugin, with it mapping will look like this:
" By default, NERDCommenter uses /* ... */ comments for c code.
" Make it use // instead
let NERD_c_alt_style=1
noremap <C-c> :call NERDComment(0, "norm")<CR>
And you will not have to define comment characters by yourself.
I pulled this off the vim tips wiki at some point and use it myself. The only downside is it adds a space to the end of the line(s) for some reason, probably something small I overlooked.
" Set comment characters for common languages
autocmd FileType python,sh,bash,zsh,ruby,perl,muttrc let StartComment="#" | let EndComment=""
autocmd FileType html let StartComment="<!--" | let EndComment="-->"
autocmd FileType php,cpp,javascript let StartComment="//" | let EndComment=""
autocmd FileType c,css let StartComment="/*" | let EndComment="*/"
autocmd FileType vim let StartComment="\"" | let EndComment=""
autocmd FileType ini let StartComment=";" | let EndComment=""
" Toggle comments on a visual block
function! CommentLines()
try
execute ":s#^".g:StartComment." #\#g"
execute ":s# ".g:EndComment."$##g"
catch
execute ":s#^#".g:StartComment." #g"
execute ":s#$# ".g:EndComment."#g"
endtry
endfunction
" Comment conveniently
vmap <Leader>c :call CommentLines()<CR>

Detect filetype from within VIM plugin?

I have a vim plugin that defines a bunch of key mappings.
I'm trying to figure out how I can change the defininition of the key mapping based on the filetype.
For example:
If the file is a *.py: then map the key to X
If the file is a *.php: then map the key to Y
Thanks!
Yes. One way would be to use autocmd to call a custom function that sets your maps. It would look roughly like the following (could have mangled the syntax, so this isn't really copy & pastable):
augroup foo
autocmd!
autocmd FileType python call MyPythonSettings()
augroup end
function !MyPythonSettings()
set noai
" set mappings...
endfunction
When specific commands/abbreviation/mappings needs to be defined, I always split my plugin into several files:
the core functions that go into autoload plugin(s)
the global mappings/commands/abbreviations that go into a "plain" plugin
the filetype specific stuff that go into ftplugins.
Useless example:
The autoload plugin
" autoload/lh/ThePlugin.vim
let g:multby = 42
function lh#ThePlugin#fn(arg)
return a:arg * g:multby
endfunction
function lh#ThePlugin#inc_mult()
let g:multby += 1
endfunction
The "plain" plugin
" plugin/ThePlugin.vim
if exist('g:ThePlugin_loaded') | finish | endif
let g:ThePlugin_loaded = '1.0.0'
nnoremap £ :call lh#ThePlugin#inc_mult()
One ftplugin
" ftplugin/cpp/cpp_ThePlugin.vim
if exist('b:ThePlugin_loaded') | finish | endif
let b:ThePlugin_loaded = '1.0.0'
inoremap <buffer> µ <c-r>=lh#ThePlugin#fn(12)<cr>
PS: note the use of <buffer> in order to not pollute other filetypes with mappings that make no sense, nor override previously defined (and specific) mappings that do make sense.
The FileType event gets fired when the user opens a file to edit. This, however requires that the user has enabled :filetype on in her vimrc (or manually), whether for those specific extensions or globally.
If the user is editing a new file, you won't get that until they first save the buffer or do :setf autohotkey :setf sql etc.

Resources