vim ex scripts and macros - vim

I have a question using ex in a bash script.
I have a file like that
data.txt
hello
foo
bar
foo
bar
hello
foo
bar
foo
bar
hello
Now I want to run a little macro over all patterns matching "hello" and I want to modify it to "hello world".
I'm using this script for that
ex -s data.txt << script
let #q="A world"
g/hello/normal #q
wq
script
I define a macro and put it to register q. Afterwards I execute this macro in all lines matching "hello". But it looks like that it's not working.
Now I'm wondering if ex scripting is limited somehow.
I know that there are hundreds of ways to achieve the same differently, but I want to use a macro in that case.
Thanks for your help!

There is ex and ex. Depending on what program is actually invoked when you do ex, the available features (or the way they interact with various config files) may or may not match with your expectations so it is strongly advised to read its documentation before using it.
As an example, let #q = "something" and normal #q are typical Vim idioms and I knew for sure that my ex is actually a fairly usable Vim build, so I knew beforehand that your snippet would work as expected on my machine:
But the ex used in your specific case may be a crippled Vim, or it may not be Vim at all, in which case using Vim idioms may not be a wise move. As an example, here is what I get running your snippet with real ex:
Adding the full output of $ ex --version to your question should help in clarifying the situation.
I know that you are not interested in alternatives, but a more defensive approach would be to limit yourself to the subset of features that was part of real ex. Something like:
$ ex -s data.txt << script
g/hello/s/$/ world/
wq
script

Related

How do I save a macro with multiple commands with let?

I'm currently learning to program in Vim, and I decided to make a macro to compile and run a C code quickly, so I made it like this:
save quickrun.c
!gcc quickrun.c -o quickrun
!quickrun
It worked as expected, no problems there, but then I tried to save the macro in the _vimrc file, and I had no problems with saving other macros with only one command using "let", like
let #a = '!gcc file.c'
But I want to save all the 3 commands in order to compile and run in the macro, how can I do it?
That's a very strange idea to put a sequence of commands into a register permanently. Typically, one uses a register to build commands on-the-fly by yanking portions of code from some script, or by recording a key sequence with q. The problem is that you have only 26 slots with fixed names, and that's quite inconvenient for storing general command sequences.
More frequent solution is to setup user commands or mappings (or both). You can define a command (it must start with a capital letter!) in your vimrc like this:
command! QRun save quickrun.c | !gcc quickrun.c -o quickrun | !quickrun
The bar symbol (|) is a command separator in Vim script, i.e. it's allowed in an interactive mode too. After that you can execute your command just like everything else: :QRun. If you feel "QRun" is too hard to type, you can additionally define a key mapping to spare a couple of keystrokes.
Also, you probably should revise your script to use arguments, and to get rid of "save[as]" in favour of plain "update", and so on.
Macros are like replayable keystrokes: they are as if you typed them. So if you want to use the ex command :!, you need to start with a colon. Then you can use the |:
let #a = ":save ... | !gcc ... \<CR> !quickrun\<CR>"
A better solution is a mapping:
nnoremap keys execute ":save ... <bar> !gcc ... <bar> !quickrun\<CR>"
But for C code you can usually just use :make

Replace visual selection with command output

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.
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>
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.
manual solution
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
plugin solution
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.

vim: Run multiple commands based off of one :global command

Apologies if this has been posted already, for I cannot find an answer, even on the vim wiki.
Is there a way I can run multiple commands in vim command-line mode off of a single :g search?
For example,
:%g/foo/ s/bar/\=#a/g | exe "norm /cat\<enter>\"ayiw"
Which (for what I intend it to do) should, on every line matching foo, replace bar with the contents of register a, and then find the next iteration of cat (even if it is many lines ahead), and put the surrounding word into register a.
Instead, this specific syntax completes the subsitution command using the current contents of the initial a register, and then executes the normal mode command on a single line (after the substitution has been completed).
This example is not my specific use-case but shows one instance where this functionality is useful. I realize I could put it all into a single exe, i.e., %g/foo/exe "norm :s/bar/\\=#a/g\<enter>/cat\<enter>\"ayiw", but I would like to do it the first way, as I feel it is more flexible.
I would prefer to do this using vanilla vim, but if a plugin exists for this, that is an okay alternative. Does anybody know if there is syntax to do such a thing?
Okay a "little bit" dirty, but does this work for you?
:let list = split(execute('g/cat/p'), '\n') | g/foo/ s/bar/\=matchstr(remove(list, 0), '\s\d\+\s\zs.*')/g
It first reads all occurences of cat save them in a list.
Then replace the first bar with the first cat... and so on.
The dirty part ist the matchstr command. the g//p also returns a number for the result so the list looks like this:
1 cat
2 cat
3 cat
...
that's why we have to remove a bit from the front. I would love to hear if someone knows a clean solution for that (I am also interested in a clean vimscript solution, does not have to be a oneliner).
You can do this (at least for multiple :s commands applied to a single :g). Example:
" SHORT STORY TITLES to single word of CapitalizedWords within <h3>s
.,$g/^\L\+$/s/[^A-Z0-9 ]\+//ge|s/\u\+/\L&/ge|s/\<\l\+\>/\u&/ge|s/ \+//ge|s/.*/<h3>&<\/h3>/

How can I use vim in a pipeline to colorize text?

I'd like to have a command I can insert into a command pipeline that adds color escapes to its input according to vim's syntax highlighting capabilities.
For example:
cat somefile.js | vim - <???> | less
The resulting text would be that of somefile.js, but colorized according to how the current vim configuration would do it in-editor.
It occurs to me that this must be possible. I agree that the example up there isn't what a sane man might call exactly useful, but that doesn't mean the idea never is.
I think your idea has one basic flaw: that nobody ever thought about allowing such a thing.
Clearly vim is capable of doing syntax highlighting. But I'll bet you an ice cream cone that if you can manage to get vim to stream text through and process it, that you won't like the results.
Consider what happens when you pipe text through more (or less if you prefer). When it goes to the terminal, these programs display one screenful and wait for you to hit the space bar. But if you redirect stdout to some other place than the terminal, these programs notice this and simply copy their input to their output unchanged.
If vim doesn't notice that you are piping text through, it is likely to send cursor-movement commands that you probably don't want in your output. If vim does notice, it is likely to just pass the text, and not syntax-color it. Only if vim does do the syntax-coloring but does not inject cursor-movement stuff will your idea work.
You could try it. Here's an answer that discusses piping stuff through vim:
Execute a command within Vim from the command line
But I say why not pipe your text through a program that was designed and intended to have text piped through it? Pygments can colorize every major programming language and markup format.
http://pygments.org/
The major advantage I see for your idea: you can customize the way vim does syntax coloring, get it the way you want it, and then also use vim to process your text. But it's probably not that hard to customize Pygments, and it might even be satisfactory out of the box, in which case it would definitely be the easiest way to go. And Pygments not only has ANSI sequence output, it also has HTML output, RTF, LaTeX, etc. So if you get Pygments working the way you want it to, it should be able to output whatever output format you need; vim will only have the ANSI sequence one.
There's a Perl module called Text::VimColor that I've heard will do kinda what you're looking for.
http://search.cpan.org/dist/Text-VimColor/
But let me ask this: Why do want it to go through less? Why not use vim as a perfectly good file viewer? view - will read from standard input in read-only mode.
https://gist.github.com/echristopherson/4090959
Via https://superuser.com/a/554531/7198.
Tried on /etc/passwd and it works surprisingly well!
This might be what you're after
cat filename.sh | vim - -c 'syntax on; syn=bash'
This is ugly, but you could alias this:
alias vim.sh="vim -c 'syntax on; syn=bash'"
Then use like this:
cat filename.sh | vim.sh -
Use vimcat !
wget -O /usr/local/bin/vimcat "https://www.vim.org/scripts/download_script.php?src_id=23422"
chmod 755 /usr/local/bin/vimcat
vimcat /etc/passwd
See also: https://www.vim.org/scripts/script.php?script_id=4325

Sending input to a screen window from vim

I have a vim function set up where I can highlight a line of text and execute in clojure. Here's the function:
function! Clojure_execline()
let cl = (getline(line(".")))
// ...
exec 'clojure -e "' . cl . '"'
endfunction
The problem with this is that it's slow to start and because it spawns a new clojure session every time I run it, I can't call a function I ran previously. Ideally, I'd like for a hidden repl to be running where I could send input from vim and retrieve the output from as well. I learned about gnu screen and thought it could help me, but I don't know how to send input from one screen window to another.
To clarify my problem, take this line of clojure:
(defn add2 [x y] (+ x y))
I'd like to be able to highlight this line in vim and execute in a running repl. I want to be able to call the line below and have it execute in the same repl:
(add2 4 5)
Afterwards, I'd like to be able to get the output of the function.
So, basically, my question is, how do I send input from one screen window to another?
Jake McCrary's suggestion is a good one. There are also a couple other scripts available, probably based on same idea:
VimClojure, which says it does "repl in a vim buffer"
and
slimv, specifically supports Clojure
and
Gorilla, I think VimClojure, above, is based on Gorilla
I don't know whether VimClojure actually does what you want, sending result back from Screen to buffer in Vim. One way to do that, I think, would be to finagle something using Vim's client-server functionality, possible with the --remote-send flag. See:
:h client-server
:h --remote-send
I don't have an exact answer, but it might be worth taking a look at slime.vim and seeing if anything can be learned from it.
blog post about it
script at vim.org
Found what I was looking for. You can execute this from a terminal to send a string directly to the stdin of a screen window:
$ screen -X stuff "ls -l\015" # \015 sends a carrige return.
You might also be interested in Conque http://code.google.com/p/conque/
I use it for Scala

Resources