How to distinguish between line-address and line-range in vim? - vim

This is a simple user-defined function:
fun! Foo() range
echo a:firstline a:lastline
endfun
:5call Foo() and :5,5call Foo() give me the same result.
However, :5j and :5,5j give me different results.
Can I write a function which behave like join?
How does join distinguish between line address and line range?

By defining a custom :command, the -range and -count attributes allow you better control over how the range is consumed. However, I think even that won't allow you to exactly duplicate the behavior of :join. The interface for custom Vim commands is not as rich as what is available to built-in commands.
As a workaround, you could use histget('cmd', -1) to get the command-line that invoked your command, and parse the exact command invocation, including the original range (which can then be re-used by passing it to another command, but doing line arithmetic with it is problematic, since it's the raw range, not the actual line numbers). The workaround will only work for interactive commands, is brittle, and demands some effort. Maybe you can avoid the issue altogether by defining two different commands instead.

Related

Various ways to execute a function in vim

I've been doing some trial-and-error on how functions can be called, and it seems like the following is my understanding:
From the command line, typing in :call MyFunction()
From the command line, typing in :call execute('call MyFunction'), where execute essentially performs a string escape (if that's the correct term?) to pass back to the first call param.
From within a function or vim file, typing in call MyFunction(). In other words, each line in a vim function/file acts like the command-line.
From within a function or vim file, typing in call execute('call MyFunction')
Is that a correct understanding of the various ways to call a function? Are there any other possible ways to do it?
I don't really understand what you are doing, but if you ask if there are other ways to call a function, yes, there are.
For example,
the eval(...) can call another function
echo getline('.') or something like this
:s/../\=getline(...)
in expr mappings
...
Simply put, in almost any place when a vimscript can be evaluated, a function can be called.

Vim Custom Replace Function

Note: I'm currently using Neovim v0.2.2 (But I believe this shouldn't change anything related this post)
I'm currently attempting to create a function within vim that allows for easily replacing text.
I understand I can create a shortcuts and macros and all that, but ideally I just want to give 2 args, and not think about what specifics go where as this can interupt my thought process.
So I decided to just have a simple wrapper disguised as a function (Which I will create a command wrapper for as well, once I figure out what I did wrong here)
function! VisualReplace(query, replacement)
" Example = '<,'>s/query\%V/replacement/g
'<,'>s/a:query\%V/a:replacement/g
endfunction
As you can see, it's a very simple function that just applies the args in it's respective position, Yet, this fails even when called as a function using : call VisualReplace('some_query', 'some_replacement'
Alternatively, if you simply use the Example I have commented out directly, there's no issue, So I was hoping someoen could enlighten me on a potential fix
If need be, I could possibly look into string building & build it incrementally
Error msg:
Pattern not found: a:query\%V
General theory
Vimscript is evaluated exactly like the Ex commands typed in the : command-line. There were no variables in ex, so there's no way to specify them. When typing a command interactively, you'd probably use <C-R>= to insert variable contents:
:sleep <C-R>=timetowait<CR>m<CR>
... but in a script, :execute must be used. All the literal parts of the Ex command must be quoted (single or double quotes), and then concatenated with the variables:
execute 'sleep' timetowait . 'm'
Your function
In order to get the a:query and a:replacement arguments into :substitute, use :execute and either string concatenation or printf():
function! VisualReplace(query, replacement)
execute "'<,'>s/" . a:query . '\%V/' . a:replacement . '/g'
endfunction
Additional critique
Passing a range to a function is so common, there's special syntactic sugar for it: The range attribute to :function, and a:firstline and a:lastline implicit arguments. Read more about it at :help function-range-example. While your use case here seems to be specifically for visual mode, in general it's useful to keep the scope of functions as broad as possible.
#Ingo Karkat answered perfectly. However, I feel like there might be some workflow alternatives which might help. (Assuming you aren't trying to script this behavior)
Visual Star
It looks like you are build a search based on a visual section. You may want to consider using a visual-star plugin to simplify the process. Here is a an example of a visual star mapping:
xnoremap * :<c-u>let #/=#"<cr>gvy:let [#/,#"]=[#",#/]<cr>/\V<c-r>=substitute(escape(#/,'/\'),'\n','\\n','g')<cr><cr>
This mapping will allow you to visually select text and then execute * to make it a search pattern. Similar to how * works in normal mode on the current word.
Search refining
I get the impression that you are trying to refine your search pattern. Vim has a nice way of doing this with q/ or pressing <c-f> while searching with /. See :h q/. This will bring up the command-line window which will allow you to edit the query/command-line with all your normal Vim keys.
Search and Replace with gn motion
Sometimes doing a substitution is just overkill or doesn't quite fit the situation right. You can mimic a search and replace by using the gn motion to operate on a search pattern. By using an operator and the gn motion together you can use the dot command, ., to repeat the action easily.
Example:
/foo
cgnbar<esc>
Now you can use . to repeat the foo -> bar replacement. Use n to skip. You can use other operators as well, e.g. gU to uppercase.
See :h gn and :h operator for more help.
Related Vimcasts episodes:
Refining search patterns with the command-line window
Operating on search matches using gn
Search for the selected text

how to understand these vim scripts

I have two question about understand those vim script. please give some help,
Question 1:
I download a.vim plugin, and i try to read this plugin, how to understand the below variable definition? the first line I can understand, but the second line, I don't know exactly "g:alternateExtensions_{'aspx.cs'}" means.
" E.g. let g:alternateExtensions_CPP = "inc,h,H,HPP,hpp"
" let g:alternateExtensions_{'aspx.cs'} = "aspx"
Question 2:
how to understand "SID" before the function name, using like below function definition and function call.
function! <SID>AddAlternateExtensionMapping(extension, alternates)
//omit define body
call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
call <SID>AddAlternateExtensionMapping('H',"C,CPP,CXX,CC")
thanks for you kindly help.
let g:alternateExtensions_{'aspx.cs'} = "aspx"
That is an inline expansion of a Vimscript expression into a variable name, a rather obscure feature that is rarely used since Vim version 7. See :help curly-braces-names for details. It is usually used to interpolate a variable, not a string literal like here ('aspx.cs'). Furthermore, this here yields an error, because periods are forbidden in variable names. Newer plugins would use a List or Dictionary variable, but those data types weren't available when a.vim was written.
To avoid polluting the function namespace, plugin-internal functions should be script-local, i.e. have the prefix s:. To invoke these from a mapping, the special <SID> prefix has to be used instead of s:, because <SID> internally gets translated into something that keeps the script's ID, whereas the pure s:, when executed as part of the mapping, has lost its association to the script that defined it.
Some plugin authors don't fully understand this unfortunate and accidental complexity of Vim's scoping implementation either, and they put the <SID> prefix also in front of the function name (which works, too). Though it's slightly more correct and recommended to write it like this:
" Define and invoke script-local function.
function! s:AddAlternateExtensionMapping(extension, alternates)
...
call s:AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
" Only in a mapping, the special <SID> prefix is actually necessary.
nmap <Leader>a :call <SID>AddAlternateExtensionMapping('h',"c,cpp,cxx,cc,CC")
<SID> is explained in :help <SID>:
When defining a function in a script, "s:" can be prepended to the name to
make it local to the script. But when a mapping is executed from outside of
the script, it doesn't know in which script the function was defined. To
avoid this problem, use "<SID>" instead of "s:". The same translation is done
as for mappings. This makes it possible to define a call to the function in
a mapping.
When a local function is executed, it runs in the context of the script it was
defined in. This means that new functions and mappings it defines can also
use "s:" or "<SID>" and it will use the same unique number as when the
function itself was defined. Also, the "s:var" local script variables can be
used.
That number is the one you see on the left when you do :scriptnames, IIRC.

How to expand function arguments in Vim command line?

Vim's Utl plugin offers a convenient way for doing web queries from within the editor. When called directly from the command line, a dictionary lookup can be done like this:
:Utl ol http://dict.leo.org/?search=my+search+term
What's the correct way for defining a custom command with the same purpose (my+search+term being user input)? I can't seem to get <f-args> right with this one:
command -nargs=1 SearchLeo :exe ":Utl ol http://dict.leo.org/?search=" . expand("<f-args>")
What's the correct way of defining function arguments here? Or should I turn this into a more complete function? Thanks!
You probably don't need expand() here; it's just for expanding globs (like *.txt) or the special variables like % for the current file.
You're quoting the argument twice, once through <f-args> (<q-args> would be slightly more correct, though it only matters with a variable number of arguments), once literally.
Use this:
command -nargs=1 SearchLeo :exe ":Utl ol http://dict.leo.org/?search=" . <q-args>

How to restrict operations to certain lines?

I have to work on some relatively huge code files in vim.
How do I restrict some operations like find-next normal-n and others to a certain function / block?
How would I visually know if I'm within that block or outside it?
Looking and line numbers seems awkward, specially that the line numbers I need to work with are generally 5 digits long!
You can set marks at the beginning and end of your block using m (e.g. ma, mb) and then refer to them in the range of a ex command as :'a,'b.
Like bignose said you can use a visual block to create an implicit region for a command, which can be passed to an ex command using :'<,'>
You can also use regexes to delimit a block (e.g. for all the lines between start and end use :/start/,/end/
For example, to make a substitution in a range of lines:
:'<,'>s/foo/bar/g
:'a,'bs/baz/quux/g
:/harpo/,/chico/s/zeppo/groucho/g
The last visually selected range is remembered so you can reuse it without reselecting it.
For more on ranges, see :help range
You can further restrict yourself within a range by using g//. For example, if you wanted to replace foo with bar only on lines containing baz in the selected range:
:'<,'>g/baz/s/foo/bar/g
When you define a new ex command, you can pass the range given to the ex-command using as <line1>,<line2>. See :help user-commands for more on defining ex-commands.
Within a vimscript function, you can access an implicitly passed range using a:firstline and a:lastline. You can detect your current linenumber using line('.'), and detect whether you're inside the block using normal boolean logic (a:firstline <= line('.') && line('.') <= a:lastline). See :help functions for more on vimscript functions.
Another approach, is to use vim's inner i and single a selectors. For example, to delete the entirety of a double quoted string, use da" in normal mode. To leave the quotes, use di". See :help object-select for more.
Vimtips has exactly what you were looking for:
Search in current function
See also :help pattern-atoms
Vim uses Shift-v to select by lines. Having selected a series of lines, many commands will then be restricted to the selection.
I don't think the search commands (/, n, etc.) are restricted that way though.
For commands like search and replace, you can easily limit yourself to a couple of lines:
:.,+3s/foo/bar/cg
replaces every occurrence of "foo" in the current line and the following 3 lines with "bar". I don't think you can do that for search, though.

Resources