Insert vim variables into text for comment shortcut - vim

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>

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.

Vim highlight a word with * without moving cursor

I would like to be able to highlight the word under cursor without going to the next result.
The solution I found uses a marker:
noremap * mP*N`P
Is there any better solution ?
I noticed vim is not really good to keep the position of its cursor after the execution of commands. For instance, when I switch buffer, the cursor go back to the begining of the line.
I might be interesting to have a global setting for this.
There's unfortunately no global setting for that.
One common solution to the "highlight without moving" problem is:
nnoremap * *``
One solution to the "keep the cursor position when switching buffers" problem is:
augroup CursorPosition
autocmd!
autocmd BufLeave * let b:winview = winsaveview()
autocmd BufEnter * if(exists('b:winview')) | call winrestview(b:winview) | endif
augroup END
As detailed here, you can define a map as follows:
:nnoremap <F8> :let #/='\<<C-R>=expand("<cword>")<CR>\>'<CR>:set hls<CR>
My SearchHighlighting plugin changes the * command, so that it doesn't jump to the next match; you can still jump by supplying a [count]. It also extends that command to visual mode (another frequently requested feature not in Vim), and some more.

Vim: disable word wrapping for specific lines

I am using :set textwidth=80 to make the Vim editor do hard wrapping automatically. However, sometimes for certain lines within a file (e.g. tables in LaTeX), I do not want Vim to do any hard wrapping automatically. Is there a way to mark certain lines to disable hard wrapping in Vim? Or automatically :set textwidth=0 just for specified lines?
There's nothing out-of-the-box, but you can build a solution with an :autocmd <buffer> on the CursorMoved,CursorMovedI events. On each move of the cursor, you have to check whether you're currently in one of those "certain lines", and modify the local 'textwidth' option accordingly:
autocmd CursorMoved,CursorMovedI <buffer> if IsSpecialLine() | setlocal textwidth=0 | else | setlocal textwidth=80 | endif
Put this into ~/.vim/after/ftplugin/tex.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/tex.vim.) Alternatively, you could define an :autocmd FileType tex autocmd ... directly in your ~/.vimrc, but this tends to become unwieldy once you have many customizations.
For the IsSpecialLine() function, you probably need to match a regular expression on the current line (getline('.') =~# "..."). If you can identify the "certain lines" through syntax highlighting, my OnSyntaxChange plugin can do all the work for you.
I've tried Ingo Karkat answer. Although it does work very well and does what the OP asks, I find it distracting (if I have long tables with row hundreds of characters long, there are a lot of shifts up and down when passing over tables) and can slow down a lot vim on big files (the textwidth and wrap are changed for the whole file, so running the autocmd for every cursor movement can be costly).
So I propose a static solution based on the idea that hopefully you have to modify a table as few times as possible. I've added the following to my ftplugin/tex.vim file:
" By default the text is
let s:textwidth = 90
let &l:textwidth=s:textwidth
" Toggle between "textwidth and wrap" and "textwidth=0 and nowrap".
" When editing a table, can be useful to have all the '&' aligned (using e.g.
" ':Tabularize /&') but without line brakes and wraps. Besides it's very
" annoying when line brakes "happen" while editing.
" As hopefully tables must be edited only from time to time, one can toggle
" wrap and textwidth by hand.
function! ToggleTwWrap() "{{{
" if textwidth and wrap is used, then disable them
if &textwidth > 0
let &l:textwidth=0
setlocal nowrap
else " otherwise re-enable them
let &l:textwidth=s:textwidth
setlocal wrap
endif
endfunction
So now if I want to edit manually a table I simply do
:call ToggleTwWrap()
to disable wrapping and textwidth and then again when I'm done with the table.
And of course you can create a command or a map
You can certainly set it for specific file types, but I don't think you can change those settings (or any, really) for individual lines.
Ingo Karat's answer works but setting textwidth on every single cursor move is too slow. This adapted version will only call setlocal textwidth= if the text width is actually going to change. This speeds things up considerably:
autocmd CursorMoved,CursorMovedI <buffer> if IsSpecialLine() | if &textwidth != 0 | setlocal textwidth=0 | endif | else | if &textwidth != 80 | setlocal textwidth=80 | endif | endif

Syntax highlighting conflict in Vim (when using keyword)

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.

Prevent (g)vim from auto-indenting comments

A while ago, I had to put
filetype plugin on
in my .vimrc for a plugin I use.
But this caused a change in autoindent: Whenever I write a comment "//", and then press enter, vim autoindentation automatically enters another "//" in the next line.
// This is a comment. <ENTER>
// <-- vim automatically puts '// ' there
What can I do to avoid this?
I use the autoindent setting in my vim file.
I already tried
filetype plugin indent off
but it does not work.
I am answering your title rather than the body of your question, since your title brings people to this page who are looking to stop Vim from indenting comments.
The variable that controls whether Vim auto-indents a new character is indentkeys. I've noticed incorrect indentation only in Python and Yaml, so I've turned off auto-indentation only for the "#" character at the beginning of the line: :set indentkeys-=0#
Since loading the filetype indentation plugin will override any .vimrc settings you've made, you can set up an autocmd to change the indentkeys after a file is created or loaded. Here are mine:
autocmd BufNewFile,BufReadPost * if &filetype == "python" | set indentkeys-=0# | endif
autocmd BufNewFile,BufReadPost * if &filetype == "yaml" | set expandtab shiftwidth=2 indentkeys-=0# | endif
See :h indentkeys
Note that because of (possibly) a bug, if you use Neovim you must also specify filetype plugin indent on, or the filetype won't be set.
Take a look at :h formatoptions and :h fo-table. The options you need to turn off are r and o. Turning them off prevents vim from automatically inserting the comment leader (in this case "//") when you press enter in insert mode or when you press o or O in normal mode.
See :help 'formatoptions' - I know how annoying this is!
Try this:
:set fo-=or

Resources