Skip to content

Commit

Permalink
Merge pull request #1040 from carapace-sh/action-uid
Browse files Browse the repository at this point in the history
action: added uid
  • Loading branch information
rsteube authored Sep 15, 2024
2 parents 68d3237 + caf5f00 commit 6c11096
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 45 deletions.
37 changes: 37 additions & 0 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package carapace

import (
"fmt"
"net/url"
"os"
"regexp"
"runtime"
Expand Down Expand Up @@ -462,6 +463,42 @@ func (a Action) Unless(condition func(c Context) bool) Action {
})
}

// Uid TODO experimental
func (a Action) Uid(scheme, host string, opts ...string) Action {
return ActionCallback(func(c Context) Action {
if length := len(opts); length%2 != 0 {
return ActionMessage("invalid amount of arguments [Uid]: %v", length)
}

invoked := a.Invoke(c)
for index, v := range invoked.action.rawValues {
uid := url.URL{
Scheme: scheme,
Host: host,
Path: v.Value,
}
if len(opts) > 0 {
values := uid.Query()
for i := 0; i < len(opts); i += 2 {
if opts[i+1] != "" { // implicitly skip empty values
values.Set(opts[i], opts[i+1])
}
}
uid.RawQuery = values.Encode()
}
invoked.action.rawValues[index].Uid = uid.String()
}
return invoked.ToA()
})
}

// UidF TODO experimental
func (a Action) UidF(f func(s string) (*url.URL, error)) Action {
return ActionCallback(func(c Context) Action {
return a.Invoke(c).UidF(f).ToA()
})
}

// Usage sets the usage.
func (a Action) Usage(usage string, args ...interface{}) Action {
return a.UsageF(func() string {
Expand Down
51 changes: 44 additions & 7 deletions action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/carapace-sh/carapace/internal/assert"
"github.com/carapace-sh/carapace/internal/common"
"github.com/carapace-sh/carapace/internal/uid"
"github.com/carapace-sh/carapace/pkg/style"
)

Expand Down Expand Up @@ -122,7 +123,14 @@ func TestActionDirectories(t *testing.T) {
"internal/", style.Of(style.Blue, style.Bold),
"pkg/", style.Of(style.Blue, style.Bold),
"third_party/", style.Of(style.Blue, style.Bold),
).NoSpace('/').Tag("directories").Invoke(Context{}),
).NoSpace('/').Tag("directories").Invoke(Context{}).UidF(uid.Map(
"example/", "file://"+wd("")+"/example/",
"example-nonposix/", "file://"+wd("")+"/example-nonposix/",
"docs/", "file://"+wd("")+"/docs/",
"internal/", "file://"+wd("")+"/internal/",
"pkg/", "file://"+wd("")+"/pkg/",
"third_party/", "file://"+wd("")+"/third_party/",
)),
ActionDirectories().Invoke(Context{Value: ""}).Filter("vendor/"),
)

Expand All @@ -134,22 +142,34 @@ func TestActionDirectories(t *testing.T) {
"internal/", style.Of(style.Blue, style.Bold),
"pkg/", style.Of(style.Blue, style.Bold),
"third_party/", style.Of(style.Blue, style.Bold),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("./"),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("./").UidF(uid.Map(
"./example/", "file://"+wd("")+"/example/",
"./example-nonposix/", "file://"+wd("")+"/example-nonposix/",
"./docs/", "file://"+wd("")+"/docs/",
"./internal/", "file://"+wd("")+"/internal/",
"./pkg/", "file://"+wd("")+"/pkg/",
"./third_party/", "file://"+wd("")+"/third_party/",
)),
ActionDirectories().Invoke(Context{Value: "./"}).Filter("./vendor/"),
)

assertEqual(t,
ActionStyledValues(
"_test/", style.Of(style.Blue, style.Bold),
"cmd/", style.Of(style.Blue, style.Bold),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("example/"),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("example/").UidF(uid.Map(
"example/_test/", "file://"+wd("")+"/example/_test/",
"example/cmd/", "file://"+wd("")+"/example/cmd/",
)),
ActionDirectories().Invoke(Context{Value: "example/"}),
)

assertEqual(t,
ActionStyledValues(
"cmd/", style.Of(style.Blue, style.Bold),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("example/"),
).NoSpace('/').Tag("directories").Invoke(Context{}).Prefix("example/").UidF(uid.Map(
"example/cmd/", "file://"+wd("")+"/example/cmd/",
)),
ActionDirectories().Invoke(Context{Value: "example/cm"}),
)
}
Expand All @@ -164,7 +184,15 @@ func TestActionFiles(t *testing.T) {
"internal/", style.Of(style.Blue, style.Bold),
"pkg/", style.Of(style.Blue, style.Bold),
"third_party/", style.Of(style.Blue, style.Bold),
).NoSpace('/').Tag("files").Invoke(Context{}),
).NoSpace('/').Tag("files").Invoke(Context{}).UidF(uid.Map(
"README.md", "file://"+wd("")+"/README.md",
"example/", "file://"+wd("")+"/example/",
"example-nonposix/", "file://"+wd("")+"/example-nonposix/",
"docs/", "file://"+wd("")+"/docs/",
"internal/", "file://"+wd("")+"/internal/",
"pkg/", "file://"+wd("")+"/pkg/",
"third_party/", "file://"+wd("")+"/third_party/",
)),
ActionFiles(".md").Invoke(Context{Value: ""}).Filter("vendor/"),
)

Expand All @@ -175,7 +203,13 @@ func TestActionFiles(t *testing.T) {
"cmd/", style.Of(style.Blue, style.Bold),
"main.go", style.Default,
"main_test.go", style.Default,
).NoSpace('/').Tag("files").Invoke(Context{}).Prefix("example/"),
).NoSpace('/').Tag("files").Invoke(Context{}).Prefix("example/").UidF(uid.Map(
"example/README.md", "file://"+wd("example")+"/README.md",
"example/_test/", "file://"+wd("example")+"/_test/",
"example/cmd/", "file://"+wd("example")+"/cmd/",
"example/main.go", "file://"+wd("example")+"/main.go",
"example/main_test.go", "file://"+wd("example")+"/main_test.go",
)),
ActionFiles().Invoke(Context{Value: "example/"}).Filter("example/example"),
)
}
Expand All @@ -197,7 +231,10 @@ func TestActionFilesChdir(t *testing.T) {
ActionStyledValues(
"action.go", style.Default,
"snippet.go", style.Default,
).NoSpace('/').Tag("files").Invoke(Context{}).Prefix("elvish/"),
).NoSpace('/').Tag("files").Invoke(Context{}).Prefix("elvish/").UidF(uid.Map(
"elvish/action.go", "file://"+wd("internal/shell")+"/elvish/action.go",
"elvish/snippet.go", "file://"+wd("internal/shell")+"/elvish/snippet.go",
)),
ActionFiles().Chdir("internal/shell").Invoke(Context{Value: "elvish/"}),
)

Expand Down
58 changes: 47 additions & 11 deletions defaultActions.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
"os/exec"
"strings"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/carapace-sh/carapace/internal/env"
"github.com/carapace-sh/carapace/internal/export"
"github.com/carapace-sh/carapace/internal/man"
"github.com/carapace-sh/carapace/internal/uid"
"github.com/carapace-sh/carapace/pkg/match"
"github.com/carapace-sh/carapace/pkg/style"
"github.com/carapace-sh/carapace/third_party/github.com/acarl005/stripansi"
Expand Down Expand Up @@ -132,18 +134,34 @@ func ActionExecute(cmd *cobra.Command) Action {

// ActionDirectories completes directories.
func ActionDirectories() Action {
return actionPath([]string{""}, true).
MultiParts("/").
StyleF(style.ForPath).
Tag("directories")
return ActionCallback(func(c Context) Action {
return actionPath([]string{""}, true).
MultiParts("/").
StyleF(style.ForPath).
UidF(func(s string) (*url.URL, error) { // TODO duplicated from ActionFiles
abs, err := c.Abs(s)
if err != nil {
return nil, err
}
return url.Parse("file://" + abs)
})
}).Tag("directories")
}

// ActionFiles completes files with optional suffix filtering.
func ActionFiles(suffix ...string) Action {
return actionPath(suffix, false).
MultiParts("/").
StyleF(style.ForPath).
Tag("files")
return ActionCallback(func(c Context) Action {
return actionPath(suffix, false).
MultiParts("/").
StyleF(style.ForPath).
UidF(func(s string) (*url.URL, error) {
abs, err := c.Abs(s)
if err != nil {
return nil, err
}
return url.Parse("file://" + abs)
})
}).Tag("files")
}

// ActionValues completes arbitrary keywords (values).
Expand Down Expand Up @@ -438,7 +456,10 @@ func ActionExecutables(dirs ...string) Action {
for i := len(dirs) - 1; i >= 0; i-- {
batch = append(batch, actionDirectoryExecutables(dirs[i], c.Value, manDescriptions))
}
return batch.ToA()
return batch.ToA().
UidF(func(s string) (*url.URL, error) {
return &url.URL{Scheme: "cmd", Host: s}, nil
})
}).Tag("executables")
}

Expand All @@ -458,7 +479,9 @@ func actionDirectoryExecutables(dir string, prefix string, manDescriptions map[s
}
}
}
return ActionStyledValuesDescribed(vals...)
return ActionStyledValuesDescribed(vals...).UidF(func(s string) (*url.URL, error) {
return url.Parse(fmt.Sprintf("file://%v/%v", dir, s)) // TODO trim slash suffix from dir | backslash path possible? (windows)
})
}
return ActionValues()
})
Expand Down Expand Up @@ -524,7 +547,20 @@ func ActionCommands(cmd *cobra.Command) Action {
}
}
}
return batch.ToA()
return batch.ToA().UidF(func(s string) (*url.URL, error) {
uid := uid.Command(cmd)
if subCommand, _, err := cmd.Find([]string{s}); err == nil {
s = subCommand.Name() // alias -> actual name
}

switch uid.Path {
case "":
uid.Path = s
default:
uid.Path = uid.Path + "/" + s
}
return uid, nil
})
})
}

Expand Down
19 changes: 17 additions & 2 deletions defaultActions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"strings"
"testing"

"github.com/carapace-sh/carapace/internal/uid"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -33,13 +34,27 @@ func TestActionImport(t *testing.T) {
}

func TestActionFlags(t *testing.T) {
cmd := &cobra.Command{}
cmd := &cobra.Command{Use: "actionFlags"}
cmd.Flags().BoolP("alpha", "a", false, "")
cmd.Flags().BoolP("beta", "b", false, "")

cmd.Flag("alpha").Changed = true
a := actionFlags(cmd).Invoke(Context{Value: "-a"})
assertEqual(t, ActionValuesDescribed("b", "", "h", "help for this command").Tag("shorthand flags").NoSpace('b', 'h').Invoke(Context{}).Prefix("-a"), a)
assertEqual(
t,
ActionValuesDescribed(
"b", "",
"h", "help for actionFlags",
).Tag("shorthand flags").
NoSpace('b', 'h').
Invoke(Context{}).
Prefix("-a").
UidF(uid.Map(
"-ab", "cmd://actionFlags?flag=beta",
"-ah", "cmd://actionFlags?flag=help",
)),
a,
)
}

func TestActionExecCommandEnv(t *testing.T) {
Expand Down
1 change: 1 addition & 0 deletions internal/common/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type RawValue struct {
Description string `json:"description,omitempty"`
Style string `json:"style,omitempty"`
Tag string `json:"tag,omitempty"`
Uid string `json:"uid,omitempty"`
}

// TrimmedDescription returns the trimmed description.
Expand Down
5 changes: 5 additions & 0 deletions internal/env/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

const (
CARAPACE_COVERDIR = "CARAPACE_COVERDIR" // coverage directory for sandbox tests
CARAPACE_EXPERIMENTAL = "CARAPACE_EXPERIMENTAL" // enable experimental features
CARAPACE_HIDDEN = "CARAPACE_HIDDEN" // show hidden commands/flags
CARAPACE_LENIENT = "CARAPACE_LENIENT" // allow unknown flags
CARAPACE_LOG = "CARAPACE_LOG" // enable logging
Expand All @@ -25,6 +26,10 @@ func ColorDisabled() bool {
return os.Getenv(NO_COLOR) != "" || os.Getenv(CLICOLOR) == "0"
}

func Experimental() bool {
return os.Getenv(CARAPACE_EXPERIMENTAL) != ""
}

func Lenient() bool {
return os.Getenv(CARAPACE_LENIENT) != ""
}
Expand Down
4 changes: 2 additions & 2 deletions internal/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package log

import (
"fmt"
"io/ioutil"
"io"
"log"
"os"

Expand All @@ -11,7 +11,7 @@ import (
"github.com/carapace-sh/carapace/pkg/ps"
)

var LOG = log.New(ioutil.Discard, "", log.Flags())
var LOG = log.New(io.Discard, "", log.Flags())

func init() {
if !env.Log() {
Expand Down
9 changes: 9 additions & 0 deletions internal/shell/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package shell

import (
"fmt"
"os/exec"
"sort"
"strings"

Expand Down Expand Up @@ -90,6 +91,14 @@ func Value(shell string, value string, meta common.Meta, values common.RawValues
}

sort.Sort(common.ByDisplay(filtered))
if env.Experimental() {
if _, err := exec.LookPath("tabdance"); err == nil {
return f(value, meta, filtered)
}
}
for index := range filtered {
filtered[index].Uid = ""
}
return f(value, meta, filtered)
}
return ""
Expand Down
Loading

0 comments on commit 6c11096

Please sign in to comment.