mirror of
https://github.com/junegunn/fzf.git
synced 2025-11-19 00:53:42 -05:00
Refactor the code so that fzf can be used as a library (#3769)
This commit is contained in:
@@ -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) {}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user