How to expand function arguments in Vim command line? - vim

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>

Related

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 get the arguments during a custom defined command?

I have this custom command that I want to define. But I can't manage to get the arguments separately:
:command -nargs=2 :%s/<args1>/<args2>/gc // <args1> <args2> dont work
Anyone has some solution for this?
<args1> and <args2> doesn't exist. You invented this yourself. The easiest way to do this is to use a function wrapper and <f-args>:
fun! s:sub(search, replace)
execute ':%s/' . a:search . '/' . a:replace . '/gc'
endfun
command! -nargs=+ Replace call s:sub(<f-args>)
<f-args> splits the the command arguments at whitespace and adds proper quoting and commas, in order to pass it to a function.
Using a function wrapper also has the added benefit of resetting the #/ register after its finished running. If you don't want this, then explicitly assign it with let #/ = a:search.
Other than this, almost everything about your :command call is wrong:
-nargs=2 isn't supported − see :help :command-nargs
You forgot to fill in the command name.
// isn't used to denote comments in VimScript; " is used for that.
See :help :command for a full description of the syntax.

Calling another user command inside the user defined command

Is it not possible to call another user defined command in a user defined command?
I tried the following two lines but neither of them worked:
command! GetRapidLinks FindRapidLinks|MatchesOnly
command! GetRapidLinks :FindRapidLinks|:MatchesOnly
The Vim help reads:
You cannot use ":X", ":Next" and ":Print"
The context of this restriction is not clear. I guess one cannot use those in a user defined command, right?
This is probably because you didn't define the :FindRapidLinks command with -bar; without it, the command "eats" the entire remaining arguments (cp. :help command-bar). So, either redefine:
:command! -bar FindRapidLinks ...
:command! GetRapidLinks FindRapidLinks|MatchesOnly
or work around this (for cases when you cannot redefine the other command) via :execute:
:command! GetRapidLinks execute 'FindRapidLinks'|MatchesOnly
As a general rule, use -bar unless your custom command needs to be passed arguments that contain special characters like |.
Actually, what the help means is, you can't call a user command :Next or :X, because those are the few builtin commands, that start with a capital letter. I am not sure, why your vim actually says, you can't define a :Print command, as it is possible to define your own custom :Print command (since the builtin command is only an alias for :print anyhow)
I am not sure, what exactly you are trying to achieve here, so I can't answer the first part of your question. You can however in a function or in a custom command call another custom command.
Perhaps try backticks ` (not to be mistaken with quotes ' or ")
for e.g.
command! GetRapidLinks `FindRapidLinks` | MatchesOnly
or
command! GetRapidLinks $(FindRapidLinks) | MatchesOnly

vim: how to replace a variable with its value in ~/.vimrc

I would like something similar too this in my .vimrc.
let dir=“/home/user/Downloads/”
set path=$dir
nnoremap gr :grep '\b<cword>\b' $dir/*<CR>
The code above is wrong of course, but maybe you can understand what I am trying to do. I would like to set path to the value of dir to /home/user/Downloads/, and replace the word dir in the third line with the value of dir. I tried and failed, can anyone tell help me out, any help appreciated!
First of all, there are fancy quotes; you need to use plain (") ones. Other than that, the :let is okay.
let dir = "/home/user/Downloads/"
You could use :execute to evaluate the defined variable with :set, but it's easier to use :let, because it can change Vim options, too, with the special notation &{optionname}:
let &path = dir
For the mapping, if dir doesn't change during runtime, it's easiest to use :execute. Note how the quoted backslashes must be escaped (i.e. doubled):
execute "nnoremap gr :grep '\\b<cword>\\b' " . dir . "/*<CR>"
All that information is part of :help eval. Learn how the excellent and comprehensive help is structured; all the information is in there (you just need to know how to find it)!
You must use this notation:
let variable_name = "value"
and use straight quotes.
To set path:
set path=/home/user/Downloads/
or to append a directory to path.
set path+=/home/user/Downloads/
Path is an Vim variable which does not seem very appropriate to use if you are going to only use it for this one remapping. It would be better to declare you own variable, as path can also have many directories within it which will not work with grep.
let g:search_path="/path/to/your/dir"
nnoremap gr :grep '\<<cword>\>' <C-R>=eval("g:search_path")<CR>
Ctrl+R lets us insert a register here, when then use that to call the expression register, which we use to evaluate g:search_path.
Check out :help expr-register for more on that!
This will evaluate your g:search_path variable on every execution of the mapping, allowing you to change the path and not have to remap gr each time.

File name expansion in vim when using set option=value

I'm trying to add an autocmd to vim that will execute whenever I open a file in a certain subdirectory and that sets the search path. Unfortunately path name expansion doesn't seem to work inside a set command.
Specifically I'd like to have a line like this in my vimrc:
setlocal path+=**;%:p:h
But this will just give me the literal value. Just calling expand() doesn't work either. Is there a way to get variable expansion to work here?
What about:
execute 'setlocal path +=**;' . fnameescape(expand('%:p:h'))
There's no need for the expansion of the current file's directory; just adding . to path will do. From the help:
To search relative to the directory of the current file, use:
:set path=.
Use
let &l:path.=(empty(&l:path)?(''):(',')).'**;'.escape(expand('%:p:h'), ',\*; ')
. This is much cleaner then using :execute 'setlocal path', especially knowing that fnameescape() was designed to escape paths for commands, not for options and I can say it is not really safe to use it here: it definitely is not going to escape comma and semicolon and add additional escape for space (one for escaping for :set, one for the option itself). (empty(&l:path)?(''):(',')) is here to imitate the behavior of set+=.

Resources