linux-qubasis
linux oasis port as a qubes template
git clone https://9o.is/git/linux-qubasis.git
0006-add-partial-multi-selection.patch
(8312B)
1 From 032d160823cb860fb7ea35360e6964ef5c7ba027 Mon Sep 17 00:00:00 2001
2 From: Jul <jul@qh.is>
3 Date: Wed, 6 Aug 2025 05:57:32 -0400
4 Subject: [PATCH] add partial multi-selection
5
6 ---
7 src/choices.c | 31 +++++++++++++++++++++++++++++--
8 src/choices.h | 4 ++++
9 src/options.c | 8 +++++++-
10 src/options.h | 1 +
11 src/tty_interface.c | 44 +++++++++++++++++++++++++++++++++++++++++---
12 5 files changed, 82 insertions(+), 6 deletions(-)
13
14 diff --git a/src/choices.c b/src/choices.c
15 index 4b6a0ca..9c7e2e4 100644
16 --- a/src/choices.c
17 +++ b/src/choices.c
18 @@ -94,8 +94,10 @@ static void choices_resize(choices_t *c, size_t new_capacity) {
19
20 static void choices_reset_search(choices_t *c) {
21 free(c->results);
22 - c->selection = c->available = 0;
23 + free(c->multiselections);
24 + c->selection = c->available = c->multiselection_size = 0;
25 c->results = NULL;
26 + c->multiselections = NULL;
27 }
28
29 void choices_init(choices_t *c, options_t *options) {
30 @@ -127,8 +129,10 @@ void choices_destroy(choices_t *c) {
31 c->capacity = c->size = 0;
32
33 free(c->results);
34 + free(c->multiselections);
35 c->results = NULL;
36 - c->available = c->selection = 0;
37 + c->multiselections = NULL;
38 + c->available = c->selection = c->multiselection_size = 0;
39 }
40
41 void choices_add(choices_t *c, const char *choice) {
42 @@ -261,6 +265,14 @@ static void *choices_search_worker(void *data) {
43 return NULL;
44 }
45
46 +void multiselection_init(choices_t *c) {
47 + c->multiselection_size = 0;
48 + c->multiselections = (unsigned char *) malloc(c->available * sizeof(unsigned char));
49 + for (size_t i = 0; i < c->available; i++) {
50 + c->multiselections[i] = 0;
51 + }
52 +}
53 +
54 void choices_search(choices_t *c, const char *search) {
55 choices_reset_search(c);
56
57 @@ -307,6 +319,7 @@ void choices_search(choices_t *c, const char *search) {
58 c->results = workers[0].result.list;
59 c->available = workers[0].result.size;
60
61 + multiselection_init(c);
62 free(workers);
63 pthread_mutex_destroy(&job->lock);
64 free(job);
65 @@ -333,3 +346,17 @@ void choices_next(choices_t *c) {
66 if (c->available)
67 c->selection = (c->selection + 1) % c->available;
68 }
69 +
70 +void choices_multiselect_toggle(choices_t *c) {
71 + if (!c->available) return;
72 + unsigned char selected = 1 == c->multiselections[c->selection];
73 +
74 + if (selected) {
75 + c->multiselections[c->selection] = 0;
76 + c->multiselection_size--;
77 + } else {
78 + c->multiselections[c->selection] = 1;
79 + c->multiselection_size++;
80 + }
81 +}
82 +
83 diff --git a/src/choices.h b/src/choices.h
84 index 9a77ccb..bbc0075 100644
85 --- a/src/choices.h
86 +++ b/src/choices.h
87 @@ -28,6 +28,9 @@ typedef struct {
88 size_t available;
89 size_t selection;
90
91 + unsigned char *multiselections;
92 + size_t multiselection_size;
93 +
94 unsigned int worker_count;
95 } choices_t;
96
97 @@ -41,6 +44,7 @@ const char *choices_get(choices_t *c, size_t n);
98 score_t choices_getscore(choices_t *c, size_t n);
99 void choices_prev(choices_t *c);
100 void choices_next(choices_t *c);
101 +void choices_multiselect_toggle(choices_t *c);
102
103 #ifdef __cplusplus
104 }
105 diff --git a/src/options.c b/src/options.c
106 index 5fd9d2a..ba452a3 100644
107 --- a/src/options.c
108 +++ b/src/options.c
109 @@ -20,6 +20,7 @@ static const char *usage_str =
110 " -0, --read-null Read input delimited by ASCII NUL characters\n"
111 " -j, --workers NUM Use NUM workers for searching. (default is # of CPUs)\n"
112 " -i, --show-info Show selection info line\n"
113 + " -m, --multi Enable multi-selection\n"
114 " -h, --help Display this help and exit\n"
115 " -v, --version Output version information and exit\n";
116
117 @@ -38,6 +39,7 @@ static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e'
118 {"benchmark", optional_argument, NULL, 'b'},
119 {"workers", required_argument, NULL, 'j'},
120 {"show-info", no_argument, NULL, 'i'},
121 + {"multi", no_argument, NULL, 'm'},
122 {"help", no_argument, NULL, 'h'},
123 {NULL, 0, NULL, 0}};
124
125 @@ -53,13 +55,14 @@ void options_init(options_t *options) {
126 options->workers = DEFAULT_WORKERS;
127 options->input_delimiter = '\n';
128 options->show_info = DEFAULT_SHOW_INFO;
129 + options->multi = 0;
130 }
131
132 void options_parse(options_t *options, int argc, char *argv[]) {
133 options_init(options);
134
135 int c;
136 - while ((c = getopt_long(argc, argv, "vhs0e:q:l:t:p:j:i", longopts, NULL)) != -1) {
137 + while ((c = getopt_long(argc, argv, "vhs0me:q:l:t:p:j:i", longopts, NULL)) != -1) {
138 switch (c) {
139 case 'v':
140 printf("%s " VERSION " © 2014-2025 John Hawthorn\n", argv[0]);
141 @@ -113,6 +116,9 @@ void options_parse(options_t *options, int argc, char *argv[]) {
142 case 'i':
143 options->show_info = 1;
144 break;
145 + case 'm':
146 + options->multi = 1;
147 + break;
148 case 'h':
149 default:
150 usage(argv[0]);
151 diff --git a/src/options.h b/src/options.h
152 index bfc3fa4..5c8be3b 100644
153 --- a/src/options.h
154 +++ b/src/options.h
155 @@ -16,6 +16,7 @@ typedef struct {
156 unsigned int workers;
157 char input_delimiter;
158 int show_info;
159 + int multi;
160 } options_t;
161
162 void options_init(options_t *options);
163 diff --git a/src/tty_interface.c b/src/tty_interface.c
164 index 18ccd59..ddcbb59 100644
165 --- a/src/tty_interface.c
166 +++ b/src/tty_interface.c
167 @@ -96,11 +96,14 @@ static void draw(tty_interface_t *state) {
168 }
169 }
170
171 - tty_setcol(tty, 0);
172 tty_clearline(tty);
173 + tty_setcol(tty, options->multi ? 2 : 0);
174 tty_printf(tty, "%s%s", options->prompt, state->search);
175
176 - if (options->show_info) {
177 + if (options->show_info && options->multi) {
178 + tty_printf(tty, " ");
179 + tty_printf(tty, "[%lu/%lu/%lu]", choices->multiselection_size, choices->available, choices->size);
180 + } else if (options->show_info) {
181 tty_printf(tty, " ");
182 tty_printf(tty, "[%lu/%lu]", choices->available, choices->size);
183 }
184 @@ -110,6 +113,7 @@ static void draw(tty_interface_t *state) {
185 tty_clearline(tty);
186 const char *choice = choices_get(choices, i);
187 if (choice) {
188 + if (options->multi) tty_printf(tty, choices->multiselections[i] == 1 ? "* " : " ");
189 draw_match(state, choice, i == choices->selection);
190 }
191 }
192 @@ -117,7 +121,7 @@ static void draw(tty_interface_t *state) {
193 if (num_lines)
194 tty_moveup(tty, num_lines);
195
196 - tty_setcol(tty, 0);
197 + tty_setcol(tty, options->multi ? 2 : 0);
198 fputs(options->prompt, tty->fout);
199 for (size_t i = 0; i < state->cursor; i++)
200 fputc(state->search[i], tty->fout);
201 @@ -136,9 +140,41 @@ static void update_state(tty_interface_t *state) {
202 }
203 }
204
205 +static void action_multiselect(tty_interface_t *state) {
206 + if (!state->options->multi) return;
207 +
208 + choices_multiselect_toggle(state->choices);
209 + choices_next(state->choices);
210 + draw(state);
211 +}
212 +
213 static void action_emit(tty_interface_t *state) {
214 update_state(state);
215
216 + if (state->options->multi) {
217 + int multiselected = 0;
218 +
219 + for (size_t i = 0; i < state->choices->available; i++) {
220 + if (state->choices->multiselections[i] != 1) continue;
221 +
222 + const char *name = choices_get(state->choices, i);
223 + if (!name) continue;
224 +
225 + if (!multiselected) {
226 + clear(state);
227 + tty_close(state->tty);
228 + multiselected = 1;
229 + }
230 +
231 + printf("%s\n", name);
232 + }
233 +
234 + if (multiselected) {
235 + state->exit = EXIT_SUCCESS;
236 + return;
237 + }
238 + }
239 +
240 /* Reset the tty as close as possible to the previous state */
241 clear(state);
242
243 @@ -249,6 +285,7 @@ static void action_pagedown(tty_interface_t *state) {
244
245 static void action_autocomplete(tty_interface_t *state) {
246 update_state(state);
247 +
248 const char *current_selection = choices_get(state->choices, state->choices->selection);
249 if (current_selection) {
250 strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX);
251 @@ -318,6 +355,7 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC *
252 {KEY_CTRL('J'), action_next}, /* C-J */
253 {KEY_CTRL('A'), action_beginning}, /* C-A */
254 {KEY_CTRL('E'), action_end}, /* C-E */
255 + {KEY_CTRL('@'), action_multiselect}, /* Space (C-@) */
256
257 {"\x1bOD", action_left}, /* LEFT */
258 {"\x1b[D", action_left}, /* LEFT */
259 --
260 2.51.0
261