Read-only mirror of https://codeberg.org/andyg/leap.nvim
at main 127 lines 4.1 kB view raw
1local api = vim.api 2 3local function is_cursor_before_eol() 4 return vim.fn.virtcol('.') == (vim.fn.virtcol('$') - 1) 5end 6 7local function is_cursor_before_eof() 8 return is_cursor_before_eol and vim.fn.line('.') == vim.fn.line('$') 9end 10 11--- Pushes cursor 1 character forward or backward, possibly beyond EOL. 12local function push_cursor(dir) 13 vim.fn.search('\\_.', (dir == 'fwd') and 'W' or (dir == 'bwd') and 'bW') 14end 15 16local function push_beyond_eol() 17 local saved = vim.o.virtualedit 18 vim.o.virtualedit = 'onemore' 19 -- Note: No need to undo this afterwards, the cursor will be moved to 20 -- the end of the operated area anyway. 21 vim.cmd('norm! l') 22 api.nvim_create_autocmd({ 23 'CursorMoved', 'WinLeave', 'BufLeave', 24 'InsertEnter', 'CmdlineEnter', 'CmdwinEnter', 25 }, { 26 once = true, 27 callback = function() vim.o.virtualedit = saved end, 28 }) 29end 30 31local function add_offset(offset) 32 if offset < 0 then 33 push_cursor('bwd') 34 elseif offset > 0 then 35 if is_cursor_before_eol() then 36 push_beyond_eol() 37 else 38 push_cursor('fwd') 39 end 40 -- deprecated 41 if offset > 1 then 42 if is_cursor_before_eol() then 43 push_beyond_eol() 44 else 45 push_cursor('fwd') 46 end 47 end 48 end 49end 50 51--- When applied after an exclusive motion (like setting the cursor via 52--- the API), makes the motion appear to behave as an inclusive one. 53local function simulate_inclusive_op(mode) 54 local force = vim.fn.matchstr(mode, '^no\\zs.') 55 if force == '' then 56 -- In the charwise case, we should push the cursor forward. 57 if is_cursor_before_eof() then 58 push_beyond_eol() 59 else 60 push_cursor('fwd') 61 end 62 elseif force == 'v' then 63 -- We also want the `v` modifier to behave in the native way, that 64 -- is, as inclusive/exclusive toggle on charwise motions (:h o_v). 65 -- As `v` will change our (technically) exclusive motion to 66 -- inclusive, we should push the cursor back to undo that. 67 push_cursor('bwd') 68 else 69 -- Blockwise (<c-v>) itself makes the motion inclusive. 70 end 71end 72 73local function force_matchparen_refresh() 74 -- HACK: :DoMatchParen turns matchparen on simply by triggering 75 -- CursorMoved events (see matchparen.vim). We can do the same, which 76 -- is cleaner for us than calling :DoMatchParen directly, since that 77 -- would wrap this in a `windo`, and might visit another buffer, 78 -- breaking our visual selection (and thus also dot-repeat, 79 -- apparently). (See :h visual-start, and lightspeed#38.) 80 -- Programming against the API would be more robust of course, but in 81 -- the unlikely case that the implementation details would change, 82 -- this still cannot do any damage on our side if called with pcall 83 -- (the feature just ceases to work then). 84 pcall(api.nvim_exec_autocmds, 'CursorMoved', { group = 'matchparen' }) 85 pcall(api.nvim_exec_autocmds, 'CursorMoved', { group = 'matchup_matchparen' }) 86end 87 88local function jump_to(pos, kwargs) 89 local lnum, col = unpack(pos) 90 local win = kwargs.win 91 local mode = kwargs.mode 92 local offset = kwargs.offset 93 local is_backward = kwargs.is_backward 94 local is_inclusive = kwargs.is_inclusive 95 local add_to_jumplist = kwargs.add_to_jumplist 96 local is_op_mode = mode:match('o') 97 98 if add_to_jumplist then 99 -- Note: <C-o> will ignore this on the same line (neovim#9874). 100 vim.cmd('norm! m`') 101 end 102 103 -- Move. 104 if win ~= api.nvim_get_current_win() then 105 api.nvim_set_current_win(win) 106 end 107 api.nvim_win_set_cursor(0, { lnum, col - 1 }) 108 if offset then 109 add_offset(offset) 110 end 111 if is_op_mode and is_inclusive and not is_backward then 112 -- Since Vim interprets our jump as exclusive (:h exclusive), we 113 -- need custom tweaks to behave as inclusive. (This is only 114 -- relevant in the forward direction, as inclusiveness applies to 115 -- the end of the selection.) 116 simulate_inclusive_op(mode) 117 end 118 119 -- Refresh view. 120 if not is_op_mode then 121 force_matchparen_refresh() 122 end 123end 124 125return { 126 jump_to = jump_to 127}