diff --git a/internal/cache/cache.go b/internal/cache/cache.go new file mode 100644 index 0000000000..6ebd10f7b7 --- /dev/null +++ b/internal/cache/cache.go @@ -0,0 +1,38 @@ +package cache + +import "strings" + +type Cache struct { + m map[string]interface{} +} + +func New() *Cache { + return &Cache{ + m: map[string]interface{}{}, + } +} + +func (c *Cache) Set(cmd string, resp interface{}) { + if c == nil { + return + } + c.m[cmd] = resp +} + +func (c *Cache) Get(cmd string) interface{} { + if c == nil { + return nil + } + return c.m[cmd] +} + +func (c *Cache) Update(namespace string) { + if c == nil { + return + } + for k := range c.m { + if strings.HasPrefix(k, namespace) { + delete(c.m, k) + } + } +} diff --git a/internal/core/autocomplete_utils.go b/internal/core/autocomplete_utils.go index d12e0ada2f..50d4f326c3 100644 --- a/internal/core/autocomplete_utils.go +++ b/internal/core/autocomplete_utils.go @@ -2,14 +2,18 @@ package core import ( "context" + "fmt" "reflect" "strings" "github.com/scaleway/scaleway-cli/v2/internal/args" + "github.com/scaleway/scaleway-cli/v2/internal/cache" "github.com/scaleway/scaleway-sdk-go/scw" "github.com/scaleway/scaleway-sdk-go/strcase" ) +var autoCompleteCache *cache.Cache + func AutocompleteProfileName() AutoCompleteArgFunc { return func(ctx context.Context, prefix string) AutocompleteSuggestions { res := AutocompleteSuggestions(nil) @@ -96,9 +100,15 @@ func AutocompleteGetArg(ctx context.Context, cmd *Command, argSpec *ArgSpec, com return runner(ctx, argsI) } } - resp, err := listCmd.Interceptor(ctx, listCmdArgs, listCmd.Run) - if err != nil { - return nil + + rawCommand := fmt.Sprintf("%s %s", listCmd.getPath(), strings.Join(listRawArgs, " ")) + resp := autoCompleteCache.Get(rawCommand) + if resp == nil { + resp, err = listCmd.Interceptor(ctx, listCmdArgs, listCmd.Run) + if err != nil { + return nil + } + autoCompleteCache.Set(rawCommand, resp) } // As we run the "list" verb instead of using the sdk ListResource, response is already the slice diff --git a/internal/core/shell.go b/internal/core/shell.go index fabd135b36..9b6c12a237 100644 --- a/internal/core/shell.go +++ b/internal/core/shell.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/c-bata/go-prompt" + "github.com/scaleway/scaleway-cli/v2/internal/cache" "github.com/scaleway/scaleway-cli/v2/internal/interactive" "github.com/spf13/cobra" ) @@ -182,37 +183,6 @@ func sortOptions(meta *meta, args []string, toSuggest string, suggestions []stri return suggests } -// CompletionCache allows to keep last completion request in cache -// Useful to avoid request spamming when adding a character -type CompletionCache struct { - wordsSum string - arg string - LastResponse *AutocompleteResponse -} - -// completionCacheResetCharacterList is the list of character that will trigger cache reset -var completionCacheResetCharacterList = []string{"=", "."} -var completionCache CompletionCache - -func (cache *CompletionCache) HasChanged(leftWords []string, currentArg string, rightWords []string) bool { - wordsSum := strings.Join(leftWords, "-") + "_" + strings.Join(rightWords, "-") - if cache.wordsSum != wordsSum { - cache.wordsSum = wordsSum - cache.arg = currentArg - return true - } - - for _, character := range completionCacheResetCharacterList { - if strings.Count(cache.arg, character) != strings.Count(currentArg, character) { - cache.arg = currentArg - return true - } - } - - cache.arg = currentArg - return false -} - // Complete returns the list of suggestion based on prompt content func (c *Completer) Complete(d prompt.Document) []prompt.Suggest { // shell lib can request duplicate Complete request with empty strings as text @@ -234,17 +204,9 @@ func (c *Completer) Complete(d prompt.Document) []prompt.Suggest { leftWords := append([]string{"scw"}, leftArgs...) - var acr *AutocompleteResponse - - if completionCache.HasChanged(leftWords, currentArg, rightWords) { - acr = AutoComplete(c.ctx, leftWords, currentArg, rightWords) - completionCache.LastResponse = acr - } else { - acr = completionCache.LastResponse - } + acr := AutoComplete(c.ctx, leftWords, currentArg, rightWords) suggestions := []prompt.Suggest(nil) - rawSuggestions := []string(acr.Suggestions) // if first suggestion is an option, all suggestions should be options @@ -289,6 +251,8 @@ func shellExecutor(rootCmd *cobra.Command, printer *Printer, meta *meta) func(s return } + autoCompleteCache.Update(meta.command.Namespace) + printErr := printer.Print(meta.result, meta.command.getHumanMarshalerOpt()) if printErr != nil { _, _ = fmt.Fprintln(os.Stderr, printErr) @@ -309,6 +273,7 @@ func getShellCommand(rootCmd *cobra.Command) *cobra.Command { // RunShell will run an interactive shell that runs cobra commands func RunShell(ctx context.Context, printer *Printer, meta *meta, rootCmd *cobra.Command, args []string) { + autoCompleteCache = cache.New() completer := NewShellCompleter(ctx) shellCobraCommand := getShellCommand(rootCmd)