Skip to content

Commit

Permalink
shorthands: Support cooperation b/w custom and built-in (#335)
Browse files Browse the repository at this point in the history
Instead of stopping expansion at the first match,
continue to expand the first argument of the result.
This allows user-defined shorthands to refer to built-in shorthands.
  • Loading branch information
abhinav authored Aug 9, 2024
1 parent d4a4c74 commit 74bec79
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
8 changes: 8 additions & 0 deletions doc/src/cli/shorthand.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,11 @@ For example:
{gray}# Replace the "branch restack" shorthand{reset}
{green}${reset} git config --global spice.shorthand.br "branch rename"
```

If the result of a user-defined shorthand refers to a built-in shorthand,
both will be expanded.

```freeze language="terminal"
{green}${reset} git config --global spice.shorthand.bb bco
{gray}# bb will expand to bco, which will expand to "branch checkout"{reset}
```
25 changes: 23 additions & 2 deletions internal/cli/shorthand/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,34 @@ type Source interface {
}

// Sources is a list of shorthand sources composed together.
// These are tried in order until one works.
// These are tried in order repeatedly until there's nothing left to expand.
type Sources []Source

var _ Source = Sources(nil)

// ExpandShorthand expands the given shorthand command.
func (ss Sources) ExpandShorthand(cmd string) ([]string, bool) {
func (ss Sources) ExpandShorthand(orig string) ([]string, bool) {
seen := make(map[string]struct{}) // to prevent infinite loops
result := []string{orig}
for len(result) > 0 {
cmd := result[0]
if _, done := seen[cmd]; done {
break
}
seen[cmd] = struct{}{}

next, ok := ss.expandOnce(cmd)
if !ok {
break
}

result = slices.Replace(result, 0, 1, next...)
}

return result, len(result) > 0 && result[0] != orig
}

func (ss Sources) expandOnce(cmd string) ([]string, bool) {
for _, s := range ss {
if args, ok := s.ExpandShorthand(cmd); ok {
return args, true
Expand Down
47 changes: 47 additions & 0 deletions internal/cli/shorthand/expand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,53 @@ func TestExpand(t *testing.T) {
args: []string{"foo"},
want: []string{"qux", "baz"},
},
{
name: "Sources/Cooperative",
src: shorthand.Sources{
shorthandMap{"can": {"ca", "--no-edit"}},
shorthandMap{"ca": {"c", "--amend"}},
shorthandMap{"c": {"commit"}},
},
args: []string{"can", "--all"},
want: []string{"commit", "--amend", "--no-edit", "--all"},
},
{
name: "Sources/CooperativeReverse",
src: shorthand.Sources{
shorthandMap{"c": {"commit"}},
shorthandMap{"ca": {"c", "--amend"}},
shorthandMap{"can": {"ca", "--no-edit"}},
},
args: []string{"can", "--all"},
want: []string{"commit", "--amend", "--no-edit", "--all"},
},
{
name: "Sources/Delete",
src: shorthand.Sources{
shorthandMap{"foo": {"bar", "baz"}},
shorthandMap{"bar": {}},
},
args: []string{"foo"},
want: []string{"baz"},
},
{
name: "Sources/NoMatch",
src: shorthand.Sources{
shorthandMap{"foo": {"bar", "baz"}},
shorthandMap{"bar": {"qux"}},
},
args: []string{"qux"},
want: []string{"qux"},
},
{
name: "Sources/InfiniteLoop",
src: shorthand.Sources{
shorthandMap{"foo": {"bar"}},
shorthandMap{"bar": {"foo"}},
},
args: []string{"foo"},
want: []string{"foo"},
},
}

for _, tt := range tests {
Expand Down

0 comments on commit 74bec79

Please sign in to comment.