дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 11:15:40 +02:00
* fix(editor): pause expensive decorations during fast scroll bursts Add a scrollThrottle plugin that flags the editor with a .cm-scrolling class while the wheel is moving. While the flag is up the sticky-scroll header and indent guides are hidden via theme CSS, and their view plugins skip per-viewport re-measures/rebuilds; a trailing scrollSettled effect re-runs them once the wheel rests for 140ms. This keeps fast mouse-wheel scrolling from painting rows of stale decorations over freshly rendered code. Fixes l0ng-ai/openidea#375 * fix(editor): keep decorations painted during scroll bursts The scroll throttle hid the sticky header and indent guides entirely while scrolling, which flickered on every burst - and the sticky header is exactly what you want to see while scrolling. Drop the display/background hiding from the theme and keep the previous paint instead: the plugins skip re-measuring during a burst and catch up once on the trailing scrollSettled update. * fix(editor): throttle the first scroll event and queued sticky measurements --------- Co-authored-by: l0ng-ai <[email protected]>
53 рядки
1.9 KiB
TypeScript
53 рядки
1.9 KiB
TypeScript
/**
|
|
* Marks the editor with a `cm-scrolling` class while the user is actively
|
|
* scrolling; expensive decorations (sticky header, indent guides) keep
|
|
* their previous paint and defer re-measuring until the wheel / trackpad
|
|
* rests for a moment. Keeps fast mouse-wheel scrolling from spending every
|
|
* frame on layout work that will be stale by the next scroll event anyway.
|
|
*/
|
|
import { StateEffect, type Extension } from "@codemirror/state";
|
|
import { ViewPlugin, type EditorView, type ViewUpdate } from "@codemirror/view";
|
|
|
|
const IDLE_MS = 140;
|
|
|
|
/** Dispatched once a scroll burst ends so plugins that skipped work can catch up. */
|
|
export const scrollSettled = StateEffect.define<null>();
|
|
|
|
class ScrollThrottle {
|
|
private timer = 0;
|
|
private active = false;
|
|
|
|
constructor(private view: EditorView) {
|
|
// Run before bubble listeners, including stickyScroll when it was
|
|
// installed first, so the very first event is throttled too.
|
|
view.scrollDOM.addEventListener("scroll", this.onScroll, { passive: true, capture: true });
|
|
}
|
|
|
|
private onScroll = () => {
|
|
if (!this.active) {
|
|
this.active = true;
|
|
this.view.dom.classList.add("cm-scrolling");
|
|
}
|
|
clearTimeout(this.timer);
|
|
this.timer = window.setTimeout(() => {
|
|
this.active = false;
|
|
this.view.dom.classList.remove("cm-scrolling");
|
|
// Wake plugins that suppressed viewport work during the burst.
|
|
this.view.dispatch({ effects: scrollSettled.of(null) });
|
|
}, IDLE_MS);
|
|
};
|
|
|
|
destroy() {
|
|
clearTimeout(this.timer);
|
|
this.view.scrollDOM.removeEventListener("scroll", this.onScroll, true);
|
|
this.view.dom.classList.remove("cm-scrolling");
|
|
}
|
|
}
|
|
|
|
/** Check whether an update marks the end of a scroll burst. */
|
|
export function isScrollSettled(u: ViewUpdate): boolean {
|
|
return u.transactions.some((tr) => tr.effects.some((e) => e.is(scrollSettled)));
|
|
}
|
|
|
|
export const scrollThrottle: Extension = ViewPlugin.fromClass(ScrollThrottle);
|