diff --git a/README.md b/README.md index 0a23357..88fc15e 100644 --- a/README.md +++ b/README.md @@ -806,34 +806,34 @@ For now, styles of following 7 items can be customized in `config.json`. ### Foreground Colors -- `"black"` for `termbox.ColorBlack` -- `"red"` for `termbox.ColorRed` -- `"green"` for `termbox.ColorGreen` -- `"yellow"` for `termbox.ColorYellow` -- `"blue"` for `termbox.ColorBlue` -- `"magenta"` for `termbox.ColorMagenta` -- `"cyan"` for `termbox.ColorCyan` -- `"white"` for `termbox.ColorWhite` +- `"black"` for `tcell.ColorBlack` +- `"red"` for `tcell.ColorRed` +- `"green"` for `tcell.ColorGreen` +- `"yellow"` for `tcell.ColorYellow` +- `"blue"` for `tcell.ColorBlue` +- `"magenta"` for `tcell.ColorMagenta` +- `"cyan"` for `tcell.ColorCyan` +- `"white"` for `tcell.ColorWhite` - `"0"`-`"255"` for 256color ([Use256Color](#use256color) must be enabled) ### Background Colors -- `"on_black"` for `termbox.ColorBlack` -- `"on_red"` for `termbox.ColorRed` -- `"on_green"` for `termbox.ColorGreen` -- `"on_yellow"` for `termbox.ColorYellow` -- `"on_blue"` for `termbox.ColorBlue` -- `"on_magenta"` for `termbox.ColorMagenta` -- `"on_cyan"` for `termbox.ColorCyan` -- `"on_white"` for `termbox.ColorWhite` +- `"on_black"` for `tcell.ColorBlack` +- `"on_red"` for `tcell.ColorRed` +- `"on_green"` for `tcell.ColorGreen` +- `"on_yellow"` for `tcell.ColorYellow` +- `"on_blue"` for `tcell.ColorBlue` +- `"on_magenta"` for `tcell.ColorMagenta` +- `"on_cyan"` for `tcell.ColorCyan` +- `"on_white"` for `tcell.ColorWhite` - `"on_0"`-`"on_255"` for 256color ([Use256Color](#use256color) must be enabled) ### Attributes -- `"bold"` for fg: `termbox.AttrBold` -- `"underline"` for fg: `termbox.AttrUnderline` -- `"reverse"` for fg: `termbox.AttrReverse` -- `"on_bold"` for bg: `termbox.AttrBold` (this attribute actually makes the background blink on some platforms/environments, e.g. linux console, xterm...) +- `"bold"` for fg: `tcell.AttrBold` +- `"underline"` for fg: `tcell.AttrUnderline` +- `"reverse"` for fg: `tcell.AttrReverse` +- `"on_bold"` for bg: `tcell.AttrBold` (this attribute actually makes the background blink on some platforms/environments, e.g. linux console, xterm...) ## CustomFilter diff --git a/event.go b/event.go index c18c7e5..fc9cb53 100644 --- a/event.go +++ b/event.go @@ -14,9 +14,7 @@ const ( EventError ) -// Event is peco's internal event type, replacing termbox.Event. -// This decouples all code outside the screen adapter from the -// terminal library. +// Event is peco's internal event type, decoupled from any terminal library. type Event struct { Type EventType Key keyseq.KeyType diff --git a/input.go b/input.go index bdfd10a..61452fd 100644 --- a/input.go +++ b/input.go @@ -49,7 +49,7 @@ func (i *Input) handleInputEvent(ctx context.Context, ev Event) error { return nil case EventKey: // ModAlt is a sequence of letters with a leading \x1b (=Esc). - // It would be nice if termbox differentiated this for us, but + // The terminal library doesn't differentiate this for us, so // we workaround it by waiting (juuuust a few milliseconds) for // extra key events. If no extra events arrive, it should be Esc diff --git a/internal/keyseq/keys.go b/internal/keyseq/keys.go index a0127e3..29d4ab3 100644 --- a/internal/keyseq/keys.go +++ b/internal/keyseq/keys.go @@ -6,10 +6,8 @@ import ( "unicode/utf8" ) -// KeyType represents a keyboard key. Values are defined to match termbox-go's -// constants so that the adapter layer in screen.go can do simple type casts -// during Phase 1 of the migration. In Phase 2, the adapter will map between -// peco's KeyType and tcell's key constants. +// KeyType represents a keyboard key. The adapter layer in screen.go maps +// between peco's KeyType and tcell's key constants. type KeyType uint16 // Function keys @@ -39,8 +37,8 @@ const ( ) // Mouse keys. -// In termbox-go there is one internal gap constant between KeyArrowRight -// and MouseLeft, so MouseLeft = KeyArrowRight - 2 (not -1). +// There is an intentional gap between KeyArrowRight and MouseLeft +// (MouseLeft = KeyArrowRight - 2, not -1) for historical reasons. const ( MouseLeft KeyType = 0xFFFF - iota - 23 // KeyArrowRight - 2 MouseMiddle // KeyArrowRight - 3 @@ -211,8 +209,7 @@ func ToKeyList(ksk string) (KeyList, error) { } // KeyEventToString returns a human-readable name for a key event described -// by the given key type, character, and modifier. This replaces the old -// EventToString that took a termbox.Event directly. +// by the given key type, character, and modifier. func KeyEventToString(key KeyType, ch rune, mod ModifierKey) (string, error) { s := "" if key == 0 { diff --git a/layout.go b/layout.go index d1ed1a9..6bcc71f 100644 --- a/layout.go +++ b/layout.go @@ -145,7 +145,7 @@ func (u UserPrompt) Draw(state *Peco) { fg := u.styles.Query.fg bg := u.styles.Query.bg - // Used to notify termbox where our cursor is + // Used to notify the screen where our cursor is var posX int switch ql { diff --git a/peco.go b/peco.go index 3cb1089..d37f3d3 100644 --- a/peco.go +++ b/peco.go @@ -118,7 +118,7 @@ func New() *Peco { idgen: newIDGen(), queryExecDelay: 50 * time.Millisecond, readyCh: make(chan struct{}), - screen: NewTermbox(), + screen: NewTcellScreen(), selection: NewSelection(), maxScanBufferSize: bufio.MaxScanTokenSize, } @@ -425,7 +425,7 @@ func (p *Peco) Run(ctx context.Context) (err error) { } p.source = src - // If --height is specified, use InlineScreen instead of the default Termbox screen + // If --height is specified, use InlineScreen instead of the default TcellScreen if p.heightSpec != nil { p.screen = NewInlineScreen(*p.heightSpec) } diff --git a/screen.go b/screen.go index 4b001d8..41d6afa 100644 --- a/screen.go +++ b/screen.go @@ -13,9 +13,8 @@ import ( "github.com/peco/peco/internal/keyseq" ) -// Termbox implements the Screen interface using tcell/v2. -// The name is kept for compatibility with the rest of the codebase. -type Termbox struct { +// TcellScreen implements the Screen interface using tcell/v2. +type TcellScreen struct { mutex sync.Mutex screen tcell.Screen resumeCh chan chan struct{} @@ -76,7 +75,7 @@ func tcellEventToEvent(tev tcell.Event) Event { if key == tcell.KeyRune { r := ev.Rune() // Special case: space must be sent as KeySpace with Ch=0 - // to match termbox behavior expected by doAcceptChar + // to match the convention expected by doAcceptChar if r == ' ' { return Event{ Type: EventKey, @@ -159,7 +158,7 @@ func attributeToTcellStyle(fg, bg Attribute) tcell.Style { return style } -func (t *Termbox) Init(cfg *Config) error { +func (t *TcellScreen) Init(cfg *Config) error { screen, err := tcell.NewScreen() if err != nil { return fmt.Errorf("failed to create tcell screen: %w", err) @@ -173,8 +172,8 @@ func (t *Termbox) Init(cfg *Config) error { return t.PostInit(cfg) } -func NewTermbox() *Termbox { - return &Termbox{ +func NewTcellScreen() *TcellScreen { + return &TcellScreen{ suspendCh: make(chan struct{}), resumeCh: make(chan chan struct{}), doneCh: make(chan struct{}), @@ -184,7 +183,7 @@ func NewTermbox() *Termbox { // finiScreen finalizes the tcell screen without signaling a permanent // shutdown. Used by the suspend handler so the goroutine continues // to listen for further suspend/resume cycles. -func (t *Termbox) finiScreen() { +func (t *TcellScreen) finiScreen() { t.mutex.Lock() s := t.screen t.screen = nil @@ -197,16 +196,16 @@ func (t *Termbox) finiScreen() { // Close permanently shuts down the screen and signals all goroutines // started by PollEvent to exit. -func (t *Termbox) Close() error { +func (t *TcellScreen) Close() error { if pdebug.Enabled { - pdebug.Printf("Termbox: Close") + pdebug.Printf("TcellScreen: Close") } t.finiScreen() t.closeOnce.Do(func() { close(t.doneCh) }) return nil } -func (t *Termbox) SetCursor(x, y int) { +func (t *TcellScreen) SetCursor(x, y int) { t.mutex.Lock() defer t.mutex.Unlock() if t.screen == nil { @@ -218,12 +217,12 @@ func (t *Termbox) SetCursor(x, y int) { // SendEvent is used to allow programmers generate random // events, but it's only useful for testing purposes. // When interacting with tcell, this method is a noop -func (t *Termbox) SendEvent(_ Event) { +func (t *TcellScreen) SendEvent(_ Event) { // no op } // Flush calls tcell's Show to synchronize the screen -func (t *Termbox) Flush() error { +func (t *TcellScreen) Flush() error { t.mutex.Lock() defer t.mutex.Unlock() if t.screen == nil { @@ -236,7 +235,7 @@ func (t *Termbox) Flush() error { // Sync forces a complete redraw of every cell on the physical display. // This recovers from screen corruption caused by external output (e.g., // STDERR messages written directly to the terminal). -func (t *Termbox) Sync() { +func (t *TcellScreen) Sync() { t.mutex.Lock() defer t.mutex.Unlock() if t.screen == nil { @@ -248,7 +247,7 @@ func (t *Termbox) Sync() { // PollEvent returns a channel that you can listen to for // terminal events. The actual polling is done in a // separate goroutine -func (t *Termbox) PollEvent(ctx context.Context, cfg *Config) chan Event { +func (t *TcellScreen) PollEvent(ctx context.Context, cfg *Config) chan Event { evCh := make(chan Event) go func() { @@ -313,14 +312,14 @@ func (t *Termbox) PollEvent(ctx context.Context, cfg *Config) chan Event { return evCh } -func (t *Termbox) Suspend() { +func (t *TcellScreen) Suspend() { select { case t.suspendCh <- struct{}{}: default: } } -func (t *Termbox) Resume(ctx context.Context) { +func (t *TcellScreen) Resume(ctx context.Context) { // Resume must be a block operation, because we can't safely proceed // without actually knowing that the screen has been re-initialized. // So we send a channel where we expect a reply back, and wait for that. @@ -343,7 +342,7 @@ func (t *Termbox) Resume(ctx context.Context) { } // SetCell writes to the terminal -func (t *Termbox) SetCell(x, y int, ch rune, fg, bg Attribute) { +func (t *TcellScreen) SetCell(x, y int, ch rune, fg, bg Attribute) { t.mutex.Lock() defer t.mutex.Unlock() if t.screen == nil { @@ -354,7 +353,7 @@ func (t *Termbox) SetCell(x, y int, ch rune, fg, bg Attribute) { } // Size returns the dimensions of the current terminal -func (t *Termbox) Size() (int, int) { +func (t *TcellScreen) Size() (int, int) { t.mutex.Lock() defer t.mutex.Unlock() if t.screen == nil { @@ -374,7 +373,7 @@ type PrintArgs struct { ANSIAttrs []ansi.AttrSpan // per-character ANSI attributes for this segment } -func (t *Termbox) Print(args PrintArgs) int { +func (t *TcellScreen) Print(args PrintArgs) int { return screenPrint(t, args) } diff --git a/screen_inline.go b/screen_inline.go index ed954ba..6aec588 100644 --- a/screen_inline.go +++ b/screen_inline.go @@ -209,7 +209,7 @@ func (s *InlineScreen) PollEvent(ctx context.Context, cfg *Config) chan Event { return evCh } -// SendEvent is a no-op for InlineScreen (same as Termbox). +// SendEvent is a no-op for InlineScreen (same as TcellScreen). func (s *InlineScreen) SendEvent(_ Event) {} // Suspend is a no-op for inline mode. diff --git a/screen_posix.go b/screen_posix.go index 4fceee6..de73da2 100644 --- a/screen_posix.go +++ b/screen_posix.go @@ -5,6 +5,6 @@ package peco // PostInit is a no-op on POSIX systems. tcell auto-detects // color capability via terminfo. -func (t *Termbox) PostInit(cfg *Config) error { +func (t *TcellScreen) PostInit(cfg *Config) error { return nil } diff --git a/screen_test.go b/screen_test.go index bdfb93d..4331b1d 100644 --- a/screen_test.go +++ b/screen_test.go @@ -8,12 +8,12 @@ import ( "github.com/stretchr/testify/require" ) -// TestTermboxSuspendHandlerExitsOnClose verifies that the suspend handler +// TestTcellScreenSuspendHandlerExitsOnClose verifies that the suspend handler // goroutine (started by PollEvent) exits when Close() is called, even if // the context has not been cancelled. This is the goroutine leak described // in CODE_REVIEW.md §7.1. -func TestTermboxSuspendHandlerExitsOnClose(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenSuspendHandlerExitsOnClose(t *testing.T) { + tb := NewTcellScreen() // Use a context that will NOT be cancelled during this test. // The goroutine must exit via doneCh, not ctx.Done(). @@ -46,11 +46,11 @@ func TestTermboxSuspendHandlerExitsOnClose(t *testing.T) { } } -// TestTermboxPollingGoroutineExitsOnClose verifies that a goroutine blocked +// TestTcellScreenPollingGoroutineExitsOnClose verifies that a goroutine blocked // on resumeCh (as the polling goroutine would be after screen finalization) // exits when Close() is called. -func TestTermboxPollingGoroutineExitsOnClose(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenPollingGoroutineExitsOnClose(t *testing.T) { + tb := NewTcellScreen() ctx := context.Background() @@ -78,11 +78,11 @@ func TestTermboxPollingGoroutineExitsOnClose(t *testing.T) { } } -// TestTermboxCloseIdempotent verifies that Close() can be called multiple +// TestTcellScreenCloseIdempotent verifies that Close() can be called multiple // times without panicking (important because the suspend handler calls // finiScreen and then Close() is called at shutdown). -func TestTermboxCloseIdempotent(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenCloseIdempotent(t *testing.T) { + tb := NewTcellScreen() require.NotPanics(t, func() { tb.Close() @@ -91,11 +91,11 @@ func TestTermboxCloseIdempotent(t *testing.T) { }) } -// TestTermboxSuspendThenClose verifies that a suspend (which calls finiScreen) +// TestTcellScreenSuspendThenClose verifies that a suspend (which calls finiScreen) // followed by a permanent Close() works correctly — the doneCh should be // closed by Close() even though finiScreen was already called. -func TestTermboxSuspendThenClose(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenSuspendThenClose(t *testing.T) { + tb := NewTcellScreen() ctx := context.Background() @@ -130,8 +130,8 @@ func TestTermboxSuspendThenClose(t *testing.T) { } } -func TestTermboxResumeNoDeadlock(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenResumeNoDeadlock(t *testing.T) { + tb := NewTcellScreen() ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -158,8 +158,8 @@ func TestTermboxResumeNoDeadlock(t *testing.T) { } } -func TestTermboxResumeDoesNotDropSend(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenResumeDoesNotDropSend(t *testing.T) { + tb := NewTcellScreen() ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() @@ -181,8 +181,8 @@ func TestTermboxResumeDoesNotDropSend(t *testing.T) { } } -func TestTermboxResumeContextCancelled(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenResumeContextCancelled(t *testing.T) { + tb := NewTcellScreen() ctx, cancel := context.WithCancel(context.Background()) @@ -203,8 +203,8 @@ func TestTermboxResumeContextCancelled(t *testing.T) { } } -func TestTermboxResumeContextCancelledWhileWaitingForReply(t *testing.T) { - tb := NewTermbox() +func TestTcellScreenResumeContextCancelledWhileWaitingForReply(t *testing.T) { + tb := NewTcellScreen() ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/screen_windows.go b/screen_windows.go index 6390d95..755b178 100644 --- a/screen_windows.go +++ b/screen_windows.go @@ -1,6 +1,6 @@ package peco // PostInit is a no-op on Windows. tcell handles input mode automatically. -func (t *Termbox) PostInit(cfg *Config) error { +func (t *TcellScreen) PostInit(cfg *Config) error { return nil }