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