@@ -20,6 +20,7 @@ local state = require('opencode.state')
2020--- @field _messages table<string , RenderedMessage> Message ID to render data
2121--- @field _parts table<string , RenderedPart> Part ID to render data
2222--- @field _line_index LineIndex Line number to ID mappings
23+ --- @field _line_index_valid boolean Whether line index is up to date
2324local RenderState = {}
2425RenderState .__index = RenderState
2526
@@ -37,6 +38,7 @@ function RenderState:reset()
3738 line_to_part = {},
3839 line_to_message = {},
3940 }
41+ self ._line_index_valid = false
4042end
4143
4244--- Get message render data by ID
@@ -71,10 +73,18 @@ function RenderState:get_part_by_call_id(call_id, message_id)
7173 return nil
7274end
7375
76+ --- Ensure line index is up to date
77+ function RenderState :_ensure_line_index ()
78+ if not self ._line_index_valid then
79+ self :_rebuild_line_index ()
80+ end
81+ end
82+
7483--- Get part at specific line
7584--- @param line integer Line number (1-indexed )
7685--- @return RenderedPart ?
7786function RenderState :get_part_at_line (line )
87+ self :_ensure_line_index ()
7888 local part_id = self ._line_index .line_to_part [line ]
7989 if not part_id then
8090 return nil
8696--- @param line integer Line number (1-indexed )
8797--- @return RenderedMessage ?
8898function RenderState :get_message_at_line (line )
99+ self :_ensure_line_index ()
89100 local message_id = self ._line_index .line_to_message [line ]
90101 if not message_id then
91102 return nil
97108--- @param line integer Line number (1-indexed )
98109--- @return table[] List of actions at that line
99110function RenderState :get_actions_at_line (line )
111+ self :_ensure_line_index ()
100112 local part_id = self ._line_index .line_to_part [line ]
101113 if not part_id then
102114 return {}
@@ -140,9 +152,7 @@ function RenderState:set_message(message_id, message_ref, line_start, line_end)
140152 end
141153
142154 if line_start and line_end then
143- for line = line_start , line_end do
144- self ._line_index .line_to_message [line ] = message_id
145- end
155+ self ._line_index_valid = false
146156 end
147157end
148158
@@ -174,9 +184,7 @@ function RenderState:set_part(part_id, part, message_id, line_start, line_end)
174184 end
175185
176186 if line_start and line_end then
177- for line = line_start , line_end do
178- self ._line_index .line_to_part [line ] = part_id
179- end
187+ self ._line_index_valid = false
180188 end
181189end
182190
@@ -197,16 +205,10 @@ function RenderState:update_part_lines(part_id, new_line_start, new_line_end)
197205 local new_line_count = new_line_end - new_line_start + 1
198206 local delta = new_line_count - old_line_count
199207
200- for line = old_line_start , old_line_end do
201- self ._line_index .line_to_part [line ] = nil
202- end
203-
204208 part_data .line_start = new_line_start
205209 part_data .line_end = new_line_end
206210
207- for line = new_line_start , new_line_end do
208- self ._line_index .line_to_part [line ] = part_id
209- end
211+ self ._line_index_valid = false
210212
211213 if delta ~= 0 then
212214 self :shift_all (old_line_end + 1 , delta )
@@ -297,11 +299,8 @@ function RenderState:remove_part(part_id)
297299 local line_count = part_data .line_end - part_data .line_start + 1
298300 local shift_from = part_data .line_end + 1
299301
300- for line = part_data .line_start , part_data .line_end do
301- self ._line_index .line_to_part [line ] = nil
302- end
303-
304302 self ._parts [part_id ] = nil
303+ self ._line_index_valid = false
305304
306305 self :shift_all (shift_from , - line_count )
307306
@@ -320,11 +319,8 @@ function RenderState:remove_message(message_id)
320319 local line_count = msg_data .line_end - msg_data .line_start + 1
321320 local shift_from = msg_data .line_end + 1
322321
323- for line = msg_data .line_start , msg_data .line_end do
324- self ._line_index .line_to_message [line ] = nil
325- end
326-
327322 self ._messages [message_id ] = nil
323+ self ._line_index_valid = false
328324
329325 self :shift_all (shift_from , - line_count )
330326
@@ -341,7 +337,7 @@ function RenderState:shift_all(from_line, delta)
341337 end
342338
343339 local found_content_before_from_line = false
344- local shifted = false
340+ local anything_shifted = false
345341
346342 for i = # state .messages , 1 , - 1 do
347343 local msg_wrapper = state .messages [i ]
@@ -353,7 +349,7 @@ function RenderState:shift_all(from_line, delta)
353349 if msg_data .line_start >= from_line then
354350 msg_data .line_start = msg_data .line_start + delta
355351 msg_data .line_end = msg_data .line_end + delta
356- shifted = true
352+ anything_shifted = true
357353 elseif msg_data .line_end < from_line then
358354 found_content_before_from_line = true
359355 end
@@ -369,7 +365,7 @@ function RenderState:shift_all(from_line, delta)
369365 if part_data .line_start >= from_line then
370366 part_data .line_start = part_data .line_start + delta
371367 part_data .line_end = part_data .line_end + delta
372- shifted = true
368+ anything_shifted = true
373369
374370 if part_data .actions then
375371 for _ , action in ipairs (part_data .actions ) do
@@ -389,8 +385,8 @@ function RenderState:shift_all(from_line, delta)
389385 end
390386 end
391387
392- if shifted then
393- self : _rebuild_line_index ()
388+ if anything_shifted then
389+ self . _line_index_valid = false
394390 end
395391end
396392
@@ -414,6 +410,7 @@ function RenderState:_rebuild_line_index()
414410 end
415411 end
416412 end
413+ self ._line_index_valid = true
417414end
418415
419416return RenderState
0 commit comments