commit de87ceb3beaa0ee787d42f8476814cfdf6aa41e1
parent 966b1da1831e3c98d83dcb0201b04eaa314f2d58
Author: Gregory Anders <greg@gpanders.com>
Date: Wed, 11 Jun 2025 08:26:58 -0500
feat(tui): support APC queries in TermResponse (#34426)
Add support for APC sequences to libtermkey and the TermResponse
autocommand event.
Diffstat:
8 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
@@ -4028,8 +4028,8 @@ nvim_ui_term_event({event}, {value}) *nvim_ui_term_event()*
Tells Nvim when a terminal event has occurred
The following terminal events are supported:
- • "termresponse": The terminal sent an OSC or DCS response sequence to
- Nvim. The payload is the received response. Sets |v:termresponse| and
+ • "termresponse": The terminal sent an OSC, DCS, or APC response sequence
+ to Nvim. The payload is the received response. Sets |v:termresponse| and
fires |TermResponse|.
Attributes: ~
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
@@ -1045,7 +1045,7 @@ TermRequest When a |:terminal| child process emits an OSC,
autocommand defined without |autocmd-nested|.
*TermResponse*
-TermResponse When Nvim receives an OSC or DCS response from
+TermResponse When Nvim receives an OSC, DCS, or APC response from
the host terminal. Sets |v:termresponse|. The
|event-data| is a table with the following fields:
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
@@ -221,7 +221,7 @@ TREESITTER
TUI
-• todo
+• |TermResponse| now supports APC query responses.
UI
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
@@ -540,7 +540,7 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa
///
/// The following terminal events are supported:
///
-/// - "termresponse": The terminal sent an OSC or DCS response sequence to
+/// - "termresponse": The terminal sent an OSC, DCS, or APC response sequence to
/// Nvim. The payload is the received response. Sets
/// |v:termresponse| and fires |TermResponse|.
///
diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c
@@ -463,7 +463,8 @@ static void tk_getkeys(TermInput *input, bool force)
handle_modereport(input, &key);
} else if (key.type == TERMKEY_TYPE_UNKNOWN_CSI) {
handle_unknown_csi(input, &key);
- } else if (key.type == TERMKEY_TYPE_OSC || key.type == TERMKEY_TYPE_DCS) {
+ } else if (key.type == TERMKEY_TYPE_OSC || key.type == TERMKEY_TYPE_DCS
+ || key.type == TERMKEY_TYPE_APC) {
handle_term_response(input, &key);
}
}
@@ -600,6 +601,9 @@ static void handle_term_response(TermInput *input, const TermKeyKey *key)
case TERMKEY_TYPE_DCS:
kv_printf(response, "\x1bP%s", str);
break;
+ case TERMKEY_TYPE_APC:
+ kv_printf(response, "\x1b_%s", str);
+ break;
default:
// Key type already checked for OSC/DCS in termkey_interpret_string
UNREACHABLE;
diff --git a/src/nvim/tui/termkey/driver-csi.c b/src/nvim/tui/termkey/driver-csi.c
@@ -890,8 +890,22 @@ static TermKeyResult peekkey_ctrlstring(TermKey *tk, TermKeyCsi *csi, size_t int
strncpy(csi->saved_string, (char *)tk->buffer + tk->buffstart + introlen, len); // NOLINT(runtime/printf)
csi->saved_string[len] = 0;
- key->type = (CHARAT(introlen - 1) & 0x1f) == 0x10
- ? TERMKEY_TYPE_DCS : TERMKEY_TYPE_OSC;
+ char type = CHARAT(introlen - 1) & 0x1f;
+ switch (type) {
+ case 0x10:
+ key->type = TERMKEY_TYPE_DCS;
+ break;
+ case 0x1d:
+ key->type = TERMKEY_TYPE_OSC;
+ break;
+ case 0x1f:
+ key->type = TERMKEY_TYPE_APC;
+ break;
+ default:
+ // Unreachable
+ abort();
+ }
+
key->code.number = csi->saved_string_id;
key->modifiers = 0;
@@ -918,6 +932,7 @@ TermKeyResult peekkey_csi(TermKey *tk, void *info, TermKeyKey *key, int force, s
case 0x50: // ESC-prefixed DCS
case 0x5d: // ESC-prefixed OSC
+ case 0x5f: // ESC-prefixed APC
return peekkey_ctrlstring(tk, csi, 2, key, force, nbytep);
case 0x5b: // ESC-prefixed CSI
diff --git a/src/nvim/tui/termkey/termkey.c b/src/nvim/tui/termkey/termkey.c
@@ -182,6 +182,9 @@ static void print_key(TermKey *tk, TermKeyKey *key)
case TERMKEY_TYPE_OSC:
fprintf(stderr, "Operating System Control");
break;
+ case TERMKEY_TYPE_APC:
+ fprintf(stderr, "Application Program Command");
+ break;
case TERMKEY_TYPE_UNKNOWN_CSI:
fprintf(stderr, "unknown CSI\n");
break;
@@ -231,7 +234,8 @@ TermKeyResult termkey_interpret_string(TermKey *tk, const TermKeyKey *key, const
}
if (key->type != TERMKEY_TYPE_DCS
- && key->type != TERMKEY_TYPE_OSC) {
+ && key->type != TERMKEY_TYPE_OSC
+ && key->type != TERMKEY_TYPE_APC) {
return TERMKEY_RES_NONE;
}
@@ -1270,6 +1274,9 @@ size_t termkey_strfkey(TermKey *tk, char *buffer, size_t len, TermKeyKey *key, T
case TERMKEY_TYPE_OSC:
l = (size_t)snprintf(buffer + pos, len - pos, "OSC");
break;
+ case TERMKEY_TYPE_APC:
+ l = (size_t)snprintf(buffer + pos, len - pos, "APC");
+ break;
case TERMKEY_TYPE_UNKNOWN_CSI:
l = (size_t)snprintf(buffer + pos, len - pos, "CSI %c", key->code.number & 0xff);
break;
diff --git a/src/nvim/tui/termkey/termkey_defs.h b/src/nvim/tui/termkey/termkey_defs.h
@@ -111,6 +111,7 @@ typedef enum {
TERMKEY_TYPE_MODEREPORT,
TERMKEY_TYPE_DCS,
TERMKEY_TYPE_OSC,
+ TERMKEY_TYPE_APC,
// add other recognised types here
TERMKEY_TYPE_UNKNOWN_CSI = -1,