aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2014-08-20 12:24:34 -0400
committerJustin M. Keyes <justinkz@gmail.com>2014-08-20 12:24:34 -0400
commit118a31c24c7ce76da909026cd10a94feb88803bc (patch)
tree8bbd708d924ef68017949e4bf9b773d5d833b82d
parentbbefc73c553d681f78f40df9d97ec89ae9b06520 (diff)
parent3b0f7fe59392138c886063b09e3cf41b25d53056 (diff)
downloadrneovim-118a31c24c7ce76da909026cd10a94feb88803bc.tar.gz
rneovim-118a31c24c7ce76da909026cd10a94feb88803bc.tar.bz2
rneovim-118a31c24c7ce76da909026cd10a94feb88803bc.zip
Merge pull request #691 from fmoralesc/master
Port vim's breakindent patch to neovim's codebase. (vim patches 7.4.338, 7.4.346, 7.4.352, 7.4.353, 7.4.370, 7.4.371, 7.4.388)
-rw-r--r--src/nvim/buffer_defs.h7
-rw-r--r--src/nvim/charset.c70
-rw-r--r--src/nvim/cursor.c2
-rw-r--r--src/nvim/edit.c22
-rw-r--r--src/nvim/ex_getln.c8
-rw-r--r--src/nvim/getchar.c2
-rw-r--r--src/nvim/indent.c75
-rw-r--r--src/nvim/misc1.c11
-rw-r--r--src/nvim/ops.c20
-rw-r--r--src/nvim/option.c64
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/screen.c109
-rw-r--r--src/nvim/testdir/Makefile6
-rw-r--r--src/nvim/testdir/test_breakindent.in79
-rw-r--r--src/nvim/testdir/test_breakindent.ok55
-rw-r--r--src/nvim/testdir/test_listlbr.in52
-rw-r--r--src/nvim/testdir/test_listlbr.ok27
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.in52
-rw-r--r--src/nvim/testdir/test_listlbr_utf8.ok21
-rw-r--r--src/nvim/version.c14
20 files changed, 613 insertions, 85 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index 9f5d7b86eb..4162df63ab 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -136,6 +136,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;
@@ -1069,6 +1073,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 */
+ bool 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 baf6895b4c..57c4aec395 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -13,6 +13,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"
@@ -775,9 +776,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;
}
@@ -785,17 +787,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;
}
@@ -922,32 +924,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;
}
@@ -959,14 +963,16 @@ 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 col_adj = 0; /* col + screen size of tab */
colnr_T colmax;
int added;
int mb_added = 0;
@@ -975,8 +981,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);
}
@@ -986,26 +992,29 @@ int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
// First get normal size, without 'linebreak'
int size = win_chartabsize(wp, s, col);
int c = *s;
+ if (tab_corr) {
+ 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(s[1])
- && !wp->w_p_list
&& wp->w_p_wrap
&& (wp->w_width != 0)) {
// Count all characters from first non-blank after a blank up to next
// non-blank after a blank.
numberextra = win_col_off(wp);
col2 = col;
- colmax = (colnr_T)(wp->w_width - numberextra);
+ colmax = (colnr_T)(wp->w_width - numberextra - col_adj);
if (col >= colmax) {
- n = colmax + win_col_off2(wp);
+ colmax += col_adj;
+ n = colmax + win_col_off2(wp);
if (n > 0) {
- colmax += (((col - colmax) / n) + 1) * n;
+ colmax += (((col - colmax) / n) + 1) * n - col_adj;
}
}
@@ -1024,7 +1033,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
col2 += win_chartabsize(wp, s, col2);
if (col2 >= colmax) { /* doesn't fit */
- size = colmax - col;
+ size = colmax - col + col_adj;
tab_corr = FALSE;
break;
}
@@ -1039,11 +1048,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;
@@ -1056,7 +1066,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 {
@@ -1157,13 +1172,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
@@ -1173,11 +1189,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;
@@ -1229,7 +1247,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 64a3b73f15..cd84d7014c 100644
--- a/src/nvim/cursor.c
+++ b/src/nvim/cursor.c
@@ -138,7 +138,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 93e127394b..6158176e56 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1551,7 +1551,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;
@@ -5898,9 +5898,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;
@@ -7914,10 +7916,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) {
@@ -7936,10 +7938,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;
}
@@ -8126,6 +8129,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();
@@ -8134,12 +8138,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 28048c933c..74f3edc8d9 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -1817,10 +1817,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);
@@ -1858,11 +1858,11 @@ redraw:
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 e98bb2744c..6c772a8a66 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2163,7 +2163,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 d4c6b36177..9258ee93b6 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -10,8 +10,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"
@@ -24,14 +26,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);
}
@@ -39,20 +41,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++;
@@ -433,6 +442,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);
+ }
+ bri = prev_indent + wp->w_p_brishift;
+
+ /* indent minus the length of the showbreak string */
+ 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.
@@ -608,10 +661,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--;
}
@@ -628,7 +683,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++;
}
@@ -658,15 +713,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 f0c4ee492e..986374b352 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -194,7 +194,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 */
@@ -631,7 +631,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) {
@@ -1306,6 +1306,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. */
@@ -1317,11 +1318,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);
}
@@ -1333,7 +1334,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 0bf338947b..0b0a913a95 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -350,7 +350,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;
}
@@ -405,7 +406,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;
}
@@ -429,7 +430,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;
@@ -2824,7 +2828,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);
@@ -2854,7 +2858,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;
@@ -4114,7 +4118,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;
@@ -4163,7 +4167,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 63ea2ee338..a878ba6455 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -176,6 +176,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)
@@ -471,6 +473,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}
@@ -3481,6 +3491,7 @@ static void didset_options(void)
(void)compile_cap_prog(curwin->w_s);
/* set cedit_key */
(void)check_cedit();
+ briopt_check();
}
/*
@@ -3815,6 +3826,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
@@ -6681,6 +6697,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);
@@ -6788,6 +6806,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;
@@ -6842,6 +6862,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);
}
/*
@@ -6859,6 +6880,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);
}
/*
@@ -8134,3 +8156,45 @@ void find_mps_values(int *initc, int *findc, int *backwards, int switchit)
++ptr;
}
}
+
+/* This is called when 'breakindentopt' is changed and when a window is
+ initialized */
+int briopt_check(void)
+{
+ char_u *p;
+ int bri_shift = 0;
+ long bri_min = 20;
+ bool 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 d862ab2761..555e9166d6 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -726,6 +726,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 03c59bf584..811f265902 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -106,6 +106,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"
@@ -2196,6 +2197,7 @@ win_line (
char_u extra[18]; /* line number and 'fdc' must fit in here */
int n_extra = 0; /* number of extra chars */
char_u *p_extra = NULL; /* string of extra chars, plus NUL */
+ char_u *p_extra_free = NULL; /* p_extra needs to be freed */
int c_extra = NUL; /* extra chars, all the same */
int extra_attr = 0; /* attributes when n_extra != 0 */
static char_u *at_end_str = (char_u *)""; /* used for p_extra when
@@ -2283,7 +2285,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 */
@@ -2540,7 +2543,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);
@@ -2817,6 +2820,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) {
@@ -3078,6 +3109,10 @@ win_line (
}
--n_extra;
} else {
+ if (p_extra_free != NULL) {
+ free(p_extra_free);
+ p_extra_free = NULL;
+ }
/*
* Get a character from the line itself.
*/
@@ -3378,17 +3413,20 @@ win_line (
/*
* Found last space before word: check for line break.
*/
- 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;
+ if (wp->w_p_lbr && vim_isbreak(c) && !vim_isbreak(*ptr)) {
+ 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, line, p, (colnr_T)vcol, NULL) - 1;
c_extra = ' ';
if (vim_iswhite(c)) {
if (c == TAB)
/* See "Tab alignment" below. */
FIX_FOR_BOGUSCOLS;
- c = ' ';
+ if (!wp->w_p_list) {
+ c = ' ';
+ }
}
}
@@ -3419,9 +3457,36 @@ win_line (
* into "ScreenLines".
*/
if (c == TAB && (!wp->w_p_list || lcs_tab1)) {
+ int tab_len = 0;
/* tab amount depends on current column */
- n_extra = (int)wp->w_buffer->b_p_ts
+ tab_len = (int)wp->w_buffer->b_p_ts
- vcol % (int)wp->w_buffer->b_p_ts - 1;
+ if (!wp->w_p_lbr || !wp->w_p_list) {
+ n_extra = tab_len;
+ } else {
+ char_u *p;
+ int len = n_extra;
+ int i;
+ int saved_nextra = n_extra;
+
+ /* if n_extra > 0, it gives the number of chars to use for
+ * a tab, else we need to calculate the width for a tab */
+ len = (tab_len * mb_char2len(lcs_tab2));
+ if (n_extra > 0) {
+ len += n_extra - tab_len;
+ }
+ c = lcs_tab1;
+ p = xmalloc(len + 1);
+ memset(p, ' ', len);
+ p[len] = NUL;
+ p_extra_free = p;
+ for (i = 0; i < tab_len; i++) {
+ mb_char2bytes(lcs_tab2, p);
+ p += mb_char2len(lcs_tab2);
+ n_extra += mb_char2len(lcs_tab2) - (saved_nextra > 0 ? 1: 0);
+ }
+ p_extra = p_extra_free;
+ }
/* Tab alignment should be identical regardless of
* 'conceallevel' value. So tab compensates of all
* previous concealed characters, and thus resets vcol_off
@@ -3432,8 +3497,12 @@ win_line (
mb_utf8 = FALSE; /* don't draw as UTF-8 */
if (wp->w_p_list) {
c = lcs_tab1;
- c_extra = lcs_tab2;
- n_attr = n_extra + 1;
+ if (wp->w_p_lbr) {
+ c_extra = NUL; /* using p_extra from above */
+ } else {
+ c_extra = lcs_tab2;
+ }
+ n_attr = tab_len + 1;
extra_attr = hl_attr(HLF_8);
saved_attr2 = char_attr; /* save current attr */
mb_c = c;
@@ -3493,11 +3562,25 @@ win_line (
mb_utf8 = FALSE; /* don't draw as UTF-8 */
} else if (c != NUL) {
p_extra = transchar(c);
+ if (n_extra == 0) {
+ n_extra = byte2cells(c);
+ }
if ((dy_flags & DY_UHEX) && wp->w_p_rl)
rl_mirror(p_extra); /* reverse "<12>" */
- n_extra = byte2cells(c) - 1;
c_extra = NUL;
- c = *p_extra++;
+ if (wp->w_p_lbr) {
+ char_u *p;
+
+ c = *p_extra;
+ p = xmalloc(n_extra + 1);
+ memset(p, ' ', n_extra);
+ STRNCPY(p, p_extra + 1, STRLEN(p_extra) - 1);
+ p[n_extra] = NUL;
+ p_extra_free = p_extra = p;
+ } else {
+ n_extra = byte2cells(c) - 1;
+ c = *p_extra++;
+ }
if (!attr_pri) {
n_attr = n_extra + 1;
extra_attr = hl_attr(HLF_8);
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index a7f9dd8fa1..db88755a01 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -30,8 +30,10 @@ SCRIPTS := test_autoformat_join.out \
test91.out test92.out test93.out test94.out test95.out \
test96.out test97.out test98.out test99.out test100.out \
test101.out test102.out test103.out test104.out test105.out \
- test106.out test107.out \
- test_options.out
+ test106.out test107.out \
+ test_options.out \
+ test_listlbr.out test_listlbr_utf8.out \
+ test_breakindent.out
SCRIPTS_GUI := test16.out
diff --git a/src/nvim/testdir/test_breakindent.in b/src/nvim/testdir/test_breakindent.in
new file mode 100644
index 0000000000..8f40e4f7e5
--- /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 0000000000..723cb25012
--- /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
diff --git a/src/nvim/testdir/test_listlbr.in b/src/nvim/testdir/test_listlbr.in
new file mode 100644
index 0000000000..0cce4c23a5
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr.in
@@ -0,0 +1,52 @@
+Test for linebreak and list option (non-utf8)
+
+STARTTEST
+:so small.vim
+:if !exists("+linebreak") | e! test.ok | w! test.out | qa! | endif
+:10new|:vsp|:vert resize 20
+:put =\"\tabcdef hijklmn\tpqrstuvwxyz_1060ABCDEFGHIJKLMNOP \"
+:norm! zt
+:set ts=4 sw=4 sts=4 linebreak sbr=+ wrap
+:fu! ScreenChar(width)
+: let c=''
+: for j in range(1,4)
+: for i in range(1,a:width)
+: let c.=nr2char(screenchar(j, i))
+: endfor
+: let c.="\n"
+: endfor
+: return c
+:endfu
+:fu! DoRecordScreen()
+: wincmd l
+: $put =printf(\"\n%s\", g:test)
+: $put =g:line
+: wincmd p
+:endfu
+:let g:test="Test 1: set linebreak"
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:let g:test="Test 2: set linebreak + set list"
+:set linebreak list listchars=
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:let g:test ="Test 3: set linebreak nolist"
+:set nolist linebreak
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:let g:test ="Test 4: set linebreak with tab and 1 line as long as screen: should break!"
+:set nolist linebreak ts=8
+:let line="1\t".repeat('a', winwidth(0)-2)
+:$put =line
+:$
+:norm! zt
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:%w! test.out
+:qa!
+ENDTEST
+dummy text
diff --git a/src/nvim/testdir/test_listlbr.ok b/src/nvim/testdir/test_listlbr.ok
new file mode 100644
index 0000000000..be323d4dc7
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr.ok
@@ -0,0 +1,27 @@
+
+ abcdef hijklmn pqrstuvwxyz_1060ABCDEFGHIJKLMNOP
+
+Test 1: set linebreak
+ abcdef
++hijklmn
++pqrstuvwxyz_1060ABC
++DEFGHIJKLMNOP
+
+Test 2: set linebreak + set list
+^Iabcdef hijklmn^I
++pqrstuvwxyz_1060ABC
++DEFGHIJKLMNOP
+
+
+Test 3: set linebreak nolist
+ abcdef
++hijklmn
++pqrstuvwxyz_1060ABC
++DEFGHIJKLMNOP
+1 aaaaaaaaaaaaaaaaaa
+
+Test 4: set linebreak with tab and 1 line as long as screen: should break!
+1
++aaaaaaaaaaaaaaaaaa
+~
+~
diff --git a/src/nvim/testdir/test_listlbr_utf8.in b/src/nvim/testdir/test_listlbr_utf8.in
new file mode 100644
index 0000000000..ba12adae05
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr_utf8.in
@@ -0,0 +1,52 @@
+Test for linebreak and list option in utf-8 mode
+
+STARTTEST
+:so small.vim
+:if !exists("+linebreak") | e! test.ok | w! test.out | qa! | endif
+:so mbyte.vim
+:if &enc !=? 'utf-8'|:e! test.ok|:w! test.out|qa!|endif
+:10new|:vsp|:vert resize 20
+:put =\"\tabcdef hijklmn\tpqrstuvwxyz\u00a01060ABCDEFGHIJKLMNOP \"
+:norm! zt
+:set ts=4 sw=4 sts=4 linebreak sbr=+ wrap
+:fu! ScreenChar(width)
+: let c=''
+: for j in range(1,4)
+: for i in range(1,a:width)
+: let c.=nr2char(screenchar(j, i))
+: endfor
+: let c.="\n"
+: endfor
+: return c
+:endfu
+:fu! DoRecordScreen()
+: wincmd l
+: $put =printf(\"\n%s\", g:test)
+: $put =g:line
+: wincmd p
+:endfu
+:let g:test ="Test 1: set linebreak + set list + fancy listchars"
+:exe "set linebreak list listchars=nbsp:\u2423,tab:\u2595\u2014,trail:\u02d1,eol:\ub6"
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:"
+:let g:test ="Test 2: set nolinebreak list"
+:set list nolinebreak
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:"
+:let g:test ="Test 3: set linebreak nolist"
+:$put =\"\t*mask = nil;\"
+:$
+:norm! zt
+:set nolist linebreak
+:redraw!
+:let line=ScreenChar(winwidth(0))
+:call DoRecordScreen()
+:"
+:%w! test.out
+:qa!
+ENDTEST
+dummy text
diff --git a/src/nvim/testdir/test_listlbr_utf8.ok b/src/nvim/testdir/test_listlbr_utf8.ok
new file mode 100644
index 0000000000..634cf3906d
--- /dev/null
+++ b/src/nvim/testdir/test_listlbr_utf8.ok
@@ -0,0 +1,21 @@
+
+ abcdef hijklmn pqrstuvwxyz 1060ABCDEFGHIJKLMNOP
+
+Test 1: set linebreak + set list + fancy listchars
+▕———abcdef
++hijklmn▕———
++pqrstuvwxyz␣1060ABC
++DEFGHIJKLMNOPˑ¶
+
+Test 2: set nolinebreak list
+▕———abcdef hijklmn▕—
++pqrstuvwxyz␣1060ABC
++DEFGHIJKLMNOPˑ¶
+ *mask = nil;
+
+Test 3: set linebreak nolist
+ *mask = nil;
+~
+~
+~
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 6e503200f0..5684a3f8d8 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -207,7 +207,7 @@ static int included_patches[] = {
//391,
//390,
//389,
- //388,
+ 388,
//387,
//386,
//385,
@@ -224,8 +224,8 @@ static int included_patches[] = {
//374,
//373,
//372,
- //371,
- //370,
+ 371,
+ 370,
//369,
//368,
//367,
@@ -242,14 +242,14 @@ static int included_patches[] = {
//356 NA
//355,
//354,
- //353,
- //352,
+ 353,
+ 352,
//351,
//350,
//349,
//348,
//347,
- //346,
+ 346,
//345,
//344,
//343,
@@ -257,7 +257,7 @@ static int included_patches[] = {
//341,
//340 NA
//339,
- //338,
+ 338,
//337,
//336,
335,