I am trying execute a nested execute command within a vimscript. I know that this command works in ex mode:
g/\(^\n\|\%1l\).\_.\{-}\n$/execute "normal! vap:call MCformat()\<cr>"
I want to be able to run that command from within a script. I have tried a number of permutations of the following code but can't get it to work.
function! RunMCformat()
silent! execute "normal! g/\(^\n\|\%1l\).\_.\{-}\n$/execute \"normal! vap:call MCformat\(\)\<cr>\""
endfunction
Probably I am not escaping the string properly but I don't know where I am going wrong.
Because of the double quotes, you'd have to escape (i.e. double) the backslashes inside the /.../ pattern definition. However, the biggest problem is the first :normal!; :g[lobal] is an Ex command. So, you're lucky, you can just prepend :silent! (which invokes Ex commands like :global), are you should be done; no nested :execute is necessary:
function! RunMCformat()
silent! global/\(^\n\|\%1l\).\_.\{-}\n$/execute "normal! vap:call MCformat()\<cr>"
endfunction
In general, I would avoid nesting of :execute; that's not readable in any case. Rather, extract parts of your code into a function / custom command (in which you can use :execute), and invoke that.
Related
I'm using the following function + command to invoke the debugger in Vim:
function! TermDebugArm(executable)
packadd termdebug
let g:termdebugger="arm-none-eabi-gdb"
Termdebug a:executable
endfunction
command! -complete=file -nargs=1 TermDebugArm :call TermDebugArm(<f-args>)
Unfortunately, the Termdebug command gets the literal argument "a:executable" and not the actual value it should represent (i.e. the filename passed to the command that called the function).
What am I doing wrong?
You need to use the :execute command to build a command from strings, which will allow you to use the value of a:executable as a literal:
execute "Termdebug ".a:executable
Or you can use the feature of :execute that will join multiple arguments with a space, so you don't need an explicit concatenation:
execute "Termdebug" a:executable
See :help :execute.
I am trying to generate a filename using a shell function and pass it to tabnew.
How might I get a working equivalent of the following, where echo foo might be replaced by any arbitrary external command?
tabnew $(!echo foo)
To be clear, the desired result is for the shell command to be evaluated, and its output passed to tabnew. In the above example, the command should evaluate to tabnew foo.
You can accomplish this using execute and system to craft a tabnew command.
Here is a function you can modify and perhaps stick in your .vimrc:
function! TabNewFoo()
execute "tabnew ".system('echo "foo"')
endfunction
Invoke with:
:call TabNewFoo()
Or, a more generalized form:
function! TabNewFoo(command)
execute "tabnew ".system(a:command)
endfunction
Invoke with:
:call TabNewFoo("echo 'foo'")
I just started learning Vimscript.
I want to write a function that opens the output of a bash function in a split.
If I do something like :call OutputScript("echo 'hello'"), I want a new buffer to open that has "hello" in it.
I tried the following code:
function! OutputScript(cmd)
if winbufnr(2) == -1
silent vsplit output
else
silent wincmd l
endif
silent normal ggdG
read! a:cmd
endfunction
If I :call OutputScript("echo 'hello'"), my output window looks like this:
bash: a:cmd: command not found
What syntax do I need to use cmd as a parameter for read?
Juste replace read! a:cmd by:
exe "read! ".a:cmd
The read command, as most of vim commands, don't expect vim variables. In order to use variables with common commands, you have to "encapsulate" it into an exe command, which can understand any vim expression.
The . is the vim operator for concatenating two strings.
Sometimes when I write a wrong script, I really have no idea why it doesn't have the expected effects. For example, the following script:
nnoremap gr :call Look_For_String()<CR><C-L>
function! Look_For_String()
exe "grep -R " . expand("<cword>") . &dir
endfunction
The function is to search the current word under cursor in directory &dir, but it doesn't do as expected. So I want to know what command it executed.Is that possible?
What I would do in this case is replacing the :execute with :echomsg, re-source the script (:so %), and re-execute.
For more complex situations, you can set the 'verbose' option. With a value of 15, every Ex command is printed. Since the display often interferes with the script's functionality, it's often advisable to redirect the diagnostic output to a file by setting 'verbosefile', too.
I've written a few macros in my .vimrc for the version control system I'm using (Perforce) (please don't suggest the perforce plugin for vim, I tried it and I don't like it). They all work fine except the revert macro, which breaks due to a confirmation prompt (which I need so I don't accidentally fat-finger my changes away). It currently looks like this:
map <F8> :if confirm('Revert to original?', "&Yes\n&No", 1)==1 | !p4 revert <C-R>=expand("%:p")<CR><CR><CR>:edit<CR> | endif
This causes bash to complain when vim tries to load the file:
bin/bash: -c: line 0: syntax error near unexpected token `('
Looking at the buffer bash sees, it looks like the error is that vim sends it everything after the first pipe, not just the part meant for bash. I tried a few alternatives but I can't seem to make it work. I've got it to show confirm dialog correctly when I removed the pipes and endif (using shorthand if), but then vim complains after the user gives a response.
I think you want something along these lines:
:map <F8> :if confirm('Revert to original?', "&Yes\n&No", 1)==1 <Bar> exe "!p4 revert" . expand("%:p") <Bar> edit <Bar> endif<CR><CR>
Remember that :map is a dumb sequence of keystrokes: what you're mapping F8 to has to be a sequence of keystrokes that would work if typed. A <CR> in the middle of the :if statement doesn't mean ‘and press Enter when executing the command at this point if the condition is true’; it means ‘press Enter here when in the middle of typing in the :if command’, which obviously isn't what you want.
Building it up a piece at time, from the inside out:
There's a shell command you sometimes want to run.
That shell command needs to be inside an :if to do the ‘sometimes’ bit, and so have an :endif following it.
After a literal ! everything following is passed to the shell, including | characters which normally signify the start of another Vim command. That's reasonable, because | is a perfectly good character to use in shell commands. So we need some way of containing the shell command. :exe can do this; it executes the supplied string as a command — and its argument, being a string, has a defined end. So the general form is :if condition | exe "!shell command" | endif.
Your shell command has an expression in it. Using :exe makes this easy, since you can simply concatenate the string constant parts of the command with the result of the expression. So the command becomes :exe "!p4 revert" . expand("%:p") — try that out on its own on a file, and check it does what you want before going any further.
Putting that inside the condition gives you :if confirm('Revert to original?', "&Yes\n&No", 1)==1 | exe "!p4 revert" . expand("%:p") | edit | endif — again try that out before defining the mapping.
Once you have that working, define the mapping. A literal | does end a mapping and signify the start of the next Vim command. In your original the mapping definition only went to the end of the condition (check it with :map <F8> after loading a file) and the !p4 part was being run immediately, on the Vim file that defines the mapping! You need to change each | in your command into <Bar>, similarly to how each press of Enter in your command needs writing as <CR>. That gives you the mapping above. Try it by typing it at the command line first, then do :map <F8> again to check it's what you think it is. And only then try pressing F8.
If that works, put the mapping in your .vimrc.
Use of the pipe to string multiple vim commands together is not particularly well-defined, and there are numerous eccentricities. Critically, (see :help :bar) it can't be used after a command like the shell command :! which sees a | character as its argument.
You might find it easier to use the system() function.
E.G.
:echo system("p4 revert " . shellescape(expand("%:p")))
The shellescape() wrapper is useful in case you have characters like spaces or quotes in the filename (or have cleverly named it ; rm -rf ~ (Don't try this at home!)).
In the interest of creating more readable/maintainable code, you may want to move your code into a function:
function Revert()
if confirm('Revert to original?', "&Yes\n&No", 1)==1
return system("p4 revert " . shellescape(expand("%:p")))
endif
endfunction
which you would access by using the :call or :echo command in your macro.