9
9
//
10
10
//===----------------------------------------------------------------------===//
11
11
12
- extension [ ParsableCommand . Type ] {
13
- /// Generates a Zsh completion script for the given command.
12
+ #if swift(>=6.0)
13
+ internal import ArgumentParserToolInfo
14
+ #else
15
+ import ArgumentParserToolInfo
16
+ #endif
17
+
18
+ extension ToolInfoV0 {
14
19
var zshCompletionScript : String {
20
+ command. zshCompletionScript
21
+ }
22
+ }
23
+
24
+ extension CommandInfoV0 {
25
+ fileprivate var zshCompletionScript : String {
15
26
// swift-format-ignore: NeverForceUnwrap
16
27
// Preconditions:
17
28
// - first must be non-empty for a zsh completion script to be of use.
18
29
// - first is guaranteed non-empty in the one place where this computed var is used.
19
- let commandName = first!. _commandName
20
- return """
21
- #compdef \( commandName)
30
+ """
31
+ #compdef \( commandName)
22
32
23
- \( completeFunctionName) () {
24
- local -ar non_empty_completions=( " ${@:#(|:*)} " )
25
- local -ar empty_completions=( " ${(M)@:#(|:*)} " )
26
- _describe '' non_empty_completions -- empty_completions -P $' \\ ' \\ ''
27
- }
33
+ \( completeFunctionName) () {
34
+ local -ar non_empty_completions=( " ${@:#(|:*)} " )
35
+ local -ar empty_completions=( " ${(M)@:#(|:*)} " )
36
+ _describe '' non_empty_completions -- empty_completions -P $' \\ ' \\ ''
37
+ }
28
38
29
- \( customCompleteFunctionName) () {
30
- local -a completions
31
- completions=( " ${(@f) " $( " ${command_name} " " ${@} " " ${command_line[@]} " ) " } " )
32
- if [[ " ${#completions[@]} " -gt 1 ]]; then
33
- \( completeFunctionName) " ${completions[@]:0:-1} "
34
- fi
35
- }
39
+ \( customCompleteFunctionName) () {
40
+ local -a completions
41
+ completions=( " ${(@f) " $( " ${command_name} " " ${@} " " ${command_line[@]} " ) " } " )
42
+ if [[ " ${#completions[@]} " -gt 1 ]]; then
43
+ \( completeFunctionName) " ${completions[@]:0:-1} "
44
+ fi
45
+ }
36
46
37
- \( cursorIndexInCurrentWordFunctionName) () {
38
- if [[ -z " ${QIPREFIX}${IPREFIX}${PREFIX} " ]]; then
39
- printf 0
40
- else
41
- printf %s " ${#${(z)LBUFFER}[-1]} "
42
- fi
43
- }
47
+ \( cursorIndexInCurrentWordFunctionName) () {
48
+ if [[ -z " ${QIPREFIX}${IPREFIX}${PREFIX} " ]]; then
49
+ printf 0
50
+ else
51
+ printf %s " ${#${(z)LBUFFER}[-1]} "
52
+ fi
53
+ }
44
54
45
- \( completionFunctions) \
46
- \( completionFunctionName ( ) )
47
- """
55
+ \( completionFunctions) \
56
+ \( completionFunctionName)
57
+ """
48
58
}
49
59
50
60
private var completionFunctions : String {
51
- guard let type = last else { return " " }
52
- let functionName = completionFunctionName ( )
53
- let isRootCommand = count == 1
61
+ let functionName = completionFunctionName
54
62
55
- let argumentSpecsAndSetupScripts = argumentsForHelp ( visibility: . default)
56
- . compactMap { argumentSpecAndSetupScript ( $0) }
63
+ let argumentSpecsAndSetupScripts = ( arguments ?? [ ] ) . compactMap {
64
+ argumentSpecAndSetupScript ( $0)
65
+ }
57
66
var argumentSpecs = argumentSpecsAndSetupScripts. map ( \. argumentSpec)
58
67
let setupScripts = argumentSpecsAndSetupScripts. compactMap ( \. setupScript)
59
68
60
- var subcommands = type. configuration. subcommands
61
- . filter { $0. configuration. shouldDisplay }
69
+ let subcommands = ( subcommands ?? [ ] ) . filter ( \. shouldDisplay)
62
70
63
71
let subcommandHandler : String
64
72
if subcommands. isEmpty {
@@ -67,17 +75,13 @@ extension [ParsableCommand.Type] {
67
75
argumentSpecs. append ( " '(-): :->command' " )
68
76
argumentSpecs. append ( " '(-)*:: :->arg' " )
69
77
70
- if isRootCommand {
71
- subcommands. addHelpSubcommandIfMissing ( )
72
- }
73
-
74
78
subcommandHandler = """
75
79
case " ${state} " in
76
80
command)
77
81
local -ar subcommands=(
78
82
\(
79
83
subcommands. map { """
80
- ' \( $0. _commandName ) : \( $0. configuration . abstract. zshEscapeForSingleQuotedExplanation ( ) ) '
84
+ ' \( $0. commandName ) : \( $0. abstract? . zshEscapeForSingleQuotedExplanation ( ) ?? " " ) '
81
85
"""
82
86
}
83
87
. joined ( separator: " \n " )
@@ -87,7 +91,7 @@ extension [ParsableCommand.Type] {
87
91
;;
88
92
arg)
89
93
case " ${words[1]} " in
90
- \( subcommands. map { $0 . _commandName } . joined ( separator: " | " ) ) )
94
+ \( subcommands. map ( \ . commandName ) . joined ( separator: " | " ) ) )
91
95
" \( functionName) _${words[1]} "
92
96
;;
93
97
esac
@@ -99,7 +103,7 @@ extension [ParsableCommand.Type] {
99
103
100
104
return """
101
105
\( functionName) () {
102
- \( isRootCommand
106
+ \( ( superCommands ?? [ ] ) . isEmpty
103
107
? """
104
108
emulate -RL zsh -G
105
109
setopt extendedglob nullglob numericglobsort
@@ -131,47 +135,50 @@ extension [ParsableCommand.Type] {
131
135
return " ${ret} "
132
136
}
133
137
134
- \( subcommands. map { ( self + [ $0 ] ) . completionFunctions } . joined ( ) )
138
+ \( subcommands. map ( \ . completionFunctions) . joined ( ) )
135
139
"""
136
140
}
137
141
138
142
private func argumentSpecAndSetupScript(
139
- _ arg: ArgumentDefinition
143
+ _ arg: ArgumentInfoV0
140
144
) -> ( argumentSpec: String , setupScript: String ? ) ? {
141
- guard arg. help . visibility . base == . default else { return nil }
145
+ guard arg. shouldDisplay else { return nil }
142
146
143
147
let line : String
144
- switch arg. names. count {
148
+ let names = arg. names ?? [ ]
149
+ switch names. count {
145
150
case 0 :
146
151
line = " "
147
152
case 1 :
153
+ // swift-format-ignore: NeverForceUnwrap
154
+ // Preconditions: names has exactly one element.
148
155
line = """
149
- \( arg. isRepeatableOption ? " * " : " " ) \( arg . names [ 0 ] . synopsisString ) \( arg. zshCompletionAbstract )
156
+ \( arg. isRepeatableOption ? " * " : " " ) \( names. first! . commonCompletionSynopsisString ( ) ) \( arg. completionAbstract )
150
157
"""
151
158
default :
152
- let synopses = arg . names. map { $0. synopsisString }
159
+ let synopses = names. map { $0. commonCompletionSynopsisString ( ) }
153
160
line = """
154
161
\( arg. isRepeatableOption ? " * " : " ( \( synopses. joined ( separator: " " ) ) ) " ) ' \
155
162
{ \( synopses. joined ( separator: " , " ) ) } \
156
- ' \( arg. zshCompletionAbstract )
163
+ ' \( arg. completionAbstract )
157
164
"""
158
165
}
159
166
160
- switch arg. update {
161
- case . unary :
167
+ switch arg. kind {
168
+ case . option , . positional :
162
169
let ( argumentAction, setupScript) = argumentActionAndSetupScript ( arg)
163
- return ( " ' \( line) : \( arg. valueName) : \( argumentAction) ' " , setupScript)
164
- case . nullary :
170
+ return ( " ' \( line) : \( arg. valueName ?? " " ) : \( argumentAction) ' " , setupScript)
171
+ case . flag :
165
172
return ( " ' \( line) ' " , nil )
166
173
}
167
174
}
168
175
169
176
/// Returns the zsh "action" for an argument completion string.
170
177
private func argumentActionAndSetupScript(
171
- _ arg: ArgumentDefinition
178
+ _ arg: ArgumentInfoV0
172
179
) -> ( argumentAction: String , setupScript: String ? ) {
173
- switch arg. completion . kind {
174
- case . default :
180
+ switch arg. completionKind {
181
+ case . none :
175
182
return ( " " , nil )
176
183
177
184
case . file( let extensions) :
@@ -201,41 +208,37 @@ extension [ParsableCommand.Type] {
201
208
202
209
case . custom:
203
210
return (
204
- " { \( customCompleteFunctionName) \( arg. customCompletionCall ( self ) ) \" ${current_word_index} \" \" $( \( cursorIndexInCurrentWordFunctionName) ) \" } " ,
211
+ " { \( customCompleteFunctionName) \( arg. commonCustomCompletionCall ( command : self ) ) \" ${current_word_index} \" \" $( \( cursorIndexInCurrentWordFunctionName) ) \" } " ,
205
212
nil
206
213
)
207
214
208
215
case . customDeprecated:
209
216
return (
210
- " { \( customCompleteFunctionName) \( arg. customCompletionCall ( self ) ) } " ,
217
+ " { \( customCompleteFunctionName) \( arg. commonCustomCompletionCall ( command : self ) ) } " ,
211
218
nil
212
219
)
213
220
}
214
221
}
215
222
216
- private func variableName( _ arg: ArgumentDefinition ) -> String {
217
- guard let argName = arg. names . preferredName else {
223
+ private func variableName( _ arg: ArgumentInfoV0 ) -> String {
224
+ guard let argName = arg. preferredName else {
218
225
return
219
- " \( shellVariableNamePrefix) _ \( arg. valueName. shellEscapeForVariableName ( ) ) "
226
+ " \( shellVariableNamePrefix) _ \( arg. valueName? . shellEscapeForVariableName ( ) ?? " " ) "
220
227
}
221
228
return
222
- " \( argName. case == . long ? " __ " : " _ " ) \( shellVariableNamePrefix) _ \( argName. valueString . shellEscapeForVariableName ( ) ) "
229
+ " \( argName. kind == . long ? " __ " : " _ " ) \( shellVariableNamePrefix) _ \( argName. name . shellEscapeForVariableName ( ) ) "
223
230
}
224
231
225
232
private var completeFunctionName : String {
226
- // swift-format-ignore: NeverForceUnwrap
227
- // Precondition: first is guaranteed to be non-empty
228
- " __ \( first!. _commandName) _complete "
233
+ " \( completionFunctionPrefix) _complete "
229
234
}
230
235
231
236
private var customCompleteFunctionName : String {
232
- // swift-format-ignore: NeverForceUnwrap
233
- // Precondition: first is guaranteed to be non-empty
234
- " __ \( first!. _commandName) _custom_complete "
237
+ " \( completionFunctionPrefix) _custom_complete "
235
238
}
236
239
237
240
private var cursorIndexInCurrentWordFunctionName : String {
238
- " __ \( first ? . _commandName ?? " " ) _cursor_index_in_current_word "
241
+ " \( completionFunctionPrefix ) _cursor_index_in_current_word "
239
242
}
240
243
}
241
244
@@ -250,13 +253,13 @@ extension String {
250
253
}
251
254
}
252
255
253
- extension ArgumentDefinition {
256
+ extension ArgumentInfoV0 {
254
257
/// - returns: `true` if `self` is an option and can be tab-completed multiple times in one command line.
255
258
/// For example, `ssh` allows the `-L` option to be given multiple times, to establish multiple port forwardings.
256
259
fileprivate var isRepeatableOption : Bool {
257
260
guard
258
- case . named ( _ ) = kind,
259
- help . options . contains ( . isRepeating)
261
+ [ . flag , . option ] . contains ( kind) ,
262
+ isRepeating
260
263
else { return false }
261
264
262
265
switch parsingStrategy {
@@ -265,8 +268,8 @@ extension ArgumentDefinition {
265
268
}
266
269
}
267
270
268
- fileprivate var zshCompletionAbstract : String {
269
- guard !help . abstract. isEmpty else { return " " }
270
- return " [ \( help . abstract. zshEscapeForSingleQuotedExplanation ( ) ) ] "
271
+ fileprivate var completionAbstract : String {
272
+ guard let abstract , ! abstract. isEmpty else { return " " }
273
+ return " [ \( abstract. zshEscapeForSingleQuotedExplanation ( ) ) ] "
271
274
}
272
275
}
0 commit comments