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 <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index 621be29..febf678 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1631,6 +1631,22 @@
   bwipe!
 endfunc
 
+func Test_virtualedit_exclusive_selection()
+  new
+  set virtualedit=all selection=exclusive
+
+  call setline(1, "a\tb")
+  normal! 0v8ly
+  call assert_equal("a\t", 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,38 +2028,114 @@
     #" Exclusive selection 2
     new
     call setline(1, ["a\tc", "x\tz", '', ''])
+
     call cursor(1, 1)
     call feedkeys("\<Esc>v2l", 'xt')
     call assert_equal(["a\t"],
           \ 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\tc", "x\tz", ''],
           \ 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\tc", "x\tz"],
           \ 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\tc", "x\tz"],
           \ 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\tz"],
           \ 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\tc"],
+          \ 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\t"],
+          \ 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(['  '],
           \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
@@ -2102,9 +2194,106 @@
           \ ],
           \ 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\tc", '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\tc"],
+          \ 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)