Fix Shift-Tab plan/build toggle and harden build pipeline
ober
557e329c95b24743c4e32bed21f7d8ec8620ef42
--- a/Makefile +++ b/Makefile @@ -10,11 +10,43 @@ TUI_SHIM_DIR := $(CURDIR)/vendor/termbox2 NATIVE_LIB_DIR := $(JERBOA_HOME)/lib LDPATH := $(SHIM_DIR):$(TUI_SHIM_DIR):$(SQLITE_LIB_DIR):$(NATIVE_LIB_DIR) -.PHONY: all build gen run test clean repl binary install tui-shim run-tui jcode-musl linux linux-check linux-local docker +.PHONY: all build gen run test clean repl binary install tui-shim run-tui jcode-musl linux linux-check linux-local docker purge-stale sqlite-shim all: build -gen: +# ── Stale-artifact prevention ─────────────────────────────────────────────── +# Delete any compiled .so/.wpo whose source .ss/.sls is newer (or whose +# source no longer exists). This makes "make build" idempotent and immune +# to the class of bug where a stale .so silently linked against an old +# shim causes a runtime "no entry for X" foreign-procedure error. +# +# Always also clear the prebuilt vendor/chez-sqlite artifacts — they pin +# absolute paths to the shim and are cheap to recompile. +purge-stale: + @find lib -name "*.so" -o -name "*.wpo" 2>/dev/null | while read f; do \ + src=$$(echo $$f | sed -e 's|^lib/|src/|' -e 's|\.so$$|.ss|' -e 's|\.wpo$$|.ss|'); \ + sls=$$(echo $$f | sed 's|\.\(so\|wpo\)$$|.sls|'); \ + if [ ! -f "$$src" ] && [ ! -f "$$sls" ]; then rm -f "$$f"; \ + elif [ -f "$$src" ] && [ "$$src" -nt "$$f" ]; then rm -f "$$f"; \ + elif [ -f "$$sls" ] && [ "$$sls" -nt "$$f" ]; then rm -f "$$f"; \ + fi; \ + done + @rm -f vendor/chez-sqlite/src/chez-sqlite.so vendor/chez-sqlite/src/chez-sqlite.wpo + +# ── chez-sqlite shim build ────────────────────────────────────────────────── +# Rebuild the FFI shim if its C source is newer than the .so. This catches +# the case where vendor/chez-sqlite is updated but the .so isn't refreshed. +sqlite-shim: + @if [ ! -f vendor/chez-sqlite/chez_sqlite_shim.so ] || \ + [ vendor/chez-sqlite/chez_sqlite_shim.c -nt vendor/chez-sqlite/chez_sqlite_shim.so ]; then \ + echo "=== Rebuilding chez_sqlite_shim.so ==="; \ + cc -shared -fPIC -O2 \ + -I$(SQLITE_LIB_DIR)/../include -L$(SQLITE_LIB_DIR) \ + -o vendor/chez-sqlite/chez_sqlite_shim.so \ + vendor/chez-sqlite/chez_sqlite_shim.c -lsqlite3; \ + fi + +gen: purge-stale sqlite-shim $(JERBUILD) src lib build: gen --- a/lib/jcode/provider/provider.sls +++ b/lib/jcode/provider/provider.sls @@ -10,10 +10,10 @@ (except (chezscheme) make-hash-table hash-table? iota \x31;+ \x31;- getenv path-extension path-absolute? thread? make-mutex mutex? mutex-name) - (std text json) (std net request) (std net tls-rustls) - (std net tcp) (std misc string) (std misc retry) - (jcode core log) (jcode core message) (jerboa core) - (jerboa runtime)) + (std text json) (except (std net request) http-post-stream) + (std net tls-rustls) (std net tcp) (std misc string) + (std misc retry) (jcode core log) (jcode core message) + (jerboa core) (jerboa runtime)) (def logger (make-logger "provider")) (def *api-retry-policy* (make-retry-policy 3 1.0 30.0 #t)) (def (retryable-error? e) --- a/lib/jcode/ui/tui-ffi.sls +++ b/lib/jcode/ui/tui-ffi.sls @@ -17,23 +17,24 @@ TB_KEY_HOME TB_KEY_END TB_KEY_PGUP TB_KEY_PGDN TB_KEY_ARROW_UP TB_KEY_ARROW_DOWN TB_KEY_ARROW_LEFT TB_KEY_ARROW_RIGHT TB_KEY_BACKSPACE TB_KEY_BACKSPACE2 - TB_KEY_TAB TB_KEY_ENTER TB_KEY_ESC TB_KEY_SPACE - TB_KEY_MOUSE_LEFT TB_KEY_MOUSE_RIGHT TB_KEY_MOUSE_MIDDLE - TB_KEY_MOUSE_RELEASE TB_KEY_MOUSE_WHEEL_UP - TB_KEY_MOUSE_WHEEL_DOWN TB_KEY_CTRL_A TB_KEY_CTRL_B - TB_KEY_CTRL_C TB_KEY_CTRL_D TB_KEY_CTRL_E TB_KEY_CTRL_F - TB_KEY_CTRL_G TB_KEY_CTRL_H TB_KEY_CTRL_I TB_KEY_CTRL_J - TB_KEY_CTRL_K TB_KEY_CTRL_L TB_KEY_CTRL_M TB_KEY_CTRL_N - TB_KEY_CTRL_O TB_KEY_CTRL_P TB_KEY_CTRL_Q TB_KEY_CTRL_R - TB_KEY_CTRL_S TB_KEY_CTRL_T TB_KEY_CTRL_U TB_KEY_CTRL_V - TB_KEY_CTRL_W TB_KEY_CTRL_X TB_KEY_CTRL_Y TB_KEY_CTRL_Z - TB_MOD_ALT TB_MOD_SHIFT TB_MOD_CTRL TB_MOD_MOTION - TB_INPUT_CURRENT TB_INPUT_ESC TB_INPUT_ALT TB_INPUT_MOUSE - TB_OUTPUT_CURRENT TB_OUTPUT_NORMAL TB_OUTPUT_256 - TB_OUTPUT_216 TB_OUTPUT_GRAYSCALE TB_OUTPUT_TRUECOLOR - TB_DEFAULT TB_BLACK TB_RED TB_GREEN TB_YELLOW TB_BLUE - TB_MAGENTA TB_CYAN TB_WHITE TB_BOLD TB_UNDERLINE TB_REVERSE - TB_ITALIC TB_BLINK TB_DIM TB_HIDE_CURSOR) + TB_KEY_TAB TB_KEY_BACK_TAB TB_KEY_ENTER TB_KEY_ESC + TB_KEY_SPACE TB_KEY_MOUSE_LEFT TB_KEY_MOUSE_RIGHT + TB_KEY_MOUSE_MIDDLE TB_KEY_MOUSE_RELEASE + TB_KEY_MOUSE_WHEEL_UP TB_KEY_MOUSE_WHEEL_DOWN TB_KEY_CTRL_A + TB_KEY_CTRL_B TB_KEY_CTRL_C TB_KEY_CTRL_D TB_KEY_CTRL_E + TB_KEY_CTRL_F TB_KEY_CTRL_G TB_KEY_CTRL_H TB_KEY_CTRL_I + TB_KEY_CTRL_J TB_KEY_CTRL_K TB_KEY_CTRL_L TB_KEY_CTRL_M + TB_KEY_CTRL_N TB_KEY_CTRL_O TB_KEY_CTRL_P TB_KEY_CTRL_Q + TB_KEY_CTRL_R TB_KEY_CTRL_S TB_KEY_CTRL_T TB_KEY_CTRL_U + TB_KEY_CTRL_V TB_KEY_CTRL_W TB_KEY_CTRL_X TB_KEY_CTRL_Y + TB_KEY_CTRL_Z TB_MOD_ALT TB_MOD_SHIFT TB_MOD_CTRL + TB_MOD_MOTION TB_INPUT_CURRENT TB_INPUT_ESC TB_INPUT_ALT + TB_INPUT_MOUSE TB_OUTPUT_CURRENT TB_OUTPUT_NORMAL + TB_OUTPUT_256 TB_OUTPUT_216 TB_OUTPUT_GRAYSCALE + TB_OUTPUT_TRUECOLOR TB_DEFAULT TB_BLACK TB_RED TB_GREEN + TB_YELLOW TB_BLUE TB_MAGENTA TB_CYAN TB_WHITE TB_BOLD + TB_UNDERLINE TB_REVERSE TB_ITALIC TB_BLINK TB_DIM + TB_HIDE_CURSOR) (import (except (chezscheme) make-hash-table hash-table? iota \x31;+ \x31;- getenv path-extension path-absolute? thread? make-mutex @@ -88,18 +89,19 @@ (def TB_KEY_ARROW_DOWN (- 65535 19)) (def TB_KEY_ARROW_LEFT (- 65535 20)) (def TB_KEY_ARROW_RIGHT (- 65535 21)) + (def TB_KEY_BACK_TAB (- 65535 22)) (def TB_KEY_BACKSPACE 8) (def TB_KEY_BACKSPACE2 127) (def TB_KEY_TAB 9) (def TB_KEY_ENTER 13) (def TB_KEY_ESC 27) (def TB_KEY_SPACE 32) - (def TB_KEY_MOUSE_LEFT (- 65535 22)) - (def TB_KEY_MOUSE_RIGHT (- 65535 23)) - (def TB_KEY_MOUSE_MIDDLE (- 65535 24)) - (def TB_KEY_MOUSE_RELEASE (- 65535 25)) - (def TB_KEY_MOUSE_WHEEL_UP (- 65535 26)) - (def TB_KEY_MOUSE_WHEEL_DOWN (- 65535 27)) + (def TB_KEY_MOUSE_LEFT (- 65535 23)) + (def TB_KEY_MOUSE_RIGHT (- 65535 24)) + (def TB_KEY_MOUSE_MIDDLE (- 65535 25)) + (def TB_KEY_MOUSE_RELEASE (- 65535 26)) + (def TB_KEY_MOUSE_WHEEL_UP (- 65535 27)) + (def TB_KEY_MOUSE_WHEEL_DOWN (- 65535 28)) (def TB_KEY_CTRL_A 1) (def TB_KEY_CTRL_B 2) (def TB_KEY_CTRL_C 3) --- a/lib/jcode/ui/tui-keys.sls +++ b/lib/jcode/ui/tui-keys.sls @@ -49,6 +49,7 @@ "alt-enter" "enter")] [(= key TB_KEY_TAB) "tab"] + [(= key TB_KEY_BACK_TAB) "shift-tab"] [(= key TB_KEY_ESC) "escape"] [(= key TB_KEY_SPACE) (if (not (zero? (bitwise-and mod TB_MOD_CTRL))) --- a/lib/jcode/ui/tui.sls +++ b/lib/jcode/ui/tui.sls @@ -230,9 +230,8 @@ (tui-log " -> cycle-theme") (cycle-theme!) (app-state-dirty?-set! state #t)] - [(and (= key TB_KEY_TAB) - (not (zero? (bitwise-and mod TB_MOD_SHIFT)))) - (tui-log " -> toggle-mode (shift-tab)") + [(= key TB_KEY_BACK_TAB) + (tui-log " -> toggle-mode (back-tab)") (toggle-mode! state) (app-state-dirty?-set! state #t)] [(= key TB_KEY_PGUP) --- a/src/jcode/provider/provider.ss +++ b/src/jcode/provider/provider.ss @@ -9,7 +9,7 @@ provider-model) (import :std/text/json - :std/net/request + (except (std net request) http-post-stream) :std/net/tls-rustls :std/net/tcp :std/misc/string --- a/src/jcode/ui/jcode_tui_shim.c +++ b/src/jcode/ui/jcode_tui_shim.c @@ -11,12 +11,58 @@ #define TB_OPT_ATTR_W 32 #define TB_IMPL #include "termbox2.h" +#include <string.h> /* Shared event struct for peek/poll results */ static struct tb_event last_event; +/* Pre-extract hook: explicitly recognize escape sequences that terminfo + * may not have loaded reliably. Currently: + * \x1b[Z → Shift-Tab (TB_KEY_BACK_TAB) + * \x1b[1;2Z → Shift-Tab in modifyOtherKeys mode + * + * Termbox2's `global` is file-scope static; this shim is compiled in the + * same translation unit (TB_IMPL), so we can access it directly. + * + * Returning TB_OK tells termbox to consume `*consumed` bytes and use the + * populated event. Returning TB_ERR falls through to its normal parsing. */ +static int jcode_tb_extract_esc_pre(struct tb_event *event, size_t *consumed) { + if (global.in.len >= 3 && + (unsigned char)global.in.buf[0] == 0x1b && + global.in.buf[1] == '[' && + global.in.buf[2] == 'Z') { + event->type = TB_EVENT_KEY; + event->key = TB_KEY_BACK_TAB; + event->ch = 0; + event->mod = 0; + *consumed = 3; + return TB_OK; + } + if (global.in.len >= 6 && + (unsigned char)global.in.buf[0] == 0x1b && + global.in.buf[1] == '[' && + global.in.buf[2] == '1' && + global.in.buf[3] == ';' && + global.in.buf[4] == '2' && + global.in.buf[5] == 'Z') { + event->type = TB_EVENT_KEY; + event->key = TB_KEY_BACK_TAB; + event->ch = 0; + event->mod = 0; + *consumed = 6; + return TB_OK; + } + return TB_ERR; +} + /* ---- Lifecycle ---- */ -int jcode_tb_init(void) { return tb_init(); } +int jcode_tb_init(void) { + int rv = tb_init(); + if (rv == TB_OK) { + tb_set_func(TB_FUNC_EXTRACT_PRE, jcode_tb_extract_esc_pre); + } + return rv; +} void jcode_tb_shutdown(void) { tb_shutdown(); } /* ---- Dimensions ---- */ --- a/src/jcode/ui/tui-ffi.ss +++ b/src/jcode/ui/tui-ffi.ss @@ -26,7 +26,7 @@ TB_KEY_INSERT TB_KEY_DELETE TB_KEY_HOME TB_KEY_END TB_KEY_PGUP TB_KEY_PGDN TB_KEY_ARROW_UP TB_KEY_ARROW_DOWN TB_KEY_ARROW_LEFT TB_KEY_ARROW_RIGHT - TB_KEY_BACKSPACE TB_KEY_BACKSPACE2 TB_KEY_TAB TB_KEY_ENTER + TB_KEY_BACKSPACE TB_KEY_BACKSPACE2 TB_KEY_TAB TB_KEY_BACK_TAB TB_KEY_ENTER TB_KEY_ESC TB_KEY_SPACE ;; Constants — mouse TB_KEY_MOUSE_LEFT TB_KEY_MOUSE_RIGHT TB_KEY_MOUSE_MIDDLE @@ -140,6 +140,7 @@ (def TB_KEY_ARROW_DOWN (- #xFFFF 19)) (def TB_KEY_ARROW_LEFT (- #xFFFF 20)) (def TB_KEY_ARROW_RIGHT (- #xFFFF 21)) +(def TB_KEY_BACK_TAB (- #xFFFF 22)) (def TB_KEY_BACKSPACE #x08) (def TB_KEY_BACKSPACE2 #x7F) (def TB_KEY_TAB #x09) @@ -148,12 +149,12 @@ (def TB_KEY_SPACE #x20) ;; Mouse keys -(def TB_KEY_MOUSE_LEFT (- #xFFFF 22)) -(def TB_KEY_MOUSE_RIGHT (- #xFFFF 23)) -(def TB_KEY_MOUSE_MIDDLE (- #xFFFF 24)) -(def TB_KEY_MOUSE_RELEASE (- #xFFFF 25)) -(def TB_KEY_MOUSE_WHEEL_UP (- #xFFFF 26)) -(def TB_KEY_MOUSE_WHEEL_DOWN (- #xFFFF 27)) +(def TB_KEY_MOUSE_LEFT (- #xFFFF 23)) +(def TB_KEY_MOUSE_RIGHT (- #xFFFF 24)) +(def TB_KEY_MOUSE_MIDDLE (- #xFFFF 25)) +(def TB_KEY_MOUSE_RELEASE (- #xFFFF 26)) +(def TB_KEY_MOUSE_WHEEL_UP (- #xFFFF 27)) +(def TB_KEY_MOUSE_WHEEL_DOWN (- #xFFFF 28)) ;; Ctrl keys (def TB_KEY_CTRL_A #x01) (def TB_KEY_CTRL_B #x02) --- a/src/jcode/ui/tui-keys.ss +++ b/src/jcode/ui/tui-keys.ss @@ -50,6 +50,7 @@ ((= key TB_KEY_ENTER) (if (not (zero? (bitwise-and mod TB_MOD_ALT))) "alt-enter" "enter")) ((= key TB_KEY_TAB) "tab") + ((= key TB_KEY_BACK_TAB) "shift-tab") ((= key TB_KEY_ESC) "escape") ((= key TB_KEY_SPACE) (if (not (zero? (bitwise-and mod TB_MOD_CTRL))) "ctrl-space" "space")) --- a/src/jcode/ui/tui.ss +++ b/src/jcode/ui/tui.ss @@ -327,9 +327,9 @@ (cycle-theme!) (app-state-dirty?-set! state #t)) - ;; Global: Shift-Tab toggle PLAN/BUILD mode (opencode-style) - ((and (= key TB_KEY_TAB) (not (zero? (bitwise-and mod TB_MOD_SHIFT)))) - (tui-log " -> toggle-mode (shift-tab)") + ;; Global: Shift-Tab (BACK_TAB) toggle PLAN/BUILD mode (opencode-style) + ((= key TB_KEY_BACK_TAB) + (tui-log " -> toggle-mode (back-tab)") (toggle-mode! state) (app-state-dirty?-set! state #t))