diff options
-rw-r--r-- | runtime/doc/change.txt | 6 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 5 | ||||
-rw-r--r-- | src/nvim/edit.c | 10 | ||||
-rw-r--r-- | src/nvim/eval.lua | 2 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 12 | ||||
-rw-r--r-- | src/nvim/normal.c | 5 | ||||
-rw-r--r-- | src/nvim/ops.c | 6 | ||||
-rw-r--r-- | src/nvim/option.c | 30 | ||||
-rw-r--r-- | src/nvim/testdir/test_vartabs.vim | 65 |
9 files changed, 129 insertions, 12 deletions
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index ed40642d0c..310d244fbc 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -445,6 +445,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right* *<* <{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. @@ -455,6 +458,9 @@ SHIFTING LINES LEFT OR RIGHT *shift-left-right* *>* >{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. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 164c6fb8e0..2911224de5 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2308,7 +2308,6 @@ perleval({expr}) any evaluate |perl| expression 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 @@ -2393,7 +2392,7 @@ sha256({string}) String SHA256 checksum of {string} shellescape({string} [, {special}]) String escape {string} for use as shell 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_getdefined([{name}]) List get a list of defined signs sign_getplaced([{expr} [, {dict}]]) @@ -7899,7 +7898,7 @@ shellescape({string} [, {special}]) *shellescape()* < See also |::S|. -shiftwidth() *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. To be backwards compatible in indent diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 90b5cfd8aa..20f31a478d 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -9156,10 +9156,16 @@ static void 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 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); + } validate_virtcol(); return curwin->w_virtcol; } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index d1a3ae3ff8..72168060cc 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -309,7 +309,7 @@ return { setwinvar={args=3}, sha256={args=1}, shellescape={args={1, 2}}, - shiftwidth={}, + shiftwidth={args={0, 1}}, sign_define={args={1, 2}}, sign_getdefined={args={0, 1}}, sign_getplaced={args={0, 2}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index eeaaf7dbd1..0f2e904b1b 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -8849,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) { + 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); } diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 0b4e2e1f23..3587b12277 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -6777,9 +6777,10 @@ static void nv_g_cmd(cmdarg_T *cap) } coladvance((colnr_T)i); if (flag) { - do + do { i = gchar_cursor(); - while (ascii_iswhite(i) && oneright()); + } while (ascii_iswhite(i) && oneright()); + curwin->w_valid &= ~VALID_WCOL; } curwin->w_set_curswant = true; break; diff --git a/src/nvim/ops.c b/src/nvim/ops.c index ffa3e3d55f..e5de57ceaa 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -288,7 +288,7 @@ void shift_line( { int count; 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 @@ -332,9 +332,9 @@ static void shift_block(oparg_T *oap, int amount) const int oldstate = State; char_u *newp; const int oldcol = curwin->w_cursor.col; - const int p_sw = get_sw_value(curbuf); - const long p_ts = curbuf->b_p_ts; + int p_sw = (int)get_sw_value_indent(curbuf); long *p_vts = curbuf->b_p_vts_array; + const long p_ts = curbuf->b_p_ts; struct block_def bd; int incr; int i = 0, j = 0; diff --git a/src/nvim/option.c b/src/nvim/option.c index 67740e8d9f..7d21086fd4 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -7342,11 +7342,39 @@ int tabstop_first(long *ts) /// 'tabstop' value when 'shiftwidth' is zero. 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); 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, /// using the shiftwidth value when 'softtabstop' is negative. int get_sts_value(void) diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index c8470952d1..81e81b7fbe 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/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,') |