Skip to content
This repository was archived by the owner on Apr 15, 2026. It is now read-only.

Commit 4935d24

Browse files
committed
Make the margin used when scrolling the cursor into view configurable
FEATURE: The new `EditorView.cursorScrollMargin` facet can now be used to configure the extra space used when scrolling the cursor into view. Issue codemirror/dev#1689
1 parent ed7d625 commit 4935d24

3 files changed

Lines changed: 30 additions & 11 deletions

File tree

src/dom.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ export function scrollRectIntoView(dom: HTMLElement, rect: Rect, side: -1 | 1,
139139

140140
let moveX = 0, moveY = 0
141141
if (y == "nearest") {
142-
if (rect.top < bounding.top) {
142+
if (rect.top < bounding.top + yMargin) {
143143
moveY = rect.top - (bounding.top + yMargin)
144144
if (side > 0 && rect.bottom > bounding.bottom + moveY)
145145
moveY = rect.bottom - bounding.bottom + yMargin
146-
} else if (rect.bottom > bounding.bottom) {
146+
} else if (rect.bottom > bounding.bottom - yMargin) {
147147
moveY = rect.bottom - bounding.bottom + yMargin
148148
if (side < 0 && (rect.top - moveY) < bounding.top)
149149
moveY = rect.top - (bounding.top + yMargin)
@@ -157,11 +157,11 @@ export function scrollRectIntoView(dom: HTMLElement, rect: Rect, side: -1 | 1,
157157
moveY = targetTop - bounding.top
158158
}
159159
if (x == "nearest") {
160-
if (rect.left < bounding.left) {
160+
if (rect.left < bounding.left + xMargin) {
161161
moveX = rect.left - (bounding.left + xMargin)
162162
if (side > 0 && rect.right > bounding.right + moveX)
163163
moveX = rect.right - bounding.right + xMargin
164-
} else if (rect.right > bounding.right) {
164+
} else if (rect.right > bounding.right - xMargin) {
165165
moveX = rect.right - bounding.right + xMargin
166166
if (side < 0 && rect.left < bounding.left + moveX)
167167
moveX = rect.left - (bounding.left + xMargin)

src/editorview.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,10 @@ export class EditorView {
305305
if (scrollTarget) scrollTarget = scrollTarget.map(tr.changes)
306306
if (tr.scrollIntoView) {
307307
let {main} = tr.state.selection
308+
let {x, y} = this.state.facet(EditorView.cursorScrollMargin)
308309
scrollTarget = new ScrollTarget(
309-
main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1))
310+
main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1),
311+
"nearest", "nearest", y, x)
310312
}
311313
for (let e of tr.effects)
312314
if (e.is(scrollIntoView)) scrollTarget = e.value.clip(this.state)
@@ -908,7 +910,8 @@ export class EditorView {
908910
xMargin?: number,
909911
} = {}): StateEffect<unknown> {
910912
return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos,
911-
options.y, options.x, options.yMargin, options.xMargin))
913+
options.y ?? "nearest", options.x ?? "nearest",
914+
options.yMargin ?? 5, options.xMargin ?? 5))
912915
}
913916

914917
/// Return an effect that resets the editor to its current (at the
@@ -1100,11 +1103,27 @@ export class EditorView {
11001103
/// supported.)
11011104
static bidiIsolatedRanges = bidiIsolatedRanges
11021105

1106+
/// Can be used to specify the distance that scrolling cursor into
1107+
/// view keeps it away from the sides of the editor, either as a
1108+
/// single pixel number or two different values for the different
1109+
/// axes. Defaults to 5 pixels on both axes.
1110+
static cursorScrollMargin = Facet.define<number | {x: number, y: number}, {x: number, y: number}>({
1111+
combine: inputs => {
1112+
let x = 5, y = 5
1113+
for (let i of inputs) {
1114+
if (typeof i == "number") x = y = i
1115+
else ({x, y} = i)
1116+
}
1117+
return {x, y}
1118+
}
1119+
})
1120+
11031121
/// Facet that allows extensions to provide additional scroll
11041122
/// margins (space around the sides of the scrolling element that
11051123
/// should be considered invisible). This can be useful when the
11061124
/// plugin introduces elements that cover part of that element (for
1107-
/// example a horizontally fixed gutter).
1125+
/// example a horizontally fixed gutter). Not to be confused with
1126+
/// [`cursorScrollMargin`](#view.EditorView^cursorScrollMargin).
11081127
static scrollMargins = scrollMargins
11091128

11101129
/// Create a theme extension. The first argument can be a

src/extension.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ export const scrollHandler = Facet.define<(
5050
export class ScrollTarget {
5151
constructor(
5252
readonly range: SelectionRange,
53-
readonly y: ScrollStrategy = "nearest",
54-
readonly x: ScrollStrategy = "nearest",
55-
readonly yMargin: number = 5,
56-
readonly xMargin: number = 5,
53+
readonly y: ScrollStrategy,
54+
readonly x: ScrollStrategy,
55+
readonly yMargin: number,
56+
readonly xMargin: number,
5757
// This data structure is abused to also store precise scroll
5858
// snapshots, instead of a `scrollIntoView` request. When this
5959
// flag is `true`, `range` points at a position in the reference

0 commit comments

Comments
 (0)