vim-patch:9.1.0147: Cannot keep a buffer focused in a window

Problem:  Cannot keep a buffer focused in a window
          (Amit Levy)
Solution: Add the 'winfixbuf' window-local option
          (Colin Kennedy)

fixes:  vim/vim#6445
closes: vim/vim#13903

2157035637

N/A patch:
vim-patch:58f1e5c0893a
This commit is contained in:
Colin Kennedy 2023-12-25 20:41:09 -08:00 committed by zeertzjq
parent a09ddd7ce5
commit 141182d6c6
27 changed files with 3414 additions and 23 deletions

View File

@ -114,6 +114,13 @@ wiped out a buffer which contains a mark or is referenced in another way.
You cannot have two buffers with exactly the same name. This includes the You cannot have two buffers with exactly the same name. This includes the
path leading to the file. path leading to the file.
*E1513* >
Cannot edit buffer. 'winfixbuf' is enabled
If a window has 'winfixbuf' enabled, you cannot change that window's current
buffer. You need to set 'nowinfixbuf' before continuing. You may use [!] to
force the window to switch buffers, if your command supports it.
*E72* > *E72* >
Close error on swap file Close error on swap file

View File

@ -160,6 +160,8 @@ The following new APIs and features were added.
• 'breakindent' performance is significantly improved for wrapped lines. • 'breakindent' performance is significantly improved for wrapped lines.
• Cursor movement, insertion with [count] and |screenpos()| are now faster. • Cursor movement, insertion with [count] and |screenpos()| are now faster.
• |'winfixbuf'| keeps a window focused onto a specific buffer
• |vim.iter()| provides a generic iterator interface for tables and Lua • |vim.iter()| provides a generic iterator interface for tables and Lua
iterators |for-in|. iterators |for-in|.

View File

@ -6271,6 +6271,8 @@ A jump table for the options with a short description can be found at |Q_op|.
"split" when both are present. "split" when both are present.
uselast If included, jump to the previously used window when uselast If included, jump to the previously used window when
jumping to errors with |quickfix| commands. jumping to errors with |quickfix| commands.
If a window has 'winfixbuf' enabled, 'switchbuf' is currently not
applied to the split window.
*'synmaxcol'* *'smc'* *'synmaxcol'* *'smc'*
'synmaxcol' 'smc' number (default 3000) 'synmaxcol' 'smc' number (default 3000)
@ -7170,6 +7172,15 @@ A jump table for the options with a short description can be found at |Q_op|.
Note: Do not confuse this with the height of the Vim window, use Note: Do not confuse this with the height of the Vim window, use
'lines' for that. 'lines' for that.
*'winfixbuf'* *'wfb'* *'nowinfixbuf'* *'nowfb'*
'winfixbuf' 'wfb' boolean (default off)
local to window
If enabled, the buffer and any window that displays it are paired.
For example, attempting to change the buffer with |:edit| will fail.
Other commands which change a window's buffer such as |:cnext| will
also skip any window with 'winfixbuf' enabled. However if a command
has an "!" option, a window can be forced to switch buffers.
*'winfixheight'* *'wfh'* *'nowinfixheight'* *'nowfh'* *'winfixheight'* *'wfh'* *'nowinfixheight'* *'nowfh'*
'winfixheight' 'wfh' boolean (default off) 'winfixheight' 'wfh' boolean (default off)
local to window |local-noglobal| local to window |local-noglobal|

View File

@ -939,6 +939,7 @@ Short explanation of each option: *option-list*
'wildoptions' 'wop' specifies how command line completion is done 'wildoptions' 'wop' specifies how command line completion is done
'winaltkeys' 'wak' when the windows system handles ALT keys 'winaltkeys' 'wak' when the windows system handles ALT keys
'window' 'wi' nr of lines to scroll for CTRL-F and CTRL-B 'window' 'wi' nr of lines to scroll for CTRL-F and CTRL-B
'winfixbuf' 'wfb' keep window focused on a single buffer
'winfixheight' 'wfh' keep window height when opening/closing windows 'winfixheight' 'wfh' keep window height when opening/closing windows
'winfixwidth' 'wfw' keep window width when opening/closing windows 'winfixwidth' 'wfw' keep window width when opening/closing windows
'winheight' 'wh' minimum number of lines for the current window 'winheight' 'wh' minimum number of lines for the current window

View File

@ -402,17 +402,22 @@ If the tag is in the current file this will always work. Otherwise the
performed actions depend on whether the current file was changed, whether a ! performed actions depend on whether the current file was changed, whether a !
is added to the command and on the 'autowrite' option: is added to the command and on the 'autowrite' option:
tag in file autowrite ~ tag in file autowrite ~
current file changed ! option action ~ current file changed ! winfixbuf option action ~
--------------------------------------------------------------------------- -----------------------------------------------------------------------------
yes x x x goto tag yes x x no x goto tag
no no x x read other file, goto tag no no x no x read other file, goto tag
no yes yes x abandon current file, read other file, goto no yes yes no x abandon current file,
tag read other file, goto tag
no yes no on write current file, read other file, goto no yes no no on write current file,
tag read other file, goto tag
no yes no off fail no yes no no off fail
--------------------------------------------------------------------------- yes x yes x x goto tag
no no no yes x fail
no yes no yes x fail
no yes no yes on fail
no yes no yes off fail
-----------------------------------------------------------------------------
- If the tag is in the current file, the command will always work. - If the tag is in the current file, the command will always work.
- If the tag is in another file and the current file was not changed, the - If the tag is in another file and the current file was not changed, the
@ -428,6 +433,8 @@ current file changed ! option action ~
the changes, use the ":w" command and then use ":tag" without an argument. the changes, use the ":w" command and then use ":tag" without an argument.
This works because the tag is put on the stack anyway. If you want to lose This works because the tag is put on the stack anyway. If you want to lose
the changes you can use the ":tag!" command. the changes you can use the ":tag!" command.
- If the tag is in another file and the window includes 'winfixbuf', the
command will fail. If the tag is in the same file then it may succeed.
*tag-security* *tag-security*
Note that Vim forbids some commands, for security reasons. This works like Note that Vim forbids some commands, for security reasons. This works like

View File

@ -6746,6 +6746,8 @@ vim.bo.swf = vim.bo.swapfile
--- "split" when both are present. --- "split" when both are present.
--- uselast If included, jump to the previously used window when --- uselast If included, jump to the previously used window when
--- jumping to errors with `quickfix` commands. --- jumping to errors with `quickfix` commands.
--- If a window has 'winfixbuf' enabled, 'switchbuf' is currently not
--- applied to the split window.
--- ---
--- @type string --- @type string
vim.o.switchbuf = "uselast" vim.o.switchbuf = "uselast"
@ -7874,6 +7876,18 @@ vim.o.wi = vim.o.window
vim.go.window = vim.o.window vim.go.window = vim.o.window
vim.go.wi = vim.go.window vim.go.wi = vim.go.window
--- If enabled, the buffer and any window that displays it are paired.
--- For example, attempting to change the buffer with `:edit` will fail.
--- Other commands which change a window's buffer such as `:cnext` will
--- also skip any window with 'winfixbuf' enabled. However if a command
--- has an "!" option, a window can be forced to switch buffers.
---
--- @type boolean
vim.o.winfixbuf = false
vim.o.wfb = vim.o.winfixbuf
vim.wo.winfixbuf = vim.o.winfixbuf
vim.wo.wfb = vim.wo.winfixbuf
--- Keep the window height when windows are opened or closed and --- Keep the window height when windows are opened or closed and
--- 'equalalways' is set. Also for `CTRL-W_=`. Set by default for the --- 'equalalways' is set. Also for `CTRL-W_=`. Set by default for the
--- `preview-window` and `quickfix-window`. --- `preview-window` and `quickfix-window`.

View File

@ -444,6 +444,7 @@ if has("statusline")
call <SID>AddOption("statusline", gettext("alternate format to be used for a status line")) call <SID>AddOption("statusline", gettext("alternate format to be used for a status line"))
call <SID>OptionG("stl", &stl) call <SID>OptionG("stl", &stl)
endif endif
call append("$", "\t" .. s:local_to_window)
call <SID>AddOption("equalalways", gettext("make all windows the same size when adding/removing windows")) call <SID>AddOption("equalalways", gettext("make all windows the same size when adding/removing windows"))
call <SID>BinOptionG("ea", &ea) call <SID>BinOptionG("ea", &ea)
call <SID>AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\"")) call <SID>AddOption("eadirection", gettext("in which direction 'equalalways' works: \"ver\", \"hor\" or \"both\""))
@ -452,6 +453,8 @@ call <SID>AddOption("winheight", gettext("minimal number of lines used for the c
call append("$", " \tset wh=" . &wh) call append("$", " \tset wh=" . &wh)
call <SID>AddOption("winminheight", gettext("minimal number of lines used for any window")) call <SID>AddOption("winminheight", gettext("minimal number of lines used for any window"))
call append("$", " \tset wmh=" . &wmh) call append("$", " \tset wmh=" . &wmh)
call <SID>AddOption("winfixbuf", gettext("keep window focused on a single buffer"))
call <SID>OptionG("wfb", &wfb)
call <SID>AddOption("winfixheight", gettext("keep the height of the window")) call <SID>AddOption("winfixheight", gettext("keep the height of the window"))
call append("$", "\t" .. s:local_to_window) call append("$", "\t" .. s:local_to_window)
call <SID>BinOptionL("wfh") call <SID>BinOptionL("wfh")

View File

@ -876,6 +876,11 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
return; return;
} }
if (curwin->w_p_wfb) {
api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
return;
}
try_start(); try_start();
int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0); int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
if (!try_end(err) && result == FAIL) { if (!try_end(err) && result == FAIL) {

View File

@ -61,6 +61,12 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
if (!win || !buf) { if (!win || !buf) {
return; return;
} }
if (win->w_p_wfb) {
api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
return;
}
if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) { if (win == cmdwin_win || win == cmdwin_old_curwin || buf == cmdwin_buf) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin); api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return; return;

View File

@ -623,6 +623,8 @@ void ex_argument(exarg_T *eap)
/// Edit file "argn" of the argument lists. /// Edit file "argn" of the argument lists.
void do_argfile(exarg_T *eap, int argn) void do_argfile(exarg_T *eap, int argn)
{ {
bool is_split_cmd = *eap->cmd == 's';
int old_arg_idx = curwin->w_arg_idx; int old_arg_idx = curwin->w_arg_idx;
if (argn < 0 || argn >= ARGCOUNT) { if (argn < 0 || argn >= ARGCOUNT) {
@ -637,10 +639,16 @@ void do_argfile(exarg_T *eap, int argn)
return; return;
} }
if (!is_split_cmd
&& (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum
&& !check_can_set_curbuf_forceit(eap->forceit)) {
return;
}
setpcmark(); setpcmark();
// split window or create new tab page first // split window or create new tab page first
if (*eap->cmd == 's' || cmdmod.cmod_tab != 0) { if (is_split_cmd || cmdmod.cmod_tab != 0) {
if (win_split(0, 0) == FAIL) { if (win_split(0, 0) == FAIL) {
return; return;
} }

View File

@ -1305,6 +1305,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
} }
return FAIL; return FAIL;
} }
if (action == DOBUF_GOTO && buf != curbuf && !check_can_set_curbuf_forceit(forceit)) {
// disallow navigating to another buffer when 'winfixbuf' is applied
return FAIL;
}
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) { if ((action == DOBUF_GOTO || action == DOBUF_SPLIT) && (buf->b_flags & BF_DUMMY)) {
// disallow navigating to the dummy buffer // disallow navigating to the dummy buffer
semsg(_(e_nobufnr), count); semsg(_(e_nobufnr), count);

View File

@ -139,6 +139,8 @@ typedef struct {
#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit' #define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit'
OptInt wo_nuw; OptInt wo_nuw;
#define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth' #define w_p_nuw w_onebuf_opt.wo_nuw // 'numberwidth'
int wo_wfb;
#define w_p_wfb w_onebuf_opt.wo_wfb // 'winfixbuf'
int wo_wfh; int wo_wfh;
#define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight' #define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight'
int wo_wfw; int wo_wfw;

View File

@ -2008,6 +2008,10 @@ static int check_readonly(int *forceit, buf_T *buf)
/// GETFILE_OPEN_OTHER for successfully opening another file. /// GETFILE_OPEN_OTHER for successfully opening another file.
int getfile(int fnum, char *ffname_arg, char *sfname_arg, bool setpm, linenr_T lnum, bool forceit) int getfile(int fnum, char *ffname_arg, char *sfname_arg, bool setpm, linenr_T lnum, bool forceit)
{ {
if (!check_can_set_curbuf_forceit(forceit)) {
return GETFILE_ERROR;
}
char *ffname = ffname_arg; char *ffname = ffname_arg;
char *sfname = sfname_arg; char *sfname = sfname_arg;
bool other; bool other;

View File

@ -812,7 +812,7 @@ module.cmds = {
}, },
{ {
command = 'drop', command = 'drop',
flags = bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR), flags = bit.bor(BANG, FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR),
addr_type = 'ADDR_NONE', addr_type = 'ADDR_NONE',
func = 'ex_drop', func = 'ex_drop',
}, },

View File

@ -444,6 +444,27 @@ int buf_write_all(buf_T *buf, bool forceit)
/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" /// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
void ex_listdo(exarg_T *eap) void ex_listdo(exarg_T *eap)
{ {
if (curwin->w_p_wfb) {
if ((eap->cmdidx == CMD_ldo || eap->cmdidx == CMD_lfdo) && !eap->forceit) {
// Disallow :ldo if 'winfixbuf' is applied
semsg("%s", e_winfixbuf_cannot_go_to_buffer);
return;
}
if (win_valid(prevwin)) {
// Change the current window to another because 'winfixbuf' is enabled
curwin = prevwin;
} else {
// Split the window, which will be 'nowinfixbuf', and set curwin to that
exarg_T new_eap = {
.cmdidx = CMD_split,
.cmd = "split",
.arg = "",
};
ex_splitview(&new_eap);
}
}
char *save_ei = NULL; char *save_ei = NULL;
// Temporarily override SHM_OVER and SHM_OVERALL to avoid that file // Temporarily override SHM_OVER and SHM_OVERALL to avoid that file

View File

@ -5334,6 +5334,10 @@ static void ex_resize(exarg_T *eap)
/// ":find [+command] <file>" command. /// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap) static void ex_find(exarg_T *eap)
{ {
if (!check_can_set_curbuf_forceit(eap->forceit)) {
return;
}
char *file_to_find = NULL; char *file_to_find = NULL;
char *search_ctx = NULL; char *search_ctx = NULL;
char *fname = find_file_in_path(eap->arg, strlen(eap->arg), char *fname = find_file_in_path(eap->arg, strlen(eap->arg),
@ -5364,6 +5368,14 @@ static void ex_find(exarg_T *eap)
/// ":edit", ":badd", ":balt", ":visual". /// ":edit", ":badd", ":balt", ":visual".
static void ex_edit(exarg_T *eap) static void ex_edit(exarg_T *eap)
{ {
// Exclude commands which keep the window's current buffer
if (eap->cmdidx != CMD_badd
&& eap->cmdidx != CMD_balt
// All other commands must obey 'winfixbuf' / ! rules
&& !check_can_set_curbuf_forceit(eap->forceit)) {
return;
}
do_exedit(eap, NULL); do_exedit(eap, NULL);
} }
@ -6670,7 +6682,7 @@ static void ex_checkpath(exarg_T *eap)
{ {
find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1, find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1,
eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW, eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
1, (linenr_T)MAXLNUM); 1, (linenr_T)MAXLNUM, eap->forceit);
} }
/// ":psearch" /// ":psearch"
@ -6729,7 +6741,7 @@ static void ex_findpat(exarg_T *eap)
if (!eap->skip) { if (!eap->skip) {
find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit, find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit,
*eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY, *eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY,
n, action, eap->line1, eap->line2); n, action, eap->line1, eap->line2, eap->forceit);
} }
} }

View File

@ -971,6 +971,9 @@ EXTERN const char e_val_too_large[] INIT(= N_("E1510: Value too large: %s"));
EXTERN const char e_undobang_cannot_redo_or_move_branch[] EXTERN const char e_undobang_cannot_redo_or_move_branch[]
INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch")); INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch"));
EXTERN const char e_winfixbuf_cannot_go_to_buffer[]
INIT(= N_("E1513: Cannot edit buffer. 'winfixbuf' is enabled"));
EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s"));
EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s")); EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s"));

View File

@ -3027,7 +3027,7 @@ static void get_next_include_file_completion(int compl_type)
((compl_type == CTRL_X_PATH_DEFINES ((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL)) && !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY), ? FIND_DEFINE : FIND_ANY),
1, ACTION_EXPAND, 1, MAXLNUM); 1, ACTION_EXPAND, 1, MAXLNUM, false);
} }
/// Get the next set of words matching "compl_pattern" in dictionary or /// Get the next set of words matching "compl_pattern" in dictionary or

View File

@ -3896,6 +3896,10 @@ static void nv_gotofile(cmdarg_T *cap)
return; return;
} }
if (!check_can_set_curbuf_disabled()) {
return;
}
char *ptr = grab_file_name(cap->count1, &lnum); char *ptr = grab_file_name(cap->count1, &lnum);
if (ptr != NULL) { if (ptr != NULL) {
@ -4232,7 +4236,8 @@ static void nv_brackets(cmdarg_T *cap)
(cap->cmdchar == ']' (cap->cmdchar == ']'
? curwin->w_cursor.lnum + 1 ? curwin->w_cursor.lnum + 1
: 1), : 1),
MAXLNUM); MAXLNUM,
false);
xfree(ptr); xfree(ptr);
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
} }

View File

@ -4629,6 +4629,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return &(win->w_p_rnu); return &(win->w_p_rnu);
case PV_NUW: case PV_NUW:
return &(win->w_p_nuw); return &(win->w_p_nuw);
case PV_WFB:
return &(win->w_p_wfb);
case PV_WFH: case PV_WFH:
return &(win->w_p_wfh); return &(win->w_p_wfh);
case PV_WFW: case PV_WFW:

View File

@ -8406,6 +8406,8 @@ return {
"split" when both are present. "split" when both are present.
uselast If included, jump to the previously used window when uselast If included, jump to the previously used window when
jumping to errors with |quickfix| commands. jumping to errors with |quickfix| commands.
If a window has 'winfixbuf' enabled, 'switchbuf' is currently not
applied to the split window.
]=], ]=],
expand_cb = 'expand_set_switchbuf', expand_cb = 'expand_set_switchbuf',
full_name = 'switchbuf', full_name = 'switchbuf',
@ -9816,6 +9818,23 @@ return {
type = 'number', type = 'number',
varname = 'p_window', varname = 'p_window',
}, },
{
abbreviation = 'wfb',
defaults = { if_true = false },
desc = [=[
If enabled, the buffer and any window that displays it are paired.
For example, attempting to change the buffer with |:edit| will fail.
Other commands which change a window's buffer such as |:cnext| will
also skip any window with 'winfixbuf' enabled. However if a command
has an "!" option, a window can be forced to switch buffers.
]=],
full_name = 'winfixbuf',
pv_name = 'p_wfb',
redraw = { 'current_window' },
scope = { 'window' },
short_desc = N_('pin a window to a specific buffer'),
type = 'boolean',
},
{ {
abbreviation = 'wfh', abbreviation = 'wfh',
defaults = { if_true = false }, defaults = { if_true = false },

View File

@ -2699,7 +2699,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum)
// Didn't find it, go to the window before the quickfix // Didn't find it, go to the window before the quickfix
// window, unless 'switchbuf' contains 'uselast': in this case we // window, unless 'switchbuf' contains 'uselast': in this case we
// try to jump to the previously used window first. // try to jump to the previously used window first.
if ((swb_flags & SWB_USELAST) && win_valid(prevwin)) { if ((swb_flags & SWB_USELAST) && !prevwin->w_p_wfb && win_valid(prevwin)) {
win = prevwin; win = prevwin;
} else if (altwin != NULL) { } else if (altwin != NULL) {
win = altwin; win = altwin;
@ -2714,6 +2714,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum)
// Remember a usable window. // Remember a usable window.
if (altwin == NULL if (altwin == NULL
&& !win->w_p_pvw && !win->w_p_pvw
&& !win->w_p_wfb
&& bt_normal(win->w_buffer)) { && bt_normal(win->w_buffer)) {
altwin = win; altwin = win;
} }
@ -2802,6 +2803,25 @@ static int qf_jump_edit_buffer(qf_info_T *qi, qfline_T *qf_ptr, int forceit, int
ECMD_HIDE + ECMD_SET_HELP, ECMD_HIDE + ECMD_SET_HELP,
prev_winid == curwin->handle ? curwin : NULL); prev_winid == curwin->handle ? curwin : NULL);
} else { } else {
if (!forceit && curwin->w_p_wfb) {
if (qi->qfl_type == QFLT_LOCATION) {
// Location lists cannot split or reassign their window
// so 'winfixbuf' windows must fail
semsg("%s", e_winfixbuf_cannot_go_to_buffer);
return QF_ABORT;
}
if (!win_valid(prevwin)) {
// Split the window, which will be 'nowinfixbuf', and set curwin to that
exarg_T new_eap = {
.cmdidx = CMD_split,
.cmd = "split",
.arg = "",
};
ex_splitview(&new_eap);
}
}
retval = buflist_getfile(qf_ptr->qf_fnum, 1, retval = buflist_getfile(qf_ptr->qf_fnum, 1,
GETF_SETMARK | GETF_SWITCH, forceit); GETF_SETMARK | GETF_SWITCH, forceit);
} }
@ -4297,6 +4317,11 @@ static void qf_jump_first(qf_info_T *qi, unsigned save_qfid, int forceit)
if (qf_restore_list(qi, save_qfid) == FAIL) { if (qf_restore_list(qi, save_qfid) == FAIL) {
return; return;
} }
if (!check_can_set_curbuf_forceit(forceit)) {
return;
}
// Autocommands might have cleared the list, check for that // Autocommands might have cleared the list, check for that
if (!qf_list_empty(qf_get_curlist(qi))) { if (!qf_list_empty(qf_get_curlist(qi))) {
qf_jump(qi, 0, 0, forceit); qf_jump(qi, 0, 0, forceit);
@ -5125,7 +5150,7 @@ void ex_cfile(exarg_T *eap)
// This function is used by the :cfile, :cgetfile and :caddfile // This function is used by the :cfile, :cgetfile and :caddfile
// commands. // commands.
// :cfile always creates a new quickfix list and jumps to the // :cfile always creates a new quickfix list and may jump to the
// first error. // first error.
// :cgetfile creates a new quickfix list but doesn't jump to the // :cgetfile creates a new quickfix list but doesn't jump to the
// first error. // first error.
@ -5587,6 +5612,10 @@ theend:
/// ":lvimgrepadd {pattern} file(s)" /// ":lvimgrepadd {pattern} file(s)"
void ex_vimgrep(exarg_T *eap) void ex_vimgrep(exarg_T *eap)
{ {
if (!check_can_set_curbuf_forceit(eap->forceit)) {
return;
}
char *au_name = vgr_get_auname(eap->cmdidx); char *au_name = vgr_get_auname(eap->cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) { curbuf->b_fname, true, curbuf)) {

View File

@ -3564,8 +3564,10 @@ static char *get_line_and_copy(linenr_T lnum, char *buf)
/// @param action What to do when we find it /// @param action What to do when we find it
/// @param start_lnum first line to start searching /// @param start_lnum first line to start searching
/// @param end_lnum last line for searching /// @param end_lnum last line for searching
/// @param forceit If true, always switch to the found path
void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments, void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool skip_comments,
int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum) int type, int count, int action, linenr_T start_lnum, linenr_T end_lnum,
int forceit)
{ {
SearchedFile *files; // Stack of included files SearchedFile *files; // Stack of included files
SearchedFile *bigger; // When we need more space SearchedFile *bigger; // When we need more space
@ -4025,7 +4027,7 @@ search_line:
break; break;
} }
if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL, if (!GETFILE_SUCCESS(getfile(curwin_save->w_buffer->b_fnum, NULL,
NULL, true, lnum, false))) { NULL, true, lnum, forceit))) {
break; // failed to jump to file break; // failed to jump to file
} }
} else { } else {
@ -4035,7 +4037,7 @@ search_line:
check_cursor(); check_cursor();
} else { } else {
if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true, if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true,
files[depth].lnum, false))) { files[depth].lnum, forceit))) {
break; // failed to jump to file break; // failed to jump to file
} }
// autocommands may have changed the lnum, we don't // autocommands may have changed the lnum, we don't

View File

@ -290,6 +290,10 @@ void set_buflocal_tfu_callback(buf_T *buf)
/// @param verbose print "tag not found" message /// @param verbose print "tag not found" message
void do_tag(char *tag, int type, int count, int forceit, bool verbose) void do_tag(char *tag, int type, int count, int forceit, bool verbose)
{ {
if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) {
return;
}
taggy_T *tagstack = curwin->w_tagstack; taggy_T *tagstack = curwin->w_tagstack;
int tagstackidx = curwin->w_tagstackidx; int tagstackidx = curwin->w_tagstackidx;
int tagstacklen = curwin->w_tagstacklen; int tagstacklen = curwin->w_tagstacklen;
@ -2784,6 +2788,10 @@ static char *tag_full_fname(tagptrs_T *tagp)
/// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise.
static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help) static int jumpto_tag(const char *lbuf_arg, int forceit, bool keep_help)
{ {
if (postponed_split == 0 && !check_can_set_curbuf_forceit(forceit)) {
return FAIL;
}
char *pbuf_end; char *pbuf_end;
char *tofree_fname = NULL; char *tofree_fname = NULL;
tagptrs_T tagp; tagptrs_T tagp;

View File

@ -133,6 +133,35 @@ static void log_frame_layout(frame_T *frame)
} }
#endif #endif
/// Check if the current window is allowed to move to a different buffer.
///
/// @return If the window has 'winfixbuf', or this function will return false.
bool check_can_set_curbuf_disabled(void)
{
if (curwin->w_p_wfb) {
semsg("%s", e_winfixbuf_cannot_go_to_buffer);
return false;
}
return true;
}
/// Check if the current window is allowed to move to a different buffer.
///
/// @param forceit If true, do not error. If false and 'winfixbuf' is enabled, error.
///
/// @return If the window has 'winfixbuf', then forceit must be true
/// or this function will return false.
bool check_can_set_curbuf_forceit(int forceit)
{
if (!forceit && curwin->w_p_wfb) {
semsg("%s", e_winfixbuf_cannot_go_to_buffer);
return false;
}
return true;
}
/// @return the current window, unless in the cmdline window and "prevwin" is /// @return the current window, unless in the cmdline window and "prevwin" is
/// set, then return "prevwin". /// set, then return "prevwin".
win_T *prevwin_curwin(void) win_T *prevwin_curwin(void)
@ -597,7 +626,7 @@ wingotofile:
ptr = xmemdupz(ptr, len); ptr = xmemdupz(ptr, len);
find_pattern_in_path(ptr, 0, len, true, Prenum == 0, find_pattern_in_path(ptr, 0, len, true, Prenum == 0,
type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); type, Prenum1, ACTION_SPLIT, 1, MAXLNUM, false);
xfree(ptr); xfree(ptr);
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
break; break;

View File

@ -0,0 +1,54 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local exec_lua = helpers.exec_lua
describe("Nvim API calls with 'winfixbuf'", function()
before_each(function()
clear()
end)
it("Calling vim.api.nvim_win_set_buf with 'winfixbuf'", function()
local results = exec_lua([[
local function _setup_two_buffers()
local buffer = vim.api.nvim_create_buf(true, true)
vim.api.nvim_create_buf(true, true) -- Make another buffer
local current_window = 0
vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window})
return buffer
end
local other_buffer = _setup_two_buffers()
local current_window = 0
local results, _ = pcall(vim.api.nvim_win_set_buf, current_window, other_buffer)
return results
]])
assert(results == false)
end)
it("Calling vim.api.nvim_set_current_buf with 'winfixbuf'", function()
local results = exec_lua([[
local function _setup_two_buffers()
local buffer = vim.api.nvim_create_buf(true, true)
vim.api.nvim_create_buf(true, true) -- Make another buffer
local current_window = 0
vim.api.nvim_set_option_value("winfixbuf", true, {win=current_window})
return buffer
end
local other_buffer = _setup_two_buffers()
local results, _ = pcall(vim.api.nvim_set_current_buf, other_buffer)
return results
]])
assert(results == false)
end)
end)

File diff suppressed because it is too large Load Diff