vim r! in cursor position

Solution 1:

With ":read" Vim will always insert the output on a new line. The solution is unfortunately not simple.

You may insert the output of a command at the current cursor position when you are in insert mode by pressing ControlR then typing =system('command')Enter. Unfortunately, if the command's output has a trailing newline (as most will) that will also be inserted, so your line will be broken.

That can be fixed by adding a substitute() call to strip trailing newlines, but that makes the command more trouble than it is worth to type out by hand.

The ultimate solution is to create some sort of mapping, but that gets even more complex due to how Vim handles what it calls "type ahead"; while you can do something like:

:nmap \e i<c-r>=substitute(system('date'),'[\r\n]*$','','')<cr><esc>

Where the command is "hard wired" in the mapping, you cannot do something like:

:nmap \e i<c-r>=substitute(system(input('Command: ')),'[\r\n]*$','','')<cr><esc>

Where you try to prompt the user for the command to run, because Vim will just get confused, beep, and enter insert mode.

So you have to prompt for the command to run first, store it in a variable, and then insert the processed output. At this point a helper function is probably needed to keep the mapping itself from becoming unmanageably messy, so we end up with something like this:

function InlineCommand()
    let l:cmd = input('Command: ')
    let l:output = system(l:cmd)
    let l:output = substitute(l:output, '[\r\n]*$', '', '')
    execute 'normal i' . l:output
endfunction

nmap <silent> \e :call InlineCommand()<CR>

Note that nmap creates mappings that only execute when typed in normal mode.

Incidentally, if you only wanted to insert the date or the current working directory, the initial answer I gave is feasible. Just enter insert mode and type ControlR=strftime('%c')Enter or ControlR=getcwd()Enter.

Solution 2:

If you want it in the current line you try just, but have in mind the old content of the line will be erased:

:.!date

If you want the exact cursor position you can:

:let a=system("date") | let b=substitute(a,"[\r\n]*$","","g") | exec 'normal i'.b

Long story short: you need to substitute cause you want to get rid of ^@ (null characters) which you can also can replace with .s/\%x00//g. This is a shorter version with less pipes:

:exec 'normal i'.substitute(system("date"),"[\n]*$","","")

And you can also set tags if you want to replace in various points at the same line:

Given this Line 1:

1 Date: <datehere> and also <datehere>

Execute:

:let a=system("date") | exec ".s/<datehere>/".a."/g" | .s/\%x00//g

After Replacement:

1 Date: jue ago 9 02:34:52 ART 2012 and also jue ago 9 02:34:52 ART 2012

Tested on Vim 7.2

Solution 3:

You can add this kind of thing in your .vimrc:

" Insert the date in YYYY-MM-DD format inline just before cursor position (normal mode)
nnoremap <Leader>di me:r !date +\%F<CR>A <Esc>0D`ePJx
  • me marks the current position.
  • :r !date +\%F<CR> puts the date on the next line in YYYY-MM-DD format (you can change this however you like, just escape each % with \).
  • A <Esc> appends a space to the end of the date output.
  • 0D deletes the date, `e takes you back to the marked position, and PJx puts the date before the cursor, joins the lines (to get rid of the empty one below) and kills the trailing space. Modify the mapping as you see fit.

If you want to do it from insert mode, I read this gem today on https://vimways.org/2018/the-mapping-business/

inoremap <C-g><C-t> <C-r>=strftime("%F")<CR>

You can modify the date output to suit your needs (man date can help with this).