Render UI directly to /dev/tty

See https://github.com/junegunn/fzf/discussions/3792

This allows us to separately capture the standard error from fzf and its
child processes, and there's less chance of user errors of redirecting
the error stream and hiding fzf.
This commit is contained in:
Junegunn Choi 2024-05-14 16:29:22 +09:00
parent 6432f00f0d
commit d274d093af
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
3 changed files with 28 additions and 5 deletions

View File

@ -73,7 +73,7 @@ func (r *LightRenderer) csi(code string) string {
func (r *LightRenderer) flush() { func (r *LightRenderer) flush() {
if r.queued.Len() > 0 { if r.queued.Len() > 0 {
fmt.Fprint(os.Stderr, "\x1b[?7l\x1b[?25l"+r.queued.String()+"\x1b[?25h\x1b[?7h") fmt.Fprint(r.ttyout, "\x1b[?7l\x1b[?25l"+r.queued.String()+"\x1b[?25h\x1b[?7h")
r.queued.Reset() r.queued.Reset()
} }
} }
@ -88,6 +88,7 @@ type LightRenderer struct {
prevDownTime time.Time prevDownTime time.Time
clicks [][2]int clicks [][2]int
ttyin *os.File ttyin *os.File
ttyout *os.File
buffer []byte buffer []byte
origState *term.State origState *term.State
width int width int
@ -131,6 +132,10 @@ func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop in
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := openTtyOut()
if err != nil {
out = os.Stderr
}
r := LightRenderer{ r := LightRenderer{
closed: util.NewAtomicBool(false), closed: util.NewAtomicBool(false),
theme: theme, theme: theme,
@ -138,6 +143,7 @@ func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop in
mouse: mouse, mouse: mouse,
clearOnExit: clearOnExit, clearOnExit: clearOnExit,
ttyin: in, ttyin: in,
ttyout: out,
yoffset: 0, yoffset: 0,
tabstop: tabstop, tabstop: tabstop,
fullscreen: fullscreen, fullscreen: fullscreen,

View File

@ -14,6 +14,8 @@ import (
"golang.org/x/term" "golang.org/x/term"
) )
var tty string
func IsLightRendererSupported() bool { func IsLightRendererSupported() bool {
return true return true
} }
@ -48,12 +50,14 @@ func (r *LightRenderer) closePlatform() {
// NOOP // NOOP
} }
func openTtyIn() (*os.File, error) { func openTty(mode int) (*os.File, error) {
in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0) in, err := os.OpenFile(consoleDevice, mode, 0)
if err != nil { if err != nil {
tty := ttyname() if len(tty) == 0 {
tty = ttyname()
}
if len(tty) > 0 { if len(tty) > 0 {
if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil { if in, err := os.OpenFile(tty, mode, 0); err == nil {
return in, nil return in, nil
} }
} }
@ -62,6 +66,14 @@ func openTtyIn() (*os.File, error) {
return in, nil return in, nil
} }
func openTtyIn() (*os.File, error) {
return openTty(syscall.O_RDONLY)
}
func openTtyOut() (*os.File, error) {
return openTty(syscall.O_WRONLY)
}
func (r *LightRenderer) setupTerminal() { func (r *LightRenderer) setupTerminal() {
term.MakeRaw(r.fd()) term.MakeRaw(r.fd())
} }

View File

@ -96,6 +96,11 @@ func openTtyIn() (*os.File, error) {
return nil, nil return nil, nil
} }
func openTtyOut() (*os.File, error) {
// not used
return nil, nil
}
func (r *LightRenderer) setupTerminal() error { func (r *LightRenderer) setupTerminal() error {
if err := windows.SetConsoleMode(windows.Handle(r.outHandle), consoleFlagsOutput); err != nil { if err := windows.SetConsoleMode(windows.Handle(r.outHandle), consoleFlagsOutput); err != nil {
return err return err