Catch piping errors not working

I assume I’m missing something obvious but all I want is to display a message if a pipe fails instead of the error silently being written to *debug* buffer - eg. pipe a selection through bla and if it fails (bla doesn’t exist) to display an info box.

How can I do this?

What I tried (seems obvious but doesn’t work): :try %{exec |bla<ret>} catch %{info bad}
Tried using command output insertions (!) too but same result.

Also, if the command fails using | it replaces the current selection with nothing - isn’t it more user friendly not to do that and leave the selection as is since it’s certainly not what the user intended? (this is why I want to show an info box so I know what actually happened).

2 Likes

As far as I can tell, Kakoune generally does not track the exit code of shell commands it executes - it calls the shell, which does whatever it does, and Kakoune collects the resulting stdout and stderr. I’m not sure if that’s an oversight, or if there’s some fundamental reason it needs to work that way (such as grep using exit code 1 to mean “successfully found no matches”).

Thanks, that’s unfortunate, and surprising, seems like a core function/feature to me - for grep etc a custom script can be written to handle it, instead I have to write a custom script to reimplement a native function to handle the 99% case… :man_shrugging:

Hello, well, not really a script, you could write a kakoune command that operates on selections and handles the error logic for you before trying to insert the text.

Hey. Yea that’s what I meant by script. It’s just a shame to have to reimplement what’s already there natively. I’d rather add error handling to the pipe functions.

Might take a look into it but was hoping someone might explain if there’s a restrictive reason why it hasn’t been done so far, before I start poking holes in the codebase.

1 Like

I see, what if you join the streams with 2>&1? The thing with piping is that every selection operation is it’s own process afik, so stopping on error requires waiting between them. In the case is a single selection I understand your use case, but we would need to join the errors in case we are piping multiple selections, I think that’s the tricky part, many things can fail and we should handle them, and being multiple selections a core philosophy of kakoune it clearly has priority. Your complaint is valid tho, there must be a way to handle errors better, but I just can think of hacky ways you work it around :p, we could check the length of the debug buffer, pipe the command(s), and then display a info with the contents of debug buffer past it’s initial length, if any.

1 Like

Interesting point. I think in the multi selection case there’s no need to wait on each one, just run in parallel and if any return an error then the whole operation returns the first error. Bonus points: could print to the debug buffer which selection(s) returned error and the code.

Any reason why the above wouldn’t work?

Otherwise I did already look into what you suggest about tracking the debug buffer changes - I was expecting to find a BufChanged hook but surprisingly there isn’t one. Seems like a useful one to have.

If there’s an easy way to detect changes to the debug buffer then that could be the simplest interim solution for my main use case - but my kakfu is too weak to figure that one out, any hints?

If I can just display an info box whenever *debug* buffer changes that would also be handy in the general case for all commands and plugins (there’s often a lot there that I’m oblivious to).

Ah, forgot about the redirect option. That’s a good one, thanks! Didn’t think of it, got tunnel vision with try/catch.

It had me scratching my head though because it seemed to not work at all! Then I realised why - because the command I execute is the selection itself, which I’m selecting using x and as a result passing the \n char to the shell before the 2>&1 suffix:
<a-!>sh -c "$kak_selection 2>&1"<ret>
#-------------------------------------^ \n present here.

I got around it by wrapping the selection in curlies - like so:
<a-!>sh -c "{ $kak_selection } 2>&1"<ret>

But now the shell throws an error if I don’t select the eol :laughing:. Trying to insert newlines around it took me down a rabbit hole of kak and shell edge cases. I don’t think I’m heading in the right direction.

This made me realise that I’d probably like to be able to execute a multiline shell commands too at some point, so I probably need a different approach, maybe using one of the repl-based commands (it just seemed like a heavy solution to a simple problem of oneliners).

1 Like

You can write a shell script with as many lines as you like and use it instead of sh -c… kak will call your script once for each selection. kak passes the selection to the stdin of the script.

I’m trying to execute a selection in the current buffer. I could do similar to what you’re suggesting - save that selection to a temporary file, then execute and delete it. It’s doable but a) feels like overkill, and b) feels like underkill - if I go that route I may as well go the repl route which is a more efficient version of the same thing with other benefits.

I was hoping to keep things light and simple by relying on existing native mechanisms, but keep running into edge cases.

I think I’m heading towards Screwtape / kakoune-repl-buffer · GitLab - but instead of outputing to separate buffer I actually do want the output to go to the current buffer. @Screwtapello do you think your plugin (or a quick hack of it) could be used for that purpose?

You need a semicolon after the command and before the closing }, otherwise the shell assumes the } is a parameter to the last command and gets confused.

My plugin is based around Kakoune’s “buffer that consumes the output of a FIFO” feature; if you want the output to go back to the original buffer instead of a FIFO, then I don’t think there’d be much left of my plugin.

I would probably write a helper script which does something like:

  • read stdin
  • copy it to stdout as-is
  • execute it with a shell, capture its stdout/stderr and exit code
  • copy the shell’s output to its own stdout, prefixing each line with ##
  • add line prefixed with # that mentions the shell’s exit code
  • add a blank line

Then, you can start with a command like:

echo hello | tr a-z A-Z

…then select it and pipe it to your helper script to get:

echo hello | tr a-z A-Z
##HELLO
# exit code 0

…and then at any later time you can select the entire block with <a-i>p and pipe it to your script to re-execute the code and regenerate the output.

You could even make a mapping that does <a-i>p|my-helper-script.sh<ret> and then you can just hit the key after each modification to the command to see if it produces the output you want.

How about simply
<a-!>sh
I tested by selecting “echo hello” (minus quotes) in a kak buffer and kak duly appended “hello” (minus quotes)

p.s. you can even pass multi-line selections to sh eg
echo howdy
echo hello
as ONE selection appends
howdy
hello
after echo hello

also works as I would expect for multiple selections…

sh 2>&1 will insert an error message into the buffer you are editing

This may be closer to what you want.

echo %sh{
if evalresult=$(eval $kak_selections); then
echo “exec a$evalresult” > $kak_command_fifo
else
echo ‘info “eval error”’ > $kak_command_fifo
fi
}

You need to put it in a file and :source it in. It only correctly handles single selections. Not sure if it will correctly handle spaces in the evaluated results. The info message could be more context specific etc. I suspect kakoune provides enough variables to %sh to loop through multiple selections and insert something different after each one but that seems like a lot of work to me.

Easier might be to loop through each selection, evaluate it and test the return code and put out info/fail error message(s) if required, but without appending any results. Then echo “exec <a-!>sh” > $kak_ command_fifo to actually modify the buffer.

As far as I know there are no if-then-else or return code tests in the Kakoune command set so you have to drop into %sh for this function. I suspect that is part of the rigid orthogonality of Kakoune design so there is no simpler way of doing this. But I’m new to Kakoune myself and would welcome a simpler solution.

A simpler approach:

#!/bin/bash
#takes kak session id as an argument
read fromkak
if ! eval “$fromkak”; then
echo ‘evaluate-commands -try-client client0 info eval_error’ |kak -p $1
fi

put the script into a file, say test1, then invoke with <a-!>test1 “sessionid”
where you have to key the session id
get around keying the sessionid by
echo %sh{ echo ‘exec “<a-!>test1 $kak_session”’ > $kak_command_fifo}

slightly improved script
#!/bin/bash
#takes kak session id as an argument
read fromkak
if ! $fromkak; then
echo evaluate-commands -try-client client0 info eval\\\\ error:\\\\ $(echo “$fromkak”|sed ‘s/ /\\\\ /g’) |kak -p $1
fi

simpler. The script can figure out which kak is calling it, eliminating the need to pass the kak pid as a parameter

#!/bin/bash
read fromkak
if ! $fromkak; then
#kak launches a shell which launches this command
#therefore need grandparent pid to communicate with kak
echo evaluate-commands -try-client client0 info eval\\\\ error:\\\\ $(echo “$fromkak”|sed ‘s/ /\\\\ /g’) |kak -p $(ps -o ppid= $PPID)
fi

You could do something fancier with the info message, like position it relative to the error selection. Or maybe instead of or in addition to info you want to highlight error selections. Invoke test1 with kak_selection_desc on the kak command line to give your script access to position information through $kak_selection_desc. see kakoune/doc/pages/expansions.asciidoc at master · mawww/kakoune · GitHub

Kakoune is designed to live within the shell and relentlessly avoids duplicating shell function. There is no scripting in Kakoune beyond the keystrokes, you have to do any additional logic in the shell. I agree it’s a bit offputting at first. You have to start by writing your own copy and paste if you want to use the system clipboard. But I like this design because it is so incredibly powerful and flexible. Documentation could be better, mostly because the design is so powerful and flexible and unusual. It would be handy to have a summary of all the permutations and combinations of how kakoune and the shell can interact with each other when you are at the kak command line, in %sh{}, in an external script (and perhaps more). I used pstree to see more clearly what was going on. The interactions are documented but you really have to take Kakoune at it’s word about orthogonality with the shell when you read them and also know the interprocess communication aspects of the shell…

maybe even simpler.

#!/bin/bash
#include kak_command_fifo on command line when invoking this script in kak
#env | grep ^kak_
read fromkak
if ! $fromkak; then
echo info eval\\ error:\\ $(echo “$fromkak”|sed ‘s/ /\\ /g’) > $kak_command_fifo
fi

invoke with <a-!>test1 kak_command_fifo