How to execute a terminal program and perform an action when it closes in vimscript - vim

I'd like to write a Vim command that does the following:
Make a new split.
Launch a terminal program.
Wait for the program to stop and close the split.
Read the output produced in the second step into the original buffer.
This seems like a very common work flow, but I'm having trouble getting it to work.
The problem is in steps 3 and 4. As a test I defined:
function MyExitFunction(a,b,c)
close
read blah
endfunction
Then did:
:new | call termopen('fzf>blah',{on_exit:MyExitFunction}
which does start the terminal and close the split after the program is done. The read command, however, seems to do nothing. Perhaps it reads the input into a wrong split?
What should I do to get the actual program output into my current buffer?
Note, fzf is not the actual program I'm running, but it works a bit like it.

If you have a command that cleanly outputs to stdout, then you simply need :read !<command>.
If you want interactivity, i.e. reading from stdin first, then you should probably run it inside a terminal with :vs | te and then yank the output over. Vim doesn't have a clean and easy way to interop with interactive scripts, so this is probably as good as it's gonna get.

Related

tmux pin to avoid scrolling

Often when I run a command that writes to stdout, and that command fails, I have to scroll up (using uncomfortable key-bindings) looking for the place where I pressed Enter, to see what the first error was (out of hundreds others, across many screens of text). This is both annoying and time-consuming. I wish there was a feature which allowed me to pin my current terminal to the place where I am now, then start the command, see only the first lines of the output (as many as fits below my cursor) and let the rest of the output be written but not displayed. In other words I would like a feature to allow me automatically scroll up to the place where I gave the command, to see the first lines of the output (where usually the origin of the failure is displayed).
I searched for it but I didn't find it. Do you know if such feature exists? Or have an idea how to implement it with some tricks or workarounds?
If you have a unique shell prompt you could bind a key to jump between shell prompts, for example something like this will make C-b S jump to the previous shell prompt then S subsequent ones:
bind S copy-mode \; send -X search-backward 'nicholas#myhost:'
bind -Tcopy-mode S send -X search-backward 'nicholas#myhost:'
Or similarly you could search for error strings if they have a recognisable prefix. If you install the tmux 3.1 release candidate, you can search for regular expressions.
Alternatively, you could use capture-pane to load the entire history into an editor with key bindings you prefer, for example:
$ tmux capturep -S- -E- -p|vim -
Or pipe to grep or whatever. Note you will need to use a temporary file for this to work with emacs.
Or try to get into the habit of teeing commands with lots of output to a file to start with.

Vim discards input characters at startup

I have been driven crazy for years with Vim's behavior of throwing away input characters. I start vim like this:
$ vim file.c
and then immediately begin typing commands. However, Vim discards some of those characters, causing the wrong action to take place.
Is there something we can put in the .vimrc to solve this issue?
Vim should be able to change the TTY to raw mode without flushing buffered input.
Update: the issue is more precisely characterized, thanks to the following investigation method. I created a script called delayvim which contains:
#!/bin/sh
sleep 2.0
vim "$#"
Now during this two second pause I can type something like iabc<ESC> and then when Vim comes up, everything is cool: the command is processed, abc is inserted and Vim pops back into command mode, with the cursor backed up over the c. Thus, it is not simply flushing the TTY input buffer.
However, if I keep typing during this delay, for instance iabcdefghijk..., it will sometimes lose a letter or two of the alphabet that is typed right around the time when the sleep terminates and the editor launches. For instance, here is the result of one trial I just performed:
abcdefghilmn_
~
~
Where are jk, oops? I am sure I typed them. I didn't type very fast; my cadence was around 4-5 strokes per second, yet two consecutive events disappeared.
Basically, it might be trying to interrogate the terminal, and in the process discarding the input that is mixed up in the response. Or it could be a combination of reading some of the prior input, then flushing the input buffer and losing the rest.
2 points that might help:
1) are you being sure to hit i first, to enter input mode, so that all characters you type afterward should go ahead and be seen in your buffer (on the screen?). Otherwise the letters you type will be processed as commands, which will often do nothing, especially if you're starting an empty file.
Note that a and o are other common commands for telling vim that you are about to begin inserting text starting with the next keystroke.
2) In case the reason on your system has to do with speed, you can look for options to change what happens at startup. For example, if you put this in your .vimrc file
autocmd VimEnter * :sleep 5
Then after processing other startup files, vim or gvim will literally do nothing for 5 seconds before showing your file on the screen. On my system, I was able to type iabcdef during those 5 seconds and when the time was up, I did see abcdef entered into my text file.
If your file was not empty, beware, as (depending on your settings) your vim installation might be kindly returning you to the last place you were editing inside the file, and you will have inserted the text there, instead of at the start.
If this doesn't work, you could try to find other things vim can do (on the web, or using :help from within vim) and program it using autocmd to happen at startup to see if it helps.

File list by ls are misaligned

I catted a binary file and hit Ctrl-Z to stop it. Now ls results are misaligned. What did it happen and how could I have them listed correctly again ?
Type reset to reset your terminal, and press Enter. Then maybe press Ctrl+L to clear the screen. You should be back to normal.
Oh and by the way, that binary cat you ran, when you pressed Ctrl-Z that just suspended it, it's probably still running. Run jobs to see the list of jobs, and if it's there, say job number 2, do kill %2 (or whichever job number).
When you catted the binary file, your terminal probably inadvertently interpreted some of its data as control sequences and tried to execute them, screwing its properties and state.
You can either kill the terminal altogether (quit it if you're in a GUI, or relog if you're on a tty), or use another control sequence to reset the terminal. echo -e \\033c should do the trick. Some systems also have a reset builtin/command, which accomplishes the same thing.

Is there a good way to start programs from gvim without losing focus?

Vim has a built-in way for executing programs, namely :!start foo for Windows and :!bar & for Unix. However, the problem I have with those is that they steal focus from Vim. I'd like to launch my build & test script, and while its working and showing output in its command window never have to manually switch back to GVim.
I have thought of 3 possible solutions:
Use Auto-It/AutoHotkey to set focus back to Gvim. Unfortunately that only works in Windows and it's brittle.
Have another process watching my sourcecode, and let that process automatically launch the script when the sourecode changes. (Another process creating a new window does not steal focus). This is a bit gimmicky, as you need to hold off on writing files until you want to start the script, and sometimes write a useless change if you want to re-test even though nothing in the source changed.
Use a client-server architecture, where Vim sends the command over a socket or something similar. This is actually my preferred solution, if there is any existing cross-platform way to do it. (I would prefer not having an extra GVim instance running for this though.)
So what would be a good way to do this? I have tried AsyncCommander but unfortunately it also steals focus.
When I'm not using a nice build system like sbt that watches source code and re-builds/re-tests for me, I tend to come up with hacks like this:
:map <F9> :!touch and-go<CR><CR>
Here hitting F9 updates a file. Pick any key binding you like.
(and of course you could do the same with imap for vim's insert mode as well.)
Then all one needs is a shell script that watches this file and executes the
command that builds/tests your project. On unix, I'd just do:
while stat -c "%Y" and-go; do sleep 1; done |
stdbuf -i0 -o0 uniq -c |
while read l; do
echo build...; # Your build/test execution commands here.
done
Running that in a separate window will be neat, because your vim/gvim window will retain it's focus throughout, while you just keep hitting F9 (or whatever) and glancing at this other window to see how your build went.
If you're up to writing a windows batch script (or powershell, I don't know) version of that as well, you should be good to go.
I think this should work:
:silent! !cmd /c start /b dir
Also, start.exe does have an option to start minimized instead of with the shared/background console.
Of course, dir is to be replaced :)

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