Skip to content
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])
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