From 17b0aba8e86cf521234c95e9ab54792b1bffd246 Mon Sep 17 00:00:00 2001 From: Daisuke Maki Date: Sun, 15 Feb 2026 14:19:08 +0900 Subject: [PATCH] Fix #511 --- README.md | 14 ++++++++++++++ interface.go | 20 +++++++++++++++----- layout.go | 37 ++++++++++++++++++++++++++++--------- layout_test.go | 15 +++++++++++++-- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3194cc2..c9afec4 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,19 @@ left intact. Default value for StickySelection is false. +### SuppressStatusMsg + +```json +{ + "SuppressStatusMsg": true +} +``` + +SuppressStatusMsg suppresses the status message bar at the bottom of the screen. +When set to true, messages like "Running query..." will not be displayed. + +Default value for SuppressStatusMsg is false. + ### OnCancel ```json @@ -807,6 +820,7 @@ Much code stolen from https://github.com/mattn/gof - [InitialFilter](#initialfilter) - [FuzzyLongestSort](#fuzzylongestsort) - [StickySelection](#stickyselection) + - [SuppressStatusMsg](#suppressstatusmsg) - [OnCancel](#oncancel) - [MaxScanBufferSize](#maxscanbuffersize) - [Keymaps](#keymaps) diff --git a/interface.go b/interface.go index 5683e3e..ebeded8 100644 --- a/interface.go +++ b/interface.go @@ -212,14 +212,22 @@ type UserPrompt struct { styles *StyleSet } -// StatusBar draws the status message bar -type StatusBar struct { +// StatusBar is the interface for printing status messages +type StatusBar interface { + PrintStatus(string, time.Duration) +} + +// screenStatusBar draws the status message bar on screen +type screenStatusBar struct { *AnchorSettings clearTimer *time.Timer styles *StyleSet timerMutex sync.Mutex } +// nullStatusBar is a no-op status bar used when SuppressStatusMsg is true +type nullStatusBar struct{} + // ListArea represents the area where the actual line buffer is // displayed in the screen type ListArea struct { @@ -235,9 +243,10 @@ type ListArea struct { // of components may be configurable, the actual types of components // that are used are set and static type BasicLayout struct { - *StatusBar - prompt *UserPrompt - list *ListArea + statusBar StatusBar + screen Screen + prompt *UserPrompt + list *ListArea } // Keymap holds all the key sequence to action map @@ -297,6 +306,7 @@ type Config struct { MaxScanBufferSize int `json:"MaxScanBufferSize" yaml:"MaxScanBufferSize"` FilterBufSize int `json:"FilterBufSize" yaml:"FilterBufSize"` FuzzyLongestSort bool `json:"FuzzyLongestSort" yaml:"FuzzyLongestSort"` + SuppressStatusMsg bool `json:"SuppressStatusMsg" yaml:"SuppressStatusMsg"` // If this is true, then the prefix for single key jump mode // is displayed by default. diff --git a/layout.go b/layout.go index f3a98ed..5d2e1e4 100644 --- a/layout.go +++ b/layout.go @@ -208,16 +208,16 @@ func (u UserPrompt) Draw(state *Peco) { u.screen.Flush() } -// NewStatusBar creates a new StatusBar struct -func NewStatusBar(screen Screen, anchor VerticalAnchor, anchorOffset int, styles *StyleSet) *StatusBar { - return &StatusBar{ +// newScreenStatusBar creates a new screenStatusBar struct +func newScreenStatusBar(screen Screen, anchor VerticalAnchor, anchorOffset int, styles *StyleSet) *screenStatusBar { + return &screenStatusBar{ AnchorSettings: NewAnchorSettings(screen, anchor, anchorOffset), clearTimer: nil, styles: styles, } } -func (s *StatusBar) stopTimer() { +func (s *screenStatusBar) stopTimer() { s.timerMutex.Lock() defer s.timerMutex.Unlock() if t := s.clearTimer; t != nil { @@ -226,7 +226,7 @@ func (s *StatusBar) stopTimer() { } } -func (s *StatusBar) setClearTimer(t *time.Timer) { +func (s *screenStatusBar) setClearTimer(t *time.Timer) { s.timerMutex.Lock() defer s.timerMutex.Unlock() s.clearTimer = t @@ -234,9 +234,9 @@ func (s *StatusBar) setClearTimer(t *time.Timer) { // PrintStatus prints a new status message. This also resets the // timer created by ClearStatus() -func (s *StatusBar) PrintStatus(msg string, clearDelay time.Duration) { +func (s *screenStatusBar) PrintStatus(msg string, clearDelay time.Duration) { if pdebug.Enabled { - g := pdebug.Marker("StatusBar.PrintStatus") + g := pdebug.Marker("screenStatusBar.PrintStatus") defer g.End() } @@ -292,6 +292,14 @@ func (s *StatusBar) PrintStatus(msg string, clearDelay time.Duration) { } } +// PrintStatus on nullStatusBar is a no-op +func (nullStatusBar) PrintStatus(_ string, _ time.Duration) {} + +// PrintStatus on BasicLayout delegates to the StatusBar +func (l *BasicLayout) PrintStatus(msg string, delay time.Duration) { + l.statusBar.PrintStatus(msg, delay) +} + // NewListArea creates a new ListArea struct func NewListArea(screen Screen, anchor VerticalAnchor, anchorOffset int, sortTopDown bool, styles *StyleSet) *ListArea { return &ListArea{ @@ -612,10 +620,20 @@ func maxOf(a, b int) int { return b } +// newStatusBar returns a StatusBar appropriate for the configuration. +// If SuppressStatusMsg is true, a nullStatusBar (no-op) is returned. +func newStatusBar(state *Peco) StatusBar { + if state.config.SuppressStatusMsg { + return nullStatusBar{} + } + return newScreenStatusBar(state.Screen(), AnchorBottom, 0+extraOffset, state.Styles()) +} + // NewDefaultLayout creates a new Layout in the default format (top-down) func NewDefaultLayout(state *Peco) *BasicLayout { return &BasicLayout{ - StatusBar: NewStatusBar(state.Screen(), AnchorBottom, 0+extraOffset, state.Styles()), + statusBar: newStatusBar(state), + screen: state.Screen(), // The prompt is at the top prompt: NewUserPrompt(state.Screen(), AnchorTop, 0, state.Prompt(), state.Styles()), // The list area is at the top, after the prompt @@ -627,7 +645,8 @@ func NewDefaultLayout(state *Peco) *BasicLayout { // NewBottomUpLayout creates a new Layout in bottom-up format func NewBottomUpLayout(state *Peco) *BasicLayout { return &BasicLayout{ - StatusBar: NewStatusBar(state.Screen(), AnchorBottom, 0+extraOffset, state.Styles()), + statusBar: newStatusBar(state), + screen: state.Screen(), // The prompt is at the bottom, above the status bar prompt: NewUserPrompt(state.Screen(), AnchorBottom, 1+extraOffset, state.Prompt(), state.Styles()), // The list area is at the bottom, above the prompt diff --git a/layout_test.go b/layout_test.go index 279093c..2a6c3b9 100644 --- a/layout_test.go +++ b/layout_test.go @@ -81,9 +81,9 @@ func TestPrintScreen(t *testing.T) { verify("日本語") } -func TestStatusBar(t *testing.T) { +func TestScreenStatusBar(t *testing.T) { screen := NewDummyScreen() - st := NewStatusBar(screen, AnchorBottom, 0, NewStyleSet()) + st := newScreenStatusBar(screen, AnchorBottom, 0, NewStyleSet()) st.PrintStatus("Hello, World!", 0) events := screen.interceptor.events @@ -93,6 +93,17 @@ func TestStatusBar(t *testing.T) { } } +func TestNullStatusBar(t *testing.T) { + screen := NewDummyScreen() + var st StatusBar = nullStatusBar{} + st.PrintStatus("Hello, World!", 0) + + events := screen.interceptor.events + if l := len(events["Flush"]); l != 0 { + t.Errorf("Expected 0 Flush events with nullStatusBar, got %d", l) + } +} + func TestMergeAttribute(t *testing.T) { colors := stringToFg