diff options
| author | VVKot <volodymyr.kot.ua@gmail.com> | 2021-02-13 19:06:37 +0000 |
|---|---|---|
| committer | VVKot <volodymyr.kot.ua@gmail.com> | 2021-03-28 08:37:01 +0100 |
| commit | 6752ac49682d63dfc7960e518ecbd6517c88392d (patch) | |
| tree | b0e4a27d32f23ea52f11ae1a6a4f24617f79a5e6 /src | |
| parent | b79596eb5e942a299aa021a0f9a3f2db909294da (diff) | |
| download | rneovim-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.c | 23 | ||||
| -rw-r--r-- | src/nvim/buffer_defs.h | 5 | ||||
| -rw-r--r-- | src/nvim/change.c | 40 | ||||
| -rw-r--r-- | src/nvim/charset.c | 11 | ||||
| -rw-r--r-- | src/nvim/edit.c | 77 | ||||
| -rw-r--r-- | src/nvim/eval/funcs.c | 1 | ||||
| -rw-r--r-- | src/nvim/ex_cmds.c | 80 | ||||
| -rw-r--r-- | src/nvim/hardcopy.c | 7 | ||||
| -rw-r--r-- | src/nvim/indent.c | 94 | ||||
| -rw-r--r-- | src/nvim/message.c | 4 | ||||
| -rw-r--r-- | src/nvim/ops.c | 25 | ||||
| -rw-r--r-- | src/nvim/option.c | 393 | ||||
| -rw-r--r-- | src/nvim/option_defs.h | 2 | ||||
| -rw-r--r-- | src/nvim/options.lua | 17 | ||||
| -rw-r--r-- | src/nvim/screen.c | 5 | ||||
| -rw-r--r-- | src/nvim/testdir/test_breakindent.vim | 609 | ||||
| -rw-r--r-- | src/nvim/testdir/test_vartabs.vim | 257 |
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 |