[PATCH] problems with visual block shifting

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] problems with visual block shifting

Lech Lorens
The attached patch (visual_block_shift.patch) fixes the problems with
shifting visual blocks when the blocks start and end inside one TAB
character.

My general impression about the relevant part of the code was that it
was very difficult to understand. This is why I allowed myself to start
from scratch.
I aimed for code clarity, hence the extensive comments, variables
declared inside the else {} block (I know Bram will probably not like
it, though).

I also added a test for visual block shift inside one TAB:
visual_block_shift_test.patch

I made changes to v_b_< and todo.txt, removing notes indicating the
problems: visual_block_shift_runtime.patch

--
Cheers,
Lech

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---


Index: src/ops.c
===================================================================
--- src/ops.c (revision 1365)
+++ src/ops.c (working copy)
@@ -75,8 +75,8 @@
     int startspaces; /* 'extra' cols of first char */
     int endspaces; /* 'extra' cols of first char */
     int textlen; /* chars in block */
-    char_u *textstart; /* pointer to 1st char in block */
-    colnr_T textcol; /* cols of chars (at least part.) in block */
+    char_u *textstart; /* pointer to 1st (at least part.) char in block */
+    colnr_T textcol; /* index of chars (at least part.) in block */
     colnr_T start_vcol; /* start col of 1st char wholly inside block */
     colnr_T end_vcol; /* start col of 1st char wholly after block */
 #ifdef FEAT_VISUALEXTRA
@@ -87,7 +87,7 @@
     int pre_whitesp_c; /* chars of ws before block */
     colnr_T end_char_vcols; /* number of vcols of post-block char */
 #endif
-    colnr_T start_char_vcols; /* number of vcols of pre-block char */
+    colnr_T start_char_vcols; /* number of displayed cols of pre-block char */
 };
 
 #ifdef FEAT_VISUALEXTRA
@@ -382,15 +382,14 @@
 {
     int left = (oap->op_type == OP_LSHIFT);
     int oldstate = State;
-    int total, split;
-    char_u *newp, *oldp, *midp, *ptr;
+    int total;
+    char_u *newp, *oldp;
     int oldcol = curwin->w_cursor.col;
     int p_sw = (int)curbuf->b_p_sw;
     int p_ts = (int)curbuf->b_p_ts;
     struct block_def bd;
-    int internal = 0;
     int incr;
-    colnr_T vcol, col = 0, ws_vcol;
+    colnr_T ws_vcol;
     int i = 0, j = 0;
     int len;
 
@@ -456,67 +455,95 @@
     }
     else /* left */
     {
- vcol = oap->start_vcol;
- /* walk vcol past ws to be removed */
- for (midp = oldp + bd.textcol;
-      vcol < (oap->start_vcol + total) && vim_iswhite(*midp); )
+ colnr_T    destination_col; /* column to which text in block will
+   be shifted */
+ char_u    *verbatim_copy_end; /* end of the part of the line which is
+   copied verbatim */
+ colnr_T    verbatim_copy_width;/* the (displayed) width of this part
+   of line */
+ unsigned    fill; /* number of spaces which replace a TAB */
+ unsigned    new_line_len; /* the length of the line after the
+   block shift */
+ size_t    block_space_width;
+ size_t    shift_amount;
+ char_u    *non_white = bd.textstart;
+ colnr_T    non_white_col;
+
+ /* Firstly, let's find the first non-whitespace character that is
+ * displayed after the block's start column and the character's column
+ * number. Also, let's calculate the width of all the whitespace
+ * characters that are displayed in the block and precede the searched
+ * non-whitespace character.
+ */
+
+ /* If "bd.startspaces" is set, "bd.textstart" points to the character,
+ * the part of which is displayed at the block's beginning. Let's start
+ * searching from the next character.
+ */
+ if (bd.startspaces)
+    mb_ptr_adv(non_white);
+
+ /* The character's column is in "bd.start_vcol".
+ */
+ non_white_col = bd.start_vcol;
+
+ while (vim_iswhite(*non_white))
  {
-    incr = lbr_chartabsize_adv(&midp, (colnr_T)vcol);
-    vcol += incr;
+    incr = lbr_chartabsize_adv(&non_white, non_white_col);
+    non_white_col += incr;
  }
- /* internal is the block-internal ws replacing a split TAB */
- if (vcol > (oap->start_vcol + total))
+
+ block_space_width = non_white_col - oap->start_vcol;
+ /* We will shift by "total" or "block_space_width", whichever is less.
+ */
+ shift_amount = (block_space_width < total? block_space_width: total);
+
+ /* The column to which we will shift the text.
+ */
+ destination_col = non_white_col - shift_amount;
+
+ /* Now let's find out how much of the beginning of the line we can
+ * reuse without modification.
+ */
+ verbatim_copy_end = bd.textstart;
+ verbatim_copy_width = bd.start_vcol;
+
+ /* If "bd.startspaces" is set, "bd.textstart" points to the character
+ * preceding the block. We have to subtract its width to obtain its
+ * column number.
+ */
+ if (bd.startspaces)
+    verbatim_copy_width -= bd.start_char_vcols;
+ while (verbatim_copy_width < destination_col)
  {
-    /* we have to split the TAB *(midp-1) */
-    internal = vcol - (oap->start_vcol + total);
+    incr = lbr_chartabsize(verbatim_copy_end, verbatim_copy_width);
+    if (verbatim_copy_width + incr > destination_col)
+ break;
+    verbatim_copy_width += incr;
+    mb_ptr_adv(verbatim_copy_end);
  }
- /* if 'expandtab' is not set, use TABs */
 
- split = bd.startspaces + internal;
- if (split > 0)
- {
-    if (!curbuf->b_p_et)
-    {
- for (ptr = oldp, col = 0; ptr < oldp+bd.textcol; )
-    col += lbr_chartabsize_adv(&ptr, (colnr_T)col);
+ /* If "destination_col" is different from the width of the initial
+ * part of the line that will be copied, it means we encountered a tab
+ * character, which we will have to partly replace with spaces.
+ */
+ fill = destination_col - verbatim_copy_width;
 
- /* col+1 now equals the start col of the first char of the
- * block (may be < oap.start_vcol if we're splitting a TAB) */
- i = ((col % p_ts) + split) / p_ts; /* number of tabs */
-    }
-    if (i)
- j = ((col % p_ts) + split) % p_ts; /* number of spp */
-    else
- j = split;
- }
+ /* The replacement line will consist of:
+ * - the beginning of the original line up to "verbatim_copy_end",
+ * - "fill" number of spaces,
+ * - the rest of the line, pointed to by non_white.
+ */
+ new_line_len = (unsigned)(verbatim_copy_end - oldp)
+           + fill
+           + (unsigned)STRLEN(non_white) + 1;
 
- newp = alloc_check(bd.textcol + i + j + (unsigned)STRLEN(midp) + 1);
+ newp = alloc_check(new_line_len);
  if (newp == NULL)
     return;
- vim_memset(newp, NUL, (size_t)(bd.textcol + i + j + STRLEN(midp) + 1));
-
- /* copy first part we want to keep */
- mch_memmove(newp, oldp, (size_t)bd.textcol);
- /* Now copy any TABS and spp to ensure correct alignment! */
- while (vim_iswhite(*midp))
- {
-    if (*midp == TAB)
- i++;
-    else /*space */
- j++;
-    midp++;
- }
- /* We might have an extra TAB worth of spp now! */
- if (j / p_ts && !curbuf->b_p_et)
- {
-    i++;
-    j -= p_ts;
- }
- copy_chars(newp + bd.textcol, (size_t)i, TAB);
- copy_spaces(newp + bd.textcol + i, (size_t)j);
-
- /* the end */
- STRMOVE(newp + STRLEN(newp), midp);
+ mch_memmove(newp, oldp, (size_t)(verbatim_copy_end - oldp));
+ copy_spaces(newp + (verbatim_copy_end - oldp), (size_t)fill);
+ STRMOVE(newp + (verbatim_copy_end - oldp) + fill, non_white);
     }
     /* replace the line */
     ml_replace(curwin->w_cursor.lnum, newp, FALSE);

diff -Nacr src/testdir_orig/test66.in src/testdir/test66.in
*** src/testdir_orig/test66.in 1970-01-01 01:00:00.000000000 +0100
--- src/testdir/test66.in 2009-02-23 00:10:00.000000000 +0100
***************
*** 0 ****
--- 1,24 ----
+
+ Test for visual block shift and tab characters.
+
+ STARTTEST
+ /^abcdefgh
+ 4jI    j<<11|D
+ 7|a 
+ 7|a   
+ 7|a       4k13|4j<
+ :$-4,$w! test.out
+ :$-4,$s/\s\+//g
+ 4kI    j<<
+ 7|a 
+ 7|a 
+ 7|a       4k13|4j3<
+ :$-4,$w >> test.out
+ :qa!
+ ENDTEST
+
+ abcdefghijklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
+ abcdefghijklmnopqrstuvwxyz
diff -Nacr src/testdir_orig/test66.ok src/testdir/test66.ok
*** src/testdir_orig/test66.ok 1970-01-01 01:00:00.000000000 +0100
--- src/testdir/test66.ok 2009-02-23 00:10:00.000000000 +0100
***************
*** 0 ****
--- 1,10 ----
+     abcdefghijklmnopqrstuvwxyz
+ abcdefghij
+     abc    defghijklmnopqrstuvwxyz
+     abc    defghijklmnopqrstuvwxyz
+     abc    defghijklmnopqrstuvwxyz
+     abcdefghijklmnopqrstuvwxyz
+ abcdefghij
+     abc    defghijklmnopqrstuvwxyz
+     abc defghijklmnopqrstuvwxyz
+     abc    defghijklmnopqrstuvwxyz

diff -cr ./doc/todo.txt ../vim7_runtime_orig/doc/todo.txt
*** ./doc/todo.txt 2009-02-22 20:32:13.000000000 +0100
--- ../vim7_runtime_orig/doc/todo.txt 2009-02-22 20:30:27.000000000 +0100
***************
*** 1614,1619 ****
--- 1614,1621 ----
  8   With 'virtualedit' set and 'selection' "exclusive", a Visual selection
      that ends in or after a tab, "d" doesn't delete (part of) the tab.
      (Helmut Stiegler)
+ 8   With 'virtualedit' set, a blockwise Visual selection that starts and ends
+     in a tab, "<" shifts too much. (Helmut Stiegler)
  9   When jumping to a tag, the search pattern is put in the history.  When
      'magic' is on, the pattern may not work.  Translate the pattern depending
      on p_magic when putting it in the history?  Alternative: Store value of
diff -cr ./doc/visual.txt ../vim7_runtime_orig/doc/visual.txt
*** ./doc/visual.txt 2009-02-22 20:32:42.000000000 +0100
--- ../vim7_runtime_orig/doc/visual.txt 2009-02-22 20:30:27.000000000 +0100
***************
*** 309,314 ****
--- 309,316 ----
  LHS of the block determines the point from which to apply a right shift, and
  padding includes TABs optimally according to 'ts' and 'et'.  The LHS of the
  block determines the point upto which to shift left.
+     Note: v_< padding is buggy if the Visual Block starts and ends in the same
+     TAB. (Vim 5.4c)
  See |v_b_>_example|.
  See |v_b_<_example|.
 
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] problems with visual block shifting

Bram Moolenaar


Lech Lorens wrote:

> The attached patch (visual_block_shift.patch) fixes the problems with
> shifting visual blocks when the blocks start and end inside one TAB
> character.
>
> My general impression about the relevant part of the code was that it
> was very difficult to understand. This is why I allowed myself to start
> from scratch.
> I aimed for code clarity, hence the extensive comments, variables
> declared inside the else {} block (I know Bram will probably not like
> it, though).
>
> I also added a test for visual block shift inside one TAB:
> visual_block_shift_test.patch
>
> I made changes to v_b_< and todo.txt, removing notes indicating the
> problems: visual_block_shift_runtime.patch

Thanks, I'll put the patch in the todo list.
The docs part is reversed, but that's not a problem.

--
Would you care for a drink?   I mean, if it were, like,
disabled and you had to look after it?

 /// Bram Moolenaar -- [hidden email] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\        download, build and distribute -- http://www.A-A-P.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

--~--~---------~--~----~------------~-------~--~----~
You received this message from the "vim_dev" maillist.
For more information, visit http://www.vim.org/maillist.php
-~----------~----~----~----~------~----~------~--~---