aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/plines.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/plines.c')
-rw-r--r--src/nvim/plines.c585
1 files changed, 268 insertions, 317 deletions
diff --git a/src/nvim/plines.c b/src/nvim/plines.c
index 6e9f92c193..eca07f0144 100644
--- a/src/nvim/plines.c
+++ b/src/nvim/plines.c
@@ -6,16 +6,20 @@
#include <string.h>
#include "nvim/ascii_defs.h"
+#include "nvim/buffer.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/decoration.h"
+#include "nvim/decoration_defs.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_defs.h"
-#include "nvim/mark.h"
+#include "nvim/mark_defs.h"
+#include "nvim/marktree.h"
#include "nvim/mbyte.h"
+#include "nvim/mbyte_defs.h"
#include "nvim/memline.h"
#include "nvim/move.h"
#include "nvim/option.h"
@@ -23,6 +27,7 @@
#include "nvim/plines.h"
#include "nvim/pos_defs.h"
#include "nvim/state.h"
+#include "nvim/state_defs.h"
#include "nvim/types_defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
@@ -31,14 +36,14 @@
/// Functions calculating horizontal size of text, when displayed in a window.
-/// Return the number of characters 'c' will take on the screen, taking
-/// into account the size of a tab.
+/// Return the number of cells the first char in "p" will take on the screen,
+/// taking into account the size of a tab.
/// Also see getvcol()
///
/// @param p
/// @param col
///
-/// @return Number of characters.
+/// @return Number of cells.
int win_chartabsize(win_T *wp, char *p, colnr_T col)
{
buf_T *buf = wp->w_buffer;
@@ -48,48 +53,21 @@ int win_chartabsize(win_T *wp, char *p, colnr_T col)
return ptr2cells(p);
}
-/// Return the number of characters the string 's' will take on the screen,
-/// taking into account the size of a tab.
-///
-/// @param s
-///
-/// @return Number of characters the string will take on the screen.
-int linetabsize_str(char *s)
-{
- return linetabsize_col(0, s);
-}
-
-/// Like linetabsize_str(), but "s" starts at column "startcol".
+/// Like linetabsize_str(), but "s" starts at virtual column "startvcol".
///
/// @param startcol
/// @param s
///
-/// @return Number of characters the string will take on the screen.
-int linetabsize_col(int startcol, char *s)
+/// @return Number of cells the string will take on the screen.
+int linetabsize_col(int startvcol, char *s)
{
- chartabsize_T cts;
- init_chartabsize_arg(&cts, curwin, 0, startcol, s, s);
- while (*cts.cts_ptr != NUL) {
- cts.cts_vcol += lbr_chartabsize_adv(&cts);
+ CharsizeArg csarg;
+ CSType const cstype = init_charsize_arg(&csarg, curwin, 0, s);
+ if (cstype == kCharsizeFast) {
+ return linesize_fast(&csarg, startvcol, MAXCOL);
+ } else {
+ return linesize_regular(&csarg, startvcol, MAXCOL);
}
- clear_chartabsize_arg(&cts);
- return cts.cts_vcol;
-}
-
-/// Like linetabsize_str(), but for a given window instead of the current one.
-///
-/// @param wp
-/// @param line
-/// @param len
-///
-/// @return Number of characters the string will take on the screen.
-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 cts.cts_vcol;
}
/// Return the number of cells line "lnum" of window "wp" will take on the
@@ -99,144 +77,100 @@ int linetabsize(win_T *wp, linenr_T lnum)
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)
-{
- 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 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;
- }
-}
+static const uint32_t inline_filter[4] = {[kMTMetaInline] = kMTFilterSelect };
-/// Prepare the structure passed to chartabsize functions.
+/// Prepare the structure passed to charsize functions.
///
-/// "line" is the start of the line, "ptr" is the first relevant character.
+/// "line" is the start of the line.
/// 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)
+CSType init_charsize_arg(CharsizeArg *csarg, win_T *wp, linenr_T lnum, char *line)
{
- cts->cts_win = wp;
- 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;
- cts->cts_row = lnum - 1;
-
- 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 mark = marktree_itr_current(cts->cts_iter);
- if (mark.pos.row == cts->cts_row) {
- cts->cts_has_virt_text = true;
+ csarg->win = wp;
+ csarg->line = line;
+ csarg->max_head_vcol = 0;
+ csarg->cur_text_width_left = 0;
+ csarg->cur_text_width_right = 0;
+ csarg->virt_row = -1;
+ csarg->indent_width = INT_MIN;
+ csarg->use_tabstop = !wp->w_p_list || wp->w_p_lcs_chars.tab1;
+
+ if (lnum > 0) {
+ if (marktree_itr_get_filter(wp->w_buffer->b_marktree, lnum - 1, 0, lnum, 0,
+ inline_filter, csarg->iter)) {
+ csarg->virt_row = lnum - 1;
}
}
-}
-
-/// 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
-///
-/// @param cts
-///
-/// @return The number of characters taken up on the screen.
-int lbr_chartabsize(chartabsize_T *cts)
-{
- if (!curwin->w_p_lbr && *get_showbreak_value(curwin) == NUL
- && !curwin->w_p_bri && !cts->cts_has_virt_text) {
- if (curwin->w_p_wrap) {
- return win_nolbr_chartabsize(cts, NULL);
- }
- return win_chartabsize(curwin, cts->cts_ptr, cts->cts_vcol);
+ if (csarg->virt_row >= 0
+ || (wp->w_p_wrap && (wp->w_p_lbr || wp->w_p_bri || *get_showbreak_value(wp) != NUL))) {
+ return kCharsizeRegular;
+ } else {
+ return kCharsizeFast;
}
- return win_lbr_chartabsize(cts, NULL);
}
-/// Call lbr_chartabsize() and advance the pointer.
-///
-/// @param cts
-///
-/// @return The number of characters take up on the screen.
-int lbr_chartabsize_adv(chartabsize_T *cts)
-{
- int retval = lbr_chartabsize(cts);
- MB_PTR_ADV(cts->cts_ptr);
- 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
+/// Get the number of cells taken up on the screen for the given arguments.
+/// "csarg->cur_text_width_left" and "csarg->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 '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
+/// When "csarg->max_head_vcol" is positive, only count in "head" the size
+/// of 'showbreak'/'breakindent' before "csarg->max_head_vcol".
+/// When "csarg->max_head_vcol" is negative, only count in "head" the size
/// of 'showbreak'/'breakindent' before where cursor should be placed.
-///
-/// Warning: "*headp" may not be set if it's 0, init to 0 before calling.
-int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
+CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vcol,
+ int32_t const cur_char)
{
- win_T *wp = cts->cts_win;
- char *line = cts->cts_line; // start of the line
- char *s = cts->cts_ptr;
- colnr_T vcol = cts->cts_vcol;
- int mb_added = 0;
-
- cts->cts_cur_text_width_left = 0;
- cts->cts_cur_text_width_right = 0;
+ csarg->cur_text_width_left = 0;
+ csarg->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
- && !cts->cts_has_virt_text) {
- if (wp->w_p_wrap) {
- return win_nolbr_chartabsize(cts, headp);
- }
- return win_chartabsize(wp, s, vcol);
- }
+ win_T *wp = csarg->win;
+ buf_T *buf = wp->w_buffer;
+ char *line = csarg->line;
+ bool const use_tabstop = cur_char == TAB && csarg->use_tabstop;
+ int mb_added = 0;
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
+ int size;
+ int is_doublewidth = false;
+ if (use_tabstop) {
+ size = tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array);
+ } else if (*cur == NUL) {
+ // 1 cell for EOL list char (if present), as opposed to the two cell ^@
+ // for a NUL character in the text.
+ size = has_lcs_eol ? 1 : 0;
+ } else if (cur_char < 0) {
+ size = kInvalidByteCells;
+ } else {
+ size = char2cells(cur_char);
+ is_doublewidth = size == 2 && cur_char > 0x80;
}
- bool is_doublewidth = size == 2 && MB_BYTE2LEN((uint8_t)(*s)) > 1;
- if (cts->cts_has_virt_text) {
+ if (csarg->virt_row >= 0) {
int tab_size = size;
- int col = (int)(s - line);
+ int col = (int)(cur - line);
while (true) {
- MTKey mark = marktree_itr_current(cts->cts_iter);
- if (mark.pos.row != cts->cts_row || mark.pos.col > col) {
+ MTKey mark = marktree_itr_current(csarg->iter);
+ if (mark.pos.row != csarg->virt_row || mark.pos.col > col) {
break;
} else if (mark.pos.col == col) {
- if (!mt_end(mark) && mark.flags & (MT_FLAG_DECOR_VIRT_TEXT_INLINE)) {
+ if (!mt_end(mark) && (mark.flags & MT_FLAG_DECOR_VIRT_TEXT_INLINE)
+ && mt_scoped_in_win(mark, wp)) {
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;
+ csarg->cur_text_width_right += vt->width;
} else {
- cts->cts_cur_text_width_left += vt->width;
+ csarg->cur_text_width_left += vt->width;
}
size += vt->width;
- if (*s == TAB) {
+ if (use_tabstop) {
// tab size changes because of the inserted text
size -= tab_size;
- tab_size = win_chartabsize(wp, s, vcol + size);
+ tab_size = tabstop_padding(vcol + size, buf->b_p_ts, buf->b_p_vts_array);
size += tab_size;
}
}
@@ -244,7 +178,8 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
}
}
}
- marktree_itr_next(wp->w_buffer->b_marktree, cts->cts_iter);
+ marktree_itr_next_filter(wp->w_buffer->b_marktree, csarg->iter, csarg->virt_row + 1, 0,
+ inline_filter);
}
}
@@ -254,16 +189,17 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
mb_added = 1;
}
+ char *const sbr = get_showbreak_value(wp);
+
// May have to add something for 'breakindent' and/or 'showbreak'
// string at the start of a screen line.
int head = mb_added;
- 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;
- colnr_T max_head_vcol = cts->cts_max_head_vcol;
+ colnr_T max_head_vcol = csarg->max_head_vcol;
int added = 0;
// cells taken by 'showbreak'/'breakindent' before current char
@@ -274,11 +210,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
if (wcol >= width2 && width2 > 0) {
wcol %= width2;
}
- if (*sbr != NUL) {
- head_prev += vim_strsize(sbr);
- }
- if (wp->w_p_bri) {
- head_prev += get_breakindent_win(wp, line);
+ head_prev = csarg->indent_width;
+ if (head_prev == INT_MIN) {
+ head_prev = 0;
+ if (*sbr != NUL) {
+ head_prev += vim_strsize(sbr);
+ }
+ if (wp->w_p_bri) {
+ head_prev += get_breakindent_win(wp, line);
+ }
+ csarg->indent_width = head_prev;
}
if (wcol < head_prev) {
head_prev -= wcol;
@@ -295,12 +236,16 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
if (wcol + size > wp->w_width) {
// cells taken by 'showbreak'/'breakindent' halfway current char
- int head_mid = 0;
- if (*sbr != NUL) {
- head_mid += vim_strsize(sbr);
- }
- if (wp->w_p_bri) {
- head_mid += get_breakindent_win(wp, line);
+ int head_mid = csarg->indent_width;
+ if (head_mid == INT_MIN) {
+ head_mid = 0;
+ if (*sbr != NUL) {
+ head_mid += vim_strsize(sbr);
+ }
+ if (wp->w_p_bri) {
+ head_mid += get_breakindent_win(wp, line);
+ }
+ csarg->indent_width = head_mid;
}
if (head_mid > 0 && wcol + size > wp->w_width_inner) {
// Calculate effective window width.
@@ -320,7 +265,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, *s == NUL);
+ int off = virt_text_cursor_off(csarg, *cur == NUL);
if (off >= prev_rem) {
if (size > off) {
head += (1 + (off - prev_rem) / width) * head_mid;
@@ -335,25 +280,20 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
size += added;
}
- if (headp != NULL) {
- *headp = head;
- }
-
- colnr_T vcol_start = 0; // start from where to consider linebreak
+ bool need_lbr = false;
// If 'linebreak' set check at a blank before a non-blank if the line
- // needs a break here
- if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0) {
- char *t = cts->cts_line;
+ // needs a break here.
+ if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width_inner != 0
+ && vim_isbreak((uint8_t)cur[0]) && !vim_isbreak((uint8_t)cur[1])) {
+ char *t = csarg->line;
while (vim_isbreak((uint8_t)t[0])) {
t++;
}
- vcol_start = (colnr_T)(t - cts->cts_line);
+ // 'linebreak' is only needed when not in leading whitespace.
+ need_lbr = cur >= t;
}
- if (wp->w_p_lbr && vcol_start <= vcol
- && vim_isbreak((uint8_t)s[0])
- && !vim_isbreak((uint8_t)s[1])
- && wp->w_p_wrap
- && wp->w_width_inner != 0) {
+ if (need_lbr) {
+ char *s = cur;
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
int numberextra = win_col_off(wp);
@@ -385,39 +325,54 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)
}
}
- return size;
+ return (CharSize){ .width = size, .head = head };
}
-/// Like win_lbr_chartabsize(), except that we know 'linebreak' is off and
-/// 'wrap' is on. This means we need to check for a double-byte character that
-/// doesn't fit at the end of the screen line.
-///
-/// @param cts
-/// @param headp
+/// Like charsize_regular(), except it doesn't handle inline virtual text,
+/// 'linebreak', 'breakindent' or 'showbreak'.
+/// Handles normal characters, tabs and wrapping.
+/// This function is always inlined.
///
-/// @return The number of characters take up on the screen.
-static int win_nolbr_chartabsize(chartabsize_T *cts, int *headp)
+/// @see charsize_regular
+/// @see charsize_fast
+static inline CharSize charsize_fast_impl(win_T *const wp, bool use_tabstop, colnr_T const vcol,
+ int32_t const cur_char)
+ FUNC_ATTR_PURE FUNC_ATTR_ALWAYS_INLINE
{
- win_T *wp = cts->cts_win;
- char *s = cts->cts_ptr;
- colnr_T col = cts->cts_vcol;
-
- 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);
- }
- int n = ptr2cells(s);
-
- // Add one cell for a double-width character in the last column of the
- // window, displayed with a ">".
- if ((n == 2) && (MB_BYTE2LEN((uint8_t)(*s)) > 1) && in_win_border(wp, col)) {
- if (headp != NULL) {
- *headp = 1;
+ // A tab gets expanded, depending on the current column
+ if (cur_char == TAB && use_tabstop) {
+ return (CharSize){
+ .width = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
+ wp->w_buffer->b_p_vts_array)
+ };
+ } else {
+ int width;
+ if (cur_char < 0) {
+ width = kInvalidByteCells;
+ } else {
+ width = char2cells(cur_char);
+ }
+
+ // If a double-width char doesn't fit at the end of a line, it wraps to the next line,
+ // and the last column displays a '>'.
+ if (width == 2 && cur_char >= 0x80 && wp->w_p_wrap && in_win_border(wp, vcol)) {
+ return (CharSize){ .width = 3, .head = 1 };
+ } else {
+ return (CharSize){ .width = width };
}
- return 3;
}
- return n;
+}
+
+/// Like charsize_regular(), except it doesn't handle inline virtual text,
+/// 'linebreak', 'breakindent' or 'showbreak'.
+/// Handles normal characters, tabs and wrapping.
+/// Can be used if CSType is kCharsizeFast.
+///
+/// @see charsize_regular
+CharSize charsize_fast(CharsizeArg *csarg, colnr_T const vcol, int32_t const cur_char)
+ FUNC_ATTR_PURE
+{
+ return charsize_fast_impl(csarg->win, csarg->use_tabstop, vcol, cur_char);
}
/// Check that virtual column "vcol" is in the rightmost column of window "wp".
@@ -448,19 +403,65 @@ static bool in_win_border(win_T *wp, colnr_T vcol)
return (vcol - width1) % width2 == width2 - 1;
}
+/// Calculate virtual column until the given "len".
+///
+/// @param arg Argument to charsize functions.
+/// @param vcol Starting virtual column.
+/// @param len First byte of the end character, or MAXCOL.
+///
+/// @return virtual column before the character at "len",
+/// or full size of the line if "len" is MAXCOL.
+int linesize_regular(CharsizeArg *const csarg, int vcol, colnr_T const len)
+{
+ char *const line = csarg->line;
+
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ while (ci.ptr - line < len && *ci.ptr != NUL) {
+ vcol += charsize_regular(csarg, ci.ptr, vcol, ci.chr.value).width;
+ ci = utfc_next(ci);
+ }
+
+ // Check for inline virtual text after the end of the line.
+ if (len == MAXCOL && csarg->virt_row >= 0) {
+ (void)charsize_regular(csarg, ci.ptr, vcol, ci.chr.value);
+ vcol += csarg->cur_text_width_left + csarg->cur_text_width_right;
+ }
+
+ return vcol;
+}
+
+/// Like linesize_regular(), but can be used when CStype is kCharsizeFast.
+///
+/// @see linesize_regular
+int linesize_fast(CharsizeArg const *const csarg, int vcol, colnr_T const len)
+{
+ win_T *const wp = csarg->win;
+ bool const use_tabstop = csarg->use_tabstop;
+
+ char *const line = csarg->line;
+
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ while (ci.ptr - line < len && *ci.ptr != NUL) {
+ vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
+ ci = utfc_next(ci);
+ }
+
+ return vcol;
+}
+
/// Get how many virtual columns inline virtual text should offset the cursor.
///
-/// @param cts should contain information stored by win_lbr_chartabsize()
+/// @param csarg should contain information stored by charsize_regular()
/// 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)
+static int virt_text_cursor_off(const CharsizeArg *csarg, bool on_NUL)
{
int off = 0;
if (!on_NUL || !(State & MODE_NORMAL)) {
- off += cts->cts_cur_text_width_left;
+ off += csarg->cur_text_width_left;
}
if (!on_NUL && (State & MODE_NORMAL)) {
- off += cts->cts_cur_text_width_right;
+ off += csarg->cur_text_width_right;
}
return off;
}
@@ -479,115 +480,53 @@ static int virt_text_cursor_off(chartabsize_T *cts, bool on_NUL)
/// @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;
- colnr_T *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
+ char *const line = ml_get_buf(wp->w_buffer, pos->lnum); // start of the line
+ int const end_col = pos->col;
- 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;
+ CharsizeArg csarg;
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);
+ CSType const cstype = init_charsize_arg(&csarg, wp, pos->lnum, line);
+ csarg.max_head_vcol = -1;
- // 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;
+ colnr_T vcol = 0;
+ CharSize char_size;
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ if (cstype == kCharsizeFast) {
+ bool const use_tabstop = csarg.use_tabstop;
+ while (true) {
+ if (*ci.ptr == NUL) {
+ // if cursor is at NUL, it is treated like 1 cell char
+ char_size = (CharSize){ .width = 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
+ char_size = charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value);
+ StrCharInfo const next = utfc_next(ci);
+ if (next.ptr - line > end_col) {
break;
}
-
- vcol += incr;
- MB_PTR_ADV(ptr);
+ ci = next;
+ vcol += char_size.width;
}
} 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);
+ char_size = charsize_regular(&csarg, ci.ptr, vcol, ci.chr.value);
+ if (*ci.ptr == NUL) {
+ // if cursor is at NUL, it is treated like 1 cell char unless there is virtual text
+ char_size.width = MAX(1, csarg.cur_text_width_left + csarg.cur_text_width_right);
on_NUL = true;
break;
}
-
- if ((posptr != NULL) && (cts.cts_ptr >= posptr)) {
- // character at pos->col
+ StrCharInfo const next = utfc_next(ci);
+ if (next.ptr - line > end_col) {
break;
}
-
- cts.cts_vcol += incr;
- MB_PTR_ADV(cts.cts_ptr);
+ ci = next;
+ vcol += char_size.width;
}
- vcol = cts.cts_vcol;
- ptr = cts.cts_ptr;
}
- clear_chartabsize_arg(&cts);
+
+ int head = char_size.head;
+ int incr = char_size.width;
if (start != NULL) {
*start = vcol + head;
@@ -598,7 +537,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *en
}
if (cursor != NULL) {
- if ((*ptr == TAB)
+ if (ci.chr.value == TAB
&& (State & MODE_NORMAL)
&& !wp->w_p_list
&& !virtual_active()
@@ -606,7 +545,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 {
- vcol += virt_text_cursor_off(&cts, on_NUL);
+ vcol += virt_text_cursor_off(&csarg, on_NUL);
// cursor at start
*cursor = vcol + head;
}
@@ -730,7 +669,8 @@ void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right
/// 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;
+ return ((wp->w_p_diff && diffopt_filler())
+ || buf_meta_total(wp->w_buffer, kMTMetaLines));
}
/// Return the number of filler lines above "lnum".
@@ -795,14 +735,18 @@ int plines_win_nofill(win_T *wp, linenr_T lnum, bool limit_winheight)
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) {
+ CharsizeArg csarg;
+ CSType const cstype = init_charsize_arg(&csarg, wp, lnum, s);
+ if (*s == NUL && csarg.virt_row < 0) {
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;
+
+ int64_t col;
+ if (cstype == kCharsizeFast) {
+ col = linesize_fast(&csarg, 0, MAXCOL);
+ } else {
+ col = linesize_regular(&csarg, 0, MAXCOL);
+ }
// If list mode is on, then the '$' at the end of the line may take up one
// extra column.
@@ -841,26 +785,33 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column)
char *line = ml_get_buf(wp->w_buffer, lnum);
- colnr_T col = 0;
- chartabsize_T cts;
+ CharsizeArg csarg;
+ CSType const cstype = init_charsize_arg(&csarg, wp, lnum, line);
- 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);
+ colnr_T vcol = 0;
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ if (cstype == kCharsizeFast) {
+ bool const use_tabstop = csarg.use_tabstop;
+ while (*ci.ptr != NUL && --column >= 0) {
+ vcol += charsize_fast_impl(wp, use_tabstop, vcol, ci.chr.value).width;
+ ci = utfc_next(ci);
+ }
+ } else {
+ while (*ci.ptr != NUL && --column >= 0) {
+ vcol += charsize_regular(&csarg, ci.ptr, vcol, ci.chr.value).width;
+ ci = utfc_next(ci);
+ }
}
- // If *cts.cts_ptr is a TAB, and the TAB is not displayed as ^I, and we're not
+ // If current char 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;
+ colnr_T col = vcol;
+ if (ci.chr.value == TAB && (State & MODE_NORMAL) && csarg.use_tabstop) {
+ col += win_charsize(cstype, col, ci.ptr, ci.chr.value, &csarg).width - 1;
}
- clear_chartabsize_arg(&cts);
// Add column offset for 'number', 'relativenumber', 'foldcolumn', etc.
int width = wp->w_width_inner - win_col_off(wp);