On Do, 15 Nov 2018, Christian Brabandt wrote:

> Ah, I have seen that error before and thought I fixed. That usually 
> happened when the buffer was not loaded. I'll have a closer look later.
> I suppose that happen when running test86?

Here is an updated patch, that fixes the crash, and contains updated 
documentation and a test. 

Note, I changed the shiftwidth() function to accept an optional column 
number instead of a position List. 

However, I am not sure it makes sense. When developing the test, I found 
it actually confusing, that e.g. `>` depends on the actual cursor 
position. So using `norm! 0>>` will behave differently than `norm! $>>`.

So it might be a better idea to simply document, that the shiftwidth() 
function will always return the `tabstop` setting instead of using 
whatever vartab setting is in effect for the current column (and also 
auto indenting will not respect the vartab feature, when the shiftwidth 
setting is zero).

So here is an example, consider this example:

:set sw=0 vartab=10,20,30,40

and the following line:

x

On hitting >>, the line will be shifted like this:
          x
                              x
                                                            x
however, if you leave the cursor at the start of the line, the indent 
will rather look like this (e.g. using norm! 0>>
          x
                    x
                              x


It might be confusing to the user to understand, why it was shifted one 
way and not the other. So I am not sure, if this change is actually 
justified.

Best,
Christian
-- 
Zur Ehe gehört nicht bloß, daß man das Mädchen, sondern auch, daß man
sich prüfe - ob nämlich 2 Vortreffliche dennoch sich einander nicht
fügen.
                -- Jean Paul

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
From 18a9d5b8c8841dd7bfb491e36bbf69f98a199f4b Mon Sep 17 00:00:00 2001
From: Christian Brabandt <c...@256bit.org>
Date: Wed, 14 Nov 2018 16:37:23 +0100
Subject: [PATCH] Return correct tabstop setting for 'sw'=0 and vartab

When the vartab feature was merged into Vim with v8.1.0105, there was
one feature forgotten. To return the actual tabstop value in effect when
the shiftwidth setting is zero (|'shiftwidth'|)

So fix this and make get_sw_value() return the actual vartabstop setting
in effect, if it is enabled. This basically means, that get_sw_value()
also needs to know the column number for which to return the correct
vartab stop value. Therefore, return the value from the current cursor
position.

This also means, that the `shiftwidth()` function needs to be extended
and returns the vartabstop value for a given position. So extend that
function to use an optional position parameter and add some documentation.

In addition, add a test to verify the feature.
---
 runtime/doc/change.txt       |  8 +++++
 runtime/doc/eval.txt         | 12 +++++--
 src/edit.c                   |  8 +++--
 src/evalfunc.c               | 16 ++++++++-
 src/normal.c                 |  1 +
 src/ops.c                    |  4 +--
 src/option.c                 | 25 +++++++++++++-
 src/proto/edit.pro           |  1 +
 src/proto/option.pro         |  2 ++
 src/testdir/test_vartabs.vim | 67 ++++++++++++++++++++++++++++++++++++
 10 files changed, 135 insertions(+), 9 deletions(-)

diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index d6b0242d2..22d616077 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -476,6 +476,10 @@ SHIFTING LINES LEFT OR RIGHT				*shift-left-right*
 							*<*
 <{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 determined by the current cursor column.
+
 							*<<*
 <<			Shift [count] lines one 'shiftwidth' leftwards.
 
@@ -487,6 +491,10 @@ SHIFTING LINES LEFT OR RIGHT				*shift-left-right*
 							*>*
  >{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 determined by the current cursor column.
+
 							*>>*
  >>			Shift [count] lines one 'shiftwidth' rightwards.
 
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index bb80a665c..b32889f13 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2386,7 +2386,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'
 simplify({filename})		String	simplify filename as much as possible
 sin({expr})			Float	sine of {expr}
 sinh({expr})			Float	hyperbolic sine of {expr}
@@ -7639,11 +7639,17 @@ 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.  This function was introduced with patch
-		7.3.694 in 2012, everybody should have it by now.
+		7.3.694 in 2012, everybody should have it by now (however it
+		did not allow for the optional {col} argument).
+
+		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
index 239881ee5..6b5bc0f63 100644
--- 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
index f55739ed5..46302a4f7 100644
--- 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,20 @@ f_shellescape(typval_T *argvars, typval_T *rettv)
     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
index a0683b207..5310824ee 100644
--- 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
index d6559a2b1..f15866f44 100644
--- 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_pos(curbuf, &curwin->w_cursor);
 
     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_pos(curbuf, &curwin->w_cursor);
 #ifdef FEAT_VARTABS
     int			*p_vts = curbuf->b_p_vts_array;
 #endif
diff --git a/src/option.c b/src/option.c
index a4a9c9711..290df9fec 100644
--- a/src/option.c
+++ b/src/option.c
@@ -13085,7 +13085,30 @@ 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);
+}
+
+    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;
+}
+
+    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
index 9ba71645b..768af3a48 100644
--- a/src/proto/edit.pro
+++ b/src/proto/edit.pro
@@ -46,4 +46,5 @@ int bracketed_paste(paste_mode_T mode, int drop, garray_T *gap);
 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
index 228036587..38a9e4f42 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -72,6 +72,8 @@ 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_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
index c8470952d..8499f3a5b 100644
--- a/src/testdir/test_vartabs.vim
+++ b/src/testdir/test_vartabs.vim
@@ -297,6 +297,73 @@ 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")
+
+  let expect = ['x                                       ']
+  let lines = ScreenLines(1, winwidth(0))
+  call s:compare_lines(expect, lines)
+  " Test 1:
+  " shiftwidth depends on where the cursor is, therefore put it at the end of
+  " the line
+  norm! $>>
+  let expect = ['          x                             ']
+  let lines = ScreenLines(1, winwidth(0))
+  call s:compare_lines(expect, lines)
+  call assert_equal(10, shiftwidth())
+  call assert_equal(10, shiftwidth(1))
+  call assert_equal(20, shiftwidth(virtcol('.')))
+  norm! $>>
+  let expect = ['                              x         ', '~                                       ']
+  let lines = ScreenLines([1,2], winwidth(0))
+  call s:compare_lines(expect, lines)
+  call assert_equal(20, shiftwidth(virtcol('.')-2))
+  call assert_equal(30, shiftwidth(virtcol('.')))
+  norm! $>>
+  let expect = ['                                        ', '                    x                   ', '~                                       ']
+  let lines = ScreenLines([1,3], winwidth(0))
+  call s:compare_lines(expect, lines)
+  call assert_equal(30, shiftwidth(virtcol('.')-2))
+  call assert_equal(40, shiftwidth(virtcol('.')))
+  norm! $>>
+  let expect = ['                                        ', '                                        ', '                    x                   ']
+  let lines = ScreenLines([1, 3], winwidth(0))
+  call assert_equal(40, shiftwidth(virtcol('.')))
+  call s:compare_lines(expect, lines)
+
+  " Test 2: Leave the cursor at the beginning of the line
+  call setline(1, "x")
+  norm! 0>>
+  let expect = ['          x                             ']
+  let lines = ScreenLines(1, winwidth(0))
+  call s:compare_lines(expect, lines)
+  norm! 0>>
+  let expect = ['                    x                   ', '~                                       ']
+  let lines = ScreenLines([1,2], winwidth(0))
+  call s:compare_lines(expect, lines)
+  norm! 0>>
+  let expect = ['                              x         ', '~                                       ', '~                                       ']
+  let lines = ScreenLines([1,3], winwidth(0))
+  call s:compare_lines(expect, lines)
+  norm! 0>>
+  let expect = ['                                        ', 'x                                       ']
+  let lines = ScreenLines([1, 2], winwidth(0))
+  call s:compare_lines(expect, lines)
+
+  " cleanup
+  bw!
+  bw!
+endfunc
+
 func Test_vartabs_failures()
   call assert_fails('set vts=8,')
   call assert_fails('set vsts=8,')
-- 
2.18.0

Raspunde prin e-mail lui