Merge pull request #13851 from VVKot/vim-8.1.0105

vim-patch:8.1.{0105,0114,0116,0126,0138,0154,0479,0542,0936}
This commit is contained in:
Jan Edmund Lazo 2021-03-28 13:40:34 -04:00 committed by GitHub
commit 63c2a7af2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1689 additions and 262 deletions

View File

@ -445,6 +445,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right*
*<* *<*
<{motion} Shift {motion} lines one 'shiftwidth' leftwards. <{motion} Shift {motion} lines one 'shiftwidth' leftwards.
If the 'shiftwidth' option is set to zero, the amount
of indent is calculated at the first non-blank
character in the line.
*<<* *<<*
<< Shift [count] lines one 'shiftwidth' leftwards. << Shift [count] lines one 'shiftwidth' leftwards.
@ -455,6 +458,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right*
*>* *>*
>{motion} Shift {motion} lines one 'shiftwidth' rightwards. >{motion} Shift {motion} lines one 'shiftwidth' rightwards.
If the 'shiftwidth' option is set to zero, the amount
of indent is calculated at the first non-blank
character in the line.
*>>* *>>*
>> Shift [count] lines one 'shiftwidth' rightwards. >> Shift [count] lines one 'shiftwidth' rightwards.
@ -934,6 +940,10 @@ This replaces each 'E' character with a euro sign. Read more in |<Char->|.
this (that's a good habit anyway). this (that's a good habit anyway).
`:retab!` may also change a sequence of spaces by `:retab!` may also change a sequence of spaces by
<Tab> characters, which can mess up a printf(). <Tab> characters, which can mess up a printf().
A list of tab widths separated by commas may be used
in place of a single tabstop. Each value in the list
represents the width of one tabstop, except the final
value which applies to all following tabstops.
*retab-example* *retab-example*
Example for using autocommands and ":retab" to edit a file which is stored Example for using autocommands and ":retab" to edit a file which is stored

View File

@ -2308,7 +2308,6 @@ perleval({expr}) any evaluate |perl| expression
pow({x}, {y}) Float {x} to the power of {y} pow({x}, {y}) Float {x} to the power of {y}
prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
printf({fmt}, {expr1}...) String format text printf({fmt}, {expr1}...) String format text
prompt_addtext({buf}, {expr}) none add text to a prompt buffer
prompt_setcallback({buf}, {expr}) none set prompt callback function prompt_setcallback({buf}, {expr}) none set prompt callback function
prompt_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setinterrupt({buf}, {text}) none set prompt interrupt function
prompt_setprompt({buf}, {text}) none set prompt text prompt_setprompt({buf}, {text}) none set prompt text
@ -2393,7 +2392,7 @@ sha256({string}) String SHA256 checksum of {string}
shellescape({string} [, {special}]) shellescape({string} [, {special}])
String escape {string} for use as shell String escape {string} for use as shell
command argument command argument
shiftwidth() Number effective value of 'shiftwidth' shiftwidth([{col}]) Number effective value of 'shiftwidth'
sign_define({name} [, {dict}]) Number define or update a sign sign_define({name} [, {dict}]) Number define or update a sign
sign_getdefined([{name}]) List get a list of defined signs sign_getdefined([{name}]) List get a list of defined signs
sign_getplaced([{expr} [, {dict}]]) sign_getplaced([{expr} [, {dict}]])
@ -7899,7 +7898,7 @@ shellescape({string} [, {special}]) *shellescape()*
< See also |::S|. < See also |::S|.
shiftwidth() *shiftwidth()* shiftwidth([{col}]) *shiftwidth()*
Returns the effective value of 'shiftwidth'. This is the Returns the effective value of 'shiftwidth'. This is the
'shiftwidth' value unless it is zero, in which case it is the 'shiftwidth' value unless it is zero, in which case it is the
'tabstop' value. To be backwards compatible in indent 'tabstop' value. To be backwards compatible in indent
@ -7915,6 +7914,11 @@ shiftwidth() *shiftwidth()*
endif endif
< And then use s:sw() instead of &sw. < And then use s:sw() instead of &sw.
When there is one argument {col} this is used as column number
for which to return the 'shiftwidth' value. This matters for the
'vartabstop' feature. If no {col} argument is given, column 1
will be assumed.
sign_define({name} [, {dict}]) *sign_define()* sign_define({name} [, {dict}]) *sign_define()*
Define a new sign named {name} or modify the attributes of an Define a new sign named {name} or modify the attributes of an
existing sign. This is similar to the |:sign-define| command. existing sign. This is similar to the |:sign-define| command.

View File

@ -5621,6 +5621,9 @@ A jump table for the options with a short description can be found at |Q_op|.
The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is The 'L' flag in 'cpoptions' changes how tabs are used when 'list' is
set. set.
The value of 'softtabstop' will be ignored if |'varsofttabstop'| is set
to anything other than an empty string.
*'spell'* *'nospell'* *'spell'* *'nospell'*
'spell' boolean (default off) 'spell' boolean (default off)
local to window local to window
@ -6158,6 +6161,9 @@ A jump table for the options with a short description can be found at |Q_op|.
though. Otherwise aligned comments will be wrong when 'tabstop' is though. Otherwise aligned comments will be wrong when 'tabstop' is
changed. changed.
The value of 'tabstop' will be ignored if |'vartabstop'| is set to
anything other than an empty string.
*'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'* *'tagbsearch'* *'tbs'* *'notagbsearch'* *'notbs'*
'tagbsearch' 'tbs' boolean (default on) 'tagbsearch' 'tbs' boolean (default on)
global global
@ -6542,6 +6548,38 @@ A jump table for the options with a short description can be found at |Q_op|.
written to disk (see |crash-recovery|). Also used for the written to disk (see |crash-recovery|). Also used for the
|CursorHold| autocommand event. |CursorHold| autocommand event.
*'varsofttabstop'* *'vsts'*
'varsofttabstop' 'vsts' string (default "")
local to buffer
A list of the number of spaces that a <Tab> counts for while editing,
such as inserting a <Tab> or using <BS>. It "feels" like variable-
width <Tab>s are being inserted, while in fact a mixture of spaces
and <Tab>s is used. Tab widths are separated with commas, with the
final value applying to all subsequent tabs.
For example, when editing assembly language files where statements
start in the 8th column and comments in the 40th, it may be useful
to use the following: >
:set varsofttabstop=8,32,8
< This will set soft tabstops at the 8th and 40th columns, and at every
8th column thereafter.
Note that the value of |'softtabstop'| will be ignored while
'varsofttabstop' is set.
*'vartabstop'* *'vts'*
'vartabstop' 'vts' string (default "")
local to buffer
A list of the number of spaces that a <Tab> in the file counts for,
separated by commas. Each value corresponds to one tab, with the
final value applying to all subsequent tabs. For example: >
:set vartabstop=4,20,10,8
< This will make the first tab 4 spaces wide, the second 20 spaces,
the third 10 spaces, and all following tabs 8 spaces.
Note that the value of |'tabstop'| will be ignored while 'vartabstop'
is set.
*'verbose'* *'vbs'* *'verbose'* *'vbs'*
'verbose' 'vbs' number (default 0) 'verbose' 'vbs' number (default 0)
global global

View File

@ -810,6 +810,14 @@ call <SID>OptionL("ts")
call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent") call append("$", "shiftwidth\tnumber of spaces used for each step of (auto)indent")
call append("$", "\t(local to buffer)") call append("$", "\t(local to buffer)")
call <SID>OptionL("sw") call <SID>OptionL("sw")
if has("vartabs")
call append("$", "vartabstop\tlist of number of spaces a tab counts for")
call append("$", "\t(local to buffer)")
call <SID>OptionL("vts")
call append("$", "varsofttabstop\tlist of number of spaces a soft tabsstop counts for")
call append("$", "\t(local to buffer)")
call <SID>OptionL("vsts")
endif
call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces") call append("$", "smarttab\ta <Tab> in an indent inserts 'shiftwidth' spaces")
call <SID>BinOptionG("sta", &sta) call <SID>BinOptionG("sta", &sta)
call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>") call append("$", "softtabstop\tif non-zero, number of spaces to insert for a <Tab>")

View File

@ -282,7 +282,7 @@ int open_buffer(
// Set/reset the Changed flag first, autocmds may change the buffer. // Set/reset the Changed flag first, autocmds may change the buffer.
// Apply the automatic commands, before processing the modelines. // 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 // When reading stdin, the buffer contents always needs writing, so set
// the changed flag. Unless in readonly mode: "ls | nvim -R -". // the changed flag. Unless in readonly mode: "ls | nvim -R -".
@ -1946,6 +1946,17 @@ void free_buf_options(buf_T *buf, int free_p_ff)
clear_string_option(&buf->b_p_fo); clear_string_option(&buf->b_p_fo);
clear_string_option(&buf->b_p_flp); clear_string_option(&buf->b_p_flp);
clear_string_option(&buf->b_p_isk); 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);
XFREE_CLEAR(buf->b_p_vts_array);
clear_string_option(&buf->b_p_keymap); clear_string_option(&buf->b_p_keymap);
keymap_ga_clear(&buf->b_kmap_ga); keymap_ga_clear(&buf->b_kmap_ga);
ga_clear(&buf->b_kmap_ga); ga_clear(&buf->b_kmap_ga);
@ -5375,8 +5386,8 @@ bool bt_terminal(const buf_T *const buf)
return buf != NULL && buf->b_p_bt[0] == 't'; return buf != NULL && buf->b_p_bt[0] == 't';
} }
// Return true if "buf" is a "nofile", "acwrite" or "terminal" buffer. // Return true if "buf" is a "nofile", "acwrite", "terminal" or "prompt"
// This means the buffer name is not a file name. // buffer. This means the buffer name is not a file name.
bool bt_nofile(const buf_T *const buf) bool bt_nofile(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{ {
@ -5386,7 +5397,8 @@ bool bt_nofile(const buf_T *const buf)
|| buf->b_p_bt[0] == 'p'); || 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) bool bt_dontwrite(const buf_T *const buf)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT 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; ///< 'wrapmargin'
long b_p_wm_nobin; ///< b_p_wm saved for binary mode long b_p_wm_nobin; ///< b_p_wm saved for binary mode
long b_p_wm_nopaste; ///< b_p_wm saved for paste 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' char_u *b_p_keymap; ///< 'keymap'
// local values for options which are normally global // 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 tab_pad;
int ind_done; int ind_done;
int round; int round;
int ind_col;
// Round 1: compute the number of characters needed for the indent // Round 1: compute the number of characters needed for the indent
// Round 2: copy the characters. // Round 2: copy the characters.
@ -835,13 +836,15 @@ int copy_indent(int size, char_u *src)
todo = size; todo = size;
ind_len = 0; ind_len = 0;
ind_done = 0; ind_done = 0;
ind_col = 0;
s = src; s = src;
// Count/copy the usable portion of the source line. // Count/copy the usable portion of the source line.
while (todo > 0 && ascii_iswhite(*s)) { while (todo > 0 && ascii_iswhite(*s)) {
if (*s == TAB) { if (*s == TAB) {
tab_pad = (int)curbuf->b_p_ts tab_pad = tabstop_padding(ind_done,
- (ind_done % (int)curbuf->b_p_ts); curbuf->b_p_ts,
curbuf->b_p_vts_array);
// Stop if this tab will overshoot the target. // Stop if this tab will overshoot the target.
if (todo < tab_pad) { if (todo < tab_pad) {
@ -849,9 +852,11 @@ int copy_indent(int size, char_u *src)
} }
todo -= tab_pad; todo -= tab_pad;
ind_done += tab_pad; ind_done += tab_pad;
ind_col += tab_pad;
} else { } else {
todo--; todo--;
ind_done++; ind_done++;
ind_col++;
} }
ind_len++; ind_len++;
@ -862,11 +867,12 @@ int copy_indent(int size, char_u *src)
} }
// Fill to next tabstop with a tab, if possible. // 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) { if ((todo >= tab_pad) && !curbuf->b_p_et) {
todo -= tab_pad; todo -= tab_pad;
ind_len++; ind_len++;
ind_col += tab_pad;
if (p != NULL) { if (p != NULL) {
*p++ = TAB; *p++ = TAB;
@ -874,12 +880,20 @@ int copy_indent(int size, char_u *src)
} }
// Add tabs required for indent. // Add tabs required for indent.
while (todo >= (int)curbuf->b_p_ts && !curbuf->b_p_et) { if (!curbuf->b_p_et) {
todo -= (int)curbuf->b_p_ts; for (;;) {
ind_len++; tab_pad = tabstop_padding(ind_col,
curbuf->b_p_ts,
if (p != NULL) { curbuf->b_p_vts_array);
*p++ = TAB; 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 || do_si
) { ) {
// count white space on current line // 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)) { if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
newindent = second_line_indent; // for ^^D command in insert mode newindent = second_line_indent; // for ^^D command in insert mode
} }
@ -1453,7 +1469,9 @@ int open_line(
if (curbuf->b_p_ai if (curbuf->b_p_ai
|| do_si || 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 // Add the indent offset

View File

@ -744,8 +744,7 @@ int vim_strnsize(char_u *s, int len)
/// @return Number of characters. /// @return Number of characters.
#define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \ #define RET_WIN_BUF_CHARTABSIZE(wp, buf, p, col) \
if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \ if (*(p) == TAB && (!(wp)->w_p_list || wp->w_p_lcs_chars.tab1)) { \
const int ts = (int)(buf)->b_p_ts; \ return tabstop_padding(col, (buf)->b_p_ts, (buf)->b_p_vts_array); \
return (ts - (int)(col % ts)); \
} else { \ } else { \
return ptr2cells(p); \ 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; int n;
if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) { if ((*s == TAB) && (!wp->w_p_list || wp->w_p_lcs_chars.tab1)) {
n = (int)wp->w_buffer->b_p_ts; return tabstop_padding(col,
return n - (col % n); wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array);
} }
n = ptr2cells(s); 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 char_u *line; // start of the line
int incr; int incr;
int head; int head;
long *vts = wp->w_buffer->b_p_vts_array;
int ts = (int)wp->w_buffer->b_p_ts; int ts = (int)wp->w_buffer->b_p_ts;
int c; 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 // A tab gets expanded, depending on the current column
if (c == TAB) { if (c == TAB) {
incr = ts - (vcol % ts); incr = tabstop_padding(vcol, ts, vts);
} else { } else {
// For utf-8, if the byte is >= 0x80, need to look at // For utf-8, if the byte is >= 0x80, need to look at
// further bytes to find the cell width. // further bytes to find the cell width.

View File

@ -597,7 +597,10 @@ static int insert_check(VimState *state)
s->mincol = curwin->w_wcol; s->mincol = curwin->w_wcol;
validate_cursor_col(); 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_wrow == curwin->w_winrow
+ curwin->w_height_inner - 1 - get_scrolloff_value(curwin) + curwin->w_height_inner - 1 - get_scrolloff_value(curwin)
&& (curwin->w_cursor.lnum != curwin->w_topline && (curwin->w_cursor.lnum != curwin->w_topline
@ -8178,24 +8181,20 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
/* /*
* Handle deleting one 'shiftwidth' or 'softtabstop'. * Handle deleting one 'shiftwidth' or 'softtabstop'.
*/ */
if ( mode == BACKSPACE_CHAR if (mode == BACKSPACE_CHAR
&& ((p_sta && in_indent) && ((p_sta && in_indent)
|| (get_sts_value() != 0 || ((get_sts_value() != 0
&& curwin->w_cursor.col > 0 || tabstop_count(curbuf->b_p_vsts_array))
&& (*(get_cursor_pos_ptr() - 1) == TAB && curwin->w_cursor.col > 0
|| (*(get_cursor_pos_ptr() - 1) == ' ' && (*(get_cursor_pos_ptr() - 1) == TAB
&& (!*inserted_space_p || (*(get_cursor_pos_ptr() - 1) == ' '
|| arrow_used)))))) { && (!*inserted_space_p || arrow_used)))))) {
int ts; int ts;
colnr_T vcol; colnr_T vcol;
colnr_T want_vcol; colnr_T want_vcol;
colnr_T start_vcol; colnr_T start_vcol;
*inserted_space_p = FALSE; *inserted_space_p = false;
if (p_sta && in_indent)
ts = get_sw_value(curbuf);
else
ts = get_sts_value();
// Compute the virtual column where we want to be. Since // Compute the virtual column where we want to be. Since
// 'showbreak' may get in the way, need to get the last column of // 'showbreak' may get in the way, need to get the last column of
// the previous character. // the previous character.
@ -8204,7 +8203,14 @@ static bool ins_bs(int c, int mode, int *inserted_space_p)
dec_cursor(); dec_cursor();
getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol); getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol);
inc_cursor(); inc_cursor();
want_vcol = (want_vcol / ts) * ts; if (p_sta && in_indent) {
ts = (int)get_sw_value(curbuf);
want_vcol = (want_vcol / ts) * ts;
} else {
want_vcol = tabstop_start(want_vcol,
get_sts_value(),
curbuf->b_p_vsts_array);
}
// delete characters until we are at or before want_vcol // delete characters until we are at or before want_vcol
while (vcol > want_vcol while (vcol > want_vcol
@ -8669,10 +8675,19 @@ static bool ins_tab(void)
can_cindent = false; 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 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; return true;
} }
@ -8686,16 +8701,22 @@ static bool ins_tab(void)
can_si_back = false; can_si_back = false;
AppendToRedobuff("\t"); AppendToRedobuff("\t");
if (p_sta && ind) { // insert tab in indent, use "shiftwidth" if (p_sta && ind) { // insert tab in indent, use 'shiftwidth'
temp = get_sw_value(curbuf); temp = (int)get_sw_value(curbuf);
} else if (curbuf->b_p_sts != 0) { // use "softtabstop" when set temp -= get_nolist_virtcol() % temp;
temp = get_sts_value(); } else if (tabstop_count(curbuf->b_p_vsts_array) > 0
} else { // otherwise use "tabstop" || curbuf->b_p_sts != 0) {
temp = (int)curbuf->b_p_ts; // use 'softtabstop' when set
temp = tabstop_padding(get_nolist_virtcol(),
get_sts_value(),
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 * 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 * replace mode. Insert the rest with ins_str(); it will not delete any
@ -8716,7 +8737,9 @@ static bool ins_tab(void)
/* /*
* When 'expandtab' not set: Replace spaces by TABs where possible. * 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 *ptr;
char_u *saved_line = NULL; // init for GCC char_u *saved_line = NULL; // init for GCC
pos_T pos; pos_T pos;
@ -9133,10 +9156,16 @@ static void ins_try_si(int c)
* Get the value that w_virtcol would have when 'list' is off. * Get the value that w_virtcol would have when 'list' is off.
* Unless 'cpo' contains the 'L' flag. * Unless 'cpo' contains the 'L' flag.
*/ */
static colnr_T get_nolist_virtcol(void) colnr_T get_nolist_virtcol(void)
{ {
if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) // check validity of cursor in current buffer
if (curwin->w_buffer == NULL || curwin->w_buffer->b_ml.ml_mfp == NULL
|| curwin->w_cursor.lnum > curwin->w_buffer->b_ml.ml_line_count) {
return 0;
}
if (curwin->w_p_list && vim_strchr(p_cpo, CPO_LISTWM) == NULL) {
return getvcol_nolist(&curwin->w_cursor); return getvcol_nolist(&curwin->w_cursor);
}
validate_virtcol(); validate_virtcol();
return curwin->w_virtcol; return curwin->w_virtcol;
} }

View File

@ -309,7 +309,7 @@ return {
setwinvar={args=3}, setwinvar={args=3},
sha256={args=1}, sha256={args=1},
shellescape={args={1, 2}}, shellescape={args={1, 2}},
shiftwidth={}, shiftwidth={args={0, 1}},
sign_define={args={1, 2}}, sign_define={args={1, 2}},
sign_getdefined={args={0, 1}}, sign_getdefined={args={0, 1}},
sign_getplaced={args={0, 2}}, sign_getplaced={args={0, 2}},

View File

@ -4312,6 +4312,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr)
"title", "title",
"user-commands", // was accidentally included in 5.4 "user-commands", // was accidentally included in 5.4
"user_commands", "user_commands",
"vartabs",
"vertsplit", "vertsplit",
"virtualedit", "virtualedit",
"visual", "visual",
@ -8848,6 +8849,18 @@ static void f_shellescape(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_shiftwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
rettv->vval.v_number = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
long col;
col = (long)tv_get_number_chk(argvars, NULL);
if (col < 0) {
return; // type error; errmsg already given
}
rettv->vval.v_number = get_sw_value_col(curbuf, col);
return;
}
rettv->vval.v_number = get_sw_value(curbuf); rettv->vval.v_number = get_sw_value(curbuf);
} }

View File

@ -712,14 +712,15 @@ void ex_retab(exarg_T *eap)
long len; long len;
long col; long col;
long vcol; long vcol;
long start_col = 0; /* For start of white-space string */ long start_col = 0; // For start of white-space string
long start_vcol = 0; /* For start of white-space string */ long start_vcol = 0; // For start of white-space string
int temp;
long old_len; long old_len;
char_u *ptr; char_u *ptr;
char_u *new_line = (char_u *)1; /* init to non-NULL */ char_u *new_line = (char_u *)1; // init to non-NULL
int did_undo; /* called u_save for current line */ int did_undo; // called u_save for current line
int new_ts; long *new_vts_array = NULL;
char_u *new_ts_str; // string value of tab argument
int save_list; int save_list;
linenr_T first_line = 0; /* first changed line */ linenr_T first_line = 0; /* first changed line */
linenr_T last_line = 0; /* last changed line */ linenr_T last_line = 0; /* last changed line */
@ -727,14 +728,24 @@ void ex_retab(exarg_T *eap)
save_list = curwin->w_p_list; save_list = curwin->w_p_list;
curwin->w_p_list = 0; /* don't want list mode here */ curwin->w_p_list = 0; /* don't want list mode here */
new_ts = getdigits_int(&(eap->arg), false, -1); new_ts_str = eap->arg;
if (new_ts < 0) { if (!tabstop_set(eap->arg, &new_vts_array)) {
EMSG(_(e_positive));
return; return;
} }
if (new_ts == 0) while (ascii_isdigit(*(eap->arg)) || *(eap->arg) == ',') {
new_ts = curbuf->b_p_ts; (eap->arg)++;
for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum) { }
// This ensures that either new_vts_array and new_ts_str are freshly
// allocated, or new_vts_array points to an existing array and new_ts_str
// is null.
if (new_vts_array == NULL) {
new_vts_array = 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); ptr = ml_get(lnum);
col = 0; col = 0;
vcol = 0; vcol = 0;
@ -758,13 +769,12 @@ void ex_retab(exarg_T *eap)
len = num_spaces = vcol - start_vcol; len = num_spaces = vcol - start_vcol;
num_tabs = 0; num_tabs = 0;
if (!curbuf->b_p_et) { if (!curbuf->b_p_et) {
temp = new_ts - (start_vcol % new_ts); int t, s;
if (num_spaces >= temp) {
num_spaces -= temp; tabstop_fromto(start_vcol, vcol,
num_tabs++; curbuf->b_p_ts, new_vts_array, &t, &s);
} num_tabs = t;
num_tabs += num_spaces / new_ts; num_spaces = s;
num_spaces -= (num_spaces / new_ts) * new_ts;
} }
if (curbuf->b_p_et || got_tab if (curbuf->b_p_et || got_tab
|| (num_spaces + num_tabs < len)) { || (num_spaces + num_tabs < len)) {
@ -817,15 +827,42 @@ void ex_retab(exarg_T *eap)
if (got_int) if (got_int)
EMSG(_(e_interr)); 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_vts_array) == 1
&& curbuf->b_p_ts == tabstop_first(new_vts_array)) {
// not changed
} else if (tabstop_count(curbuf->b_p_vts_array) > 0
&& tabstop_eq(curbuf->b_p_vts_array, new_vts_array)) {
// not changed
} else {
redraw_curbuf_later(NOT_VALID); redraw_curbuf_later(NOT_VALID);
}
if (first_line != 0) { if (first_line != 0) {
changed_lines(first_line, 0, last_line + 1, 0L, true); changed_lines(first_line, 0, last_line + 1, 0L, true);
} }
curwin->w_p_list = save_list; /* restore 'list' */ 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_vts_array) > 1) {
set_string_option_direct("vts", -1, new_ts_str,
OPT_FREE | OPT_LOCAL, 0);
curbuf->b_p_vts_array = new_vts_array;
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_vts_array);
xfree(new_vts_array);
}
xfree(new_ts_str);
}
coladvance(curwin->w_curswant); coladvance(curwin->w_curswant);
u_clearline(); 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. * Appropriately expand any tabs to spaces.
*/ */
if (line[col] == TAB || tab_spaces != 0) { if (line[col] == TAB || tab_spaces != 0) {
if (tab_spaces == 0) if (tab_spaces == 0) {
tab_spaces = (int)(curbuf->b_p_ts - (print_pos % curbuf->b_p_ts)); tab_spaces = tabstop_padding(print_pos,
curbuf->b_p_ts,
curbuf->b_p_vts_array);
}
while (tab_spaces > 0) { while (tab_spaces > 0) {
need_break = mch_print_text_out((char_u *)" ", 1); 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. // Count the size (in window cells) of the indent in the current line.
int get_indent(void) 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". // Count the size (in window cells) of the indent in line "lnum".
int get_indent_lnum(linenr_T 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". // "buf".
int get_indent_buf(buf_T *buf, linenr_T lnum) 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; 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. // Set the indent of the current line.
// Leaves the cursor on the first non-blank in the 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 line_len;
int doit = false; int doit = false;
int ind_done = 0; // Measured in spaces. int ind_done = 0; // Measured in spaces.
int ind_col = 0;
int tab_pad; int tab_pad;
int retval = false; int retval = false;
@ -130,7 +164,9 @@ int set_indent(int size, int flags)
// Count as many characters as we can use. // Count as many characters as we can use.
while (todo > 0 && ascii_iswhite(*p)) { while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) { 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. // Stop if this tab will overshoot the target.
if (todo < tab_pad) { if (todo < tab_pad) {
@ -147,35 +183,41 @@ int set_indent(int size, int flags)
p++; p++;
} }
// These diverge from this point.
ind_col = ind_done;
// Set initial number of whitespace chars to copy if we are // Set initial number of whitespace chars to copy if we are
// preserving indent but expandtab is set. // preserving indent but expandtab is set.
if (curbuf->b_p_et) { if (curbuf->b_p_et) {
orig_char_len = ind_len; orig_char_len = ind_len;
} }
// Fill to next tabstop with a tab, if possible. // 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)) { if ((todo >= tab_pad) && (orig_char_len == -1)) {
doit = true; doit = true;
todo -= tab_pad; todo -= tab_pad;
ind_len++; ind_len++;
// ind_done += tab_pad; // ind_done += tab_pad;
ind_col += tab_pad;
} }
} }
// Count tabs required for indent. // 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) { if (*p != TAB) {
doit = true; doit = true;
} else { } else {
p++; p++;
} }
todo -= (int)curbuf->b_p_ts; todo -= tab_pad;
ind_len++; ind_len++;
ind_col += tab_pad;
// ind_done += (int)curbuf->b_p_ts;
} }
} }
@ -255,7 +297,9 @@ int set_indent(int size, int flags)
while (todo > 0 && ascii_iswhite(*p)) { while (todo > 0 && ascii_iswhite(*p)) {
if (*p == TAB) { 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. // Stop if this tab will overshoot the target.
if (todo < tab_pad) { if (todo < tab_pad) {
@ -272,18 +316,28 @@ int set_indent(int size, int flags)
} }
// Fill to next tabstop with a tab, if possible. // 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) { if (todo >= tab_pad) {
*s++ = TAB; *s++ = TAB;
todo -= tab_pad; todo -= tab_pad;
ind_done += tab_pad;
} }
p = skipwhite(p); 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; *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 long prev_ts = 0; // Cached tabstop value.
static const char_u *prev_line = NULL; // cached pointer to line. static const char_u *prev_line = NULL; // cached pointer to line.
static varnumber_T prev_tick = 0; // Changedtick of cached value. static varnumber_T prev_tick = 0; // Changedtick of cached value.
static long *prev_vts = NULL; // Cached vartabs values.
int bri = 0; int bri = 0;
// window width minus window margin space, i.e. what rests for text // window width minus window margin space, i.e. what rests for text
const int eff_wwidth = wp->w_width_inner 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 // used cached indent, unless pointer or 'tabstop' changed
if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts 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_line = line;
prev_ts = wp->w_buffer->b_p_ts; 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_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; bri = prev_indent + wp->w_briopt_shift;

View File

@ -1758,7 +1758,9 @@ void msg_prt_line(char_u *s, int list)
c = *s++; c = *s++;
if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) { if (c == TAB && (!list || curwin->w_p_lcs_chars.tab1)) {
// tab amount depends on current column // 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) { if (!list) {
c = ' '; c = ' ';
c_extra = ' '; c_extra = ' ';

View File

@ -6777,9 +6777,10 @@ static void nv_g_cmd(cmdarg_T *cap)
} }
coladvance((colnr_T)i); coladvance((colnr_T)i);
if (flag) { if (flag) {
do do {
i = gchar_cursor(); i = gchar_cursor();
while (ascii_iswhite(i) && oneright()); } while (ascii_iswhite(i) && oneright());
curwin->w_valid &= ~VALID_WCOL;
} }
curwin->w_set_curswant = true; curwin->w_set_curswant = true;
break; break;

View File

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

View File

@ -180,6 +180,8 @@ static long p_ts;
static long p_tw; static long p_tw;
static int p_udf; static int p_udf;
static long p_wm; static long p_wm;
static char_u *p_vsts;
static char_u *p_vts;
static char_u *p_keymap; static char_u *p_keymap;
// Saved values for when 'bin' is set. // Saved values for when 'bin' is set.
@ -194,6 +196,7 @@ static int p_et_nopaste;
static long p_sts_nopaste; static long p_sts_nopaste;
static long p_tw_nopaste; static long p_tw_nopaste;
static long p_wm_nopaste; static long p_wm_nopaste;
static char_u *p_vsts_nopaste;
typedef struct vimoption { typedef struct vimoption {
char *fullname; // full option name char *fullname; // full option name
@ -1998,6 +2001,8 @@ static void didset_options2(void)
// Parse default for 'wildmode'. // Parse default for 'wildmode'.
check_opt_wim(); 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). /// 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_lw);
check_string_option(&buf->b_p_bkc); check_string_option(&buf->b_p_bkc);
check_string_option(&buf->b_p_menc); 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. /// Free the string allocated for an option.
@ -3110,6 +3117,67 @@ ambw_end:
if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) { if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
errmsg = e_invarg; 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))) {
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 { } else {
// Options that are a list of flags. // Options that are a list of flags.
p = NULL; p = NULL;
@ -5684,6 +5752,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_TW: return (char_u *)&(curbuf->b_p_tw); case PV_TW: return (char_u *)&(curbuf->b_p_tw);
case PV_UDF: return (char_u *)&(curbuf->b_p_udf); case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
case PV_WM: return (char_u *)&(curbuf->b_p_wm); 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_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl); case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl); case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
@ -5935,6 +6005,15 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_tfu = vim_strsave(p_tfu); buf->b_p_tfu = vim_strsave(p_tfu);
buf->b_p_sts = p_sts; buf->b_p_sts = p_sts;
buf->b_p_sts_nopaste = p_sts_nopaste; 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_com = vim_strsave(p_com);
buf->b_p_cms = vim_strsave(p_cms); buf->b_p_cms = vim_strsave(p_cms);
buf->b_p_fo = vim_strsave(p_fo); buf->b_p_fo = vim_strsave(p_fo);
@ -6006,10 +6085,21 @@ void buf_copy_options(buf_T *buf, int flags)
*/ */
if (dont_do_help) { if (dont_do_help) {
buf->b_p_isk = save_p_isk; 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 { } else {
buf->b_p_isk = vim_strsave(p_isk); buf->b_p_isk = vim_strsave(p_isk);
did_isk = true; did_isk = true;
buf->b_p_ts = p_ts; 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; buf->b_help = false;
if (buf->b_p_bt[0] == 'h') { if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt); clear_string_option(&buf->b_p_bt);
@ -6624,6 +6714,12 @@ static void paste_option_changed(void)
buf->b_p_sts_nopaste = buf->b_p_sts; buf->b_p_sts_nopaste = buf->b_p_sts;
buf->b_p_ai_nopaste = buf->b_p_ai; buf->b_p_ai_nopaste = buf->b_p_ai;
buf->b_p_et_nopaste = buf->b_p_et; 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 // save global options
@ -6638,6 +6734,12 @@ static void paste_option_changed(void)
p_sts_nopaste = p_sts; p_sts_nopaste = p_sts;
p_tw_nopaste = p_tw; p_tw_nopaste = p_tw;
p_wm_nopaste = p_wm; 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 // Always set the option values, also when 'paste' is set when it is
@ -6649,6 +6751,14 @@ static void paste_option_changed(void)
buf->b_p_sts = 0; // softtabstop is 0 buf->b_p_sts = 0; // softtabstop is 0
buf->b_p_ai = 0; // no auto-indent buf->b_p_ai = 0; // no auto-indent
buf->b_p_et = 0; // no expandtab 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 // set global options
@ -6665,6 +6775,10 @@ static void paste_option_changed(void)
p_wm = 0; p_wm = 0;
p_sts = 0; p_sts = 0;
p_ai = 0; p_ai = 0;
if (p_vsts) {
free_string_option(p_vsts);
}
p_vsts = empty_option;
} else if (old_p_paste) { } else if (old_p_paste) {
// Paste switched from on to off: Restore saved values. // Paste switched from on to off: Restore saved values.
@ -6675,6 +6789,20 @@ static void paste_option_changed(void)
buf->b_p_sts = buf->b_p_sts_nopaste; buf->b_p_sts = buf->b_p_sts_nopaste;
buf->b_p_ai = buf->b_p_ai_nopaste; buf->b_p_ai = buf->b_p_ai_nopaste;
buf->b_p_et = buf->b_p_et_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 // restore global options
@ -6692,6 +6820,10 @@ static void paste_option_changed(void)
p_sts = p_sts_nopaste; p_sts = p_sts_nopaste;
p_tw = p_tw_nopaste; p_tw = p_tw_nopaste;
p_wm = p_wm_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; old_p_paste = p_paste;
@ -6941,17 +7073,308 @@ int check_ff_value(char_u *p)
return check_opt_strings(p, p_ff_values, false); 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] == NUL || (var[0] == '0' && var[1] == NUL)) {
*array = NULL;
return true;
}
for (cp = var; *cp != NUL; 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[0] == ',' && cp > var && cp[-1] != ',' && cp[1] != NUL) {
valcount++;
continue;
}
EMSG(_(e_invarg));
return false;
}
*array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
(*array)[0] = valcount;
t = 1;
for (cp = var; *cp != NUL;) {
(*array)[t++] = atoi((char *)cp);
while (*cp != NUL && *cp != ',') {
cp++;
}
if (*cp != NUL) {
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 == NULL || 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 == NULL || 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_arg,
long *vts,
int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
int padding = 0;
int tabcount;
int t;
long ts = ts_arg == 0 ? curbuf->b_p_ts : ts_arg;
if (vts == NULL || vts[0] == 0) {
int tabs = 0;
int initspc = 0;
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 /// Return the effective shiftwidth value for current buffer, using the
/// 'tabstop' value when 'shiftwidth' is zero. /// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf) int get_sw_value(buf_T *buf)
{ {
long result = buf->b_p_sw ? buf->b_p_sw : buf->b_p_ts; long result = get_sw_value_col(buf, 0);
assert(result >= 0 && result <= INT_MAX); assert(result >= 0 && result <= INT_MAX);
return (int)result; return (int)result;
} }
// Idem, using the first non-black in the current line.
long get_sw_value_indent(buf_T *buf)
{
pos_T pos = curwin->w_cursor;
pos.col = (colnr_T)getwhitecols_curline();
return get_sw_value_pos(buf, &pos);
}
// Idem, using "pos".
long get_sw_value_pos(buf_T *buf, pos_T *pos)
{
pos_T save_cursor = curwin->w_cursor;
long sw_value;
curwin->w_cursor = *pos;
sw_value = get_sw_value_col(buf, get_nolist_virtcol());
curwin->w_cursor = save_cursor;
return sw_value;
}
// Idem, using virtual column "col".
long get_sw_value_col(buf_T *buf, colnr_T col)
{
return buf->b_p_sw ? buf->b_p_sw
: tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
}
/// Return the effective softtabstop value for the current buffer, /// Return the effective softtabstop value for the current buffer,
/// using the effective shiftwidth value when 'softtabstop' is negative. /// using the shiftwidth value when 'softtabstop' is negative.
int get_sts_value(void) int get_sts_value(void)
{ {
long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts; long result = curbuf->b_p_sts < 0 ? get_sw_value(curbuf) : curbuf->b_p_sts;

View File

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

View File

@ -2998,6 +2998,23 @@ return {
varname='p_ut', varname='p_ut',
defaults={if_true={vi=4000}} 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', full_name='verbose', abbreviation='vbs',
short_desc=N_("give informative messages"), short_desc=N_("give informative messages"),

View File

@ -3441,8 +3441,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
// TODO: is passing p for start of the line OK? // TODO: is passing p for start of the line OK?
n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1;
if (c == TAB && n_extra + col > grid->Columns) { if (c == TAB && n_extra + col > grid->Columns) {
n_extra = (int)wp->w_buffer->b_p_ts n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts,
- vcol % (int)wp->w_buffer->b_p_ts - 1; wp->w_buffer->b_p_vts_array) - 1;
} }
c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' '; c_extra = mb_off > 0 ? MB_FILLER_CHAR : ' ';
c_final = NUL; c_final = NUL;
@ -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); vcol_adjusted = vcol - MB_CHARLEN(p_sbr);
} }
// tab amount depends on current column // tab amount depends on current column
tab_len = (int)wp->w_buffer->b_p_ts tab_len = tabstop_padding(vcol_adjusted,
- vcol_adjusted % (int)wp->w_buffer->b_p_ts - 1; wp->w_buffer->b_p_ts,
wp->w_buffer->b_p_vts_array) - 1;
if (!wp->w_p_lbr || !wp->w_p_list) { if (!wp->w_p_lbr || !wp->w_p_list) {
n_extra = tab_len; n_extra = tab_len;
@ -3542,6 +3543,10 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow,
xfree(p_extra_free); xfree(p_extra_free);
p_extra_free = p; p_extra_free = p;
for (i = 0; i < tab_len; i++) { for (i = 0; i < tab_len; i++) {
if (*p == NUL) {
tab_len = i;
break;
}
int lcs = wp->w_p_lcs_chars.tab2; int lcs = wp->w_p_lcs_chars.tab2;
// if tab3 is given, need to change the char // if tab3 is given, need to change the char

View File

@ -12,56 +12,88 @@ source view_util.vim
let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" 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) 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")) 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) call NewWindow(10, 20)
setl ts=4 sw=4 sts=4 breakindent setl ts=4 sw=4 sts=4 breakindent
put =s:input put =s:input
exe get(a:000, 0, '') exe get(a:000, 0, '')
endfunction endfunc
function s:close_windows(...) func s:close_windows(...)
call CloseWindow() call CloseWindow()
exe get(a:000, 0, '') exe get(a:000, 0, '')
endfunction endfunc
function Test_breakindent01() func Test_breakindent01()
" simple breakindent test " simple breakindent test
call s:test_windows('setl briopt=min:0') call s:test_windows('setl briopt=min:0')
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ " abcd", \ " abcd",
\ " qrst", \ " qrst",
\ " GHIJ", \ " GHIJ",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() 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 " simple breakindent test with showbreak set
call s:test_windows('setl briopt=min:0 sbr=>>') call s:test_windows('setl briopt=min:0 sbr=>>')
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ " abcd", \ " abcd",
\ " >>qr", \ " >>qr",
\ " >>EF", \ " >>EF",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set sbr=') 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 " simple breakindent test with showbreak set and briopt including sbr
call s:test_windows('setl briopt=sbr,min:0 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=[ let expect=[
\ " abcd", \ " abcd",
\ "++ qrst", \ "++ qrst",
@ -70,77 +102,177 @@ function Test_breakindent03()
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" clean up " clean up
call s:close_windows('set sbr=') 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 " breakindent set with min width 18
call s:test_windows('setl sbr= briopt=min:18') call s:test_windows('setl sbr= briopt=min:18')
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ " abcd", \ " abcd",
\ " qrstuv", \ " qrstuv",
\ " IJKLMN", \ " IJKLMN",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" clean up " clean up
call s:close_windows('set sbr=') 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 " breakindent set and shift by 2
call s:test_windows('setl briopt=shift:2,min:0') call s:test_windows('setl briopt=shift:2,min:0')
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ " abcd", \ " abcd",
\ " qr", \ " qr",
\ " EF", \ " EF",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() 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 " breakindent set and shift by -1
call s:test_windows('setl briopt=shift:-1,min:0') call s:test_windows('setl briopt=shift:-1,min:0')
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ " abcd", \ " abcd",
\ " qrstu", \ " qrstu",
\ " HIJKL", \ " HIJKL",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() 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 " 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') 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 lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ab", \ " 2 ab",
\ "? m", \ "? m",
\ "? x", \ "? x",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" clean up " clean up
call s:close_windows('set sbr= cpo-=n') 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 " 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') call s:test_windows('setl briopt=shift:1,sbr,min:0 nu sbr=? nuw=4')
let lines=s:screen_lines(line('.'),10) let lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ab", \ " 2 ab",
\ " ? m", \ " ? m",
\ " ? x", \ " ? x",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
" clean up " clean up
call s:close_windows('set sbr=') 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 " 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') 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! " make sure, cache is invalidated!
@ -148,43 +280,96 @@ function Test_breakindent08()
redraw! redraw!
set ts=4 set ts=4
redraw! redraw!
let lines=s:screen_lines(line('.'),10) let lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ^Iabcd", \ " 2 ^Iabcd",
\ "# opq", \ "# opq",
\ "# BCD", \ "# BCD",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n') 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 " 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') call s:test_windows('setl briopt=shift:1,sbr,min:0 nu nuw=4 sbr=# list')
let lines=s:screen_lines(line('.'),10) let lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ^Iabcd", \ " 2 ^Iabcd",
\ " # opq", \ " # opq",
\ " # BCD", \ " # BCD",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set sbr=') 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=# " 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') call s:test_windows('setl briopt=shift:1,min:0 nu nuw=4 sbr=# list')
let lines=s:screen_lines(line('.'),10) let lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ^Iabcd", \ " 2 ^Iabcd",
\ " #op", \ " #op",
\ " #AB", \ " #AB",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set sbr=') 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=~ " breakindent set, Number set sbr=~
call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0') call s:test_windows('setl cpo+=n sbr=~ nu nuw=4 nolist briopt=sbr,min:0')
" make sure, cache is invalidated! " make sure, cache is invalidated!
@ -192,41 +377,91 @@ function Test_breakindent10()
redraw! redraw!
set ts=4 set ts=4
redraw! redraw!
let lines=s:screen_lines(line('.'),10) let lines = s:screen_lines(line('.'),10)
let expect=[ let expect = [
\ " 2 ab", \ " 2 ab",
\ "~ mn", \ "~ mn",
\ "~ yz", \ "~ yz",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set sbr= cpo-=n') 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() " test strdisplaywidth()
call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4') call s:test_windows('setl cpo-=n sbr=>> nu nuw=4 nolist briopt= ts=4')
let text=getline(2) let text=getline(2)
let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times let width = strlen(text[1:])+indent(2)+strlen(&sbr)*3 " text wraps 3 times
call assert_equal(width, strdisplaywidth(text)) call assert_equal(width, strdisplaywidth(text))
call s:close_windows('set sbr=') 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 " test breakindent with long indent
let s:input="\t\t\t\t\t{" 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:>-') 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 lines = s:screen_lines(2,16)
let expect=[ let expect = [
\ " 2 >--->--->--->", \ " 2 >--->--->--->",
\ " ---{ ", \ " ---{ ",
\ "~ ", \ "~ ",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows('set nuw=4 listchars=') call s:close_windows('set nuw=4 listchars=')
endfunction endfunc
function Test_breakindent13() func Test_breakindent12_vartabs()
let s:input="" 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') call s:test_windows('setl breakindent briopt=min:10 ts=8')
vert resize 20 vert resize 20
call setline(1, [" a\tb\tc\td\te", " z y x w v"]) 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('d', @a)
call assert_equal('w', @b) call assert_equal('w', @b)
call s:close_windows() call s:close_windows()
endfunction endfunc
function Test_breakindent14() func Test_breakindent13_vartabs()
let s:input="" 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') call s:test_windows('setl breakindent briopt= ts=8')
vert resize 30 vert resize 30
norm! 3a1234567890 norm! 3a1234567890
norm! a abcde norm! a abcde
exec "norm! 0\<C-V>tex" exec "norm! 0\<C-V>tex"
let lines=s:screen_lines(line('.'),8) let lines = s:screen_lines(line('.'),8)
let expect=[ let expect = [
\ "e ", \ "e ",
\ "~ ", \ "~ ",
\ "~ ", \ "~ ",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() call s:close_windows()
endfunction endfunc
function Test_breakindent15() func Test_breakindent14_vartabs()
let s:input="" 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') call s:test_windows('setl breakindent briopt= ts=8 sw=8')
vert resize 30 vert resize 30
norm! 4a1234567890 norm! 4a1234567890
exe "normal! >>\<C-V>3f0x" exe "normal! >>\<C-V>3f0x"
let lines=s:screen_lines(line('.'),20) let lines = s:screen_lines(line('.'),20)
let expect=[ let expect = [
\ " 1234567890 ", \ " 1234567890 ",
\ "~ ", \ "~ ",
\ "~ ", \ "~ ",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() 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. " 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 s:test_windows('setl breakindent briopt=min:0 ts=4')
call setline(1, "\t".repeat("1234567890", 10)) call setline(1, "\t".repeat("1234567890", 10))
resize 6 resize 6
norm! 1gg$ norm! 1gg$
redraw! redraw!
let lines=s:screen_lines(1,10) let lines = s:screen_lines(1,10)
let expect=[ let expect = [
\ " 789012", \ " 789012",
\ " 345678", \ " 345678",
\ " 901234", \ " 901234",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
let lines=s:screen_lines(4,10) let lines = s:screen_lines(4,10)
let expect=[ let expect = [
\ " 567890", \ " 567890",
\ " 123456", \ " 123456",
\ " 7890 ", \ " 7890 ",
\ ] \ ]
call s:compare_lines(expect, lines) call s:compare_lines(expect, lines)
call s:close_windows() 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() func Test_breakindent17_vartabs()
if !has("vartabs") if !has("vartabs")

View File

@ -1,3 +1,4 @@
" Various tests for inserting a Tab.
" Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set. " Tests for "r<Tab>" with 'smarttab' and 'expandtab' set/not set.
" Also test that dv_ works correctly " Also test that dv_ works correctly
@ -43,3 +44,47 @@ func Test_smarttab()
enew! enew!
set expandtab& smartindent& copyindent& ts& sw& sts& set expandtab& smartindent& copyindent& ts& sw& sts&
endfunc endfunc
func Test_softtabstop()
new
set sts=0 sw=0
exe "normal ix\<Tab>x\<Esc>"
call assert_equal("x\tx", getline(1))
call setline(1, '')
set sts=4
exe "normal ix\<Tab>x\<Esc>"
call assert_equal("x x", getline(1))
call setline(1, '')
set sts=-1 sw=4
exe "normal ix\<Tab>x\<Esc>"
call assert_equal("x x", getline(1))
call setline(1, 'x ')
set sts=0 sw=0 backspace=start
exe "normal A\<BS>x\<Esc>"
call assert_equal("x x", getline(1))
call setline(1, 'x ')
set sts=4
exe "normal A\<BS>x\<Esc>"
call assert_equal("x x", getline(1))
call setline(1, 'x ')
set sts=-1 sw=4
exe "normal A\<BS>x\<Esc>"
call assert_equal("x x", getline(1))
call setline(1, 'x')
set sts=-1 sw=0 smarttab
exe "normal I\<Tab>\<Esc>"
call assert_equal("\tx", getline(1))
call setline(1, 'x')
exe "normal I\<Tab>\<BS>\<Esc>"
call assert_equal("x", getline(1))
set sts=0 sw=0 backspace& nosmarttab
bwipe!
endfunc

View File

@ -0,0 +1,374 @@
" Test for variable tabstops
if !has("vartabs")
finish
endif
source view_util.vim
func s:compare_lines(expect, actual)
call assert_equal(join(a:expect, "\n"), join(a:actual, "\n"))
endfunc
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
func Test_vartabs_linebreak()
if winwidth(0) < 40
return
endif
new
40vnew
%d
setl linebreak vartabstop=10,20,30,40
call setline(1, "\tx\tx\tx\tx")
let expect = [' x ',
\ 'x x ',
\ 'x ']
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect, lines)
setl list listchars=tab:>-
let expect = ['>---------x>------------------ ',
\ 'x>------------------x>------------------',
\ 'x ']
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect, lines)
setl linebreak vartabstop=40
let expect = ['>---------------------------------------',
\ 'x>--------------------------------------',
\ 'x>--------------------------------------',
\ 'x>--------------------------------------',
\ 'x ']
let lines = ScreenLines([1, 5], winwidth(0))
call s:compare_lines(expect, lines)
" cleanup
bw!
bw!
set nolist listchars&vim
endfunc
func Test_vartabs_shiftwidth()
"return
if winwidth(0) < 40
return
endif
new
40vnew
%d
" setl varsofttabstop=10,20,30,40
setl shiftwidth=0 vartabstop=10,20,30,40
call setline(1, "x")
" Check without any change.
let expect = ['x ']
let lines = ScreenLines(1, winwidth(0))
call s:compare_lines(expect, lines)
" Test 1:
" shiftwidth depends on the indent, first check with cursor at the end of the
" line (which is the same as the start of the line, since there is only one
" character).
norm! $>>
let expect1 = [' x ']
let lines = ScreenLines(1, winwidth(0))
call s:compare_lines(expect1, lines)
call assert_equal(10, shiftwidth())
call assert_equal(10, shiftwidth(1))
call assert_equal(20, shiftwidth(virtcol('.')))
norm! $>>
let expect2 = [' x ', '~ ']
let lines = ScreenLines([1, 2], winwidth(0))
call s:compare_lines(expect2, lines)
call assert_equal(20, shiftwidth(virtcol('.')-2))
call assert_equal(30, shiftwidth(virtcol('.')))
norm! $>>
let expect3 = [' ', ' x ', '~ ']
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect3, lines)
call assert_equal(30, shiftwidth(virtcol('.')-2))
call assert_equal(40, shiftwidth(virtcol('.')))
norm! $>>
let expect4 = [' ', ' ', ' x ']
let lines = ScreenLines([1, 3], winwidth(0))
call assert_equal(40, shiftwidth(virtcol('.')))
call s:compare_lines(expect4, lines)
" Test 2: Put the cursor at the first column, result should be the same
call setline(1, "x")
norm! 0>>
let lines = ScreenLines(1, winwidth(0))
call s:compare_lines(expect1, lines)
norm! 0>>
let lines = ScreenLines([1, 2], winwidth(0))
call s:compare_lines(expect2, lines)
norm! 0>>
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect3, lines)
norm! 0>>
let lines = ScreenLines([1, 3], winwidth(0))
call s:compare_lines(expect4, lines)
" cleanup
bw!
bw!
endfunc
func Test_vartabs_failures()
call assert_fails('set vts=8,')
call assert_fails('set vsts=8,')
call assert_fails('set vts=8,,8')
call assert_fails('set vsts=8,,8')
call assert_fails('set vts=8,,8,')
call assert_fails('set vsts=8,,8,')
call assert_fails('set vts=,8')
call assert_fails('set vsts=,8')
endfunc

View File

@ -24,8 +24,6 @@ describe('Ex cmds', function()
pcall_err(command, ':menu 9999999999999999999999999999999999999999')) pcall_err(command, ':menu 9999999999999999999999999999999999999999'))
eq('Vim(bdelete):E939: Positive count required', eq('Vim(bdelete):E939: Positive count required',
pcall_err(command, ':bdelete 9999999999999999999999999999999999999999')) pcall_err(command, ':bdelete 9999999999999999999999999999999999999999'))
eq('Vim(retab):E487: Argument must be positive',
pcall_err(command, ':retab 9999999999999999999999999999999999999999'))
assert_alive() assert_alive()
end) end)
end) end)