cobra/nushell_completions.go
2024-12-11 17:22:41 -08:00

132 lines
4.4 KiB
Go

// Copyright 2013-2022 The Cobra Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cobra
import (
"bytes"
"fmt"
"io"
"os"
)
func (c *Command) GenNushellCompletion(w io.Writer, includeDesc bool) error {
buf := new(bytes.Buffer)
WriteStringAndCheck(buf, "# nushell completion -*- shell-script -*- \n")
WriteStringAndCheck(buf, fmt.Sprintf(`
let cobra_completer = {|spans|
let ShellCompDirectiveError = %[1]d
let ShellCompDirectiveNoSpace = %[2]d
let ShellCompDirectiveNoFileComp = %[3]d
let ShellCompDirectiveFilterFileExt = %[4]d
let ShellCompDirectiveFilterDirs = %[5]d
let ShellCompDirectiveKeepOrder = %[6]d
let cmd = $spans | first
let rest = $spans | skip
def cobra_log [message] {
let file = do -i {$env | get NUSHELL_COMP_DEBUG_FILE}
if $file != null {
echo $"($message)\n" | save $file --append
}
}
cobra_log $"External Completer called for cmd ($cmd)"
def exec_complete [
spans: list<string>
] {
# This will catch the stderr message related to the directive and any other errors,
# such as the command not being a cobra based command
let result = do --ignore-errors { COBRA_ACTIVE_HELP=0 run-external $cmd "__complete" ...$spans | complete }
if $result != null and $result.exit_code == 0 {
let completions = $result.stdout | lines
# the directive is the last line
let directive = do -i { $completions | last | str replace ':' '' | into int }
let completions = $completions | drop | each { |it|
# the first word is the command, the rest is the description
let words = $it | split row -r '\s{1}'
# If the last span contains a hypen and equals, attach it to the name
let last_span = $spans | last
let words = if ($last_span =~ '^-') and ($last_span =~ '=$') {
$words | each {|it| $"($last_span)($it)" }
} else {
$words
}
{value: ($words | first | str trim), description: ($words | skip | str join ' ')}
}
{completions: $completions, directive: $directive}
} else {
{completions: [], directive: -1}
}
}
if (not ($rest | is-empty)) {
let result = exec_complete $rest
let completions = $result.completions
let directive = $result.directive
# Add space at the end of each completion
let completions = if $directive != $ShellCompDirectiveNoSpace {
$completions | each {|it| {value: $"($it.value) ", description: $it.description}}
} else {
$completions
}
# Cobra returns a list of completions that are supported with this directive
# There is no way to currently support this in a nushell external completer
let completions = if $directive == $ShellCompDirectiveFilterFileExt {
[]
} else {
$completions
}
if $directive == $ShellCompDirectiveNoFileComp {
# Allow empty results as this will stop file completion
$completions
} else if ($completions | is-empty) or $directive == $ShellCompDirectiveError {
# Not returning null causes file completions to break
# Return null if there are no completions or ShellCompDirectiveError
null
} else {
$completions
}
} else {
null
}
}
`, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp,
ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder))
_, err := buf.WriteTo(w)
return err
}
func (c *Command) GenNushellCompletionFile(filename string, includeDesc bool) error {
outFile, err := os.Create(filename)
if err != nil {
return err
}
defer outFile.Close()
return c.GenNushellCompletion(outFile, includeDesc)
}