mirror of
https://github.com/sbrow/envr.git
synced 2026-08-26 09:53:32 -04:00
feat: Added bash completion support.
This commit is contained in:
+155
-2
@@ -29,7 +29,7 @@ def output [] {
|
||||
}
|
||||
|
||||
def shells [] {
|
||||
['nushell']
|
||||
['nushell' 'bash']
|
||||
}
|
||||
|
||||
`
|
||||
@@ -43,9 +43,11 @@ cmd_completion :: proc(cmd: ^Command) {
|
||||
switch cmd.args[0] {
|
||||
case "nushell":
|
||||
fmt.wprint(cmd.out, generate_nushell_completion(), flush = true)
|
||||
case "bash":
|
||||
fmt.wprint(cmd.out, generate_bash_completion(), flush = true)
|
||||
case:
|
||||
fmt.wprintf(cmd.err, "Unsupported shell: %s\n", cmd.args[0])
|
||||
fmt.wprintln(cmd.err, "Supported shells: nushell")
|
||||
fmt.wprintln(cmd.err, "Supported shells: nushell, bash")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,3 +115,154 @@ nushell_positional_line :: proc(arg: Positional_Arg) -> string {
|
||||
return fmt.tprintf("%s: %s", name, ntype)
|
||||
}
|
||||
|
||||
bash_header :: `
|
||||
_envr() {
|
||||
local cur prev cmd
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
cmd="${COMP_WORDS[1]}"
|
||||
|
||||
`
|
||||
|
||||
bash_footer :: `
|
||||
}
|
||||
|
||||
complete -F _envr envr
|
||||
`
|
||||
|
||||
generate_bash_completion :: proc() -> string {
|
||||
sb: strings.Builder
|
||||
strings.builder_init(&sb, context.temp_allocator)
|
||||
defer strings.builder_destroy(&sb)
|
||||
|
||||
fmt.sbprint(&sb, bash_header)
|
||||
|
||||
// B: Subcommand completion
|
||||
fmt.sbprintf(&sb, " if [[ $COMP_CWORD -eq 1 ]]; then\n")
|
||||
fmt.sbprintf(&sb, " COMPREPLY=( $(compgen -W \"")
|
||||
first := true
|
||||
for c in COMMANDS {
|
||||
if !first do fmt.sbprintf(&sb, " ")
|
||||
fmt.sbprintf(&sb, "%s", c.name)
|
||||
first = false
|
||||
for a in c.aliases {
|
||||
fmt.sbprintf(&sb, " %s", a)
|
||||
}
|
||||
}
|
||||
fmt.sbprintf(&sb, "\" -- \"$cur\") )\n")
|
||||
fmt.sbprintf(&sb, " return\n")
|
||||
fmt.sbprintf(&sb, " fi\n\n")
|
||||
|
||||
// C: Flag value completion
|
||||
fmt.sbprintf(&sb, " case \"$prev\" in\n")
|
||||
for ft in Flag_Type {
|
||||
f := flag_field(ft)
|
||||
if f.kind == .Bool do continue
|
||||
|
||||
pattern := fmt.tprintf("--%s", f.long_name)
|
||||
if len(f.short_name) > 0 {
|
||||
pattern = fmt.tprintf("%s|-%s", pattern, f.short_name)
|
||||
}
|
||||
fmt.sbprintf(&sb, " %s)\n", pattern)
|
||||
|
||||
switch f.kind {
|
||||
case .Enum:
|
||||
fmt.sbprintf(
|
||||
&sb,
|
||||
" COMPREPLY=( $(compgen -W \"%s\" -- \"$cur\") )\n",
|
||||
bash_enum_values(f.enum_values),
|
||||
)
|
||||
case .String:
|
||||
fmt.sbprintf(&sb, " COMPREPLY=( $(compgen -f -- \"$cur\") )\n")
|
||||
case .Bool:
|
||||
panic("unexpected")
|
||||
}
|
||||
fmt.sbprintf(&sb, " return\n")
|
||||
fmt.sbprintf(&sb, " ;;\n")
|
||||
}
|
||||
fmt.sbprintf(&sb, " esac\n\n")
|
||||
|
||||
// D: Flag name completion per command
|
||||
fmt.sbprintf(&sb, " case \"$cur\" in -*)\n")
|
||||
fmt.sbprintf(&sb, " case \"$cmd\" in\n")
|
||||
for c in COMMANDS {
|
||||
if c.flags == {} do continue
|
||||
|
||||
cmd_pattern := c.name
|
||||
for a in c.aliases {
|
||||
cmd_pattern = fmt.tprintf("%s|%s", cmd_pattern, a)
|
||||
}
|
||||
fmt.sbprintf(&sb, " %s)\n", cmd_pattern)
|
||||
fmt.sbprintf(&sb, " COMPREPLY=( $(compgen -W \"")
|
||||
|
||||
flag_first := true
|
||||
for ft in Flag_Type {
|
||||
if ft not_in c.flags do continue
|
||||
f := flag_field(ft)
|
||||
if !flag_first do fmt.sbprintf(&sb, " ")
|
||||
fmt.sbprintf(&sb, "--%s", f.long_name)
|
||||
if len(f.short_name) > 0 {
|
||||
fmt.sbprintf(&sb, " -%s", f.short_name)
|
||||
}
|
||||
flag_first = false
|
||||
}
|
||||
fmt.sbprintf(&sb, "\" -- \"$cur\") )\n")
|
||||
fmt.sbprintf(&sb, " return\n")
|
||||
fmt.sbprintf(&sb, " ;;\n")
|
||||
}
|
||||
fmt.sbprintf(&sb, " esac\n")
|
||||
fmt.sbprintf(&sb, " ;;\n")
|
||||
fmt.sbprintf(&sb, " esac\n\n")
|
||||
|
||||
// E: Positional completion
|
||||
fmt.sbprintf(&sb, " case \"$cmd\" in\n")
|
||||
for c in COMMANDS {
|
||||
has_comp := false
|
||||
for arg in c.args {
|
||||
if len(arg.completion) > 0 {
|
||||
has_comp = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has_comp do continue
|
||||
|
||||
cmd_pattern := c.name
|
||||
for a in c.aliases {
|
||||
cmd_pattern = fmt.tprintf("%s|%s", cmd_pattern, a)
|
||||
}
|
||||
fmt.sbprintf(&sb, " %s)\n", cmd_pattern)
|
||||
|
||||
for arg in c.args {
|
||||
if len(arg.completion) == 0 do continue
|
||||
|
||||
comp: string
|
||||
switch arg.completion {
|
||||
case "tracked-paths":
|
||||
comp = "$(envr list --output json 2>/dev/null)"
|
||||
case "untracked-paths":
|
||||
comp = "$(envr scan --output json 2>/dev/null)"
|
||||
case "shells":
|
||||
comp = "nushell bash"
|
||||
case:
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.sbprintf(&sb, " COMPREPLY=( $(compgen -W \"%s\" -- \"$cur\") )\n", comp)
|
||||
}
|
||||
fmt.sbprintf(&sb, " return\n")
|
||||
fmt.sbprintf(&sb, " ;;\n")
|
||||
}
|
||||
fmt.sbprintf(&sb, " esac\n")
|
||||
|
||||
fmt.sbprint(&sb, bash_footer)
|
||||
|
||||
return strings.to_string(sb)
|
||||
}
|
||||
|
||||
bash_enum_values :: proc(enum_values: string) -> string {
|
||||
s, _ := strings.replace(enum_values, "'", "", -1, context.temp_allocator)
|
||||
s, _ = strings.replace(s, "|", " ", -1, context.temp_allocator)
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user