From a175593b65d0a69a4b4b428db2ce32382ae16c2b Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 15:23:15 -0300 Subject: [PATCH 1/8] Add comments to Move functions. Document what mode the function is expected to be called in, and what positive and negative distances mean. --- plugin/move.vim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugin/move.vim b/plugin/move.vim index dceda3d..f07e626 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -34,6 +34,10 @@ function! s:RestoreDefaultRegister() let @" = s:default_register_value endfunction +" +" In visual mode, move the selected lines vertically. +" Moves down if (distance > 0) and up if (distance < 0). +" function s:MoveBlockVertically(distance) range if !&modifiable return @@ -53,6 +57,10 @@ function s:MoveBlockVertically(distance) range normal! gv endfunction +" +" In visual mode, move the selected block to the left +" Switches to visual-block mode first if another visual mode is selected. +" function! s:MoveBlockLeft(distance) range let l:min_col = min([virtcol("'<"), virtcol("'>")]) let l:distance = min([a:distance, l:min_col - 1]) @@ -92,6 +100,10 @@ function! s:MoveBlockLeft(distance) range let &virtualedit = l:old_virtualedit endfunction +" +" In visual mode, move the selected block to the right +" Switches to visual-block mode first if another visual mode is selected. +" function! s:MoveBlockRight(distance) range let l:max_col = max([virtcol("'<"), virtcol("'>")]) @@ -148,6 +160,10 @@ function! s:MoveBlockRight(distance) range let &virtualedit = l:old_virtualedit endfunction +" +" In normal mode, move the current line vertically. +" Moves down if (distance > 0) and up if (distance < 0). +" function! s:MoveLineVertically(distance) if !&modifiable return @@ -176,6 +192,9 @@ function! s:MoveLineVertically(distance) execute 'silent normal!' l:new_cursor_col . '|' endfunction +" +" In normal mode, move the character under the cursor to the left +" function! s:MoveCharLeft(distance) if !&modifiable || virtcol("$") == 1 || virtcol(".") == 1 return @@ -194,6 +213,9 @@ function! s:MoveCharLeft(distance) call s:RestoreDefaultRegister() endfunction +" +" In normal mode, move the character under the cursor to the right +" function! s:MoveCharRight(distance) if !&modifiable || virtcol("$") == 1 return From 26df6d08af51638bd738c403308f706a72f980e6 Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 15:19:23 -0300 Subject: [PATCH 2/8] Don't use "range" for MoveBlockVertically The rest of this function already expects us to be in visual mode. I think it is clearer if we just use '< and '> instead of relying on a:firstline and a:lastline, which are finicky to use. --- plugin/move.vim | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index f07e626..ffbcedf 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -38,17 +38,20 @@ endfunction " In visual mode, move the selected lines vertically. " Moves down if (distance > 0) and up if (distance < 0). " -function s:MoveBlockVertically(distance) range +function s:MoveBlockVertically(distance) if !&modifiable return endif + let l:first = line("'<") + let l:last = line("'>") + if a:distance <= 0 - let l:after = max([1, a:firstline + a:distance]) - 1 + let l:after = max([1, l:first + a:distance]) - 1 else - let l:after = min([line('$'), a:lastline + a:distance]) + let l:after = min([line('$'), l:last + a:distance]) endif - execute 'silent' a:firstline ',' a:lastline 'move ' l:after + execute 'silent' l:first ',' l:last 'move ' l:after if g:move_auto_indent normal! gv= @@ -243,10 +246,10 @@ function! s:MoveKey(key) endfunction -vnoremap MoveBlockDown :call MoveBlockVertically( v:count1) -vnoremap MoveBlockUp :call MoveBlockVertically(-v:count1) -vnoremap MoveBlockHalfPageDown :call MoveBlockVertically( v:count1 * HalfPageSize()) -vnoremap MoveBlockHalfPageUp :call MoveBlockVertically(-v:count1 * HalfPageSize()) +vnoremap MoveBlockDown : call MoveBlockVertically( v:count1) +vnoremap MoveBlockUp : call MoveBlockVertically(-v:count1) +vnoremap MoveBlockHalfPageDown : call MoveBlockVertically( v:count1 * HalfPageSize()) +vnoremap MoveBlockHalfPageUp : call MoveBlockVertically(-v:count1 * HalfPageSize()) vnoremap MoveBlockLeft :call MoveBlockLeft(v:count1) vnoremap MoveBlockRight :call MoveBlockRight(v:count1) From 3aca1d3d323981ec13e8ff1ebe921d2dd0c72721 Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 17:41:41 -0300 Subject: [PATCH 3/8] Consolidate MoveCharLeft and MoveCharRight Use a single function, to reduce code duplication and reduce the number of corner cases - Always move the same way, using | instead of h,l,0,$. - Always paste the same way, using P instead of sometimes P and sometimes p. - Always use virtualedit=all --- plugin/move.vim | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index ffbcedf..99e8703 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -196,45 +196,35 @@ function! s:MoveLineVertically(distance) endfunction " -" In normal mode, move the character under the cursor to the left +" In normal mode, move the character under the cursor horizontally +" Moves right (distance > 0) and left if (distance < 0). " -function! s:MoveCharLeft(distance) - if !&modifiable || virtcol("$") == 1 || virtcol(".") == 1 +function! s:MoveCharHorizontally(distance) + if !&modifiable return endif - call s:SaveDefaultRegister() - - if (virtcol('.') - a:distance <= 0) - silent normal! x0P - else - let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'onemore'] - execute 'silent normal! x' . a:distance . 'hP' - let &virtualedit = l:old_virtualedit + let l:curr = virtcol('.') + let l:next = l:curr + a:distance + if !g:move_past_end_of_line + let l:next = max([1, min([l:next, virtcol('$')-1])]) endif - call s:RestoreDefaultRegister() -endfunction - -" -" In normal mode, move the character under the cursor to the right -" -function! s:MoveCharRight(distance) - if !&modifiable || virtcol("$") == 1 + if l:curr == l:next + " Don't add an empty change to the undo stack. return endif call s:SaveDefaultRegister() + let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] - if !g:move_past_end_of_line && (virtcol('.') + a:distance >= virtcol('$') - 1) - silent normal! x$p - else - let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] - execute 'silent normal! x' . a:distance . 'lP' - let &virtualedit = l:old_virtualedit - endif + normal! x + execute 'normal!' . (l:next.'|') + normal! P + let &virtualedit = l:old_virtualedit call s:RestoreDefaultRegister() + endfunction function! s:HalfPageSize() @@ -261,8 +251,8 @@ nnoremap MoveLineDown : call MoveLineVertica nnoremap MoveLineUp : call MoveLineVertically(-v:count1) nnoremap MoveLineHalfPageDown : call MoveLineVertically( v:count1 * HalfPageSize()) nnoremap MoveLineHalfPageUp : call MoveLineVertically(-v:count1 * HalfPageSize()) -nnoremap MoveCharLeft : call MoveCharLeft(v:count1) -nnoremap MoveCharRight : call MoveCharRight(v:count1) +nnoremap MoveCharRight : call MoveCharHorizontally( v:count1) +nnoremap MoveCharLeft : call MoveCharHorizontally(-v:count1) if g:move_map_keys From e3f7412cf720ecae365fb4bd87b0c76a539642a2 Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 17:50:56 -0300 Subject: [PATCH 4/8] Avoid using "silent" I think that silencing errors is no longer necessary now that our movement commands check the bounds before attempting a move. --- plugin/move.vim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index 99e8703..7adc092 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -51,7 +51,7 @@ function s:MoveBlockVertically(distance) else let l:after = min([line('$'), l:last + a:distance]) endif - execute 'silent' l:first ',' l:last 'move ' l:after + execute l:first ',' l:last 'move ' l:after if g:move_auto_indent normal! gv= @@ -175,7 +175,7 @@ function! s:MoveLineVertically(distance) " Remember the current cursor position. When we move or reindent a line " Vim will move the cursor to the first non-blank character. let l:old_cursor_col = virtcol('.') - silent normal! ^ + normal! ^ let l:old_indent = virtcol('.') if a:distance <= 0 @@ -183,16 +183,16 @@ function! s:MoveLineVertically(distance) else let l:after = min([line('$'), line('.') + a:distance]) endif - execute 'silent move' l:after + execute 'move' l:after if g:move_auto_indent - silent normal! == + normal! == endif " Restore the cursor column, taking indentation changes into account. let l:new_indent = virtcol('.') let l:new_cursor_col = max([1, l:old_cursor_col - l:old_indent + l:new_indent]) - execute 'silent normal!' l:new_cursor_col . '|' + execute 'normal!' (l:new_cursor_col . '|') endfunction " From 8f01d7ea8eb82b3a24d30d2aaf637a34ae280ab3 Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 17:56:35 -0300 Subject: [PATCH 5/8] Save the @" in a local var instead of a global We should save the default register @" in a local variable instead of a global variable. This way the value is discarded after the function returns instead of being preserved forever. --- plugin/move.vim | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index 7adc092..36ae0ca 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -26,14 +26,6 @@ if !exists('g:move_past_end_of_line') let g:move_past_end_of_line = 1 endif -function! s:SaveDefaultRegister() - let s:default_register_value = @" -endfunction - -function! s:RestoreDefaultRegister() - let @" = s:default_register_value -endfunction - " " In visual mode, move the selected lines vertically. " Moves down if (distance > 0) and up if (distance < 0). @@ -80,7 +72,7 @@ function! s:MoveBlockLeft(distance) range endif let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'onemore'] - call s:SaveDefaultRegister() + let l:old_default_register = @" " save previous cursor position silent normal! gv @@ -99,7 +91,7 @@ function! s:MoveBlockLeft(distance) range silent normal! O endif - call s:RestoreDefaultRegister() + let @" = l:old_default_register let &virtualedit = l:old_virtualedit endfunction @@ -135,7 +127,7 @@ function! s:MoveBlockRight(distance) range let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] - call s:SaveDefaultRegister() + let l:old_default_register = @" " save previous cursor position silent normal! gv @@ -159,7 +151,7 @@ function! s:MoveBlockRight(distance) range silent normal! O endif - call s:RestoreDefaultRegister() + let @" = l:old_default_register let &virtualedit = l:old_virtualedit endfunction @@ -215,7 +207,7 @@ function! s:MoveCharHorizontally(distance) return endif - call s:SaveDefaultRegister() + let l:old_default_register = @" let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] normal! x @@ -223,7 +215,7 @@ function! s:MoveCharHorizontally(distance) normal! P let &virtualedit = l:old_virtualedit - call s:RestoreDefaultRegister() + let @" = l:old_default_register endfunction From 2958bc805e4757aaee0ecb3556df02d67d3f79ca Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Sun, 5 Jul 2020 20:38:53 -0300 Subject: [PATCH 6/8] Consolidate MoveBlockLeft and MoveBlockRight As before, merge the two functions into a single function to consolidate the logic in a single place. We also introduce the name "before" in the MoveHorizontally functions, to parallel the "after" from MoveVertically functions. --- plugin/move.vim | 128 ++++++++++++++++-------------------------------- 1 file changed, 42 insertions(+), 86 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index 36ae0ca..8437216 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -54,105 +54,59 @@ endfunction " " In visual mode, move the selected block to the left +" Moves right (distance > 0) and left if (distance < 0). " Switches to visual-block mode first if another visual mode is selected. " -function! s:MoveBlockLeft(distance) range - let l:min_col = min([virtcol("'<"), virtcol("'>")]) - let l:distance = min([a:distance, l:min_col - 1]) - - if !&modifiable || virtcol("$") == 1 || l:distance <= 0 || visualmode() ==# "V" - normal! gv +function! s:MoveBlockHorizontally(distance) + if !&modifiable return endif - if visualmode() ==# "v" && a:lastline - a:firstline > 0 - execute "silent normal! gv\" - echomsg "Switching to visual block mode for moving multiple lines with MoveBlockLeft" + if visualmode() ==# 'V' + echomsg 'vim-move: Cannot move horizontally in linewise visual mode' return endif - let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'onemore'] - let l:old_default_register = @" + normal! gv - " save previous cursor position - silent normal! gv - let l:row_pos = getcurpos()[1] - let l:is_rhs = virtcol(".") == max([virtcol("'<"), virtcol("'>")]) - - execute 'silent normal! gvd' . l:distance . "hP`[\`]" - - " restore previous cursor position - if getcurpos()[1] != l:row_pos - silent normal! o - if l:is_rhs - silent normal! O - endif - elseif !l:is_rhs - silent normal! O + if visualmode() ==# 'v' + echomsg 'vim-move: Switching to visual block mode' + execute "normal! \" endif - let @" = l:old_default_register - let &virtualedit = l:old_virtualedit -endfunction - -" -" In visual mode, move the selected block to the right -" Switches to visual-block mode first if another visual mode is selected. -" -function! s:MoveBlockRight(distance) range - let l:max_col = max([virtcol("'<"), virtcol("'>")]) + let l:cols = [virtcol("'<"), virtcol("'>")] + let l:first = min(l:cols) + let l:last = max(l:cols) + let l:width = l:last - l:first + 1 - let l:distance = a:distance - if !g:move_past_end_of_line - let l:shorter_line_len = min(map(getline(a:firstline, a:lastline), 'strwidth(v:val)')) - let l:distance = min([l:shorter_line_len - l:max_col, l:distance]) + let l:before = max([1, l:first + a:distance]) + if a:distance > 0 && !g:move_past_end_of_line + let l:shortest = min(map(getline("'<", "'>"), 'strwidth(v:val)')) + let l:limit = max([l:first, l:shortest-width+1]) + let l:before = min([l:before, l:limit]) end - if !&modifiable || virtcol("$") == 1 || l:distance <= 0 - normal! gv - return - endif - - if visualmode() ==# "V" - execute "silent normal! gv\o0o$h" - echomsg "Switching to visual block mode for moving whole line(s) with MoveBlockRight" - return - endif - - if visualmode() ==# "v" && a:lastline - a:firstline > 0 - execute "silent normal! gv\" - echomsg "Switching to visual block mode for moving multiple lines with MoveBlockRight" + if l:first == l:before + " Don't add an empty change to the undo stack. return endif - let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] let l:old_default_register = @" - " save previous cursor position - silent normal! gv - let l:row_pos = getcurpos()[1] - let l:is_rhs = virtcol(".") == l:max_col - - execute 'silent normal! gvd' . l:distance . "l" - " P behaves inconsistently in virtualedit 'all' mode; sometimes the cursor - " moves one right after pasting, other times it doesn't. This makes it - " difficult to rely on `[ to determine the start of the shifted selection. - let l:new_start_pos = virtcol(".") - execute 'silent normal! P' . l:new_start_pos . "|\`]" - - " restore previous cursor position - if getcurpos()[1] != l:row_pos - silent normal! o - if l:is_rhs - silent normal! O - endif - elseif !l:is_rhs - silent normal! O - endif + normal! d + execute 'normal!' . (l:before.'|') + normal! P let @" = l:old_default_register let &virtualedit = l:old_virtualedit + + " Reselect the pasted text. + " For some reason, `[ doesn't always point where it should -- sometimes it + " is off by one. Maybe it is because of the virtualedit=all? The + " workaround we found is to recompute the destination column by hand. + execute 'normal!' . (l:before.'|') . "\`]" + endfunction " @@ -197,12 +151,12 @@ function! s:MoveCharHorizontally(distance) endif let l:curr = virtcol('.') - let l:next = l:curr + a:distance + let l:before = l:curr + a:distance if !g:move_past_end_of_line - let l:next = max([1, min([l:next, virtcol('$')-1])]) + let l:before = max([1, min([l:before, virtcol('$')-1])]) endif - if l:curr == l:next + if l:curr == l:before " Don't add an empty change to the undo stack. return endif @@ -211,7 +165,7 @@ function! s:MoveCharHorizontally(distance) let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] normal! x - execute 'normal!' . (l:next.'|') + execute 'normal!' . (l:before.'|') normal! P let &virtualedit = l:old_virtualedit @@ -227,18 +181,20 @@ function! s:MoveKey(key) return '<' . g:move_key_modifier . '-' . a:key . '>' endfunction +" Note: An older version of this program used callbacks with the "range" +" attribute to support being called with a selection range as a parameter. +" However, that had some problems: we would get E16 errors if the user tried +" to perform an out-of bounds move and the computations that used col() would +" also return the wrong results. Because of this, we have switched everything +" to using . vnoremap MoveBlockDown : call MoveBlockVertically( v:count1) vnoremap MoveBlockUp : call MoveBlockVertically(-v:count1) vnoremap MoveBlockHalfPageDown : call MoveBlockVertically( v:count1 * HalfPageSize()) vnoremap MoveBlockHalfPageUp : call MoveBlockVertically(-v:count1 * HalfPageSize()) -vnoremap MoveBlockLeft :call MoveBlockLeft(v:count1) -vnoremap MoveBlockRight :call MoveBlockRight(v:count1) +vnoremap MoveBlockRight : call MoveBlockHorizontally( v:count1) +vnoremap MoveBlockLeft : call MoveBlockHorizontally(-v:count1) -" We can't use functions defined with the 'range' attribute for moving lines -" or characters. In the case of lines, it causes vim to complain with E16 -" (Invalid adress) if we try to move out of bounds. In the case of characters, -" it messes up the result of calling col(). nnoremap MoveLineDown : call MoveLineVertically( v:count1) nnoremap MoveLineUp : call MoveLineVertically(-v:count1) nnoremap MoveLineHalfPageDown : call MoveLineVertically( v:count1 * HalfPageSize()) From afc73b6fcf255779cb15587e9e38dd1cc2d2c08b Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Tue, 7 Jul 2020 00:41:53 -0300 Subject: [PATCH 7/8] Clarify the move_past_end_of_line logic Hopefully it should be more self explanatory now. The situation that we have to worry about is when we create a selection that is already past the end of line. For example if we have ABC DEFGH and ask to move the rectangle "C FGH" to the right, we want to stay put, because we area already past the end of line. We should be careful to not accidentally move it backwards. Without that `last < shortest` test it would be transformedi into something like C AB FGHED --- plugin/move.vim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index 8437216..79c6d4c 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -82,9 +82,12 @@ function! s:MoveBlockHorizontally(distance) let l:before = max([1, l:first + a:distance]) if a:distance > 0 && !g:move_past_end_of_line let l:shortest = min(map(getline("'<", "'>"), 'strwidth(v:val)')) - let l:limit = max([l:first, l:shortest-width+1]) - let l:before = min([l:before, l:limit]) - end + if l:last < l:shortest + let l:before = min([l:before, l:shortest - width + 1]) + else + let l:before = l:first + endif + endif if l:first == l:before " Don't add an empty change to the undo stack. From b7b3973ba398add31f56e6746238f9257a5532b3 Mon Sep 17 00:00:00 2001 From: Hugo Musso Gualandi Date: Tue, 7 Jul 2020 14:09:04 -0300 Subject: [PATCH 8/8] Reorder the functions Put the MoveVertically and the MoveHorizontally functions next to each other. --- plugin/move.vim | 127 ++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/plugin/move.vim b/plugin/move.vim index 79c6d4c..a8d0467 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -26,6 +26,38 @@ if !exists('g:move_past_end_of_line') let g:move_past_end_of_line = 1 endif +" +" In normal mode, move the current line vertically. +" Moves down if (distance > 0) and up if (distance < 0). +" +function! s:MoveLineVertically(distance) + if !&modifiable + return + endif + + " Remember the current cursor position. When we move or reindent a line + " Vim will move the cursor to the first non-blank character. + let l:old_cursor_col = virtcol('.') + normal! ^ + let l:old_indent = virtcol('.') + + if a:distance <= 0 + let l:after = max([1, line('.') + a:distance]) - 1 + else + let l:after = min([line('$'), line('.') + a:distance]) + endif + execute 'move' l:after + + if g:move_auto_indent + normal! == + endif + + " Restore the cursor column, taking indentation changes into account. + let l:new_indent = virtcol('.') + let l:new_cursor_col = max([1, l:old_cursor_col - l:old_indent + l:new_indent]) + execute 'normal!' (l:new_cursor_col . '|') +endfunction + " " In visual mode, move the selected lines vertically. " Moves down if (distance > 0) and up if (distance < 0). @@ -52,6 +84,38 @@ function s:MoveBlockVertically(distance) normal! gv endfunction +" +" In normal mode, move the character under the cursor horizontally +" Moves right (distance > 0) and left if (distance < 0). +" +function! s:MoveCharHorizontally(distance) + if !&modifiable + return + endif + + let l:curr = virtcol('.') + let l:before = l:curr + a:distance + if !g:move_past_end_of_line + let l:before = max([1, min([l:before, virtcol('$')-1])]) + endif + + if l:curr == l:before + " Don't add an empty change to the undo stack. + return + endif + + let l:old_default_register = @" + let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] + + normal! x + execute 'normal!' . (l:before.'|') + normal! P + + let &virtualedit = l:old_virtualedit + let @" = l:old_default_register + +endfunction + " " In visual mode, move the selected block to the left " Moves right (distance > 0) and left if (distance < 0). @@ -112,69 +176,6 @@ function! s:MoveBlockHorizontally(distance) endfunction -" -" In normal mode, move the current line vertically. -" Moves down if (distance > 0) and up if (distance < 0). -" -function! s:MoveLineVertically(distance) - if !&modifiable - return - endif - - " Remember the current cursor position. When we move or reindent a line - " Vim will move the cursor to the first non-blank character. - let l:old_cursor_col = virtcol('.') - normal! ^ - let l:old_indent = virtcol('.') - - if a:distance <= 0 - let l:after = max([1, line('.') + a:distance]) - 1 - else - let l:after = min([line('$'), line('.') + a:distance]) - endif - execute 'move' l:after - - if g:move_auto_indent - normal! == - endif - - " Restore the cursor column, taking indentation changes into account. - let l:new_indent = virtcol('.') - let l:new_cursor_col = max([1, l:old_cursor_col - l:old_indent + l:new_indent]) - execute 'normal!' (l:new_cursor_col . '|') -endfunction - -" -" In normal mode, move the character under the cursor horizontally -" Moves right (distance > 0) and left if (distance < 0). -" -function! s:MoveCharHorizontally(distance) - if !&modifiable - return - endif - - let l:curr = virtcol('.') - let l:before = l:curr + a:distance - if !g:move_past_end_of_line - let l:before = max([1, min([l:before, virtcol('$')-1])]) - endif - - if l:curr == l:before - " Don't add an empty change to the undo stack. - return - endif - - let l:old_default_register = @" - let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] - - normal! x - execute 'normal!' . (l:before.'|') - normal! P - - let &virtualedit = l:old_virtualedit - let @" = l:old_default_register - -endfunction function! s:HalfPageSize() return winheight('.') / 2