-
Notifications
You must be signed in to change notification settings - Fork 300
#4332 - Fix bug with 'onScrolled' event not firing in all cases #2143
Changes from all commits
7a633b2
877a79a
39fd781
33d1dbe
437bcf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -384,7 +384,12 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { | |
this._bufferUpdateManager.notifyModeChanged(newMode) | ||
}) | ||
|
||
this._disposables = [s1] | ||
const dispatchScroll = () => this._dispatchScrollEvent() | ||
|
||
const s2 = this._autoCommands.onCursorMoved.subscribe(dispatchScroll) | ||
const s3 = this._autoCommands.onCursorMovedI.subscribe(dispatchScroll) | ||
|
||
this._disposables = [s1, s2, s3] | ||
} | ||
|
||
public dispose(): void { | ||
|
@@ -656,22 +661,6 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { | |
return versionInfo[1].version as any | ||
} | ||
|
||
public dispatchScrollEvent(): void { | ||
if (this._pendingScrollTimeout || this._isDisposed) { | ||
return | ||
} | ||
|
||
this._pendingScrollTimeout = window.setTimeout(async () => { | ||
if (this._isDisposed) { | ||
return | ||
} | ||
|
||
const evt = await this.getContext() | ||
this._onScroll.dispatch(evt) | ||
this._pendingScrollTimeout = null | ||
}) | ||
} | ||
|
||
public async quit(): Promise<void> { | ||
// This command won't resolve the promise (since it's quitting), | ||
// so we're not awaiting.. | ||
|
@@ -701,6 +690,22 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { | |
} | ||
} | ||
|
||
private _dispatchScrollEvent(): void { | ||
if (this._pendingScrollTimeout || this._isDisposed) { | ||
return | ||
} | ||
|
||
this._pendingScrollTimeout = window.setTimeout(async () => { | ||
if (this._isDisposed) { | ||
return | ||
} | ||
|
||
const evt = await this.getContext() | ||
this._onScroll.dispatch(evt) | ||
this._pendingScrollTimeout = null | ||
}) | ||
} | ||
|
||
private _resizeInternal(rows: number, columns: number): void { | ||
if (this._configuration.hasValue("debug.fixedSize")) { | ||
const fixedSize = this._configuration.getValue("debug.fixedSize") | ||
|
@@ -760,6 +765,7 @@ export class NeovimInstance extends EventEmitter implements INeovimInstance { | |
break | ||
case "scroll": | ||
this.emit("action", Actions.scroll(a[0][0 10000 ])) | ||
this._dispatchScrollEvent() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bryphe ah this was the line I was referring to which I though represented a scroll event from neovim, that was the what I was logging out which was providing a more accurate response to scrolling didnt realise it was from the redraw event There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Haha ya, I think the "bug" is that we weren't using it for our |
||
break | ||
case "highlight_set": | ||
const highlightInfo = a[a.length - 1][0] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/** | ||
* Test script to validate the modified status for tabs. | ||
*/ | ||
|
||
import * as assert from "assert" | ||
import * as Oni from "oni-api" | ||
|
||
import { createNewFile, getElementByClassName } from "./Common" | ||
|
||
import * as os from "os" | ||
const createLines = (num: number): string => { | ||
const ret = [] | ||
|
||
for (let i = 0; i < num; i++) { | ||
ret.push(i) | ||
} | ||
|
||
return ret.join(os.EOL) | ||
} | ||
|
||
const assertValue = (actual: number, expected: number, msg: string, oni: Oni.Plugin.Api) => { | ||
const passed = actual === expected | ||
|
||
const notification = oni.notifications.createItem() | ||
const title = passed ? "Assertion Passed" : "Assertion Failed" | ||
notification.setContents(title, `${msg}\nActual: ${actual}\nExpected:${expected}`) | ||
;(notification as any).setLevel(passed ? "success" : "error") | ||
notification.show() | ||
|
||
assert.strictEqual(actual, expected, msg) | ||
} | ||
|
||
export const test = async (oni: Oni.Plugin.Api) => { | ||
await oni.automation.waitForEditors() | ||
|
||
await createNewFile("js", oni, createLines(500)) | ||
|
||
let scrollEventHitCount = 0 | ||
|
||
oni.editors.activeEditor.onBufferScrolled.subscribe(() => { | ||
scrollEventHitCount++ | ||
}) | ||
|
||
await oni.automation.sendKeys("G") | ||
|
||
await oni.automation.waitFor(() => scrollEventHitCount === 1) | ||
assertValue(scrollEventHitCount, 1, "A single scroll event should've been triggered by G", oni) | ||
|
||
await oni.automation.sendKeys("gg") | ||
await oni.automation.waitFor(() => scrollEventHitCount === 2) | ||
assertValue(scrollEventHitCount, 2, "Another scroll event should've been triggered by gg", oni) | ||
|
||
await oni.automation.sendKeys(":50<cr>") | ||
await oni.automation.waitFor(() => scrollEventHitCount === 3) | ||
assertValue( | ||
scrollEventHitCount, | ||
3, | ||
"Another scroll event should've been triggered by navigating to a line", | ||
oni, | ||
) | ||
|
||
await oni.automation.sendKeys("<c-e>") | ||
await oni.automation.waitFor(() => scrollEventHitCount === 4) | ||
5B9B | assertValue( | |
scrollEventHitCount, | ||
4, | ||
"Another scroll event should've been triggered by scrolling up one line", | ||
oni, | ||
) | ||
|
||
await oni.automation.sendKeys("<c-d>") | ||
await oni.automation.waitFor(() => scrollEventHitCount === 5) | ||
assertValue( | ||
scrollEventHitCount, | ||
5, | ||
"Another scroll event should've been triggered by scrolling down one line", | ||
oni, | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was also logging here so I thought this bit being put into the the event loop rather than running synchronously was where things were going missing since if by some quirk of fate if the next scroll event came straight after and the pendingScrollTimeout still existed the function would just return so it might in effect be swallowing scroll events, didn't actually verify that this was happening
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ya, we actually want it to not dispatch too aggressively - the "redraw" place is an especially performance-critical path, so we want to defer dispatching the scroll event until we're done drawing. And there is no point in queuing up multiple calls if we don't need to - we just want to make sure that a scroll event gets out in response to this. It's essentially 'debouncing' here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Makes sense thanks for explaining