@@ -53,9 +53,10 @@ pub fn complete(
5353 let mut current_cmd = & * cmd;
5454 let mut pos_index = 1 ;
5555 let mut is_escaped = false ;
56+ let mut state = ParseState :: ValueDone ;
5657 while let Some ( arg) = raw_args. next ( & mut cursor) {
5758 if cursor == target_cursor {
58- return complete_arg ( & arg, current_cmd, current_dir, pos_index, is_escaped ) ;
59+ return complete_arg ( & arg, current_cmd, current_dir, pos_index, state ) ;
5960 }
6061
6162 debug ! ( "complete::next: Begin parsing '{:?}'" , arg. to_value_os( ) , ) ;
@@ -64,18 +65,103 @@ pub fn complete(
6465 if let Some ( next_cmd) = current_cmd. find_subcommand ( value) {
6566 current_cmd = next_cmd;
6667 pos_index = 1 ;
68+ state = ParseState :: ValueDone ;
6769 continue ;
6870 }
6971 }
7072
7173 if is_escaped {
7274 pos_index += 1 ;
75+ state = ParseState :: Pos ( pos_index) ;
7376 } else if arg. is_escape ( ) {
7477 is_escaped = true ;
75- } else if let Some ( _long) = arg. to_long ( ) {
76- } else if let Some ( _short) = arg. to_short ( ) {
78+ state = ParseState :: ValueDone ;
79+ } else if let Some ( ( flag, value) ) = arg. to_long ( ) {
80+ if let Ok ( flag) = flag {
81+ let opt = current_cmd. get_arguments ( ) . find ( |a| {
82+ let longs = a. get_long_and_visible_aliases ( ) ;
83+ let is_find = longs. map ( |v| {
84+ let mut iter = v. into_iter ( ) ;
85+ let s = iter. find ( |s| * s == flag) ;
86+ s. is_some ( )
87+ } ) ;
88+ is_find. unwrap_or ( false )
89+ } ) ;
90+ state = match opt. map ( |o| o. get_action ( ) ) {
91+ Some ( clap:: ArgAction :: Set ) | Some ( clap:: ArgAction :: Append ) => {
92+ if value. is_some ( ) {
93+ ParseState :: ValueDone
94+ } else {
95+ ParseState :: Opt ( opt. unwrap ( ) . clone ( ) )
96+ }
97+ }
98+ Some ( clap:: ArgAction :: SetTrue ) | Some ( clap:: ArgAction :: SetFalse ) => {
99+ ParseState :: ValueDone
100+ }
101+ Some ( clap:: ArgAction :: Count ) => ParseState :: ValueDone ,
102+ Some ( clap:: ArgAction :: Version ) => ParseState :: ValueDone ,
103+ Some ( clap:: ArgAction :: Help )
104+ | Some ( clap:: ArgAction :: HelpLong )
105+ | Some ( clap:: ArgAction :: HelpShort ) => ParseState :: ValueDone ,
106+ Some ( _) => ParseState :: ValueDone ,
107+ None => ParseState :: ValueDone ,
108+ } ;
109+ } else {
110+ state = ParseState :: ValueDone ;
111+ }
112+ } else if let Some ( mut short) = arg. to_short ( ) {
113+ let mut takes_value = false ;
114+ loop {
115+ if let Some ( Ok ( opt) ) = short. next_flag ( ) {
116+ let opt = current_cmd. get_arguments ( ) . find ( |a| {
117+ let shorts = a. get_short_and_visible_aliases ( ) ;
118+ let is_find = shorts. map ( |v| {
119+ let mut iter = v. into_iter ( ) ;
120+ let c = iter. find ( |c| * c == opt) ;
121+ c. is_some ( )
122+ } ) ;
123+ is_find. unwrap_or ( false )
124+ } ) ;
125+
126+ state = match opt. map ( |o| o. get_action ( ) ) {
127+ Some ( clap:: ArgAction :: Set ) | Some ( clap:: ArgAction :: Append ) => {
128+ takes_value = true ;
129+ if short. next_value_os ( ) . is_some ( ) {
130+ ParseState :: ValueDone
131+ } else {
132+ ParseState :: Opt ( opt. unwrap ( ) . clone ( ) )
133+ }
134+ }
135+ Some ( clap:: ArgAction :: SetTrue ) | Some ( clap:: ArgAction :: SetFalse ) => {
136+ ParseState :: ValueDone
137+ }
138+ Some ( clap:: ArgAction :: Count ) => ParseState :: ValueDone ,
139+ Some ( clap:: ArgAction :: Version ) => ParseState :: ValueDone ,
140+ Some ( clap:: ArgAction :: Help )
141+ | Some ( clap:: ArgAction :: HelpShort )
142+ | Some ( clap:: ArgAction :: HelpLong ) => ParseState :: ValueDone ,
143+ Some ( _) => ParseState :: ValueDone ,
144+ None => ParseState :: ValueDone ,
145+ } ;
146+
147+ if takes_value {
148+ break ;
149+ }
150+ } else {
151+ state = ParseState :: ValueDone ;
152+ break ;
153+ }
154+ }
77155 } else {
78- pos_index += 1 ;
156+ match state {
157+ ParseState :: ValueDone | ParseState :: Pos ( _) => {
158+ pos_index += 1 ;
159+ state = ParseState :: ValueDone ;
160+ }
161+ ParseState :: Opt ( _) => {
162+ state = ParseState :: ValueDone ;
163+ }
164+ }
79165 }
80166 }
81167
@@ -85,96 +171,123 @@ pub fn complete(
85171 ) )
86172}
87173
174+ #[ derive( Debug , PartialEq , Eq , Clone ) ]
175+ enum ParseState {
176+ /// Parsing a value done, there is no state to record.
177+ ValueDone ,
178+
179+ /// Parsing a positional argument after `--`
180+ Pos ( usize ) ,
181+
182+ /// Parsing a optional flag argument
183+ Opt ( clap:: Arg ) ,
184+ }
185+
88186fn complete_arg (
89187 arg : & clap_lex:: ParsedArg < ' _ > ,
90188 cmd : & clap:: Command ,
91189 current_dir : Option < & std:: path:: Path > ,
92190 pos_index : usize ,
93- is_escaped : bool ,
191+ state : ParseState ,
94192) -> Result < Vec < CompletionCandidate > , std:: io:: Error > {
95193 debug ! (
96- "complete_arg: arg={:?}, cmd={:?}, current_dir={:?}, pos_index={}, is_escaped={ }" ,
194+ "complete_arg: arg={:?}, cmd={:?}, current_dir={:?}, pos_index={:? }, state={:? }" ,
97195 arg,
98196 cmd. get_name( ) ,
99197 current_dir,
100198 pos_index,
101- is_escaped
199+ state
102200 ) ;
103201 let mut completions = Vec :: < CompletionCandidate > :: new ( ) ;
104202
105- if !is_escaped {
106- if let Some ( ( flag, value) ) = arg. to_long ( ) {
107- if let Ok ( flag) = flag {
108- if let Some ( value) = value {
109- if let Some ( arg) = cmd. get_arguments ( ) . find ( |a| a. get_long ( ) == Some ( flag) ) {
110- completions. extend (
111- complete_arg_value ( value. to_str ( ) . ok_or ( value) , arg, current_dir)
112- . into_iter ( )
113- . map ( |comp| {
114- CompletionCandidate :: new ( format ! (
115- "--{}={}" ,
116- flag,
117- comp. get_content( ) . to_string_lossy( )
118- ) )
119- . help ( comp. get_help ( ) . cloned ( ) )
120- . visible ( comp. is_visible ( ) )
121- } ) ,
122- ) ;
203+ match state {
204+ ParseState :: ValueDone => {
205+ if let Some ( ( flag, value) ) = arg. to_long ( ) {
206+ if let Ok ( flag) = flag {
207+ if let Some ( value) = value {
208+ if let Some ( arg) = cmd. get_arguments ( ) . find ( |a| a. get_long ( ) == Some ( flag) )
209+ {
210+ completions. extend (
211+ complete_arg_value ( value. to_str ( ) . ok_or ( value) , arg, current_dir)
212+ . into_iter ( )
213+ . map ( |comp| {
214+ CompletionCandidate :: new ( format ! (
215+ "--{}={}" ,
216+ flag,
217+ comp. get_content( ) . to_string_lossy( )
218+ ) )
219+ . help ( comp. get_help ( ) . cloned ( ) )
220+ . visible ( comp. is_visible ( ) )
221+ } ) ,
222+ ) ;
223+ }
224+ } else {
225+ completions. extend ( longs_and_visible_aliases ( cmd) . into_iter ( ) . filter (
226+ |comp| {
227+ comp. get_content ( )
228+ . starts_with ( format ! ( "--{}" , flag) . as_str ( ) )
229+ } ,
230+ ) ) ;
231+
232+ completions. extend ( hidden_longs_aliases ( cmd) . into_iter ( ) . filter ( |comp| {
233+ comp. get_content ( )
234+ . starts_with ( format ! ( "--{}" , flag) . as_str ( ) )
235+ } ) )
123236 }
124- } else {
125- completions. extend ( longs_and_visible_aliases ( cmd) . into_iter ( ) . filter ( |comp| {
126- comp. get_content ( )
127- . starts_with ( format ! ( "--{}" , flag) . as_str ( ) )
128- } ) ) ;
129-
130- completions. extend ( hidden_longs_aliases ( cmd) . into_iter ( ) . filter ( |comp| {
131- comp. get_content ( )
132- . starts_with ( format ! ( "--{}" , flag) . as_str ( ) )
133- } ) )
134237 }
135- }
136- } else if arg. is_escape ( ) || arg. is_stdio ( ) || arg. is_empty ( ) {
137- // HACK: Assuming knowledge of is_escape / is_stdio
138- completions. extend ( longs_and_visible_aliases ( cmd) ) ;
238+ } else if arg. is_escape ( ) || arg. is_stdio ( ) || arg. is_empty ( ) {
239+ // HACK: Assuming knowledge of is_escape / is_stdio
240+ completions. extend ( longs_and_visible_aliases ( cmd) ) ;
139241
140- completions. extend ( hidden_longs_aliases ( cmd) ) ;
141- }
242+ completions. extend ( hidden_longs_aliases ( cmd) ) ;
243+ }
142244
143- if arg. is_empty ( ) || arg. is_stdio ( ) || arg. is_short ( ) {
144- let dash_or_arg = if arg. is_empty ( ) {
145- "-" . into ( )
146- } else {
147- arg. to_value_os ( ) . to_string_lossy ( )
148- } ;
149- // HACK: Assuming knowledge of is_stdio
150- completions. extend (
151- shorts_and_visible_aliases ( cmd)
152- . into_iter ( )
153- // HACK: Need better `OsStr` manipulation
154- . map ( |comp| {
155- CompletionCandidate :: new ( format ! (
156- "{}{}" ,
157- dash_or_arg,
158- comp. get_content( ) . to_string_lossy( )
159- ) )
160- . help ( comp. get_help ( ) . cloned ( ) )
161- . visible ( true )
162- } ) ,
163- ) ;
164- }
165- }
245+ if arg. is_empty ( ) || arg. is_stdio ( ) || arg. is_short ( ) {
246+ let dash_or_arg = if arg. is_empty ( ) {
247+ "-" . into ( )
248+ } else {
249+ arg. to_value_os ( ) . to_string_lossy ( )
250+ } ;
251+ // HACK: Assuming knowledge of is_stdio
252+ completions. extend (
253+ shorts_and_visible_aliases ( cmd)
254+ . into_iter ( )
255+ // HACK: Need better `OsStr` manipulation
256+ . map ( |comp| {
257+ CompletionCandidate :: new ( format ! (
258+ "{}{}" ,
259+ dash_or_arg,
260+ comp. get_content( ) . to_string_lossy( )
261+ ) )
262+ . help ( comp. get_help ( ) . cloned ( ) )
263+ . visible ( true )
264+ } ) ,
265+ ) ;
266+ }
166267
167- if let Some ( positional) = cmd
168- . get_positionals ( )
169- . find ( |p| p. get_index ( ) == Some ( pos_index) )
170- {
171- completions. extend ( complete_arg_value ( arg. to_value ( ) , positional, current_dir) . into_iter ( ) ) ;
172- }
268+ if let Some ( positional) = cmd
269+ . get_positionals ( )
270+ . find ( |p| p. get_index ( ) == Some ( pos_index) )
271+ {
272+ completions. extend ( complete_arg_value ( arg. to_value ( ) , positional, current_dir) ) ;
273+ }
173274
174- if let Ok ( value) = arg. to_value ( ) {
175- completions. extend ( complete_subcommand ( value, cmd) ) ;
275+ if let Ok ( value) = arg. to_value ( ) {
276+ completions. extend ( complete_subcommand ( value, cmd) ) ;
277+ }
278+ }
279+ ParseState :: Pos ( _) => {
280+ if let Some ( positional) = cmd
281+ . get_positionals ( )
282+ . find ( |p| p. get_index ( ) == Some ( pos_index) )
283+ {
284+ completions. extend ( complete_arg_value ( arg. to_value ( ) , positional, current_dir) ) ;
285+ }
286+ }
287+ ParseState :: Opt ( opt) => {
288+ completions. extend ( complete_arg_value ( arg. to_value ( ) , & opt, current_dir) ) ;
289+ }
176290 }
177-
178291 if completions. iter ( ) . any ( |a| a. is_visible ( ) ) {
179292 completions. retain ( |a| a. is_visible ( ) )
180293 }
0 commit comments