changeset 12323:4dba3e4f3b01 v8.0.1041

patch 8.0.1041: bogus characters when indenting during visual-block append commit https://github.com/vim/vim/commit/e2e69e48134cbfdedea7802810932f8592705024 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Sep 2 20:30:35 2017 +0200 patch 8.0.1041: bogus characters when indenting during visual-block append Problem: Bogus characters appear when indenting kicks in while doing a visual-block append. Solution: Recompute when indenting is done. (Christian Brabandt)
author Christian Brabandt <cb@256bit.org>
date Sat, 02 Sep 2017 20:45:04 +0200
parents 8efa25a05876
children fa16821a3a90
files runtime/doc/visual.txt src/charset.c src/edit.c src/misc1.c src/ops.c src/proto/charset.pro src/proto/misc1.pro src/screen.c src/spell.c src/testdir/test_cindent.vim src/version.c
diffstat 11 files changed, 70 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/visual.txt
+++ b/runtime/doc/visual.txt
@@ -314,8 +314,8 @@ Visual-block Insert						*v_b_I*
 With a blockwise selection, I{string}<ESC> will insert {string} at the start
 of block on every line of the block, provided that the line extends into the
 block.  Thus lines that are short will remain unmodified.  TABs are split to
-retain visual columns.
-See |v_b_I_example|.
+retain visual columns.  Works only for adding text to a line, not for
+deletions.  See |v_b_I_example|.
 
 Visual-block Append						*v_b_A*
 With a blockwise selection, A{string}<ESC> will append {string} to the end of
@@ -331,6 +331,7 @@ See |v_b_A_example|.
 Note: "I" and "A" behave differently for lines that don't extend into the
 selected block.  This was done intentionally, so that you can do it the way
 you want.
+Works only for adding text to a line, not for deletions.
 
 Visual-block change						*v_b_c*
 All selected text in the block will be replaced by the same text string.  When
--- a/src/charset.c
+++ b/src/charset.c
@@ -1536,6 +1536,22 @@ skipwhite(char_u *q)
 }
 
 /*
+ * getwhitecols: return the number of whitespace
+ * columns (bytes) at the start of a given line
+ */
+    int
+getwhitecols_curline()
+{
+    return getwhitecols(ml_get_curline());
+}
+
+    int
+getwhitecols(char_u *p)
+{
+    return skipwhite(p) - p;
+}
+
+/*
  * skip over digits
  */
     char_u *
--- a/src/edit.c
+++ b/src/edit.c
@@ -5182,7 +5182,7 @@ ins_complete(int c, int enable_pum)
 		     * first non_blank in the line, if it is not a wordchar
 		     * include it to get a better pattern, but then we don't
 		     * want the "\\<" prefix, check it bellow */
-		    compl_col = (colnr_T)(skipwhite(line) - line);
+		    compl_col = (colnr_T)getwhitecols(line);
 		    compl_startpos.col = compl_col;
 		    compl_startpos.lnum = curwin->w_cursor.lnum;
 		    compl_cont_status &= ~CONT_SOL;   /* clear SOL if present */
@@ -5348,7 +5348,7 @@ ins_complete(int c, int enable_pum)
 	}
 	else if (CTRL_X_MODE_LINE_OR_EVAL(ctrl_x_mode))
 	{
-	    compl_col = (colnr_T)(skipwhite(line) - line);
+	    compl_col = (colnr_T)getwhitecols(line);
 	    compl_length = (int)curs_col - (int)compl_col;
 	    if (compl_length < 0)	/* cursor in indent: empty pattern */
 		compl_length = 0;
@@ -8208,8 +8208,7 @@ in_cinkeys(
 		{
 		    /* "0=word": Check if there are only blanks before the
 		     * word. */
-		    line = ml_get_curline();
-		    if ((int)(skipwhite(line) - line) !=
+		    if (getwhitecols(line) !=
 				     (int)(curwin->w_cursor.col - (p - look)))
 			match = FALSE;
 		}
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -1589,8 +1589,7 @@ open_line(
 	    && curbuf->b_p_ai)
     {
 	fixthisline(get_lisp_indent);
-	p = ml_get_curline();
-	ai_col = (colnr_T)(skipwhite(p) - p);
+	ai_col = (colnr_T)getwhitecols_curline();
     }
 #endif
 #ifdef FEAT_CINDENT
@@ -1608,8 +1607,7 @@ open_line(
 		: KEY_OPEN_BACK, ' ', linewhite(curwin->w_cursor.lnum)))
     {
 	do_c_expr_indent();
-	p = ml_get_curline();
-	ai_col = (colnr_T)(skipwhite(p) - p);
+	ai_col = (colnr_T)getwhitecols_curline();
     }
 #endif
 #if defined(FEAT_VREPLACE) && (defined(FEAT_LISP) || defined(FEAT_CINDENT))
--- a/src/ops.c
+++ b/src/ops.c
@@ -2507,6 +2507,7 @@ op_insert(oparg_T *oap, long count1)
 {
     long		ins_len, pre_textlen = 0;
     char_u		*firstline, *ins_text;
+    colnr_T		ind_pre, ind_post;
     struct block_def	bd;
     int			i;
     pos_T		t1;
@@ -2541,7 +2542,10 @@ op_insert(oparg_T *oap, long count1)
 #endif
 	/* Get the info about the block before entering the text */
 	block_prep(oap, &bd, oap->start.lnum, TRUE);
+	/* Get indent information */
+	ind_pre = (colnr_T)getwhitecols_curline();
 	firstline = ml_get(oap->start.lnum) + bd.textcol;
+
 	if (oap->op_type == OP_APPEND)
 	    firstline += bd.textlen;
 	pre_textlen = (long)STRLEN(firstline);
@@ -2593,6 +2597,14 @@ op_insert(oparg_T *oap, long count1)
 	    && LT_POS(curbuf->b_op_start_orig, t1))
 	oap->start = curbuf->b_op_start_orig;
 
+    /* if indent kicked in, the firstline might have changed
+     * but only do that, if the indent actually increased */
+    ind_post = (colnr_T)getwhitecols_curline();
+    if (curbuf->b_op_start.col > ind_pre && ind_post > ind_pre)
+    {
+	bd.textcol += ind_post - ind_pre;
+	bd.start_vcol += ind_post - ind_pre;
+    }
     /* If user has moved off this line, we don't know what to do, so do
      * nothing.
      * Also don't repeat the insert when Insert mode ended with CTRL-C. */
@@ -2754,7 +2766,7 @@ op_change(oparg_T *oap)
 # endif
 	firstline = ml_get(oap->start.lnum);
 	pre_textlen = (long)STRLEN(firstline);
-	pre_indent = (long)(skipwhite(firstline) - firstline);
+	pre_indent = (long)getwhitecols(firstline);
 	bd.textcol = curwin->w_cursor.col;
     }
 #endif
@@ -2779,7 +2791,7 @@ op_change(oparg_T *oap)
 	firstline = ml_get(oap->start.lnum);
 	if (bd.textcol > (colnr_T)pre_indent)
 	{
-	    long new_indent = (long)(skipwhite(firstline) - firstline);
+	    long new_indent = (long)getwhitecols(firstline);
 
 	    pre_textlen += new_indent - pre_indent;
 	    bd.textcol += new_indent - pre_indent;
@@ -5065,8 +5077,7 @@ format_lines(
 #endif
 		    if (second_indent > 0)  /* the "leader" for FO_Q_SECOND */
 		{
-		    char_u *p = ml_get_curline();
-		    int indent = (int)(skipwhite(p) - p);
+		    int indent = getwhitecols_curline();
 
 		    if (indent > 0)
 		    {
--- a/src/proto/charset.pro
+++ b/src/proto/charset.pro
@@ -35,6 +35,8 @@ colnr_T getvcol_nolist(pos_T *posp);
 void getvvcol(win_T *wp, pos_T *pos, colnr_T *start, colnr_T *cursor, colnr_T *end);
 void getvcols(win_T *wp, pos_T *pos1, pos_T *pos2, colnr_T *left, colnr_T *right);
 char_u *skipwhite(char_u *q);
+int getwhitecols_curline(void);
+int getwhitecols(char_u *p);
 char_u *skipdigits(char_u *q);
 char_u *skipbin(char_u *q);
 char_u *skiphex(char_u *q);
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -1,4 +1,5 @@
 /* misc1.c */
+int get_whitespace_line_start(linenr_T lnum);
 int get_indent(void);
 int get_indent_lnum(linenr_T lnum);
 int get_indent_buf(buf_T *buf, linenr_T lnum);
--- a/src/screen.c
+++ b/src/screen.c
@@ -3463,7 +3463,7 @@ win_line(
     {
 	/* For checking first word with a capital skip white space. */
 	if (cap_col == 0)
-	    cap_col = (int)(skipwhite(line) - line);
+	    cap_col = getwhitecols(line);
 
 	/* To be able to spell-check over line boundaries copy the end of the
 	 * current line into nextline[].  Above the start of the next line was
--- a/src/spell.c
+++ b/src/spell.c
@@ -1625,11 +1625,11 @@ spell_move_to(
 
 	/* For checking first word with a capital skip white space. */
 	if (capcol == 0)
-	    capcol = (int)(skipwhite(line) - line);
+	    capcol = getwhitecols(line);
 	else if (curline && wp == curwin)
 	{
 	    /* For spellbadword(): check if first word needs a capital. */
-	    col = (int)(skipwhite(line) - line);
+	    col = getwhitecols(line);
 	    if (check_need_cap(lnum, col))
 		capcol = col;
 
@@ -3593,7 +3593,7 @@ check_need_cap(linenr_T lnum, colnr_T co
 
     line = ml_get_curline();
     endcol = 0;
-    if ((int)(skipwhite(line) - line) >= (int)col)
+    if (getwhitecols(line) >= (int)col)
     {
 	/* At start of line, check if previous line is empty or sentence
 	 * ends there. */
--- a/src/testdir/test_cindent.vim
+++ b/src/testdir/test_cindent.vim
@@ -71,7 +71,7 @@ func Test_cino_extern_c()
   bwipe!
 endfunc
 
-func! Test_cindent_rawstring()
+func Test_cindent_rawstring()
   new
   setl cindent
   call feedkeys("i" .
@@ -81,5 +81,25 @@ func! Test_cindent_rawstring()
           \ "statement;\<Esc>", "x")
   call assert_equal("\tstatement;", getline(line('.')))
   bw!
-endfunction
+endfunc
+
+func Test_cindent_expr()
+  new
+  func! MyIndentFunction()
+    return v:lnum == 1 ? shiftwidth() : 0
+  endfunc
+  setl expandtab sw=8 indentkeys+=; indentexpr=MyIndentFunction()
+  call setline(1, ['var_a = something()', 'b = something()'])
+  call cursor(1, 1)
+  call feedkeys("^\<c-v>j$A;\<esc>", 'tnix')
+  call assert_equal(['        var_a = something();', 'b = something();'], getline(1, '$'))
+
+  %d
+  call setline(1, ['                var_a = something()', '                b = something()'])
+  call cursor(1, 1)
+  call feedkeys("^\<c-v>j$A;\<esc>", 'tnix')
+  call assert_equal(['        var_a = something();', '                b = something()'], getline(1, '$'))
+  bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -770,6 +770,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1041,
+/**/
     1040,
 /**/
     1039,