Skip to content

Commit 98ac369

Browse files
authored
Merge pull request #26 from nao1215/add-auto-completion
Auto-generate shell completion file
2 parents 063673a + 77f319c commit 98ac369

File tree

6 files changed

+259
-1
lines changed

6 files changed

+259
-1
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22
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).
33

4+
# [0.10.0] - 2022-04-17
5+
## Added
6+
- Added automatic generation of completion files for bash, zsh, and fish
47
# [0.9.3] - 2022-04-16
58
## Changed
69
- Parallelized update subcommand process

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ $ ls /home/nao/.config/gup/gup.conf
112112
$ gup import
113113
```
114114

115+
### Auto-generate shell completion file (for bash, zsh, fish)
116+
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.
117+
118+
```
119+
$ gup
120+
gup:INFO : create bash-completion file: /home/nao/.bash_completion
121+
gup:INFO : create fish-completion file: /home/nao/.config/fish/completions/gup.fish
122+
gup:INFO : create zsh-completion file: /home/nao/.zsh/completion/_gup
123+
```
124+
115125
# Contributing
116126
First off, thanks for taking the time to contribute! ❤️
117127
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.

cmd/root.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
package cmd
22

33
import (
4+
"bytes"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
411
"github.com/nao1215/gup/internal/assets"
12+
"github.com/nao1215/gup/internal/completion"
13+
"github.com/nao1215/gup/internal/file"
514
"github.com/nao1215/gup/internal/print"
615
"github.com/spf13/cobra"
716
)
@@ -15,7 +24,206 @@ If you update all binaries, just run '$ gup update'`,
1524
// Execute run gup process.
1625
func Execute() {
1726
assets.DeployIconIfNeeded()
27+
deployShellCompletionFileIfNeeded(rootCmd)
28+
29+
rootCmd.CompletionOptions.DisableDefaultCmd = true
1830
if err := rootCmd.Execute(); err != nil {
1931
print.Err(err)
2032
}
2133
}
34+
35+
// deleteShellCompletionFileIfNeeded creates the shell completion file.
36+
// If the file with the same contents already exists, it is not created.
37+
func deployShellCompletionFileIfNeeded(cmd *cobra.Command) {
38+
makeBashCompletionFileIfNeeded(cmd)
39+
makeFishCompletionFileIfNeeded(cmd)
40+
makeZshCompletionFileIfNeeded(cmd)
41+
}
42+
43+
func makeBashCompletionFileIfNeeded(cmd *cobra.Command) {
44+
if existSameBashCompletionFile(cmd) {
45+
return
46+
}
47+
48+
path := completion.BashCompletionFilePath()
49+
bashCompletion := new(bytes.Buffer)
50+
if err := cmd.GenBashCompletion(bashCompletion); err != nil {
51+
print.Err(fmt.Errorf("can not generate bash completion content: %w", err))
52+
return
53+
}
54+
55+
if !file.IsFile(path) {
56+
fp, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0664)
57+
if err != nil {
58+
print.Err(fmt.Errorf("can not create .bash_completion: %w", err))
59+
return
60+
}
61+
defer fp.Close()
62+
63+
if _, err := fp.WriteString(bashCompletion.String()); err != nil {
64+
print.Err(fmt.Errorf("can not write .bash_completion %w", err))
65+
}
66+
print.Info("create bash-completion file: " + path)
67+
return
68+
}
69+
70+
fp, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND, 0664)
71+
if err != nil {
72+
print.Err(fmt.Errorf("can not append .bash_completion for gup: %w", err))
73+
return
74+
}
75+
defer fp.Close()
76+
77+
if _, err := fp.WriteString(bashCompletion.String()); err != nil {
78+
print.Err(fmt.Errorf("can not append .bash_completion for gup: %w", err))
79+
return
80+
}
81+
82+
print.Info("append bash-completion for gup: " + path)
83+
}
84+
85+
func makeFishCompletionFileIfNeeded(cmd *cobra.Command) {
86+
if isSameFishCompletionFile(cmd) {
87+
return
88+
}
89+
90+
path := completion.FishCompletionFilePath()
91+
if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
92+
print.Err(fmt.Errorf("can not create fish-completion file: %w", err))
93+
return
94+
}
95+
96+
if err := cmd.GenFishCompletionFile(path, false); err != nil {
97+
print.Err(fmt.Errorf("can not create fish-completion file: %w", err))
98+
return
99+
}
100+
print.Info("create fish-completion file: " + path)
101+
}
102+
103+
func makeZshCompletionFileIfNeeded(cmd *cobra.Command) {
104+
if isSameZshCompletionFile(cmd) {
105+
return
106+
}
107+
108+
path := completion.ZshCompletionFilePath()
109+
if err := os.MkdirAll(filepath.Dir(path), 0775); err != nil {
110+
print.Err(fmt.Errorf("can not create zsh-completion file: %w", err))
111+
return
112+
}
113+
114+
if err := cmd.GenZshCompletionFile(path); err != nil {
115+
print.Err(fmt.Errorf("can not create zsh-completion file: %w", err))
116+
return
117+
}
118+
print.Info("create zsh-completion file: " + path)
119+
120+
const zshFpath = `
121+
# setting for gup command (auto generate)
122+
fpath=(~/.zsh/completion $fpath)
123+
autoload -Uz compinit && compinit -i
124+
`
125+
zshrcPath := completion.ZshrcPath()
126+
if !file.IsFile(zshrcPath) {
127+
fp, err := os.OpenFile(zshrcPath, os.O_RDWR|os.O_CREATE, 0664)
128+
if err != nil {
129+
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
130+
return
131+
}
132+
defer fp.Close()
133+
134+
if _, err := fp.WriteString(zshFpath); err != nil {
135+
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
136+
}
137+
return
138+
}
139+
140+
zshrc, err := ioutil.ReadFile(zshrcPath)
141+
if err != nil {
142+
print.Err(fmt.Errorf("can not read .zshrc: %w", err))
143+
return
144+
}
145+
146+
if strings.Contains(string(zshrc), zshFpath) {
147+
return
148+
}
149+
150+
fp, err := os.OpenFile(zshrcPath, os.O_RDWR|os.O_APPEND, 0664)
151+
if err != nil {
152+
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
153+
return
154+
}
155+
defer fp.Close()
156+
157+
if _, err := fp.WriteString(zshFpath); err != nil {
158+
print.Err(fmt.Errorf("can not add zsh $fpath in .zshrc: %w", err))
159+
return
160+
}
161+
}
162+
163+
func existSameBashCompletionFile(cmd *cobra.Command) bool {
164+
if !file.IsFile(completion.BashCompletionFilePath()) {
165+
return false
166+
}
167+
return hasSameBashCompletionContent(cmd)
168+
}
169+
170+
func hasSameBashCompletionContent(cmd *cobra.Command) bool {
171+
bashCompletionFileInLocal, err := ioutil.ReadFile(completion.BashCompletionFilePath())
172+
if err != nil {
173+
print.Err(fmt.Errorf("can not read .bash_completion: %w", err))
174+
return false
175+
}
176+
177+
currentBashCompletion := new(bytes.Buffer)
178+
if err := cmd.GenBashCompletion(currentBashCompletion); err != nil {
179+
return false
180+
}
181+
if !strings.Contains(string(bashCompletionFileInLocal), currentBashCompletion.String()) {
182+
return false
183+
}
184+
return true
185+
}
186+
187+
func isSameFishCompletionFile(cmd *cobra.Command) bool {
188+
path := completion.FishCompletionFilePath()
189+
if !file.IsFile(path) {
190+
return false
191+
}
192+
193+
currentFishCompletion := new(bytes.Buffer)
194+
if err := cmd.GenFishCompletion(currentFishCompletion, false); err != nil {
195+
return false
196+
}
197+
198+
fishCompletionInLocal, err := ioutil.ReadFile(path)
199+
if err != nil {
200+
return false
201+
}
202+
203+
if bytes.Compare(currentFishCompletion.Bytes(), fishCompletionInLocal) != 0 {
204+
return false
205+
}
206+
return true
207+
}
208+
209+
func isSameZshCompletionFile(cmd *cobra.Command) bool {
210+
path := completion.ZshCompletionFilePath()
211+
if !file.IsFile(path) {
212+
return false
213+
}
214+
215+
currentZshCompletion := new(bytes.Buffer)
216+
if err := cmd.GenZshCompletion(currentZshCompletion); err != nil {
217+
return false
218+
}
219+
220+
zshCompletionInLocal, err := ioutil.ReadFile(path)
221+
if err != nil {
222+
return false
223+
}
224+
225+
if bytes.Compare(currentZshCompletion.Bytes(), zshCompletionInLocal) != 0 {
226+
return false
227+
}
228+
return true
229+
}

doc/ja/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,15 @@ $ ls /home/nao/.config/gup/gup.conf
107107
/home/nao/.config/gup/gup.conf
108108
$ gup import
109109
```
110+
111+
### シェル補完ファイルの自動生成 (bash, zsh, fish)
112+
gupコマンドは、bash、zsh、fish向けのシェル補完ファイルを自動生成します。ユーザーがgupを実行した後、シェル補完ファイルがシステムに存在しない場合は、自動生成処理を開始します。シェル補完を有効にするには、シェルを再起動してください。
113+
```
114+
$ gup
115+
gup:INFO : create bash-completion file: /home/nao/.bash_completion
116+
gup:INFO : create fish-completion file: /home/nao/.config/fish/completions/gup.fish
117+
gup:INFO : create zsh-completion file: /home/nao/.zsh/completion/_gup
118+
```
110119
# 連絡先
111120
開発者に対して「バグ報告」や「機能の追加要望」がある場合は、コメントをください。その際、以下の連絡先を使用してください。
112121
- [GitHub Issue](https://github.com/nao1215/gup/issues)

internal/cmdinfo/cmdinfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66

77
const (
88
name = "gup"
9-
version = "0.9.4"
9+
version = "0.10.0"
1010
)
1111

1212
// Version return gup command version.

internal/completion/completion.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package completion
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/nao1215/gup/internal/cmdinfo"
8+
)
9+
10+
// BashCompletionFilePath return bash-completion file path.
11+
func BashCompletionFilePath() string {
12+
return filepath.Join(os.Getenv("HOME"), ".bash_completion")
13+
}
14+
15+
// FishCompletionFilePath return fish-completion file path.
16+
func FishCompletionFilePath() string {
17+
return filepath.Join(os.Getenv("HOME"), ".config", "fish", "completions", cmdinfo.Name()+".fish")
18+
}
19+
20+
// ZshCompletionFilePath return zsh-completion file path.
21+
func ZshCompletionFilePath() string {
22+
return filepath.Join(os.Getenv("HOME"), ".zsh", "completion", "_"+cmdinfo.Name())
23+
}
24+
25+
// ZshrcPath return .zshrc path.
26+
func ZshrcPath() string {
27+
return filepath.Join(os.Getenv("HOME"), ".zshrc")
28+
}

0 commit comments

Comments
 (0)