2016-08-29 22:49:24 +02:00
|
|
|
/* vi:set ts=8 sts=4 sw=4 noet:
|
2004-06-13 20:20:40 +00:00
|
|
|
*
|
|
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
|
|
*
|
|
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
|
|
* See README.txt for an overview of the Vim source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* buffer.c: functions for dealing with the buffer structure
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The buffer list is a double linked list of all buffers.
|
|
|
|
* Each buffer can be in one of these states:
|
|
|
|
* never loaded: BF_NEVERLOADED is set, only the file name is valid
|
|
|
|
* not loaded: b_ml.ml_mfp == NULL, no memfile allocated
|
|
|
|
* hidden: b_nwindows == 0, loaded but not displayed in a window
|
|
|
|
* normal: loaded and displayed in a window
|
|
|
|
*
|
|
|
|
* Instead of storing file names all over the place, each file name is
|
|
|
|
* stored in the buffer list. It can be referenced by a number.
|
|
|
|
*
|
|
|
|
* The current implementation remembers all file names ever used.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vim.h"
|
|
|
|
|
2021-05-15 17:23:28 +02:00
|
|
|
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
// Determines how deeply nested %{} blocks will be evaluated in statusline.
|
|
|
|
# define MAX_STL_EVAL_DEPTH 100
|
|
|
|
#endif
|
|
|
|
|
2019-08-20 20:13:45 +02:00
|
|
|
static void enter_buffer(buf_T *buf);
|
|
|
|
static void buflist_getfpos(void);
|
2016-01-29 22:03:47 +01:00
|
|
|
static char_u *buflist_match(regmatch_T *rmp, buf_T *buf, int ignore_case);
|
|
|
|
static char_u *fname_match(regmatch_T *rmp, char_u *name, int ignore_case);
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
2016-07-01 17:17:39 +02:00
|
|
|
static buf_T *buflist_findname_stat(char_u *ffname, stat_T *st);
|
|
|
|
static int otherfile_buf(buf_T *buf, char_u *ffname, stat_T *stp);
|
|
|
|
static int buf_same_ino(buf_T *buf, stat_T *stp);
|
2004-06-13 20:20:40 +00:00
|
|
|
#else
|
2016-01-29 22:03:47 +01:00
|
|
|
static int otherfile_buf(buf_T *buf, char_u *ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2018-06-16 22:58:15 +02:00
|
|
|
static int value_changed(char_u *str, char_u **last);
|
2016-01-29 22:03:47 +01:00
|
|
|
static int append_arg_number(win_T *wp, char_u *buf, int buflen, int add_file);
|
|
|
|
static void free_buffer(buf_T *);
|
|
|
|
static void free_buffer_stuff(buf_T *buf, int free_options);
|
2022-08-26 12:58:17 +01:00
|
|
|
static int bt_nofileread(buf_T *buf);
|
2022-10-09 18:53:32 +01:00
|
|
|
static void no_write_message_buf(buf_T *buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#ifdef UNIX
|
|
|
|
# define dev_T dev_t
|
|
|
|
#else
|
|
|
|
# define dev_T unsigned
|
|
|
|
#endif
|
|
|
|
|
2020-04-06 22:13:01 +02:00
|
|
|
#define FOR_ALL_BUFS_FROM_LAST(buf) \
|
|
|
|
for ((buf) = lastbuf; (buf) != NULL; (buf) = (buf)->b_prev)
|
|
|
|
|
2017-09-16 20:54:51 +02:00
|
|
|
#if defined(FEAT_QUICKFIX)
|
2010-07-25 16:58:46 +02:00
|
|
|
static char *msg_loclist = N_("[Location List]");
|
|
|
|
static char *msg_qflist = N_("[Quickfix List]");
|
|
|
|
#endif
|
|
|
|
|
2019-08-21 22:25:30 +02:00
|
|
|
// Number of times free_buffer() was called.
|
2016-07-10 18:21:50 +02:00
|
|
|
static int buf_free_count = 0;
|
|
|
|
|
2019-08-21 22:25:30 +02:00
|
|
|
static int top_file_num = 1; // highest file number
|
|
|
|
static garray_T buf_reuse = GA_EMPTY; // file numbers to recycle
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the highest possible buffer number.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
get_highest_fnum(void)
|
|
|
|
{
|
|
|
|
return top_file_num - 1;
|
|
|
|
}
|
|
|
|
|
2019-06-08 18:07:21 +02:00
|
|
|
/*
|
|
|
|
* Read data from buffer for retrying.
|
|
|
|
*/
|
2016-08-09 22:14:05 +02:00
|
|
|
static int
|
|
|
|
read_buffer(
|
2019-11-30 20:52:27 +01:00
|
|
|
int read_stdin, // read file from stdin, otherwise fifo
|
|
|
|
exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
|
|
|
|
int flags) // extra flags for readfile()
|
2016-08-09 22:14:05 +02:00
|
|
|
{
|
|
|
|
int retval = OK;
|
|
|
|
linenr_T line_count;
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// Read from the buffer which the text is already filled in and append at
|
|
|
|
// the end. This makes it possible to retry when 'fileformat' or
|
|
|
|
// 'fileencoding' was guessed wrong.
|
2016-08-09 22:14:05 +02:00
|
|
|
line_count = curbuf->b_ml.ml_line_count;
|
|
|
|
retval = readfile(
|
|
|
|
read_stdin ? NULL : curbuf->b_ffname,
|
|
|
|
read_stdin ? NULL : curbuf->b_fname,
|
2021-10-02 11:26:51 +01:00
|
|
|
line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
|
2016-08-09 22:14:05 +02:00
|
|
|
flags | READ_BUFFER);
|
|
|
|
if (retval == OK)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Delete the binary lines.
|
2016-08-09 22:14:05 +02:00
|
|
|
while (--line_count >= 0)
|
2020-05-30 20:30:46 +02:00
|
|
|
ml_delete((linenr_T)1);
|
2016-08-09 22:14:05 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Delete the converted lines.
|
2016-08-09 22:14:05 +02:00
|
|
|
while (curbuf->b_ml.ml_line_count > line_count)
|
2020-05-30 20:30:46 +02:00
|
|
|
ml_delete(line_count);
|
2016-08-09 22:14:05 +02:00
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// Put the cursor on the first line.
|
2016-08-09 22:14:05 +02:00
|
|
|
curwin->w_cursor.lnum = 1;
|
|
|
|
curwin->w_cursor.col = 0;
|
|
|
|
|
|
|
|
if (read_stdin)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Set or reset 'modified' before executing autocommands, so that
|
|
|
|
// it can be changed there.
|
2017-03-12 18:23:53 +01:00
|
|
|
if (!readonlymode && !BUFEMPTY())
|
2016-08-09 22:14:05 +02:00
|
|
|
changed();
|
2017-01-13 22:01:02 +01:00
|
|
|
else if (retval == OK)
|
2019-06-08 18:07:21 +02:00
|
|
|
unchanged(curbuf, FALSE, TRUE);
|
2016-08-09 22:14:05 +02:00
|
|
|
|
2017-01-13 22:01:02 +01:00
|
|
|
if (retval == OK)
|
|
|
|
{
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2017-01-13 22:01:02 +01:00
|
|
|
apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
|
2018-03-04 18:08:14 +01:00
|
|
|
curbuf, &retval);
|
|
|
|
#else
|
2017-01-13 22:01:02 +01:00
|
|
|
apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf);
|
2016-08-09 22:14:05 +02:00
|
|
|
#endif
|
2018-03-04 18:08:14 +01:00
|
|
|
}
|
2016-08-09 22:14:05 +02:00
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2022-01-08 12:41:16 +00:00
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
2019-06-30 22:16:10 +02:00
|
|
|
/*
|
|
|
|
* Ensure buffer "buf" is loaded. Does not trigger the swap-exists action.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
buffer_ensure_loaded(buf_T *buf)
|
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
if (buf->b_ml.ml_mfp != NULL)
|
|
|
|
return;
|
2019-06-30 22:16:10 +02:00
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
aco_save_T aco;
|
|
|
|
|
|
|
|
// Make sure the buffer is in a window. If not then skip it.
|
|
|
|
aucmd_prepbuf(&aco, buf);
|
|
|
|
if (curbuf == buf)
|
|
|
|
{
|
|
|
|
if (swap_exists_action != SEA_READONLY)
|
|
|
|
swap_exists_action = SEA_NONE;
|
|
|
|
open_buffer(FALSE, NULL, 0);
|
|
|
|
aucmd_restbuf(&aco);
|
2019-06-30 22:16:10 +02:00
|
|
|
}
|
|
|
|
}
|
2022-01-08 12:41:16 +00:00
|
|
|
#endif
|
2019-06-30 22:16:10 +02:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
2010-05-23 23:34:36 +02:00
|
|
|
* Open current buffer, that is: open the memfile and read the file into
|
|
|
|
* memory.
|
|
|
|
* Return FAIL for failure, OK otherwise.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
open_buffer(
|
2019-11-30 20:52:27 +01:00
|
|
|
int read_stdin, // read file from stdin
|
|
|
|
exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
|
2022-08-26 11:55:01 +01:00
|
|
|
int flags_arg) // extra flags for readfile()
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-08-26 11:55:01 +01:00
|
|
|
int flags = flags_arg;
|
2004-06-13 20:20:40 +00:00
|
|
|
int retval = OK;
|
2016-07-10 22:11:16 +02:00
|
|
|
bufref_T old_curbuf;
|
2013-02-17 15:45:37 +01:00
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
long old_tw = curbuf->b_p_tw;
|
|
|
|
#endif
|
2016-08-09 22:14:05 +02:00
|
|
|
int read_fifo = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
|
|
|
|
// When re-entering the same buffer, it should not change, because the
|
|
|
|
// user may have reset the flag by hand.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (readonlymode && curbuf->b_ffname != NULL
|
|
|
|
&& (curbuf->b_flags & BF_NEVERLOADED))
|
|
|
|
curbuf->b_p_ro = TRUE;
|
|
|
|
|
2006-01-12 23:22:24 +00:00
|
|
|
if (ml_open(curbuf) == FAIL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2021-10-19 20:48:52 +01:00
|
|
|
// There MUST be a memfile, otherwise we can't do anything
|
|
|
|
// If we can't create one for the current buffer, take another buffer
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(NULL, curbuf, 0, FALSE, FALSE);
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_BUFFERS(curbuf)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curbuf->b_ml.ml_mfp != NULL)
|
|
|
|
break;
|
2021-10-19 20:48:52 +01:00
|
|
|
// If there is no memfile at all, exit.
|
|
|
|
// This is OK, since there are no changes to lose.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curbuf == NULL)
|
|
|
|
{
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_cannot_allocate_any_buffer_exiting));
|
2019-08-20 22:58:37 +02:00
|
|
|
|
|
|
|
// Don't try to do any saving, with "curbuf" NULL almost nothing
|
|
|
|
// will work.
|
|
|
|
v_dying = 2;
|
2004-06-13 20:20:40 +00:00
|
|
|
getout(2);
|
|
|
|
}
|
2019-08-20 22:58:37 +02:00
|
|
|
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_cannot_allocate_buffer_using_other_one));
|
2004-06-13 20:20:40 +00:00
|
|
|
enter_buffer(curbuf);
|
2013-02-17 15:45:37 +01:00
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
if (old_tw != curbuf->b_p_tw)
|
|
|
|
check_colorcolumn(curwin);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2023-05-27 18:02:55 +01:00
|
|
|
// Do not sync this buffer yet, may first want to read the file.
|
|
|
|
if (curbuf->b_ml.ml_mfp != NULL)
|
|
|
|
curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES_NOSYNC;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// The autocommands in readfile() may change the buffer, but only AFTER
|
|
|
|
// reading the file.
|
2016-07-10 22:11:16 +02:00
|
|
|
set_bufref(&old_curbuf, curbuf);
|
2004-06-13 20:20:40 +00:00
|
|
|
modified_was_set = FALSE;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// mark cursor position as being invalid
|
2010-02-24 16:58:36 +01:00
|
|
|
curwin->w_valid = 0;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2022-08-26 11:55:01 +01:00
|
|
|
// A buffer without an actual file should not use the buffer name to read a
|
|
|
|
// file.
|
2022-08-26 12:58:17 +01:00
|
|
|
if (bt_nofileread(curbuf))
|
2022-08-26 11:55:01 +01:00
|
|
|
flags |= READ_NOFILE;
|
|
|
|
|
2022-08-25 12:45:21 +01:00
|
|
|
// Read the file if there is one.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curbuf->b_ffname != NULL
|
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
|
|
|
&& netbeansReadFile
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
2016-03-15 15:09:29 +01:00
|
|
|
int old_msg_silent = msg_silent;
|
2016-08-09 22:14:05 +02:00
|
|
|
#ifdef UNIX
|
|
|
|
int save_bin = curbuf->b_p_bin;
|
|
|
|
int perm;
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
|
|
|
int oldFire = netbeansFireChanges;
|
|
|
|
|
|
|
|
netbeansFireChanges = 0;
|
2016-08-09 22:14:05 +02:00
|
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
|
|
perm = mch_getperm(curbuf->b_ffname);
|
2018-08-11 13:57:20 +02:00
|
|
|
if (perm >= 0 && (S_ISFIFO(perm)
|
2016-08-09 22:14:05 +02:00
|
|
|
|| S_ISSOCK(perm)
|
2016-08-20 15:05:39 +02:00
|
|
|
# ifdef OPEN_CHR_FILES
|
|
|
|
|| (S_ISCHR(perm) && is_dev_fd_file(curbuf->b_ffname))
|
2016-08-09 22:14:05 +02:00
|
|
|
# endif
|
|
|
|
))
|
|
|
|
read_fifo = TRUE;
|
|
|
|
if (read_fifo)
|
|
|
|
curbuf->b_p_bin = TRUE;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2016-03-15 15:09:29 +01:00
|
|
|
if (shortmess(SHM_FILEINFO))
|
|
|
|
msg_silent = 1;
|
2004-06-13 20:20:40 +00:00
|
|
|
retval = readfile(curbuf->b_ffname, curbuf->b_fname,
|
2010-07-24 20:27:03 +02:00
|
|
|
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
|
2016-08-09 22:14:05 +02:00
|
|
|
flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
|
|
|
|
#ifdef UNIX
|
|
|
|
if (read_fifo)
|
|
|
|
{
|
|
|
|
curbuf->b_p_bin = save_bin;
|
|
|
|
if (retval == OK)
|
|
|
|
retval = read_buffer(FALSE, eap, flags);
|
|
|
|
}
|
|
|
|
#endif
|
2016-03-15 15:09:29 +01:00
|
|
|
msg_silent = old_msg_silent;
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
|
|
|
netbeansFireChanges = oldFire;
|
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
// Help buffer is filtered.
|
2017-07-27 22:03:50 +02:00
|
|
|
if (bt_help(curbuf))
|
2004-06-13 20:20:40 +00:00
|
|
|
fix_help_buffer();
|
|
|
|
}
|
|
|
|
else if (read_stdin)
|
|
|
|
{
|
2016-08-09 22:14:05 +02:00
|
|
|
int save_bin = curbuf->b_p_bin;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// First read the text in binary mode into the buffer.
|
|
|
|
// Then read from that same buffer and append at the end. This makes
|
|
|
|
// it possible to retry when 'fileformat' or 'fileencoding' was
|
|
|
|
// guessed wrong.
|
2004-06-13 20:20:40 +00:00
|
|
|
curbuf->b_p_bin = TRUE;
|
|
|
|
retval = readfile(NULL, NULL, (linenr_T)0,
|
2010-07-24 20:27:03 +02:00
|
|
|
(linenr_T)0, (linenr_T)MAXLNUM, NULL,
|
|
|
|
flags | (READ_NEW + READ_STDIN));
|
2004-06-13 20:20:40 +00:00
|
|
|
curbuf->b_p_bin = save_bin;
|
|
|
|
if (retval == OK)
|
2016-08-09 22:14:05 +02:00
|
|
|
retval = read_buffer(TRUE, eap, flags);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2023-05-27 18:02:55 +01:00
|
|
|
// Can now sync this buffer in ml_sync_all().
|
|
|
|
if (curbuf->b_ml.ml_mfp != NULL
|
|
|
|
&& curbuf->b_ml.ml_mfp->mf_dirty == MF_DIRTY_YES_NOSYNC)
|
|
|
|
curbuf->b_ml.ml_mfp->mf_dirty = MF_DIRTY_YES;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// if first time loading this buffer, init b_chartab[]
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curbuf->b_flags & BF_NEVERLOADED)
|
2013-11-05 07:13:41 +01:00
|
|
|
{
|
2004-06-13 20:20:40 +00:00
|
|
|
(void)buf_init_chartab(curbuf, FALSE);
|
2013-11-05 07:13:41 +01:00
|
|
|
parse_cino(curbuf);
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// Set/reset the Changed flag first, autocmds may change the buffer.
|
|
|
|
// Apply the automatic commands, before processing the modelines.
|
|
|
|
// So the modelines have priority over autocommands.
|
|
|
|
//
|
2019-11-30 20:52:27 +01:00
|
|
|
// When reading stdin, the buffer contents always needs writing, so set
|
|
|
|
// the changed flag. Unless in readonly mode: "ls | gview -".
|
|
|
|
// When interrupted and 'cpoptions' contains 'i' set changed flag.
|
2007-06-19 13:36:52 +00:00
|
|
|
if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
|
2019-11-30 20:52:27 +01:00
|
|
|
|| modified_was_set // ":set modified" used in autocmd
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2004-06-13 20:20:40 +00:00
|
|
|
|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
|
|
|
|
#endif
|
2007-06-19 13:36:52 +00:00
|
|
|
)
|
2004-06-13 20:20:40 +00:00
|
|
|
changed();
|
2017-01-13 22:01:02 +01:00
|
|
|
else if (retval == OK && !read_stdin && !read_fifo)
|
2019-06-08 18:07:21 +02:00
|
|
|
unchanged(curbuf, FALSE, TRUE);
|
2019-11-30 20:52:27 +01:00
|
|
|
save_file_ff(curbuf); // keep this fileformat
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Set last_changedtick to avoid triggering a TextChanged autocommand right
|
|
|
|
// after it was added.
|
2018-03-23 22:39:31 +01:00
|
|
|
curbuf->b_last_changedtick = CHANGEDTICK(curbuf);
|
2021-10-16 11:58:55 +01:00
|
|
|
curbuf->b_last_changedtick_i = CHANGEDTICK(curbuf);
|
2018-03-23 22:39:31 +01:00
|
|
|
curbuf->b_last_changedtick_pum = CHANGEDTICK(curbuf);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// require "!" to overwrite the file, because it wasn't read completely
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
if (aborting())
|
|
|
|
#else
|
|
|
|
if (got_int)
|
|
|
|
#endif
|
|
|
|
curbuf->b_flags |= BF_READERR;
|
|
|
|
|
2004-07-26 12:53:41 +00:00
|
|
|
#ifdef FEAT_FOLDING
|
2019-11-30 20:52:27 +01:00
|
|
|
// Need to update automatic folding. Do this before the autocommands,
|
|
|
|
// they may use the fold info.
|
2004-07-26 12:53:41 +00:00
|
|
|
foldUpdateAll(curwin);
|
|
|
|
#endif
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// need to set w_topline, unless some autocommand already did that.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!(curwin->w_valid & VALID_TOPLINE))
|
|
|
|
{
|
|
|
|
curwin->w_topline = 1;
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_DIFF
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin->w_topfill = 0;
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2004-06-13 20:20:40 +00:00
|
|
|
apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
|
2018-03-04 18:08:14 +01:00
|
|
|
#else
|
2004-06-13 20:20:40 +00:00
|
|
|
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
|
|
|
|
#endif
|
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
if (retval != OK)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
// The autocommands may have changed the current buffer. Apply the
|
|
|
|
// modelines to the correct buffer, if it still exists and is loaded.
|
|
|
|
if (bufref_valid(&old_curbuf) && old_curbuf.br_buf->b_ml.ml_mfp != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2023-01-09 19:04:23 +00:00
|
|
|
aco_save_T aco;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
// Go to the buffer that was opened, make sure it is in a window.
|
|
|
|
// If not then skip it.
|
|
|
|
aucmd_prepbuf(&aco, old_curbuf.br_buf);
|
|
|
|
if (curbuf == old_curbuf.br_buf)
|
|
|
|
{
|
|
|
|
do_modelines(0);
|
|
|
|
curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
if ((flags & READ_NOWINENTER) == 0)
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2023-01-09 19:04:23 +00:00
|
|
|
apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL,
|
|
|
|
FALSE, curbuf, &retval);
|
2018-03-04 18:08:14 +01:00
|
|
|
#else
|
2023-01-09 19:04:23 +00:00
|
|
|
apply_autocmds(EVENT_BUFWINENTER, NULL, NULL,
|
|
|
|
FALSE, curbuf);
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
// restore curwin/curbuf and a few other things
|
|
|
|
aucmd_restbuf(&aco);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2016-07-10 18:21:50 +02:00
|
|
|
/*
|
|
|
|
* Store "buf" in "bufref" and set the free count.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_bufref(bufref_T *bufref, buf_T *buf)
|
|
|
|
{
|
|
|
|
bufref->br_buf = buf;
|
2017-06-19 20:35:32 +02:00
|
|
|
bufref->br_fnum = buf == NULL ? 0 : buf->b_fnum;
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref->br_buf_free_count = buf_free_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-06-04 14:58:02 +02:00
|
|
|
* Return TRUE if "bufref->br_buf" points to the same buffer as when
|
|
|
|
* set_bufref() was called and it is a valid buffer.
|
2016-07-10 18:21:50 +02:00
|
|
|
* Only goes through the buffer list if buf_free_count changed.
|
2017-06-04 14:58:02 +02:00
|
|
|
* Also checks if b_fnum is still the same, a :bwipe followed by :new might get
|
|
|
|
* the same allocated memory, but it's a different buffer.
|
2016-07-10 18:21:50 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
bufref_valid(bufref_T *bufref)
|
|
|
|
{
|
|
|
|
return bufref->br_buf_free_count == buf_free_count
|
2017-06-04 14:58:02 +02:00
|
|
|
? TRUE : buf_valid(bufref->br_buf)
|
|
|
|
&& bufref->br_fnum == bufref->br_buf->b_fnum;
|
2016-07-10 18:21:50 +02:00
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" points to a valid buffer (in the buffer list).
|
2016-07-10 18:21:50 +02:00
|
|
|
* This can be slow if there are many buffers, prefer using bufref_valid().
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_valid(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *bp;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Assume that we more often have a recent buffer, start with the last
|
|
|
|
// one.
|
2020-04-06 22:13:01 +02:00
|
|
|
FOR_ALL_BUFS_FROM_LAST(bp)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (bp == buf)
|
|
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2016-07-14 22:09:39 +02:00
|
|
|
/*
|
|
|
|
* A hash table used to quickly lookup a buffer by its number.
|
|
|
|
*/
|
|
|
|
static hashtab_T buf_hashtab;
|
|
|
|
|
|
|
|
static void
|
|
|
|
buf_hashtab_add(buf_T *buf)
|
|
|
|
{
|
|
|
|
sprintf((char *)buf->b_key, "%x", buf->b_fnum);
|
2022-11-25 16:31:51 +00:00
|
|
|
if (hash_add(&buf_hashtab, buf->b_key, "create buffer") == FAIL)
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_buffer_cannot_be_registered));
|
2016-07-14 22:09:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
buf_hashtab_remove(buf_T *buf)
|
|
|
|
{
|
|
|
|
hashitem_T *hi = hash_find(&buf_hashtab, buf->b_key);
|
|
|
|
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
2022-11-25 16:31:51 +00:00
|
|
|
hash_remove(&buf_hashtab, hi, "close buffer");
|
2016-07-14 22:09:39 +02:00
|
|
|
}
|
|
|
|
|
2018-09-01 15:30:03 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE when buffer "buf" can be unloaded.
|
|
|
|
* Give an error message and return FALSE when the buffer is locked or the
|
|
|
|
* screen is being redrawn and the buffer is in a window.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
can_unload_buffer(buf_T *buf)
|
|
|
|
{
|
|
|
|
int can_unload = !buf->b_locked;
|
|
|
|
|
|
|
|
if (can_unload && updating_screen)
|
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
|
|
|
|
FOR_ALL_WINDOWS(wp)
|
|
|
|
if (wp->w_buffer == buf)
|
2018-09-21 16:59:45 +02:00
|
|
|
{
|
2018-09-01 15:30:03 +02:00
|
|
|
can_unload = FALSE;
|
2018-09-21 16:59:45 +02:00
|
|
|
break;
|
|
|
|
}
|
2018-09-01 15:30:03 +02:00
|
|
|
}
|
|
|
|
if (!can_unload)
|
2022-09-28 11:48:30 +01:00
|
|
|
{
|
|
|
|
char_u *fname = buf->b_fname != NULL ? buf->b_fname : buf->b_ffname;
|
|
|
|
|
|
|
|
semsg(_(e_attempt_to_delete_buffer_that_is_in_use_str),
|
|
|
|
fname != NULL ? fname : (char_u *)"[No Name]");
|
|
|
|
}
|
2018-09-01 15:30:03 +02:00
|
|
|
return can_unload;
|
|
|
|
}
|
2018-04-17 23:24:06 +02:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Close the link to a buffer.
|
|
|
|
* "action" is used when there is no longer a window for the buffer.
|
|
|
|
* It can be:
|
|
|
|
* 0 buffer becomes hidden
|
|
|
|
* DOBUF_UNLOAD buffer is unloaded
|
2023-07-01 20:24:40 +01:00
|
|
|
* DOBUF_DEL buffer is unloaded and removed from buffer list
|
2004-06-13 20:20:40 +00:00
|
|
|
* DOBUF_WIPE buffer is unloaded and really deleted
|
2019-08-21 22:25:30 +02:00
|
|
|
* DOBUF_WIPE_REUSE idem, and add to buf_reuse list
|
2004-06-13 20:20:40 +00:00
|
|
|
* When doing all but the first one on the current buffer, the caller should
|
|
|
|
* get a new buffer very soon!
|
|
|
|
*
|
|
|
|
* The 'bufhidden' option can force freeing and deleting.
|
2012-02-22 14:58:37 +01:00
|
|
|
*
|
|
|
|
* When "abort_if_last" is TRUE then do not close the buffer if autocommands
|
|
|
|
* cause there to be only one window with this buffer. e.g. when ":quit" is
|
|
|
|
* supposed to close the window but autocommands close all other windows.
|
2019-12-14 16:18:15 +01:00
|
|
|
*
|
|
|
|
* When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE.
|
2021-01-15 16:22:52 +01:00
|
|
|
*
|
|
|
|
* Return TRUE when we got to the end and b_nwindows was decremented.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
2021-01-15 16:22:52 +01:00
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
close_buffer(
|
2019-11-30 20:52:27 +01:00
|
|
|
win_T *win, // if not NULL, set b_last_cursor
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_T *buf,
|
|
|
|
int action,
|
2019-12-14 16:18:15 +01:00
|
|
|
int abort_if_last,
|
|
|
|
int ignore_abort)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
int is_curbuf;
|
2010-01-19 14:59:56 +01:00
|
|
|
int nwindows;
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
2016-10-30 21:57:52 +01:00
|
|
|
int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
|
2016-09-04 21:33:09 +02:00
|
|
|
win_T *the_curwin = curwin;
|
|
|
|
tabpage_T *the_curtab = curtab;
|
2004-06-13 20:20:40 +00:00
|
|
|
int unload_buf = (action != 0);
|
2019-08-21 22:25:30 +02:00
|
|
|
int wipe_buf = (action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
|
|
|
|
int del_buf = (action == DOBUF_DEL || wipe_buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2020-03-11 14:19:58 +01:00
|
|
|
CHECK_CURBUF;
|
2021-10-19 20:48:52 +01:00
|
|
|
|
|
|
|
// Force unloading or deleting when 'bufhidden' says so.
|
|
|
|
// The caller must take care of NOT deleting/freeing when 'bufhidden' is
|
|
|
|
// "hide" (otherwise we could never free or delete a buffer).
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf->b_p_bh[0] == 'd') // 'bufhidden' == "delete"
|
2017-09-17 19:08:02 +02:00
|
|
|
{
|
|
|
|
del_buf = TRUE;
|
|
|
|
unload_buf = TRUE;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
else if (buf->b_p_bh[0] == 'w') // 'bufhidden' == "wipe"
|
2017-09-17 19:08:02 +02:00
|
|
|
{
|
|
|
|
del_buf = TRUE;
|
|
|
|
unload_buf = TRUE;
|
|
|
|
wipe_buf = TRUE;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
else if (buf->b_p_bh[0] == 'u') // 'bufhidden' == "unload"
|
2017-09-17 19:08:02 +02:00
|
|
|
unload_buf = TRUE;
|
|
|
|
|
2017-08-01 21:44:33 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
2022-10-10 11:46:16 +01:00
|
|
|
// depending on how we get here b_nwindows may already be zero
|
|
|
|
if (bt_terminal(buf) && (buf->b_nwindows <= 1 || del_buf))
|
2017-08-01 21:44:33 +02:00
|
|
|
{
|
2020-03-11 14:19:58 +01:00
|
|
|
CHECK_CURBUF;
|
2017-08-01 21:44:33 +02:00
|
|
|
if (term_job_running(buf->b_term))
|
|
|
|
{
|
2017-08-17 16:55:13 +02:00
|
|
|
if (wipe_buf || unload_buf)
|
2018-04-17 23:24:06 +02:00
|
|
|
{
|
2018-09-01 15:30:03 +02:00
|
|
|
if (!can_unload_buffer(buf))
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2018-09-01 15:30:03 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Wiping out or unloading a terminal buffer kills the job.
|
2017-08-01 21:44:33 +02:00
|
|
|
free_terminal(buf);
|
2022-10-10 11:46:16 +01:00
|
|
|
|
|
|
|
// A terminal buffer is wiped out when job has finished.
|
|
|
|
del_buf = TRUE;
|
|
|
|
unload_buf = TRUE;
|
|
|
|
wipe_buf = TRUE;
|
2018-04-17 23:24:06 +02:00
|
|
|
}
|
2017-08-01 21:44:33 +02:00
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// The job keeps running, hide the buffer.
|
2017-08-01 21:44:33 +02:00
|
|
|
del_buf = FALSE;
|
|
|
|
unload_buf = FALSE;
|
|
|
|
}
|
|
|
|
}
|
2020-11-24 19:36:16 +01:00
|
|
|
else if (buf->b_p_bh[0] == 'h' && !del_buf)
|
|
|
|
{
|
|
|
|
// Hide a terminal buffer.
|
|
|
|
unload_buf = FALSE;
|
|
|
|
}
|
2017-08-01 21:44:33 +02:00
|
|
|
else
|
|
|
|
{
|
2022-10-10 11:46:16 +01:00
|
|
|
if (del_buf || unload_buf)
|
|
|
|
{
|
|
|
|
// A terminal buffer is wiped out if the job has finished.
|
|
|
|
// We only do this when there's an intention to unload the
|
|
|
|
// buffer. This way, :hide and other similar commands won't
|
|
|
|
// wipe the buffer.
|
|
|
|
del_buf = TRUE;
|
|
|
|
unload_buf = TRUE;
|
|
|
|
wipe_buf = TRUE;
|
|
|
|
}
|
2017-08-01 21:44:33 +02:00
|
|
|
}
|
2020-03-11 14:19:58 +01:00
|
|
|
CHECK_CURBUF;
|
2017-08-01 21:44:33 +02:00
|
|
|
}
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Disallow deleting the buffer when it is locked (already being closed or
|
|
|
|
// halfway a command that relies on it). Unloading is allowed.
|
2018-09-01 15:30:03 +02:00
|
|
|
if ((del_buf || wipe_buf) && !can_unload_buffer(buf))
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2016-09-04 19:50:54 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// check no autocommands closed the window
|
2017-09-16 20:54:51 +02:00
|
|
|
if (win != NULL && win_valid_any_tab(win))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Set b_last_cursor when closing the last window for the buffer.
|
|
|
|
// Remember the last cursor position and window options of the buffer.
|
|
|
|
// This used to be only for the current window, but then options like
|
|
|
|
// 'foldmethod' may be lost with a ":only" command.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_nwindows == 1)
|
|
|
|
set_last_cursor(win);
|
|
|
|
buflist_setfpos(buf, win,
|
|
|
|
win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
|
|
|
|
win->w_cursor.col, TRUE);
|
|
|
|
}
|
|
|
|
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// When the buffer is no longer in a window, trigger BufWinLeave
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_nwindows == 1)
|
|
|
|
{
|
2016-09-04 19:50:54 +02:00
|
|
|
++buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
++buf->b_locked_split;
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
|
|
|
|
FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2012-02-22 14:58:37 +01:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands deleted the buffer.
|
2012-06-06 19:02:45 +02:00
|
|
|
aucmd_abort:
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_autocommands_caused_command_to_abort));
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2012-02-22 14:58:37 +01:00
|
|
|
}
|
2016-09-04 19:50:54 +02:00
|
|
|
--buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
--buf->b_locked_split;
|
2012-06-06 19:02:45 +02:00
|
|
|
if (abort_if_last && one_window())
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands made this the only window.
|
2012-06-06 19:02:45 +02:00
|
|
|
goto aucmd_abort;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// When the buffer becomes hidden, but is not unloaded, trigger
|
|
|
|
// BufHidden
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!unload_buf)
|
|
|
|
{
|
2016-09-04 19:50:54 +02:00
|
|
|
++buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
++buf->b_locked_split;
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
|
|
|
|
FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands deleted the buffer.
|
2012-06-06 19:02:45 +02:00
|
|
|
goto aucmd_abort;
|
2016-09-04 19:50:54 +02:00
|
|
|
--buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
--buf->b_locked_split;
|
2012-06-06 19:02:45 +02:00
|
|
|
if (abort_if_last && one_window())
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands made this the only window.
|
2012-06-06 19:02:45 +02:00
|
|
|
goto aucmd_abort;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-12-14 16:18:15 +01:00
|
|
|
// autocmds may abort script processing
|
|
|
|
if (!ignore_abort && aborting())
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2016-09-04 21:33:09 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If the buffer was in curwin and the window has changed, go back to that
|
|
|
|
// window, if it still exists. This avoids that ":edit x" triggering a
|
|
|
|
// "tabnext" BufUnload autocmd leaves a window behind without a buffer.
|
2016-09-04 21:33:09 +02:00
|
|
|
if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin))
|
|
|
|
{
|
|
|
|
block_autocmds();
|
|
|
|
goto_tabpage_win(the_curtab, the_curwin);
|
|
|
|
unblock_autocmds();
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
nwindows = buf->b_nwindows;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// decrease the link count from windows (unless not in any window)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_nwindows > 0)
|
|
|
|
--buf->b_nwindows;
|
|
|
|
|
2017-12-01 20:35:58 +01:00
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
if (diffopt_hiddenoff() && !unload_buf && buf->b_nwindows == 0)
|
2019-11-30 20:52:27 +01:00
|
|
|
diff_buf_delete(buf); // Clear 'diff' for hidden buffer.
|
2017-12-01 20:35:58 +01:00
|
|
|
#endif
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Return when a window is displaying the buffer or when it's not
|
|
|
|
// unloaded.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_nwindows > 0 || !unload_buf)
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Always remove the buffer when there is no file name.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_ffname == NULL)
|
|
|
|
del_buf = TRUE;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// When closing the current buffer stop Visual mode before freeing
|
|
|
|
// anything.
|
2016-09-11 14:39:53 +02:00
|
|
|
if (buf == curbuf && VIsual_active
|
2016-09-09 12:57:09 +02:00
|
|
|
#if defined(EXITFREE)
|
|
|
|
&& !entered_free_all_mem
|
|
|
|
#endif
|
|
|
|
)
|
2016-09-08 23:35:30 +02:00
|
|
|
end_visual_mode();
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// Free all things allocated for this buffer.
|
|
|
|
// Also calls the "BufDelete" autocommands when del_buf is TRUE.
|
|
|
|
//
|
2019-11-30 20:52:27 +01:00
|
|
|
// Remember if we are closing the current buffer. Restore the number of
|
|
|
|
// windows, so that autocommands in buf_freeall() don't get confused.
|
2004-06-13 20:20:40 +00:00
|
|
|
is_curbuf = (buf == curbuf);
|
|
|
|
buf->b_nwindows = nwindows;
|
|
|
|
|
2019-12-14 16:18:15 +01:00
|
|
|
buf_freeall(buf, (del_buf ? BFA_DEL : 0)
|
2020-10-26 21:05:27 +01:00
|
|
|
+ (wipe_buf ? BFA_WIPE : 0)
|
2019-12-14 16:18:15 +01:00
|
|
|
+ (ignore_abort ? BFA_IGNORE_ABORT : 0));
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands may have deleted the buffer.
|
2016-07-10 18:21:50 +02:00
|
|
|
if (!bufref_valid(&bufref))
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-12-14 16:18:15 +01:00
|
|
|
// autocmds may abort script processing
|
|
|
|
if (!ignore_abort && aborting())
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// It's possible that autocommands change curbuf to the one being deleted.
|
|
|
|
// This might cause the previous curbuf to be deleted unexpectedly. But
|
|
|
|
// in some cases it's OK to delete the curbuf, because a new one is
|
|
|
|
// obtained anyway. Therefore only return if curbuf changed to the
|
|
|
|
// deleted buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf == curbuf && !is_curbuf)
|
2021-01-15 16:22:52 +01:00
|
|
|
return FALSE;
|
2016-07-09 15:21:02 +02:00
|
|
|
|
2017-09-16 20:54:51 +02:00
|
|
|
if (win_valid_any_tab(win) && win->w_buffer == buf)
|
2019-11-30 20:52:27 +01:00
|
|
|
win->w_buffer = NULL; // make sure we don't use the buffer now
|
2016-07-09 15:21:02 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands may have opened or closed windows for this buffer.
|
|
|
|
// Decrement the count for the close we do here.
|
2016-07-09 15:21:02 +02:00
|
|
|
if (buf->b_nwindows > 0)
|
|
|
|
--buf->b_nwindows;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove the buffer from the list.
|
|
|
|
*/
|
|
|
|
if (wipe_buf)
|
|
|
|
{
|
2022-03-26 16:28:06 +00:00
|
|
|
// Do not wipe out the buffer if it is used in a window.
|
|
|
|
if (buf->b_nwindows > 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2019-08-21 22:25:30 +02:00
|
|
|
if (action == DOBUF_WIPE_REUSE)
|
|
|
|
{
|
|
|
|
// we can re-use this buffer number, store it
|
|
|
|
if (buf_reuse.ga_itemsize == 0)
|
|
|
|
ga_init2(&buf_reuse, sizeof(int), 50);
|
|
|
|
if (ga_grow(&buf_reuse, 1) == OK)
|
|
|
|
((int *)buf_reuse.ga_data)[buf_reuse.ga_len++] = buf->b_fnum;
|
|
|
|
}
|
2018-10-11 19:27:47 +02:00
|
|
|
if (buf->b_sfname != buf->b_ffname)
|
|
|
|
VIM_CLEAR(buf->b_sfname);
|
|
|
|
else
|
|
|
|
buf->b_sfname = NULL;
|
|
|
|
VIM_CLEAR(buf->b_ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_prev == NULL)
|
|
|
|
firstbuf = buf->b_next;
|
|
|
|
else
|
|
|
|
buf->b_prev->b_next = buf->b_next;
|
|
|
|
if (buf->b_next == NULL)
|
|
|
|
lastbuf = buf->b_prev;
|
|
|
|
else
|
|
|
|
buf->b_next->b_prev = buf->b_prev;
|
|
|
|
free_buffer(buf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (del_buf)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Free all internal variables and reset option values, to make
|
|
|
|
// ":bdel" compatible with Vim 5.7.
|
2004-06-13 20:20:40 +00:00
|
|
|
free_buffer_stuff(buf, TRUE);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Make it look like a new buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Init the options when loaded again.
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_p_initialized = FALSE;
|
|
|
|
}
|
|
|
|
buf_clear_file(buf);
|
|
|
|
if (del_buf)
|
|
|
|
buf->b_p_bl = FALSE;
|
|
|
|
}
|
2020-03-11 14:19:58 +01:00
|
|
|
// NOTE: at this point "curbuf" may be invalid!
|
2021-01-15 16:22:52 +01:00
|
|
|
return TRUE;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make buffer not contain a file.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_clear_file(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf->b_ml.ml_line_count = 1;
|
2019-06-08 18:07:21 +02:00
|
|
|
unchanged(buf, TRUE, TRUE);
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_shortname = FALSE;
|
2022-10-29 20:01:52 +01:00
|
|
|
buf->b_p_eof = FALSE;
|
|
|
|
buf->b_start_eof = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_p_eol = TRUE;
|
|
|
|
buf->b_start_eol = TRUE;
|
|
|
|
buf->b_p_bomb = FALSE;
|
2007-08-12 13:51:26 +00:00
|
|
|
buf->b_start_bomb = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_ml.ml_mfp = NULL;
|
2019-11-30 20:52:27 +01:00
|
|
|
buf->b_ml.ml_flags = ML_EMPTY; // empty buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
|
|
|
netbeans_deleted_all_lines(buf);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* buf_freeall() - free all things allocated for a buffer that are related to
|
2016-09-03 16:29:04 +02:00
|
|
|
* the file. Careful: get here with "curwin" NULL when exiting.
|
|
|
|
* flags:
|
2019-12-14 16:18:15 +01:00
|
|
|
* BFA_DEL buffer is going to be deleted
|
|
|
|
* BFA_WIPE buffer is going to be wiped out
|
|
|
|
* BFA_KEEP_UNDO do not free undo information
|
|
|
|
* BFA_IGNORE_ABORT don't abort even when aborting() returns TRUE
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_freeall(buf_T *buf, int flags)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
int is_curbuf = (buf == curbuf);
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
2016-09-04 23:41:42 +02:00
|
|
|
int is_curwin = (curwin != NULL && curwin->w_buffer == buf);
|
2016-09-03 16:29:04 +02:00
|
|
|
win_T *the_curwin = curwin;
|
|
|
|
tabpage_T *the_curtab = curtab;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Make sure the buffer isn't closed by autocommands.
|
2016-09-04 19:50:54 +02:00
|
|
|
++buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
++buf->b_locked_split;
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
2016-05-24 16:07:40 +02:00
|
|
|
if (buf->b_ml.ml_mfp != NULL)
|
|
|
|
{
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname,
|
|
|
|
FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2019-11-30 20:52:27 +01:00
|
|
|
// autocommands deleted the buffer
|
2016-05-24 16:07:40 +02:00
|
|
|
return;
|
|
|
|
}
|
2010-07-24 20:27:03 +02:00
|
|
|
if ((flags & BFA_DEL) && buf->b_p_bl)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname,
|
|
|
|
FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2019-11-30 20:52:27 +01:00
|
|
|
// autocommands deleted the buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
return;
|
|
|
|
}
|
2010-07-24 20:27:03 +02:00
|
|
|
if (flags & BFA_WIPE)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname,
|
|
|
|
FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2019-11-30 20:52:27 +01:00
|
|
|
// autocommands deleted the buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-09-04 19:50:54 +02:00
|
|
|
--buf->b_locked;
|
2021-02-07 12:12:43 +01:00
|
|
|
--buf->b_locked_split;
|
2016-09-03 16:29:04 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If the buffer was in curwin and the window has changed, go back to that
|
|
|
|
// window, if it still exists. This avoids that ":edit x" triggering a
|
|
|
|
// "tabnext" BufUnload autocmd leaves a window behind without a buffer.
|
2016-09-03 16:29:04 +02:00
|
|
|
if (is_curwin && curwin != the_curwin && win_valid_any_tab(the_curwin))
|
|
|
|
{
|
|
|
|
block_autocmds();
|
|
|
|
goto_tabpage_win(the_curtab, the_curwin);
|
|
|
|
unblock_autocmds();
|
|
|
|
}
|
|
|
|
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-12-14 16:18:15 +01:00
|
|
|
// autocmds may abort script processing
|
|
|
|
if ((flags & BFA_IGNORE_ABORT) == 0 && aborting())
|
2004-06-13 20:20:40 +00:00
|
|
|
return;
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// It's possible that autocommands change curbuf to the one being deleted.
|
|
|
|
// This might cause curbuf to be deleted unexpectedly. But in some cases
|
|
|
|
// it's OK to delete the curbuf, because a new one is obtained anyway.
|
|
|
|
// Therefore only return if curbuf changed to the deleted buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf == curbuf && !is_curbuf)
|
|
|
|
return;
|
|
|
|
#ifdef FEAT_DIFF
|
2019-11-30 20:52:27 +01:00
|
|
|
diff_buf_delete(buf); // Can't use 'diff' for unloaded buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2011-09-14 14:43:25 +02:00
|
|
|
#ifdef FEAT_SYN_HL
|
2019-11-30 20:52:27 +01:00
|
|
|
// Remove any ownsyntax, unless exiting.
|
2016-09-04 23:41:42 +02:00
|
|
|
if (curwin != NULL && curwin->w_buffer == buf)
|
2011-11-30 15:40:56 +01:00
|
|
|
reset_synblock(curwin);
|
2011-09-14 14:43:25 +02:00
|
|
|
#endif
|
2007-05-10 16:44:05 +00:00
|
|
|
|
|
|
|
#ifdef FEAT_FOLDING
|
2019-11-30 20:52:27 +01:00
|
|
|
// No folds in an empty buffer.
|
2007-05-10 16:44:05 +00:00
|
|
|
{
|
|
|
|
win_T *win;
|
|
|
|
tabpage_T *tp;
|
|
|
|
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, win)
|
|
|
|
if (win->w_buffer == buf)
|
|
|
|
clearFolding(win);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_TCL
|
|
|
|
tcl_buffer_free(buf);
|
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
ml_close(buf, TRUE); // close and delete the memline/memfile
|
|
|
|
buf->b_ml.ml_line_count = 0; // no lines in buffer
|
2010-07-24 20:27:03 +02:00
|
|
|
if ((flags & BFA_KEEP_UNDO) == 0)
|
2024-02-15 20:17:37 +01:00
|
|
|
// free the memory allocated for undo
|
|
|
|
// and reset all undo information
|
|
|
|
u_clearallandblockfree(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_SYN_HL
|
2019-11-30 20:52:27 +01:00
|
|
|
syntax_clear(&buf->b_s); // reset syntax info
|
2018-12-13 22:20:09 +01:00
|
|
|
#endif
|
2019-11-30 22:48:27 +01:00
|
|
|
#ifdef FEAT_PROP_POPUP
|
2018-12-13 22:20:09 +01:00
|
|
|
clear_buf_prop_types(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
buf->b_flags &= ~BF_READERR; // a read error is no longer relevant
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free a buffer structure and the things it contains related to the buffer
|
|
|
|
* itself (not the file, that must have been done already).
|
|
|
|
*/
|
|
|
|
static void
|
2016-01-30 15:14:10 +01:00
|
|
|
free_buffer(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-10 18:21:50 +02:00
|
|
|
++buf_free_count;
|
2004-06-13 20:20:40 +00:00
|
|
|
free_buffer_stuff(buf, TRUE);
|
2013-04-15 12:27:36 +02:00
|
|
|
#ifdef FEAT_EVAL
|
2019-11-30 20:52:27 +01:00
|
|
|
// b:changedtick uses an item in buf_T, remove it now
|
2022-11-25 16:31:51 +00:00
|
|
|
dictitem_remove(buf->b_vars, (dictitem_T *)&buf->b_ct_di, "free buffer");
|
2013-04-15 12:27:36 +02:00
|
|
|
unref_var_dict(buf->b_vars);
|
2019-10-01 17:02:16 +02:00
|
|
|
remove_listeners(buf);
|
2013-04-15 12:27:36 +02:00
|
|
|
#endif
|
2010-07-14 23:23:17 +02:00
|
|
|
#ifdef FEAT_LUA
|
|
|
|
lua_buffer_free(buf);
|
|
|
|
#endif
|
2004-07-05 15:58:32 +00:00
|
|
|
#ifdef FEAT_MZSCHEME
|
|
|
|
mzscheme_buffer_free(buf);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_PERL
|
|
|
|
perl_buf_free(buf);
|
|
|
|
#endif
|
|
|
|
#ifdef FEAT_PYTHON
|
|
|
|
python_buffer_free(buf);
|
|
|
|
#endif
|
2010-07-17 21:19:38 +02:00
|
|
|
#ifdef FEAT_PYTHON3
|
|
|
|
python3_buffer_free(buf);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_RUBY
|
|
|
|
ruby_buffer_free(buf);
|
2004-12-24 14:35:23 +00:00
|
|
|
#endif
|
2016-05-09 20:38:53 +02:00
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
|
|
|
channel_buffer_free(buf);
|
|
|
|
#endif
|
2017-07-17 23:20:24 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
2017-07-28 21:51:57 +02:00
|
|
|
free_terminal(buf);
|
2017-07-17 23:20:24 +02:00
|
|
|
#endif
|
2018-06-03 14:47:35 +02:00
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
|
|
|
vim_free(buf->b_prompt_text);
|
2019-06-01 13:28:35 +02:00
|
|
|
free_callback(&buf->b_prompt_callback);
|
2019-10-01 17:02:16 +02:00
|
|
|
free_callback(&buf->b_prompt_interrupt);
|
2018-06-03 14:47:35 +02:00
|
|
|
#endif
|
2016-07-14 22:09:39 +02:00
|
|
|
|
|
|
|
buf_hashtab_remove(buf);
|
|
|
|
|
2016-07-14 23:03:19 +02:00
|
|
|
aubuflocal_remove(buf);
|
|
|
|
|
2014-04-06 20:45:43 +02:00
|
|
|
if (autocmd_busy)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Do not free the buffer structure while autocommands are executing,
|
|
|
|
// it's still needed. Free it when autocmd_busy is reset.
|
2014-04-06 20:45:43 +02:00
|
|
|
buf->b_next = au_pending_free_buf;
|
|
|
|
au_pending_free_buf = buf;
|
|
|
|
}
|
|
|
|
else
|
2020-03-11 14:19:58 +01:00
|
|
|
{
|
2014-04-06 20:45:43 +02:00
|
|
|
vim_free(buf);
|
2020-03-11 14:19:58 +01:00
|
|
|
if (curbuf == buf)
|
|
|
|
curbuf = NULL; // make clear it's not to be used
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-17 16:31:35 +01:00
|
|
|
/*
|
2017-02-25 14:59:34 +01:00
|
|
|
* Initializes b:changedtick.
|
2017-02-17 16:31:35 +01:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
init_changedtick(buf_T *buf)
|
|
|
|
{
|
2017-02-25 14:59:34 +01:00
|
|
|
dictitem_T *di = (dictitem_T *)&buf->b_ct_di;
|
2017-02-17 16:31:35 +01:00
|
|
|
|
2017-02-25 14:59:34 +01:00
|
|
|
di->di_flags = DI_FLAGS_FIX | DI_FLAGS_RO;
|
|
|
|
di->di_tv.v_type = VAR_NUMBER;
|
|
|
|
di->di_tv.v_lock = VAR_FIXED;
|
|
|
|
di->di_tv.vval.v_number = 0;
|
|
|
|
|
2017-02-25 15:41:37 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2017-02-25 14:59:34 +01:00
|
|
|
STRCPY(buf->b_ct_di.di_key, "changedtick");
|
|
|
|
(void)dict_add(buf->b_vars, di);
|
2017-02-25 15:41:37 +01:00
|
|
|
#endif
|
2017-02-17 16:31:35 +01:00
|
|
|
}
|
|
|
|
|
2022-08-26 12:58:17 +01:00
|
|
|
/*
|
|
|
|
* Free the b_wininfo list for buffer "buf".
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
clear_wininfo(buf_T *buf)
|
|
|
|
{
|
|
|
|
wininfo_T *wip;
|
|
|
|
|
|
|
|
while (buf->b_wininfo != NULL)
|
|
|
|
{
|
|
|
|
wip = buf->b_wininfo;
|
|
|
|
buf->b_wininfo = wip->wi_next;
|
|
|
|
free_wininfo(wip);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Free stuff in the buffer for ":bdel" and when wiping out the buffer.
|
|
|
|
*/
|
|
|
|
static void
|
2016-01-30 15:14:10 +01:00
|
|
|
free_buffer_stuff(
|
|
|
|
buf_T *buf,
|
2019-11-30 20:52:27 +01:00
|
|
|
int free_options) // free options as well
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (free_options)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
clear_wininfo(buf); // including window-local options
|
2004-06-13 20:20:40 +00:00
|
|
|
free_buf_options(buf, TRUE);
|
2010-10-27 16:18:00 +02:00
|
|
|
#ifdef FEAT_SPELL
|
|
|
|
ga_clear(&buf->b_s.b_langp);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
#ifdef FEAT_EVAL
|
2017-02-17 16:31:35 +01:00
|
|
|
{
|
2017-02-25 14:59:34 +01:00
|
|
|
varnumber_T tick = CHANGEDTICK(buf);
|
2017-02-17 16:31:35 +01:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
vars_clear(&buf->b_vars->dv_hashtab); // free all buffer variables
|
2017-02-17 16:31:35 +01:00
|
|
|
hash_init(&buf->b_vars->dv_hashtab);
|
|
|
|
init_changedtick(buf);
|
2017-02-25 14:59:34 +01:00
|
|
|
CHANGEDTICK(buf) = tick;
|
2020-01-03 21:00:02 +01:00
|
|
|
remove_listeners(buf);
|
2017-02-17 16:31:35 +01:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2019-04-27 13:04:13 +02:00
|
|
|
uc_clear(&buf->b_ucmds); // clear local user commands
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_SIGNS
|
2019-04-27 13:04:13 +02:00
|
|
|
buf_delete_signs(buf, (char_u *)"*"); // delete any signs
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2009-01-06 15:14:30 +00:00
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
2010-05-22 21:34:09 +02:00
|
|
|
netbeans_file_killed(buf);
|
2022-07-25 19:07:04 +01:00
|
|
|
#endif
|
|
|
|
#ifdef FEAT_PROP_POPUP
|
|
|
|
ga_clear_strings(&buf->b_textprop_text);
|
2009-01-06 15:14:30 +00:00
|
|
|
#endif
|
2022-06-29 10:37:40 +01:00
|
|
|
map_clear_mode(buf, MAP_ALL_MODES, TRUE, FALSE); // clear local mappings
|
|
|
|
map_clear_mode(buf, MAP_ALL_MODES, TRUE, TRUE); // clear local abbrevs
|
2018-02-10 18:45:26 +01:00
|
|
|
VIM_CLEAR(buf->b_start_fenc);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 17:55:09 +01:00
|
|
|
/*
|
|
|
|
* Free one wininfo_T.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
free_wininfo(wininfo_T *wip)
|
|
|
|
{
|
|
|
|
if (wip->wi_optset)
|
|
|
|
{
|
|
|
|
clear_winopt(&wip->wi_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
deleteFoldRecurse(&wip->wi_folds);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
vim_free(wip);
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Go to another buffer. Handles the result of the ATTENTION dialog.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
goto_buffer(
|
|
|
|
exarg_T *eap,
|
|
|
|
int start,
|
|
|
|
int dir,
|
|
|
|
int count)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-10 22:11:16 +02:00
|
|
|
bufref_T old_curbuf;
|
2022-04-04 16:57:21 +01:00
|
|
|
int save_sea = swap_exists_action;
|
2016-07-10 22:11:16 +02:00
|
|
|
|
|
|
|
set_bufref(&old_curbuf, curbuf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2022-04-04 16:57:21 +01:00
|
|
|
if (swap_exists_action == SEA_NONE)
|
|
|
|
swap_exists_action = SEA_DIALOG;
|
2004-06-13 20:20:40 +00:00
|
|
|
(void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
|
|
|
|
start, dir, count, eap->forceit);
|
|
|
|
if (swap_exists_action == SEA_QUIT && *eap->cmd == 's')
|
|
|
|
{
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2004-09-13 20:26:32 +00:00
|
|
|
cleanup_T cs;
|
2004-07-26 12:53:41 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Reset the error/interrupt/exception state here so that
|
|
|
|
// aborting() returns FALSE when closing a window.
|
2004-09-13 20:26:32 +00:00
|
|
|
enter_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-07-26 12:53:41 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Quitting means closing the split window, nothing else.
|
2004-06-13 20:20:40 +00:00
|
|
|
win_close(curwin, TRUE);
|
2022-04-04 16:57:21 +01:00
|
|
|
swap_exists_action = save_sea;
|
2005-12-16 21:49:31 +00:00
|
|
|
swap_exists_did_quit = TRUE;
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Restore the error/interrupt/exception state if not discarded by a
|
|
|
|
// new aborting error, interrupt, or uncaught exception.
|
2004-09-13 20:26:32 +00:00
|
|
|
leave_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
2016-07-10 22:11:16 +02:00
|
|
|
handle_swap_exists(&old_curbuf);
|
2018-03-29 16:04:08 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the situation of swap_exists_action being set.
|
|
|
|
* It is allowed for "old_curbuf" to be NULL or invalid.
|
|
|
|
*/
|
|
|
|
void
|
2016-07-10 22:11:16 +02:00
|
|
|
handle_swap_exists(bufref_T *old_curbuf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2004-09-13 20:26:32 +00:00
|
|
|
cleanup_T cs;
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
|
|
|
#ifdef FEAT_SYN_HL
|
2013-02-17 15:45:37 +01:00
|
|
|
long old_tw = curbuf->b_p_tw;
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2016-07-10 22:11:16 +02:00
|
|
|
buf_T *buf;
|
2004-07-26 12:53:41 +00:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if (swap_exists_action == SEA_QUIT)
|
|
|
|
{
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Reset the error/interrupt/exception state here so that
|
|
|
|
// aborting() returns FALSE when closing a buffer.
|
2004-09-13 20:26:32 +00:00
|
|
|
enter_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// User selected Quit at ATTENTION prompt. Go back to previous
|
|
|
|
// buffer. If that buffer is gone or the same as the current one,
|
|
|
|
// open a new, empty buffer.
|
|
|
|
swap_exists_action = SEA_NONE; // don't want it again
|
2005-12-16 21:49:31 +00:00
|
|
|
swap_exists_did_quit = TRUE;
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE, FALSE);
|
2016-07-10 22:11:16 +02:00
|
|
|
if (old_curbuf == NULL || !bufref_valid(old_curbuf)
|
|
|
|
|| old_curbuf->br_buf == curbuf)
|
2021-07-04 13:27:11 +02:00
|
|
|
{
|
|
|
|
// Block autocommands here because curwin->w_buffer is NULL.
|
|
|
|
block_autocmds();
|
2016-07-10 22:11:16 +02:00
|
|
|
buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED);
|
2021-07-04 13:27:11 +02:00
|
|
|
unblock_autocmds();
|
|
|
|
}
|
2016-07-10 22:11:16 +02:00
|
|
|
else
|
|
|
|
buf = old_curbuf->br_buf;
|
|
|
|
if (buf != NULL)
|
2013-02-17 15:45:37 +01:00
|
|
|
{
|
2018-08-21 18:50:18 +02:00
|
|
|
int old_msg_silent = msg_silent;
|
|
|
|
|
|
|
|
if (shortmess(SHM_FILEINFO))
|
|
|
|
msg_silent = 1; // prevent fileinfo message
|
2016-07-10 22:11:16 +02:00
|
|
|
enter_buffer(buf);
|
2018-08-21 18:50:18 +02:00
|
|
|
// restore msg_silent, so that the command line will be shown
|
|
|
|
msg_silent = old_msg_silent;
|
|
|
|
|
2019-04-28 22:25:38 +02:00
|
|
|
#ifdef FEAT_SYN_HL
|
2013-02-17 15:45:37 +01:00
|
|
|
if (old_tw != curbuf->b_p_tw)
|
|
|
|
check_colorcolumn(curwin);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2013-02-17 15:45:37 +01:00
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// If "old_curbuf" is NULL we are in big trouble here...
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Restore the error/interrupt/exception state if not discarded by a
|
|
|
|
// new aborting error, interrupt, or uncaught exception.
|
2004-09-13 20:26:32 +00:00
|
|
|
leave_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else if (swap_exists_action == SEA_RECOVER)
|
|
|
|
{
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Reset the error/interrupt/exception state here so that
|
|
|
|
// aborting() returns FALSE when closing a buffer.
|
2004-09-13 20:26:32 +00:00
|
|
|
enter_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// User selected Recover at ATTENTION prompt.
|
2004-06-13 20:20:40 +00:00
|
|
|
msg_scroll = TRUE;
|
2019-05-23 21:35:48 +02:00
|
|
|
ml_recover(FALSE);
|
2019-11-30 20:52:27 +01:00
|
|
|
msg_puts("\n"); // don't overwrite the last message
|
2004-06-13 20:20:40 +00:00
|
|
|
cmdline_row = msg_row;
|
2006-03-08 21:32:40 +00:00
|
|
|
do_modelines(0);
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Restore the error/interrupt/exception state if not discarded by a
|
|
|
|
// new aborting error, interrupt, or uncaught exception.
|
2004-09-13 20:26:32 +00:00
|
|
|
leave_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
swap_exists_action = SEA_NONE;
|
|
|
|
}
|
|
|
|
|
2014-01-10 16:43:14 +01:00
|
|
|
/*
|
|
|
|
* Make the current buffer empty.
|
|
|
|
* Used when it is wiped out and it's the last buffer.
|
|
|
|
*/
|
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
empty_curbuf(
|
|
|
|
int close_others,
|
|
|
|
int forceit,
|
|
|
|
int action)
|
2014-01-10 16:43:14 +01:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
buf_T *buf = curbuf;
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
2014-01-10 16:43:14 +01:00
|
|
|
|
|
|
|
if (action == DOBUF_UNLOAD)
|
|
|
|
{
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_cannot_unload_last_buffer));
|
2014-01-10 16:43:14 +01:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
2014-01-10 16:43:14 +01:00
|
|
|
if (close_others)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Close any other windows on this buffer, then make it empty.
|
2014-01-10 16:43:14 +01:00
|
|
|
close_windows(buf, TRUE);
|
|
|
|
|
|
|
|
setpcmark();
|
|
|
|
retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
|
|
|
|
forceit ? ECMD_FORCEIT : 0, curwin);
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// do_ecmd() may create a new buffer, then we have to delete
|
|
|
|
// the old one. But do_ecmd() may have done that already, check
|
|
|
|
// if the buffer still exists.
|
2016-07-10 18:21:50 +02:00
|
|
|
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows == 0)
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(NULL, buf, action, FALSE, FALSE);
|
2014-01-10 16:43:14 +01:00
|
|
|
if (!close_others)
|
|
|
|
need_fileinfo = FALSE;
|
|
|
|
return retval;
|
|
|
|
}
|
2018-09-01 15:30:03 +02:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Implementation of the commands for the buffer list.
|
|
|
|
*
|
|
|
|
* action == DOBUF_GOTO go to specified buffer
|
|
|
|
* action == DOBUF_SPLIT split window and go to specified buffer
|
|
|
|
* action == DOBUF_UNLOAD unload specified buffer(s)
|
|
|
|
* action == DOBUF_DEL delete specified buffer(s) from buffer list
|
|
|
|
* action == DOBUF_WIPE delete specified buffer(s) really
|
2019-08-21 22:25:30 +02:00
|
|
|
* action == DOBUF_WIPE_REUSE idem, and add number to "buf_reuse"
|
2004-06-13 20:20:40 +00:00
|
|
|
*
|
|
|
|
* start == DOBUF_CURRENT go to "count" buffer from current buffer
|
|
|
|
* start == DOBUF_FIRST go to "count" buffer from first buffer
|
|
|
|
* start == DOBUF_LAST go to "count" buffer from last buffer
|
|
|
|
* start == DOBUF_MOD go to "count" modified buffer from current buffer
|
|
|
|
*
|
|
|
|
* Return FAIL or OK.
|
|
|
|
*/
|
2021-06-10 21:07:48 +02:00
|
|
|
static int
|
|
|
|
do_buffer_ext(
|
2016-01-30 15:14:10 +01:00
|
|
|
int action,
|
|
|
|
int start,
|
2019-11-30 20:52:27 +01:00
|
|
|
int dir, // FORWARD or BACKWARD
|
|
|
|
int count, // buffer number or number of buffers
|
2021-06-10 21:07:48 +02:00
|
|
|
int flags) // DOBUF_FORCEIT etc.
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
buf_T *bp;
|
|
|
|
int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
|
2019-08-21 22:25:30 +02:00
|
|
|
|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
switch (start)
|
|
|
|
{
|
|
|
|
case DOBUF_FIRST: buf = firstbuf; break;
|
|
|
|
case DOBUF_LAST: buf = lastbuf; break;
|
|
|
|
default: buf = curbuf; break;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
if (start == DOBUF_MOD) // find next modified buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
while (count-- > 0)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
buf = buf->b_next;
|
|
|
|
if (buf == NULL)
|
|
|
|
buf = firstbuf;
|
|
|
|
}
|
|
|
|
while (buf != curbuf && !bufIsChanged(buf));
|
|
|
|
}
|
|
|
|
if (!bufIsChanged(buf))
|
|
|
|
{
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_no_modified_buffer_found));
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
else if (start == DOBUF_FIRST && count) // find specified buffer number
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
while (buf != NULL && buf->b_fnum != count)
|
|
|
|
buf = buf->b_next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bp = NULL;
|
|
|
|
while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// remember the buffer where we start, we come back there when all
|
|
|
|
// buffers are unlisted.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (bp == NULL)
|
|
|
|
bp = buf;
|
|
|
|
if (dir == FORWARD)
|
|
|
|
{
|
|
|
|
buf = buf->b_next;
|
|
|
|
if (buf == NULL)
|
|
|
|
buf = firstbuf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
buf = buf->b_prev;
|
|
|
|
if (buf == NULL)
|
|
|
|
buf = lastbuf;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// don't count unlisted buffers
|
2004-06-13 20:20:40 +00:00
|
|
|
if (unload || buf->b_p_bl)
|
|
|
|
{
|
|
|
|
--count;
|
2019-11-30 20:52:27 +01:00
|
|
|
bp = NULL; // use this buffer as new starting point
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
if (bp == buf)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// back where we started, didn't find anything.
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_there_is_no_listed_buffer));
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf == NULL) // could not find it
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (start == DOBUF_FIRST)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// don't warn when deleting
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!unload)
|
2021-12-05 22:19:27 +00:00
|
|
|
semsg(_(e_buffer_nr_does_not_exist), count);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else if (dir == FORWARD)
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_cannot_go_beyond_last_buffer));
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2021-12-05 22:19:27 +00:00
|
|
|
emsg(_(e_cannot_go_before_first_buffer));
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
2021-06-10 21:07:48 +02:00
|
|
|
#ifdef FEAT_PROP_POPUP
|
2022-08-25 15:11:15 +01:00
|
|
|
if ((flags & DOBUF_NOPOPUP) && bt_popup(buf) && !bt_terminal(buf))
|
2021-06-10 21:07:48 +02:00
|
|
|
return OK;
|
|
|
|
#endif
|
2022-10-18 17:05:54 +01:00
|
|
|
if ((action == DOBUF_GOTO || action == DOBUF_SPLIT)
|
|
|
|
&& (buf->b_flags & BF_DUMMY))
|
|
|
|
{
|
|
|
|
// disallow navigating to the dummy buffer
|
|
|
|
semsg(_(e_buffer_nr_does_not_exist), count);
|
|
|
|
return FAIL;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#ifdef FEAT_GUI
|
|
|
|
need_mouse_correct = TRUE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2021-10-19 20:48:52 +01:00
|
|
|
* delete buffer "buf" from memory and/or the list
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
if (unload)
|
|
|
|
{
|
|
|
|
int forward;
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
|
|
|
|
2018-09-01 15:30:03 +02:00
|
|
|
if (!can_unload_buffer(buf))
|
2018-04-17 23:24:06 +02:00
|
|
|
return FAIL;
|
|
|
|
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// When unloading or deleting a buffer that's already unloaded and
|
|
|
|
// unlisted: fail silently.
|
2019-08-21 22:25:30 +02:00
|
|
|
if (action != DOBUF_WIPE && action != DOBUF_WIPE_REUSE
|
|
|
|
&& buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
|
2021-06-10 21:07:48 +02:00
|
|
|
if ((flags & DOBUF_FORCEIT) == 0 && bufIsChanged(buf))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-03-29 16:04:08 +02:00
|
|
|
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
|
2020-10-24 20:49:43 +02:00
|
|
|
if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-10-09 18:53:32 +01:00
|
|
|
# ifdef FEAT_TERMINAL
|
|
|
|
if (term_job_running(buf->b_term))
|
|
|
|
{
|
|
|
|
if (term_confirm_stop(buf) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
# endif
|
|
|
|
{
|
|
|
|
dialog_changed(buf, FALSE);
|
|
|
|
if (!bufref_valid(&bufref))
|
|
|
|
// Autocommand deleted buffer, oops! It's not changed
|
|
|
|
// now.
|
|
|
|
return FAIL;
|
|
|
|
// If it's still changed fail silently, the dialog already
|
|
|
|
// mentioned why it fails.
|
|
|
|
if (bufIsChanged(buf))
|
|
|
|
return FAIL;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2004-12-09 21:34:53 +00:00
|
|
|
else
|
2018-03-29 16:04:08 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-10-09 18:53:32 +01:00
|
|
|
no_write_message_buf(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// When closing the current buffer stop Visual mode.
|
2016-09-11 14:39:53 +02:00
|
|
|
if (buf == curbuf && VIsual_active)
|
2016-09-08 23:35:30 +02:00
|
|
|
end_visual_mode();
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// If deleting the last (listed) buffer, make it empty.
|
|
|
|
// The last (listed) buffer cannot be unloaded.
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_BUFFERS(bp)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (bp->b_p_bl && bp != buf)
|
|
|
|
break;
|
|
|
|
if (bp == NULL && buf == curbuf)
|
2021-06-10 21:07:48 +02:00
|
|
|
return empty_curbuf(TRUE, (flags & DOBUF_FORCEIT), action);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// If the deleted buffer is the current one, close the current window
|
|
|
|
// (unless it's the only window). Repeat this so long as we end up in
|
|
|
|
// a window with this buffer.
|
2006-02-16 22:11:02 +00:00
|
|
|
while (buf == curbuf
|
2016-09-04 19:50:54 +02:00
|
|
|
&& !(curwin->w_closing || curwin->w_buffer->b_locked > 0)
|
2016-11-10 18:16:33 +01:00
|
|
|
&& (!ONE_WINDOW || first_tabpage->tp_next != NULL))
|
2013-08-14 17:11:20 +02:00
|
|
|
{
|
|
|
|
if (win_close(curwin, FALSE) == FAIL)
|
|
|
|
break;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// If the buffer to be deleted is not the current one, delete it here.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf != curbuf)
|
|
|
|
{
|
2006-02-16 22:11:02 +00:00
|
|
|
close_windows(buf, FALSE);
|
2017-09-16 20:54:51 +02:00
|
|
|
if (buf != curbuf && bufref_valid(&bufref) && buf->b_nwindows <= 0)
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(NULL, buf, action, FALSE, FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deleting the current buffer: Need to find another buffer to go to.
|
2014-01-10 16:43:14 +01:00
|
|
|
* There should be another, otherwise it would have been handled
|
|
|
|
* above. However, autocommands may have deleted all buffers.
|
2016-07-10 19:03:57 +02:00
|
|
|
* First use au_new_curbuf.br_buf, if it is valid.
|
2004-06-13 20:20:40 +00:00
|
|
|
* Then prefer the buffer we most recently visited.
|
|
|
|
* Else try to find one that is loaded, after the current buffer,
|
|
|
|
* then before the current buffer.
|
|
|
|
* Finally use any buffer.
|
|
|
|
*/
|
2019-11-30 20:52:27 +01:00
|
|
|
buf = NULL; // selected buffer
|
|
|
|
bp = NULL; // used when no loaded buffer found
|
2016-07-10 19:03:57 +02:00
|
|
|
if (au_new_curbuf.br_buf != NULL && bufref_valid(&au_new_curbuf))
|
|
|
|
buf = au_new_curbuf.br_buf;
|
2018-03-04 18:08:14 +01:00
|
|
|
else if (curwin->w_jumplistlen > 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
int jumpidx;
|
|
|
|
|
|
|
|
jumpidx = curwin->w_jumplistidx - 1;
|
|
|
|
if (jumpidx < 0)
|
|
|
|
jumpidx = curwin->w_jumplistlen - 1;
|
|
|
|
|
|
|
|
forward = jumpidx;
|
|
|
|
while (jumpidx != curwin->w_jumplistidx)
|
|
|
|
{
|
|
|
|
buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
|
|
|
|
if (buf != NULL)
|
|
|
|
{
|
2022-02-08 15:05:20 +00:00
|
|
|
// Skip current and unlisted bufs. Also skip a quickfix
|
|
|
|
// buffer, it might be deleted soon.
|
2022-08-25 15:11:15 +01:00
|
|
|
if (buf == curbuf || !buf->b_p_bl || bt_quickfix(buf))
|
2022-02-08 15:05:20 +00:00
|
|
|
buf = NULL;
|
2004-06-13 20:20:40 +00:00
|
|
|
else if (buf->b_ml.ml_mfp == NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// skip unloaded buf, but may keep it for later
|
2004-06-13 20:20:40 +00:00
|
|
|
if (bp == NULL)
|
|
|
|
bp = buf;
|
|
|
|
buf = NULL;
|
|
|
|
}
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf != NULL) // found a valid buffer: stop searching
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
2019-11-30 20:52:27 +01:00
|
|
|
// advance to older entry in jump list
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
|
|
|
|
break;
|
|
|
|
if (--jumpidx < 0)
|
|
|
|
jumpidx = curwin->w_jumplistlen - 1;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (jumpidx == forward) // List exhausted for sure
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf == NULL) // No previous buffer, Try 2'nd approach
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
forward = TRUE;
|
|
|
|
buf = curbuf->b_next;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
if (buf == NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
if (!forward) // tried both directions
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
buf = curbuf->b_prev;
|
|
|
|
forward = FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// in non-help buffer, try to skip help buffers, and vv
|
2022-02-08 15:05:20 +00:00
|
|
|
if (buf->b_help == curbuf->b_help && buf->b_p_bl
|
2022-08-25 15:11:15 +01:00
|
|
|
&& !bt_quickfix(buf))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf->b_ml.ml_mfp != NULL) // found loaded buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (bp == NULL) // remember unloaded buf for later
|
2004-06-13 20:20:40 +00:00
|
|
|
bp = buf;
|
|
|
|
}
|
|
|
|
if (forward)
|
|
|
|
buf = buf->b_next;
|
|
|
|
else
|
|
|
|
buf = buf->b_prev;
|
|
|
|
}
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf == NULL) // No loaded buffer, use unloaded one
|
2004-06-13 20:20:40 +00:00
|
|
|
buf = bp;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf == NULL) // No loaded buffer, find listed one
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_BUFFERS(buf)
|
2022-08-25 15:11:15 +01:00
|
|
|
if (buf->b_p_bl && buf != curbuf && !bt_quickfix(buf))
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buf == NULL) // Still no buffer, just take one
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (curbuf->b_next != NULL)
|
|
|
|
buf = curbuf->b_next;
|
|
|
|
else
|
|
|
|
buf = curbuf->b_prev;
|
2022-02-08 15:05:20 +00:00
|
|
|
if (bt_quickfix(buf))
|
|
|
|
buf = NULL;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-10 16:43:14 +01:00
|
|
|
if (buf == NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands must have wiped out all other buffers. Only option
|
|
|
|
// now is to make the current buffer empty.
|
2021-06-10 21:07:48 +02:00
|
|
|
return empty_curbuf(FALSE, (flags & DOBUF_FORCEIT), action);
|
2014-01-10 16:43:14 +01:00
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
2021-10-19 20:48:52 +01:00
|
|
|
* make "buf" the current buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
2019-11-30 20:52:27 +01:00
|
|
|
if (action == DOBUF_SPLIT) // split window first
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2023-05-14 17:24:22 +01:00
|
|
|
// If 'switchbuf' is set jump to the window containing "buf".
|
|
|
|
if (swbuf_goto_win_with_buf(buf) != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
return OK;
|
2023-05-14 17:24:22 +01:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if (win_split(0, 0) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// go to current buffer - nothing to do
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf == curbuf)
|
|
|
|
return OK;
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// Check if the current buffer may be abandoned.
|
2021-06-10 21:07:48 +02:00
|
|
|
if (action == DOBUF_GOTO && !can_abandon(curbuf, (flags & DOBUF_FORCEIT)))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
|
2020-10-24 20:49:43 +02:00
|
|
|
if ((p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-10-09 18:53:32 +01:00
|
|
|
# ifdef FEAT_TERMINAL
|
|
|
|
if (term_job_running(curbuf->b_term))
|
|
|
|
{
|
|
|
|
if (term_confirm_stop(curbuf) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
// Manually kill the terminal here because this command will
|
|
|
|
// hide it otherwise.
|
|
|
|
free_terminal(curbuf);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
# endif
|
|
|
|
{
|
|
|
|
bufref_T bufref;
|
2016-07-10 18:21:50 +02:00
|
|
|
|
2022-10-09 18:53:32 +01:00
|
|
|
set_bufref(&bufref, buf);
|
|
|
|
dialog_changed(curbuf, FALSE);
|
|
|
|
if (!bufref_valid(&bufref))
|
|
|
|
// Autocommand deleted buffer, oops!
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
if (bufIsChanged(curbuf))
|
|
|
|
{
|
|
|
|
no_write_message();
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2022-10-09 18:53:32 +01:00
|
|
|
else
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
{
|
2017-08-17 16:55:13 +02:00
|
|
|
no_write_message();
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Go to the other buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
set_curbuf(buf, action);
|
|
|
|
|
|
|
|
if (action == DOBUF_SPLIT)
|
2019-11-30 20:52:27 +01:00
|
|
|
RESET_BINDING(curwin); // reset 'scrollbind' and 'cursorbind'
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2018-03-04 18:08:14 +01:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
if (aborting()) // autocmds may abort script processing
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2021-06-10 21:07:48 +02:00
|
|
|
int
|
|
|
|
do_buffer(
|
|
|
|
int action,
|
|
|
|
int start,
|
|
|
|
int dir, // FORWARD or BACKWARD
|
|
|
|
int count, // buffer number or number of buffers
|
|
|
|
int forceit) // TRUE when using !
|
|
|
|
{
|
|
|
|
return do_buffer_ext(action, start, dir, count,
|
|
|
|
forceit ? DOBUF_FORCEIT : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do_bufdel() - delete or unload buffer(s)
|
|
|
|
*
|
|
|
|
* addr_count == 0: ":bdel" - delete current buffer
|
|
|
|
* addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
|
|
|
|
* buffer "end_bnr", then any other arguments.
|
|
|
|
* addr_count == 2: ":N,N bdel" - delete buffers in range
|
|
|
|
*
|
|
|
|
* command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
|
|
|
|
* DOBUF_DEL (":bdel")
|
|
|
|
*
|
|
|
|
* Returns error message or NULL
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
do_bufdel(
|
|
|
|
int command,
|
|
|
|
char_u *arg, // pointer to extra arguments
|
|
|
|
int addr_count,
|
|
|
|
int start_bnr, // first buffer number in a range
|
|
|
|
int end_bnr, // buffer nr or last buffer nr in a range
|
|
|
|
int forceit)
|
|
|
|
{
|
|
|
|
int do_current = 0; // delete current buffer?
|
|
|
|
int deleted = 0; // number of buffers deleted
|
|
|
|
char *errormsg = NULL; // return value
|
|
|
|
int bnr; // buffer number
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
if (addr_count == 0)
|
|
|
|
{
|
|
|
|
(void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (addr_count == 2)
|
|
|
|
{
|
|
|
|
if (*arg) // both range and argument is not allowed
|
2022-01-01 15:58:22 +00:00
|
|
|
return ex_errmsg(e_trailing_characters_str, arg);
|
2021-06-10 21:07:48 +02:00
|
|
|
bnr = start_bnr;
|
|
|
|
}
|
|
|
|
else // addr_count == 1
|
|
|
|
bnr = end_bnr;
|
|
|
|
|
|
|
|
for ( ;!got_int; ui_breakcheck())
|
|
|
|
{
|
2021-10-19 20:48:52 +01:00
|
|
|
// Delete the current buffer last, otherwise when the
|
|
|
|
// current buffer is deleted, the next buffer becomes
|
|
|
|
// the current one and will be loaded, which may then
|
|
|
|
// also be deleted, etc.
|
2021-06-10 21:07:48 +02:00
|
|
|
if (bnr == curbuf->b_fnum)
|
|
|
|
do_current = bnr;
|
2022-01-28 15:28:04 +00:00
|
|
|
else if (do_buffer_ext(command, DOBUF_FIRST, FORWARD, bnr,
|
2021-06-10 21:07:48 +02:00
|
|
|
DOBUF_NOPOPUP | (forceit ? DOBUF_FORCEIT : 0)) == OK)
|
|
|
|
++deleted;
|
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// find next buffer number to delete/unload
|
2021-06-10 21:07:48 +02:00
|
|
|
if (addr_count == 2)
|
|
|
|
{
|
|
|
|
if (++bnr > end_bnr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else // addr_count == 1
|
|
|
|
{
|
|
|
|
arg = skipwhite(arg);
|
|
|
|
if (*arg == NUL)
|
|
|
|
break;
|
|
|
|
if (!VIM_ISDIGIT(*arg))
|
|
|
|
{
|
|
|
|
p = skiptowhite_esc(arg);
|
|
|
|
bnr = buflist_findpat(arg, p,
|
|
|
|
command == DOBUF_WIPE || command == DOBUF_WIPE_REUSE,
|
|
|
|
FALSE, FALSE);
|
|
|
|
if (bnr < 0) // failed
|
|
|
|
break;
|
|
|
|
arg = p;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
bnr = getdigits(&arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
|
|
|
|
FORWARD, do_current, forceit) == OK)
|
|
|
|
++deleted;
|
|
|
|
|
|
|
|
if (deleted == 0)
|
|
|
|
{
|
|
|
|
if (command == DOBUF_UNLOAD)
|
2021-12-31 19:59:55 +00:00
|
|
|
STRCPY(IObuff, _(e_no_buffers_were_unloaded));
|
2021-06-10 21:07:48 +02:00
|
|
|
else if (command == DOBUF_DEL)
|
2021-12-31 19:59:55 +00:00
|
|
|
STRCPY(IObuff, _(e_no_buffers_were_deleted));
|
2021-06-10 21:07:48 +02:00
|
|
|
else
|
2021-12-31 19:59:55 +00:00
|
|
|
STRCPY(IObuff, _(e_no_buffers_were_wiped_out));
|
2021-06-10 21:07:48 +02:00
|
|
|
errormsg = (char *)IObuff;
|
|
|
|
}
|
|
|
|
else if (deleted >= p_report)
|
|
|
|
{
|
|
|
|
if (command == DOBUF_UNLOAD)
|
|
|
|
smsg(NGETTEXT("%d buffer unloaded",
|
|
|
|
"%d buffers unloaded", deleted), deleted);
|
|
|
|
else if (command == DOBUF_DEL)
|
|
|
|
smsg(NGETTEXT("%d buffer deleted",
|
|
|
|
"%d buffers deleted", deleted), deleted);
|
|
|
|
else
|
|
|
|
smsg(NGETTEXT("%d buffer wiped out",
|
|
|
|
"%d buffers wiped out", deleted), deleted);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return errormsg;
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Set current buffer to "buf". Executes autocommands and closes current
|
|
|
|
* buffer. "action" tells how to close the current buffer:
|
|
|
|
* DOBUF_GOTO free or hide it
|
|
|
|
* DOBUF_SPLIT nothing
|
|
|
|
* DOBUF_UNLOAD unload it
|
|
|
|
* DOBUF_DEL delete it
|
|
|
|
* DOBUF_WIPE wipe it out
|
2019-08-21 22:25:30 +02:00
|
|
|
* DOBUF_WIPE_REUSE wipe it out and add to "buf_reuse"
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
set_curbuf(buf_T *buf, int action)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *prevbuf;
|
|
|
|
int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
|
2019-08-21 22:25:30 +02:00
|
|
|
|| action == DOBUF_WIPE || action == DOBUF_WIPE_REUSE);
|
2013-02-17 15:45:37 +01:00
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
long old_tw = curbuf->b_p_tw;
|
|
|
|
#endif
|
2017-12-18 12:37:55 +01:00
|
|
|
bufref_T newbufref;
|
|
|
|
bufref_T prevbufref;
|
2022-02-01 13:54:17 +00:00
|
|
|
int valid;
|
patch 9.1.0143: [security]: autocmd causes use-after-free in set_curbuf()
Problem: [security]: autocmd cause use-after-free in set_curbuf()
(kawarimidoll)
Solution: check side-effect of BufLeave autocommand, when the number
of windows changed, close windows containing buffers that will
be wiped, if curbuf changed unexpectedly make sure b_nwindows
is decremented otherwise it cannot be wiped
set_curbuf() already makes some efforts to ensure the BufLeave
autocommands do not cause issues. However there are still 2 issues
that are not taken care of:
1) If a BufLeave autocommand opens a new window containing the same
buffer as that is going got be closed in close_buffer() a bit later,
we suddenly have another window open, containing a free'd buffer. So we
must check if the number of windows changed and if it does (and the
current buffer is going to be wiped (according to the 'bufhidden'
setting), let's immediately close all windows containing the current
buffer using close_windows()
2) If a BufLeave autocommand changes our current buffer (displays it in
the current window), buf->b_nwindow will be incremented. As part of
set_curbuf() we will however enter another buffer soon, which means, the
newly created curbuf will have b_nwindows still have set, even so the
buffer is no longer displayed in a window. This causes later problems,
because it will no longer be possible to wipe such a buffer. So just
before entering the final buffer, check if the curbuf changed when
calling the BufLeave autocommand and if it does (and curbuf is still
valid), decrement curbuf->b_nwindows.
Both issues can be verified using the provided test (however the second
issue only because such an impacted buffer won't be wiped, causing
futher issues in later tests).
fixes: #13839
closes: #14104
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-28 23:32:00 +01:00
|
|
|
int last_winid = get_last_winid();
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
setpcmark();
|
2020-10-24 20:49:43 +02:00
|
|
|
if ((cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
|
2019-11-30 20:52:27 +01:00
|
|
|
curwin->w_alt_fnum = curbuf->b_fnum; // remember alternate file
|
|
|
|
buflist_altfpos(curwin); // remember curpos
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Don't restart Select mode after switching to another buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
VIsual_reselect = FALSE;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// close_windows() or apply_autocmds() may change curbuf and wipe out "buf"
|
2004-06-13 20:20:40 +00:00
|
|
|
prevbuf = curbuf;
|
2017-12-18 12:37:55 +01:00
|
|
|
set_bufref(&prevbufref, prevbuf);
|
|
|
|
set_bufref(&newbufref, buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-02-07 12:12:43 +01:00
|
|
|
// Autocommands may delete the current buffer and/or the buffer we want to
|
|
|
|
// go to. In those cases don't close the buffer.
|
2016-07-10 17:00:38 +02:00
|
|
|
if (!apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf)
|
2017-12-18 12:37:55 +01:00
|
|
|
|| (bufref_valid(&prevbufref)
|
|
|
|
&& bufref_valid(&newbufref)
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2017-12-18 12:37:55 +01:00
|
|
|
&& !aborting()
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2018-03-04 18:08:14 +01:00
|
|
|
))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2011-09-14 14:43:25 +02:00
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
if (prevbuf == curwin->w_buffer)
|
|
|
|
reset_synblock(curwin);
|
|
|
|
#endif
|
patch 9.1.0143: [security]: autocmd causes use-after-free in set_curbuf()
Problem: [security]: autocmd cause use-after-free in set_curbuf()
(kawarimidoll)
Solution: check side-effect of BufLeave autocommand, when the number
of windows changed, close windows containing buffers that will
be wiped, if curbuf changed unexpectedly make sure b_nwindows
is decremented otherwise it cannot be wiped
set_curbuf() already makes some efforts to ensure the BufLeave
autocommands do not cause issues. However there are still 2 issues
that are not taken care of:
1) If a BufLeave autocommand opens a new window containing the same
buffer as that is going got be closed in close_buffer() a bit later,
we suddenly have another window open, containing a free'd buffer. So we
must check if the number of windows changed and if it does (and the
current buffer is going to be wiped (according to the 'bufhidden'
setting), let's immediately close all windows containing the current
buffer using close_windows()
2) If a BufLeave autocommand changes our current buffer (displays it in
the current window), buf->b_nwindow will be incremented. As part of
set_curbuf() we will however enter another buffer soon, which means, the
newly created curbuf will have b_nwindows still have set, even so the
buffer is no longer displayed in a window. This causes later problems,
because it will no longer be possible to wipe such a buffer. So just
before entering the final buffer, check if the curbuf changed when
calling the BufLeave autocommand and if it does (and curbuf is still
valid), decrement curbuf->b_nwindows.
Both issues can be verified using the provided test (however the second
issue only because such an impacted buffer won't be wiped, causing
futher issues in later tests).
fixes: #13839
closes: #14104
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-28 23:32:00 +01:00
|
|
|
// autocommands may have opened a new window
|
|
|
|
// with prevbuf, grr
|
|
|
|
if (unload ||
|
|
|
|
(last_winid != get_last_winid() &&
|
|
|
|
strchr((char *)"wdu", prevbuf->b_p_bh[0]) != NULL))
|
2006-02-16 22:11:02 +00:00
|
|
|
close_windows(prevbuf, FALSE);
|
2018-03-04 18:08:14 +01:00
|
|
|
#if defined(FEAT_EVAL)
|
2017-12-18 12:37:55 +01:00
|
|
|
if (bufref_valid(&prevbufref) && !aborting())
|
2004-06-13 20:20:40 +00:00
|
|
|
#else
|
2017-12-18 12:37:55 +01:00
|
|
|
if (bufref_valid(&prevbufref))
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2006-03-28 20:57:42 +00:00
|
|
|
{
|
2012-07-06 16:22:02 +02:00
|
|
|
win_T *previouswin = curwin;
|
2020-01-26 22:00:26 +01:00
|
|
|
|
2022-01-20 21:00:54 +00:00
|
|
|
// Do not sync when in Insert mode and the buffer is open in
|
|
|
|
// another window, might be a timer doing something in another
|
|
|
|
// window.
|
|
|
|
if (prevbuf == curbuf
|
2022-05-07 20:01:16 +01:00
|
|
|
&& ((State & MODE_INSERT) == 0 || curbuf->b_nwindows <= 1))
|
2006-04-10 14:55:34 +00:00
|
|
|
u_sync(FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
|
|
|
|
unload ? action : (action == DOBUF_GOTO
|
2017-08-03 22:44:55 +02:00
|
|
|
&& !buf_hide(prevbuf)
|
2019-12-14 16:18:15 +01:00
|
|
|
&& !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0,
|
|
|
|
FALSE, FALSE);
|
2012-07-06 16:22:02 +02:00
|
|
|
if (curwin != previouswin && win_valid(previouswin))
|
2019-11-30 20:52:27 +01:00
|
|
|
// autocommands changed curwin, Grr!
|
2012-07-06 16:22:02 +02:00
|
|
|
curwin = previouswin;
|
2006-03-28 20:57:42 +00:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// An autocommand may have deleted "buf", already entered it (e.g., when
|
|
|
|
// it did ":bunload") or aborted the script processing.
|
|
|
|
// If curwin->w_buffer is null, enter_buffer() will make it valid again
|
2022-02-01 13:54:17 +00:00
|
|
|
valid = buf_valid(buf);
|
|
|
|
if ((valid && buf != curbuf
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2017-09-16 20:54:51 +02:00
|
|
|
&& !aborting()
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2018-03-04 18:08:14 +01:00
|
|
|
) || curwin->w_buffer == NULL)
|
2013-02-17 15:45:37 +01:00
|
|
|
{
|
patch 9.1.0143: [security]: autocmd causes use-after-free in set_curbuf()
Problem: [security]: autocmd cause use-after-free in set_curbuf()
(kawarimidoll)
Solution: check side-effect of BufLeave autocommand, when the number
of windows changed, close windows containing buffers that will
be wiped, if curbuf changed unexpectedly make sure b_nwindows
is decremented otherwise it cannot be wiped
set_curbuf() already makes some efforts to ensure the BufLeave
autocommands do not cause issues. However there are still 2 issues
that are not taken care of:
1) If a BufLeave autocommand opens a new window containing the same
buffer as that is going got be closed in close_buffer() a bit later,
we suddenly have another window open, containing a free'd buffer. So we
must check if the number of windows changed and if it does (and the
current buffer is going to be wiped (according to the 'bufhidden'
setting), let's immediately close all windows containing the current
buffer using close_windows()
2) If a BufLeave autocommand changes our current buffer (displays it in
the current window), buf->b_nwindow will be incremented. As part of
set_curbuf() we will however enter another buffer soon, which means, the
newly created curbuf will have b_nwindows still have set, even so the
buffer is no longer displayed in a window. This causes later problems,
because it will no longer be possible to wipe such a buffer. So just
before entering the final buffer, check if the curbuf changed when
calling the BufLeave autocommand and if it does (and curbuf is still
valid), decrement curbuf->b_nwindows.
Both issues can be verified using the provided test (however the second
issue only because such an impacted buffer won't be wiped, causing
futher issues in later tests).
fixes: #13839
closes: #14104
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-28 23:32:00 +01:00
|
|
|
// autocommands changed curbuf and we will move to another
|
|
|
|
// buffer soon, so decrement curbuf->b_nwindows
|
|
|
|
if (curbuf != NULL && prevbuf != curbuf)
|
|
|
|
curbuf->b_nwindows--;
|
2022-02-01 13:54:17 +00:00
|
|
|
// If the buffer is not valid but curwin->w_buffer is NULL we must
|
|
|
|
// enter some buffer. Using the last one is hopefully OK.
|
|
|
|
if (!valid)
|
|
|
|
enter_buffer(lastbuf);
|
|
|
|
else
|
|
|
|
enter_buffer(buf);
|
2013-02-17 15:45:37 +01:00
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
if (old_tw != curbuf->b_p_tw)
|
|
|
|
check_colorcolumn(curwin);
|
|
|
|
#endif
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enter a new current buffer.
|
2013-02-17 15:45:37 +01:00
|
|
|
* Old curbuf must have been abandoned already! This also means "curbuf" may
|
|
|
|
* be pointing to freed memory.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
2019-08-20 20:13:45 +02:00
|
|
|
static void
|
2016-01-30 15:14:10 +01:00
|
|
|
enter_buffer(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-08-13 14:09:44 +01:00
|
|
|
// when closing the current buffer stop Visual mode
|
|
|
|
if (VIsual_active
|
|
|
|
#if defined(EXITFREE)
|
|
|
|
&& !entered_free_all_mem
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
end_visual_mode();
|
|
|
|
|
2019-09-25 20:37:36 +02:00
|
|
|
// Get the buffer in the current window.
|
|
|
|
curwin->w_buffer = buf;
|
|
|
|
curbuf = buf;
|
|
|
|
++curbuf->b_nwindows;
|
|
|
|
|
|
|
|
// Copy buffer and window local option values. Not for a help buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
|
|
|
|
if (!buf->b_help)
|
|
|
|
get_winopts(buf);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
else
|
2019-09-25 20:37:36 +02:00
|
|
|
// Remove all folds in the window.
|
2004-06-13 20:20:40 +00:00
|
|
|
clearFolding(curwin);
|
2019-09-25 20:37:36 +02:00
|
|
|
foldUpdateAll(curwin); // update folds (later).
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef FEAT_DIFF
|
2006-02-17 21:45:41 +00:00
|
|
|
if (curwin->w_p_diff)
|
|
|
|
diff_buf_add(curbuf);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
2011-09-14 14:43:25 +02:00
|
|
|
#ifdef FEAT_SYN_HL
|
2017-07-17 23:20:24 +02:00
|
|
|
curwin->w_s = &(curbuf->b_s);
|
2011-09-14 14:43:25 +02:00
|
|
|
#endif
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Cursor on first line by default.
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin->w_cursor.lnum = 1;
|
|
|
|
curwin->w_cursor.col = 0;
|
|
|
|
curwin->w_cursor.coladd = 0;
|
|
|
|
curwin->w_set_curswant = TRUE;
|
2008-11-15 15:06:17 +00:00
|
|
|
curwin->w_topline_was_set = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// mark cursor position as being invalid
|
2010-02-24 16:58:36 +01:00
|
|
|
curwin->w_valid = 0;
|
|
|
|
|
2018-09-21 16:59:45 +02:00
|
|
|
buflist_setfpos(curbuf, curwin, curbuf->b_last_cursor.lnum,
|
|
|
|
curbuf->b_last_cursor.col, TRUE);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Make sure the buffer is loaded.
|
|
|
|
if (curbuf->b_ml.ml_mfp == NULL) // need to load the file
|
2004-09-02 19:12:26 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// If there is no filetype, allow for detecting one. Esp. useful for
|
2021-12-27 17:21:41 +00:00
|
|
|
// ":ball" used in an autocommand. If there already is a filetype we
|
2019-11-30 20:52:27 +01:00
|
|
|
// might prefer to keep it.
|
2004-09-02 19:12:26 +00:00
|
|
|
if (*curbuf->b_p_ft == NUL)
|
|
|
|
did_filetype = FALSE;
|
|
|
|
|
2010-07-24 20:27:03 +02:00
|
|
|
open_buffer(FALSE, NULL, 0);
|
2004-09-02 19:12:26 +00:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
2019-05-16 20:29:44 +02:00
|
|
|
if (!msg_silent && !shortmess(SHM_FILEINFO))
|
|
|
|
need_fileinfo = TRUE; // display file info after redraw
|
|
|
|
|
|
|
|
// check if file changed
|
|
|
|
(void)buf_check_timestamp(curbuf, FALSE);
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin->w_topline = 1;
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_DIFF
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin->w_topfill = 0;
|
2018-03-04 18:08:14 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
|
|
|
|
apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If autocommands did not change the cursor position, restore cursor lnum
|
|
|
|
// and possibly cursor col.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curwin->w_cursor.lnum == 1 && inindent(0))
|
|
|
|
buflist_getfpos();
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
check_arg_idx(curwin); // check for valid arg_idx
|
2004-06-13 20:20:40 +00:00
|
|
|
maketitle();
|
2019-11-30 20:52:27 +01:00
|
|
|
// when autocmds didn't change it
|
2008-11-15 15:06:17 +00:00
|
|
|
if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
|
2023-02-14 17:41:20 +00:00
|
|
|
scroll_cursor_halfway(FALSE, FALSE); // redisplay at correct position
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2004-10-24 19:18:58 +00:00
|
|
|
#ifdef FEAT_NETBEANS_INTG
|
2019-11-30 20:52:27 +01:00
|
|
|
// Send fileOpened event because we've changed buffers.
|
2010-05-22 21:34:09 +02:00
|
|
|
netbeans_file_activated(curbuf);
|
2004-10-24 19:18:58 +00:00
|
|
|
#endif
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Change directories when the 'acd' option is set.
|
2018-04-10 18:47:20 +02:00
|
|
|
DO_AUTOCHDIR;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#ifdef FEAT_KEYMAP
|
|
|
|
if (curbuf->b_kmap_state & KEYMAP_INIT)
|
2009-05-13 10:51:08 +00:00
|
|
|
(void)keymap_init();
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2007-05-06 21:55:31 +00:00
|
|
|
#ifdef FEAT_SPELL
|
2019-11-30 20:52:27 +01:00
|
|
|
// May need to set the spell language. Can only do this after the buffer
|
|
|
|
// has been properly setup.
|
2010-06-05 23:22:07 +02:00
|
|
|
if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
|
2023-02-20 12:16:39 +00:00
|
|
|
(void)parse_spelllang(curwin);
|
2007-05-06 21:55:31 +00:00
|
|
|
#endif
|
2016-07-03 17:47:26 +02:00
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
curbuf->b_last_used = vim_time();
|
|
|
|
#endif
|
2007-05-06 21:55:31 +00:00
|
|
|
|
2022-08-14 14:17:45 +01:00
|
|
|
redraw_later(UPD_NOT_VALID);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2006-09-05 14:31:54 +00:00
|
|
|
#if defined(FEAT_AUTOCHDIR) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Change to the directory of the current buffer.
|
2016-02-23 16:19:07 +01:00
|
|
|
* Don't do this while still starting up.
|
2006-09-05 14:31:54 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
do_autochdir(void)
|
2006-09-05 14:31:54 +00:00
|
|
|
{
|
2016-07-09 23:40:45 +02:00
|
|
|
if ((starting == 0 || test_autochdir)
|
2016-02-23 16:19:07 +01:00
|
|
|
&& curbuf->b_ffname != NULL
|
2018-02-03 17:36:27 +01:00
|
|
|
&& vim_chdirfile(curbuf->b_ffname, "auto") == OK)
|
2021-11-18 18:53:45 +00:00
|
|
|
{
|
2006-09-05 14:31:54 +00:00
|
|
|
shorten_fnames(TRUE);
|
2021-11-18 18:53:45 +00:00
|
|
|
last_chdir_reason = "autochdir";
|
|
|
|
}
|
2006-09-05 14:31:54 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-10-09 18:53:32 +01:00
|
|
|
static void
|
|
|
|
no_write_message_buf(buf_T *buf UNUSED)
|
|
|
|
{
|
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
if (term_job_running(buf->b_term))
|
|
|
|
emsg(_(e_job_still_running_add_bang_to_end_the_job));
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
semsg(_(e_no_write_since_last_change_for_buffer_nr_add_bang_to_override),
|
|
|
|
buf->b_fnum);
|
|
|
|
}
|
|
|
|
|
2017-08-17 16:55:13 +02:00
|
|
|
void
|
|
|
|
no_write_message(void)
|
|
|
|
{
|
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
if (term_job_running(curbuf->b_term))
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_job_still_running_add_bang_to_end_the_job));
|
2017-08-17 16:55:13 +02:00
|
|
|
else
|
|
|
|
#endif
|
2021-07-20 21:07:36 +02:00
|
|
|
emsg(_(e_no_write_since_last_change_add_bang_to_override));
|
2017-08-17 16:55:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-02-19 23:10:02 +01:00
|
|
|
no_write_message_nobang(buf_T *buf UNUSED)
|
2017-08-17 16:55:13 +02:00
|
|
|
{
|
|
|
|
#ifdef FEAT_TERMINAL
|
2018-02-19 23:10:02 +01:00
|
|
|
if (term_job_running(buf->b_term))
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_job_still_running));
|
2017-08-17 16:55:13 +02:00
|
|
|
else
|
|
|
|
#endif
|
2021-07-20 21:07:36 +02:00
|
|
|
emsg(_(e_no_write_since_last_change));
|
2017-08-17 16:55:13 +02:00
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* functions for dealing with the buffer list
|
|
|
|
*/
|
|
|
|
|
2018-04-24 21:58:51 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if the current buffer is empty, unnamed, unmodified and used in
|
|
|
|
* only one window. That means it can be re-used.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
curbuf_reusable(void)
|
|
|
|
{
|
|
|
|
return (curbuf != NULL
|
|
|
|
&& curbuf->b_ffname == NULL
|
|
|
|
&& curbuf->b_nwindows <= 1
|
|
|
|
&& (curbuf->b_ml.ml_mfp == NULL || BUFEMPTY())
|
2019-04-07 12:04:51 +02:00
|
|
|
&& !bt_quickfix(curbuf)
|
2018-04-24 21:58:51 +02:00
|
|
|
&& !curbufIsChanged());
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Add a file name to the buffer list. Return a pointer to the buffer.
|
|
|
|
* If the same file name already exists return a pointer to that buffer.
|
|
|
|
* If it does not exist, or if fname == NULL, a new entry is created.
|
|
|
|
* If (flags & BLN_CURBUF) is TRUE, may use current buffer.
|
|
|
|
* If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
|
|
|
|
* If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
|
2016-05-29 16:24:50 +02:00
|
|
|
* If (flags & BLN_NEW) is TRUE, don't use an existing buffer.
|
2016-07-10 17:00:38 +02:00
|
|
|
* If (flags & BLN_NOOPT) is TRUE, don't copy options from the current buffer
|
|
|
|
* if the buffer already exists.
|
2019-08-21 22:25:30 +02:00
|
|
|
* If (flags & BLN_REUSE) is TRUE, may use buffer number from "buf_reuse".
|
2004-06-13 20:20:40 +00:00
|
|
|
* This is the ONLY way to create a new buffer.
|
|
|
|
*/
|
|
|
|
buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_new(
|
2018-10-11 19:27:47 +02:00
|
|
|
char_u *ffname_arg, // full path of fname or relative
|
|
|
|
char_u *sfname_arg, // short fname or NULL
|
|
|
|
linenr_T lnum, // preferred cursor line
|
|
|
|
int flags) // BLN_ defines
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-10-11 19:27:47 +02:00
|
|
|
char_u *ffname = ffname_arg;
|
|
|
|
char_u *sfname = sfname_arg;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf_T *buf;
|
|
|
|
#ifdef UNIX
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T st;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
2016-07-14 22:09:39 +02:00
|
|
|
if (top_file_num == 1)
|
|
|
|
hash_init(&buf_hashtab);
|
|
|
|
|
2018-10-11 19:27:47 +02:00
|
|
|
fname_expand(curbuf, &ffname, &sfname); // will allocate ffname
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
2021-10-19 20:48:52 +01:00
|
|
|
* If the file name already exists in the list, update the entry.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
#ifdef UNIX
|
2019-11-30 20:52:27 +01:00
|
|
|
// On Unix we can use inode numbers when the file exists. Works better
|
|
|
|
// for hard links.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (sfname == NULL || mch_stat((char *)sfname, &st) < 0)
|
|
|
|
st.st_dev = (dev_T)-1;
|
|
|
|
#endif
|
2016-05-29 16:24:50 +02:00
|
|
|
if (ffname != NULL && !(flags & (BLN_DUMMY | BLN_NEW)) && (buf =
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
|
|
|
buflist_findname_stat(ffname, &st)
|
|
|
|
#else
|
|
|
|
buflist_findname(ffname)
|
|
|
|
#endif
|
|
|
|
) != NULL)
|
|
|
|
{
|
|
|
|
vim_free(ffname);
|
|
|
|
if (lnum != 0)
|
2020-10-25 17:09:50 +01:00
|
|
|
buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin,
|
|
|
|
lnum, (colnr_T)0, FALSE);
|
2016-07-10 17:00:38 +02:00
|
|
|
|
|
|
|
if ((flags & BLN_NOOPT) == 0)
|
2019-11-30 20:52:27 +01:00
|
|
|
// copy the options now, if 'cpo' doesn't have 's' and not done
|
|
|
|
// already
|
2016-07-10 17:00:38 +02:00
|
|
|
buf_copy_options(buf, 0);
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if ((flags & BLN_LISTED) && !buf->b_p_bl)
|
|
|
|
{
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
2018-03-04 18:08:14 +01:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_p_bl = TRUE;
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!(flags & BLN_DUMMY))
|
2014-04-06 20:45:43 +02:00
|
|
|
{
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2014-04-06 20:45:43 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the current buffer has no name and no contents, use the current
|
|
|
|
* buffer. Otherwise: Need to allocate a new buffer structure.
|
|
|
|
*
|
|
|
|
* This is the ONLY place where a new buffer structure is allocated!
|
2006-01-12 23:22:24 +00:00
|
|
|
* (A spell file buffer is allocated in spell.c, but that's not a normal
|
|
|
|
* buffer.)
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
buf = NULL;
|
2018-04-24 21:58:51 +02:00
|
|
|
if ((flags & BLN_CURBUF) && curbuf_reusable())
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf = curbuf;
|
2019-11-30 20:52:27 +01:00
|
|
|
// It's like this buffer is deleted. Watch out for autocommands that
|
|
|
|
// change curbuf! If that happens, allocate a new buffer anyway.
|
2022-04-19 16:24:12 +01:00
|
|
|
buf_freeall(buf, BFA_WIPE | BFA_DEL);
|
|
|
|
if (buf != curbuf) // autocommands deleted the buffer!
|
|
|
|
return NULL;
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-11-30 20:52:27 +01:00
|
|
|
if (aborting()) // autocmds may abort script processing
|
2020-03-27 20:58:37 +01:00
|
|
|
{
|
|
|
|
vim_free(ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
return NULL;
|
2020-03-27 20:58:37 +01:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (buf != curbuf || curbuf == NULL)
|
|
|
|
{
|
2019-05-28 23:08:19 +02:00
|
|
|
buf = ALLOC_CLEAR_ONE(buf_T);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf == NULL)
|
|
|
|
{
|
|
|
|
vim_free(ffname);
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-04-15 12:27:36 +02:00
|
|
|
#ifdef FEAT_EVAL
|
2019-11-30 20:52:27 +01:00
|
|
|
// init b: variables
|
2022-04-03 11:22:38 +01:00
|
|
|
buf->b_vars = dict_alloc_id(aid_newbuf_bvars);
|
2013-04-15 12:27:36 +02:00
|
|
|
if (buf->b_vars == NULL)
|
|
|
|
{
|
|
|
|
vim_free(ffname);
|
|
|
|
vim_free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE);
|
|
|
|
#endif
|
2017-02-17 16:31:35 +01:00
|
|
|
init_changedtick(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ffname != NULL)
|
|
|
|
{
|
|
|
|
buf->b_ffname = ffname;
|
|
|
|
buf->b_sfname = vim_strsave(sfname);
|
|
|
|
}
|
|
|
|
|
|
|
|
clear_wininfo(buf);
|
2019-05-28 23:08:19 +02:00
|
|
|
buf->b_wininfo = ALLOC_CLEAR_ONE(wininfo_T);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL))
|
|
|
|
|| buf->b_wininfo == NULL)
|
|
|
|
{
|
2018-10-11 19:27:47 +02:00
|
|
|
if (buf->b_sfname != buf->b_ffname)
|
|
|
|
VIM_CLEAR(buf->b_sfname);
|
|
|
|
else
|
|
|
|
buf->b_sfname = NULL;
|
2018-02-10 18:45:26 +01:00
|
|
|
VIM_CLEAR(buf->b_ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf != curbuf)
|
|
|
|
free_buffer(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf == curbuf)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
free_buffer_stuff(buf, FALSE); // delete local variables et al.
|
2012-11-20 12:16:58 +01:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Init the options.
|
2012-11-20 12:16:58 +01:00
|
|
|
buf->b_p_initialized = FALSE;
|
|
|
|
buf_copy_options(buf, BCO_ENTER);
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_KEYMAP
|
2019-11-30 20:52:27 +01:00
|
|
|
// need to reload lmaps and set b:keymap_name
|
2004-06-13 20:20:40 +00:00
|
|
|
curbuf->b_kmap_state |= KEYMAP_INIT;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-10-19 20:48:52 +01:00
|
|
|
// put the new buffer at the end of the buffer list
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_next = NULL;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (firstbuf == NULL) // buffer list is empty
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf->b_prev = NULL;
|
|
|
|
firstbuf = buf;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
else // append new buffer at end of list
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
lastbuf->b_next = buf;
|
|
|
|
buf->b_prev = lastbuf;
|
|
|
|
}
|
|
|
|
lastbuf = buf;
|
|
|
|
|
2019-08-21 22:25:30 +02:00
|
|
|
if ((flags & BLN_REUSE) && buf_reuse.ga_len > 0)
|
|
|
|
{
|
|
|
|
// Recycle a previously used buffer number. Used for buffers which
|
|
|
|
// are normally hidden, e.g. in a popup window. Avoids that the
|
|
|
|
// buffer number grows rapidly.
|
|
|
|
--buf_reuse.ga_len;
|
|
|
|
buf->b_fnum = ((int *)buf_reuse.ga_data)[buf_reuse.ga_len];
|
2019-12-10 23:44:48 +01:00
|
|
|
|
|
|
|
// Move buffer to the right place in the buffer list.
|
|
|
|
while (buf->b_prev != NULL && buf->b_fnum < buf->b_prev->b_fnum)
|
|
|
|
{
|
|
|
|
buf_T *prev = buf->b_prev;
|
|
|
|
|
|
|
|
prev->b_next = buf->b_next;
|
|
|
|
if (prev->b_next != NULL)
|
|
|
|
prev->b_next->b_prev = prev;
|
|
|
|
buf->b_next = prev;
|
|
|
|
buf->b_prev = prev->b_prev;
|
|
|
|
if (buf->b_prev != NULL)
|
|
|
|
buf->b_prev->b_next = buf;
|
|
|
|
prev->b_prev = buf;
|
|
|
|
if (lastbuf == buf)
|
|
|
|
lastbuf = prev;
|
|
|
|
if (firstbuf == prev)
|
|
|
|
firstbuf = buf;
|
|
|
|
}
|
2019-08-21 22:25:30 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
buf->b_fnum = top_file_num++;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (top_file_num < 0) // wrap around (may cause duplicates)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-01-13 23:38:42 +01:00
|
|
|
emsg(_("W14: Warning: List of file names overflow"));
|
2020-10-28 20:20:00 +01:00
|
|
|
if (emsg_silent == 0 && !in_assert_fails)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
out_flush();
|
2019-11-17 17:06:33 +01:00
|
|
|
ui_delay(3001L, TRUE); // make sure it is noticed
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
top_file_num = 1;
|
|
|
|
}
|
2016-07-14 22:09:39 +02:00
|
|
|
buf_hashtab_add(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2021-10-19 20:48:52 +01:00
|
|
|
// Always copy the options from the current buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
buf_copy_options(buf, BCO_ALWAYS);
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->b_wininfo->wi_fpos.lnum = lnum;
|
|
|
|
buf->b_wininfo->wi_win = curwin;
|
|
|
|
|
2005-01-25 21:44:33 +00:00
|
|
|
#ifdef FEAT_SYN_HL
|
2010-06-05 23:22:07 +02:00
|
|
|
hash_init(&buf->b_s.b_keywtab);
|
|
|
|
hash_init(&buf->b_s.b_keywtab_ic);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
buf->b_fname = buf->b_sfname;
|
|
|
|
#ifdef UNIX
|
|
|
|
if (st.st_dev == (dev_T)-1)
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = TRUE;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_dev = st.st_dev;
|
|
|
|
buf->b_ino = st.st_ino;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
buf->b_u_synced = TRUE;
|
|
|
|
buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
|
2004-12-29 20:58:21 +00:00
|
|
|
if (flags & BLN_DUMMY)
|
|
|
|
buf->b_flags |= BF_DUMMY;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf_clear_file(buf);
|
2019-11-30 20:52:27 +01:00
|
|
|
clrallmarks(buf); // clear marks
|
|
|
|
fmarks_check_names(buf); // check file marks for this file
|
|
|
|
buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; // init 'buflisted'
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!(flags & BLN_DUMMY))
|
|
|
|
{
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Tricky: these autocommands may change the buffer list. They could
|
|
|
|
// also split the window with re-using the one empty buffer. This may
|
|
|
|
// result in unexpectedly losing the empty buffer.
|
2016-07-10 18:21:50 +02:00
|
|
|
set_bufref(&bufref, buf);
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2014-04-06 20:45:43 +02:00
|
|
|
return NULL;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (flags & BLN_LISTED)
|
2014-04-06 20:45:43 +02:00
|
|
|
{
|
2016-07-10 17:00:38 +02:00
|
|
|
if (apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf)
|
2016-07-10 18:21:50 +02:00
|
|
|
&& !bufref_valid(&bufref))
|
2014-04-06 20:45:43 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
2018-03-04 18:08:14 +01:00
|
|
|
#ifdef FEAT_EVAL
|
2019-11-30 20:52:27 +01:00
|
|
|
if (aborting()) // autocmds may abort script processing
|
2004-06-13 20:20:40 +00:00
|
|
|
return NULL;
|
|
|
|
#endif
|
2018-03-04 18:08:14 +01:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Free the memory for the options of a buffer.
|
|
|
|
* If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
|
|
|
|
* 'fileencoding'.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
free_buf_options(
|
|
|
|
buf_T *buf,
|
|
|
|
int free_p_ff)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (free_p_ff)
|
|
|
|
{
|
|
|
|
clear_string_option(&buf->b_p_fenc);
|
|
|
|
clear_string_option(&buf->b_p_ff);
|
|
|
|
clear_string_option(&buf->b_p_bh);
|
|
|
|
clear_string_option(&buf->b_p_bt);
|
|
|
|
}
|
|
|
|
#ifdef FEAT_FIND_ID
|
|
|
|
clear_string_option(&buf->b_p_def);
|
|
|
|
clear_string_option(&buf->b_p_inc);
|
|
|
|
# ifdef FEAT_EVAL
|
|
|
|
clear_string_option(&buf->b_p_inex);
|
|
|
|
# endif
|
|
|
|
#endif
|
2022-05-21 20:17:31 +01:00
|
|
|
#if defined(FEAT_EVAL)
|
2004-06-13 20:20:40 +00:00
|
|
|
clear_string_option(&buf->b_p_inde);
|
|
|
|
clear_string_option(&buf->b_p_indk);
|
|
|
|
#endif
|
2006-03-20 21:47:49 +00:00
|
|
|
#if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
|
|
|
|
clear_string_option(&buf->b_p_bexpr);
|
|
|
|
#endif
|
2010-07-20 17:32:38 +02:00
|
|
|
#if defined(FEAT_CRYPT)
|
|
|
|
clear_string_option(&buf->b_p_cm);
|
|
|
|
#endif
|
2017-01-24 17:48:36 +01:00
|
|
|
clear_string_option(&buf->b_p_fp);
|
2006-02-14 22:29:30 +00:00
|
|
|
#if defined(FEAT_EVAL)
|
|
|
|
clear_string_option(&buf->b_p_fex);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_CRYPT
|
2021-07-29 20:37:49 +02:00
|
|
|
# ifdef FEAT_SODIUM
|
2023-04-23 17:50:22 +01:00
|
|
|
if (buf->b_p_key != NULL && *buf->b_p_key != NUL
|
|
|
|
&& crypt_method_is_sodium(crypt_get_method_nr(buf)))
|
2022-01-19 13:32:57 +00:00
|
|
|
crypt_sodium_munlock(buf->b_p_key, STRLEN(buf->b_p_key));
|
2021-07-29 20:37:49 +02:00
|
|
|
# endif
|
2004-06-13 20:20:40 +00:00
|
|
|
clear_string_option(&buf->b_p_key);
|
|
|
|
#endif
|
|
|
|
clear_string_option(&buf->b_p_kp);
|
|
|
|
clear_string_option(&buf->b_p_mps);
|
|
|
|
clear_string_option(&buf->b_p_fo);
|
2004-12-27 21:59:20 +00:00
|
|
|
clear_string_option(&buf->b_p_flp);
|
2004-06-13 20:20:40 +00:00
|
|
|
clear_string_option(&buf->b_p_isk);
|
2018-06-23 19:23:02 +02:00
|
|
|
#ifdef FEAT_VARTABS
|
|
|
|
clear_string_option(&buf->b_p_vsts);
|
2023-03-07 17:45:11 +00:00
|
|
|
VIM_CLEAR(buf->b_p_vsts_nopaste);
|
2022-02-01 13:54:17 +00:00
|
|
|
VIM_CLEAR(buf->b_p_vsts_array);
|
2018-06-23 19:23:02 +02:00
|
|
|
clear_string_option(&buf->b_p_vts);
|
2019-02-16 19:05:11 +01:00
|
|
|
VIM_CLEAR(buf->b_p_vts_array);
|
2018-06-23 19:23:02 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_KEYMAP
|
|
|
|
clear_string_option(&buf->b_p_keymap);
|
2018-01-28 17:05:16 +01:00
|
|
|
keymap_clear(&buf->b_kmap_ga);
|
2004-06-13 20:20:40 +00:00
|
|
|
ga_clear(&buf->b_kmap_ga);
|
|
|
|
#endif
|
|
|
|
clear_string_option(&buf->b_p_com);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
clear_string_option(&buf->b_p_cms);
|
|
|
|
#endif
|
|
|
|
clear_string_option(&buf->b_p_nf);
|
|
|
|
#ifdef FEAT_SYN_HL
|
|
|
|
clear_string_option(&buf->b_p_syn);
|
2016-01-19 22:29:28 +01:00
|
|
|
clear_string_option(&buf->b_s.b_syn_isk);
|
2006-03-12 21:50:18 +00:00
|
|
|
#endif
|
|
|
|
#ifdef FEAT_SPELL
|
2010-06-05 23:22:07 +02:00
|
|
|
clear_string_option(&buf->b_s.b_p_spc);
|
|
|
|
clear_string_option(&buf->b_s.b_p_spf);
|
2013-06-08 18:19:48 +02:00
|
|
|
vim_regfree(buf->b_s.b_cap_prog);
|
2010-06-05 23:22:07 +02:00
|
|
|
buf->b_s.b_cap_prog = NULL;
|
|
|
|
clear_string_option(&buf->b_s.b_p_spl);
|
2020-06-10 21:47:00 +02:00
|
|
|
clear_string_option(&buf->b_s.b_p_spo);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
clear_string_option(&buf->b_p_sua);
|
|
|
|
clear_string_option(&buf->b_p_ft);
|
|
|
|
clear_string_option(&buf->b_p_cink);
|
|
|
|
clear_string_option(&buf->b_p_cino);
|
2022-10-15 16:05:33 +01:00
|
|
|
clear_string_option(&buf->b_p_lop);
|
2022-04-07 12:39:08 +01:00
|
|
|
clear_string_option(&buf->b_p_cinsd);
|
2004-06-13 20:20:40 +00:00
|
|
|
clear_string_option(&buf->b_p_cinw);
|
|
|
|
clear_string_option(&buf->b_p_cpt);
|
2004-07-02 15:38:35 +00:00
|
|
|
#ifdef FEAT_COMPL_FUNC
|
|
|
|
clear_string_option(&buf->b_p_cfu);
|
2021-12-03 11:09:29 +00:00
|
|
|
free_callback(&buf->b_cfu_cb);
|
2005-09-01 20:46:49 +00:00
|
|
|
clear_string_option(&buf->b_p_ofu);
|
2021-12-03 11:09:29 +00:00
|
|
|
free_callback(&buf->b_ofu_cb);
|
2021-10-16 21:14:11 +01:00
|
|
|
clear_string_option(&buf->b_p_tsrfu);
|
2021-12-03 11:09:29 +00:00
|
|
|
free_callback(&buf->b_tsrfu_cb);
|
2004-07-02 15:38:35 +00:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_QUICKFIX
|
|
|
|
clear_string_option(&buf->b_p_gp);
|
|
|
|
clear_string_option(&buf->b_p_mp);
|
|
|
|
clear_string_option(&buf->b_p_efm);
|
|
|
|
#endif
|
|
|
|
clear_string_option(&buf->b_p_ep);
|
|
|
|
clear_string_option(&buf->b_p_path);
|
|
|
|
clear_string_option(&buf->b_p_tags);
|
2015-11-24 18:48:14 +01:00
|
|
|
clear_string_option(&buf->b_p_tc);
|
2019-04-28 18:05:35 +02:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
clear_string_option(&buf->b_p_tfu);
|
2021-11-24 16:32:55 +00:00
|
|
|
free_callback(&buf->b_tfu_cb);
|
2019-04-28 18:05:35 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
clear_string_option(&buf->b_p_dict);
|
|
|
|
clear_string_option(&buf->b_p_tsr);
|
2004-07-02 15:38:35 +00:00
|
|
|
clear_string_option(&buf->b_p_qe);
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_p_ar = -1;
|
2013-11-06 05:26:15 +01:00
|
|
|
buf->b_p_ul = NO_LOCAL_UNDOLEVEL;
|
2014-03-12 18:55:58 +01:00
|
|
|
clear_string_option(&buf->b_p_lw);
|
2014-09-23 15:45:08 +02:00
|
|
|
clear_string_option(&buf->b_p_bkc);
|
2017-03-05 17:43:31 +01:00
|
|
|
clear_string_option(&buf->b_p_menc);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-06-04 14:58:02 +02:00
|
|
|
* Get alternate file "n".
|
|
|
|
* Set linenr to "lnum" or altfpos.lnum if "lnum" == 0.
|
|
|
|
* Also set cursor column to altfpos.col if 'startofline' is not set.
|
2004-06-13 20:20:40 +00:00
|
|
|
* if (options & GETF_SETMARK) call setpcmark()
|
|
|
|
* if (options & GETF_ALT) we are jumping to an alternate file.
|
|
|
|
* if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping
|
|
|
|
*
|
2017-06-04 14:58:02 +02:00
|
|
|
* Return FAIL for failure, OK for success.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_getfile(
|
|
|
|
int n,
|
|
|
|
linenr_T lnum,
|
|
|
|
int options,
|
|
|
|
int forceit)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
win_T *wp = NULL;
|
|
|
|
pos_T *fpos;
|
|
|
|
colnr_T col;
|
|
|
|
|
|
|
|
buf = buflist_findnr(n);
|
|
|
|
if (buf == NULL)
|
|
|
|
{
|
|
|
|
if ((options & GETF_ALT) && n == 0)
|
2021-06-27 22:03:33 +02:00
|
|
|
emsg(_(e_no_alternate_file));
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2021-12-16 20:56:57 +00:00
|
|
|
semsg(_(e_buffer_nr_not_found), n);
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// if alternate file is the current buffer, nothing to do
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf == curbuf)
|
|
|
|
return OK;
|
|
|
|
|
2022-05-30 15:23:09 +01:00
|
|
|
if (text_or_buf_locked())
|
2006-04-05 20:41:53 +00:00
|
|
|
return FAIL;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// altfpos may be changed by getfile(), get it now
|
2004-06-13 20:20:40 +00:00
|
|
|
if (lnum == 0)
|
|
|
|
{
|
|
|
|
fpos = buflist_findfpos(buf);
|
|
|
|
lnum = fpos->lnum;
|
|
|
|
col = fpos->col;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
col = 0;
|
|
|
|
|
|
|
|
if (options & GETF_SWITCH)
|
|
|
|
{
|
2023-05-14 17:24:22 +01:00
|
|
|
// If 'switchbuf' is set jump to the window containing "buf".
|
|
|
|
wp = swbuf_goto_win_with_buf(buf);
|
2015-06-19 14:41:49 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If 'switchbuf' contains "split", "vsplit" or "newtab" and the
|
|
|
|
// current buffer isn't empty: open new tab or window
|
2015-06-19 14:41:49 +02:00
|
|
|
if (wp == NULL && (swb_flags & (SWB_VSPLIT | SWB_SPLIT | SWB_NEWTAB))
|
2017-03-12 18:23:53 +01:00
|
|
|
&& !BUFEMPTY())
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2015-06-19 14:41:49 +02:00
|
|
|
if (swb_flags & SWB_NEWTAB)
|
2008-06-24 20:19:36 +00:00
|
|
|
tabpage_new();
|
2015-06-19 14:41:49 +02:00
|
|
|
else if (win_split(0, (swb_flags & SWB_VSPLIT) ? WSP_VERT : 0)
|
|
|
|
== FAIL)
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
2010-09-21 16:56:35 +02:00
|
|
|
RESET_BINDING(curwin);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++RedrawingDisabled;
|
2023-05-20 14:07:00 +01:00
|
|
|
int retval = FAIL;
|
2017-06-05 16:01:59 +02:00
|
|
|
if (GETFILE_SUCCESS(getfile(buf->b_fnum, NULL, NULL,
|
|
|
|
(options & GETF_SETMARK), lnum, forceit)))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// cursor is at to BOL and w_cursor.lnum is checked due to getfile()
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!p_sol && col != 0)
|
|
|
|
{
|
|
|
|
curwin->w_cursor.col = col;
|
|
|
|
check_cursor_col();
|
|
|
|
curwin->w_cursor.coladd = 0;
|
|
|
|
curwin->w_set_curswant = TRUE;
|
|
|
|
}
|
2023-05-20 14:07:00 +01:00
|
|
|
retval = OK;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2023-05-20 14:07:00 +01:00
|
|
|
|
|
|
|
if (RedrawingDisabled > 0)
|
|
|
|
--RedrawingDisabled;
|
|
|
|
return retval;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* go to the last know line number for the current buffer
|
|
|
|
*/
|
2019-08-20 20:13:45 +02:00
|
|
|
static void
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_getfpos(void)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
pos_T *fpos;
|
|
|
|
|
|
|
|
fpos = buflist_findfpos(curbuf);
|
|
|
|
|
|
|
|
curwin->w_cursor.lnum = fpos->lnum;
|
|
|
|
check_cursor_lnum();
|
|
|
|
|
|
|
|
if (p_sol)
|
|
|
|
curwin->w_cursor.col = 0;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
curwin->w_cursor.col = fpos->col;
|
|
|
|
check_cursor_col();
|
|
|
|
curwin->w_cursor.coladd = 0;
|
|
|
|
curwin->w_set_curswant = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-29 20:58:21 +00:00
|
|
|
/*
|
|
|
|
* Find file in buffer list by name (it has to be for the current window).
|
|
|
|
* Returns NULL if not found.
|
|
|
|
*/
|
|
|
|
buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findname_exp(char_u *fname)
|
2004-12-29 20:58:21 +00:00
|
|
|
{
|
|
|
|
char_u *ffname;
|
|
|
|
buf_T *buf = NULL;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// First make the name into a full path name
|
2004-12-29 20:58:21 +00:00
|
|
|
ffname = FullName_save(fname,
|
|
|
|
#ifdef UNIX
|
2019-11-30 20:52:27 +01:00
|
|
|
TRUE // force expansion, get rid of symbolic links
|
2004-12-29 20:58:21 +00:00
|
|
|
#else
|
|
|
|
FALSE
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
if (ffname != NULL)
|
|
|
|
{
|
|
|
|
buf = buflist_findname(ffname);
|
|
|
|
vim_free(ffname);
|
|
|
|
}
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Find file in buffer list by name (it has to be for the current window).
|
|
|
|
* "ffname" must have a full path.
|
2004-12-29 20:58:21 +00:00
|
|
|
* Skips dummy buffers.
|
|
|
|
* Returns NULL if not found.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findname(char_u *ffname)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
#ifdef UNIX
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T st;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (mch_stat((char *)ffname, &st) < 0)
|
|
|
|
st.st_dev = (dev_T)-1;
|
|
|
|
return buflist_findname_stat(ffname, &st);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Same as buflist_findname(), but pass the stat structure to avoid getting it
|
|
|
|
* twice for the same file.
|
2004-12-29 20:58:21 +00:00
|
|
|
* Returns NULL if not found.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
static buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findname_stat(
|
|
|
|
char_u *ffname,
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T *stp)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
#endif
|
|
|
|
buf_T *buf;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Start at the last buffer, expect to find a match sooner.
|
2020-04-06 22:13:01 +02:00
|
|
|
FOR_ALL_BUFS_FROM_LAST(buf)
|
2004-12-29 20:58:21 +00:00
|
|
|
if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
|
|
|
, stp
|
|
|
|
#endif
|
|
|
|
))
|
|
|
|
return buf;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find file in buffer list by a regexp pattern.
|
|
|
|
* Return fnum of the found buffer.
|
|
|
|
* Return < 0 for error.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findpat(
|
|
|
|
char_u *pattern,
|
2019-11-30 20:52:27 +01:00
|
|
|
char_u *pattern_end, // pointer to first char after pattern
|
|
|
|
int unlisted, // find unlisted buffers
|
|
|
|
int diffmode UNUSED, // find diff-mode buffers only
|
|
|
|
int curtab_only) // find buffers in current tab only
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
int match = -1;
|
|
|
|
int find_listed;
|
|
|
|
char_u *pat;
|
|
|
|
char_u *patend;
|
|
|
|
int attempt;
|
|
|
|
char_u *p;
|
|
|
|
int toggledollar;
|
|
|
|
|
2021-01-23 15:15:01 +01:00
|
|
|
// "%" is current file, "%%" or "#" is alternate file
|
|
|
|
if ((pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#'))
|
|
|
|
|| (in_vim9script() && pattern_end == pattern + 2
|
|
|
|
&& pattern[0] == '%' && pattern[1] == '%'))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2021-01-23 15:15:01 +01:00
|
|
|
if (*pattern == '#' || pattern_end == pattern + 2)
|
2004-06-13 20:20:40 +00:00
|
|
|
match = curwin->w_alt_fnum;
|
2021-01-23 15:15:01 +01:00
|
|
|
else
|
|
|
|
match = curbuf->b_fnum;
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
if (diffmode && !diff_mode_buf(buflist_findnr(match)))
|
|
|
|
match = -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try four ways of matching a listed buffer:
|
|
|
|
* attempt == 0: without '^' or '$' (at any position)
|
2007-05-10 16:44:05 +00:00
|
|
|
* attempt == 1: with '^' at start (only at position 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
* attempt == 2: with '$' at end (only match at end)
|
|
|
|
* attempt == 3: with '^' at start and '$' at end (only full match)
|
|
|
|
* Repeat this for finding an unlisted buffer if there was no matching
|
|
|
|
* listed buffer.
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE);
|
|
|
|
if (pat == NULL)
|
|
|
|
return -1;
|
|
|
|
patend = pat + STRLEN(pat) - 1;
|
|
|
|
toggledollar = (patend > pat && *patend == '$');
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// First try finding a listed buffer. If not found and "unlisted"
|
|
|
|
// is TRUE, try finding an unlisted buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
find_listed = TRUE;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
for (attempt = 0; attempt <= 3; ++attempt)
|
|
|
|
{
|
2014-11-19 16:38:07 +01:00
|
|
|
regmatch_T regmatch;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// may add '^' and '$'
|
2004-06-13 20:20:40 +00:00
|
|
|
if (toggledollar)
|
2019-11-30 20:52:27 +01:00
|
|
|
*patend = (attempt < 2) ? NUL : '$'; // add/remove '$'
|
2004-06-13 20:20:40 +00:00
|
|
|
p = pat;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (*p == '^' && !(attempt & 1)) // add/remove '^'
|
2004-06-13 20:20:40 +00:00
|
|
|
++p;
|
2020-12-21 19:59:08 +01:00
|
|
|
regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2020-04-06 22:13:01 +02:00
|
|
|
FOR_ALL_BUFS_FROM_LAST(buf)
|
2022-05-15 14:50:12 +01:00
|
|
|
{
|
|
|
|
if (regmatch.regprog == NULL)
|
|
|
|
{
|
|
|
|
// invalid pattern, possibly after switching engine
|
|
|
|
vim_free(pat);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_p_bl == find_listed
|
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
&& (!diffmode || diff_mode_buf(buf))
|
|
|
|
#endif
|
2014-11-19 16:38:07 +01:00
|
|
|
&& buflist_match(®match, buf, FALSE) != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2013-03-19 14:25:54 +01:00
|
|
|
if (curtab_only)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Ignore the match if the buffer is not open in
|
|
|
|
// the current tab.
|
2013-03-19 14:25:54 +01:00
|
|
|
win_T *wp;
|
|
|
|
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_WINDOWS(wp)
|
2013-03-19 14:25:54 +01:00
|
|
|
if (wp->w_buffer == buf)
|
|
|
|
break;
|
|
|
|
if (wp == NULL)
|
|
|
|
continue;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
if (match >= 0) // already found a match
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
match = -2;
|
|
|
|
break;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
match = buf->b_fnum; // remember first match
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2022-05-15 14:50:12 +01:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2014-11-19 16:38:07 +01:00
|
|
|
vim_regfree(regmatch.regprog);
|
2019-11-30 20:52:27 +01:00
|
|
|
if (match >= 0) // found one match
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Only search for unlisted buffers if there was no match with
|
|
|
|
// a listed buffer.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!unlisted || !find_listed || match != -1)
|
|
|
|
break;
|
|
|
|
find_listed = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(pat);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (match == -2)
|
2021-12-16 20:56:57 +00:00
|
|
|
semsg(_(e_more_than_one_match_for_str), pattern);
|
2004-06-13 20:20:40 +00:00
|
|
|
else if (match < 0)
|
2021-12-16 20:56:57 +00:00
|
|
|
semsg(_(e_no_matching_buffer_for_str), pattern);
|
2004-06-13 20:20:40 +00:00
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
typedef struct {
|
|
|
|
buf_T *buf;
|
|
|
|
char_u *match;
|
|
|
|
} bufmatch_T;
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Find all buffer names that match.
|
|
|
|
* For command line expansion of ":buf" and ":sbuf".
|
|
|
|
* Return OK if matches found, FAIL otherwise.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
ExpandBufnames(
|
|
|
|
char_u *pat,
|
|
|
|
int *num_file,
|
|
|
|
char_u ***file,
|
|
|
|
int options)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
int count;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf_T *buf;
|
|
|
|
int round;
|
|
|
|
char_u *p;
|
2022-02-24 13:28:41 +00:00
|
|
|
char_u *patc = NULL;
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
bufmatch_T *matches = NULL;
|
|
|
|
#endif
|
2022-02-24 13:28:41 +00:00
|
|
|
int fuzzy;
|
|
|
|
fuzmatch_str_T *fuzmatch = NULL;
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
regmatch_T regmatch;
|
|
|
|
int score = 0;
|
|
|
|
int to_free = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
*num_file = 0; // return values in case of FAIL
|
2004-06-13 20:20:40 +00:00
|
|
|
*file = NULL;
|
|
|
|
|
2019-12-30 21:59:03 +01:00
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
if ((options & BUF_DIFF_FILTER) && !curwin->w_p_diff)
|
|
|
|
return FAIL;
|
|
|
|
#endif
|
|
|
|
|
2022-02-24 13:28:41 +00:00
|
|
|
fuzzy = cmdline_fuzzy_complete(pat);
|
|
|
|
|
|
|
|
// Make a copy of "pat" and change "^" to "\(^\|[\/]\)" (if doing regular
|
|
|
|
// expression matching)
|
|
|
|
if (!fuzzy)
|
2005-02-26 23:04:13 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (*pat == '^' && pat[1] != NUL)
|
2022-02-24 13:28:41 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
int len = (int)STRLEN(pat);
|
|
|
|
patc = alloc(len);
|
2022-02-24 13:28:41 +00:00
|
|
|
if (patc == NULL)
|
|
|
|
return FAIL;
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
STRNCPY(patc, pat + 1, len - 1);
|
|
|
|
patc[len - 1] = NUL;
|
|
|
|
to_free = TRUE;
|
2022-02-24 13:28:41 +00:00
|
|
|
}
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
else if (*pat == '^')
|
|
|
|
patc = (char_u *)"";
|
2022-02-24 13:28:41 +00:00
|
|
|
else
|
|
|
|
patc = pat;
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
regmatch.regprog = vim_regcomp(patc, RE_MAGIC);
|
2005-02-26 23:04:13 +00:00
|
|
|
}
|
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
// round == 1: Count the matches.
|
|
|
|
// round == 2: Build the array to keep the matches.
|
|
|
|
for (round = 1; round <= 2; ++round)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
count = 0;
|
|
|
|
FOR_ALL_BUFFERS(buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (!buf->b_p_bl) // skip unlisted buffers
|
|
|
|
continue;
|
2019-12-29 13:56:33 +01:00
|
|
|
#ifdef FEAT_DIFF
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (options & BUF_DIFF_FILTER)
|
|
|
|
// Skip buffers not suitable for
|
|
|
|
// :diffget or :diffput completion.
|
|
|
|
if (buf == curbuf || !diff_mode_buf(buf))
|
|
|
|
continue;
|
2019-12-29 13:56:33 +01:00
|
|
|
#endif
|
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (!fuzzy)
|
|
|
|
{
|
|
|
|
if (regmatch.regprog == NULL)
|
2022-05-15 14:50:12 +01:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
// invalid pattern, possibly after recompiling
|
|
|
|
if (to_free)
|
|
|
|
vim_free(patc);
|
|
|
|
return FAIL;
|
2022-05-15 14:50:12 +01:00
|
|
|
}
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
p = buflist_match(®match, buf, p_wic);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
p = NULL;
|
|
|
|
// first try matching with the short file name
|
|
|
|
if ((score = fuzzy_match_str(buf->b_sfname, pat)) != 0)
|
|
|
|
p = buf->b_sfname;
|
|
|
|
if (p == NULL)
|
2022-02-24 13:28:41 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
// next try matching with the full path file name
|
|
|
|
if ((score = fuzzy_match_str(buf->b_ffname, pat)) != 0)
|
|
|
|
p = buf->b_ffname;
|
2022-02-24 13:28:41 +00:00
|
|
|
}
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
}
|
2022-02-24 13:28:41 +00:00
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (p == NULL)
|
|
|
|
continue;
|
2022-02-28 13:28:38 +00:00
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (round == 1)
|
|
|
|
{
|
|
|
|
++count;
|
|
|
|
continue;
|
|
|
|
}
|
2022-02-24 13:28:41 +00:00
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (options & WILD_HOME_REPLACE)
|
|
|
|
p = home_replace_save(buf, p);
|
|
|
|
else
|
|
|
|
p = vim_strsave(p);
|
2022-02-28 13:28:38 +00:00
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (!fuzzy)
|
|
|
|
{
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (matches != NULL)
|
2022-02-28 13:28:38 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
matches[count].buf = buf;
|
|
|
|
matches[count].match = p;
|
2022-02-28 13:28:38 +00:00
|
|
|
count++;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
(*file)[count++] = p;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fuzmatch[count].idx = count;
|
|
|
|
fuzmatch[count].str = p;
|
|
|
|
fuzmatch[count].score = score;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (count == 0) // no match found, break here
|
|
|
|
break;
|
|
|
|
if (round == 1)
|
|
|
|
{
|
|
|
|
if (!fuzzy)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
*file = ALLOC_MULT(char_u *, count);
|
|
|
|
if (*file == NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
vim_regfree(regmatch.regprog);
|
|
|
|
if (to_free)
|
|
|
|
vim_free(patc);
|
|
|
|
return FAIL;
|
|
|
|
}
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (options & WILD_BUFLASTUSED)
|
|
|
|
matches = ALLOC_MULT(bufmatch_T, count);
|
2019-10-27 05:12:45 +01:00
|
|
|
#endif
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
|
|
|
|
if (fuzmatch == NULL)
|
2022-02-24 13:28:41 +00:00
|
|
|
{
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
*num_file = 0;
|
|
|
|
*file = NULL;
|
|
|
|
return FAIL;
|
2022-02-24 13:28:41 +00:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
patch 9.1.0131: buffer-completion may not always find all matches
Problem: buffer-completion code too complicated and does not always
find all matches (irisjae)
Solution: do not try to anchor pattern to beginning of line or
directory-separator, always return all matches
Note: we are considering the non-fuzzy buffer-matching here.
Currently, the buffer-completion code makes 2 attempts to match a
pattern against the list of available patterns. First try is to match
the pattern and anchor it to either the beginning of the file name or
at a directory-separator (// or \\).
When a match is found, Vim returns the matching buffers and does not try
to find a match anywhere within a buffer name. So if you have opened two
buffers like /tmp/Foobar.c and /tmp/MyFoobar.c using `:b Foo` will only
complete to the first filename, but not the second (the same happens
with `getcompletion('Foo', 'buffer')`).
It may make sense, that completion priorities buffer names at directory
boundaries, but it inconsistent, may cause confusion why a certain
buffer name is not completed when typing `:b Foo<C-D>` which returns
only a single file name and then pressing Enter (to switch to that
buffer), Vim will error with 'E93: More than one match for Foo').
Similar things may happen when wiping the /tmp/Foobar.c pattern and
afterwards the completion starts completing other buffers.
So let's simplify the code and always match the pattern anywhere in the
buffer name, do not try to favor matches at directory boundaries. This
is also simplifies the code a bit, we do not need to run over the list
of buffers several times, but only twice.
fixes #13894
closes: #14082
Signed-off-by: Christian Brabandt <cb@256bit.org>
2024-02-24 14:12:13 +01:00
|
|
|
if (!fuzzy)
|
|
|
|
{
|
|
|
|
vim_regfree(regmatch.regprog);
|
|
|
|
if (to_free)
|
|
|
|
vim_free(patc);
|
|
|
|
}
|
2005-02-26 23:04:13 +00:00
|
|
|
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
2022-02-24 13:28:41 +00:00
|
|
|
if (!fuzzy)
|
2019-10-27 05:12:45 +01:00
|
|
|
{
|
2022-02-24 13:28:41 +00:00
|
|
|
if (matches != NULL)
|
2019-10-27 05:12:45 +01:00
|
|
|
{
|
2022-02-24 13:28:41 +00:00
|
|
|
int i;
|
|
|
|
if (count > 1)
|
|
|
|
qsort(matches, count, sizeof(bufmatch_T), buf_compare);
|
|
|
|
// if the current buffer is first in the list, place it at the end
|
|
|
|
if (matches[0].buf == curbuf)
|
|
|
|
{
|
|
|
|
for (i = 1; i < count; i++)
|
|
|
|
(*file)[i-1] = matches[i].match;
|
|
|
|
(*file)[count-1] = matches[0].match;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
(*file)[i] = matches[i].match;
|
|
|
|
}
|
|
|
|
vim_free(matches);
|
2019-10-27 05:12:45 +01:00
|
|
|
}
|
2022-02-24 13:28:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fuzzymatches_to_strmatches(fuzmatch, file, count, FALSE) == FAIL)
|
|
|
|
return FAIL;
|
2019-10-27 05:12:45 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
*num_file = count;
|
|
|
|
return (count == 0 ? FAIL : OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for a match on the file name for buffer "buf" with regprog "prog".
|
2022-05-15 14:50:12 +01:00
|
|
|
* Note that rmp->regprog may become NULL when switching regexp engine.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
static char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_match(
|
|
|
|
regmatch_T *rmp,
|
|
|
|
buf_T *buf,
|
2019-11-30 20:52:27 +01:00
|
|
|
int ignore_case) // when TRUE ignore case, when FALSE use 'fic'
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *match;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// First try the short file name, then the long file name.
|
2014-11-19 16:38:07 +01:00
|
|
|
match = fname_match(rmp, buf->b_sfname, ignore_case);
|
2022-05-11 11:42:28 +01:00
|
|
|
if (match == NULL && rmp->regprog != NULL)
|
2014-11-19 16:38:07 +01:00
|
|
|
match = fname_match(rmp, buf->b_ffname, ignore_case);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-05-15 14:50:12 +01:00
|
|
|
* Try matching the regexp in "rmp->regprog" with file name "name".
|
|
|
|
* Note that rmp->regprog may become NULL when switching regexp engine.
|
2004-06-13 20:20:40 +00:00
|
|
|
* Return "name" when there is a match, NULL when not.
|
|
|
|
*/
|
|
|
|
static char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
fname_match(
|
|
|
|
regmatch_T *rmp,
|
|
|
|
char_u *name,
|
2019-11-30 20:52:27 +01:00
|
|
|
int ignore_case) // when TRUE ignore case, when FALSE use 'fic'
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *match = NULL;
|
|
|
|
char_u *p;
|
|
|
|
|
2022-05-15 14:50:12 +01:00
|
|
|
// extra check for valid arguments
|
2023-01-09 19:04:23 +00:00
|
|
|
if (name == NULL || rmp->regprog == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
// Ignore case when 'fileignorecase' or the argument is set.
|
|
|
|
rmp->rm_ic = p_fic || ignore_case;
|
|
|
|
if (vim_regexec(rmp, name, (colnr_T)0))
|
|
|
|
match = name;
|
|
|
|
else if (rmp->regprog != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2023-01-09 19:04:23 +00:00
|
|
|
// Replace $(HOME) with '~' and try matching again.
|
|
|
|
p = home_replace_save(NULL, name);
|
|
|
|
if (p != NULL && vim_regexec(rmp, p, (colnr_T)0))
|
2004-06-13 20:20:40 +00:00
|
|
|
match = name;
|
2023-01-09 19:04:23 +00:00
|
|
|
vim_free(p);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return match;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-07-14 22:09:39 +02:00
|
|
|
* Find a file in the buffer list by buffer number.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findnr(int nr)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-14 22:09:39 +02:00
|
|
|
char_u key[VIM_SIZEOF_INT * 2 + 1];
|
|
|
|
hashitem_T *hi;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (nr == 0)
|
|
|
|
nr = curwin->w_alt_fnum;
|
2016-07-14 22:09:39 +02:00
|
|
|
sprintf((char *)key, "%x", nr);
|
|
|
|
hi = hash_find(&buf_hashtab, key);
|
|
|
|
|
|
|
|
if (!HASHITEM_EMPTY(hi))
|
|
|
|
return (buf_T *)(hi->hi_key
|
|
|
|
- ((unsigned)(curbuf->b_key - (char_u *)curbuf)));
|
2004-06-13 20:20:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get name of file 'n' in the buffer list.
|
|
|
|
* When the file has no name an empty string is returned.
|
|
|
|
* home_replace() is used to shorten the file name (used for marks).
|
|
|
|
* Returns a pointer to allocated memory, of NULL when failed.
|
|
|
|
*/
|
|
|
|
char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_nr2name(
|
|
|
|
int n,
|
|
|
|
int fullname,
|
2019-11-30 20:52:27 +01:00
|
|
|
int helptail) // for help buffers return tail only
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
|
|
|
|
buf = buflist_findnr(n);
|
|
|
|
if (buf == NULL)
|
|
|
|
return NULL;
|
|
|
|
return home_replace_save(helptail ? buf : NULL,
|
|
|
|
fullname ? buf->b_ffname : buf->b_fname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the "lnum" and "col" for the buffer "buf" and the current window.
|
|
|
|
* When "copy_options" is TRUE save the local window option values.
|
|
|
|
* When "lnum" is 0 only do the options.
|
|
|
|
*/
|
2019-07-21 19:25:37 +02:00
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_setfpos(
|
|
|
|
buf_T *buf,
|
2020-10-25 17:09:50 +01:00
|
|
|
win_T *win, // may be NULL when using :badd
|
2016-01-30 15:14:10 +01:00
|
|
|
linenr_T lnum,
|
|
|
|
colnr_T col,
|
|
|
|
int copy_options)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
wininfo_T *wip;
|
|
|
|
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_BUF_WININFO(buf, wip)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (wip->wi_win == win)
|
|
|
|
break;
|
|
|
|
if (wip == NULL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// allocate a new entry
|
2019-05-28 23:08:19 +02:00
|
|
|
wip = ALLOC_CLEAR_ONE(wininfo_T);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (wip == NULL)
|
|
|
|
return;
|
|
|
|
wip->wi_win = win;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (lnum == 0) // set lnum even when it's 0
|
2004-06-13 20:20:40 +00:00
|
|
|
lnum = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// remove the entry from the list
|
2004-06-13 20:20:40 +00:00
|
|
|
if (wip->wi_prev)
|
|
|
|
wip->wi_prev->wi_next = wip->wi_next;
|
|
|
|
else
|
|
|
|
buf->b_wininfo = wip->wi_next;
|
|
|
|
if (wip->wi_next)
|
|
|
|
wip->wi_next->wi_prev = wip->wi_prev;
|
|
|
|
if (copy_options && wip->wi_optset)
|
|
|
|
{
|
|
|
|
clear_winopt(&wip->wi_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
deleteFoldRecurse(&wip->wi_folds);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (lnum != 0)
|
|
|
|
{
|
|
|
|
wip->wi_fpos.lnum = lnum;
|
|
|
|
wip->wi_fpos.col = col;
|
|
|
|
}
|
2022-04-10 17:59:26 +01:00
|
|
|
if (win != NULL)
|
|
|
|
wip->wi_changelistidx = win->w_changelistidx;
|
2020-10-25 17:09:50 +01:00
|
|
|
if (copy_options && win != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Save the window-specific option values.
|
2004-06-13 20:20:40 +00:00
|
|
|
copy_winopt(&win->w_onebuf_opt, &wip->wi_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
wip->wi_fold_manual = win->w_fold_manual;
|
|
|
|
cloneFoldGrowArray(&win->w_folds, &wip->wi_folds);
|
|
|
|
#endif
|
|
|
|
wip->wi_optset = TRUE;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// insert the entry in front of the list
|
2004-06-13 20:20:40 +00:00
|
|
|
wip->wi_next = buf->b_wininfo;
|
|
|
|
buf->b_wininfo = wip;
|
|
|
|
wip->wi_prev = NULL;
|
|
|
|
if (wip->wi_next)
|
|
|
|
wip->wi_next->wi_prev = wip;
|
|
|
|
}
|
|
|
|
|
2008-11-15 13:12:07 +00:00
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
/*
|
|
|
|
* Return TRUE when "wip" has 'diff' set and the diff is only for another tab
|
|
|
|
* page. That's because a diff is local to a tab page.
|
|
|
|
*/
|
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
wininfo_other_tab_diff(wininfo_T *wip)
|
2008-11-15 13:12:07 +00:00
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
if (!wip->wi_opt.wo_diff)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
FOR_ALL_WINDOWS(wp)
|
|
|
|
// return FALSE when it's a window in the current tab page, thus
|
|
|
|
// the buffer was in diff mode here
|
|
|
|
if (wip->wi_win == wp)
|
|
|
|
return FALSE;
|
|
|
|
return TRUE;
|
2008-11-15 13:12:07 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Find info for the current window in buffer "buf".
|
|
|
|
* If not found, return the info for the most recently used window.
|
2020-10-25 17:09:50 +01:00
|
|
|
* When "need_options" is TRUE skip entries where wi_optset is FALSE.
|
2008-11-15 13:12:07 +00:00
|
|
|
* When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in
|
|
|
|
* another tab page.
|
2004-06-13 20:20:40 +00:00
|
|
|
* Returns NULL when there isn't any info.
|
|
|
|
*/
|
|
|
|
static wininfo_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
find_wininfo(
|
|
|
|
buf_T *buf,
|
2020-10-25 17:09:50 +01:00
|
|
|
int need_options,
|
2016-01-30 15:14:10 +01:00
|
|
|
int skip_diff_buffer UNUSED)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
wininfo_T *wip;
|
|
|
|
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_BUF_WININFO(buf, wip)
|
2008-11-15 13:12:07 +00:00
|
|
|
if (wip->wi_win == curwin
|
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
&& (!skip_diff_buffer || !wininfo_other_tab_diff(wip))
|
|
|
|
#endif
|
2020-10-25 17:09:50 +01:00
|
|
|
|
|
|
|
&& (!need_options || wip->wi_optset))
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
2008-11-15 13:12:07 +00:00
|
|
|
|
2023-01-09 19:04:23 +00:00
|
|
|
if (wip != NULL)
|
|
|
|
return wip;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If no wininfo for curwin, use the first in the list (that doesn't have
|
|
|
|
// 'diff' set and is in another tab page).
|
2020-10-25 17:09:50 +01:00
|
|
|
// If "need_options" is TRUE skip entries that don't have options set,
|
|
|
|
// unless the window is editing "buf", so we can copy from the window
|
|
|
|
// itself.
|
2008-11-15 13:12:07 +00:00
|
|
|
#ifdef FEAT_DIFF
|
2023-01-09 19:04:23 +00:00
|
|
|
if (skip_diff_buffer)
|
|
|
|
{
|
|
|
|
FOR_ALL_BUF_WININFO(buf, wip)
|
|
|
|
if (!wininfo_other_tab_diff(wip)
|
|
|
|
&& (!need_options || wip->wi_optset
|
|
|
|
|| (wip->wi_win != NULL
|
|
|
|
&& wip->wi_win->w_buffer == buf)))
|
|
|
|
break;
|
2008-11-15 13:12:07 +00:00
|
|
|
}
|
2023-01-09 19:04:23 +00:00
|
|
|
else
|
|
|
|
#endif
|
|
|
|
wip = buf->b_wininfo;
|
2004-06-13 20:20:40 +00:00
|
|
|
return wip;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the local window options to the values last used in this window.
|
|
|
|
* If the buffer wasn't used in this window before, use the values from
|
|
|
|
* the most recently used window. If the values were never set, use the
|
|
|
|
* global values for the window.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
get_winopts(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
wininfo_T *wip;
|
|
|
|
|
|
|
|
clear_winopt(&curwin->w_onebuf_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
clearFolding(curwin);
|
|
|
|
#endif
|
|
|
|
|
2020-10-25 17:09:50 +01:00
|
|
|
wip = find_wininfo(buf, TRUE, TRUE);
|
2018-05-13 18:05:33 +02:00
|
|
|
if (wip != NULL && wip->wi_win != NULL
|
|
|
|
&& wip->wi_win != curwin && wip->wi_win->w_buffer == buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// The buffer is currently displayed in the window: use the actual
|
|
|
|
// option values instead of the saved (possibly outdated) values.
|
2018-05-13 18:05:33 +02:00
|
|
|
win_T *wp = wip->wi_win;
|
|
|
|
|
|
|
|
copy_winopt(&wp->w_onebuf_opt, &curwin->w_onebuf_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
curwin->w_fold_manual = wp->w_fold_manual;
|
|
|
|
curwin->w_foldinvalid = TRUE;
|
|
|
|
cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (wip != NULL && wip->wi_optset)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// the buffer was displayed in the current window earlier
|
2004-06-13 20:20:40 +00:00
|
|
|
copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt);
|
|
|
|
#ifdef FEAT_FOLDING
|
|
|
|
curwin->w_fold_manual = wip->wi_fold_manual;
|
|
|
|
curwin->w_foldinvalid = TRUE;
|
|
|
|
cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt);
|
2022-04-10 17:59:26 +01:00
|
|
|
if (wip != NULL)
|
|
|
|
curwin->w_changelistidx = wip->wi_changelistidx;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#ifdef FEAT_FOLDING
|
2019-11-30 20:52:27 +01:00
|
|
|
// Set 'foldlevel' to 'foldlevelstart' if it's not negative.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (p_fdls >= 0)
|
|
|
|
curwin->w_p_fdl = p_fdls;
|
|
|
|
#endif
|
2019-09-25 20:37:36 +02:00
|
|
|
after_copy_winopt(curwin);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the position (lnum and col) for the buffer 'buf' for the current
|
|
|
|
* window.
|
|
|
|
* Returns a pointer to no_position if no position is found.
|
|
|
|
*/
|
|
|
|
pos_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findfpos(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
wininfo_T *wip;
|
2019-01-26 17:28:26 +01:00
|
|
|
static pos_T no_position = {1, 0, 0};
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2020-10-25 17:09:50 +01:00
|
|
|
wip = find_wininfo(buf, FALSE, FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (wip != NULL)
|
|
|
|
return &(wip->wi_fpos);
|
|
|
|
else
|
|
|
|
return &no_position;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find the lnum for the buffer 'buf' for the current window.
|
|
|
|
*/
|
|
|
|
linenr_T
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_findlnum(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
return buflist_findfpos(buf)->lnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2017-06-04 14:58:02 +02:00
|
|
|
* List all known file names (for :files and :buffers command).
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_list(exarg_T *eap)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-10-27 05:12:45 +01:00
|
|
|
buf_T *buf = firstbuf;
|
2004-06-13 20:20:40 +00:00
|
|
|
int len;
|
|
|
|
int i;
|
2017-08-13 20:43:48 +02:00
|
|
|
int ro_char;
|
|
|
|
int changed_char;
|
2018-03-29 16:37:16 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
int job_running;
|
|
|
|
int job_none_open;
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
garray_T buflist;
|
|
|
|
buf_T **buflist_data = NULL, **p;
|
|
|
|
|
|
|
|
if (vim_strchr(eap->arg, 't'))
|
|
|
|
{
|
|
|
|
ga_init2(&buflist, sizeof(buf_T *), 50);
|
2020-04-02 18:50:46 +02:00
|
|
|
FOR_ALL_BUFFERS(buf)
|
2019-10-27 05:12:45 +01:00
|
|
|
{
|
|
|
|
if (ga_grow(&buflist, 1) == OK)
|
|
|
|
((buf_T **)buflist.ga_data)[buflist.ga_len++] = buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
qsort(buflist.ga_data, (size_t)buflist.ga_len,
|
|
|
|
sizeof(buf_T *), buf_compare);
|
|
|
|
|
2019-11-06 23:26:20 +01:00
|
|
|
buflist_data = (buf_T **)buflist.ga_data;
|
|
|
|
buf = *buflist_data;
|
2019-10-27 05:12:45 +01:00
|
|
|
}
|
2019-11-06 23:26:20 +01:00
|
|
|
p = buflist_data;
|
2019-10-27 05:12:45 +01:00
|
|
|
|
2019-11-06 23:26:20 +01:00
|
|
|
for (; buf != NULL && !got_int; buf = buflist_data != NULL
|
2019-10-27 05:12:45 +01:00
|
|
|
? (++p < buflist_data + buflist.ga_len ? *p : NULL)
|
|
|
|
: buf->b_next)
|
|
|
|
#else
|
2004-06-13 20:20:40 +00:00
|
|
|
for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next)
|
2019-10-27 05:12:45 +01:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-03-29 16:37:16 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
job_running = term_job_running(buf->b_term);
|
2022-05-29 22:37:05 +01:00
|
|
|
job_none_open = term_none_open(buf->b_term);
|
2018-03-29 16:37:16 +02:00
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
// skip unlisted buffers, unless ! was used
|
2015-07-21 15:03:06 +02:00
|
|
|
if ((!buf->b_p_bl && !eap->forceit && !vim_strchr(eap->arg, 'u'))
|
|
|
|
|| (vim_strchr(eap->arg, 'u') && buf->b_p_bl)
|
|
|
|
|| (vim_strchr(eap->arg, '+')
|
|
|
|
&& ((buf->b_flags & BF_READERR) || !bufIsChanged(buf)))
|
|
|
|
|| (vim_strchr(eap->arg, 'a')
|
2018-03-29 16:37:16 +02:00
|
|
|
&& (buf->b_ml.ml_mfp == NULL || buf->b_nwindows == 0))
|
2015-07-21 15:03:06 +02:00
|
|
|
|| (vim_strchr(eap->arg, 'h')
|
2018-03-29 16:37:16 +02:00
|
|
|
&& (buf->b_ml.ml_mfp == NULL || buf->b_nwindows != 0))
|
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
|| (vim_strchr(eap->arg, 'R')
|
|
|
|
&& (!job_running || (job_running && job_none_open)))
|
|
|
|
|| (vim_strchr(eap->arg, '?')
|
|
|
|
&& (!job_running || (job_running && !job_none_open)))
|
|
|
|
|| (vim_strchr(eap->arg, 'F')
|
|
|
|
&& (job_running || buf->b_term == NULL))
|
|
|
|
#endif
|
2015-07-21 15:03:06 +02:00
|
|
|
|| (vim_strchr(eap->arg, '-') && buf->b_p_ma)
|
|
|
|
|| (vim_strchr(eap->arg, '=') && !buf->b_p_ro)
|
|
|
|
|| (vim_strchr(eap->arg, 'x') && !(buf->b_flags & BF_READERR))
|
|
|
|
|| (vim_strchr(eap->arg, '%') && buf != curbuf)
|
|
|
|
|| (vim_strchr(eap->arg, '#')
|
|
|
|
&& (buf == curbuf || curwin->w_alt_fnum != buf->b_fnum)))
|
2004-06-13 20:20:40 +00:00
|
|
|
continue;
|
|
|
|
if (buf_spname(buf) != NULL)
|
2012-10-03 18:25:00 +02:00
|
|
|
vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE);
|
2016-08-24 00:12:12 +02:00
|
|
|
if (message_filtered(NameBuff))
|
|
|
|
continue;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2017-08-13 20:43:48 +02:00
|
|
|
changed_char = (buf->b_flags & BF_READERR) ? 'x'
|
|
|
|
: (bufIsChanged(buf) ? '+' : ' ');
|
|
|
|
#ifdef FEAT_TERMINAL
|
2022-05-29 22:37:05 +01:00
|
|
|
if (job_running)
|
2017-08-13 20:43:48 +02:00
|
|
|
{
|
2022-05-29 22:37:05 +01:00
|
|
|
if (job_none_open)
|
2017-09-16 20:54:51 +02:00
|
|
|
ro_char = '?';
|
|
|
|
else
|
|
|
|
ro_char = 'R';
|
2019-11-30 20:52:27 +01:00
|
|
|
changed_char = ' '; // bufIsChanged() returns TRUE to avoid
|
|
|
|
// closing, but it's not actually changed.
|
2017-08-13 20:43:48 +02:00
|
|
|
}
|
|
|
|
else if (buf->b_term != NULL)
|
|
|
|
ro_char = 'F';
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
ro_char = !buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' ');
|
|
|
|
|
2016-08-24 00:12:12 +02:00
|
|
|
msg_putchar('\n');
|
2005-06-05 21:54:54 +00:00
|
|
|
len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"",
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_fnum,
|
|
|
|
buf->b_p_bl ? ' ' : 'u',
|
|
|
|
buf == curbuf ? '%' :
|
|
|
|
(curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '),
|
|
|
|
buf->b_ml.ml_mfp == NULL ? ' ' :
|
|
|
|
(buf->b_nwindows == 0 ? 'h' : 'a'),
|
2017-08-13 20:43:48 +02:00
|
|
|
ro_char,
|
|
|
|
changed_char,
|
2005-06-04 21:55:20 +00:00
|
|
|
NameBuff);
|
2016-01-10 20:54:17 +01:00
|
|
|
if (len > IOSIZE - 20)
|
|
|
|
len = IOSIZE - 20;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// put "line 999" in column 40 or after the file name
|
2004-06-13 20:20:40 +00:00
|
|
|
i = 40 - vim_strsize(IObuff);
|
|
|
|
do
|
|
|
|
IObuff[len++] = ' ';
|
2019-03-30 18:47:01 +01:00
|
|
|
while (--i > 0 && len < IOSIZE - 18);
|
2019-10-27 05:12:45 +01:00
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
if (vim_strchr(eap->arg, 't') && buf->b_last_used)
|
|
|
|
add_time(IObuff + len, (size_t)(IOSIZE - len), buf->b_last_used);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len),
|
|
|
|
_("line %ld"), buf == curbuf ? curwin->w_cursor.lnum
|
2005-05-19 20:53:52 +00:00
|
|
|
: (long)buflist_findlnum(buf));
|
2004-06-13 20:20:40 +00:00
|
|
|
msg_outtrans(IObuff);
|
2019-11-30 20:52:27 +01:00
|
|
|
out_flush(); // output one line at a time
|
2004-06-13 20:20:40 +00:00
|
|
|
ui_breakcheck();
|
|
|
|
}
|
2019-10-27 05:12:45 +01:00
|
|
|
|
|
|
|
#ifdef FEAT_VIMINFO
|
|
|
|
if (buflist_data)
|
|
|
|
ga_clear(&buflist);
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get file name and line number for file 'fnum'.
|
|
|
|
* Used by DoOneCmd() for translating '%' and '#'.
|
|
|
|
* Used by insert_reg() and cmdline_paste() for '#' register.
|
|
|
|
* Return FAIL if not found, OK for success.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_name_nr(
|
|
|
|
int fnum,
|
|
|
|
char_u **fname,
|
|
|
|
linenr_T *lnum)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
|
|
|
|
buf = buflist_findnr(fnum);
|
|
|
|
if (buf == NULL || buf->b_fname == NULL)
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
*fname = buf->b_fname;
|
|
|
|
*lnum = buflist_findlnum(buf);
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-10-11 19:27:47 +02:00
|
|
|
* Set the file name for "buf"' to "ffname_arg", short file name to
|
|
|
|
* "sfname_arg".
|
2004-06-13 20:20:40 +00:00
|
|
|
* The file name with the full path is also remembered, for when :cd is used.
|
|
|
|
* Returns FAIL for failure (file name already in use by other buffer)
|
|
|
|
* OK otherwise.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
setfname(
|
|
|
|
buf_T *buf,
|
2018-10-11 19:27:47 +02:00
|
|
|
char_u *ffname_arg,
|
|
|
|
char_u *sfname_arg,
|
2019-11-30 20:52:27 +01:00
|
|
|
int message) // give message when buffer already exists
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-10-11 19:27:47 +02:00
|
|
|
char_u *ffname = ffname_arg;
|
|
|
|
char_u *sfname = sfname_arg;
|
2004-12-29 20:58:21 +00:00
|
|
|
buf_T *obuf = NULL;
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T st;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ffname == NULL || *ffname == NUL)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Removing the name.
|
2018-10-11 19:27:47 +02:00
|
|
|
if (buf->b_sfname != buf->b_ffname)
|
|
|
|
VIM_CLEAR(buf->b_sfname);
|
|
|
|
else
|
|
|
|
buf->b_sfname = NULL;
|
2018-02-10 18:45:26 +01:00
|
|
|
VIM_CLEAR(buf->b_ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
|
|
|
st.st_dev = (dev_T)-1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
fname_expand(buf, &ffname, &sfname); // will allocate ffname
|
|
|
|
if (ffname == NULL) // out of memory
|
2004-06-13 20:20:40 +00:00
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
/*
|
2021-10-19 20:48:52 +01:00
|
|
|
* If the file name is already used in another buffer:
|
2004-06-13 20:20:40 +00:00
|
|
|
* - if the buffer is loaded, fail
|
|
|
|
* - if the buffer is not loaded, delete it from the list
|
|
|
|
*/
|
|
|
|
#ifdef UNIX
|
|
|
|
if (mch_stat((char *)ffname, &st) < 0)
|
|
|
|
st.st_dev = (dev_T)-1;
|
2004-12-29 20:58:21 +00:00
|
|
|
#endif
|
|
|
|
if (!(buf->b_flags & BF_DUMMY))
|
|
|
|
#ifdef UNIX
|
|
|
|
obuf = buflist_findname_stat(ffname, &st);
|
2004-06-13 20:20:40 +00:00
|
|
|
#else
|
2004-12-29 20:58:21 +00:00
|
|
|
obuf = buflist_findname(ffname);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
if (obuf != NULL && obuf != buf)
|
|
|
|
{
|
2021-10-04 23:13:13 +01:00
|
|
|
win_T *win;
|
|
|
|
tabpage_T *tab;
|
|
|
|
int in_use = FALSE;
|
|
|
|
|
|
|
|
// during startup a window may use a buffer that is not loaded yet
|
|
|
|
FOR_ALL_TAB_WINDOWS(tab, win)
|
|
|
|
if (win->w_buffer == obuf)
|
|
|
|
in_use = TRUE;
|
|
|
|
|
|
|
|
// it's loaded or used in a window, fail
|
|
|
|
if (obuf->b_ml.ml_mfp != NULL || in_use)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (message)
|
2021-12-16 20:56:57 +00:00
|
|
|
emsg(_(e_buffer_with_this_name_already_exists));
|
2004-06-13 20:20:40 +00:00
|
|
|
vim_free(ffname);
|
|
|
|
return FAIL;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// delete from the list
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(NULL, obuf, DOBUF_WIPE, FALSE, FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
sfname = vim_strsave(sfname);
|
|
|
|
if (ffname == NULL || sfname == NULL)
|
|
|
|
{
|
|
|
|
vim_free(sfname);
|
|
|
|
vim_free(ffname);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
#ifdef USE_FNAME_CASE
|
2019-11-30 20:52:27 +01:00
|
|
|
fname_case(sfname, 0); // set correct case for short file name
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2018-10-11 19:27:47 +02:00
|
|
|
if (buf->b_sfname != buf->b_ffname)
|
|
|
|
vim_free(buf->b_sfname);
|
2004-06-13 20:20:40 +00:00
|
|
|
vim_free(buf->b_ffname);
|
|
|
|
buf->b_ffname = ffname;
|
|
|
|
buf->b_sfname = sfname;
|
|
|
|
}
|
|
|
|
buf->b_fname = buf->b_sfname;
|
|
|
|
#ifdef UNIX
|
|
|
|
if (st.st_dev == (dev_T)-1)
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = TRUE;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_dev = st.st_dev;
|
|
|
|
buf->b_ino = st.st_ino;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
buf->b_shortname = FALSE;
|
|
|
|
|
|
|
|
buf_name_changed(buf);
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2004-12-27 21:59:20 +00:00
|
|
|
/*
|
|
|
|
* Crude way of changing the name of a buffer. Use with care!
|
|
|
|
* The name should be relative to the current directory.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_set_name(int fnum, char_u *name)
|
2004-12-27 21:59:20 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
|
|
|
|
buf = buflist_findnr(fnum);
|
2023-01-02 16:54:53 +00:00
|
|
|
if (buf == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (buf->b_sfname != buf->b_ffname)
|
|
|
|
vim_free(buf->b_sfname);
|
|
|
|
vim_free(buf->b_ffname);
|
|
|
|
buf->b_ffname = vim_strsave(name);
|
|
|
|
buf->b_sfname = NULL;
|
|
|
|
// Allocate ffname and expand into full path. Also resolves .lnk
|
|
|
|
// files on Win32.
|
|
|
|
fname_expand(buf, &buf->b_ffname, &buf->b_sfname);
|
|
|
|
buf->b_fname = buf->b_sfname;
|
2004-12-27 21:59:20 +00:00
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Take care of what needs to be done when the name of buffer "buf" has
|
|
|
|
* changed.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_name_changed(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the file name changed, also change the name of the swapfile
|
|
|
|
*/
|
|
|
|
if (buf->b_ml.ml_mfp != NULL)
|
|
|
|
ml_setname(buf);
|
|
|
|
|
2021-11-19 17:01:08 +00:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
if (buf->b_term != NULL)
|
|
|
|
term_clear_status_text(buf->b_term);
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curwin->w_buffer == buf)
|
2019-11-30 20:52:27 +01:00
|
|
|
check_arg_idx(curwin); // check file name for arg list
|
|
|
|
maketitle(); // set window title
|
|
|
|
status_redraw_all(); // status lines need to be redrawn
|
|
|
|
fmarks_check_names(buf); // check named file marks
|
|
|
|
ml_timestamp(buf); // reset timestamp
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set alternate file name for current window
|
|
|
|
*
|
|
|
|
* Used by do_one_cmd(), do_write() and do_ecmd().
|
|
|
|
* Return the buffer.
|
|
|
|
*/
|
|
|
|
buf_T *
|
2016-01-30 15:14:10 +01:00
|
|
|
setaltfname(
|
|
|
|
char_u *ffname,
|
|
|
|
char_u *sfname,
|
|
|
|
linenr_T lnum)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Create a buffer. 'buflisted' is not set if it's a new buffer
|
2004-06-13 20:20:40 +00:00
|
|
|
buf = buflist_new(ffname, sfname, lnum, 0);
|
2020-10-24 20:49:43 +02:00
|
|
|
if (buf != NULL && (cmdmod.cmod_flags & CMOD_KEEPALT) == 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin->w_alt_fnum = buf->b_fnum;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get alternate file name for current window.
|
|
|
|
* Return NULL if there isn't any, and give error message if requested.
|
|
|
|
*/
|
|
|
|
char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
getaltfname(
|
2019-11-30 20:52:27 +01:00
|
|
|
int errmsg) // give error message
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *fname;
|
|
|
|
linenr_T dummy;
|
|
|
|
|
|
|
|
if (buflist_name_nr(0, &fname, &dummy) == FAIL)
|
|
|
|
{
|
|
|
|
if (errmsg)
|
2021-06-27 22:03:33 +02:00
|
|
|
emsg(_(e_no_alternate_file));
|
2004-06-13 20:20:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return fname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a file name to the buflist and return its number.
|
|
|
|
* Uses same flags as buflist_new(), except BLN_DUMMY.
|
|
|
|
*
|
|
|
|
* used by qf_init(), main() and doarglist()
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_add(char_u *fname, int flags)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
|
|
|
|
buf = buflist_new(fname, NULL, (linenr_T)0, flags);
|
|
|
|
if (buf != NULL)
|
|
|
|
return buf->b_fnum;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Adjust slashes in file names. Called after 'shellslash' was set.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_slash_adjust(void)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *bp;
|
|
|
|
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_BUFFERS(bp)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (bp->b_ffname != NULL)
|
|
|
|
slash_adjust(bp->b_ffname);
|
|
|
|
if (bp->b_sfname != NULL)
|
|
|
|
slash_adjust(bp->b_sfname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2008-11-15 13:12:07 +00:00
|
|
|
* Set alternate cursor position for the current buffer and window "win".
|
2004-06-13 20:20:40 +00:00
|
|
|
* Also save the local window option values.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buflist_altfpos(win_T *win)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2008-11-15 13:12:07 +00:00
|
|
|
buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if 'ffname' is not the same file as current file.
|
|
|
|
* Fname must have a full path (expanded by mch_FullName()).
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
otherfile(char_u *ffname)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
return otherfile_buf(curbuf, ffname
|
|
|
|
#ifdef UNIX
|
|
|
|
, NULL
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
otherfile_buf(
|
|
|
|
buf_T *buf,
|
|
|
|
char_u *ffname
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef UNIX
|
2016-07-01 17:17:39 +02:00
|
|
|
, stat_T *stp
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2016-01-30 15:14:10 +01:00
|
|
|
)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// no name is different
|
2004-06-13 20:20:40 +00:00
|
|
|
if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL)
|
|
|
|
return TRUE;
|
|
|
|
if (fnamecmp(ffname, buf->b_ffname) == 0)
|
|
|
|
return FALSE;
|
|
|
|
#ifdef UNIX
|
|
|
|
{
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T st;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// If no stat_T given, get it now
|
2004-06-13 20:20:40 +00:00
|
|
|
if (stp == NULL)
|
|
|
|
{
|
2009-05-13 18:48:16 +00:00
|
|
|
if (!buf->b_dev_valid || mch_stat((char *)ffname, &st) < 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
st.st_dev = (dev_T)-1;
|
|
|
|
stp = &st;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// Use dev/ino to check if the files are the same, even when the names
|
|
|
|
// are different (possible with links). Still need to compare the
|
|
|
|
// name above, for when the file doesn't exist yet.
|
|
|
|
// Problem: The dev/ino changes when a file is deleted (and created
|
|
|
|
// again) and remains the same when renamed/moved. We don't want to
|
|
|
|
// mch_stat() each buffer each time, that would be too slow. Get the
|
|
|
|
// dev/ino again when they appear to match, but not when they appear
|
|
|
|
// to be different: Could skip a buffer when it's actually the same
|
|
|
|
// file.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf_same_ino(buf, stp))
|
|
|
|
{
|
|
|
|
buf_setino(buf);
|
|
|
|
if (buf_same_ino(buf, stp))
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(UNIX) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Set inode and device number for a buffer.
|
|
|
|
* Must always be called when b_fname is changed!.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_setino(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T st;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0)
|
|
|
|
{
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = TRUE;
|
2004-06-13 20:20:40 +00:00
|
|
|
buf->b_dev = st.st_dev;
|
|
|
|
buf->b_ino = st.st_ino;
|
|
|
|
}
|
|
|
|
else
|
2009-05-13 18:48:16 +00:00
|
|
|
buf->b_dev_valid = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if dev/ino in buffer "buf" matches with "stp".
|
|
|
|
*/
|
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_same_ino(
|
|
|
|
buf_T *buf,
|
2016-07-01 17:17:39 +02:00
|
|
|
stat_T *stp)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2009-05-13 18:48:16 +00:00
|
|
|
return (buf->b_dev_valid
|
2004-06-13 20:20:40 +00:00
|
|
|
&& stp->st_dev == buf->b_dev
|
|
|
|
&& stp->st_ino == buf->b_ino);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2006-02-14 22:29:30 +00:00
|
|
|
/*
|
|
|
|
* Print info about the current buffer.
|
|
|
|
*/
|
2004-06-13 20:20:40 +00:00
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
fileinfo(
|
2019-11-30 20:52:27 +01:00
|
|
|
int fullname, // when non-zero print full path
|
2016-01-30 15:14:10 +01:00
|
|
|
int shorthelp,
|
|
|
|
int dont_truncate)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *name;
|
|
|
|
int n;
|
2019-01-19 17:43:09 +01:00
|
|
|
char *p;
|
|
|
|
char *buffer;
|
2005-05-19 20:53:52 +00:00
|
|
|
size_t len;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-05-28 23:08:19 +02:00
|
|
|
buffer = alloc(IOSIZE);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buffer == NULL)
|
|
|
|
return;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (fullname > 1) // 2 CTRL-G: include buffer number
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_snprintf(buffer, IOSIZE, "buf %d: ", curbuf->b_fnum);
|
2004-06-13 20:20:40 +00:00
|
|
|
p = buffer + STRLEN(buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
p = buffer;
|
|
|
|
|
|
|
|
*p++ = '"';
|
|
|
|
if (buf_spname(curbuf) != NULL)
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_strncpy((char_u *)p, buf_spname(curbuf), IOSIZE - (p - buffer) - 1);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!fullname && curbuf->b_fname != NULL)
|
|
|
|
name = curbuf->b_fname;
|
|
|
|
else
|
|
|
|
name = curbuf->b_ffname;
|
2019-01-19 17:43:09 +01:00
|
|
|
home_replace(shorthelp ? curbuf : NULL, name, (char_u *)p,
|
2004-06-13 20:20:40 +00:00
|
|
|
(int)(IOSIZE - (p - buffer)), TRUE);
|
|
|
|
}
|
|
|
|
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_snprintf_add(buffer, IOSIZE, "\"%s%s%s%s%s%s",
|
2004-06-13 20:20:40 +00:00
|
|
|
curbufIsChanged() ? (shortmess(SHM_MOD)
|
|
|
|
? " [+]" : _(" [Modified]")) : " ",
|
2022-08-25 15:11:15 +01:00
|
|
|
(curbuf->b_flags & BF_NOTEDITED) && !bt_dontwrite(curbuf)
|
2004-06-13 20:20:40 +00:00
|
|
|
? _("[Not edited]") : "",
|
2022-08-25 15:11:15 +01:00
|
|
|
(curbuf->b_flags & BF_NEW) && !bt_dontwrite(curbuf)
|
2020-06-12 22:31:00 +02:00
|
|
|
? new_file_message() : "",
|
2004-06-13 20:20:40 +00:00
|
|
|
(curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "",
|
2013-06-07 20:17:11 +02:00
|
|
|
curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]")
|
2004-06-13 20:20:40 +00:00
|
|
|
: _("[readonly]")) : "",
|
|
|
|
(curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK)
|
|
|
|
|| curbuf->b_p_ro) ?
|
|
|
|
" " : "");
|
2019-11-30 20:52:27 +01:00
|
|
|
// With 32 bit longs and more than 21,474,836 lines multiplying by 100
|
|
|
|
// causes an overflow, thus for large numbers divide instead.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curwin->w_cursor.lnum > 1000000L)
|
|
|
|
n = (int)(((long)curwin->w_cursor.lnum) /
|
|
|
|
((long)curbuf->b_ml.ml_line_count / 100L));
|
|
|
|
else
|
|
|
|
n = (int)(((long)curwin->w_cursor.lnum * 100L) /
|
|
|
|
(long)curbuf->b_ml.ml_line_count);
|
|
|
|
if (curbuf->b_ml.ml_flags & ML_EMPTY)
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_snprintf_add(buffer, IOSIZE, "%s", _(no_lines_msg));
|
2004-06-13 20:20:40 +00:00
|
|
|
else if (p_ru)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Current line and column are already on the screen -- webb
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_snprintf_add(buffer, IOSIZE,
|
2018-08-21 15:12:14 +02:00
|
|
|
NGETTEXT("%ld line --%d%%--", "%ld lines --%d%%--",
|
|
|
|
curbuf->b_ml.ml_line_count),
|
|
|
|
(long)curbuf->b_ml.ml_line_count, n);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
2019-01-19 17:43:09 +01:00
|
|
|
vim_snprintf_add(buffer, IOSIZE,
|
2004-06-13 20:20:40 +00:00
|
|
|
_("line %ld of %ld --%d%%-- col "),
|
|
|
|
(long)curwin->w_cursor.lnum,
|
|
|
|
(long)curbuf->b_ml.ml_line_count,
|
|
|
|
n);
|
|
|
|
validate_virtcol();
|
2009-05-13 10:51:08 +00:00
|
|
|
len = STRLEN(buffer);
|
2019-01-19 17:43:09 +01:00
|
|
|
col_print((char_u *)buffer + len, IOSIZE - len,
|
2004-06-13 20:20:40 +00:00
|
|
|
(int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1);
|
|
|
|
}
|
|
|
|
|
2019-01-19 17:43:09 +01:00
|
|
|
(void)append_arg_number(curwin, (char_u *)buffer, IOSIZE,
|
|
|
|
!shortmess(SHM_FILE));
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (dont_truncate)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Temporarily set msg_scroll to avoid the message being truncated.
|
|
|
|
// First call msg_start() to get the message in the right place.
|
2004-06-13 20:20:40 +00:00
|
|
|
msg_start();
|
|
|
|
n = msg_scroll;
|
|
|
|
msg_scroll = TRUE;
|
|
|
|
msg(buffer);
|
|
|
|
msg_scroll = n;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-01-28 15:28:04 +00:00
|
|
|
p = msg_trunc_attr(buffer, FALSE, 0);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (restart_edit != 0 || (msg_scrolled && !need_wait_return))
|
2019-11-30 20:52:27 +01:00
|
|
|
// Need to repeat the message after redrawing when:
|
|
|
|
// - When restart_edit is set (otherwise there will be a delay
|
|
|
|
// before redrawing).
|
|
|
|
// - When the screen was scrolled but there is no wait-return
|
|
|
|
// prompt.
|
2019-01-19 17:43:09 +01:00
|
|
|
set_keep_msg((char_u *)p, 0);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
col_print(
|
|
|
|
char_u *buf,
|
|
|
|
size_t buflen,
|
|
|
|
int col,
|
|
|
|
int vcol)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (col == vcol)
|
2009-05-13 10:51:08 +00:00
|
|
|
vim_snprintf((char *)buf, buflen, "%d", col);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2009-05-13 10:51:08 +00:00
|
|
|
vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static char_u *lasttitle = NULL;
|
|
|
|
static char_u *lasticon = NULL;
|
|
|
|
|
2018-06-16 22:58:15 +02:00
|
|
|
/*
|
|
|
|
* Put the file name in the title bar and icon of the window.
|
|
|
|
*/
|
2004-06-13 20:20:40 +00:00
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
maketitle(void)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *p;
|
2018-06-16 22:58:15 +02:00
|
|
|
char_u *title_str = NULL;
|
|
|
|
char_u *icon_str = NULL;
|
2004-06-13 20:20:40 +00:00
|
|
|
int maxlen = 0;
|
|
|
|
int len;
|
|
|
|
int mustset;
|
|
|
|
char_u buf[IOSIZE];
|
|
|
|
int off;
|
|
|
|
|
|
|
|
if (!redrawing())
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Postpone updating the title when 'lazyredraw' is set.
|
2004-06-13 20:20:40 +00:00
|
|
|
need_maketitle = TRUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
need_maketitle = FALSE;
|
2010-07-25 13:42:29 +02:00
|
|
|
if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL)
|
2018-06-16 22:58:15 +02:00
|
|
|
return; // nothing to do
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (p_title)
|
|
|
|
{
|
|
|
|
if (p_titlelen > 0)
|
|
|
|
{
|
|
|
|
maxlen = p_titlelen * Columns / 100;
|
|
|
|
if (maxlen < 10)
|
|
|
|
maxlen = 10;
|
|
|
|
}
|
|
|
|
|
2018-06-16 22:58:15 +02:00
|
|
|
title_str = buf;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (*p_titlestring != NUL)
|
|
|
|
{
|
|
|
|
#ifdef FEAT_STL_OPT
|
2006-03-16 21:35:52 +00:00
|
|
|
if (stl_syntax & STL_IN_TITLE)
|
2022-11-07 12:16:51 +00:00
|
|
|
build_stl_str_hl(curwin, title_str, sizeof(buf), p_titlestring,
|
|
|
|
(char_u *)"titlestring", 0,
|
|
|
|
0, maxlen, NULL, NULL);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
#endif
|
2018-06-16 22:58:15 +02:00
|
|
|
title_str = p_titlestring;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// format: "fname + (path) (1 of 2) - VIM"
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2012-09-05 13:30:40 +02:00
|
|
|
#define SPACE_FOR_FNAME (IOSIZE - 100)
|
|
|
|
#define SPACE_FOR_DIR (IOSIZE - 20)
|
2019-11-30 20:52:27 +01:00
|
|
|
#define SPACE_FOR_ARGNR (IOSIZE - 10) // at least room for " - VIM"
|
2004-06-13 20:20:40 +00:00
|
|
|
if (curbuf->b_fname == NULL)
|
2012-09-05 13:30:40 +02:00
|
|
|
vim_strncpy(buf, (char_u *)_("[No Name]"), SPACE_FOR_FNAME);
|
2017-07-24 21:44:43 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
else if (curbuf->b_term != NULL)
|
|
|
|
{
|
|
|
|
vim_strncpy(buf, term_get_status_text(curbuf->b_term),
|
|
|
|
SPACE_FOR_FNAME);
|
|
|
|
}
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
p = transstr(gettail(curbuf->b_fname));
|
2012-09-05 13:30:40 +02:00
|
|
|
vim_strncpy(buf, p, SPACE_FOR_FNAME);
|
2004-06-13 20:20:40 +00:00
|
|
|
vim_free(p);
|
|
|
|
}
|
|
|
|
|
2017-07-24 21:44:43 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
if (curbuf->b_term == NULL)
|
|
|
|
#endif
|
|
|
|
switch (bufIsChanged(curbuf)
|
|
|
|
+ (curbuf->b_p_ro * 2)
|
|
|
|
+ (!curbuf->b_p_ma * 4))
|
|
|
|
{
|
|
|
|
case 1: STRCAT(buf, " +"); break;
|
|
|
|
case 2: STRCAT(buf, " ="); break;
|
|
|
|
case 3: STRCAT(buf, " =+"); break;
|
|
|
|
case 4:
|
|
|
|
case 6: STRCAT(buf, " -"); break;
|
|
|
|
case 5:
|
|
|
|
case 7: STRCAT(buf, " -+"); break;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2017-07-24 21:44:43 +02:00
|
|
|
if (curbuf->b_fname != NULL
|
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
&& curbuf->b_term == NULL
|
|
|
|
#endif
|
|
|
|
)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Get path of file, replace home dir with ~
|
2004-06-13 20:20:40 +00:00
|
|
|
off = (int)STRLEN(buf);
|
|
|
|
buf[off++] = ' ';
|
|
|
|
buf[off++] = '(';
|
|
|
|
home_replace(curbuf, curbuf->b_ffname,
|
2012-09-05 13:30:40 +02:00
|
|
|
buf + off, SPACE_FOR_DIR - off, TRUE);
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
2019-11-30 20:52:27 +01:00
|
|
|
// avoid "c:/name" to be reduced to "c"
|
2024-01-04 21:19:04 +01:00
|
|
|
if (SAFE_isalpha(buf[off]) && buf[off + 1] == ':')
|
2004-06-13 20:20:40 +00:00
|
|
|
off += 2;
|
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
// remove the file name
|
2004-12-19 22:46:22 +00:00
|
|
|
p = gettail_sep(buf + off);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (p == buf + off)
|
2017-07-23 19:51:01 +02:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// must be a help buffer
|
2017-07-24 21:44:43 +02:00
|
|
|
vim_strncpy(buf + off, (char_u *)_("help"),
|
2012-09-05 13:30:40 +02:00
|
|
|
(size_t)(SPACE_FOR_DIR - off - 1));
|
2017-07-23 19:51:01 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
*p = NUL;
|
2004-12-19 22:46:22 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Translate unprintable chars and concatenate. Keep some
|
|
|
|
// room for the server name. When there is no room (very long
|
|
|
|
// file name) use (...).
|
2012-09-05 13:30:40 +02:00
|
|
|
if (off < SPACE_FOR_DIR)
|
|
|
|
{
|
|
|
|
p = transstr(buf + off);
|
|
|
|
vim_strncpy(buf + off, p, (size_t)(SPACE_FOR_DIR - off));
|
|
|
|
vim_free(p);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vim_strncpy(buf + off, (char_u *)"...",
|
|
|
|
(size_t)(SPACE_FOR_ARGNR - off));
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
STRCAT(buf, ")");
|
|
|
|
}
|
|
|
|
|
2012-09-05 13:30:40 +02:00
|
|
|
append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#if defined(FEAT_CLIENTSERVER)
|
|
|
|
if (serverName != NULL)
|
|
|
|
{
|
|
|
|
STRCAT(buf, " - ");
|
2011-04-11 16:56:35 +02:00
|
|
|
vim_strcat(buf, serverName, IOSIZE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
STRCAT(buf, " - VIM");
|
|
|
|
|
|
|
|
if (maxlen > 0)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// make it shorter by removing a bit in the middle
|
2012-01-20 20:44:43 +01:00
|
|
|
if (vim_strsize(buf) > maxlen)
|
|
|
|
trunc_string(buf, buf, maxlen, IOSIZE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-06-16 22:58:15 +02:00
|
|
|
mustset = value_changed(title_str, &lasttitle);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (p_icon)
|
|
|
|
{
|
2018-06-16 22:58:15 +02:00
|
|
|
icon_str = buf;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (*p_iconstring != NUL)
|
|
|
|
{
|
|
|
|
#ifdef FEAT_STL_OPT
|
2006-03-16 21:35:52 +00:00
|
|
|
if (stl_syntax & STL_IN_ICON)
|
2022-11-07 12:16:51 +00:00
|
|
|
build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring,
|
|
|
|
(char_u *)"iconstring", 0, 0, 0, NULL, NULL);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
#endif
|
2018-06-16 22:58:15 +02:00
|
|
|
icon_str = p_iconstring;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (buf_spname(curbuf) != NULL)
|
2018-06-16 22:58:15 +02:00
|
|
|
p = buf_spname(curbuf);
|
2019-11-30 20:52:27 +01:00
|
|
|
else // use file name only in icon
|
2018-06-16 22:58:15 +02:00
|
|
|
p = gettail(curbuf->b_ffname);
|
|
|
|
*icon_str = NUL;
|
2019-11-30 20:52:27 +01:00
|
|
|
// Truncate name at 100 bytes.
|
2018-06-16 22:58:15 +02:00
|
|
|
len = (int)STRLEN(p);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (len > 100)
|
|
|
|
{
|
|
|
|
len -= 100;
|
|
|
|
if (has_mbyte)
|
2018-06-16 22:58:15 +02:00
|
|
|
len += (*mb_tail_off)(p, p + len) + 1;
|
|
|
|
p += len;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2018-06-16 22:58:15 +02:00
|
|
|
STRCPY(icon_str, p);
|
|
|
|
trans_characters(icon_str, IOSIZE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-16 22:58:15 +02:00
|
|
|
mustset |= value_changed(icon_str, &lasticon);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (mustset)
|
|
|
|
resettitle();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Used for title and icon: Check if "str" differs from "*last". Set "*last"
|
|
|
|
* from "str" if it does.
|
2018-06-16 22:58:15 +02:00
|
|
|
* Return TRUE if resettitle() is to be called.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
static int
|
2018-06-16 22:58:15 +02:00
|
|
|
value_changed(char_u *str, char_u **last)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if ((str == NULL) != (*last == NULL)
|
|
|
|
|| (str != NULL && *last != NULL && STRCMP(str, *last) != 0))
|
|
|
|
{
|
|
|
|
vim_free(*last);
|
|
|
|
if (str == NULL)
|
2018-06-16 22:58:15 +02:00
|
|
|
{
|
2004-06-13 20:20:40 +00:00
|
|
|
*last = NULL;
|
2018-08-07 22:31:44 +02:00
|
|
|
mch_restore_title(
|
|
|
|
last == &lasttitle ? SAVE_RESTORE_TITLE : SAVE_RESTORE_ICON);
|
2018-06-16 22:58:15 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2018-06-16 22:58:15 +02:00
|
|
|
{
|
2004-06-13 20:20:40 +00:00
|
|
|
*last = vim_strsave(str);
|
2018-06-16 22:58:15 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put current window title back (used after calling a shell)
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
resettitle(void)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
mch_settitle(lasttitle, lasticon);
|
|
|
|
}
|
2005-06-25 22:49:46 +00:00
|
|
|
|
|
|
|
# if defined(EXITFREE) || defined(PROTO)
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
free_titles(void)
|
2005-06-25 22:49:46 +00:00
|
|
|
{
|
|
|
|
vim_free(lasttitle);
|
|
|
|
vim_free(lasticon);
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2006-02-25 21:45:02 +00:00
|
|
|
#if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO)
|
2020-10-26 21:05:27 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Used for building in the status line.
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char_u *stl_start;
|
|
|
|
int stl_minwid;
|
|
|
|
int stl_maxwid;
|
|
|
|
enum {
|
|
|
|
Normal,
|
|
|
|
Empty,
|
|
|
|
Group,
|
2023-02-11 11:15:25 +00:00
|
|
|
Separate,
|
2020-10-26 21:05:27 +01:00
|
|
|
Highlight,
|
|
|
|
TabPage,
|
|
|
|
Trunc
|
|
|
|
} stl_type;
|
|
|
|
} stl_item_T;
|
|
|
|
|
|
|
|
static size_t stl_items_len = 20; // Initial value, grows as needed.
|
|
|
|
static stl_item_T *stl_items = NULL;
|
|
|
|
static int *stl_groupitem = NULL;
|
|
|
|
static stl_hlrec_T *stl_hltab = NULL;
|
|
|
|
static stl_hlrec_T *stl_tabtab = NULL;
|
2023-02-11 11:15:25 +00:00
|
|
|
static int *stl_separator_locations = NULL;
|
2020-10-26 21:05:27 +01:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
2006-02-20 21:27:21 +00:00
|
|
|
* Build a string from the status line items in "fmt".
|
2004-06-13 20:20:40 +00:00
|
|
|
* Return length of string in screen cells.
|
|
|
|
*
|
2006-02-20 21:27:21 +00:00
|
|
|
* Normally works for window "wp", except when working for 'tabline' then it
|
|
|
|
* is "curwin".
|
|
|
|
*
|
2004-06-13 20:20:40 +00:00
|
|
|
* Items are drawn interspersed with the text that surrounds it
|
2023-02-11 11:15:25 +00:00
|
|
|
* Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation
|
2004-06-13 20:20:40 +00:00
|
|
|
* Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional
|
|
|
|
*
|
|
|
|
* If maxwidth is not zero, the string will be filled at any middle marker
|
|
|
|
* or truncated if too long, fillchar is used for all whitespace.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
build_stl_str_hl(
|
|
|
|
win_T *wp,
|
2019-11-30 20:52:27 +01:00
|
|
|
char_u *out, // buffer to write into != NameBuff
|
|
|
|
size_t outlen, // length of out[]
|
2016-01-30 15:14:10 +01:00
|
|
|
char_u *fmt,
|
2022-11-07 12:16:51 +00:00
|
|
|
char_u *opt_name, // option name corresponding to "fmt"
|
|
|
|
int opt_scope, // scope for "opt_name"
|
2016-01-30 15:14:10 +01:00
|
|
|
int fillchar,
|
|
|
|
int maxwidth,
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_hlrec_T **hltab, // return: HL attributes (can be NULL)
|
|
|
|
stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-01-20 18:25:54 +01:00
|
|
|
linenr_T lnum;
|
|
|
|
size_t len;
|
2004-06-13 20:20:40 +00:00
|
|
|
char_u *p;
|
|
|
|
char_u *s;
|
|
|
|
char_u *t;
|
2013-04-24 16:52:36 +02:00
|
|
|
int byteval;
|
2004-06-13 20:20:40 +00:00
|
|
|
#ifdef FEAT_EVAL
|
2022-11-07 12:16:51 +00:00
|
|
|
int use_sandbox;
|
2017-09-08 13:59:21 +02:00
|
|
|
win_T *save_curwin;
|
|
|
|
buf_T *save_curbuf;
|
2019-11-30 15:05:22 +01:00
|
|
|
int save_VIsual_active;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
int empty_line;
|
|
|
|
colnr_T virtcol;
|
|
|
|
long l;
|
|
|
|
long n;
|
|
|
|
int prevchar_isflag;
|
|
|
|
int prevchar_isitem;
|
|
|
|
int itemisflag;
|
|
|
|
int fillable;
|
|
|
|
char_u *str;
|
|
|
|
long num;
|
|
|
|
int width;
|
|
|
|
int itemcnt;
|
|
|
|
int curitem;
|
2017-10-22 14:22:16 +02:00
|
|
|
int group_end_userhl;
|
|
|
|
int group_start_userhl;
|
2004-06-13 20:20:40 +00:00
|
|
|
int groupdepth;
|
2021-05-15 17:23:28 +02:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
int evaldepth;
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
int minwid;
|
|
|
|
int maxwid;
|
|
|
|
int zeropad;
|
|
|
|
char_u base;
|
|
|
|
char_u opt;
|
|
|
|
#define TMPLEN 70
|
2019-05-23 22:11:59 +02:00
|
|
|
char_u buf_tmp[TMPLEN];
|
|
|
|
char_u win_tmp[TMPLEN];
|
2006-02-21 22:02:53 +00:00
|
|
|
char_u *usefmt = fmt;
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_hlrec_T *sp;
|
2022-08-22 15:19:16 +01:00
|
|
|
int save_redraw_not_allowed = redraw_not_allowed;
|
2022-02-15 16:17:44 +00:00
|
|
|
int save_KeyTyped = KeyTyped;
|
2022-11-07 12:16:51 +00:00
|
|
|
// TODO: find out why using called_emsg_before makes tests fail, does it
|
|
|
|
// matter?
|
|
|
|
// int called_emsg_before = called_emsg;
|
|
|
|
int did_emsg_before = did_emsg;
|
2006-02-21 22:02:53 +00:00
|
|
|
|
2022-08-22 15:19:16 +01:00
|
|
|
// When inside update_screen() we do not want redrawing a statusline,
|
|
|
|
// ruler, title, etc. to trigger another redraw, it may cause an endless
|
|
|
|
// loop.
|
|
|
|
if (updating_screen)
|
|
|
|
redraw_not_allowed = TRUE;
|
|
|
|
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items == NULL)
|
|
|
|
{
|
|
|
|
stl_items = ALLOC_MULT(stl_item_T, stl_items_len);
|
|
|
|
stl_groupitem = ALLOC_MULT(int, stl_items_len);
|
2022-02-19 11:45:03 +00:00
|
|
|
|
|
|
|
// Allocate one more, because the last element is used to indicate the
|
|
|
|
// end of the list.
|
|
|
|
stl_hltab = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1);
|
|
|
|
stl_tabtab = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1);
|
2023-02-11 11:15:25 +00:00
|
|
|
|
|
|
|
stl_separator_locations = ALLOC_MULT(int, stl_items_len);
|
2020-10-26 21:05:27 +01:00
|
|
|
}
|
|
|
|
|
2006-02-21 22:02:53 +00:00
|
|
|
#ifdef FEAT_EVAL
|
2022-11-07 12:16:51 +00:00
|
|
|
// if "fmt" was set insecurely it needs to be evaluated in the sandbox
|
|
|
|
use_sandbox = was_set_insecurely(opt_name, opt_scope);
|
|
|
|
|
|
|
|
// When the format starts with "%!" then evaluate it as an expression and
|
|
|
|
// use the result as the actual format string.
|
2006-02-21 22:02:53 +00:00
|
|
|
if (fmt[0] == '%' && fmt[1] == '!')
|
|
|
|
{
|
2019-05-23 22:11:59 +02:00
|
|
|
typval_T tv;
|
|
|
|
|
|
|
|
tv.v_type = VAR_NUMBER;
|
|
|
|
tv.vval.v_number = wp->w_id;
|
|
|
|
set_var((char_u *)"g:statusline_winid", &tv, FALSE);
|
|
|
|
|
2022-10-01 19:43:52 +01:00
|
|
|
usefmt = eval_to_string_safe(fmt + 2, use_sandbox, FALSE, FALSE);
|
2006-02-21 22:02:53 +00:00
|
|
|
if (usefmt == NULL)
|
2006-08-29 14:48:14 +00:00
|
|
|
usefmt = fmt;
|
2019-05-23 22:11:59 +02:00
|
|
|
|
|
|
|
do_unlet((char_u *)"g:statusline_winid", TRUE);
|
2006-02-21 22:02:53 +00:00
|
|
|
}
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (fillchar == 0)
|
|
|
|
fillchar = ' ';
|
2006-04-05 20:41:53 +00:00
|
|
|
|
2019-01-20 18:25:54 +01:00
|
|
|
// The cursor in windows other than the current one isn't always
|
|
|
|
// up-to-date, esp. because of autocommands and timers.
|
|
|
|
lnum = wp->w_cursor.lnum;
|
|
|
|
if (lnum > wp->w_buffer->b_ml.ml_line_count)
|
|
|
|
{
|
|
|
|
lnum = wp->w_buffer->b_ml.ml_line_count;
|
|
|
|
wp->w_cursor.lnum = lnum;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get line & check if empty (cursorpos will show "0-1"). Note that
|
|
|
|
// p will become invalid when getting another buffer line.
|
|
|
|
p = ml_get_buf(wp->w_buffer, lnum, FALSE);
|
2013-04-24 16:52:36 +02:00
|
|
|
empty_line = (*p == NUL);
|
|
|
|
|
2019-01-20 18:25:54 +01:00
|
|
|
// Get the byte value now, in case we need it below. This is more efficient
|
|
|
|
// than making a copy of the line.
|
|
|
|
len = STRLEN(p);
|
|
|
|
if (wp->w_cursor.col > (colnr_T)len)
|
|
|
|
{
|
|
|
|
// Line may have changed since checking the cursor column, or the lnum
|
|
|
|
// was adjusted above.
|
|
|
|
wp->w_cursor.col = (colnr_T)len;
|
|
|
|
wp->w_cursor.coladd = 0;
|
2013-04-24 16:52:36 +02:00
|
|
|
byteval = 0;
|
2019-01-20 18:25:54 +01:00
|
|
|
}
|
2013-04-24 16:52:36 +02:00
|
|
|
else
|
|
|
|
byteval = (*mb_ptr2char)(p + wp->w_cursor.col);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
groupdepth = 0;
|
2021-05-15 17:23:28 +02:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
evaldepth = 0;
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
p = out;
|
|
|
|
curitem = 0;
|
|
|
|
prevchar_isflag = TRUE;
|
|
|
|
prevchar_isitem = FALSE;
|
2022-07-27 15:48:45 +01:00
|
|
|
for (s = usefmt; *s != NUL; )
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (curitem == (int)stl_items_len)
|
2011-02-15 14:24:46 +01:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
size_t new_len = stl_items_len * 3 / 2;
|
|
|
|
|
2023-02-11 11:15:25 +00:00
|
|
|
stl_item_T *new_items =
|
|
|
|
vim_realloc(stl_items, sizeof(stl_item_T) * new_len);
|
2020-10-26 21:05:27 +01:00
|
|
|
if (new_items == NULL)
|
|
|
|
break;
|
|
|
|
stl_items = new_items;
|
2023-02-11 11:15:25 +00:00
|
|
|
|
|
|
|
int *new_groupitem =
|
|
|
|
vim_realloc(stl_groupitem, sizeof(int) * new_len);
|
2020-10-26 21:05:27 +01:00
|
|
|
if (new_groupitem == NULL)
|
|
|
|
break;
|
|
|
|
stl_groupitem = new_groupitem;
|
2023-02-11 11:15:25 +00:00
|
|
|
|
|
|
|
stl_hlrec_T *new_hlrec = vim_realloc(stl_hltab,
|
2022-02-19 11:45:03 +00:00
|
|
|
sizeof(stl_hlrec_T) * (new_len + 1));
|
2020-10-26 21:05:27 +01:00
|
|
|
if (new_hlrec == NULL)
|
|
|
|
break;
|
|
|
|
stl_hltab = new_hlrec;
|
2022-02-19 11:45:03 +00:00
|
|
|
new_hlrec = vim_realloc(stl_tabtab,
|
|
|
|
sizeof(stl_hlrec_T) * (new_len + 1));
|
2020-10-26 21:05:27 +01:00
|
|
|
if (new_hlrec == NULL)
|
|
|
|
break;
|
|
|
|
stl_tabtab = new_hlrec;
|
2023-02-11 11:15:25 +00:00
|
|
|
|
|
|
|
int *new_separator_locs = vim_realloc(stl_separator_locations,
|
|
|
|
sizeof(int) * new_len);
|
|
|
|
if (new_separator_locs == NULL)
|
|
|
|
break;
|
|
|
|
stl_separator_locations = new_separator_locs;;
|
|
|
|
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items_len = new_len;
|
2011-02-15 14:24:46 +01:00
|
|
|
}
|
|
|
|
|
2022-07-27 15:48:45 +01:00
|
|
|
if (*s != '%')
|
2004-06-13 20:20:40 +00:00
|
|
|
prevchar_isflag = prevchar_isitem = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle up to the next '%' or the end.
|
|
|
|
*/
|
|
|
|
while (*s != NUL && *s != '%' && p + 1 < out + outlen)
|
|
|
|
*p++ = *s++;
|
|
|
|
if (*s == NUL || p + 1 >= out + outlen)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle one '%' item.
|
|
|
|
*/
|
|
|
|
s++;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (*s == NUL) // ignore trailing %
|
2011-02-01 21:55:01 +01:00
|
|
|
break;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (*s == '%')
|
|
|
|
{
|
|
|
|
if (p + 1 >= out + outlen)
|
|
|
|
break;
|
|
|
|
*p++ = *s++;
|
|
|
|
prevchar_isflag = prevchar_isitem = FALSE;
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-11 11:15:25 +00:00
|
|
|
// STL_SEPARATE: Separation between items, filled with white space.
|
|
|
|
if (*s == STL_SEPARATE)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
s++;
|
|
|
|
if (groupdepth > 0)
|
|
|
|
continue;
|
2023-02-11 11:15:25 +00:00
|
|
|
stl_items[curitem].stl_type = Separate;
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem++].stl_start = p;
|
2004-06-13 20:20:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*s == STL_TRUNCMARK)
|
|
|
|
{
|
|
|
|
s++;
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_type = Trunc;
|
|
|
|
stl_items[curitem++].stl_start = p;
|
2004-06-13 20:20:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (*s == ')')
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
if (groupdepth < 1)
|
|
|
|
continue;
|
|
|
|
groupdepth--;
|
|
|
|
|
2020-10-26 21:05:27 +01:00
|
|
|
t = stl_items[stl_groupitem[groupdepth]].stl_start;
|
2004-06-13 20:20:40 +00:00
|
|
|
*p = NUL;
|
|
|
|
l = vim_strsize(t);
|
2020-10-26 21:05:27 +01:00
|
|
|
if (curitem > stl_groupitem[groupdepth] + 1
|
|
|
|
&& stl_items[stl_groupitem[groupdepth]].stl_minwid == 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// remove group if all items are empty and highlight group
|
|
|
|
// doesn't change
|
2017-10-22 14:22:16 +02:00
|
|
|
group_start_userhl = group_end_userhl = 0;
|
2020-10-26 21:05:27 +01:00
|
|
|
for (n = stl_groupitem[groupdepth] - 1; n >= 0; n--)
|
2017-10-26 18:21:24 +02:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == Highlight)
|
2017-10-26 18:21:24 +02:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
group_start_userhl = group_end_userhl =
|
|
|
|
stl_items[n].stl_minwid;
|
2017-10-26 18:21:24 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-10-26 21:05:27 +01:00
|
|
|
for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
|
2017-10-22 14:22:16 +02:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == Normal)
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == Highlight)
|
|
|
|
group_end_userhl = stl_items[n].stl_minwid;
|
2017-10-22 14:22:16 +02:00
|
|
|
}
|
|
|
|
if (n == curitem && group_start_userhl == group_end_userhl)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2020-07-21 19:25:18 +02:00
|
|
|
// empty group
|
2004-06-13 20:20:40 +00:00
|
|
|
p = t;
|
|
|
|
l = 0;
|
2020-10-26 21:05:27 +01:00
|
|
|
for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
|
2020-07-21 19:25:18 +02:00
|
|
|
{
|
|
|
|
// do not use the highlighting from the removed group
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == Highlight)
|
|
|
|
stl_items[n].stl_type = Empty;
|
2020-07-21 19:25:18 +02:00
|
|
|
// adjust the start position of TabPage to the next
|
|
|
|
// item position
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == TabPage)
|
|
|
|
stl_items[n].stl_start = p;
|
2020-07-21 19:25:18 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-26 21:05:27 +01:00
|
|
|
if (l > stl_items[stl_groupitem[groupdepth]].stl_maxwid)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// truncate, remove n bytes of text at the start
|
2004-06-13 20:20:40 +00:00
|
|
|
if (has_mbyte)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Find the first character that should be included.
|
2004-06-13 20:20:40 +00:00
|
|
|
n = 0;
|
2020-10-26 21:05:27 +01:00
|
|
|
while (l >= stl_items[stl_groupitem[groupdepth]].stl_maxwid)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
l -= ptr2cells(t + n);
|
2005-08-10 21:07:57 +00:00
|
|
|
n += (*mb_ptr2len)(t + n);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2020-10-26 21:05:27 +01:00
|
|
|
n = (long)(p - t) - stl_items[stl_groupitem[groupdepth]]
|
|
|
|
.stl_maxwid + 1;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
*t = '<';
|
2009-05-13 10:51:08 +00:00
|
|
|
mch_memmove(t + 1, t + n, (size_t)(p - (t + n)));
|
2004-06-13 20:20:40 +00:00
|
|
|
p = p - n + 1;
|
2019-01-24 15:04:48 +01:00
|
|
|
|
|
|
|
// Fill up space left over by half a double-wide char.
|
2020-10-26 21:05:27 +01:00
|
|
|
while (++l < stl_items[stl_groupitem[groupdepth]].stl_minwid)
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, p);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// correct the start of the items for the truncation
|
2020-10-26 21:05:27 +01:00
|
|
|
for (l = stl_groupitem[groupdepth] + 1; l < curitem; l++)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2022-05-09 21:03:47 +01:00
|
|
|
// Minus one for the leading '<' added above.
|
|
|
|
stl_items[l].stl_start -= n - 1;
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_start < t)
|
|
|
|
stl_items[l].stl_start = t;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-26 21:05:27 +01:00
|
|
|
else if (abs(stl_items[stl_groupitem[groupdepth]].stl_minwid) > l)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// fill
|
2020-10-26 21:05:27 +01:00
|
|
|
n = stl_items[stl_groupitem[groupdepth]].stl_minwid;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (n < 0)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// fill by appending characters
|
2004-06-13 20:20:40 +00:00
|
|
|
n = 0 - n;
|
|
|
|
while (l++ < n && p + 1 < out + outlen)
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, p);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// fill by inserting characters
|
2021-03-04 21:55:58 +01:00
|
|
|
l = (n - l) * MB_CHAR2LEN(fillchar);
|
|
|
|
mch_memmove(t + l, t, (size_t)(p - t));
|
2004-06-13 20:20:40 +00:00
|
|
|
if (p + l >= out + outlen)
|
2006-04-17 22:14:47 +00:00
|
|
|
l = (long)((out + outlen) - p - 1);
|
2004-06-13 20:20:40 +00:00
|
|
|
p += l;
|
2020-10-26 21:05:27 +01:00
|
|
|
for (n = stl_groupitem[groupdepth] + 1; n < curitem; n++)
|
|
|
|
stl_items[n].stl_start += l;
|
2004-06-13 20:20:40 +00:00
|
|
|
for ( ; l > 0; l--)
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, t);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
minwid = 0;
|
|
|
|
maxwid = 9999;
|
|
|
|
zeropad = FALSE;
|
|
|
|
l = 1;
|
|
|
|
if (*s == '0')
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
zeropad = TRUE;
|
|
|
|
}
|
|
|
|
if (*s == '-')
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
l = -1;
|
|
|
|
}
|
|
|
|
if (VIM_ISDIGIT(*s))
|
|
|
|
{
|
|
|
|
minwid = (int)getdigits(&s);
|
2019-11-30 20:52:27 +01:00
|
|
|
if (minwid < 0) // overflow
|
2004-06-13 20:20:40 +00:00
|
|
|
minwid = 0;
|
|
|
|
}
|
2006-02-21 22:02:53 +00:00
|
|
|
if (*s == STL_USER_HL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_type = Highlight;
|
|
|
|
stl_items[curitem].stl_start = p;
|
|
|
|
stl_items[curitem].stl_minwid = minwid > 9 ? 1 : minwid;
|
2004-06-13 20:20:40 +00:00
|
|
|
s++;
|
|
|
|
curitem++;
|
|
|
|
continue;
|
|
|
|
}
|
2006-02-22 21:25:37 +00:00
|
|
|
if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR)
|
|
|
|
{
|
|
|
|
if (*s == STL_TABCLOSENR)
|
|
|
|
{
|
|
|
|
if (minwid == 0)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// %X ends the close label, go back to the previously
|
|
|
|
// define tab label nr.
|
2006-02-22 21:25:37 +00:00
|
|
|
for (n = curitem - 1; n >= 0; --n)
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[n].stl_type == TabPage
|
|
|
|
&& stl_items[n].stl_minwid >= 0)
|
2006-02-22 21:25:37 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
minwid = stl_items[n].stl_minwid;
|
2006-02-22 21:25:37 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2019-11-30 20:52:27 +01:00
|
|
|
// close nrs are stored as negative values
|
2006-02-22 21:25:37 +00:00
|
|
|
minwid = - minwid;
|
|
|
|
}
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_type = TabPage;
|
|
|
|
stl_items[curitem].stl_start = p;
|
|
|
|
stl_items[curitem].stl_minwid = minwid;
|
2006-02-22 21:25:37 +00:00
|
|
|
s++;
|
|
|
|
curitem++;
|
|
|
|
continue;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
if (*s == '.')
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
if (VIM_ISDIGIT(*s))
|
|
|
|
{
|
|
|
|
maxwid = (int)getdigits(&s);
|
2019-11-30 20:52:27 +01:00
|
|
|
if (maxwid <= 0) // overflow
|
2004-06-13 20:20:40 +00:00
|
|
|
maxwid = 50;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
minwid = (minwid > 50 ? 50 : minwid) * l;
|
|
|
|
if (*s == '(')
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_groupitem[groupdepth++] = curitem;
|
|
|
|
stl_items[curitem].stl_type = Group;
|
|
|
|
stl_items[curitem].stl_start = p;
|
|
|
|
stl_items[curitem].stl_minwid = minwid;
|
|
|
|
stl_items[curitem].stl_maxwid = maxwid;
|
2004-06-13 20:20:40 +00:00
|
|
|
s++;
|
|
|
|
curitem++;
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-15 17:23:28 +02:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
// Denotes end of expanded %{} block
|
|
|
|
if (*s == '}' && evaldepth > 0)
|
|
|
|
{
|
|
|
|
s++;
|
|
|
|
evaldepth--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
if (vim_strchr(STL_ALL, *s) == NULL)
|
|
|
|
{
|
2023-01-04 14:31:49 +00:00
|
|
|
if (*s == NUL) // can happen with "%0"
|
|
|
|
break;
|
2004-06-13 20:20:40 +00:00
|
|
|
s++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
opt = *s++;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// OK - now for the real work
|
2004-06-13 20:20:40 +00:00
|
|
|
base = 'D';
|
|
|
|
itemisflag = FALSE;
|
|
|
|
fillable = TRUE;
|
|
|
|
num = -1;
|
|
|
|
str = NULL;
|
|
|
|
switch (opt)
|
|
|
|
{
|
|
|
|
case STL_FILEPATH:
|
|
|
|
case STL_FULLPATH:
|
|
|
|
case STL_FILENAME:
|
2019-11-30 20:52:27 +01:00
|
|
|
fillable = FALSE; // don't change ' ' to fillchar
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf_spname(wp->w_buffer) != NULL)
|
2012-10-03 18:25:00 +02:00
|
|
|
vim_strncpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL - 1);
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
|
2006-02-14 22:29:30 +00:00
|
|
|
: wp->w_buffer->b_fname;
|
2004-06-13 20:20:40 +00:00
|
|
|
home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE);
|
|
|
|
}
|
|
|
|
trans_characters(NameBuff, MAXPATHL);
|
|
|
|
if (opt != STL_FILENAME)
|
|
|
|
str = NameBuff;
|
|
|
|
else
|
|
|
|
str = gettail(NameBuff);
|
|
|
|
break;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
case STL_VIM_EXPR: // '{'
|
2021-05-15 17:23:28 +02:00
|
|
|
{
|
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
char_u *block_start = s - 1;
|
|
|
|
#endif
|
|
|
|
int reevaluate = (*s == '%');
|
|
|
|
|
|
|
|
if (reevaluate)
|
|
|
|
s++;
|
2004-06-13 20:20:40 +00:00
|
|
|
itemisflag = TRUE;
|
|
|
|
t = p;
|
2021-05-15 17:23:28 +02:00
|
|
|
while ((*s != '}' || (reevaluate && s[-1] != '%'))
|
|
|
|
&& *s != NUL && p + 1 < out + outlen)
|
2004-06-13 20:20:40 +00:00
|
|
|
*p++ = *s++;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (*s != '}') // missing '}' or out of space
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
s++;
|
2021-05-15 17:23:28 +02:00
|
|
|
if (reevaluate)
|
|
|
|
p[-1] = 0; // remove the % at the end of %{% expr %}
|
|
|
|
else
|
|
|
|
*p = 0;
|
2004-06-13 20:20:40 +00:00
|
|
|
p = t;
|
|
|
|
#ifdef FEAT_EVAL
|
2019-05-23 22:11:59 +02:00
|
|
|
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp),
|
|
|
|
"%d", curbuf->b_fnum);
|
|
|
|
set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp);
|
|
|
|
vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->w_id);
|
|
|
|
set_internal_string_var((char_u *)"g:actual_curwin", win_tmp);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2017-09-08 13:59:21 +02:00
|
|
|
save_curbuf = curbuf;
|
|
|
|
save_curwin = curwin;
|
2019-11-30 15:05:22 +01:00
|
|
|
save_VIsual_active = VIsual_active;
|
2004-06-13 20:20:40 +00:00
|
|
|
curwin = wp;
|
|
|
|
curbuf = wp->w_buffer;
|
2019-11-30 15:05:22 +01:00
|
|
|
// Visual mode is only valid in the current window.
|
|
|
|
if (curwin != save_curwin)
|
|
|
|
VIsual_active = FALSE;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2022-10-01 19:43:52 +01:00
|
|
|
str = eval_to_string_safe(p, use_sandbox, FALSE, FALSE);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2017-09-08 13:59:21 +02:00
|
|
|
curwin = save_curwin;
|
|
|
|
curbuf = save_curbuf;
|
2019-11-30 15:05:22 +01:00
|
|
|
VIsual_active = save_VIsual_active;
|
2005-01-31 18:58:23 +00:00
|
|
|
do_unlet((char_u *)"g:actual_curbuf", TRUE);
|
2019-05-23 22:11:59 +02:00
|
|
|
do_unlet((char_u *)"g:actual_curwin", TRUE);
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (str != NULL && *str != 0)
|
|
|
|
{
|
|
|
|
if (*skipdigits(str) == NUL)
|
|
|
|
{
|
|
|
|
num = atoi((char *)str);
|
2018-02-10 18:45:26 +01:00
|
|
|
VIM_CLEAR(str);
|
2004-06-13 20:20:40 +00:00
|
|
|
itemisflag = FALSE;
|
|
|
|
}
|
|
|
|
}
|
2021-05-15 17:23:28 +02:00
|
|
|
|
|
|
|
// If the output of the expression needs to be evaluated
|
|
|
|
// replace the %{} block with the result of evaluation
|
|
|
|
if (reevaluate && str != NULL && *str != 0
|
|
|
|
&& strchr((const char *)str, '%') != NULL
|
|
|
|
&& evaldepth < MAX_STL_EVAL_DEPTH)
|
|
|
|
{
|
|
|
|
size_t parsed_usefmt = (size_t)(block_start - usefmt);
|
|
|
|
size_t str_length = strlen((const char *)str);
|
|
|
|
size_t fmt_length = strlen((const char *)s);
|
|
|
|
size_t new_fmt_len = parsed_usefmt
|
|
|
|
+ str_length + fmt_length + 3;
|
|
|
|
char_u *new_fmt = (char_u *)alloc(new_fmt_len * sizeof(char_u));
|
|
|
|
char_u *new_fmt_p = new_fmt;
|
|
|
|
|
|
|
|
new_fmt_p = (char_u *)memcpy(new_fmt_p, usefmt, parsed_usefmt)
|
|
|
|
+ parsed_usefmt;
|
|
|
|
new_fmt_p = (char_u *)memcpy(new_fmt_p , str, str_length)
|
|
|
|
+ str_length;
|
|
|
|
new_fmt_p = (char_u *)memcpy(new_fmt_p, "%}", 2) + 2;
|
|
|
|
new_fmt_p = (char_u *)memcpy(new_fmt_p , s, fmt_length)
|
|
|
|
+ fmt_length;
|
|
|
|
*new_fmt_p = 0;
|
|
|
|
new_fmt_p = NULL;
|
|
|
|
|
|
|
|
if (usefmt != fmt)
|
|
|
|
vim_free(usefmt);
|
|
|
|
VIM_CLEAR(str);
|
|
|
|
usefmt = new_fmt;
|
|
|
|
s = usefmt + parsed_usefmt;
|
|
|
|
evaldepth++;
|
|
|
|
continue;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
2021-05-15 17:23:28 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
case STL_LINE:
|
|
|
|
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY)
|
|
|
|
? 0L : (long)(wp->w_cursor.lnum);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_NUMLINES:
|
|
|
|
num = wp->w_buffer->b_ml.ml_line_count;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_COLUMN:
|
2022-05-07 20:01:16 +01:00
|
|
|
num = (State & MODE_INSERT) == 0 && empty_line
|
|
|
|
? 0 : (int)wp->w_cursor.col + 1;
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_VIRTCOL:
|
|
|
|
case STL_VIRTCOL_ALT:
|
2022-01-14 20:11:38 +00:00
|
|
|
virtcol = wp->w_virtcol + 1;
|
2019-11-30 20:52:27 +01:00
|
|
|
// Don't display %V if it's the same as %c.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (opt == STL_VIRTCOL_ALT
|
2022-05-07 20:01:16 +01:00
|
|
|
&& (virtcol == (colnr_T)((State & MODE_INSERT) == 0
|
|
|
|
&& empty_line ? 0 : (int)wp->w_cursor.col + 1)))
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
num = (long)virtcol;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_PERCENTAGE:
|
|
|
|
num = (int)(((long)wp->w_cursor.lnum * 100L) /
|
|
|
|
(long)wp->w_buffer->b_ml.ml_line_count);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_ALTPERCENT:
|
2019-05-23 22:11:59 +02:00
|
|
|
str = buf_tmp;
|
2009-05-13 10:51:08 +00:00
|
|
|
get_rel_pos(wp, str, TMPLEN);
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
|
2022-12-15 13:15:39 +00:00
|
|
|
case STL_SHOWCMD:
|
|
|
|
if (p_sc && STRCMP(opt_name, p_sloc) == 0)
|
|
|
|
str = showcmd_buf;
|
|
|
|
break;
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
case STL_ARGLISTSTAT:
|
|
|
|
fillable = FALSE;
|
2019-05-23 22:11:59 +02:00
|
|
|
buf_tmp[0] = 0;
|
|
|
|
if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), FALSE))
|
|
|
|
str = buf_tmp;
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_KEYMAP:
|
|
|
|
fillable = FALSE;
|
2019-05-23 22:11:59 +02:00
|
|
|
if (get_keymap_str(wp, (char_u *)"<%s>", buf_tmp, TMPLEN))
|
|
|
|
str = buf_tmp;
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
case STL_PAGENUM:
|
2006-03-03 22:50:42 +00:00
|
|
|
#if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE)
|
2006-02-25 21:45:02 +00:00
|
|
|
num = printer_page_num;
|
2004-06-13 20:20:40 +00:00
|
|
|
#else
|
|
|
|
num = 0;
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_BUFNO:
|
|
|
|
num = wp->w_buffer->b_fnum;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_OFFSET_X:
|
|
|
|
base = 'X';
|
2019-11-30 20:52:27 +01:00
|
|
|
// FALLTHROUGH
|
2004-06-13 20:20:40 +00:00
|
|
|
case STL_OFFSET:
|
|
|
|
#ifdef FEAT_BYTEOFF
|
|
|
|
l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL);
|
2022-05-07 20:01:16 +01:00
|
|
|
num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0
|
|
|
|
? 0L : l + 1 + ((State & MODE_INSERT) == 0 && empty_line
|
|
|
|
? 0 : (int)wp->w_cursor.col);
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_BYTEVAL_X:
|
|
|
|
base = 'X';
|
2019-11-30 20:52:27 +01:00
|
|
|
// FALLTHROUGH
|
2004-06-13 20:20:40 +00:00
|
|
|
case STL_BYTEVAL:
|
2013-04-24 16:52:36 +02:00
|
|
|
num = byteval;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (num == NL)
|
|
|
|
num = 0;
|
|
|
|
else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC)
|
|
|
|
num = NL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_ROFLAG:
|
|
|
|
case STL_ROFLAG_ALT:
|
|
|
|
itemisflag = TRUE;
|
|
|
|
if (wp->w_buffer->b_p_ro)
|
2013-06-07 20:17:11 +02:00
|
|
|
str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]"));
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_HELPFLAG:
|
|
|
|
case STL_HELPFLAG_ALT:
|
|
|
|
itemisflag = TRUE;
|
|
|
|
if (wp->w_buffer->b_help)
|
|
|
|
str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP"
|
2006-03-26 21:06:50 +00:00
|
|
|
: _("[Help]"));
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_FILETYPE:
|
|
|
|
if (*wp->w_buffer->b_p_ft != NUL
|
|
|
|
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3)
|
|
|
|
{
|
2019-05-23 22:11:59 +02:00
|
|
|
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "[%s]",
|
2005-05-19 20:53:52 +00:00
|
|
|
wp->w_buffer->b_p_ft);
|
2019-05-23 22:11:59 +02:00
|
|
|
str = buf_tmp;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STL_FILETYPE_ALT:
|
|
|
|
itemisflag = TRUE;
|
|
|
|
if (*wp->w_buffer->b_p_ft != NUL
|
|
|
|
&& STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2)
|
|
|
|
{
|
2019-05-23 22:11:59 +02:00
|
|
|
vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), ",%s",
|
2005-05-19 20:53:52 +00:00
|
|
|
wp->w_buffer->b_p_ft);
|
2019-05-23 22:11:59 +02:00
|
|
|
for (t = buf_tmp; *t != 0; t++)
|
2004-06-13 20:20:40 +00:00
|
|
|
*t = TOUPPER_LOC(*t);
|
2019-05-23 22:11:59 +02:00
|
|
|
str = buf_tmp;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2017-09-16 20:54:51 +02:00
|
|
|
#if defined(FEAT_QUICKFIX)
|
2004-06-13 20:20:40 +00:00
|
|
|
case STL_PREVIEWFLAG:
|
|
|
|
case STL_PREVIEWFLAG_ALT:
|
|
|
|
itemisflag = TRUE;
|
|
|
|
if (wp->w_p_pvw)
|
|
|
|
str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV"
|
|
|
|
: _("[Preview]"));
|
|
|
|
break;
|
2010-07-25 16:58:46 +02:00
|
|
|
|
|
|
|
case STL_QUICKFIX:
|
|
|
|
if (bt_quickfix(wp->w_buffer))
|
|
|
|
str = (char_u *)(wp->w_llist_ref
|
|
|
|
? _(msg_loclist)
|
|
|
|
: _(msg_qflist));
|
|
|
|
break;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
case STL_MODIFIED:
|
|
|
|
case STL_MODIFIED_ALT:
|
|
|
|
itemisflag = TRUE;
|
|
|
|
switch ((opt == STL_MODIFIED_ALT)
|
|
|
|
+ bufIsChanged(wp->w_buffer) * 2
|
|
|
|
+ (!wp->w_buffer->b_p_ma) * 4)
|
|
|
|
{
|
|
|
|
case 2: str = (char_u *)"[+]"; break;
|
|
|
|
case 3: str = (char_u *)",+"; break;
|
|
|
|
case 4: str = (char_u *)"[-]"; break;
|
|
|
|
case 5: str = (char_u *)",-"; break;
|
|
|
|
case 6: str = (char_u *)"[+-]"; break;
|
|
|
|
case 7: str = (char_u *)",+-"; break;
|
|
|
|
}
|
|
|
|
break;
|
2006-02-21 22:02:53 +00:00
|
|
|
|
|
|
|
case STL_HIGHLIGHT:
|
|
|
|
t = s;
|
|
|
|
while (*s != '#' && *s != NUL)
|
|
|
|
++s;
|
|
|
|
if (*s == '#')
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_type = Highlight;
|
|
|
|
stl_items[curitem].stl_start = p;
|
|
|
|
stl_items[curitem].stl_minwid = -syn_namen2id(t, (int)(s - t));
|
2006-02-21 22:02:53 +00:00
|
|
|
curitem++;
|
|
|
|
}
|
2013-11-02 04:39:38 +01:00
|
|
|
if (*s != NUL)
|
|
|
|
++s;
|
2006-02-21 22:02:53 +00:00
|
|
|
continue;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_start = p;
|
|
|
|
stl_items[curitem].stl_type = Normal;
|
2004-06-13 20:20:40 +00:00
|
|
|
if (str != NULL && *str)
|
|
|
|
{
|
|
|
|
t = str;
|
|
|
|
if (itemisflag)
|
|
|
|
{
|
|
|
|
if ((t[0] && t[1])
|
|
|
|
&& ((!prevchar_isitem && *t == ',')
|
|
|
|
|| (prevchar_isflag && *t == ' ')))
|
|
|
|
t++;
|
|
|
|
prevchar_isflag = TRUE;
|
|
|
|
}
|
|
|
|
l = vim_strsize(t);
|
|
|
|
if (l > 0)
|
|
|
|
prevchar_isitem = TRUE;
|
|
|
|
if (l > maxwid)
|
|
|
|
{
|
|
|
|
while (l >= maxwid)
|
|
|
|
if (has_mbyte)
|
|
|
|
{
|
|
|
|
l -= ptr2cells(t);
|
2005-08-10 21:07:57 +00:00
|
|
|
t += (*mb_ptr2len)(t);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
l -= byte2cells(*t++);
|
|
|
|
if (p + 1 >= out + outlen)
|
|
|
|
break;
|
|
|
|
*p++ = '<';
|
|
|
|
}
|
|
|
|
if (minwid > 0)
|
|
|
|
{
|
|
|
|
for (; l < minwid && p + 1 < out + outlen; l++)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Don't put a "-" in front of a digit.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (l + 1 == minwid && fillchar == '-' && VIM_ISDIGIT(*t))
|
|
|
|
*p++ = ' ';
|
|
|
|
else
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, p);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
minwid = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
minwid *= -1;
|
2021-03-04 21:55:58 +01:00
|
|
|
for (; *t && p + 1 < out + outlen; t++)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Change a space by fillchar, unless fillchar is '-' and a
|
|
|
|
// digit follows.
|
2021-03-04 21:55:58 +01:00
|
|
|
if (fillable && *t == ' '
|
|
|
|
&& (!VIM_ISDIGIT(*(t + 1)) || fillchar != '-'))
|
|
|
|
MB_CHAR2BYTES(fillchar, p);
|
|
|
|
else
|
|
|
|
*p++ = *t;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
for (; l < minwid && p + 1 < out + outlen; l++)
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, p);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else if (num >= 0)
|
|
|
|
{
|
|
|
|
int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16));
|
|
|
|
char_u nstr[20];
|
|
|
|
|
|
|
|
if (p + 20 >= out + outlen)
|
2019-11-30 20:52:27 +01:00
|
|
|
break; // not sufficient space
|
2004-06-13 20:20:40 +00:00
|
|
|
prevchar_isitem = TRUE;
|
|
|
|
t = nstr;
|
|
|
|
if (opt == STL_VIRTCOL_ALT)
|
|
|
|
{
|
|
|
|
*t++ = '-';
|
|
|
|
minwid--;
|
|
|
|
}
|
|
|
|
*t++ = '%';
|
|
|
|
if (zeropad)
|
|
|
|
*t++ = '0';
|
|
|
|
*t++ = '*';
|
2009-05-13 10:51:08 +00:00
|
|
|
*t++ = nbase == 16 ? base : (char_u)(nbase == 8 ? 'o' : 'd');
|
2004-06-13 20:20:40 +00:00
|
|
|
*t = 0;
|
|
|
|
|
|
|
|
for (n = num, l = 1; n >= nbase; n /= nbase)
|
|
|
|
l++;
|
|
|
|
if (opt == STL_VIRTCOL_ALT)
|
|
|
|
l++;
|
|
|
|
if (l > maxwid)
|
|
|
|
{
|
|
|
|
l += 2;
|
|
|
|
n = l - maxwid;
|
|
|
|
while (l-- > maxwid)
|
|
|
|
num /= nbase;
|
|
|
|
*t++ = '>';
|
|
|
|
*t++ = '%';
|
|
|
|
*t = t[-3];
|
|
|
|
*++t = 0;
|
2005-05-19 20:53:52 +00:00
|
|
|
vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
|
|
|
|
0, num, n);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
2005-05-19 20:53:52 +00:00
|
|
|
vim_snprintf((char *)p, outlen - (p - out), (char *)nstr,
|
|
|
|
minwid, num);
|
2004-06-13 20:20:40 +00:00
|
|
|
p += STRLEN(p);
|
|
|
|
}
|
|
|
|
else
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[curitem].stl_type = Empty;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2022-08-22 15:19:16 +01:00
|
|
|
if (num >= 0 || (!itemisflag && str != NULL && *str != NUL))
|
|
|
|
prevchar_isflag = FALSE; // Item not NULL, but not a flag
|
|
|
|
//
|
2004-06-13 20:20:40 +00:00
|
|
|
if (opt == STL_VIM_EXPR)
|
|
|
|
vim_free(str);
|
|
|
|
curitem++;
|
|
|
|
}
|
|
|
|
*p = NUL;
|
|
|
|
itemcnt = curitem;
|
|
|
|
|
2006-02-21 22:02:53 +00:00
|
|
|
#ifdef FEAT_EVAL
|
|
|
|
if (usefmt != fmt)
|
|
|
|
vim_free(usefmt);
|
|
|
|
#endif
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
width = vim_strsize(out);
|
|
|
|
if (maxwidth > 0 && width > maxwidth)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Result is too long, must truncate somewhere.
|
2004-06-13 20:20:40 +00:00
|
|
|
l = 0;
|
|
|
|
if (itemcnt == 0)
|
|
|
|
s = out;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for ( ; l < itemcnt; l++)
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_type == Trunc)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Truncate at %< item.
|
2020-10-26 21:05:27 +01:00
|
|
|
s = stl_items[l].stl_start;
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (l == itemcnt)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// No %< item, truncate first item.
|
2020-10-26 21:05:27 +01:00
|
|
|
s = stl_items[0].stl_start;
|
2004-06-13 20:20:40 +00:00
|
|
|
l = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (width - vim_strsize(s) >= maxwidth)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Truncation mark is beyond max length
|
2004-06-13 20:20:40 +00:00
|
|
|
if (has_mbyte)
|
|
|
|
{
|
|
|
|
s = out;
|
|
|
|
width = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
width += ptr2cells(s);
|
|
|
|
if (width >= maxwidth)
|
|
|
|
break;
|
2005-08-10 21:07:57 +00:00
|
|
|
s += (*mb_ptr2len)(s);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
// Fill up for half a double-wide character.
|
2004-06-13 20:20:40 +00:00
|
|
|
while (++width < maxwidth)
|
2021-03-04 21:55:58 +01:00
|
|
|
MB_CHAR2BYTES(fillchar, s);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
s = out + maxwidth - 1;
|
|
|
|
for (l = 0; l < itemcnt; l++)
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_start > s)
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
itemcnt = l;
|
|
|
|
*s++ = '>';
|
|
|
|
*s = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (has_mbyte)
|
|
|
|
{
|
|
|
|
n = 0;
|
|
|
|
while (width >= maxwidth)
|
|
|
|
{
|
|
|
|
width -= ptr2cells(s + n);
|
2005-08-10 21:07:57 +00:00
|
|
|
n += (*mb_ptr2len)(s + n);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
n = width - maxwidth + 1;
|
|
|
|
p = s + n;
|
2008-06-24 20:19:36 +00:00
|
|
|
STRMOVE(s + 1, p);
|
2004-06-13 20:20:40 +00:00
|
|
|
*s = '<';
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
--n; // count the '<'
|
2004-06-13 20:20:40 +00:00
|
|
|
for (; l < itemcnt; l++)
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_start - n >= s)
|
|
|
|
stl_items[l].stl_start -= n;
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2020-10-26 21:05:27 +01:00
|
|
|
stl_items[l].stl_start = s;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2023-07-01 20:24:40 +01:00
|
|
|
|
|
|
|
// Fill up for half a double-wide character.
|
|
|
|
while (++width < maxwidth)
|
|
|
|
{
|
|
|
|
s = s + STRLEN(s);
|
|
|
|
MB_CHAR2BYTES(fillchar, s);
|
|
|
|
*s = NUL;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
width = maxwidth;
|
|
|
|
}
|
|
|
|
else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen)
|
|
|
|
{
|
2023-02-11 11:15:25 +00:00
|
|
|
// Find how many separators there are, which we will use when
|
|
|
|
// figuring out how many groups there are.
|
|
|
|
int num_separators = 0;
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
for (l = 0; l < itemcnt; l++)
|
|
|
|
{
|
2023-02-11 11:15:25 +00:00
|
|
|
if (stl_items[l].stl_type == Separate)
|
|
|
|
{
|
|
|
|
// Create an array of the start location for each separator
|
|
|
|
// mark.
|
|
|
|
stl_separator_locations[num_separators] = l;
|
|
|
|
num_separators++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we have separated groups, then we deal with it now
|
|
|
|
if (num_separators)
|
|
|
|
{
|
|
|
|
int standard_spaces;
|
|
|
|
int final_spaces;
|
|
|
|
|
|
|
|
standard_spaces = (maxwidth - width) / num_separators;
|
|
|
|
final_spaces = (maxwidth - width) -
|
|
|
|
standard_spaces * (num_separators - 1);
|
|
|
|
for (l = 0; l < num_separators; l++)
|
|
|
|
{
|
|
|
|
int dislocation = (l == (num_separators - 1)) ?
|
|
|
|
final_spaces : standard_spaces;
|
|
|
|
dislocation *= MB_CHAR2LEN(fillchar);
|
|
|
|
char_u *start = stl_items[stl_separator_locations[l]].stl_start;
|
|
|
|
char_u *seploc = start + dislocation;
|
|
|
|
STRMOVE(seploc, start);
|
|
|
|
for (s = start; s < seploc;)
|
|
|
|
MB_CHAR2BYTES(fillchar, s);
|
|
|
|
|
|
|
|
for (int i = stl_separator_locations[l] + 1; i < itemcnt; i++)
|
|
|
|
stl_items[i].stl_start += dislocation;
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
width = maxwidth;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Store the info about highlighting.
|
2006-02-22 21:25:37 +00:00
|
|
|
if (hltab != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
*hltab = stl_hltab;
|
|
|
|
sp = stl_hltab;
|
2004-06-13 20:20:40 +00:00
|
|
|
for (l = 0; l < itemcnt; l++)
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_type == Highlight)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
sp->start = stl_items[l].stl_start;
|
|
|
|
sp->userhl = stl_items[l].stl_minwid;
|
2006-02-22 21:25:37 +00:00
|
|
|
sp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sp->start = NULL;
|
|
|
|
sp->userhl = 0;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Store the info about tab pages labels.
|
2006-02-22 21:25:37 +00:00
|
|
|
if (tabtab != NULL)
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
*tabtab = stl_tabtab;
|
|
|
|
sp = stl_tabtab;
|
2006-02-22 21:25:37 +00:00
|
|
|
for (l = 0; l < itemcnt; l++)
|
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
if (stl_items[l].stl_type == TabPage)
|
2006-02-22 21:25:37 +00:00
|
|
|
{
|
2020-10-26 21:05:27 +01:00
|
|
|
sp->start = stl_items[l].stl_start;
|
|
|
|
sp->userhl = stl_items[l].stl_minwid;
|
2006-02-22 21:25:37 +00:00
|
|
|
sp++;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-22 21:25:37 +00:00
|
|
|
sp->start = NULL;
|
|
|
|
sp->userhl = 0;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2022-08-22 15:19:16 +01:00
|
|
|
redraw_not_allowed = save_redraw_not_allowed;
|
2017-09-08 13:59:21 +02:00
|
|
|
|
2022-02-15 16:17:44 +00:00
|
|
|
// A user function may reset KeyTyped, restore it.
|
|
|
|
KeyTyped = save_KeyTyped;
|
|
|
|
|
2022-11-07 12:16:51 +00:00
|
|
|
// Check for an error. If there is one the display will be messed up and
|
|
|
|
// might loop redrawing. Avoid that by making the corresponding option
|
|
|
|
// empty.
|
|
|
|
// TODO: find out why using called_emsg_before makes tests fail, does it
|
|
|
|
// matter?
|
|
|
|
// if (called_emsg > called_emsg_before)
|
|
|
|
if (did_emsg > did_emsg_before)
|
|
|
|
set_string_option_direct(opt_name, -1, (char_u *)"",
|
|
|
|
OPT_FREE | opt_scope, SID_ERROR);
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
return width;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
#endif // FEAT_STL_OPT
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
2023-04-29 12:09:53 +01:00
|
|
|
* Get relative cursor position in window into "buf[buflen]", in the localized
|
|
|
|
* percentage form like %99, 99%; using "Top", "Bot" or "All" when appropriate.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
get_rel_pos(
|
|
|
|
win_T *wp,
|
|
|
|
char_u *buf,
|
|
|
|
int buflen)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
long above; // number of lines above window
|
|
|
|
long below; // number of lines below window
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (buflen < 3) // need at least 3 chars for writing
|
2015-01-07 13:31:52 +01:00
|
|
|
return;
|
2004-06-13 20:20:40 +00:00
|
|
|
above = wp->w_topline - 1;
|
|
|
|
#ifdef FEAT_DIFF
|
|
|
|
above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill;
|
2015-08-04 17:43:25 +02:00
|
|
|
if (wp->w_topline == 1 && wp->w_topfill >= 1)
|
2019-11-30 20:52:27 +01:00
|
|
|
above = 0; // All buffer lines are displayed and there is an
|
|
|
|
// indication of filler lines, that can be considered
|
|
|
|
// seeing all lines.
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
|
|
|
below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1;
|
|
|
|
if (below <= 0)
|
2009-05-13 10:51:08 +00:00
|
|
|
vim_strncpy(buf, (char_u *)(above == 0 ? _("All") : _("Bot")),
|
2023-04-29 12:09:53 +01:00
|
|
|
(size_t)(buflen - 1));
|
2004-06-13 20:20:40 +00:00
|
|
|
else if (above <= 0)
|
2009-05-13 10:51:08 +00:00
|
|
|
vim_strncpy(buf, (char_u *)_("Top"), (size_t)(buflen - 1));
|
2004-06-13 20:20:40 +00:00
|
|
|
else
|
2023-04-29 12:09:53 +01:00
|
|
|
{
|
|
|
|
int perc = (above > 1000000L)
|
|
|
|
? (int)(above / ((above + below) / 100L))
|
|
|
|
: (int)(above * 100L / (above + below));
|
|
|
|
|
|
|
|
char *p = (char *)buf;
|
|
|
|
size_t l = buflen;
|
|
|
|
if (perc < 10)
|
|
|
|
{
|
|
|
|
// prepend one space
|
|
|
|
buf[0] = ' ';
|
|
|
|
++p;
|
|
|
|
--l;
|
|
|
|
}
|
|
|
|
// localized percentage value
|
|
|
|
vim_snprintf(p, l, _("%d%%"), perc);
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-05-13 10:51:08 +00:00
|
|
|
* Append (file 2 of 8) to "buf[buflen]", if editing more than one file.
|
2004-06-13 20:20:40 +00:00
|
|
|
* Return TRUE if it was appended.
|
|
|
|
*/
|
2009-05-13 10:51:08 +00:00
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
append_arg_number(
|
|
|
|
win_T *wp,
|
|
|
|
char_u *buf,
|
|
|
|
int buflen,
|
2019-11-30 20:52:27 +01:00
|
|
|
int add_file) // Add "file" before the arg number
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
if (ARGCOUNT <= 1) // nothing to do
|
2004-06-13 20:20:40 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2023-05-23 18:00:58 +01:00
|
|
|
char *msg;
|
|
|
|
switch ((wp->w_arg_idx_invalid ? 1 : 0) + (add_file ? 2 : 0))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2023-05-23 18:00:58 +01:00
|
|
|
case 0: msg = _(" (%d of %d)"); break;
|
|
|
|
case 1: msg = _(" ((%d) of %d)"); break;
|
|
|
|
case 2: msg = _(" (file %d of %d)"); break;
|
|
|
|
case 3: msg = _(" (file (%d) of %d)"); break;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2023-05-23 18:00:58 +01:00
|
|
|
|
|
|
|
char_u *p = buf + STRLEN(buf); // go to the end of the buffer
|
|
|
|
vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), msg,
|
|
|
|
wp->w_arg_idx + 1, ARGCOUNT);
|
2004-06-13 20:20:40 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If fname is not a full path, make it a full path.
|
|
|
|
* Returns pointer to allocated memory (NULL for failure).
|
|
|
|
*/
|
|
|
|
char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
fix_fname(char_u *fname)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Force expanding the path always for Unix, because symbolic links may
|
|
|
|
* mess up the full path name, even though it starts with a '/'.
|
|
|
|
* Also expand when there is ".." in the file name, try to remove it,
|
|
|
|
* because "c:/src/../README" is equal to "c:/README".
|
2007-10-03 12:31:33 +00:00
|
|
|
* Similarly "c:/src//file" is equal to "c:/src/file".
|
2004-06-13 20:20:40 +00:00
|
|
|
* For MS-Windows also expand names like "longna~1" to "longname".
|
|
|
|
*/
|
2007-03-06 19:22:53 +00:00
|
|
|
#ifdef UNIX
|
2004-06-13 20:20:40 +00:00
|
|
|
return FullName_save(fname, TRUE);
|
|
|
|
#else
|
2007-10-03 12:31:33 +00:00
|
|
|
if (!vim_isAbsName(fname)
|
|
|
|
|| strstr((char *)fname, "..") != NULL
|
|
|
|
|| strstr((char *)fname, "//") != NULL
|
|
|
|
# ifdef BACKSLASH_IN_FILENAME
|
|
|
|
|| strstr((char *)fname, "\\\\") != NULL
|
|
|
|
# endif
|
2016-02-23 14:53:34 +01:00
|
|
|
# if defined(MSWIN)
|
2004-06-13 20:20:40 +00:00
|
|
|
|| vim_strchr(fname, '~') != NULL
|
2007-10-03 12:31:33 +00:00
|
|
|
# endif
|
2004-06-13 20:20:40 +00:00
|
|
|
)
|
|
|
|
return FullName_save(fname, FALSE);
|
|
|
|
|
|
|
|
fname = vim_strsave(fname);
|
|
|
|
|
2007-10-03 12:31:33 +00:00
|
|
|
# ifdef USE_FNAME_CASE
|
2019-02-12 22:37:27 +01:00
|
|
|
if (fname != NULL)
|
2019-11-30 20:52:27 +01:00
|
|
|
fname_case(fname, 0); // set correct case for file name
|
2007-10-03 12:31:33 +00:00
|
|
|
# endif
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
return fname;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-10-11 19:27:47 +02:00
|
|
|
* Make "*ffname" a full file name, set "*sfname" to "*ffname" if not NULL.
|
|
|
|
* "*ffname" becomes a pointer to allocated memory (or NULL).
|
|
|
|
* When resolving a link both "*sfname" and "*ffname" will point to the same
|
|
|
|
* allocated memory.
|
|
|
|
* The "*ffname" and "*sfname" pointer values on call will not be freed.
|
2019-11-02 22:54:41 +01:00
|
|
|
* Note that the resulting "*ffname" pointer should be considered not allocated.
|
2004-06-13 20:20:40 +00:00
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
fname_expand(
|
|
|
|
buf_T *buf UNUSED,
|
|
|
|
char_u **ffname,
|
|
|
|
char_u **sfname)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-10-11 19:27:47 +02:00
|
|
|
if (*ffname == NULL) // no file name given, nothing to do
|
2004-06-13 20:20:40 +00:00
|
|
|
return;
|
2018-10-11 19:27:47 +02:00
|
|
|
if (*sfname == NULL) // no short file name given, use ffname
|
2004-06-13 20:20:40 +00:00
|
|
|
*sfname = *ffname;
|
2018-10-11 19:27:47 +02:00
|
|
|
*ffname = fix_fname(*ffname); // expand to full path
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
#ifdef FEAT_SHORTCUT
|
|
|
|
if (!buf->b_p_bin)
|
|
|
|
{
|
2006-04-27 00:02:13 +00:00
|
|
|
char_u *rfname;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2018-10-11 19:27:47 +02:00
|
|
|
// If the file name is a shortcut file, use the file it links to.
|
2019-02-10 23:18:53 +01:00
|
|
|
rfname = mch_resolve_path(*ffname, FALSE);
|
2006-04-27 00:02:13 +00:00
|
|
|
if (rfname != NULL)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
vim_free(*ffname);
|
|
|
|
*ffname = rfname;
|
|
|
|
*sfname = rfname;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open a window for a number of buffers.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
ex_buffer_all(exarg_T *eap)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *buf;
|
|
|
|
win_T *wp, *wpnext;
|
|
|
|
int split_ret = OK;
|
|
|
|
int p_ea_save;
|
|
|
|
int open_wins = 0;
|
|
|
|
int r;
|
2019-11-30 20:52:27 +01:00
|
|
|
int count; // Maximum number of windows to open.
|
|
|
|
int all; // When TRUE also load inactive buffers.
|
2020-10-24 20:49:43 +02:00
|
|
|
int had_tab = cmdmod.cmod_tab;
|
2006-03-01 22:01:55 +00:00
|
|
|
tabpage_T *tpnext;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (eap->addr_count == 0) // make as many windows as possible
|
2004-06-13 20:20:40 +00:00
|
|
|
count = 9999;
|
|
|
|
else
|
2019-11-30 20:52:27 +01:00
|
|
|
count = eap->line2; // make as many windows as specified
|
2004-06-13 20:20:40 +00:00
|
|
|
if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide)
|
|
|
|
all = FALSE;
|
|
|
|
else
|
|
|
|
all = TRUE;
|
|
|
|
|
2023-02-20 14:35:20 +00:00
|
|
|
// Stop Visual mode, the cursor and "VIsual" may very well be invalid after
|
|
|
|
// switching to another buffer.
|
|
|
|
reset_VIsual_and_resel();
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
setpcmark();
|
|
|
|
|
|
|
|
#ifdef FEAT_GUI
|
|
|
|
need_mouse_correct = TRUE;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close superfluous windows (two windows for the same buffer).
|
|
|
|
* Also close windows that are not full-width.
|
|
|
|
*/
|
2006-03-01 22:01:55 +00:00
|
|
|
if (had_tab > 0)
|
2013-05-06 04:50:35 +02:00
|
|
|
goto_tabpage_tp(first_tabpage, TRUE, TRUE);
|
2006-03-01 22:01:55 +00:00
|
|
|
for (;;)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2006-03-01 22:01:55 +00:00
|
|
|
tpnext = curtab->tp_next;
|
|
|
|
for (wp = firstwin; wp != NULL; wp = wpnext)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2006-03-01 22:01:55 +00:00
|
|
|
wpnext = wp->w_next;
|
2006-03-03 22:50:42 +00:00
|
|
|
if ((wp->w_buffer->b_nwindows > 1
|
2022-03-22 18:13:01 +00:00
|
|
|
|| ((cmdmod.cmod_split & WSP_VERT)
|
|
|
|
? wp->w_height + wp->w_status_height < Rows - p_ch
|
|
|
|
- tabline_height()
|
|
|
|
: wp->w_width != Columns)
|
|
|
|
|| (had_tab > 0 && wp != firstwin))
|
|
|
|
&& !ONE_WINDOW
|
|
|
|
&& !(wp->w_closing || wp->w_buffer->b_locked > 0)
|
|
|
|
&& !win_unlisted(wp))
|
2006-03-01 22:01:55 +00:00
|
|
|
{
|
2022-03-22 18:13:01 +00:00
|
|
|
if (win_close(wp, FALSE) == FAIL)
|
|
|
|
break;
|
|
|
|
// Just in case an autocommand does something strange with
|
|
|
|
// windows: start all over...
|
|
|
|
wpnext = firstwin;
|
|
|
|
tpnext = first_tabpage;
|
2006-03-01 22:01:55 +00:00
|
|
|
open_wins = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
++open_wins;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2006-03-01 22:01:55 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Without the ":tab" modifier only do the current tab page.
|
2006-03-01 22:01:55 +00:00
|
|
|
if (had_tab == 0 || tpnext == NULL)
|
|
|
|
break;
|
2013-05-06 04:50:35 +02:00
|
|
|
goto_tabpage_tp(tpnext, TRUE, TRUE);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through the buffer list. When a buffer doesn't have a window yet,
|
|
|
|
* open one. Otherwise move the window to the right position.
|
|
|
|
* Watch out for autocommands that delete buffers or windows!
|
|
|
|
*/
|
2019-11-30 20:52:27 +01:00
|
|
|
// Don't execute Win/Buf Enter/Leave autocommands here.
|
2004-06-13 20:20:40 +00:00
|
|
|
++autocmd_no_enter;
|
|
|
|
win_enter(lastwin, FALSE);
|
|
|
|
++autocmd_no_leave;
|
|
|
|
for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Check if this buffer needs a window
|
2004-06-13 20:20:40 +00:00
|
|
|
if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl)
|
|
|
|
continue;
|
|
|
|
|
2006-03-02 22:40:52 +00:00
|
|
|
if (had_tab != 0)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// With the ":tab" modifier don't move the window.
|
2006-03-02 22:40:52 +00:00
|
|
|
if (buf->b_nwindows > 0)
|
2019-11-30 20:52:27 +01:00
|
|
|
wp = lastwin; // buffer has a window, skip it
|
2006-03-02 22:40:52 +00:00
|
|
|
else
|
|
|
|
wp = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// Check if this buffer already has a window
|
2016-07-24 22:04:11 +02:00
|
|
|
FOR_ALL_WINDOWS(wp)
|
2006-03-02 22:40:52 +00:00
|
|
|
if (wp->w_buffer == buf)
|
|
|
|
break;
|
2019-11-30 20:52:27 +01:00
|
|
|
// If the buffer already has a window, move it
|
2006-03-02 22:40:52 +00:00
|
|
|
if (wp != NULL)
|
|
|
|
win_move_after(wp, curwin);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wp == NULL && split_ret == OK)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2016-07-10 18:21:50 +02:00
|
|
|
bufref_T bufref;
|
|
|
|
|
|
|
|
set_bufref(&bufref, buf);
|
2018-03-04 18:08:14 +01:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Split the window and put the buffer in it
|
2004-06-13 20:20:40 +00:00
|
|
|
p_ea_save = p_ea;
|
2019-11-30 20:52:27 +01:00
|
|
|
p_ea = TRUE; // use space from all windows
|
2004-06-13 20:20:40 +00:00
|
|
|
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
|
|
|
|
++open_wins;
|
|
|
|
p_ea = p_ea_save;
|
|
|
|
if (split_ret == FAIL)
|
|
|
|
continue;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Open the buffer in this window.
|
2004-06-13 20:20:40 +00:00
|
|
|
swap_exists_action = SEA_DIALOG;
|
|
|
|
set_curbuf(buf, DOBUF_GOTO);
|
2016-07-10 18:21:50 +02:00
|
|
|
if (!bufref_valid(&bufref))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// autocommands deleted the buffer!!!
|
2004-06-13 20:20:40 +00:00
|
|
|
swap_exists_action = SEA_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (swap_exists_action == SEA_QUIT)
|
|
|
|
{
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2004-09-13 20:26:32 +00:00
|
|
|
cleanup_T cs;
|
2004-07-26 12:53:41 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Reset the error/interrupt/exception state here so that
|
|
|
|
// aborting() returns FALSE when closing a window.
|
2004-09-13 20:26:32 +00:00
|
|
|
enter_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-07-26 12:53:41 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// User selected Quit at ATTENTION prompt; close this window.
|
2004-06-13 20:20:40 +00:00
|
|
|
win_close(curwin, TRUE);
|
|
|
|
--open_wins;
|
|
|
|
swap_exists_action = SEA_NONE;
|
2005-12-16 21:49:31 +00:00
|
|
|
swap_exists_did_quit = TRUE;
|
2004-09-13 20:26:32 +00:00
|
|
|
|
2019-04-28 22:25:38 +02:00
|
|
|
#if defined(FEAT_EVAL)
|
2019-11-30 20:52:27 +01:00
|
|
|
// Restore the error/interrupt/exception state if not
|
|
|
|
// discarded by a new aborting error, interrupt, or uncaught
|
|
|
|
// exception.
|
2004-09-13 20:26:32 +00:00
|
|
|
leave_cleanup(&cs);
|
2019-04-28 22:25:38 +02:00
|
|
|
#endif
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
handle_swap_exists(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
ui_breakcheck();
|
|
|
|
if (got_int)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
(void)vgetc(); // only break the file loading, not the rest
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
2004-07-26 12:53:41 +00:00
|
|
|
#ifdef FEAT_EVAL
|
2019-11-30 20:52:27 +01:00
|
|
|
// Autocommands deleted the buffer or aborted script processing!!!
|
2004-07-26 12:53:41 +00:00
|
|
|
if (aborting())
|
|
|
|
break;
|
2006-03-01 22:01:55 +00:00
|
|
|
#endif
|
2019-11-30 20:52:27 +01:00
|
|
|
// When ":tab" was used open a new tab for a new window repeatedly.
|
2006-03-01 22:01:55 +00:00
|
|
|
if (had_tab > 0 && tabpage_index(NULL) <= p_tpm)
|
2020-10-24 20:49:43 +02:00
|
|
|
cmdmod.cmod_tab = 9999;
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
--autocmd_no_enter;
|
2019-11-30 20:52:27 +01:00
|
|
|
win_enter(firstwin, FALSE); // back to first window
|
2004-06-13 20:20:40 +00:00
|
|
|
--autocmd_no_leave;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close superfluous windows.
|
|
|
|
*/
|
|
|
|
for (wp = lastwin; open_wins > count; )
|
|
|
|
{
|
2017-08-03 22:44:55 +02:00
|
|
|
r = (buf_hide(wp->w_buffer) || !bufIsChanged(wp->w_buffer)
|
2004-06-13 20:20:40 +00:00
|
|
|
|| autowrite(wp->w_buffer, FALSE) == OK);
|
|
|
|
if (!win_valid(wp))
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// BufWrite Autocommands made the window invalid, start over
|
2004-06-13 20:20:40 +00:00
|
|
|
wp = lastwin;
|
|
|
|
}
|
2018-03-04 18:08:14 +01:00
|
|
|
else if (r)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2017-08-03 22:44:55 +02:00
|
|
|
win_close(wp, !buf_hide(wp->w_buffer));
|
2004-06-13 20:20:40 +00:00
|
|
|
--open_wins;
|
|
|
|
wp = lastwin;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wp = wp->w_prev;
|
|
|
|
if (wp == NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-29 22:03:47 +01:00
|
|
|
static int chk_modeline(linenr_T, int);
|
2006-03-08 21:32:40 +00:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* do_modelines() - process mode lines for the current file
|
|
|
|
*
|
2006-03-08 21:32:40 +00:00
|
|
|
* "flags" can be:
|
|
|
|
* OPT_WINONLY only set options local to window
|
|
|
|
* OPT_NOWIN don't set options local to window
|
|
|
|
*
|
2004-06-13 20:20:40 +00:00
|
|
|
* Returns immediately if the "ml" option isn't set.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
do_modelines(int flags)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2004-09-06 17:44:46 +00:00
|
|
|
linenr_T lnum;
|
|
|
|
int nmlines;
|
|
|
|
static int entered = 0;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0)
|
|
|
|
return;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Disallow recursive entry here. Can happen when executing a modeline
|
|
|
|
// triggers an autocommand, which reloads modelines with a ":do".
|
2004-06-13 20:20:40 +00:00
|
|
|
if (entered)
|
|
|
|
return;
|
|
|
|
|
|
|
|
++entered;
|
2021-08-28 20:42:50 +02:00
|
|
|
for (lnum = 1; curbuf->b_p_ml && lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines;
|
2004-06-13 20:20:40 +00:00
|
|
|
++lnum)
|
2006-03-08 21:32:40 +00:00
|
|
|
if (chk_modeline(lnum, flags) == FAIL)
|
2004-06-13 20:20:40 +00:00
|
|
|
nmlines = 0;
|
|
|
|
|
2021-08-28 20:42:50 +02:00
|
|
|
for (lnum = curbuf->b_ml.ml_line_count; curbuf->b_p_ml && lnum > 0 && lnum > nmlines
|
2004-06-13 20:20:40 +00:00
|
|
|
&& lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum)
|
2006-03-08 21:32:40 +00:00
|
|
|
if (chk_modeline(lnum, flags) == FAIL)
|
2004-06-13 20:20:40 +00:00
|
|
|
nmlines = 0;
|
|
|
|
--entered;
|
|
|
|
}
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
#include "version.h" // for version number
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* chk_modeline() - check a single line for a mode string
|
|
|
|
* Return FAIL if an error encountered.
|
|
|
|
*/
|
|
|
|
static int
|
2016-01-30 15:14:10 +01:00
|
|
|
chk_modeline(
|
|
|
|
linenr_T lnum,
|
2019-11-30 20:52:27 +01:00
|
|
|
int flags) // Same as for do_modelines().
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
char_u *s;
|
|
|
|
char_u *e;
|
2019-11-30 20:52:27 +01:00
|
|
|
char_u *linecopy; // local copy of any modeline found
|
2004-06-13 20:20:40 +00:00
|
|
|
int prev;
|
|
|
|
int vers;
|
|
|
|
int end;
|
|
|
|
int retval = OK;
|
2018-09-10 21:05:02 +02:00
|
|
|
sctx_T save_current_sctx;
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_DECLARATION;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
prev = -1;
|
|
|
|
for (s = ml_get(lnum); *s != NUL; ++s)
|
|
|
|
{
|
|
|
|
if (prev == -1 || vim_isspace(prev))
|
|
|
|
{
|
|
|
|
if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0)
|
|
|
|
|| STRNCMP(s, "vi:", (size_t)3) == 0)
|
|
|
|
break;
|
2019-11-30 20:52:27 +01:00
|
|
|
// Accept both "vim" and "Vim".
|
2013-06-26 20:04:35 +02:00
|
|
|
if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm')
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (s[3] == '<' || s[3] == '=' || s[3] == '>')
|
|
|
|
e = s + 4;
|
|
|
|
else
|
|
|
|
e = s + 3;
|
|
|
|
vers = getdigits(&e);
|
|
|
|
if (*e == ':'
|
2013-06-29 15:07:22 +02:00
|
|
|
&& (s[0] != 'V'
|
|
|
|
|| STRNCMP(skipwhite(e + 1), "set", 3) == 0)
|
2004-06-13 20:20:40 +00:00
|
|
|
&& (s[3] == ':'
|
2024-01-04 21:19:04 +01:00
|
|
|
|| (VIM_VERSION_100 >= vers && SAFE_isdigit(s[3]))
|
2004-06-13 20:20:40 +00:00
|
|
|
|| (VIM_VERSION_100 < vers && s[3] == '<')
|
|
|
|
|| (VIM_VERSION_100 > vers && s[3] == '>')
|
|
|
|
|| (VIM_VERSION_100 == vers && s[3] == '=')))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
prev = *s;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*s)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
do // skip over "ex:", "vi:" or "vim:"
|
2004-06-13 20:20:40 +00:00
|
|
|
++s;
|
|
|
|
while (s[-1] != ':');
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
s = linecopy = vim_strsave(s); // copy the line, it will change
|
2004-06-13 20:20:40 +00:00
|
|
|
if (linecopy == NULL)
|
|
|
|
return FAIL;
|
|
|
|
|
2019-12-29 23:04:25 +01:00
|
|
|
// prepare for emsg()
|
|
|
|
estack_push(ETYPE_MODELINE, (char_u *)"modelines", lnum);
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_SETUP;
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
end = FALSE;
|
|
|
|
while (end == FALSE)
|
|
|
|
{
|
|
|
|
s = skipwhite(s);
|
|
|
|
if (*s == NUL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find end of set command: ':' or end of line.
|
|
|
|
* Skip over "\:", replacing it with ":".
|
|
|
|
*/
|
|
|
|
for (e = s; *e != ':' && *e != NUL; ++e)
|
|
|
|
if (e[0] == '\\' && e[1] == ':')
|
2008-06-24 20:19:36 +00:00
|
|
|
STRMOVE(e, e + 1);
|
2004-06-13 20:20:40 +00:00
|
|
|
if (*e == NUL)
|
|
|
|
end = TRUE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is a "set" command, require a terminating ':' and
|
|
|
|
* ignore the stuff after the ':'.
|
|
|
|
* "vi:set opt opt opt: foo" -- foo not interpreted
|
|
|
|
* "vi:opt opt opt: foo" -- foo interpreted
|
|
|
|
* Accept "se" for compatibility with Elvis.
|
|
|
|
*/
|
|
|
|
if (STRNCMP(s, "set ", (size_t)4) == 0
|
|
|
|
|| STRNCMP(s, "se ", (size_t)3) == 0)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
if (*e != ':') // no terminating ':'?
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
end = TRUE;
|
|
|
|
s = vim_strchr(s, ' ') + 1;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
*e = NUL; // truncate the set command
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (*s != NUL) // skip over an empty "::"
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2018-12-21 13:03:28 +01:00
|
|
|
int secure_save = secure;
|
2020-12-28 18:26:00 +01:00
|
|
|
|
2018-09-10 21:05:02 +02:00
|
|
|
save_current_sctx = current_sctx;
|
2020-12-28 18:26:00 +01:00
|
|
|
current_sctx.sc_version = 1;
|
|
|
|
#ifdef FEAT_EVAL
|
2018-09-10 21:05:02 +02:00
|
|
|
current_sctx.sc_sid = SID_MODELINE;
|
2018-11-10 17:33:29 +01:00
|
|
|
current_sctx.sc_seq = 0;
|
2019-12-29 23:04:25 +01:00
|
|
|
current_sctx.sc_lnum = lnum;
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2020-12-28 18:26:00 +01:00
|
|
|
|
2018-11-20 04:25:21 +01:00
|
|
|
// Make sure no risky things are executed as a side effect.
|
2019-03-24 14:02:04 +01:00
|
|
|
secure = 1;
|
2018-11-20 04:25:21 +01:00
|
|
|
|
2006-03-08 21:32:40 +00:00
|
|
|
retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
|
2018-11-20 04:25:21 +01:00
|
|
|
|
2018-12-21 13:03:28 +01:00
|
|
|
secure = secure_save;
|
2018-09-10 21:05:02 +02:00
|
|
|
current_sctx = save_current_sctx;
|
2019-11-30 20:52:27 +01:00
|
|
|
if (retval == FAIL) // stop if error found
|
2004-06-13 20:20:40 +00:00
|
|
|
break;
|
|
|
|
}
|
2019-11-30 20:52:27 +01:00
|
|
|
s = e + 1; // advance to next part
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
2023-04-15 13:17:50 +01:00
|
|
|
ESTACK_CHECK_NOW;
|
2019-12-29 23:04:25 +01:00
|
|
|
estack_pop();
|
2004-06-13 20:20:40 +00:00
|
|
|
vim_free(linecopy);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2018-08-01 17:53:12 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a normal buffer, 'buftype' is empty.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_normal(buf_T *buf)
|
|
|
|
{
|
|
|
|
return buf != NULL && buf->b_p_bt[0] == NUL;
|
|
|
|
}
|
|
|
|
|
2017-07-25 23:31:12 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is the quickfix buffer.
|
|
|
|
*/
|
|
|
|
int
|
2022-08-25 16:16:45 +01:00
|
|
|
bt_quickfix(buf_T *buf UNUSED)
|
2017-07-25 23:31:12 +02:00
|
|
|
{
|
2022-08-25 15:11:15 +01:00
|
|
|
#ifdef FEAT_QUICKFIX
|
2023-09-03 21:43:46 +02:00
|
|
|
return buf != NULL && buf_valid(buf) && buf->b_p_bt[0] == 'q';
|
2022-08-25 15:11:15 +01:00
|
|
|
#else
|
|
|
|
return FALSE;
|
2019-01-20 15:30:40 +01:00
|
|
|
#endif
|
2022-08-25 15:11:15 +01:00
|
|
|
}
|
2017-07-25 23:31:12 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a terminal buffer.
|
|
|
|
*/
|
|
|
|
int
|
2022-08-25 16:16:45 +01:00
|
|
|
bt_terminal(buf_T *buf UNUSED)
|
2017-07-25 23:31:12 +02:00
|
|
|
{
|
2022-08-25 15:11:15 +01:00
|
|
|
#if defined(FEAT_TERMINAL)
|
2017-07-25 23:31:12 +02:00
|
|
|
return buf != NULL && buf->b_p_bt[0] == 't';
|
2022-08-25 15:11:15 +01:00
|
|
|
#else
|
|
|
|
return FALSE;
|
2019-01-20 15:30:40 +01:00
|
|
|
#endif
|
2022-08-25 15:11:15 +01:00
|
|
|
}
|
2017-07-25 23:31:12 +02:00
|
|
|
|
2017-07-27 22:03:50 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a help buffer.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_help(buf_T *buf)
|
|
|
|
{
|
|
|
|
return buf != NULL && buf->b_help;
|
|
|
|
}
|
|
|
|
|
2018-06-03 14:47:35 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a prompt buffer.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_prompt(buf_T *buf)
|
|
|
|
{
|
2019-05-25 19:51:39 +02:00
|
|
|
return buf != NULL && buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'r';
|
|
|
|
}
|
|
|
|
|
2022-01-08 12:41:16 +00:00
|
|
|
#if defined(FEAT_PROP_POPUP) || defined(PROTO)
|
2019-05-25 19:51:39 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a buffer for a popup window.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_popup(buf_T *buf)
|
|
|
|
{
|
|
|
|
return buf != NULL && buf->b_p_bt != NULL
|
|
|
|
&& buf->b_p_bt[0] == 'p' && buf->b_p_bt[1] == 'o';
|
2018-06-03 14:47:35 +02:00
|
|
|
}
|
2022-01-08 12:41:16 +00:00
|
|
|
#endif
|
2018-06-03 14:47:35 +02:00
|
|
|
|
2017-07-25 23:31:12 +02:00
|
|
|
/*
|
2018-06-23 19:23:02 +02:00
|
|
|
* Return TRUE if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
|
2022-08-26 12:58:17 +01:00
|
|
|
* buffer. This means the buffer name may not be a file name, at least not for
|
|
|
|
* writing the buffer.
|
2017-07-25 23:31:12 +02:00
|
|
|
*/
|
|
|
|
int
|
2019-06-15 19:37:15 +02:00
|
|
|
bt_nofilename(buf_T *buf)
|
2017-07-25 23:31:12 +02:00
|
|
|
{
|
|
|
|
return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
|
|
|
|
|| buf->b_p_bt[0] == 'a'
|
2018-06-03 14:47:35 +02:00
|
|
|
|| buf->b_p_bt[0] == 't'
|
|
|
|
|| buf->b_p_bt[0] == 'p');
|
2017-07-25 23:31:12 +02:00
|
|
|
}
|
|
|
|
|
2022-08-26 12:58:17 +01:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" is a "nofile", "quickfix", "terminal" or "prompt"
|
|
|
|
* buffer. This means the buffer is not to be read from a file.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
bt_nofileread(buf_T *buf)
|
|
|
|
{
|
|
|
|
return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f')
|
|
|
|
|| buf->b_p_bt[0] == 't'
|
|
|
|
|| buf->b_p_bt[0] == 'q'
|
|
|
|
|| buf->b_p_bt[0] == 'p');
|
|
|
|
}
|
|
|
|
|
2022-01-08 12:41:16 +00:00
|
|
|
#if defined(FEAT_QUICKFIX) || defined(PROTO)
|
2019-06-15 19:37:15 +02:00
|
|
|
/*
|
|
|
|
* Return TRUE if "buf" has 'buftype' set to "nofile".
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_nofile(buf_T *buf)
|
|
|
|
{
|
|
|
|
return buf != NULL && buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f';
|
|
|
|
}
|
2022-01-08 12:41:16 +00:00
|
|
|
#endif
|
2019-06-15 19:37:15 +02:00
|
|
|
|
2017-07-25 23:31:12 +02:00
|
|
|
/*
|
2022-10-09 18:53:32 +01:00
|
|
|
* Return TRUE if "buf" is a "nowrite", "nofile", "terminal", "prompt", or
|
|
|
|
* "popup" buffer.
|
2017-07-25 23:31:12 +02:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
bt_dontwrite(buf_T *buf)
|
|
|
|
{
|
2018-06-03 14:47:35 +02:00
|
|
|
return buf != NULL && (buf->b_p_bt[0] == 'n'
|
2020-10-26 21:05:27 +01:00
|
|
|
|| buf->b_p_bt[0] == 't'
|
|
|
|
|| buf->b_p_bt[0] == 'p');
|
2017-07-25 23:31:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
bt_dontwrite_msg(buf_T *buf)
|
|
|
|
{
|
|
|
|
if (bt_dontwrite(buf))
|
|
|
|
{
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_cannot_write_buftype_option_is_set));
|
2017-07-25 23:31:12 +02:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if the buffer should be hidden, according to 'hidden', ":hide"
|
|
|
|
* and 'bufhidden'.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
buf_hide(buf_T *buf)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// 'bufhidden' overrules 'hidden' and ":hide", check it first
|
2017-07-25 23:31:12 +02:00
|
|
|
switch (buf->b_p_bh[0])
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
case 'u': // "unload"
|
|
|
|
case 'w': // "wipe"
|
|
|
|
case 'd': return FALSE; // "delete"
|
|
|
|
case 'h': return TRUE; // "hide"
|
2017-07-25 23:31:12 +02:00
|
|
|
}
|
2020-10-24 20:49:43 +02:00
|
|
|
return (p_hid || (cmdmod.cmod_flags & CMOD_HIDE));
|
2017-07-25 23:31:12 +02:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return special buffer name.
|
|
|
|
* Returns NULL when the buffer has a normal file name.
|
|
|
|
*/
|
2012-10-03 18:25:00 +02:00
|
|
|
char_u *
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_spname(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2017-09-16 20:54:51 +02:00
|
|
|
#if defined(FEAT_QUICKFIX)
|
2004-06-13 20:20:40 +00:00
|
|
|
if (bt_quickfix(buf))
|
2006-01-25 22:02:51 +00:00
|
|
|
{
|
|
|
|
/*
|
2019-02-05 21:23:04 +01:00
|
|
|
* Differentiate between the quickfix and location list buffers using
|
|
|
|
* the buffer number stored in the global quickfix stack.
|
2006-01-25 22:02:51 +00:00
|
|
|
*/
|
2019-02-05 21:23:04 +01:00
|
|
|
if (buf->b_fnum == qf_stack_get_bufnr())
|
2012-10-03 18:25:00 +02:00
|
|
|
return (char_u *)_(msg_qflist);
|
2019-02-05 21:23:04 +01:00
|
|
|
else
|
|
|
|
return (char_u *)_(msg_loclist);
|
2006-01-25 22:02:51 +00:00
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
#endif
|
2017-07-24 21:44:43 +02:00
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// There is no _file_ when 'buftype' is "nofile", b_sfname
|
|
|
|
// contains the name as specified by the user.
|
2019-06-15 19:37:15 +02:00
|
|
|
if (bt_nofilename(buf))
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2017-07-24 21:44:43 +02:00
|
|
|
#ifdef FEAT_TERMINAL
|
|
|
|
if (buf->b_term != NULL)
|
|
|
|
return term_get_status_text(buf->b_term);
|
|
|
|
#endif
|
2017-08-29 22:44:59 +02:00
|
|
|
if (buf->b_fname != NULL)
|
|
|
|
return buf->b_fname;
|
2023-08-16 17:15:05 +01:00
|
|
|
if (buf == cmdwin_buf)
|
|
|
|
return (char_u *)_("[Command Line]");
|
2018-06-06 18:02:39 +02:00
|
|
|
#ifdef FEAT_JOB_CHANNEL
|
|
|
|
if (bt_prompt(buf))
|
|
|
|
return (char_u *)_("[Prompt]");
|
2019-05-30 22:32:34 +02:00
|
|
|
#endif
|
2019-11-30 22:48:27 +01:00
|
|
|
#ifdef FEAT_PROP_POPUP
|
2019-05-30 22:32:34 +02:00
|
|
|
if (bt_popup(buf))
|
|
|
|
return (char_u *)_("[Popup]");
|
2018-06-06 18:02:39 +02:00
|
|
|
#endif
|
2012-10-03 18:25:00 +02:00
|
|
|
return (char_u *)_("[Scratch]");
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
2017-07-24 21:44:43 +02:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_fname == NULL)
|
2020-11-05 19:36:38 +01:00
|
|
|
return buf_get_fname(buf);
|
2004-06-13 20:20:40 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-11-05 19:36:38 +01:00
|
|
|
/*
|
|
|
|
* Get "buf->b_fname", use "[No Name]" if it is NULL.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
buf_get_fname(buf_T *buf)
|
|
|
|
{
|
|
|
|
if (buf->b_fname == NULL)
|
|
|
|
return (char_u *)_("[No Name]");
|
|
|
|
return buf->b_fname;
|
|
|
|
}
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
/*
|
|
|
|
* Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
set_buflisted(int on)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
if (on == curbuf->b_p_bl)
|
|
|
|
return;
|
|
|
|
|
|
|
|
curbuf->b_p_bl = on;
|
|
|
|
if (on)
|
|
|
|
apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf);
|
|
|
|
else
|
|
|
|
apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the file for "buf" again and check if the contents changed.
|
|
|
|
* Return TRUE if it changed or this could not be checked.
|
|
|
|
*/
|
|
|
|
int
|
2016-01-30 15:14:10 +01:00
|
|
|
buf_contents_changed(buf_T *buf)
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
buf_T *newbuf;
|
|
|
|
int differ = TRUE;
|
|
|
|
linenr_T lnum;
|
|
|
|
aco_save_T aco;
|
|
|
|
exarg_T ea;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Allocate a buffer without putting it in the buffer list.
|
2004-06-13 20:20:40 +00:00
|
|
|
newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY);
|
|
|
|
if (newbuf == NULL)
|
|
|
|
return TRUE;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// Force the 'fileencoding' and 'fileformat' to be equal.
|
2004-06-13 20:20:40 +00:00
|
|
|
if (prep_exarg(&ea, buf) == FAIL)
|
|
|
|
{
|
|
|
|
wipe_buffer(newbuf, FALSE);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2022-11-28 18:51:43 +00:00
|
|
|
// Set curwin/curbuf to buf and save a few things.
|
2004-06-13 20:20:40 +00:00
|
|
|
aucmd_prepbuf(&aco, newbuf);
|
2022-11-28 18:51:43 +00:00
|
|
|
if (curbuf != newbuf)
|
|
|
|
{
|
|
|
|
// Failed to find a window for "newbuf".
|
|
|
|
wipe_buffer(newbuf, FALSE);
|
|
|
|
return TRUE;
|
|
|
|
}
|
2004-06-13 20:20:40 +00:00
|
|
|
|
2023-10-11 21:08:13 +02:00
|
|
|
// We don't want to trigger autocommands now, they may have nasty
|
|
|
|
// side-effects like wiping buffers
|
|
|
|
block_autocmds();
|
2006-01-12 23:22:24 +00:00
|
|
|
if (ml_open(curbuf) == OK
|
2004-06-13 20:20:40 +00:00
|
|
|
&& readfile(buf->b_ffname, buf->b_fname,
|
|
|
|
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM,
|
|
|
|
&ea, READ_NEW | READ_DUMMY) == OK)
|
|
|
|
{
|
2019-11-30 20:52:27 +01:00
|
|
|
// compare the two files line by line
|
2004-06-13 20:20:40 +00:00
|
|
|
if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count)
|
|
|
|
{
|
|
|
|
differ = FALSE;
|
|
|
|
for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum)
|
|
|
|
if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0)
|
|
|
|
{
|
|
|
|
differ = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
vim_free(ea.cmd);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
// restore curwin/curbuf and a few other things
|
2004-06-13 20:20:40 +00:00
|
|
|
aucmd_restbuf(&aco);
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (curbuf != newbuf) // safety check
|
2004-06-13 20:20:40 +00:00
|
|
|
wipe_buffer(newbuf, FALSE);
|
|
|
|
|
2023-10-11 21:08:13 +02:00
|
|
|
unblock_autocmds();
|
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
return differ;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Wipe out a buffer and decrement the last buffer number if it was used for
|
|
|
|
* this buffer. Call this to wipe out a temp buffer that does not contain any
|
|
|
|
* marks.
|
|
|
|
*/
|
|
|
|
void
|
2016-01-30 15:14:10 +01:00
|
|
|
wipe_buffer(
|
|
|
|
buf_T *buf,
|
2019-12-14 16:18:15 +01:00
|
|
|
int aucmd) // When TRUE trigger autocommands.
|
2004-06-13 20:20:40 +00:00
|
|
|
{
|
|
|
|
if (buf->b_fnum == top_file_num - 1)
|
|
|
|
--top_file_num;
|
|
|
|
|
2019-11-30 20:52:27 +01:00
|
|
|
if (!aucmd) // Don't trigger BufDelete autocommands here.
|
2007-09-29 12:16:41 +00:00
|
|
|
block_autocmds();
|
2018-03-04 18:08:14 +01:00
|
|
|
|
2019-12-14 16:18:15 +01:00
|
|
|
close_buffer(NULL, buf, DOBUF_WIPE, FALSE, TRUE);
|
2018-03-04 18:08:14 +01:00
|
|
|
|
2004-06-13 20:20:40 +00:00
|
|
|
if (!aucmd)
|
2007-09-29 12:16:41 +00:00
|
|
|
unblock_autocmds();
|
2004-06-13 20:20:40 +00:00
|
|
|
}
|