fix(editor): reduce Vim arrow key lag

This commit is contained in:
zipg authored and GitHub committed 2026-09-12 09:29:27 +08:00
1 parent 0bfb1035e4
commit e60b0f3083
2 files changed
+67 -1

No files matched your search

@@ -287,4 +287,54 @@ describe("currentStatementFrameLayer", () => {
expect(layer).toHaveBeenCalledWith(expect.objectContaining({ above: true, class: "cm-db-currentStatementFrameLayer" }));
});
it("does not rebuild the frame while the cursor stays in the same statement", () => {
const layer = vi.fn((config) => config);
const RectangleMarker = vi.fn();
const view = buildView(["SELECT 1;", "SELECT 2;"]);
const resolver = vi.fn(() => ({ from: 0, to: 8 }));
const config = currentStatementFrameLayer({ layer, RectangleMarker } as never, resolver) as unknown as {
markers: (currentView: typeof view) => unknown;
update: (update: { docChanged: boolean; selectionSet: boolean; viewportChanged: boolean; geometryChanged: boolean; transactions: never[]; view: typeof view }) => boolean;
};
config.markers(view);
expect(
config.update({
docChanged: false,
selectionSet: true,
viewportChanged: false,
geometryChanged: false,
transactions: [],
view,
}),
).toBe(false);
expect(resolver).toHaveBeenCalledTimes(2);
});
it("rebuilds the frame when the cursor enters another statement", () => {
const layer = vi.fn((config) => config);
const RectangleMarker = vi.fn();
const view = buildView(["SELECT 1;", "SELECT 2;"]);
let range = { from: 0, to: 8 };
const config = currentStatementFrameLayer({ layer, RectangleMarker } as never, () => range) as unknown as {
markers: (currentView: typeof view) => unknown;
update: (update: { docChanged: boolean; selectionSet: boolean; viewportChanged: boolean; geometryChanged: boolean; transactions: never[]; view: typeof view }) => boolean;
};
config.markers(view);
range = { from: 9, to: 17 };
expect(
config.update({
docChanged: false,
selectionSet: true,
viewportChanged: false,
geometryChanged: false,
transactions: [],
view,
}),
).toBe(true);
});
});
@@ -151,6 +151,10 @@ interface CurrentStatementFrameModule {
* returning null hides the frame.
*/
export function currentStatementFrameLayer(viewModule: CurrentStatementFrameModule, resolve: StatementFrameResolver): Extension {
let lastRequest: StatementFrameRequest | null | undefined;
const sameRequest = (left: StatementFrameRequest | null | undefined, right: StatementFrameRequest | null): boolean => left === right || (!!left && !!right && left.from === right.from && left.to === right.to);
return viewModule.layer({
// Keep the outline above line decorations so opaque active-line colors
// from editor themes cannot cover the frame border.
@@ -158,13 +162,25 @@ export function currentStatementFrameLayer(viewModule: CurrentStatementFrameModu
class: "cm-db-currentStatementFrameLayer",
markers(view) {
const request = resolve(view);
lastRequest = request;
if (!request || request.to < request.from) return [];
const rect = currentStatementFrameRect(view, request.from, request.to);
if (!rect) return [];
return [new viewModule.RectangleMarker("cm-db-currentStatementFrame", rect.left, rect.top, rect.width, rect.height)];
},
update(update) {
return update.docChanged || update.selectionSet || update.viewportChanged || update.geometryChanged || update.transactions.some((transaction) => transaction.reconfigured);
if (update.docChanged || update.viewportChanged || update.geometryChanged || update.transactions.some((transaction) => transaction.reconfigured)) {
lastRequest = undefined;
return true;
}
if (!update.selectionSet) return false;
// Moving inside one statement does not change its frame. Avoid repeating
// the per-line DOM geometry probes for every auto-repeated key event.
const request = resolve(update.view);
if (sameRequest(lastRequest, request)) return false;
lastRequest = undefined;
return true;
},
});
}