linux-qubasis

linux oasis port as a qubes template

git clone https://9o.is/git/linux-qubasis.git

commit 72505ceb37e184eb2c9f79ffe36eb6c4cffca318
parent 71f8a9bdb0a1aadbdbc877f473e705752ce18ee4
Author: Jul <jul@9o.is>
Date:   Tue,  7 Oct 2025 11:01:58 +0800

add fzy

Diffstat:
Mgen.sh | 1+
Dpkg/fzy/build | 24------------------------
Apkg/fzy/gen.sh | 22++++++++++++++++++++++
Apkg/fzy/patch/0001-fix-clearline-issues-for-use-in-editor.patch | 46++++++++++++++++++++++++++++++++++++++++++++++
Apkg/fzy/patch/0002-center-scrolling.patch | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/fzy/patch/0003-truncate-lines-to-terminal-width.patch | 27+++++++++++++++++++++++++++
Apkg/fzy/patch/0004-add-halfpage-up-down-vi-bindings.patch | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/fzy/patch/0005-inline-info.patch | 37+++++++++++++++++++++++++++++++++++++
Apkg/fzy/patch/0006-add-partial-multi-selection.patch | 261+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apkg/fzy/patch/0007-fix-path-of-config.h-includes.patch | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dpkg/fzy/patches/0001-fix-clearline-issues-for-use-in-editor.patch | 46----------------------------------------------
Dpkg/fzy/patches/0002-center-scrolling.patch | 55-------------------------------------------------------
Dpkg/fzy/patches/0003-truncate-lines-to-terminal-width.patch | 27---------------------------
Dpkg/fzy/patches/0004-add-halfpage-up-down-vi-bindings.patch | 49-------------------------------------------------
Dpkg/fzy/patches/0005-inline-info.patch | 37-------------------------------------
Dpkg/fzy/patches/0006-add-partial-multi-selection.patch | 261-------------------------------------------------------------------------------
16 files changed, 593 insertions(+), 499 deletions(-)

diff --git a/gen.sh b/gen.sh @@ -25,6 +25,7 @@ subgen pkg/byacc subgen pkg/e2fsprogs subgen pkg/fe subgen pkg/fspec-sync +subgen pkg/fzy subgen pkg/less subgen pkg/libtermkey subgen pkg/linux-headers diff --git a/pkg/fzy/build b/pkg/fzy/build @@ -1,24 +0,0 @@ -#!/bin/sh -set -euo pipefail - -gitref="0b97373a" -repodir="$srcdir/repo" - -if [ "$skip_clean" != "true" ]; then - git -C "$repodir" clean -dx - git -C "$repodir" reset --hard "$gitref" - - for patch in $srcdir/patches/*.patch; do - git -C "$repodir" am --no-gpg-sign "$patch" - done -fi - -rm -rf "$outdir" -cp "$srcdir/config.h" "$repodir" - -make -C "$repodir" -make DESTDIR="$outdir" PREFIX=/usr -C "$repodir" install - -if [ "$local_install" == "true" ]; then - sudo make -C "$repodir" PREFIX=/usr install -fi diff --git a/pkg/fzy/gen.sh b/pkg/fzy/gen.sh @@ -0,0 +1,22 @@ +fetch git + +cflags " + -std=c99 + -D _GNU_SOURCE + -D VERSION='\"1.1\"' + -I $srcdir/src + -I $dir + -Wno-maybe-uninitialized +" + +exe fzy " + src/choices.c + src/fzy.c + src/match.c + src/options.c + src/tty.c + src/tty_interface.c +" + +bin fzy +man fzy.1 diff --git a/pkg/fzy/patch/0001-fix-clearline-issues-for-use-in-editor.patch b/pkg/fzy/patch/0001-fix-clearline-issues-for-use-in-editor.patch @@ -0,0 +1,46 @@ +From 261aed15e27d8603979b4caec40377c7834027cb Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Tue, 5 Aug 2025 02:37:51 -0400 +Subject: [PATCH] fix clearline issues for use in editor + +--- + src/tty.c | 2 +- + src/tty_interface.c | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/tty.c b/src/tty.c +index b8a555d..bc20c0a 100644 +--- a/src/tty.c ++++ b/src/tty.c +@@ -165,7 +165,7 @@ void tty_newline(tty_t *tty) { + } + + void tty_clearline(tty_t *tty) { +- tty_printf(tty, "%c%cK", 0x1b, '['); ++ tty_printf(tty, "%c%c2K\r", 0x1b, '['); + } + + void tty_setcol(tty_t *tty, int col) { +diff --git a/src/tty_interface.c b/src/tty_interface.c +index 343dde8..59c9c74 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -92,12 +92,13 @@ static void draw(tty_interface_t *state) { + } + + tty_setcol(tty, 0); +- tty_printf(tty, "%s%s", options->prompt, state->search); + tty_clearline(tty); ++ tty_printf(tty, "%s%s", options->prompt, state->search); + + if (options->show_info) { +- tty_printf(tty, "\n[%lu/%lu]", choices->available, choices->size); ++ tty_printf(tty, "\n"); + tty_clearline(tty); ++ tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); + } + + for (size_t i = start; i < start + num_lines; i++) { +-- +2.51.0 + diff --git a/pkg/fzy/patch/0002-center-scrolling.patch b/pkg/fzy/patch/0002-center-scrolling.patch @@ -0,0 +1,55 @@ +From 5b9e811551524283fab11d656304445831f9001a Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Tue, 5 Aug 2025 02:50:21 -0400 +Subject: [PATCH] center scrolling + +--- + src/options.c | 1 - + src/options.h | 1 - + src/tty_interface.c | 5 +++-- + 3 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/options.c b/src/options.c +index 485fc98..5fd9d2a 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -47,7 +47,6 @@ void options_init(options_t *options) { + options->filter = NULL; + options->init_search = NULL; + options->show_scores = 0; +- options->scrolloff = 1; + options->tty_filename = DEFAULT_TTY; + options->num_lines = DEFAULT_NUM_LINES; + options->prompt = DEFAULT_PROMPT; +diff --git a/src/options.h b/src/options.h +index 1a0886e..bfc3fa4 100644 +--- a/src/options.h ++++ b/src/options.h +@@ -12,7 +12,6 @@ typedef struct { + const char *tty_filename; + int show_scores; + unsigned int num_lines; +- unsigned int scrolloff; + const char *prompt; + unsigned int workers; + char input_delimiter; +diff --git a/src/tty_interface.c b/src/tty_interface.c +index 59c9c74..02c26a0 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -82,9 +82,10 @@ static void draw(tty_interface_t *state) { + + unsigned int num_lines = options->num_lines; + size_t start = 0; ++ size_t scroll_threshold = (num_lines / 2) + 1; + size_t current_selection = choices->selection; +- if (current_selection + options->scrolloff >= num_lines) { +- start = current_selection + options->scrolloff - num_lines + 1; ++ if (current_selection >= scroll_threshold) { ++ start = current_selection - scroll_threshold + 1; + size_t available = choices_available(choices); + if (start + num_lines >= available && available > 0) { + start = available - num_lines; +-- +2.51.0 + diff --git a/pkg/fzy/patch/0003-truncate-lines-to-terminal-width.patch b/pkg/fzy/patch/0003-truncate-lines-to-terminal-width.patch @@ -0,0 +1,27 @@ +From 17a36a608407996c2c350ffc7d58669e5d796714 Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Thu, 31 Jul 2025 05:34:48 -0400 +Subject: [PATCH] truncate lines to terminal width + +--- + src/tty_interface.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/tty_interface.c b/src/tty_interface.c +index 02c26a0..aed8a21 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -59,6 +59,10 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected) + + tty_setnowrap(tty); + for (size_t i = 0, p = 0; choice[i] != '\0'; i++) { ++ if (i >= tty_getwidth(state->tty) - 5) { ++ tty_printf(tty, "..."); ++ break; ++ } + if (positions[p] == i) { + tty_setfg(tty, TTY_COLOR_HIGHLIGHT); + p++; +-- +2.51.0 + diff --git a/pkg/fzy/patch/0004-add-halfpage-up-down-vi-bindings.patch b/pkg/fzy/patch/0004-add-halfpage-up-down-vi-bindings.patch @@ -0,0 +1,49 @@ +From 98c8f49bccd938d67230e97904b5b4f5577964d6 Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Tue, 5 Aug 2025 03:20:20 -0400 +Subject: [PATCH] add halfpage up/down vi bindings + +--- + src/tty_interface.c | 17 ++++++++++++----- + 1 file changed, 12 insertions(+), 5 deletions(-) + +diff --git a/src/tty_interface.c b/src/tty_interface.c +index aed8a21..b4a99c1 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -186,9 +186,16 @@ static void action_del_word(tty_interface_t *state) { + state->cursor = cursor; + } + +-static void action_del_all(tty_interface_t *state) { +- memmove(state->search, &state->search[state->cursor], strlen(state->search) - state->cursor + 1); +- state->cursor = 0; ++static void action_halfpageup(tty_interface_t *state) { ++ update_state(state); ++ for (size_t i = 0; i < (state->options->num_lines / 2) && state->choices->selection > 0; i++) ++ choices_prev(state->choices); ++} ++ ++static void action_halfpagedown(tty_interface_t *state) { ++ update_state(state); ++ for (size_t i = 0; i < (state->options->num_lines / 2) && state->choices->selection < state->choices->available - 1; i++) ++ choices_next(state->choices); + } + + static void action_prev(tty_interface_t *state) { +@@ -300,10 +307,10 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC * + + {KEY_CTRL('H'), action_del_char}, /* Backspace (C-H) */ + {KEY_CTRL('W'), action_del_word}, /* C-W */ +- {KEY_CTRL('U'), action_del_all}, /* C-U */ + {KEY_CTRL('I'), action_autocomplete}, /* TAB (C-I ) */ ++ {KEY_CTRL('U'), action_halfpageup}, /* C-U */ ++ {KEY_CTRL('D'), action_halfpagedown}, /* C-D */ + {KEY_CTRL('C'), action_exit}, /* C-C */ +- {KEY_CTRL('D'), action_exit}, /* C-D */ + {KEY_CTRL('G'), action_exit}, /* C-G */ + {KEY_CTRL('M'), action_emit}, /* CR */ + {KEY_CTRL('P'), action_prev}, /* C-P */ +-- +2.51.0 + diff --git a/pkg/fzy/patch/0005-inline-info.patch b/pkg/fzy/patch/0005-inline-info.patch @@ -0,0 +1,37 @@ +From 2eaab24a615e5f3ffc275128c5824821c36ed596 Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Tue, 5 Aug 2025 04:26:59 -0400 +Subject: [PATCH] inline info + +--- + src/tty_interface.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/tty_interface.c b/src/tty_interface.c +index b4a99c1..18ccd59 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -101,8 +101,7 @@ static void draw(tty_interface_t *state) { + tty_printf(tty, "%s%s", options->prompt, state->search); + + if (options->show_info) { +- tty_printf(tty, "\n"); +- tty_clearline(tty); ++ tty_printf(tty, " "); + tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); + } + +@@ -115,8 +114,8 @@ static void draw(tty_interface_t *state) { + } + } + +- if (num_lines + options->show_info) +- tty_moveup(tty, num_lines + options->show_info); ++ if (num_lines) ++ tty_moveup(tty, num_lines); + + tty_setcol(tty, 0); + fputs(options->prompt, tty->fout); +-- +2.51.0 + diff --git a/pkg/fzy/patch/0006-add-partial-multi-selection.patch b/pkg/fzy/patch/0006-add-partial-multi-selection.patch @@ -0,0 +1,261 @@ +From 032d160823cb860fb7ea35360e6964ef5c7ba027 Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Wed, 6 Aug 2025 05:57:32 -0400 +Subject: [PATCH] add partial multi-selection + +--- + src/choices.c | 31 +++++++++++++++++++++++++++++-- + src/choices.h | 4 ++++ + src/options.c | 8 +++++++- + src/options.h | 1 + + src/tty_interface.c | 44 +++++++++++++++++++++++++++++++++++++++++--- + 5 files changed, 82 insertions(+), 6 deletions(-) + +diff --git a/src/choices.c b/src/choices.c +index 4b6a0ca..9c7e2e4 100644 +--- a/src/choices.c ++++ b/src/choices.c +@@ -94,8 +94,10 @@ static void choices_resize(choices_t *c, size_t new_capacity) { + + static void choices_reset_search(choices_t *c) { + free(c->results); +- c->selection = c->available = 0; ++ free(c->multiselections); ++ c->selection = c->available = c->multiselection_size = 0; + c->results = NULL; ++ c->multiselections = NULL; + } + + void choices_init(choices_t *c, options_t *options) { +@@ -127,8 +129,10 @@ void choices_destroy(choices_t *c) { + c->capacity = c->size = 0; + + free(c->results); ++ free(c->multiselections); + c->results = NULL; +- c->available = c->selection = 0; ++ c->multiselections = NULL; ++ c->available = c->selection = c->multiselection_size = 0; + } + + void choices_add(choices_t *c, const char *choice) { +@@ -261,6 +265,14 @@ static void *choices_search_worker(void *data) { + return NULL; + } + ++void multiselection_init(choices_t *c) { ++ c->multiselection_size = 0; ++ c->multiselections = (unsigned char *) malloc(c->available * sizeof(unsigned char)); ++ for (size_t i = 0; i < c->available; i++) { ++ c->multiselections[i] = 0; ++ } ++} ++ + void choices_search(choices_t *c, const char *search) { + choices_reset_search(c); + +@@ -307,6 +319,7 @@ void choices_search(choices_t *c, const char *search) { + c->results = workers[0].result.list; + c->available = workers[0].result.size; + ++ multiselection_init(c); + free(workers); + pthread_mutex_destroy(&job->lock); + free(job); +@@ -333,3 +346,17 @@ void choices_next(choices_t *c) { + if (c->available) + c->selection = (c->selection + 1) % c->available; + } ++ ++void choices_multiselect_toggle(choices_t *c) { ++ if (!c->available) return; ++ unsigned char selected = 1 == c->multiselections[c->selection]; ++ ++ if (selected) { ++ c->multiselections[c->selection] = 0; ++ c->multiselection_size--; ++ } else { ++ c->multiselections[c->selection] = 1; ++ c->multiselection_size++; ++ } ++} ++ +diff --git a/src/choices.h b/src/choices.h +index 9a77ccb..bbc0075 100644 +--- a/src/choices.h ++++ b/src/choices.h +@@ -28,6 +28,9 @@ typedef struct { + size_t available; + size_t selection; + ++ unsigned char *multiselections; ++ size_t multiselection_size; ++ + unsigned int worker_count; + } choices_t; + +@@ -41,6 +44,7 @@ const char *choices_get(choices_t *c, size_t n); + score_t choices_getscore(choices_t *c, size_t n); + void choices_prev(choices_t *c); + void choices_next(choices_t *c); ++void choices_multiselect_toggle(choices_t *c); + + #ifdef __cplusplus + } +diff --git a/src/options.c b/src/options.c +index 5fd9d2a..ba452a3 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -20,6 +20,7 @@ static const char *usage_str = + " -0, --read-null Read input delimited by ASCII NUL characters\n" + " -j, --workers NUM Use NUM workers for searching. (default is # of CPUs)\n" + " -i, --show-info Show selection info line\n" ++ " -m, --multi Enable multi-selection\n" + " -h, --help Display this help and exit\n" + " -v, --version Output version information and exit\n"; + +@@ -38,6 +39,7 @@ static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e' + {"benchmark", optional_argument, NULL, 'b'}, + {"workers", required_argument, NULL, 'j'}, + {"show-info", no_argument, NULL, 'i'}, ++ {"multi", no_argument, NULL, 'm'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0}}; + +@@ -53,13 +55,14 @@ void options_init(options_t *options) { + options->workers = DEFAULT_WORKERS; + options->input_delimiter = '\n'; + options->show_info = DEFAULT_SHOW_INFO; ++ options->multi = 0; + } + + void options_parse(options_t *options, int argc, char *argv[]) { + options_init(options); + + int c; +- while ((c = getopt_long(argc, argv, "vhs0e:q:l:t:p:j:i", longopts, NULL)) != -1) { ++ while ((c = getopt_long(argc, argv, "vhs0me:q:l:t:p:j:i", longopts, NULL)) != -1) { + switch (c) { + case 'v': + printf("%s " VERSION " © 2014-2025 John Hawthorn\n", argv[0]); +@@ -113,6 +116,9 @@ void options_parse(options_t *options, int argc, char *argv[]) { + case 'i': + options->show_info = 1; + break; ++ case 'm': ++ options->multi = 1; ++ break; + case 'h': + default: + usage(argv[0]); +diff --git a/src/options.h b/src/options.h +index bfc3fa4..5c8be3b 100644 +--- a/src/options.h ++++ b/src/options.h +@@ -16,6 +16,7 @@ typedef struct { + unsigned int workers; + char input_delimiter; + int show_info; ++ int multi; + } options_t; + + void options_init(options_t *options); +diff --git a/src/tty_interface.c b/src/tty_interface.c +index 18ccd59..ddcbb59 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -96,11 +96,14 @@ static void draw(tty_interface_t *state) { + } + } + +- tty_setcol(tty, 0); + tty_clearline(tty); ++ tty_setcol(tty, options->multi ? 2 : 0); + tty_printf(tty, "%s%s", options->prompt, state->search); + +- if (options->show_info) { ++ if (options->show_info && options->multi) { ++ tty_printf(tty, " "); ++ tty_printf(tty, "[%lu/%lu/%lu]", choices->multiselection_size, choices->available, choices->size); ++ } else if (options->show_info) { + tty_printf(tty, " "); + tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); + } +@@ -110,6 +113,7 @@ static void draw(tty_interface_t *state) { + tty_clearline(tty); + const char *choice = choices_get(choices, i); + if (choice) { ++ if (options->multi) tty_printf(tty, choices->multiselections[i] == 1 ? "* " : " "); + draw_match(state, choice, i == choices->selection); + } + } +@@ -117,7 +121,7 @@ static void draw(tty_interface_t *state) { + if (num_lines) + tty_moveup(tty, num_lines); + +- tty_setcol(tty, 0); ++ tty_setcol(tty, options->multi ? 2 : 0); + fputs(options->prompt, tty->fout); + for (size_t i = 0; i < state->cursor; i++) + fputc(state->search[i], tty->fout); +@@ -136,9 +140,41 @@ static void update_state(tty_interface_t *state) { + } + } + ++static void action_multiselect(tty_interface_t *state) { ++ if (!state->options->multi) return; ++ ++ choices_multiselect_toggle(state->choices); ++ choices_next(state->choices); ++ draw(state); ++} ++ + static void action_emit(tty_interface_t *state) { + update_state(state); + ++ if (state->options->multi) { ++ int multiselected = 0; ++ ++ for (size_t i = 0; i < state->choices->available; i++) { ++ if (state->choices->multiselections[i] != 1) continue; ++ ++ const char *name = choices_get(state->choices, i); ++ if (!name) continue; ++ ++ if (!multiselected) { ++ clear(state); ++ tty_close(state->tty); ++ multiselected = 1; ++ } ++ ++ printf("%s\n", name); ++ } ++ ++ if (multiselected) { ++ state->exit = EXIT_SUCCESS; ++ return; ++ } ++ } ++ + /* Reset the tty as close as possible to the previous state */ + clear(state); + +@@ -249,6 +285,7 @@ static void action_pagedown(tty_interface_t *state) { + + static void action_autocomplete(tty_interface_t *state) { + update_state(state); ++ + const char *current_selection = choices_get(state->choices, state->choices->selection); + if (current_selection) { + strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX); +@@ -318,6 +355,7 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC * + {KEY_CTRL('J'), action_next}, /* C-J */ + {KEY_CTRL('A'), action_beginning}, /* C-A */ + {KEY_CTRL('E'), action_end}, /* C-E */ ++ {KEY_CTRL('@'), action_multiselect}, /* Space (C-@) */ + + {"\x1bOD", action_left}, /* LEFT */ + {"\x1b[D", action_left}, /* LEFT */ +-- +2.51.0 + diff --git a/pkg/fzy/patch/0007-fix-path-of-config.h-includes.patch b/pkg/fzy/patch/0007-fix-path-of-config.h-includes.patch @@ -0,0 +1,95 @@ +From c8c976469a877888a3474cf385ea7ff3738ed75b Mon Sep 17 00:00:00 2001 +From: Jul <jul@qh.is> +Date: Tue, 7 Oct 2025 10:58:58 +0800 +Subject: [PATCH] fix path of config.h includes + +--- + src/bonus.h | 2 +- + src/fzy.c | 2 +- + src/match.c | 2 +- + src/options.c | 2 +- + src/tty.c | 2 +- + src/tty_interface.c | 2 +- + 6 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src/bonus.h b/src/bonus.h +index 77f3f44..f5ae3b2 100644 +--- a/src/bonus.h ++++ b/src/bonus.h +@@ -1,7 +1,7 @@ + #ifndef BONUS_H + #define BONUS_H BONUS_H + +-#include "../config.h" ++#include "config.h" + + #ifdef __cplusplus + extern "C" { +diff --git a/src/fzy.c b/src/fzy.c +index 967a1fc..d4ab8d8 100644 +--- a/src/fzy.c ++++ b/src/fzy.c +@@ -11,7 +11,7 @@ + #include "options.h" + #include "tty_interface.h" + +-#include "../config.h" ++#include "config.h" + + int main(int argc, char *argv[]) { + int ret = 0; +diff --git a/src/match.c b/src/match.c +index 597d579..9a55921 100644 +--- a/src/match.c ++++ b/src/match.c +@@ -9,7 +9,7 @@ + #include "match.h" + #include "bonus.h" + +-#include "../config.h" ++#include "config.h" + + char *strcasechr(const char *s, char c) { + const char accept[3] = {c, toupper(c), 0}; +diff --git a/src/options.c b/src/options.c +index ba452a3..fc23dc5 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -6,7 +6,7 @@ + + #include "options.h" + +-#include "../config.h" ++#include "config.h" + + static const char *usage_str = + "" +diff --git a/src/tty.c b/src/tty.c +index bc20c0a..01e2125 100644 +--- a/src/tty.c ++++ b/src/tty.c +@@ -10,7 +10,7 @@ + + #include "tty.h" + +-#include "../config.h" ++#include "config.h" + + void tty_reset(tty_t *tty) { + tcsetattr(tty->fdin, TCSANOW, &tty->original_termios); +diff --git a/src/tty_interface.c b/src/tty_interface.c +index ddcbb59..ce49f3e 100644 +--- a/src/tty_interface.c ++++ b/src/tty_interface.c +@@ -5,7 +5,7 @@ + + #include "match.h" + #include "tty_interface.h" +-#include "../config.h" ++#include "config.h" + + static int isprint_unicode(char c) { + return isprint(c) || c & (1 << 7); +-- +2.51.0 + diff --git a/pkg/fzy/patches/0001-fix-clearline-issues-for-use-in-editor.patch b/pkg/fzy/patches/0001-fix-clearline-issues-for-use-in-editor.patch @@ -1,46 +0,0 @@ -From 15e99555ab310f4c6d776fb305dd4050ea9939ff Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Tue, 5 Aug 2025 02:37:51 -0400 -Subject: [PATCH 1/6] fix clearline issues for use in editor - ---- - src/tty.c | 2 +- - src/tty_interface.c | 5 +++-- - 2 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/tty.c b/src/tty.c -index b8a555d..bc20c0a 100644 ---- a/src/tty.c -+++ b/src/tty.c -@@ -165,7 +165,7 @@ void tty_newline(tty_t *tty) { - } - - void tty_clearline(tty_t *tty) { -- tty_printf(tty, "%c%cK", 0x1b, '['); -+ tty_printf(tty, "%c%c2K\r", 0x1b, '['); - } - - void tty_setcol(tty_t *tty, int col) { -diff --git a/src/tty_interface.c b/src/tty_interface.c -index 343dde8..59c9c74 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -92,12 +92,13 @@ static void draw(tty_interface_t *state) { - } - - tty_setcol(tty, 0); -- tty_printf(tty, "%s%s", options->prompt, state->search); - tty_clearline(tty); -+ tty_printf(tty, "%s%s", options->prompt, state->search); - - if (options->show_info) { -- tty_printf(tty, "\n[%lu/%lu]", choices->available, choices->size); -+ tty_printf(tty, "\n"); - tty_clearline(tty); -+ tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); - } - - for (size_t i = start; i < start + num_lines; i++) { --- -2.50.1 - diff --git a/pkg/fzy/patches/0002-center-scrolling.patch b/pkg/fzy/patches/0002-center-scrolling.patch @@ -1,55 +0,0 @@ -From 950782fb37e0dba49f28349fdbd93acb58f91da8 Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Tue, 5 Aug 2025 02:50:21 -0400 -Subject: [PATCH 2/6] center scrolling - ---- - src/options.c | 1 - - src/options.h | 1 - - src/tty_interface.c | 5 +++-- - 3 files changed, 3 insertions(+), 4 deletions(-) - -diff --git a/src/options.c b/src/options.c -index 485fc98..5fd9d2a 100644 ---- a/src/options.c -+++ b/src/options.c -@@ -47,7 +47,6 @@ void options_init(options_t *options) { - options->filter = NULL; - options->init_search = NULL; - options->show_scores = 0; -- options->scrolloff = 1; - options->tty_filename = DEFAULT_TTY; - options->num_lines = DEFAULT_NUM_LINES; - options->prompt = DEFAULT_PROMPT; -diff --git a/src/options.h b/src/options.h -index 1a0886e..bfc3fa4 100644 ---- a/src/options.h -+++ b/src/options.h -@@ -12,7 +12,6 @@ typedef struct { - const char *tty_filename; - int show_scores; - unsigned int num_lines; -- unsigned int scrolloff; - const char *prompt; - unsigned int workers; - char input_delimiter; -diff --git a/src/tty_interface.c b/src/tty_interface.c -index 59c9c74..02c26a0 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -82,9 +82,10 @@ static void draw(tty_interface_t *state) { - - unsigned int num_lines = options->num_lines; - size_t start = 0; -+ size_t scroll_threshold = (num_lines / 2) + 1; - size_t current_selection = choices->selection; -- if (current_selection + options->scrolloff >= num_lines) { -- start = current_selection + options->scrolloff - num_lines + 1; -+ if (current_selection >= scroll_threshold) { -+ start = current_selection - scroll_threshold + 1; - size_t available = choices_available(choices); - if (start + num_lines >= available && available > 0) { - start = available - num_lines; --- -2.50.1 - diff --git a/pkg/fzy/patches/0003-truncate-lines-to-terminal-width.patch b/pkg/fzy/patches/0003-truncate-lines-to-terminal-width.patch @@ -1,27 +0,0 @@ -From ebe328796dbf17f2c86d63afa512c73b7c00fba7 Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Thu, 31 Jul 2025 05:34:48 -0400 -Subject: [PATCH 3/6] truncate lines to terminal width - ---- - src/tty_interface.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/src/tty_interface.c b/src/tty_interface.c -index 02c26a0..aed8a21 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -59,6 +59,10 @@ static void draw_match(tty_interface_t *state, const char *choice, int selected) - - tty_setnowrap(tty); - for (size_t i = 0, p = 0; choice[i] != '\0'; i++) { -+ if (i >= tty_getwidth(state->tty) - 5) { -+ tty_printf(tty, "..."); -+ break; -+ } - if (positions[p] == i) { - tty_setfg(tty, TTY_COLOR_HIGHLIGHT); - p++; --- -2.50.1 - diff --git a/pkg/fzy/patches/0004-add-halfpage-up-down-vi-bindings.patch b/pkg/fzy/patches/0004-add-halfpage-up-down-vi-bindings.patch @@ -1,49 +0,0 @@ -From f9238875892ab2a302026c7e61ab59ac5f066747 Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Tue, 5 Aug 2025 03:20:20 -0400 -Subject: [PATCH 4/6] add halfpage up/down vi bindings - ---- - src/tty_interface.c | 17 ++++++++++++----- - 1 file changed, 12 insertions(+), 5 deletions(-) - -diff --git a/src/tty_interface.c b/src/tty_interface.c -index aed8a21..b4a99c1 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -186,9 +186,16 @@ static void action_del_word(tty_interface_t *state) { - state->cursor = cursor; - } - --static void action_del_all(tty_interface_t *state) { -- memmove(state->search, &state->search[state->cursor], strlen(state->search) - state->cursor + 1); -- state->cursor = 0; -+static void action_halfpageup(tty_interface_t *state) { -+ update_state(state); -+ for (size_t i = 0; i < (state->options->num_lines / 2) && state->choices->selection > 0; i++) -+ choices_prev(state->choices); -+} -+ -+static void action_halfpagedown(tty_interface_t *state) { -+ update_state(state); -+ for (size_t i = 0; i < (state->options->num_lines / 2) && state->choices->selection < state->choices->available - 1; i++) -+ choices_next(state->choices); - } - - static void action_prev(tty_interface_t *state) { -@@ -300,10 +307,10 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC * - - {KEY_CTRL('H'), action_del_char}, /* Backspace (C-H) */ - {KEY_CTRL('W'), action_del_word}, /* C-W */ -- {KEY_CTRL('U'), action_del_all}, /* C-U */ - {KEY_CTRL('I'), action_autocomplete}, /* TAB (C-I ) */ -+ {KEY_CTRL('U'), action_halfpageup}, /* C-U */ -+ {KEY_CTRL('D'), action_halfpagedown}, /* C-D */ - {KEY_CTRL('C'), action_exit}, /* C-C */ -- {KEY_CTRL('D'), action_exit}, /* C-D */ - {KEY_CTRL('G'), action_exit}, /* C-G */ - {KEY_CTRL('M'), action_emit}, /* CR */ - {KEY_CTRL('P'), action_prev}, /* C-P */ --- -2.50.1 - diff --git a/pkg/fzy/patches/0005-inline-info.patch b/pkg/fzy/patches/0005-inline-info.patch @@ -1,37 +0,0 @@ -From abbd962e4891ff3f07ccbc0d56f29c7f633b867e Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Tue, 5 Aug 2025 04:26:59 -0400 -Subject: [PATCH 5/6] inline info - ---- - src/tty_interface.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/src/tty_interface.c b/src/tty_interface.c -index b4a99c1..18ccd59 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -101,8 +101,7 @@ static void draw(tty_interface_t *state) { - tty_printf(tty, "%s%s", options->prompt, state->search); - - if (options->show_info) { -- tty_printf(tty, "\n"); -- tty_clearline(tty); -+ tty_printf(tty, " "); - tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); - } - -@@ -115,8 +114,8 @@ static void draw(tty_interface_t *state) { - } - } - -- if (num_lines + options->show_info) -- tty_moveup(tty, num_lines + options->show_info); -+ if (num_lines) -+ tty_moveup(tty, num_lines); - - tty_setcol(tty, 0); - fputs(options->prompt, tty->fout); --- -2.50.1 - diff --git a/pkg/fzy/patches/0006-add-partial-multi-selection.patch b/pkg/fzy/patches/0006-add-partial-multi-selection.patch @@ -1,261 +0,0 @@ -From b15050410a3d95ff60145febcfaf24096b4defb4 Mon Sep 17 00:00:00 2001 -From: Jul <jul@qh.is> -Date: Wed, 6 Aug 2025 05:57:32 -0400 -Subject: [PATCH 6/6] add partial multi-selection - ---- - src/choices.c | 31 +++++++++++++++++++++++++++++-- - src/choices.h | 4 ++++ - src/options.c | 8 +++++++- - src/options.h | 1 + - src/tty_interface.c | 44 +++++++++++++++++++++++++++++++++++++++++--- - 5 files changed, 82 insertions(+), 6 deletions(-) - -diff --git a/src/choices.c b/src/choices.c -index 4b6a0ca..9c7e2e4 100644 ---- a/src/choices.c -+++ b/src/choices.c -@@ -94,8 +94,10 @@ static void choices_resize(choices_t *c, size_t new_capacity) { - - static void choices_reset_search(choices_t *c) { - free(c->results); -- c->selection = c->available = 0; -+ free(c->multiselections); -+ c->selection = c->available = c->multiselection_size = 0; - c->results = NULL; -+ c->multiselections = NULL; - } - - void choices_init(choices_t *c, options_t *options) { -@@ -127,8 +129,10 @@ void choices_destroy(choices_t *c) { - c->capacity = c->size = 0; - - free(c->results); -+ free(c->multiselections); - c->results = NULL; -- c->available = c->selection = 0; -+ c->multiselections = NULL; -+ c->available = c->selection = c->multiselection_size = 0; - } - - void choices_add(choices_t *c, const char *choice) { -@@ -261,6 +265,14 @@ static void *choices_search_worker(void *data) { - return NULL; - } - -+void multiselection_init(choices_t *c) { -+ c->multiselection_size = 0; -+ c->multiselections = (unsigned char *) malloc(c->available * sizeof(unsigned char)); -+ for (size_t i = 0; i < c->available; i++) { -+ c->multiselections[i] = 0; -+ } -+} -+ - void choices_search(choices_t *c, const char *search) { - choices_reset_search(c); - -@@ -307,6 +319,7 @@ void choices_search(choices_t *c, const char *search) { - c->results = workers[0].result.list; - c->available = workers[0].result.size; - -+ multiselection_init(c); - free(workers); - pthread_mutex_destroy(&job->lock); - free(job); -@@ -333,3 +346,17 @@ void choices_next(choices_t *c) { - if (c->available) - c->selection = (c->selection + 1) % c->available; - } -+ -+void choices_multiselect_toggle(choices_t *c) { -+ if (!c->available) return; -+ unsigned char selected = 1 == c->multiselections[c->selection]; -+ -+ if (selected) { -+ c->multiselections[c->selection] = 0; -+ c->multiselection_size--; -+ } else { -+ c->multiselections[c->selection] = 1; -+ c->multiselection_size++; -+ } -+} -+ -diff --git a/src/choices.h b/src/choices.h -index 9a77ccb..bbc0075 100644 ---- a/src/choices.h -+++ b/src/choices.h -@@ -28,6 +28,9 @@ typedef struct { - size_t available; - size_t selection; - -+ unsigned char *multiselections; -+ size_t multiselection_size; -+ - unsigned int worker_count; - } choices_t; - -@@ -41,6 +44,7 @@ const char *choices_get(choices_t *c, size_t n); - score_t choices_getscore(choices_t *c, size_t n); - void choices_prev(choices_t *c); - void choices_next(choices_t *c); -+void choices_multiselect_toggle(choices_t *c); - - #ifdef __cplusplus - } -diff --git a/src/options.c b/src/options.c -index 5fd9d2a..ba452a3 100644 ---- a/src/options.c -+++ b/src/options.c -@@ -20,6 +20,7 @@ static const char *usage_str = - " -0, --read-null Read input delimited by ASCII NUL characters\n" - " -j, --workers NUM Use NUM workers for searching. (default is # of CPUs)\n" - " -i, --show-info Show selection info line\n" -+ " -m, --multi Enable multi-selection\n" - " -h, --help Display this help and exit\n" - " -v, --version Output version information and exit\n"; - -@@ -38,6 +39,7 @@ static struct option longopts[] = {{"show-matches", required_argument, NULL, 'e' - {"benchmark", optional_argument, NULL, 'b'}, - {"workers", required_argument, NULL, 'j'}, - {"show-info", no_argument, NULL, 'i'}, -+ {"multi", no_argument, NULL, 'm'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}}; - -@@ -53,13 +55,14 @@ void options_init(options_t *options) { - options->workers = DEFAULT_WORKERS; - options->input_delimiter = '\n'; - options->show_info = DEFAULT_SHOW_INFO; -+ options->multi = 0; - } - - void options_parse(options_t *options, int argc, char *argv[]) { - options_init(options); - - int c; -- while ((c = getopt_long(argc, argv, "vhs0e:q:l:t:p:j:i", longopts, NULL)) != -1) { -+ while ((c = getopt_long(argc, argv, "vhs0me:q:l:t:p:j:i", longopts, NULL)) != -1) { - switch (c) { - case 'v': - printf("%s " VERSION " © 2014-2025 John Hawthorn\n", argv[0]); -@@ -113,6 +116,9 @@ void options_parse(options_t *options, int argc, char *argv[]) { - case 'i': - options->show_info = 1; - break; -+ case 'm': -+ options->multi = 1; -+ break; - case 'h': - default: - usage(argv[0]); -diff --git a/src/options.h b/src/options.h -index bfc3fa4..5c8be3b 100644 ---- a/src/options.h -+++ b/src/options.h -@@ -16,6 +16,7 @@ typedef struct { - unsigned int workers; - char input_delimiter; - int show_info; -+ int multi; - } options_t; - - void options_init(options_t *options); -diff --git a/src/tty_interface.c b/src/tty_interface.c -index 18ccd59..ddcbb59 100644 ---- a/src/tty_interface.c -+++ b/src/tty_interface.c -@@ -96,11 +96,14 @@ static void draw(tty_interface_t *state) { - } - } - -- tty_setcol(tty, 0); - tty_clearline(tty); -+ tty_setcol(tty, options->multi ? 2 : 0); - tty_printf(tty, "%s%s", options->prompt, state->search); - -- if (options->show_info) { -+ if (options->show_info && options->multi) { -+ tty_printf(tty, " "); -+ tty_printf(tty, "[%lu/%lu/%lu]", choices->multiselection_size, choices->available, choices->size); -+ } else if (options->show_info) { - tty_printf(tty, " "); - tty_printf(tty, "[%lu/%lu]", choices->available, choices->size); - } -@@ -110,6 +113,7 @@ static void draw(tty_interface_t *state) { - tty_clearline(tty); - const char *choice = choices_get(choices, i); - if (choice) { -+ if (options->multi) tty_printf(tty, choices->multiselections[i] == 1 ? "* " : " "); - draw_match(state, choice, i == choices->selection); - } - } -@@ -117,7 +121,7 @@ static void draw(tty_interface_t *state) { - if (num_lines) - tty_moveup(tty, num_lines); - -- tty_setcol(tty, 0); -+ tty_setcol(tty, options->multi ? 2 : 0); - fputs(options->prompt, tty->fout); - for (size_t i = 0; i < state->cursor; i++) - fputc(state->search[i], tty->fout); -@@ -136,9 +140,41 @@ static void update_state(tty_interface_t *state) { - } - } - -+static void action_multiselect(tty_interface_t *state) { -+ if (!state->options->multi) return; -+ -+ choices_multiselect_toggle(state->choices); -+ choices_next(state->choices); -+ draw(state); -+} -+ - static void action_emit(tty_interface_t *state) { - update_state(state); - -+ if (state->options->multi) { -+ int multiselected = 0; -+ -+ for (size_t i = 0; i < state->choices->available; i++) { -+ if (state->choices->multiselections[i] != 1) continue; -+ -+ const char *name = choices_get(state->choices, i); -+ if (!name) continue; -+ -+ if (!multiselected) { -+ clear(state); -+ tty_close(state->tty); -+ multiselected = 1; -+ } -+ -+ printf("%s\n", name); -+ } -+ -+ if (multiselected) { -+ state->exit = EXIT_SUCCESS; -+ return; -+ } -+ } -+ - /* Reset the tty as close as possible to the previous state */ - clear(state); - -@@ -249,6 +285,7 @@ static void action_pagedown(tty_interface_t *state) { - - static void action_autocomplete(tty_interface_t *state) { - update_state(state); -+ - const char *current_selection = choices_get(state->choices, state->choices->selection); - if (current_selection) { - strncpy(state->search, choices_get(state->choices, state->choices->selection), SEARCH_SIZE_MAX); -@@ -318,6 +355,7 @@ static const keybinding_t keybindings[] = {{"\x1b", action_exit}, /* ESC * - {KEY_CTRL('J'), action_next}, /* C-J */ - {KEY_CTRL('A'), action_beginning}, /* C-A */ - {KEY_CTRL('E'), action_end}, /* C-E */ -+ {KEY_CTRL('@'), action_multiselect}, /* Space (C-@) */ - - {"\x1bOD", action_left}, /* LEFT */ - {"\x1b[D", action_left}, /* LEFT */ --- -2.50.1 -