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.
Related
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.)
I use ack.vim, and have below keymap in my .vimrc,
nnoremap ;f :Ack! ^\"
to search for tags in my note files, which are lines that begin with double quote followed by non-space characters.
which works fine, since my note files lies in a common directory, say ~/notes.
Now, say at a git repo, eg, ~/code/some_repo, I want below keymap at that directory,
nnoremap ;f :Ack! ^\/\/\ *\"
I could manually set the keymap if work at given directory, but it soon become tedious.
So I wonder, how can I set keymap base on working directory when I start vim.
-- hopefully vimscript solution, with possible aid of bash command.
Have a look at https://stackoverflow.com/a/456889/15934
The solutions exposed can solve your question. Either by defining buffer specific mappings (:h :map-<buffer>, or by defining buffer variables that you could use in your mappings (:h b:var).
As #Luc suggested, I could use autocmd base on file name with BufEnter and BufCreate. Which works fine.
Then I ask myself, why stick to command only??
Just utilize vim builtin functions and simple vimscript statement!!
So, let's break my requirement to different parts.
get current working directory. -- getcwd().
pattern match on that. -- =~. What does =~ mean in VimScript?
do keymap base on match or not.
below is what's in my vimrc then,
"git, see also " grep
if getcwd() =~ "/code/repoA/"
nnoremap ;f :Ack! ^\/\/\ *\"
nnoremap ,a :!cd %:p:h && git add %<CR>
else
nnoremap ;f :Ack! ^\"
endif
I am trying to personalize my ~/.vimrc file.
Here is what I want: when the file name opened with vi is titi45.tex, and when I press <space> in normal mode, I want that the command make toto45
is executed. And if the file opened with vi is called titi65.tex, I want that
make toto65 is executed, and so on.
I tried to use a command
au FileType tex nmap <space> :w<CR>:!make<CR><CR>
in my .vimrc but I don't know how to match the file name and use the number.
Can you help me?
Mathieu
You are looking for :make %<. BTW why don't you compile within vim? Avoid :!make. Prefer :make, and check the help related to the quickfix mode (:h quickfix).
Your mapping would then be:
nnoremap <buffer> <silent> <space> :update<cr>:make %<<cr>
(the <buffer> part is very important, it makes sure your mapping won't leak to other filetypes; the other are not critical here, but good practices)
EDIT: Sorry I missed the exact requirement.
Then, you'll have to transform the target name. You'll to play with substitute() and :exe. But your substitution won't be a simple substitution. It looks like a rotating substitution. Solutions for this kind of substitution have been described over there: How do I substitute from a list of strings in VIM?
And IIIRC, there exist a plugin that does similar things.
In your case, I guess I would use a dictionary to define how names are substituted:
let k_subs = { 'toto': 'titi', 'titi': 'toto' }
nnoremap <buffer> <silent> <space> :update<cr>:exe 'make '.substitute(expand('%'), join(keys(k_subs), '\|'), '\=k_subs[submatch(0)]', '')cr>
NB: I haven't tested it.
If you want to get rid of the extension, it'd better be done in expand() argument.
Hum...
finally i use an additionnal script
#!/bin/bash
maRegex='source_(enonce|corrige)([0-9]+)$'
if [[ "${1}" =~ $maRegex ]]
then
commande="make enonce${BASH_REMATCH[2]}"
else
commande="make plouf"
fi
echo commande de compilation lancée: $commande
$commande
This script in launched by vimrc.
I realize that I can :nmap <leader>rc :!cat %<CR> to provide an easy set of triggers, but I would like to do this instead.
nmap <leader>rc :up :!cat %<CR> but it complains about needing only one filename. How do I get vim to recognize both commands, in series?
You are missing a <CR> after :up. <CR> tells vim you want a carriage return here.
nmap <leader>rc :up<CR> :!cat %<CR>
The reason up is complaining about multiple file names is that it sees :!cat and %<CR> as two arguments to up.
So the new macro executes
:up
:!cat %
instead of
:up :!cat %
(Side Note: you should probably use nnoremap instead of nmap)
ZyX recommends using the following mapping instead.
nnoremap ,rc :up\|execute "!cat" shellescape(#%, 1)<CR>
This uses | to separate commands and escapes the %. Escaping the % leads to a more robust mapping just incase the filename contains special characters.
Help for :h execute and :h shellescape
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.