diff options
| author | Junegunn Choi <junegunn.c@gmail.com> | 2024-05-07 01:06:42 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-07 01:06:42 +0900 |
| commit | e8405f40fe2eb3675f1cb4f69e825eff5f13f269 (patch) | |
| tree | c917367f1f0098939f9cdf7376a2a135907024fc /src/tui | |
| parent | 065b9e6fb2ce3e6e50ff423c3786989afa04ee14 (diff) | |
| download | fzf-e8405f40fe2eb3675f1cb4f69e825eff5f13f269.tar.gz | |
Refactor the code so that fzf can be used as a library (#3769)
Diffstat (limited to 'src/tui')
| -rw-r--r-- | src/tui/dummy.go | 4 | ||||
| -rw-r--r-- | src/tui/eventtype_string.go | 51 | ||||
| -rw-r--r-- | src/tui/light.go | 49 | ||||
| -rw-r--r-- | src/tui/light_unix.go | 18 | ||||
| -rw-r--r-- | src/tui/light_windows.go | 6 | ||||
| -rw-r--r-- | src/tui/tcell.go | 18 | ||||
| -rw-r--r-- | src/tui/tui.go | 10 |
7 files changed, 88 insertions, 68 deletions
diff --git a/src/tui/dummy.go b/src/tui/dummy.go index 7760a724..1a761460 100644 --- a/src/tui/dummy.go +++ b/src/tui/dummy.go @@ -8,7 +8,7 @@ func HasFullscreenRenderer() bool { return false } -var DefaultBorderShape BorderShape = BorderRounded +var DefaultBorderShape = BorderRounded func (a Attr) Merge(b Attr) Attr { return a | b @@ -29,7 +29,7 @@ const ( StrikeThrough = Attr(1 << 7) ) -func (r *FullscreenRenderer) Init() {} +func (r *FullscreenRenderer) Init() error { return nil } func (r *FullscreenRenderer) Resize(maxHeightFunc func(int) int) {} func (r *FullscreenRenderer) Pause(bool) {} func (r *FullscreenRenderer) Resume(bool, bool) {} diff --git a/src/tui/eventtype_string.go b/src/tui/eventtype_string.go index ce34d36e..d752163d 100644 --- a/src/tui/eventtype_string.go +++ b/src/tui/eventtype_string.go @@ -83,34 +83,35 @@ func _() { _ = x[Alt-72] _ = x[CtrlAlt-73] _ = x[Invalid-74] - _ = x[Mouse-75] - _ = x[DoubleClick-76] - _ = x[LeftClick-77] - _ = x[RightClick-78] - _ = x[SLeftClick-79] - _ = x[SRightClick-80] - _ = x[ScrollUp-81] - _ = x[ScrollDown-82] - _ = x[SScrollUp-83] - _ = x[SScrollDown-84] - _ = x[PreviewScrollUp-85] - _ = x[PreviewScrollDown-86] - _ = x[Resize-87] - _ = x[Change-88] - _ = x[BackwardEOF-89] - _ = x[Start-90] - _ = x[Load-91] - _ = x[Focus-92] - _ = x[One-93] - _ = x[Zero-94] - _ = x[Result-95] - _ = x[Jump-96] - _ = x[JumpCancel-97] + _ = x[Fatal-75] + _ = x[Mouse-76] + _ = x[DoubleClick-77] + _ = x[LeftClick-78] + _ = x[RightClick-79] + _ = x[SLeftClick-80] + _ = x[SRightClick-81] + _ = x[ScrollUp-82] + _ = x[ScrollDown-83] + _ = x[SScrollUp-84] + _ = x[SScrollDown-85] + _ = x[PreviewScrollUp-86] + _ = x[PreviewScrollDown-87] + _ = x[Resize-88] + _ = x[Change-89] + _ = x[BackwardEOF-90] + _ = x[Start-91] + _ = x[Load-92] + _ = x[Focus-93] + _ = x[One-94] + _ = x[Zero-95] + _ = x[Result-96] + _ = x[Jump-97] + _ = x[JumpCancel-98] } -const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLCtrlMCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancel" +const _EventType_name = "RuneCtrlACtrlBCtrlCCtrlDCtrlECtrlFCtrlGCtrlHTabCtrlJCtrlKCtrlLCtrlMCtrlNCtrlOCtrlPCtrlQCtrlRCtrlSCtrlTCtrlUCtrlVCtrlWCtrlXCtrlYCtrlZEscCtrlSpaceCtrlDeleteCtrlBackSlashCtrlRightBracketCtrlCaretCtrlSlashShiftTabBackspaceDeletePageUpPageDownUpDownLeftRightHomeEndInsertShiftUpShiftDownShiftLeftShiftRightShiftDeleteF1F2F3F4F5F6F7F8F9F10F11F12AltBackspaceAltUpAltDownAltLeftAltRightAltShiftUpAltShiftDownAltShiftLeftAltShiftRightAltCtrlAltInvalidFatalMouseDoubleClickLeftClickRightClickSLeftClickSRightClickScrollUpScrollDownSScrollUpSScrollDownPreviewScrollUpPreviewScrollDownResizeChangeBackwardEOFStartLoadFocusOneZeroResultJumpJumpCancel" -var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 458, 467, 477, 487, 498, 506, 516, 525, 536, 551, 568, 574, 580, 591, 596, 600, 605, 608, 612, 618, 622, 632} +var _EventType_index = [...]uint16{0, 4, 9, 14, 19, 24, 29, 34, 39, 44, 47, 52, 57, 62, 67, 72, 77, 82, 87, 92, 97, 102, 107, 112, 117, 122, 127, 132, 135, 144, 154, 167, 183, 192, 201, 209, 218, 224, 230, 238, 240, 244, 248, 253, 257, 260, 266, 273, 282, 291, 301, 312, 314, 316, 318, 320, 322, 324, 326, 328, 330, 333, 336, 339, 351, 356, 363, 370, 378, 388, 400, 412, 425, 428, 435, 442, 447, 452, 463, 472, 482, 492, 503, 511, 521, 530, 541, 556, 573, 579, 585, 596, 601, 605, 610, 613, 617, 623, 627, 637} func (i EventType) String() string { if i < 0 || i >= EventType(len(_EventType_index)-1) { diff --git a/src/tui/light.go b/src/tui/light.go index a045b783..3ef8b60e 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -2,6 +2,7 @@ package tui import ( "bytes" + "errors" "fmt" "os" "regexp" @@ -10,6 +11,7 @@ import ( "time" "unicode/utf8" + "github.com/junegunn/fzf/src/util" "github.com/rivo/uniseg" "golang.org/x/term" @@ -27,8 +29,8 @@ const ( const consoleDevice string = "/dev/tty" -var offsetRegexp *regexp.Regexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R") -var offsetRegexpBegin *regexp.Regexp = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R") +var offsetRegexp = regexp.MustCompile("(.*)\x1b\\[([0-9]+);([0-9]+)R") +var offsetRegexpBegin = regexp.MustCompile("^\x1b\\[[0-9]+;[0-9]+R") func (r *LightRenderer) PassThrough(str string) { r.queued.WriteString("\x1b7" + str + "\x1b8") @@ -78,6 +80,7 @@ func (r *LightRenderer) flush() { // Light renderer type LightRenderer struct { + closed *util.AtomicBool theme *ColorTheme mouse bool forceBlack bool @@ -123,19 +126,24 @@ type LightWindow struct { bg Color } -func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) Renderer { +func NewLightRenderer(theme *ColorTheme, forceBlack bool, mouse bool, tabstop int, clearOnExit bool, fullscreen bool, maxHeightFunc func(int) int) (Renderer, error) { + in, err := openTtyIn() + if err != nil { + return nil, err + } r := LightRenderer{ + closed: util.NewAtomicBool(false), theme: theme, forceBlack: forceBlack, mouse: mouse, clearOnExit: clearOnExit, - ttyin: openTtyIn(), + ttyin: in, yoffset: 0, tabstop: tabstop, fullscreen: fullscreen, upOneLine: false, maxHeightFunc: maxHeightFunc} - return &r + return &r, nil } func repeat(r rune, times int) string { @@ -153,11 +161,11 @@ func atoi(s string, defaultValue int) int { return value } -func (r *LightRenderer) Init() { +func (r *LightRenderer) Init() error { r.escDelay = atoi(os.Getenv("ESCDELAY"), defaultEscDelay) if err := r.initPlatform(); err != nil { - errorExit(err.Error()) + return err } r.updateTerminalSize() initTheme(r.theme, r.defaultTheme(), r.forceBlack) @@ -195,6 +203,7 @@ func (r *LightRenderer) Init() { if !r.fullscreen && r.mouse { r.yoffset, _ = r.findOffset() } + return nil } func (r *LightRenderer) Resize(maxHeightFunc func(int) int) { @@ -233,15 +242,16 @@ func getEnv(name string, defaultValue int) int { return atoi(env, defaultValue) } -func (r *LightRenderer) getBytes() []byte { - return r.getBytesInternal(r.buffer, false) +func (r *LightRenderer) getBytes() ([]byte, error) { + bytes, err := r.getBytesInternal(r.buffer, false) + return bytes, err } -func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte { +func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) ([]byte, error) { c, ok := r.getch(nonblock) if !nonblock && !ok { r.Close() - errorExit("Failed to read " + consoleDevice) + return nil, errors.New("failed to read " + consoleDevice) } retries := 0 @@ -272,19 +282,23 @@ func (r *LightRenderer) getBytesInternal(buffer []byte, nonblock bool) []byte { // so terminate fzf immediately. if len(buffer) > maxInputBuffer { r.Close() - panic(fmt.Sprintf("Input buffer overflow (%d): %v", len(buffer), buffer)) + return nil, fmt.Errorf("input buffer overflow (%d): %v", len(buffer), buffer) } } - return buffer + return buffer, nil } func (r *LightRenderer) GetChar() Event { + var err error if len(r.buffer) == 0 { - r.buffer = r.getBytes() + r.buffer, err = r.getBytes() + if err != nil { + return Event{Fatal, 0, nil} + } } if len(r.buffer) == 0 { - panic("Empty buffer") + return Event{Fatal, 0, nil} } sz := 1 @@ -315,7 +329,9 @@ func (r *LightRenderer) GetChar() Event { ev := r.escSequence(&sz) // Second chance if ev.Type == Invalid { - r.buffer = r.getBytes() + if r.buffer, err = r.getBytes(); err != nil { + return Event{Fatal, 0, nil} + } ev = r.escSequence(&sz) } return ev @@ -738,6 +754,7 @@ func (r *LightRenderer) Close() { r.flush() r.closePlatform() r.restoreTerminal() + r.closed.Set(true) } func (r *LightRenderer) Top() int { diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go index 55e2b246..a5499a00 100644 --- a/src/tui/light_unix.go +++ b/src/tui/light_unix.go @@ -3,7 +3,7 @@ package tui import ( - "fmt" + "errors" "os" "os/exec" "strings" @@ -48,19 +48,18 @@ func (r *LightRenderer) closePlatform() { // NOOP } -func openTtyIn() *os.File { +func openTtyIn() (*os.File, error) { in, err := os.OpenFile(consoleDevice, syscall.O_RDONLY, 0) if err != nil { tty := ttyname() if len(tty) > 0 { if in, err := os.OpenFile(tty, syscall.O_RDONLY, 0); err == nil { - return in + return in, nil } } - fmt.Fprintln(os.Stderr, "Failed to open "+consoleDevice) - util.Exit(2) + return nil, errors.New("failed to open " + consoleDevice) } - return in + return in, nil } func (r *LightRenderer) setupTerminal() { @@ -86,9 +85,14 @@ func (r *LightRenderer) updateTerminalSize() { func (r *LightRenderer) findOffset() (row int, col int) { r.csi("6n") r.flush() + var err error bytes := []byte{} for tries := 0; tries < offsetPollTries; tries++ { - bytes = r.getBytesInternal(bytes, tries > 0) + bytes, err = r.getBytesInternal(bytes, tries > 0) + if err != nil { + return -1, -1 + } + offsets := offsetRegexp.FindSubmatch(bytes) if len(offsets) > 3 { // Add anything we skipped over to the input buffer diff --git a/src/tui/light_windows.go b/src/tui/light_windows.go index 62b10c12..635b8926 100644 --- a/src/tui/light_windows.go +++ b/src/tui/light_windows.go @@ -72,7 +72,7 @@ func (r *LightRenderer) initPlatform() error { go func() { fd := int(r.inHandle) b := make([]byte, 1) - for { + for !r.closed.Get() { // HACK: if run from PSReadline, something resets ConsoleMode to remove ENABLE_VIRTUAL_TERMINAL_INPUT. _ = windows.SetConsoleMode(windows.Handle(r.inHandle), consoleFlagsInput) @@ -91,9 +91,9 @@ func (r *LightRenderer) closePlatform() { windows.SetConsoleMode(windows.Handle(r.inHandle), r.origStateInput) } -func openTtyIn() *os.File { +func openTtyIn() (*os.File, error) { // not used - return nil + return nil, nil } func (r *LightRenderer) setupTerminal() error { diff --git a/src/tui/tcell.go b/src/tui/tcell.go index 9b8f8620..16ce452d 100644 --- a/src/tui/tcell.go +++ b/src/tui/tcell.go @@ -7,7 +7,6 @@ import ( "time" "github.com/gdamore/tcell/v2" - "github.com/gdamore/tcell/v2/encoding" "github.com/junegunn/fzf/src/util" "github.com/rivo/uniseg" @@ -146,13 +145,13 @@ var ( _initialResize bool = true ) -func (r *FullscreenRenderer) initScreen() { +func (r *FullscreenRenderer) initScreen() error { s, e := tcell.NewScreen() if e != nil { - errorExit(e.Error()) + return e } if e = s.Init(); e != nil { - errorExit(e.Error()) + return e } if r.mouse { s.EnableMouse() @@ -160,16 +159,21 @@ func (r *FullscreenRenderer) initScreen() { s.DisableMouse() } _screen = s + + return nil } -func (r *FullscreenRenderer) Init() { +func (r *FullscreenRenderer) Init() error { if os.Getenv("TERM") == "cygwin" { os.Setenv("TERM", "") } - encoding.Register() - r.initScreen() + if err := r.initScreen(); err != nil { + return err + } initTheme(r.theme, r.defaultTheme(), r.forceBlack) + + return nil } func (r *FullscreenRenderer) Top() int { diff --git a/src/tui/tui.go b/src/tui/tui.go index a56edc7f..e4858c66 100644 --- a/src/tui/tui.go +++ b/src/tui/tui.go @@ -1,8 +1,6 @@ package tui import ( - "fmt" - "os" "strconv" "time" @@ -104,6 +102,7 @@ const ( CtrlAlt Invalid + Fatal Mouse DoubleClick @@ -525,7 +524,7 @@ type TermSize struct { } type Renderer interface { - Init() + Init() error Resize(maxHeightFunc func(int) int) Pause(clear bool) Resume(clear bool, sigcont bool) @@ -685,11 +684,6 @@ func NoColorTheme() *ColorTheme { } } -func errorExit(message string) { - fmt.Fprintln(os.Stderr, message) - util.Exit(2) -} - func init() { Default16 = &ColorTheme{ Colored: true, |
