]> granicus.if.org Git - vim/commitdiff
patch 8.2.2675: directory change in a terminal window shell is not followed v8.2.2675
authorBram Moolenaar <Bram@vim.org>
Mon, 29 Mar 2021 18:49:05 +0000 (20:49 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 29 Mar 2021 18:49:05 +0000 (20:49 +0200)
Problem:    Directory change in a terminal window shell is not followed.
Solution:   Add the 'autoshelldir' option. (closes #6290)

runtime/doc/options.txt
runtime/doc/quickref.txt
runtime/optwin.vim
src/charset.c
src/feature.h
src/option.h
src/optiondefs.h
src/terminal.c
src/testdir/check.vim
src/testdir/test_terminal3.vim
src/version.c

index 7fcdfbe953a1f143b40d9b97852edb29ebc57213..babe7217d02724ff990cb852ac7563015ea320a5 100644 (file)
@@ -749,6 +749,15 @@ A jump table for the options with a short description can be found at |Q_op|.
        or selected.
        Note: When this option is on some plugins may not work.
 
+                       *'autoshelldir'* *'asd'* *'noautoshelldir'* *'noasd'*
+'autoshelldir' 'asd'   boolean (default off)
+                       global
+       When on, Vim will change the current working directory whenever you
+       change the directory of the shell running in a terminal window. You
+       need proper setting-up, so whenever the shell's pwd changes an OSC 7
+       escape sequence will be emitted. For example, on Linux, you can source
+       /etc/profile.d/vte.sh in your shell profile if you use bash or zsh.
+
                                *'arabic'* *'arab'* *'noarabic'* *'noarab'*
 'arabic' 'arab'                boolean (default off)
                        local to window
index c0a070d2482a84731b99bb5fe15776109024b2bd..ead42f9968b63ce03676b4d59d397152e2ee865a 100644 (file)
@@ -605,6 +605,7 @@ Short explanation of each option:           *option-list*
 'ambiwidth'      'ambw'    what to do with Unicode chars of ambiguous width
 'antialias'      'anti'    Mac OS X: use smooth, antialiased fonts
 'autochdir'      'acd'     change directory to the file in the current window
+'autoshelldir'   'asd'     change directory to the shell's current directory
 'arabic'         'arab'    for Arabic as a default second language
 'arabicshape'    'arshape' do shaping for Arabic characters
 'autoindent'     'ai'      take indent for new line from previous line
index 45aa8f9307bb95b44b297903ed13670b76f2cb22..176f9eafc81f5556797baf7c2113aba3e5e6977e 100644 (file)
@@ -266,6 +266,8 @@ if exists("+autochdir")
   call <SID>AddOption("autochdir", gettext("change to directory of file in buffer"))
   call <SID>BinOptionG("acd", &acd)
 endif
+call <SID>AddOption("autoshelldir", gettext("change to pwd of shell in terminal buffer"))
+call <SID>BinOptionG("asd", &asd)
 call <SID>AddOption("wrapscan", gettext("search commands wrap around the end of the buffer"))
 call <SID>BinOptionG("ws", &ws)
 call <SID>AddOption("incsearch", gettext("show match for partly typed search command"))
index d06a273643c62ce6e84064baac7c5d7f78ec8694..db0c146f88c83e06b0551195ed50c974bc317b13 100644 (file)
@@ -2005,7 +2005,8 @@ hex2nr(int c)
     return c - '0';
 }
 
-#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) || defined(PROTO)
+#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) \
+        || defined(PROTO) || defined(FEAT_AUTOSHELLDIR)
 /*
  * Convert two hex characters to a byte.
  * Return -1 if one of the characters is not hex.
index 5f6f800c22b0a2a57984b0bfb3b89e7f2d1521af..4df73d1cb4f7ae9a3dd721754996547cd626044b 100644 (file)
 # define FEAT_SYN_HL
 #endif
 
+/*
+ * +autoshelldir           'autoshelldir' option.
+ */
+#if defined(FEAT_TERMINAL)
+# define FEAT_AUTOSHELLDIR
+#endif
 /*
  * +textprop and +popupwin     Text PROPerties and POPUP windows
  */
index 349268c3d41deedab15b1e578dfb6f9610701526..0b1183fecfa4643ebef2549fa4a5649788c34627 100644 (file)
@@ -383,6 +383,9 @@ EXTERN char_u       *p_ambw;        // 'ambiwidth'
 #ifdef FEAT_AUTOCHDIR
 EXTERN int     p_acd;          // 'autochdir'
 #endif
+#ifdef FEAT_AUTOSHELLDIR
+EXTERN int     p_asd;          // 'autoshelldir'
+#endif
 EXTERN int     p_ai;           // 'autoindent'
 EXTERN int     p_bin;          // 'binary'
 EXTERN int     p_bomb;         // 'bomb'
index dcd195d7efb6e51f693cb4b2d7136821a636c19b..42f355c287018af95613093d4c81028e708ff1af 100644 (file)
@@ -370,6 +370,15 @@ static struct vimoption options[] =
 #else
                            (char_u *)NULL, PV_NONE,
                            {(char_u *)0L, (char_u *)0L}
+#endif
+                           SCTX_INIT},
+    {"autoshelldir",  "asd",   P_BOOL|P_VI_DEF,
+#ifdef FEAT_AUTOSHELLDIR
+                           (char_u *)&p_asd, PV_NONE,
+                           {(char_u *)FALSE, (char_u *)0L}
+#else
+                           (char_u *)NULL, PV_NONE,
+                           {(char_u *)0L, (char_u *)0L}
 #endif
                            SCTX_INIT},
     {"autoindent",  "ai",   P_BOOL|P_VI_DEF,
index 4c43ea7482e7dfbb08e7bb7cdb401433692af8d2..6f07055ebc11efbad20fd7ad374556b9f76102d8 100644 (file)
@@ -4292,6 +4292,73 @@ handle_call_command(term_T *term, channel_T *channel, listitem_T *item)
        ch_log(channel, "Calling function %s failed", func);
 }
 
+/*
+ * URL decoding (also know as Percent-encoding).
+ *
+ * Note this function currently is only used for decoding shell's
+ * OSC 7 escape sequence which we can assume all bytes are valid
+ * UTF-8 bytes. Thus we don't need to deal with invalid UTF-8
+ * encoding bytes like 0xfe, 0xff.
+ */
+    static size_t
+url_decode(const char *src, const size_t len, char_u *dst)
+{
+    size_t i = 0, j = 0;
+
+    while (i < len)
+    {
+       if (src[i] == '%' && i + 2 < len)
+       {
+           dst[j] = hexhex2nr((char_u *)&src[i + 1]);
+           j++;
+           i += 3;
+       }
+       else
+       {
+           dst[j] = src[i];
+           i++;
+           j++;
+       }
+    }
+    dst[j] = '\0';
+    return j;
+}
+
+/*
+ * Sync terminal buffer's cwd with shell's pwd with the help of OSC 7.
+ *
+ * The OSC 7 sequence has the format of
+ * "\033]7;file://HOSTNAME/CURRENT/DIR\033\\"
+ * and what VTerm provides via VTermStringFragment is
+ * "file://HOSTNAME/CURRENT/DIR"
+ */
+    static void
+sync_shell_dir(VTermStringFragment *frag)
+{
+    int       offset = 7; // len of "file://" is 7
+    char      *pos = (char *)frag->str + offset;
+    char_u    *new_dir;
+
+    // remove HOSTNAME to get PWD
+    while (*pos != '/' && offset < frag->len)
+    {
+        offset += 1;
+        pos += 1;
+    }
+
+    if (offset >= frag->len)
+    {
+        semsg(_(e_failed_to_extract_pwd_from_str_check_your_shell_config),
+                                                                   frag->str);
+        return;
+    }
+
+    new_dir = alloc(frag->len - offset + 1);
+    url_decode(pos, frag->len-offset, new_dir);
+    changedir_func(new_dir, TRUE, CDSCOPE_WINDOW);
+    vim_free(new_dir);
+}
+
 /*
  * Called by libvterm when it cannot recognize an OSC sequence.
  * We recognize a terminal API command.
@@ -4306,7 +4373,13 @@ parse_osc(int command, VTermStringFragment frag, void *user)
                                                    : term->tl_job->jv_channel;
     garray_T   *gap = &term->tl_osc_buf;
 
-    // We recognize only OSC 5 1 ; {command}
+    // We recognize only OSC 5 1 ; {command} and OSC 7 ; {command}
+    if (p_asd && command == 7)
+    {
+       sync_shell_dir(&frag);
+       return 1;
+    }
+
     if (command != 51)
        return 0;
 
index 6190399e5d6aba2fccf04a76632dc1211682c31c..4c5f3eef5d28d9e39811621e5b6ad011848b7b2c 100644 (file)
@@ -92,6 +92,14 @@ func CheckLinux()
   endif
 endfunc
 
+" Command to check for not running on a BSD system.
+command CheckNotBSD call CheckNotBSD()
+func CheckNotBSD()
+  if has('bsd')
+    throw 'Skipped: does not work on BSD'
+  endif
+endfunc
+
 " Command to check that making screendumps is supported.
 " Caller must source screendump.vim
 command CheckScreendump call CheckScreendump()
index 037d935582c928e049c0c21255371687c05a18ca..ab926b686a4afb6139df36728121c0d659b37292 100644 (file)
@@ -475,6 +475,35 @@ func Test_term_mouse()
   call delete('Xbuf')
 endfunc
 
+" Test for sync buffer cwd with shell's pwd
+func Test_terminal_sync_shell_dir()
+  CheckUnix
+  " The test always use sh (see src/testdir/unix.vim).
+  " However, BSD's sh doesn't seem to play well with OSC 7 escape sequence.
+  CheckNotBSD
+
+  set asd
+  " , is
+  "  1. a valid character for directory names
+  "  2. a reserved character in url-encoding
+  let chars = ",a"
+  " "," is url-encoded as '%2C'
+  let chars_url = "%2Ca"
+  let tmpfolder = fnamemodify(tempname(),':h').'/'.chars
+  let tmpfolder_url = fnamemodify(tempname(),':h').'/'.chars_url
+  call mkdir(tmpfolder, "p")
+  let buf = Run_shell_in_terminal({})
+  call term_sendkeys(buf, "echo -ne $'\\e\]7;file://".tmpfolder_url."\\a'\<CR>")
+  "call term_sendkeys(buf, "cd ".tmpfolder."\<CR>")
+  call TermWait(buf)
+  if has("mac")
+    let expected = "/private".tmpfolder
+  else
+    let expected = tmpfolder
+  endif
+  call assert_equal(expected, getcwd(winnr()))
+endfunc
+
 " Test for modeless selection in a terminal
 func Test_term_modeless_selection()
   CheckUnix
index da20571e26d77202881e413f25b192ca2ba6c1a0..bc47c43909730909a00034a91fc58fed9530ff3f 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2675,
 /**/
     2674,
 /**/