@@ -12,6 +12,59 @@ local function has_actions(actions)
1212 return type (actions ) == ' table' and # actions > 0
1313end
1414
15+ local function unchanged_prefix_len (previous_formatted , formatted_data )
16+ local previous_lines = previous_formatted and previous_formatted .lines or {}
17+ local next_lines = formatted_data and formatted_data .lines or {}
18+ local prefix_len = 0
19+
20+ for i = 1 , math.min (# previous_lines , # next_lines ) do
21+ if previous_lines [i ] ~= next_lines [i ] then
22+ break
23+ end
24+ prefix_len = i
25+ end
26+
27+ return prefix_len
28+ end
29+
30+ local function slice_lines (lines , start_idx )
31+ local slice = {}
32+ for i = start_idx , # (lines or {}) do
33+ slice [# slice + 1 ] = lines [i ]
34+ end
35+ return slice
36+ end
37+
38+ local function slice_extmarks (extmarks , start_line )
39+ local slice = {}
40+ for line_idx , marks in pairs (extmarks or {}) do
41+ if line_idx >= start_line + 1 then
42+ slice [line_idx - start_line ] = vim .deepcopy (marks )
43+ end
44+ end
45+ return slice
46+ end
47+
48+ local function highlight_written_lines (start_line , lines )
49+ if # lines == 0 then
50+ return
51+ end
52+ output_window .highlight_changed_lines (start_line , start_line + # lines - 1 )
53+ end
54+
55+ local function apply_extmarks (previous_formatted , formatted_data , line_start , old_line_end , new_line_end )
56+ local prefix_len = unchanged_prefix_len (previous_formatted , formatted_data )
57+ local clear_start = line_start + prefix_len
58+ local clear_end = math.max (old_line_end , new_line_end ) + 1
59+
60+ output_window .clear_extmarks (clear_start , clear_end )
61+
62+ local extmarks = slice_extmarks (formatted_data .extmarks , prefix_len )
63+ if has_extmarks (extmarks ) then
64+ output_window .set_extmarks (extmarks , clear_start )
65+ end
66+ end
67+
1568local function get_message_insert_line (message_id )
1669 local rendered_message = ctx .render_state :get_message (message_id )
1770 if rendered_message and rendered_message .line_start then
80133
81134local function write_at (lines , start_line , end_line )
82135 output_window .set_lines (lines , start_line , end_line )
136+ highlight_written_lines (start_line , lines )
83137 return {
84138 line_start = start_line ,
85139 line_end = start_line + # lines - 1 ,
@@ -100,13 +154,7 @@ local function apply_part_actions(part_id, formatted_data, line_start)
100154 end
101155end
102156
103- local function apply_part_extmarks (part_id , formatted_data , line_start , line_end )
104- output_window .clear_extmarks (line_start - 1 , line_end + 1 )
105-
106- if has_extmarks (formatted_data .extmarks ) then
107- output_window .set_extmarks (formatted_data .extmarks , line_start )
108- end
109-
157+ local function set_part_extmark_state (part_id , formatted_data )
110158 local part_data = ctx .render_state :get_part (part_id )
111159 if part_data then
112160 part_data .has_extmarks = has_extmarks (formatted_data .extmarks )
@@ -142,17 +190,19 @@ function M.find_part_by_call_id(call_id, message_id)
142190 return ctx .render_state :get_part_by_call_id (call_id , message_id )
143191end
144192
145- function M .upsert_message_now (message_id , formatted_data )
193+ function M .upsert_message_now (message_id , formatted_data , previous_formatted )
146194 local cached = ctx .render_state :get_message (message_id )
147195 if cached and cached .line_start and cached .line_end then
148- output_window .clear_extmarks (cached .line_start , cached .line_end + 1 )
149- output_window .set_lines (formatted_data .lines , cached .line_start , cached .line_end + 1 )
150- if has_extmarks (formatted_data .extmarks ) then
151- output_window .set_extmarks (formatted_data .extmarks , cached .line_start )
152- end
153-
154196 local old_line_end = cached .line_end
197+ local prefix_len = unchanged_prefix_len (previous_formatted , formatted_data )
198+ local write_start = cached .line_start + prefix_len
199+ local lines_to_write = slice_lines (formatted_data .lines , prefix_len + 1 )
200+
201+ output_window .set_lines (lines_to_write , write_start , cached .line_end + 1 )
202+ highlight_written_lines (write_start , lines_to_write )
203+
155204 local new_line_end = cached .line_start + # formatted_data .lines - 1
205+ apply_extmarks (previous_formatted , formatted_data , cached .line_start , old_line_end , new_line_end )
156206 ctx .render_state :set_message (cached .message , cached .line_start , new_line_end )
157207
158208 local delta = new_line_end - old_line_end
@@ -178,18 +228,25 @@ function M.upsert_message_now(message_id, formatted_data)
178228 return false
179229end
180230
181- function M .upsert_part_now (part_id , message_id , formatted_data )
231+ function M .upsert_part_now (part_id , message_id , formatted_data , previous_formatted )
182232 local cached = ctx .render_state :get_part (part_id )
183233 if cached and cached .line_start and cached .line_end then
184- output_window .set_lines (formatted_data .lines , cached .line_start , cached .line_end + 1 )
234+ local old_line_end = cached .line_end
235+ local prefix_len = unchanged_prefix_len (previous_formatted , formatted_data )
236+ local write_start = cached .line_start + prefix_len
237+ local lines_to_write = slice_lines (formatted_data .lines , prefix_len + 1 )
238+
239+ output_window .set_lines (lines_to_write , write_start , cached .line_end + 1 )
240+ highlight_written_lines (write_start , lines_to_write )
185241
186242 local new_line_end = cached .line_start + # formatted_data .lines - 1
187243 apply_part_actions (part_id , formatted_data , cached .line_start )
188244
189245 if new_line_end ~= cached .line_end then
190246 ctx .render_state :update_part_lines (part_id , cached .line_start , new_line_end )
191247 end
192- apply_part_extmarks (part_id , formatted_data , cached .line_start , new_line_end )
248+ apply_extmarks (previous_formatted , formatted_data , cached .line_start , old_line_end , new_line_end )
249+ set_part_extmark_state (part_id , formatted_data )
193250 return true
194251 end
195252
@@ -204,29 +261,35 @@ function M.upsert_part_now(part_id, message_id, formatted_data)
204261 ctx .render_state :shift_all (insert_at , # formatted_data .lines )
205262 ctx .render_state :set_part (part_data .part , range .line_start , range .line_end )
206263 apply_part_actions (part_id , formatted_data , range .line_start )
207- apply_part_extmarks (part_id , formatted_data , range .line_start , range .line_end )
264+ if has_extmarks (formatted_data .extmarks ) then
265+ output_window .set_extmarks (formatted_data .extmarks , range .line_start )
266+ end
267+ set_part_extmark_state (part_id , formatted_data )
208268 return true
209269 end
210270
211271 return false
212272end
213273
214- function M .append_part_now (part_id , extra_lines , extra_extmarks )
274+ function M .append_part_now (part_id , extra_lines , extra_extmarks , previous_formatted )
215275 local cached = ctx .render_state :get_part (part_id )
216276 if not cached or not cached .line_start or not cached .line_end or # extra_lines == 0 then
217277 return false
218278 end
219279
220280 local insert_at = cached .line_end + 1
281+ local old_line_end = cached .line_end
221282 output_window .set_lines (extra_lines , insert_at , insert_at )
283+ highlight_written_lines (insert_at , extra_lines )
222284
223285 local new_line_end = cached .line_end + # extra_lines
224286 ctx .render_state :update_part_lines (part_id , cached .line_start , new_line_end )
225287
226288 local formatted_data = ctx .formatted_parts [part_id ]
227289 if formatted_data then
228290 apply_part_actions (part_id , formatted_data , cached .line_start )
229- apply_part_extmarks (part_id , formatted_data , cached .line_start , new_line_end )
291+ apply_extmarks (previous_formatted , formatted_data , cached .line_start , old_line_end , new_line_end )
292+ set_part_extmark_state (part_id , formatted_data )
230293 elseif has_extmarks (extra_extmarks ) then
231294 output_window .set_extmarks (extra_extmarks , insert_at )
232295 end
0 commit comments