Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bash completion #1684

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/auxents/auxents.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func init() {
{"lecat", lecatMain},
{"termcvt", termcvtMain},
{"unhex", unhexMain},
{"completion", genCompletion},
}
}

Expand Down
35 changes: 35 additions & 0 deletions pkg/auxents/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package auxents

import "fmt"

func genCompletion(args []string) int {
verb := args[1]
args = args[2:]

printUsage := func() {
fmt.Printf("Usage: mlr %s SHELL\n", verb)
fmt.Println("Supported shells: bash")
fmt.Println()
fmt.Println("Add below to your bashrc to enable completion")
fmt.Println("source <(mlr completion bash)")
}

if len(args) != 1 {
printUsage()
return 1
}

if args[0] == "-h" || args[0] == "--help" {
printUsage()
return 0
}

if args[0] != "bash" {
fmt.Printlf("Unsupported shell: %s\n", args[0])

Check failure on line 28 in pkg/auxents/completion.go

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

undefined: fmt.Printlf

Check failure on line 28 in pkg/auxents/completion.go

View workflow job for this annotation

GitHub Actions / build (macos-latest)

undefined: fmt.Printlf
printUsage()
return 1
}

fmt.Println(`complete -o nospace -o nosort -C "mlr _complete_bash" mlr`)
return 0
}
65 changes: 65 additions & 0 deletions pkg/completion/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Package completion handles Shell completion
package completion

import (
"fmt"
"os"
"sort"
"strings"

"github.com/johnkerl/miller/v6/pkg/transformers"
)

func DoCompletion() {
if os.Args[1] != "_complete_bash" {
return
}
if len(os.Args) < 5 {
Debug()
return
}
// See: https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html#index-complete
// Bash completion calls with three arguments: $1 is the name of the
// command whose arguments are being completed, $2 is the word being
// completed, and $3 is the word preceding the word being completed. Since
// we already set one argument, the rest of them are shifted by one
// position i.e. `mlr _complete_bash <mlr> <last> <prev>`,
last := os.Args[3]
prev := os.Args[4]
if prev == "then" {
matches := GetMatchingVerbs(last)
// See: https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-COMP_005fTYPE
// When tab is hit two times, bash sets COMP_TYPE to ascii value of `?` i.e. 63
if len(matches) == 1 && matches[0].Verb == last && os.Getenv("COMP_TYPE") == "63" {
v := matches[0]
v.UsageFunc(os.Stdout)
} else {
sort.Slice(matches, func(i, j int) bool { return matches[i].Verb < matches[j].Verb })
for _, verb := range matches {
fmt.Println(verb.Verb)
}
}
}
}

func GetMatchingVerbs(partVerb string) []*transformers.TransformerSetup {
var matches []*transformers.TransformerSetup
for _, verb := range transformers.TRANSFORMER_LOOKUP_TABLE {
localv := verb
if strings.HasPrefix(verb.Verb, partVerb) {
matches = append(matches, &localv)
}
}
return matches
}

func Debug() {
for i, arg := range os.Args {
fmt.Fprintln(os.Stderr, i, arg)
}
for _, val := range os.Environ() {
if strings.HasPrefix(val, "COMP") {
fmt.Fprintln(os.Stderr, val)
}
}
}
9 changes: 9 additions & 0 deletions pkg/entrypoint/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"fmt"
"os"
"path"
"strings"

"github.com/johnkerl/miller/v6/pkg/auxents"
"github.com/johnkerl/miller/v6/pkg/cli"
"github.com/johnkerl/miller/v6/pkg/climain"
"github.com/johnkerl/miller/v6/pkg/completion"
"github.com/johnkerl/miller/v6/pkg/lib"
"github.com/johnkerl/miller/v6/pkg/platform"
"github.com/johnkerl/miller/v6/pkg/stream"
Expand All @@ -24,6 +26,13 @@ type MainReturn struct {
}

func Main() MainReturn {

if len(os.Args) > 1 {
if strings.HasPrefix(os.Args[1], "_complete") {
completion.DoCompletion()
return MainReturn{PrintElapsedTime: false}
}
}
// Special handling for Windows so we can do things like:
//
// mlr put '$a = $b . "cd \"efg\" hi"' foo.dat
Expand Down
Loading