mirror of
https://github.com/peco/peco.git
synced 2026-09-10 07:16:29 -04:00
Merge pull request #595 from peco/gh-445
Add new layout, refactr layout lookup/registration
This commit is contained in:
commit
757abc8b66
|
|
@ -437,8 +437,7 @@ func doToggleSelectionAndSelectNext(ctx context.Context, state *Peco, e Event) {
|
|||
state.Hub().Batch(ctx, func(ctx context.Context) {
|
||||
ctx = context.WithValue(ctx, isTopLevelActionCall, false)
|
||||
doToggleSelection(ctx, state, e)
|
||||
// XXX This is sucky. Fix later
|
||||
if state.LayoutType() == "top-down" {
|
||||
if state.LayoutType() != LayoutTypeBottomUp {
|
||||
doSelectDown(ctx, state, e)
|
||||
} else {
|
||||
doSelectUp(ctx, state, e)
|
||||
|
|
|
|||
10
interface.go
10
interface.go
|
|
@ -33,9 +33,10 @@ const (
|
|||
)
|
||||
|
||||
const (
|
||||
DefaultLayoutType = LayoutTypeTopDown // LayoutTypeTopDown makes the layout so the items read from top to bottom
|
||||
LayoutTypeTopDown = "top-down" // LayoutTypeBottomUp changes the layout to read from bottom to up
|
||||
LayoutTypeBottomUp = "bottom-up"
|
||||
DefaultLayoutType = LayoutTypeTopDown // LayoutTypeTopDown makes the layout so the items read from top to bottom
|
||||
LayoutTypeTopDown = "top-down" // LayoutTypeTopDown displays prompt at top, list top-to-bottom
|
||||
LayoutTypeBottomUp = "bottom-up" // LayoutTypeBottomUp displays prompt at bottom, list bottom-to-top
|
||||
LayoutTypeTopDownQueryBottom = "top-down-query-bottom" // LayoutTypeTopDownQueryBottom displays list top-to-bottom, prompt at bottom
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -195,6 +196,7 @@ type Layout interface {
|
|||
DrawScreen(*Peco, *DrawOptions)
|
||||
MovePage(*Peco, PagingRequest) (moved bool)
|
||||
PurgeDisplayCache()
|
||||
SortTopDown() bool
|
||||
}
|
||||
|
||||
// AnchorSettings groups items that are required to control
|
||||
|
|
@ -457,7 +459,7 @@ type CLIOptions struct {
|
|||
OptInitialMatcher string `long:"initial-matcher" description:"specify the default matcher (deprecated)"`
|
||||
OptInitialFilter string `long:"initial-filter" description:"specify the default filter"`
|
||||
OptPrompt string `long:"prompt" description:"specify the prompt string"`
|
||||
OptLayout string `long:"layout" description:"layout to be used. 'top-down' or 'bottom-up'. default is 'top-down'"`
|
||||
OptLayout string `long:"layout" description:"layout to be used. 'top-down', 'bottom-up', or 'top-down-query-bottom'. default is 'top-down'"`
|
||||
OptSelect1 bool `long:"select-1" description:"select first item and immediately exit if the input contains only 1 item"`
|
||||
OptExitZero bool `long:"exit-0" description:"exit immediately with status 1 if the input is empty"`
|
||||
OptSelectAll bool `long:"select-all" description:"select all items and immediately exit"`
|
||||
|
|
|
|||
46
layout.go
46
layout.go
|
|
@ -15,9 +15,28 @@ import (
|
|||
|
||||
var extraOffset int = 0
|
||||
|
||||
// LayoutFactory is a function that creates a BasicLayout for the given Peco state.
|
||||
type LayoutFactory func(*Peco) *BasicLayout
|
||||
|
||||
var layoutRegistry = map[LayoutType]LayoutFactory{}
|
||||
|
||||
// RegisterLayout registers a layout factory under the given name.
|
||||
func RegisterLayout(name LayoutType, factory LayoutFactory) {
|
||||
layoutRegistry[name] = factory
|
||||
}
|
||||
|
||||
// NewLayout creates a layout by looking up the registry. Falls back to top-down.
|
||||
func NewLayout(layoutType LayoutType, state *Peco) *BasicLayout {
|
||||
if factory, ok := layoutRegistry[layoutType]; ok {
|
||||
return factory(state)
|
||||
}
|
||||
return layoutRegistry[LayoutTypeTopDown](state)
|
||||
}
|
||||
|
||||
// IsValidLayoutType checks if a string is a supported layout type
|
||||
func IsValidLayoutType(v LayoutType) bool {
|
||||
return v == LayoutTypeTopDown || v == LayoutTypeBottomUp
|
||||
_, ok := layoutRegistry[v]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsValidVerticalAnchor checks if the specified anchor is supported
|
||||
|
|
@ -656,6 +675,31 @@ func NewBottomUpLayout(state *Peco) *BasicLayout {
|
|||
}
|
||||
}
|
||||
|
||||
// NewTopDownQueryBottomLayout creates a new Layout with list top-to-bottom
|
||||
// and the query prompt at the bottom.
|
||||
func NewTopDownQueryBottomLayout(state *Peco) *BasicLayout {
|
||||
return &BasicLayout{
|
||||
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 top
|
||||
// It's displayed in top-to-bottom order
|
||||
list: NewListArea(state.Screen(), AnchorTop, 0, true, state.Styles()),
|
||||
}
|
||||
}
|
||||
|
||||
// SortTopDown returns whether this layout sorts lines from top to bottom.
|
||||
func (l *BasicLayout) SortTopDown() bool {
|
||||
return l.list.sortTopDown
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterLayout(LayoutTypeTopDown, NewDefaultLayout)
|
||||
RegisterLayout(LayoutTypeBottomUp, NewBottomUpLayout)
|
||||
RegisterLayout(LayoutTypeTopDownQueryBottom, NewTopDownQueryBottomLayout)
|
||||
}
|
||||
|
||||
func (l *BasicLayout) PurgeDisplayCache() {
|
||||
l.list.purgeDisplayCache()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ func TestLayoutType(t *testing.T) {
|
|||
}{
|
||||
{LayoutTypeTopDown, true},
|
||||
{LayoutTypeBottomUp, true},
|
||||
{LayoutTypeTopDownQueryBottom, true},
|
||||
{"foobar", false},
|
||||
}
|
||||
for _, l := range layouts {
|
||||
|
|
@ -366,3 +367,71 @@ func TestGHIssue455_DrawScreenForceSync(t *testing.T) {
|
|||
require.Empty(t, syncEvents, "expected no Sync calls with nil options")
|
||||
})
|
||||
}
|
||||
|
||||
// TestNewLayout verifies the layout registry returns correct layout types.
|
||||
func TestNewLayout(t *testing.T) {
|
||||
makeState := func() *Peco {
|
||||
state := New()
|
||||
state.screen = NewDummyScreen()
|
||||
state.skipReadConfig = true
|
||||
state.Filters().Add(filter.NewIgnoreCase())
|
||||
return state
|
||||
}
|
||||
|
||||
t.Run("top-down", func(t *testing.T) {
|
||||
state := makeState()
|
||||
layout := NewLayout(LayoutTypeTopDown, state)
|
||||
require.NotNil(t, layout)
|
||||
require.True(t, layout.SortTopDown(), "top-down layout should sort top-down")
|
||||
require.Equal(t, AnchorTop, layout.prompt.anchor, "top-down prompt should be anchored at top")
|
||||
})
|
||||
|
||||
t.Run("bottom-up", func(t *testing.T) {
|
||||
state := makeState()
|
||||
layout := NewLayout(LayoutTypeBottomUp, state)
|
||||
require.NotNil(t, layout)
|
||||
require.False(t, layout.SortTopDown(), "bottom-up layout should not sort top-down")
|
||||
require.Equal(t, AnchorBottom, layout.prompt.anchor, "bottom-up prompt should be anchored at bottom")
|
||||
})
|
||||
|
||||
t.Run("top-down-query-bottom", func(t *testing.T) {
|
||||
state := makeState()
|
||||
layout := NewLayout(LayoutTypeTopDownQueryBottom, state)
|
||||
require.NotNil(t, layout)
|
||||
require.True(t, layout.SortTopDown(), "top-down-query-bottom layout should sort top-down")
|
||||
require.Equal(t, AnchorBottom, layout.prompt.anchor, "top-down-query-bottom prompt should be anchored at bottom")
|
||||
require.Equal(t, AnchorTop, layout.list.anchor, "top-down-query-bottom list should be anchored at top")
|
||||
})
|
||||
|
||||
t.Run("unknown falls back to top-down", func(t *testing.T) {
|
||||
state := makeState()
|
||||
layout := NewLayout("unknown-layout", state)
|
||||
require.NotNil(t, layout)
|
||||
require.True(t, layout.SortTopDown(), "fallback layout should sort top-down")
|
||||
require.Equal(t, AnchorTop, layout.prompt.anchor, "fallback prompt should be anchored at top")
|
||||
})
|
||||
}
|
||||
|
||||
// TestTopDownQueryBottomLayout verifies the specific properties of the
|
||||
// top-down-query-bottom layout.
|
||||
func TestTopDownQueryBottomLayout(t *testing.T) {
|
||||
state := New()
|
||||
state.screen = NewDummyScreen()
|
||||
state.skipReadConfig = true
|
||||
state.Filters().Add(filter.NewIgnoreCase())
|
||||
|
||||
layout := NewTopDownQueryBottomLayout(state)
|
||||
|
||||
require.Equal(t, AnchorBottom, layout.prompt.anchor,
|
||||
"prompt should be anchored at bottom")
|
||||
require.Equal(t, 1+extraOffset, layout.prompt.anchorOffset,
|
||||
"prompt anchor offset should be 1+extraOffset")
|
||||
require.Equal(t, AnchorTop, layout.list.anchor,
|
||||
"list should be anchored at top")
|
||||
require.Equal(t, 0, layout.list.anchorOffset,
|
||||
"list anchor offset should be 0")
|
||||
require.True(t, layout.list.sortTopDown,
|
||||
"list should sort top-down")
|
||||
require.True(t, layout.SortTopDown(),
|
||||
"SortTopDown() should return true")
|
||||
}
|
||||
|
|
|
|||
8
view.go
8
view.go
|
|
@ -25,13 +25,7 @@ func (jlr JumpToLineRequest) Line() int {
|
|||
}
|
||||
|
||||
func NewView(state *Peco) *View {
|
||||
var layout Layout
|
||||
switch state.LayoutType() {
|
||||
case LayoutTypeBottomUp:
|
||||
layout = NewBottomUpLayout(state)
|
||||
default:
|
||||
layout = NewDefaultLayout(state)
|
||||
}
|
||||
layout := NewLayout(LayoutType(state.LayoutType()), state)
|
||||
return &View{
|
||||
state: state,
|
||||
layout: layout,
|
||||
|
|
|
|||
Loading…
Reference in a new issue