vim-patch:8.1.0105: all tab stops are the same

Problem:    All tab stops are the same.
Solution:   Add the variable tabstop feature. (Christian Brabandt,
            closes vim/vim#2711)
04958cbaf2
This commit is contained in:
VVKot
2021-02-13 19:06:37 +00:00
parent b79596eb5e
commit 6752ac4968
22 changed files with 1458 additions and 249 deletions

View File

@@ -282,7 +282,7 @@ int open_buffer(
// Set/reset the Changed flag first, autocmds may change the buffer.
// Apply the automatic commands, before processing the modelines.
// So the modelines have priority over auto commands.
// So the modelines have priority over autocommands.
// When reading stdin, the buffer contents always needs writing, so set
// the changed flag. Unless in readonly mode: "ls | nvim -R -".
@@ -1946,6 +1946,20 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_fo);
clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk);
clear_string_option(&buf->b_p_vsts);
if (buf->b_p_vsts_nopaste) {
xfree(buf->b_p_vsts_nopaste);
}
buf->b_p_vsts_nopaste = NULL;
if (buf->b_p_vsts_array) {
xfree(buf->b_p_vsts_array);
}
buf->b_p_vsts_array = NULL;
clear_string_option(&buf->b_p_vts);
if (buf->b_p_vts_array) {
xfree(buf->b_p_vts_array);
}
buf->b_p_vts_array = NULL;
clear_string_option(&buf->b_p_keymap);
keymap_ga_clear(&buf->b_kmap_ga);
ga_clear(&buf->b_kmap_ga);
@@ -5375,8 +5389,8 @@ bool bt_terminal(const buf_T *const buf)
return buf != NULL && buf->b_p_bt[0] == 't';
}
// Return true if "buf" is a "nofile", "acwrite" or "terminal" buffer.
// This means the buffer name is not a file name.
// Return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
// buffer. This means the buffer name is not a file name.
bool bt_nofile(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{
@@ -5386,7 +5400,8 @@ bool bt_nofile(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p');
}
// Return true if "buf" is a "nowrite", "nofile" or "terminal" buffer.
// Return true if "buf" is a "nowrite", "nofile", "terminal" or "prompt"
// buffer.
bool bt_dontwrite(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{

View File

@@ -743,6 +743,11 @@ struct file_buffer {
long b_p_wm; ///< 'wrapmargin'
long b_p_wm_nobin; ///< b_p_wm saved for binary mode
long b_p_wm_nopaste; ///< b_p_wm saved for paste mode
char_u *b_p_vsts; ///< 'varsofttabstop'
long *b_p_vsts_array; ///< 'varsofttabstop' in internal format
char_u *b_p_vsts_nopaste; ///< b_p_vsts saved for paste mode
char_u *b_p_vts; ///< 'vartabstop'
long *b_p_vts_array; ///< 'vartabstop' in internal format
char_u *b_p_keymap; ///< 'keymap'
// local values for options which are normally global

View File

@@ -828,6 +828,7 @@ int copy_indent(int size, char_u *src)
int tab_pad;
int ind_done;
int round;
int ind_col;
// Round 1: compute the number of characters needed for the indent
// Round 2: copy the characters.
@@ -835,13 +836,15 @@ int copy_indent(int size, char_u *src)
todo = size;
ind_len = 0;
ind_done = 0;
ind_col = 0;
s = src;
// Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) {
if (*s == TAB) {
tab_pad = (int)curbuf->b_p_ts
- (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -849,9 +852,11 @@ int copy_indent(int size, char_u *src)
}
todo -= tab_pad;
ind_done += tab_pad;
ind_col += tab_pad;
} else {
todo--;
ind_done++;
ind_col++;
}
ind_len++;
@@ -862,11 +867,12 @@ int copy_indent(int size, char_u *src)
}
// Fill to next tabstop with a tab, if possible.
tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
if ((todo >= tab_pad) && !curbuf->b_p_et) {
todo -= tab_pad;
ind_len++;
ind_col += tab_pad;
if (p != NULL) {
*p++ = TAB;
@@ -874,12 +880,20 @@ int copy_indent(int size, char_u *src)
}
// Add tabs required for indent.
while (todo >= (int)curbuf->b_p_ts && !curbuf->b_p_et) {
todo -= (int)curbuf->b_p_ts;
ind_len++;
if (p != NULL) {
*p++ = TAB;
if (!curbuf->b_p_et) {
for (;;) {
tab_pad = tabstop_padding(ind_col,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
if (todo < tab_pad) {
break;
}
todo -= tab_pad;
ind_len++;
ind_col += tab_pad;
if (p != NULL) {
*p++ = TAB;
}
}
}
@@ -1029,7 +1043,9 @@ int open_line(
|| do_si
) {
// count white space on current line
newindent = get_indent_str(saved_line, (int)curbuf->b_p_ts, false);
newindent = get_indent_str_vtab(saved_line,
curbuf->b_p_ts,
curbuf->b_p_vts_array, false);
if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
newindent = second_line_indent; // for ^^D command in insert mode
}
@@ -1453,7 +1469,9 @@ int open_line(
if (curbuf->b_p_ai
|| do_si
) {
newindent = get_indent_str(leader, (int)curbuf->b_p_ts, false);
newindent = get_indent_str_vtab(leader,
curbuf->b_p_ts,
curbuf->b_p_vts_array, false);
}
// Add the indent offset

View File

@@ -744,8 +744,7 @@ int vim_strnsize(char_u *s, int len)
/// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
const int ts = (int)(buf)->b_p_ts; \
return (ts - (int)(col % ts)); \
return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
} else { \
return ptr2cells(p); \
}
@@ -1143,8 +1142,9 @@ static int win_nolbr_chartabsize(win_T *wp, char_u *s, colnr_T col, int *headp)
int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
n = (int)wp->w_buffer->b_p_ts;
return n - (col % n);
return tabstop_padding(col,
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array);
}
n = ptr2cells(s);
@@ -1211,6 +1211,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
char_u *line; // start of the line
int incr;
int head;
long *vts = wp->w_buffer->b_p_vts_array;
int ts = (int)wp->w_buffer->b_p_ts;
int c;
@@ -1251,7 +1252,7 @@ void getvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor,
// A tab gets expanded, depending on the current column
if (c == TAB) {
incr = ts - (vcol % ts);
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.

View File

@@ -597,7 +597,10 @@ static int insert_check(VimState *state)
s->mincol = curwin->w_wcol;
validate_cursor_col();
if (curwin->w_wcol < s->mincol - curbuf->b_p_ts
if (
curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
curbuf->b_p_ts,
curbuf->b_p_vts_array)
&& curwin->w_wrow == curwin->w_winrow
+ curwin->w_height_inner - 1 - get_scrolloff_value(curwin)
&& (curwin->w_cursor.lnum != curwin->w_topline
@@ -8178,24 +8181,19 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
/*
* Handle deleting one 'shiftwidth' or 'softtabstop'.
*/
if ( mode == BACKSPACE_CHAR
&& ((p_sta && in_indent)
|| (get_sts_value() != 0
&& curwin->w_cursor.col > 0
&& (*(get_cursor_pos_ptr() - 1) == TAB
|| (*(get_cursor_pos_ptr() - 1) == ' '
&& (!*inserted_space_p
|| arrow_used)))))) {
int ts;
if (mode == BACKSPACE_CHAR
&& ((p_sta && in_indent)
|| ((get_sts_value() != 0
|| tabstop_count(curbuf->b_p_vsts_array))
&& curwin->w_cursor.col > 0
&& (*(get_cursor_pos_ptr() - 1) == TAB
|| (*(get_cursor_pos_ptr() - 1) == ' '
&& (!*inserted_space_p || arrow_used)))))) {
colnr_T vcol;
colnr_T want_vcol;
colnr_T start_vcol;
*inserted_space_p = FALSE;
if (p_sta && in_indent)
ts = get_sw_value(curbuf);
else
ts = get_sts_value();
*inserted_space_p = false;
// Compute the virtual column where we want to be. Since
// 'showbreak' may get in the way, need to get the last column of
// the previous character.
@@ -8204,7 +8202,13 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
dec_cursor();
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol);
inc_cursor();
want_vcol = (want_vcol / ts) * ts;
if (p_sta && in_indent) {
want_vcol = (want_vcol / curbuf->b_p_sw) * curbuf->b_p_sw;
} else {
want_vcol = tabstop_start(want_vcol,
curbuf->b_p_sts,
curbuf->b_p_vsts_array);
}
// delete characters until we are at or before want_vcol
while (vcol > want_vcol
@@ -8669,10 +8673,19 @@ static bool ins_tab(void)
can_cindent = false;
}
// When nothing special, insert TAB like a normal character
// When nothing special, insert TAB like a normal character.
if (!curbuf->b_p_et
&& !(p_sta && ind && curbuf->b_p_ts != get_sw_value(curbuf))
&& get_sts_value() == 0) {
&& !(
p_sta
&& ind
// These five lines mean 'tabstop' != 'shiftwidth'
&& ((tabstop_count(curbuf->b_p_vts_array) > 1)
|| (tabstop_count(curbuf->b_p_vts_array) == 1
&& tabstop_first(curbuf->b_p_vts_array)
!= get_sw_value(curbuf))
|| (tabstop_count(curbuf->b_p_vts_array) == 0
&& curbuf->b_p_ts != get_sw_value(curbuf))))
&& tabstop_count(curbuf->b_p_vsts_array) == 0 && get_sts_value() == 0) {
return true;
}
@@ -8686,16 +8699,22 @@ static bool ins_tab(void)
can_si_back = false;
AppendToRedobuff("\t");
if (p_sta && ind) { // insert tab in indent, use "shiftwidth"
temp = get_sw_value(curbuf);
} else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set
temp = get_sts_value();
} else { // otherwise use "tabstop"
temp = (int)curbuf->b_p_ts;
if (p_sta && ind) { // insert tab in indent, use 'shiftwidth'
temp = (int)curbuf->b_p_sw;
temp -= get_nolist_virtcol() % temp;
} else if (tabstop_count(curbuf->b_p_vsts_array) > 0
|| curbuf->b_p_sts > 0) {
// use 'softtabstop' when set
temp = tabstop_padding(get_nolist_virtcol(),
curbuf->b_p_sts,
curbuf->b_p_vsts_array);
} else {
// otherwise use 'tabstop'
temp = tabstop_padding(get_nolist_virtcol(),
curbuf->b_p_ts,
curbuf->b_p_vts_array);
}
temp -= get_nolist_virtcol() % temp;
/*
* Insert the first space with ins_char(). It will delete one char in
* replace mode. Insert the rest with ins_str(); it will not delete any
@@ -8716,7 +8735,9 @@ static bool ins_tab(void)
/*
* When 'expandtab' not set: Replace spaces by TABs where possible.
*/
if (!curbuf->b_p_et && (get_sts_value() || (p_sta && ind))) {
if (!curbuf->b_p_et && (tabstop_count(curbuf->b_p_vsts_array) > 0
|| get_sts_value() > 0
|| (p_sta && ind))) {
char_u *ptr;
char_u *saved_line = NULL; // init for GCC
pos_T pos;

View File

@@ -4312,6 +4312,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"title",
"user-commands", // was accidentally included in 5.4
"user_commands",
"vartabs",
"vertsplit",
"virtualedit",
"visual",

View File

@@ -712,14 +712,15 @@ void ex_retab(exarg_T *eap)
long len;
long col;
long vcol;
long start_col = 0; /* For start of white-space string */
long start_vcol = 0; /* For start of white-space string */
int temp;
long start_col = 0; // For start of white-space string
long start_vcol = 0; // For start of white-space string
long old_len;
char_u *ptr;
char_u *new_line = (char_u *)1; /* init to non-NULL */
int did_undo; /* called u_save for current line */
int new_ts;
char_u *new_line = (char_u *)1; // init to non-NULL
int did_undo; // called u_save for current line
long *new_ts = 0;
char_u *new_ts_str; // string value of tab argument
int save_list;
linenr_T first_line = 0; /* first changed line */
linenr_T last_line = 0; /* last changed line */
@@ -727,14 +728,23 @@ void ex_retab(exarg_T *eap)
save_list = curwin->w_p_list;
curwin->w_p_list = 0; /* don't want list mode here */
new_ts = getdigits_int(&(eap->arg), false, -1);
if (new_ts < 0) {
EMSG(_(e_positive));
new_ts_str = eap->arg;
if (!tabstop_set(eap->arg, &new_ts)) {
return;
}
if (new_ts == 0)
new_ts = curbuf->b_p_ts;
for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) {
while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
(eap->arg)++;
}
// This ensures that either new_ts and new_ts_str are freshly allocated,
// or new_ts points to an existing array and new_ts_str is null.
if (new_ts == 0) {
new_ts = curbuf->b_p_vts_array;
new_ts_str = NULL;
} else {
new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
}
for (lnum = eap->line1; !got_int && lnum <= eap->line2; lnum++) {
ptr = ml_get(lnum);
col = 0;
vcol = 0;
@@ -758,13 +768,14 @@ void ex_retab(exarg_T *eap)
len = num_spaces = vcol - start_vcol;
num_tabs = 0;
if (!curbuf->b_p_et) {
temp = new_ts - (start_vcol % new_ts);
if (num_spaces >= temp) {
num_spaces -= temp;
num_tabs++;
}
num_tabs += num_spaces / new_ts;
num_spaces -= (num_spaces / new_ts) * new_ts;
int t, s;
tabstop_fromto(start_vcol, vcol,
tabstop_count(new_ts) ? 0 : curbuf->b_p_ts,
new_ts,
&t, &s);
num_tabs = t;
num_spaces = s;
}
if (curbuf->b_p_et || got_tab
|| (num_spaces + num_tabs < len)) {
@@ -817,15 +828,42 @@ void ex_retab(exarg_T *eap)
if (got_int)
EMSG(_(e_interr));
if (curbuf->b_p_ts != new_ts)
// If a single value was given then it can be considered equal to
// either the value of 'tabstop' or the value of 'vartabstop'.
if (tabstop_count(curbuf->b_p_vts_array) == 0
&& tabstop_count(new_ts) == 1
&& curbuf->b_p_ts == tabstop_first(new_ts)) {
// not changed
} else if (tabstop_count(curbuf->b_p_vts_array) > 0
&& tabstop_eq(curbuf->b_p_vts_array, new_ts)) {
// not changed
} else {
redraw_curbuf_later(NOT_VALID);
}
if (first_line != 0) {
changed_lines(first_line, 0, last_line + 1, 0L, true);
}
curwin->w_p_list = save_list; /* restore 'list' */
curbuf->b_p_ts = new_ts;
if (new_ts_str != NULL) { // set the new tabstop
// If 'vartabstop' is in use or if the value given to retab has more
// than one tabstop then update 'vartabstop'.
long *old_vts_ary = curbuf->b_p_vts_array;
if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_ts) > 1) {
set_string_option_direct("vts", -1, new_ts_str,
OPT_FREE | OPT_LOCAL, 0);
xfree(new_ts_str);
curbuf->b_p_vts_array = new_ts;
xfree(old_vts_ary);
} else {
// 'vartabstop' wasn't in use and a single value was given to
// retab then update 'tabstop'.
curbuf->b_p_ts = tabstop_first(new_ts);
xfree(new_ts);
}
}
coladvance(curwin->w_curswant);
u_clearline();

View File

@@ -890,8 +890,11 @@ static colnr_T hardcopy_line(prt_settings_T *psettings, int page_line, prt_pos_T
* Appropriately expand any tabs to spaces.
*/
if (line[col] == TAB || tab_spaces != 0) {
if (tab_spaces == 0)
tab_spaces = (int)(curbuf->b_p_ts - (print_pos % curbuf->b_p_ts));
if (tab_spaces == 0) {
tab_spaces = tabstop_padding(print_pos,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
}
while (tab_spaces > 0) {
need_break = mch_print_text_out((char_u *)" ", 1);

View File

@@ -34,14 +34,20 @@
// 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, false);
return get_indent_str_vtab(get_cursor_line_ptr(),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
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, false);
return get_indent_str_vtab(ml_get(lnum),
curbuf->b_p_ts,
curbuf->b_p_vts_array,
false);
}
@@ -49,7 +55,10 @@ 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, false);
return get_indent_str_vtab(ml_get_buf(buf, lnum, false),
curbuf->b_p_ts,
buf->b_p_vts_array,
false);
}
@@ -82,6 +91,30 @@ int get_indent_str(const char_u *ptr, int ts, int list)
return count;
}
// Count the size (in window cells) of the indent in line "ptr", using
// variable tabstops.
// if "list" is true, count only screen size for tabs.
int get_indent_str_vtab(const char_u *ptr, long ts, long *vts, bool list)
{
int count = 0;
for (; *ptr; ptr++) {
if (*ptr == TAB) { // count a tab for what it is worth
if (!list || curwin->w_p_lcs_chars.tab1) {
count += tabstop_padding(count, ts, vts);
} else {
// In list mode, when tab is not set, count screen char width
// for Tab, displays: ^I
count += ptr2cells(ptr);
}
} else if (*ptr == ' ') {
count++; // count a space for one
} else {
break;
}
}
return count;
}
// Set the indent of the current line.
// Leaves the cursor on the first non-blank in the line.
@@ -104,6 +137,7 @@ int set_indent(int size, int flags)
int line_len;
int doit = false;
int ind_done = 0; // Measured in spaces.
int ind_col = 0;
int tab_pad;
int retval = false;
@@ -130,7 +164,9 @@ int set_indent(int size, int flags)
// Count as many characters as we can use.
while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) {
tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -147,35 +183,41 @@ int set_indent(int size, int flags)
p++;
}
// These diverge from this point.
ind_col = ind_done;
// Set initial number of whitespace chars to copy if we are
// preserving indent but expandtab is set.
if (curbuf->b_p_et) {
orig_char_len = ind_len;
}
// Fill to next tabstop with a tab, if possible.
tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
if ((todo >= tab_pad) && (orig_char_len == -1)) {
doit = true;
todo -= tab_pad;
ind_len++;
// ind_done += tab_pad;
ind_col += tab_pad;
}
}
// Count tabs required for indent.
while (todo >= (int)curbuf->b_p_ts) {
for (;;) {
tab_pad = tabstop_padding(ind_col, curbuf->b_p_ts, curbuf->b_p_vts_array);
if (todo < tab_pad) {
break;
}
if (*p != TAB) {
doit = true;
} else {
p++;
}
todo -= (int)curbuf->b_p_ts;
todo -= tab_pad;
ind_len++;
// ind_done += (int)curbuf->b_p_ts;
ind_col += tab_pad;
}
}
@@ -255,7 +297,9 @@ int set_indent(int size, int flags)
while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) {
tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target.
if (todo < tab_pad) {
@@ -272,18 +316,28 @@ int set_indent(int size, int flags)
}
// Fill to next tabstop with a tab, if possible.
tab_pad = (int)curbuf->b_p_ts - (ind_done % (int)curbuf->b_p_ts);
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
if (todo >= tab_pad) {
*s++ = TAB;
todo -= tab_pad;
ind_done += tab_pad;
}
p = skipwhite(p);
}
while (todo >= (int)curbuf->b_p_ts) {
for (;;) {
tab_pad = tabstop_padding(ind_done,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
if (todo < tab_pad) {
break;
}
*s++ = TAB;
todo -= (int)curbuf->b_p_ts;
todo -= tab_pad;
ind_done += tab_pad;
}
}
@@ -387,6 +441,7 @@ int get_breakindent_win(win_T *wp, const char_u *line)
static long prev_ts = 0; // Cached tabstop value.
static const char_u *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value.
static long *prev_vts = NULL; // Cached vartabs values.
int bri = 0;
// window width minus window margin space, i.e. what rests for text
const int eff_wwidth = wp->w_width_inner
@@ -396,11 +451,16 @@ int get_breakindent_win(win_T *wp, const char_u *line)
// used cached indent, unless pointer or 'tabstop' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts
|| prev_tick != buf_get_changedtick(wp->w_buffer)) {
|| prev_tick != buf_get_changedtick(wp->w_buffer)
|| prev_vts != wp->w_buffer->b_p_vts_array) {
prev_line = line;
prev_ts = wp->w_buffer->b_p_ts;
prev_tick = buf_get_changedtick(wp->w_buffer);
prev_indent = get_indent_str(line, (int)wp->w_buffer->b_p_ts, wp->w_p_list);
prev_vts = wp->w_buffer->b_p_vts_array;
prev_indent = get_indent_str_vtab(line,
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array,
wp->w_p_list);
}
bri = prev_indent + wp->w_briopt_shift;

View File

@@ -1758,7 +1758,9 @@ void msg_prt_line(char_u *s, int list)
c = *s++;
if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
// tab amount depends on current column
n_extra = curbuf->b_p_ts - col % curbuf->b_p_ts - 1;
n_extra = tabstop_padding(col,
curbuf->b_p_ts,
curbuf->b_p_vts_array) - 1;
if (!list) {
c = ' ';
c_extra = ' ';

View File

@@ -333,7 +333,8 @@ static void shift_block(oparg_T *oap, int amount)
char_u *newp;
const int oldcol = curwin->w_cursor.col;
const int p_sw = get_sw_value(curbuf);
const int p_ts = (int)curbuf->b_p_ts;
const long p_ts = curbuf->b_p_ts;
long *p_vts = curbuf->b_p_vts_array;
struct block_def bd;
int incr;
int i = 0, j = 0;
@@ -383,12 +384,11 @@ static void shift_block(oparg_T *oap, int amount)
}
/* OK, now total=all the VWS reqd, and textstart points at the 1st
* non-ws char in the block. */
if (!curbuf->b_p_et)
i = ((ws_vcol % p_ts) + total) / p_ts; /* number of tabs */
if (i)
j = ((ws_vcol % p_ts) + total) % p_ts; /* number of spp */
else
if (!curbuf->b_p_et) {
tabstop_fromto(ws_vcol, ws_vcol + total, p_ts, p_vts, &i, &j);
} else {
j = total;
}
// if we're splitting a TAB, allow for it
int col_pre = bd.pre_whitesp_c - (bd.startspaces != 0);
@@ -3061,14 +3061,17 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags)
if (gchar_cursor() == TAB) {
/* Don't need to insert spaces when "p" on the last position of a
* tab or "P" on the first position. */
int viscol = getviscol();
if (dir == FORWARD
? (int)curwin->w_cursor.coladd < curbuf->b_p_ts - 1
: curwin->w_cursor.coladd > 0)
coladvance_force(getviscol());
else
? tabstop_padding(viscol, curbuf->b_p_ts, curbuf->b_p_vts_array) != 1
: curwin->w_cursor.coladd > 0) {
coladvance_force(viscol);
} else {
curwin->w_cursor.coladd = 0;
} else if (curwin->w_cursor.coladd > 0 || gchar_cursor() == NUL)
}
} else if (curwin->w_cursor.coladd > 0 || gchar_cursor() == NUL) {
coladvance_force(getviscol() + (dir == FORWARD));
}
}
lnum = curwin->w_cursor.lnum;

View File

@@ -180,6 +180,8 @@ static long p_ts;
static long p_tw;
static int p_udf;
static long p_wm;
static char_u *p_vsts;
static char_u *p_vts;
static char_u *p_keymap;
// Saved values for when 'bin' is set.
@@ -194,6 +196,7 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
static char_u *p_vsts_nopaste;
typedef struct vimoption {
char *fullname; // full option name
@@ -1998,6 +2001,8 @@ static void didset_options2(void)
// Parse default for 'wildmode'.
check_opt_wim();
tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
}
/// Check for string options that are NULL (normally only termcap options).
@@ -2064,6 +2069,8 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_lw);
check_string_option(&buf->b_p_bkc);
check_string_option(&buf->b_p_menc);
check_string_option(&buf->b_p_vsts);
check_string_option(&buf->b_p_vts);
}
/// Free the string allocated for an option.
@@ -3110,6 +3117,69 @@ ambw_end:
if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
errmsg = e_invarg;
}
} else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
if (curbuf->b_p_vsts_array) {
xfree(curbuf->b_p_vsts_array);
curbuf->b_p_vsts_array = 0;
}
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
continue;
}
errmsg = e_invarg;
break;
}
if (errmsg == NULL) {
long *oldarray = curbuf->b_p_vsts_array;
if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
if (oldarray) {
xfree(oldarray);
}
} else {
errmsg = e_invarg;
}
}
}
} else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
if (curbuf->b_p_vts_array) {
xfree(curbuf->b_p_vts_array);
curbuf->b_p_vts_array = NULL;
}
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
continue;
}
errmsg = e_invarg;
break;
}
if (errmsg == NULL) {
long *oldarray = curbuf->b_p_vts_array;
if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
if (oldarray) {
xfree(oldarray);
}
if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
}
} else {
errmsg = e_invarg;
}
}
}
} else {
// Options that are a list of flags.
p = NULL;
@@ -5684,6 +5754,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_TW: return (char_u *)&(curbuf->b_p_tw);
case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
case PV_WM: return (char_u *)&(curbuf->b_p_wm);
case PV_VSTS: return (char_u *)&(curbuf->b_p_vsts);
case PV_VTS: return (char_u *)&(curbuf->b_p_vts);
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
@@ -5935,6 +6007,15 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_tfu = vim_strsave(p_tfu);
buf->b_p_sts = p_sts;
buf->b_p_sts_nopaste = p_sts_nopaste;
buf->b_p_vsts = vim_strsave(p_vsts);
if (p_vsts && p_vsts != empty_option) {
tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
buf->b_p_vsts_nopaste = p_vsts_nopaste
? vim_strsave(p_vsts_nopaste)
: NULL;
buf->b_p_com = vim_strsave(p_com);
buf->b_p_cms = vim_strsave(p_cms);
buf->b_p_fo = vim_strsave(p_fo);
@@ -6006,10 +6087,21 @@ void buf_copy_options(buf_T *buf, int flags)
*/
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
} else {
buf->b_p_isk = vim_strsave(p_isk);
did_isk = true;
buf->b_p_ts = p_ts;
buf->b_p_vts = vim_strsave(p_vts);
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
buf->b_help = false;
if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt);
@@ -6624,6 +6716,12 @@ static void paste_option_changed(void)
buf->b_p_sts_nopaste = buf->b_p_sts;
buf->b_p_ai_nopaste = buf->b_p_ai;
buf->b_p_et_nopaste = buf->b_p_et;
if (buf->b_p_vsts_nopaste) {
xfree(buf->b_p_vsts_nopaste);
}
buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
? vim_strsave(buf->b_p_vsts)
: NULL;
}
// save global options
@@ -6638,6 +6736,12 @@ static void paste_option_changed(void)
p_sts_nopaste = p_sts;
p_tw_nopaste = p_tw;
p_wm_nopaste = p_wm;
if (p_vsts_nopaste) {
xfree(p_vsts_nopaste);
}
p_vsts_nopaste = p_vsts && p_vsts != empty_option
? vim_strsave(p_vsts)
: NULL;
}
// Always set the option values, also when 'paste' is set when it is
@@ -6649,6 +6753,14 @@ static void paste_option_changed(void)
buf->b_p_sts = 0; // softtabstop is 0
buf->b_p_ai = 0; // no auto-indent
buf->b_p_et = 0; // no expandtab
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
buf->b_p_vsts = empty_option;
if (buf->b_p_vsts_array) {
xfree(buf->b_p_vsts_array);
}
buf->b_p_vsts_array = 0;
}
// set global options
@@ -6665,6 +6777,10 @@ static void paste_option_changed(void)
p_wm = 0;
p_sts = 0;
p_ai = 0;
if (p_vsts) {
free_string_option(p_vsts);
}
p_vsts = empty_option;
} else if (old_p_paste) {
// Paste switched from on to off: Restore saved values.
@@ -6675,6 +6791,20 @@ static void paste_option_changed(void)
buf->b_p_sts = buf->b_p_sts_nopaste;
buf->b_p_ai = buf->b_p_ai_nopaste;
buf->b_p_et = buf->b_p_et_nopaste;
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
buf->b_p_vsts = buf->b_p_vsts_nopaste
? vim_strsave(buf->b_p_vsts_nopaste)
: empty_option;
if (buf->b_p_vsts_array) {
xfree(buf->b_p_vsts_array);
}
if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
}
// restore global options
@@ -6692,6 +6822,10 @@ static void paste_option_changed(void)
p_sts = p_sts_nopaste;
p_tw = p_tw_nopaste;
p_wm = p_wm_nopaste;
if (p_vsts) {
free_string_option(p_vsts);
}
p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
}
old_p_paste = p_paste;
@@ -6941,6 +7075,265 @@ int check_ff_value(char_u *p)
return check_opt_strings(p, p_ff_values, false);
}
// Set the integer values corresponding to the string setting of 'vartabstop'.
// "array" will be set, caller must free it if needed.
bool tabstop_set(char_u *var, long **array)
{
long valcount = 1;
int t;
char_u *cp;
if ((!var[0] || (var[0] == '0' && !var[1]))) {
*array = NULL;
return true;
}
for (cp = var; *cp; cp++) {
if (cp == var || *(cp - 1) == ',') {
char_u *end;
if (strtol((char *)cp, (char **)&end, 10) <= 0) {
if (cp != end) {
EMSG(_(e_positive));
} else {
EMSG(_(e_invarg));
}
return false;
}
}
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > var && *(cp - 1) != ',') {
valcount++;
continue;
}
EMSG(_(e_invarg));
return false;
}
*array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
(*array)[0] = valcount;
t = 1;
for (cp = var; *cp;) {
(*array)[t++] = atoi((char *)cp);
while (*cp && *cp != ',') {
cp++;
}
if (*cp) {
cp++;
}
}
return true;
}
// Calculate the number of screen spaces a tab will occupy.
// If "vts" is set then the tab widths are taken from that array,
// otherwise the value of ts is used.
int tabstop_padding(colnr_T col, long ts_arg, long *vts)
{
long ts = ts_arg == 0 ? 8 : ts_arg;
long tabcount;
colnr_T tabcol = 0;
int t;
long padding = 0;
if (vts == NULL || vts[0] == 0) {
return (int)ts - (col % (int)ts);
}
tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
padding = (int)(tabcol - col);
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]);
}
return (int)padding;
}
// Find the size of the tab that covers a particular column.
int tabstop_at(colnr_T col, long ts, long *vts)
{
int tabcount;
colnr_T tabcol = 0;
int t;
int tab_size = 0;
if (vts == 0 || vts[0] == 0) {
return (int)ts;
}
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
tab_size = (int)vts[t];
break;
}
}
if (t > tabcount) {
tab_size = (int)vts[tabcount];
}
return tab_size;
}
// Find the column on which a tab starts.
colnr_T tabstop_start(colnr_T col, long ts, long *vts)
{
int tabcount;
colnr_T tabcol = 0;
int t;
int excess;
if (vts == 0 || vts[0] == 0) {
return (col / (int)ts) * (int)ts;
}
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
return tabcol - (int)vts[t];
}
}
excess = tabcol % vts[tabcount];
return excess + ((col - excess) / (int)vts[tabcount]) * (int)vts[tabcount];
}
// Find the number of tabs and spaces necessary to get from one column
// to another.
void tabstop_fromto(colnr_T start_col,
colnr_T end_col,
long ts,
long *vts,
int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
int padding = 0;
int tabcount;
int t;
if (vts == 0 || vts[0] == 0) {
int tabs = 0;
int initspc = (int)ts - (start_col % (int)ts);
if (spaces >= initspc) {
spaces -= initspc;
tabs++;
}
tabs += spaces / ts;
spaces -= (spaces / ts) * ts;
*ntabs = tabs;
*nspcs = spaces;
return;
}
// Find the padding needed to reach the next tabstop.
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > start_col) {
padding = (int)(tabcol - start_col);
break;
}
}
if (t > tabcount) {
padding =
(int)vts[tabcount] - (int)((start_col - tabcol) % (int)vts[tabcount]);
}
// If the space needed is less than the padding no tabs can be used.
if (spaces < padding) {
*ntabs = 0;
*nspcs = spaces;
return;
}
*ntabs = 1;
spaces -= padding;
// At least one tab has been used. See if any more will fit.
while (spaces != 0 && ++t <= tabcount) {
padding = (int)vts[t];
if (spaces < padding) {
*nspcs = spaces;
return;
}
*ntabs += 1;
spaces -= padding;
}
*ntabs += spaces / (int)vts[tabcount];
*nspcs = spaces % (int)vts[tabcount];
}
// See if two tabstop arrays contain the same values.
bool tabstop_eq(long *ts1, long *ts2)
{
int t;
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
}
if (ts1 == ts2) {
return true;
}
if (ts1[0] != ts2[0]) {
return false;
}
for (t = 1; t <= ts1[0]; t++) {
if (ts1[t] != ts2[t]) {
return false;
}
}
return true;
}
// Copy a tabstop array, allocating space for the new array.
int *tabstop_copy(long *oldts)
{
long *newts;
int t;
if (oldts == 0) {
return 0;
}
newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
for (t = 0; t <= oldts[0]; t++) {
newts[t] = oldts[t];
}
return (int *)newts;
}
// Return a count of the number of tabstops.
int tabstop_count(long *ts)
{
return ts != NULL ? (int)ts[0] : 0;
}
// Return the first tabstop, or 8 if there are no tabstops defined.
int tabstop_first(long *ts)
{
return ts != NULL ? (int)ts[1] : 8;
}
/// Return the effective shiftwidth value for current buffer, using the
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)

View File

@@ -824,6 +824,8 @@ enum {
, BV_UDF
, BV_UL
, BV_WM
, BV_VSTS
, BV_VTS
, BV_COUNT // must be the last one
};

View File

@@ -2998,6 +2998,23 @@ return {
varname='p_ut',
defaults={if_true={vi=4000}}
},
{
full_name='varsofttabstop', abbreviation='vsts',
short_desc=N_("list of numbers of spaces that <Tab> uses while editing"),
type='string', list='comma', scope={'buffer'},
vi_def=true,
varname='p_vsts',
defaults={if_true={vi=""}}
},
{
full_name='vartabstop', abbreviation='vts',
short_desc=N_("list of numbers of spaces that <Tab> in file uses"),
type='string', list='comma', scope={'buffer'},
vi_def=true,
varname='p_vts',
redraw={'current_buffer'},
defaults={if_true={vi=""}}
},
{
full_name='verbose', abbreviation='vbs',
short_desc=N_("give informative messages"),

View File

@@ -3508,8 +3508,9 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
vcol_adjusted = vcol - MB_CHARLEN(p_sbr);
}
// tab amount depends on current column
tab_len = (int)wp->w_buffer->b_p_ts
- vcol_adjusted % (int)wp->w_buffer->b_p_ts - 1;
tab_len = tabstop_padding(vcol_adjusted,
wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array) - 1;
if (!wp->w_p_lbr || !wp->w_p_list) {
n_extra = tab_len;

View File

@@ -12,56 +12,88 @@ source view_util.vim
let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
function s:screen_lines(lnum, width) abort
func s:screen_lines(lnum, width) abort
return ScreenLines([a:lnum, a:lnum + 2], a:width)
endfunction
endfunc
function! s:compare_lines(expect, actual)
func! s:compare_lines(expect, actual)
call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
endfunction
endfunc
function s:test_windows(...)
func s:test_windows(...)
call NewWindow(10, 20)
setl ts=4 sw=4 sts=4 breakindent
put =s:input
exe get(a:000, 0, '')
endfunction
endfunc
function s:close_windows(...)
func s:close_windows(...)
call CloseWindow()
exe get(a:000, 0, '')
endfunction
endfunc
function Test_breakindent01()
func Test_breakindent01()
" simple breakindent test
call s:test_windows('setl briopt=min:0')
let lines=s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ " qrst",
\ " GHIJ",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrst",
\ " GHIJ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
function Test_breakindent02()
func Test_breakindent01_vartabs()
" like 01 but with vartabs feature
if !has("vartabs")
return
endif
call s:test_windows('setl briopt=min:0 vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrst",
\ " GHIJ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent02()
" simple breakindent test with showbreak set
call s:test_windows('setl briopt=min:0 sbr=>>')
let lines=s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ " >>qr",
\ " >>EF",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " >>qr",
\ " >>EF",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent03()
func Test_breakindent02_vartabs()
if !has("vartabs")
return
endif
" simple breakindent test with showbreak set
call s:test_windows('setl briopt=min:0 sbr=>> vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " >>qr",
\ " >>EF",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent03()
" simple breakindent test with showbreak set and briopt including sbr
call s:test_windows('setl briopt=sbr,min:0 sbr=++')
let lines=s:screen_lines(line('.'),8)
let lines = s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ "++ qrst",
@@ -70,77 +102,177 @@ function Test_breakindent03()
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent04()
func Test_breakindent03_vartabs()
" simple breakindent test with showbreak set and briopt including sbr
if !has("vartabs")
return
endif
call s:test_windows('setl briopt=sbr,min:0 sbr=++ vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ "++ qrst",
\ "++ GHIJ",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent04()
" breakindent set with min width 18
call s:test_windows('setl sbr= briopt=min:18')
let lines=s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ " qrstuv",
\ " IJKLMN",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrstuv",
\ " IJKLMN",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent05()
func Test_breakindent04_vartabs()
" breakindent set with min width 18
if !has("vartabs")
return
endif
call s:test_windows('setl sbr= briopt=min:18 vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrstuv",
\ " IJKLMN",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent05()
" breakindent set and shift by 2
call s:test_windows('setl briopt=shift:2,min:0')
let lines=s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ " qr",
\ " EF",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qr",
\ " EF",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
function Test_breakindent06()
func Test_breakindent05_vartabs()
" breakindent set and shift by 2
if !has("vartabs")
return
endif
call s:test_windows('setl briopt=shift:2,min:0 vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qr",
\ " EF",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent06()
" breakindent set and shift by -1
call s:test_windows('setl briopt=shift:-1,min:0')
let lines=s:screen_lines(line('.'),8)
let expect=[
\ " abcd",
\ " qrstu",
\ " HIJKL",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrstu",
\ " HIJKL",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
function Test_breakindent07()
func Test_breakindent06_vartabs()
" breakindent set and shift by -1
if !has("vartabs")
return
endif
call s:test_windows('setl briopt=shift:-1,min:0 vts=4')
let lines = s:screen_lines(line('.'),8)
let expect = [
\ " abcd",
\ " qrstu",
\ " HIJKL",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent07()
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n')
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ab",
\ "? m",
\ "? x",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ "? m",
\ "? x",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= cpo-=n')
endfunction
endfunc
function Test_breakindent07a()
func Test_breakindent07_vartabs()
if !has("vartabs")
return
endif
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 cpo+=n vts=4')
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ "? m",
\ "? x",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= cpo-=n vts&')
endfunc
func Test_breakindent07a()
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4')
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ab",
\ " ? m",
\ " ? x",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ " ? m",
\ " ? x",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent08()
func Test_breakindent07a_vartabs()
if !has("vartabs")
return
endif
" breakindent set and shift by 1, Number set sbr=? and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4 vts=4')
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ " ? m",
\ " ? x",
\ ]
call s:compare_lines(expect, lines)
" clean up
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent08()
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4')
" make sure, cache is invalidated!
@@ -148,43 +280,96 @@ function Test_breakindent08()
redraw!
set ts=4
redraw!
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ^Iabcd",
\ "# opq",
\ "# BCD",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ "# opq",
\ "# BCD",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n')
endfunction
endfunc
function Test_breakindent08a()
func Test_breakindent08_vartabs()
if !has("vartabs")
return
endif
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list cpo+=n ts=4 vts=4')
" make sure, cache is invalidated!
set ts=8
redraw!
set ts=4
redraw!
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ "# opq",
\ "# BCD",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n vts&')
endfunc
func Test_breakindent08a()
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list')
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ^Iabcd",
\ " # opq",
\ " # BCD",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ " # opq",
\ " # BCD",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent09()
func Test_breakindent08a_vartabs()
if !has("vartabs")
return
endif
" breakindent set and shift by 1, Number and list set sbr=# and briopt:sbr
call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list vts=4')
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ " # opq",
\ " # BCD",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent09()
" breakindent set and shift by 1, Number and list set sbr=#
call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list')
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ^Iabcd",
\ " #op",
\ " #AB",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ " #op",
\ " #AB",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent10()
func Test_breakindent09_vartabs()
if !has("vartabs")
return
endif
" breakindent set and shift by 1, Number and list set sbr=#
call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list vts=4')
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ^Iabcd",
\ " #op",
\ " #AB",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent10()
" breakindent set, Number set sbr=~
call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0')
" make sure, cache is invalidated!
@@ -192,41 +377,91 @@ function Test_breakindent10()
redraw!
set ts=4
redraw!
let lines=s:screen_lines(line('.'),10)
let expect=[
\ " 2 ab",
\ "~ mn",
\ "~ yz",
\ ]
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ "~ mn",
\ "~ yz",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n')
endfunction
endfunc
function Test_breakindent11()
func Test_breakindent10_vartabs()
if !has("vartabs")
return
endif
" breakindent set, Number set sbr=~
call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0 vts=4')
" make sure, cache is invalidated!
set ts=8
redraw!
set ts=4
redraw!
let lines = s:screen_lines(line('.'),10)
let expect = [
\ " 2 ab",
\ "~ mn",
\ "~ yz",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n vts&')
endfunc
func Test_breakindent11()
" test strdisplaywidth()
call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4')
let text=getline(2)
let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr=')
endfunction
endfunc
function Test_breakindent12()
func Test_breakindent11_vartabs()
if !has("vartabs")
return
endif
" test strdisplaywidth()
call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4 vts=4')
let text = getline(2)
let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr= vts&')
endfunc
func Test_breakindent12()
" test breakindent with long indent
let s:input="\t\t\t\t\t{"
call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>-')
let lines=s:screen_lines(2,16)
let expect=[
\ " 2 >--->--->--->",
\ " ---{ ",
\ "~ ",
\ ]
let lines = s:screen_lines(2,16)
let expect = [
\ " 2 >--->--->--->",
\ " ---{ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set nuw=4 listchars=')
endfunction
endfunc
function Test_breakindent13()
let s:input=""
func Test_breakindent12_vartabs()
if !has("vartabs")
return
endif
" test breakindent with long indent
let s:input = "\t\t\t\t\t{"
call s:test_windows('setl breakindent linebreak briopt=min:10 nu numberwidth=3 ts=4 list listchars=tab:>- vts=4')
let lines = s:screen_lines(2,16)
let expect = [
\ " 2 >--->--->--->",
\ " ---{ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set nuw=4 listchars= vts&')
endfunc
func Test_breakindent13()
let s:input = ""
call s:test_windows('setl breakindent briopt=min:10 ts=8')
vert resize 20
call setline(1, [" a\tb\tc\td\te", " z y x w v"])
@@ -237,65 +472,149 @@ function Test_breakindent13()
call assert_equal('d', @a)
call assert_equal('w', @b)
call s:close_windows()
endfunction
endfunc
function Test_breakindent14()
let s:input=""
func Test_breakindent13_vartabs()
if !has("vartabs")
return
endif
let s:input = ""
call s:test_windows('setl breakindent briopt=min:10 ts=8 vts=8')
vert resize 20
call setline(1, [" a\tb\tc\td\te", " z y x w v"])
1
norm! fbgj"ayl
2
norm! fygj"byl
call assert_equal('d', @a)
call assert_equal('w', @b)
call s:close_windows('set vts&')
endfunc
func Test_breakindent14()
let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8')
vert resize 30
norm! 3a1234567890
norm! a abcde
exec "norm! 0\<C-V>tex"
let lines=s:screen_lines(line('.'),8)
let expect=[
\ "e ",
\ "~ ",
\ "~ ",
\ ]
let lines = s:screen_lines(line('.'),8)
let expect = [
\ "e ",
\ "~ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
function Test_breakindent15()
let s:input=""
func Test_breakindent14_vartabs()
if !has("vartabs")
return
endif
let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8 vts=8')
vert resize 30
norm! 3a1234567890
norm! a abcde
exec "norm! 0\<C-V>tex"
let lines = s:screen_lines(line('.'),8)
let expect = [
\ "e ",
\ "~ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent15()
let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8 sw=8')
vert resize 30
norm! 4a1234567890
exe "normal! >>\<C-V>3f0x"
let lines=s:screen_lines(line('.'),20)
let expect=[
\ " 1234567890 ",
\ "~ ",
\ "~ ",
\ ]
let lines = s:screen_lines(line('.'),20)
let expect = [
\ " 1234567890 ",
\ "~ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
function Test_breakindent16()
func Test_breakindent15_vartabs()
if !has("vartabs")
return
endif
let s:input = ""
call s:test_windows('setl breakindent briopt= ts=8 sw=8 vts=8')
vert resize 30
norm! 4a1234567890
exe "normal! >>\<C-V>3f0x"
let lines = s:screen_lines(line('.'),20)
let expect = [
\ " 1234567890 ",
\ "~ ",
\ "~ ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent16()
" Check that overlong lines are indented correctly.
let s:input=""
let s:input = ""
call s:test_windows('setl breakindent briopt=min:0 ts=4')
call setline(1, "\t".repeat("1234567890", 10))
resize 6
norm! 1gg$
redraw!
let lines=s:screen_lines(1,10)
let expect=[
\ " 789012",
\ " 345678",
\ " 901234",
\ ]
let lines = s:screen_lines(1,10)
let expect = [
\ " 789012",
\ " 345678",
\ " 901234",
\ ]
call s:compare_lines(expect, lines)
let lines=s:screen_lines(4,10)
let expect=[
\ " 567890",
\ " 123456",
\ " 7890 ",
\ ]
let lines = s:screen_lines(4,10)
let expect = [
\ " 567890",
\ " 123456",
\ " 7890 ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows()
endfunction
endfunc
func Test_breakindent16_vartabs()
if !has("vartabs")
return
endif
" Check that overlong lines are indented correctly.
let s:input = ""
call s:test_windows('setl breakindent briopt=min:0 ts=4 vts=4')
call setline(1, "\t".repeat("1234567890", 10))
resize 6
norm! 1gg$
redraw!
let lines = s:screen_lines(1,10)
let expect = [
\ " 789012",
\ " 345678",
\ " 901234",
\ ]
call s:compare_lines(expect, lines)
let lines = s:screen_lines(4,10)
let expect = [
\ " 567890",
\ " 123456",
\ " 7890 ",
\ ]
call s:compare_lines(expect, lines)
call s:close_windows('set vts&')
endfunc
func Test_breakindent17_vartabs()
if !has("vartabs")

View File

@@ -0,0 +1,257 @@
" Test for variable tabstops
if !has("vartabs")
finish
endif
func! Test_vartabs()
new
%d
" Test normal operation of tabstops ...
set ts=4
call setline(1, join(split('aaaaa', '\zs'), "\t"))
retab 8
let expect = "a a\<tab>a a\<tab>a"
call assert_equal(expect, getline(1))
" ... and softtabstops
set ts=8 sts=6
exe "norm! Sb\<tab>b\<tab>b\<tab>b\<tab>b"
let expect = "b b\<tab> b\<tab> b\<tab>b"
call assert_equal(expect, getline(1))
" Test variable tabstops.
set sts=0 vts=4,8,4,8
exe "norm! Sc\<tab>c\<tab>c\<tab>c\<tab>c\<tab>c"
retab 8
let expect = "c c\<tab> c\<tab>c\<tab>c\<tab>c"
call assert_equal(expect, getline(1))
set et vts=4,8,4,8
exe "norm! Sd\<tab>d\<tab>d\<tab>d\<tab>d\<tab>d"
let expect = "d d d d d d"
call assert_equal(expect, getline(1))
" Changing ts should have no effect if vts is in use.
call cursor(1, 1)
set ts=6
exe "norm! Se\<tab>e\<tab>e\<tab>e\<tab>e\<tab>e"
let expect = "e e e e e e"
call assert_equal(expect, getline(1))
" Clearing vts should revert to using ts.
set vts=
exe "norm! Sf\<tab>f\<tab>f\<tab>f\<tab>f\<tab>f"
let expect = "f f f f f f"
call assert_equal(expect, getline(1))
" Test variable softtabstops.
set noet ts=8 vsts=12,2,6
exe "norm! Sg\<tab>g\<tab>g\<tab>g\<tab>g\<tab>g"
let expect = "g\<tab> g g\<tab> g\<tab> g\<tab>g"
call assert_equal(expect, getline(1))
" Variable tabstops and softtabstops combined.
set vsts=6,12,8 vts=4,6,8
exe "norm! Sh\<tab>h\<tab>h\<tab>h\<tab>h"
let expect = "h\<tab> h\<tab>\<tab>h\<tab>h\<tab>h"
call assert_equal(expect, getline(1))
" Retab with a single value, not using vts.
set ts=8 sts=0 vts= vsts=
exe "norm! Si\<tab>i\<tab>i\<tab>i\<tab>i"
retab 4
let expect = "i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i"
call assert_equal(expect, getline(1))
" Retab with a single value, using vts.
set ts=8 sts=0 vts=6 vsts=
exe "norm! Sj\<tab>j\<tab>j\<tab>j\<tab>j"
retab 4
let expect = "j\<tab> j\<tab>\<tab>j\<tab> j\<tab>\<tab>j"
call assert_equal(expect, getline(1))
" Retab with multiple values, not using vts.
set ts=6 sts=0 vts= vsts=
exe "norm! Sk\<tab>k\<tab>k\<tab>k\<tab>k\<tab>k"
retab 4,8
let expect = "k\<tab> k\<tab>k k\<tab> k\<tab> k"
call assert_equal(expect, getline(1))
" Retab with multiple values, using vts.
set ts=8 sts=0 vts=6 vsts=
exe "norm! Sl\<tab>l\<tab>l\<tab>l\<tab>l\<tab>l"
retab 4,8
let expect = "l\<tab> l\<tab>l l\<tab> l\<tab> l"
call assert_equal(expect, getline(1))
" Check that global and local values are set.
set ts=4 vts=6 sts=8 vsts=10
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
new
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
bwipeout!
" Check that local values only are set.
setlocal ts=5 vts=7 sts=9 vsts=11
call assert_equal(&ts, 5)
call assert_equal(&vts, '7')
call assert_equal(&sts, 9)
call assert_equal(&vsts, '11')
new
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
bwipeout!
" Check that global values only are set.
setglobal ts=6 vts=8 sts=10 vsts=12
call assert_equal(&ts, 5)
call assert_equal(&vts, '7')
call assert_equal(&sts, 9)
call assert_equal(&vsts, '11')
new
call assert_equal(&ts, 6)
call assert_equal(&vts, '8')
call assert_equal(&sts, 10)
call assert_equal(&vsts, '12')
bwipeout!
set ts& vts& sts& vsts& et&
bwipeout!
endfunc
func! Test_vartabs_breakindent()
if !exists("+breakindent")
return
endif
new
%d
" Test normal operation of tabstops ...
set ts=4
call setline(1, join(split('aaaaa', '\zs'), "\t"))
retab 8
let expect = "a a\<tab>a a\<tab>a"
call assert_equal(expect, getline(1))
" ... and softtabstops
set ts=8 sts=6
exe "norm! Sb\<tab>b\<tab>b\<tab>b\<tab>b"
let expect = "b b\<tab> b\<tab> b\<tab>b"
call assert_equal(expect, getline(1))
" Test variable tabstops.
set sts=0 vts=4,8,4,8
exe "norm! Sc\<tab>c\<tab>c\<tab>c\<tab>c\<tab>c"
retab 8
let expect = "c c\<tab> c\<tab>c\<tab>c\<tab>c"
call assert_equal(expect, getline(1))
set et vts=4,8,4,8
exe "norm! Sd\<tab>d\<tab>d\<tab>d\<tab>d\<tab>d"
let expect = "d d d d d d"
call assert_equal(expect, getline(1))
" Changing ts should have no effect if vts is in use.
call cursor(1, 1)
set ts=6
exe "norm! Se\<tab>e\<tab>e\<tab>e\<tab>e\<tab>e"
let expect = "e e e e e e"
call assert_equal(expect, getline(1))
" Clearing vts should revert to using ts.
set vts=
exe "norm! Sf\<tab>f\<tab>f\<tab>f\<tab>f\<tab>f"
let expect = "f f f f f f"
call assert_equal(expect, getline(1))
" Test variable softtabstops.
set noet ts=8 vsts=12,2,6
exe "norm! Sg\<tab>g\<tab>g\<tab>g\<tab>g\<tab>g"
let expect = "g\<tab> g g\<tab> g\<tab> g\<tab>g"
call assert_equal(expect, getline(1))
" Variable tabstops and softtabstops combined.
set vsts=6,12,8 vts=4,6,8
exe "norm! Sh\<tab>h\<tab>h\<tab>h\<tab>h"
let expect = "h\<tab> h\<tab>\<tab>h\<tab>h\<tab>h"
call assert_equal(expect, getline(1))
" Retab with a single value, not using vts.
set ts=8 sts=0 vts= vsts=
exe "norm! Si\<tab>i\<tab>i\<tab>i\<tab>i"
retab 4
let expect = "i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i\<tab>\<tab>i"
call assert_equal(expect, getline(1))
" Retab with a single value, using vts.
set ts=8 sts=0 vts=6 vsts=
exe "norm! Sj\<tab>j\<tab>j\<tab>j\<tab>j"
retab 4
let expect = "j\<tab> j\<tab>\<tab>j\<tab> j\<tab>\<tab>j"
call assert_equal(expect, getline(1))
" Retab with multiple values, not using vts.
set ts=6 sts=0 vts= vsts=
exe "norm! Sk\<tab>k\<tab>k\<tab>k\<tab>k\<tab>k"
retab 4,8
let expect = "k\<tab> k\<tab>k k\<tab> k\<tab> k"
call assert_equal(expect, getline(1))
" Retab with multiple values, using vts.
set ts=8 sts=0 vts=6 vsts=
exe "norm! Sl\<tab>l\<tab>l\<tab>l\<tab>l\<tab>l"
retab 4,8
let expect = "l\<tab> l\<tab>l l\<tab> l\<tab> l"
call assert_equal(expect, getline(1))
" Check that global and local values are set.
set ts=4 vts=6 sts=8 vsts=10
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
new
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
bwipeout!
" Check that local values only are set.
setlocal ts=5 vts=7 sts=9 vsts=11
call assert_equal(&ts, 5)
call assert_equal(&vts, '7')
call assert_equal(&sts, 9)
call assert_equal(&vsts, '11')
new
call assert_equal(&ts, 4)
call assert_equal(&vts, '6')
call assert_equal(&sts, 8)
call assert_equal(&vsts, '10')
bwipeout!
" Check that global values only are set.
setglobal ts=6 vts=8 sts=10 vsts=12
call assert_equal(&ts, 5)
call assert_equal(&vts, '7')
call assert_equal(&sts, 9)
call assert_equal(&vsts, '11')
new
call assert_equal(&ts, 6)
call assert_equal(&vts, '8')
call assert_equal(&sts, 10)
call assert_equal(&vsts, '12')
bwipeout!
bwipeout!
endfunc