Vim - Navigate to next differing indent - vim

Is there a way in Vim in which I could navigate to the next differing indent level?
So from here to there for example:
-> var a = 1;
var b = 2;
var func = function(){
-> return a + b;
}

This should work for indents made up of spaces (not tabs):
call search('^ \{0,'.eval(indent(".")-1).'}\S\|^ \{'.eval(indent(".")+1).',}\S')
This is made up of two regular expressions:
^ \{0,'.eval(indent(".")-1).'}\S matches a smaller indent, using the \{n,m} construction matching from n to m of the preceding space.
^ \{'.eval(indent(".")+1).',}\S' matches a larger indent, using the \{n,} construction matching at least n of the preceding space.
The regexes are sandwiched between ^ and \S to apply only to the leading whitespace on the line. Then they are joined by the \| ('OR') operator.
Of course the search() call could be mapped to a key combination for convenience.
EDIT
Chris Johnsen points out that the calls to eval() are superfluous, so the command can be reduced to this:
call search('^ \{0,'.(indent(".")-1).'}\S\|^ \{'.(indent(".")+1).',}\S')

Related

Build regular expression

I need to build a regular expression for strings in which the +/- characters cannot stand side by side (must be separated by some other). I got this option: (a*(+|-)a)* , where a is any character, * is the Kleene closure, () is for clarity. But this expression does not recognize lines of the form: "+", "-","+ a-" etc. Maybe someone will be able to move me from the dead point. I need regularity to build a finite automaton.
That might do:
^(\+(?![\+-])|-(?![\+-])|[^\+-])*?$
It bounds everything to the single line: ^ to $. The lazy quantifier ()*? ensures that no more then one line is going to be recognized.
The 3 concatenations inside the parentheses are as follows:
\+(?![\+-]) if the character is + the next one must not be + or -
-(?![\+-]) if the character is - the next one must not be + or -
the previous two have the second character only looked-ahead, and could be combined into one concatenation: [\+-](?![\+-]).
[^\+-] any character that is not + and -
However, you must know that a regex is more powerful than a regular expression. You need a regular grammar more than a regular expression:
S = +T
S = -T
S = #S
S = ε
T = #S
T = ε
This grammar is right-regular, and # is any character that is not + nor -. ε is epsilon = nothing.
Here is the deterministic finite automaton (where P=+ M=- #=not + and not -):
/---- # ----\
| |
| v
(S = start/final)--- P,M -->(T = final)---- P,M --->(error)
^ |
| |
\----------- # ------------/

variable search and replace in vim

I have a list of items that I want to add a method call to. An example is easiest. Here's what I have now:
assists: 12,
level: 14,
deaths: 5,
...
I want to change that list to look like this:
assists: build_average(:assists),
level: build_average(:level),
deaths: build_average(:deaths),
...
Is it possible to add that method call to the end of every line with the name of the key as the argument with a neat Vim expression?
More of a regular expression:
:%s/\(\w\+\):.\+/\1: build_average(:\1),/
Note that this applies to all lines in your file. To only replace in a region, select the region (using V) and then use :s (which results in :<,>s/...).
Using more complex regular expressions in VIM can be tricky, because metacharacters are different from "normal" regular expression syntax (you need to write \+ instead of +, but can use . without escaping it, for example). I found this guide very handy to refer to the special VIM-syntax of regular expressions: http://vimregex.com/#pattern
Alternatively you can record a macro:
q // record macro
q // assign it to letter 'q'
0 // go to start of line
/:<ENTER> // search for ':'
l // move cursor 1 position to the right
d$ // delete to end of line (line is now 'assists:')
yyp // duplicate current line (cursor moves 1 line down)
k // move cursor up
A build_average( // append " build_average("
<ESC> // exit edit mode
J // join next line
A ), // append " ),"
<ESC> // exit edit mode
j // move 1 line down
q // stop recording macro
2#q // execute macro 'q' 2 times
More regexp gymnastics:
:%s/\v(\w+):\s+\zs.*\ze,/build_average(:\1)/
Decrypting it:
:help \v
:help \w
:help \s
:help \zs
:help \ze
:help \1

Vim (vimscript) get exact character under the cursor

I am getting the character under the cursor in vimscript the following way:
getline('.')[col('.')-1]
It works exactly like it should, however there is something I dislike. consider this [] the cursor. When there is a bracket next to the cursor like so:
}[] , ][] , )[] or {[] the cursor actually returns the bracket. What do I have to set so it will always return the character exactly under the cursor or atleast ignore if there is a bracket to it's left?
Note: I suspect that it might have to do with the brackets highlight, though I am not sure.
Note2: for the situation to occur there has to be a matching bracket.
Though I cannot reproduce the problem you're describing, there's another problem with your code: Because of the string indexing (and this is one of the uglier sides of Vimscript), it only works with single-byte characters, but will fail to capture chars like Ä or 𠔻 (depending on the encoding used). This is a better way of capturing the character under the cursor:
:echo matchstr(getline('.'), '\%' . col('.') . 'c.')
Edit: Since about Vim 7.4.1742, Vim has new strgetchar() and strcharpart() functions that work with character indexes, not byte addressing. This is helpful in many circumstances, but not here, because you still can only get the byte-index position of the cursor (or the screen column with virtcol(), but that's not the same as character index).
nr2char(strgetchar(getline('.')[col('.') - 1:], 0))
or
strcharpart(getline('.')[col('.') - 1:], 0, 1)
Another way to get the character index under cursor that deal with both ASCII and non-ASCII characters is the like the following:
function! CharAtIdx(str, idx) abort
" Get char at idx from str. Note that this is based on character index
" instead of the byte index.
return strcharpart(a:str, a:idx, 1)
endfunction
function! CursorCharIdx() abort
" A more concise way to get character index under cursor.
let cursor_byte_idx = col('.')
if cursor_byte_idx == 1
return 0
endif
let pre_cursor_text = getline('.')[:col('.')-2]
return strchars(pre_cursor_text)
endfunction
Then if you want to get char under cursor, use the following command:
let cur_char_idx = CursorCharIdx()
let cur_char = CharAtIdx(getline('.'), cur_char_idx)
See also this post on how to get pre-cursor char.

vim scripting - count number of matches on a line

I'm trying to count the number of regex matches in a line, and I need to use the result in a vim function. For example, count the number of open braces.
function! numberOfMatchesExample(lnum)
let line_text = getline(a:lnum)
" This next line is wrong and is the part I'm looking for help with
let match_list = matchlist(line_text, '{')
return len(match_list)
endfunction
So I'd like to find a way in a vim function to capture into a variable the number of regex matches of a line.
There are plenty of examples of how to do this and show the result on the status bar, see
:h count-items, but I need to capture the number into a variable for use in a function.
The split() function splits a string on a regular expression. You can use it to split the line in question, and then subtract 1 from the number of resulting pieces to obtain the match count.
let nmatches = len(split(getline(a:lnum), '{', 1)) - 1
See :h split().
For the special case of counting a single ASCII character like {, I'd simply substitute() away all other characters, and use the length:
:let cnt = len(substitute(line_text, '[^{]', '', 'g'))
You can use a hack with substitute() with side effects:
function CountFigureBrackets(lnum)
let line=getline(a:lnum)
let d={'num': 0}
call substitute(line, '{', '\=extend(d, {"num": d.num+1}).num', 'g')
return d.num
endfunction

VIM: How to avoid substitution within substitution?

I created a function to search a custom number of empty lines (with or without spaces) and to replace them with a new (custom) number of empty lines.
fun! s:AddRemoveNumbEmptyLines()
if !exists("emptylinesbefore")
let emptylinesbefore = "How many empty lines do you search? \n (p.e. 2,3 4, ,3)"
endif
let b = inputdialog(emptylinesbefore)
if !exists("emptylinesafter")
let emptylinesafter = "How many empty lines must it be?"
endif
let c = inputdialog(emptylinesafter)
let m = repeat('\r', c)
exe 's/\(^\s*$\n\)\{'.b.'}/'.m.'/gc'
endfun
Let say b = 2, (2 and more) AND m = 3
If vim finds 4 empty lines it does a substitution to 3 empty lines.
(thats ok).
But when I refuse the substitution request (I use the "c" (confirm) flag) it finds at the same place 3 empty lines and asks again if it has to be replaced with 3 empty lines. When I refuse again, it finds at the same place, 2 empty lines and asks again if I want to do a substitution.
How can I avoid these multiple substitution requests (at the same place)?
Hope I made myself clear :)
resolved it! I just had to check for a non space \S in the line before and the line after.
My new exe = 's/^.*\S\+.*\n\zs\(^\s*$\n\)\{'.b.'}\ze\s*\S\+/'.m.'/gc

Resources