Skip to content

Commit

Permalink
Merge pull request #26 from nao1215/add-auto-completion
Browse files Browse the repository at this point in the history
Auto-generate shell completion file
  • Loading branch information
nao1215 authored Apr 16, 2022
2 parents 063673a + 77f319c commit 98ac369
Show file tree
Hide file tree
Showing 6 changed files with 259 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [0.10.0] - 2022-04-17
## Added
- Added automatic generation of completion files for bash, zsh, and fish
# [0.9.3] - 2022-04-16
## Changed
- Parallelized update subcommand process
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,16 @@ $ ls /home/nao/.config/gup/gup.conf
$ gup import
```

### Auto-generate shell completion file (for bash, zsh, fish)
gup command automatically generates shell completion files for bash, zsh, and fish. After the user executes gup, if the shell completion file does not exist in the system, the auto-generation process will begin. To activate the completion feature, restart the shell.

```
$ gup
gup:INFO : create bash-completion file: /home/nao/.bash_completion
gup:INFO : create fish-completion file: /home/nao/.config/fish/completions/gup.fish
gup:INFO : create zsh-completion file: /home/nao/.zsh/completion/_gup
```

# Contributing
First off, thanks for taking the time to contribute! ❤️
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
Expand Down
208 changes: 208 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package cmd

import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/nao1215/gup/internal/assets"
"github.com/nao1215/gup/internal/completion"
"github.com/nao1215/gup/internal/file"
"github.com/nao1215/gup/internal/print"
"github.com/spf13/cobra"
)
Expand All @@ -15,7 +24,206 @@ If you update all binaries, just run '$ gup update'`,
// Execute run gup process.
func Execute() {
assets.DeployIconIfNeeded()
deployShellCompletionFileIfNeeded(rootCmd)

rootCmd.CompletionOptions.DisableDefaultCmd = true
if err := rootCmd.Execute(); err != nil {
print.Err(err)
}
}

// deleteShellCompletionFileIfNeeded creates the shell completion file.
// If the file with the same contents already exists, it is not created.
func deployShellCompletionFileIfNeeded(cmd *cobra.Command) {
makeBashCompletionFileIfNeeded(cmd)
makeFishCompletionFileIfNeeded(cmd)
makeZshCompletionFileIfNeeded(cmd)
}

func makeBashCompletionFileIfNeeded(cmd *cobra.Command) {
if existSameBashCompletionFile(cmd) {
return
}

path := completion.BashCompletionFilePath()
bashCompletion := new(bytes.Buffer)
if err := cmd.GenBashCompletion(bashCompletion); err != nil {
print.Err(fmt.Errorf("can not generate bash completion content: %w", err))
return
}

if !file.IsFile(path) {
fp, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0664)
if err != nil {
print.Err(fmt.Errorf("can not create .bash_completion: %w", err))
return
}
defer fp.Close()

if _, err := fp.WriteString(bashCompletion.String()); err != nil {
print.Err(fmt.Errorf("can not write .bash_completion %w", err))
}
print.Info("create bash-completion file: " + path)
return
}

fp, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0664)
if err != nil {
print.Err(fmt.Errorf("can not append .bash_completion for gup: %w", err))
return
}
defer fp.Close()

if _, err := fp.WriteString(bashCompletion.String()); err != nil {
print.Err(fmt.Errorf("can not append .bash_completion for gup: %w", err))
return
}

print.Info("append bash-completion for gup: " + path)
}

func makeFishCompletionFileIfNeeded(cmd *cobra.Command) {
if isSameFishCompletionFile(cmd) {
return
}

path := completion.FishCompletionFilePath()
if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
print.Err(fmt.Errorf("can not create fish-completion file: %w", err))
return
}

if err := cmd.GenFishCompletionFile(path, false); err != nil {
print.Err(fmt.Errorf("can not create fish-completion file: %w", err))
return
}
print.Info("create fish-completion file: " + path)
}

func makeZshCompletionFileIfNeeded(cmd *cobra.Command) {
if isSameZshCompletionFile(cmd) {
return
}

path := completion.ZshCompletionFilePath()
if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
print.Err(fmt.Errorf("can not create zsh-completion file: %w", err))
return
}

if err := cmd.GenZshCompletionFile(path); err != nil {
print.Err(fmt.Errorf("can not create zsh-completion file: %w", err))
return
}
print.Info("create zsh-completion file: " + path)

const zshFpath = `
# setting for gup command (auto generate)
fpath=(~/.zsh/completion $fpath)
autoload -Uz compinit && compinit -i
`
zshrcPath := completion.ZshrcPath()
if !file.IsFile(zshrcPath) {
fp, err := os.OpenFile(zshrcPath, os.O_RDWR|os.O_CREATE, 0664)
if err != nil {
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
return
}
defer fp.Close()

if _, err := fp.WriteString(zshFpath); err != nil {
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
}
return
}

zshrc, err := ioutil.ReadFile(zshrcPath)
if err != nil {
print.Err(fmt.Errorf("can not read .zshrc: %w", err))
return
}

if strings.Contains(string(zshrc), zshFpath) {
return
}

fp, err := os.OpenFile(zshrcPath, os.O_RDWR|os.O_APPEND, 0664)
if err != nil {
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
return
}
defer fp.Close()

if _, err := fp.WriteString(zshFpath); err != nil {
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
return
}
}

func existSameBashCompletionFile(cmd *cobra.Command) bool {
if !file.IsFile(completion.BashCompletionFilePath()) {
return false
}
return hasSameBashCompletionContent(cmd)
}

func hasSameBashCompletionContent(cmd *cobra.Command) bool {
bashCompletionFileInLocal, err := ioutil.ReadFile(completion.BashCompletionFilePath())
if err != nil {
print.Err(fmt.Errorf("can not read .bash_completion: %w", err))
return false
}

currentBashCompletion := new(bytes.Buffer)
if err := cmd.GenBashCompletion(currentBashCompletion); err != nil {
return false
}
if !strings.Contains(string(bashCompletionFileInLocal), currentBashCompletion.String()) {
return false
}
return true
}

func isSameFishCompletionFile(cmd *cobra.Command) bool {
path := completion.FishCompletionFilePath()
if !file.IsFile(path) {
return false
}

currentFishCompletion := new(bytes.Buffer)
if err := cmd.GenFishCompletion(currentFishCompletion, false); err != nil {
return false
}

fishCompletionInLocal, err := ioutil.ReadFile(path)
if err != nil {
return false
}

if bytes.Compare(currentFishCompletion.Bytes(), fishCompletionInLocal) != 0 {
return false
}
return true
}

func isSameZshCompletionFile(cmd *cobra.Command) bool {
path := completion.ZshCompletionFilePath()
if !file.IsFile(path) {
return false
}

currentZshCompletion := new(bytes.Buffer)
if err := cmd.GenZshCompletion(currentZshCompletion); err != nil {
return false
}

zshCompletionInLocal, err := ioutil.ReadFile(path)
if err != nil {
return false
}

if bytes.Compare(currentZshCompletion.Bytes(), zshCompletionInLocal) != 0 {
return false
}
return true
}
9 changes: 9 additions & 0 deletions doc/ja/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,15 @@ $ ls /home/nao/.config/gup/gup.conf
/home/nao/.config/gup/gup.conf
$ gup import
```

### シェル補完ファイルの自動生成 (bash, zsh, fish)
gupコマンドは、bash、zsh、fish向けのシェル補完ファイルを自動生成します。ユーザーがgupを実行した後、シェル補完ファイルがシステムに存在しない場合は、自動生成処理を開始します。シェル補完を有効にするには、シェルを再起動してください。
```
$ gup
gup:INFO : create bash-completion file: /home/nao/.bash_completion
gup:INFO : create fish-completion file: /home/nao/.config/fish/completions/gup.fish
gup:INFO : create zsh-completion file: /home/nao/.zsh/completion/_gup
```
# 連絡先
開発者に対して「バグ報告」や「機能の追加要望」がある場合は、コメントをください。その際、以下の連絡先を使用してください。
- [GitHub Issue](https://github.com/nao1215/gup/issues)
Expand Down
2 changes: 1 addition & 1 deletion internal/cmdinfo/cmdinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

const (
name = "gup"
version = "0.9.4"
version = "0.10.0"
)

// Version return gup command version.
Expand Down
28 changes: 28 additions & 0 deletions internal/completion/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package completion

import (
"os"
"path/filepath"

"github.com/nao1215/gup/internal/cmdinfo"
)

// BashCompletionFilePath return bash-completion file path.
func BashCompletionFilePath() string {
return filepath.Join(os.Getenv("HOME"), ".bash_completion")
}

// FishCompletionFilePath return fish-completion file path.
func FishCompletionFilePath() string {
return filepath.Join(os.Getenv("HOME"), ".config", "fish", "completions", cmdinfo.Name()+".fish")
}

// ZshCompletionFilePath return zsh-completion file path.
func ZshCompletionFilePath() string {
return filepath.Join(os.Getenv("HOME"), ".zsh", "completion", "_"+cmdinfo.Name())
}

// ZshrcPath return .zshrc path.
func ZshrcPath() string {
return filepath.Join(os.Getenv("HOME"), ".zshrc")
}

0 comments on commit 98ac369

Please sign in to comment.