Skip to content

Commit 8b9abfd

Browse files
committed
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 #6290)
1 parent 9dbe701 commit 8b9abfd

File tree

11 files changed

+145
-2
lines changed

11 files changed

+145
-2
lines changed

runtime/doc/options.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,15 @@ A jump table for the options with a short description can be found at |Q_op|.
749749
or selected.
750750
Note: When this option is on some plugins may not work.
751751

752+
*'autoshelldir'* *'asd'* *'noautoshelldir'* *'noasd'*
753+
'autoshelldir' 'asd' boolean (default off)
754+
global
755+
When on, Vim will change the current working directory whenever you
756+
change the directory of the shell running in a terminal window. You
757+
need proper setting-up, so whenever the shell's pwd changes an OSC 7
758+
escape sequence will be emitted. For example, on Linux, you can source
759+
/etc/profile.d/vte.sh in your shell profile if you use bash or zsh.
760+
752761
*'arabic'* *'arab'* *'noarabic'* *'noarab'*
753762
'arabic' 'arab' boolean (default off)
754763
local to window

runtime/doc/quickref.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ Short explanation of each option: *option-list*
605605
'ambiwidth' 'ambw' what to do with Unicode chars of ambiguous width
606606
'antialias' 'anti' Mac OS X: use smooth, antialiased fonts
607607
'autochdir' 'acd' change directory to the file in the current window
608+
'autoshelldir' 'asd' change directory to the shell's current directory
608609
'arabic' 'arab' for Arabic as a default second language
609610
'arabicshape' 'arshape' do shaping for Arabic characters
610611
'autoindent' 'ai' take indent for new line from previous line

runtime/optwin.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ if exists("+autochdir")
266266
call <SID>AddOption("autochdir", gettext("change to directory of file in buffer"))
267267
call <SID>BinOptionG("acd", &acd)
268268
endif
269+
call <SID>AddOption("autoshelldir", gettext("change to pwd of shell in terminal buffer"))
270+
call <SID>BinOptionG("asd", &asd)
269271
call <SID>AddOption("wrapscan", gettext("search commands wrap around the end of the buffer"))
270272
call <SID>BinOptionG("ws", &ws)
271273
call <SID>AddOption("incsearch", gettext("show match for partly typed search command"))

src/charset.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2005,7 +2005,8 @@ hex2nr(int c)
20052005
return c - '0';
20062006
}
20072007

2008-
#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) || defined(PROTO)
2008+
#if defined(FEAT_TERMRESPONSE) || defined(FEAT_GUI_GTK) \
2009+
|| defined(PROTO) || defined(FEAT_AUTOSHELLDIR)
20092010
/*
20102011
* Convert two hex characters to a byte.
20112012
* Return -1 if one of the characters is not hex.

src/feature.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,12 @@
11471147
# define FEAT_SYN_HL
11481148
#endif
11491149

1150+
/*
1151+
* +autoshelldir 'autoshelldir' option.
1152+
*/
1153+
#if defined(FEAT_TERMINAL)
1154+
# define FEAT_AUTOSHELLDIR
1155+
#endif
11501156
/*
11511157
* +textprop and +popupwin Text PROPerties and POPUP windows
11521158
*/

src/option.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ EXTERN char_u *p_ambw; // 'ambiwidth'
383383
#ifdef FEAT_AUTOCHDIR
384384
EXTERN int p_acd; // 'autochdir'
385385
#endif
386+
#ifdef FEAT_AUTOSHELLDIR
387+
EXTERN int p_asd; // 'autoshelldir'
388+
#endif
386389
EXTERN int p_ai; // 'autoindent'
387390
EXTERN int p_bin; // 'binary'
388391
EXTERN int p_bomb; // 'bomb'

src/optiondefs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,15 @@ static struct vimoption options[] =
370370
#else
371371
(char_u *)NULL, PV_NONE,
372372
{(char_u *)0L, (char_u *)0L}
373+
#endif
374+
SCTX_INIT},
375+
{"autoshelldir", "asd", P_BOOL|P_VI_DEF,
376+
#ifdef FEAT_AUTOSHELLDIR
377+
(char_u *)&p_asd, PV_NONE,
378+
{(char_u *)FALSE, (char_u *)0L}
379+
#else
380+
(char_u *)NULL, PV_NONE,
381+
{(char_u *)0L, (char_u *)0L}
373382
#endif
374383
SCTX_INIT},
375384
{"autoindent", "ai", P_BOOL|P_VI_DEF,

src/terminal.c

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4292,6 +4292,73 @@ handle_call_command(term_T *term, channel_T *channel, listitem_T *item)
42924292
ch_log(channel, "Calling function %s failed", func);
42934293
}
42944294

4295+
/*
4296+
* URL decoding (also know as Percent-encoding).
4297+
*
4298+
* Note this function currently is only used for decoding shell's
4299+
* OSC 7 escape sequence which we can assume all bytes are valid
4300+
* UTF-8 bytes. Thus we don't need to deal with invalid UTF-8
4301+
* encoding bytes like 0xfe, 0xff.
4302+
*/
4303+
static size_t
4304+
url_decode(const char *src, const size_t len, char_u *dst)
4305+
{
4306+
size_t i = 0, j = 0;
4307+
4308+
while (i < len)
4309+
{
4310+
if (src[i] == '%' && i + 2 < len)
4311+
{
4312+
dst[j] = hexhex2nr((char_u *)&src[i + 1]);
4313+
j++;
4314+
i += 3;
4315+
}
4316+
else
4317+
{
4318+
dst[j] = src[i];
4319+
i++;
4320+
j++;
4321+
}
4322+
}
4323+
dst[j] = '\0';
4324+
return j;
4325+
}
4326+
4327+
/*
4328+
* Sync terminal buffer's cwd with shell's pwd with the help of OSC 7.
4329+
*
4330+
* The OSC 7 sequence has the format of
4331+
* "\033]7;file://HOSTNAME/CURRENT/DIR\033\\"
4332+
* and what VTerm provides via VTermStringFragment is
4333+
* "file://HOSTNAME/CURRENT/DIR"
4334+
*/
4335+
static void
4336+
sync_shell_dir(VTermStringFragment *frag)
4337+
{
4338+
int offset = 7; // len of "file://" is 7
4339+
char *pos = (char *)frag->str + offset;
4340+
char_u *new_dir;
4341+
4342+
// remove HOSTNAME to get PWD
4343+
while (*pos != '/' && offset < frag->len)
4344+
{
4345+
offset += 1;
4346+
pos += 1;
4347+
}
4348+
4349+
if (offset >= frag->len)
4350+
{
4351+
semsg(_(e_failed_to_extract_pwd_from_str_check_your_shell_config),
4352+
frag->str);
4353+
return;
4354+
}
4355+
4356+
new_dir = alloc(frag->len - offset + 1);
4357+
url_decode(pos, frag->len-offset, new_dir);
4358+
changedir_func(new_dir, TRUE, CDSCOPE_WINDOW);
4359+
vim_free(new_dir);
4360+
}
4361+
42954362
/*
42964363
* Called by libvterm when it cannot recognize an OSC sequence.
42974364
* We recognize a terminal API command.
@@ -4306,7 +4373,13 @@ parse_osc(int command, VTermStringFragment frag, void *user)
43064373
: term->tl_job->jv_channel;
43074374
garray_T *gap = &term->tl_osc_buf;
43084375

4309-
// We recognize only OSC 5 1 ; {command}
4376+
// We recognize only OSC 5 1 ; {command} and OSC 7 ; {command}
4377+
if (p_asd && command == 7)
4378+
{
4379+
sync_shell_dir(&frag);
4380+
return 1;
4381+
}
4382+
43104383
if (command != 51)
43114384
return 0;
43124385

src/testdir/check.vim

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ func CheckLinux()
9292
endif
9393
endfunc
9494

95+
" Command to check for not running on a BSD system.
96+
command CheckNotBSD call CheckNotBSD()
97+
func CheckNotBSD()
98+
if has('bsd')
99+
throw 'Skipped: does not work on BSD'
100+
endif
101+
endfunc
102+
95103
" Command to check that making screendumps is supported.
96104
" Caller must source screendump.vim
97105
command CheckScreendump call CheckScreendump()

src/testdir/test_terminal3.vim

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,35 @@ func Test_term_mouse()
475475
call delete('Xbuf')
476476
endfunc
477477

478+
" Test for sync buffer cwd with shell's pwd
479+
func Test_terminal_sync_shell_dir()
480+
CheckUnix
481+
" The test always use sh (see src/testdir/unix.vim).
482+
" However, BSD's sh doesn't seem to play well with OSC 7 escape sequence.
483+
CheckNotBSD
484+
485+
set asd
486+
" , is
487+
" 1. a valid character for directory names
488+
" 2. a reserved character in url-encoding
489+
let chars = ",a"
490+
" "," is url-encoded as '%2C'
491+
let chars_url = "%2Ca"
492+
let tmpfolder = fnamemodify(tempname(),':h').'/'.chars
493+
let tmpfolder_url = fnamemodify(tempname(),':h').'/'.chars_url
494+
call mkdir(tmpfolder, "p")
495+
let buf = Run_shell_in_terminal({})
496+
call term_sendkeys(buf, "echo -ne $'\\e\]7;file://".tmpfolder_url."\\a'\<CR>")
497+
"call term_sendkeys(buf, "cd ".tmpfolder."\<CR>")
498+
call TermWait(buf)
499+
if has("mac")
500+
let expected = "/private".tmpfolder
501+
else
502+
let expected = tmpfolder
503+
endif
504+
call assert_equal(expected, getcwd(winnr()))
505+
endfunc
506+
478507
" Test for modeless selection in a terminal
479508
func Test_term_modeless_selection()
480509
CheckUnix

0 commit comments

Comments
 (0)