@@ -72,15 +72,17 @@ static void init_add_i_state(struct add_i_state *s, struct repository *r)
7272struct prefix_item_list {
7373 struct string_list items ;
7474 struct string_list sorted ;
75+ int * selected ; /* for multi-selections */
7576 size_t min_length , max_length ;
7677};
7778#define PREFIX_ITEM_LIST_INIT \
78- { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, 1, 4 }
79+ { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, NULL, 1, 4 }
7980
8081static void prefix_item_list_clear (struct prefix_item_list * list )
8182{
8283 string_list_clear (& list -> items , 1 );
8384 string_list_clear (& list -> sorted , 0 );
85+ FREE_AND_NULL (list -> selected );
8486}
8587
8688static void extend_prefix_length (struct string_list_item * p ,
@@ -182,11 +184,12 @@ static ssize_t find_unique(const char *string, struct prefix_item_list *list)
182184struct list_options {
183185 int columns ;
184186 const char * header ;
185- void (* print_item )(int i , struct string_list_item * item , void * print_item_data );
187+ void (* print_item )(int i , int selected , struct string_list_item * item ,
188+ void * print_item_data );
186189 void * print_item_data ;
187190};
188191
189- static void list (struct add_i_state * s , struct string_list * list ,
192+ static void list (struct add_i_state * s , struct string_list * list , int * selected ,
190193 struct list_options * opts )
191194{
192195 int i , last_lf = 0 ;
@@ -199,7 +202,8 @@ static void list(struct add_i_state *s, struct string_list *list,
199202 "%s" , opts -> header );
200203
201204 for (i = 0 ; i < list -> nr ; i ++ ) {
202- opts -> print_item (i , list -> items + i , opts -> print_item_data );
205+ opts -> print_item (i , selected ? selected [i ] : 0 , list -> items + i ,
206+ opts -> print_item_data );
203207
204208 if ((opts -> columns ) && ((i + 1 ) % (opts -> columns ))) {
205209 putchar ('\t' );
@@ -218,14 +222,19 @@ struct list_and_choose_options {
218222 struct list_options list_opts ;
219223
220224 const char * prompt ;
225+ enum {
226+ SINGLETON = (1 <<0 ),
227+ IMMEDIATE = (1 <<1 ),
228+ } flags ;
221229 void (* print_help )(struct add_i_state * s );
222230};
223231
224232#define LIST_AND_CHOOSE_ERROR (-1)
225233#define LIST_AND_CHOOSE_QUIT (-2)
226234
227235/*
228- * Returns the selected index.
236+ * Returns the selected index in singleton mode, the number of selected items
237+ * otherwise.
229238 *
230239 * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
231240 * `LIST_AND_CHOOSE_QUIT` is returned.
@@ -234,8 +243,19 @@ static ssize_t list_and_choose(struct add_i_state *s,
234243 struct prefix_item_list * items ,
235244 struct list_and_choose_options * opts )
236245{
246+ int singleton = opts -> flags & SINGLETON ;
247+ int immediate = opts -> flags & IMMEDIATE ;
248+
237249 struct strbuf input = STRBUF_INIT ;
238- ssize_t res = LIST_AND_CHOOSE_ERROR ;
250+ ssize_t res = singleton ? LIST_AND_CHOOSE_ERROR : 0 ;
251+
252+ if (!singleton ) {
253+ free (items -> selected );
254+ CALLOC_ARRAY (items -> selected , items -> items .nr );
255+ }
256+
257+ if (singleton && !immediate )
258+ BUG ("singleton requires immediate" );
239259
240260 find_unique_prefixes (items );
241261
@@ -244,15 +264,16 @@ static ssize_t list_and_choose(struct add_i_state *s,
244264
245265 strbuf_reset (& input );
246266
247- list (s , & items -> items , & opts -> list_opts );
267+ list (s , & items -> items , items -> selected , & opts -> list_opts );
248268
249269 color_fprintf (stdout , s -> prompt_color , "%s" , opts -> prompt );
250- fputs (" > " , stdout );
270+ fputs (singleton ? "> " : "> > " , stdout );
251271 fflush (stdout );
252272
253273 if (strbuf_getline (& input , stdin ) == EOF ) {
254274 putchar ('\n' );
255- res = LIST_AND_CHOOSE_QUIT ;
275+ if (immediate )
276+ res = LIST_AND_CHOOSE_QUIT ;
256277 break ;
257278 }
258279 strbuf_trim (& input );
@@ -268,7 +289,9 @@ static ssize_t list_and_choose(struct add_i_state *s,
268289 p = input .buf ;
269290 for (;;) {
270291 size_t sep = strcspn (p , " \t\r\n," );
271- ssize_t index = -1 ;
292+ int choose = 1 ;
293+ /* `from` is inclusive, `to` is exclusive */
294+ ssize_t from = -1 , to = -1 ;
272295
273296 if (!sep ) {
274297 if (!* p )
@@ -277,30 +300,70 @@ static ssize_t list_and_choose(struct add_i_state *s,
277300 continue ;
278301 }
279302
280- if (isdigit (* p )) {
303+ /* Input that begins with '-'; de-select */
304+ if (* p == '-' ) {
305+ choose = 0 ;
306+ p ++ ;
307+ sep -- ;
308+ }
309+
310+ if (sep == 1 && * p == '*' ) {
311+ from = 0 ;
312+ to = items -> items .nr ;
313+ } else if (isdigit (* p )) {
281314 char * endp ;
282- index = strtoul (p , & endp , 10 ) - 1 ;
283- if (endp != p + sep )
284- index = -1 ;
315+ /*
316+ * A range can be specified like 5-7 or 5-.
317+ *
318+ * Note: `from` is 0-based while the user input
319+ * is 1-based, hence we have to decrement by
320+ * one. We do not have to decrement `to` even
321+ * if it is 0-based because it is an exclusive
322+ * boundary.
323+ */
324+ from = strtoul (p , & endp , 10 ) - 1 ;
325+ if (endp == p + sep )
326+ to = from + 1 ;
327+ else if (* endp == '-' ) {
328+ to = strtoul (++ endp , & endp , 10 );
329+ /* extra characters after the range? */
330+ if (endp != p + sep )
331+ from = -1 ;
332+ }
285333 }
286334
287335 if (p [sep ])
288336 p [sep ++ ] = '\0' ;
289- if (index < 0 )
290- index = find_unique (p , items );
337+ if (from < 0 ) {
338+ from = find_unique (p , items );
339+ if (from >= 0 )
340+ to = from + 1 ;
341+ }
291342
292- if (index < 0 || index >= items -> items .nr )
343+ if (from < 0 || from >= items -> items .nr ||
344+ (singleton && from + 1 != to )) {
293345 color_fprintf_ln (stdout , s -> error_color ,
294346 _ ("Huh (%s)?" ), p );
295- else {
296- res = index ;
347+ break ;
348+ } else if (singleton ) {
349+ res = from ;
297350 break ;
298351 }
299352
353+ if (to > items -> items .nr )
354+ to = items -> items .nr ;
355+
356+ for (; from < to ; from ++ )
357+ if (items -> selected [from ] != choose ) {
358+ items -> selected [from ] = choose ;
359+ res += choose ? +1 : -1 ;
360+ }
361+
300362 p += sep ;
301363 }
302364
303- if (res != LIST_AND_CHOOSE_ERROR )
365+ if ((immediate && res != LIST_AND_CHOOSE_ERROR ) ||
366+ !strcmp (input .buf , "*" ))
304367 break ;
305368 }
306369
@@ -500,7 +563,7 @@ struct print_file_item_data {
500563 struct strbuf buf , index , worktree ;
501564};
502565
503- static void print_file_item (int i , struct string_list_item * item ,
566+ static void print_file_item (int i , int selected , struct string_list_item * item ,
504567 void * print_file_item_data )
505568{
506569 struct file_item * c = item -> util ;
@@ -515,7 +578,7 @@ static void print_file_item(int i, struct string_list_item *item,
515578 strbuf_addf (& d -> buf , d -> modified_fmt ,
516579 d -> index .buf , d -> worktree .buf , item -> string );
517580
518- printf (" % 2d: %s" , i + 1 , d -> buf .buf );
581+ printf ("%c% 2d: %s" , selected ? '*' : ' ' , i + 1 , d -> buf .buf );
519582}
520583
521584static int run_status (struct add_i_state * s , const struct pathspec * ps ,
@@ -524,7 +587,7 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
524587 if (get_modified_files (s -> r , NO_FILTER , files , ps ) < 0 )
525588 return -1 ;
526589
527- list (s , files , opts );
590+ list (s , files , NULL , opts );
528591 putchar ('\n' );
529592
530593 return 0 ;
@@ -563,7 +626,8 @@ struct print_command_item_data {
563626 const char * color , * reset ;
564627};
565628
566- static void print_command_item (int i , struct string_list_item * item ,
629+ static void print_command_item (int i , int selected ,
630+ struct string_list_item * item ,
567631 void * print_command_item_data )
568632{
569633 struct print_command_item_data * d = print_command_item_data ;
@@ -596,7 +660,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
596660 struct print_command_item_data data = { "[" , "]" };
597661 struct list_and_choose_options main_loop_opts = {
598662 { 4 , N_ ("*** Commands ***" ), print_command_item , & data },
599- N_ ("What now" ), command_prompt_help
663+ N_ ("What now" ), SINGLETON | IMMEDIATE , command_prompt_help
600664 };
601665 struct {
602666 const char * string ;
0 commit comments