How to ignore space after comments when calculating indent level in Vim - vim

Consider writing a JavaDoc-style comment which includes an indented list (when expandtab is set and softtabstop=2):
/**
* First line:
* - Indented text
*/
Currently, after typing First line: and hitting return, Vim will correctly insert *<space>. However, when I hit tab to indent the second line, only one space will be inserted instead of two.
Is it possible to fix this, so the space after * will be ignored during indent calculations?

I am still a beginner at VimScript, but I cooked this up for you. Give it a try and let me know what you think.
function AdjustSoftTabStop()
" Only act if we are in a /* */ comment region
if match(getline('.'), '\s*\*') == 0
" Compensate for switching out of insert mode sometimes removing lone
" final space
if match(getline('.'), '\*$') != -1
" Put back in the space that was removed
substitute/\*$/\* /
" Adjust position of the cursor accordingly
normal l
endif
" Temporary new value for softtabstop; use the currect column like a
" base to extend off of the normal distance
let &softtabstop+=col('.')
endif
endfunction
function ResetSoftTabStop()
" Note that you will want to change this if you do not like your tabstop
" and softtabstop equal.
let &softtabstop=&tabstop
endfunction
" Create mapping to call the function when <TAB> is pressed. Note that because
" this is mapped with inoremap (mapping in insert mode disallowing remapping of
" character on the RHS), it does not result in infinite recursion.
inoremap <TAB> <ESC>:call AdjustSoftTabStop()<CR>a<TAB><ESC>:call ResetSoftTabStop()<CR>a

Related

How to toggle (all) line numbers on or off

Let's say I have some combination of:
" one if not both is usually on
set number " could be on or off
set relativenumber " could be on or off
Is there a way to toggle these on/off without losing information (not knowing what is set -- i.e., I would like to make a simple keyboard shortcut to toggle the visibility of the current line-number selection)? For example if I have only rnu set and I do:
:set number!
It really doesn't help me at all, since I'll still have rnu set and there will still be a line-number column on the left. If so, how could this be done?
give this a try:
currently, I am mapping it to <F7> you can change the mapping if you like
I am using the global variable, you can change the scope if it is required
This function will disable all line-number displays and restore to the old line number settings.
function! MagicNumberToggle() abort
if &nu + &rnu == 0
let &nu = g:old_nu
let &rnu = g:old_rnu
else
let g:old_nu = &nu
let g:old_rnu = &rnu
let &nu = 0
let &rnu =0
endif
endfunction
nnoremap <F7> :call MagicNumberToggle()<cr>
The one liner solution
:nnoremap <silent> <C-n> :let [&nu, &rnu] = [!&rnu, &nu+&rnu==1]<cr>
To understand what happens try:
:echo [&nu, !&rnu]
&nu ............. gets the value of number
!&rnu ........... the oposite value of relative number
For more :h nu

Check if a selection exists in vimscript

I want to write a function in vimscript that echoes the selected text or, if no text is selected, the entire buffer.
How can I distinguish between these two cases?
Define two mappings, an :nmap using the entire buffer, and a :vmap for the selected text. Both can invoke the same function, passing an isVisual boolean flag or a mode argument.
Anything else (custom commands, direct function :call) would require an explicit hint, because in order to invoke them, visual mode as already been left (for command-line mode). You also cannot use the '<,'> marks for the detection, because they will keep the last selection even after it has been removed.
I write a function to get visually selected text.
I hope it can help you.
function! GetSelected()
" save reg
let reg = '"'
let reg_save = getreg(reg)
let reg_type = getregtype(reg)
" yank visually selected text
silent exe 'norm! gv"'.reg.'y'
let value = getreg(reg)
" restore reg
call setreg(reg,reg_save,reg_type)
return value
endfun
" viusal map
vnoremap gs :<C-U>echo GetSelected()<CR>
" normal map
nnoremap gs :<C-U>echo join(getline(1, '$'), "\n")<CR>

How to count characters while typing?

I often use VIM to write comments in newspapers or blog sites.
Often there is a max number of characters to type.
How do I create a counter (p.e. in the statusbar) to see the characters I have typed (including whitespaces) while typing?
The 'statusline' setting allows evaluation of expressions with the %{...} special item.
So if we can come up with an expression that returns the number of characters (not bytes!) in the current buffer we can incorporate it in our statusline to solve the problem.
This command does it:
:set statusline+=\ %{strwidth(join(getline(1,'$'),'\ '))}
For text with CJK characters strwidth() is not good enough, since it returns a display cell count, not a character count. If double-width characters are part of the requirement, use this improved version instead:
:set statusline+=\ %{strlen(substitute(join(getline(1,'$'),'.'),'.','.','g'))}
But be aware that the expression is evaluated on every single change to the buffer.
See :h 'statusline'.
Sunday afternoon bonus – The character position under the cursor can also be packed into a single expression. Not for the faint of heart:
:set statusline+=\ %{strlen(substitute(join(add(getline(1,line('.')-1),strpart(getline('.'),0,col('.')-1)),'.'),'.','.','g'))+1}
By mixing glts answer and this post and a bit of fiddling with the code, I made the following for my self which you can put it in ~/.vimrc file (you need to have 1 second idol cursor so the function calculates the words and characters and the value can be changed by modifying set updatetime=1000):
let g:word_count = "<unknown>"
let g:char_count = "<unknown>"
function WordCount()
return g:word_count
endfunction
function CharCount()
return g:char_count
endfunction
function UpdateWordCount()
let lnum = 1
let n = 0
while lnum <= line('$')
let n = n + len(split(getline(lnum)))
let lnum = lnum + 1
endwhile
let g:word_count = n
let g:char_count = strlen(substitute(join(getline(1,'$'),'.'),'.','.','g'))
endfunction
" Update the count when cursor is idle in command or insert mode.
" Update when idle for 1000 msec (default is 4000 msec).
set updatetime=1000
augroup WordCounter
au! CursorHold,CursorHoldI * call UpdateWordCount()
augroup END
" Set statusline, shown here a piece at a time
highlight User1 ctermbg=green guibg=green ctermfg=black guifg=black
set statusline=%1* " Switch to User1 color highlight
set statusline+=%<%F " file name, cut if needed at start
set statusline+=%M " modified flag
set statusline+=%y " file type
set statusline+=%= " separator from left to right justified
set statusline+=\ %{WordCount()}\ words,
set statusline+=\ %{CharCount()}\ chars,
set statusline+=\ %l/%L\ lines,\ %P " percentage through the file
It will look like this:

Preserve cursor position when using ==

I am trying to make Vim indent lines like Emacs (that is, "make the current line the correct indent" instead of "insert tab character"). Vim can do this with = (or ==) for one line). I have imap <Tab> <Esc>==i in my .vimrc but this makes the cursor move to the first non-space character on the line. I would like the cursor position to be preserved, so I can just hit tab and go back to typing without having to adjust the cursor again. Is this possible?
Example
What I have now (| represents the cursor):
function f() {
doso|mething();
}
Tab
function f() {
|dosomething();
}
What I would like:
function f() {
doso|mething();
}
Tab
function f() {
doso|mething();
}
Also
function f() {
| dosomething();
}
Tab
function f() {
|dosomething();
}
I don't believe there's an "easy" way to do this (that is, with strictly built-in functionality) but a simple function does it just fine. In your .vimrc file:
function! DoIndent()
" Check if we are at the *very* end of the line
if col(".") == col("$") - 1
let l:needsAppend = 1
else
let l:needsAppend = 0
endif
" Move to location where insert mode was last exited
normal `^
" Save the distance from the first nonblank column
let l:colsFromBlank = col(".")
normal ^
let l:colsFromBlank = l:colsFromBlank - col(".")
" If that distance is less than 0 (cursor is left of nonblank col) make it 0
if l:colsFromBlank < 0
let l:colsFromBlank = 0
endif
" Align line
normal ==
" Move proper number of times to the right if needed
if l:colsFromBlank > 0
execute "normal " . l:colsFromBlank . "l"
endif
" Start either insert or line append
if l:needsAppend == 0
startinsert
else
startinsert!
endif
endfunction
" Map <Tab> to call this function
inoremap <Tab> <ESC>:call DoIndent()<CR>
There's always <C-T> and <C-D> (that is CtrlT and CtrlD) in insert mode to indent and outdent on the fly.
These don't change the cursor position – and they're built-in functionality.
Try using a mark of last insert mode, and use the append command to set the cursor back just where it was before, like:
:imap <Tab> <Esc>==`^a

vim - set auto indent to fill the leading space with space or tabstop

It seems if we enable 'ai', vim will fill the the leading space with tabstop.
I can make it fill with just space with 'et'. I don't like a C file mixed with space and tabstop.
My vimrc:
set ts=4 et
set ai
set hlsearch
syntax on
filetype plugin indent on
autocmd FileType make setlocal noexpandtab
However, in some condition I do need to input tabstop when I hit the 'TAB' on keyboard, for example, in makefile and some others.
The 'autocmd FileType' command is not good: I can't add every file type in vimrc.
What I want is simple:
autoindent to fill leading area with
space;
when hit 'TAB' on keyboard, tabstop
input, not space (so no 'et')
How to do it?
inoremap <expr> <tab> ((getline('.')[:col('.')-2]=~'\S')?("\<C-v>\t"):(repeat(' ', &ts-((virtcol('.')-1)%&ts))))
It does the same as #Lynch answer if I read it correctly.
You can also use <C-v><Tab>: this will insert <Tab> without invoking any mappings and ignores expandtab unless you remapped <C-v> or <C-v><Tab> for some reason.
If you want to just insert tab do
inoremap <Tab> <C-v><Tab>
It will ignore expandtab setting.
I did it using a function. I tested it, but maybe in some particular case you will have to fix some bugs. Try adding this to your vimrc:
set et
function! Inserttab()
let insert = ""
let line = getline('.')
let pos = getpos('.')[2]
let before = ""
let after = line
if pos != 1
let before = line[ 0: pos - 1]
let after = line[pos : strlen(line) ]
endif
if pos != 1 && substitute(before, "[ \t]", "", "g") != ""
let insert = "\t"
else
let insert = " "
endif
let line = before . insert . after
call setline('.', line)
call cursor(line('.'), strlen(before . insert))
endfunction
inoremap <tab> <esc>:call Inserttab()<CR>a
Basicaly it does remap your key in visual mode to the function Inserttab(). Also note that if you change ts for something other than 4 it will still output 4 spaces instead of two because the value is hard coded.
Also im not very familiar with vim scripts, but I think all the variables used will be global which is a bad thing.
I forgot to mention that to "see" white spaces you can use set list. You disable this with set nolist. Also in normal mode you can use ga to see information about the character your cursor is on.
Edit
I realise that you may want to insert tab at the beginin of the line. My script insert space at the begining and tab anywhere else.
If you really want a tab every time you hit tab key you could simply use this:
set et
function! Inserttab()
let insert = ""
let line = getline('.')
let pos = getpos('.')[2]
let before = ""
let after = line
if pos != 1
let before = line[ 0: pos - 1]
let after = line[pos : strlen(line) ]
endif
let insert = "\t"
let line = before . insert . after
call setline('.', line)
call cursor(line('.'), strlen(before . insert))
endfunction
inoremap <tab> <esc>:call Inserttab()<CR>a
But I dont see the point, with this version you will never be able to indent manually from insert mode.
One way to do it is
:set sw=4 (or whatever you want)
:set ts=46 (or some large number)
Then autoindent will not insert tabs unless you reach 46 spaces, in which case you can put in a higher number.
Only drag about this is if someone else is using tabs, then you have to reset ts to agree with the file you are editing. On the other hand, it will make the tabs immediately obvious, which can be desirable as well.

Resources