summaryrefslogtreecommitdiff
path: root/src/tui
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2024-05-07 01:06:42 +0900
committerGitHub <noreply@github.com>2024-05-07 01:06:42 +0900
commite8405f40fe2eb3675f1cb4f69e825eff5f13f269 (patch)
treec917367f1f0098939f9cdf7376a2a135907024fc /src/tui
parent065b9e6fb2ce3e6e50ff423c3786989afa04ee14 (diff)
downloadfzf-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.go4
-rw-r--r--src/tui/eventtype_string.go51
-rw-r--r--src/tui/light.go49
-rw-r--r--src/tui/light_unix.go18
-rw-r--r--src/tui/light_windows.go6
-rw-r--r--src/tui/tcell.go18
-rw-r--r--src/tui/tui.go10
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,