@@ -315,6 +315,110 @@ local function get_preview_line(source_bufnr, line, max_width, preview_prefix)
315315 }
316316end
317317
318+ --- @param line string
319+ --- @param line_hls PeekstackStackViewHighlight[]
320+ --- @param preview PeekstackStackViewPreviewLine ?
321+ --- @return string
322+ local function line_render_key (line , line_hls , preview )
323+ local parts = { line }
324+ for _ , hl in ipairs (line_hls ) do
325+ parts [# parts + 1 ] = string.format (" %d:%d:%s" , hl .col_start , hl .col_end , hl .hl_group )
326+ end
327+ if preview then
328+ parts [# parts + 1 ] = string.format (
329+ " preview:%d:%d:%d:%d:%d" ,
330+ preview .source_bufnr ,
331+ preview .source_line ,
332+ preview .source_col_start ,
333+ preview .source_col_end ,
334+ preview .preview_col_start
335+ )
336+ end
337+ return table.concat (parts , " |" )
338+ end
339+
340+ --- @param items string[]
341+ --- @param start_idx integer
342+ --- @param end_idx integer
343+ --- @return string[]
344+ local function slice_lines (items , start_idx , end_idx )
345+ if end_idx < start_idx then
346+ return {}
347+ end
348+
349+ --- @type string[]
350+ local slice = {}
351+ for idx = start_idx , end_idx do
352+ slice [# slice + 1 ] = items [idx ]
353+ end
354+ return slice
355+ end
356+
357+ --- @param old_keys string[]
358+ --- @param new_keys string[]
359+ --- @return integer ?, integer ?, integer ?
360+ local function diff_range (old_keys , new_keys )
361+ local old_count = # old_keys
362+ local new_count = # new_keys
363+ local start_idx = 1
364+
365+ while start_idx <= old_count and start_idx <= new_count and old_keys [start_idx ] == new_keys [start_idx ] do
366+ start_idx = start_idx + 1
367+ end
368+
369+ if start_idx > old_count and start_idx > new_count then
370+ return nil , nil , nil
371+ end
372+
373+ local old_end = old_count
374+ local new_end = new_count
375+ while old_end >= start_idx and new_end >= start_idx and old_keys [old_end ] == new_keys [new_end ] do
376+ old_end = old_end - 1
377+ new_end = new_end - 1
378+ end
379+
380+ return start_idx , old_end , new_end
381+ end
382+
383+ --- @param bufnr integer
384+ --- @param highlights PeekstackStackViewHighlight[][]
385+ --- @param preview_lines table<integer , PeekstackStackViewPreviewLine>
386+ --- @param start_idx integer
387+ --- @param end_idx integer
388+ local function apply_highlights_in_range (bufnr , highlights , preview_lines , start_idx , end_idx )
389+ if end_idx < start_idx then
390+ return
391+ end
392+
393+ for line_idx = start_idx , end_idx do
394+ for _ , hl in ipairs (highlights [line_idx ] or {}) do
395+ local opts = {
396+ end_col = hl .col_end ,
397+ hl_group = hl .hl_group ,
398+ }
399+ if hl .hl_group == " PeekstackStackViewPreview" then
400+ opts .priority = PREVIEW_BASE_HL_PRIORITY
401+ end
402+ vim .api .nvim_buf_set_extmark (bufnr , NS , line_idx - 1 , hl .col_start , {
403+ end_col = opts .end_col ,
404+ hl_group = opts .hl_group ,
405+ priority = opts .priority ,
406+ })
407+ end
408+ end
409+
410+ --- @type table<integer , PeekstackStackViewPreviewLine>
411+ local changed_previews = {}
412+ for line_idx = start_idx , end_idx do
413+ if preview_lines [line_idx ] then
414+ changed_previews [line_idx ] = preview_lines [line_idx ]
415+ end
416+ end
417+ if next (changed_previews ) then
418+ apply_preview_treesitter_highlights (bufnr , changed_previews )
419+ end
420+ end
421+
318422--- @param s PeekstackStackViewState
319423--- @param is_ready fun ( s : PeekstackStackViewState ): boolean
320424function M .render (s , is_ready )
@@ -478,29 +582,25 @@ function M.render(s, is_ready)
478582 end
479583 end
480584
481- vim .bo [s .bufnr ].modifiable = true
482- vim .api .nvim_buf_set_lines (s .bufnr , 0 , - 1 , false , lines )
483- vim .bo [s .bufnr ].modifiable = false
484-
485- vim .api .nvim_buf_clear_namespace (s .bufnr , NS , 0 , - 1 )
486- for line_idx , line_hls in ipairs (highlights ) do
487- for _ , hl in ipairs (line_hls ) do
488- local opts = {
489- end_col = hl .col_end ,
490- hl_group = hl .hl_group ,
491- }
492- if hl .hl_group == " PeekstackStackViewPreview" then
493- opts .priority = PREVIEW_BASE_HL_PRIORITY
494- end
495- vim .api .nvim_buf_set_extmark (s .bufnr , NS , line_idx - 1 , hl .col_start , {
496- end_col = opts .end_col ,
497- hl_group = opts .hl_group ,
498- priority = opts .priority ,
499- })
500- end
585+ --- @type string[]
586+ local line_keys = {}
587+ for line_idx , line in ipairs (lines ) do
588+ line_keys [line_idx ] = line_render_key (line , highlights [line_idx ] or {}, preview_lines [line_idx ])
501589 end
502590
503- apply_preview_treesitter_highlights (s .bufnr , preview_lines )
591+ local old_keys = s .render_keys or {}
592+ local start_idx , old_end , new_end = diff_range (old_keys , line_keys )
593+ if start_idx then
594+ local replace = slice_lines (lines , start_idx , new_end )
595+
596+ vim .bo [s .bufnr ].modifiable = true
597+ vim .api .nvim_buf_set_lines (s .bufnr , start_idx - 1 , old_end , false , replace )
598+ vim .bo [s .bufnr ].modifiable = false
599+
600+ vim .api .nvim_buf_clear_namespace (s .bufnr , NS , start_idx - 1 , old_end )
601+ apply_highlights_in_range (s .bufnr , highlights , preview_lines , start_idx , new_end )
602+ end
603+ s .render_keys = line_keys
504604
505605 if s .winid and vim .api .nvim_win_is_valid (s .winid ) and s .bufnr and vim .api .nvim_buf_is_valid (s .bufnr ) then
506606 local line_count = vim .api .nvim_buf_line_count (s .bufnr )
0 commit comments