@@ -628,35 +628,86 @@ examples:
628
628
<summary >Linewise motions</summary >
629
629
630
630
``` lua
631
- local function get_line_starts (winid , skip_range )
632
- local wininfo = vim .fn .getwininfo (winid )[1 ]
633
- local cur_line = vim .fn .line (' .' )
631
+ -- ref: https://stackoverflow.com/a/6081639/14110650
632
+ local function serialize_table (val , name , skipnewlines , depth )
633
+ skipnewlines = skipnewlines or false
634
+ depth = depth or 0
635
+ local tmp = string.rep (" " , depth )
636
+ if name then
637
+ tmp = tmp .. name .. " = "
638
+ end
639
+ if type (val ) == " table" then
640
+ tmp = tmp .. " {" .. (not skipnewlines and " \n " or " " )
641
+ for k , v in pairs (val ) do
642
+ tmp = tmp
643
+ .. serialize_table (v , k , skipnewlines , depth + 1 )
644
+ .. " ,"
645
+ .. (not skipnewlines and " \n " or " " )
646
+ end
647
+ tmp = tmp .. string.rep (" " , depth ) .. " }"
648
+ elseif type (val ) == " number" then
649
+ tmp = tmp .. tostring (val )
650
+ elseif type (val ) == " string" then
651
+ tmp = tmp .. string.format (" %q" , val )
652
+ elseif type (val ) == " boolean" then
653
+ tmp = tmp .. (val and " true" or " false" )
654
+ else
655
+ tmp = tmp .. ' "[inserializeable datatype:' .. type (val ) .. ' ]"'
656
+ end
657
+ return tmp
658
+ end
659
+
660
+ local function get_line_len (bufid , line_number )
661
+ local line_content =
662
+ vim .api .nvim_buf_get_lines (bufid , line_number - 1 , line_number , false )[1 ]
663
+ return string.len (line_content )
664
+ end
665
+
666
+ local function get_line_targets (winid , skip_range , is_upward , keep_column )
667
+ local wininfo = vim .fn .getwininfo (winid )[1 ]
668
+ local bufid = vim .api .nvim_win_get_buf (winid )
669
+ local cur_line = vim .fn .line " ."
670
+ local cur_col = vim .fn .col " ."
634
671
-- Skip lines close to the cursor.
635
672
local skip_range = skip_range or 2
673
+ local keep_column = keep_column or false
674
+ local is_directional = is_upward ~= nil and true or false
636
675
637
676
-- Get targets.
638
677
local targets = {}
639
678
local lnum = wininfo .topline
679
+ local cnum = 1
640
680
while lnum <= wininfo .botline do
641
681
local fold_end = vim .fn .foldclosedend (lnum )
642
682
-- Skip folded ranges.
643
683
if fold_end ~= - 1 then
644
684
lnum = fold_end + 1
645
685
else
686
+ if is_directional then
687
+ if is_upward and lnum > cur_line - skip_range then
688
+ break
689
+ elseif not is_upward and lnum < cur_line + skip_range then
690
+ goto continue
691
+ end
692
+ end
693
+ if keep_column then
694
+ cnum = math.min (cur_col , get_line_len (bufid , lnum ))
695
+ end
646
696
if (lnum < cur_line - skip_range ) or (lnum > cur_line + skip_range ) then
647
- table.insert (targets , { pos = { lnum , 1 } })
697
+ table.insert (targets , { pos = { lnum , cnum } })
648
698
end
699
+ :: continue::
649
700
lnum = lnum + 1
650
701
end
651
702
end
652
703
653
704
-- Sort them by vertical screen distance from cursor.
654
- local cur_screen_row = vim .fn .screenpos (winid , cur_line , 1 )[' row' ]
705
+ local cur_screen_row = vim .fn .screenpos (winid , cur_line , 1 )[" row" ]
655
706
local function screen_rows_from_cur (t )
656
- local t_screen_row = vim .fn .screenpos (winid , t .pos [1 ], t .pos [2 ])[' row' ]
707
+ local t_screen_row = vim .fn .screenpos (winid , t .pos [1 ], t .pos [2 ])[" row" ]
657
708
return math.abs (cur_screen_row - t_screen_row )
658
709
end
659
- table.sort (targets , function (t1 , t2 )
710
+ table.sort (targets , function (t1 , t2 )
660
711
return screen_rows_from_cur (t1 ) < screen_rows_from_cur (t2 )
661
712
end )
662
713
@@ -665,23 +716,60 @@ local function get_line_starts(winid, skip_range)
665
716
end
666
717
end
667
718
668
- -- You can pass an argument to specify a range to be skipped
669
- -- before/after the cursor (default is +/-2).
670
- function leap_line_start (skip_range )
719
+ -- You can pass a table of arguments to specify the jump behavior:
720
+ -- skip_range - range to be skipped before/after the cursor (default is +/-2)
721
+ -- is_upward - set the jump direction, true - up, false - down (default nil - bidirectional)
722
+ -- keep_column - whether to try to keep the column after the jump (default false)
723
+ function leap_vertically (args )
724
+ local args = args or {}
725
+ local skip_range = args .skip_range
726
+ local is_upward = args .is_upward
727
+ local keep_column = args .keep_column
671
728
local winid = vim .api .nvim_get_current_win ()
672
- require (' leap' ).leap {
729
+ require (" leap" ).leap {
673
730
target_windows = { winid },
674
- targets = get_line_starts (winid , skip_range ),
731
+ targets = get_line_targets (winid , skip_range , is_upward , keep_column ),
675
732
}
676
733
end
677
734
678
735
-- For maximum comfort, force linewise selection in the mappings:
679
- vim .keymap .set (' x' , ' |' , function ()
680
- -- Only force V if not already in it (otherwise it would exit Visual mode).
681
- if vim .fn .mode (1 ) ~= ' V' then vim .cmd (' normal! V' ) end
682
- leap_line_start ()
683
- end )
684
- vim .keymap .set (' o' , ' |' , " V<cmd>lua leap_line_start()<cr>" )
736
+ -- Create mappings for "|", "<leader><leader>J", "<leader><leader>K",
737
+ -- "<leader><leader>j", "<leader><leader>k" in normal, visual, and operator-pending modes.
738
+ for key , args in pairs {
739
+ [" |" ] = { { keep_column = true }, { desc = " Leap vertically" } },
740
+ [" <leader><leader>J" ] = {
741
+ { is_upward = false },
742
+ { desc = " Leap to line start downwards" },
743
+ },
744
+ [" <leader><leader>K" ] = {
745
+ { is_upward = true },
746
+ { desc = " Leap to line start upwards" },
747
+ },
748
+ [" <leader><leader>j" ] = {
749
+ { is_upward = false , keep_column = true },
750
+ { desc = " Leap downwards" },
751
+ },
752
+ [" <leader><leader>k" ] = {
753
+ { is_upward = true , keep_column = true },
754
+ { desc = " Leap upwards" },
755
+ },
756
+ } do
757
+ for mode , rhs_expr in pairs {
758
+ n = function ()
759
+ leap_vertically (args [1 ])
760
+ end ,
761
+ x = function ()
762
+ -- Only force V if not already in it (otherwise it would exit Visual mode).
763
+ if vim .fn .mode (1 ) ~= " V" then
764
+ vim .cmd " normal! V"
765
+ end
766
+ leap_vertically (args [1 ])
767
+ end ,
768
+ o = " V<Cmd>lua leap_vertically(" .. serialize_table (args [1 ]):gsub (" \n " , " " ) .. " )<CR>" ,
769
+ } do
770
+ vim .keymap .set (mode , key , rhs_expr , args [2 ])
771
+ end
772
+ end
685
773
```
686
774
</details >
687
775
0 commit comments