Skip to content

Commit

Permalink
Merge pull request #273 from rsteube/implicit-nospace
Browse files Browse the repository at this point in the history
implicit nospace
  • Loading branch information
rsteube authored Apr 7, 2021
2 parents 50198ce + 2de2ef1 commit e88524b
Show file tree
Hide file tree
Showing 14 changed files with 91 additions and 66 deletions.
77 changes: 29 additions & 48 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,9 @@ import (

// Action indicates how to complete a flag or positional argument
type Action struct {
rawValues []common.RawValue
bash func(callbackValue string) string
elvish func(callbackValue string) string
fish func(callbackValue string) string
ion func(callbackValue string) string
nushell func(callbackValue string) string
oil func(callbackValue string) string
powershell func(callbackValue string) string
xonsh func(callbackValue string) string
zsh func(callbackValue string) string
callback CompletionCallback
rawValues []common.RawValue
callback CompletionCallback
nospace bool
}

type ActionMap map[string]Action
Expand Down Expand Up @@ -122,7 +114,12 @@ func (a InvokedAction) Merge(others ...InvokedAction) InvokedAction {
for _, c := range uniqueRawValues {
rawValues = append(rawValues, c)
}
return InvokedAction(actionRawValues(rawValues...))
return InvokedAction(actionRawValues(rawValues...).noSpace(a.nospace))
}

func (a Action) noSpace(state bool) Action {
a.nospace = a.nospace || state
return a
}

func (a InvokedAction) Filter(values []string) InvokedAction {
Expand All @@ -136,7 +133,7 @@ func (a InvokedAction) Filter(values []string) InvokedAction {
filtered = append(filtered, rawValue)
}
}
return InvokedAction(actionRawValues(filtered...))
return InvokedAction(actionRawValues(filtered...).noSpace(a.nospace))
}

func (a InvokedAction) Prefix(prefix string) InvokedAction {
Expand Down Expand Up @@ -177,45 +174,40 @@ func (a InvokedAction) ToMultiPartsA(divider string) Action {
vals = append(vals, val, description)
}

return ActionValuesDescribed(vals...)
return ActionValuesDescribed(vals...).noSpace(true)
})
}

func (a Action) nestedAction(c Context, maxDepth int) Action {
if a.rawValues == nil && a.callback != nil && maxDepth > 0 {
return a.callback(c).nestedAction(c, maxDepth-1)
return a.callback(c).nestedAction(c, maxDepth-1).noSpace(a.nospace)
} else {
return a
}
}

func (a InvokedAction) value(shell string, callbackValue string) string { // TODO use context instead?
var f func(callbackValue string) string
switch shell {
case "bash":
f = a.bash
return bash.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "fish":
f = a.fish
return fish.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "elvish":
f = a.elvish
return elvish.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "ion":
f = a.ion
return ion.ActionRawValues(callbackValue, a.nospace, a.rawValues)
case "nushell":
f = a.nushell
return nushell.ActionRawValues(callbackValue, a.nospace, a.rawValues)
case "oil":
f = a.oil
return oil.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "powershell":
f = a.powershell
return powershell.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "xonsh":
f = a.xonsh
return xonsh.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
case "zsh":
f = a.zsh
}

if f == nil {
return zsh.ActionRawValues(callbackValue, a.nospace, a.rawValues...)
default:
return ""
} else {
return f(callbackValue)
}
}

Expand All @@ -232,14 +224,14 @@ func ActionBool() Action {
// ActionDirectories completes directories
func ActionDirectories() Action {
return ActionCallback(func(c Context) Action {
return actionPath([]string{""}, true).Invoke(c).ToMultiPartsA("/")
return actionPath([]string{""}, true).Invoke(c).ToMultiPartsA("/").noSpace(true)
})
}

// ActionFiles completes files with optional suffix filtering
func ActionFiles(suffix ...string) Action {
return ActionCallback(func(c Context) Action {
return actionPath(suffix, false).Invoke(c).ToMultiPartsA("/")
return actionPath(suffix, false).Invoke(c).ToMultiPartsA("/").noSpace(true)
})
}

Expand Down Expand Up @@ -324,16 +316,7 @@ func ActionValuesDescribed(values ...string) Action {

func actionRawValues(rawValues ...common.RawValue) Action {
return Action{
rawValues: rawValues,
bash: func(callbackValue string) string { return bash.ActionRawValues(callbackValue, rawValues...) },
elvish: func(callbackValue string) string { return elvish.ActionRawValues(callbackValue, rawValues...) },
fish: func(callbackValue string) string { return fish.ActionRawValues(callbackValue, rawValues...) },
ion: func(callbackValue string) string { return ion.ActionRawValues(callbackValue, rawValues) },
nushell: func(callbackValue string) string { return nushell.ActionRawValues(callbackValue, rawValues) },
oil: func(callbackValue string) string { return oil.ActionRawValues(callbackValue, rawValues...) },
powershell: func(callbackValue string) string { return powershell.ActionRawValues(callbackValue, rawValues...) },
xonsh: func(callbackValue string) string { return xonsh.ActionRawValues(callbackValue, rawValues...) },
zsh: func(callbackValue string) string { return zsh.ActionRawValues(callbackValue, rawValues...) },
rawValues: rawValues,
}
}

Expand All @@ -343,10 +326,8 @@ var skipCache bool
// ActionMessage displays a help messages in places where no completions can be generated
func ActionMessage(msg string) Action {
return ActionCallback(func(c Context) Action {
return ActionCallback(func(c Context) Action {
skipCache = true // TODO find a better solution - any call to ActionMessage i assumed to be an error for now
return ActionValuesDescribed("_", "", "ERR", msg).Invoke(c).Prefix(c.CallbackValue).ToA() // needs to be prefixed with current callback value to not be filtered out
})
skipCache = true // TODO find a better solution - any call to ActionMessage i assumed to be an error for now
return ActionValuesDescribed("_", "", "ERR", msg).Invoke(c).Prefix(c.CallbackValue).ToA().noSpace(true) // needs to be prefixed with current callback value to not be filtered out
})
}

Expand All @@ -367,7 +348,7 @@ func ActionMultiParts(divider string, callback func(c Context) Action) Action {
}
c.Parts = parts

return callback(c).Invoke(c).Prefix(prefix).ToA()
return callback(c).Invoke(c).Prefix(prefix).ToA().noSpace(true)
})
}

Expand Down Expand Up @@ -418,7 +399,7 @@ func actionFlags(cmd *cobra.Command) Action {
if isShorthandSeries {
matches := re.FindStringSubmatch(c.CallbackValue)
parts := strings.Split(matches[1], "")
return ActionValuesDescribed(vals...).Invoke(c).Filter(parts).Prefix(c.CallbackValue).ToA()
return ActionValuesDescribed(vals...).Invoke(c).Filter(parts).Prefix(c.CallbackValue).ToA().noSpace(true)
} else {
return ActionValuesDescribed(vals...)
}
Expand Down
2 changes: 1 addition & 1 deletion example/cmd/_test/elvish.elv
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
edit:completion:arg-completer[example] = [@arg]{
example _carapace elvish _ (all $arg) | from-json | all (one) | each [c]{ edit:complex-candidate $c[Value] &display=$c[Display] }
example _carapace elvish _ (all $arg) | from-json | all (one) | each [c]{ edit:complex-candidate $c[Value] &display=$c[Display] &code-suffix=$c[CodeSuffix] }
}

8 changes: 7 additions & 1 deletion example/cmd/_test/zsh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,13 @@ function _example_completion {
local vals=(${c%%$'\t'*})
# shellcheck disable=SC2034,2206
local descriptions=(${c##*$'\t'})
compadd -S '' -d descriptions -a -- vals

local suffix=' '
[[ ${vals[1]} == *$'\001' ]] && suffix=''
# shellcheck disable=SC2034,2206
vals=(${vals%%$'\001'*})

compadd -S "${suffix}" -d descriptions -a -- vals
}
compquote '' 2>/dev/null && _example_completion
compdef _example_completion example
Expand Down
10 changes: 8 additions & 2 deletions internal/bash/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ func commonValuePrefix(values ...common.RawValue) (prefix string) {
return
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
const nospaceIndicator = "\001"

func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
filtered := make([]common.RawValue, 0)

lastSegment := callbackValue // last segment of callbackValue split by COMP_WORDBREAKS
Expand All @@ -99,7 +101,7 @@ func ActionRawValues(callbackValue string, values ...common.RawValue) string {
// When all display values have the same prefix bash will insert is as partial completion (which skips prefixes/formatting).
if valuePrefix := commonValuePrefix(filtered...); lastSegment != valuePrefix {
// replace values with common value prefix (`\001` is removed in snippet and compopt nospace will be set)
filtered = common.RawValuesFrom(commonValuePrefix(filtered...) + "\001")
filtered = common.RawValuesFrom(commonValuePrefix(filtered...) + nospaceIndicator)
} else {
// prevent insertion of partial display values by prefixing one with space
filtered[0].Display = " " + filtered[0].Display
Expand All @@ -108,6 +110,10 @@ func ActionRawValues(callbackValue string, values ...common.RawValue) string {

vals := make([]string, len(filtered))
for index, val := range filtered {
if nospace && !strings.HasSuffix(val.Value, nospaceIndicator) {
val.Value = val.Value + nospaceIndicator
}

if len(filtered) == 1 {
vals[index] = quoter.Replace(sanitizer.Replace(val.Value))
} else {
Expand Down
16 changes: 11 additions & 5 deletions internal/elvish/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,24 @@ func sanitize(values []common.RawValue) []common.RawValue {
}

type complexCandidate struct {
Value string
Display string
Value string
Display string
CodeSuffix string
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
suffix := " "
if nospace {
suffix = ""
}

vals := make([]complexCandidate, len(values))
for index, val := range sanitize(values) {
// TODO have a look at this again later: seems elvish does a good job quoting any problematic characterS so the sanitize step was removed
if val.Description == "" {
vals[index] = complexCandidate{Value: val.Value, Display: val.Display}
vals[index] = complexCandidate{Value: val.Value, Display: val.Display, CodeSuffix: suffix}
} else {
vals[index] = complexCandidate{Value: val.Value, Display: fmt.Sprintf(`%v (%v)`, val.Display, val.Description)}
vals[index] = complexCandidate{Value: val.Value, Display: fmt.Sprintf(`%v (%v)`, val.Display, val.Description), CodeSuffix: suffix}
}
}
m, _ := json.Marshal(vals)
Expand Down
2 changes: 1 addition & 1 deletion internal/elvish/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

func Snippet(cmd *cobra.Command) string {
return fmt.Sprintf(`edit:completion:arg-completer[%v] = [@arg]{
%v _carapace elvish _ (all $arg) | from-json | all (one) | each [c]{ edit:complex-candidate $c[Value] &display=$c[Display] }
%v _carapace elvish _ (all $arg) | from-json | all (one) | each [c]{ edit:complex-candidate $c[Value] &display=$c[Display] &code-suffix=$c[CodeSuffix] }
}
`, cmd.Name(), uid.Executable())
}
2 changes: 1 addition & 1 deletion internal/fish/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var sanitizer = strings.NewReplacer(
"\t", ``,
)

func ActionRawValues(callbackValues string, values ...common.RawValue) string {
func ActionRawValues(callbackValues string, nospace bool, values ...common.RawValue) string {
vals := make([]string, len(values))
for index, val := range values {
vals[index] = fmt.Sprintf("%v\t%v", sanitizer.Replace(val.Value), sanitizer.Replace(val.Description))
Expand Down
6 changes: 5 additions & 1 deletion internal/ion/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ type suggestion struct {
Display string
}

func ActionRawValues(callbackValue string, values common.RawValues) string {
func ActionRawValues(callbackValue string, nospace bool, values common.RawValues) string {
filtered := values.FilterPrefix(callbackValue)
sort.Sort(common.ByDisplay(filtered))

vals := make([]suggestion, len(filtered))
for index, val := range sanitize(filtered) {
if !nospace {
val.Value = val.Value + " "
}

if val.Description == "" {
vals[index] = suggestion{Value: val.Value, Display: val.Display}
} else {
Expand Down
6 changes: 5 additions & 1 deletion internal/nushell/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@ type suggestion struct {
Display string
}

func ActionRawValues(callbackValue string, values common.RawValues) string {
func ActionRawValues(callbackValue string, nospace bool, values common.RawValues) string {
filtered := values.FilterPrefix(callbackValue)
sort.Sort(common.ByDisplay(filtered))

vals := make([]suggestion, len(filtered))
for index, val := range sanitize(filtered) {
if !nospace {
val.Value = val.Value + " "
}

if val.Description == "" {
vals[index] = suggestion{Value: val.Value, Display: val.Display}
} else {
Expand Down
2 changes: 1 addition & 1 deletion internal/oil/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func Sanitize(values ...string) []string {
return sanitized
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
filtered := make([]common.RawValue, 0)

for _, r := range values {
Expand Down
6 changes: 5 additions & 1 deletion internal/powershell/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func ensureNotEmpty(s string) string {
return s
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
filtered := common.ByValues(values).Filter(callbackValue)
sort.Sort(common.ByDisplay(filtered))

Expand All @@ -50,6 +50,10 @@ func ActionRawValues(callbackValue string, values ...common.RawValue) string {
val.Value = fmt.Sprintf("'%v'", val.Value)
}

if !nospace {
val.Value = val.Value + " "
}

vals = append(vals, completionResult{
CompletionText: val.Value,
ListItemText: ensureNotEmpty(sanitizer.Replace(val.Display)),
Expand Down
7 changes: 6 additions & 1 deletion internal/xonsh/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type richCompletion struct {
Description string
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
filtered := make([]common.RawValue, 0)

for _, r := range values {
Expand All @@ -48,6 +48,11 @@ func ActionRawValues(callbackValue string, values ...common.RawValue) string {
val.Value = fmt.Sprintf("'%v'", val.Value)
}
}

if !nospace {
val.Value = val.Value + " "
}

vals[index] = richCompletion{Value: val.Value, Display: val.Display, Description: val.Description}
}
m, _ := json.Marshal(vals)
Expand Down
5 changes: 4 additions & 1 deletion internal/zsh/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func Sanitize(values ...string) []string {
return sanitized
}

func ActionRawValues(callbackValue string, values ...common.RawValue) string {
func ActionRawValues(callbackValue string, nospace bool, values ...common.RawValue) string {
filtered := make([]common.RawValue, 0)

for _, r := range values {
Expand All @@ -34,6 +34,9 @@ func ActionRawValues(callbackValue string, values ...common.RawValue) string {
vals := make([]string, len(filtered))
for index, val := range filtered {
val.Value = sanitizer.Replace(val.Value)
if nospace {
val.Value = val.Value + "\001"
}
val.Display = sanitizer.Replace(val.Display)
val.Description = sanitizer.Replace(val.Description)

Expand Down
8 changes: 7 additions & 1 deletion internal/zsh/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ function _%v_completion {
local vals=(${c%%%%$'\t'*})
# shellcheck disable=SC2034,2206
local descriptions=(${c##*$'\t'})
compadd -S '' -d descriptions -a -- vals
local suffix=' '
[[ ${vals[1]} == *$'\001' ]] && suffix=''
# shellcheck disable=SC2034,2206
vals=(${vals%%%%$'\001'*})
compadd -S "${suffix}" -d descriptions -a -- vals
}
compquote '' 2>/dev/null && _%v_completion
compdef _%v_completion %v
Expand Down

0 comments on commit e88524b

Please sign in to comment.