diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index a505df5e935239..f5e9c5ecc74a86 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -127,6 +127,10 @@ struct buffheader { typedef struct { int wo_arab; # define w_p_arab w_onebuf_opt.wo_arab /* 'arabic' */ + int wo_bri; +# define w_p_bri w_onebuf_opt.wo_bri /* 'breakindent' */ + char_u *wo_briopt; +# define w_p_briopt w_onebuf_opt.wo_briopt /* 'breakindentopt' */ int wo_diff; # define w_p_diff w_onebuf_opt.wo_diff /* 'diff' */ long wo_fdc; @@ -1060,6 +1064,9 @@ struct window_S { long_u w_p_fde_flags; /* flags for 'foldexpr' */ long_u w_p_fdt_flags; /* flags for 'foldtext' */ int *w_p_cc_cols; /* array of columns to highlight or NULL */ + int w_p_brimin; /* minimum width for breakindent */ + int w_p_brishift; /* additional shift for breakindent */ + int w_p_brisbr; /* sbr in 'briopt' */ /* transform a pointer to a "onebuf" option into a "allbuf" option */ #define GLOBAL_WO(p) ((char *)p + sizeof(winopt_T)) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 549c926763579f..efd356ccabdce4 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -10,6 +10,7 @@ #include "nvim/charset.h" #include "nvim/farsi.h" #include "nvim/func_attr.h" +#include "nvim/indent.h" #include "nvim/main.h" #include "nvim/mbyte.h" #include "nvim/memline.h" @@ -778,9 +779,10 @@ int linetabsize(char_u *s) int linetabsize_col(int startcol, char_u *s) { colnr_T col = startcol; + char_u *line = s; /* pointer to start of line, for breakindent */ while (*s != NUL) { - col += lbr_chartabsize_adv(&s, col); + col += lbr_chartabsize_adv(line, &s, col); } return (int)col; } @@ -788,17 +790,17 @@ int linetabsize_col(int startcol, char_u *s) /// Like linetabsize(), but for a given window instead of the current one. /// /// @param wp -/// @param p +/// @param line /// @param len /// /// @return Number of characters the string will take on the screen. -int win_linetabsize(win_T *wp, char_u *p, colnr_T len) +int win_linetabsize(win_T *wp, char_u *line, colnr_T len) { colnr_T col = 0; char_u *s; - for (s = p; *s != NUL && (len == MAXCOL || s < p + len); mb_ptr_adv(s)) { - col += win_lbr_chartabsize(wp, s, col, NULL); + for (s = line; *s != NUL && (len == MAXCOL || s < line + len); mb_ptr_adv(s)) { + col += win_lbr_chartabsize(wp, line, s, col, NULL); } return (int)col; } @@ -925,32 +927,34 @@ int vim_isprintc_strict(int c) /// like chartabsize(), but also check for line breaks on the screen /// +/// @param line /// @param s /// @param col /// /// @return The number of characters taken up on the screen. -int lbr_chartabsize(unsigned char *s, colnr_T col) +int lbr_chartabsize(char_u *line, unsigned char *s, colnr_T col) { - if (!curwin->w_p_lbr && (*p_sbr == NUL)) { + if (!curwin->w_p_lbr && (*p_sbr == NUL) && !curwin->w_p_bri) { if (curwin->w_p_wrap) { return win_nolbr_chartabsize(curwin, s, col, NULL); } RET_WIN_BUF_CHARTABSIZE(curwin, curbuf, s, col) } - return win_lbr_chartabsize(curwin, s, col, NULL); + return win_lbr_chartabsize(curwin, line == NULL ? s: line, s, col, NULL); } /// Call lbr_chartabsize() and advance the pointer. /// +/// @param line /// @param s /// @param col /// /// @return The number of characters take up on the screen. -int lbr_chartabsize_adv(char_u **s, colnr_T col) +int lbr_chartabsize_adv(char_u *line, char_u **s, colnr_T col) { int retval; - retval = lbr_chartabsize(*s, col); + retval = lbr_chartabsize(line, *s, col); mb_ptr_adv(*s); return retval; } @@ -962,12 +966,13 @@ int lbr_chartabsize_adv(char_u **s, colnr_T col) /// value, init to 0 before calling. /// /// @param wp +/// @param line /// @param s /// @param col /// @param headp /// /// @return The number of characters taken up on the screen. -int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) +int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *headp) { colnr_T col2; colnr_T colmax; @@ -978,8 +983,8 @@ int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) int tab_corr = (*s == TAB); int n; - // No 'linebreak' and 'showbreak': return quickly. - if (!wp->w_p_lbr && (*p_sbr == NUL)) { + // No 'linebreak', 'showbreak' and 'breakindent': return quickly. + if (!wp->w_p_lbr && !wp->w_p_bri && (*p_sbr == NUL)) { if (wp->w_p_wrap) { return win_nolbr_chartabsize(wp, s, col, headp); } @@ -1042,11 +1047,12 @@ int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) mb_added = 1; } - // May have to add something for 'showbreak' string at start of line + // May have to add something for 'breakindent' and/or 'showbreak' + // string at start of line. // Set *headp to the size of what we add. added = 0; - if ((*p_sbr != NUL) && wp->w_p_wrap && (col != 0)) { + if ((*p_sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && (col != 0)) { numberextra = win_col_off(wp); col += numberextra + mb_added; @@ -1059,7 +1065,12 @@ int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp) } if ((col == 0) || (col + size > (colnr_T)wp->w_width)) { - added = vim_strsize(p_sbr); + added = 0; + if (*p_sbr != NUL) + added += vim_strsize(p_sbr); + if (wp->w_p_bri) + added += get_breakindent_win(wp, line); + if (tab_corr) { size += (added / wp->w_buffer->b_p_ts) * wp->w_buffer->b_p_ts; } else { @@ -1160,13 +1171,14 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T vcol; char_u *ptr; // points to current char char_u *posptr; // points to char at pos->col + char_u *line; // start of the line int incr; int head; int ts = wp->w_buffer->b_p_ts; int c; vcol = 0; - ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); + line = ptr = ml_get_buf(wp->w_buffer, pos->lnum, FALSE); if (pos->col == MAXCOL) { // continue until the NUL @@ -1176,11 +1188,13 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, } // This function is used very often, do some speed optimizations. - // When 'list', 'linebreak' and 'showbreak' are not set use a simple loop. - // Also use this when 'list' is set but tabs take their normal size. + // When 'list', 'linebreak', 'showbreak' and 'breakindent' are not set + // use a simple loop. + // Also use this when 'list' is set but tabs take their normal size. if ((!wp->w_p_list || (lcs_tab1 != NUL)) && !wp->w_p_lbr - && (*p_sbr == NUL)) { + && (*p_sbr == NUL) + && !wp->w_p_bri ) { for (;;) { head = 0; c = *ptr; @@ -1232,7 +1246,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, for (;;) { // A tab gets expanded, depending on the current column head = 0; - incr = win_lbr_chartabsize(wp, ptr, vcol, &head); + incr = win_lbr_chartabsize(wp, line, ptr, vcol, &head); // make sure we don't go past the end of the line if (*ptr == NUL) { diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 18e95f07410605..9d41ef44619575 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -136,7 +136,7 @@ static int coladvance2( ptr = line; while (col <= wcol && *ptr != NUL) { /* Count a tab for what it's worth (if list mode not on) */ - csize = win_lbr_chartabsize(curwin, ptr, col, &head); + csize = win_lbr_chartabsize(curwin, line, ptr, col, &head); mb_ptr_adv(ptr); col += csize; } diff --git a/src/nvim/edit.c b/src/nvim/edit.c index c0a99baf0fa5e4..feea29244eb070 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -1546,7 +1546,7 @@ change_indent ( new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col); else ++new_cursor_col; - vcol += lbr_chartabsize(ptr + new_cursor_col, (colnr_T)vcol); + vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol); } vcol = last_vcol; @@ -5884,9 +5884,11 @@ int oneleft(void) width = 1; for (;; ) { coladvance(v - width); - /* getviscol() is slow, skip it when 'showbreak' is empty and - * there are no multi-byte characters */ + /* getviscol() is slow, skip it when 'showbreak' is empty, + 'breakindent' is not set and there are no multi-byte + characters */ if ((*p_sbr == NUL + && !curwin->w_p_bri && !has_mbyte ) || getviscol() < v) break; @@ -7900,10 +7902,10 @@ static int ins_tab(void) getvcol(curwin, &fpos, &vcol, NULL, NULL); getvcol(curwin, cursor, &want_vcol, NULL, NULL); - /* Use as many TABs as possible. Beware of 'showbreak' and - * 'linebreak' adding extra virtual columns. */ + /* Use as many TABs as possible. Beware of 'breakindent', 'showbreak' + and 'linebreak' adding extra virtual columns. */ while (vim_iswhite(*ptr)) { - i = lbr_chartabsize((char_u *)"\t", vcol); + i = lbr_chartabsize(NULL, (char_u *)"\t", vcol); if (vcol + i > want_vcol) break; if (*ptr != TAB) { @@ -7922,10 +7924,11 @@ static int ins_tab(void) if (change_col >= 0) { int repl_off = 0; + char_u *line = ptr; /* Skip over the spaces we need. */ while (vcol < want_vcol && *ptr == ' ') { - vcol += lbr_chartabsize(ptr, vcol); + vcol += lbr_chartabsize(line, ptr, vcol); ++ptr; ++repl_off; } @@ -8112,6 +8115,7 @@ int ins_copychar(linenr_T lnum) int c; int temp; char_u *ptr, *prev_ptr; + char_u *line; if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { vim_beep(); @@ -8120,12 +8124,12 @@ int ins_copychar(linenr_T lnum) /* try to advance to the cursor column */ temp = 0; - ptr = ml_get(lnum); + line = ptr = ml_get(lnum); prev_ptr = ptr; validate_virtcol(); while ((colnr_T)temp < curwin->w_virtcol && *ptr != NUL) { prev_ptr = ptr; - temp += lbr_chartabsize_adv(&ptr, (colnr_T)temp); + temp += lbr_chartabsize_adv(line, &ptr, (colnr_T)temp); } if ((colnr_T)temp > curwin->w_virtcol) ptr = prev_ptr; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 9b09abb1b78def..7d2e038e582307 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1813,10 +1813,10 @@ getexmodeline ( p = (char_u *)line_ga.ga_data; p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8); + indent = get_indent_str(p, 8, FALSE); indent += sw - indent % sw; add_indent: - while (get_indent_str(p, 8) < indent) { + while (get_indent_str(p, 8, FALSE) < indent) { char_u *s = skipwhite(p); ga_grow(&line_ga, 1); @@ -1854,11 +1854,11 @@ getexmodeline ( p[--line_ga.ga_len] = NUL; } else { p[line_ga.ga_len] = NUL; - indent = get_indent_str(p, 8); + indent = get_indent_str(p, 8, FALSE); --indent; indent -= indent % get_sw_value(curbuf); } - while (get_indent_str(p, 8) > indent) { + while (get_indent_str(p, 8, FALSE) > indent) { char_u *s = skipwhite(p); memmove(s - 1, s, line_ga.ga_len - (s - p) + 1); diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b169899cea5a73..3055525230d347 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2161,7 +2161,7 @@ static int vgetorpeek(int advance) while (col < curwin->w_cursor.col) { if (!vim_iswhite(ptr[col])) curwin->w_wcol = vcol; - vcol += lbr_chartabsize(ptr + col, + vcol += lbr_chartabsize(ptr, ptr + col, (colnr_T)vcol); if (has_mbyte) col += (*mb_ptr2len)(ptr + col); diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 91fc1588cc7526..9a3dec172a4160 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -6,8 +6,10 @@ #include "nvim/memory.h" #include "nvim/misc1.h" #include "nvim/misc2.h" +#include "nvim/move.h" #include "nvim/option.h" #include "nvim/regexp.h" +#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/strings.h" #include "nvim/undo.h" @@ -20,14 +22,14 @@ // Count the size (in window cells) of the indent in the current line. int get_indent(void) { - return get_indent_str(get_cursor_line_ptr(), (int)curbuf->b_p_ts); + return get_indent_str(get_cursor_line_ptr(), (int)curbuf->b_p_ts, false); } // Count the size (in window cells) of the indent in line "lnum". int get_indent_lnum(linenr_T lnum) { - return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts); + return get_indent_str(ml_get(lnum), (int)curbuf->b_p_ts, false); } @@ -35,20 +37,27 @@ int get_indent_lnum(linenr_T lnum) // "buf". int get_indent_buf(buf_T *buf, linenr_T lnum) { - return get_indent_str(ml_get_buf(buf, lnum, false), (int)buf->b_p_ts); + return get_indent_str(ml_get_buf(buf, lnum, false), (int)buf->b_p_ts, false); } // Count the size (in window cells) of the indent in line "ptr", with // 'tabstop' at "ts". -int get_indent_str(char_u *ptr, int ts) +// If @param list is TRUE, count only screen size for tabs. +int get_indent_str(char_u *ptr, int ts, int list) { int count = 0; for (; *ptr; ++ptr) { // Count a tab for what it is worth. if (*ptr == TAB) { - count += ts - (count % ts); + if (!list || lcs_tab1) { // count a tab for what it is worth + count += ts - (count % ts); + } else { + // in list mode, when tab is not set, count screen char width for Tab: + // ^I + count += ptr2cells(ptr); + } } else if (*ptr == ' ') { // Count a space for one. count++; @@ -429,6 +438,50 @@ int get_number_indent(linenr_T lnum) return (int)col; } +/* + * Return appropriate space number for breakindent, taking influencing + * parameters into account. Window must be specified, since it is not + * necessarily always the current one. + */ +int get_breakindent_win(win_T *wp, char_u *line) { + static int prev_indent = 0; /* cached indent value */ + static int prev_ts = 0L; /* cached tabstop value */ + static char_u *prev_line = NULL; /* cached pointer to line */ + int bri = 0; + /* window width minus window margin space, i.e. what rests for text */ + const int eff_wwidth = wp->w_width + - ((wp->w_p_nu || wp->w_p_rnu) + && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) + ? number_width(wp) + 1 : 0); + + /* used cached indent, unless pointer or 'tabstop' changed */ + if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts) + { + prev_line = line; + prev_ts = wp->w_buffer->b_p_ts; + prev_indent = get_indent_str(line, + (int)wp->w_buffer->b_p_ts, wp->w_p_list) + wp->w_p_brishift; + } + + /* indent minus the length of the showbreak string */ + bri = prev_indent; + if (wp->w_p_brisbr) + bri -= vim_strsize(p_sbr); + + /* Add offset for number column, if 'n' is in 'cpoptions' */ + bri += win_col_off2(wp); + + /* never indent past left window margin */ + if (bri < 0) + bri = 0; + /* always leave at least bri_min characters on the left, + * if text width is sufficient */ + else if (bri > eff_wwidth - wp->w_p_brimin) + bri = (eff_wwidth - wp->w_p_brimin < 0) + ? 0 : eff_wwidth - wp->w_p_brimin; + + return bri; +} // When extra == 0: Return true if the cursor is before or on the first // non-blank in the line. @@ -604,10 +657,12 @@ int get_lisp_indent(void) if (vi_lisp && (get_indent() == 0)) { amount = 2; } else { + char_u *line = that; + amount = 0; while (*that && col) { - amount += lbr_chartabsize_adv(&that, (colnr_T)amount); + amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); col--; } @@ -624,7 +679,7 @@ int get_lisp_indent(void) firsttry = amount; while (vim_iswhite(*that)) { - amount += lbr_chartabsize(that, (colnr_T)amount); + amount += lbr_chartabsize(line, that, (colnr_T)amount); that++; } @@ -654,15 +709,15 @@ int get_lisp_indent(void) parencount--; } if ((*that == '\\') && (*(that + 1) != NUL)) { - amount += lbr_chartabsize_adv(&that, (colnr_T)amount); + amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); } - amount += lbr_chartabsize_adv(&that, (colnr_T)amount); + amount += lbr_chartabsize_adv(line, &that, (colnr_T)amount); } } while (vim_iswhite(*that)) { - amount += lbr_chartabsize(that, (colnr_T)amount); + amount += lbr_chartabsize(line, that, (colnr_T)amount); that++; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index f025ff7f65350c..3e057f30784101 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -189,7 +189,7 @@ open_line ( /* * count white space on current line */ - newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts); + newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, FALSE); if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) newindent = second_line_indent; /* for ^^D command in insert mode */ @@ -626,7 +626,7 @@ open_line ( if (curbuf->b_p_ai || do_si ) - newindent = get_indent_str(leader, (int)curbuf->b_p_ts); + newindent = get_indent_str(leader, (int)curbuf->b_p_ts, FALSE); /* Add the indent offset */ if (newindent + off < 0) { @@ -1301,6 +1301,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) char_u *s; int lines = 0; int width; + char_u *line; /* Check for filler lines above this buffer line. When folded the result * is one line anyway. */ @@ -1312,11 +1313,11 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) if (wp->w_width == 0) return lines + 1; - s = ml_get_buf(wp->w_buffer, lnum, FALSE); + line = s = ml_get_buf(wp->w_buffer, lnum, FALSE); col = 0; while (*s != NUL && --column >= 0) { - col += win_lbr_chartabsize(wp, s, (colnr_T)col, NULL); + col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL); mb_ptr_adv(s); } @@ -1328,7 +1329,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) * 'ts') -- webb. */ if (*s == TAB && (State & NORMAL) && (!wp->w_p_list || lcs_tab1)) - col += win_lbr_chartabsize(wp, s, (colnr_T)col, NULL) - 1; + col += win_lbr_chartabsize(wp, line, s, (colnr_T)col, NULL) - 1; /* * Add column offset for 'number', 'relativenumber', 'foldcolumn', etc. diff --git a/src/nvim/ops.c b/src/nvim/ops.c index e80ffac6e18e34..ce059df0ce24b2 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -343,7 +343,8 @@ static void shift_block(oparg_T *oap, int amount) ++bd.textstart; } for (; vim_iswhite(*bd.textstart); ) { - incr = lbr_chartabsize_adv(&bd.textstart, (colnr_T)(bd.start_vcol)); + // TODO: is passing bd.textstart for start of the line OK? + incr = lbr_chartabsize_adv(bd.textstart, &bd.textstart, (colnr_T)(bd.start_vcol)); total += incr; bd.start_vcol += incr; } @@ -398,7 +399,7 @@ static void shift_block(oparg_T *oap, int amount) non_white_col = bd.start_vcol; while (vim_iswhite(*non_white)) { - incr = lbr_chartabsize_adv(&non_white, non_white_col); + incr = lbr_chartabsize_adv(bd.textstart, &non_white, non_white_col); non_white_col += incr; } @@ -422,7 +423,10 @@ static void shift_block(oparg_T *oap, int amount) if (bd.startspaces) verbatim_copy_width -= bd.start_char_vcols; while (verbatim_copy_width < destination_col) { - incr = lbr_chartabsize(verbatim_copy_end, verbatim_copy_width); + char_u *line = verbatim_copy_end; + + // TODO: is passing verbatim_copy_end for start of the line OK? + incr = lbr_chartabsize(line, verbatim_copy_end, verbatim_copy_width); if (verbatim_copy_width + incr > destination_col) break; verbatim_copy_width += incr; @@ -2794,7 +2798,7 @@ do_put ( oldlen = (int)STRLEN(oldp); for (ptr = oldp; vcol < col && *ptr; ) { /* Count a tab for what it's worth (if list mode not on) */ - incr = lbr_chartabsize_adv(&ptr, (colnr_T)vcol); + incr = lbr_chartabsize_adv(oldp, &ptr, (colnr_T)vcol); vcol += incr; } bd.textcol = (colnr_T)(ptr - oldp); @@ -2824,7 +2828,7 @@ do_put ( /* calculate number of spaces required to fill right side of block*/ spaces = y_width + 1; for (j = 0; j < yanklen; j++) - spaces -= lbr_chartabsize(&y_array[i][j], 0); + spaces -= lbr_chartabsize(NULL, &y_array[i][j], 0); if (spaces < 0) spaces = 0; @@ -4082,7 +4086,7 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i prev_pstart = line; while (bdp->start_vcol < oap->start_vcol && *pstart) { /* Count a tab for what it's worth (if list mode not on) */ - incr = lbr_chartabsize(pstart, (colnr_T)bdp->start_vcol); + incr = lbr_chartabsize(line, pstart, (colnr_T)bdp->start_vcol); bdp->start_vcol += incr; if (vim_iswhite(*pstart)) { bdp->pre_whitesp += incr; @@ -4131,7 +4135,9 @@ static void block_prep(oparg_T *oap, struct block_def *bdp, linenr_T lnum, int i while (bdp->end_vcol <= oap->end_vcol && *pend != NUL) { /* Count a tab for what it's worth (if list mode not on) */ prev_pend = pend; - incr = lbr_chartabsize_adv(&pend, (colnr_T)bdp->end_vcol); + // TODO: is passing prev_pend for start of the line OK? + // prehaps it should be "line" + incr = lbr_chartabsize_adv(prev_pend, &pend, (colnr_T)bdp->end_vcol); bdp->end_vcol += incr; } if (bdp->end_vcol <= oap->end_vcol diff --git a/src/nvim/option.c b/src/nvim/option.c index 8390efe9798279..ccf77f9ed1141f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -171,6 +171,8 @@ */ #define PV_LIST OPT_WIN(WV_LIST) # define PV_ARAB OPT_WIN(WV_ARAB) +# define PV_BRI OPT_WIN(WV_BRI) +# define PV_BRIOPT OPT_WIN(WV_BRIOPT) # define PV_DIFF OPT_WIN(WV_DIFF) # define PV_FDC OPT_WIN(WV_FDC) # define PV_FEN OPT_WIN(WV_FEN) @@ -466,6 +468,14 @@ static struct vimoption (char_u *)&p_breakat, PV_NONE, {(char_u *)" \t!@*-+;:,./?", (char_u *)0L} SCRIPTID_INIT}, + {"breakindent", "bri", P_BOOL|P_VI_DEF|P_VIM|P_RWIN, + (char_u *)VAR_WIN, PV_BRI, + {(char_u *)FALSE, (char_u *)0L} + SCRIPTID_INIT}, + {"breakindentopt", "briopt", P_STRING|P_ALLOCED|P_VI_DEF|P_RBUF|P_COMMA|P_NODUP, + (char_u *)VAR_WIN, PV_BRIOPT, + {(char_u *)"", (char_u *)NULL} + SCRIPTID_INIT}, {"browsedir", "bsdir",P_STRING|P_VI_DEF, (char_u *)NULL, PV_NONE, {(char_u *)0L, (char_u *)0L} @@ -3496,6 +3506,7 @@ static void didset_options(void) (void)compile_cap_prog(curwin->w_s); /* set cedit_key */ (void)check_cedit(); + briopt_check(); } /* @@ -3829,6 +3840,11 @@ did_set_string_option ( *p_pm == '.' ? p_pm + 1 : p_pm) == 0) errmsg = (char_u *)N_("E589: 'backupext' and 'patchmode' are equal"); } + /* 'breakindentopt' */ + else if (varp == &curwin->w_p_briopt) { + if (briopt_check() == FAIL) + errmsg = e_invarg; + } /* * 'isident', 'iskeyword', 'isprint or 'isfname' option: refill chartab[] * If the new option is invalid, use old value. 'lisp' option: refill @@ -6686,6 +6702,8 @@ static char_u *get_varp(struct vimoption *p) case PV_SCROLL: return (char_u *)&(curwin->w_p_scr); case PV_WRAP: return (char_u *)&(curwin->w_p_wrap); case PV_LBR: return (char_u *)&(curwin->w_p_lbr); + case PV_BRI: return (char_u *)&(curwin->w_p_bri); + case PV_BRIOPT: return (char_u *)&(curwin->w_p_briopt); case PV_SCBIND: return (char_u *)&(curwin->w_p_scb); case PV_CRBIND: return (char_u *)&(curwin->w_p_crb); case PV_COCU: return (char_u *)&(curwin->w_p_cocu); @@ -6793,6 +6811,8 @@ void copy_winopt(winopt_T *from, winopt_T *to) to->wo_wrap = from->wo_wrap; to->wo_wrap_save = from->wo_wrap_save; to->wo_lbr = from->wo_lbr; + to->wo_bri = from->wo_bri; + to->wo_briopt = vim_strsave(from->wo_briopt); to->wo_scb = from->wo_scb; to->wo_scb_save = from->wo_scb_save; to->wo_crb = from->wo_crb; @@ -6847,6 +6867,7 @@ void check_winopt(winopt_T *wop) check_string_option(&wop->wo_stl); check_string_option(&wop->wo_cc); check_string_option(&wop->wo_cocu); + check_string_option(&wop->wo_briopt); } /* @@ -6864,6 +6885,7 @@ void clear_winopt(winopt_T *wop) clear_string_option(&wop->wo_stl); clear_string_option(&wop->wo_cc); clear_string_option(&wop->wo_cocu); + clear_string_option(&wop->wo_briopt); } /* @@ -8139,3 +8161,44 @@ void find_mps_values(int *initc, int *findc, int *backwards, int switchit) ++ptr; } } + +/* This is called when 'breakindentopt' is changed and whenn a window is + initialized */ +int briopt_check() { + char_u *p; + int bri_shift = 0; + long bri_min = 20; + int bri_sbr = FALSE; + + p = curwin->w_p_briopt; + while (*p != NUL) + { + if (STRNCMP(p, "shift:", 6) == 0 + && ((p[6] == '-' && VIM_ISDIGIT(p[7])) || VIM_ISDIGIT(p[6]))) + { + p += 6; + bri_shift = getdigits(&p); + } + else if (STRNCMP(p, "min:", 4) == 0 && VIM_ISDIGIT(p[4])) + { + p += 4; + bri_min = getdigits(&p); + } + else if (STRNCMP(p, "sbr", 3) == 0) + { + p += 3; + bri_sbr = TRUE; + } + if (*p != ',' && *p != NUL) + return FAIL; + if (*p == ',') + ++p; + } + + curwin->w_p_brishift = bri_shift; + curwin->w_p_brimin = bri_min; + curwin->w_p_brisbr = bri_sbr; + + return OK; +} + diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 55cc892cb01330..646a007edfbd9d 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -720,6 +720,8 @@ enum { , WV_COCU , WV_COLE , WV_CRBIND + , WV_BRI + , WV_BRIOPT , WV_DIFF , WV_FDC , WV_FEN diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 54dcaff39eede6..7384bc5665f7c7 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -102,6 +102,7 @@ #include "nvim/farsi.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/indent.h" #include "nvim/getchar.h" #include "nvim/main.h" #include "nvim/mbyte.h" @@ -2279,7 +2280,8 @@ win_line ( # define WL_FOLD WL_CMDLINE + 1 /* 'foldcolumn' */ # define WL_SIGN WL_FOLD + 1 /* column for signs */ #define WL_NR WL_SIGN + 1 /* line number */ -# define WL_SBR WL_NR + 1 /* 'showbreak' or 'diff' */ +# define WL_BRI WL_NR + 1 /* 'breakindent' */ +# define WL_SBR WL_BRI + 1 /* 'showbreak' or 'diff' */ #define WL_LINE WL_SBR + 1 /* text in the line */ int draw_state = WL_START; /* what to draw next */ @@ -2536,7 +2538,7 @@ win_line ( if (v > 0) { char_u *prev_ptr = ptr; while (vcol < v && *ptr != NUL) { - c = win_lbr_chartabsize(wp, ptr, (colnr_T)vcol, NULL); + c = win_lbr_chartabsize(wp, line, ptr, (colnr_T)vcol, NULL); vcol += c; prev_ptr = ptr; mb_ptr_adv(ptr); @@ -2813,6 +2815,34 @@ win_line ( } } + if (wp->w_p_brisbr && draw_state == WL_BRI - 1 + && n_extra == 0 && *p_sbr != NUL) { + // draw indent after showbreak value + draw_state = WL_BRI; + } else if (wp->w_p_brisbr && draw_state == WL_SBR && n_extra == 0) { + // after the showbreak, draw the breakindent + draw_state = WL_BRI - 1; + } + + // draw 'breakindent': indent wrapped text accodringly + if (draw_state == WL_BRI - 1 && n_extra == 0) { + draw_state = WL_BRI; + if (wp->w_p_bri && n_extra == 0 && row != startrow && filler_lines == 0) { + char_attr = 0; // was: hl_attr(HLF_AT); + + if (diff_hlf != (hlf_T)0) + char_attr = hl_attr(diff_hlf); + + p_extra = NULL; + c_extra = ' '; + n_extra = get_breakindent_win(wp, ml_get_buf(wp->w_buffer, lnum, FALSE)); + /* Correct end of highlighted area for 'breakindent', + required wen 'linebreak' is also set. */ + if (tocol == vcol) + tocol += n_extra; + } + } + if (draw_state == WL_SBR - 1 && n_extra == 0) { draw_state = WL_SBR; if (filler_todo > 0) { @@ -3376,9 +3406,11 @@ win_line ( */ if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr) && !wp->w_p_list) { - n_extra = win_lbr_chartabsize(wp, ptr - ( - has_mbyte ? mb_l : - 1), (colnr_T)vcol, NULL) - 1; + char_u *p = ptr - ( + has_mbyte ? mb_l : + 1); + // TODO: is passing p for start of the line OK? + n_extra = win_lbr_chartabsize(wp, p, p, (colnr_T)vcol, NULL) - 1; c_extra = ' '; if (vim_iswhite(c)) { if (c == TAB) diff --git a/src/nvim/testdir/test_breakindent.in b/src/nvim/testdir/test_breakindent.in new file mode 100644 index 00000000000000..8f40e4f7e522ad --- /dev/null +++ b/src/nvim/testdir/test_breakindent.in @@ -0,0 +1,79 @@ +Test for breakindent + +STARTTEST +:so small.vim +:if !exists("+breakindent") | e! test.ok | w! test.out | qa! | endif +:10new|:vsp|:vert resize 20 +:put =\"\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP\" +:set ts=4 sw=4 sts=4 breakindent +:fu! ScreenChar(width) +: let c='' +: for i in range(1,a:width) +: let c.=nr2char(screenchar(line('.'), i)) +: endfor +: let c.="\n" +: for i in range(1,a:width) +: let c.=nr2char(screenchar(line('.')+1, i)) +: endfor +: let c.="\n" +: for i in range(1,a:width) +: let c.=nr2char(screenchar(line('.')+2, i)) +: endfor +: return c +:endfu +:fu DoRecordScreen() +: wincmd l +: $put =printf(\"\n%s\", g:test) +: $put =g:line1 +: wincmd p +:endfu +:let g:test="Test 1: Simple breakindent" +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test="Test 2: Simple breakindent + sbr=>>" +:set sbr=>> +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test ="Test 3: Simple breakindent + briopt:sbr" +:set briopt=sbr,min:0 sbr=++ +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test ="Test 4: Simple breakindent + min width: 18" +:set sbr= briopt=min:18 +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test =" Test 5: Simple breakindent + shift by 2" +:set briopt=shift:2,min:0 +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test=" Test 6: Simple breakindent + shift by -1" +:set briopt=shift:-1,min:0 +:let line1=ScreenChar(8) +:call DoRecordScreen() +:let g:test=" Test 7: breakindent + shift by +1 + nu + sbr=? briopt:sbr" +:set briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 +:let line1=ScreenChar(10) +:call DoRecordScreen() +:let g:test=" Test 8: breakindent + shift:1 + nu + sbr=# list briopt:sbr" +:set briopt=shift:1,sbr,min:0 nu sbr=# list +:let line1=ScreenChar(10) +:call DoRecordScreen() +:let g:test=" Test 9: breakindent + shift by +1 + 'nu' + sbr=# list" +:set briopt-=sbr +:let line1=ScreenChar(10) +:call DoRecordScreen() +:let g:test=" Test 10: breakindent + shift by +1 + 'nu' + sbr=~ cpo+=n" +:set cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0 +:let line1=ScreenChar(10) +:call DoRecordScreen() +:wincmd p +:let g:test="\n Test 11: strdisplaywidth when breakindent is on" +:set cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4 +:let text=getline(2) "skip leading tab when calculating text width +:let width = strlen(text[1:])+indent(2)*4+strlen(&sbr)*3 " text wraps 3 times +:$put =g:test +:$put =printf(\"strdisplaywidth: %d == calculated: %d\", strdisplaywidth(text), width) +:%w! test.out +:qa! +ENDTEST +dummy text diff --git a/src/nvim/testdir/test_breakindent.ok b/src/nvim/testdir/test_breakindent.ok new file mode 100644 index 00000000000000..723cb2501269bf --- /dev/null +++ b/src/nvim/testdir/test_breakindent.ok @@ -0,0 +1,55 @@ + + abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP + +Test 1: Simple breakindent + abcd + qrst + GHIJ + +Test 2: Simple breakindent + sbr=>> + abcd + >>qr + >>EF + +Test 3: Simple breakindent + briopt:sbr + abcd +++ qrst +++ GHIJ + +Test 4: Simple breakindent + min width: 18 + abcd + qrstuv + IJKLMN + + Test 5: Simple breakindent + shift by 2 + abcd + qr + EF + + Test 6: Simple breakindent + shift by -1 + abcd + qrstu + HIJKL + + Test 7: breakindent + shift by +1 + nu + sbr=? briopt:sbr + 2 ab +? m +? x + + Test 8: breakindent + shift:1 + nu + sbr=# list briopt:sbr + 2 ^Iabcd +# opq +# BCD + + Test 9: breakindent + shift by +1 + 'nu' + sbr=# list + 2 ^Iabcd + #op + #AB + + Test 10: breakindent + shift by +1 + 'nu' + sbr=~ cpo+=n + 2 ab +~ mn +~ yz + + Test 11: strdisplaywidth when breakindent is on +strdisplaywidth: 46 == calculated: 64