Read-only mirror of https://codeberg.org/andyg/leap.nvim
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}