fix(cwd): E812 when a message opens a window during a file read #41458

Problem:
When ui2 is enabled, opening an already-open file in another Neovim
instance results in the error `E812: Autocommands changed buffer…`.

Analysis:
On E812 the file is not loaded.  The default SwapExists handler notifies
`W325: Ignoring swapfile…`, and ui2 shows that message by opening
a window, which is a temp context switch.  `ctx_dirs_restore()`
re-shortens every buffer name on the way out, and `shorten_buf_fname()`
always frees and reallocates `b_sfname`.  `readfile()` aliases `b_fname`
across `check_need_swap()` and compares the pointer to detect a rename.

Regression by b296666e41, which replaced the `cs_save_sfname` restore
(that kept curbuf's pointer) with `shorten_fnames(true)`.

Solution:
Keep the allocation in `shorten_buf_fname()` when the short name is
unchanged.  Pointer stability is what the E200/E201/E812 guards actually
assert.
This commit is contained in:
Justin M. Keyes
2026-08-23 19:32:07 -04:00
committed by GitHub
parent 716835c232
commit eeefe7ca65
2 changed files with 62 additions and 16 deletions

View File

@@ -2366,22 +2366,28 @@ static char *check_for_bom(const char *p_in, int size, int *lenp, int flags)
/// name.
void shorten_buf_fname(buf_T *buf, char *dirname, int force)
{
if (buf->b_fname != NULL
&& !bt_nofilename(buf)
&& !path_with_url(buf->b_fname)
&& (force
|| buf->b_sfname == NULL
|| path_is_absolute(buf->b_sfname))) {
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
}
char *p = path_shorten_fname(buf->b_ffname, dirname);
if (p != NULL && *p != NUL) {
buf->b_sfname = xstrdup(p);
buf->b_fname = buf->b_sfname;
} else {
buf->b_fname = buf->b_ffname;
}
if (buf->b_fname == NULL
|| bt_nofilename(buf)
|| path_with_url(buf->b_fname)
|| !(force || buf->b_sfname == NULL || path_is_absolute(buf->b_sfname))) {
return;
}
char *p = path_shorten_fname(buf->b_ffname, dirname);
if (p != NULL && *p != NUL && buf->b_sfname != NULL && strcmp(p, buf->b_sfname) == 0) {
// Same name: keep the allocation. Callers (readfile()) alias `b_fname` across autocommands and
// check the pointer to detect a rename. Very cool... #41454
return;
}
if (buf->b_sfname != buf->b_ffname) {
XFREE_CLEAR(buf->b_sfname);
}
if (p != NULL && *p != NUL) {
buf->b_sfname = xstrdup(p);
buf->b_fname = buf->b_sfname;
} else {
buf->b_fname = buf->b_ffname;
}
}