Vim write temporary info as :write does - vim

I have a macro which calls a function I defined in my vimrc
:function! DoStuff()
:!mycommand
:end
map <C-p>i :call DoStuff()<CR>
When I press the macro keys I get right a shell with the output of mycommand and it works fine but I would like improve that.
I noticed that when I write a file (:w) a short message is displayed for short time in the command bar which says "File X written", I want to achieve the same result, when macro sequence is pressed I want check if the command went fine (checking the return code) and then display a message (such "Ok" or "Not Okay") as the :write command does.
Any ideas ?

Write external command output to a temporary file
via Vimscript functions
First, capture the output of the external command in a variable, whose contents can then be written to a (temporary) file via writefile().
:let output = system('mycommand')
:call writefile(output, 'path/to/tempfile')
via scratch buffer
If you need to apply arbitrary commands to the captured contents, or need Vim to handle different file encodings, you have to use a buffer:
:new path/to/tempfile
:0read !mycommand
:write
:bdelete
via file redirection
This is the simplest approach when you need the output as-is:
:!mycommand > path/to/tempfile
Check for command success and notify
After external command execution, the special variable v:shell_error contains exit status of the last shell command. You can test that to report a message to the user:
!mycommand
if v:shell_error != 0
echohl ErrorMsg
echomsg "The command failed"
echohl None
else
echomsg "The command succeeded"
endif
If you've used system() to execute the command, you also have the error message returned by it, and can include that in the notification.

Related

Vim: Avoid having to press ENTER after successful make?

$ ls
Makefile html-page/ page-generator.m4
Run includes/
Alongside the Makefile, I have a script Run that is executed only when make completes without errors. This I've managed to implement with the following in my .vimrc file, which also looks for the Makefile in parent directories if needed.
" Before the 'make' quickfix command, run my quickfix pre-commands
autocmd QuickfixCmdPre make call MyQuickfixCmdPre()
" After the 'make' quickfix command, run my quickfix post-commands
autocmd QuickfixCmdPost make call MyQuickfixCmdPost()
and
function! MyQuickfixCmdPre()
" Save current buffer, but only if it's been modified
update
" (h)ead of (p)ath of % (current buffer), i.e. path of current file
let l:dir = expand('%:p:h')
" Remove final / and smack a /Makefile on the end, glob gives empty if file doesn't exist
while empty(glob(substitute(l:dir, '/$', '', '') . '/Makefile'))
" There's no Makefile here. Are we at the root dir?
if l:dir ==# "/"
" Just use dir of current file then
let l:dir = '.'
break
else
" Try the parent dir. Get (h)ead of dir, i.e. remove rightmost dir name from it
let l:dir = fnamemodify(l:dir, ':h')
endif
endwhile
" Makefile is in this dir, so local-cd (only this window) to the dir
execute "lcd " . l:dir
endfunction
function! MyQuickfixCmdPost()
" Get number of valid quickfix entries, i.e. number of errors reported,
" using filter to check the 'valid' flag
let l:err_count = len(filter(getqflist(), 'v:val.valid'))
if l:err_count ==# 0
" The make succeeded. Execute the Run script expected in the same dir as Makefile
call system('./Run')
redraw!
endif
endfunction
With this in place, after typing :mak in vim, the code is made and run... There are two possible results:
If there are errors during make, vim will present these errors with a Press ENTER or type command to continue afterwards, which is all good.
If make succeeds without errors, however, my Run script is executed, for testing my code (in this case an html file shown in a browser), but then when I switch back to vim, I have to press enter to get rid of a message from vim that I don't need to read because it doesn't tell me about errors. This message used to look like this:
"includes/m4includes/subs.m4" 34L, 759B written
:!make 2>&1| tee /var/folders/zk/0bsgbxne3pe5c86jsbgdt27f3333yd/T/vkbxFyd/255
m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
(1 of 1): m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
Press ENTER or type command to continue
but after introducing the redraw! in MyQuickfixCmdPost() is now reduced to:
(1 of 1): m4 -I includes/m4includes page-generator.m4 >html-page/mypage.html
Press ENTER or type command to continue
yet still with the need to press enter.
How do we avoid having to press enter every single time we return to vim after a successful compilation? Any ideas?
Note: vim has a -silent command-line option, but as far as I can see this would silence all the Press ENTERs, and the goal here is to only avoid them after a successful make.
Just add call feedkeys("\<CR>") afterwards. There are not many places you need feedkeys() (often normal! or similar commands will do), and there are subtle effects (look at the flags it takes carefully). Fortunately this is one place it is useful.

call from command line, show file also if the search does not match

I use a msdos-script to search with vim for patterns and show me the result
Script: tel.bat
rem script is called: tel.bat <pattern>
gvim -R %WORKSPACE%\telliste.csv "+set ignorecase" "+set ft=javascript" -c /%1
This works fine if the pattern exist in the file. If the pattern is not matched, I get an error message and I am stuck. No keystroke or mouse action changes the state. Like:
Enter key - has no effect
Esc key - has no effect
Ctrl + C - the error-messages disappears, but the editor is frozen. No action possible
Mouse click in editor - has no effect
I can only close vim and try again. That's what I get as error, when I call the script tel.bat konez on the command line:
Error message translated:
Error during execution of "command line":
E486: Pattern not found: konez
Confirm with the ENTER Key or place a command
How can I work further on the file, even if the pattern is not found? In other words how can I avoid that I am stuck in vim.
I tried already with -c ":execute 'silent !'" in the batch file, but this was not recognized. Perhaps I did it in the wrong way...
This should work, and I cannot reproduce this on Linux with Vim version 8.0.1358; I can accept the error message with <Enter> and continue.
This could be a plugin / configuration issue; try launching with gvim --clean.
The multi-line error message is ugly. You could avoid it by moving to the lower-level search() function:
gvim ... -c "call search('%1')"
By evaluating its return value; you could also craft your own error message: if search(...) == 0 | echomsg 'No matches' | endif

Vim: cannot see :echomsg messages in message history list

I'm working on a vim script and want to see the value of a variable that is generated in the script for debugging purposes.
I use something like:
echomsg 'My variable = ' . b:variable
reload the source with
:source %
and then look the message history list with
:messages
But there's nothing there and I'm pretty sure that the command would be executed.
What am I doing wrong here?
EDIT:
I'm trying to make a change to vim-cucumber and so I wanted to see the value of a variable. In the ftplugin/cucumber.vim file, I have the following:
let b:cucumber_root = expand('%:p:h:s?.*[\/]\%(features\|stories\)/step_definitions/mobile_website\zs[\/].*??')
echomsg 'cucumber_root = ' . b:cucumber_root
If you look at the top of file you will find
if (exists("b:did_ftplugin"))
finish
endif
Which stops the file from being sourced again after the first time. You need to unlet b:did_ftplugin before sourcing the file again.
Other options would be to use another tpope plugin to do this for you called scripttease with the :Runtime command.

Capture the output of an interactive script in vim

I have a an interactive Perl script, which prints prompts to STDERR and reads lines from STDIN. The final output of this script is an IP address, printed to STDOUT. Here's a numpty version of such a script as an example.
my #pieces;
for (1..4) {
print STDERR "please enter piece $_ of the IP:"; chomp(my $in = <>);
push #pieces, $in;
}
print join '.', #pieces;
print "\n";
I use the vim-fireplace vim plugin. This plugin has a feature where I can say:
:Connect nrepl://127.0.0.1:9999
I want to know how to configure vim so that when I issue a particular command, let's say:
:InteractiveConnect
it will do the following:
Run the Perl script, allowing me to enter 4 pieces of the IP address.
Capture the IP address output by the Perl script.
Interpolate the IP address into the :Connect command
Run the :Connect command.
A bit more info based on some of the responses:
If I call this script using:
:!/path/to/myscript.pl
Then it executes fine and I am able to see the result from it printed in the vim window, followed by
Press ENTER or type command to continue
If the output of the script is being saved in some buffer after execution via !, is it possible to get access to that buffer in vimscript and just capture the bit I want (the last line) with a regex?
Okay, there's probably a more elegant way to do this, but how about this:
function! <SID>InteractiveConnect()
let tempfile=tempname()
exe '!/path/to/your/script.pl >' . shellescape(tempfile)
try
exe 'Connect nrepl://' . readfile(tempfile, '', -1)[0]
finally
call delete(tempfile)
endtry
endfunction
command! -nargs=0 InteractiveConnect call <SID>InteractiveConnect()
This creates a temporary file, writes to it with the script (using system() doesn't work because it doesn't wait for input), reads the last line in the tempfile to the Connect command, and then finally deletes the tempfile.
Maybe something like:
exec 'Connect nrepl://' . matchstr(system('your/script.pl'), '^.\+\%$')
(Untested.) This runs the script using system() then matches the output against the regular expression ^.\+\%$, (where \%$ means end-of-file; if your file is terminated with a newline, an additional \n might be neccessary before it) and feeds the matched str to the Connect command. .

Access return value of Bash script in Vim

I want to have a function in my .vimrc that will run a bash script, check the output, and do different things depending on the bash script output.
Currently, my bash script returns "yes". My vim function is then:
function! Callscript()
if 'r ! . test.sh' == 'yes'
echom "returned yes"
else
echom "returned something else"
endif
endfunction
This will always print "returned something else". When I run the command ':r ! . test.sh', it returns:
test.sh: line 5: return: yes: numeric argument required
I didn't see anything in :help r! that mentioned doing anything extra to get the return values, but I tried:
if 'r ! . test.sh'.return == 'yes'
if 'r ! . test.sh':return == 'yes'
which gave me errors. I have tried using system, but could not get that to work. The most obvious approach I tried with system was:
if system('r ! . test.sh') == 'yes'
When I try system('. test.sh') in the editor, it tells me system is not an editor command.
How can I access the return value of the bash script so that I can use it in my vim function?
if 'r ! . test.sh' == 'yes'
That syntax is nonsense. First, the :r !{cmd} is reading the output of {cmd} into the current buffer, which is probably not what you want. To get this into a variable, use :let output = system('test.sh'), or directly compare via:
if system('test.sh') == 'yes'
Note that comparing the output (especially if it's a true / false or success / failure boolean value as suggested by your question) is unusual. Rather, the script's exit status is checked (e.g. with commands like grep). You can get that in Vim via the special v:shell_error variable (which is filled by both system() and :r !).

Resources