aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/edit.c3
-rw-r--r--src/nvim/eval/funcs.c2
-rw-r--r--src/nvim/indent.c47
-rw-r--r--src/nvim/ops.c4
-rw-r--r--test/old/testdir/test_vartabs.vim59
5 files changed, 96 insertions, 19 deletions
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 146e95df87..595b4da589 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -474,7 +474,8 @@ static int insert_check(VimState *state)
if (curwin->w_wcol < s->mincol - tabstop_at(get_nolist_virtcol(),
curbuf->b_p_ts,
- curbuf->b_p_vts_array)
+ curbuf->b_p_vts_array,
+ false)
&& curwin->w_wrow == curwin->w_height_inner - 1 - get_scrolloff_value(curwin)
&& (curwin->w_cursor.lnum != curwin->w_topline
|| curwin->w_topfill > 0)) {
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 0165e4af60..e704135366 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -8302,7 +8302,7 @@ static void f_shiftwidth(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
if (col < 0) {
return; // type error; errmsg already given
}
- rettv->vval.v_number = get_sw_value_col(curbuf, col);
+ rettv->vval.v_number = get_sw_value_col(curbuf, col, false);
return;
}
rettv->vval.v_number = get_sw_value(curbuf);
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
index 69c95e9038..66cb443e4d 100644
--- a/src/nvim/indent.c
+++ b/src/nvim/indent.c
@@ -147,25 +147,42 @@ int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts)
}
/// Find the size of the tab that covers a particular column.
-int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts)
+///
+/// If this is being called as part of a shift operation, col is not the cursor
+/// column but is the column number to the left of the first non-whitespace
+/// character in the line. If the shift is to the left (left == true), then
+/// return the size of the tab interval to the left of the column.
+int tabstop_at(colnr_T col, OptInt ts, const colnr_T *vts, bool left)
{
- colnr_T tabcol = 0;
- int t;
- int tab_size = 0;
-
if (vts == NULL || vts[0] == 0) {
return (int)ts;
}
- const int tabcount = vts[0];
+ colnr_T tabcol = 0; // Column of the tab stop under consideration.
+ int t; // Tabstop index in the list of variable tab stops.
+ int tab_size = 0; // Size of the tab stop interval to the right or left of the col.
+ const int tabcount // Number of tab stops in the list of variable tab stops.
+ = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
- tab_size = vts[t];
+ // If shifting left (left == true), and if the column to the left of
+ // the first first non-blank character (col) in the line is
+ // already to the left of the first tabstop, set the shift amount
+ // (tab_size) to just enough to shift the line to the left margin.
+ // The value doesn't seem to matter as long as it is at least that
+ // distance.
+ if (left && (t == 1)) {
+ tab_size = col;
+ } else {
+ tab_size = vts[t - (left ? 1 : 0)];
+ }
break;
}
}
- if (t > tabcount) {
+ if (t > tabcount) { // If the value of the index t is beyond the
+ // end of the list, use the tab stop value at
+ // the end of the list.
tab_size = vts[tabcount];
}
@@ -312,35 +329,35 @@ int tabstop_first(colnr_T *ts)
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)
{
- int result = get_sw_value_col(buf, 0);
+ int result = get_sw_value_col(buf, 0, false);
return result;
}
/// Idem, using "pos".
-int get_sw_value_pos(buf_T *buf, pos_T *pos)
+int get_sw_value_pos(buf_T *buf, pos_T *pos, bool left)
{
pos_T save_cursor = curwin->w_cursor;
curwin->w_cursor = *pos;
- int sw_value = get_sw_value_col(buf, get_nolist_virtcol());
+ int sw_value = get_sw_value_col(buf, get_nolist_virtcol(), left);
curwin->w_cursor = save_cursor;
return sw_value;
}
/// Idem, using the first non-black in the current line.
-int get_sw_value_indent(buf_T *buf)
+int get_sw_value_indent(buf_T *buf, bool left)
{
pos_T pos = curwin->w_cursor;
pos.col = (colnr_T)getwhitecols_curline();
- return get_sw_value_pos(buf, &pos);
+ return get_sw_value_pos(buf, &pos, left);
}
/// Idem, using virtual column "col".
-int get_sw_value_col(buf_T *buf, colnr_T col)
+int get_sw_value_col(buf_T *buf, colnr_T col, bool left)
{
return buf->b_p_sw ? (int)buf->b_p_sw
- : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
+ : tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array, left);
}
/// Return the effective softtabstop value for the current buffer,
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 47d302df43..8ae1d70882 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -282,7 +282,7 @@ void op_shift(oparg_T *oap, bool curs_top, int amount)
/// @param call_changed_bytes call changed_bytes()
void shift_line(bool left, bool round, int amount, int call_changed_bytes)
{
- const int sw_val = get_sw_value_indent(curbuf);
+ const int sw_val = get_sw_value_indent(curbuf, left);
int count = get_indent(); // get current indent
@@ -328,7 +328,7 @@ static void shift_block(oparg_T *oap, int amount)
const int oldstate = State;
char *newp;
const int oldcol = curwin->w_cursor.col;
- const int sw_val = get_sw_value_indent(curbuf);
+ const int sw_val = get_sw_value_indent(curbuf, left);
const int ts_val = (int)curbuf->b_p_ts;
struct block_def bd;
int incr;
diff --git a/test/old/testdir/test_vartabs.vim b/test/old/testdir/test_vartabs.vim
index 274531fdc1..82c3a513f9 100644
--- a/test/old/testdir/test_vartabs.vim
+++ b/test/old/testdir/test_vartabs.vim
@@ -458,5 +458,64 @@ func Test_vartabstop_latin1()
set nocompatible linebreak& list& revins& smarttab& vartabstop&
endfunc
+" Verify that right-shifting and left-shifting adjust lines to the proper
+" tabstops.
+func Test_vartabstop_shift_right_left()
+ new
+ set expandtab
+ set shiftwidth=0
+ set vartabstop=17,11,7
+ exe "norm! aword"
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to first tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to second tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to third tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to fourth tabstop, repeating the third shift width.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the third tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the second tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the first tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the left margin.
+ norm! <<
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ " Shift again back to the left margin.
+ norm! <<
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ bwipeout!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab