Skip to content

Commit 8f2480d

Browse files
committed
fix: use neovim-api for setting the cursor. (closes L3MON4D3#302)
This makes setting the cursor less vulnerable to all kinds of settings, eg. langmaps or virtualedit, which are unavoidable with "manual" movement via `hjklG`.
1 parent f082faa commit 8f2480d

File tree

5 files changed

+70
-132
lines changed

5 files changed

+70
-132
lines changed

lua/luasnip/nodes/functionNode.lua

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ local util = require("luasnip.util.util")
44
local node_util = require("luasnip.nodes.util")
55
local types = require("luasnip.util.types")
66
local events = require("luasnip.util.events")
7+
local tNode = require("luasnip.nodes.textNode")
78

89
local function F(fn, args, ...)
910
return FunctionNode:new({
@@ -15,16 +16,7 @@ local function F(fn, args, ...)
1516
})
1617
end
1718

18-
function FunctionNode:input_enter()
19-
vim.api.nvim_feedkeys(
20-
vim.api.nvim_replace_termcodes("<Esc>", true, false, true),
21-
"n",
22-
true
23-
)
24-
util.normal_move_on_insert(self.mark:pos_begin())
25-
26-
self:event(events.enter)
27-
end
19+
FunctionNode.input_enter = tNode.input_enter
2820

2921
function FunctionNode:get_static_text()
3022
-- static_text will already have been generated, if possible.

lua/luasnip/nodes/insertNode.lua

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ function ExitNode:input_enter(no_move)
3333
-- Don't enter node for -1-node, it isn't in the node-table.
3434
if self.pos == 0 then
3535
InsertNode.input_enter(self, no_move)
36-
-- -1-node:
3736
else
37+
-- -1-node:
3838
self:set_mark_rgrav(true, true)
3939
if not no_move then
40-
local mark_begin_pos = self.mark:pos_begin()
40+
local mark_begin_pos = self.mark:pos_begin_raw()
4141

4242
if vim.fn.mode() == "i" then
4343
util.insert_move_on(mark_begin_pos)
@@ -95,36 +95,15 @@ function InsertNode:input_enter(no_move)
9595
self.parent:enter_node(self.indx)
9696

9797
-- SELECT snippet text only when there is text to select (more oft than not there isnt).
98-
local mark_begin_pos, mark_end_pos = self.mark:pos_begin_end()
98+
local mark_begin_pos, mark_end_pos = self.mark:pos_begin_end_raw()
9999
if not util.pos_equal(mark_begin_pos, mark_end_pos) then
100-
vim.api.nvim_feedkeys(
101-
vim.api.nvim_replace_termcodes("<Esc>", true, false, true),
102-
"n",
103-
true
104-
)
105-
util.normal_move_on(mark_begin_pos)
106-
vim.api.nvim_feedkeys(
107-
vim.api.nvim_replace_termcodes("v", true, false, true),
108-
"n",
109-
true
110-
)
111-
-- with `exclusive` set, visual won't include the last cursor-position.
112-
-- The cursor has to be moved one column further to account for that.
113-
if vim.o.selection == "exclusive" then
114-
util.normal_move_on(mark_end_pos)
115-
else
116-
util.normal_move_before(mark_end_pos)
117-
end
118-
vim.api.nvim_feedkeys(
119-
vim.api.nvim_replace_termcodes("o<C-G>", true, false, true),
120-
"n",
121-
true
122-
)
100+
util.any_select(mark_begin_pos, mark_end_pos)
123101
else
124102
-- if current and target mode is INSERT, there's no reason to leave it.
125103
if vim.fn.mode() == "i" then
126104
util.insert_move_on(mark_begin_pos)
127105
else
106+
-- mode might be VISUAL or something else, but <Esc> always leads to normal.
128107
vim.api.nvim_feedkeys(
129108
vim.api.nvim_replace_termcodes("<Esc>", true, false, true),
130109
"n",

lua/luasnip/nodes/textNode.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ end
1515

1616
function TextNode:input_enter(no_move)
1717
if not no_move then
18-
local mark_begin_pos = self.mark:pos_begin()
18+
local mark_begin_pos = self.mark:pos_begin_raw()
1919
if vim.fn.mode() == "i" then
2020
util.insert_move_on(mark_begin_pos)
2121
else

lua/luasnip/nodes/util.lua

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,25 +92,8 @@ local function enter_nodes_between(parent, child, no_move)
9292
end
9393

9494
local function select_node(node)
95-
vim.api.nvim_feedkeys(
96-
vim.api.nvim_replace_termcodes("<Esc>", true, false, true),
97-
"n",
98-
true
99-
)
100-
-- columns in screencolumns.
101-
local node_begin, node_end = node.mark:pos_begin_end()
102-
util.normal_move_on(node_begin)
103-
vim.api.nvim_feedkeys(
104-
vim.api.nvim_replace_termcodes("v", true, false, true),
105-
"n",
106-
true
107-
)
108-
util.normal_move_before(node_end)
109-
vim.api.nvim_feedkeys(
110-
vim.api.nvim_replace_termcodes("o<C-G>", true, false, true),
111-
"n",
112-
true
113-
)
95+
local node_begin, node_end = node.mark:pos_begin_end_raw()
96+
util.any_select(node_begin, node_end)
11497
end
11598

11699
local function print_dict(dict)

lua/luasnip/util/util.lua

Lines changed: 60 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -104,89 +104,74 @@ local function bytecol_to_utfcol(pos)
104104
return { pos[1], vim.str_utfindex(line[1] or "", pos[2]) }
105105
end
106106

107-
local function normal_move_before(new_cur_pos)
108-
-- +1: indexing
109-
if new_cur_pos[2] - 1 > 0 then
110-
local keys = vim.api.nvim_replace_termcodes(
111-
tostring(new_cur_pos[1] + 1)
112-
.. "G0"
113-
.. tostring(new_cur_pos[2] - 1)
114-
.. "<Right>",
115-
true,
116-
false,
117-
true
118-
)
119-
-- passing only "n" doesn't open folds (:h feedkeys).
120-
vim.api.nvim_feedkeys(keys, "nt", true)
121-
elseif new_cur_pos[2] - 1 == 0 then
122-
vim.api.nvim_feedkeys(tostring(new_cur_pos[1] + 1) .. "G0", "nt", true)
123-
else
124-
-- column is 0, includes end of previous line. Move there.
125-
vim.api.nvim_feedkeys(tostring(new_cur_pos[1]) .. "G$", "nt", true)
126-
end
107+
local function replace_feedkeys(keys, opts)
108+
vim.api.nvim_feedkeys(
109+
vim.api.nvim_replace_termcodes(keys, true, false, true),
110+
-- folds are opened manually now, no need to pass t.
111+
-- n prevents langmap from interfering.
112+
opts or "n",
113+
true
114+
)
127115
end
128116

129-
local function normal_move_on(new_cur_pos)
130-
if new_cur_pos[2] ~= 0 then
131-
local keys = vim.api.nvim_replace_termcodes(
132-
tostring(new_cur_pos[1] + 1)
133-
.. "G0"
134-
.. tostring(new_cur_pos[2])
135-
-- open folds!
136-
.. "<Right>",
137-
true,
138-
false,
139-
true
140-
)
141-
-- passing only "n" doesn't open folds (:h feedkeys).
142-
vim.api.nvim_feedkeys(keys, "nt", true)
143-
else
144-
vim.api.nvim_feedkeys(tostring(new_cur_pos[1] + 1) .. "G0", "nt", true)
117+
-- pos: (0,0)-indexed.
118+
local function cursor_set_keys(pos, before)
119+
if before then
120+
if pos[2] == 0 then
121+
pos[1] = pos[1] - 1
122+
-- pos2 is set to last columnt of previous line.
123+
-- # counts bytes, but win_set_cursor expects bytes, so all's good.
124+
pos[2] = #vim.api.nvim_buf_get_lines(
125+
0,
126+
pos[1],
127+
pos[1] + 1,
128+
false
129+
)[1]
130+
else
131+
pos[2] = pos[2] - 1
132+
end
145133
end
134+
135+
return "<cmd>lua vim.api.nvim_win_set_cursor(0,{"
136+
-- +1, win_set_cursor starts at 1.
137+
.. pos[1] + 1
138+
.. ","
139+
-- -1 works for multibyte because of rounding, apparently.
140+
.. pos[2]
141+
.. "})"
142+
.. "<cr><cmd>:silent! foldopen!<cr>"
143+
end
144+
145+
-- any for any mode.
146+
-- other functions prefixed with eg. normal have to be in that mode, the
147+
-- initial esc removes that need.
148+
local function any_select(b, e)
149+
-- stylua: ignore
150+
replace_feedkeys(
151+
-- this esc -> movement sometimes leads to a slight flicker
152+
-- TODO: look into preventing that reliably.
153+
-- simple move -> <esc>v isn't possible, leaving insert moves the
154+
-- cursor, maybe do check for mode beforehand.
155+
"<esc>"
156+
.. cursor_set_keys(b)
157+
.. "v"
158+
.. (vim.o.selection == "exclusive" and
159+
cursor_set_keys(e) or
160+
-- set before
161+
cursor_set_keys(e, true))
162+
.. "o<C-G>" )
146163
end
147164

148165
local function normal_move_on_insert(new_cur_pos)
149-
local keys = vim.api.nvim_replace_termcodes(
150-
tostring(new_cur_pos[1] + 1)
151-
-- open folds!
152-
.. "G0i"
153-
.. string.rep("<Right>", new_cur_pos[2]),
154-
true,
155-
false,
156-
true
157-
)
158-
-- passing only "n" doesn't open folds (:h feedkeys).
159-
vim.api.nvim_feedkeys(keys, "nt", true)
166+
-- moving in normal and going into insert is kind of annoying, eg. when the
167+
-- cursor is, in normal, on a tab, i will set it on the beginning of the
168+
-- tab. There's more problems, but this is very safe.
169+
replace_feedkeys("i" .. cursor_set_keys(new_cur_pos))
160170
end
161171

162172
local function insert_move_on(new_cur_pos)
163-
local current_line = get_cursor_0ind()[1]
164-
165-
-- only compute diff for lines, seems safer and may even be faster because
166-
-- the current column isn't required (cursor returns in bytes, calculating
167-
-- columns from bytes costs too).
168-
local direction, count
169-
if current_line > new_cur_pos[1] then
170-
-- current line is lower on screen, we need to move up.
171-
direction = "<Up>"
172-
count = current_line - new_cur_pos[1]
173-
else
174-
direction = "<Down>"
175-
count = new_cur_pos[1] - current_line
176-
end
177-
178-
-- stylua: ignore
179-
local try =
180-
-- move cursor to first column
181-
"<Home>"
182-
-- move to correct line.
183-
.. direction:rep(count)
184-
-- move to correct column
185-
.. string.rep("<Right>", new_cur_pos[2])
186-
187-
local keys = vim.api.nvim_replace_termcodes(try, true, false, true)
188-
-- passing only "n" doesn't open folds (:h feedkeys).
189-
vim.api.nvim_feedkeys(keys, "nt", true)
173+
-- maybe feedkeys this too.
174+
set_cursor_0ind(new_cur_pos)
190175
end
191176

192177
local function multiline_equal(t1, t2)
@@ -569,10 +554,9 @@ return {
569554
get_cursor_0ind = get_cursor_0ind,
570555
set_cursor_0ind = set_cursor_0ind,
571556
move_to_mark = move_to_mark,
572-
normal_move_before = normal_move_before,
573-
normal_move_on = normal_move_on,
574557
normal_move_on_insert = normal_move_on_insert,
575558
insert_move_on = insert_move_on,
559+
any_select = any_select,
576560
remove_n_before_cur = remove_n_before_cur,
577561
get_current_line_to_cursor = get_current_line_to_cursor,
578562
mark_pos_equal = mark_pos_equal,

0 commit comments

Comments
 (0)