Fix duplicated content when copying across blob viewer chunks

What does this MR do and why?

Fixes duplicated clipboard content when selecting text across a blob viewer chunk boundary (~70 lines). Copying such a selection pasted the entire preceding chunk's content in addition to the selected lines (roughly 3x the intended content).

Root cause: CopyAsGFM (copy_as_gfm.js) intercepts every copy in pre.code.highlight and rebuilds the clipboard from all .line elements in the selection fragment. A cross-chunk selection includes the previous chunk's inert syntax-highlight overlay (introduced in !235933 (merged)), whose per-line .line elements leak into the rebuilt clipboard. Selections within one chunk clone plain text (no elements), which is why the bug only reproduces at chunk boundaries.

Fix (two small changes):

  1. Mark the highlight overlay with data-gfm-ignore — the existing CopyAsGFM escape hatch for presentational overlays (chunk.vue).
  2. Move the existing [data-gfm-ignore] strip to run before .line extraction instead of after. It previously ran after line contents were already moved out of their ignored ancestor, so it could not catch this case; stripping up front covers both the old scenario (block-level overlays serializing to blank lines) and this one.

Out of scope (pre-existing, unrelated to this change):

  • Classic diffs copy pastes as plain text (no .line elements in that DOM). I didn't log an issue assuming we're pushing forward with rapid diffs.
  • Safari-only ghost text while selecting in the blob viewer (two-layer rendering artifact) #608075

Screenshots or screen recordings

Browser
Chrome

Firefox

Safari

(the ghost text issue while selecting is pre-existing #608075)

CleanShot 2026-08-04 at 15.05.52.gif

How to set up and validate locally

  1. Open a file longer than 70 lines in the blob viewer. Note, I used this file copy_test_607428.txt since it explicitly marks each line and make verifying changes easier ?
  2. Drag-select from line 65 to line 75 (crosses the 70/71 chunk boundary).
  3. Copy and paste into a plain-text editor: only lines 65–75 should be pasted, each exactly once.
  4. Regression check: refresh at the top of the file, use native find (⌘+F) for a string beyond line 70 — the browser scrolls to the match and the highlight persists, with no duplicate match count.
Since this is a critical area, I've conducted a bit more manual testing

Cross-chunk copy (core fix)

  • Drag-select across one chunk boundary → paste is exact (Ctrl+C and right-click → Copy)
  • Reverse drag (bottom to top) and multi-boundary selection (e.g. L60–L150)
  • Boundary-exact selections (L70–L71 only; L1–L70)
  • Keyboard selection (Shift+Click, Shift+Arrow across a boundary)
  • Select-all in the blob → file content exactly once
  • Within-chunk selection (control, untouched fast path)

CopyAsGFM feature (transform changed)

  • Blob selection → paste into MR comment box → fenced code block with only the selected lines
  • Rapid MR diff: copy from old/new side (inline and side-by-side) → only that side's lines, no +/- markers or line numbers

Find-in-page regression (#565353 (closed) must stay fixed)

  • ⌘+F a string in an unrendered chunk → highlight persists after scroll
  • Match count is 1x per occurrence (overlay stays excluded from find)

Adjacent behaviors

  • Code navigation click/hover on a file with code intelligence
  • Line permalinks (#L65-75), blame gutter never included in copies
  • Files with tabs, CJK/emoji, long unwrapped lines; file ≤ 70 lines; file above highlight size limit
  • Chrome, Firefox, Safari

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Chaoyue Zhao

Merge request reports

Loading