Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Debouncer; use it for HatAllocator #1646

Merged
merged 1 commit into from
Jul 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions packages/cursorless-engine/src/core/Debouncer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ide } from "../singletons/ide.singleton";

/**
* Debounces a callback. Uses the `decorationDebounceDelayMs` configuration
* value to determine the debounce delay.
*/
export class Debouncer {
private timeoutHandle: NodeJS.Timeout | null = null;

constructor(
/** The callback to debounce */
private callback: () => void,
) {
this.run = this.run.bind(this);
}

run() {
if (this.timeoutHandle != null) {
clearTimeout(this.timeoutHandle);
}

const decorationDebounceDelayMs = ide().configuration.getOwnConfiguration(
"decorationDebounceDelayMs",
);

this.timeoutHandle = setTimeout(() => {
this.callback();
this.timeoutHandle = null;
}, decorationDebounceDelayMs);
}

dispose() {
if (this.timeoutHandle != null) {
clearTimeout(this.timeoutHandle);
}
}
}
46 changes: 14 additions & 32 deletions packages/cursorless-engine/src/core/HatAllocator.ts
Original file line number Diff line number Diff line change
@@ -2,44 +2,45 @@ import type { Disposable, Hats, TokenHat } from "@cursorless/common";
import { ide } from "../singletons/ide.singleton";
import tokenGraphemeSplitter from "../singletons/tokenGraphemeSplitter.singleton";
import { allocateHats } from "../util/allocateHats";
import { Debouncer } from "./Debouncer";
import { IndividualHatMap } from "./IndividualHatMap";

interface Context {
getActiveMap(): Promise<IndividualHatMap>;
}

export class HatAllocator {
private timeoutHandle: NodeJS.Timeout | null = null;
private disposables: Disposable[] = [];
private debouncer = new Debouncer(() => this.allocateHats());

constructor(private hats: Hats, private context: Context) {
ide().disposeOnExit(this);

this.allocateHatsDebounced = this.allocateHatsDebounced.bind(this);

this.disposables.push(
this.hats.onDidChangeEnabledHatStyles(this.allocateHatsDebounced),
this.hats.onDidChangeIsEnabled(this.allocateHatsDebounced),
this.hats.onDidChangeEnabledHatStyles(this.debouncer.run),
this.hats.onDidChangeIsEnabled(this.debouncer.run),

// An event that fires when a text document opens
ide().onDidOpenTextDocument(this.allocateHatsDebounced),
ide().onDidOpenTextDocument(this.debouncer.run),
// An event that fires when a text document closes
ide().onDidCloseTextDocument(this.allocateHatsDebounced),
ide().onDidCloseTextDocument(this.debouncer.run),
// An Event which fires when the active editor has changed. Note that the event also fires when the active editor changes to undefined.
ide().onDidChangeActiveTextEditor(this.allocateHatsDebounced),
ide().onDidChangeActiveTextEditor(this.debouncer.run),
// An Event which fires when the array of visible editors has changed.
ide().onDidChangeVisibleTextEditors(this.allocateHatsDebounced),
ide().onDidChangeVisibleTextEditors(this.debouncer.run),
// An event that is emitted when a text document is changed. This usually happens when the contents changes but also when other things like the dirty-state changes.
ide().onDidChangeTextDocument(this.allocateHatsDebounced),
ide().onDidChangeTextDocument(this.debouncer.run),
// An Event which fires when the selection in an editor has changed.
ide().onDidChangeTextEditorSelection(this.allocateHatsDebounced),
ide().onDidChangeTextEditorSelection(this.debouncer.run),
// An Event which fires when the visible ranges of an editor has changed.
ide().onDidChangeTextEditorVisibleRanges(this.allocateHatsDebounced),
ide().onDidChangeTextEditorVisibleRanges(this.debouncer.run),
// Re-draw hats on grapheme splitting algorithm change in case they
// changed their token hat splitting setting.
tokenGraphemeSplitter().registerAlgorithmChangeListener(
this.allocateHatsDebounced,
this.debouncer.run,
),

this.debouncer,
);
}

@@ -75,26 +76,7 @@ export class HatAllocator {
);
}

allocateHatsDebounced() {
if (this.timeoutHandle != null) {
clearTimeout(this.timeoutHandle);
}

const decorationDebounceDelayMs = ide().configuration.getOwnConfiguration(
"decorationDebounceDelayMs",
);

this.timeoutHandle = setTimeout(() => {
this.allocateHats();
this.timeoutHandle = null;
}, decorationDebounceDelayMs);
}

dispose() {
this.disposables.forEach(({ dispose }) => dispose());

if (this.timeoutHandle != null) {
clearTimeout(this.timeoutHandle);
}
}
}