Skip to content

Commit c921b8a

Browse files
committed
refactor(paste): use text change animation instead of relying on event
1 parent 87c9e1c commit c921b8a

File tree

3 files changed

+149
-146
lines changed

3 files changed

+149
-146
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
local M = {}
2+
3+
---@return nil
4+
function M.handle_text_change_animation(callback)
5+
local detach_listener = false
6+
local ranges = {}
7+
local iter = 0
8+
9+
---Callback function for buffer changes
10+
---@param event any Event type
11+
---@param bufnr number Buffer number
12+
---@param changedtick number Changed tick
13+
---@param start_row number Starting row of change
14+
---@param start_col number Starting column of change
15+
---@param byte_offset number Byte offset
16+
---@param old_end_row number Old end row
17+
---@param old_end_col number Old end column
18+
---@param old_byte_end number Old end byte
19+
---@param new_end_row number New end row
20+
---@param new_end_col number New end column
21+
---@param new_byte_end number New end byte
22+
function M.on_bytes(
23+
event,
24+
bufnr,
25+
changedtick,
26+
start_row,
27+
start_col,
28+
byte_offset,
29+
old_end_row,
30+
old_end_col,
31+
old_byte_end,
32+
new_end_row,
33+
new_end_col,
34+
new_byte_end
35+
)
36+
if detach_listener then
37+
return true
38+
end
39+
40+
-- Calculate the affected text range
41+
local buffer_line_count = vim.api.nvim_buf_line_count(0)
42+
local end_row = start_row + new_end_row
43+
local end_col = start_col + new_end_col
44+
45+
-- Adjust end column for changes at buffer end
46+
if end_row >= buffer_line_count then
47+
local last_line = vim.api.nvim_buf_get_lines(0, -2, -1, false)[1]
48+
end_col = #last_line
49+
end
50+
51+
local range = {
52+
start_line = start_row,
53+
start_col = start_col,
54+
end_line = end_row,
55+
end_col = end_col,
56+
}
57+
58+
table.insert(ranges, range)
59+
60+
iter = iter + 1
61+
end
62+
63+
-- Attach buffer listener
64+
vim.api.nvim_buf_attach(0, false, {
65+
on_bytes = M.on_bytes,
66+
})
67+
68+
vim.schedule(function()
69+
detach_listener = true
70+
71+
local final_ranges = {}
72+
73+
-- Sort ranges by start line and then by start column
74+
table.sort(ranges, function(a, b)
75+
if a.start_line == b.start_line then
76+
return a.start_col < b.start_col
77+
end
78+
return a.start_line < b.start_line
79+
end)
80+
81+
if #ranges > 0 then
82+
local current = ranges[1]
83+
84+
for i = 2, #ranges do
85+
local next_range = ranges[i]
86+
87+
-- Check if ranges overlap or are adjacent
88+
if
89+
current.end_line < next_range.start_line
90+
or (current.end_line == next_range.start_line and current.end_col < next_range.start_col)
91+
then
92+
-- No overlap, add current range and start new one
93+
if current.start_line ~= current.end_line or current.start_col ~= current.end_col then
94+
table.insert(final_ranges, current)
95+
end
96+
current = next_range
97+
else
98+
-- Merge overlapping ranges
99+
current.end_line = math.max(current.end_line, next_range.end_line)
100+
if current.end_line == next_range.end_line then
101+
current.end_col = math.max(current.end_col, next_range.end_col)
102+
end
103+
end
104+
end
105+
106+
if current.start_line ~= current.end_line or current.start_col ~= current.end_col then
107+
table.insert(final_ranges, current)
108+
end
109+
end
110+
111+
vim.schedule(function()
112+
if callback then
113+
callback(final_ranges)
114+
end
115+
end)
116+
end)
117+
end
118+
119+
return M

lua/tiny-glimmer/overwrite/paste.lua

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,25 @@
11
local M = {}
2-
local utils = require("tiny-glimmer.utils")
3-
local AnimationFactory = require("tiny-glimmer.animation.factory")
42

5-
local function split_lines(text)
6-
local lines = {}
3+
local handle_text_change_animation = require("tiny-glimmer.animation.text_change").handle_text_change_animation
74

8-
for i = 0, vim.v.count1 - 1 do
9-
for line in text:gmatch("[^\r\n]+") do
10-
table.insert(lines, line)
5+
local function create_callback(opts)
6+
return function(ranges)
7+
for i = 1, #ranges do
8+
local range = ranges[i]
9+
10+
if ranges[i] ~= nil then
11+
require("tiny-glimmer.animation.factory")
12+
.get_instance()
13+
:create_named_text_animation("paste_" .. i, opts.default_animation, {
14+
base = { range = range },
15+
})
16+
end
1117
end
1218
end
13-
14-
return lines
1519
end
1620

1721
local function animate_paste(opts, mode)
18-
local register = vim.v.register or '"'
19-
local text = split_lines(vim.fn.getreg(register, true))
20-
21-
vim.defer_fn(function()
22-
local range = utils.get_range_yank()
23-
24-
AnimationFactory.get_instance():create_text_animation(opts.default_animation, {
25-
base = {
26-
range = range,
27-
},
28-
is_paste = true,
29-
content = text,
30-
})
31-
end, 10)
22+
handle_text_change_animation(create_callback(opts))
3223
end
3324

3425
function M.paste(opts)

lua/tiny-glimmer/overwrite/undo.lua

Lines changed: 16 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,33 @@
11
local M = {}
22

3-
---@param opts table Animation options
4-
---@return nil
5-
local function handle_text_change_animation(opts)
6-
local detach_listener = false
7-
local ranges = {}
8-
local iter = 0
9-
10-
---Callback function for buffer changes
11-
---@param event any Event type
12-
---@param bufnr number Buffer number
13-
---@param changedtick number Changed tick
14-
---@param start_row number Starting row of change
15-
---@param start_col number Starting column of change
16-
---@param byte_offset number Byte offset
17-
---@param old_end_row number Old end row
18-
---@param old_end_col number Old end column
19-
---@param old_byte_end number Old end byte
20-
---@param new_end_row number New end row
21-
---@param new_end_col number New end column
22-
---@param new_byte_end number New end byte
23-
function M.on_bytes(
24-
event,
25-
bufnr,
26-
changedtick,
27-
start_row,
28-
start_col,
29-
byte_offset,
30-
old_end_row,
31-
old_end_col,
32-
old_byte_end,
33-
new_end_row,
34-
new_end_col,
35-
new_byte_end
36-
)
37-
if detach_listener then
38-
return true
39-
end
40-
41-
-- Calculate the affected text range
42-
local buffer_line_count = vim.api.nvim_buf_line_count(0)
43-
local end_row = start_row + new_end_row
44-
local end_col = start_col + new_end_col
45-
46-
-- Adjust end column for changes at buffer end
47-
if end_row >= buffer_line_count then
48-
local last_line = vim.api.nvim_buf_get_lines(0, -2, -1, false)[1]
49-
end_col = #last_line
50-
end
51-
52-
local range = {
53-
start_line = start_row,
54-
start_col = start_col,
55-
end_line = end_row,
56-
end_col = end_col,
57-
}
58-
59-
table.insert(ranges, range)
60-
61-
iter = iter + 1
62-
end
63-
64-
-- Attach buffer listener
65-
vim.api.nvim_buf_attach(0, false, {
66-
on_bytes = M.on_bytes,
67-
})
68-
69-
vim.schedule(function()
70-
detach_listener = true
71-
72-
local final_ranges = {}
73-
74-
-- Sort ranges by start line and then by start column
75-
table.sort(ranges, function(a, b)
76-
if a.start_line == b.start_line then
77-
return a.start_col < b.start_col
78-
end
79-
return a.start_line < b.start_line
80-
end)
81-
82-
if #ranges > 0 then
83-
local current = ranges[1]
84-
85-
for i = 2, #ranges do
86-
local next_range = ranges[i]
87-
88-
-- Check if ranges overlap or are adjacent
89-
if
90-
current.end_line < next_range.start_line
91-
or (current.end_line == next_range.start_line and current.end_col < next_range.start_col)
92-
then
93-
-- No overlap, add current range and start new one
94-
if current.start_line ~= current.end_line or current.start_col ~= current.end_col then
95-
table.insert(final_ranges, current)
96-
end
97-
current = next_range
98-
else
99-
-- Merge overlapping ranges
100-
current.end_line = math.max(current.end_line, next_range.end_line)
101-
if current.end_line == next_range.end_line then
102-
current.end_col = math.max(current.end_col, next_range.end_col)
103-
end
104-
end
105-
end
106-
107-
if current.start_line ~= current.end_line or current.start_col ~= current.end_col then
108-
table.insert(final_ranges, current)
3+
local handle_text_change_animation = require("tiny-glimmer.animation.text_change").handle_text_change_animation
4+
5+
local function create_callback(opts)
6+
return function(ranges)
7+
for i = 1, #ranges do
8+
local range = ranges[i]
9+
10+
if ranges[i] ~= nil then
11+
require("tiny-glimmer.animation.factory")
12+
.get_instance()
13+
:create_named_text_animation("undo_" .. i, opts.default_animation, {
14+
base = { range = range },
15+
})
10916
end
11017
end
111-
112-
vim.schedule(function()
113-
for i = 1, #final_ranges do
114-
local range = final_ranges[i]
115-
116-
if final_ranges[i] ~= nil then
117-
require("tiny-glimmer.animation.factory")
118-
.get_instance()
119-
:create_named_text_animation("undo_" .. i, opts.default_animation, {
120-
base = { range = range },
121-
})
122-
end
123-
end
124-
end)
125-
end)
18+
end
12619
end
12720

12821
---Animate undo operation
12922
---@param opts table Animation options
13023
function M.undo(opts)
131-
handle_text_change_animation(opts)
24+
handle_text_change_animation(create_callback(opts))
13225
end
13326

13427
---Animate redo operation
13528
---@param opts table Animation options
13629
function M.redo(opts)
137-
handle_text_change_animation(opts)
30+
handle_text_change_animation(create_callback(opts))
13831
end
13932

14033
return M

0 commit comments

Comments
 (0)