vim codewalk through - vim

Following code has been picked up from this blog
function! Privatize()
let priorMethod = PriorMethodDefinition()
exec "normal iprivate :" . priorMethod . "\<Esc>=="
endfunction
function! PriorMethodDefinition()
let lineNumber = search('def', 'bn')
let line = getline(lineNumber)
if line == 0
echo "No prior method definition found"
endif
return matchlist(line, 'def \(\w\+\).*')[1]
endfunction
map <Leader>p :call Privatize()<CR>
I tried but I fail to understand PriorMethodDefinition method. Can someone walk me through this code?

PriorMethodDefinition returns the name of the first method definition above the cursor.
It does this by searching backwards for a line containing the text 'def'. The search function returns the line number and getline is used to retrieve the content of that line.
The function checks that it has found a valid line, before using a regular expression to get the name of the method and return it.
You can read more about these functions if you're curious about the specifics - see:
:help search
:help getline
:help matchlist
Edit: you can also read about the regular expression pattern
:help pattern
But I found it a little confusing at first, so allow me to explain it a little. Here's the expression used:
'def \(\w\+\).*'
This will search for any text matching the following pattern: "the text def followed by one or more 'word' characters \w\+ followed by zero or more characters .*". The part matching the word characters is placed into a group (or atom), designated by the escaped parens \( & \). More info on the definitions of word characters etc can be found in the help link above.
The matchlist function returns a list of matches, the first [0] of which is the full text matching the regex, followed by submatches (ie our group). We are interested in the first such submatch, hence the [1].

Related

Get the length of the string in substitution

I'd like to calculate the length of a replace string used in a substitution. That is, "bar" in :s/foo/bar. Suppose I have access to this command string, I can run and undo it, and may separate the parts marked by / with split(). How would I get the string length of the replace string if it contains special characters like \1, \2 etc or ~?
For instance if I have
:s/\v(foo)|(bars)/\2\rreplace/
the replace length would be strlen("bars\rreplace") = 12.
EDIT: Just to be clear, I hope to use this to move the cursor past the text that was affected by a substitute operation. I'd appreciate alternative solutions as well.
You have to use :help sub-replace-expression. In it, you use submatch(2) instead of \2. If the expression is a custom function, you can as a side effect store the original length in a variable, and access that later:
function! Replace()
let g:replaceLength = strlen(submatch(0))
" Equivalent of \2\rreplace
return submatch(2) . "\r" . 'replace'
endfunction
:s/\v(foo)|(bars)/\=Replace()/

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: Replace [aeiou]' with the respective accented letter

I know that VIM support digraph, and it would be perfect if it's possible to use with :s command, but I can't find a way to use it!
I think something like:
:%s/\([aeiouAEIOU]\)'/\=digraph(submatch(1)."!")/g
Would be perfect, but I didn't find a digraph function.
Thanks in advance.
EDIT
Ok, after a bit of diggin in the built-in VIM's functions, I've found tr and a first solution to the problem:
:%s/\([aeiouAEIOU]\)'/\=tr(submatch(1), 'aeiouAEIOU', 'àèìòùÀÈÌÒÙ')/g
However, I still want to know if there's a way to use digraph in expressions :)
function! Digraph(letter, type)
silent exec "normal! :let l:s = '\<c-k>".a:letter.a:type."'\<cr>"
return l:s
endfunction
This function will allow you to generate any digraph you want.
It simulates typing <c-k><char><char> by running it with the normal command and assigning it to the local variable s. And then it returns s.
After this function is defined and you can use it like this.
:%s/\([aeiouAEIOU]\)'/\=Digraph(submatch(1), "!")/g
Note: This was based off of the source code for EasyDigraph
Here's another approach using a hand-coded vim function (add to your vimrc):
" get a matching digraph for a given ASCII character
function! GetDigraph(var1)
"incomplete dictionary of digraphs, add your own....
let DigDict = {'a': 'à', 'e': 'è', 'i': 'ì'}
"get the matching digraph. If no match, just return the given character
let DictEntry = get(DigDict, a:var1, a:var1)
return DictEntry
endfunction
Call it like this :%s/\([aeiouAEIOU]\)'/\=GetDigraph(submatch(1))/g

Is there a way to make use of two custom, complete functions in Vimscript?

Is there a way to achieve the following in Vim?
command! -nargs=* -complete=customlist,CustomFunc1 -complete=customlist,CustomFunc2 Foo call MyFunction(<f-args>)
The user will be able to tab-complete two arguments when calling the function Foo from Vim command line. The auto-complete will pull from two different lists.
E.g.:
:Foo arg1 good<TAB> whi<TAB>
Pressing Tab completes the words:
:Foo arg1 goodyear white
There is sufficient information passed to completion function through
its arguments. Knowing current cursor position in the command line to
be completed, it is possible to determine the number of the argument
that is currently being edited. Here is the function that returns
that number as the only completion suggestion:
" Custom completion function for the command 'Foo'
function! FooComplete(arg, line, pos)
let l = split(a:line[:a:pos-1], '\%(\%(\%(^\|[^\\]\)\\\)\#<!\s\)\+', 1)
let n = len(l) - index(l, 'Foo') - 1
return [string(n)]
endfunction
Substitute the last line with a call to one of the functions
completing specific argument (assuming they are already written).
For instance:
let funcs = ['FooCompleteFirst', 'FooCompleteSecond']
return call(funcs[n], [a:arg, a:line, a:pos])
Note that it is necessary to ignore whitespace-separated words before
the command name, because those could be the limits of a range, or
a count, if the command has either of them (spaces are allowed in both).
The regular expression used to split command line into arguments takes
into account escaped whitespace which is a part of an argument, and
not a separator. (Of course, completion functions should escape
whitespace in suggested candidates, as usual in case of the command
having more than one possible argument.)
There's no built-in way for vim to do this. What I'd do in this situation is embed the logic into the completion function. When you set -complete=customlist,CompletionFunction, the specified function is invoked with three arguments, in this order:
The current argument
The entire command line up to this point
The cursor position
So, you can analyze these and call another function depending on whether it looks like you're on the second parameter. Here's an example:
command! -nargs=* -complete=customlist,FooComplete Foo call Foo(<f-args>)
function! Foo(...)
" ...
endfunction
function! FooComplete(current_arg, command_line, cursor_position)
" split by whitespace to get the separate components:
let parts = split(a:command_line, '\s\+')
if len(parts) > 2
" then we're definitely finished with the first argument:
return SecondCompletion(a:current_arg)
elseif len(parts) > 1 && a:current_arg =~ '^\s*$'
" then we've entered the first argument, but the current one is still blank:
return SecondCompletion(a:current_arg)
else
" we're still on the first argument:
return FirstCompletion(a:current_arg)
endif
endfunction
function! FirstCompletion(arg)
" ...
endfunction
function! SecondCompletion(arg)
" ...
endfunction
One problem with this example is that it would fail for completions that contain whitespace, so if that's a possibility, you're going to have to make more careful checks.

Resources