Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

extend lastcmd mode to support all the previous cmds #1831

Merged
merged 1 commit into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 29 additions & 15 deletions pkg/cli/modes/lastcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,20 +45,26 @@ func NewLastcmd(app cli.App, cfg LastcmdSpec) (Lastcmd, error) {
}
c := cfg.Store.Cursor("")
c.Prev()
cmd, err := c.Get()
if err != nil {
return nil, fmt.Errorf("db error: %v", err)
}
wordifier := cfg.Wordifier
if wordifier == nil {
wordifier = strings.Fields
}
cmdText := cmd.Text
words := wordifier(cmdText)
entries := make([]lastcmdEntry, len(words)+1)
entries[0] = lastcmdEntry{content: cmdText}
for i, word := range words {
entries[i+1] = lastcmdEntry{strconv.Itoa(i), strconv.Itoa(i - len(words)), word}

get_items := func(c histutil.Cursor) []lastcmdEntry {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be called getItems - also the choice of making c an argument seems arbitrary, it's always the same argument

cmd, err := c.Get()
if err != nil {
return []lastcmdEntry{}
}

wordifier := cfg.Wordifier
if wordifier == nil {
wordifier = strings.Fields
}
cmdText := cmd.Text
words := wordifier(cmdText)
entries := make([]lastcmdEntry, len(words)+1)
entries[0] = lastcmdEntry{content: cmdText}
for i, word := range words {
entries[i+1] = lastcmdEntry{strconv.Itoa(i), strconv.Itoa(i - len(words)), word}
}

return entries
}

accept := func(text string) {
Expand All @@ -74,9 +80,17 @@ func NewLastcmd(app cli.App, cfg LastcmdSpec) (Lastcmd, error) {
OnAccept: func(it tk.Items, i int) {
accept(it.(lastcmdItems).entries[i].content)
},
OnUnderFlow: func(lb tk.ListBox) {
c.Prev()
lb.Reset(filterLastcmdItems(get_items(c), ""), 0)
},
OnOverFlow: func(lb tk.ListBox) {
c.Next()
lb.Reset(filterLastcmdItems(get_items(c), ""), 0)
},
},
OnFilter: func(w tk.ComboBox, p string) {
items := filterLastcmdItems(entries, p)
items := filterLastcmdItems(get_items(c), p)
if len(items.entries) == 1 {
accept(items.entries[0].content)
} else {
Expand Down
59 changes: 42 additions & 17 deletions pkg/cli/modes/lastcmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,6 @@ func TestNewLastcmd_FocusedWidgetNotCodeArea(t *testing.T) {
})
}

func TestNewLastcmd_StoreError(t *testing.T) {
f := Setup()
defer f.Stop()

db := histutil.NewFaultyInMemoryDB()
store, err := histutil.NewDBStore(db)
if err != nil {
panic(err)
}
db.SetOneOffError(errMock)

_, err = NewLastcmd(f.App, LastcmdSpec{Store: store})
if err.Error() != "db error: mock error" {
t.Error("expect db error")
}
}

func TestLastcmd(t *testing.T) {
f := Setup()
defer f.Stop()
Expand Down Expand Up @@ -115,3 +98,45 @@ func startLastcmd(app cli.App, spec LastcmdSpec) {
w, err := NewLastcmd(app, spec)
startMode(app, w, err)
}

func TestLastcmdOverFlow(t *testing.T) {
f := Setup()
defer f.Stop()

st := histutil.NewMemStore("foo", "bar")
startLastcmd(f.App, LastcmdSpec{
Store: st,
})

// Test UI.
f.TestTTY(t,
"\n", // empty code area
" LASTCMD ", Styles,
"********* ", term.DotHere, "\n",
" bar \n", Styles,
"++++++++++++++++++++++++++++++++++++++++++++++++++",
" 0 bar",
)

// Test underflow.
f.TTY.Inject(term.K(ui.Up, ui.Alt))
f.TestTTY(t,
"\n", // empty code area
" LASTCMD ", Styles,
"********* ", term.DotHere, "\n",
" foo \n", Styles,
"++++++++++++++++++++++++++++++++++++++++++++++++++",
" 0 foo",
)

// Test overflow.
f.TTY.Inject(term.K(ui.Down, ui.Alt))
f.TestTTY(t,
"\n", // empty code area
" LASTCMD ", Styles,
"********* ", term.DotHere, "\n",
" bar \n", Styles,
"++++++++++++++++++++++++++++++++++++++++++++++++++",
" 0 bar",
)
}
16 changes: 16 additions & 0 deletions pkg/cli/tk/listbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type ListBoxSpec struct {
OnSelect func(it Items, i int)
// A function called on the accept event.
OnAccept func(it Items, i int)
// A function called when selecting before the beginning of the items.
OnUnderFlow func(ListBox)
// A function called when selecting after the ending of the items.
OnOverFlow func(ListBox)
Comment on lines +38 to +41
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are misnomers that they're no longer triggered when underflow/overflow. Maybe OnPrevItemSet would be a bit better.

// Whether the listbox should be rendered in a horizontal layout. Note that
// in the horizontal layout, items must have only one line.
Horizontal bool
Expand Down Expand Up @@ -65,6 +69,12 @@ func NewListBox(spec ListBoxSpec) ListBox {
if spec.OnAccept == nil {
spec.OnAccept = func(Items, int) {}
}
if spec.OnUnderFlow == nil {
spec.OnUnderFlow = func(ListBox) {}
}
if spec.OnOverFlow == nil {
spec.OnOverFlow = func(ListBox) {}
}
if spec.OnSelect == nil {
spec.OnSelect = func(Items, int) {}
} else {
Expand Down Expand Up @@ -284,6 +294,12 @@ func (w *listBox) Handle(event term.Event) bool {
case term.K(ui.Enter):
w.Accept()
return true
case term.K(ui.Up, ui.Alt):
w.OnUnderFlow(w)
return true
case term.K(ui.Down, ui.Alt):
w.OnOverFlow(w)
return true
Comment on lines +297 to +302
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nicer if this is part of the keybinding rather than hardcoded in the event handler.

}
return false
}
Expand Down
Loading