I am a Vim newbie with one week experience and I'm already enjoying it.

I've successfully written an (ugly) command+function+mapping with which I can extract some code to a new variable in JavaScript. This is my code, it works with motions (\jsexviw) and selection in visual mode:

command! -range -nargs=1 JsExVar normal `<v`>d^[i<args>^[Ovar <args> = (^[pa);^[

function! FnJsExVar(type)
    silent exec 'JsExVar '.input("Variable name: ")

vnoremap <silent> <expr> <Leader>jsexv ":JsExVar ".input("Variable name: ")."<cr>"
nnoremap <silent> <Leader>jsexv :set opfunc=FnJsExVar<CR>g@

I still don't fully understand the different ways of executing things, so I assume the code can be improved and cleaned a lot. Thanks in advance for any correction and suggestion.

----- EDIT ------

I've refactored a bit to avoid duplicated input("Variable name: ") but now I have a weird (0) param when calling the function from the command:

command! -range JsExVar call ExecJsExVar(0) 

vnoremap <silent> <expr> <Leader>jsexv ":JsExVar<cr>"
nnoremap <silent> <Leader>jsexv :set opfunc=ExecJsExVar<CR>g@

function! ExecJsExVar(type)
    let varname = input("Variable name: ")
    silent exec "normal `<v`>di".varname."^["
    silent exec "normal Ovar ".varname." = ^["
    silent exec "normal pa;^["

----- (more) EDIT ------

Ok, it wasn't really working with motions. I think this works... but the question is: can this be done with one function?

command! -range JsExVar call JavascriptExtractVariable(0) 
vnoremap <silent> <Leader>jsxv :JsExVar<cr>
nnoremap <silent> <Leader>jsxv :set opfunc=JavascriptExtractVariable2<CR>g@

function! JavascriptExtractVariable(type)
    let varname = input("Variable name: ")
    silent exec "normal `<v`>di".varname.""
    silent exec "normal Ovar ".varname." = ("
    silent exec "normal pa);"

function! JavascriptExtractVariable2(type)
    let varname = input("Variable name: ")
    silent exec "normal `[v`]di".varname.""
    silent exec "normal Ovar ".varname." = ("
    silent exec "normal pa);"

(re-edited because wrong code)


Using this answer I've been able to make it work without the command and with one function. And I learnt what that argument was for, how to use optional arguments and the func-range modifier:

vnoremap <silent> <Leader>jsxv :call JsExtractVariable(visualmode(), 1)<cr>
nnoremap <silent> <Leader>jsxv :set opfunc=JsExtractVariable<CR>g@
function! JsExtractVariable(vt, ...) range
    let varname = input("Variable name: ")
    silent exec "normal ".SelectByMode(a:0)."di".varname."^["
    silent exec "normal Ovar ".varname." = (^[pa);^["

function! SelectByMode(isvisual)
    return (a:isvisual ? "`<v`>" : "`[v`]")

I post this as an answer because it's the kind of improvement I was looking for. Any other suggestion will be welcome, thanks.

  • "I've been able to make it work without the command and with one function." There's two functions in your code ;). – Rich Feb 20 '18 at 11:04
  • Hehe, @Rich you're right, but you know, 2nd function it's only for reusing... not like the ugly JavascriptExtractVariable / JavascriptExtractVariable2 functions :P – raul.vila Feb 20 '18 at 11:16
  • I was attempting to be funny :(. – Rich Feb 20 '18 at 11:17
  • It looks like a bug in my irony detector, mea culpa :D – raul.vila Feb 20 '18 at 11:22

Here's a couple of things you might not already know:

Including Special Characters in String Constants

Instead of inserting special characters literally, by typing e.g. Ctrl-VEsc, you can use the forms described in :help string:

silent exec "normal Ovar ".varname." = (\<Esc>pa);\<Esc>"

This makes it easier to see what the code is doing when viewed in contexts which do not highlight the special characters, and is better for cut and paste (I had to edit all the ESC characters in your code when copying it into my .vimrc to try it out)

Simple Variable Extraction

This isn't really the sort of information you were looking for, but you can perform a more basic version of your edits by using Ctrl-A in insert mode to insert the previously inserted text.

From visual mode:

  1. Type c followed by your new variable name and press Esc to leave insert mode.
  2. Type Ovar (including the trailing space)
  3. Press Ctrl-A to add in the variable name again.
  4. Type = ;<Esc> (including the leading space) to complete the line.
  5. Press P to put the value of the new variable before the semi-colon.

To do the same from normal mode, simply add in the motion after you press c in step 1.

You could of course extract Steps 2–5 into a normal mode mapping which you could then use after performing step 1 manually (from either visual or normal mode):

execute "nnoremap <Leader>jsxv Ovar \<C-A> = ;\<Esc>P"
  • Thanks @Rich, great info! I didn't know <C-A>, it looks usefull. And I've been using special characters in both styles with some fears of the differences. – raul.vila Feb 20 '18 at 11:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.