Reputation: 7384
I'm trying to port a small python script of mine to nim. The core is a call to fzf:
fzf = subprocess.Popen(["fzf"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
stdout, stderr = fzf.communicate(some_data)
As far as I understand fzf open /dev/tty
to make its interactions with the user and uses stdin/stdout to communicate with the program which called it. The corrosponding nim code:
var p = startProcess(path_to_fzf)
var stream = p.inputStream()
stream.write(some_data)
stream.flush()
But in my nim version the fzf TUI does not appear. But input to the terminal is read, because based on my input the correct item is chosen.
I have no idea left why that might be the case. I thought maybe nim creates a PTY with startProcess
but /proc/sys/kernel/pty/nr
does not increase, so I think that's not the case. I tried compiling with -d:useFork
because .. well no idea why that should fix it, and it didn't. As far as I understand fzf/terminal/tty there shouldn't really something be that nim could do to prevent fzf from writing to /dev/tty
. Or maybe python does something I'm not aware of that triggers fzf to show its TUI?
So my question is how to convince fzf to show its TUI from nim (and why/how it doesn't already).
Upvotes: 2
Views: 886
Reputation: 1734
As a proper answer I found the option poParentStreams
in the osproc
module. It can be used like this to start a TUI:
import osproc
let process = startProcess("htop", options = {poParentStreams, poUsePath})
let eCode = process.waitForExit()
if eCode == 0:
echo "htop completed successfully"
else:
echo "htop aborted with error code: " & $eCode
@syntonym pointed out in the comments that fzf
opens /dev/tty
in order to show the TUI without disrupting the stdin/stdout flow. I'm not entirely sure why this doesn't work in Nim, but a solution is of course to write the result into a named pipe and read out from that, like this:
import osproc
import posix
import streams
discard mkfifo("/tmp/fzfoutput", 0o644)
let process = startProcess("echo \"hello\nworld\" | fzf > /tmp/fzfoutput", options = {poUsePath, poEvalCommand, poParentStreams})
var f: File
discard f.open("/tmp/fzfoutput")
echo "Output of fzf:" & f.readLine()
let eCode = process.waitForExit()
if eCode == 0:
echo "fzf completed successfully"
else:
echo "fzf aborted with error code: " & $eCode
discard execCmd("rm /tmp/fzfoutput")
Upvotes: 1