From 269b95eab37a5c9820efa4a4d2fc358b8607d15f Mon Sep 17 00:00:00 2001 From: cpendery Date: Tue, 12 Sep 2023 18:48:04 -0400 Subject: [PATCH] refactor: suggestion response & add argument description rendering Signed-off-by: cpendery --- autocomplete/completion.go | 46 ++++++++++++++++++++++------------- autocomplete/model/model.go | 5 ++++ ui/suggestions/suggestions.go | 22 +++++++++++++++-- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/autocomplete/completion.go b/autocomplete/completion.go index 7a1911b..0367063 100644 --- a/autocomplete/completion.go +++ b/autocomplete/completion.go @@ -9,10 +9,11 @@ import ( ) var ( - cmdDelimiter = regexp.MustCompile(`(\|\|)|(&&)|(;)`) - lastSuggestionCmd = "" - lastSuggestion = []Suggestion{} - lastCmdRunes = 0 + cmdDelimiter = regexp.MustCompile(`(\|\|)|(&&)|(;)`) + lastSuggestionCmd = "" + lastSuggestion = []Suggestion{} + lastArgDescription = "" + lastCmdRunes = 0 ) type Suggestion struct { @@ -83,7 +84,7 @@ func getPersistentTokens(tokens []model.ProcessedToken) []model.ProcessedToken { return persistentTokens } -func getSubcommandDrivenRecommendation(spec model.Subcommand, persistentOptions []model.Option, partialCmd *commandToken, onlyRecommendSubcommands bool, acceptedTokens []model.ProcessedToken) []model.TermSuggestion { +func getSubcommandDrivenRecommendation(spec model.Subcommand, persistentOptions []model.Option, partialCmd *commandToken, onlyRecommendSubcommands bool, acceptedTokens []model.ProcessedToken) model.TermSuggestions { suggestions := []model.TermSuggestion{} allOptions := append(spec.Options, persistentOptions...) @@ -109,10 +110,12 @@ func getSubcommandDrivenRecommendation(spec model.Subcommand, persistentOptions } } - return suggestions + return model.TermSuggestions{ + Suggestions: suggestions, + } } -func getArgDrivenRecommendation(args []model.Arg, spec model.Subcommand, persistentOptions []model.Option, partialCmd *commandToken, acceptedTokens []model.ProcessedToken) []model.TermSuggestion { +func getArgDrivenRecommendation(args []model.Arg, spec model.Subcommand, persistentOptions []model.Option, partialCmd *commandToken, acceptedTokens []model.ProcessedToken) model.TermSuggestions { suggestions := []model.TermSuggestion{} activeArg := args[0] allOptions := append(spec.Options, persistentOptions...) @@ -135,10 +138,18 @@ func getArgDrivenRecommendation(args []model.Arg, spec model.Subcommand, persist } } - return suggestions + argDescriptionSuggestion := "" + if len(suggestions) == 0 { + argDescriptionSuggestion = activeArg.Name + } + + return model.TermSuggestions{ + ArgumentDescription: argDescriptionSuggestion, + Suggestions: suggestions, + } } -func handleSubcommand(tokens []commandToken, spec model.Subcommand, persistentOptions []model.Option, argsDepleted bool, acceptedTokens []model.ProcessedToken) (suggestions []model.TermSuggestion) { +func handleSubcommand(tokens []commandToken, spec model.Subcommand, persistentOptions []model.Option, argsDepleted bool, acceptedTokens []model.ProcessedToken) (suggestions model.TermSuggestions) { if len(tokens) == 0 { return getSubcommandDrivenRecommendation(spec, persistentOptions, nil, argsDepleted, acceptedTokens) } else if !tokens[0].complete { @@ -163,7 +174,7 @@ func handleSubcommand(tokens []commandToken, spec model.Subcommand, persistentOp return handleArg(tokens, spec.Args, spec, persistentOptions, acceptedTokens) } -func handleOption(tokens []commandToken, option model.Option, spec model.Subcommand, persistentOptions []model.Option, acceptedTokens []model.ProcessedToken) (suggestions []model.TermSuggestion) { +func handleOption(tokens []commandToken, option model.Option, spec model.Subcommand, persistentOptions []model.Option, acceptedTokens []model.ProcessedToken) (suggestions model.TermSuggestions) { if len(tokens) == 0 { slog.Error("invalid state reached, option with no tokens") return @@ -186,7 +197,7 @@ persistenceDetermined: return handleArg(tokens[1:], option.Args, spec, persistentOptions, acceptedTokens) } -func handleArg(tokens []commandToken, args []model.Arg, spec model.Subcommand, persistentOptions []model.Option, acceptedTokens []model.ProcessedToken) (suggestions []model.TermSuggestion) { +func handleArg(tokens []commandToken, args []model.Arg, spec model.Subcommand, persistentOptions []model.Option, acceptedTokens []model.ProcessedToken) (suggestions model.TermSuggestions) { if len(args) == 0 { return handleSubcommand(tokens, spec, persistentOptions, true, acceptedTokens) } else if len(tokens) == 0 { @@ -226,7 +237,7 @@ func handleArg(tokens []commandToken, args []model.Arg, spec model.Subcommand, p return handleArg(tokens[1:], args[1:], spec, persistentOptions, getPersistentTokens(acceptedTokens)) } -func loadSuggestions(cmd string) (suggestions []model.TermSuggestion, charsInLastCmd int) { +func loadSuggestions(cmd string) (suggestions model.TermSuggestions, charsInLastCmd int) { activeCmd := ParseCommand(cmd) if len(activeCmd) <= 0 { return @@ -246,15 +257,16 @@ func loadSuggestions(cmd string) (suggestions []model.TermSuggestion, charsInLas return } -func LoadSuggestions(cmd string) ([]Suggestion, int) { +func LoadSuggestions(cmd string) ([]Suggestion, string, int) { if cmd == lastSuggestionCmd { - return lastSuggestion, lastCmdRunes + return lastSuggestion, lastArgDescription, lastCmdRunes } termSuggestions, lastRunes := loadSuggestions(cmd) + suggestions := []Suggestion{} - for _, suggestion := range termSuggestions { + for _, suggestion := range termSuggestions.Suggestions { suggestions = append(suggestions, Suggestion{Name: suggestion.Name, Description: suggestion.Description}) } - lastSuggestionCmd, lastSuggestion, lastCmdRunes = cmd, suggestions, lastRunes - return suggestions, lastRunes + lastSuggestionCmd, lastSuggestion, lastCmdRunes, lastArgDescription = cmd, suggestions, lastRunes, termSuggestions.ArgumentDescription + return suggestions, termSuggestions.ArgumentDescription, lastRunes } diff --git a/autocomplete/model/model.go b/autocomplete/model/model.go index db4e7c7..f87a109 100644 --- a/autocomplete/model/model.go +++ b/autocomplete/model/model.go @@ -50,6 +50,11 @@ type Suggestion struct { Description string } +type TermSuggestions struct { + ArgumentDescription string + Suggestions []TermSuggestion +} + type TermSuggestion struct { Name string Description string diff --git a/ui/suggestions/suggestions.go b/ui/suggestions/suggestions.go index e9a6be7..a22c91b 100644 --- a/ui/suggestions/suggestions.go +++ b/ui/suggestions/suggestions.go @@ -19,6 +19,7 @@ type Model struct { cursor int userInputCursorLocation int suggestions []autocomplete.Suggestion + argDescription string keyMap KeyMap windowWidth int windowHeight int @@ -98,7 +99,7 @@ func (m Model) Update(msg tea.Msg, command string, userInputCursorLocation int) m.windowWidth = msg.Width } m.userInputCursorLocation = userInputCursorLocation - m.suggestions, m.runesToRemove = autocomplete.LoadSuggestions(command) + m.suggestions, m.argDescription, m.runesToRemove = autocomplete.LoadSuggestions(command) if len(m.suggestions) == 0 { m.cursor = 0 } @@ -157,14 +158,31 @@ func (m Model) renderDescription(width int) string { return style.Render(wordWrap(m.suggestions[m.cursor].Description, width)) } +func (m Model) renderArgumentDescription(width int) string { + argDescriptionWidth := utils.Clamp(len(m.argDescription), 0, width) + maxLeftPaddingDescription := m.windowWidth - argDescriptionWidth - BorderOffset + var style = lipgloss. + NewStyle(). + MarginLeft(utils.Clamp(m.userInputCursorLocation, 0, maxLeftPaddingDescription)). + Width(argDescriptionWidth). + BorderStyle(lipgloss.NormalBorder()). + BorderForeground(lipgloss.Color("63")) + + return style.Render(wordWrap(m.argDescription, argDescriptionWidth)) +} + func (m Model) View() string { - if len(m.suggestions) == 0 { + if len(m.suggestions) == 0 && m.argDescription == "" { return "" } suggestionWidth := min(m.windowWidth-BorderOffset, SuggestionWidth) descriptionWidth := min(m.windowWidth-BorderOffset, DescriptionWidth) + if len(m.suggestions) == 0 && m.argDescription != "" { + return m.renderArgumentDescription(descriptionWidth) + } + suggestionsView := m.renderSuggestions(suggestionWidth) descriptionView := m.renderDescription(descriptionWidth)