7

I would like to define a vim macro that breaks for user input at certain times, is this possible?

EDIT: Turns out I ment recordings (q), not macros

It is possible to use the input command in a recording, but it's more trouble than it's worth.

I first mapped insert input escape to a key

:map <F2> a<C-R>=input('input: ')<CR>

then I made this recording in the q register

name: 

and pasted it into a new tab

iname: ^[

And after the final escape I pressed <C-V><F2> making the line:

iname ^[^[OQ

That I yanked back to the q buffer then used the macro, letting me use the input function. It works, but terribly.

everett1992
  • 2,351
  • 3
  • 27
  • 38

4 Answers4

3

Yes. See the function input({prompt}, [, {text} [, {completion}] ]). There is even inputdialog({prompt} [, {text} [, {cancelreturn}]]), for a dialog popup.

fork0
  • 3,401
  • 23
  • 23
  • vim is just awesome! is there ANY question like "can i do xxx in vim" with a no as answer? :) – moritz Jul 19 '12 at 19:09
  • input() doesn't seem to be available in macros (not even through `Ctrl-R=input('prompt: ')`) but if you're okay with normal mappings or functions it will do the job nicely. – mogelbrod Jul 19 '12 at 19:09
  • @VictorHallberg: Hmm. Just (re)tried. Works for me (Vim 7.3.547). Just in case, what do you mean by _macro_? Not what `:help macro` explains? – fork0 Jul 19 '12 at 19:14
  • @fork0: How did you do it? `qq i=input('>')testq` will insert `test` when played since it was part of the macro :( Edit: turns out that what I considered macros are called recordings in vim, my bad :) – mogelbrod Jul 19 '12 at 19:18
  • @mogelbrod: Ah, I see. No, I just did a mapping (which is what `:help macro` begins with). `q` does not work, indeed. Sadly. – fork0 Jul 19 '12 at 19:24
  • I was also thinking of recordings (q) as well. Mapings still get the job done, but it's unfortunate that you cannot watch the effect as you create it. – everett1992 Jul 19 '12 at 19:33
  • @moritz first comment. (it's a joke) Well you can't ask "Can I write all the code in my head in vim absolutely correct with just 5 ?" and get an answer :) – Dimitar Slavchev Aug 01 '12 at 14:09
2

If you use input() inside a mapping or macro, the remaining characters will be taken as input, which is not what you want. Vim offers the inputsave() and inputrestore() functions to temporarily suspend reading from the mapping character stream.

Based on mogelbrod's answer, this doesn't work; the itest is read in as input:

oBEFORE ^R=input('prompt> ')^Mitest

But this does:

function! Input()
    call inputsave()
    let text = input('prompt> ')
    call inputrestore()
    return text
endfunction
oBEFORE ^R=Input()^Mitest

Unfortunately, because <C-R> takes an expression, we cannot put the commands inline, but have to define a separate Input() function.

Ingo Karkat
  • 167,457
  • 16
  • 250
  • 324
  • Hi there, I don't think this works any more. The input is still saved on the macro register. – Liarokapis Alexandros Sep 21 '20 at 08:20
  • @LiarokapisAlexandros: Still working here, Vim 8.2.1719, and failing to do so would be a bug in Vim, as `inputsave()` et al. are expressly created for this. – Ingo Karkat Sep 21 '20 at 14:26
  • vim 8.0 here, it's kind of curious, the text entered while input prompts is actually contained in the macro buffer so it appends the text to next repetition's input. I will have to check with mappings too. – Liarokapis Alexandros Sep 22 '20 at 08:45
1

Unfortunately it doesn't seem to be possible. You can trigger input() inside a macro, but continuing on afterwards doesn't seem to be possible as any additional input recorded is inserted into the input prompt.

Yank the line into a named register ("qY) and run it (@q) to try it out.
Note: replace ^R and ^M with Ctrl-V Ctrl-R/M (see :help i_CTRL-V).

  • oBEFORE ^R=input('prompt> ') - works
  • oBEFORE ^R=input('prompt> ')^Mitest - works, but inserts itest into the prompt
  • oBEFORE ^R=input('prompt> ')<CR>test - fails
mogelbrod
  • 2,246
  • 19
  • 20
0

I have collected information from this and other threads and written this script:

function! MacroInterrupt()
    "call inputsave()
    if strlen(reg_recording()) == 0
        if mode() == 'n'
            call inputsave()
            let tmp_col = col('.')
            let tmp_line = line('.')
            let text = input('input:')
            let line = getline('.')
            call setline('.', strpart(line, 0, col('.') - 1) . text . strpart(line, col('.') - 1))
            call cursor(tmp_line, tmp_col + strlen(text))
            call inputrestore()
            return text
        else
            call inputsave()
            let text = input('input:')
            call inputrestore()
            return text
        endif
    else
        echo "Interrupt added to macro"
        call setreg(reg_recording(), getreg(reg_recording()) . "\<F2>")
        "echo getreg("q")
    endif
    "call inputrestore()
endfunction

map <F2> :call MacroInterrupt() <CR>
inoremap <buffer><expr> <F2> MacroInterrupt()

I hope this can help especially people attempting the same.