Cache per-chunk blame slices across renders

What does this MR do and why?

Adds createBlameSliceBuilder (plus a private isSameBlameSlice helper) to source_viewer/utils.js and wires it into source_viewer.vue's blameGroupsByChunk computed and created() hook.

Without it, one chunk's blame arriving rebuilds the slices for every chunk, handing each already-rendered chunk a new blameGroups array identity and forcing it to re-render even though its own data hasn't changed. createBlameSliceBuilder holds the previous per-chunk result and hands back the same array reference when a chunk's slice is unchanged, so re-renders are confined to the chunks whose blame actually moved.

This is purely a performance optimization — behaviour is identical with or without it. It's split into its own MR because it holds mutable cross-call state (the cache of previous slices), which is the part of this work most likely to hide a stale-cache bug, so it warrants review on its own rather than being folded into the subgrid rendering change.

Between MR 2 of this split (!251529 (merged)) merging and this MR merging, there's a window where all appeared chunks re-render on every blame fetch. That's a performance regression relative to the final state, not a correctness issue.

This MR targets 589723-blame-subgrid-layout, the branch of !251529 (merged). Once that MR merges, this MR's target should be changed to master.

Closes #589723 (closed)

This is the third of three MRs split out of !248531 (closed) following review feedback asking for smaller diffs.

How to set up and validate locally

The inline_blame feature flag is default_enabled: true, so no flag toggle is needed.

  1. Open a blob page on any project and click the blame toggle in the file header.
  2. Scroll so that chunks further down the file load their blame data off-screen, then scroll back up.
  3. Confirm there is no layout shift and blame cells stay aligned with their lines, same as before this MR.
  4. Confirm the blame column resizer still works.
  5. (Optional, to observe the optimization) Use the Vue devtools performance tab or a render-count log to confirm that a chunk whose blame slice hasn't changed doesn't re-render when another chunk's blame data arrives.

Implementation breakdown

Step MR Status
Blame group normalization and per-chunk mapping (utils only, no callers) !251528 (merged) ✅ Merged
Render inline blame in a CSS subgrid instead of an overlay !251529 (merged) ✅ Merged
Cache per-chunk blame slices across renders (this MR) ⏳ In review
Migrate simple_viewer.vue off blame_info.vue, remove legacy blame positioning code TBD Planned
Edited by Joseph Fletcher

Merge request reports

Loading
Loading