mirror of
https://github.com/neovim/neovim.git
synced 2025-09-28 22:18:33 +00:00
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:
@@ -1750,7 +1750,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|
|||||||
saveRedobuff(&save_redo);
|
saveRedobuff(&save_redo);
|
||||||
did_save_redobuff = true;
|
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.
|
// 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().
|
// Remember that FileType was triggered. Used for did_filetype().
|
||||||
if (event == EVENT_FILETYPE) {
|
if (event == EVENT_FILETYPE) {
|
||||||
did_filetype = true;
|
curbuf->b_did_filetype = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *tail = path_tail(fname);
|
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) {
|
if (did_save_redobuff) {
|
||||||
restoreRedobuff(&save_redo);
|
restoreRedobuff(&save_redo);
|
||||||
}
|
}
|
||||||
did_filetype = false;
|
curbuf->b_did_filetype = false;
|
||||||
while (au_pending_free_buf != NULL) {
|
while (au_pending_free_buf != NULL) {
|
||||||
buf_T *b = au_pending_free_buf->b_next;
|
buf_T *b = au_pending_free_buf->b_next;
|
||||||
|
|
||||||
@@ -1901,7 +1901,7 @@ BYPASS_AU:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (retval == OK && event == EVENT_FILETYPE) {
|
if (retval == OK && event == EVENT_FILETYPE) {
|
||||||
au_did_filetype = true;
|
curbuf->b_au_did_filetype = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
@@ -2645,7 +2645,7 @@ void do_filetype_autocmd(buf_T *buf, bool force)
|
|||||||
secure = 0;
|
secure = 0;
|
||||||
|
|
||||||
ft_recursive++;
|
ft_recursive++;
|
||||||
did_filetype = true;
|
buf->b_did_filetype = true;
|
||||||
// Only pass true for "force" when it is true or
|
// Only pass true for "force" when it is true or
|
||||||
// used recursively, to avoid endless recurrence.
|
// used recursively, to avoid endless recurrence.
|
||||||
apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf);
|
apply_autocmds(EVENT_FILETYPE, buf->b_p_ft, buf->b_fname, force || ft_recursive == 1, buf);
|
||||||
|
@@ -15,14 +15,6 @@
|
|||||||
#include "nvim/pos_defs.h"
|
#include "nvim/pos_defs.h"
|
||||||
#include "nvim/types_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
|
/// For CursorMoved event
|
||||||
EXTERN win_T *last_cursormoved_win INIT( = NULL);
|
EXTERN win_T *last_cursormoved_win INIT( = NULL);
|
||||||
/// For CursorMoved event, only used when last_cursormoved_win == curwin
|
/// 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 bool autocmd_busy INIT( = false); ///< Is apply_autocmds() busy?
|
||||||
EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled
|
EXTERN int autocmd_no_enter INIT( = false); ///< Buf/WinEnter autocmds disabled
|
||||||
EXTERN int autocmd_no_leave INIT( = false); ///< Buf/WinLeave 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.
|
/// When deleting the current buffer, another one must be loaded.
|
||||||
/// If we know which one is preferred, au_new_curbuf is set to it.
|
/// If we know which one is preferred, au_new_curbuf is set to it.
|
||||||
|
@@ -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
|
// The autocommands in readfile() may change the buffer, but only AFTER
|
||||||
// reading the file.
|
// reading the file.
|
||||||
set_bufref(&old_curbuf, curbuf);
|
set_bufref(&old_curbuf, curbuf);
|
||||||
modified_was_set = false;
|
curbuf->b_modified_was_set = false;
|
||||||
|
|
||||||
// mark cursor position as being invalid
|
// mark cursor position as being invalid
|
||||||
curwin->w_valid = 0;
|
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 -".
|
// the changed flag. Unless in readonly mode: "ls | nvim -R -".
|
||||||
// When interrupted and 'cpoptions' contains 'i' set changed flag.
|
// When interrupted and 'cpoptions' contains 'i' set changed flag.
|
||||||
if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
|
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)) {
|
|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
|
||||||
changed(curbuf);
|
changed(curbuf);
|
||||||
} else if (retval != FAIL && !read_stdin && !read_fifo) {
|
} 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
|
// ":ball" used in an autocommand. If there already is a filetype we
|
||||||
// might prefer to keep it.
|
// might prefer to keep it.
|
||||||
if (*curbuf->b_p_ft == NUL) {
|
if (*curbuf->b_p_ft == NUL) {
|
||||||
did_filetype = false;
|
curbuf->b_did_filetype = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
open_buffer(false, NULL, 0);
|
open_buffer(false, NULL, 0);
|
||||||
|
@@ -461,6 +461,19 @@ struct file_buffer {
|
|||||||
|
|
||||||
bool b_marks_read; // Have we read ShaDa marks yet?
|
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.
|
// The following only used in undo.c.
|
||||||
u_header_T *b_u_oldhead; // pointer to oldest header
|
u_header_T *b_u_oldhead; // pointer to oldest header
|
||||||
u_header_T *b_u_newhead; // pointer to newest header; may not be valid
|
u_header_T *b_u_newhead; // pointer to newest header; may not be valid
|
||||||
|
@@ -1361,7 +1361,7 @@ static void f_dictwatcherdel(typval_T *argvars, typval_T *rettv, EvalFuncData fp
|
|||||||
/// "did_filetype()" function
|
/// "did_filetype()" function
|
||||||
static void f_did_filetype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
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
|
/// "diff_filler()" function
|
||||||
|
@@ -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
|
// Since we are starting to edit a file, consider the filetype to be
|
||||||
// unset. Helps for when an autocommand changes files and expects syntax
|
// unset. Helps for when an autocommand changes files and expects syntax
|
||||||
// highlighting to work in the other file.
|
// highlighting to work in the other file.
|
||||||
did_filetype = false;
|
curbuf->b_did_filetype = false;
|
||||||
|
|
||||||
// other_file oldbuf
|
// other_file oldbuf
|
||||||
// false false re-edit same file, buffer is re-used
|
// false false re-edit same file, buffer is re-used
|
||||||
|
@@ -7382,7 +7382,7 @@ void filetype_maybe_enable(void)
|
|||||||
/// ":setfiletype [FALLBACK] {name}"
|
/// ":setfiletype [FALLBACK] {name}"
|
||||||
static void ex_setfiletype(exarg_T *eap)
|
static void ex_setfiletype(exarg_T *eap)
|
||||||
{
|
{
|
||||||
if (did_filetype) {
|
if (curbuf->b_did_filetype) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7393,7 +7393,7 @@ static void ex_setfiletype(exarg_T *eap)
|
|||||||
|
|
||||||
set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL);
|
set_option_value_give_err(kOptFiletype, CSTR_AS_OPTVAL(arg), OPT_LOCAL);
|
||||||
if (arg != eap->arg) {
|
if (arg != eap->arg) {
|
||||||
did_filetype = false;
|
curbuf->b_did_filetype = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -234,7 +234,7 @@ int readfile(char *fname, char *sfname, linenr_T from, linenr_T lines_to_skip,
|
|||||||
int using_b_fname;
|
int using_b_fname;
|
||||||
static char *msg_is_a_directory = N_("is a directory");
|
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
|
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)) {
|
} else if (newfile || (read_buffer && sfname != NULL)) {
|
||||||
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
|
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
|
||||||
false, curbuf, eap);
|
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
|
// EVENT_FILETYPE was not triggered but the buffer already has a
|
||||||
// filetype. Trigger EVENT_FILETYPE using the existing filetype.
|
// filetype. Trigger EVENT_FILETYPE using the existing filetype.
|
||||||
apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, true, curbuf);
|
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) {
|
if (saved == OK) {
|
||||||
curbuf->b_flags |= BF_CHECK_RO; // check for RO again
|
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,
|
if (readfile(buf->b_ffname, buf->b_fname, 0, 0,
|
||||||
(linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) {
|
(linenr_T)MAXLNUM, &ea, flags, shortmess(SHM_FILEINFO)) != OK) {
|
||||||
if (!aborting()) {
|
if (!aborting()) {
|
||||||
@@ -3199,7 +3199,7 @@ void buf_reload(buf_T *buf, int orig_mode, bool reload_options)
|
|||||||
curwin->w_cursor = old_cursor;
|
curwin->w_cursor = old_cursor;
|
||||||
check_cursor(curwin);
|
check_cursor(curwin);
|
||||||
update_topline(curwin);
|
update_topline(curwin);
|
||||||
keep_filetype = false;
|
curbuf->b_keep_filetype = false;
|
||||||
|
|
||||||
// Update folds unless they are defined manually.
|
// Update folds unless they are defined manually.
|
||||||
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
FOR_ALL_TAB_WINDOWS(tp, wp) {
|
||||||
|
@@ -341,8 +341,6 @@ EXTERN bool did_check_timestamps INIT( = false); // did check timestamps
|
|||||||
// recently
|
// recently
|
||||||
EXTERN int no_check_timestamps INIT( = 0); // Don't check timestamps
|
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()
|
// Mouse coordinates, set by handle_mouse_event()
|
||||||
EXTERN int mouse_grid;
|
EXTERN int mouse_grid;
|
||||||
EXTERN int mouse_row;
|
EXTERN int mouse_row;
|
||||||
|
@@ -2244,7 +2244,7 @@ static const char *did_set_modified(optset_T *args)
|
|||||||
save_file_ff(buf); // Buffer is unchanged
|
save_file_ff(buf); // Buffer is unchanged
|
||||||
}
|
}
|
||||||
redraw_titles();
|
redraw_titles();
|
||||||
modified_was_set = (int)args->os_newval.boolean;
|
buf->b_modified_was_set = (int)args->os_newval.boolean;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
set_option_value_give_err(kOptFiletype, STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL);
|
||||||
curbuf->b_p_ma = false;
|
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_BUFREADPOST, "quickfix", NULL, false, curbuf);
|
||||||
apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf);
|
apply_autocmds(EVENT_BUFWINENTER, "quickfix", NULL, false, curbuf);
|
||||||
keep_filetype = false;
|
curbuf->b_keep_filetype = false;
|
||||||
curbuf->b_ro_locked--;
|
curbuf->b_ro_locked--;
|
||||||
|
|
||||||
// make sure it will be redrawn
|
// make sure it will be redrawn
|
||||||
|
@@ -3977,4 +3977,81 @@ func Test_Changed_ChangedI_2()
|
|||||||
call delete('XTextChangedI3')
|
call delete('XTextChangedI3')
|
||||||
endfunc
|
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
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Reference in New Issue
Block a user