From 59e641a0eb4005f04d65371790ff1423c5e0bcd1 Mon Sep 17 00:00:00 2001 From: Saul Axel Date: Tue, 21 Aug 2018 18:03:07 -0500 Subject: [PATCH 1/2] Bug corrections and minor functionality changes The lose of one column issue with 'virtualedit' wasn't related with the longest line as previously thought, but with the first line (smallest one) of selection, so changing it fixes now for real (hopefully) the bug of losing one column after moving. Using "range" in functions that didn't used visual selection was a problem when (a:firstline + count) greater than the last line so that range was deleted and replaced with at the function calls. The name 'are_same_line' was really referring to a column, so it was renamed after 'are_same_cols' Create function to initialize configurations variables to its defaults. --- doc/move.txt | 5 ++ plugin/move.vim | 204 ++++++++++++++++++++---------------------------- 2 files changed, 88 insertions(+), 121 deletions(-) diff --git a/doc/move.txt b/doc/move.txt index ed6348b..ccdf3dc 100644 --- a/doc/move.txt +++ b/doc/move.txt @@ -141,6 +141,11 @@ license. =============================================================================== 3. Changelog *move-changelog* +v1.4.1 + * Bug corrections from 1.4 + * If vertical movement receives a count past end of file, it moves to the + end instead of staying in place. + v1.4 * Released on 08/19/18 * New functionality for moving horizontally diff --git a/plugin/move.vim b/plugin/move.vim index c4a3fb3..565f339 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -1,46 +1,57 @@ " ============================================================================= " File: plugin/move.vim -" Description: Move lines and selections up and even down. +" Description: Move lines and selections up, down, left and even right to the +" infinity. " Author: Matthias Vogelgesang " ============================================================================= if exists('g:loaded_move') || &compatible finish endif - let g:loaded_move = 1 -if !exists('g:move_map_keys') - let g:move_map_keys = 1 -endif - -if !exists('g:move_key_modifier') - let g:move_key_modifier = 'A' -endif +" =====[ Configuration Variables ]===== +function! s:SetDefaultValue(var, val) + if !exists(a:var) + execute 'let' a:var '=' string(a:val) + endif +endfunction -if !exists('g:move_auto_indent') - let g:move_auto_indent = 1 -endif +call s:SetDefaultValue('g:move_map_keys', 1) +call s:SetDefaultValue('g:move_key_modifier', 'A') +call s:SetDefaultValue('g:move_auto_indent', 1) +call s:SetDefaultValue('g:move_map_keys', 1) +call s:SetDefaultValue('g:move_past_end_of_line', 1) -if !exists('g:move_past_end_of_line') - let g:move_past_end_of_line = 1 -endif +" =====[ Script local variables ]===== +let s:command_blockwise_selection = "\" +" After deleting and pasting "gv" would select the old position but the +" following command selects the position of the pasted text +let s:command_select_after_P = '`[' . s:command_blockwise_selection . '`]' +" =====[ Utility functions ]===== function! s:ResetCursor() normal! gv=gv^ endfunction -function! s:MoveBlockDown(start, end, count) - let l:next_line = a:end + a:count - - if v:count > 0 - let l:next_line = l:next_line + v:count - 1 +function! s:AssertBlockwiseVisual(mode) + if a:mode ==# 'v' + execute 'normal!' s:command_blockwise_selection endif +endfunction - if l:next_line > line('$') - call s:ResetCursor() - return - endif +function! s:HalfWin() + return winheight('.') / 2 +endfunction + +function! s:MoveKey(key) + return '<' . g:move_key_modifier . '-' . a:key . '>' +endfunction + +" =====[ Functionality ]===== +function! s:MoveBlockDown(start, end, num) + let l:next_line = a:end + a:num + max([0, v:count - 1]) + let l:next_line = min([line('$'), l:next_line]) execute 'silent' a:start ',' a:end 'move ' l:next_line if (g:move_auto_indent == 1) @@ -50,17 +61,9 @@ function! s:MoveBlockDown(start, end, count) endif endfunction -function! s:MoveBlockUp(start, end, count) - let l:prev_line = a:start - a:count - 1 - - if v:count > 0 - let l:prev_line = l:prev_line - v:count + 1 - endif - - if l:prev_line < 0 - call s:ResetCursor() - return - endif +function! s:MoveBlockUp(start, end, num) + let l:prev_line = a:start - a:num - 1 - max([0, v:count - 1]) + let l:prev_line = max([0, l:prev_line]) execute 'silent' a:start ',' a:end 'move ' l:prev_line if (g:move_auto_indent == 1) @@ -71,36 +74,33 @@ function! s:MoveBlockUp(start, end, count) endfunction function! s:MoveBlockLeft() range - if visualmode() ==# "\" - echomsg 'MoveBlockLeft can only be used in visual block' - endif + call s:AssertBlockwiseVisual(visualmode()) let l:distance = v:count ? v:count : 1 - - let l:min_col = min([col("'<"), col("'>")]) + let l:min_col = min([col("'<"), col("'>")]) let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'onemore'] + + let l:move_command = 'silent normal! gvd' if l:min_col - l:distance <= 1 - execute "silent normal! gvd0P`[\`]" + let l:move_command .= '0P' else - execute 'silent normal! gvd' . l:distance . "hP`[\`]" + let l:move_command .= l:distance . 'hP' endif + execute l:move_command . s:command_select_after_P let &virtualedit = l:old_virtualedit endfunction function! s:MoveBlockRight() range - if visualmode() ==# "\" - echomsg 'MoveBlockLeft can only be used in visual block' - endif + call s:AssertBlockwiseVisual(visualmode()) let l:distance = v:count ? v:count : 1 - let l:lens = map(getline(a:firstline, a:lastline), 'len(v:val)') - let [l:shorter_line_len, l:longer_line_len] = [min(l:lens), max(l:lens)] + let l:shorter_line_len = min(l:lens) - let l:are_same_lines = col("'<") == col("'>") - let l:max_col = max([col("'<"), col("'>")]) + let l:are_same_cols = (col("'<") == col("'>")) + let l:max_col = max([col("'<"), col("'>")]) if !g:move_past_end_of_line && (l:max_col + l:distance >= l:shorter_line_len) let l:distance = l:shorter_line_len - l:max_col @@ -112,67 +112,54 @@ function! s:MoveBlockRight() range endif let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'all'] - execute 'silent normal! gvd' . l:distance . "lP`[\`]" - " Very strange things happen with 'virtualedit' set to all. One of the is that - " the selection loses one column at the left at reselection. - " The next line fixes it - if !l:are_same_lines && (l:max_col + l:distance < l:longer_line_len) + execute 'silent normal! gvd' . l:distance . 'lP' . s:command_select_after_P + + " When 'virtualedit' is set to all the selection loses one column at the + " left at reselection. The next line fixes it + if !l:are_same_cols && (l:max_col + l:distance < l:lens[0]) normal! oho endif - let &virtualedit = l:old_virtualedit + let &virtualedit = l:old_virtualedit endfunction -function! s:MoveLineUp(count) range - let l:distance = a:count + 1 - - if v:count > 0 - let l:distance = l:distance + v:count - 1 - endif +function! s:MoveLineUp(count) + let l:distance = a:count + 1 + max([0, v:count - 1]) + let l:command = 'silent move ' if (line('.') - l:distance) < 0 - execute 'silent move 0' - if (g:move_auto_indent == 1) - normal! == - endif - return + let l:command .= '0' + else + let l:command .= '-' . l:distance endif - - execute 'silent m-' . l:distance + execute l:command if (g:move_auto_indent == 1) normal! == endif endfunction -function! s:MoveLineDown(count) range - let l:distance = a:count - - if v:count > 0 - let l:distance = l:distance + v:count - 1 - endif +function! s:MoveLineDown(count) + let l:distance = a:count + max([0, v:count - 1]) + let l:command = 'silent move ' if (line('.') + l:distance) > line('$') - silent move $ - if (g:move_auto_indent == 1) - normal! == - endif - return + let l:command .= '$' + else + let l:command .= '+' . l:distance endif + execute l:command - execute 'silent m+' . l:distance if (g:move_auto_indent == 1) normal! == endif endfunction -" Using range here fucks the col() function (because col() always returns 1 in -" range functions), so use normal function and clear the range with later function! s:MoveCharLeft() let l:distance = v:count ? v:count : 1 - if (col('.') - l:distance <= 0) + if (col('.') - l:distance <= 1) silent normal! x0P return endif @@ -195,54 +182,29 @@ function! s:MoveCharRight() let &virtualedit = l:old_virtualedit endfunction -function! s:MoveBlockOneLineUp() range - call s:MoveBlockUp(a:firstline, a:lastline, 1) +function! s:MoveBlockNumDown(num) range + call s:MoveBlockDown(a:firstline, a:lastline, a:num) endfunction -function! s:MoveBlockOneLineDown() range - call s:MoveBlockDown(a:firstline, a:lastline, 1) +function! s:MoveBlockNumUp(num) range + call s:MoveBlockUp(a:firstline, a:lastline, a:num) endfunction -function! s:MoveBlockHalfPageUp() range - let l:distance = winheight('.') / 2 - call s:MoveBlockUp(a:firstline, a:lastline, l:distance) -endfunction - -function! s:MoveBlockHalfPageDown() range - let l:distance = winheight('.') / 2 - call s:MoveBlockDown(a:firstline, a:lastline, l:distance) -endfunction - -function! s:MoveLineHalfPageUp() range - let l:distance = winheight('.') / 2 - call s:MoveLineUp(l:distance) -endfunction - -function! s:MoveLineHalfPageDown() range - let l:distance = winheight('.') / 2 - call s:MoveLineDown(l:distance) -endfunction - -function! s:MoveKey(key) - return '<' . g:move_key_modifier . '-' . a:key . '>' -endfunction - - -vnoremap MoveBlockDown :call MoveBlockOneLineDown() -vnoremap MoveBlockUp :call MoveBlockOneLineUp() -vnoremap MoveBlockHalfPageDown :call MoveBlockHalfPageDown() -vnoremap MoveBlockHalfPageUp :call MoveBlockHalfPageUp() +" =====[ API ]===== +vnoremap MoveBlockDown :call MoveBlockNumDown(1) +vnoremap MoveBlockUp :call MoveBlockNumUp(1) +vnoremap MoveBlockHalfPageDown :call MoveBlockNumDown(s:HalfWin()) +vnoremap MoveBlockHalfPageUp :call MoveBlockNumUp(s:HalfWin()) vnoremap MoveBlockLeft :call MoveBlockLeft() vnoremap MoveBlockRight :call MoveBlockRight() -nnoremap MoveLineDown :call MoveLineDown(1) -nnoremap MoveLineUp :call MoveLineUp(1) -nnoremap MoveLineHalfPageDown :call MoveLineHalfPageDown() -nnoremap MoveLineHalfPageUp :call MoveLineHalfPageUp() +nnoremap MoveLineDown :call MoveLineDown(1) +nnoremap MoveLineUp :call MoveLineUp(1) +nnoremap MoveLineHalfPageDown :call MoveLineDown(s:HalfWin()) +nnoremap MoveLineHalfPageUp :call MoveLineUp(s:HalfWin()) nnoremap MoveCharLeft :call MoveCharLeft() nnoremap MoveCharRight :call MoveCharRight() - if g:move_map_keys execute 'vmap' s:MoveKey('j') 'MoveBlockDown' execute 'vmap' s:MoveKey('k') 'MoveBlockUp' From 592751c40c58bb977b677e25af67ab7159619500 Mon Sep 17 00:00:00 2001 From: Saul Axel Date: Wed, 22 Aug 2018 16:41:25 -0500 Subject: [PATCH 2/2] Configuration dissection using dictionaries The variable g:move_map_key can also be a dictionary to permit enabling/disabling of parts of the default mappings using the entries 'vertical' and 'horizontal' and its subentries 'normal' and 'visual'. --- doc/move.txt | 24 ++++++++++++++++++++++- plugin/move.vim | 52 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 65 insertions(+), 11 deletions(-) diff --git a/doc/move.txt b/doc/move.txt index ccdf3dc..34fd581 100644 --- a/doc/move.txt +++ b/doc/move.txt @@ -20,10 +20,31 @@ wrapping the :move command. =============================================================================== 2. Mappings *move-mappings* -To enable custom key maps you must disable the automatic key maps with > +To enable/disable automatic key maps you can define the variable +g:move_map_keys in to 1 or 0 respectively. IE, if you want to define your own +key mappings you would put in your vimrc: > let g:move_map_keys = 0 +The default value of g:move_map_keys is 1 so all mappings are usually defined. + +The automatic key maps also can be enabled/disabled by parts defining +g:move_map_keys as a dictionary instad of an integer. This can be +done through the entries 'vertical' and 'horizontal' each of which should be +also a dictionary with the integer (boolean) entries 'normal' and 'visual'. +With that in mind, if you want to have the vertical mappings only enabled in +visual mode but the horizontal mappings to be enabled in both modes you can +define g:move_map_keys as: > + + let g:move_map_keys = {} + let g:move_map_keys.vertical = {'normal': 0, 'visual': 1} + let g:move_map_keys.horizontal = {'normal': 1, 'visual': 1} + +Or, as the default values of all entries are also 1, it can be simply +written as: > + + let g:move_map_keys = {'vertical': {'normal': 0}} + The plugin provide finger-friendly mappings to move text around by using keys. You can specify the key modifier that is used in key bindings with > @@ -145,6 +166,7 @@ v1.4.1 * Bug corrections from 1.4 * If vertical movement receives a count past end of file, it moves to the end instead of staying in place. + * The standard mappings can be enabled by parts through a dictionary. v1.4 * Released on 08/19/18 diff --git a/plugin/move.vim b/plugin/move.vim index 565f339..6e6071d 100644 --- a/plugin/move.vim +++ b/plugin/move.vim @@ -10,7 +10,7 @@ if exists('g:loaded_move') || &compatible endif let g:loaded_move = 1 -" =====[ Configuration Variables ]===== +" =====[ Configuration Variables ]================ function! s:SetDefaultValue(var, val) if !exists(a:var) execute 'let' a:var '=' string(a:val) @@ -23,13 +23,22 @@ call s:SetDefaultValue('g:move_auto_indent', 1) call s:SetDefaultValue('g:move_map_keys', 1) call s:SetDefaultValue('g:move_past_end_of_line', 1) -" =====[ Script local variables ]===== +if type(g:move_map_keys) == type({}) + call s:SetDefaultValue('g:move_map_keys.vertical', {}) + call s:SetDefaultValue('g:move_map_keys.vertical.normal', 1) + call s:SetDefaultValue('g:move_map_keys.vertical.visual', 1) + call s:SetDefaultValue('g:move_map_keys.horizontal', {}) + call s:SetDefaultValue('g:move_map_keys.horizontal.normal', 1) + call s:SetDefaultValue('g:move_map_keys.horizontal.visual', 1) +endif + +" =====[ Script local variables ]================= let s:command_blockwise_selection = "\" " After deleting and pasting "gv" would select the old position but the " following command selects the position of the pasted text let s:command_select_after_P = '`[' . s:command_blockwise_selection . '`]' -" =====[ Utility functions ]===== +" =====[ Utility functions ]====================== function! s:ResetCursor() normal! gv=gv^ endfunction @@ -48,7 +57,7 @@ function! s:MoveKey(key) return '<' . g:move_key_modifier . '-' . a:key . '>' endfunction -" =====[ Functionality ]===== +" =====[ Functionality ]========================== function! s:MoveBlockDown(start, end, num) let l:next_line = a:end + a:num + max([0, v:count - 1]) let l:next_line = min([line('$'), l:next_line]) @@ -79,6 +88,8 @@ function! s:MoveBlockLeft() range let l:distance = v:count ? v:count : 1 let l:min_col = min([col("'<"), col("'>")]) + " Having 'virtualenv' set to 'onemore' fixes problem of one more movement + " that needed when moving block from end of line to the left let [l:old_virtualedit, &virtualedit] = [&virtualedit, 'onemore'] let l:move_command = 'silent normal! gvd' @@ -190,7 +201,7 @@ function! s:MoveBlockNumUp(num) range call s:MoveBlockUp(a:firstline, a:lastline, a:num) endfunction -" =====[ API ]===== +" =====[ API ]=================================== vnoremap MoveBlockDown :call MoveBlockNumDown(1) vnoremap MoveBlockUp :call MoveBlockNumUp(1) vnoremap MoveBlockHalfPageDown :call MoveBlockNumDown(s:HalfWin()) @@ -205,14 +216,35 @@ nnoremap MoveLineHalfPageUp :call MoveLineUp(s:Hal nnoremap MoveCharLeft :call MoveCharLeft() nnoremap MoveCharRight :call MoveCharRight() -if g:move_map_keys - execute 'vmap' s:MoveKey('j') 'MoveBlockDown' - execute 'vmap' s:MoveKey('k') 'MoveBlockUp' - execute 'vmap' s:MoveKey('h') 'MoveBlockLeft' - execute 'vmap' s:MoveKey('l') 'MoveBlockRight' +function! s:UserWantMap(movement, mode) + " In vim 8, v:t_number can be used instead of type(0) and v:t_dict instead + " of type({}), but at the cost of losing compatibility with previous + " versions + if type(g:move_map_keys) == type(0) + return g:move_map_keys != 0 + endif + + if type(g:move_map_keys) == type({}) + return g:move_map_keys[a:movement][a:mode] != 0 + endif +endfunction + +if s:UserWantMap('vertical', 'visual') + execute 'xmap' s:MoveKey('j') 'MoveBlockDown' + execute 'xmap' s:MoveKey('k') 'MoveBlockUp' +endif + +if s:UserWantMap('horizontal', 'visual') + execute 'xmap' s:MoveKey('h') 'MoveBlockLeft' + execute 'xmap' s:MoveKey('l') 'MoveBlockRight' +endif +if s:UserWantMap('vertical', 'normal') execute 'nmap' s:MoveKey('j') 'MoveLineDown' execute 'nmap' s:MoveKey('k') 'MoveLineUp' +endif + +if s:UserWantMap('horizontal', 'normal') execute 'nmap' s:MoveKey('h') 'MoveCharLeft' execute 'nmap' s:MoveKey('l') 'MoveCharRight' endif