]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1932: OSC terminal response hard to detect v9.1.1932
authorFoxe Chen <chen.foxe@gmail.com>
Thu, 27 Nov 2025 20:53:36 +0000 (20:53 +0000)
committerChristian Brabandt <cb@256bit.org>
Thu, 27 Nov 2025 20:53:36 +0000 (20:53 +0000)
Problem:  OSC terminal response hard to detect
Solution: Add the <OSC> and <xOSC> pseudo keys
          (Foxe Chen).

related: #18660
closes: #18799

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
runtime/doc/intro.txt
runtime/doc/tags
src/evalfunc.c
src/getchar.c
src/globals.h
src/keymap.h
src/misc2.c
src/term.c
src/testdir/test_termcodes.vim
src/version.c

index a25c31342835e4c4673ec16df16420edcce117c0..c09e8e744d12da631a8c4bd1b69043e3dc3b1823 100644 (file)
@@ -1,4 +1,4 @@
-*intro.txt*    For Vim version 9.1.  Last change: 2025 Nov 09
+*intro.txt*    For Vim version 9.1.  Last change: 2025 Nov 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -450,6 +450,8 @@ notation    meaning             equivalent  decimal value(s)        ~
 <Del>          delete                          127
 <CSI>          command sequence intro  ALT-Esc 155     *<CSI>*
 <xCSI>         CSI when typed in the GUI               *<xCSI>*
+<OSC>          operating system command        157     *<OSC>*
+<xOSC>         received OSC response                   *<xOSC>*
 
 <EOL>          end-of-line (can be <CR>, <NL> or <CR><NL>,
                depends on system and 'fileformat')     *<EOL>*
index 64b548a8662cd573d7fac3a7b5778af026927512..0e7000d2831e41eadd481f0eb5cae2aedf4a87b6 100644 (file)
@@ -3841,6 +3841,7 @@ $quote    eval.txt        /*$quote*
 <NL>   motion.txt      /*<NL>*
 <Nop>  map.txt /*<Nop>*
 <Nul>  intro.txt       /*<Nul>*
+<OSC>  intro.txt       /*<OSC>*
 <PageDown>     scroll.txt      /*<PageDown>*
 <PageUp>       scroll.txt      /*<PageUp>*
 <Plug> map.txt /*<Plug>*
@@ -3961,6 +3962,7 @@ $quote    eval.txt        /*$quote*
 <xHome>        term.txt        /*<xHome>*
 <xHome>-xterm  term.txt        /*<xHome>-xterm*
 <xLeft>        term.txt        /*<xLeft>*
+<xOSC> intro.txt       /*<xOSC>*
 <xRight>       term.txt        /*<xRight>*
 <xUp>  term.txt        /*<xUp>*
 =      change.txt      /*=*
index e770321eaff7aeb1678eb7054675195e0842c150..eca95bbcfd413902bb4d7f3aa6aaa5b5574ecd47 100644 (file)
@@ -5150,7 +5150,9 @@ f_feedkeys(typval_T *argvars, typval_T *rettv UNUSED)
                ++ex_normal_busy;
                ++in_feedkeys;
            }
+           ++allow_osc_key;
            exec_normal(TRUE, lowlevel, TRUE);
+           --allow_osc_key;
            if (!dangerous)
            {
                --ex_normal_busy;
index 3219fd2cef11fd0bb1f0a03980c952f349d82e03..a7b9741bd345182df06708a9b42a53ddd71c221d 100644 (file)
@@ -1977,6 +1977,9 @@ vgetc(void)
                }
                c = TO_SPECIAL(c2, c);
 
+               if (allow_osc_key == 0 && c == K_OSC)
+                   continue;
+
                // K_ESC is used to avoid ambiguity with the single Esc
                // character that might be the start of an escape sequence.
                // Convert it back to a single Esc here.
@@ -2452,6 +2455,7 @@ getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
 
     ++no_mapping;
     ++allow_keys;
+    ++allow_osc_key;
     if (!simplify)
        ++no_reduce_keys;
     for (;;)
@@ -2479,6 +2483,7 @@ getchar_common(typval_T *argvars, typval_T *rettv, int allow_number)
     }
     --no_mapping;
     --allow_keys;
+    --allow_osc_key;
     if (!simplify)
        --no_reduce_keys;
 
index 7e72ea744efbcd59721f466005543dafaf2b7a91..f412ab5fc297aa7422da1229f9197150a71ad07d 100644 (file)
@@ -2124,3 +2124,6 @@ INIT(= CLIENTSERVER_METHOD_NONE);
 // Path to socket of last client that communicated with us
 EXTERN char_u *client_socket INIT(= NULL);
 #endif
+
+// If the <xOSC> key should be propogated from vgetc()
+EXTERN int allow_osc_key INIT(= 0);
index e993e0999a437300d71d11706a95db6eae3d5042..b3be60c48d379789558602ec099603f4274d311a 100644 (file)
@@ -280,6 +280,7 @@ enum key_extra
     , KE_SID = 106             // <SID> special key, followed by {nr};
     , KE_ESC = 107             // used for K_ESC
     , KE_WILD = 108            // triggers wildmode completion
+    , KE_OSC = 109             // finished OSC sequence
 };
 
 /*
@@ -478,6 +479,7 @@ enum key_extra
 #define K_MOUSERIGHT   TERMCAP2KEY(KS_EXTRA, KE_MOUSERIGHT)
 
 #define K_CSI          TERMCAP2KEY(KS_EXTRA, KE_CSI)
+#define K_OSC          TERMCAP2KEY(KS_EXTRA, KE_OSC)
 #define K_SNR          TERMCAP2KEY(KS_EXTRA, KE_SNR)
 #define K_PLUG         TERMCAP2KEY(KS_EXTRA, KE_PLUG)
 #define K_CMDWIN       TERMCAP2KEY(KS_EXTRA, KE_CMDWIN)
index 8380e745c9b0ce7ecd6afc2bb3b85b784750d330..fcbf0dd1cd257b8ceb27f899fd575de91c6e0ab0 100644 (file)
@@ -1053,6 +1053,7 @@ static struct key_name_entry
     {TRUE, NL, STRING_INIT("NewLine"), TRUE},
     {TRUE, NL, STRING_INIT("NL"), FALSE},
     {TRUE, K_ZERO, STRING_INIT("Nul"), FALSE},
+    {TRUE, OSC, STRING_INIT("OSC"), FALSE},
     {TRUE, K_PAGEDOWN, STRING_INIT("PageDown"), FALSE},
     {TRUE, K_PAGEUP, STRING_INIT("PageUp"), FALSE},
     {TRUE, K_PE, STRING_INIT("PasteEnd"), FALSE},
@@ -1111,6 +1112,7 @@ static struct key_name_entry
     {TRUE, K_XF4, STRING_INIT("xF4"), FALSE},
     {TRUE, K_XHOME, STRING_INIT("xHome"), FALSE},
     {TRUE, K_XLEFT, STRING_INIT("xLeft"), FALSE},
+    {TRUE, K_OSC, STRING_INIT("xOSC"), FALSE},
     {TRUE, K_XRIGHT, STRING_INIT("xRight"), FALSE},
     {TRUE, K_XUP, STRING_INIT("xUp"), FALSE},
     {TRUE, K_ZEND, STRING_INIT("zEnd"), FALSE},
index c317ca60122e2382566efb41db0794e01629c43a..d3939ef4d7f7b6d9be6f1a8653e1dbba6522248b 100644 (file)
@@ -5921,6 +5921,9 @@ handle_osc(char_u *tp, int len, char_u *key_name, int *slen)
        // The whole OSC response may be larger than the typeahead buffer.
        // To handle this, keep reading data in and out of the typeahead
        // buffer until we read an OSC terminator or timeout.
+
+       // We can't use the previous buffer since we transferred ownership of it
+       // to the vim var.
        ga_init2(&osc_state.buf, 1, 1024);
 #ifdef ELAPSED_FUNC
        ELAPSED_INIT(osc_state.start_tv);
@@ -5933,7 +5936,6 @@ handle_osc(char_u *tp, int len, char_u *key_name, int *slen)
        last_char = ((char_u *)osc_state.buf.ga_data)[osc_state.buf.ga_len - 1];
 
     key_name[0] = (int)KS_EXTRA;
-    key_name[1] = (int)KE_IGNORE;
 
     // Read data and append to buffer. If we reach a terminator, then
     // finally set the vim var.
@@ -5945,6 +5947,8 @@ handle_osc(char_u *tp, int len, char_u *key_name, int *slen)
        {
            osc_state.processing = FALSE;
 
+           key_name[1] = (int)KE_OSC;
+
            ga_concat_len(&osc_state.buf, tp, i + 1 + (tp[i] == ESC));
            ga_append(&osc_state.buf, NUL);
            *slen = i + 1 + (tp[i] == ESC);
@@ -5962,6 +5966,8 @@ handle_osc(char_u *tp, int len, char_u *key_name, int *slen)
            return OK;
        }
 
+    key_name[1] = (int)KE_IGNORE;
+
 #ifdef ELAPSED_FUNC
     if (ELAPSED_FUNC(osc_state.start_tv) >= p_ost)
     {
@@ -6167,9 +6173,15 @@ check_termcode(
        }
 
        if (osc_state.processing)
+       {
            // Still processing OSC response data, go straight to handler
            // function.
+           tp[len] = NUL;
+           key_name[0] = NUL;
+           key_name[1] = NUL;
+           modifiers = 0;
            goto handle_osc;
+       }
 
        /*
         * Skip this position if the character does not appear as the first
@@ -6690,12 +6702,8 @@ handle_osc:
         */
        key = handle_x_keys(TERMCAP2KEY(key_name[0], key_name[1]));
 
-       if (osc_state.processing)
-           // We don't want to add anything to the typeahead buffer.
-           new_slen = 0;
-       else
-           // Add any modifier codes to our string.
-           new_slen = modifiers2keycode(modifiers, &key, string);
+       // Add any modifier codes to our string.
+       new_slen = modifiers2keycode(modifiers, &key, string);
 
        // Finally, add the special key code to our string
        key_name[0] = KEY2TERMCAP0(key);
@@ -6708,8 +6716,10 @@ handle_osc:
            else
                string[new_slen++] = key_name[1];
        }
-       else if (new_slen == 0 && key_name[0] == KS_EXTRA
-                                                 && key_name[1] == KE_IGNORE)
+       else if (osc_state.processing ||
+               (new_slen == 0
+                && key_name[0] == KS_EXTRA
+                && key_name[1] == KE_IGNORE))
        {
            // Do not put K_IGNORE into the buffer, do return KEYLEN_REMOVED
            // to indicate what happened.
index ffe3576ff0e26c5ee31cf21c973e6e1c43d628b2..5c30f5a8d1a82425270a85b917322ff0cefc15eb 100644 (file)
@@ -2847,14 +2847,38 @@ func Test_term_response_osc()
   " Test if large OSC responses (that must be processed in chunks) are handled
   let data = repeat('a', 3000)
 
-  call feedkeys("\<Esc>]12;" .. data .. "\x07", 'Lx!')
-  call assert_equal("\<Esc>]12;" .. data .. "\x07", v:termosc)
+  call feedkeys("\<Esc>]12;" .. data .. "\<Esc>\\", 'Lx!')
+  call assert_equal("\<Esc>]12;" .. data .. "\<Esc>\\", v:termosc)
 
   " Test small OSC responses
-  call feedkeys("\<Esc>]15;hello world!\07", 'Lx!')
+  call feedkeys("\<Esc>]15;hello world!\x07", 'Lx!')
   call assert_equal("\<Esc>]15;hello world!\x07", v:termosc)
 endfunc
 
+" Test if xOSC key is emitted.
+func Test_term_response_xosc_key()
+  CheckRunVimInTerminal
+
+  let lines =<< trim END
+    func Test()
+      while getcharstr(-1) != "\<xOSC>"
+      endwhile
+      call writefile(["done"], 'XTestResult')
+    endfunc
+  END
+  call writefile(lines, 'XTest', 'D')
+  defer delete('XTestResult')
+
+  let buf = RunVimInTerminal("-S XTest", {'rows': 10})
+  call TermWait(buf)
+  call term_sendkeys(buf, "\<Esc>:call Test()\<CR>")
+  call TermWait(buf)
+  call term_sendkeys(buf, "\<Esc>]52;hello;\<Esc>\\")
+  call TermWait(buf)
+  call WaitForAssert({-> assert_equal(["done"], readfile('XTestResult'))})
+  call StopVimInTerminal(buf)
+endfunc
+
 " This only checks if the sequence is recognized.
 func Test_term_rgb_response()
   set t_RF=x
index 75095d126de4755ee4d05477880d4ead863882af..38609746c24b79765f190719beedd64cf58d6602 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1932,
 /**/
     1931,
 /**/