Skip to content

Commit c88b02c

Browse files
committed
Always check whether to scroll window on .scrollIntoView
Issue codemirror#4491
1 parent facc9c8 commit c88b02c

File tree

3 files changed

+48
-43
lines changed

3 files changed

+48
-43
lines changed

src/display/operations.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ function endOperation_finish(op) {
155155
}
156156
// If we need to scroll a specific position into view, do so.
157157
if (op.scrollToPos) {
158-
let coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
159-
clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
160-
if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords)
158+
let rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
159+
clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin)
160+
maybeScrollWindow(cm, rect)
161161
}
162162

163163
// Fire events for markers that are hidden/unidden by editing or

src/display/scrolling.js

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import { setScrollLeft, setScrollTop } from "./scroll_events"
1010

1111
// If an editor sits on the top or bottom of the window, partially
1212
// scrolled out of view, this ensures that the cursor is visible.
13-
export function maybeScrollWindow(cm, coords) {
13+
export function maybeScrollWindow(cm, rect) {
1414
if (signalDOMEvent(cm, "scrollCursorIntoView")) return
1515

1616
let display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null
17-
if (coords.top + box.top < 0) doScroll = true
18-
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false
17+
if (rect.top + box.top < 0) doScroll = true
18+
else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false
1919
if (doScroll != null && !phantom) {
2020
let scrollNode = elt("div", "\u200b", null, `position: absolute;
21-
top: ${coords.top - display.viewOffset - paddingTop(cm.display)}px;
22-
height: ${coords.bottom - coords.top + scrollGap(cm) + display.barHeight}px;
23-
left: ${coords.left}px; width: 2px;`)
21+
top: ${rect.top - display.viewOffset - paddingTop(cm.display)}px;
22+
height: ${rect.bottom - rect.top + scrollGap(cm) + display.barHeight}px;
23+
left: ${rect.left}px; width: ${Math.max(2, rect.right - rect.left)}px;`)
2424
cm.display.lineSpace.appendChild(scrollNode)
2525
scrollNode.scrollIntoView(doScroll)
2626
cm.display.lineSpace.removeChild(scrollNode)
@@ -32,15 +32,16 @@ export function maybeScrollWindow(cm, coords) {
3232
// measured, the position of something may 'drift' during drawing).
3333
export function scrollPosIntoView(cm, pos, end, margin) {
3434
if (margin == null) margin = 0
35-
let coords
35+
let rect
3636
for (let limit = 0; limit < 5; limit++) {
3737
let changed = false
38-
coords = cursorCoords(cm, pos)
38+
let coords = cursorCoords(cm, pos)
3939
let endCoords = !end || end == pos ? coords : cursorCoords(cm, end)
40-
let scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
41-
Math.min(coords.top, endCoords.top) - margin,
42-
Math.max(coords.left, endCoords.left),
43-
Math.max(coords.bottom, endCoords.bottom) + margin)
40+
rect = {left: Math.min(coords.left, endCoords.left),
41+
top: Math.min(coords.top, endCoords.top) - margin,
42+
right: Math.max(coords.left, endCoords.left),
43+
bottom: Math.max(coords.bottom, endCoords.bottom) + margin}
44+
let scrollPos = calculateScrollPos(cm, rect)
4445
let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft
4546
if (scrollPos.scrollTop != null) {
4647
setScrollTop(cm, scrollPos.scrollTop)
@@ -52,12 +53,12 @@ export function scrollPosIntoView(cm, pos, end, margin) {
5253
}
5354
if (!changed) break
5455
}
55-
return coords
56+
return rect
5657
}
5758

5859
// Scroll a given set of coordinates into view (immediately).
59-
export function scrollIntoView(cm, x1, y1, x2, y2) {
60-
let scrollPos = calculateScrollPos(cm, x1, y1, x2, y2)
60+
export function scrollIntoView(cm, rect) {
61+
let scrollPos = calculateScrollPos(cm, rect)
6162
if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop)
6263
if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft)
6364
}
@@ -66,31 +67,31 @@ export function scrollIntoView(cm, x1, y1, x2, y2) {
6667
// rectangle into view. Returns an object with scrollTop and
6768
// scrollLeft properties. When these are undefined, the
6869
// vertical/horizontal position does not need to be adjusted.
69-
export function calculateScrollPos(cm, x1, y1, x2, y2) {
70+
export function calculateScrollPos(cm, rect) {
7071
let display = cm.display, snapMargin = textHeight(cm.display)
71-
if (y1 < 0) y1 = 0
72+
if (rect.top < 0) rect.top = 0
7273
let screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop
7374
let screen = displayHeight(cm), result = {}
74-
if (y2 - y1 > screen) y2 = y1 + screen
75+
if (rect.bottom - rect.top > screen) rect.bottom = rect.top + screen
7576
let docBottom = cm.doc.height + paddingVert(display)
76-
let atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin
77-
if (y1 < screentop) {
78-
result.scrollTop = atTop ? 0 : y1
79-
} else if (y2 > screentop + screen) {
80-
let newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen)
77+
let atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin
78+
if (rect.top < screentop) {
79+
result.scrollTop = atTop ? 0 : rect.top
80+
} else if (rect.bottom > screentop + screen) {
81+
let newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen)
8182
if (newTop != screentop) result.scrollTop = newTop
8283
}
8384

8485
let screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft
8586
let screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0)
86-
let tooWide = x2 - x1 > screenw
87-
if (tooWide) x2 = x1 + screenw
88-
if (x1 < 10)
87+
let tooWide = rect.right - rect.left > screenw
88+
if (tooWide) rect.right = rect.left + screenw
89+
if (rect.left < 10)
8990
result.scrollLeft = 0
90-
else if (x1 < screenleft)
91-
result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10))
92-
else if (x2 > screenw + screenleft - 3)
93-
result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw
91+
else if (rect.left < screenleft)
92+
result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10))
93+
else if (rect.right > screenw + screenleft - 3)
94+
result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw
9495
return result
9596
}
9697

@@ -113,7 +114,7 @@ export function ensureCursorVisible(cm) {
113114
from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur
114115
to = Pos(cur.line, cur.ch + 1)
115116
}
116-
cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}
117+
cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin}
117118
}
118119

119120
// When an operation has its scrollToPos property set, and another
@@ -125,10 +126,12 @@ export function resolveScrollToPos(cm) {
125126
if (range) {
126127
cm.curOp.scrollToPos = null
127128
let from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to)
128-
let sPos = calculateScrollPos(cm, Math.min(from.left, to.left),
129-
Math.min(from.top, to.top) - range.margin,
130-
Math.max(from.right, to.right),
131-
Math.max(from.bottom, to.bottom) + range.margin)
129+
let sPos = calculateScrollPos(cm, {
130+
left: Math.min(from.left, to.left),
131+
top: Math.min(from.top, to.top) - range.margin,
132+
right: Math.max(from.right, to.right),
133+
bottom: Math.max(from.bottom, to.bottom) + range.margin
134+
})
132135
cm.scrollTo(sPos.scrollLeft, sPos.scrollTop)
133136
}
134137
}

src/edit/methods.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ export default function(CodeMirror) {
252252
node.style.left = left + "px"
253253
}
254254
if (scroll)
255-
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight)
255+
scrollIntoView(this, {left, top, right: left + node.offsetWidth, bottom: top + node.offsetHeight})
256256
},
257257

258258
triggerOnKeyDown: methodOp(onKeyDown),
@@ -388,10 +388,12 @@ export default function(CodeMirror) {
388388
resolveScrollToPos(this)
389389
this.curOp.scrollToPos = range
390390
} else {
391-
let sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left),
392-
Math.min(range.from.top, range.to.top) - range.margin,
393-
Math.max(range.from.right, range.to.right),
394-
Math.max(range.from.bottom, range.to.bottom) + range.margin)
391+
let sPos = calculateScrollPos(this, {
392+
left: Math.min(range.from.left, range.to.left),
393+
top: Math.min(range.from.top, range.to.top) - range.margin,
394+
right: Math.max(range.from.right, range.to.right),
395+
bottom: Math.max(range.from.bottom, range.to.bottom) + range.margin
396+
})
395397
this.scrollTo(sPos.scrollLeft, sPos.scrollTop)
396398
}
397399
}),

0 commit comments

Comments
 (0)