'In vim, how to send just part of a line as stdin to a shell command?

I'm looking at some obfuscated JS:

const f = Buffer.from("Lw==", "base64");
// select ^---------------------------^

How can I send the selection to node -p and replace?

I tried :'<,'>!node -p, but that produced undefined, because it sent the whole line instead of just the selected part of the line. It also deleted the indent and const f = , which I didn't want to do.



Solution 1:[1]

Ex commands operate on lines, not on arbitrary text, so you can forget about :w or :!.

Here is a working solution but I'm afraid there is no dedicated built-in method so you won't be able to avoid at least some level of complexity:

v<motion>
:'<,'>s/\%V.*\%V/\=system('node -p ' . shellescape(submatch(0) . '.toString()'))->trim()/<CR>

That's obviously quite a lot to remember and type but you can turn it into a visual mode mapping for more convenience:

xnoremap <key> :s/\%V.*\%V/\=system('node -p ' . shellescape(submatch(0) . '.toString()'))->trim()/<CR>

which you can then use like this:

v<motion><key>

But a real solution would also deal with sanitizing input and output, handling errors, handling multiline input and output, etc.

Solution 2:[2]

d
:call setreg('a', system('printf "%s" "$(echo '.shellescape(@").'".toString()" | node -p)"'))
"aP

This produced the expected / as output in the correct position:

const f = /;

However, it feels overly complicated compared to the linewise version. I'm looking for a better way.

Solution 3:[3]

Another approach is to temporarily isolate the selection to its own line.

<ESC>           "exit visual mode
`>a             "append after selection
.toString()     "modify JS before sending to node -p
<CR><ESC>
`<i             "insert before selection
<CR><ESC>
V               "select line
:'<,'>!node -p  "do the usual linewise filter
k               "go previous line
3gJ             "join 3 lines without adding or removing spaces

Unfortunately this will do strange things if the command returns multiple lines.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Chaim Leib Halbert
Solution 3