• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

essentialkaos / ek / 24389768914

14 Apr 2026 08:46AM UTC coverage: 99.307% (-0.07%) from 99.372%
24389768914

push

github

web-flow
Merge pull request #645 from essentialkaos/develop

Version 14.0.0

2148 of 2175 new or added lines in 105 files covered. (98.76%)

11 existing lines in 5 files now uncovered.

15196 of 15302 relevant lines covered (99.31%)

98.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.67
/usage/completion/bash/bash.go
1
// Package bash provides methods for generating bash completion
2
package bash
3

4
// ////////////////////////////////////////////////////////////////////////////////// //
5
//                                                                                    //
6
//                         Copyright (c) 2026 ESSENTIAL KAOS                          //
7
//      Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0>     //
8
//                                                                                    //
9
// ////////////////////////////////////////////////////////////////////////////////// //
10

11
import (
12
        "bytes"
13
        "fmt"
14
        "strings"
15

16
        "github.com/essentialkaos/ek/v14/usage"
17
)
18

19
// ////////////////////////////////////////////////////////////////////////////////// //
20

21
// _BASH_TEMPLATE is template used for completion generation
22
const _BASH_TEMPLATE = `# Completion for {{COMPNAME}}
23
# This completion is automatically generated
24

25
_{{COMPNAME_SAFE}}() {
26
  local cur prev cmds opts show_files
27

28
  COMPREPLY=()
29
  cur="${COMP_WORDS[COMP_CWORD]}"
30
  prev="${COMP_WORDS[COMP_CWORD-1]}"
31

32
  cmds="{{COMMANDS}}"
33
  opts="{{GLOBAL_OPTIONS}}"
34
  show_files="{{SHOW_FILES}}"
35
  file_glob="{{FILE_GLOB}}"
36

37
{{COMMANDS_HANDLERS}}
38

39
  if [[ $cur == -* ]] ; then
40
    COMPREPLY=($(compgen -W "$opts" -- "$cur"))
41
    return 0
42
  fi
43

44
  _{{COMPNAME_SAFE}}_filter "$cmds" "$opts" "$show_files" "$file_glob"
45
}
46

47
_{{COMPNAME_SAFE}}_filter() {
48
  local cmds="$1"
49
  local opts="$2"
50
  local show_files="$3"
51
  local file_glob="$4"
52

53
  local cmd1 cmd2
54

55
  for cmd1 in $cmds ; do
56
    for cmd2 in ${COMP_WORDS[*]} ; do
57
      if [[ "$cmd1" == "$cmd2" ]] ; then
58
        if [[ -z "$show_files" ]] ; then
59
          COMPREPLY=($(compgen -W "$opts" -- "$cur"))
60
        else
61
          _filedir "$file_glob"
62
        fi
63

64
        return 0
65
      fi
66
    done
67
  done
68

69
  if [[ -z "$show_files" ]] ; then
70
    COMPREPLY=($(compgen -W "$cmds" -- "$cur"))
71
    return 0
72
  fi
73

74
  _filedir "$file_glob"
75
}
76

77
complete -F _{{COMPNAME_SAFE}} {{COMPNAME}} {{COMP_OPTS}}
78
`
79

80
// ////////////////////////////////////////////////////////////////////////////////// //
81

82
// Generate generates Bash completion code
83
func Generate(info *usage.Info, name string, fileExt ...string) string {
8✔
84
        result := _BASH_TEMPLATE
8✔
85

8✔
86
        result = strings.ReplaceAll(result, "{{COMMANDS}}", genCommandsList(info))
8✔
87
        result = strings.ReplaceAll(result, "{{GLOBAL_OPTIONS}}", genGlobalOptionsList(info))
8✔
88
        result = strings.ReplaceAll(result, "{{COMMANDS_HANDLERS}}", genCommandsHandlers(info))
8✔
89
        result = strings.ReplaceAll(result, "{{COMPNAME}}", name)
8✔
90

8✔
91
        if len(info.Args) != 0 {
12✔
92
                result = strings.ReplaceAll(result, "{{SHOW_FILES}}", "true")
4✔
93
                result = strings.ReplaceAll(result, "{{COMP_OPTS}}", "-o filenames")
4✔
94
                if len(fileExt) != 0 {
8✔
95
                        result = strings.ReplaceAll(result, "{{FILE_GLOB}}", fileExt[0])
4✔
96
                } else {
4✔
NEW
97
                        result = strings.ReplaceAll(result, "{{FILE_GLOB}}", "")
×
UNCOV
98
                }
×
99
        } else {
4✔
100
                result = strings.ReplaceAll(result, "{{SHOW_FILES}}", "")
4✔
101
                result = strings.ReplaceAll(result, "{{COMP_OPTS}}", "")
4✔
102
                result = strings.ReplaceAll(result, "{{FILE_GLOB}}", "")
4✔
103
        }
4✔
104

105
        nameSafe := strings.ReplaceAll(name, "-", "_")
8✔
106
        result = strings.ReplaceAll(result, "{{COMPNAME_SAFE}}", nameSafe)
8✔
107

8✔
108
        return result
8✔
109
}
110

111
// ////////////////////////////////////////////////////////////////////////////////// //
112

113
// genGlobalOptionsList generates list with global options
114
func genGlobalOptionsList(info *usage.Info) string {
8✔
115
        var result []string
8✔
116

8✔
117
        nonGlobalOptions := make(map[string]bool)
8✔
118

8✔
119
        for _, cmd := range info.Commands {
24✔
120
                for _, opt := range cmd.BoundOptions {
40✔
121
                        nonGlobalOptions[opt] = true
24✔
122
                }
24✔
123
        }
124

125
        for _, opt := range info.Options {
48✔
126
                if nonGlobalOptions[opt.Long] {
56✔
127
                        continue
16✔
128
                }
129

130
                result = append(result, "--"+opt.Long)
24✔
131
        }
132

133
        return strings.Join(result, " ")
8✔
134
}
135

136
// genCommandsHandlers generates command handler
137
func genCommandsHandlers(info *usage.Info) string {
12✔
138
        if !isCommandHandlersRequired(info) {
16✔
139
                return ""
4✔
140
        }
4✔
141

142
        var buf bytes.Buffer
8✔
143

8✔
144
        buf.WriteString("  case $prev in\n")
8✔
145

8✔
146
        for _, cmd := range info.Commands {
24✔
147
                if len(cmd.BoundOptions) != 0 {
24✔
148
                        buf.WriteString(genCommandHandler(cmd, info))
8✔
149
                }
8✔
150
        }
151

152
        buf.WriteString("  esac")
8✔
153

8✔
154
        return buf.String()
8✔
155
}
156

157
// genCommandHandler generates handler for given command
158
func genCommandHandler(cmd *usage.Command, info *usage.Info) string {
8✔
159
        var buf bytes.Buffer
8✔
160

8✔
161
        fmt.Fprintf(&buf, "    %s)\n", cmd.Name)
8✔
162

8✔
163
        var options []string
8✔
164

8✔
165
        for _, optName := range cmd.BoundOptions {
32✔
166
                opt := info.GetOption(optName)
24✔
167

24✔
168
                if opt == nil {
32✔
169
                        continue
8✔
170
                }
171

172
                options = append(options, "--"+opt.Long)
16✔
173
        }
174

175
        fmt.Fprintf(&buf, "      opts=\"%s\"\n", strings.Join(options, " "))
8✔
176
        buf.WriteString("      COMPREPLY=($(compgen -W \"$opts\" -- \"$cur\"))\n")
8✔
177
        buf.WriteString("      return 0\n")
8✔
178
        buf.WriteString("      ;;\n\n")
8✔
179

8✔
180
        return buf.String()
8✔
181
}
182

183
// getCommandsList returns slice with available commands
184
func genCommandsList(info *usage.Info) string {
8✔
185
        var result []string
8✔
186

8✔
187
        for _, command := range info.Commands {
24✔
188
                result = append(result, command.Name)
16✔
189
        }
16✔
190

191
        return strings.Join(result, " ")
8✔
192
}
193

194
// isCommandHandlersRequired returns true if commands have bound options
195
func isCommandHandlersRequired(info *usage.Info) bool {
16✔
196
        for _, cmd := range info.Commands {
32✔
197
                if len(cmd.BoundOptions) != 0 {
24✔
198
                        return true
8✔
199
                }
8✔
200
        }
201

202
        return false
8✔
203
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc