Move install package back to project root
This commit is contained in:
parent
ca6cedb614
commit
c3bfbddfe6
|
@ -7,8 +7,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/posener/complete/v2/install"
|
||||||
"github.com/posener/complete/v2/internal/arg"
|
"github.com/posener/complete/v2/internal/arg"
|
||||||
"github.com/posener/complete/v2/internal/install"
|
|
||||||
"github.com/posener/complete/v2/internal/tokener"
|
"github.com/posener/complete/v2/internal/tokener"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
1
go.sum
1
go.sum
|
@ -8,6 +8,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.2.2 h1:xu2vuFugxm4IfAymbmmVrnEVy29eGUDn8I7HheRseHg=
|
github.com/posener/complete v1.2.2 h1:xu2vuFugxm4IfAymbmmVrnEVy29eGUDn8I7HheRseHg=
|
||||||
github.com/posener/complete v1.2.2/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
github.com/posener/complete v1.2.2/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||||
|
github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package arg
|
package arg
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
import "github.com/posener/complete/v2/internal/tokener"
|
"github.com/posener/complete/v2/internal/tokener"
|
||||||
|
)
|
||||||
|
|
||||||
// Arg is typed a command line argument.
|
// Arg is typed a command line argument.
|
||||||
type Arg struct {
|
type Arg struct {
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
package install
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// (un)install in bash
|
|
||||||
// basically adds/remove from .bashrc:
|
|
||||||
//
|
|
||||||
// complete -C </path/to/completion/command> <command>
|
|
||||||
type bash struct {
|
|
||||||
rc string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bash) IsInstalled(cmd, bin string) bool {
|
|
||||||
completeCmd := b.cmd(cmd, bin)
|
|
||||||
return lineInFile(b.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bash) Install(cmd, bin string) error {
|
|
||||||
if b.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("already installed in %s", b.rc)
|
|
||||||
}
|
|
||||||
completeCmd := b.cmd(cmd, bin)
|
|
||||||
return appendToFile(b.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b bash) Uninstall(cmd, bin string) error {
|
|
||||||
if !b.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("does not installed in %s", b.rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
completeCmd := b.cmd(cmd, bin)
|
|
||||||
return removeFromFile(b.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bash) cmd(cmd, bin string) string {
|
|
||||||
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package install
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"text/template"
|
|
||||||
)
|
|
||||||
|
|
||||||
// (un)install in fish
|
|
||||||
|
|
||||||
type fish struct {
|
|
||||||
configDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fish) IsInstalled(cmd, bin string) bool {
|
|
||||||
completionFile := f.getCompletionFilePath(cmd)
|
|
||||||
if _, err := os.Stat(completionFile); err == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fish) Install(cmd, bin string) error {
|
|
||||||
if f.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("already installed at %s", f.getCompletionFilePath(cmd))
|
|
||||||
}
|
|
||||||
|
|
||||||
completionFile := f.getCompletionFilePath(cmd)
|
|
||||||
completeCmd, err := f.cmd(cmd, bin)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return createFile(completionFile, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fish) Uninstall(cmd, bin string) error {
|
|
||||||
if !f.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("does not installed in %s", f.configDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
completionFile := f.getCompletionFilePath(cmd)
|
|
||||||
return os.Remove(completionFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fish) getCompletionFilePath(cmd string) string {
|
|
||||||
return filepath.Join(f.configDir, "completions", fmt.Sprintf("%s.fish", cmd))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fish) cmd(cmd, bin string) (string, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
params := struct{ Cmd, Bin string }{cmd, bin}
|
|
||||||
tmpl := template.Must(template.New("cmd").Parse(`
|
|
||||||
function __complete_{{.Cmd}}
|
|
||||||
set -lx COMP_LINE (commandline -cp)
|
|
||||||
test -z (commandline -ct)
|
|
||||||
and set COMP_LINE "$COMP_LINE "
|
|
||||||
{{.Bin}}
|
|
||||||
end
|
|
||||||
complete -f -c {{.Cmd}} -a "(__complete_{{.Cmd}})"
|
|
||||||
`))
|
|
||||||
err := tmpl.Execute(&buf, params)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
|
@ -1,176 +0,0 @@
|
||||||
package install
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run(name string, uninstall, yes bool, out io.Writer, in io.Reader) {
|
|
||||||
action := "install"
|
|
||||||
if uninstall {
|
|
||||||
action = "uninstall"
|
|
||||||
}
|
|
||||||
if !yes {
|
|
||||||
fmt.Fprintf(out, "%s completion for %s? ", action, name)
|
|
||||||
var answer string
|
|
||||||
fmt.Fscanln(in, &answer)
|
|
||||||
switch strings.ToLower(answer) {
|
|
||||||
case "y", "yes":
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(out, "Cancelling...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintf(out, action+"ing...")
|
|
||||||
|
|
||||||
if uninstall {
|
|
||||||
Uninstall(name)
|
|
||||||
} else {
|
|
||||||
Install(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type installer interface {
|
|
||||||
IsInstalled(cmd, bin string) bool
|
|
||||||
Install(cmd, bin string) error
|
|
||||||
Uninstall(cmd, bin string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install complete command given:
|
|
||||||
// cmd: is the command name
|
|
||||||
func Install(cmd string) error {
|
|
||||||
is := installers()
|
|
||||||
if len(is) == 0 {
|
|
||||||
return errors.New("Did not find any shells to install")
|
|
||||||
}
|
|
||||||
bin, err := getBinaryPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range is {
|
|
||||||
errI := i.Install(cmd, bin)
|
|
||||||
if errI != nil {
|
|
||||||
err = multierror.Append(err, errI)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsInstalled returns true if the completion
|
|
||||||
// for the given cmd is installed.
|
|
||||||
func IsInstalled(cmd string) bool {
|
|
||||||
bin, err := getBinaryPath()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range installers() {
|
|
||||||
installed := i.IsInstalled(cmd, bin)
|
|
||||||
if installed {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Uninstall complete command given:
|
|
||||||
// cmd: is the command name
|
|
||||||
func Uninstall(cmd string) error {
|
|
||||||
is := installers()
|
|
||||||
if len(is) == 0 {
|
|
||||||
return errors.New("Did not find any shells to uninstall")
|
|
||||||
}
|
|
||||||
bin, err := getBinaryPath()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range is {
|
|
||||||
errI := i.Uninstall(cmd, bin)
|
|
||||||
if errI != nil {
|
|
||||||
err = multierror.Append(err, errI)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func installers() (i []installer) {
|
|
||||||
// The list of bash config files candidates where it is
|
|
||||||
// possible to install the completion command.
|
|
||||||
var bashConfFiles []string
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "darwin":
|
|
||||||
bashConfFiles = []string{".bash_profile"}
|
|
||||||
default:
|
|
||||||
bashConfFiles = []string{".bashrc", ".bash_profile", ".bash_login", ".profile"}
|
|
||||||
}
|
|
||||||
for _, rc := range bashConfFiles {
|
|
||||||
if f := rcFile(rc); f != "" {
|
|
||||||
i = append(i, bash{f})
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f := rcFile(".zshrc"); f != "" {
|
|
||||||
i = append(i, zsh{f})
|
|
||||||
}
|
|
||||||
if d := fishConfigDir(); d != "" {
|
|
||||||
i = append(i, fish{d})
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func fishConfigDir() string {
|
|
||||||
configDir := filepath.Join(getConfigHomePath(), "fish")
|
|
||||||
if configDir == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if info, err := os.Stat(configDir); err != nil || !info.IsDir() {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return configDir
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConfigHomePath() string {
|
|
||||||
u, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
configHome := os.Getenv("XDG_CONFIG_HOME")
|
|
||||||
if configHome == "" {
|
|
||||||
return filepath.Join(u.HomeDir, ".config")
|
|
||||||
}
|
|
||||||
return configHome
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBinaryPath() (string, error) {
|
|
||||||
bin, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.Abs(bin)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rcFile(name string) string {
|
|
||||||
u, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
path := filepath.Join(u.HomeDir, name)
|
|
||||||
if _, err := os.Stat(path); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
package install
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
func lineInFile(name string, lookFor string) bool {
|
|
||||||
f, err := os.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
r := bufio.NewReader(f)
|
|
||||||
prefix := []byte{}
|
|
||||||
for {
|
|
||||||
line, isPrefix, err := r.ReadLine()
|
|
||||||
if err == io.EOF {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if isPrefix {
|
|
||||||
prefix = append(prefix, line...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line = append(prefix, line...)
|
|
||||||
if string(line) == lookFor {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
prefix = prefix[:0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createFile(name string, content string) error {
|
|
||||||
// make sure file directory exists
|
|
||||||
if err := os.MkdirAll(filepath.Dir(name), 0775); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the file
|
|
||||||
f, err := os.Create(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
// write file content
|
|
||||||
_, err = f.WriteString(fmt.Sprintf("%s\n", content))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendToFile(name string, content string) error {
|
|
||||||
f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
_, err = f.WriteString(fmt.Sprintf("\n%s\n", content))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeFromFile(name string, content string) error {
|
|
||||||
backup := name + ".bck"
|
|
||||||
err := copyFile(name, backup)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
temp, err := removeContentToTempFile(name, content)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = copyFile(temp, name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.Remove(backup)
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeContentToTempFile(name, content string) (string, error) {
|
|
||||||
rf, err := os.Open(name)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer rf.Close()
|
|
||||||
wf, err := ioutil.TempFile("/tmp", "complete-")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer wf.Close()
|
|
||||||
|
|
||||||
r := bufio.NewReader(rf)
|
|
||||||
prefix := []byte{}
|
|
||||||
for {
|
|
||||||
line, isPrefix, err := r.ReadLine()
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if isPrefix {
|
|
||||||
prefix = append(prefix, line...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
line = append(prefix, line...)
|
|
||||||
str := string(line)
|
|
||||||
if str == content {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = wf.WriteString(str + "\n")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
prefix = prefix[:0]
|
|
||||||
}
|
|
||||||
return wf.Name(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyFile(src string, dst string) error {
|
|
||||||
in, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer in.Close()
|
|
||||||
out, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
_, err = io.Copy(out, in)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
package install
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// (un)install in zsh
|
|
||||||
// basically adds/remove from .zshrc:
|
|
||||||
//
|
|
||||||
// autoload -U +X bashcompinit && bashcompinit"
|
|
||||||
// complete -C </path/to/completion/command> <command>
|
|
||||||
type zsh struct {
|
|
||||||
rc string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (z zsh) IsInstalled(cmd, bin string) bool {
|
|
||||||
completeCmd := z.cmd(cmd, bin)
|
|
||||||
return lineInFile(z.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (z zsh) Install(cmd, bin string) error {
|
|
||||||
if z.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("already installed in %s", z.rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
completeCmd := z.cmd(cmd, bin)
|
|
||||||
bashCompInit := "autoload -U +X bashcompinit && bashcompinit"
|
|
||||||
if !lineInFile(z.rc, bashCompInit) {
|
|
||||||
completeCmd = bashCompInit + "\n" + completeCmd
|
|
||||||
}
|
|
||||||
|
|
||||||
return appendToFile(z.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (z zsh) Uninstall(cmd, bin string) error {
|
|
||||||
if !z.IsInstalled(cmd, bin) {
|
|
||||||
return fmt.Errorf("does not installed in %s", z.rc)
|
|
||||||
}
|
|
||||||
|
|
||||||
completeCmd := z.cmd(cmd, bin)
|
|
||||||
return removeFromFile(z.rc, completeCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (zsh) cmd(cmd, bin string) string {
|
|
||||||
return fmt.Sprintf("complete -o nospace -C %s %s", bin, cmd)
|
|
||||||
}
|
|
Loading…
Reference in New Issue