Skip to content

Commit 689563d

Browse files
committed
Adjust shift click selection to work with an anchor like dragging
1 parent 035defe commit 689563d

File tree

1 file changed

+33
-47
lines changed

1 file changed

+33
-47
lines changed

web/src/lib/components/diff/concise-diff-view.svelte.ts

Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,8 +1155,8 @@ export class ConciseDiffViewState<K> {
11551155

11561156
private readonly props: ConciseDiffViewStateProps<K>;
11571157

1158-
// Drag selection state
1159-
private dragState: { hunk: DiffViewerPatchHunk; hunkIdx: number; line: PatchLine; lineIdx: number; didMove: boolean } | null = null;
1158+
private selectionAnchor: { hunkIdx: number; lineIdx: number } | null = null;
1159+
private dragSelectionState: { hunk: DiffViewerPatchHunk; didMove: boolean } | null = null;
11601160
private suppressNextClick = false;
11611161

11621162
constructor(props: ConciseDiffViewStateProps<K>) {
@@ -1284,7 +1284,8 @@ export class ConciseDiffViewState<K> {
12841284
}
12851285

12861286
private startDrag(element: HTMLElement, pointerId: number, hunk: DiffViewerPatchHunk, hunkIdx: number, line: PatchLine, lineIdx: number) {
1287-
this.dragState = { hunk, hunkIdx, line, lineIdx, didMove: false };
1287+
this.selectionAnchor = { hunkIdx, lineIdx };
1288+
this.dragSelectionState = { hunk, didMove: false };
12881289

12891290
// Set initial selection
12901291
this.props.selection.current = {
@@ -1303,7 +1304,7 @@ export class ConciseDiffViewState<K> {
13031304
element,
13041305
"pointermove",
13051306
(e: PointerEvent) => {
1306-
if (!this.dragState) return;
1307+
if (!this.dragSelectionState || !this.selectionAnchor) return;
13071308

13081309
// Get the root element for this diff view
13091310
const rootElement = document.getElementById(this.props.rootElementId);
@@ -1323,12 +1324,12 @@ export class ConciseDiffViewState<K> {
13231324
const currentLineIdx = Number(lineElement.dataset.lineIdx);
13241325

13251326
// Only allow dragging within the same hunk
1326-
if (currentHunkIdx !== this.dragState.hunkIdx || !Number.isFinite(currentHunkIdx) || !Number.isFinite(currentLineIdx)) {
1327+
if (currentHunkIdx !== this.selectionAnchor.hunkIdx || !Number.isFinite(currentHunkIdx) || !Number.isFinite(currentLineIdx)) {
13271328
return;
13281329
}
13291330

1330-
if (this.dragState) {
1331-
this.dragState.didMove = true;
1331+
if (this.dragSelectionState) {
1332+
this.dragSelectionState.didMove = true;
13321333
}
13331334
this.updateDragSelection(currentLineIdx);
13341335
},
@@ -1340,10 +1341,10 @@ export class ConciseDiffViewState<K> {
13401341
abortController.abort();
13411342

13421343
// Suppress the click event only if we actually moved during the drag
1343-
if (this.dragState?.didMove) {
1344+
if (this.dragSelectionState?.didMove) {
13441345
this.suppressNextClick = true;
13451346
}
1346-
this.dragState = null;
1347+
this.dragSelectionState = null;
13471348
};
13481349

13491350
on(element, "pointerup", onDragEnd, { signal });
@@ -1359,17 +1360,18 @@ export class ConciseDiffViewState<K> {
13591360
}
13601361

13611362
private updateDragSelection(currentLineIdx: number) {
1362-
if (!this.dragState) return;
1363+
if (!this.dragSelectionState || !this.selectionAnchor) return;
13631364

1364-
const { hunk, hunkIdx, lineIdx: startIdx } = this.dragState;
1365+
const { hunk } = this.dragSelectionState;
1366+
const { hunkIdx, lineIdx: anchorIdx } = this.selectionAnchor;
13651367
const currentLine = hunk.lines[currentLineIdx];
13661368

13671369
if (currentLine.type === PatchLineType.SPACER || currentLine.type === PatchLineType.HEADER) {
13681370
return;
13691371
}
13701372

1371-
const minIdx = Math.min(startIdx, currentLineIdx);
1372-
const maxIdx = Math.max(startIdx, currentLineIdx);
1373+
const minIdx = Math.min(anchorIdx, currentLineIdx);
1374+
const maxIdx = Math.max(anchorIdx, currentLineIdx);
13731375

13741376
this.props.selection.current = {
13751377
hunk: hunkIdx,
@@ -1385,52 +1387,36 @@ export class ConciseDiffViewState<K> {
13851387
// New selection (no shift or different hunk)
13861388
if (!shift || !existingSelection || existingSelection.hunk !== hunkIdx) {
13871389
this.props.selection.current = { hunk: hunkIdx, start: clicked, end: clicked };
1390+
this.selectionAnchor = { hunkIdx, lineIdx };
13881391
return;
13891392
}
13901393

13911394
// Shift click on single-line selection: clear selection
13921395
if (existingSelection.start.idx === existingSelection.end.idx && lineIdx === existingSelection.start.idx) {
13931396
this.props.selection.current = undefined;
1397+
this.selectionAnchor = null;
13941398
return;
13951399
}
13961400

1397-
// Shift click outside selection: expand selection
1398-
if (lineIdx < existingSelection.start.idx) {
1399-
this.props.selection.current = { ...existingSelection, start: clicked };
1400-
return;
1401-
}
1402-
if (lineIdx > existingSelection.end.idx) {
1403-
this.props.selection.current = { ...existingSelection, end: clicked };
1404-
return;
1401+
// Determine anchor point: use existing anchor, or default to start of selection
1402+
let anchorIdx: number;
1403+
if (this.selectionAnchor && this.selectionAnchor.hunkIdx === hunkIdx) {
1404+
anchorIdx = this.selectionAnchor.lineIdx;
1405+
} else {
1406+
// No anchor or anchor is in different hunk, default to start of selection
1407+
anchorIdx = existingSelection.start.idx;
1408+
this.selectionAnchor = { hunkIdx, lineIdx: anchorIdx };
14051409
}
14061410

1407-
// Shift click inside selection: shrink closest side
1408-
const distToStart = lineIdx - existingSelection.start.idx;
1409-
const distToEnd = existingSelection.end.idx - lineIdx;
1411+
// Shift click: create selection from anchor to clicked line
1412+
const minIdx = Math.min(anchorIdx, lineIdx);
1413+
const maxIdx = Math.max(anchorIdx, lineIdx);
14101414

1411-
if (distToStart <= distToEnd) {
1412-
// Shrink from start: move start to line after clicked
1413-
const newStartIdx = lineIdx + 1;
1414-
if (newStartIdx > existingSelection.end.idx) {
1415-
this.props.selection.current = undefined;
1416-
return;
1417-
}
1418-
this.props.selection.current = {
1419-
...existingSelection,
1420-
start: this.createLineRef(hunk.lines[newStartIdx], newStartIdx),
1421-
};
1422-
} else {
1423-
// Shrink from end: move end to line before clicked
1424-
const newEndIdx = lineIdx - 1;
1425-
if (newEndIdx < existingSelection.start.idx) {
1426-
this.props.selection.current = undefined;
1427-
return;
1428-
}
1429-
this.props.selection.current = {
1430-
...existingSelection,
1431-
end: this.createLineRef(hunk.lines[newEndIdx], newEndIdx),
1432-
};
1433-
}
1415+
this.props.selection.current = {
1416+
hunk: hunkIdx,
1417+
start: this.createLineRef(hunk.lines[minIdx], minIdx),
1418+
end: this.createLineRef(hunk.lines[maxIdx], maxIdx),
1419+
};
14341420
}
14351421

14361422
isSelected(hunkIdx: number, lineIdx: number): boolean {

0 commit comments

Comments
 (0)