MCPcopy Index your code
hub / github.com/codeaashu/claude-code / shiftSelection

Function shiftSelection

src/ink/selection.ts:470–565  ·  view source on GitHub ↗
(
  s: SelectionState,
  dRow: number,
  minRow: number,
  maxRow: number,
  width: number,
)

Source from the content-addressed store, hash-verified

468 * scroll can jump either way.
469 */
470export function shiftSelection(
471 s: SelectionState,
472 dRow: number,
473 minRow: number,
474 maxRow: number,
475 width: number,
476): void {
477 if (!s.anchor || !s.focus) return
478 // Virtual rows track pre-clamp positions so reverse scrolls restore
479 // correctly. Without this, clamp(5→0) + shift(+10) = 10, not the true 5,
480 // and scrolledOffAbove stays stale (highlight ≠ copy).
481 const vAnchor = (s.virtualAnchorRow ?? s.anchor.row) + dRow
482 const vFocus = (s.virtualFocusRow ?? s.focus.row) + dRow
483 if (
484 (vAnchor < minRow && vFocus < minRow) ||
485 (vAnchor > maxRow && vFocus > maxRow)
486 ) {
487 clearSelection(s)
488 return
489 }
490 // Debt = how far the nearer endpoint overshoots each edge. When debt
491 // shrinks (reverse scroll), those rows are back on-screen — pop from
492 // the accumulator so getSelectedText doesn't double-count them.
493 const oldMin = Math.min(
494 s.virtualAnchorRow ?? s.anchor.row,
495 s.virtualFocusRow ?? s.focus.row,
496 )
497 const oldMax = Math.max(
498 s.virtualAnchorRow ?? s.anchor.row,
499 s.virtualFocusRow ?? s.focus.row,
500 )
501 const oldAboveDebt = Math.max(0, minRow - oldMin)
502 const oldBelowDebt = Math.max(0, oldMax - maxRow)
503 const newAboveDebt = Math.max(0, minRow - Math.min(vAnchor, vFocus))
504 const newBelowDebt = Math.max(0, Math.max(vAnchor, vFocus) - maxRow)
505 if (newAboveDebt < oldAboveDebt) {
506 // scrolledOffAbove pushes newest at the end (closest to on-screen).
507 const drop = oldAboveDebt - newAboveDebt
508 s.scrolledOffAbove.length -= drop
509 s.scrolledOffAboveSW.length = s.scrolledOffAbove.length
510 }
511 if (newBelowDebt < oldBelowDebt) {
512 // scrolledOffBelow unshifts newest at the front (closest to on-screen).
513 const drop = oldBelowDebt - newBelowDebt
514 s.scrolledOffBelow.splice(0, drop)
515 s.scrolledOffBelowSW.splice(0, drop)
516 }
517 // Invariant: accumulator length ≤ debt. If the accumulator exceeds debt,
518 // the excess is stale — e.g., moveFocus cleared virtualFocusRow without
519 // trimming the accumulator, orphaning entries the pop above can never
520 // reach because oldDebt was ALREADY 0. Truncate to debt (keeping the
521 // newest = closest-to-on-screen entries). Check newDebt (not oldDebt):
522 // captureScrolledRows runs BEFORE this shift in the real flow (ink.tsx),
523 // so at entry the accumulator is populated but oldDebt is still 0 —
524 // that's the normal establish-debt path, not stale.
525 if (s.scrolledOffAbove.length > newAboveDebt) {
526 // Above pushes newest at END → keep END.
527 s.scrolledOffAbove =

Callers 1

Calls 5

clearSelectionFunction · 0.85
shiftFunction · 0.85
spFunction · 0.85
maxMethod · 0.80
spliceMethod · 0.80

Tested by

no test coverage detected