changeset 24268:9257f3980f4a v8.2.2675

patch 8.2.2675: directory change in a terminal window shell is not followed Commit: https://github.com/vim/vim/commit/8b9abfd86c736ed3f298dfa8b50a962c118b3983 Author: Bram Moolenaar <Bram@vim.org> Date: Mon Mar 29 20:49:05 2021 +0200 patch 8.2.2675: directory change in a terminal window shell is not followed Problem: Directory change in a terminal window shell is not followed. Solution: Add the 'autoshelldir' option. (closes https://github.com/vim/vim/issues/6290)
author Bram Moolenaar <Bram@vim.org>
date Mon, 29 Mar 2021 21:00:03 +0200
parents 57c5644a9065
children 6a25a22b8d8c
files 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
diffstat 11 files changed, 145 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -749,6 +749,15 @@ A jump table for the options with a shor
 	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
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -605,6 +605,7 @@ Short explanation of each option:		*opti
 '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
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -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"))
--- a/src/charset.c
+++ b/src/charset.c
@@ -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.
--- a/src/feature.h
+++ b/src/feature.h
@@ -1148,6 +1148,12 @@
 #endif
 
 /*
+ * +autoshelldir	    'autoshelldir' option.
+ */
+#if defined(FEAT_TERMINAL)
+# define FEAT_AUTOSHELLDIR
+#endif
+/*
  * +textprop and +popupwin	Text PROPerties and POPUP windows
  */
 #if defined(FEAT_EVAL) && defined(FEAT_SYN_HL)
--- a/src/option.h
+++ b/src/option.h
@@ -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'
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -372,6 +372,15 @@ static struct vimoption options[] =
 			    {(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,
 			    (char_u *)&p_ai, PV_AI,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -4293,6 +4293,73 @@ handle_call_command(term_T *term, channe
 }
 
 /*
+ * 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, VTermStringFragme
 						    : 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;
 
--- a/src/testdir/check.vim
+++ b/src/testdir/check.vim
@@ -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()
--- a/src/testdir/test_terminal3.vim
+++ b/src/testdir/test_terminal3.vim
@@ -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
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2675,
+/**/
     2674,
 /**/
     2673,