I want to map <M-o> to quickly look up buflist from command-mode.
Here's an example normal-mode mapping:
nnoremap <M-o> :ls<CR>
Here's an attempt to do the same for command mode using execute():
cnoremap <M-o> <C-r>=execute(':ls')<CR>
This however simply appends whitespace-separated :ls results to the command line, with no side effect of the :ls table actually showing.
Is there a way to execute a command without leaving command-mode?
Update:
Closest I've come to cracking this using Vimscript is the following:
function! RunCmdFromCmdline(cmd)
let l:cmd_line = getcmdline()
echo execute(a:cmd)
return l:cmd_line
endfunction
cnoremap <M-o> <C-\>eRunCmdFromCmdline(':ls')<CR>
The echo makes it so the results of a:cmd do pop on the screen, however in invisible font. I tried playing with :redraw <bar> in different combinations with no success.
Exiting from the command mode from within a <C-r>= or <C-\>e prompt does not seem possible.
You can save the contents of the current command line in a register, then run ls and paste the contents of the register:
cnoremap <esc>o <home>let #x="<end>"<cr>:ls<cr>:<c-r>x
So if command line looks like this:
:echo 'some'
type <M-o> to get:
:ls
1 %a + "filename" line 1
:echo 'some'
This works. It uses global variables, but arguably this is an acceptable use for them, since there's only a single command-line you can be in at a time.
function! SaveCmdline()
let g:save_cmdline = getcmdline()
let g:save_cmdpos = getcmdpos()
return ''
endfunction
function! RestoreCmdline()
call setcmdpos(g:save_cmdpos)
return g:save_cmdline
endfunction
cnoremap <M-o> <C-\>eSaveCmdline()<CR>ls<CR>:<C-\>eRestoreCmdline()<CR>
This works with whatever quotes or unfinished syntax you have in your command-line, restores the full command-line and the cursor to the position it was before running the command, doesn't mess with any registers (only touches the two global variables) and doesn't mess with command-line history either (see q: command for history.)
It does have one tiny bug, in that cnoremap actually also works when you're typing a search expression (after / or ?) among a few other sub-modes (see getcmdtype() for a list of those.) If you use the mapping there, it will search for "ls" and then restore your search term as an Ex command, oops...
You can probably use getcmdtype() to detect that and possibly only perform the actions while you're typing an Ex command and not in the other modes.
UPDATE: This fixes the last part, only executing the command while in the Ex command-line, not in search or other context. It also makes the interjected command parameterizable.
function! CmdlineExecute(cmd)
if getcmdtype() != ':'
return ''
endif
return "\<C-\>eSaveCmdline()\r".a:cmd.
\ "\r:\<C-\>eRestoreCmdline()\r"
endfunction
cnoremap <expr> <M-o> CmdlineExecute('ls')
(Note this still needs the definition of the two functions above.)
Related
Does anyone know why I'm getting the following error? I'm mapping the following function
function! ToggleCompileErrors()
:if w:syntastic_is_open == 1
:call SyntasticReset()<CR>
let w:syntastic_is_open = 0
:else
:call SyntasticCheck()<CR>
let w:syntastic_is_open = 1
:endif
endfunction
With this command
command ToggleCompileErrors :call ToggleCompileErrors()
and it is getting called by the following keymapping
nnoremap <Leader>b :ToggleCompileErrors<CR>
And I don't know if it makes a difference but I am using neovim 0.2.2
Watch your modes. That <CR> :help key-notation is necessary for mappings, but not inside functions, which use Ex commands.
Likewise, you don't need to prefix commands inside a function with : (and this is rather odd here, especially because you're not even consistent about it). : is a normal mode command that enters command-line mode. As commands in a function already are Ex commands, the : is not needed.
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.
I have a small operator mapping for use with the Pydoc plugin. The code for it is below:
nnoremap <buffer> <localleader>d :set operatorfunc=<SID>PydocOperator<cr>g#
vnoremap <buffer> <localleader>d :<c-u>call <SID>PydocOperator(visualmode())<cr>
function! s:PydocOperator(type)
let l:orig_register = ##
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
execute 'Pydoc ' . shellescape(##)
let ## = l:orig_register
endfunction
However, vim throws an error:
E116: Invalid arguments for function <SNR>117_ShowPyDoc
The same error happens if I copy some text manually and run this command:
execute 'Pydoc ' . shellescape(##)
This is very odd, considering that the :Pydoc should work as a normal command, taking one argument as its input. I looked at the code where the :Pydoc command is defined, (that line of code is here) and discovered that passing an argument to the :Pydoc command that is in quotes might be causing an issue. So I ran :Pydoc 'sys' to see if it would throw the same error as the operator mapping, which it did. So if it is having an issue with the quotes around the argument, how do I format the execute command so that it doesn't give an invalid argument?
The shellescape() function is not necessary for the :Pydoc command. shellescape includes quotes in the returned string, which causes :Pydoc to self destruct. However, if the command were :grep, for instance, shellescape would need to be used.
Relevant help topics:
:help shellescape()
:help 'operatorfunc'
:help :map-operator
I've used Vim to code my Go script for a month, and I'm pretty comfortable with :Import and :Drop vim command when it comes to importing and unimporting any packages.
However, I've more and more tired of typing such a long word which includes Capital letter so frequently, so I came to think that if I can use :i and :d other than :Import and :Drop, I'd be satisfied even more. However, I'm not sure whether I can define such a command in Vim, since it involves 1) turning into command-line mode, and 2) taking a variable which is determined dynamically in typing.
So for example, if I import encoding/csv package, all I want to type in is :i encoding/csv, etc... Can I define those commands?
I stumbled upon this question, but I cannot get what it is doing nor I'm not sure whether the answer applies to this question in the first place. Can I solve the issue by replacing all of w and W on the linked post to i or d?
Also, does it differ from the case that doesn't take a variable (say, Fmt command to go-format the script)?
Thanks.
EDIT 2: As pointed out by glts below, it's better to use cnoreabbrev or cnorea as pointed out in this answer.
Better working example (paste into ~/.vimrc):
cnorea <expr> i ((getcmdtype() is# ':' && getcmdline() is# 'i')?('Import'):('i'))
cnorea <expr> d ((getcmdtype() is# ':' && getcmdline() is# 'd')?('Drop'):('d'))
EDIT: Simple answer. Just using cabbrev or ca (command abbreviation) seems to work:
WORKING EXAMPLE (Paste this in ~/.vimrc):
ca i Import
ca d Drop
Working on vim 7.3, Ubuntu 64 bit.
ORIGINAL ANSWER (more complex):
According to http://vim.wikia.com/wiki/Replace_a_builtin_command_using_cabbrev :
You can use :command to define your own commands, but user-defined commands must start with an uppercase letter to avoid confusion with built-in commands.
So, using :command, you can probably use :I and :D, but not :i and :d.
It goes on to say:
Suppose you have a user-defined :E command that you want to use to override the default :e command. You could do the following:
:cabbrev e <c-r>=(getcmdtype()==':' && getcmdpos()==1 ? 'E' : 'e')<CR>
The (getcmdtype()==':' && getcmdpos()) makes sure the replacement happens only in the first column of the command line (i.e. not later in the line, where it is most likely NOT intended to be used as a command, and not on the search line, which is also affected by cabbrev).
If you do this a lot, it would be useful to define a function to do it for you. Use this to quickly and easily define lowercase abbreviations for whatever command you want:
function! CommandCabbr(abbreviation, expansion)
execute 'cabbr ' . a:abbreviation . ' <c-r>=getcmdpos() == 1 && getcmdtype() == ":" ? "' . a:expansion . '" : "' . a:abbreviation . '"<CR>'
endfunction
command! -nargs=+ CommandCabbr call CommandCabbr(<f-args>)
" Use it on itself to define a simpler abbreviation for itself.
CommandCabbr ccab CommandCabbr
This not only creates the function, but also provides the (lowercase!) command :ccab to define such abbreviations "on the fly".
So using a function looks like one way to go if you want to use lowercase :i and :d.
The cmdalias.vim - Create aliases for Vim commands plugin allows you to set up lowercase aliases, e.g.:
:Alias i Import
A simple solution:
nnoremap <leader>i :Import<Space>
nnoremap <leader>d :Drop<Space>
In normal mode, <leader>i populates the command-line with
:Import | <--- cursor
ready for you to type in or <tab>-complete an argument.
Better:
nnoremap <leader>i :Import <C-d>
nnoremap <leader>d :Drop <C-d>
<leader>i populates the command-line with
:Import | <--- cursor
and shows a list of possible completions.
Even better:
set wildcharm=<C-z>
nnoremap <leader>i :Import <C-z>
nnoremap <leader>d :Drop <C-z>
Supposing you have wildmenu already setup, <leader>i populates the command-line with
:Import firstpackagenameinthelist
with the wildmenu open and ready for <tab>bing.
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.