patch 9.1.0433: Wrong yanking with exclusive selection and ve=all

Commit: 
https://github.com/vim/vim/commit/701ad50a9efcf0adfe6d787b606c4e4dbd31f26d
Author: zeertzjq <zeert...@outlook.com>
Date:   Thu May 23 07:47:55 2024 +0200

    patch 9.1.0433: Wrong yanking with exclusive selection and ve=all
    
    Problem:  Wrong yanking with exclusive selection and virtualedit=all,
              and integer overflow when using getregion() on it.
    Solution: Set coladd when decreasing column and 'virtualedit' is active.
              Add more tests for getregion() with 'virtualedit' (zeertzjq).
    
    closes: #14830
    
    Signed-off-by: zeertzjq <zeert...@outlook.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/evalfunc.c b/src/evalfunc.c
index b65df5dd9..f70b032ad 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -5594,31 +5594,12 @@ getregionpos(
 
     if (*region_type == MCHAR)
     {
-       // handle 'selection' == "exclusive"
+       // Handle 'selection' == "exclusive".
        if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
-       {
-           if (p2->coladd > 0)
-               p2->coladd--;
-           else if (p2->col > 0)
-           {
-               p2->col--;
-
-               mb_adjustpos(curbuf, p2);
-           }
-           else if (p2->lnum > 1)
-           {
-               p2->lnum--;
-               p2->col = ml_get_len(p2->lnum);
-               if (p2->col > 0)
-               {
-                   p2->col--;
-
-                   mb_adjustpos(curbuf, p2);
-               }
-           }
-       }
-       // if fp2 is on NUL (empty line) inclusive becomes false
-       if (*ml_get_pos(p2) == NUL && !virtual_op)
+           // When backing up to previous line, inclusive becomes false.
+           *inclusive = !unadjust_for_sel_inner(p2);
+       // If p2 is on NUL (end of line), inclusive becomes false.
+       if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
            *inclusive = FALSE;
     }
     else if (*region_type == MBLOCK)
diff --git a/src/normal.c b/src/normal.c
index 38c6bad80..b55d941fc 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -6696,29 +6696,40 @@ adjust_for_sel(cmdarg_T *cap)
     int
 unadjust_for_sel(void)
 {
-    pos_T      *pp;
-
     if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
+       return unadjust_for_sel_inner(LT_POS(VIsual, curwin->w_cursor)
+                                       ? &curwin->w_cursor : &VIsual);
+    return FALSE;
+}
+
+/*
+ * Move position "*pp" back one character for 'selection' == "exclusive".
+ * Returns TRUE when backed up to the previous line.
+ */
+    int
+unadjust_for_sel_inner(pos_T *pp)
+{
+    colnr_T    cs, ce;
+
+    if (pp->coladd > 0)
+       --pp->coladd;
+    else if (pp->col > 0)
     {
-       if (LT_POS(VIsual, curwin->w_cursor))
-           pp = &curwin->w_cursor;
-       else
-           pp = &VIsual;
-       if (pp->coladd > 0)
-           --pp->coladd;
-       else
-       if (pp->col > 0)
-       {
-           --pp->col;
-           mb_adjustpos(curbuf, pp);
-       }
-       else if (pp->lnum > 1)
+       --pp->col;
+       mb_adjustpos(curbuf, pp);
+       if (virtual_active())
        {
-           --pp->lnum;
-           pp->col = ml_get_len(pp->lnum);
-           return TRUE;
+           getvcol(curwin, pp, &cs, NULL, &ce);
+           pp->coladd = ce - cs;
        }
     }
+    else if (pp->lnum > 1)
+    {
+       --pp->lnum;
+       pp->col = ml_get_len(pp->lnum);
+       return TRUE;
+    }
+
     return FALSE;
 }
 
diff --git a/src/proto/normal.pro b/src/proto/normal.pro
index 6dcbe414f..36a26ec48 100644
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -31,5 +31,6 @@ int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp);
 void start_selection(void);
 void may_start_select(int c);
 int unadjust_for_sel(void);
+int unadjust_for_sel_inner(pos_T *pp);
 void set_cursor_for_append_to_line(void);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 621be298d..febf67855 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1631,6 +1631,22 @@ func Test_visual_substitute_visual()
   bwipe!
 endfunc
 
+func Test_virtualedit_exclusive_selection()
+  new
+  set virtualedit=all selection=exclusive
+
+  call setline(1, "a   b")
+  normal! 0v8ly
+  call assert_equal("a ", getreg('"'))
+  normal! 0v6ly
+  call assert_equal('a     ', getreg('"'))
+  normal! 06lv2ly
+  call assert_equal('  ', getreg('"'))
+
+  set virtualedit& selection&
+  bwipe!
+endfunc
+
 func Test_visual_getregion()
   let lines =<< trim END
     new
@@ -2012,37 +2028,113 @@ func Test_visual_getregion()
     #" Exclusive selection 2
     new
     call setline(1, ["a        c", "x  z", '', ''])
+
     call cursor(1, 1)
     call feedkeys("\<Esc>v2l", 'xt')
     call assert_equal(["a      "],
           \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>v$G", 'xt')
     call assert_equal(["a      c", "x  z", ''],
           \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+          \   [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>v$j", 'xt')
     call assert_equal(["a      c", "x  z"],
           \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>\<C-v>$j", 'xt')
     call assert_equal(["a      c", "x  z"],
           \ getregion(getpos('v'), getpos('.'),
           \           {'exclusive': v:true, 'type': "\<C-v>" }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'),
+          \              {'exclusive': v:true, 'type': "\<C-v>" }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>\<C-v>$G", 'xt')
     call assert_equal(["a", "x", '', ''],
           \ getregion(getpos('v'), getpos('.'),
           \           {'exclusive': v:true, 'type': "\<C-v>" }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+          \   [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+          \   [[bufnr('%'), 4, 0, 0], [bufnr('%'), 4, 0, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'),
+          \              {'exclusive': v:true, 'type': "\<C-v>" }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>wv2j", 'xt')
     call assert_equal(["c", "x z"],
           \ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
 
-    #" virtualedit
+    #" 'virtualedit' with exclusive selection
     set selection=exclusive
     set virtualedit=all
 
+    call cursor(1, 1)
+    call feedkeys("\<Esc>vj", 'xt')
+    call assert_equal(["a      c"],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>v8l", 'xt')
+    call assert_equal(["a      "],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>v6l", 'xt')
+    call assert_equal(['a     '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 5]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>6lv2l", 'xt')
+    call assert_equal(['  '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
     call cursor(1, 1)
     call feedkeys("\<Esc>lv2l", 'xt')
     call assert_equal(['  '],
@@ -2102,9 +2194,106 @@ func Test_visual_getregion()
           \ ],
           \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
 
-    set virtualedit&
+    #" 'virtualedit' with inclusive selection
     set selection&
 
+    call cursor(1, 1)
+    call feedkeys("\<Esc>vj", 'xt')
+    call assert_equal(["a      c", 'x'],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>v8l", 'xt')
+    call assert_equal(["a      c"],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>v6l", 'xt')
+    call assert_equal(['a      '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 6]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>6lv2l", 'xt')
+    call assert_equal(['  c'],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>lv2l", 'xt')
+    call assert_equal(['   '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>2lv2l", 'xt')
+    call assert_equal(['   '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call feedkeys('j', 'xt')
+    call assert_equal(['      c', 'x    '],
+          \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 4]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>6l\<C-v>2lj", 'xt')
+    call assert_equal(['  c', '  z'],
+          \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+          \   [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 0]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>l\<C-v>2l2j", 'xt')
+    call assert_equal(['   ', '   ', '   '],
+          \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+          \   [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]],
+          \   [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 3]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+    call cursor(1, 1)
+    call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
+    call assert_equal(['   ', '   ', '   '],
+          \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+    call assert_equal([
+          \   [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+          \   [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]],
+          \   [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 3]],
+          \ ],
+          \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+    set virtualedit&
     bwipe!
   END
   call v9.CheckLegacyAndVim9Success(lines)
diff --git a/src/version.c b/src/version.c
index b37dbcf85..c4bccb447 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    433,
 /**/
     432,
 /**/

-- 
-- 
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.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1sA1Uh-001fAC-OH%40256bit.org.

Reply via email to