@@ -45,6 +45,132 @@ static void init_add_i_state(struct add_i_state *s, struct repository *r)
45
45
init_color (r , s , "header" , s -> header_color , GIT_COLOR_BOLD );
46
46
}
47
47
48
+ /*
49
+ * A "prefix item list" is a list of items that are identified by a string, and
50
+ * a unique prefix (if any) is determined for each item.
51
+ *
52
+ * It is implemented in the form of a pair of `string_list`s, the first one
53
+ * duplicating the strings, with the `util` field pointing at a structure whose
54
+ * first field must be `size_t prefix_length`.
55
+ *
56
+ * That `prefix_length` field will be computed by `find_unique_prefixes()`; It
57
+ * will be set to zero if no valid, unique prefix could be found.
58
+ *
59
+ * The second `string_list` is called `sorted` and does _not_ duplicate the
60
+ * strings but simply reuses the first one's, with the `util` field pointing at
61
+ * the `string_item_list` of the first `string_list`. It will be populated and
62
+ * sorted by `find_unique_prefixes()`.
63
+ */
64
+ struct prefix_item_list {
65
+ struct string_list items ;
66
+ struct string_list sorted ;
67
+ size_t min_length , max_length ;
68
+ };
69
+ #define PREFIX_ITEM_LIST_INIT \
70
+ { STRING_LIST_INIT_DUP, STRING_LIST_INIT_NODUP, 1, 4 }
71
+
72
+ static void prefix_item_list_clear (struct prefix_item_list * list )
73
+ {
74
+ string_list_clear (& list -> items , 1 );
75
+ string_list_clear (& list -> sorted , 0 );
76
+ }
77
+
78
+ static void extend_prefix_length (struct string_list_item * p ,
79
+ const char * other_string , size_t max_length )
80
+ {
81
+ size_t * len = p -> util ;
82
+
83
+ if (!* len || memcmp (p -> string , other_string , * len ))
84
+ return ;
85
+
86
+ for (;;) {
87
+ char c = p -> string [* len ];
88
+
89
+ /*
90
+ * Is `p` a strict prefix of `other`? Or have we exhausted the
91
+ * maximal length of the prefix? Or is the current character a
92
+ * multi-byte UTF-8 one? If so, there is no valid, unique
93
+ * prefix.
94
+ */
95
+ if (!c || ++ * len > max_length || !isascii (c )) {
96
+ * len = 0 ;
97
+ break ;
98
+ }
99
+
100
+ if (c != other_string [* len - 1 ])
101
+ break ;
102
+ }
103
+ }
104
+
105
+ static void find_unique_prefixes (struct prefix_item_list * list )
106
+ {
107
+ size_t i ;
108
+
109
+ if (list -> sorted .nr == list -> items .nr )
110
+ return ;
111
+
112
+ string_list_clear (& list -> sorted , 0 );
113
+ /* Avoid reallocating incrementally */
114
+ list -> sorted .items = xmalloc (st_mult (sizeof (* list -> sorted .items ),
115
+ list -> items .nr ));
116
+ list -> sorted .nr = list -> sorted .alloc = list -> items .nr ;
117
+
118
+ for (i = 0 ; i < list -> items .nr ; i ++ ) {
119
+ list -> sorted .items [i ].string = list -> items .items [i ].string ;
120
+ list -> sorted .items [i ].util = list -> items .items + i ;
121
+ }
122
+
123
+ string_list_sort (& list -> sorted );
124
+
125
+ for (i = 0 ; i < list -> sorted .nr ; i ++ ) {
126
+ struct string_list_item * sorted_item = list -> sorted .items + i ;
127
+ struct string_list_item * item = sorted_item -> util ;
128
+ size_t * len = item -> util ;
129
+
130
+ * len = 0 ;
131
+ while (* len < list -> min_length ) {
132
+ char c = item -> string [(* len )++ ];
133
+
134
+ if (!c || !isascii (c )) {
135
+ * len = 0 ;
136
+ break ;
137
+ }
138
+ }
139
+
140
+ if (i > 0 )
141
+ extend_prefix_length (item , sorted_item [-1 ].string ,
142
+ list -> max_length );
143
+ if (i + 1 < list -> sorted .nr )
144
+ extend_prefix_length (item , sorted_item [1 ].string ,
145
+ list -> max_length );
146
+ }
147
+ }
148
+
149
+ static ssize_t find_unique (const char * string , struct prefix_item_list * list )
150
+ {
151
+ int index = string_list_find_insert_index (& list -> sorted , string , 1 );
152
+ struct string_list_item * item ;
153
+
154
+ if (list -> items .nr != list -> sorted .nr )
155
+ BUG ("prefix_item_list in inconsistent state (%" PRIuMAX
156
+ " vs %" PRIuMAX ")" ,
157
+ (uintmax_t )list -> items .nr , (uintmax_t )list -> sorted .nr );
158
+
159
+ if (index < 0 )
160
+ item = list -> sorted .items [-1 - index ].util ;
161
+ else if (index > 0 &&
162
+ starts_with (list -> sorted .items [index - 1 ].string , string ))
163
+ return -1 ;
164
+ else if (index + 1 < list -> sorted .nr &&
165
+ starts_with (list -> sorted .items [index + 1 ].string , string ))
166
+ return -1 ;
167
+ else if (index < list -> sorted .nr )
168
+ item = list -> sorted .items [index ].util ;
169
+ else
170
+ return -1 ;
171
+ return item - list -> items .items ;
172
+ }
173
+
48
174
struct list_options {
49
175
int columns ;
50
176
const char * header ;
@@ -95,18 +221,21 @@ struct list_and_choose_options {
95
221
* If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
96
222
* `LIST_AND_CHOOSE_QUIT` is returned.
97
223
*/
98
- static ssize_t list_and_choose (struct add_i_state * s , struct string_list * items ,
224
+ static ssize_t list_and_choose (struct add_i_state * s ,
225
+ struct prefix_item_list * items ,
99
226
struct list_and_choose_options * opts )
100
227
{
101
228
struct strbuf input = STRBUF_INIT ;
102
229
ssize_t res = LIST_AND_CHOOSE_ERROR ;
103
230
231
+ find_unique_prefixes (items );
232
+
104
233
for (;;) {
105
234
char * p ;
106
235
107
236
strbuf_reset (& input );
108
237
109
- list (s , items , & opts -> list_opts );
238
+ list (s , & items -> items , & opts -> list_opts );
110
239
111
240
printf ("%s%s" , opts -> prompt , "> " );
112
241
fflush (stdout );
@@ -142,7 +271,10 @@ static ssize_t list_and_choose(struct add_i_state *s, struct string_list *items,
142
271
143
272
if (p [sep ])
144
273
p [sep ++ ] = '\0' ;
145
- if (index < 0 || index >= items -> nr )
274
+ if (index < 0 )
275
+ index = find_unique (p , items );
276
+
277
+ if (index < 0 || index >= items -> items .nr )
146
278
printf (_ ("Huh (%s)?\n" ), p );
147
279
else {
148
280
res = index ;
@@ -308,6 +440,23 @@ static void render_adddel(struct strbuf *buf,
308
440
strbuf_addstr (buf , no_changes );
309
441
}
310
442
443
+ /* filters out prefixes which have special meaning to list_and_choose() */
444
+ static int is_valid_prefix (const char * prefix , size_t prefix_len )
445
+ {
446
+ return prefix_len && prefix &&
447
+ /*
448
+ * We expect `prefix` to be NUL terminated, therefore this
449
+ * `strcspn()` call is okay, even if it might do much more
450
+ * work than strictly necessary.
451
+ */
452
+ strcspn (prefix , " \t\r\n," ) >= prefix_len && /* separators */
453
+ * prefix != '-' && /* deselection */
454
+ !isdigit (* prefix ) && /* selection */
455
+ (prefix_len != 1 ||
456
+ (* prefix != '*' && /* "all" wildcard */
457
+ * prefix != '?' )); /* prompt help */
458
+ }
459
+
311
460
struct print_file_item_data {
312
461
const char * modified_fmt ;
313
462
struct strbuf buf , index , worktree ;
@@ -347,10 +496,23 @@ typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
347
496
struct string_list * files ,
348
497
struct list_options * opts );
349
498
499
+ struct command_item {
500
+ size_t prefix_length ;
501
+ command_t command ;
502
+ };
503
+
350
504
static void print_command_item (int i , struct string_list_item * item ,
351
505
void * print_command_item_data )
352
506
{
353
- printf (" %2d: %s" , i + 1 , item -> string );
507
+ struct command_item * util = item -> util ;
508
+
509
+ if (!util -> prefix_length ||
510
+ !is_valid_prefix (item -> string , util -> prefix_length ))
511
+ printf (" %2d: %s" , i + 1 , item -> string );
512
+ else
513
+ printf (" %2d: [%.*s]%s" , i + 1 ,
514
+ (int )util -> prefix_length , item -> string ,
515
+ item -> string + util -> prefix_length );
354
516
}
355
517
356
518
int run_add_i (struct repository * r , const struct pathspec * ps )
@@ -366,7 +528,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
366
528
} command_list [] = {
367
529
{ "status" , run_status },
368
530
};
369
- struct string_list commands = STRING_LIST_INIT_NODUP ;
531
+ struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT ;
370
532
371
533
struct print_file_item_data print_file_item_data = {
372
534
"%12s %12s %s" , STRBUF_INIT , STRBUF_INIT , STRBUF_INIT
@@ -379,9 +541,12 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
379
541
ssize_t i ;
380
542
int res = 0 ;
381
543
382
- for (i = 0 ; i < ARRAY_SIZE (command_list ); i ++ )
383
- string_list_append (& commands , command_list [i ].string )
384
- -> util = command_list [i ].command ;
544
+ for (i = 0 ; i < ARRAY_SIZE (command_list ); i ++ ) {
545
+ struct command_item * util = xcalloc (sizeof (* util ), 1 );
546
+ util -> command = command_list [i ].command ;
547
+ string_list_append (& commands .items , command_list [i ].string )
548
+ -> util = util ;
549
+ }
385
550
386
551
init_add_i_state (& s , r );
387
552
@@ -406,8 +571,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
406
571
break ;
407
572
}
408
573
if (i != LIST_AND_CHOOSE_ERROR ) {
409
- command_t command = commands .items [i ].util ;
410
- res = command (& s , ps , & files , & opts );
574
+ struct command_item * util =
575
+ commands .items .items [i ].util ;
576
+ res = util -> command (& s , ps , & files , & opts );
411
577
}
412
578
}
413
579
@@ -416,7 +582,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
416
582
strbuf_release (& print_file_item_data .index );
417
583
strbuf_release (& print_file_item_data .worktree );
418
584
strbuf_release (& header );
419
- string_list_clear (& commands , 0 );
585
+ prefix_item_list_clear (& commands );
420
586
421
587
return res ;
422
588
}
0 commit comments