aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVVKot <volodymyr.kot.ua@gmail.com>2021-02-13 19:06:37 +0000
committerVVKot <volodymyr.kot.ua@gmail.com>2021-03-28 08:37:01 +0100
commit6752ac49682d63dfc7960e518ecbd6517c88392d (patch)
treeb0e4a27d32f23ea52f11ae1a6a4f24617f79a5e6 /src
parentb79596eb5e942a299aa021a0f9a3f2db909294da (diff)
downloadrneovim-6752ac49682d63dfc7960e518ecbd6517c88392d.tar.gz
rneovim-6752ac49682d63dfc7960e518ecbd6517c88392d.tar.bz2
rneovim-6752ac49682d63dfc7960e518ecbd6517c88392d.zip
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) https://github.com/vim/vim/commit/04958cbaf25eea27eceedaa987adfb354ad5f7fd
Diffstat (limited to 'src')
-rw-r--r--src/nvim/buffer.c23
-rw-r--r--src/nvim/buffer_defs.h5
-rw-r--r--src/nvim/change.c40
-rw-r--r--src/nvim/charset.c11
-rw-r--r--src/nvim/edit.c77
-rw-r--r--src/nvim/eval/funcs.c1
-rw-r--r--src/nvim/ex_cmds.c80
-rw-r--r--src/nvim/hardcopy.c7
-rw-r--r--src/nvim/indent.c94
-rw-r--r--src/nvim/message.c4
-rw-r--r--src/nvim/ops.c25
-rw-r--r--src/nvim/option.c393
-rw-r--r--src/nvim/option_defs.h2
-rw-r--r--src/nvim/options.lua17
-rw-r--r--src/nvim/screen.c5
-rw-r--r--src/nvim/testdir/test_breakindent.vim609
-rw-r--r--src/nvim/testdir/test_vartabs.vim257
17 files changed, 1403 insertions, 247 deletions
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c42a0e2dad..1daedb90b5 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -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
{
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index e8038e7281..a4105e2796 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -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
diff --git a/src/nvim/change.c b/src/nvim/change.c
index 0f5081c94c..38bd591eca 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -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
diff --git a/src/nvim/charset.c b/src/nvim/charset.c
index 5ad1fe0dfd..e2d844a351 100644
--- a/src/nvim/charset.c
+++ b/src/nvim/charset.c
@@ -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.
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index b5d5d67e90..110494bb5c 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -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;
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 650b4e3882..eeaaf7dbd1 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -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",
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 854faf5377..d679a3c8f8 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -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();
diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c
index 4ec949759c..abba5425e7 100644
--- a/src/nvim/hardcopy.c
+++ b/src/nvim/hardcopy.c
@@ -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);
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index fae971b3b3..92ecbe160b 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -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;
diff --git a/src/nvim/message.c b/src/nvim/message.c
index 71cb345878..cdac4b602a 100644
--- a/src/nvim/message.c
+++ b/src/nvim/message.c
@@ -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 = ' ';
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 0ff427c261..ffa3e3d55f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -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;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 949558894f..ad4d12ff7b 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -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