kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit cedfcc8aee4a6e9d775a902f56388573592f6546
parent 79fec5699a0062b63c3c49b97b4adafc61a1d5de
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 12:19:11 -0700

dbg: Ctrl-G edits the current REPL line in $EDITOR

The line editor now binds Ctrl-G to drop the currently typed line into
$EDITOR (seeded with the buffer via driver_edit_temp, tagged with the
current JIT language extension), then reloads the edited text back into
the prompt. Complements the `edit` command, which opens an empty buffer.

driver_read_line_edit gains an `edit_suffix` parameter; the POSIX raw-mode
editor intercepts Ctrl-G (0x07), the cooked-mode Windows fallback ignores
it. Updated the cmdline + in-REPL help and the repl-help golden.

Diffstat:
Mdriver/cmd/dbg.c | 14++++++++++++--
Mdriver/env.h | 8+++++++-
Mdriver/env/posix.c | 30+++++++++++++++++++++++++++++-
Mdriver/env/windows.c | 7++++++-
Mtest/dbg/cases/repl-help/expected | 1+
5 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/driver/cmd/dbg.c b/driver/cmd/dbg.c @@ -128,6 +128,8 @@ void driver_help_dbg(void) { "snippet\n" " { ... } same as jit { ... }\n" " edit [LANG|NAME] edit and append a language snippet\n" + " (Ctrl-G edits the current input line " + "in $EDITOR)\n" " expr EXPR | expr { ... } compile and call an expression " "thunk\n" " EXPR same as expr EXPR\n" @@ -2513,6 +2515,8 @@ static void dbg_cmd_help(void) { "snippet\n" " { ... } same as jit { ... }\n" " edit [LANG|NAME], e [...] edit and append a language snippet\n" + " Ctrl-G edit the current input line in " + "$EDITOR\n" " expr EXPR | expr { ... } compile and call an expression thunk\n" " EXPR same as expr EXPR\n" " LANG defaults to the selected " @@ -3207,8 +3211,14 @@ static void dbg_repl(DbgState* s) { /* Phase 3: interactive loop. */ for (;;) { int n; - n = driver_read_line_edit(s->env, "(kit) ", line, sizeof(line), - &s->line_history, dbg_complete, s); + char edit_suffix[DBG_JIT_SUFFIX_CAP]; + /* Ctrl-G edits the typed line in $EDITOR; tag the temp file with the + * current language's extension so editors highlight it. Recomputed each + * iteration since `:language` can change the default mid-session. */ + n = driver_read_line_edit( + s->env, "(kit) ", line, sizeof(line), &s->line_history, dbg_complete, s, + dbg_jit_language_suffix(s->compiler, s->default_jit_lang, edit_suffix, + sizeof edit_suffix)); if (n == 0) { /* EOF — Ctrl-D */ driver_printf("\n"); return; diff --git a/driver/env.h b/driver/env.h @@ -354,9 +354,15 @@ typedef void (*DriverLineCompleteFn)(void* user, const char* line, int driver_line_completion_add(DriverLineCompletionList*, const char* text, size_t len); +/* Raw-mode line editor with history + tab completion. Ctrl-G drops the + * currently typed line into $EDITOR (seeded via driver_edit_temp using + * `edit_suffix` for the temp file's extension) and reloads the edited text + * back into the buffer, so the prompt is populated with what was typed. + * `edit_suffix` may be NULL to use an extension-less temp file; it is ignored + * on hosts whose line editor reads in cooked mode (no key interception). */ int driver_read_line_edit(DriverEnv*, const char* prompt, char* buf, size_t cap, DriverLineHistory*, DriverLineCompleteFn, - void* complete_user); + void* complete_user, const char* edit_suffix); void driver_line_history_fini(DriverEnv*, DriverLineHistory*); /* Flush the host stdout. The dbg REPL prompt has no trailing newline, so diff --git a/driver/env/posix.c b/driver/env/posix.c @@ -1211,7 +1211,8 @@ static void line_complete(DriverEnv* env, char* buf, size_t cap, size_t* len, int driver_read_line_edit(DriverEnv* env, const char* prompt, char* buf, size_t cap, DriverLineHistory* hist, - DriverLineCompleteFn complete, void* complete_user) { + DriverLineCompleteFn complete, void* complete_user, + const char* edit_suffix) { struct termios orig; struct termios raw; char* saved = NULL; @@ -1281,6 +1282,33 @@ int driver_read_line_edit(DriverEnv* env, const char* prompt, char* buf, } continue; } + if (c == 7) { + /* Ctrl-G: hand the currently typed line to $EDITOR (seeded with the + * buffer), then reload whatever was saved. Drop to cooked mode so the + * editor owns the terminal, then restore raw mode and redraw. */ + uint8_t* edited = NULL; + size_t edited_size = 0; + buf[len] = '\0'; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig); + if (driver_edit_temp(env, edit_suffix, (const uint8_t*)buf, len, &edited, + &edited_size)) { + size_t n = edited_size; + /* Editors append a trailing newline; drop trailing CR/LF so the + * reloaded prompt stays a single line. */ + while (n > 0 && (edited[n - 1] == '\n' || edited[n - 1] == '\r')) --n; + if (n >= cap) n = cap - 1u; + memcpy(buf, edited, n); + buf[n] = '\0'; + len = n; + cursor = n; + have_saved = 0; /* edited text supersedes history navigation */ + hist_pos = hist ? hist->count : 0; + } + if (edited) driver_free(env, edited, edited_size); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); + line_redraw(prompt, buf, len, cursor); + continue; + } if (c == '\t') { buf[len] = '\0'; line_complete(env, buf, cap, &len, &cursor, prompt, complete, diff --git a/driver/env/windows.c b/driver/env/windows.c @@ -1415,10 +1415,15 @@ static int win_line_history_add(DriverEnv* env, DriverLineHistory* h, int driver_read_line_edit(DriverEnv* env, const char* prompt, char* buf, size_t cap, DriverLineHistory* hist, - DriverLineCompleteFn complete, void* complete_user) { + DriverLineCompleteFn complete, void* complete_user, + const char* edit_suffix) { int n; (void)complete; (void)complete_user; + /* This host reads in cooked mode, so there is no keystroke interception + * for a Ctrl-G inline-edit binding; the `edit` REPL command remains the + * editor entry point here. */ + (void)edit_suffix; if (prompt && *prompt) { fputs(prompt, stdout); fflush(stdout); diff --git a/test/dbg/cases/repl-help/expected b/test/dbg/cases/repl-help/expected @@ -13,6 +13,7 @@ Commands (abbrev. shown): jit [LANG|NAME] { ... } compile and append a language snippet { ... } same as jit { ... } edit [LANG|NAME], e [...] edit and append a language snippet + Ctrl-G edit the current input line in $EDITOR expr EXPR | expr { ... } compile and call an expression thunk EXPR same as expr EXPR LANG defaults to the selected language