aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/api/vim.c6
-rw-r--r--src/nvim/buffer_defs.h2
-rw-r--r--src/nvim/change.c5
-rw-r--r--src/nvim/charset.c298
-rw-r--r--src/nvim/cursor.c1
-rw-r--r--src/nvim/edit.c7
-rw-r--r--src/nvim/eval/funcs.c7
-rw-r--r--src/nvim/ex_cmds.c3
-rw-r--r--src/nvim/ex_getln.c4
-rw-r--r--src/nvim/fold.c1
-rw-r--r--src/nvim/getchar.c1
-rw-r--r--src/nvim/indent.c1
-rw-r--r--src/nvim/misc1.c172
-rw-r--r--src/nvim/mouse.c31
-rw-r--r--src/nvim/move.c117
-rw-r--r--src/nvim/normal.c9
-rw-r--r--src/nvim/ops.c1
-rw-r--r--src/nvim/plines.c481
-rw-r--r--src/nvim/plines.h9
-rw-r--r--src/nvim/regexp.c1
-rw-r--r--src/nvim/screen.c5
-rw-r--r--src/nvim/window.c1
22 files changed, 603 insertions, 560 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index 58ee53a408..400dbb126c 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2301,7 +2301,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
}
}
}
- ParserLine plines[] = {
+ ParserLine parser_lines[] = {
{
.data = expr.data,
.size = expr.size,
@@ -2309,7 +2309,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
},
{ NULL, 0, false },
};
- ParserLine *plines_p = plines;
+ ParserLine *plines_p = parser_lines;
ParserHighlight colors;
kvi_init(colors);
ParserHighlight *const colors_p = (highlight ? &colors : NULL);
@@ -2335,7 +2335,7 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight,
ret.items[ret.size++] = (KeyValuePair) {
.key = STATIC_CSTR_TO_STRING("len"),
.value = INTEGER_OBJ((Integer)(pstate.pos.line == 1
- ? plines[0].size
+ ? parser_lines[0].size
: pstate.pos.col)),
};
if (east.err.msg != NULL) {
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 1247e48c5f..b03d69a04c 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -1298,7 +1298,7 @@ struct window_S {
/*
* w_cline_height is the number of physical lines taken by the buffer line
- * that the cursor is on. We use this to avoid extra calls to plines().
+ * that the cursor is on. We use this to avoid extra calls to plines_win().
*/
int w_cline_height; // current size of cursor line
bool w_cline_folded; // cursor line is folded
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 49e403425a..41e1e3911b 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -22,6 +22,7 @@
#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
@@ -593,9 +594,9 @@ void ins_char_bytes(char_u *buf, size_t charlen)
// cells. May result in adding spaces to fill a gap.
colnr_T vcol;
getvcol(curwin, &curwin->w_cursor, NULL, &vcol, NULL);
- colnr_T new_vcol = vcol + chartabsize(buf, vcol);
+ colnr_T new_vcol = vcol + win_chartabsize(curwin, buf, vcol);
while (oldp[col + oldlen] != NUL && vcol < new_vcol) {
- vcol += chartabsize(oldp + col + oldlen, vcol);
+ vcol += win_chartabsize(curwin, oldp + col + oldlen, vcol);
// Don't need to remove a TAB that takes us to the right
// position.
if (vcol > new_vcol && oldp[col + oldlen] == TAB) {
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index e2d844a351..0252ef4e9c 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -25,6 +25,7 @@
#include "nvim/move.h"
#include "nvim/option.h"
#include "nvim/os_unix.h"
+#include "nvim/plines.h"
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/path.h"
@@ -733,80 +734,6 @@ int vim_strnsize(char_u *s, int len)
return size;
}
-/// Return the number of characters 'c' will take on the screen, taking
-/// into account the size of a tab.
-/// Use a define to make it fast, this is used very often!!!
-/// Also see getvcol() below.
-///
-/// @param p
-/// @param col
-///
-/// @return Number of characters.
-#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
- if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
- return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
- } else { \
- return ptr2cells(p); \
- }
-
-int chartabsize(char_u *p, colnr_T col)
-{
- RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, p, col)
-}
-
-static int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
-{
- RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, p, col)
-}
-
-/// Return the number of characters the string 's' will take on the screen,
-/// taking into account the size of a tab.
-///
-/// @param s
-///
-/// @return Number of characters the string will take on the screen.
-int linetabsize(char_u *s)
-{
- return linetabsize_col(0, s);
-}
-
-/// Like linetabsize(), but starting at column "startcol".
-///
-/// @param startcol
-/// @param s
-///
-/// @return Number of characters the string will take on the screen.
-int linetabsize_col(int startcol, char_u *s)
-{
- colnr_T col = startcol;
- char_u *line = s; /* pointer to start of line, for breakindent */
-
- while (*s != NUL) {
- col += lbr_chartabsize_adv(line, &s, col);
- }
- return (int)col;
-}
-
-/// Like linetabsize(), but for a given window instead of the current one.
-///
-/// @param wp
-/// @param line
-/// @param len
-///
-/// @return Number of characters the string will take on the screen.
-unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
-{
- colnr_T col = 0;
-
- for (char_u *s = line;
- *s != NUL && (len == MAXCOL || s < line + len);
- MB_PTR_ADV(s)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
- }
-
- return (unsigned int)col;
-}
-
/// Check that "c" is a normal identifier character:
/// Letters and characters from the 'isident' option.
///
@@ -936,229 +863,6 @@ bool vim_isprintc_strict(int c)
return c > 0 && (g_chartab[c] & CT_PRINT_CHAR);
}
-/// like chartabsize(), but also check for line breaks on the screen
-///
-/// @param line
-/// @param s
-/// @param col
-///
-/// @return The number of characters taken up on the screen.
-int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
-{
- if (!curwin->w_p_lbr && (*p_sbr == NUL) && !curwin->w_p_bri) {
- if (curwin->w_p_wrap) {
- return win_nolbr_chartabsize(curwin, s, col, NULL);
- }
- RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col)
- }
- return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL);
-}
-
-/// Call lbr_chartabsize() and advance the pointer.
-///
-/// @param line
-/// @param s
-/// @param col
-///
-/// @return The number of characters take up on the screen.
-int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
-{
- int retval;
-
- retval = lbr_chartabsize(line, *s, col);
- MB_PTR_ADV(*s);
- return retval;
-}
-
-/// This function is used very often, keep it fast!!!!
-///
-/// If "headp" not NULL, set *headp to the size of what we for 'showbreak'
-/// string at start of line. Warning: *headp is only set if it's a non-zero
-/// value, init to 0 before calling.
-///
-/// @param wp
-/// @param line
-/// @param s
-/// @param col
-/// @param headp
-///
-/// @return The number of characters taken up on the screen.
-int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp)
-{
- colnr_T col2;
- colnr_T col_adj = 0; /* col + screen size of tab */
- colnr_T colmax;
- int added;
- int mb_added = 0;
- int numberextra;
- char_u *ps;
- int n;
-
- // No 'linebreak', 'showbreak' and 'breakindent': return quickly.
- if (!wp->w_p_lbr && !wp->w_p_bri && (*p_sbr == NUL)) {
- if (wp->w_p_wrap) {
- return win_nolbr_chartabsize(wp, s, col, headp);
- }
- RET_WIN_BUF_CHARTABSIZE(wp, wp->w_buffer, s, col)
- }
-
- // First get normal size, without 'linebreak'
- int size = win_chartabsize(wp, s, col);
- int c = *s;
- if (*s == TAB) {
- col_adj = size - 1;
- }
-
- // If 'linebreak' set check at a blank before a non-blank if the line
- // needs a break here
- if (wp->w_p_lbr
- && vim_isbreak(c)
- && !vim_isbreak((int)s[1])
- && wp->w_p_wrap
- && (wp->w_width_inner != 0)) {
- // Count all characters from first non-blank after a blank up to next
- // non-blank after a blank.
- numberextra = win_col_off(wp);
- col2 = col;
- colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
-
- if (col >= colmax) {
- colmax += col_adj;
- n = colmax + win_col_off2(wp);
-
- if (n > 0) {
- colmax += (((col - colmax) / n) + 1) * n - col_adj;
- }
- }
-
- for (;;) {
- ps = s;
- MB_PTR_ADV(s);
- c = *s;
-
- if (!(c != NUL
- && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) {
- break;
- }
-
- col2 += win_chartabsize(wp, s, col2);
-
- if (col2 >= colmax) { /* doesn't fit */
- size = colmax - col + col_adj;
- break;
- }
- }
- } else if ((size == 2)
- && (MB_BYTE2LEN(*s) > 1)
- && wp->w_p_wrap
- && in_win_border(wp, col)) {
- // Count the ">" in the last column.
- ++size;
- mb_added = 1;
- }
-
- // May have to add something for 'breakindent' and/or 'showbreak'
- // string at start of line.
- // Set *headp to the size of what we add.
- added = 0;
-
- if ((*p_sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && (col != 0)) {
- colnr_T sbrlen = 0;
- int numberwidth = win_col_off(wp);
-
- numberextra = numberwidth;
- col += numberextra + mb_added;
-
- if (col >= (colnr_T)wp->w_width_inner) {
- col -= wp->w_width_inner;
- numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
- }
- if (*p_sbr != NUL) {
- sbrlen = (colnr_T)MB_CHARLEN(p_sbr);
- if (col >= sbrlen) {
- col -= sbrlen;
- }
- }
- if (col >= numberextra && numberextra > 0) {
- col %= numberextra;
- } else if (col > 0 && numberextra > 0) {
- col += numberwidth - win_col_off2(wp);
- }
-
- numberwidth -= win_col_off2(wp);
- }
-
- if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
- if (*p_sbr != NUL) {
- if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
- // Calculate effective window width.
- int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
- int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
- : 0;
-
- if (width <= 0) {
- width = 1;
- }
- added += ((size - prev_width) / width) * vim_strsize(p_sbr);
- if ((size - prev_width) % width) {
- // Wrapped, add another length of 'sbr'.
- added += vim_strsize(p_sbr);
- }
- } else {
- added += vim_strsize(p_sbr);
- }
- }
-
- if (wp->w_p_bri)
- added += get_breakindent_win(wp, line);
-
- size += added;
- if (col != 0) {
- added = 0;
- }
- }
- }
-
- if (headp != NULL) {
- *headp = added + mb_added;
- }
- return size;
-}
-
-/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and
-/// 'wrap' is on. This means we need to check for a double-byte character that
-/// doesn't fit at the end of the screen line.
-///
-/// @param wp
-/// @param s
-/// @param col
-/// @param headp
-///
-/// @return The number of characters take up on the screen.
-static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
-{
- int n;
-
- if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- return tabstop_padding(col,
- wp->w_buffer->b_p_ts,
- wp->w_buffer->b_p_vts_array);
- }
- n = ptr2cells(s);
-
- // Add one cell for a double-width character in the last column of the
- // window, displayed with a ">".
- if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) {
- if (headp != NULL) {
- *headp = 1;
- }
- return 3;
- }
- return n;
-}
-
/// Check that virtual column "vcol" is in the rightmost column of window "wp".
///
/// @param wp window
diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c
index 5d2210dc7d..d4a68adeda 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -13,6 +13,7 @@
#include "nvim/memory.h"
#include "nvim/misc1.h"
#include "nvim/move.h"
+#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/extmark.h"
#include "nvim/state.h"
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 2305faa20c..fec8da2c3c 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -41,6 +41,7 @@
#include "nvim/option.h"
#include "nvim/path.h"
#include "nvim/popupmnu.h"
+#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
@@ -7187,7 +7188,7 @@ static void replace_do_bs(int limit_col)
// Get the number of screen cells used by the character we are
// going to delete.
getvcol(curwin, &curwin->w_cursor, NULL, &start_vcol, NULL);
- orig_vcols = chartabsize(get_cursor_pos_ptr(), start_vcol);
+ orig_vcols = win_chartabsize(curwin, get_cursor_pos_ptr(), start_vcol);
}
(void)del_char_after_col(limit_col);
if (l_State & VREPLACE_FLAG) {
@@ -7201,8 +7202,8 @@ static void replace_do_bs(int limit_col)
p = get_cursor_pos_ptr();
ins_len = (int)STRLEN(p) - orig_len;
vcol = start_vcol;
- for (i = 0; i < ins_len; ++i) {
- vcol += chartabsize(p + i, vcol);
+ for (i = 0; i < ins_len; i++) {
+ vcol += win_chartabsize(curwin, p + i, vcol);
i += (*mb_ptr2len)(p) - 1;
}
vcol -= start_vcol;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 21c858373c..4e409cca50 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -47,6 +47,7 @@
#include "nvim/os/input.h"
#include "nvim/os/shell.h"
#include "nvim/path.h"
+#include "nvim/plines.h"
#include "nvim/popupmnu.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
@@ -1055,8 +1056,10 @@ static void f_col(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (virtual_active() && fp == &curwin->w_cursor) {
char_u *p = get_cursor_pos_ptr();
- if (curwin->w_cursor.coladd >= (colnr_T)chartabsize(p,
- curwin->w_virtcol - curwin->w_cursor.coladd)) {
+ if (curwin->w_cursor.coladd
+ >= (colnr_T)win_chartabsize(curwin, p,
+ (curwin->w_virtcol
+ - curwin->w_cursor.coladd))) {
int l;
if (*p != NUL && p[(l = (*mb_ptr2len)(p))] == NUL)
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 3ad1fb1adc..041140211e 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -55,6 +55,7 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
@@ -828,7 +829,7 @@ void ex_retab(exarg_T *eap)
}
if (ptr[col] == NUL)
break;
- vcol += chartabsize(ptr + col, (colnr_T)vcol);
+ vcol += win_chartabsize(curwin, ptr + col, (colnr_T)vcol);
col += utfc_ptr2len(ptr + col);
}
if (new_line == NULL) /* out of memory */
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 2b3d773ca4..af8a23f5cc 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -2593,7 +2593,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
ColoredCmdline *const ret_ccline_colors)
FUNC_ATTR_NONNULL_ALL
{
- ParserLine plines[] = {
+ ParserLine parser_lines[] = {
{
.data = (const char *)colored_ccline->cmdbuff,
.size = STRLEN(colored_ccline->cmdbuff),
@@ -2601,7 +2601,7 @@ static void color_expr_cmdline(const CmdlineInfo *const colored_ccline,
},
{ NULL, 0, false },
};
- ParserLine *plines_p = plines;
+ ParserLine *plines_p = parser_lines;
ParserHighlight colors;
kvi_init(colors);
ParserState pstate;
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index ad8418034a..6989c29d57 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -29,6 +29,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/garray.h"
#include "nvim/move.h"
#include "nvim/option.h"
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index b0d06b7a30..f955fe8859 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -34,6 +34,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/keymap.h"
#include "nvim/garray.h"
#include "nvim/move.h"
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index bfb77688b0..a6df0e97e6 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -19,6 +19,7 @@
#include "nvim/misc1.h"
#include "nvim/move.h"
#include "nvim/option.h"
+#include "nvim/plines.h"
#include "nvim/regexp.h"
#include "nvim/screen.h"
#include "nvim/search.h"
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 771174b854..6d94632687 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -349,178 +349,6 @@ int get_last_leader_offset(char_u *line, char_u **flags)
return result;
}
-/*
- * Return the number of window lines occupied by buffer line "lnum".
- */
-int plines(const linenr_T lnum)
-{
- return plines_win(curwin, lnum, true);
-}
-
-int plines_win(
- win_T *const wp,
- const linenr_T lnum,
- const bool winheight // when true limit to window height
-)
-{
- /* Check for filler lines above this buffer line. When folded the result
- * is one line anyway. */
- return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum);
-}
-
-int plines_nofill(const linenr_T lnum)
-{
- return plines_win_nofill(curwin, lnum, true);
-}
-
-int plines_win_nofill(
- win_T *const wp,
- const linenr_T lnum,
- const bool winheight // when true limit to window height
-)
-{
- if (!wp->w_p_wrap) {
- return 1;
- }
-
- if (wp->w_width_inner == 0) {
- return 1;
- }
-
- // A folded lines is handled just like an empty line.
- if (lineFolded(wp, lnum)) {
- return 1;
- }
-
- const int lines = plines_win_nofold(wp, lnum);
- if (winheight && lines > wp->w_height_inner) {
- return wp->w_height_inner;
- }
- return lines;
-}
-
-/*
- * Return number of window lines physical line "lnum" will occupy in window
- * "wp". Does not care about folding, 'wrap' or 'diff'.
- */
-int plines_win_nofold(win_T *wp, linenr_T lnum)
-{
- char_u *s;
- unsigned int col;
- int width;
-
- s = ml_get_buf(wp->w_buffer, lnum, FALSE);
- if (*s == NUL) /* empty line */
- return 1;
- col = win_linetabsize(wp, s, MAXCOL);
-
- // If list mode is on, then the '$' at the end of the line may take up one
- // extra column.
- if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) {
- col += 1;
- }
-
- /*
- * Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
- */
- width = wp->w_width_inner - win_col_off(wp);
- if (width <= 0 || col > 32000) {
- return 32000; // bigger than the number of screen columns
- }
- if (col <= (unsigned int)width) {
- return 1;
- }
- col -= (unsigned int)width;
- width += win_col_off2(wp);
- assert(col <= INT_MAX && (int)col < INT_MAX - (width -1));
- return ((int)col + (width - 1)) / width + 1;
-}
-
-/*
- * Like plines_win(), but only reports the number of physical screen lines
- * used from the start of the line to the given column number.
- */
-int plines_win_col(win_T *wp, linenr_T lnum, long column)
-{
- // Check for filler lines above this buffer line. When folded the result
- // is one line anyway.
- int lines = diff_check_fill(wp, lnum);
-
- if (!wp->w_p_wrap)
- return lines + 1;
-
- if (wp->w_width_inner == 0) {
- return lines + 1;
- }
-
- char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
- char_u *s = line;
-
- colnr_T col = 0;
- while (*s != NUL && --column >= 0) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL);
- MB_PTR_ADV(s);
- }
-
- // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
- // INSERT mode, then col must be adjusted so that it represents the last
- // screen position of the TAB. This only fixes an error when the TAB wraps
- // from one screen line to the next (when 'columns' is not a multiple of
- // 'ts') -- webb.
- if (*s == TAB && (State & NORMAL)
- && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
- col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
- }
-
- // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
- int width = wp->w_width_inner - win_col_off(wp);
- if (width <= 0) {
- return 9999;
- }
-
- lines += 1;
- if (col > width)
- lines += (col - width) / (width + win_col_off2(wp)) + 1;
- return lines;
-}
-
-/// Get the number of screen lines lnum takes up. This takes care of
-/// both folds and topfill, and limits to the current window height.
-///
-/// @param[in] wp window line is in
-/// @param[in] lnum line number
-/// @param[out] nextp if not NULL, the line after a fold
-/// @param[out] foldedp if not NULL, whether lnum is on a fold
-/// @param[in] cache whether to use the window's cache for folds
-///
-/// @return the total number of screen lines
-int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp,
- bool *const foldedp, const bool cache)
-{
- bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL);
- if (foldedp) {
- *foldedp = folded;
- }
- if (folded) {
- return 1;
- } else if (lnum == wp->w_topline) {
- return plines_win_nofill(wp, lnum, true) + wp->w_topfill;
- }
- return plines_win(wp, lnum, true);
-}
-
-int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
-{
- int count = 0;
-
- while (first <= last) {
- linenr_T next = first;
- count += plines_win_full(wp, first, &next, NULL, false);
- first = next + 1;
- }
- return count;
-}
-
int gchar_pos(pos_T *pos)
FUNC_ATTR_NONNULL_ARG(1)
{
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index f1ad0ed105..6c25525936 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -18,6 +18,7 @@
#include "nvim/diff.h"
#include "nvim/move.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/cursor.h"
#include "nvim/buffer_defs.h"
#include "nvim/memline.h"
@@ -236,12 +237,14 @@ retnomove:
if (row < 0) {
count = 0;
for (first = true; curwin->w_topline > 1; ) {
- if (curwin->w_topfill < diff_check(curwin, curwin->w_topline))
- ++count;
- else
- count += plines(curwin->w_topline - 1);
- if (!first && count > -row)
+ if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) {
+ count++;
+ } else {
+ count += plines_win(curwin, curwin->w_topline - 1, true);
+ }
+ if (!first && count > -row) {
break;
+ }
first = false;
(void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL);
if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) {
@@ -262,7 +265,7 @@ retnomove:
if (curwin->w_topfill > 0) {
++count;
} else {
- count += plines(curwin->w_topline);
+ count += plines_win(curwin, curwin->w_topline, true);
}
if (!first && count > row - curwin->w_height_inner + 1) {
@@ -522,7 +525,7 @@ static colnr_T scroll_line_len(linenr_T lnum)
char_u *line = ml_get(lnum);
if (*line != NUL) {
for (;;) {
- int numchar = chartabsize(line, col);
+ int numchar = win_chartabsize(curwin, line, col);
MB_PTR_ADV(line);
if (*line == NUL) { // don't count the last character
break;
@@ -618,10 +621,10 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// scanned *up to* `col`, nudging it left or right when concealed characters
// are encountered.
//
- // chartabsize() is used to keep track of the virtual column position relative
- // to the line's bytes. For example: if col == 9 and the line starts with a
- // tab that's 8 columns wide, we would want the cursor to be highlighting the
- // second byte, not the ninth.
+ // win_chartabsize() is used to keep track of the virtual column position
+ // relative to the line's bytes. For example: if col == 9 and the line
+ // starts with a tab that's 8 columns wide, we would want the cursor to be
+ // highlighting the second byte, not the ninth.
linenr_T lnum = wp->w_cursor.lnum;
char_u *line = ml_get(lnum);
@@ -645,7 +648,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
// checked for concealed characters.
vcol = 0;
while (vcol < offset && *ptr != NUL) {
- vcol += chartabsize(ptr, vcol);
+ vcol += win_chartabsize(curwin, ptr, vcol);
ptr += utfc_ptr2len(ptr);
}
@@ -656,7 +659,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
vcol = offset;
ptr_end = ptr_row_offset;
while (vcol < col && *ptr_end != NUL) {
- vcol += chartabsize(ptr_end, vcol);
+ vcol += win_chartabsize(curwin, ptr_end, vcol);
ptr_end += utfc_ptr2len(ptr_end);
}
@@ -671,7 +674,7 @@ static int mouse_adjust_click(win_T *wp, int row, int col)
#define decr() nudge--; ptr_end -= utfc_ptr2len(ptr_end)
while (ptr < ptr_end && *ptr != NUL) {
- cwidth = chartabsize(ptr, vcol);
+ cwidth = win_chartabsize(curwin, ptr, vcol);
vcol += cwidth;
if (cwidth > 1 && *ptr == '\t' && nudge > 0) {
// A tab will "absorb" any previous adjustments.
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 293f51f2d9..09815d1e6a 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -27,6 +27,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/option.h"
#include "nvim/popupmnu.h"
#include "nvim/screen.h"
@@ -1055,8 +1056,9 @@ bool scrolldown(long line_count, int byfold)
line_count -= curwin->w_topline - first - 1;
curwin->w_botline -= curwin->w_topline - first;
curwin->w_topline = first;
- } else
- done += plines_nofill(curwin->w_topline);
+ } else {
+ done += plines_win_nofill(curwin, curwin->w_topline, true);
+ }
}
--curwin->w_botline; /* approximate w_botline */
invalidate_botline();
@@ -1090,8 +1092,9 @@ bool scrolldown(long line_count, int byfold)
curwin->w_cursor.lnum = 1;
else
curwin->w_cursor.lnum = first - 1;
- } else
- wrow -= plines(curwin->w_cursor.lnum--);
+ } else {
+ wrow -= plines_win(curwin, curwin->w_cursor.lnum--, true);
+ }
curwin->w_valid &=
~(VALID_WROW|VALID_WCOL|VALID_CHEIGHT|VALID_CROW|VALID_VIRTCOL);
moved = true;
@@ -1194,7 +1197,7 @@ check_topfill (
*/
static void max_topfill(void)
{
- int n = plines_nofill(curwin->w_topline);
+ int n = plines_win_nofill(curwin, curwin->w_topline, true);
if (n >= curwin->w_height_inner) {
curwin->w_topfill = 0;
} else {
@@ -1221,19 +1224,16 @@ void scrolldown_clamp(void)
validate_cursor(); /* w_wrow needs to be valid */
- /*
- * Compute the row number of the last row of the cursor line
- * and make sure it doesn't go off the screen. Make sure the cursor
- * doesn't go past 'scrolloff' lines from the screen end.
- */
+ // Compute the row number of the last row of the cursor line
+ // and make sure it doesn't go off the screen. Make sure the cursor
+ // doesn't go past 'scrolloff' lines from the screen end.
int end_row = curwin->w_wrow;
- if (can_fill)
- ++end_row;
- else
- end_row += plines_nofill(curwin->w_topline - 1);
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0
- ) {
+ if (can_fill) {
+ end_row++;
+ } else {
+ end_row += plines_win_nofill(curwin, curwin->w_topline - 1, true);
+ }
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
validate_cheight();
validate_virtcol();
end_row += curwin->w_cline_height - 1 -
@@ -1266,16 +1266,13 @@ void scrollup_clamp(void)
validate_cursor(); /* w_wrow needs to be valid */
- /*
- * Compute the row number of the first row of the cursor line
- * and make sure it doesn't go off the screen. Make sure the cursor
- * doesn't go before 'scrolloff' lines from the screen start.
- */
- int start_row = curwin->w_wrow - plines_nofill(curwin->w_topline)
- - curwin->w_topfill;
- if (curwin->w_p_wrap
- && curwin->w_width_inner != 0
- ) {
+ // Compute the row number of the first row of the cursor line
+ // and make sure it doesn't go off the screen. Make sure the cursor
+ // doesn't go before 'scrolloff' lines from the screen start.
+ int start_row = (curwin->w_wrow
+ - plines_win_nofill(curwin, curwin->w_topline, true)
+ - curwin->w_topfill);
+ if (curwin->w_p_wrap && curwin->w_width_inner != 0) {
validate_virtcol();
start_row -= curwin->w_virtcol / curwin->w_width_inner;
}
@@ -1422,14 +1419,15 @@ void scroll_cursor_top(int min_scroll, int always)
while (top > 0) {
int i = hasFolding(top, &top, NULL)
? 1 // count one logical line for a sequence of folded lines
- : plines_nofill(top);
+ : plines_win_nofill(curwin, top, true);
used += i;
if (extra + i <= off && bot < curbuf->b_ml.ml_line_count) {
- if (hasFolding(bot, NULL, &bot))
- /* count one logical line for a sequence of folded lines */
- ++used;
- else
- used += plines(bot);
+ if (hasFolding(bot, NULL, &bot)) {
+ // count one logical line for a sequence of folded lines
+ used++;
+ } else {
+ used += plines_win(curwin, bot, true);
+ }
}
if (used > curwin->w_height_inner) {
break;
@@ -1555,12 +1553,12 @@ void scroll_cursor_bot(int min_scroll, int set_topbot)
validate_botline(curwin);
}
- /* The lines of the cursor line itself are always used. */
- used = plines_nofill(cln);
+ // The lines of the cursor line itself are always used.
+ used = plines_win_nofill(curwin, cln, true);
- /* If the cursor is below botline, we will at least scroll by the height
- * of the cursor line. Correct for empty lines, which are really part of
- * botline. */
+ // If the cursor is below botline, we will at least scroll by the height
+ // of the cursor line. Correct for empty lines, which are really part of
+ // botline.
if (cln >= curwin->w_botline) {
scrolled = used;
if (cln == curwin->w_botline)
@@ -1704,7 +1702,7 @@ void scroll_cursor_halfway(int atend)
loff.lnum = boff.lnum = curwin->w_cursor.lnum;
(void)hasFolding(loff.lnum, &loff.lnum, &boff.lnum);
- int used = plines_nofill(loff.lnum);
+ int used = plines_win_nofill(curwin, loff.lnum, true);
loff.fill = 0;
boff.fill = 0;
linenr_T topline = loff.lnum;
@@ -1809,17 +1807,19 @@ void cursor_correct(void)
int below = curwin->w_filler_rows; /* screen lines below botline */
while ((above < above_wanted || below < below_wanted) && topline < botline) {
if (below < below_wanted && (below <= above || above >= above_wanted)) {
- if (hasFolding(botline, &botline, NULL))
- ++below;
- else
- below += plines(botline);
- --botline;
+ if (hasFolding(botline, &botline, NULL)) {
+ below++;
+ } else {
+ below += plines_win(curwin, botline, true);
+ }
+ botline--;
}
if (above < above_wanted && (above < below || below >= below_wanted)) {
- if (hasFolding(topline, NULL, &topline))
- ++above;
- else
- above += plines_nofill(topline);
+ if (hasFolding(topline, NULL, &topline)) {
+ above++;
+ } else {
+ above += plines_win_nofill(curwin, topline, true);
+ }
/* Count filler lines below this line as context. */
if (topline < botline)
@@ -2047,10 +2047,11 @@ static void get_scroll_overlap(lineoff_T *lp, int dir)
{
int min_height = curwin->w_height_inner - 2;
- if (lp->fill > 0)
+ if (lp->fill > 0) {
lp->height = 1;
- else
- lp->height = plines_nofill(lp->lnum);
+ } else {
+ lp->height = plines_win_nofill(curwin, lp->lnum, true);
+ }
int h1 = lp->height;
if (h1 > min_height)
return; /* no overlap */
@@ -2120,7 +2121,7 @@ void halfpage(bool flag, linenr_T Prenum)
n--;
curwin->w_topfill--;
} else {
- i = plines_nofill(curwin->w_topline);
+ i = plines_win_nofill(curwin, curwin->w_topline, true);
n -= i;
if (n < 0 && scrolled > 0)
break;
@@ -2146,12 +2147,12 @@ void halfpage(bool flag, linenr_T Prenum)
else {
room += i;
do {
- i = plines(curwin->w_botline);
- if (i > room)
+ i = plines_win(curwin, curwin->w_botline, true);
+ if (i > room) {
break;
- (void)hasFolding(curwin->w_botline, NULL,
- &curwin->w_botline);
- ++curwin->w_botline;
+ }
+ (void)hasFolding(curwin->w_botline, NULL, &curwin->w_botline);
+ curwin->w_botline++;
room -= i;
} while (curwin->w_botline <= curbuf->b_ml.ml_line_count);
}
@@ -2180,7 +2181,7 @@ void halfpage(bool flag, linenr_T Prenum)
n--;
curwin->w_topfill++;
} else {
- i = plines_nofill(curwin->w_topline - 1);
+ i = plines_win_nofill(curwin, curwin->w_topline - 1, true);
n -= i;
if (n < 0 && scrolled > 0)
break;
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index 54ca216a53..2a530db934 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -45,6 +45,7 @@
#include "nvim/mouse.h"
#include "nvim/ops.h"
#include "nvim/option.h"
+#include "nvim/plines.h"
#include "nvim/quickfix.h"
#include "nvim/screen.h"
#include "nvim/search.h"
@@ -5122,11 +5123,13 @@ static void nv_scroll(cmdarg_T *cap)
--n;
break;
}
- used += plines(curwin->w_topline + n);
- if (used >= half)
+ used += plines_win(curwin, curwin->w_topline + n, true);
+ if (used >= half) {
break;
- if (hasFolding(curwin->w_topline + n, NULL, &lnum))
+ }
+ if (hasFolding(curwin->w_topline + n, NULL, &lnum)) {
n = lnum - curwin->w_topline;
+ }
}
if (n > 0 && used > curwin->w_height_inner) {
n--;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index c51dd09d40..a6eda26d75 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -41,6 +41,7 @@
#include "nvim/normal.h"
#include "nvim/option.h"
#include "nvim/path.h"
+#include "nvim/plines.h"
#include "nvim/screen.h"
#include "nvim/search.h"
#include "nvim/state.h"
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
new file mode 100644
index 0000000000..6718b7f7a4
--- /dev/null
+++ b/src/nvim/plines.c
@@ -0,0 +1,481 @@
+// This is an open source non-commercial project. Dear PVS-Studio, please check
+// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
+
+// plines.c: calculate the vertical and horizontal size of text in a window
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <string.h>
+#include <limits.h>
+
+#include "nvim/vim.h"
+#include "nvim/ascii.h"
+#include "nvim/plines.h"
+#include "nvim/charset.h"
+#include "nvim/cursor.h"
+#include "nvim/diff.h"
+#include "nvim/func_attr.h"
+#include "nvim/fold.h"
+#include "nvim/indent.h"
+#include "nvim/main.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline.h"
+#include "nvim/memory.h"
+#include "nvim/move.h"
+#include "nvim/option.h"
+#include "nvim/screen.h"
+#include "nvim/strings.h"
+#include "nvim/window.h"
+#include "nvim/buffer.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "plines.c.generated.h"
+#endif
+
+/// Functions calculating vertical size of text when displayed inside a window.
+/// Calls horizontal size functions defined below.
+
+/// @param winheight when true limit to window height
+int plines_win(win_T *wp, linenr_T lnum, bool winheight)
+{
+ // Check for filler lines above this buffer line. When folded the result
+ // is one line anyway.
+ return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum);
+}
+
+/// @param winheight when true limit to window height
+int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight)
+{
+ if (!wp->w_p_wrap) {
+ return 1;
+ }
+
+ if (wp->w_width_inner == 0) {
+ return 1;
+ }
+
+ // A folded lines is handled just like an empty line.
+ if (lineFolded(wp, lnum)) {
+ return 1;
+ }
+
+ const int lines = plines_win_nofold(wp, lnum);
+ if (winheight && lines > wp->w_height_inner) {
+ return wp->w_height_inner;
+ }
+ return lines;
+}
+
+/// @Return number of window lines physical line "lnum" will occupy in window
+/// "wp". Does not care about folding, 'wrap' or 'diff'.
+int plines_win_nofold(win_T *wp, linenr_T lnum)
+{
+ char_u *s;
+ unsigned int col;
+ int width;
+
+ s = ml_get_buf(wp->w_buffer, lnum, false);
+ if (*s == NUL) { // empty line
+ return 1;
+ }
+ col = win_linetabsize(wp, s, MAXCOL);
+
+ // If list mode is on, then the '$' at the end of the line may take up one
+ // extra column.
+ if (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL) {
+ col += 1;
+ }
+
+ // Add column offset for 'number', 'relativenumber' and 'foldcolumn'.
+ width = wp->w_width_inner - win_col_off(wp);
+ if (width <= 0 || col > 32000) {
+ return 32000; // bigger than the number of screen columns
+ }
+ if (col <= (unsigned int)width) {
+ return 1;
+ }
+ col -= (unsigned int)width;
+ width += win_col_off2(wp);
+ assert(col <= INT_MAX && (int)col < INT_MAX - (width -1));
+ return ((int)col + (width - 1)) / width + 1;
+}
+
+/// Like plines_win(), but only reports the number of physical screen lines
+/// used from the start of the line to the given column number.
+int plines_win_col(win_T *wp, linenr_T lnum, long column)
+{
+ // Check for filler lines above this buffer line. When folded the result
+ // is one line anyway.
+ int lines = diff_check_fill(wp, lnum);
+
+ if (!wp->w_p_wrap) {
+ return lines + 1;
+ }
+
+ if (wp->w_width_inner == 0) {
+ return lines + 1;
+ }
+
+ char_u *line = ml_get_buf(wp->w_buffer, lnum, false);
+ char_u *s = line;
+
+ colnr_T col = 0;
+ while (*s != NUL && --column >= 0) {
+ col += win_lbr_chartabsize(wp, line, s, col, NULL);
+ MB_PTR_ADV(s);
+ }
+
+ // If *s is a TAB, and the TAB is not displayed as ^I, and we're not in
+ // INSERT mode, then col must be adjusted so that it represents the last
+ // screen position of the TAB. This only fixes an error when the TAB wraps
+ // from one screen line to the next (when 'columns' is not a multiple of
+ // 'ts') -- webb.
+ if (*s == TAB && (State & NORMAL)
+ && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ col += win_lbr_chartabsize(wp, line, s, col, NULL) - 1;
+ }
+
+ // Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
+ int width = wp->w_width_inner - win_col_off(wp);
+ if (width <= 0) {
+ return 9999;
+ }
+
+ lines += 1;
+ if (col > width) {
+ lines += (col - width) / (width + win_col_off2(wp)) + 1;
+ }
+ return lines;
+}
+
+/// Get the number of screen lines lnum takes up. This takes care of
+/// both folds and topfill, and limits to the current window height.
+///
+/// @param[in] wp window line is in
+/// @param[in] lnum line number
+/// @param[out] nextp if not NULL, the line after a fold
+/// @param[out] foldedp if not NULL, whether lnum is on a fold
+/// @param[in] cache whether to use the window's cache for folds
+///
+/// @return the total number of screen lines
+int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp,
+ bool *const foldedp, const bool cache)
+{
+ bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL);
+ if (foldedp) {
+ *foldedp = folded;
+ }
+ if (folded) {
+ return 1;
+ } else if (lnum == wp->w_topline) {
+ return plines_win_nofill(wp, lnum, true) + wp->w_topfill;
+ }
+ return plines_win(wp, lnum, true);
+}
+
+int plines_m_win(win_T *wp, linenr_T first, linenr_T last)
+{
+ int count = 0;
+
+ while (first <= last) {
+ linenr_T next = first;
+ count += plines_win_full(wp, first, &next, NULL, false);
+ first = next + 1;
+ }
+ return count;
+}
+
+/// Functions calculating horizontal size of text, when displayed in a window.
+
+/// Return the number of characters 'c' will take on the screen, taking
+/// into account the size of a tab.
+/// Also see getvcol()
+///
+/// @param p
+/// @param col
+///
+/// @return Number of characters.
+int win_chartabsize(win_T *wp, char_u *p, colnr_T col)
+{
+ buf_T *buf = wp->w_buffer;
+ if (*p == TAB && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ return tabstop_padding(col, buf->b_p_ts, buf->b_p_vts_array);
+ } else {
+ return ptr2cells(p);
+ }
+}
+
+/// Return the number of characters the string 's' will take on the screen,
+/// taking into account the size of a tab.
+///
+/// @param s
+///
+/// @return Number of characters the string will take on the screen.
+int linetabsize(char_u *s)
+{
+ return linetabsize_col(0, s);
+}
+
+/// Like linetabsize(), but starting at column "startcol".
+///
+/// @param startcol
+/// @param s
+///
+/// @return Number of characters the string will take on the screen.
+int linetabsize_col(int startcol, char_u *s)
+{
+ colnr_T col = startcol;
+ char_u *line = s; // pointer to start of line, for breakindent
+
+ while (*s != NUL) {
+ col += lbr_chartabsize_adv(line, &s, col);
+ }
+ return (int)col;
+}
+
+/// Like linetabsize(), but for a given window instead of the current one.
+///
+/// @param wp
+/// @param line
+/// @param len
+///
+/// @return Number of characters the string will take on the screen.
+unsigned int win_linetabsize(win_T *wp, char_u *line, colnr_T len)
+{
+ colnr_T col = 0;
+
+ for (char_u *s = line;
+ *s != NUL && (len == MAXCOL || s < line + len);
+ MB_PTR_ADV(s)) {
+ col += win_lbr_chartabsize(wp, line, s, col, NULL);
+ }
+
+ return (unsigned int)col;
+}
+
+/// like win_chartabsize(), but also check for line breaks on the screen
+///
+/// @param line
+/// @param s
+/// @param col
+///
+/// @return The number of characters taken up on the screen.
+int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col)
+{
+ if (!curwin->w_p_lbr && (*p_sbr == NUL) && !curwin->w_p_bri) {
+ if (curwin->w_p_wrap) {
+ return win_nolbr_chartabsize(curwin, s, col, NULL);
+ }
+ return win_chartabsize(curwin, s, col);
+ }
+ return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL);
+}
+
+/// Call lbr_chartabsize() and advance the pointer.
+///
+/// @param line
+/// @param s
+/// @param col
+///
+/// @return The number of characters take up on the screen.
+int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col)
+{
+ int retval;
+
+ retval = lbr_chartabsize(line, *s, col);
+ MB_PTR_ADV(*s);
+ return retval;
+}
+
+/// This function is used very often, keep it fast!!!!
+///
+/// If "headp" not NULL, set *headp to the size of what we for 'showbreak'
+/// string at start of line. Warning: *headp is only set if it's a non-zero
+/// value, init to 0 before calling.
+///
+/// @param wp
+/// @param line
+/// @param s
+/// @param col
+/// @param headp
+///
+/// @return The number of characters taken up on the screen.
+int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s,
+ colnr_T col, int *headp)
+{
+ colnr_T col2;
+ colnr_T col_adj = 0; // col + screen size of tab
+ colnr_T colmax;
+ int added;
+ int mb_added = 0;
+ int numberextra;
+ char_u *ps;
+ int n;
+
+ // No 'linebreak', 'showbreak' and 'breakindent': return quickly.
+ if (!wp->w_p_lbr && !wp->w_p_bri && (*p_sbr == NUL)) {
+ if (wp->w_p_wrap) {
+ return win_nolbr_chartabsize(wp, s, col, headp);
+ }
+ return win_chartabsize(wp, s, col);
+ }
+
+ // First get normal size, without 'linebreak'
+ int size = win_chartabsize(wp, s, col);
+ int c = *s;
+ if (*s == TAB) {
+ col_adj = size - 1;
+ }
+
+ // If 'linebreak' set check at a blank before a non-blank if the line
+ // needs a break here
+ if (wp->w_p_lbr
+ && vim_isbreak(c)
+ && !vim_isbreak((int)s[1])
+ && wp->w_p_wrap
+ && (wp->w_width_inner != 0)) {
+ // Count all characters from first non-blank after a blank up to next
+ // non-blank after a blank.
+ numberextra = win_col_off(wp);
+ col2 = col;
+ colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj);
+
+ if (col >= colmax) {
+ colmax += col_adj;
+ n = colmax + win_col_off2(wp);
+
+ if (n > 0) {
+ colmax += (((col - colmax) / n) + 1) * n - col_adj;
+ }
+ }
+
+ for (;;) {
+ ps = s;
+ MB_PTR_ADV(s);
+ c = *s;
+
+ if (!(c != NUL
+ && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) {
+ break;
+ }
+
+ col2 += win_chartabsize(wp, s, col2);
+
+ if (col2 >= colmax) { // doesn't fit
+ size = colmax - col + col_adj;
+ break;
+ }
+ }
+ } else if ((size == 2)
+ && (MB_BYTE2LEN(*s) > 1)
+ && wp->w_p_wrap
+ && in_win_border(wp, col)) {
+ // Count the ">" in the last column.
+ size++;
+ mb_added = 1;
+ }
+
+ // May have to add something for 'breakindent' and/or 'showbreak'
+ // string at start of line.
+ // Set *headp to the size of what we add.
+ added = 0;
+
+ if ((*p_sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && (col != 0)) {
+ colnr_T sbrlen = 0;
+ int numberwidth = win_col_off(wp);
+
+ numberextra = numberwidth;
+ col += numberextra + mb_added;
+
+ if (col >= (colnr_T)wp->w_width_inner) {
+ col -= wp->w_width_inner;
+ numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp));
+ if (col >= numberextra && numberextra > 0) {
+ col %= numberextra;
+ }
+ if (*p_sbr != NUL) {
+ sbrlen = (colnr_T)MB_CHARLEN(p_sbr);
+ if (col >= sbrlen) {
+ col -= sbrlen;
+ }
+ }
+ if (col >= numberextra && numberextra > 0) {
+ col %= numberextra;
+ } else if (col > 0 && numberextra > 0) {
+ col += numberwidth - win_col_off2(wp);
+ }
+
+ numberwidth -= win_col_off2(wp);
+ }
+
+ if (col == 0 || (col + size + sbrlen > (colnr_T)wp->w_width_inner)) {
+ if (*p_sbr != NUL) {
+ if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) {
+ // Calculate effective window width.
+ int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth;
+ int prev_width = col ? ((colnr_T)wp->w_width_inner - (sbrlen + col))
+ : 0;
+
+ if (width <= 0) {
+ width = 1;
+ }
+ added += ((size - prev_width) / width) * vim_strsize(p_sbr);
+ if ((size - prev_width) % width) {
+ // Wrapped, add another length of 'sbr'.
+ added += vim_strsize(p_sbr);
+ }
+ } else {
+ added += vim_strsize(p_sbr);
+ }
+ }
+
+ if (wp->w_p_bri) {
+ added += get_breakindent_win(wp, line);
+ }
+
+ size += added;
+ if (col != 0) {
+ added = 0;
+ }
+ }
+ }
+
+ if (headp != NULL) {
+ *headp = added + mb_added;
+ }
+ return size;
+}
+
+/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and
+/// 'wrap' is on. This means we need to check for a double-byte character that
+/// doesn't fit at the end of the screen line.
+///
+/// @param wp
+/// @param s
+/// @param col
+/// @param headp
+///
+/// @return The number of characters take up on the screen.
+static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
+{
+ int n;
+
+ if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
+ return tabstop_padding(col,
+ wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array);
+ }
+ n = ptr2cells(s);
+
+ // Add one cell for a double-width character in the last column of the
+ // window, displayed with a ">".
+ if ((n == 2) && (MB_BYTE2LEN(*s) > 1) && in_win_border(wp, col)) {
+ if (headp != NULL) {
+ *headp = 1;
+ }
+ return 3;
+ }
+ return n;
+}
+
diff --git a/src/nvim/plines.h b/src/nvim/plines.h
new file mode 100644
index 0000000000..32778b69f1
--- /dev/null
+++ b/src/nvim/plines.h
@@ -0,0 +1,9 @@
+#ifndef NVIM_PLINES_H
+#define NVIM_PLINES_H
+
+#include "nvim/vim.h"
+
+#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "plines.h.generated.h"
+#endif
+#endif // NVIM_PLINES_H
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index c2ef217638..6379174938 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -63,6 +63,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/garray.h"
#include "nvim/strings.h"
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 2fc5777937..38ed2816ba 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -101,6 +101,7 @@
#include "nvim/option.h"
#include "nvim/os_unix.h"
#include "nvim/path.h"
+#include "nvim/plines.h"
#include "nvim/popupmnu.h"
#include "nvim/quickfix.h"
#include "nvim/regexp.h"
@@ -1694,7 +1695,7 @@ static void win_update(win_T *wp, Providers *providers)
/*
* There is a trick with w_botline. If we invalidate it on each
* change that might modify it, this will cause a lot of expensive
- * calls to plines() in update_topline() each time. Therefore the
+ * calls to plines_win() in update_topline() each time. Therefore the
* value of w_botline is often approximated, and this value is used to
* compute the value of w_topline. If the value of w_botline was
* wrong, check that the value of w_topline is correct (cursor is on
@@ -4119,7 +4120,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
/*
* Update w_cline_height and w_cline_folded if the cursor line was
- * updated (saves a call to plines() later).
+ * updated (saves a call to plines_win() later).
*/
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
curwin->w_cline_row = startrow;
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 2f43faef50..fe6ab5af55 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -31,6 +31,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
+#include "nvim/plines.h"
#include "nvim/file_search.h"
#include "nvim/garray.h"
#include "nvim/move.h"