vim-patch:9.1.0231: Filetype may be undetected when SwapExists sets ft in other buf (#28136)

Problem:  Filetype may be undetected when a SwapExists autocommand sets
          filetype in another buffer.
Solution: Make filetype detection state buffer-specific.  Also fix a
          similar problem for 'modified' (zeertzjq).

closes: vim/vim#14344

5bf6c2117f
This commit is contained in:
zeertzjq 2024-04-01 06:12:11 +08:00 committed by GitHub
parent e005b8d2eb
commit b08667d4f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 109 additions and 32 deletions

View File

@ -1750,7 +1750,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
saveRedobuff(&save_redo);
did_save_redobuff = true;
}
did_filetype = keep_filetype;
curbuf->b_did_filetype = curbuf->b_keep_filetype;
}
// Note that we are applying autocmds. Some commands need to know.
@ -1760,7 +1760,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
// Remember that FileType was triggered. Used for did_filetype().
if (event == EVENT_FILETYPE) {
did_filetype = true;
curbuf->b_did_filetype = true;
}
char *tail = path_tail(fname);
@ -1864,7 +1864,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
if (did_save_redobuff) {
restoreRedobuff(&save_redo);
}
did_filetype = false;
curbuf->b_did_filetype = false;
while (au_pending_free_buf != NULL) {
buf_T *b = au_pending_free_buf->b_next;
@ -1901,7 +1901,7 @@ BYPASS_AU:
}
if (retval == OK && event == EVENT_FILETYPE) {
au_did_filetype = true;
curbuf->b_au_did_filetype = true;
}
return retval;
@ -2645,7 +2645,7 @@ void do_filetype_autocmd(buf_T *buf, bool force)
secure = 0;
ft_recursive++;
did_filetype = true;
buf->b_did_filetype = true;
// Only pass true for "force" when it is true or
// used recursively, to avoid endless recurrence.
apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf);

View File

@ -15,14 +15,6 @@
#include "nvim/pos_defs.h"
#include "nvim/types_defs.h"
// Set by the apply_autocmds_group function if the given event is equal to
// EVENT_FILETYPE. Used by the readfile function in order to determine if
// EVENT_BUFREADPOST triggered the EVENT_FILETYPE.
//
// Relying on this value requires one to reset it prior calling
// apply_autocmds_group.
EXTERN bool au_did_filetype INIT( = false);
/// For CursorMoved event
EXTERN win_T *last_cursormoved_win INIT( = NULL);
/// For CursorMoved event, only used when last_cursormoved_win == curwin
@ -31,9 +23,6 @@ EXTERN pos_T last_cursormoved INIT( = { 0, 0, 0 });
EXTERN bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy?
EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled
EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave autocmds disabled
EXTERN bool did_filetype INIT( = false); ///< FileType event found
/// value for did_filetype when starting to execute autocommands
EXTERN bool keep_filetype INIT( = false);
/// When deleting the current buffer, another one must be loaded.
/// If we know which one is preferred, au_new_curbuf is set to it.

View File

@ -267,7 +267,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg)
// The autocommands in readfile() may change the buffer, but only AFTER
// reading the file.
set_bufref(&old_curbuf, curbuf);
modified_was_set = false;
curbuf->b_modified_was_set = false;
// mark cursor position as being invalid
curwin->w_valid = 0;
@ -350,7 +350,7 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg)
// the changed flag. Unless in readonly mode: "ls | nvim -R -".
// When interrupted and 'cpoptions' contains 'i' set changed flag.
if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
|| modified_was_set // ":set modified" used in autocmd
|| curbuf->b_modified_was_set // autocmd did ":set modified"
|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
changed(curbuf);
} else if (retval != FAIL && !read_stdin && !read_fifo) {
@ -1725,7 +1725,7 @@ void enter_buffer(buf_T *buf)
// ":ball" used in an autocommand. If there already is a filetype we
// might prefer to keep it.
if (*curbuf->b_p_ft == NUL) {
did_filetype = false;
curbuf->b_did_filetype = false;
}
open_buffer(false, NULL, 0);

View File

@ -461,6 +461,19 @@ struct file_buffer {
bool b_marks_read; // Have we read ShaDa marks yet?
bool b_modified_was_set; ///< did ":set modified"
bool b_did_filetype; ///< FileType event found
bool b_keep_filetype; ///< value for did_filetype when starting
///< to execute autocommands
/// Set by the apply_autocmds_group function if the given event is equal to
/// EVENT_FILETYPE. Used by the readfile function in order to determine if
/// EVENT_BUFREADPOST triggered the EVENT_FILETYPE.
///
/// Relying on this value requires one to reset it prior calling
/// apply_autocmds_group().
bool b_au_did_filetype;
// The following only used in undo.c.
u_header_T *b_u_oldhead; // pointer to oldest header
u_header_T *b_u_newhead; // pointer to newest header; may not be valid

View File

@ -1361,7 +1361,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
/// "did_filetype()" function
static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
rettv->vval.v_number = did_filetype;
rettv->vval.v_number = curbuf->b_did_filetype;
}
/// "diff_filler()" function

View File

@ -2466,7 +2466,7 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// Since we are starting to edit a file, consider the filetype to be
// unset. Helps for when an autocommand changes files and expects syntax
// highlighting to work in the other file.
did_filetype = false;
curbuf->b_did_filetype = false;
// other_file oldbuf
// false false re-edit same file, buffer is re-used

View File

@ -7382,7 +7382,7 @@ void filetype_maybe_enable(void)
/// ":setfiletype [FALLBACK] {name}"
static void ex_setfiletype(exarg_T *eap)
{
if (did_filetype) {
if (curbuf->b_did_filetype) {
return;
}
@ -7393,7 +7393,7 @@ static void ex_setfiletype(exarg_T *eap)
set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL);
if (arg != eap->arg) {
did_filetype = false;
curbuf->b_did_filetype = false;
}
}

View File

@ -234,7 +234,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
int using_b_fname;
static char *msg_is_a_directory = N_("is a directory");
au_did_filetype = false; // reset before triggering any autocommands
curbuf->b_au_did_filetype = false; // reset before triggering any autocommands
curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read
@ -1854,7 +1854,7 @@ failed:
} else if (newfile || (read_buffer && sfname != NULL)) {
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
false, curbuf, eap);
if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
if (!curbuf->b_au_did_filetype && *curbuf->b_p_ft != NUL) {
// EVENT_FILETYPE was not triggered but the buffer already has a
// filetype. Trigger EVENT_FILETYPE using the existing filetype.
apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
@ -3151,7 +3151,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
if (saved == OK) {
curbuf->b_flags |= BF_CHECK_RO; // check for RO again
keep_filetype = true; // don't detect 'filetype'
curbuf->b_keep_filetype = true; // don't detect 'filetype'
if (readfile(buf->b_ffname, buf->b_fname, 0, 0,
(linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) {
if (!aborting()) {
@ -3199,7 +3199,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
curwin->w_cursor = old_cursor;
check_cursor(curwin);
update_topline(curwin);
keep_filetype = false;
curbuf->b_keep_filetype = false;
// Update folds unless they are defined manually.
FOR_ALL_TAB_WINDOWS(tp, wp) {

View File

@ -341,8 +341,6 @@ EXTERN bool did_check_timestamps INIT( = false); // did check timestamps
// recently
EXTERN int no_check_timestamps INIT( = 0); // Don't check timestamps
EXTERN int modified_was_set; // did ":set modified"
// Mouse coordinates, set by handle_mouse_event()
EXTERN int mouse_grid;
EXTERN int mouse_row;

View File

@ -2244,7 +2244,7 @@ static const char *did_set_modified(optset_T *args)
save_file_ff(buf); // Buffer is unchanged
}
redraw_titles();
modified_was_set = (int)args->os_newval.boolean;
buf->b_modified_was_set = (int)args->os_newval.boolean;
return NULL;
}

View File

@ -4274,10 +4274,10 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q
set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL);
curbuf->b_p_ma = false;
keep_filetype = true; // don't detect 'filetype'
curbuf->b_keep_filetype = true; // don't detect 'filetype'
apply_autocmds(EVENT_BUFREADPOST, "quickfix", NULL, false, curbuf);
apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf);
keep_filetype = false;
curbuf->b_keep_filetype = false;
curbuf->b_ro_locked--;
// make sure it will be redrawn

View File

@ -3977,4 +3977,81 @@ func Test_Changed_ChangedI_2()
call delete('XTextChangedI3')
endfunc
" Test that filetype detection still works when SwapExists autocommand sets
" filetype in another buffer.
func Test_SwapExists_set_other_buf_filetype()
let lines =<< trim END
set nocompatible directory=.
filetype on
let g:buf = bufnr()
new
func SwapExists()
let v:swapchoice = 'o'
call setbufvar(g:buf, '&filetype', 'text')
endfunc
func SafeState()
edit <script>
redir! > XftSwapExists.out
set readonly? filetype?
redir END
qall!
endfunc
autocmd SwapExists * ++nested call SwapExists()
autocmd SafeState * ++nested ++once call SafeState()
END
call writefile(lines, 'XftSwapExists.vim', 'D')
new XftSwapExists.vim
if RunVim('', '', ' -S XftSwapExists.vim')
call assert_equal(
\ ['', ' readonly', ' filetype=vim'],
\ readfile('XftSwapExists.out'))
call delete('XftSwapExists.out')
endif
bwipe!
endfunc
" Test that file is not marked as modified when SwapExists autocommand sets
" 'modified' in another buffer.
func Test_SwapExists_set_other_buf_modified()
let lines =<< trim END
set nocompatible directory=.
let g:buf = bufnr()
new
func SwapExists()
let v:swapchoice = 'o'
call setbufvar(g:buf, '&modified', 1)
endfunc
func SafeState()
edit <script>
redir! > XmodSwapExists.out
set readonly? modified?
redir END
qall!
endfunc
autocmd SwapExists * ++nested call SwapExists()
autocmd SafeState * ++nested ++once call SafeState()
END
call writefile(lines, 'XmodSwapExists.vim', 'D')
new XmodSwapExists.vim
if RunVim('', '', ' -S XmodSwapExists.vim')
call assert_equal(
\ ['', ' readonly', 'nomodified'],
\ readfile('XmodSwapExists.out'))
call delete('XmodSwapExists.out')
endif
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab