1

I want to put a string function output in my vim when press xx. My current working example is:

nnoremap <silent> xx :call append('.', 'string_output')<CR>

However, this put a line without respecting the indentation. In current setting it set the whole line from beginning and do not respect the indentation. Based on vim help I can use append! but that lead to error on extra parenthesis. The syntax according to vim help is :{range}a[ppend][!], am I missing something?

As an alternative I tried setline in which I expects to complete the line from current cursor (not beginning of the line) so it respects the current cursor position (aka indentation).

1 Answer 1

2

Regarding the ! in specific, you're mixing up the append() function (which you can use with :call) with the Ex command :append. While they have the same name, they're pretty different things (as in general, in Vimscript, with functions and Ex commands.)

Functions such as append() and setline() will typically not preserve indentation. For one thing, since they can append or modify an arbitrary line of the buffer, which indentation to copy might be a hard question to answer... Also, Vimscript functions typically try to depend less on the behavior of options such as 'autoindent' and friends.

So, if you want to use the append() function and preserve indentation of the current line, you have to do that yourself. You can, for instance, use the indent() function, which will return the number of spaces that are equivalent to the indentation of the current line, together with the repeat() function to prepend those spaces into the appended string:

:call append(repeat(' ', indent('.')) . 'string_output')

There are quite a few limitations to this approach. For one, this will always indent using spaces and not tabs (regardless of the 'expandtab' setting), it will also indent unconditionally regardless of the 'autoindent' setting and will also only copy indentation of the current line and not respect 'indentexpr' which might decide on a different indentation for the next line...

For those reasons, it's often more productive to use an Ex command or a normal mode command (such as i or, in this case, o) to append or modify lines, when you want to preserve indentation and other features of the text that Vim typically managed during normal text editing.

The interface for :append is a little awkward (entering multiple lines ending on a line with just a dot), but it's still possible to use it from a mapping:

nnoremap <silent> xx :append<CR>string_output<CR>.<CR>

It might be easier to use the o Normal mode command though:

nnoremap xx ostring_output<Esc>

You also mentioned the ! in :append, but note that :append will by default do whatever the current 'autoindent' setting is set to do (either preserve or ignore indentation), so typically that's what you want... The :append! command will do the opposite of the current 'autoindent' setting, but in a sense it still depends on it.

I also imagine that the string_output in your question is just an example. Using the result of a function or an expression is much easier in a function call such as append(), but still can be done in :append or o by using the <C-R> shortcut to recall the contents of a register, possibly combined with the expression register = which allows you to enter an expression.

For example:

nnoremap <silent> xx :append<CR><C-R>='string_output'<CR><CR>.<CR>

(Note you need two <CR>s in a row, one to finish entering the expression for the expression register, and another to finish entering the line in :append.)

Also:

nnoremap xx o<C-R>='string_output'<CR><Esc>

In these examples, I still used 'string_output', but hopefully it's clear that any other valid Vimscript expression would be also possible here.

6
  • 1
    First thanks for multiple solutions, clarification and the description. I ended up using your first solution using indent(). The reason I endup with using append() instead of normal/Ex mode in the first place was bcz instead of 'string_output' I use concatenation of two external bash script's output. Also, I wanted the whole process in silent which means I don't want vim to throw a subshell, ask for ENTER and then append. Currently I'm working on your next solutions but right now I couldn't use them with system() and systemlist() commands in place of 'string_output'.
    – PiTao
    Commented Dec 9, 2021 at 22:27
  • 1
    @PiTao Nice that one of them worked for you already! I just tried nnoremap xx o<C-R>=system('...')<CR><Esc> with a command that produces a couple lines of output and that worked for me. You might want to use a trim(system(...)) to drop the final newline from there. If your system() command outputs odd/non-standard characters, that might present an issue... Feel free to post a follow up question about that if you think you could use more help with that!
    – filbranden
    Commented Dec 9, 2021 at 22:33
  • 1
    @PiTao You can also use the string concatenation operator . as in o<C-R>=system('foo').system('bar')<CR><Esc>.
    – filbranden
    Commented Dec 9, 2021 at 22:54
  • 1
    Nice. I really need to fundamentally take a course on vim. There are so many such tricks which I can't find easily. Thanks
    – PiTao
    Commented Dec 9, 2021 at 23:27
  • 1
    @PiTao If you'd like to learn more on Vimscript, this is an excellent place to start: learnvimscriptthehardway.stevelosh.com
    – filbranden
    Commented Dec 9, 2021 at 23:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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