Reputation: 12384
I would like to replace parts of one line with the result of the selection being piped into a command.
For example:
echo "hello $(echo "world" | base64)" | vim -
This will open a buffer with hello d29ybGQK
in it. Now press wvw
to visually select d29ybGQK
.
Then I attempted :!base64 -d
and I expected the buffer to contain hello world
, which did not happen. Indeed, the whole line was piped into the command, and the whole line was replaced.
Is it possible to replace only the visual selection, and have only that selection piped into the command?
I also attempted c<c-r>=system('base64 -d')
but that did not send the visual selection to the command's stdin.
Upvotes: 6
Views: 1530
Reputation: 59307
Filtering with !
is always line-wise. Your solution with c
and the
expression register is an excellent way to solve this. You only forgot
to pass the input to system()
, which is its second optional argument.
Since you just changed the text selected, it went into the "
register
automatically. All you need to do is to grab it back and pass it to
system with getreg()
:
c<C-R>=system('base64 -D', getreg('"'))
Note that base64
may echo a newline at the end. If you want to remove
it, either wrap the whole thing in trim()
, a new function in Vim 8, or
use [:-2]
:
c<C-R>=trim(system('base64 -D', getreg('"')))
c<C-R>=system('base64 -D', getreg('"'))[:-2]
This is a shorthand for [0:-2]
, meaning grab everything from character
0 to second-last in the resulting string.
Consider creating a visual map if you use it often:
vnoremap <leader>d c<C-R>=system('base64 -D', getreg('"'))[:-2]<CR>
Upvotes: 12
Reputation: 172758
For historical reasons, the Ex commands are inherently line-based; venerable vi also didn't have visual mode yet. That limitation includes filtering through an external command with :range!
; it will always filter complete lines.
For simple input like in your example, it's probably easiest to temporarily split the line, filter, and then reassemble. For example:
:substitute/ /\r/ | execute '.!base64 -d' | -1join
For a universal solution, you need to use or implement a plugin that grabs the selected text, filters it (probably through system()
), and then replaces the selection with the result.
My SubstituteExpression plugin has a {Visual}g=
mapping that can filter through Vimscript expressions, Vim functions and commands, and external commands.
express.vim by Tom McDonald offers an almost identical implementation. It also allows on-the-fly creation of operators via :MapExpress
and :MapSubpress
, something for which I would use my TextTransform plugin, which you need to install as a dependency, anyway. My plugin offers more advanced (cross-mode) repeats, and the :Ex
-command expression variant, but has two large dependencies you also need to install.
Upvotes: 1