diff --git a/autocomplete/generators/ai.go b/autocomplete/generators/ai.go index 1b178da..51c17ee 100644 --- a/autocomplete/generators/ai.go +++ b/autocomplete/generators/ai.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/cpendery/clac/autocomplete/model" + "github.com/google/uuid" ) const ( @@ -24,10 +25,6 @@ const ( maxRunes = maxTokens * tokensToRuneRatio * marginRatio ) -var ( - suggestionCache = make(map[string][]model.TermSuggestion) -) - type PromptFunction func(executeShellCommand func(string) string) string type MessageFunction func(executeShellCommand func(string) string) string @@ -77,10 +74,6 @@ func executeShellCommand(script string) string { } func request(name, prompt, message, splitOn string) []model.TermSuggestion { - cacheKey := name + "|" + prompt + "|" + message - if suggestions, exists := suggestionCache[cacheKey]; exists { - return suggestions - } suggestions := []model.TermSuggestion{} jsonBytes, err := json.Marshal(apiRequest{ Model: "gpt-3.5-turbo", @@ -146,12 +139,12 @@ func request(name, prompt, message, splitOn string) []model.TermSuggestion { Type: model.TermSuggestionTypeAI, }) } - suggestionCache[cacheKey] = suggestions return suggestions } func AI(name string, prompt PromptFunction, message MessageFunction, splitOn string) *model.Generator { return &model.Generator{ + Id: uuid.New(), Function: func() []model.TermSuggestion { suggestions := []model.TermSuggestion{} if !enabled() { diff --git a/autocomplete/generators/generator.go b/autocomplete/generators/generator.go index 795ff6c..e1369a3 100644 --- a/autocomplete/generators/generator.go +++ b/autocomplete/generators/generator.go @@ -6,9 +6,17 @@ import ( "strings" "github.com/cpendery/clac/autocomplete/model" + "github.com/google/uuid" +) + +var ( + generatorCache = make(map[uuid.UUID][]model.TermSuggestion) ) func Run(g model.Generator) []model.TermSuggestion { + if cachedSuggestions, executed := generatorCache[g.Id]; executed { + return cachedSuggestions + } suggestions := []model.TermSuggestion{} if g.Script != "" { args := strings.Split(g.Script, " ") @@ -38,6 +46,7 @@ func Run(g model.Generator) []model.TermSuggestion { } suggestions = append(suggestions, RunTemplates(g.Template)...) + generatorCache[g.Id] = suggestions return suggestions } diff --git a/autocomplete/model/model.go b/autocomplete/model/model.go index 3bcc66b..4eaa6f3 100644 --- a/autocomplete/model/model.go +++ b/autocomplete/model/model.go @@ -1,5 +1,7 @@ package model +import "github.com/google/uuid" + type Subcommand struct { Name []string //single or array string, required Description string @@ -63,6 +65,7 @@ type ProcessedToken struct { } type Generator struct { + Id uuid.UUID Script string Function func() []TermSuggestion PostProcess func(string) []TermSuggestion diff --git a/ui/suggestions/suggestions.go b/ui/suggestions/suggestions.go index 1b77076..bc0e615 100644 --- a/ui/suggestions/suggestions.go +++ b/ui/suggestions/suggestions.go @@ -11,6 +11,7 @@ import ( "github.com/cpendery/clac/autocomplete" "github.com/cpendery/clac/ui/theme" "github.com/cpendery/clac/ui/utils" + "github.com/google/uuid" "github.com/mattn/go-runewidth" "github.com/muesli/reflow/wordwrap" "github.com/muesli/reflow/wrap" @@ -25,6 +26,7 @@ type Model struct { windowWidth int windowHeight int runesToRemove int + suggestionId uuid.UUID } const ( @@ -34,6 +36,13 @@ const ( MaxSuggestions = 5 ) +type SuggestionMessage struct { + Id uuid.UUID + Suggestions []autocomplete.Suggestion + ArgumentDescription string + RunesToRemove int +} + type KeyMap struct { LineUp key.Binding LineDown key.Binding @@ -62,6 +71,18 @@ func New() Model { } } +func SuggestCmd(cmd string, suggestionId uuid.UUID) tea.Cmd { + return func() tea.Msg { + suggestions, argDescription, runesToRemove := autocomplete.LoadSuggestions(cmd) + return SuggestionMessage{ + Suggestions: suggestions, + ArgumentDescription: argDescription, + RunesToRemove: runesToRemove, + Id: suggestionId, + } + } +} + func (m *Model) cursorUp() { m.cursor = utils.Clamp(m.cursor-1, 0, len(m.suggestions)-1) } @@ -82,7 +103,8 @@ func (m *Model) ResetCursor() { m.cursor = 0 } -func (m Model) Update(msg tea.Msg, command string, userInputCursorLocation int) Model { +func (m Model) Update(msg tea.Msg, command string, userInputCursorLocation int) (Model, tea.Cmd) { + var cmds []tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch { @@ -91,6 +113,9 @@ func (m Model) Update(msg tea.Msg, command string, userInputCursorLocation int) case key.Matches(msg, m.keyMap.LineDown): m.cursorDown() } + m.suggestions, m.argDescription, m.runesToRemove = []autocomplete.Suggestion{}, "", 0 + m.suggestionId = uuid.New() + cmds = append(cmds, SuggestCmd(command, m.suggestionId)) case cursor.BlinkMsg: if runtime.GOOS == "windows" { m.windowWidth, m.windowHeight = utils.GetWindowSize() @@ -98,13 +123,16 @@ func (m Model) Update(msg tea.Msg, command string, userInputCursorLocation int) case tea.WindowSizeMsg: m.windowHeight = msg.Height m.windowWidth = msg.Width + case SuggestionMessage: + if m.suggestionId == msg.Id { + m.suggestions, m.argDescription, m.runesToRemove = msg.Suggestions, msg.ArgumentDescription, msg.RunesToRemove + } } m.userInputCursorLocation = userInputCursorLocation - m.suggestions, m.argDescription, m.runesToRemove = autocomplete.LoadSuggestions(command) if m.cursor > len(m.suggestions)-1 { m.cursor = 0 } - return m + return m, tea.Batch(cmds...) } func (m Model) renderSuggestion(suggestion autocomplete.Suggestion, position, cursor, width int) string { diff --git a/ui/ui.go b/ui/ui.go index f534fff..5363b1e 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -55,8 +55,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { s := currentValue[:len(currentValue)-runesToRemove] + activeSuggestion + " " m.textInput.SetValue(s) m.textInput.SetCursor(len(s)) - m.suggestions.ResetCursor() - return m, nil } case cursor.BlinkMsg: if runtime.GOOS == "windows" { @@ -69,13 +67,12 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.textInput.Width = m.windowWidth - widthOffset } - var cmd tea.Cmd - - m.textInput, cmd = m.textInput.Update(msg) + var textInputCmd, suggestionsCmd tea.Cmd + m.textInput, textInputCmd = m.textInput.Update(msg) cursorLocation := len(m.textInput.Value()) + len(m.textInput.Prompt) - m.suggestions = m.suggestions.Update(msg, m.textInput.Value(), cursorLocation) + m.suggestions, suggestionsCmd = m.suggestions.Update(msg, m.textInput.Value(), cursorLocation) - return m, cmd + return m, tea.Batch(textInputCmd, suggestionsCmd) } func (m model) View() string {