Question about usage of stderr/stdout in `atuin search -i`

Should a design/implementation question come to the General category? It might be Help or Feedback instead, but I see a keybinding discussion here. I’m sorry if not.

Currently, the TUI of atuin search -i seems to be rendered through stdout and the result seems to be returned through stderr. Is there a reason for this design? It seems to be opposite to the one typically used. stdout is the stream to output the result, and, for example, Bash prints all its UI to stderr. However, a cleverer way is to print its TUI to /dev/tty. /dev/tty opens the control terminal of the current (process) session. It is defined by the POSIX standard and is supposed to be available in all the systems (including the limited ones as far as shells work there).

In fact, in the shell integration scripts of Atuin, stdout and stderr are swapped at the script side. Also, the non-interactive Atuin search seems to output the result to stdout.

I’d rather we stick to general, until there’s an obvious category that can be pulled out of it.

For instance, if we have a bunch of design/implementation topics started then a category would make sense. But I’d rather not initially setup a bunch of categories, and hope that they are used. Categories generally work best when they are emergent, vs predesigned.

Is there a reason for this design? It seems to be opposite to the one typically used. stdout is the stream to output the result, and, for example, Bash prints all its UI to stderr.

Very much racking my brains for this one. The implementation of this was almost 3 years ago now (!)

I think it was because I wanted to write the Rust code in a way that meant I could continue to work with stdout generally, and only write to stderr when a history item was found. Swapping them would mean that stray println wouldn’t break the search/shell, and just mess up the UI. Therefore the redirect in the shell was very much intentional, and more because of the design of the binary.

It could also have been because of the terminal backend used at the time. Crossterm (what we use now) has robust support for most things, and writes to stderr. Back then, we used tui-rs and Termion. I can’t remember what Termion supported, but most of their examples show using stdout for the TUI.

Maybe also worth reading: FAQ | Ratatui

I don’t stand by this decision, as I don’t think much thought was put into it at the time.

I stand by my now-pretty-old TODO comment though:

# swap stderr and stdout, so that the tui stuff works
# TODO: not this

I’d be happy to first investigate unswapping stderr/stdout and ensure we do things properly in the binary, rather than correcting in the shell. Investigating using tty as you say would be cool too, though I’d to ensure that has no adverse affects.

Oh, thank you very much for your detailed reply!

Thank you for the explanation. So it is a historical reason, yet I understand the cost of switching.

Another option is to do the swapping of the file descriptors in the Rust code. That introduces dependencies on the Unix-like systems, but I expect that it wouldn’t be the actual problem because the terminal thing is already the Unix one. I searched for it and found the following StackOverflow Q&A, (where directly using println is discouraged by an answer, sadly):

I’d think there are generally no adverse effects with using /dev/tty because recently many tools including fzf, zsh, and fish are already using /dev/tty. However, there can be possible problems specific to the detailed implementation of the current Atuin. We anyway need investigations.

1 Like

Yep totally historical!

Hm I’d rather avoid swapping them in Rust. I suspect that would have issues across platforms, and it would be nice to present the system how we want it to be used before the application starts

We could totally try /dev/tty, but flagging it is probably sensible.