aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/vim.c5
-rw-r--r--src/nvim/api/window.c6
-rw-r--r--src/nvim/arglist.c10
-rw-r--r--src/nvim/buffer.c6
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/ex_cmds.c4
-rw-r--r--src/nvim/ex_cmds.lua2
-rw-r--r--src/nvim/ex_cmds2.c21
-rw-r--r--src/nvim/ex_docmd.c16
-rw-r--r--src/nvim/globals.h3
-rw-r--r--src/nvim/insexpand.c2
-rw-r--r--src/nvim/normal.c7
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/options.lua19
-rw-r--r--src/nvim/quickfix.c33
-rw-r--r--src/nvim/search.c8
-rw-r--r--src/nvim/tag.c8
-rw-r--r--src/nvim/window.c31
18 files changed, 173 insertions, 12 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 84a2f24dbc..24ad7d5fbc 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -876,6 +876,11 @@ void nvim_set_current_buf(Buffer buffer, Error *err)
return;
}
+ if (curwin->w_p_wfb) {
+ api_set_error(err, kErrorTypeException, "%s", e_winfixbuf_cannot_go_to_buffer);
+ return;
+ }
+
try_start();
int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0);
if (!try_end(err) && result == FAIL) {
diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c
index ed51eedf1b..1a80e9ea16 100644
--- a/src/nvim/api/window.c
+++ b/src/nvim/api/window.c
@@ -61,6 +61,12 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err)
if (!win || !buf) {
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) {
api_set_error(err, kErrorTypeException, "%s", e_cmdwin);
return;
diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c
index a02c22deae..4d493c9d03 100644
--- a/src/nvim/arglist.c
+++ b/src/nvim/arglist.c
@@ -623,6 +623,8 @@ void ex_argument(exarg_T *eap)
/// Edit file "argn" of the argument lists.
void do_argfile(exarg_T *eap, int argn)
{
+ bool is_split_cmd = *eap->cmd == 's';
+
int old_arg_idx = curwin->w_arg_idx;
if (argn < 0 || argn >= ARGCOUNT) {
@@ -637,10 +639,16 @@ void do_argfile(exarg_T *eap, int argn)
return;
}
+ if (!is_split_cmd
+ && (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum
+ && !check_can_set_curbuf_forceit(eap->forceit)) {
+ return;
+ }
+
setpcmark();
// 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) {
return;
}
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 7154be36be..e141706edd 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1305,6 +1305,12 @@ int do_buffer(int action, int start, int dir, int count, int forceit)
}
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)) {
// disallow navigating to the dummy buffer
semsg(_(e_nobufnr), count);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 1e5086309c..7f7300706c 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -139,6 +139,8 @@ typedef struct {
#define w_ve_flags w_onebuf_opt.wo_ve_flags // flags for 'virtualedit'
OptInt wo_nuw;
#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;
#define w_p_wfh w_onebuf_opt.wo_wfh // 'winfixheight'
int wo_wfw;
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 74ad8e95a2..14bd2b87e3 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2008,6 +2008,10 @@ static int check_readonly(int *forceit, buf_T *buf)
/// 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)
{
+ if (!check_can_set_curbuf_forceit(forceit)) {
+ return GETFILE_ERROR;
+ }
+
char *ffname = ffname_arg;
char *sfname = sfname_arg;
bool other;
diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua
index 1318eda5eb..e2196f99ec 100644
--- a/src/nvim/ex_cmds.lua
+++ b/src/nvim/ex_cmds.lua
@@ -812,7 +812,7 @@ module.cmds = {
},
{
command = 'drop',
- flags = bit.bor(FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR),
+ flags = bit.bor(BANG, FILES, CMDARG, NEEDARG, ARGOPT, TRLBAR),
addr_type = 'ADDR_NONE',
func = 'ex_drop',
},
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 8016e37ca7..3120868350 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -444,6 +444,27 @@ int buf_write_all(buf_T *buf, bool forceit)
/// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo"
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;
// Temporarily override SHM_OVER and SHM_OVERALL to avoid that file
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 2913f6d4e9..1b4e83d392 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -5334,6 +5334,10 @@ static void ex_resize(exarg_T *eap)
/// ":find [+command] <file>" command.
static void ex_find(exarg_T *eap)
{
+ if (!check_can_set_curbuf_forceit(eap->forceit)) {
+ return;
+ }
+
char *file_to_find = NULL;
char *search_ctx = NULL;
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".
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);
}
@@ -6670,7 +6682,7 @@ static void ex_checkpath(exarg_T *eap)
{
find_pattern_in_path(NULL, 0, 0, false, false, CHECK_PATH, 1,
eap->forceit ? ACTION_SHOW_ALL : ACTION_SHOW,
- 1, (linenr_T)MAXLNUM);
+ 1, (linenr_T)MAXLNUM, eap->forceit);
}
/// ":psearch"
@@ -6729,7 +6741,7 @@ static void ex_findpat(exarg_T *eap)
if (!eap->skip) {
find_pattern_in_path(eap->arg, 0, strlen(eap->arg), whole, !eap->forceit,
*eap->cmd == 'd' ? FIND_DEFINE : FIND_ANY,
- n, action, eap->line1, eap->line2);
+ n, action, eap->line1, eap->line2, eap->forceit);
}
}
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 113985cb52..aecb9d1116 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -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[]
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_unknown_option2[] INIT(= N_("E355: Unknown option: %s"));
diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c
index 41b964323e..d0cd24773f 100644
--- a/src/nvim/insexpand.c
+++ b/src/nvim/insexpand.c
@@ -3027,7 +3027,7 @@ static void get_next_include_file_completion(int compl_type)
((compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL))
? 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
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index f586ad6704..aae9621d4a 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -3896,6 +3896,10 @@ static void nv_gotofile(cmdarg_T *cap)
return;
}
+ if (!check_can_set_curbuf_disabled()) {
+ return;
+ }
+
char *ptr = grab_file_name(cap->count1, &lnum);
if (ptr != NULL) {
@@ -4232,7 +4236,8 @@ static void nv_brackets(cmdarg_T *cap)
(cap->cmdchar == ']'
? curwin->w_cursor.lnum + 1
: 1),
- MAXLNUM);
+ MAXLNUM,
+ false);
xfree(ptr);
curwin->w_set_curswant = true;
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 0ac65ed95d..fcc5b5eb06 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -4629,6 +4629,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return &(win->w_p_rnu);
case PV_NUW:
return &(win->w_p_nuw);
+ case PV_WFB:
+ return &(win->w_p_wfb);
case PV_WFH:
return &(win->w_p_wfh);
case PV_WFW:
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 72f9ff849d..5e8bc1361c 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -8406,6 +8406,8 @@ return {
"split" when both are present.
uselast If included, jump to the previously used window when
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',
full_name = 'switchbuf',
@@ -9817,6 +9819,23 @@ return {
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',
defaults = { if_true = false },
desc = [=[
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 651ebc9f93..a88b781f32 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -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
// window, unless 'switchbuf' contains 'uselast': in this case we
// 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;
} else if (altwin != NULL) {
win = altwin;
@@ -2714,6 +2714,7 @@ static void qf_goto_win_with_qfl_file(int qf_fnum)
// Remember a usable window.
if (altwin == NULL
&& !win->w_p_pvw
+ && !win->w_p_wfb
&& bt_normal(win->w_buffer)) {
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,
prev_winid == curwin->handle ? curwin : NULL);
} 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,
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) {
return;
}
+
+ if (!check_can_set_curbuf_forceit(forceit)) {
+ return;
+ }
+
// Autocommands might have cleared the list, check for that
if (!qf_list_empty(qf_get_curlist(qi))) {
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
// 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.
// :cgetfile creates a new quickfix list but doesn't jump to the
// first error.
@@ -5587,6 +5612,10 @@ theend:
/// ":lvimgrepadd {pattern} file(s)"
void ex_vimgrep(exarg_T *eap)
{
+ if (!check_can_set_curbuf_forceit(eap->forceit)) {
+ return;
+ }
+
char *au_name = vgr_get_auname(eap->cmdidx);
if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, true, curbuf)) {
diff --git a/src/nvim/search.c b/src/nvim/search.c
index 48e41c290d..2fea28ba7c 100644
--- a/src/nvim/search.c
+++ b/src/nvim/search.c
@@ -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 start_lnum first line to start 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,
- 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 *bigger; // When we need more space
@@ -4025,7 +4027,7 @@ search_line:
break;
}
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
}
} else {
@@ -4035,7 +4037,7 @@ search_line:
check_cursor();
} else {
if (!GETFILE_SUCCESS(getfile(0, files[depth].name, NULL, true,
- files[depth].lnum, false))) {
+ files[depth].lnum, forceit))) {
break; // failed to jump to file
}
// autocommands may have changed the lnum, we don't
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index ab5bfc6773..776498fa29 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -290,6 +290,10 @@ void set_buflocal_tfu_callback(buf_T *buf)
/// @param verbose print "tag not found" message
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;
int tagstackidx = curwin->w_tagstackidx;
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.
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 *tofree_fname = NULL;
tagptrs_T tagp;
diff --git a/src/nvim/window.c b/src/nvim/window.c
index ff40a9adef..9f84713ee7 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -133,6 +133,35 @@ static void log_frame_layout(frame_T *frame)
}
#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
/// set, then return "prevwin".
win_T *prevwin_curwin(void)
@@ -597,7 +626,7 @@ wingotofile:
ptr = xmemdupz(ptr, len);
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);
curwin->w_set_curswant = true;
break;