diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/charset.c | 299 | ||||
-rw-r--r-- | src/nvim/charset.h | 3 | ||||
-rw-r--r-- | src/nvim/indent_c.c | 1 | ||||
-rw-r--r-- | src/nvim/plines.c | 709 | ||||
-rw-r--r-- | src/nvim/plines.h | 4 | ||||
-rw-r--r-- | src/nvim/search.c | 1 | ||||
-rw-r--r-- | src/nvim/statusline.c | 1 |
7 files changed, 512 insertions, 506 deletions
diff --git a/src/nvim/charset.c b/src/nvim/charset.c index ce49f2ad86..2756a60d60 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -18,24 +18,17 @@ #include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/cursor.h" -#include "nvim/eval/typval.h" #include "nvim/eval/typval_defs.h" #include "nvim/garray.h" #include "nvim/globals.h" #include "nvim/grid_defs.h" -#include "nvim/indent.h" #include "nvim/keycodes.h" #include "nvim/macros.h" -#include "nvim/mark.h" #include "nvim/mbyte.h" -#include "nvim/memline.h" #include "nvim/memory.h" -#include "nvim/move.h" #include "nvim/option.h" #include "nvim/path.h" -#include "nvim/plines.h" #include "nvim/pos.h" -#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/vim.h" @@ -921,298 +914,6 @@ bool vim_isprintc_strict(int c) return c > 0 && (g_chartab[c] & CT_PRINT_CHAR); } -/// Check that virtual column "vcol" is in the rightmost column of window "wp". -/// -/// @param wp window -/// @param vcol column number -bool in_win_border(win_T *wp, colnr_T vcol) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) -{ - if (wp->w_width_inner == 0) { - // there is no border - return false; - } - int width1 = wp->w_width_inner - win_col_off(wp); // width of first line (after line number) - - if ((int)vcol < width1 - 1) { - return false; - } - - if ((int)vcol == width1 - 1) { - return true; - } - int width2 = width1 + win_col_off2(wp); // width of further lines - - if (width2 <= 0) { - return false; - } - return (vcol - width1) % width2 == width2 - 1; -} - -/// Get virtual column number of pos. -/// start: on the first position of this character (TAB, ctrl) -/// cursor: where the cursor is on this character (first char, except for TAB) -/// end: on the last position of this character (TAB, ctrl) -/// -/// This is used very often, keep it fast! -/// -/// @param wp -/// @param pos -/// @param start -/// @param cursor -/// @param end -void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) -{ - char *ptr; // points to current char - char *posptr; // points to char at pos->col - int incr; - int head; - long *vts = wp->w_buffer->b_p_vts_array; - int ts = (int)wp->w_buffer->b_p_ts; - - colnr_T vcol = 0; - char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line - - if (pos->col == MAXCOL) { - // continue until the NUL - posptr = NULL; - } else { - // In a few cases the position can be beyond the end of the line. - for (colnr_T i = 0; i < pos->col; i++) { - if (ptr[i] == NUL) { - pos->col = i; - break; - } - } - posptr = ptr + pos->col; - posptr -= utf_head_off(line, posptr); - } - - chartabsize_T cts; - bool on_NUL = false; - init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); - cts.cts_max_head_vcol = -1; - - // This function is used very often, do some speed optimizations. - // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set - // and there are no virtual text use a simple loop. - // Also use this when 'list' is set but tabs take their normal size. - if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) - && !wp->w_p_lbr - && *get_showbreak_value(wp) == NUL - && !wp->w_p_bri - && !cts.cts_has_virt_text) { - while (true) { - head = 0; - int c = (uint8_t)(*ptr); - - // make sure we don't go past the end of the line - if (c == NUL) { - // NUL at end of line only takes one column - incr = 1; - break; - } - - // A tab gets expanded, depending on the current column - if (c == TAB) { - incr = tabstop_padding(vcol, ts, vts); - } else { - // For utf-8, if the byte is >= 0x80, need to look at - // further bytes to find the cell width. - if (c >= 0x80) { - incr = utf_ptr2cells(ptr); - } else { - incr = g_chartab[c] & CT_CELL_MASK; - } - - // If a double-cell char doesn't fit at the end of a line - // it wraps to the next line, it's like this char is three - // cells wide. - if ((incr == 2) - && wp->w_p_wrap - && (MB_BYTE2LEN((uint8_t)(*ptr)) > 1) - && in_win_border(wp, vcol)) { - incr++; - head = 1; - } - } - - if ((posptr != NULL) && (ptr >= posptr)) { - // character at pos->col - break; - } - - vcol += incr; - MB_PTR_ADV(ptr); - } - } else { - while (true) { - // A tab gets expanded, depending on the current column - // Other things also take up space. - head = 0; - incr = win_lbr_chartabsize(&cts, &head); - - // make sure we don't go past the end of the line - if (*cts.cts_ptr == NUL) { - // NUL at end of line only takes one column, unless there is virtual text - incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); - on_NUL = true; - break; - } - - if ((posptr != NULL) && (cts.cts_ptr >= posptr)) { - // character at pos->col - break; - } - - cts.cts_vcol += incr; - MB_PTR_ADV(cts.cts_ptr); - } - vcol = cts.cts_vcol; - ptr = cts.cts_ptr; - } - clear_chartabsize_arg(&cts); - - if (start != NULL) { - *start = vcol + head; - } - - if (end != NULL) { - *end = vcol + incr - 1; - } - - if (cursor != NULL) { - if ((*ptr == TAB) - && (State & MODE_NORMAL) - && !wp->w_p_list - && !virtual_active() - && !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) { - // cursor at end - *cursor = vcol + incr - 1; - } else { - if (!on_NUL || !(State & MODE_NORMAL)) { - vcol += cts.cts_cur_text_width_left; - } - if (!on_NUL && (State & MODE_NORMAL)) { - vcol += cts.cts_cur_text_width_right; - } - // cursor at start - *cursor = vcol + head; - } - } -} - -/// Get virtual cursor column in the current window, pretending 'list' is off. -/// -/// @param posp -/// -/// @retujrn The virtual cursor column. -colnr_T getvcol_nolist(pos_T *posp) -{ - int list_save = curwin->w_p_list; - colnr_T vcol; - - curwin->w_p_list = false; - if (posp->coladd) { - getvvcol(curwin, posp, NULL, &vcol, NULL); - } else { - getvcol(curwin, posp, NULL, &vcol, NULL); - } - curwin->w_p_list = list_save; - return vcol; -} - -/// Get virtual column in virtual mode. -/// -/// @param wp -/// @param pos -/// @param start -/// @param cursor -/// @param end -void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) -{ - colnr_T col; - - if (virtual_active()) { - // For virtual mode, only want one value - getvcol(wp, pos, &col, NULL, NULL); - - colnr_T coladd = pos->coladd; - colnr_T endadd = 0; - - // Cannot put the cursor on part of a wide character. - char *ptr = ml_get_buf(wp->w_buffer, pos->lnum); - - if (pos->col < (colnr_T)strlen(ptr)) { - int c = utf_ptr2char(ptr + pos->col); - if ((c != TAB) && vim_isprintc(c)) { - endadd = (colnr_T)(char2cells(c) - 1); - if (coladd > endadd) { - // past end of line - endadd = 0; - } else { - coladd = 0; - } - } - } - col += coladd; - - if (start != NULL) { - *start = col; - } - - if (cursor != NULL) { - *cursor = col; - } - - if (end != NULL) { - *end = col + endadd; - } - } else { - getvcol(wp, pos, start, cursor, end); - } -} - -/// Get the leftmost and rightmost virtual column of pos1 and pos2. -/// Used for Visual block mode. -/// -/// @param wp -/// @param pos1 -/// @param pos2 -/// @param left -/// @param right -void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right) -{ - colnr_T from1; - colnr_T from2; - colnr_T to1; - colnr_T to2; - - if (lt(*pos1, *pos2)) { - getvvcol(wp, pos1, &from1, NULL, &to1); - getvvcol(wp, pos2, &from2, NULL, &to2); - } else { - getvvcol(wp, pos2, &from1, NULL, &to1); - getvvcol(wp, pos1, &from2, NULL, &to2); - } - - if (from2 < from1) { - *left = from2; - } else { - *left = from1; - } - - if (to2 > to1) { - if ((*p_sel == 'e') && (from2 - 1 >= to1)) { - *right = from2 - 1; - } else { - *right = to2; - } - } else { - *right = to1; - } -} - /// skipwhite: skip over ' ' and '\t'. /// /// @param[in] p String to skip in. diff --git a/src/nvim/charset.h b/src/nvim/charset.h index 258e95bec0..cb5fbb22ed 100644 --- a/src/nvim/charset.h +++ b/src/nvim/charset.h @@ -2,13 +2,12 @@ #define NVIM_CHARSET_H #include <stdbool.h> +#include <stdint.h> #include "nvim/buffer_defs.h" #include "nvim/eval/typval_defs.h" -#include "nvim/option_defs.h" #include "nvim/pos.h" #include "nvim/strings.h" -#include "nvim/types.h" /// Return the folded-case equivalent of the given character /// diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c index 37a76542d4..1671bf009a 100644 --- a/src/nvim/indent_c.c +++ b/src/nvim/indent_c.c @@ -20,6 +20,7 @@ #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/option.h" +#include "nvim/plines.h" #include "nvim/pos.h" #include "nvim/search.h" #include "nvim/strings.h" diff --git a/src/nvim/plines.c b/src/nvim/plines.c index e11a509178..8ecefc805a 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -3,7 +3,6 @@ // plines.c: calculate the vertical and horizontal size of text in a window -#include <assert.h> #include <inttypes.h> #include <limits.h> #include <stdbool.h> @@ -17,214 +16,21 @@ #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/macros.h" +#include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" #include "nvim/move.h" #include "nvim/option.h" #include "nvim/plines.h" #include "nvim/pos.h" +#include "nvim/state.h" +#include "nvim/types.h" #include "nvim/vim.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. - -/// Return the number of window lines occupied by buffer line "lnum". -/// Includes any filler lines. -/// -/// @param limit_winheight when true limit to window height -int plines_win(win_T *wp, linenr_T lnum, bool limit_winheight) -{ - // Check for filler lines above this buffer line. - return plines_win_nofill(wp, lnum, limit_winheight) + win_get_fill(wp, lnum); -} - -/// Return the number of filler lines above "lnum". -/// -/// @param wp -/// @param lnum -/// -/// @return Number of filler lines above lnum -int win_get_fill(win_T *wp, linenr_T lnum) -{ - int virt_lines = decor_virt_lines(wp, lnum, NULL, kNone); - - // be quick when there are no filler lines - if (diffopt_filler()) { - int n = diff_check(wp, lnum); - - if (n > 0) { - return virt_lines + n; - } - } - return virt_lines; -} - -bool win_may_fill(win_T *wp) -{ - return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks; -} - -/// Return the number of window lines occupied by buffer line "lnum". -/// Does not include filler lines. -/// -/// @param limit_winheight when true limit to window height -int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight) -{ - if (!wp->w_p_wrap) { - return 1; - } - - if (wp->w_width_inner == 0) { - return 1; - } - - // Folded lines are handled just like an empty line. - if (lineFolded(wp, lnum)) { - return 1; - } - - const int lines = plines_win_nofold(wp, lnum); - if (limit_winheight && lines > wp->w_height_inner) { - return wp->w_height_inner; - } - return lines; -} - -/// Get number of window lines physical line "lnum" will occupy in window "wp". -/// Does not care about folding, 'wrap' or filler lines. -int plines_win_nofold(win_T *wp, linenr_T lnum) -{ - char *s = ml_get_buf(wp->w_buffer, lnum); - chartabsize_T cts; - init_chartabsize_arg(&cts, wp, lnum, 0, s, s); - if (*s == NUL && !cts.cts_has_virt_text) { - return 1; // be quick for an empty line - } - win_linetabsize_cts(&cts, (colnr_T)MAXCOL); - clear_chartabsize_arg(&cts); - int64_t col = cts.cts_vcol; - - // 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'. - int width = wp->w_width_inner - win_col_off(wp); - if (width <= 0) { - return 32000; // bigger than the number of screen lines - } - if (col <= width) { - return 1; - } - col -= width; - width += win_col_off2(wp); - const int64_t lines = (col + (width - 1)) / width + 1; - return (lines > 0 && lines <= INT_MAX) ? (int)lines : INT_MAX; -} - -/// 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. - int lines = win_get_fill(wp, lnum); - - if (!wp->w_p_wrap) { - return lines + 1; - } - - if (wp->w_width_inner == 0) { - return lines + 1; - } - - char *line = ml_get_buf(wp->w_buffer, lnum); - - colnr_T col = 0; - chartabsize_T cts; - - init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - while (*cts.cts_ptr != NUL && --column >= 0) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - MB_PTR_ADV(cts.cts_ptr); - } - - // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not - // in MODE_INSERT state, 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. - col = cts.cts_vcol; - if (*cts.cts_ptr == TAB && (State & MODE_NORMAL) - && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { - col += win_lbr_chartabsize(&cts, NULL) - 1; - } - clear_chartabsize_arg(&cts); - - // 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 buffer line "lnum" will take in window "wp". -/// This takes care of both folds and topfill. -/// -/// XXX: Because of topfill, this only makes sense when lnum >= wp->w_topline. -/// -/// @param[in] wp window the 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 -/// @param[in] limit_winheight when true limit to window height -/// -/// @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, const bool limit_winheight) -{ - bool folded = hasFoldingWin(wp, lnum, &lnum, nextp, cache, NULL); - if (foldedp != NULL) { - *foldedp = folded; - } - return ((folded ? 1 : plines_win_nofill(wp, lnum, limit_winheight)) + - (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum))); -} - -/// Get the number of screen lines a range of buffer lines will take in window "wp". -/// This takes care of both folds and topfill. -/// -/// XXX: Because of topfill, this only makes sense when first >= wp->w_topline. -/// -/// @param first first line number -/// @param last last line number -/// @param limit_winheight when true limit each line to window height -/// -/// @see win_text_height -int plines_m_win(win_T *wp, linenr_T first, linenr_T last, bool limit_winheight) -{ - int count = 0; - - while (first <= last) { - linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false, limit_winheight); - 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 @@ -563,13 +369,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) head += (max_head_vcol - (vcol + head_prev + prev_rem) + width2 - 1) / width2 * head_mid; } else if (max_head_vcol < 0) { - int off = 0; - if (c != NUL || !(State & MODE_NORMAL)) { - off += cts->cts_cur_text_width_left; - } - if (c != NUL && (State & MODE_NORMAL)) { - off += cts->cts_cur_text_width_right; - } + int off = virt_text_cursor_off(cts, c == NUL); if (off >= prev_rem) { head += (1 + (off - prev_rem) / width) * head_mid; } @@ -619,6 +419,507 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) return n; } +/// Check that virtual column "vcol" is in the rightmost column of window "wp". +/// +/// @param wp window +/// @param vcol column number +static bool in_win_border(win_T *wp, colnr_T vcol) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) +{ + if (wp->w_width_inner == 0) { + // there is no border + return false; + } + int width1 = wp->w_width_inner - win_col_off(wp); // width of first line (after line number) + + if ((int)vcol < width1 - 1) { + return false; + } + + if ((int)vcol == width1 - 1) { + return true; + } + int width2 = width1 + win_col_off2(wp); // width of further lines + + if (width2 <= 0) { + return false; + } + return (vcol - width1) % width2 == width2 - 1; +} + +/// Get how many virtual columns inline virtual text should offset the cursor. +/// +/// @param cts should contain information stored by win_lbr_chartabsize() +/// about widths of left and right gravity virtual text +/// @param on_NUL whether this is the end of the line +static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL) +{ + int off = 0; + if (!on_NUL || !(State & MODE_NORMAL)) { + off += cts->cts_cur_text_width_left; + } + if (!on_NUL && (State & MODE_NORMAL)) { + off += cts->cts_cur_text_width_right; + } + return off; +} + +/// Get virtual column number of pos. +/// start: on the first position of this character (TAB, ctrl) +/// cursor: where the cursor is on this character (first char, except for TAB) +/// end: on the last position of this character (TAB, ctrl) +/// +/// This is used very often, keep it fast! +/// +/// @param wp +/// @param pos +/// @param start +/// @param cursor +/// @param end +void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) +{ + char *ptr; // points to current char + char *posptr; // points to char at pos->col + int incr; + int head; + long *vts = wp->w_buffer->b_p_vts_array; + int ts = (int)wp->w_buffer->b_p_ts; + + colnr_T vcol = 0; + char *line = ptr = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line + + if (pos->col == MAXCOL) { + // continue until the NUL + posptr = NULL; + } else { + // In a few cases the position can be beyond the end of the line. + for (colnr_T i = 0; i < pos->col; i++) { + if (ptr[i] == NUL) { + pos->col = i; + break; + } + } + posptr = ptr + pos->col; + posptr -= utf_head_off(line, posptr); + } + + chartabsize_T cts; + bool on_NUL = false; + init_chartabsize_arg(&cts, wp, pos->lnum, 0, line, line); + cts.cts_max_head_vcol = -1; + + // This function is used very often, do some speed optimizations. + // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set + // and there are no virtual text use a simple loop. + // Also use this when 'list' is set but tabs take their normal size. + if ((!wp->w_p_list || (wp->w_p_lcs_chars.tab1 != NUL)) + && !wp->w_p_lbr + && *get_showbreak_value(wp) == NUL + && !wp->w_p_bri + && !cts.cts_has_virt_text) { + while (true) { + head = 0; + int c = (uint8_t)(*ptr); + + // make sure we don't go past the end of the line + if (c == NUL) { + // NUL at end of line only takes one column + incr = 1; + break; + } + + // A tab gets expanded, depending on the current column + if (c == TAB) { + incr = tabstop_padding(vcol, ts, vts); + } else { + // For utf-8, if the byte is >= 0x80, need to look at + // further bytes to find the cell width. + if (c >= 0x80) { + incr = utf_ptr2cells(ptr); + } else { + incr = byte2cells(c); + } + + // If a double-cell char doesn't fit at the end of a line + // it wraps to the next line, it's like this char is three + // cells wide. + if ((incr == 2) + && wp->w_p_wrap + && (MB_BYTE2LEN((uint8_t)(*ptr)) > 1) + && in_win_border(wp, vcol)) { + incr++; + head = 1; + } + } + + if ((posptr != NULL) && (ptr >= posptr)) { + // character at pos->col + break; + } + + vcol += incr; + MB_PTR_ADV(ptr); + } + } else { + while (true) { + // A tab gets expanded, depending on the current column + // Other things also take up space. + head = 0; + incr = win_lbr_chartabsize(&cts, &head); + + // make sure we don't go past the end of the line + if (*cts.cts_ptr == NUL) { + // NUL at end of line only takes one column, unless there is virtual text + incr = MAX(1, cts.cts_cur_text_width_left + cts.cts_cur_text_width_right); + on_NUL = true; + break; + } + + if ((posptr != NULL) && (cts.cts_ptr >= posptr)) { + // character at pos->col + break; + } + + cts.cts_vcol += incr; + MB_PTR_ADV(cts.cts_ptr); + } + vcol = cts.cts_vcol; + ptr = cts.cts_ptr; + } + clear_chartabsize_arg(&cts); + + if (start != NULL) { + *start = vcol + head; + } + + if (end != NULL) { + *end = vcol + incr - 1; + } + + if (cursor != NULL) { + if ((*ptr == TAB) + && (State & MODE_NORMAL) + && !wp->w_p_list + && !virtual_active() + && !(VIsual_active && ((*p_sel == 'e') || ltoreq(*pos, VIsual)))) { + // cursor at end + *cursor = vcol + incr - 1; + } else { + vcol += virt_text_cursor_off(&cts, on_NUL); + // cursor at start + *cursor = vcol + head; + } + } +} + +/// Get virtual cursor column in the current window, pretending 'list' is off. +/// +/// @param posp +/// +/// @retujrn The virtual cursor column. +colnr_T getvcol_nolist(pos_T *posp) +{ + int list_save = curwin->w_p_list; + colnr_T vcol; + + curwin->w_p_list = false; + if (posp->coladd) { + getvvcol(curwin, posp, NULL, &vcol, NULL); + } else { + getvcol(curwin, posp, NULL, &vcol, NULL); + } + curwin->w_p_list = list_save; + return vcol; +} + +/// Get virtual column in virtual mode. +/// +/// @param wp +/// @param pos +/// @param start +/// @param cursor +/// @param end +void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end) +{ + colnr_T col; + + if (virtual_active()) { + // For virtual mode, only want one value + getvcol(wp, pos, &col, NULL, NULL); + + colnr_T coladd = pos->coladd; + colnr_T endadd = 0; + + // Cannot put the cursor on part of a wide character. + char *ptr = ml_get_buf(wp->w_buffer, pos->lnum); + + if (pos->col < (colnr_T)strlen(ptr)) { + int c = utf_ptr2char(ptr + pos->col); + if ((c != TAB) && vim_isprintc(c)) { + endadd = (colnr_T)(char2cells(c) - 1); + if (coladd > endadd) { + // past end of line + endadd = 0; + } else { + coladd = 0; + } + } + } + col += coladd; + + if (start != NULL) { + *start = col; + } + + if (cursor != NULL) { + *cursor = col; + } + + if (end != NULL) { + *end = col + endadd; + } + } else { + getvcol(wp, pos, start, cursor, end); + } +} + +/// Get the leftmost and rightmost virtual column of pos1 and pos2. +/// Used for Visual block mode. +/// +/// @param wp +/// @param pos1 +/// @param pos2 +/// @param left +/// @param right +void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right) +{ + colnr_T from1; + colnr_T from2; + colnr_T to1; + colnr_T to2; + + if (lt(*pos1, *pos2)) { + getvvcol(wp, pos1, &from1, NULL, &to1); + getvvcol(wp, pos2, &from2, NULL, &to2); + } else { + getvvcol(wp, pos2, &from1, NULL, &to1); + getvvcol(wp, pos1, &from2, NULL, &to2); + } + + if (from2 < from1) { + *left = from2; + } else { + *left = from1; + } + + if (to2 > to1) { + if ((*p_sel == 'e') && (from2 - 1 >= to1)) { + *right = from2 - 1; + } else { + *right = to2; + } + } else { + *right = to1; + } +} + +/// Functions calculating vertical size of text when displayed inside a window. +/// Calls horizontal size functions defined above. + +/// Check the there may be filler inlines anywhere in window "wp" +bool win_may_fill(win_T *wp) +{ + return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_blocks; +} + +/// Return the number of filler lines above "lnum". +/// +/// @param wp +/// @param lnum +/// +/// @return Number of filler lines above lnum +int win_get_fill(win_T *wp, linenr_T lnum) +{ + int virt_lines = decor_virt_lines(wp, lnum, NULL, kNone); + + // be quick when there are no filler lines + if (diffopt_filler()) { + int n = diff_check(wp, lnum); + + if (n > 0) { + return virt_lines + n; + } + } + return virt_lines; +} + +/// Return the number of window lines occupied by buffer line "lnum". +/// Includes any filler lines. +/// +/// @param limit_winheight when true limit to window height +int plines_win(win_T *wp, linenr_T lnum, bool limit_winheight) +{ + // Check for filler lines above this buffer line. + return plines_win_nofill(wp, lnum, limit_winheight) + win_get_fill(wp, lnum); +} + +/// Return the number of window lines occupied by buffer line "lnum". +/// Does not include filler lines. +/// +/// @param limit_winheight when true limit to window height +int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight) +{ + if (!wp->w_p_wrap) { + return 1; + } + + if (wp->w_width_inner == 0) { + return 1; + } + + // Folded lines are handled just like an empty line. + if (lineFolded(wp, lnum)) { + return 1; + } + + const int lines = plines_win_nofold(wp, lnum); + if (limit_winheight && lines > wp->w_height_inner) { + return wp->w_height_inner; + } + return lines; +} + +/// Get number of window lines physical line "lnum" will occupy in window "wp". +/// Does not care about folding, 'wrap' or filler lines. +int plines_win_nofold(win_T *wp, linenr_T lnum) +{ + char *s = ml_get_buf(wp->w_buffer, lnum); + chartabsize_T cts; + init_chartabsize_arg(&cts, wp, lnum, 0, s, s); + if (*s == NUL && !cts.cts_has_virt_text) { + return 1; // be quick for an empty line + } + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + int64_t col = cts.cts_vcol; + + // 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'. + int width = wp->w_width_inner - win_col_off(wp); + if (width <= 0) { + return 32000; // bigger than the number of screen lines + } + if (col <= width) { + return 1; + } + col -= width; + width += win_col_off2(wp); + const int64_t lines = (col + (width - 1)) / width + 1; + return (lines > 0 && lines <= INT_MAX) ? (int)lines : INT_MAX; +} + +/// 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. + int lines = win_get_fill(wp, lnum); + + if (!wp->w_p_wrap) { + return lines + 1; + } + + if (wp->w_width_inner == 0) { + return lines + 1; + } + + char *line = ml_get_buf(wp->w_buffer, lnum); + + colnr_T col = 0; + chartabsize_T cts; + + init_chartabsize_arg(&cts, wp, lnum, 0, line, line); + while (*cts.cts_ptr != NUL && --column >= 0) { + cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); + MB_PTR_ADV(cts.cts_ptr); + } + + // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not + // in MODE_INSERT state, 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. + col = cts.cts_vcol; + if (*cts.cts_ptr == TAB && (State & MODE_NORMAL) + && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { + col += win_lbr_chartabsize(&cts, NULL) - 1; + } + clear_chartabsize_arg(&cts); + + // 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 buffer line "lnum" will take in window "wp". +/// This takes care of both folds and topfill. +/// +/// XXX: Because of topfill, this only makes sense when lnum >= wp->w_topline. +/// +/// @param[in] wp window the 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 +/// @param[in] limit_winheight when true limit to window height +/// +/// @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, const bool limit_winheight) +{ + bool folded = hasFoldingWin(wp, lnum, &lnum, nextp, cache, NULL); + if (foldedp != NULL) { + *foldedp = folded; + } + return ((folded ? 1 : plines_win_nofill(wp, lnum, limit_winheight)) + + (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum))); +} + +/// Get the number of screen lines a range of buffer lines will take in window "wp". +/// This takes care of both folds and topfill. +/// +/// XXX: Because of topfill, this only makes sense when first >= wp->w_topline. +/// +/// @param first first line number +/// @param last last line number +/// @param limit_winheight when true limit each line to window height +/// +/// @see win_text_height +int plines_m_win(win_T *wp, linenr_T first, linenr_T last, bool limit_winheight) +{ + int count = 0; + + while (first <= last) { + linenr_T next = first; + count += plines_win_full(wp, first, &next, NULL, false, limit_winheight); + first = next + 1; + } + return count; +} + /// Get the number of screen lines a range of text will take in window "wp". /// /// @param[in] start_lnum Starting line number, 1-based inclusive. diff --git a/src/nvim/plines.h b/src/nvim/plines.h index 153d84e8bb..ba04ef4554 100644 --- a/src/nvim/plines.h +++ b/src/nvim/plines.h @@ -2,9 +2,11 @@ #define NVIM_PLINES_H #include <stdbool.h> +#include <stdint.h> #include "nvim/buffer_defs.h" -#include "nvim/vim.h" +#include "nvim/marktree.h" +#include "nvim/pos.h" /// Argument for lbr_chartabsize(). typedef struct { diff --git a/src/nvim/search.c b/src/nvim/search.c index d42da8fb18..cb6596d211 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -47,6 +47,7 @@ #include "nvim/os/input.h" #include "nvim/os/time.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/profile.h" #include "nvim/regexp.h" #include "nvim/search.h" diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index b708ccb4a3..30c98a2f1e 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -36,6 +36,7 @@ #include "nvim/optionstr.h" #include "nvim/os/os.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/pos.h" #include "nvim/sign.h" #include "nvim/statusline.h" |