commit d3a8e9217f39c59dd7762bd22a76b8bd03ca85ff
parent 5a8fe0769cc9c5d8323b073d5c45ee37ce91c049
Author: Gregory Anders <8965202+gpanders@users.noreply.github.com>
Date: Fri, 19 Jan 2024 14:51:10 -0600
feat(ui): add chdir UI event (#27093)
When an embedded Nvim instance changes its current directory a "chdir"
UI event is emitted. Attached UIs can use this information however they
wish. In the TUI it is used to synchronize the cwd of the TUI process
with the cwd of the embedded Nvim process.
Diffstat:
7 files changed, 82 insertions(+), 2 deletions(-)
diff --git 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
@@ -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
@@ -33,6 +33,7 @@
# include <sys/xattr.h>
#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
@@ -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
@@ -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
@@ -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
@@ -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