Skip to content

Commit 08d737d

Browse files
committed
fix
1 parent 34ad008 commit 08d737d

1 file changed

Lines changed: 59 additions & 7 deletions

File tree

src/cm/lineNumberSelection.ts

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ type LineNumberClickEvent = Pick<
1414
| "defaultPrevented"
1515
>;
1616

17+
function toDocumentOffset(
18+
value: number | null | undefined,
19+
fallback = 0,
20+
): number {
21+
const resolved = value != null ? Number(value) : fallback;
22+
return Number.isFinite(resolved) ? resolved : fallback;
23+
}
24+
1725
/**
1826
* Resolve the selection range for a clicked document line.
1927
* Includes the trailing line break when one exists to mirror Ace's
@@ -24,18 +32,60 @@ export function getLineSelectionRange(
2432
line: LineInfo,
2533
): { from: number; to: number } | null {
2634
if (!line) return null;
27-
const from = Math.max(0, Number(line.from) || 0);
28-
const to = Math.max(from, Number(line.to) || from);
35+
const from = Math.max(0, toDocumentOffset(line.from));
36+
const to = Math.max(from, toDocumentOffset(line.to, from));
2937
return {
3038
from,
3139
to: Math.min(to + 1, state.doc.length),
3240
};
3341
}
3442

43+
function getCurrentSelectionLineRange(state: EditorView["state"]): {
44+
from: number;
45+
to: number;
46+
} {
47+
const selection = state.selection.main;
48+
const startLine = state.doc.lineAt(selection.from);
49+
const endPos = selection.empty
50+
? selection.head
51+
: Math.max(selection.to - 1, selection.from);
52+
const endLine = state.doc.lineAt(endPos);
53+
const startRange = getLineSelectionRange(state, startLine);
54+
const endRange = getLineSelectionRange(state, endLine);
55+
56+
return {
57+
from: startRange?.from ?? selection.from,
58+
to: endRange?.to ?? selection.to,
59+
};
60+
}
61+
62+
function createLineSelection(range: {
63+
from: number;
64+
to: number;
65+
}): EditorSelection {
66+
return EditorSelection.single(range.to, range.from);
67+
}
68+
69+
function createExtendedLineSelection(
70+
state: EditorView["state"],
71+
clickedRange: { from: number; to: number },
72+
): EditorSelection {
73+
const currentRange = getCurrentSelectionLineRange(state);
74+
const from = Math.min(currentRange.from, clickedRange.from);
75+
const to = Math.max(currentRange.to, clickedRange.to);
76+
77+
if (clickedRange.from <= currentRange.from) {
78+
return EditorSelection.single(to, from);
79+
}
80+
81+
return EditorSelection.single(from, to);
82+
}
83+
3584
/**
3685
* Select the clicked line from the line-number gutter.
37-
* Ignores modified and non-primary clicks so it doesn't interfere with
38-
* context menus or alternate selection gestures.
86+
* Shift-click extends the current selection by whole lines.
87+
* Other modified or non-primary clicks are ignored so they don't interfere
88+
* with context menus or alternate selection gestures.
3989
*/
4090
export function handleLineNumberClick(
4191
view: EditorView | null | undefined,
@@ -44,7 +94,7 @@ export function handleLineNumberClick(
4494
): boolean {
4595
if (!view || !event || event.defaultPrevented) return false;
4696
if ((event.button ?? 0) !== 0) return false;
47-
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) {
97+
if (event.altKey || event.ctrlKey || event.metaKey) {
4898
return false;
4999
}
50100

@@ -53,8 +103,10 @@ export function handleLineNumberClick(
53103

54104
event.preventDefault();
55105
view.dispatch({
56-
selection: EditorSelection.single(range.from, range.to),
57-
userEvent: "select.pointer",
106+
selection: event.shiftKey
107+
? createExtendedLineSelection(view.state, range)
108+
: createLineSelection(range),
109+
userEvent: event.shiftKey ? "select.extend.pointer" : "select.pointer",
58110
});
59111
view.focus();
60112
return true;

0 commit comments

Comments
 (0)