syntonym
syntonym

Reputation: 7384

Calling fzf from nim doesn't show fzfs TUI

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

Answers (1)

PMunch
PMunch

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

Related Questions