@@ -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 ;
@@ -305,6 +437,23 @@ static void render_adddel(struct strbuf *buf,
305
437
strbuf_addstr (buf , no_changes );
306
438
}
307
439
440
+ /* filters out prefixes which have special meaning to list_and_choose() */
441
+ static int is_valid_prefix (const char * prefix , size_t prefix_len )
442
+ {
443
+ return prefix_len && prefix &&
444
+ /*
445
+ * We expect `prefix` to be NUL terminated, therefore this
446
+ * `strcspn()` call is okay, even if it might do much more
447
+ * work than strictly necessary.
448
+ */
449
+ strcspn (prefix , " \t\r\n," ) >= prefix_len && /* separators */
450
+ * prefix != '-' && /* deselection */
451
+ !isdigit (* prefix ) && /* selection */
452
+ (prefix_len != 1 ||
453
+ (* prefix != '*' && /* "all" wildcard */
454
+ * prefix != '?' )); /* prompt help */
455
+ }
456
+
308
457
struct print_file_item_data {
309
458
const char * modified_fmt ;
310
459
struct strbuf buf , index , worktree ;
@@ -344,10 +493,23 @@ typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
344
493
struct string_list * files ,
345
494
struct list_options * opts );
346
495
496
+ struct command_item {
497
+ size_t prefix_length ;
498
+ command_t command ;
499
+ };
500
+
347
501
static void print_command_item (int i , struct string_list_item * item ,
348
502
void * print_command_item_data )
349
503
{
350
- printf (" %2d: %s" , i + 1 , item -> string );
504
+ struct command_item * util = item -> util ;
505
+
506
+ if (!util -> prefix_length ||
507
+ !is_valid_prefix (item -> string , util -> prefix_length ))
508
+ printf (" %2d: %s" , i + 1 , item -> string );
509
+ else
510
+ printf (" %2d: [%.*s]%s" , i + 1 ,
511
+ (int )util -> prefix_length , item -> string ,
512
+ item -> string + util -> prefix_length );
351
513
}
352
514
353
515
int run_add_i (struct repository * r , const struct pathspec * ps )
@@ -363,7 +525,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
363
525
} command_list [] = {
364
526
{ "status" , run_status },
365
527
};
366
- struct string_list commands = STRING_LIST_INIT_NODUP ;
528
+ struct prefix_item_list commands = PREFIX_ITEM_LIST_INIT ;
367
529
368
530
struct print_file_item_data print_file_item_data = {
369
531
"%12s %12s %s" , STRBUF_INIT , STRBUF_INIT , STRBUF_INIT
@@ -376,9 +538,12 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
376
538
ssize_t i ;
377
539
int res = 0 ;
378
540
379
- for (i = 0 ; i < ARRAY_SIZE (command_list ); i ++ )
380
- string_list_append (& commands , command_list [i ].string )
381
- -> util = command_list [i ].command ;
541
+ for (i = 0 ; i < ARRAY_SIZE (command_list ); i ++ ) {
542
+ struct command_item * util = xcalloc (sizeof (* util ), 1 );
543
+ util -> command = command_list [i ].command ;
544
+ string_list_append (& commands .items , command_list [i ].string )
545
+ -> util = util ;
546
+ }
382
547
383
548
init_add_i_state (& s , r );
384
549
@@ -403,8 +568,9 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
403
568
break ;
404
569
}
405
570
if (i != LIST_AND_CHOOSE_ERROR ) {
406
- command_t command = commands .items [i ].util ;
407
- res = command (& s , ps , & files , & opts );
571
+ struct command_item * util =
572
+ commands .items .items [i ].util ;
573
+ res = util -> command (& s , ps , & files , & opts );
408
574
}
409
575
}
410
576
@@ -413,7 +579,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
413
579
strbuf_release (& print_file_item_data .index );
414
580
strbuf_release (& print_file_item_data .worktree );
415
581
strbuf_release (& header );
416
- string_list_clear (& commands , 0 );
582
+ prefix_item_list_clear (& commands );
417
583
418
584
return res ;
419
585
}
0 commit comments