diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 8546478935..c81420d1f2 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -228,6 +228,10 @@ the editor. however a UI might still use such options when rendering raw text sent from Nvim, like for |ui-cmdline|. +["chdir", path] ~ + The |current-directory| of the embedded Nvim process changed to + `path`. + ["mode_change", mode, mode_idx] ~ Editor mode changed. The `mode` parameter is a string representing the current mode. `mode_idx` is an index into the array emitted in diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index bda0c72423..c2f02c34f8 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -39,6 +39,8 @@ void screenshot(String path) FUNC_API_SINCE(7); void option_set(String name, Object value) FUNC_API_SINCE(4); +void chdir(String path) + FUNC_API_SINCE(12); // Stop event is not exported as such, represented by EOF in the msgpack stream. void stop(void) FUNC_API_NOEXPORT; diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 09203990bb..d80539708d 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -33,6 +33,7 @@ # include #endif +#include "nvim/api/private/helpers.h" #include "nvim/ascii_defs.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" @@ -44,6 +45,7 @@ #include "nvim/os/os.h" #include "nvim/path.h" #include "nvim/types_defs.h" +#include "nvim/ui.h" #include "nvim/vim_defs.h" #ifdef HAVE_SYS_UIO_H @@ -90,7 +92,11 @@ int os_chdir(const char *path) smsg(0, "chdir(%s)", path); verbose_leave(); } - return uv_chdir(path); + int err = uv_chdir(path); + if (err == 0) { + ui_call_chdir(cstr_as_string((char *)path)); + } + return err; } /// Get the name of current directory. diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 6b8b73a2a0..f9560ce076 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1500,6 +1500,14 @@ void tui_option_set(TUIData *tui, String name, Object value) } } +void tui_chdir(TUIData *tui, String path) +{ + int err = uv_chdir(path.data); + if (err != 0) { + ELOG("Failed to chdir to %s: %s", path.data, strerror(err)); + } +} + void tui_raw_line(TUIData *tui, Integer g, Integer linerow, Integer startcol, Integer endcol, Integer clearcol, Integer clearattr, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 8888535878..316342c028 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -384,6 +384,12 @@ void ui_attach_impl(UI *ui, uint64_t chanid) ui_refresh_options(); resettitle(); + char cwd[MAXPATHL]; + size_t cwdlen = sizeof(cwd); + if (uv_cwd(cwd, &cwdlen) == 0) { + ui_call_chdir((String){ .data = cwd, .size = cwdlen }); + } + for (UIExtension i = kUIGlobalCount; (int)i < kUIExtCount; i++) { ui_set_ext_option(ui, i, ui->ui_ext[i]); } diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua index e655ee1b54..f6bdd2215d 100644 --- a/test/functional/ui/embed_spec.lua +++ b/test/functional/ui/embed_spec.lua @@ -171,6 +171,56 @@ describe('--embed UI', function() } eq({ [16711935] = true }, seen) -- we only saw the last one, despite 16777215 was set internally earlier end) + + it('updates cwd of attached UI #21771', function() + clear { args_rm = { '--headless' } } + + local screen = Screen.new(40, 8) + screen:attach() + + screen:expect { + condition = function() + eq(helpers.paths.test_source_path, screen.pwd) + end, + } + + -- Change global cwd + helpers.command(string.format('cd %s/src/nvim', helpers.paths.test_source_path)) + + screen:expect { + condition = function() + eq(string.format('%s/src/nvim', helpers.paths.test_source_path), screen.pwd) + end, + } + + -- Split the window and change the cwd in the split + helpers.command('new') + helpers.command(string.format('lcd %s/test', helpers.paths.test_source_path)) + + screen:expect { + condition = function() + eq(string.format('%s/test', helpers.paths.test_source_path), screen.pwd) + end, + } + + -- Move to the original window + helpers.command('wincmd p') + + screen:expect { + condition = function() + eq(string.format('%s/src/nvim', helpers.paths.test_source_path), screen.pwd) + end, + } + + -- Change global cwd again + helpers.command(string.format('cd %s', helpers.paths.test_source_path)) + + screen:expect { + condition = function() + eq(helpers.paths.test_source_path, screen.pwd) + end, + } + end) end) describe('--embed --listen UI', function() diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua index 644ee910b6..07333f2e21 100644 --- a/test/functional/ui/screen.lua +++ b/test/functional/ui/screen.lua @@ -140,6 +140,7 @@ function Screen.new(width, height) suspended = false, mode = 'normal', options = {}, + pwd = '', popupmenu = nil, cmdline = {}, cmdline_block = {}, @@ -212,7 +213,6 @@ function Screen:attach(options, session) if options.ext_linegrid == nil then options.ext_linegrid = true end - self._session = session self._options = options self._clear_attrs = (not options.ext_linegrid) and {} or nil @@ -1108,6 +1108,10 @@ function Screen:_handle_option_set(name, value) self.options[name] = value end +function Screen:_handle_chdir(path) + self.pwd = vim.fs.normalize(path, { expand_env = false }) +end + function Screen:_handle_popupmenu_show(items, selected, row, col, grid) self.popupmenu = { items = items, pos = selected, anchor = { grid, row, col } } end