This commit is contained in:
Daisuke Maki 2026-02-15 14:19:08 +09:00
parent 7c8c95b391
commit 17b0aba8e8
4 changed files with 70 additions and 16 deletions

View file

@ -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)

View file

@ -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.

View file

@ -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

View file

@ -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