From 06b9142b6a8d5e49528fef84515f2142cc8c2505 Mon Sep 17 00:00:00 2001 From: rsteube Date: Sun, 7 Jan 2024 21:29:53 +0100 Subject: [PATCH 1/2] pkg/cli/modes: Filter by tags. --- pkg/cli/modes/completion.go | 68 +++++++++++++++++++++++++++++++++-- pkg/edit/complete/raw_item.go | 2 ++ pkg/edit/completion.go | 8 +++-- pkg/edit/completion_test.go | 4 +-- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/pkg/cli/modes/completion.go b/pkg/cli/modes/completion.go index 17d42b111..50744762e 100644 --- a/pkg/cli/modes/completion.go +++ b/pkg/cli/modes/completion.go @@ -2,9 +2,11 @@ package modes import ( "errors" + "sort" "strings" "src.elv.sh/pkg/cli" + "src.elv.sh/pkg/cli/term" "src.elv.sh/pkg/cli/tk" "src.elv.sh/pkg/diag" "src.elv.sh/pkg/ui" @@ -23,6 +25,8 @@ type CompletionSpec struct { Replace diag.Ranging Items []CompletionItem Filter FilterSpec + tags []string + tagIndex int } // CompletionItem represents a completion item, also known as a candidate. @@ -31,6 +35,8 @@ type CompletionItem struct { ToShow ui.Text // Used when inserting a candidate. ToInsert string + // TODO + Tag string } type completion struct { @@ -42,6 +48,26 @@ var errNoCandidates = errors.New("no candidates") // NewCompletion starts the completion UI. func NewCompletion(app cli.App, cfg CompletionSpec) (Completion, error) { + itemsByTag := make(map[string][]CompletionItem) + for _, item := range cfg.Items { + itemsByTag[item.Tag] = append(itemsByTag[item.Tag], item) + } + // TODO presort items? + for tag := range itemsByTag { + if strings.TrimSpace(tag) != "" { + cfg.tags = append(cfg.tags, tag) + } + } + sort.Strings(cfg.tags) + switch len(cfg.tags) { + case 1: + if strings.TrimSpace(cfg.tags[0]) == "" { + cfg.tags = []string{cfg.Name} + } + default: + cfg.tags = append([]string{cfg.Name}, cfg.tags...) + } + codeArea, err := FocusedCodeArea(app) if err != nil { return nil, err @@ -49,9 +75,41 @@ func NewCompletion(app cli.App, cfg CompletionSpec) (Completion, error) { if len(cfg.Items) == 0 { return nil, errNoCandidates } - w := tk.NewComboBox(tk.ComboBoxSpec{ + var w tk.ComboBox + w = tk.NewComboBox(tk.ComboBoxSpec{ CodeArea: tk.CodeAreaSpec{ - Prompt: modePrompt(" COMPLETING "+cfg.Name+" ", true), + Bindings: tk.MapBindings{ + term.K('n', ui.Alt): func(tk.Widget) { + if len(cfg.tags) == 1 { + return + } + + cfg.tagIndex += 1 + if cfg.tagIndex >= len(cfg.tags) { + cfg.tagIndex = 0 + } + w.Refilter() + }, + term.K('p', ui.Alt): func(t tk.Widget) { + if len(cfg.tags) == 1 { + return + } + + cfg.tagIndex -= 1 + if cfg.tagIndex < 0 { + cfg.tagIndex = len(cfg.tags) - 1 + } + w.Refilter() + }, + }, + Prompt: func() ui.Text { + prompt := modePrompt(" COMPLETING "+cfg.tags[cfg.tagIndex]+" ", false)() + if cfg.tagIndex > 0 { + prompt = ui.StyleText(prompt, ui.Inverse) + } + prompt = ui.Concat(prompt, ui.T(" ")) + return prompt + }, Highlighter: cfg.Filter.Highlighter, }, ListBox: tk.ListBoxSpec{ @@ -71,7 +129,11 @@ func NewCompletion(app cli.App, cfg CompletionSpec) (Completion, error) { ExtendStyle: true, }, OnFilter: func(w tk.ComboBox, p string) { - w.ListBox().Reset(filterCompletionItems(cfg.Items, cfg.Filter.makePredicate(p)), 0) + filtered := cfg.Items + if cfg.tagIndex > 0 { + filtered = itemsByTag[cfg.tags[cfg.tagIndex]] + } + w.ListBox().Reset(filterCompletionItems(filtered, cfg.Filter.makePredicate(p)), 0) }, }) return completion{w, codeArea}, nil diff --git a/pkg/edit/complete/raw_item.go b/pkg/edit/complete/raw_item.go index 943ab658d..d3c658ed8 100644 --- a/pkg/edit/complete/raw_item.go +++ b/pkg/edit/complete/raw_item.go @@ -33,6 +33,7 @@ type ComplexItem struct { Stem string // Used in the code and the menu. CodeSuffix string // Appended to the code. Display ui.Text // How the item is displayed. If empty, defaults to ui.T(Stem). + Tag string // TODO } func (c ComplexItem) String() string { return c.Stem } @@ -46,5 +47,6 @@ func (c ComplexItem) Cook(q parse.PrimaryType) modes.CompletionItem { return modes.CompletionItem{ ToInsert: quoted + c.CodeSuffix, ToShow: display, + Tag: c.Tag, } } diff --git a/pkg/edit/completion.go b/pkg/edit/completion.go index e268950db..03268bd1c 100644 --- a/pkg/edit/completion.go +++ b/pkg/edit/completion.go @@ -25,6 +25,7 @@ import ( type complexCandidateOpts struct { CodeSuffix string Display any + Tag string } func (*complexCandidateOpts) SetDefaultOptions() {} @@ -46,6 +47,7 @@ func complexCandidate(fm *eval.Frame, opts complexCandidateOpts, stem string) (c Stem: stem, CodeSuffix: opts.CodeSuffix, Display: display, + Tag: opts.Tag, }, nil } @@ -162,6 +164,8 @@ func (c complexItem) Index(k any) (any, bool) { return c.CodeSuffix, true case "display": return c.Display, true + case "tag": + return c.Tag, true } return nil, false } @@ -188,8 +192,8 @@ func (c complexItem) Hash() uint32 { func (c complexItem) Repr(indent int) string { // TODO(xiaq): Pretty-print when indent >= 0 - return fmt.Sprintf("(edit:complex-candidate %s &code-suffix=%s &display=%s)", - parse.Quote(c.Stem), parse.Quote(c.CodeSuffix), vals.Repr(c.Display, indent+1)) + return fmt.Sprintf("(edit:complex-candidate %s &code-suffix=%s &display=%s &tag=%s)", + parse.Quote(c.Stem), parse.Quote(c.CodeSuffix), vals.Repr(c.Display, indent+1), parse.Quote(c.Tag)) } type wrappedArgGenerator func(*eval.Frame, ...string) error diff --git a/pkg/edit/completion_test.go b/pkg/edit/completion_test.go index 1127ec4ea..db22a0326 100644 --- a/pkg/edit/completion_test.go +++ b/pkg/edit/completion_test.go @@ -96,9 +96,9 @@ func TestComplexCandidate(t *testing.T) { That("cc a/b").Puts(complexItem{Stem: "a/b"}), That("cc a/b &code-suffix=' '").Puts(complexItem{Stem: "a/b", CodeSuffix: " "}), That("cc a/b &code-suffix=' ' &display=A/B").Puts( - complexItem{"a/b", " ", ui.T("A/B")}), + complexItem{"a/b", " ", ui.T("A/B"), ""}), That("cc a/b &code-suffix=' ' &display=(styled A/B red)").Puts( - complexItem{"a/b", " ", ui.T("A/B", ui.FgRed)}), + complexItem{"a/b", " ", ui.T("A/B", ui.FgRed), ""}), That("cc a/b &code-suffix=' ' &display=[]").Throws( errs.BadValue{What: "&display", Valid: "string or styled", Actual: "[]"}), From d925d2230ab0720359dbc228c6005ce541f14fed Mon Sep 17 00:00:00 2001 From: rsteube Date: Tue, 9 Jan 2024 02:06:20 +0100 Subject: [PATCH 2/2] tmp --- pkg/cli/modes/completion.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cli/modes/completion.go b/pkg/cli/modes/completion.go index 50744762e..5c1065908 100644 --- a/pkg/cli/modes/completion.go +++ b/pkg/cli/modes/completion.go @@ -61,7 +61,7 @@ func NewCompletion(app cli.App, cfg CompletionSpec) (Completion, error) { sort.Strings(cfg.tags) switch len(cfg.tags) { case 1: - if strings.TrimSpace(cfg.tags[0]) == "" { + if len(itemsByTag) > 1 || strings.TrimSpace(cfg.tags[0]) == "" { cfg.tags = []string{cfg.Name} } default: