commit
758253551e
|
@ -1,153 +1,32 @@
|
||||||
package install
|
package install
|
||||||
|
|
||||||
import (
|
import "fmt"
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
|
||||||
)
|
|
||||||
|
|
||||||
type bash struct{}
|
// (un)install in bash
|
||||||
|
// basically adds/remove from .bashrc:
|
||||||
func (bash) Install(cmd, bin string) error {
|
//
|
||||||
bashRCFileName, err := bashRCFileName()
|
// complete -C </path/to/completion/command> <command>
|
||||||
if err != nil {
|
type bash struct {
|
||||||
return err
|
rc string
|
||||||
}
|
|
||||||
completeCmd := completeCmd(cmd, bin)
|
|
||||||
if isInFile(bashRCFileName, completeCmd) {
|
|
||||||
return errors.New("Already installed in ~/.bashrc")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bashRC, err := os.OpenFile(bashRCFileName, os.O_RDWR|os.O_APPEND, 0)
|
func (b bash) Install(cmd, bin string) error {
|
||||||
if err != nil {
|
completeCmd := b.cmd(cmd, bin)
|
||||||
return err
|
if lineInFile(b.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("already installed in %s", b.rc)
|
||||||
}
|
}
|
||||||
defer bashRC.Close()
|
return appendToFile(b.rc, completeCmd)
|
||||||
_, err = bashRC.WriteString(fmt.Sprintf("\n%s\n", completeCmd))
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bash) Uninstall(cmd, bin string) error {
|
func (b bash) Uninstall(cmd, bin string) error {
|
||||||
bashRC, err := bashRCFileName()
|
completeCmd := b.cmd(cmd, bin)
|
||||||
if err != nil {
|
if !lineInFile(b.rc, completeCmd) {
|
||||||
return err
|
return fmt.Errorf("does not installed in %s", b.rc)
|
||||||
}
|
|
||||||
backup := bashRC + ".bck"
|
|
||||||
err = copyFile(bashRC, backup)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
completeCmd := completeCmd(cmd, bin)
|
|
||||||
if !isInFile(bashRC, completeCmd) {
|
|
||||||
return errors.New("Does not installed in ~/.bashrc")
|
|
||||||
}
|
|
||||||
temp, err := uninstallToTemp(bashRC, completeCmd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = copyFile(temp, bashRC)
|
return removeFromFile(b.rc, completeCmd)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Remove(backup)
|
func (bash) cmd(cmd, bin string) string {
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func completeCmd(cmd, bin string) string {
|
|
||||||
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func bashRCFileName() (string, error) {
|
|
||||||
u, err := user.Current()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return filepath.Join(u.HomeDir, ".bashrc"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func isInFile(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 uninstallToTemp(bashRCFileName, completeCmd string) (string, error) {
|
|
||||||
rf, err := os.Open(bashRCFileName)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer rf.Close()
|
|
||||||
wf, err := ioutil.TempFile("/tmp", "bashrc-")
|
|
||||||
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 == completeCmd {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
wf.WriteString(str + "\n")
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,9 +2,11 @@ package install
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
type installer interface {
|
type installer interface {
|
||||||
|
@ -15,46 +17,55 @@ type installer interface {
|
||||||
// Install complete command given:
|
// Install complete command given:
|
||||||
// cmd: is the command name
|
// cmd: is the command name
|
||||||
func Install(cmd string) error {
|
func Install(cmd string) error {
|
||||||
shell := shellType()
|
is := installers()
|
||||||
if shell == "" {
|
if len(is) == 0 {
|
||||||
return errors.New("must install through a terminatl")
|
return errors.New("Did not found any shells to install")
|
||||||
}
|
|
||||||
i := getInstaller(shell)
|
|
||||||
if i == nil {
|
|
||||||
return fmt.Errorf("shell %s not supported", shell)
|
|
||||||
}
|
}
|
||||||
bin, err := getBinaryPath()
|
bin, err := getBinaryPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return i.Install(cmd, bin)
|
|
||||||
|
for _, i := range is {
|
||||||
|
errI := i.Install(cmd, bin)
|
||||||
|
if errI != nil {
|
||||||
|
multierror.Append(err, errI)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uninstall complete command given:
|
// Uninstall complete command given:
|
||||||
// cmd: is the command name
|
// cmd: is the command name
|
||||||
func Uninstall(cmd string) error {
|
func Uninstall(cmd string) error {
|
||||||
shell := shellType()
|
is := installers()
|
||||||
if shell == "" {
|
if len(is) == 0 {
|
||||||
return errors.New("must uninstall through a terminatl")
|
return errors.New("Did not found any shells to uninstall")
|
||||||
}
|
|
||||||
i := getInstaller(shell)
|
|
||||||
if i == nil {
|
|
||||||
return fmt.Errorf("shell %s not supported", shell)
|
|
||||||
}
|
}
|
||||||
bin, err := getBinaryPath()
|
bin, err := getBinaryPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return i.Uninstall(cmd, bin)
|
|
||||||
|
for _, i := range is {
|
||||||
|
errI := i.Uninstall(cmd, bin)
|
||||||
|
if errI != nil {
|
||||||
|
multierror.Append(err, errI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInstaller(shell string) installer {
|
return err
|
||||||
switch shell {
|
|
||||||
case "bash":
|
|
||||||
return bash{}
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func installers() (i []installer) {
|
||||||
|
if f := rcFile(".bashrc"); f != "" {
|
||||||
|
i = append(i, bash{f})
|
||||||
|
}
|
||||||
|
if f := rcFile(".zshrc"); f != "" {
|
||||||
|
i = append(i, zsh{f})
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBinaryPath() (string, error) {
|
func getBinaryPath() (string, error) {
|
||||||
|
@ -65,7 +76,10 @@ func getBinaryPath() (string, error) {
|
||||||
return filepath.Abs(bin)
|
return filepath.Abs(bin)
|
||||||
}
|
}
|
||||||
|
|
||||||
func shellType() string {
|
func rcFile(name string) string {
|
||||||
shell := os.Getenv("SHELL")
|
u, err := user.Current()
|
||||||
return filepath.Base(shell)
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return filepath.Join(u.HomeDir, name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
wf.WriteString(str + "\n")
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
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) Install(cmd, bin string) error {
|
||||||
|
completeCmd := z.cmd(cmd, bin)
|
||||||
|
if lineInFile(z.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("already installed in %s", z.rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
completeCmd := z.cmd(cmd, bin)
|
||||||
|
if !lineInFile(z.rc, completeCmd) {
|
||||||
|
return fmt.Errorf("does not installed in %s", z.rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return removeFromFile(z.rc, completeCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (zsh) cmd(cmd, bin string) string {
|
||||||
|
return fmt.Sprintf("complete -C %s %s", bin, cmd)
|
||||||
|
}
|
11
readme.md
11
readme.md
|
@ -36,7 +36,14 @@ Uninstall by `gocomplete -uninstall`
|
||||||
- Complete packages names or `.go` files when necessary.
|
- Complete packages names or `.go` files when necessary.
|
||||||
- Complete test names after `-run` flag.
|
- Complete test names after `-run` flag.
|
||||||
|
|
||||||
## Usage
|
## complete package
|
||||||
|
|
||||||
|
Supported shells:
|
||||||
|
|
||||||
|
[x] bash
|
||||||
|
[x] zsh
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
Assuming you have program called `run` and you want to have bash completion
|
Assuming you have program called `run` and you want to have bash completion
|
||||||
for it, meaning, if you type `run` then space, then press the `Tab` key,
|
for it, meaning, if you type `run` then space, then press the `Tab` key,
|
||||||
|
@ -95,7 +102,7 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Self completing program
|
### Self completing program
|
||||||
|
|
||||||
In case that the program that we want to complete is written in go we
|
In case that the program that we want to complete is written in go we
|
||||||
can make it self completing. Here is an [example](./example/self/main.go)
|
can make it self completing. Here is an [example](./example/self/main.go)
|
||||||
|
|
Loading…
Reference in New Issue