diff options
Diffstat (limited to 'src/nvim/plines.c')
-rw-r--r-- | src/nvim/plines.c | 299 |
1 files changed, 298 insertions, 1 deletions
diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 60d194d781..6718b7f7a4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -1,7 +1,7 @@ // 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: functions that calculate the vertical and horizontal size of text +// plines.c: calculate the vertical and horizontal size of text in a window #include <assert.h> #include <inttypes.h> @@ -17,6 +17,7 @@ #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" @@ -32,6 +33,8 @@ # 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) @@ -182,3 +185,297 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) } 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; +} + |