aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/buffer.c')
-rw-r--r--src/nvim/buffer.c1506
1 files changed, 26 insertions, 1480 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index f23a1caf8b..ab5b32bf3e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -25,17 +25,21 @@
#include <string.h>
#include "nvim/api/private/helpers.h"
+#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/assert.h"
+#include "nvim/autocmd.h"
#include "nvim/buffer.h"
#include "nvim/buffer_updates.h"
#include "nvim/change.h"
#include "nvim/channel.h"
#include "nvim/charset.h"
+#include "nvim/cmdexpand.h"
#include "nvim/cursor.h"
#include "nvim/decoration.h"
#include "nvim/diff.h"
#include "nvim/digraph.h"
+#include "nvim/drawscreen.h"
#include "nvim/eval.h"
#include "nvim/eval/vars.h"
#include "nvim/ex_cmds.h"
@@ -50,6 +54,7 @@
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/hashtab.h"
+#include "nvim/help.h"
#include "nvim/highlight_group.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -62,6 +67,7 @@
#include "nvim/message.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/optionstr.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/time.h"
@@ -69,9 +75,10 @@
#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
-#include "nvim/screen.h"
+#include "nvim/runtime.h"
#include "nvim/sign.h"
#include "nvim/spell.h"
+#include "nvim/statusline.h"
#include "nvim/strings.h"
#include "nvim/syntax.h"
#include "nvim/ui.h"
@@ -85,11 +92,6 @@
# include "buffer.c.generated.h"
#endif
-// Determines how deeply nested %{} blocks will be evaluated in statusline.
-#define MAX_STL_EVAL_DEPTH 100
-
-static char *msg_loclist = N_("[Location List]");
-static char *msg_qflist = N_("[Quickfix List]");
static char *e_auabort = N_("E855: Autocommands caused command to abort");
static char *e_buflocked = N_("E937: Attempt to delete a buffer that is in use");
@@ -793,8 +795,8 @@ static void free_buffer(buf_T *buf)
if (autocmd_busy) {
// Do not free the buffer structure while autocommands are executing,
// it's still needed. Free it when autocmd_busy is reset.
- memset(&buf->b_namedm[0], 0, sizeof(buf->b_namedm));
- memset(&buf->b_changelist[0], 0, sizeof(buf->b_changelist));
+ CLEAR_FIELD(buf->b_namedm);
+ CLEAR_FIELD(buf->b_changelist);
buf->b_next = au_pending_free_buf;
au_pending_free_buf = buf;
} else {
@@ -1532,6 +1534,15 @@ void set_curbuf(buf_T *buf, int action)
/// be pointing to freed memory.
void enter_buffer(buf_T *buf)
{
+ // when closing the current buffer stop Visual mode
+ if (VIsual_active
+#if defined(EXITFREE)
+ && !entered_free_all_mem
+#endif
+ ) {
+ end_visual_mode();
+ }
+
// Get the buffer in the current window.
curwin->w_buffer = buf;
curbuf = buf;
@@ -2595,7 +2606,7 @@ void get_winopts(buf_T *buf)
}
if (curwin->w_float_config.style == kWinStyleMinimal) {
- didset_window_options(curwin);
+ didset_window_options(curwin, false);
win_set_minimal_style(curwin);
}
@@ -2603,7 +2614,7 @@ void get_winopts(buf_T *buf)
if (p_fdls >= 0) {
curwin->w_p_fdl = p_fdls;
}
- didset_window_options(curwin);
+ didset_window_options(curwin, false);
}
/// Find the mark for the buffer 'buf' for the current window.
@@ -3346,1192 +3357,6 @@ void free_titles(void)
#endif
-/// Enumeration specifying the valid numeric bases that can
-/// be used when printing numbers in the status line.
-typedef enum {
- kNumBaseDecimal = 10,
- kNumBaseHexadecimal = 16,
-} NumberBase;
-
-/// Build a string from the status line items in "fmt".
-/// Return length of string in screen cells.
-///
-/// Normally works for window "wp", except when working for 'tabline' then it
-/// is "curwin".
-///
-/// Items are drawn interspersed with the text that surrounds it
-/// Specials: %-<wid>(xxx%) => group, %= => separation marker, %< => truncation
-/// 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.
-///
-/// @param wp The window to build a statusline for
-/// @param out The output buffer to write the statusline to
-/// Note: This should not be NameBuff
-/// @param outlen The length of the output buffer
-/// @param fmt The statusline format string
-/// @param use_sandbox Use a sandboxed environment when evaluating fmt
-/// @param fillchar Character to use when filling empty space in the statusline
-/// @param maxwidth The maximum width to make the statusline
-/// @param hltab HL attributes (can be NULL)
-/// @param tabtab Tab clicks definition (can be NULL).
-///
-/// @return The final width of the statusline
-int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, int use_sandbox, int fillchar,
- int maxwidth, stl_hlrec_t **hltab, StlClickRecord **tabtab)
-{
- static size_t stl_items_len = 20; // Initial value, grows as needed.
- static stl_item_t *stl_items = NULL;
- static int *stl_groupitems = NULL;
- static stl_hlrec_t *stl_hltab = NULL;
- static StlClickRecord *stl_tabtab = NULL;
- static int *stl_separator_locations = NULL;
-
-#define TMPLEN 70
- char buf_tmp[TMPLEN];
- char win_tmp[TMPLEN];
- char *usefmt = fmt;
- const int save_must_redraw = must_redraw;
- const int save_redr_type = curwin->w_redr_type;
-
- if (stl_items == NULL) {
- stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len);
- stl_groupitems = xmalloc(sizeof(int) * stl_items_len);
-
- // Allocate one more, because the last element is used to indicate the
- // end of the list.
- stl_hltab = xmalloc(sizeof(stl_hlrec_t) * (stl_items_len + 1));
- stl_tabtab = xmalloc(sizeof(StlClickRecord) * (stl_items_len + 1));
-
- stl_separator_locations = xmalloc(sizeof(int) * stl_items_len);
- }
-
- // When the format starts with "%!" then evaluate it as an expression and
- // use the result as the actual format string.
- if (fmt[0] == '%' && fmt[1] == '!') {
- typval_T tv = {
- .v_type = VAR_NUMBER,
- .vval.v_number = wp->handle,
- };
- set_var(S_LEN("g:statusline_winid"), &tv, false);
-
- usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox);
- if (usefmt == NULL) {
- usefmt = fmt;
- }
-
- do_unlet(S_LEN("g:statusline_winid"), true);
- }
-
- if (fillchar == 0) {
- fillchar = ' ';
- }
-
- // The cursor in windows other than the current one isn't always
- // up-to-date, esp. because of autocommands and timers.
- linenr_T 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").
- const char *line_ptr = (char *)ml_get_buf(wp->w_buffer, lnum, false);
- bool empty_line = (*line_ptr == NUL);
-
- // Get the byte value now, in case we need it below. This is more
- // efficient than making a copy of the line.
- int byteval;
- const size_t len = STRLEN(line_ptr);
- 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;
- byteval = 0;
- } else {
- byteval = utf_ptr2char(line_ptr + wp->w_cursor.col);
- }
-
- int groupdepth = 0;
- int evaldepth = 0;
-
- int curitem = 0;
- bool prevchar_isflag = true;
- bool prevchar_isitem = false;
-
- // out_p is the current position in the output buffer
- char *out_p = out;
-
- // out_end_p is the last valid character in the output buffer
- // Note: The null termination character must occur here or earlier,
- // so any user-visible characters must occur before here.
- char *out_end_p = (out + outlen) - 1;
-
- // Proceed character by character through the statusline format string
- // fmt_p is the current position in the input buffer
- for (char *fmt_p = usefmt; *fmt_p != NUL;) {
- if (curitem == (int)stl_items_len) {
- size_t new_len = stl_items_len * 3 / 2;
-
- stl_items = xrealloc(stl_items, sizeof(stl_item_t) * new_len);
- stl_groupitems = xrealloc(stl_groupitems, sizeof(int) * new_len);
- stl_hltab = xrealloc(stl_hltab, sizeof(stl_hlrec_t) * (new_len + 1));
- stl_tabtab = xrealloc(stl_tabtab, sizeof(StlClickRecord) * (new_len + 1));
- stl_separator_locations =
- xrealloc(stl_separator_locations, sizeof(int) * new_len);
-
- stl_items_len = new_len;
- }
-
- if (*fmt_p != '%') {
- prevchar_isflag = prevchar_isitem = false;
- }
-
- // Copy the formatting verbatim until we reach the end of the string
- // or find a formatting item (denoted by `%`)
- // or run out of room in our output buffer.
- while (*fmt_p != NUL && *fmt_p != '%' && out_p < out_end_p) {
- *out_p++ = *fmt_p++;
- }
-
- // If we have processed the entire format string or run out of
- // room in our output buffer, exit the loop.
- if (*fmt_p == NUL || out_p >= out_end_p) {
- break;
- }
-
- // The rest of this loop will handle a single `%` item.
- // Note: We increment here to skip over the `%` character we are currently
- // on so we can process the item's contents.
- fmt_p++;
-
- // Ignore `%` at the end of the format string
- if (*fmt_p == NUL) {
- break;
- }
-
- // Two `%` in a row is the escape sequence to print a
- // single `%` in the output buffer.
- if (*fmt_p == '%') {
- *out_p++ = *fmt_p++;
- prevchar_isflag = prevchar_isitem = false;
- continue;
- }
-
- // STL_SEPARATE: Separation place between left and right aligned items.
- if (*fmt_p == STL_SEPARATE) {
- fmt_p++;
- // Ignored when we are inside of a grouping
- if (groupdepth > 0) {
- continue;
- }
- stl_items[curitem].type = Separate;
- stl_items[curitem++].start = out_p;
- continue;
- }
-
- // STL_TRUNCMARK: Where to begin truncating if the statusline is too long.
- if (*fmt_p == STL_TRUNCMARK) {
- fmt_p++;
- stl_items[curitem].type = Trunc;
- stl_items[curitem++].start = out_p;
- continue;
- }
-
- // The end of a grouping
- if (*fmt_p == ')') {
- fmt_p++;
- // Ignore if we are not actually inside a group currently
- if (groupdepth < 1) {
- continue;
- }
- groupdepth--;
-
- // Determine how long the group is.
- // Note: We set the current output position to null
- // so `vim_strsize` will work.
- char *t = stl_items[stl_groupitems[groupdepth]].start;
- *out_p = NUL;
- long group_len = vim_strsize(t);
-
- // If the group contained internal items
- // and the group did not have a minimum width,
- // and if there were no normal items in the group,
- // move the output pointer back to where the group started.
- // Note: This erases any non-item characters that were in the group.
- // Otherwise there would be no reason to do this step.
- if (curitem > stl_groupitems[groupdepth] + 1
- && stl_items[stl_groupitems[groupdepth]].minwid == 0) {
- // remove group if all items are empty and highlight group
- // doesn't change
- int group_start_userhl = 0;
- int group_end_userhl = 0;
- int n;
- for (n = stl_groupitems[groupdepth] - 1; n >= 0; n--) {
- if (stl_items[n].type == Highlight) {
- group_start_userhl = group_end_userhl = stl_items[n].minwid;
- break;
- }
- }
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- if (stl_items[n].type == Normal) {
- break;
- }
- if (stl_items[n].type == Highlight) {
- group_end_userhl = stl_items[n].minwid;
- }
- }
- if (n == curitem && group_start_userhl == group_end_userhl) {
- // empty group
- out_p = t;
- group_len = 0;
- for (n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- // do not use the highlighting from the removed group
- if (stl_items[n].type == Highlight) {
- stl_items[n].type = Empty;
- }
- // adjust the start position of TabPage to the next
- // item position
- if (stl_items[n].type == TabPage) {
- stl_items[n].start = out_p;
- }
- }
- }
- }
-
- // If the group is longer than it is allowed to be
- // truncate by removing bytes from the start of the group text.
- if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid) {
- // { Determine the number of bytes to remove
-
- // Find the first character that should be included.
- long n = 0;
- while (group_len >= stl_items[stl_groupitems[groupdepth]].maxwid) {
- group_len -= ptr2cells(t + n);
- n += utfc_ptr2len(t + n);
- }
- // }
-
- // Prepend the `<` to indicate that the output was truncated.
- *t = '<';
-
- // { Move the truncated output
- memmove(t + 1, t + n, (size_t)(out_p - (t + n)));
- out_p = out_p - n + 1;
- // Fill up space left over by half a double-wide char.
- while (++group_len < stl_items[stl_groupitems[groupdepth]].minwid) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // }
-
- // correct the start of the items for the truncation
- for (int idx = stl_groupitems[groupdepth] + 1; idx < curitem; idx++) {
- // Shift everything back by the number of removed bytes
- // Minus one for the leading '<' added above.
- stl_items[idx].start -= n - 1;
-
- // If the item was partially or completely truncated, set its
- // start to the start of the group
- if (stl_items[idx].start < t) {
- stl_items[idx].start = t;
- }
- }
- // If the group is shorter than the minimum width, add padding characters.
- } else if (abs(stl_items[stl_groupitems[groupdepth]].minwid) > group_len) {
- long min_group_width = stl_items[stl_groupitems[groupdepth]].minwid;
- // If the group is left-aligned, add characters to the right.
- if (min_group_width < 0) {
- min_group_width = 0 - min_group_width;
- while (group_len++ < min_group_width && out_p < out_end_p) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- // If the group is right-aligned, shift everything to the right and
- // prepend with filler characters.
- } else {
- // { Move the group to the right
- group_len = (min_group_width - group_len) * utf_char2len(fillchar);
- memmove(t + group_len, t, (size_t)(out_p - t));
- if (out_p + group_len >= (out_end_p + 1)) {
- group_len = (long)(out_end_p - out_p);
- }
- out_p += group_len;
- // }
-
- // Adjust item start positions
- for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
- stl_items[n].start += group_len;
- }
-
- // Prepend the fill characters
- for (; group_len > 0; group_len--) {
- MB_CHAR2BYTES(fillchar, t);
- }
- }
- }
- continue;
- }
- int minwid = 0;
- int maxwid = 9999;
- bool left_align = false;
-
- // Denotes that numbers should be left-padded with zeros
- bool zeropad = (*fmt_p == '0');
- if (zeropad) {
- fmt_p++;
- }
-
- // Denotes that the item should be left-aligned.
- // This is tracked by using a negative length.
- if (*fmt_p == '-') {
- fmt_p++;
- left_align = true;
- }
-
- // The first digit group is the item's min width
- if (ascii_isdigit(*fmt_p)) {
- minwid = getdigits_int(&fmt_p, false, 0);
- }
-
- // User highlight groups override the min width field
- // to denote the styling to use.
- if (*fmt_p == STL_USER_HL) {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid > 9 ? 1 : minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // TABPAGE pairs are used to denote a region that when clicked will
- // either switch to or close a tab.
- //
- // Ex: tabline=%0Ttab\ zero%X
- // This tabline has a TABPAGENR item with minwid `0`,
- // which is then closed with a TABCLOSENR item.
- // Clicking on this region with mouse enabled will switch to tab 0.
- // Setting the minwid to a different value will switch
- // to that tab, if it exists
- //
- // Ex: tabline=%1Xtab\ one%X
- // This tabline has a TABCLOSENR item with minwid `1`,
- // which is then closed with a TABCLOSENR item.
- // Clicking on this region with mouse enabled will close tab 0.
- // This is determined by the following formula:
- // tab to close = (1 - minwid)
- // This is because for TABPAGENR we use `minwid` = `tab number`.
- // For TABCLOSENR we store the tab number as a negative value.
- // Because 0 is a valid TABPAGENR value, we have to
- // start our numbering at `-1`.
- // So, `-1` corresponds to us wanting to close tab `0`
- //
- // Note: These options are only valid when creating a tabline.
- if (*fmt_p == STL_TABPAGENR || *fmt_p == STL_TABCLOSENR) {
- if (*fmt_p == STL_TABCLOSENR) {
- if (minwid == 0) {
- // %X ends the close label, go back to the previous tab label nr.
- for (long n = curitem - 1; n >= 0; n--) {
- if (stl_items[n].type == TabPage && stl_items[n].minwid >= 0) {
- minwid = stl_items[n].minwid;
- break;
- }
- }
- } else {
- // close nrs are stored as negative values
- minwid = -minwid;
- }
- }
- stl_items[curitem].type = TabPage;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- if (*fmt_p == STL_CLICK_FUNC) {
- fmt_p++;
- char *t = fmt_p;
- while (*fmt_p != STL_CLICK_FUNC && *fmt_p) {
- fmt_p++;
- }
- if (*fmt_p != STL_CLICK_FUNC) {
- break;
- }
- stl_items[curitem].type = ClickFunc;
- stl_items[curitem].start = out_p;
- stl_items[curitem].cmd = xmemdupz(t, (size_t)(fmt_p - t));
- stl_items[curitem].minwid = minwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // Denotes the end of the minwid
- // the maxwid may follow immediately after
- if (*fmt_p == '.') {
- fmt_p++;
- if (ascii_isdigit(*fmt_p)) {
- maxwid = getdigits_int(&fmt_p, false, 50);
- }
- }
-
- // Bound the minimum width at 50.
- // Make the number negative to denote left alignment of the item
- minwid = (minwid > 50 ? 50 : minwid) * (left_align ? -1 : 1);
-
- // Denotes the start of a new group
- if (*fmt_p == '(') {
- stl_groupitems[groupdepth++] = curitem;
- stl_items[curitem].type = Group;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = minwid;
- stl_items[curitem].maxwid = maxwid;
- fmt_p++;
- curitem++;
- continue;
- }
-
- // Denotes end of expanded %{} block
- if (*fmt_p == '}' && evaldepth > 0) {
- fmt_p++;
- evaldepth--;
- continue;
- }
-
- // An invalid item was specified.
- // Continue processing on the next character of the format string.
- if (vim_strchr(STL_ALL, *fmt_p) == NULL) {
- fmt_p++;
- continue;
- }
-
- // The status line item type
- char opt = *fmt_p++;
-
- // OK - now for the real work
- NumberBase base = kNumBaseDecimal;
- bool itemisflag = false;
- bool fillable = true;
- long num = -1;
- char *str = NULL;
- switch (opt) {
- case STL_FILEPATH:
- case STL_FULLPATH:
- case STL_FILENAME:
- // Set fillable to false so that ' ' in the filename will not
- // get replaced with the fillchar
- fillable = false;
- if (buf_spname(wp->w_buffer) != NULL) {
- STRLCPY(NameBuff, buf_spname(wp->w_buffer), MAXPATHL);
- } else {
- char *t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname
- : wp->w_buffer->b_fname;
- home_replace(wp->w_buffer, t, (char *)NameBuff, MAXPATHL, true);
- }
- trans_characters((char *)NameBuff, MAXPATHL);
- if (opt != STL_FILENAME) {
- str = (char *)NameBuff;
- } else {
- str = path_tail((char *)NameBuff);
- }
- break;
- case STL_VIM_EXPR: // '{'
- {
- char *block_start = fmt_p - 1;
- int reevaluate = (*fmt_p == '%');
- itemisflag = true;
-
- if (reevaluate) {
- fmt_p++;
- }
-
- // Attempt to copy the expression to evaluate into
- // the output buffer as a null-terminated string.
- char *t = out_p;
- while ((*fmt_p != '}' || (reevaluate && fmt_p[-1] != '%'))
- && *fmt_p != NUL && out_p < out_end_p) {
- *out_p++ = *fmt_p++;
- }
- if (*fmt_p != '}') { // missing '}' or out of space
- break;
- }
- fmt_p++;
- if (reevaluate) {
- out_p[-1] = 0; // remove the % at the end of %{% expr %}
- } else {
- *out_p = 0;
- }
-
- // Move our position in the output buffer
- // to the beginning of the expression
- out_p = t;
-
- // { Evaluate the expression
-
- // Store the current buffer number as a string variable
- vim_snprintf(buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum);
- set_internal_string_var("g:actual_curbuf", buf_tmp);
- vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle);
- set_internal_string_var("g:actual_curwin", (char *)win_tmp);
-
- buf_T *const save_curbuf = curbuf;
- win_T *const save_curwin = curwin;
- const int save_VIsual_active = VIsual_active;
- curwin = wp;
- curbuf = wp->w_buffer;
- // Visual mode is only valid in the current window.
- if (curwin != save_curwin) {
- VIsual_active = false;
- }
-
- // Note: The result stored in `t` is unused.
- str = eval_to_string_safe(out_p, &t, use_sandbox);
-
- curwin = save_curwin;
- curbuf = save_curbuf;
- VIsual_active = save_VIsual_active;
-
- // Remove the variable we just stored
- do_unlet(S_LEN("g:actual_curbuf"), true);
- do_unlet(S_LEN("g:actual_curwin"), true);
-
- // }
-
- // Check if the evaluated result is a number.
- // If so, convert the number to an int and free the string.
- if (str != NULL && *str != 0) {
- if (*skipdigits(str) == NUL) {
- num = atoi(str);
- XFREE_CLEAR(str);
- itemisflag = false;
- }
- }
-
- // 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(str);
- size_t fmt_length = STRLEN(fmt_p);
- size_t new_fmt_len = parsed_usefmt + str_length + fmt_length + 3;
- char *new_fmt = xmalloc(new_fmt_len * sizeof(char));
- char *new_fmt_p = new_fmt;
-
- new_fmt_p = (char *)memcpy(new_fmt_p, usefmt, parsed_usefmt) + parsed_usefmt;
- new_fmt_p = (char *)memcpy(new_fmt_p, str, str_length) + str_length;
- new_fmt_p = (char *)memcpy(new_fmt_p, "%}", 2) + 2;
- new_fmt_p = (char *)memcpy(new_fmt_p, fmt_p, fmt_length) + fmt_length;
- *new_fmt_p = 0;
- new_fmt_p = NULL;
-
- if (usefmt != fmt) {
- xfree(usefmt);
- }
- XFREE_CLEAR(str);
- usefmt = new_fmt;
- fmt_p = usefmt + parsed_usefmt;
- evaldepth++;
- continue;
- }
- break;
- }
-
- 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:
- num = (State & MODE_INSERT) == 0 && empty_line ? 0 : (int)wp->w_cursor.col + 1;
- break;
-
- case STL_VIRTCOL:
- case STL_VIRTCOL_ALT: {
- colnr_T virtcol = wp->w_virtcol + 1;
- // Don't display %V if it's the same as %c.
- if (opt == STL_VIRTCOL_ALT
- && (virtcol == (colnr_T)((State & MODE_INSERT) == 0 && empty_line
- ? 0 : (int)wp->w_cursor.col + 1))) {
- 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:
- // Store the position percentage in our temporary buffer.
- // Note: We cannot store the value in `num` because
- // `get_rel_pos` can return a named position. Ex: "Top"
- get_rel_pos(wp, buf_tmp, TMPLEN);
- str = buf_tmp;
- break;
-
- case STL_ARGLISTSTAT:
- fillable = false;
-
- // Note: This is important because `append_arg_number` starts appending
- // at the end of the null-terminated string.
- // Setting the first byte to null means it will place the argument
- // number string at the beginning of the buffer.
- buf_tmp[0] = 0;
-
- // Note: The call will only return true if it actually
- // appended data to the `buf_tmp` buffer.
- if (append_arg_number(wp, buf_tmp, (int)sizeof(buf_tmp), false)) {
- str = buf_tmp;
- }
- break;
-
- case STL_KEYMAP:
- fillable = false;
- if (get_keymap_str(wp, "<%s>", buf_tmp, TMPLEN)) {
- str = buf_tmp;
- }
- break;
- case STL_PAGENUM:
- num = printer_page_num;
- break;
-
- case STL_BUFNO:
- num = wp->w_buffer->b_fnum;
- break;
-
- case STL_OFFSET_X:
- base = kNumBaseHexadecimal;
- FALLTHROUGH;
- case STL_OFFSET: {
- long l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL,
- false);
- 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);
- break;
- }
- case STL_BYTEVAL_X:
- base = kNumBaseHexadecimal;
- FALLTHROUGH;
- case STL_BYTEVAL:
- num = byteval;
- 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) {
- str = (opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]");
- }
- break;
-
- case STL_HELPFLAG:
- case STL_HELPFLAG_ALT:
- itemisflag = true;
- if (wp->w_buffer->b_help) {
- str = (opt == STL_HELPFLAG_ALT) ? ",HLP" : _("[Help]");
- }
- break;
-
- case STL_FILETYPE:
- // Copy the filetype if it is not null and the formatted string will fit
- // in the temporary buffer
- // (including the brackets and null terminating character)
- if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) {
- vim_snprintf(buf_tmp, sizeof(buf_tmp), "[%s]",
- wp->w_buffer->b_p_ft);
- str = buf_tmp;
- }
- break;
-
- case STL_FILETYPE_ALT:
- itemisflag = true;
- // Copy the filetype if it is not null and the formatted string will fit
- // in the temporary buffer
- // (including the comma and null terminating character)
- if (*wp->w_buffer->b_p_ft != NUL
- && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) {
- vim_snprintf(buf_tmp, sizeof(buf_tmp), ",%s", wp->w_buffer->b_p_ft);
- // Uppercase the file extension
- for (char *t = buf_tmp; *t != 0; t++) {
- *t = (char)TOUPPER_LOC(*t);
- }
- str = buf_tmp;
- }
- break;
- case STL_PREVIEWFLAG:
- case STL_PREVIEWFLAG_ALT:
- itemisflag = true;
- if (wp->w_p_pvw) {
- str = (opt == STL_PREVIEWFLAG_ALT) ? ",PRV" : _("[Preview]");
- }
- break;
-
- case STL_QUICKFIX:
- if (bt_quickfix(wp->w_buffer)) {
- str = wp->w_llist_ref ? _(msg_loclist) : _(msg_qflist);
- }
- break;
-
- case STL_MODIFIED:
- case STL_MODIFIED_ALT:
- itemisflag = true;
- switch ((opt == STL_MODIFIED_ALT)
- + bufIsChanged(wp->w_buffer) * 2
- + (!MODIFIABLE(wp->w_buffer)) * 4) {
- case 2:
- str = "[+]"; break;
- case 3:
- str = ",+"; break;
- case 4:
- str = "[-]"; break;
- case 5:
- str = ",-"; break;
- case 6:
- str = "[+-]"; break;
- case 7:
- str = ",+-"; break;
- }
- break;
-
- case STL_HIGHLIGHT: {
- // { The name of the highlight is surrounded by `#`
- char *t = fmt_p;
- while (*fmt_p != '#' && *fmt_p != NUL) {
- fmt_p++;
- }
- // }
-
- // Create a highlight item based on the name
- if (*fmt_p == '#') {
- stl_items[curitem].type = Highlight;
- stl_items[curitem].start = out_p;
- stl_items[curitem].minwid = -syn_name2id_len(t, (size_t)(fmt_p - t));
- curitem++;
- fmt_p++;
- }
- continue;
- }
- }
-
- // If we made it this far, the item is normal and starts at
- // our current position in the output buffer.
- // Non-normal items would have `continued`.
- stl_items[curitem].start = out_p;
- stl_items[curitem].type = Normal;
-
- // Copy the item string into the output buffer
- if (str != NULL && *str) {
- // { Skip the leading `,` or ` ` if the item is a flag
- // and the proper conditions are met
- char *t = str;
- if (itemisflag) {
- if ((t[0] && t[1])
- && ((!prevchar_isitem && *t == ',')
- || (prevchar_isflag && *t == ' '))) {
- t++;
- }
- prevchar_isflag = true;
- }
- // }
-
- long l = vim_strsize(t);
-
- // If this item is non-empty, record that the last thing
- // we put in the output buffer was an item
- if (l > 0) {
- prevchar_isitem = true;
- }
-
- // If the item is too wide, truncate it from the beginning
- if (l > maxwid) {
- while (l >= maxwid) {
- l -= ptr2cells(t);
- t += utfc_ptr2len(t);
- }
-
- // Early out if there isn't enough room for the truncation marker
- if (out_p >= out_end_p) {
- break;
- }
-
- // Add the truncation marker
- *out_p++ = '<';
- }
-
- // If the item is right aligned and not wide enough,
- // pad with fill characters.
- if (minwid > 0) {
- for (; l < minwid && out_p < out_end_p; l++) {
- // Don't put a "-" in front of a digit.
- if (l + 1 == minwid && fillchar == '-' && ascii_isdigit(*t)) {
- *out_p++ = ' ';
- } else {
- MB_CHAR2BYTES(fillchar, out_p);
- }
- }
- minwid = 0;
- } else {
- // Note: The negative value denotes a left aligned item.
- // Here we switch the minimum width back to a positive value.
- minwid *= -1;
- }
-
- // { Copy the string text into the output buffer
- for (; *t && out_p < out_end_p; t++) {
- // Change a space by fillchar, unless fillchar is '-' and a
- // digit follows.
- if (fillable && *t == ' '
- && (!ascii_isdigit(*(t + 1)) || fillchar != '-')) {
- MB_CHAR2BYTES(fillchar, out_p);
- } else {
- *out_p++ = *t;
- }
- }
- // }
-
- // For left-aligned items, fill any remaining space with the fillchar
- for (; l < minwid && out_p < out_end_p; l++) {
- MB_CHAR2BYTES(fillchar, out_p);
- }
-
- // Otherwise if the item is a number, copy that to the output buffer.
- } else if (num >= 0) {
- if (out_p + 20 > out_end_p) {
- break; // not sufficient space
- }
- prevchar_isitem = true;
-
- // { Build the formatting string
- char nstr[20];
- char *t = nstr;
- if (opt == STL_VIRTCOL_ALT) {
- *t++ = '-';
- minwid--;
- }
- *t++ = '%';
- if (zeropad) {
- *t++ = '0';
- }
-
- // Note: The `*` means we take the width as one of the arguments
- *t++ = '*';
- *t++ = base == kNumBaseHexadecimal ? 'X' : 'd';
- *t = 0;
- // }
-
- // { Determine how many characters the number will take up when printed
- // Note: We have to cast the base because the compiler uses
- // unsigned ints for the enum values.
- long num_chars = 1;
- for (long n = num; n >= (int)base; n /= (int)base) {
- num_chars++;
- }
-
- // VIRTCOL_ALT takes up an extra character because
- // of the `-` we added above.
- if (opt == STL_VIRTCOL_ALT) {
- num_chars++;
- }
- // }
-
- assert(out_end_p >= out_p);
- size_t remaining_buf_len = (size_t)(out_end_p - out_p) + 1;
-
- // If the number is going to take up too much room
- // Figure out the approximate number in "scientific" type notation.
- // Ex: 14532 with maxwid of 4 -> '14>3'
- if (num_chars > maxwid) {
- // Add two to the width because the power piece will take
- // two extra characters
- num_chars += 2;
-
- // How many extra characters there are
- long n = num_chars - maxwid;
-
- // { Reduce the number by base^n
- while (num_chars-- > maxwid) {
- num /= (long)base;
- }
- // }
-
- // { Add the format string for the exponent bit
- *t++ = '>';
- *t++ = '%';
- // Use the same base as the first number
- *t = t[-3];
- *++t = 0;
- // }
-
- vim_snprintf(out_p, remaining_buf_len, nstr, 0, num, n);
- } else {
- vim_snprintf(out_p, remaining_buf_len, nstr, minwid, num);
- }
-
- // Advance the output buffer position to the end of the
- // number we just printed
- out_p += STRLEN(out_p);
-
- // Otherwise, there was nothing to print so mark the item as empty
- } else {
- stl_items[curitem].type = Empty;
- }
-
- // Only free the string buffer if we allocated it.
- // Note: This is not needed if `str` is pointing at `tmp`
- if (opt == STL_VIM_EXPR) {
- XFREE_CLEAR(str);
- }
-
- if (num >= 0 || (!itemisflag && str && *str)) {
- prevchar_isflag = false; // Item not NULL, but not a flag
- }
-
- // Item processed, move to the next
- curitem++;
- }
-
- *out_p = NUL;
- int itemcnt = curitem;
-
- // Free the format buffer if we allocated it internally
- if (usefmt != fmt) {
- xfree(usefmt);
- }
-
- // We have now processed the entire statusline format string.
- // What follows is post-processing to handle alignment and highlighting.
-
- int width = vim_strsize(out);
- if (maxwidth > 0 && width > maxwidth) {
- // Result is too long, must truncate somewhere.
- int item_idx = 0;
- char *trunc_p;
-
- // If there are no items, truncate from beginning
- if (itemcnt == 0) {
- trunc_p = out;
-
- // Otherwise, look for the truncation item
- } else {
- // Default to truncating at the first item
- trunc_p = stl_items[0].start;
- item_idx = 0;
-
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Trunc) {
- // Truncate at %< stl_items.
- trunc_p = stl_items[i].start;
- item_idx = i;
- break;
- }
- }
- }
-
- // If the truncation point we found is beyond the maximum
- // length of the string, truncate the end of the string.
- if (width - vim_strsize(trunc_p) >= maxwidth) {
- // Walk from the beginning of the
- // string to find the last character that will fit.
- trunc_p = out;
- width = 0;
- for (;;) {
- width += ptr2cells(trunc_p);
- if (width >= maxwidth) {
- break;
- }
-
- // Note: Only advance the pointer if the next
- // character will fit in the available output space
- trunc_p += utfc_ptr2len(trunc_p);
- }
-
- // Ignore any items in the statusline that occur after
- // the truncation point
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].start > trunc_p) {
- itemcnt = i;
- break;
- }
- }
-
- // Truncate the output
- *trunc_p++ = '>';
- *trunc_p = 0;
-
- // Truncate at the truncation point we found
- } else {
- // { Determine how many bytes to remove
- long trunc_len = 0;
- while (width >= maxwidth) {
- width -= ptr2cells(trunc_p + trunc_len);
- trunc_len += utfc_ptr2len(trunc_p + trunc_len);
- }
- // }
-
- // { Truncate the string
- char *trunc_end_p = trunc_p + trunc_len;
- STRMOVE(trunc_p + 1, trunc_end_p);
-
- // Put a `<` to mark where we truncated at
- *trunc_p = '<';
-
- if (width + 1 < maxwidth) {
- // Advance the pointer to the end of the string
- trunc_p = trunc_p + STRLEN(trunc_p);
- }
-
- // Fill up for half a double-wide character.
- while (++width < maxwidth) {
- MB_CHAR2BYTES(fillchar, trunc_p);
- *trunc_p = NUL;
- }
- // }
-
- // { Change the start point for items based on
- // their position relative to our truncation point
-
- // Note: The offset is one less than the truncation length because
- // the truncation marker `<` is not counted.
- long item_offset = trunc_len - 1;
-
- for (int i = item_idx; i < itemcnt; i++) {
- // Items starting at or after the end of the truncated section need
- // to be moved backwards.
- if (stl_items[i].start >= trunc_end_p) {
- stl_items[i].start -= item_offset;
- // Anything inside the truncated area is set to start
- // at the `<` truncation character.
- } else {
- stl_items[i].start = trunc_p;
- }
- }
- // }
- }
- width = maxwidth;
-
- // If there is room left in our statusline, and room left in our buffer,
- // add characters at the separate marker (if there is one) to
- // fill up the available space.
- } else if (width < maxwidth
- && STRLEN(out) + (size_t)(maxwidth - width) + 1 < outlen) {
- // Find how many separators there are, which we will use when
- // figuring out how many groups there are.
- int num_separators = 0;
- for (int i = 0; i < itemcnt; i++) {
- if (stl_items[i].type == Separate) {
- // Create an array of the start location for each
- // separator mark.
- stl_separator_locations[num_separators] = i;
- num_separators++;
- }
- }
-
- // If we have separated groups, then we deal with it now
- if (num_separators) {
- int standard_spaces = (maxwidth - width) / num_separators;
- int final_spaces = (maxwidth - width) -
- standard_spaces * (num_separators - 1);
-
- for (int i = 0; i < num_separators; i++) {
- int dislocation = (i == (num_separators - 1)) ? final_spaces : standard_spaces;
- dislocation *= utf_char2len(fillchar);
- char *start = stl_items[stl_separator_locations[i]].start;
- char *seploc = start + dislocation;
- STRMOVE(seploc, start);
- for (char *s = start; s < seploc;) {
- MB_CHAR2BYTES(fillchar, s);
- }
-
- for (int item_idx = stl_separator_locations[i] + 1;
- item_idx < itemcnt;
- item_idx++) {
- stl_items[item_idx].start += dislocation;
- }
- }
-
- width = maxwidth;
- }
- }
-
- // Store the info about highlighting.
- if (hltab != NULL) {
- *hltab = stl_hltab;
- stl_hlrec_t *sp = stl_hltab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == Highlight) {
- sp->start = stl_items[l].start;
- sp->userhl = stl_items[l].minwid;
- sp++;
- }
- }
- sp->start = NULL;
- sp->userhl = 0;
- }
-
- // Store the info about tab pages labels.
- if (tabtab != NULL) {
- *tabtab = stl_tabtab;
- StlClickRecord *cur_tab_rec = stl_tabtab;
- for (long l = 0; l < itemcnt; l++) {
- if (stl_items[l].type == TabPage) {
- cur_tab_rec->start = stl_items[l].start;
- if (stl_items[l].minwid == 0) {
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- } else {
- int tabnr = stl_items[l].minwid;
- if (stl_items[l].minwid > 0) {
- cur_tab_rec->def.type = kStlClickTabSwitch;
- } else {
- cur_tab_rec->def.type = kStlClickTabClose;
- tabnr = -tabnr;
- }
- cur_tab_rec->def.tabnr = tabnr;
- }
- cur_tab_rec->def.func = NULL;
- cur_tab_rec++;
- } else if (stl_items[l].type == ClickFunc) {
- cur_tab_rec->start = stl_items[l].start;
- cur_tab_rec->def.type = kStlClickFuncRun;
- cur_tab_rec->def.tabnr = stl_items[l].minwid;
- cur_tab_rec->def.func = stl_items[l].cmd;
- cur_tab_rec++;
- }
- }
- cur_tab_rec->start = NULL;
- cur_tab_rec->def.type = kStlClickDisabled;
- cur_tab_rec->def.tabnr = 0;
- cur_tab_rec->def.func = NULL;
- }
-
- // 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) {
- must_redraw = save_must_redraw;
- curwin->w_redr_type = save_redr_type;
- }
-
- return width;
-}
-
/// Get relative cursor position in window into "buf[buflen]", in the form 99%,
/// using "Top", "Bot" or "All" when appropriate.
void get_rel_pos(win_T *wp, char *buf, int buflen)
@@ -4571,7 +3396,7 @@ void get_rel_pos(win_T *wp, char *buf, int buflen)
/// @param add_file if true, add "file" before the arg number
///
/// @return true if it was appended.
-static bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
+bool append_arg_number(win_T *wp, char *buf, int buflen, bool add_file)
FUNC_ATTR_NONNULL_ALL
{
// Nothing to do
@@ -4628,279 +3453,6 @@ void fname_expand(buf_T *buf, char **ffname, char **sfname)
#endif
}
-/// Get the file name for an argument list entry.
-char *alist_name(aentry_T *aep)
-{
- buf_T *bp;
-
- // Use the name from the associated buffer if it exists.
- bp = buflist_findnr(aep->ae_fnum);
- if (bp == NULL || bp->b_fname == NULL) {
- return (char *)aep->ae_fname;
- }
- return bp->b_fname;
-}
-
-/// do_arg_all(): Open up to 'count' windows, one for each argument.
-///
-/// @param forceit hide buffers in current windows
-/// @param keep_tabs keep current tabs, for ":tab drop file"
-void do_arg_all(int count, int forceit, int keep_tabs)
-{
- uint8_t *opened; // Array of weight for which args are open:
- // 0: not opened
- // 1: opened in other tab
- // 2: opened in curtab
- // 3: opened in curtab and curwin
-
- int opened_len; // length of opened[]
- int use_firstwin = false; // use first window for arglist
- bool tab_drop_empty_window = false;
- int split_ret = OK;
- bool p_ea_save;
- alist_T *alist; // argument list to be used
- buf_T *buf;
- tabpage_T *tpnext;
- int had_tab = cmdmod.cmod_tab;
- win_T *old_curwin, *last_curwin;
- tabpage_T *old_curtab, *last_curtab;
- win_T *new_curwin = NULL;
- tabpage_T *new_curtab = NULL;
-
- assert(firstwin != NULL); // satisfy coverity
-
- if (ARGCOUNT <= 0) {
- // Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
- return;
- }
- setpcmark();
-
- opened_len = ARGCOUNT;
- opened = xcalloc((size_t)opened_len, 1);
-
- // Autocommands may do anything to the argument list. Make sure it's not
- // freed while we are working here by "locking" it. We still have to
- // watch out for its size to be changed.
- alist = curwin->w_alist;
- alist->al_refcount++;
-
- old_curwin = curwin;
- old_curtab = curtab;
-
- // Try closing all windows that are not in the argument list.
- // Also close windows that are not full width;
- // When 'hidden' or "forceit" set the buffer becomes hidden.
- // Windows that have a changed buffer and can't be hidden won't be closed.
- // When the ":tab" modifier was used do this for all tab pages.
- if (had_tab > 0) {
- goto_tabpage_tp(first_tabpage, true, true);
- }
- for (;;) {
- win_T *wpnext = NULL;
- tpnext = curtab->tp_next;
- for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
- int i;
- wpnext = wp->w_next;
- buf = wp->w_buffer;
- if (buf->b_ffname == NULL
- || (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
- i = opened_len;
- } else {
- // check if the buffer in this window is in the arglist
- for (i = 0; i < opened_len; i++) {
- if (i < alist->al_ga.ga_len
- && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
- || path_full_compare(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname,
- true, true) & kEqualFiles)) {
- int weight = 1;
-
- if (old_curtab == curtab) {
- weight++;
- if (old_curwin == wp) {
- weight++;
- }
- }
-
- if (weight > (int)opened[i]) {
- opened[i] = (uint8_t)weight;
- if (i == 0) {
- if (new_curwin != NULL) {
- new_curwin->w_arg_idx = opened_len;
- }
- new_curwin = wp;
- new_curtab = curtab;
- }
- } else if (keep_tabs) {
- i = opened_len;
- }
-
- if (wp->w_alist != alist) {
- // Use the current argument list for all windows containing a file from it.
- alist_unlink(wp->w_alist);
- wp->w_alist = alist;
- wp->w_alist->al_refcount++;
- }
- break;
- }
- }
- }
- wp->w_arg_idx = i;
-
- if (i == opened_len && !keep_tabs) { // close this window
- if (buf_hide(buf) || forceit || buf->b_nwindows > 1
- || !bufIsChanged(buf)) {
- // If the buffer was changed, and we would like to hide it, try autowriting.
- if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
- bufref_T bufref;
- set_bufref(&bufref, buf);
- (void)autowrite(buf, false);
- // Check if autocommands removed the window.
- if (!win_valid(wp) || !bufref_valid(&bufref)) {
- wpnext = firstwin; // Start all over...
- continue;
- }
- }
- // don't close last window
- if (ONE_WINDOW
- && (first_tabpage->tp_next == NULL || !had_tab)) {
- use_firstwin = true;
- } else {
- win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
- // check if autocommands removed the next window
- if (!win_valid(wpnext)) {
- // start all over...
- wpnext = firstwin;
- }
- }
- }
- }
- }
-
- // Without the ":tab" modifier only do the current tab page.
- if (had_tab == 0 || tpnext == NULL) {
- break;
- }
-
- // check if autocommands removed the next tab page
- if (!valid_tabpage(tpnext)) {
- tpnext = first_tabpage; // start all over...
- }
- goto_tabpage_tp(tpnext, true, true);
- }
-
- // Open a window for files in the argument list that don't have one.
- // ARGCOUNT may change while doing this, because of autocommands.
- if (count > opened_len || count <= 0) {
- count = opened_len;
- }
-
- // Don't execute Win/Buf Enter/Leave autocommands here.
- autocmd_no_enter++;
- autocmd_no_leave++;
- last_curwin = curwin;
- last_curtab = curtab;
- win_enter(lastwin, false);
- // ":tab drop file" should re-use an empty window to avoid "--remote-tab"
- // leaving an empty tab page when executed locally.
- if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
- && curbuf->b_ffname == NULL && !curbuf->b_changed) {
- use_firstwin = true;
- tab_drop_empty_window = true;
- }
-
- for (int i = 0; i < count && !got_int; i++) {
- if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
- arg_had_last = true;
- }
- if (opened[i] > 0) {
- // Move the already present window to below the current window
- if (curwin->w_arg_idx != i) {
- FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
- if (wp->w_arg_idx == i) {
- if (keep_tabs) {
- new_curwin = wp;
- new_curtab = curtab;
- } else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
- emsg(_("E249: window layout changed unexpectedly"));
- i = count;
- break;
- } else {
- win_move_after(wp, curwin);
- }
- break;
- }
- }
- }
- } else if (split_ret == OK) {
- // trigger events for tab drop
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter--;
- }
- if (!use_firstwin) { // split current window
- p_ea_save = p_ea;
- p_ea = true; // use space from all windows
- split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
- p_ea = p_ea_save;
- if (split_ret == FAIL) {
- continue;
- }
- } else { // first window: do autocmd for leaving this buffer
- autocmd_no_leave--;
- }
-
- // edit file "i"
- curwin->w_arg_idx = i;
- if (i == 0) {
- new_curwin = curwin;
- new_curtab = curtab;
- }
- (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
- ((buf_hide(curwin->w_buffer)
- || bufIsChanged(curwin->w_buffer))
- ? ECMD_HIDE : 0) + ECMD_OLDBUF,
- curwin);
- if (tab_drop_empty_window && i == count - 1) {
- autocmd_no_enter++;
- }
- if (use_firstwin) {
- autocmd_no_leave++;
- }
- use_firstwin = false;
- }
- os_breakcheck();
-
- // When ":tab" was used open a new tab for a new window repeatedly.
- if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
- cmdmod.cmod_tab = 9999;
- }
- }
-
- // Remove the "lock" on the argument list.
- alist_unlink(alist);
-
- autocmd_no_enter--;
- // restore last referenced tabpage's curwin
- if (last_curtab != new_curtab) {
- if (valid_tabpage(last_curtab)) {
- goto_tabpage_tp(last_curtab, true, true);
- }
- if (win_valid(last_curwin)) {
- win_enter(last_curwin, false);
- }
- }
- // to window with first arg
- if (valid_tabpage(new_curtab)) {
- goto_tabpage_tp(new_curtab, true, true);
- }
- if (win_valid(new_curwin)) {
- win_enter(new_curwin, false);
- }
-
- autocmd_no_leave--;
- xfree(opened);
-}
-
/// @return true if "buf" is a prompt buffer.
bool bt_prompt(buf_T *buf)
FUNC_ATTR_PURE
@@ -5139,8 +3691,6 @@ static int chk_modeline(linenr_T lnum, int flags)
intmax_t vers;
int end;
int retval = OK;
- char *save_sourcing_name;
- linenr_T save_sourcing_lnum;
prev = -1;
for (s = (char *)ml_get(lnum); *s != NUL; s++) {
@@ -5185,10 +3735,8 @@ static int chk_modeline(linenr_T lnum, int flags)
s = linecopy = xstrdup(s); // copy the line, it will change
- save_sourcing_lnum = sourcing_lnum;
- save_sourcing_name = sourcing_name;
- sourcing_lnum = lnum; // prepare for emsg()
- sourcing_name = "modelines";
+ // prepare for emsg()
+ estack_push(ETYPE_MODELINE, "modelines", lnum);
end = false;
while (end == false) {
@@ -5228,7 +3776,7 @@ static int chk_modeline(linenr_T lnum, int flags)
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_MODELINE;
current_sctx.sc_seq = 0;
- current_sctx.sc_lnum = 0;
+ current_sctx.sc_lnum = lnum;
// Make sure no risky things are executed as a side effect.
secure = 1;
@@ -5243,9 +3791,7 @@ static int chk_modeline(linenr_T lnum, int flags)
s = e + 1; // advance to next part
}
- sourcing_lnum = save_sourcing_lnum;
- sourcing_name = save_sourcing_name;
-
+ estack_pop();
xfree(linecopy);
return retval;