mirror of
https://github.com/neovim/neovim.git
synced 2026-07-15 13:50:33 +00:00
feat(statusline)!: allow %= in item groups, scope %< to item groups #40369
Problem: Separation markers (%=) are ignored within item groups. This lead to a regression when the C implementation of the statusline was replaced with a default expression. When the user configured a custom ruler expression with a %= and used the overloaded item group syntax to set the ruler width, the separation marker worked in the ruler, but not when the ruler was incorporated into the statusline where the item group syntax was interpreted in the usual way. Solution: Analogously to top-level behaviour, expand separation markers evenly within item groups until `minwid` is reached (if set). ref https://github.com/neovim/neovim/pull/33036 fix https://github.com/neovim/neovim/issues/39984 ref https://github.com/neovim/neovim/issues/40247 Problem: The recursion offset into the static `stl_items` was not taken into account when adjusting the item count after truncation. Steps to reproduce: first prepare `stl_items`: set stl=%{%repeat('%#Error#',10)%} then watch how the Error highlight leaks into the recursive call: set stl=%l%l%l%{%nvim_eval_statusline('test%l%<',{'maxwidth':3,'highlights':1}).highlights%} ref https://github.com/neovim/neovim/issues/32259 * fix(statusline): consistent truncation at multicell character Problem 1: truncation of item groups at multicell character didn't take into account that minwid can be specified as a negative number. Problem 2: after truncation at top-level from the right at multicell character, the returned width was always `maxwidth`, even though the actual width was reduced. In vim, this can be observed as a statusline that is not fully drawn until the edge of the screen: vim --clean +"set ls=2 stl=%{%repeat('x',&columns-2)%}🙂x%<" Problem 3: after truncation at top-level from the left at multicell character, the resulting gap to reach `maxwidth` again was filled with fillchars, but then the final NUL was not set correctly. This can be seen in the following example, where the statuscolumn spills into the editing area starting from line 10: nvim --clean +"set number stc=%<x🙂%{repeat('x',43)}%l" +"norm yy10p" Solution: fix the small errors and, at top-level, consistently reduce the size instead of compensating with fillchars. In the case of the statusline and the winbar, the remaining place is filled with the configured fillchars in `win_redr_custom`, after `build_stl_str_hl` has returned. In all other cases (title, icon, statuscol, tabline, ruler), there seems to be no point in adding additional spaces at the end. * feat(statusline)!: scope %< to item groups Problem: Previously, item groups were only truncated at the beginning, which is often not desired. In the example %.15(path: %f%) the group's title/label is truncated away: <th/to/file.txt Truncation markers (%<) in item groups were processed at the top-level in the end, which can be confusing. Only the first %< is used for the whole string, and it is used even if the containing item group is hidden. Additionally, in the case of hidden item groups, the marker's position was not adapted. For example, %(hidden%<%)%f had the effect of truncating the path somewhere in the middle: /path/<file.txt Solution: Make truncation consistent with top-level behaviour, which has a better default of truncating at the first `Normal` item, i.e. path: <file.txt and allows for fine-grained control with truncation markers (%<). E.g. %.15(path: %f%<%) now yields path: /path/to> The original behaviour can be restored like so: %.15(%<path: %f%) BREAKING CHANGE: %< is no longer processed at top-level - the default truncation behaviour has changed: now at first item - truncation markers inside item groups don't affect truncation outside of the item group anymore - several truncation markers can now have an effect when separated with item groups, whereas previously only the first one globally had ref https://github.com/neovim/neovim/issues/39984
This commit is contained in:
committed by
GitHub
parent
cd8f609520
commit
444d0b8b6c
@@ -108,7 +108,14 @@ LUA
|
||||
|
||||
OPTIONS
|
||||
|
||||
• todo
|
||||
• The 'statusline' `%=` item is no longer ignored in item groups. It applies
|
||||
to the containing item group and its `minwid`.
|
||||
An `%<` item inside an item group now only applies to the containing group
|
||||
and its `maxwid`. Therefore, multiple `%<` can now be effective.
|
||||
The default truncation of item groups is now at the first contained item.
|
||||
To restore the previous truncation from the left, add a `%<` to the start.
|
||||
The same applies to 'rulerformat', 'statuscolumn', 'tabline', 'winbar',
|
||||
'titlestring', and 'iconstring'.
|
||||
|
||||
PLUGINS
|
||||
|
||||
@@ -260,6 +267,11 @@ OPTIONS
|
||||
• 'scrolloffpad' allows vertically centering cursor at the end of file.
|
||||
• 'shortmess' flag |shm-u| silences undo/redo messages.
|
||||
• 'statusline' supports |stl-%0{| to insert the expression result verbatim.
|
||||
The `%=` item is supported within item groups to separate alignment sections
|
||||
within the containing group until its `minwid` is reached. The truncation
|
||||
of an item group to its `maxwid` can be controlled with the `%<` item.
|
||||
The same applies to 'rulerformat', 'statuscolumn', 'tabline', 'winbar',
|
||||
'titlestring', and 'iconstring'.
|
||||
• 'winpinned' prevents window from closing unless specifically targeted.
|
||||
|
||||
PERFORMANCE
|
||||
|
||||
@@ -6478,12 +6478,16 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
this label.
|
||||
Use |getmousepos()|.winid in the specified function to get the
|
||||
corresponding |window-ID| of the clicked item.
|
||||
< - Where to truncate line if too long. Default is at the start.
|
||||
< - Where to truncate line if too long. Default is at the first
|
||||
item. Truncation markers within item groups apply to the
|
||||
truncation of that group until its maxwid is reached.
|
||||
No width fields allowed.
|
||||
= - Separation point between alignment sections. Each section will
|
||||
be separated by an equal number of spaces. With one %= what
|
||||
comes after it will be right-aligned. With two %= there is a
|
||||
middle part, with white space left and right of it.
|
||||
Alignment sections within item groups will be separated until
|
||||
minwid of the group is reached.
|
||||
No width fields allowed.
|
||||
# - Set highlight group. The name must follow and then a # again.
|
||||
Thus use %#HLname# for highlight group HLname. The same
|
||||
|
||||
@@ -439,7 +439,8 @@ Options:
|
||||
- 'showcmdloc' cannot be set to empty.
|
||||
- 'signcolumn' can show multiple signs (dynamic or fixed columns)
|
||||
- 'statuscolumn' full control of columns using 'statusline' format
|
||||
- 'statusline' default is exposed as a statusline expression.
|
||||
- 'statusline' default is exposed as a statusline expression.
|
||||
- 'statusline' and related options apply `%=` and `%<` to item groups.
|
||||
- 'splitkeep' cannot be set to empty.
|
||||
- 'tabline' middle-click on tabpage label closes tabpage,
|
||||
and %@Func@foo%X can call any function on mouse-click
|
||||
|
||||
6
runtime/lua/vim/_meta/options.gen.lua
generated
6
runtime/lua/vim/_meta/options.gen.lua
generated
@@ -6875,12 +6875,16 @@ vim.wo.stc = vim.wo.statuscolumn
|
||||
--- this label.
|
||||
--- Use `getmousepos()`.winid in the specified function to get the
|
||||
--- corresponding `window-ID` of the clicked item.
|
||||
--- \< - Where to truncate line if too long. Default is at the start.
|
||||
--- \< - Where to truncate line if too long. Default is at the first
|
||||
--- item. Truncation markers within item groups apply to the
|
||||
--- truncation of that group until its maxwid is reached.
|
||||
--- No width fields allowed.
|
||||
--- = - Separation point between alignment sections. Each section will
|
||||
--- be separated by an equal number of spaces. With one %= what
|
||||
--- comes after it will be right-aligned. With two %= there is a
|
||||
--- middle part, with white space left and right of it.
|
||||
--- Alignment sections within item groups will be separated until
|
||||
--- minwid of the group is reached.
|
||||
--- No width fields allowed.
|
||||
--- # - Set highlight group. The name must follow and then a # again.
|
||||
--- Thus use %#HLname# for highlight group HLname. The same
|
||||
|
||||
@@ -8932,12 +8932,16 @@ local options = {
|
||||
this label.
|
||||
Use |getmousepos()|.winid in the specified function to get the
|
||||
corresponding |window-ID| of the clicked item.
|
||||
\< - Where to truncate line if too long. Default is at the start.
|
||||
\< - Where to truncate line if too long. Default is at the first
|
||||
item. Truncation markers within item groups apply to the
|
||||
truncation of that group until its maxwid is reached.
|
||||
No width fields allowed.
|
||||
= - Separation point between alignment sections. Each section will
|
||||
be separated by an equal number of spaces. With one %= what
|
||||
comes after it will be right-aligned. With two %= there is a
|
||||
middle part, with white space left and right of it.
|
||||
Alignment sections within item groups will be separated until
|
||||
minwid of the group is reached.
|
||||
No width fields allowed.
|
||||
# - Set highlight group. The name must follow and then a # again.
|
||||
Thus use %#HLname# for highlight group HLname. The same
|
||||
|
||||
@@ -53,6 +53,13 @@
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/window.h"
|
||||
|
||||
// Specifies whether/where to add padding to reach target width.
|
||||
typedef enum {
|
||||
kPaddingNone,
|
||||
kPaddingLeft,
|
||||
kPaddingRight,
|
||||
} StlPadding;
|
||||
|
||||
#include "statusline.c.generated.h"
|
||||
|
||||
// Determines how deeply nested %{} blocks will be evaluated in statusline.
|
||||
@@ -851,6 +858,215 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, int relnum, int virtnum, char
|
||||
return width;
|
||||
}
|
||||
|
||||
/// Truncates the statusline at the first truncation marker (%<) if present,
|
||||
/// or alternatively at the first item, or otherwise at the beginning.
|
||||
///
|
||||
/// @param width Current width in cells (will be adjusted if truncation took place)
|
||||
/// @param minwid Minimum width in cells
|
||||
/// @param maxwid Maximum width in cells
|
||||
/// @param fillchar Fillchar
|
||||
/// @param stl_items Items
|
||||
/// @param startitem Start item index (inclusive)
|
||||
/// @param curitem End item index (exclusive)
|
||||
/// @param start_p Start position in the output buffer
|
||||
/// @param out_p Current output buffer position (will be adjusted if truncation took place)
|
||||
static void stl_truncate(int *width, int minwid, int maxwid, schar_T fillchar,
|
||||
stl_item_t *stl_items, int startitem, int *curitem, char *start_p,
|
||||
char **out_p)
|
||||
{
|
||||
minwid = MIN(minwid, maxwid);
|
||||
|
||||
int item_idx = startitem;
|
||||
char *trunc_p;
|
||||
|
||||
// If there are no items, truncate from beginning
|
||||
if (startitem == *curitem) {
|
||||
trunc_p = start_p;
|
||||
|
||||
// Otherwise, look for the truncation item
|
||||
} else {
|
||||
// Default to truncating at the first item
|
||||
trunc_p = stl_items[item_idx].start;
|
||||
|
||||
for (int i = startitem; i < *curitem; 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) >= maxwid) {
|
||||
// Walk from the beginning of the
|
||||
// string to find the last character that will fit.
|
||||
trunc_p = start_p;
|
||||
*width = 0;
|
||||
while (true) {
|
||||
int char_cells = ptr2cells(trunc_p);
|
||||
if (*width + char_cells >= maxwid) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Note: Only advance the pointer if the next
|
||||
// character will fit in the available output space
|
||||
*width += char_cells;
|
||||
trunc_p += utfc_ptr2len(trunc_p);
|
||||
}
|
||||
|
||||
// Ignore any items in the statusline that occur after
|
||||
// the truncation point
|
||||
for (int i = startitem; i < *curitem; i++) {
|
||||
if (stl_items[i].start > trunc_p) {
|
||||
for (int j = i; j < *curitem; j++) {
|
||||
if (stl_items[j].type == ClickFunc) {
|
||||
XFREE_CLEAR(stl_items[j].cmd);
|
||||
}
|
||||
}
|
||||
*curitem = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate the output
|
||||
*trunc_p++ = '>';
|
||||
*out_p = trunc_p;
|
||||
|
||||
// Truncate at the truncation point we found
|
||||
} else {
|
||||
// { Determine how many bytes to remove
|
||||
int trunc_len = 0;
|
||||
while (*width >= maxwid) {
|
||||
*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;
|
||||
memmove(trunc_p + 1, trunc_end_p, (size_t)(*out_p - trunc_end_p) + 1); // +1 for NUL
|
||||
*out_p -= (size_t)(trunc_end_p - (trunc_p + 1));
|
||||
|
||||
// Put a `<` to mark where we truncated at
|
||||
*trunc_p = '<';
|
||||
// }
|
||||
|
||||
// { 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.
|
||||
int item_offset = trunc_len - 1;
|
||||
|
||||
for (int i = item_idx; i < *curitem; 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;
|
||||
} else {
|
||||
// Anything inside the truncated area is set to start
|
||||
// at the `<` truncation character.
|
||||
stl_items[i].start = trunc_p;
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// Fill up for half a double-wide character.
|
||||
while (++*width < minwid) {
|
||||
schar_get_adv(out_p, fillchar);
|
||||
}
|
||||
**out_p = NUL;
|
||||
}
|
||||
|
||||
/// Expands the statusline at separation markers (%=) if present,
|
||||
/// or otherwise according to padding choice.
|
||||
///
|
||||
/// @param width Current width in cells (will be adjusted if expansion took place)
|
||||
/// @param target_width Target width in cells
|
||||
/// @param padding Padding to add until target width is reached, if there are no separation markers
|
||||
/// @param remaining_capacity Remaining output buffer capacity in bytes
|
||||
/// @param fillchar Fillchar
|
||||
/// @param stl_items Items
|
||||
/// @param startitem Start item index (inclusive)
|
||||
/// @param curitem End item index (exclusive)
|
||||
/// @param start_p Start position in the output buffer
|
||||
/// @param out_p Current output buffer position (will be adjusted if expansion took place)
|
||||
static void stl_expand(int *width, int target_width, StlPadding padding, int remaining_capacity,
|
||||
schar_T fillchar, stl_item_t *stl_items, int startitem, int curitem,
|
||||
char *start_p, char **out_p)
|
||||
{
|
||||
int fillchar_bytes = (int)schar_len(fillchar);
|
||||
int added_cells = target_width - *width;
|
||||
int added_bytes = added_cells * fillchar_bytes;
|
||||
if (added_bytes > remaining_capacity) {
|
||||
added_cells = remaining_capacity / fillchar_bytes;
|
||||
added_bytes = added_cells * fillchar_bytes;
|
||||
}
|
||||
|
||||
// First, try to expand at separation markers %=.
|
||||
int num_separators = 0;
|
||||
for (int i = startitem; i < curitem; i++) {
|
||||
if (stl_items[i].type == Separate) {
|
||||
num_separators += 1;
|
||||
}
|
||||
}
|
||||
if (num_separators > 0) {
|
||||
int standard_spaces = added_cells / num_separators;
|
||||
int final_spaces = added_cells - standard_spaces * (num_separators - 1);
|
||||
int cumulated_dislocation = 0;
|
||||
|
||||
for (int i = startitem; i < curitem; i++) {
|
||||
stl_items[i].start += cumulated_dislocation;
|
||||
|
||||
if (stl_items[i].type == Separate) {
|
||||
int dislocation = --num_separators > 0 ? standard_spaces : final_spaces;
|
||||
dislocation *= fillchar_bytes;
|
||||
cumulated_dislocation += dislocation;
|
||||
|
||||
char *start = stl_items[i].start;
|
||||
char *seploc = start + dislocation;
|
||||
STRMOVE(seploc, start);
|
||||
for (char *s = start; s < seploc;) {
|
||||
schar_get_adv(&s, fillchar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*width += added_cells;
|
||||
*out_p += added_bytes;
|
||||
|
||||
// Add filler characters to the right.
|
||||
} else if (padding == kPaddingRight) {
|
||||
for (; added_cells > 0; added_cells--) {
|
||||
schar_get_adv(out_p, fillchar);
|
||||
}
|
||||
|
||||
*width += added_cells;
|
||||
|
||||
// Shift everything to the right and prepend with filler characters.
|
||||
} else if (padding == kPaddingLeft) {
|
||||
// Move the group to the right
|
||||
memmove(start_p + added_bytes, start_p, (size_t)(*out_p - start_p));
|
||||
|
||||
// Prepend the fill characters
|
||||
for (; added_cells > 0; added_cells--) {
|
||||
schar_get_adv(&start_p, fillchar);
|
||||
}
|
||||
|
||||
// Adjust item start positions
|
||||
for (int n = startitem; n < curitem; n++) {
|
||||
stl_items[n].start += added_bytes;
|
||||
}
|
||||
|
||||
*width += added_cells;
|
||||
*out_p += added_bytes;
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a string from the status line items in "fmt".
|
||||
/// Return length of string in screen cells.
|
||||
///
|
||||
@@ -887,7 +1103,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
static int *stl_groupitems = NULL;
|
||||
static stl_hlrec_t *stl_hltab = NULL;
|
||||
static StlClickRecord *stl_tabtab = NULL;
|
||||
static int *stl_separator_locations = NULL;
|
||||
static int curitem = 0;
|
||||
|
||||
#define TMPLEN 70
|
||||
@@ -915,8 +1130,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
// 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);
|
||||
}
|
||||
|
||||
// If "fmt" was set insecurely it needs to be evaluated in the sandbox.
|
||||
@@ -1000,8 +1213,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
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;
|
||||
}
|
||||
@@ -1041,13 +1252,9 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
continue;
|
||||
}
|
||||
|
||||
// STL_SEPARATE: Separation between items, filled with white space.
|
||||
// STL_SEPARATE: Separation between items, filled with fillchars.
|
||||
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;
|
||||
@@ -1075,7 +1282,7 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
// so `vim_strsize` will work.
|
||||
char *t = stl_items[stl_groupitems[groupdepth]].start;
|
||||
*out_p = NUL;
|
||||
ptrdiff_t group_len = vim_strsize(t);
|
||||
int group_len = vim_strsize(t);
|
||||
|
||||
// If the group contained internal items
|
||||
// and the group did not have a minimum width,
|
||||
@@ -1122,84 +1329,34 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, OptIndex op
|
||||
}
|
||||
}
|
||||
|
||||
int maxwid = stl_items[stl_groupitems[groupdepth]].maxwid;
|
||||
int minwid = stl_items[stl_groupitems[groupdepth]].minwid;
|
||||
StlPadding padding = minwid < 0 ? kPaddingRight : kPaddingLeft;
|
||||
minwid = abs(minwid);
|
||||
|
||||
// If the group is longer than it is allowed to be truncate by removing
|
||||
// bytes from the start of the group text. Don't truncate when item is a
|
||||
// 'statuscolumn' fold item to ensure correctness of the mouse clicks.
|
||||
if (group_len > stl_items[stl_groupitems[groupdepth]].maxwid
|
||||
&& stl_items[stl_groupitems[groupdepth]].type != HighlightFold) {
|
||||
// { Determine the number of bytes to remove
|
||||
int maxwid = stl_items[stl_groupitems[groupdepth]].maxwid;
|
||||
if (group_len > maxwid && stl_items[stl_groupitems[groupdepth]].type != HighlightFold) {
|
||||
stl_truncate(&group_len, minwid, maxwid, fillchar, stl_items,
|
||||
stl_groupitems[groupdepth] + 1, &curitem, t, &out_p);
|
||||
|
||||
// Find the first character that should be included.
|
||||
int n = 0;
|
||||
while (group_len >= 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.
|
||||
int minwid_fixed = MIN(minwid, maxwid);
|
||||
while (++group_len < minwid_fixed) {
|
||||
schar_get_adv(&out_p, fillchar);
|
||||
}
|
||||
// }
|
||||
|
||||
// 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
|
||||
stl_items[idx].start = MAX(stl_items[idx].start, t);
|
||||
}
|
||||
// If the group is shorter than the minimum width, add padding characters.
|
||||
} else if (abs(minwid) > group_len) {
|
||||
ptrdiff_t fillchar_bytes = (ptrdiff_t)schar_len(fillchar);
|
||||
// If the group is left-aligned, add characters to the right.
|
||||
if (minwid < 0) {
|
||||
minwid = 0 - minwid;
|
||||
while (group_len++ < minwid && out_p + fillchar_bytes <= out_end_p) {
|
||||
schar_get_adv(&out_p, fillchar);
|
||||
}
|
||||
// If the group is right-aligned, shift everything to the right and
|
||||
// prepend with filler characters.
|
||||
} else {
|
||||
ptrdiff_t added_cells = minwid - group_len;
|
||||
ptrdiff_t added_bytes = added_cells * fillchar_bytes;
|
||||
if (out_p + added_bytes > out_end_p) {
|
||||
added_cells = (out_end_p - out_p) / fillchar_bytes;
|
||||
added_bytes = added_cells * fillchar_bytes;
|
||||
}
|
||||
} else if (minwid > group_len) {
|
||||
stl_expand(&group_len, minwid, padding, (int)(out_end_p - out_p), fillchar, stl_items,
|
||||
stl_groupitems[groupdepth] + 1, curitem, t, &out_p);
|
||||
}
|
||||
|
||||
// { Move the group to the right
|
||||
memmove(t + added_bytes, t, (size_t)(out_p - t));
|
||||
out_p += added_bytes;
|
||||
// }
|
||||
|
||||
// Prepend the fill characters
|
||||
for (; added_cells > 0; added_cells--) {
|
||||
schar_get_adv(&t, fillchar);
|
||||
}
|
||||
|
||||
// Adjust item start positions
|
||||
for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
|
||||
stl_items[n].start += added_bytes;
|
||||
}
|
||||
// Deactivate separation/truncation markers for wrapping item groups and top-level.
|
||||
for (int n = stl_groupitems[groupdepth] + 1; n < curitem; n++) {
|
||||
if (stl_items[n].type == Separate || stl_items[n].type == Trunc) {
|
||||
stl_items[n].type = Empty;
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
int minwid = 0;
|
||||
int maxwid = 9999;
|
||||
int foldsignitem = -1; // Start of fold or sign item
|
||||
@@ -1915,11 +2072,6 @@ stcsign:
|
||||
}
|
||||
|
||||
*out_p = NUL;
|
||||
// Length of out[] used (excluding the NUL)
|
||||
size_t outputlen = (size_t)(out_p - out);
|
||||
// Subtract offset from `itemcnt` and restore `curitem` to previous recursion level.
|
||||
int itemcnt = curitem - evalstart;
|
||||
curitem = evalstart;
|
||||
|
||||
// Free the format buffer if we allocated it internally
|
||||
if (usefmt != fmt) {
|
||||
@@ -1932,167 +2084,20 @@ stcsign:
|
||||
int width = vim_strsize(out);
|
||||
if (maxwidth > 0 && width > maxwidth && (!stcp || width > MAX_STCWIDTH)) {
|
||||
// Result is too long, must truncate somewhere.
|
||||
int item_idx = evalstart;
|
||||
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[item_idx].start;
|
||||
|
||||
for (int i = evalstart; i < itemcnt + evalstart; 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;
|
||||
while (true) {
|
||||
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 = evalstart; i < itemcnt + evalstart; i++) {
|
||||
if (stl_items[i].start > trunc_p) {
|
||||
for (int j = i; j < itemcnt + evalstart; j++) {
|
||||
if (stl_items[j].type == ClickFunc) {
|
||||
XFREE_CLEAR(stl_items[j].cmd);
|
||||
}
|
||||
}
|
||||
itemcnt = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate the output
|
||||
*trunc_p++ = '>';
|
||||
*trunc_p = NUL;
|
||||
|
||||
// Truncate at the truncation point we found
|
||||
} else {
|
||||
char *end = out + outputlen;
|
||||
|
||||
// { Determine how many bytes to remove
|
||||
int 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;
|
||||
memmove(trunc_p + 1, trunc_end_p, (size_t)(end - trunc_end_p) + 1); // +1 for NUL
|
||||
end -= (size_t)(trunc_end_p - (trunc_p + 1));
|
||||
|
||||
// Put a `<` to mark where we truncated at
|
||||
*trunc_p = '<';
|
||||
// }
|
||||
|
||||
// { 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.
|
||||
int item_offset = trunc_len - 1;
|
||||
|
||||
for (int i = item_idx; i < itemcnt + evalstart; 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;
|
||||
} else {
|
||||
// Anything inside the truncated area is set to start
|
||||
// at the `<` truncation character.
|
||||
stl_items[i].start = trunc_p;
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
if (width + 1 < maxwidth) {
|
||||
// Advance the pointer to the end of the string
|
||||
trunc_p = end;
|
||||
}
|
||||
|
||||
// Fill up for half a double-wide character.
|
||||
while (++width < maxwidth) {
|
||||
schar_get_adv(&trunc_p, fillchar);
|
||||
end = trunc_p;
|
||||
}
|
||||
(void)end;
|
||||
}
|
||||
width = maxwidth;
|
||||
stl_truncate(&width, 0, maxwidth, fillchar, stl_items, evalstart, &curitem, out, &out_p);
|
||||
|
||||
// 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
|
||||
&& outputlen + (size_t)(maxwidth - width) * schar_len(fillchar) + 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 = evalstart; i < itemcnt + evalstart; 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 l = 0; l < num_separators; l++) {
|
||||
int dislocation = (l == (num_separators - 1)) ? final_spaces : standard_spaces;
|
||||
dislocation *= (int)schar_len(fillchar);
|
||||
char *start = stl_items[stl_separator_locations[l]].start;
|
||||
char *seploc = start + dislocation;
|
||||
STRMOVE(seploc, start);
|
||||
for (char *s = start; s < seploc;) {
|
||||
schar_get_adv(&s, fillchar);
|
||||
}
|
||||
|
||||
for (int item_idx = stl_separator_locations[l] + 1;
|
||||
item_idx < itemcnt + evalstart;
|
||||
item_idx++) {
|
||||
stl_items[item_idx].start += dislocation;
|
||||
}
|
||||
}
|
||||
|
||||
width = maxwidth;
|
||||
}
|
||||
// add characters at the separation markers (if there are any) to fill up the available space.
|
||||
} else if (width < maxwidth) {
|
||||
stl_expand(&width, maxwidth, kPaddingNone, (int)(out_end_p - out_p), fillchar, stl_items,
|
||||
evalstart, curitem, out, &out_p);
|
||||
}
|
||||
|
||||
// Store the info about highlighting.
|
||||
if (hltab != NULL) {
|
||||
*hltab = stl_hltab;
|
||||
stl_hlrec_t *sp = stl_hltab;
|
||||
for (int l = evalstart; l < itemcnt + evalstart; l++) {
|
||||
for (int l = evalstart; l < curitem; l++) {
|
||||
if (stl_items[l].type == Highlight || stl_items[l].type == HighlightCombining
|
||||
|| stl_items[l].type == HighlightFold || stl_items[l].type == HighlightSign) {
|
||||
sp->start = stl_items[l].start;
|
||||
@@ -2108,14 +2113,14 @@ stcsign:
|
||||
sp->userhl = 0;
|
||||
}
|
||||
if (hltab_len) {
|
||||
*hltab_len = (size_t)itemcnt;
|
||||
*hltab_len = (size_t)(curitem - evalstart);
|
||||
}
|
||||
|
||||
// Store the info about tab pages labels.
|
||||
if (tabtab != NULL) {
|
||||
*tabtab = stl_tabtab;
|
||||
StlClickRecord *cur_tab_rec = stl_tabtab;
|
||||
for (int l = evalstart; l < itemcnt + evalstart; l++) {
|
||||
for (int l = evalstart; l < curitem; l++) {
|
||||
if (stl_items[l].type == TabPage) {
|
||||
cur_tab_rec->start = stl_items[l].start;
|
||||
if (stl_items[l].minwid == 0) {
|
||||
@@ -2147,6 +2152,9 @@ stcsign:
|
||||
cur_tab_rec->def.func = NULL;
|
||||
}
|
||||
|
||||
// Restore `curitem` to previous recursion level.
|
||||
curitem = evalstart;
|
||||
|
||||
redraw_not_allowed = save_redraw_not_allowed;
|
||||
|
||||
// Check for an error. If there is one the display will be messed up and
|
||||
|
||||
@@ -956,6 +956,20 @@ describe('statusline', function()
|
||||
eq('B:' .. truncated, rendered)
|
||||
end)
|
||||
|
||||
it('truncation inside nested nvim_eval_statusline correctly adjusts highlights', function()
|
||||
exec_lua('vim.o.laststatus = 2')
|
||||
exec_lua([[vim.o.statusline = "%{%repeat('%#Error#',20)%}"]])
|
||||
exec_lua([[
|
||||
vim.o.statusline = "%l%l%l%l%{%nvim_eval_statusline('test%#Error#%l%<',{'maxwidth':4,'highlights':1}).highlights%}"
|
||||
]])
|
||||
screen:expect([[
|
||||
^ |
|
||||
{1:~ }|*5
|
||||
{3:< 'groups': ['StatusLine'], 'start': 0}]}|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
it('no cmdline ruler for autocmd window #39938', function()
|
||||
command('set ruler laststatus=2')
|
||||
api.nvim_create_autocmd('BufDelete', { command = 'redrawstatus' })
|
||||
|
||||
@@ -83,26 +83,28 @@ describe('build_stl_str_hl', function()
|
||||
fillchar = fillchar,
|
||||
}
|
||||
|
||||
eq(expected_stl, get_str(output_buffer, expected_byte_length))
|
||||
local output_str = get_str(output_buffer, expected_byte_length + 1)
|
||||
eq(expected_stl, output_str:sub(1, -2))
|
||||
eq('\0', output_str:sub(-1), 'output string must be NUL-terminated')
|
||||
eq(expected_cell_count, result_cell_count)
|
||||
end)
|
||||
end
|
||||
|
||||
-- expression testing
|
||||
statusline_test('Should expand expression', 2, '%!expand(20+1)', '21')
|
||||
statusline_test('Should expand broken expression to itself', 11, '%!expand(20+1', 'expand(20+1')
|
||||
statusline_test('expands expression', 2, '%!expand(20+1)', '21')
|
||||
statusline_test('expands broken expression to itself', 11, '%!expand(20+1', 'expand(20+1')
|
||||
|
||||
-- file name testing
|
||||
statusline_test('should print no file name', 10, '%f', '[No Name]', { expected_cell_count = 9 })
|
||||
statusline_test('prints no file name', 10, '%f', '[No Name]', { expected_cell_count = 9 })
|
||||
statusline_test(
|
||||
'should print the relative file name',
|
||||
'prints the relative file name',
|
||||
30,
|
||||
'%f',
|
||||
'test/unit/buffer_spec.lua',
|
||||
{ file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 25 }
|
||||
)
|
||||
statusline_test(
|
||||
'should print the full file name',
|
||||
'prints the full file name',
|
||||
40,
|
||||
'%F',
|
||||
'/test/unit/buffer_spec.lua',
|
||||
@@ -110,50 +112,32 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
-- fillchar testing
|
||||
statusline_test('handles `!` as a fillchar', 10, 'abcde%=', 'abcde!!!!!', { fillchar = '!' })
|
||||
statusline_test('handles `~` as a fillchar', 10, '%=abcde', '~~~~~abcde', { fillchar = '~' })
|
||||
statusline_test(
|
||||
'should handle `!` as a fillchar',
|
||||
10,
|
||||
'abcde%=',
|
||||
'abcde!!!!!',
|
||||
{ fillchar = '!' }
|
||||
)
|
||||
statusline_test(
|
||||
'should handle `~` as a fillchar',
|
||||
10,
|
||||
'%=abcde',
|
||||
'~~~~~abcde',
|
||||
{ fillchar = '~' }
|
||||
)
|
||||
statusline_test(
|
||||
'should put fillchar `!` in between text',
|
||||
'puts fillchar `!` in between text',
|
||||
10,
|
||||
'abc%=def',
|
||||
'abc!!!!def',
|
||||
{ fillchar = '!' }
|
||||
)
|
||||
statusline_test(
|
||||
'should put fillchar `~` in between text',
|
||||
'puts fillchar `~` in between text',
|
||||
10,
|
||||
'abc%=def',
|
||||
'abc~~~~def',
|
||||
{ fillchar = '~' }
|
||||
)
|
||||
statusline_test(
|
||||
'should put fillchar `━` in between text',
|
||||
'puts fillchar `━` in between text',
|
||||
10,
|
||||
'abc%=def',
|
||||
'abc━━━━def',
|
||||
{ fillchar = '━' }
|
||||
)
|
||||
statusline_test('handles zero-fillchar as a space', 10, 'abcde%=', 'abcde ', { fillchar = 0 })
|
||||
statusline_test(
|
||||
'should handle zero-fillchar as a space',
|
||||
10,
|
||||
'abcde%=',
|
||||
'abcde ',
|
||||
{ fillchar = 0 }
|
||||
)
|
||||
statusline_test(
|
||||
'should print the tail file name',
|
||||
'prints the tail file name',
|
||||
80,
|
||||
'%t',
|
||||
'buffer_spec.lua',
|
||||
@@ -162,7 +146,7 @@ describe('build_stl_str_hl', function()
|
||||
|
||||
-- standard text testing
|
||||
statusline_test(
|
||||
'should copy plain text',
|
||||
'copies plain text',
|
||||
80,
|
||||
'this is a test',
|
||||
'this is a test',
|
||||
@@ -170,16 +154,16 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
-- line number testing
|
||||
statusline_test('should print the buffer number', 80, '%n', '1', { expected_cell_count = 1 })
|
||||
statusline_test('prints the buffer number', 80, '%n', '1', { expected_cell_count = 1 })
|
||||
statusline_test(
|
||||
'should print the current line number in the buffer',
|
||||
'prints the current line number in the buffer',
|
||||
80,
|
||||
'%l',
|
||||
'0',
|
||||
{ expected_cell_count = 1 }
|
||||
)
|
||||
statusline_test(
|
||||
'should print the number of lines in the buffer',
|
||||
'prints the number of lines in the buffer',
|
||||
80,
|
||||
'%L',
|
||||
'1',
|
||||
@@ -188,55 +172,21 @@ describe('build_stl_str_hl', function()
|
||||
|
||||
-- truncation testing
|
||||
statusline_test(
|
||||
'should truncate when standard text pattern is too long',
|
||||
'truncates when standard text pattern is too long',
|
||||
10,
|
||||
'0123456789abcde',
|
||||
'<6789abcde'
|
||||
)
|
||||
statusline_test('should truncate when using =', 10, 'abcdef%=ghijkl', 'abcdef<jkl')
|
||||
statusline_test(
|
||||
'should truncate centered text when using ==',
|
||||
10,
|
||||
'abcde%=gone%=fghij',
|
||||
'abcde<ghij'
|
||||
)
|
||||
statusline_test('should respect the `<` marker', 10, 'abc%<defghijkl', 'abc<ghijkl')
|
||||
statusline_test(
|
||||
'should truncate at `<` with one `=`, test 1',
|
||||
10,
|
||||
'abc%<def%=ghijklmno',
|
||||
'abc<jklmno'
|
||||
)
|
||||
statusline_test(
|
||||
'should truncate at `<` with one `=`, test 2',
|
||||
10,
|
||||
'abcdef%=ghijkl%<mno',
|
||||
'abcdefghi>'
|
||||
)
|
||||
statusline_test(
|
||||
'should truncate at `<` with one `=`, test 3',
|
||||
10,
|
||||
'abc%<def%=ghijklmno',
|
||||
'abc<jklmno'
|
||||
)
|
||||
statusline_test('should truncate at `<` with one `=`, test 4', 10, 'abc%<def%=ghij', 'abcdefghij')
|
||||
statusline_test(
|
||||
'should truncate at `<` with one `=`, test 4',
|
||||
10,
|
||||
'abc%<def%=ghijk',
|
||||
'abc<fghijk'
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'should truncate at `<` with many `=`, test 4',
|
||||
10,
|
||||
'ab%<cdef%=g%=h%=ijk',
|
||||
'ab<efghijk'
|
||||
)
|
||||
|
||||
statusline_test('should truncate at the first `<`', 10, 'abc%<def%<ghijklm', 'abc<hijklm')
|
||||
|
||||
statusline_test('should ignore trailing %', 3, 'abc%', 'abc')
|
||||
statusline_test('truncates when using =', 10, 'abcdef%=ghijkl', 'abcdef<jkl')
|
||||
statusline_test('truncates centered text when using ==', 10, 'abcde%=gone%=fghij', 'abcde<ghij')
|
||||
statusline_test('respects the `<` marker', 10, 'abc%<defghijkl', 'abc<ghijkl')
|
||||
statusline_test('truncates at `<` with one `=`, test 1', 10, 'abc%<def%=ghijklmno', 'abc<jklmno')
|
||||
statusline_test('truncates at `<` with one `=`, test 2', 10, 'abcdef%=ghijkl%<mno', 'abcdefghi>')
|
||||
statusline_test('truncates at `<` with one `=`, test 3', 10, 'abc%<def%=ghij', 'abcdefghij')
|
||||
statusline_test('truncates at `<` with one `=`, test 4', 10, 'abc%<def%=ghijk', 'abc<fghijk')
|
||||
statusline_test('truncates at `<` with many `=`, test 5', 10, 'ab%<cdef%=g%=h%=ijk', 'ab<efghijk')
|
||||
statusline_test('truncates at the first `<`', 10, 'abc%<def%<ghijklm', 'abc<hijklm')
|
||||
statusline_test('ignores trailing %', 3, 'abc%', 'abc')
|
||||
|
||||
-- alignment testing with fillchar
|
||||
local function statusline_test_align(
|
||||
@@ -272,122 +222,117 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
end
|
||||
|
||||
statusline_test_align('should right align when using =', 20, 'neo%=vim', 'neo~~~~~~~~~~~~~~vim')
|
||||
statusline_test_align('right aligns when using =', 20, 'neo%=vim', 'neo~~~~~~~~~~~~~~vim')
|
||||
statusline_test_align(
|
||||
'should, when possible, center text when using %=text%=',
|
||||
'centers text when using %=text%=',
|
||||
20,
|
||||
'abc%=neovim%=def',
|
||||
'abc~~~~neovim~~~~def'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should handle uneven spacing in the buffer when using %=text%=',
|
||||
'handles uneven spacing in the buffer when using %=text%=',
|
||||
20,
|
||||
'abc%=neo_vim%=def',
|
||||
'abc~~~neo_vim~~~~def'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should have equal spaces even with non-equal sides when using =',
|
||||
'produces approximately equal spaces even with non-equal sides when using =',
|
||||
20,
|
||||
'foobar%=test%=baz',
|
||||
'foobar~~~test~~~~baz'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should have equal spaces even with longer right side when using =',
|
||||
'produces approximately equal spaces even with longer right side when using =',
|
||||
20,
|
||||
'a%=test%=longtext',
|
||||
'a~~~test~~~~longtext'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should handle an empty left side when using ==',
|
||||
'handles an empty left side when using ==',
|
||||
20,
|
||||
'%=test%=baz',
|
||||
'~~~~~~test~~~~~~~baz'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should handle an empty right side when using ==',
|
||||
'handles an empty right side when using ==',
|
||||
20,
|
||||
'foobar%=test%=',
|
||||
'foobar~~~~~test~~~~~'
|
||||
)
|
||||
statusline_test_align('handles consecutive empty ==', 20, '%=%=test%=', '~~~~~~~~~~test~~~~~~')
|
||||
statusline_test_align('handles an = alone', 20, '%=', '~~~~~~~~~~~~~~~~~~~~')
|
||||
statusline_test_align(
|
||||
'should handle consecutive empty ==',
|
||||
20,
|
||||
'%=%=test%=',
|
||||
'~~~~~~~~~~test~~~~~~'
|
||||
)
|
||||
statusline_test_align('should handle an = alone', 20, '%=', '~~~~~~~~~~~~~~~~~~~~')
|
||||
statusline_test_align(
|
||||
'should right align text when it is alone with =',
|
||||
'right aligns text when it is alone with =',
|
||||
20,
|
||||
'%=foo',
|
||||
'~~~~~~~~~~~~~~~~~foo'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should left align text when it is alone with =',
|
||||
'left aligns text when it is alone with =',
|
||||
20,
|
||||
'foo%=',
|
||||
'foo~~~~~~~~~~~~~~~~~'
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should approximately center text when using %=text%=',
|
||||
'approximately centers text when using %=text%=',
|
||||
21,
|
||||
'abc%=neovim%=def',
|
||||
'abc~~~~neovim~~~~~def'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should completely fill the buffer when using %=text%=',
|
||||
'completely fills the buffer when using %=text%=',
|
||||
21,
|
||||
'abc%=neo_vim%=def',
|
||||
'abc~~~~neo_vim~~~~def'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should have equal spacing even with non-equal sides when using =',
|
||||
'produces equal spacing even with non-equal sides when using =',
|
||||
21,
|
||||
'foobar%=test%=baz',
|
||||
'foobar~~~~test~~~~baz'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should have equal spacing even with longer right side when using =',
|
||||
'produces equal spacing even with longer right side when using =',
|
||||
21,
|
||||
'a%=test%=longtext',
|
||||
'a~~~~test~~~~longtext'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should handle an empty left side when using ==',
|
||||
'handles an empty left side when using ==',
|
||||
21,
|
||||
'%=test%=baz',
|
||||
'~~~~~~~test~~~~~~~baz'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should handle an empty right side when using ==',
|
||||
'handles an empty right side when using ==',
|
||||
21,
|
||||
'foobar%=test%=',
|
||||
'foobar~~~~~test~~~~~~'
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should quadrant the text when using 3 %=',
|
||||
'quadrants the text when using 3 %=',
|
||||
40,
|
||||
'abcd%=n%=eovim%=ef',
|
||||
'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~ef'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %t',
|
||||
'works well with %t',
|
||||
40,
|
||||
'%t%=right_aligned',
|
||||
'buffer_spec.lua~~~~~~~~~~~~right_aligned',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %t and regular text',
|
||||
'works well with %t and regular text',
|
||||
40,
|
||||
'l%=m_l %t m_r%=r',
|
||||
'l~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %=, %t, %L, and %l',
|
||||
'works well with %=, %t, %L, and %l',
|
||||
40,
|
||||
'%t %= %L %= %l',
|
||||
'buffer_spec.lua ~~~~~~~~~ 1 ~~~~~~~~~~ 0',
|
||||
@@ -395,27 +340,27 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should quadrant the text when using 3 %=',
|
||||
'quadrants the text when using 3 %=',
|
||||
41,
|
||||
'abcd%=n%=eovim%=ef',
|
||||
'abcd~~~~~~~~~n~~~~~~~~~eovim~~~~~~~~~~~ef'
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %t',
|
||||
'works well with %t',
|
||||
41,
|
||||
'%t%=right_aligned',
|
||||
'buffer_spec.lua~~~~~~~~~~~~~right_aligned',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %t and regular text',
|
||||
'works well with %t and regular text',
|
||||
41,
|
||||
'l%=m_l %t m_r%=r',
|
||||
'l~~~~~~~~m_l buffer_spec.lua m_r~~~~~~~~r',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
statusline_test_align(
|
||||
'should work well with %=, %t, %L, and %l',
|
||||
'works well with %=, %t, %L, and %l',
|
||||
41,
|
||||
'%t %= %L %= %l',
|
||||
'buffer_spec.lua ~~~~~~~~~~ 1 ~~~~~~~~~~ 0',
|
||||
@@ -423,7 +368,7 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should work with 10 %=',
|
||||
'works with 10 %=',
|
||||
50,
|
||||
'aaaa%=b%=c%=d%=e%=fg%=hi%=jk%=lmnop%=qrstuv%=wxyz',
|
||||
'aaaa~~b~~c~~d~~e~~fg~~hi~~jk~~lmnop~~qrstuv~~~wxyz'
|
||||
@@ -432,7 +377,7 @@ describe('build_stl_str_hl', function()
|
||||
-- item group testing
|
||||
|
||||
statusline_test_align(
|
||||
'should right align in right aligned groups',
|
||||
'right aligns in right aligned groups',
|
||||
30,
|
||||
'%5(%l%), %5.(%l%), %5.5(%l%), %5.10(%l%)',
|
||||
'~~~~0, ~~~~0, ~~~~0, ~~~~0',
|
||||
@@ -440,27 +385,114 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'should left align in left aligned groups',
|
||||
'left aligns in left aligned groups',
|
||||
30,
|
||||
'%-5(%l%), %-5.(%l%), %-5.5(%l%), %-5.10(%l%)',
|
||||
'0~~~~, 0~~~~, 0~~~~, 0~~~~',
|
||||
{ expected_cell_count = 26 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'expands at %= in item groups according to minwid',
|
||||
60,
|
||||
'%10(neo%=vim%), %10.(neo%=vim%), %10.10(neo%=vim%), %10.20(neo%=vim%)',
|
||||
'neo~~~~vim, neo~~~~vim, neo~~~~vim, neo~~~~vim',
|
||||
{ expected_cell_count = 46 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'expands at %= in item groups with normal items',
|
||||
60,
|
||||
'%10(%L%=%l,%c%), %10(%=%L%=%l,%c%=%), %10(x%=%L%=%l,%c%=y%)',
|
||||
'1~~~~~~0,0, ~~1~~0,0~~, x~1~0,0~~y',
|
||||
{ expected_cell_count = 34 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'expands at %= in nested item groups and top-level',
|
||||
29,
|
||||
'%21(foo%=%7(x%=y%=z%)%=bar%)%=%5(a%=b%=c%)',
|
||||
'foo~~~~x~~y~~z~~~~bar~~~a~b~c'
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'Should truncate groups according to maxwid',
|
||||
'ignores unexpanded %= in item groups when expanding at top-level',
|
||||
20,
|
||||
'%4(%L%=neo%)vim',
|
||||
'1neovim',
|
||||
{ expected_cell_count = 7 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'ignores truncated %= in item groups when expanding at top-level',
|
||||
20,
|
||||
'%10.10(%<hidden%=%f%)TEST',
|
||||
'<_spec.luaTEST',
|
||||
{ file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 14 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'ignores %= in hidden item groups when expanding at top-level',
|
||||
20,
|
||||
'%(hidden%=%h%)TEST',
|
||||
'TEST',
|
||||
{ expected_cell_count = 4 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'truncates item groups according to maxwid',
|
||||
30,
|
||||
'%.5(1234567%), %2.5(1234567%), %5.5(1234567%)',
|
||||
'<4567, <4567, <4567',
|
||||
{ expected_cell_count = 19 }
|
||||
)
|
||||
|
||||
statusline_test_align(
|
||||
'Should compensate with fillchar after truncating item group at multicell character',
|
||||
20,
|
||||
'%.5(12🙂345%), %5.5(12🙂345%), %50.5(12🙂345%)',
|
||||
'<345, <345~, <345~',
|
||||
{ expected_cell_count = 18 }
|
||||
statusline_test(
|
||||
'truncates at first normal item in item groups',
|
||||
30,
|
||||
'%.15(path: %f%)',
|
||||
'path: <spec.lua',
|
||||
{ file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 15 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'truncates at %< in item groups',
|
||||
60,
|
||||
'%.15(%<path: %f%), %.15(path: %<%f%), %.15(path: %f%<%)',
|
||||
'<uffer_spec.lua, path: <spec.lua, path: test/uni>',
|
||||
{ file_name = 'test/unit/buffer_spec.lua', expected_cell_count = 49 }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'truncates at %< in nested item groups and top-level',
|
||||
40,
|
||||
'%.24(trim both ends: %<%.17(%f%<%)%), path: %<%f',
|
||||
'trim both ends: <buffer>, path: <pec.lua',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'ignores unused %< in item groups when truncating at top-level',
|
||||
25,
|
||||
'%.6(%L%< test%) file: %<%f',
|
||||
'1 test file: <er_spec.lua',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'ignores second %< in item groups when truncating at top-level',
|
||||
32,
|
||||
'%.15(file: %<%f%<%), file: %<%f',
|
||||
'file: <spec.lua, file: <spec.lua',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
|
||||
statusline_test(
|
||||
'ignores %< in hidden item groups when truncating at top-level',
|
||||
15,
|
||||
'%.15(hidden%<%)%f%<',
|
||||
'test/unit/buff>',
|
||||
{ file_name = 'test/unit/buffer_spec.lua' }
|
||||
)
|
||||
|
||||
-- stl item testing
|
||||
@@ -468,30 +500,53 @@ describe('build_stl_str_hl', function()
|
||||
for i = 1, 1000 do
|
||||
tabline = tabline .. (i % 2 == 0 and '%#TabLineSel#' or '%#TabLineFill#') .. tostring(i % 2)
|
||||
end
|
||||
statusline_test('should handle a large amount of any items', 20, tabline, '<1010101010101010101') -- Should not show any error
|
||||
statusline_test('handles a large amount of any items', 20, tabline, '<1010101010101010101') -- Should not show any error
|
||||
statusline_test(
|
||||
'should handle a larger amount of = than stl initial item',
|
||||
'handles a larger amount of = than stl initial item',
|
||||
20,
|
||||
('%='):rep(STL_INITIAL_ITEMS * 5),
|
||||
' '
|
||||
) -- Should not show any error
|
||||
statusline_test(
|
||||
'should handle many extra characters',
|
||||
'handles many extra characters',
|
||||
20,
|
||||
'a' .. ('a'):rep(STL_INITIAL_ITEMS * 5),
|
||||
'<aaaaaaaaaaaaaaaaaaa'
|
||||
) -- Does not show any error
|
||||
statusline_test(
|
||||
'should handle many extra characters and flags',
|
||||
'handles many extra characters and flags',
|
||||
20,
|
||||
'a' .. ('%=a'):rep(STL_INITIAL_ITEMS * 2),
|
||||
'a<aaaaaaaaaaaaaaaaaa'
|
||||
) -- Should not show any error
|
||||
|
||||
-- multi-byte testing
|
||||
statusline_test('should handle multibyte characters', 10, 'Ĉ%=x', 'Ĉ x')
|
||||
-- multi-cell character testing
|
||||
statusline_test(
|
||||
'should handle multibyte characters and different fillchars',
|
||||
'returns reduced width after truncating top-level from the left at multicell character',
|
||||
5,
|
||||
'12🙂345',
|
||||
'<345',
|
||||
{ expected_cell_count = 4 }
|
||||
)
|
||||
statusline_test(
|
||||
'returns reduced width after truncating top-level from the right at multicell character',
|
||||
5,
|
||||
'123🙂45%<',
|
||||
'123>',
|
||||
{ expected_cell_count = 4 }
|
||||
)
|
||||
statusline_test_align(
|
||||
'compensates with fillchar to reach minwid after truncating item group at multicell character',
|
||||
100,
|
||||
'%.5(12🙂345%), %4.5(12🙂345%), %5.5(12🙂345%), %-5.5(12🙂345%), %50.5(12🙂345%), %.5(123🙂%L%<%), %4.5(123🙂%L%<%), %5.5(123🙂%L%<%), %-5.5(123🙂%L%<%), %50.5(123🙂%L%<%)',
|
||||
'<345, <345, <345~, <345~, <345~, 123>, 123>, 123>~, 123>~, 123>~',
|
||||
{ expected_cell_count = 64 }
|
||||
)
|
||||
|
||||
-- multi-byte testing
|
||||
statusline_test('handles multibyte characters', 10, 'Ĉ%=x', 'Ĉ x')
|
||||
statusline_test(
|
||||
'handles multibyte characters and different fillchars',
|
||||
10,
|
||||
'Ą%=mid%=end',
|
||||
'Ą@mid@@end',
|
||||
@@ -499,7 +554,7 @@ describe('build_stl_str_hl', function()
|
||||
)
|
||||
|
||||
-- escaping % testing
|
||||
statusline_test('should handle escape of %', 4, 'abc%%', 'abc%')
|
||||
statusline_test('handles escape of %', 4, 'abc%%', 'abc%')
|
||||
statusline_test('case where escaped % does not fit', 3, 'abc%%abcabc', '<bc')
|
||||
statusline_test('escaped % is first', 1, '%%', '%')
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user