This commit is contained in:
vanaigr 2024-05-05 10:05:59 -04:00 committed by GitHub
commit 1c2d9e8727
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 199 additions and 56 deletions

View File

@ -95,6 +95,7 @@ CSType init_charsize_arg(CharsizeArg *csarg, win_T *wp, linenr_T lnum, char *lin
csarg->virt_row = -1;
csarg->indent_width = INT_MIN;
csarg->use_tabstop = !wp->w_p_list || wp->w_p_lcs_chars.tab1;
csarg->lbr_skip_count = -1;
if (lnum > 0) {
if (marktree_itr_get_filter(wp->w_buffer->b_marktree, lnum - 1, 0, lnum, 0,
@ -283,46 +284,98 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco
}
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
&& vim_isbreak((uint8_t)cur[0]) && !vim_isbreak((uint8_t)cur[1])) {
char *t = csarg->line;
while (vim_isbreak((uint8_t)t[0])) {
t++;
if (wp->w_p_wrap && wp->w_p_lbr && wp->w_width_inner != 0) {
// note: cur[1] may be a part of current char.
need_lbr = vim_isbreak((uint8_t)cur[0]) && !vim_isbreak((uint8_t)cur[1]);
// Determine if a line break should be skipped.
// If the line starts with 'breakat' characters
// then the first line break is skipped.
if (csarg->lbr_skip_count < 0) {
if (EXPECT(cur == csarg->line, true)) {
// If iterating from the start of the line,
// the first time a possible linebreak position is encountered
// would be the first such position for the entire line.
csarg->lbr_skip_count = vim_isbreak((uint8_t)cur[0]) ? 1 : 0;
} else if (need_lbr) {
// CharsizeArg is initialized in the middle of the line.
// Need to scan the line from the start to find if there was a
// possible linebreak position before this one.
char *first_word = csarg->line;
while (vim_isbreak((uint8_t)(*first_word))) {
first_word++;
}
csarg->lbr_skip_count = cur < first_word ? 1 : 0;
}
}
// 'linebreak' is only needed when not in leading whitespace.
need_lbr = cur >= t;
}
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);
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;
if (need_lbr && csarg->lbr_skip_count > 0) {
csarg->lbr_skip_count--;
} else if (need_lbr) {
int const width = wp->w_width_inner - win_col_off(wp);
int const width2 = width + win_col_off2(wp);
int cur_vcol = vcol + size; // start of the word (next character)
bool can_break;
int colmax;
if (width <= 0 || cur_vcol == width) {
can_break = false;
} else if (cur_vcol < width) {
can_break = true;
colmax = width;
} else {
assert(width2 > 0); // win_col__off2() >= 0, width > 0
int const line_count = (cur_vcol - width) / width2;
int const line_vcol = (cur_vcol - width) % width2;
if (line_vcol == 0) { // next char is at the start of the line
can_break = false;
} else {
can_break = true;
colmax = width + (line_count + 1) * width2;
}
}
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;
if (can_break) {
int indent_width = csarg->indent_width;
if (indent_width == INT_MIN) {
indent_width = 0;
if (*sbr != NUL) {
indent_width += vim_strsize(sbr);
}
if (wp->w_p_bri) {
indent_width += get_breakindent_win(wp, line);
}
csarg->indent_width = indent_width;
}
vcol2 += win_chartabsize(wp, s, vcol2);
if (vcol2 >= colmax) { // doesn't fit
size = colmax - vcol + col_adj;
break;
can_break = (width2 - indent_width) > (colmax - cur_vcol);
}
if (can_break) {
int const start_vcol = cur_vcol;
bool const tabstop = csarg->use_tabstop;
StrCharInfo sci = utf_ptr2StrCharInfo(cur);
bool prev_break = true;
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
// note: doesn't consider inline virtual text
while (true) {
sci = utfc_next(sci);
bool const cur_break = vim_isbreak((uint8_t)(*sci.ptr));
if (*sci.ptr == NUL || (prev_break && !cur_break && cur_vcol != start_vcol)) {
break;
}
prev_break = cur_break;
cur_vcol += charsize_nowrap(buf, tabstop, cur_vcol, sci.chr.value);
if (cur_vcol > colmax) {
size = colmax - vcol;
break;
}
}
}
}
@ -330,6 +383,19 @@ CharSize charsize_regular(CharsizeArg *csarg, char *const cur, colnr_T const vco
return (CharSize){ .width = size, .head = head };
}
/// Return the number of cells the cur_char will take on the screen.
int charsize_nowrap(buf_T *buf, bool use_tabstop, int vcol, int32_t cur_char)
FUNC_ATTR_PURE
{
if (cur_char == TAB && use_tabstop) {
return tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array);
} else if (cur_char < 0) {
return kInvalidByteCells;
} else {
return char2cells(cur_char);
}
}
/// Like charsize_regular(), except it doesn't handle inline virtual text,
/// 'linebreak', 'breakindent' or 'showbreak'.
/// Handles normal characters, tabs and wrapping.

View File

@ -21,6 +21,7 @@ typedef struct {
char *line; ///< Start of the line.
bool use_tabstop; ///< Use 'tabstop' instead of char2cells() for a TAB.
int8_t lbr_skip_count; ///< Positive - skip next n line breaks, negative - not calculated.
int indent_width; ///< Width of 'showbreak' and 'breakindent' on wrapped
///< parts of lines, INT_MIN if not yet calculated.

View File

@ -142,10 +142,10 @@ describe('listlbr', function()
abcdef hijklmn pqrstuvwxyz_1060ABCDEFGHIJKLMNOP
Test 1: set linebreak
abcdef
+hijklmn
abcdef hijklmn
+pqrstuvwxyz_1060ABC
+DEFGHIJKLMNOP
~
Test 2: set linebreak + set list
^Iabcdef hijklmn^I
@ -154,10 +154,10 @@ describe('listlbr', function()
Test 3: set linebreak nolist
abcdef
+hijklmn
abcdef hijklmn
+pqrstuvwxyz_1060ABC
+DEFGHIJKLMNOP
1 aaaaaaaaaaaaaaaaaa
Test 4: set linebreak with tab and 1 line as long as screen: should break!

View File

@ -157,10 +157,10 @@ describe('linebreak', function()
abcdef hijklmn pqrstuvwxyz 1060ABCDEFGHIJKLMNOP
Test 1: set linebreak + set list + fancy listchars
abcdef
+hijklmn
abcdef hijklmn
+pqrstuvwxyz1060ABC
+DEFGHIJKLMNOPˑ
~
Test 2: set nolinebreak list
abcdef hijklmn

View File

@ -33,10 +33,10 @@ func Test_set_linebreak()
call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ " abcdef ",
\ "+hijklmn ",
\ " abcdef hijklmn ",
\ "+pqrstuvwxyz_1060ABC",
\ "+DEFGHIJKLMNOP ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
@ -64,10 +64,10 @@ func Test_linebreak_with_nolist()
call setline(1, "\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP ")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ " abcdef ",
\ "+hijklmn ",
\ " abcdef hijklmn ",
\ "+pqrstuvwxyz_1060ABC",
\ "+DEFGHIJKLMNOP ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
@ -373,19 +373,95 @@ func Test_ctrl_char_on_wrap_column()
call s:close_windows()
endfunc
func Test_linebreak_no_break_after_whitespace_only()
func Test_linebreak_break_after_whitespace()
call s:test_windows('setl ts=4 linebreak wrap')
call setline(1, "\t abcdefghijklmnopqrstuvwxyz" ..
setl list listchars=tab:>-,space:.
call setline(1, "\t abcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ " abcdefghijklmn",
\ "opqrstuvwxyzabcdefgh",
\ "ijklmnopqrstuvwxyz ",
\ "~ ",
\ ]
\ ">---.abcdefghijklmno",
\ 'pqrstuvwxyzabcdefghi',
\ "jklmnopqrstuvwxyz ",
\ "~ "]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunc
func Test_linebreak_with_breakindent()
call s:test_windows('setl ts=4 breakindent briopt=min:15 sbr=>>')
setl list listchars=tab:>-,space:.
call setline(1, "\t a bcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ ">---.a.bcdefghijklmn",
\ " >>opqrstuvwxyza",
\ " >>bcdefghijklmn",
\ " >>opqrstuvwxyz "]
call s:compare_lines(expect, lines)
call s:close_windows()
call s:test_windows('setl ts=4 breakindent briopt=min:16 sbr=>>')
setl list listchars=tab:>-,space:.
call setline(1, "\t a bcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ ">---.a. ",
\ " >>bcdefghijklmno",
\ " >>pqrstuvwxyzabc",
\ " >>defghijklmnopq"]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunc
func Test_linebreak_with_showbreak()
call s:test_windows('setl ts=4 linebreak showbreak=123456 wrap')
setl list listchars=tab:>-,space:.
call setline(1, "!@#$ abcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ '!@#$.abcdefghijklmno',
\ '123456pqrstuvwxyzabc',
\ '123456defghijklmnopq',
\ '123456rstuvwxyz ']
call s:compare_lines(expect, lines)
call s:close_windows()
call s:test_windows('setl ts=4 linebreak showbreak=12345 wrap')
setl list listchars=tab:>-,space:.
call setline(1, "!@#$ abcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ '!@#$.abcdefghijklmno',
\ '12345pqrstuvwxyzabcd',
\ '12345efghijklmnopqrs',
\ '12345tuvwxyz ']
call s:compare_lines(expect, lines)
call s:close_windows()
call s:test_windows('setl ts=4 linebreak showbreak=1234 wrap')
setl list listchars=tab:>-,space:.
call setline(1, "!@#$ abcdefghijklmnopqrstuvwxyz" ..
\ "abcdefghijklmnopqrstuvwxyz")
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ '!@#$. ',
\ '1234abcdefghijklmnop',
\ '1234qrstuvwxyzabcdef',
\ '1234ghijklmnopqrstuv']
call s:compare_lines(expect, lines)
call s:close_windows()
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -47,10 +47,10 @@ func Test_linebreak_with_fancy_listchars()
redraw!
let lines = s:screen_lines([1, 4], winwidth(0))
let expect = [
\ "▕———abcdef ",
\ "+hijklmn▕——— ",
\ "▕———abcdef hijklmn▕—",
\ "+pqrstuvwxyz␣1060ABC",
\ "+DEFGHIJKLMNOPˑ¶ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()

View File

@ -294,14 +294,14 @@ func Test_vartabs_linebreak()
call setline(1, "\tx\tx\tx\tx")
let expect = [' x ',
\ 'x x ',
\ 'x ']
\ 'x ',
\ 'x x ']
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect, lines)
setl list listchars=tab:>-
let expect = ['>---------x>------------------ ',
\ 'x>------------------x>------------------',
\ 'x ']
\ 'x>------------------ ',
\ 'x>------------------x ']
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect, lines)
setl linebreak vartabstop=40