# HG changeset patch # User Bram Moolenaar # Date 1542852910 -3600 # Node ID 3a94f7918980c69bd30baba11088ddf518753967 # Parent 0572862c3162e8391377a7b94a4a0acfa0c6bfbc patch 8.1.0542: shiftwidth() does not take 'vartabstop' into account commit https://github.com/vim/vim/commit/f951416a8396a54bbbe21de1a8b16716428549f2 Author: Bram Moolenaar Date: Thu Nov 22 03:08:29 2018 +0100 patch 8.1.0542: shiftwidth() does not take 'vartabstop' into account Problem: shiftwidth() does not take 'vartabstop' into account. Solution: Use the cursor position or a position explicitly passed. Also make >> and << work better with 'vartabstop'. (Christian Brabandt) diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -476,6 +476,10 @@ SHIFTING LINES LEFT OR RIGHT *shift-l *<* <{motion} Shift {motion} lines one 'shiftwidth' leftwards. + If the 'vartabstop' feature is enabled, and 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. @@ -487,6 +491,10 @@ SHIFTING LINES LEFT OR RIGHT *shift-l *>* >{motion} Shift {motion} lines one 'shiftwidth' rightwards. + If the 'vartabstop' feature is enabled, and 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. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2308,7 +2308,6 @@ perleval({expr}) any evaluate |Perl| ex pow({x}, {y}) Float {x} to the power of {y} prevnonblank({lnum}) Number line nr of non-blank line <= {lnum} 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_setinterrupt({buf}, {text}) none set prompt interrupt function prompt_setprompt({buf}, {text}) none set prompt text @@ -2386,7 +2385,7 @@ sha256({string}) String SHA256 checksum shellescape({string} [, {special}]) String escape {string} for use as shell command argument -shiftwidth([{list}]) Number effective value of 'shiftwidth' +shiftwidth([{col}]) Number effective value of 'shiftwidth' simplify({filename}) String simplify filename as much as possible sin({expr}) Float sine of {expr} sinh({expr}) Float hyperbolic sine of {expr} @@ -7639,19 +7638,17 @@ shellescape({string} [, {special}]) *s < See also |::S|. -shiftwidth([{list}]) *shiftwidth()* +shiftwidth([{col}]) *shiftwidth()* Returns the effective value of 'shiftwidth'. This is the 'shiftwidth' value unless it is zero, in which case it is the 'tabstop' value. This function was introduced with patch - 7.3.694 in 2012, everybody should have it by now. - - When there is one argument {list} this is used as position - |List| for which to return the 'shiftwidth' value (actually - only the column number is relevant). This matters for the - 'vartabstop' feature. For the {list} arguments see |cursor()| - function. If the 'vartabstop' setting is enabled and no - {list} argument is given, the current cursor position is - taken into account. + 7.3.694 in 2012, everybody should have it by now (however it + did not allow for the optional {col} argument until 8.1.542). + + 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 the 'vartabstop' setting is enabled and + no {col} argument is given, column 1 will be assumed. simplify({filename}) *simplify()* diff --git a/src/edit.c b/src/edit.c --- a/src/edit.c +++ b/src/edit.c @@ -262,7 +262,6 @@ static int ins_ctrl_ey(int tc); #ifdef FEAT_SMARTINDENT static void ins_try_si(int c); #endif -static colnr_T get_nolist_virtcol(void); #if defined(FEAT_EVAL) static char_u *do_insert_char_pre(int c); #endif @@ -10681,9 +10680,14 @@ ins_try_si(int c) * Get the value that w_virtcol would have when 'list' is off. * Unless 'cpo' contains the 'L' flag. */ - static colnr_T + colnr_T get_nolist_virtcol(void) { + // 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); validate_virtcol(); diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -835,7 +835,7 @@ static struct fst {"sha256", 1, 1, f_sha256}, #endif {"shellescape", 1, 2, f_shellescape}, - {"shiftwidth", 0, 0, f_shiftwidth}, + {"shiftwidth", 0, 1, f_shiftwidth}, {"simplify", 1, 1, f_simplify}, #ifdef FEAT_FLOAT {"sin", 1, 1, f_sin}, @@ -11241,6 +11241,21 @@ f_shellescape(typval_T *argvars, typval_ static void f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv) { + rettv->vval.v_number = 0; + + if (argvars[0].v_type != VAR_UNKNOWN) + { + long col; + + col = (long)get_tv_number_chk(argvars, NULL); + if (col < 0) + return; // type error; errmsg already given +#ifdef FEAT_VARTABS + rettv->vval.v_number = get_sw_value_col(curbuf, col); + return; +#endif + } + rettv->vval.v_number = get_sw_value(curbuf); } diff --git a/src/normal.c b/src/normal.c --- a/src/normal.c +++ b/src/normal.c @@ -8143,6 +8143,7 @@ nv_g_cmd(cmdarg_T *cap) do i = gchar_cursor(); while (VIM_ISWHITE(i) && oneright() == OK); + curwin->w_valid &= ~VALID_WCOL; } curwin->w_set_curswant = TRUE; break; diff --git a/src/ops.c b/src/ops.c --- a/src/ops.c +++ b/src/ops.c @@ -334,7 +334,7 @@ shift_line( { int count; int i, j; - int p_sw = (int)get_sw_value(curbuf); + int p_sw = (int)get_sw_value_indent(curbuf); count = get_indent(); /* get current indent */ @@ -386,7 +386,7 @@ shift_block(oparg_T *oap, int amount) int total; char_u *newp, *oldp; int oldcol = curwin->w_cursor.col; - int p_sw = (int)get_sw_value(curbuf); + int p_sw = (int)get_sw_value_indent(curbuf); #ifdef FEAT_VARTABS int *p_vts = curbuf->b_p_vts_array; #endif diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -13113,7 +13113,48 @@ tabstop_first(int *ts) long get_sw_value(buf_T *buf) { - return buf->b_p_sw ? buf->b_p_sw : buf->b_p_ts; + return get_sw_value_col(buf, 0); +} + +/* + * 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 = 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 UNUSED) +{ + return buf->b_p_sw ? buf->b_p_sw : + #ifdef FEAT_VARTABS + tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array); + #else + buf->b_p_ts; + #endif } /* diff --git a/src/proto/edit.pro b/src/proto/edit.pro --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -46,4 +46,5 @@ int bracketed_paste(paste_mode_T mode, i void ins_scroll(void); void ins_horscroll(void); int ins_copychar(linenr_T lnum); +colnr_T get_nolist_virtcol(void); /* vim: set ft=c : */ diff --git a/src/proto/option.pro b/src/proto/option.pro --- a/src/proto/option.pro +++ b/src/proto/option.pro @@ -72,6 +72,9 @@ int *tabstop_copy(int *oldts); int tabstop_count(int *ts); int tabstop_first(int *ts); long get_sw_value(buf_T *buf); +long get_sw_value_indent(buf_T *buf); +long get_sw_value_pos(buf_T *buf, pos_T *pos); +long get_sw_value_col(buf_T *buf, colnr_T col); long get_sts_value(void); void find_mps_values(int *initc, int *findc, int *backwards, int switchit); unsigned int get_bkc_value(buf_T *buf); diff --git a/src/testdir/test_vartabs.vim b/src/testdir/test_vartabs.vim --- a/src/testdir/test_vartabs.vim +++ b/src/testdir/test_vartabs.vim @@ -297,6 +297,71 @@ func Test_vartabs_linebreak() 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,') diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -793,6 +793,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 542, +/**/ 541, /**/ 540,