From 27177e581902967dcf4f2f426464da1b636ca420 Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Sat, 11 Feb 2023 14:14:24 +0100 Subject: refactor: reduce scope of locals as per the style guide (#22211) --- src/nvim/plines.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5469d94800..a3e5640b18 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -356,14 +356,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) char *s = cts->cts_ptr; colnr_T vcol = cts->cts_vcol; - colnr_T col2; colnr_T col_adj = 0; // vcol + screen size of tab - colnr_T colmax; - int added; int mb_added = 0; int numberextra; - char *ps; - int n; cts->cts_cur_text_width = 0; @@ -397,12 +392,12 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // Count all characters from first non-blank after a blank up to next // non-blank after a blank. numberextra = win_col_off(wp); - col2 = vcol; - colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); + colnr_T col2 = vcol; + colnr_T colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); if (vcol >= colmax) { colmax += col_adj; - n = colmax + win_col_off2(wp); + int n = colmax + win_col_off2(wp); if (n > 0) { colmax += (((vcol - colmax) / n) + 1) * n - col_adj; @@ -410,7 +405,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } for (;;) { - ps = s; + char *ps = s; MB_PTR_ADV(s); c = (uint8_t)(*s); @@ -439,7 +434,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // string at start of line. // Set *headp to the size of what we add. // Do not use 'showbreak' at the NUL after the text. - added = 0; + int added = 0; char *const sbr = c == NUL ? empty_option : get_showbreak_value(wp); if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) { colnr_T sbrlen = 0; -- cgit From 706f871014b46300180156590ff269ee38473989 Mon Sep 17 00:00:00 2001 From: Lewis Russell Date: Wed, 19 Apr 2023 17:04:00 +0100 Subject: build: update uncrustify to 0.76 --- src/nvim/plines.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index a3e5640b18..64a2e0bb61 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -302,7 +302,8 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR /// Free any allocated item in "cts". void clear_chartabsize_arg(chartabsize_T *cts) -{} +{ +} /// like win_chartabsize(), but also check for line breaks on the screen /// -- cgit From 3b0df1780e2c8526bda5dead18ee7cc45925caba Mon Sep 17 00:00:00 2001 From: dundargoc <33953936+dundargoc@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:23:44 +0200 Subject: refactor: uncrustify Notable changes: replace all infinite loops to `while(true)` and remove `int` from `unsigned int`. --- src/nvim/plines.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 64a2e0bb61..b2a4ac710d 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -100,7 +100,7 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) int plines_win_nofold(win_T *wp, linenr_T lnum) { char *s; - unsigned int col; + unsigned col; int width; s = ml_get_buf(wp->w_buffer, lnum, false); @@ -120,10 +120,10 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) if (width <= 0 || col > 32000) { return 32000; // bigger than the number of screen columns } - if (col <= (unsigned int)width) { + if (col <= (unsigned)width) { return 1; } - col -= (unsigned int)width; + col -= (unsigned)width; width += win_col_off2(wp); assert(col <= INT_MAX && (int)col < INT_MAX - (width - 1)); return ((int)col + (width - 1)) / width + 1; @@ -272,7 +272,7 @@ int linetabsize_col(int startcol, char *s) /// @param len /// /// @return Number of characters the string will take on the screen. -unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) +unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); @@ -281,7 +281,7 @@ unsigned int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); } clear_chartabsize_arg(&cts); - return (unsigned int)cts.cts_vcol; + return (unsigned)cts.cts_vcol; } /// Prepare the structure passed to chartabsize functions. @@ -405,7 +405,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } - for (;;) { + while (true) { char *ps = s; MB_PTR_ADV(s); c = (uint8_t)(*s); -- cgit From c426f7a6228cb82af0f75ac4f2421543408ff091 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 00:03:46 +0200 Subject: vim-patch:9.0.0751: 'scrolloff' does not work well with 'smoothscroll' Problem: 'scrolloff' does not work well with 'smoothscroll'. Solution: Make positioning the cursor a bit better. Rename functions. https://github.com/vim/vim/commit/c9121f798f49fa71e814912cb186d89c164090c3 Co-authored-by: Bram Moolenaar --- src/nvim/plines.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index b2a4ac710d..3c8ee7d66d 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -243,12 +243,12 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col) /// @param s /// /// @return Number of characters the string will take on the screen. -int linetabsize(char *s) +int linetabsize_str(char *s) { return linetabsize_col(0, s); } -/// Like linetabsize(), but "s" starts at column "startcol". +/// Like linetabsize_str(), but "s" starts at column "startcol". /// /// @param startcol /// @param s @@ -265,7 +265,7 @@ int linetabsize_col(int startcol, char *s) return cts.cts_vcol; } -/// Like linetabsize(), but for a given window instead of the current one. +/// Like linetabsize_str(), but for a given window instead of the current one. /// /// @param wp /// @param line @@ -284,6 +284,13 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) return (unsigned)cts.cts_vcol; } +/// Return the number of cells line "lnum" of window "wp" will take on the +/// screen, taking into account the size of a tab and text properties. +unsigned linetabsize(win_T *wp, linenr_T lnum) +{ + return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. -- cgit From 88d13d2778a4fab3fdcc2bf4c1adf631b38ea3ce Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Thu, 27 Apr 2023 03:36:31 +0200 Subject: vim-patch:9.0.0807: with 'smoothscroll' typing "0" may not go to the first column Problem: With 'smoothscroll' typing "0" may not go to the first column. Solution: Recompute w_cline_height when needed. Do not scroll up when it would move the cursor. https://github.com/vim/vim/commit/d5337efece7c68e9b4ce864532ea49b02453b674 Co-authored-by: Bram Moolenaar --- src/nvim/plines.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3c8ee7d66d..3e69e547cb 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -189,10 +189,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) /// @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] 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 cache, const bool winheight) { bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); if (foldedp) { @@ -201,9 +202,9 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const if (folded) { return 1; } else if (lnum == wp->w_topline) { - return plines_win_nofill(wp, lnum, true) + wp->w_topfill; + return plines_win_nofill(wp, lnum, winheight) + wp->w_topfill; } - return plines_win(wp, lnum, true); + return plines_win(wp, lnum, winheight); } int plines_m_win(win_T *wp, linenr_T first, linenr_T last) @@ -212,7 +213,7 @@ int plines_m_win(win_T *wp, linenr_T first, linenr_T last) while (first <= last) { linenr_T next = first; - count += plines_win_full(wp, first, &next, NULL, false); + count += plines_win_full(wp, first, &next, NULL, false, true); first = next + 1; } return count; -- cgit From efa9b299a7cb68909e9bcd290e4d12bcb6d0bb03 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 16:31:08 +1100 Subject: feat(ui): inline virtual text vim-patch:9.0.0067: cannot show virtual text Problem: Cannot show virtual text. Solution: Initial changes for virtual text support, using text properties. https://github.com/vim/vim/commit/7f9969c559b51446632ac7e8f76cde07e7d0078d vim-patch:9.0.0116: virtual text not displayed if 'signcolumn' is "yes" Problem: Virtual text not displayed if 'signcolumn' is "yes". Solution: Set c_extra and c_final to NUL. https://github.com/vim/vim/commit/711483cd1381a4ed848d783ae0a6792d5b04447b Co-authored-by: bfredl --- src/nvim/plines.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3e69e547cb..738057b696 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -295,17 +295,25 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. -/// When "lnum" is zero do not use text properties that insert text. -void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum FUNC_ATTR_UNUSED, - colnr_T col, char *line, char *ptr) +/// When "lnum" is zero do not use inline virtual text. +void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T col, char *line, + char *ptr) { cts->cts_win = wp; cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; cts->cts_cur_text_width = 0; - // TODO(bfredl): actually lookup inline virtual text here cts->cts_has_virt_text = false; + cts->cts_row = lnum - 1; + + if (cts->cts_row >= 0) { + marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row == cts->cts_row) { + cts->cts_has_virt_text = true; + } + } } /// Free any allocated item in "cts". @@ -383,7 +391,22 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { - // TODO(bfredl): inline virtual text + int col = (int)(s - line); + while (true) { + mtkey_t mark = marktree_itr_current(cts->cts_iter); + if (mark.pos.row != cts->cts_row || mark.pos.col > col) { + break; + } else if (mark.pos.col == col) { // TODO: or maybe unconditionally, what if byte-misaligned? + if (!mt_end(mark)) { + Decoration decor = get_decor(mark); + if (decor.virt_text_pos == kVTInline) { + cts->cts_cur_text_width = decor.virt_text_width; + size += cts->cts_cur_text_width; + } + } + } + marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter); + } } int c = (uint8_t)(*s); -- cgit From a38d7f99845d6ef566b2885737b892c660749d5e Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 19 Mar 2023 17:37:28 +1100 Subject: fix(ui): fix cursor position with multiple inline virtual text vim-patch9.0.0121: cannot put virtual text after or below a line Problem: Cannot put virtual text after or below a line. Solution: Add "text_align" and "text_wrap" arguments. https://github.com/vim/vim/commit/b7963df98f9dbbb824713acad2f47c9989fcf8f3 This only patches the fix, not the whole thing. --- src/nvim/plines.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 738057b696..dd4955c352 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -396,12 +396,13 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col == col) { // TODO: or maybe unconditionally, what if byte-misaligned? + } else if (mark.pos.col + == col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned? if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { - cts->cts_cur_text_width = decor.virt_text_width; - size += cts->cts_cur_text_width; + cts->cts_cur_text_width += decor.virt_text_width; + size += decor.virt_text_width; } } } -- cgit From 8eaf3c4f8c2ee4dc5a6e12bb809058ad263dbb65 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 00:10:28 +1100 Subject: vim-patch:9.0.0143: cursor positioned after virtual text in empty line Problem: Cursor positioned after virtual text in empty line. Solution: Keep cursor in the first column. (closes vim/vim#10786) https://github.com/vim/vim/commit/afd2aa79eda3fe69f2e7c87d0b9b4bca874f386a --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index dd4955c352..1c6ed4f4c1 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -390,7 +390,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); - if (cts->cts_has_virt_text) { + if (cts->cts_has_virt_text && *line != NUL) { int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); -- cgit From 43c2eaada220d5e7d44fa9086655b00ee3e5fbb5 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:49:09 +1100 Subject: vim-patch:9.0.0179: cursor pos wrong with wrapping virtual text in empty line Problem: Cursor position wrong with wrapping virtual text in empty line. Solution: Adjust handling of an empty line. (closes vim/vim#10875) https://github.com/vim/vim/commit/49a90792d950c51608d0459ef8699fe921070718 Co-authored-by: Bram Moolenaar --- src/nvim/plines.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 1c6ed4f4c1..59c45d3615 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -102,12 +102,16 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) char *s; unsigned col; int width; + chartabsize_T cts; s = ml_get_buf(wp->w_buffer, lnum, false); - if (*s == NUL) { // empty line - return 1; + 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 } - col = win_linetabsize(wp, lnum, s, MAXCOL); + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + col = (unsigned)cts.cts_vcol; // If list mode is on, then the '$' at the end of the line may take up one // extra column. @@ -262,6 +266,11 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } + if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { + // check for virtual text in an empty line + (void)lbr_chartabsize_adv(&cts); + cts.cts_vcol += cts.cts_cur_text_width; + } clear_chartabsize_arg(&cts); return cts.cts_vcol; } @@ -277,10 +286,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); - for (; *cts.cts_ptr != NUL && (len == MAXCOL || cts.cts_ptr < line + len); - MB_PTR_ADV(cts.cts_ptr)) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); - } + win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); return (unsigned)cts.cts_vcol; } @@ -292,6 +298,20 @@ unsigned linetabsize(win_T *wp, linenr_T lnum) return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } +void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) +{ + for (; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len); + MB_PTR_ADV(cts->cts_ptr)) { + cts->cts_vcol += win_lbr_chartabsize(cts, NULL); + } + // check for a virtual text on an empty line + if (cts->cts_has_virt_text && *cts->cts_ptr == NUL + && cts->cts_ptr == cts->cts_line) { + (void)win_lbr_chartabsize(cts, NULL); + cts->cts_vcol += cts->cts_cur_text_width; + } +} + /// Prepare the structure passed to chartabsize functions. /// /// "line" is the start of the line, "ptr" is the first relevant character. @@ -390,14 +410,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); - if (cts->cts_has_virt_text && *line != NUL) { + if (cts->cts_has_virt_text) { + int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col - == col) { // TODO(bfredl): or maybe unconditionally, what if byte-misaligned? + } else if (mark.pos.col >= col + && mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what + // if byte-misaligned? if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { -- cgit From dee3af2122d5072d351551ef023d2c177334b332 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Sun, 26 Mar 2023 16:09:09 +1100 Subject: vim-patch:9.0.0178: cursor position wrong with virtual text before Tab Problem: Cursor position wrong with virtual text before Tab. Solution: Use the byte length, not the cell with, to compare the column. Correct tab size after text prop. (closes vim/vim#10866) https://github.com/vim/vim/commit/e428fa04a758cc87ea580c856a796e58e407504b Co-authored-by: Bram Moolenaar --- src/nvim/plines.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 59c45d3615..2df9c23902 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -411,6 +411,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { + int tab_size = size; int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { @@ -425,6 +426,12 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (decor.virt_text_pos == kVTInline) { cts->cts_cur_text_width += decor.virt_text_width; size += decor.virt_text_width; + if (*s == TAB) { + // tab size changes because of the inserted text + size -= tab_size; + tab_size = win_chartabsize(wp, s, vcol + size); + size += tab_size; + } } } } -- cgit From be273c3a23cf65665e843cfb13fd5319657cc5c2 Mon Sep 17 00:00:00 2001 From: Ibby <33922797+SleepySwords@users.noreply.github.com> Date: Tue, 28 Mar 2023 01:23:21 +1100 Subject: vim-patch:9.0.0205: cursor in wrong position when inserting after virtual text Problem: Cursor in wrong position when inserting after virtual text. (Ben Jackson) Solution: Put the cursor after the virtual text, where the text will be inserted. (closes vim/vim#10914) https://github.com/vim/vim/commit/28c9f895716cfa8f1220bc41b72a534c0e10cabe Co-authored-by: Bram Moolenaar --- src/nvim/plines.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 2df9c23902..3b4883d5c2 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -325,6 +325,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_ptr = ptr; cts->cts_cur_text_width = 0; cts->cts_has_virt_text = false; + cts->cts_has_right_gravity = true; cts->cts_row = lnum - 1; if (cts->cts_row >= 0) { @@ -425,6 +426,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { cts->cts_cur_text_width += decor.virt_text_width; + cts->cts_has_right_gravity = mt_right(mark); size += decor.virt_text_width; if (*s == TAB) { // tab size changes because of the inserted text -- cgit From a78fd18ed92d7474d10b5640dcf7a15728d2e03a Mon Sep 17 00:00:00 2001 From: bfredl Date: Tue, 9 May 2023 14:26:03 +0200 Subject: fix(extmark): fix cursor position with both left and right gravity inline text --- src/nvim/plines.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3b4883d5c2..25c745ae97 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -269,7 +269,7 @@ int linetabsize_col(int startcol, char *s) if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { // check for virtual text in an empty line (void)lbr_chartabsize_adv(&cts); - cts.cts_vcol += cts.cts_cur_text_width; + cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right; } clear_chartabsize_arg(&cts); return cts.cts_vcol; @@ -308,7 +308,7 @@ void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) if (cts->cts_has_virt_text && *cts->cts_ptr == NUL && cts->cts_ptr == cts->cts_line) { (void)win_lbr_chartabsize(cts, NULL); - cts->cts_vcol += cts->cts_cur_text_width; + cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; } } @@ -323,9 +323,9 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; cts->cts_has_virt_text = false; - cts->cts_has_right_gravity = true; cts->cts_row = lnum - 1; if (cts->cts_row >= 0) { @@ -398,7 +398,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int mb_added = 0; int numberextra; - cts->cts_cur_text_width = 0; + cts->cts_cur_text_width_left = 0; + cts->cts_cur_text_width_right = 0; // No 'linebreak', 'showbreak' and 'breakindent': return quickly. if (!wp->w_p_lbr && !wp->w_p_bri && *get_showbreak_value(wp) == NUL @@ -419,14 +420,15 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col >= col - && mark.pos.col < col + charlen) { // TODO(bfredl): or maybe unconditionally, what - // if byte-misaligned? + } else if (mark.pos.col >= col && mark.pos.col < col + charlen) { if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { - cts->cts_cur_text_width += decor.virt_text_width; - cts->cts_has_right_gravity = mt_right(mark); + if (mt_right(mark)) { + cts->cts_cur_text_width_right += decor.virt_text_width; + } else { + cts->cts_cur_text_width_left += decor.virt_text_width; + } size += decor.virt_text_width; if (*s == TAB) { // tab size changes because of the inserted text -- cgit From 7955c90621bb679f9c16b6788fbcb6145739886f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 5 Jun 2023 06:58:44 +0800 Subject: fix(plines): folded lines with virt_lines attached to line above --- src/nvim/plines.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 25c745ae97..5f28715f53 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -38,8 +38,7 @@ /// @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. + // Check for filler lines above this buffer line. return plines_win_nofill(wp, lnum, winheight) + win_get_fill(wp, lnum); } @@ -199,16 +198,12 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const foldedp, const bool cache, const bool winheight) { - bool folded = hasFoldingWin(wp, lnum, NULL, nextp, cache, NULL); - if (foldedp) { + bool folded = hasFoldingWin(wp, lnum, &lnum, nextp, cache, NULL); + if (foldedp != NULL) { *foldedp = folded; } - if (folded) { - return 1; - } else if (lnum == wp->w_topline) { - return plines_win_nofill(wp, lnum, winheight) + wp->w_topfill; - } - return plines_win(wp, lnum, winheight); + return ((folded ? 1 : plines_win_nofill(wp, lnum, winheight)) + + (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum))); } int plines_m_win(win_T *wp, linenr_T first, linenr_T last) -- cgit From a3fba5cafcf124946ea65a68fc1b9dfbeb197525 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 5 Jun 2023 17:11:58 +0800 Subject: fix(mouse): handle folded lines with virt_lines attached to line above (#23912) --- src/nvim/plines.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5f28715f53..523b85fd32 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -136,8 +136,7 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) /// 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. + // Check for filler lines above this buffer line. int lines = win_get_fill(wp, lnum); if (!wp->w_p_wrap) { -- cgit From 35c3275b4896a65d67caf2a4355d7516b6ddec29 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 3 Jul 2023 22:57:45 +0800 Subject: fix(plines): handle inline virtual text after last char (#24241) Also remove dead code in win_lbr_chartabsize(). --- src/nvim/plines.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 523b85fd32..eb1ae540a4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -287,7 +287,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) /// Return the number of cells line "lnum" of window "wp" will take on the /// screen, taking into account the size of a tab and text properties. -unsigned linetabsize(win_T *wp, linenr_T lnum) +unsigned linetabsize(win_T *wp, linenr_T lnum) { return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); } @@ -298,9 +298,8 @@ void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) MB_PTR_ADV(cts->cts_ptr)) { cts->cts_vcol += win_lbr_chartabsize(cts, NULL); } - // check for a virtual text on an empty line - if (cts->cts_has_virt_text && *cts->cts_ptr == NUL - && cts->cts_ptr == cts->cts_line) { + // check for a virtual text after the end of the line + if (len == MAXCOL && cts->cts_has_virt_text && *cts->cts_ptr == NUL) { (void)win_lbr_chartabsize(cts, NULL); cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; } @@ -406,15 +405,15 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // First get normal size, without 'linebreak' or virtual text int size = win_chartabsize(wp, s, vcol); + if (cts->cts_has_virt_text) { int tab_size = size; - int charlen = *s == NUL ? 1 : utf_ptr2len(s); int col = (int)(s - line); while (true) { mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; - } else if (mark.pos.col >= col && mark.pos.col < col + charlen) { + } else if (mark.pos.col == col) { if (!mt_end(mark)) { Decoration decor = get_decor(mark); if (decor.virt_text_pos == kVTInline) { -- cgit From e8b3ed74bca4bd6ececcc240f816451bef7fd58c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 4 Jul 2023 14:14:09 +0800 Subject: fix(ui-ext): "scroll_delta" handle topfill and skipcol (#24249) --- src/nvim/plines.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index eb1ae540a4..46bbbeeb07 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -595,3 +595,52 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) } return n; } + +int64_t win_get_text_height(win_T *const wp, const linenr_T first, const linenr_T last, + const int64_t start_vcol, const int64_t end_vcol) +{ + int width1 = 0; + int width2 = 0; + if (start_vcol >= 0 || end_vcol >= 0) { + width1 = wp->w_width_inner - win_col_off(wp); + width2 = width1 + win_col_off2(wp); + width1 = MAX(width1, 0); + width2 = MAX(width2, 0); + } + + int64_t size = 0; + int64_t height_nofill = 0; + linenr_T lnum = first; + + if (start_vcol >= 0) { + linenr_T lnum_next = lnum; + const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + height_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); + size += height_nofill; + const int64_t row_off = (start_vcol < width1 || width2 <= 0) + ? 0 + : 1 + (start_vcol - width1) / width2; + size -= MIN(row_off, height_nofill); + lnum = lnum_next + 1; + } + + while (lnum <= last) { + linenr_T lnum_next = lnum; + const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + height_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); + size += win_get_fill(wp, lnum) + height_nofill; + lnum = lnum_next + 1; + } + + if (end_vcol >= 0) { + size -= height_nofill; + const int64_t row_off = end_vcol == 0 + ? 0 + : (end_vcol <= width1 || width2 <= 0) + ? 1 + : 1 + (end_vcol - width1 + width2 - 1) / width2; + size += MIN(row_off, height_nofill); + } + + return size; +} -- cgit From 317038e7cb11d3db3f3b4679e260de4e119c210c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 5 Jul 2023 16:30:23 +0800 Subject: fix(plines): don't return very large height on very long line (#24260) --- src/nvim/plines.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 46bbbeeb07..615ce9100b 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -98,19 +98,15 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) /// "wp". Does not care about folding, 'wrap' or 'diff'. int plines_win_nofold(win_T *wp, linenr_T lnum) { - char *s; - unsigned col; - int width; + char *s = ml_get_buf(wp->w_buffer, lnum, false); chartabsize_T cts; - - s = ml_get_buf(wp->w_buffer, lnum, false); 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); - col = (unsigned)cts.cts_vcol; + 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. @@ -119,17 +115,17 @@ int plines_win_nofold(win_T *wp, linenr_T lnum) } // 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 + int width = wp->w_width_inner - win_col_off(wp); + if (width <= 0) { + return 32000; // bigger than the number of screen lines } - if (col <= (unsigned)width) { + if (col <= width) { return 1; } - col -= (unsigned)width; + col -= width; width += win_col_off2(wp); - assert(col <= INT_MAX && (int)col < INT_MAX - (width - 1)); - return ((int)col + (width - 1)) / width + 1; + 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 -- cgit From a0c9c04f006dbc6cb38d620ec036e32ccb8e76cc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 6 Jul 2023 04:36:22 +0800 Subject: refactor(plines.c): update outdated comments (#24264) --- src/nvim/plines.c | 58 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 21 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 615ce9100b..476e5b2b7d 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -94,8 +94,8 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) return lines; } -/// @Return number of window lines physical line "lnum" will occupy in window -/// "wp". Does not care about folding, 'wrap' or 'diff'. +/// 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, false); @@ -179,10 +179,12 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) 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. +/// Get the number of screen lines buffer line "lnum" will take in window "wp". +/// This takes care of both folds and topfill. /// -/// @param[in] wp window line is in +/// 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 @@ -201,6 +203,16 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const (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. +/// XXX: This limits the size of each line to current window height. +/// +/// @param first first line number +/// @param last last line number +/// +/// @see win_text_height int plines_m_win(win_T *wp, linenr_T first, linenr_T last) { int count = 0; @@ -282,7 +294,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) } /// Return the number of cells line "lnum" of window "wp" will take on the -/// screen, taking into account the size of a tab and text properties. +/// screen, taking into account the size of a tab and inline virtual text. unsigned linetabsize(win_T *wp, linenr_T lnum) { return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); @@ -294,7 +306,7 @@ void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) MB_PTR_ADV(cts->cts_ptr)) { cts->cts_vcol += win_lbr_chartabsize(cts, NULL); } - // check for a virtual text after the end of the line + // check for inline virtual text after the end of the line if (len == MAXCOL && cts->cts_has_virt_text && *cts->cts_ptr == NUL) { (void)win_lbr_chartabsize(cts, NULL); cts->cts_vcol += cts->cts_cur_text_width_left + cts->cts_cur_text_width_right; @@ -333,9 +345,7 @@ void clear_chartabsize_arg(chartabsize_T *cts) /// like win_chartabsize(), but also check for line breaks on the screen /// -/// @param line -/// @param s -/// @param col +/// @param cts /// /// @return The number of characters taken up on the screen. int lbr_chartabsize(chartabsize_T *cts) @@ -352,9 +362,7 @@ int lbr_chartabsize(chartabsize_T *cts) /// Call lbr_chartabsize() and advance the pointer. /// -/// @param line -/// @param s -/// @param col +/// @param cts /// /// @return The number of characters take up on the screen. int lbr_chartabsize_adv(chartabsize_T *cts) @@ -399,7 +407,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) return win_chartabsize(wp, s, vcol); } - // First get normal size, without 'linebreak' or virtual text + // First get normal size, without 'linebreak' or inline virtual text int size = win_chartabsize(wp, s, vcol); if (cts->cts_has_virt_text) { @@ -561,9 +569,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) /// '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 cts /// @param headp /// /// @return The number of characters take up on the screen. @@ -592,8 +598,18 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) return n; } -int64_t win_get_text_height(win_T *const wp, const linenr_T first, const linenr_T last, - const int64_t start_vcol, const int64_t end_vcol) +/// Get the number of screen lines a range of text will take in window "wp". +/// +/// @param start_lnum first line number +/// @param start_vcol >= 0: virtual column on "start_lnum" where counting starts, +/// rounded down to full screen lines +/// < 0: count a full "start_lnum", including filler lines above +/// @param end_lnum last line number +/// @param end_vcol >= 0: virtual column on "end_lnum" where counting ends, +/// rounded up to full screen lines +/// < 0: count a full "end_lnum", not including fillers lines below +int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_t start_vcol, + const linenr_T end_lnum, const int64_t end_vcol) { int width1 = 0; int width2 = 0; @@ -606,7 +622,7 @@ int64_t win_get_text_height(win_T *const wp, const linenr_T first, const linenr_ int64_t size = 0; int64_t height_nofill = 0; - linenr_T lnum = first; + linenr_T lnum = start_lnum; if (start_vcol >= 0) { linenr_T lnum_next = lnum; @@ -620,7 +636,7 @@ int64_t win_get_text_height(win_T *const wp, const linenr_T first, const linenr_ lnum = lnum_next + 1; } - while (lnum <= last) { + while (lnum <= end_lnum) { linenr_T lnum_next = lnum; const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); height_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); -- cgit From db8fe63a9398efd57c3ff28aa3d93e45fb70ee1a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 11 Jul 2023 07:15:46 +0800 Subject: feat(api): add nvim_win_text_height (#24236) It uses the same code as "scroll_delta" of "win_viewport" UI event to calculate text height, but is more flexible. --- src/nvim/plines.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 476e5b2b7d..df1414bc8f 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -600,14 +600,14 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) /// Get the number of screen lines a range of text will take in window "wp". /// -/// @param start_lnum first line number -/// @param start_vcol >= 0: virtual column on "start_lnum" where counting starts, -/// rounded down to full screen lines -/// < 0: count a full "start_lnum", including filler lines above -/// @param end_lnum last line number -/// @param end_vcol >= 0: virtual column on "end_lnum" where counting ends, -/// rounded up to full screen lines -/// < 0: count a full "end_lnum", not including fillers lines below +/// @param start_lnum Starting line number, 1-based inclusive. +/// @param start_vcol >= 0: Starting virtual column index on "start_lnum", +/// 0-based inclusive, rounded down to full screen lines. +/// < 0: Count a full "start_lnum", including filler lines above. +/// @param end_lnum Ending line number, 1-based inclusive. +/// @param end_vcol >= 0: Ending virtual column index on "end_lnum", +/// 0-based exclusive, rounded up to full screen lines. +/// < 0: Count a full "end_lnum", not including fillers lines below. int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_t start_vcol, const linenr_T end_lnum, const int64_t end_vcol) { @@ -620,39 +620,39 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ width2 = MAX(width2, 0); } - int64_t size = 0; - int64_t height_nofill = 0; + int64_t height_sum = 0; + int64_t height_cur_nofill = 0; linenr_T lnum = start_lnum; if (start_vcol >= 0) { linenr_T lnum_next = lnum; const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); - height_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); - size += height_nofill; + height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); + height_sum += height_cur_nofill; const int64_t row_off = (start_vcol < width1 || width2 <= 0) ? 0 : 1 + (start_vcol - width1) / width2; - size -= MIN(row_off, height_nofill); + height_sum -= MIN(row_off, height_cur_nofill); lnum = lnum_next + 1; } while (lnum <= end_lnum) { linenr_T lnum_next = lnum; const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); - height_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); - size += win_get_fill(wp, lnum) + height_nofill; + height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); + height_sum += win_get_fill(wp, lnum) + height_cur_nofill; lnum = lnum_next + 1; } if (end_vcol >= 0) { - size -= height_nofill; + height_sum -= height_cur_nofill; const int64_t row_off = end_vcol == 0 ? 0 : (end_vcol <= width1 || width2 <= 0) ? 1 : 1 + (end_vcol - width1 + width2 - 1) / width2; - size += MIN(row_off, height_nofill); + height_sum += MIN(row_off, height_cur_nofill); } - return size; + return height_sum; } -- cgit From ef94fb69c65bc2e4c67826de6373c202b55b7d5a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 12 Jul 2023 10:38:53 +0800 Subject: perf(extmarks): don't handle inline virt_text if there is none (#24322) Extreme testcase: ```lua vim.fn.setline(1, 'foobar') local ns = vim.api.nvim_create_namespace('') for _ = 1, 100000 do vim.api.nvim_buf_set_extmark(0, ns, 0, 3, {}) end local start_time = vim.loop.hrtime() vim.fn.virtcol('$') local stop_time = vim.loop.hrtime() print(stop_time - start_time) ``` Before #20130: 31696 On master branch: 26191344 After this PR: 37692 --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index df1414bc8f..73b15edb27 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -329,7 +329,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_has_virt_text = false; cts->cts_row = lnum - 1; - if (cts->cts_row >= 0) { + if (cts->cts_row >= 0 && wp->w_buffer->b_virt_text_inline > 0) { marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); mtkey_t mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row == cts->cts_row) { -- cgit From abe39f2b243dc813456225a779fbeb7ae6affc27 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 16 Jul 2023 18:02:53 +0800 Subject: feat(api)!: change return type of nvim_win_text_height to Dict (#24365) --- src/nvim/plines.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 73b15edb27..236a992bf9 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -600,16 +600,17 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) /// Get the number of screen lines a range of text will take in window "wp". /// -/// @param start_lnum Starting line number, 1-based inclusive. -/// @param start_vcol >= 0: Starting virtual column index on "start_lnum", -/// 0-based inclusive, rounded down to full screen lines. -/// < 0: Count a full "start_lnum", including filler lines above. -/// @param end_lnum Ending line number, 1-based inclusive. -/// @param end_vcol >= 0: Ending virtual column index on "end_lnum", -/// 0-based exclusive, rounded up to full screen lines. -/// < 0: Count a full "end_lnum", not including fillers lines below. +/// @param[in] start_lnum Starting line number, 1-based inclusive. +/// @param[in] start_vcol >= 0: Starting virtual column index on "start_lnum", +/// 0-based inclusive, rounded down to full screen lines. +/// < 0: Count a full "start_lnum", including filler lines above. +/// @param[in] end_lnum Ending line number, 1-based inclusive. +/// @param[in] end_vcol >= 0: Ending virtual column index on "end_lnum", +/// 0-based exclusive, rounded up to full screen lines. +/// < 0: Count a full "end_lnum", not including filler lines below. +/// @param[out] fill If not NULL, set to the number of filler lines in the range. int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_t start_vcol, - const linenr_T end_lnum, const int64_t end_vcol) + const linenr_T end_lnum, const int64_t end_vcol, int64_t *const fill) { int width1 = 0; int width2 = 0; @@ -620,39 +621,44 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ width2 = MAX(width2, 0); } - int64_t height_sum = 0; + int64_t height_sum_fill = 0; int64_t height_cur_nofill = 0; + int64_t height_sum_nofill = 0; linenr_T lnum = start_lnum; if (start_vcol >= 0) { linenr_T lnum_next = lnum; const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); - height_sum += height_cur_nofill; + height_sum_nofill += height_cur_nofill; const int64_t row_off = (start_vcol < width1 || width2 <= 0) ? 0 : 1 + (start_vcol - width1) / width2; - height_sum -= MIN(row_off, height_cur_nofill); + height_sum_nofill -= MIN(row_off, height_cur_nofill); lnum = lnum_next + 1; } while (lnum <= end_lnum) { linenr_T lnum_next = lnum; const bool folded = hasFoldingWin(wp, lnum, &lnum, &lnum_next, true, NULL); + height_sum_fill += win_get_fill(wp, lnum); height_cur_nofill = folded ? 1 : plines_win_nofill(wp, lnum, false); - height_sum += win_get_fill(wp, lnum) + height_cur_nofill; + height_sum_nofill += height_cur_nofill; lnum = lnum_next + 1; } if (end_vcol >= 0) { - height_sum -= height_cur_nofill; + height_sum_nofill -= height_cur_nofill; const int64_t row_off = end_vcol == 0 ? 0 : (end_vcol <= width1 || width2 <= 0) ? 1 : 1 + (end_vcol - width1 + width2 - 1) / width2; - height_sum += MIN(row_off, height_cur_nofill); + height_sum_nofill += MIN(row_off, height_cur_nofill); } - return height_sum; + if (fill != NULL) { + *fill = height_sum_fill; + } + return height_sum_fill + height_sum_nofill; } -- cgit From dd09630a2124ce53edfd19bf6fab0f0cbae3ad13 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 14 Aug 2023 06:47:51 +0800 Subject: vim-patch:9.0.1711: dead code in charset.c (#24706) Problem: dead code in charset.c Solution: remove it linetabsize_col() calls init_chartabsize_arg() with 0 as "lnum", so cts.cts_has_prop_with_text is always FALSE. closes: #PR https://github.com/vim/vim/commit/d3515a1e88cf25c1d5eae8faa965b587a124e687 N/A patches for version.c: vim-patch:9.0.1702: Undo test is flaky --- src/nvim/plines.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 236a992bf9..6925368cf7 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -268,11 +268,6 @@ int linetabsize_col(int startcol, char *s) while (*cts.cts_ptr != NUL) { cts.cts_vcol += lbr_chartabsize_adv(&cts); } - if (cts.cts_has_virt_text && cts.cts_ptr == cts.cts_line) { - // check for virtual text in an empty line - (void)lbr_chartabsize_adv(&cts); - cts.cts_vcol += cts.cts_cur_text_width_left + cts.cts_cur_text_width_right; - } clear_chartabsize_arg(&cts); return cts.cts_vcol; } -- cgit From c54682f75aca8785a4ee9e32b8dc44a1012c3e39 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 18 Aug 2023 09:01:50 +0800 Subject: vim-patch:9.0.1729: screenpos() wrong when w_skipcol and cpoptions+=n (#24773) Problem: screenpos() wrong result with w_skipcol and cpoptions+=n Solution: Use adjust_plines_for_skipcol() instead of subtracting w_skipcol. closes: vim/vim#12625 https://github.com/vim/vim/commit/bfe377b8f2d080e5f85c8cbecf3533456e1d6312 --- src/nvim/plines.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 6925368cf7..431e4cd8c9 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -35,11 +35,11 @@ /// Return the number of window lines occupied by buffer line "lnum". /// Includes any filler lines. /// -/// @param winheight when true limit to window height -int plines_win(win_T *wp, linenr_T lnum, bool winheight) +/// @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, winheight) + win_get_fill(wp, lnum); + return plines_win_nofill(wp, lnum, limit_winheight) + win_get_fill(wp, lnum); } /// Return the number of filler lines above "lnum". @@ -71,8 +71,8 @@ bool win_may_fill(win_T *wp) /// Return the number of window lines occupied by buffer line "lnum". /// Does not include filler lines. /// -/// @param winheight when true limit to window height -int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) +/// @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; @@ -88,7 +88,7 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool winheight) } const int lines = plines_win_nofold(wp, lnum); - if (winheight && lines > wp->w_height_inner) { + if (limit_winheight && lines > wp->w_height_inner) { return wp->w_height_inner; } return lines; @@ -184,22 +184,22 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) /// /// 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] winheight when true limit to window height +/// @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 winheight) + 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, winheight)) + + return ((folded ? 1 : plines_win_nofill(wp, lnum, limit_winheight)) + (lnum == wp->w_topline ? wp->w_topfill : win_get_fill(wp, lnum))); } @@ -207,19 +207,19 @@ int plines_win_full(win_T *wp, linenr_T lnum, linenr_T *const nextp, bool *const /// This takes care of both folds and topfill. /// /// XXX: Because of topfill, this only makes sense when first >= wp->w_topline. -/// XXX: This limits the size of each line to current window height. /// -/// @param first first line number -/// @param last last line number +/// @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) +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, true); + count += plines_win_full(wp, first, &next, NULL, false, limit_winheight); first = next + 1; } return count; -- cgit From a1d71ad55e0f7149f284178b2d04ac78263b09ff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Aug 2023 10:12:00 +0800 Subject: vim-patch:9.0.1783: Display issues with virt text smoothscroll and showbreak Problem: Wrong display with wrapping virtual text or unprintable chars, 'showbreak' and 'smoothscroll'. Solution: Don't skip cells taken by 'showbreak' in screen lines before "w_skipcol". Combined "n_skip" and "skip_cells". closes: vim/vim#12597 https://github.com/vim/vim/commit/b557f4898208105b674df605403cac1b1292707b --- src/nvim/plines.c | 126 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 74 insertions(+), 52 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 431e4cd8c9..ab4a12cd89 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -369,16 +369,19 @@ int lbr_chartabsize_adv(chartabsize_T *cts) return retval; } +/// Get the number of characters taken up on the screen indicated by "cts". +/// "cts->cts_cur_text_width_left" and "cts->cts_cur_text_width_right" are set +/// to the extra size for inline virtual text. /// 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. +/// If "headp" not NULL, set "*headp" to the size of 'showbreak'/'breakindent' +/// included in the return value. +/// When "cts->cts_max_head_vcol" is positive, only count in "*headp" the size +/// of 'showbreak'/'breakindent' before "cts->cts_max_head_vcol". +/// When "cts->cts_max_head_vcol" is negative, only count in "*headp" the size +/// of 'showbreak'/'breakindent' before where cursor should be placed. /// -/// @param cts -/// @param headp -/// -/// @return The number of characters taken up on the screen. +/// Warning: "*headp" may not be set if it's 0, init to 0 before calling. int win_lbr_chartabsize(chartabsize_T *cts, int *headp) { win_T *wp = cts->cts_win; @@ -388,7 +391,6 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) colnr_T col_adj = 0; // vcol + screen size of tab int mb_added = 0; - int numberextra; cts->cts_cur_text_width_left = 0; cts->cts_cur_text_width_right = 0; @@ -449,7 +451,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) && (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); + int numberextra = win_col_off(wp); colnr_T col2 = vcol; colnr_T colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); @@ -490,72 +492,92 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // May have to add something for 'breakindent' and/or 'showbreak' // string at start of line. - // Set *headp to the size of what we add. // Do not use 'showbreak' at the NUL after the text. - int added = 0; + int head = mb_added; char *const sbr = c == NUL ? empty_option : get_showbreak_value(wp); - if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) { - colnr_T sbrlen = 0; - int numberwidth = win_col_off(wp); - - numberextra = numberwidth; - vcol += numberextra + mb_added; - - if (vcol >= (colnr_T)wp->w_width_inner) { - vcol -= wp->w_width_inner; - numberextra = wp->w_width_inner - (numberextra - win_col_off2(wp)); - if (vcol >= numberextra && numberextra > 0) { - vcol %= numberextra; + if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap) { + int col_off_prev = win_col_off(wp); + int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); + colnr_T wcol = vcol + col_off_prev; + // cells taken by 'showbreak'/'breakindent' before current char + int head_prev = 0; + if (wcol >= wp->w_width_inner) { + wcol -= wp->w_width_inner; + col_off_prev = wp->w_width_inner - width2; + if (wcol >= width2 && width2 > 0) { + wcol %= width2; } if (*sbr != NUL) { - sbrlen = (colnr_T)mb_charlen(sbr); - if (vcol >= sbrlen) { - vcol -= sbrlen; - } + head_prev += vim_strsize(sbr); } - if (vcol >= numberextra && numberextra > 0) { - vcol %= numberextra; - } else if (vcol > 0 && numberextra > 0) { - vcol += numberwidth - win_col_off2(wp); + if (wp->w_p_bri) { + head_prev += get_breakindent_win(wp, line); } - - numberwidth -= win_col_off2(wp); + if (wcol < head_prev) { + wcol = head_prev; + } + wcol += col_off_prev; } - if (vcol == 0 || (vcol + size + sbrlen > (colnr_T)wp->w_width_inner)) { + if ((vcol > 0 && wcol == col_off_prev + head_prev) + || wcol + size > wp->w_width_inner) { + int added = 0; + colnr_T max_head_vcol = cts->cts_max_head_vcol; + + if (vcol > 0 && wcol == col_off_prev + head_prev) { + added += head_prev; + if (max_head_vcol <= 0 || vcol < max_head_vcol) { + head += head_prev; + } + } + + // cells taken by 'showbreak'/'breakindent' halfway current char + int head_mid = 0; if (*sbr != NUL) { - if (size + sbrlen + numberwidth > (colnr_T)wp->w_width_inner) { + head_mid += vim_strsize(sbr); + } + if (wp->w_p_bri) { + head_mid += get_breakindent_win(wp, line); + } + if (head_mid > 0) { + if (wcol + size > wp->w_width_inner) { // Calculate effective window width. - int width = (colnr_T)wp->w_width_inner - sbrlen - numberwidth; - int prev_width = vcol ? ((colnr_T)wp->w_width_inner - (sbrlen + vcol)) - : 0; + int prev_rem = wp->w_width_inner - wcol; + int width = width2 - head_mid; if (width <= 0) { width = 1; } - added += ((size - prev_width) / width) * vim_strsize(sbr); - if ((size - prev_width) % width) { - // Wrapped, add another length of 'sbr'. - added += vim_strsize(sbr); + // divide "size - prev_width" by "width", rounding up + int cnt = (size - prev_rem + width - 1) / width; + added += cnt * head_mid; + + if (max_head_vcol == 0 || vcol + size + added < max_head_vcol) { + head += cnt * head_mid; + } else if (max_head_vcol > vcol + head_prev + prev_rem) { + 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; + } + if (off >= prev_rem) { + head += (1 + (off - prev_rem) / width) * head_mid; + } } - } else { - added += vim_strsize(sbr); } } - if (wp->w_p_bri) { - added += get_breakindent_win(wp, line); - } - size += added; - if (vcol != 0) { - added = 0; - } } } if (headp != NULL) { - *headp = added + mb_added; + *headp = head; } return size; } -- cgit From 908f247c224db88ffd25e207314d41031519b128 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 23 Aug 2023 11:28:17 +0800 Subject: fix(plines): count 'showbreak' for virtual text at eol --- src/nvim/plines.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index ab4a12cd89..c2447103dc 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -494,7 +494,10 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // string at start of line. // Do not use 'showbreak' at the NUL after the text. int head = mb_added; - char *const sbr = c == NUL ? empty_option : get_showbreak_value(wp); + char *const sbr + // XXX: there should be a better check deeper below + = ((c == NUL && cts->cts_cur_text_width_left + cts->cts_cur_text_width_right == 0) + ? empty_option : get_showbreak_value(wp)); if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap) { int col_off_prev = win_col_off(wp); int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); -- cgit From 466c18b8185c44f4fbf67ae91a2ffe27c1919306 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Aug 2023 07:19:18 +0800 Subject: vim-patch:9.0.1785: wrong cursor position with 'showbreak' and lcs-eol (#24852) Problem: wrong cursor position with 'showbreak' and lcs-eol Solution: Add size of 'showbreak' before when 'listchars' "eol" is used. Also fix wrong cursor position with wrapping virtual text on empty line and 'showbreak'. closes: vim/vim#12891 https://github.com/vim/vim/commit/1193951bebcff50d88403ce17dec5d3be14f131d --- src/nvim/plines.c | 70 +++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index c2447103dc..c378b55413 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -404,8 +404,13 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) return win_chartabsize(wp, s, vcol); } + bool has_lcs_eol = wp->w_p_list && wp->w_p_lcs_chars.eol != NUL; + // First get normal size, without 'linebreak' or inline virtual text int size = win_chartabsize(wp, s, vcol); + if (*s == NUL && !has_lcs_eol) { + size = 0; // NUL is not displayed + } if (cts->cts_has_virt_text) { int tab_size = size; @@ -491,14 +496,11 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } // May have to add something for 'breakindent' and/or 'showbreak' - // string at start of line. - // Do not use 'showbreak' at the NUL after the text. + // string at the start of a screen line. int head = mb_added; - char *const sbr - // XXX: there should be a better check deeper below - = ((c == NUL && cts->cts_cur_text_width_left + cts->cts_cur_text_width_right == 0) - ? empty_option : get_showbreak_value(wp)); - if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap) { + char *const sbr = get_showbreak_value(wp); + // When "size" is 0, no new screen line is started. + if (size > 0 && wp->w_p_wrap && (*sbr != NUL || wp->w_p_bri)) { int col_off_prev = win_col_off(wp); int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); colnr_T wcol = vcol + col_off_prev; @@ -542,35 +544,33 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (wp->w_p_bri) { head_mid += get_breakindent_win(wp, line); } - if (head_mid > 0) { - if (wcol + size > wp->w_width_inner) { - // Calculate effective window width. - int prev_rem = wp->w_width_inner - wcol; - int width = width2 - head_mid; - - if (width <= 0) { - width = 1; + if (head_mid > 0 && wcol + size > wp->w_width_inner) { + // Calculate effective window width. + int prev_rem = wp->w_width_inner - wcol; + int width = width2 - head_mid; + + if (width <= 0) { + width = 1; + } + // divide "size - prev_width" by "width", rounding up + int cnt = (size - prev_rem + width - 1) / width; + added += cnt * head_mid; + + if (max_head_vcol == 0 || vcol + size + added < max_head_vcol) { + head += cnt * head_mid; + } else if (max_head_vcol > vcol + head_prev + prev_rem) { + 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; } - // divide "size - prev_width" by "width", rounding up - int cnt = (size - prev_rem + width - 1) / width; - added += cnt * head_mid; - - if (max_head_vcol == 0 || vcol + size + added < max_head_vcol) { - head += cnt * head_mid; - } else if (max_head_vcol > vcol + head_prev + prev_rem) { - 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; - } - if (off >= prev_rem) { - head += (1 + (off - prev_rem) / width) * head_mid; - } + if (c != NUL && (State & MODE_NORMAL)) { + off += cts->cts_cur_text_width_right; + } + if (off >= prev_rem) { + head += (1 + (off - prev_rem) / width) * head_mid; } } } -- cgit From cbadb39d16c15b2a3d4f9a122644b3d61ba2d494 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Aug 2023 11:46:07 +0800 Subject: fix(plines.c): initialize cts_max_head_vcol (#24855) --- src/nvim/plines.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index c378b55413..526c2f8e08 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -319,6 +319,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T cts->cts_vcol = col; cts->cts_line = line; cts->cts_ptr = ptr; + cts->cts_max_head_vcol = 0; cts->cts_cur_text_width_left = 0; cts->cts_cur_text_width_right = 0; cts->cts_has_virt_text = false; -- cgit From cefd774fac76b91f5368833555818c80c992c3b1 Mon Sep 17 00:00:00 2001 From: bfredl Date: Thu, 24 Aug 2023 15:14:23 +0200 Subject: refactor(memline): distinguish mutating uses of ml_get_buf() ml_get_buf() takes a third parameters to indicate whether the caller wants to mutate the memline data in place. However the vast majority of the call sites is using this function just to specify a buffer but without any mutation. This makes it harder to grep for the places which actually perform mutation. Solution: Remove the bool param from ml_get_buf(). it now works like ml_get() except for a non-current buffer. Add a new ml_get_buf_mut() function for the mutating use-case, which can be grepped along with the other ml_replace() etc functions which can modify the memline. --- src/nvim/plines.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 526c2f8e08..e11a509178 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -98,7 +98,7 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight) /// 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, false); + 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) { @@ -143,7 +143,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) return lines + 1; } - char *line = ml_get_buf(wp->w_buffer, lnum, false); + char *line = ml_get_buf(wp->w_buffer, lnum); colnr_T col = 0; chartabsize_T cts; @@ -292,7 +292,7 @@ unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) /// screen, taking into account the size of a tab and inline virtual text. unsigned linetabsize(win_T *wp, linenr_T lnum) { - return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum, false), (colnr_T)MAXCOL); + return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL); } void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) -- cgit From f08648182b21a184f461123bbdc7707353be3dbf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 25 Aug 2023 19:36:15 +0800 Subject: refactor(plines.c): move vertical size functions to the bottom Problem: It may be unclear what "below" in first comment refers to. Solution: Move vertical size functions to the bottom so that it can be changed to "above". --- src/nvim/plines.c | 393 +++++++++++++++++++++++++++--------------------------- 1 file changed, 197 insertions(+), 196 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index e11a509178..1123d42916 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -29,202 +29,6 @@ # 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 @@ -619,6 +423,203 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) return n; } +/// 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. -- cgit From 93af6d9ed0af984be7fc383f25ca20b595961c46 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 25 Aug 2023 19:36:43 +0800 Subject: refactor: move virtcol functions to plines.c Problem: Functions for virtcol and chartabsize are similar (both compute horizontal size), but appear in two different source files. Solution: Move virtcol functions to plines.c. --- src/nvim/plines.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 295 insertions(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 1123d42916..4fba3b4c4e 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 #include #include #include @@ -17,12 +16,15 @@ #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 @@ -423,6 +425,298 @@ 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 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 { + 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; + } +} + /// Functions calculating vertical size of text when displayed inside a window. /// Calls horizontal size functions defined above. -- cgit From ff67bb3d05d25ab4fb353839d1570d86f88dbfef Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 25 Aug 2023 20:05:00 +0800 Subject: refactor(plines.c): deduplicate code for virtual text cursor offset --- src/nvim/plines.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 4fba3b4c4e..8ecefc805a 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -369,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; } @@ -453,6 +447,23 @@ static bool in_win_border(win_T *wp, colnr_T vcol) 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) @@ -594,12 +605,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en // 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; - } + vcol += virt_text_cursor_off(&cts, on_NUL); // cursor at start *cursor = vcol + head; } -- cgit From b1cfb299df2ef412339f594173ed23c75c090c8a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Aug 2023 08:35:05 +0800 Subject: docs: various clarifications (#24876) --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 8ecefc805a..be6bcd22ae 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -726,7 +726,7 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right /// 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" +/// Check if 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; -- cgit From 1635c9e75e21e07c4331cf983e14a11c7e09b119 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 26 Aug 2023 11:13:20 +0800 Subject: refactor: move some structs out of buffer_defs.h (#24878) --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index be6bcd22ae..c95f362518 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -726,7 +726,7 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right /// Functions calculating vertical size of text when displayed inside a window. /// Calls horizontal size functions defined above. -/// Check if there may be filler inlines anywhere in window "wp". +/// Check if there may be filler lines 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; -- cgit From 128091a256d64db2f983d70a888b379d7e63f131 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 24 Aug 2023 07:27:18 +0800 Subject: fix(ui): wrong cursor position with left gravity inline virt text at eol --- src/nvim/plines.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index c95f362518..b95adc1415 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -359,7 +359,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (width <= 0) { width = 1; } - // divide "size - prev_width" by "width", rounding up + // Divide "size - prev_rem" by "width", rounding up. int cnt = (size - prev_rem + width - 1) / width; added += cnt * head_mid; @@ -371,7 +371,11 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } else if (max_head_vcol < 0) { int off = virt_text_cursor_off(cts, c == NUL); if (off >= prev_rem) { - head += (1 + (off - prev_rem) / width) * head_mid; + if (size > off) { + head += (1 + (off - prev_rem) / width) * head_mid; + } else { + head += (off - prev_rem + width - 1) / width * head_mid; + } } } } -- cgit From 794981d9bed7048fb3ee1ada38fcf1ebdace4c53 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 29 Aug 2023 09:36:55 +0800 Subject: vim-patch:9.0.1813: linebreak incorrect drawn with breakindent (#24917) Problem: 'linebreak' is incorrectly drawn after 'breakindent'. Solution: Don't include 'breakindent' size when already after it. closes: vim/vim#12937 closes: vim/vim#12940 https://github.com/vim/vim/commit/1d3e0e8f3110a7807431eae056914ccea57b057b --- src/nvim/plines.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index b95adc1415..e18e774a72 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -311,6 +311,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int col_off_prev = win_col_off(wp); int width2 = wp->w_width_inner - col_off_prev + win_col_off2(wp); colnr_T wcol = vcol + col_off_prev; + colnr_T max_head_vcol = cts->cts_max_head_vcol; + int added = 0; + // cells taken by 'showbreak'/'breakindent' before current char int head_prev = 0; if (wcol >= wp->w_width_inner) { @@ -326,23 +329,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) head_prev += get_breakindent_win(wp, line); } if (wcol < head_prev) { - wcol = head_prev; - } - wcol += col_off_prev; - } - - if ((vcol > 0 && wcol == col_off_prev + head_prev) - || wcol + size > wp->w_width_inner) { - int added = 0; - colnr_T max_head_vcol = cts->cts_max_head_vcol; - - if (vcol > 0 && wcol == col_off_prev + head_prev) { + head_prev -= wcol; + wcol += head_prev; added += head_prev; if (max_head_vcol <= 0 || vcol < max_head_vcol) { head += head_prev; } } + wcol += col_off_prev; + } + if (wcol + size > wp->w_width) { // cells taken by 'showbreak'/'breakindent' halfway current char int head_mid = 0; if (*sbr != NUL) { @@ -379,9 +376,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } } - - size += added; } + + size += added; } if (headp != NULL) { -- cgit From 839d919098ed2cf3dfb93b6337a3d2ea2bd210a0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 31 Aug 2023 08:35:08 +0800 Subject: vim-patch:9.0.1825: wrong cursor position with virt text and 'linebreak' (#24957) Problem: Wrong cursor position with virtual text before a whitespace character and 'linebreak'. Solution: Always set "col_adj" to "size - 1" and apply 'linebreak' after adding the size of 'breakindent' and 'showbreak'. closes: vim/vim#12956 https://github.com/vim/vim/commit/6e55e85f92aff43c1b3cb564201440f3552d63f0 N/A patches: vim-patch:9.0.1826: keytrans() doesn't translate recorded key typed in a GUI --- src/nvim/plines.c | 95 ++++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 51 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index e18e774a72..f59a1754f5 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -195,8 +195,6 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) char *line = cts->cts_line; // start of the line char *s = cts->cts_ptr; colnr_T vcol = cts->cts_vcol; - - colnr_T col_adj = 0; // vcol + screen size of tab int mb_added = 0; cts->cts_cur_text_width_left = 0; @@ -249,54 +247,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } - int c = (uint8_t)(*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((uint8_t)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. - int numberextra = win_col_off(wp); - colnr_T col2 = vcol; - colnr_T colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); - - if (vcol >= colmax) { - colmax += col_adj; - int n = colmax + win_col_off2(wp); - - if (n > 0) { - colmax += (((vcol - colmax) / n) + 1) * n - col_adj; - } - } - - while (true) { - char *ps = s; - MB_PTR_ADV(s); - c = (uint8_t)(*s); - - if (!(c != NUL - && (vim_isbreak(c) || col2 == vcol || !vim_isbreak((uint8_t)(*ps))))) { - break; - } - - col2 += win_chartabsize(wp, s, col2); - - if (col2 >= colmax) { // doesn't fit - size = colmax - vcol + col_adj; - break; - } - } - } else if ((size == 2) - && (MB_BYTE2LEN((uint8_t)(*s)) > 1) - && wp->w_p_wrap - && in_win_border(wp, vcol)) { + if (size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1 + && wp->w_p_wrap && in_win_border(wp, vcol)) { // Count the ">" in the last column. size++; mb_added = 1; @@ -335,6 +287,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (max_head_vcol <= 0 || vcol < max_head_vcol) { head += head_prev; } + } else { + head_prev = 0; } wcol += col_off_prev; } @@ -366,7 +320,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 = virt_text_cursor_off(cts, c == NUL); + int off = virt_text_cursor_off(cts, *s == NUL); if (off >= prev_rem) { if (size > off) { head += (1 + (off - prev_rem) / width) * head_mid; @@ -384,6 +338,45 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (headp != NULL) { *headp = head; } + + // 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((uint8_t)s[0]) + && !vim_isbreak((uint8_t)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. + int numberextra = win_col_off(wp); + colnr_T col_adj = size - 1; + colnr_T colmax = (colnr_T)(wp->w_width_inner - numberextra - col_adj); + if (vcol >= colmax) { + colmax += col_adj; + int n = colmax + win_col_off2(wp); + if (n > 0) { + colmax += (((vcol - colmax) / n) + 1) * n - col_adj; + } + } + + colnr_T vcol2 = vcol; + while (true) { + char *ps = s; + MB_PTR_ADV(s); + int c = (uint8_t)(*s); + if (!(c != NUL + && (vim_isbreak(c) || vcol2 == vcol || !vim_isbreak((uint8_t)(*ps))))) { + break; + } + + vcol2 += win_chartabsize(wp, s, vcol2); + if (vcol2 >= colmax) { // doesn't fit + size = colmax - vcol + col_adj; + break; + } + } + } + return size; } -- cgit From 592a8f1e90d5abaa695280bf6d41a547b3631d0d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 1 Sep 2023 06:45:27 +0800 Subject: vim-patch:9.0.1828: cursor wrong with virt text before double-width char (#24967) Problem: Wrong cursor position with virtual text before double-width char at window edge. Solution: Check for double-width char before adding virtual text size. closes: vim/vim#12977 https://github.com/vim/vim/commit/ac2d8815ae7a93c54b07cba76475cfb3f26a3ac6 --- src/nvim/plines.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index f59a1754f5..82554c7785 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -216,6 +216,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (*s == NUL && !has_lcs_eol) { size = 0; // NUL is not displayed } + bool is_doublewidth = size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1; if (cts->cts_has_virt_text) { int tab_size = size; @@ -247,8 +248,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) } } - if (size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1 - && wp->w_p_wrap && in_win_border(wp, vcol)) { + if (is_doublewidth && wp->w_p_wrap && in_win_border(wp, vcol + size - 2)) { // Count the ">" in the last column. size++; mb_added = 1; -- cgit From b04286a187d57c50f01cd36cd4668b7a69026579 Mon Sep 17 00:00:00 2001 From: bfredl Date: Sun, 22 Nov 2020 10:10:37 +0100 Subject: feat(extmark): support proper multiline ranges The removes the previous restriction that nvim_buf_set_extmark() could not be used to highlight arbitrary multi-line regions The problem can be summarized as follows: let's assume an extmark with a hl_group is placed covering the region (5,0) to (50,0) Now, consider what happens if nvim needs to redraw a window covering the lines 20-30. It needs to be able to ask the marktree what extmarks cover this region, even if they don't begin or end here. Therefore the marktree needs to be augmented with the information covers a point, not just what marks begin or end there. To do this, we augment each node with a field "intersect" which is a set the ids of the marks which overlap this node, but only if it is not part of the set of any parent. This ensures the number of nodes that need to be explicitly marked grows only logarithmically with the total number of explicitly nodes (and thus the number of of overlapping marks). Thus we can quickly iterate all marks which overlaps any query position by looking up what leaf node contains that position. Then we only need to consider all "start" marks within that leaf node, and the "intersect" set of that node and all its parents. Now, and the major source of complexity is that the tree restructuring operations (to ensure that each node has T-1 <= size <= 2*T-1) also need to update these sets. If a full inner node is split in two, one of the new parents might start to completely overlap some ranges and its ids will need to be moved from its children's sets to its own set. Similarly, if two undersized nodes gets joined into one, it might no longer completely overlap some ranges, and now the children which do needs to have the have the ids in its set instead. And then there are the pivots! Yes the pivot operations when a child gets moved from one parent to another. --- src/nvim/plines.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 82554c7785..99f666ef3f 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -133,7 +133,7 @@ void init_chartabsize_arg(chartabsize_T *cts, win_T *wp, linenr_T lnum, colnr_T if (cts->cts_row >= 0 && wp->w_buffer->b_virt_text_inline > 0) { marktree_itr_get(wp->w_buffer->b_marktree, cts->cts_row, 0, cts->cts_iter); - mtkey_t mark = marktree_itr_current(cts->cts_iter); + MTKey mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row == cts->cts_row) { cts->cts_has_virt_text = true; } @@ -222,7 +222,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) int tab_size = size; int col = (int)(s - line); while (true) { - mtkey_t mark = marktree_itr_current(cts->cts_iter); + MTKey mark = marktree_itr_current(cts->cts_iter); if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; } else if (mark.pos.col == col) { -- cgit From af7d317f3ff31d5ac5d8724b5057a422e1451b54 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 26 Sep 2023 22:36:08 +0200 Subject: refactor: remove long long is 32-bits even on 64-bit windows which makes the type suboptimal for a codebase meant to be cross-platform. --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 99f666ef3f..8cadb5a81a 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -476,7 +476,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en char *posptr; // points to char at pos->col int incr; int head; - long *vts = wp->w_buffer->b_p_vts_array; + colnr_T *vts = wp->w_buffer->b_p_vts_array; int ts = (int)wp->w_buffer->b_p_ts; colnr_T vcol = 0; -- cgit From cf8b2c0e74fd5e723b0c15c2ce84e6900fd322d3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 12:05:28 +0800 Subject: build(iwyu): add a few more _defs.h mappings (#25435) --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 8cadb5a81a..5334ab0091 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -3,9 +3,9 @@ // plines.c: calculate the vertical and horizontal size of text in a window -#include #include #include +#include #include #include "nvim/ascii.h" -- cgit From dc6d0d2daf69e2fdadda81feb97906dbc962a239 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 Sep 2023 14:41:34 +0800 Subject: refactor: reorganize option header files (#25437) - Move vimoption_T to option.h - option_defs.h is for option-related types - option_vars.h corresponds to Vim's option.h - option_defs.h and option_vars.h don't include each other --- src/nvim/plines.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5334ab0091..97782b39bc 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -21,6 +21,7 @@ #include "nvim/memline.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/option_vars.h" #include "nvim/plines.h" #include "nvim/pos.h" #include "nvim/state.h" -- cgit From e2406d119f12bd3d2d35710ff6368c828deecdbd Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 12 Oct 2023 06:49:11 +0800 Subject: vim-patch:9.0.2017: linebreak applies for leading whitespace (#25604) Problem: linebreak applies for leading whitespace Solution: only apply linebreak, once we have found non-breakat chars in the line closes: vim/vim#13228 closes: vim/vim#13243 https://github.com/vim/vim/commit/dd75fcfbdff1934c6e531b5a89ebc636318bf4a2 Co-authored-by: Christian Brabandt --- src/nvim/plines.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 97782b39bc..2b8cdd4ad9 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -340,9 +340,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) *headp = head; } + colnr_T vcol_start = 0; // start from where to consider linebreak // If 'linebreak' set check at a blank before a non-blank if the line // needs a break here - if (wp->w_p_lbr + if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) { + char *t = cts->cts_line; + while (vim_isbreak((uint8_t)(*t))) { + t++; + } + vcol_start = (colnr_T)(t - cts->cts_line); + } + if (wp->w_p_lbr && vcol_start <= vcol && vim_isbreak((uint8_t)s[0]) && !vim_isbreak((uint8_t)s[1]) && wp->w_p_wrap -- cgit From 99b1163b5ae7a2f199803541c09f3da80547b40c Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 14 Oct 2023 18:39:24 +0800 Subject: vim-patch:9.0.2021: Coverity complains about change in charset (#25634) Problem: Coverity complains about change in charset (after v9.0.2017) Solution: check pointer t at index 0 closes: vim/vim#13322 https://github.com/vim/vim/commit/cd6ee6935811ab223605a3f39a550d26a617867d Co-authored-by: Christian Brabandt --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 2b8cdd4ad9..64dd3ff4d4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -345,7 +345,7 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) // needs a break here if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) { char *t = cts->cts_line; - while (vim_isbreak((uint8_t)(*t))) { + while (vim_isbreak((uint8_t)t[0])) { t++; } vcol_start = (colnr_T)(t - cts->cts_line); -- cgit From 353a4be7e84fdc101318215bdcc8a7e780d737fe Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sun, 12 Nov 2023 13:13:58 +0100 Subject: build: remove PVS We already have an extensive suite of static analysis tools we use, which causes a fair bit of redundancy as we get duplicate warnings. PVS is also prone to give false warnings which creates a lot of work to identify and disable. --- src/nvim/plines.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 64dd3ff4d4..3a168320e4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -1,6 +1,3 @@ -// 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 -- cgit From 6952b1951b6a60df8d477279f4451094fb51c413 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 17 Nov 2023 08:40:02 +0800 Subject: vim-patch:9.0.2107: [security]: FPE in adjust_plines_for_skipcol (#26082) Problem: [security]: FPE in adjust_plines_for_skipcol Solution: don't divide by zero, return zero Prevent a floating point exception when calculating w_skipcol (which can happen with a small window when the number option is set and cpo+=n). Add a test to verify https://github.com/vim/vim/commit/cb0b99f0672d8446585d26e998343dceca17d1ce Co-authored-by: Christian Brabandt --- src/nvim/plines.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 3a168320e4..07c77a5d72 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -83,18 +83,18 @@ int linetabsize_col(int startcol, char *s) /// @param len /// /// @return Number of characters the string will take on the screen. -unsigned win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) +int win_linetabsize(win_T *wp, linenr_T lnum, char *line, colnr_T len) { chartabsize_T cts; init_chartabsize_arg(&cts, wp, lnum, 0, line, line); win_linetabsize_cts(&cts, len); clear_chartabsize_arg(&cts); - return (unsigned)cts.cts_vcol; + return cts.cts_vcol; } /// Return the number of cells line "lnum" of window "wp" will take on the /// screen, taking into account the size of a tab and inline virtual text. -unsigned linetabsize(win_T *wp, linenr_T lnum) +int linetabsize(win_T *wp, linenr_T lnum) { return win_linetabsize(wp, lnum, ml_get_buf(wp->w_buffer, lnum), (colnr_T)MAXCOL); } -- cgit From ac1113ded5f8f09dd99a9894d7a7e795626fb728 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 13 Nov 2023 23:40:37 +0100 Subject: refactor: follow style guide - reduce variable scope - prefer initialization over declaration and assignment --- src/nvim/plines.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 07c77a5d72..2e1b36c3dc 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -167,9 +167,7 @@ int lbr_chartabsize(chartabsize_T *cts) /// @return The number of characters take up on the screen. int lbr_chartabsize_adv(chartabsize_T *cts) { - int retval; - - retval = lbr_chartabsize(cts); + int retval = lbr_chartabsize(cts); MB_PTR_ADV(cts->cts_ptr); return retval; } @@ -399,14 +397,13 @@ static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp) win_T *wp = cts->cts_win; char *s = cts->cts_ptr; colnr_T col = cts->cts_vcol; - 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); + int n = ptr2cells(s); // Add one cell for a double-width character in the last column of the // window, displayed with a ">". -- cgit From a6e3d93421ba13c407a96fac9cc01fa41ec7ad98 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Thu, 16 Nov 2023 10:59:11 +0100 Subject: refactor: enable formatting for ternaries This requires removing the "Inner expression should be aligned" rule from clint as it prevents essentially any formatting regarding ternary operators. --- src/nvim/plines.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 2e1b36c3dc..b51d262cd9 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -971,8 +971,8 @@ int64_t win_text_height(win_T *const wp, const linenr_T start_lnum, const int64_ const int64_t row_off = end_vcol == 0 ? 0 : (end_vcol <= width1 || width2 <= 0) - ? 1 - : 1 + (end_vcol - width1 + width2 - 1) / width2; + ? 1 + : 1 + (end_vcol - width1 + width2 - 1) / width2; height_sum_nofill += MIN(row_off, height_cur_nofill); } -- cgit From 0b38fe4dbb77c15ae6f5779174855acab25fc86c Mon Sep 17 00:00:00 2001 From: bfredl Date: Wed, 8 Mar 2023 15:18:02 +0100 Subject: refactor(decorations): break up Decoration struct into smaller pieces Remove the monolithic Decoration struct. Before this change, each extmark could either represent just a hl_id + priority value as a inline decoration, or it would take a pointer to this monolitic 112 byte struct which has to be allocated. This change separates the decorations into two pieces: DecorSignHighlight for signs, highlights and simple set-flag decorations (like spell, ui-watched), and DecorVirtText for virtual text and lines. The main separation here is whether they are expected to allocate more memory. Currently this is not really true as sign text has to be an allocated string, but the plan is to get rid of this eventually (it can just be an array of two schar_T:s). Further refactors are expected to improve the representation of each decoration kind individually. The goal of this particular PR is to get things started by cutting the Gordian knot which was the monolithic struct Decoration. Now, each extmark can either contain chained indicies/pointers to these kinds of objects, or it can fit a subset of DecorSignHighlight inline. The point of this change is not only to make decorations smaller in memory. In fact, the main motivation is to later allow them to grow _larger_, but on a dynamic, on demand fashion. As a simple example, it would be possible to augment highlights to take a list of multiple `hl_group`:s, which then would trivially map to a chain of multiple DecorSignHighlight entries. One small feature improvement included with this refactor itself, is that the restriction that extmarks cannot be removed inside a decoration provider has been lifted. These are instead safely lifetime extended on a "to free" list until the current iteration of screen drawing is done. NB: flags is a mess. but DecorLevel is useless, this slightly less so --- src/nvim/plines.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index b51d262cd9..acbb9637a4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -222,21 +222,25 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp) if (mark.pos.row != cts->cts_row || mark.pos.col > col) { break; } else if (mark.pos.col == col) { - if (!mt_end(mark)) { - Decoration decor = get_decor(mark); - if (decor.virt_text_pos == kVTInline) { - if (mt_right(mark)) { - cts->cts_cur_text_width_right += decor.virt_text_width; - } else { - cts->cts_cur_text_width_left += decor.virt_text_width; - } - size += decor.virt_text_width; - if (*s == TAB) { - // tab size changes because of the inserted text - size -= tab_size; - tab_size = win_chartabsize(wp, s, vcol + size); - size += tab_size; + if (!mt_end(mark) && mark.flags & (MT_FLAG_DECOR_VIRT_TEXT_INLINE)) { + DecorInline decor = mt_decor(mark); + DecorVirtText *vt = decor.ext ? decor.data.ext.vt : NULL; + while (vt) { + if (!(vt->flags & kVTIsLines) && vt->pos == kVPosInline) { + if (mt_right(mark)) { + cts->cts_cur_text_width_right += vt->width; + } else { + cts->cts_cur_text_width_left += vt->width; + } + size += vt->width; + if (*s == TAB) { + // tab size changes because of the inserted text + size -= tab_size; + tab_size = win_chartabsize(wp, s, vcol + size); + size += tab_size; + } } + vt = vt->next; } } } -- cgit From a827003e3052c6d9ee7bdb71518182e9bd76317d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Sat, 25 Nov 2023 11:32:32 +0100 Subject: build: rework IWYU mapping files Create mapping to most of the C spec and some POSIX specific functions. This is more robust than relying files shipped with IWYU. --- src/nvim/plines.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index acbb9637a4..7f6472ab42 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -8,6 +8,7 @@ #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/decoration.h" +#include "nvim/decoration_defs.h" #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/globals.h" -- cgit From 6361806aa28edca55ad3316a58bc3e936df9c0eb Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 26 Nov 2023 22:58:52 +0800 Subject: refactor: move garray_T to garray_defs.h (#26227) --- src/nvim/plines.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 7f6472ab42..acbb9637a4 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -8,7 +8,6 @@ #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/decoration.h" -#include "nvim/decoration_defs.h" #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/globals.h" -- cgit From 8b428ca8b79ebb7b36c3e403ff3bcb6924a635a6 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 16:00:21 +0100 Subject: build(IWYU): fix includes for func_attr.h --- src/nvim/plines.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index acbb9637a4..bd1351b490 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -10,6 +10,7 @@ #include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/indent.h" #include "nvim/macros.h" -- cgit From f4aedbae4cb1f206f5b7c6142697b71dd473059b Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 18:39:38 +0100 Subject: build(IWYU): fix includes for undo_defs.h --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index bd1351b490..e900d64715 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -21,7 +21,7 @@ #include "nvim/option.h" #include "nvim/option_vars.h" #include "nvim/plines.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/state.h" #include "nvim/types.h" #include "nvim/vim.h" -- cgit From 6c14ae6bfaf51415b555e9a6b85d1d280976358d Mon Sep 17 00:00:00 2001 From: dundargoc Date: Mon, 27 Nov 2023 20:27:32 +0100 Subject: refactor: rename types.h to types_defs.h --- src/nvim/plines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index e900d64715..f4a0daffd6 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -23,7 +23,7 @@ #include "nvim/plines.h" #include "nvim/pos_defs.h" #include "nvim/state.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -- cgit From 79b6ff28ad1204fbb4199b9092f5c578d88cb28e Mon Sep 17 00:00:00 2001 From: dundargoc Date: Tue, 28 Nov 2023 20:31:00 +0100 Subject: refactor: fix headers with IWYU --- src/nvim/plines.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index f4a0daffd6..5ad453043b 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -5,7 +5,7 @@ #include #include -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/charset.h" #include "nvim/decoration.h" #include "nvim/diff.h" @@ -13,7 +13,7 @@ #include "nvim/func_attr.h" #include "nvim/globals.h" #include "nvim/indent.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/mark.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -24,7 +24,7 @@ #include "nvim/pos_defs.h" #include "nvim/state.h" #include "nvim/types_defs.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "plines.c.generated.h" -- cgit From a6cba103cebce535279db197f9efeb34e9d1171f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 29 Nov 2023 20:32:40 +0800 Subject: refactor: move some constants out of vim_defs.h (#26298) --- src/nvim/plines.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/nvim/plines.c') diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 5ad453043b..6e9f92c193 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -24,7 +24,6 @@ #include "nvim/pos_defs.h" #include "nvim/state.h" #include "nvim/types_defs.h" -#include "nvim/vim_defs.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "plines.c.generated.h" -- cgit