Skip to content

Commit

Permalink
Defers the WorkingCopyService.onDidContentChange event
Browse files Browse the repository at this point in the history
Previously dealing with working copies was taking about 1.5% of the total
runtime for the keypress function, this reduces it to around 0.2% by
deferring all handling of the event until the text has rendered. Since the
onDidContentChange event is not used, this should be a low risk change.

Part of #161622
  • Loading branch information
Tyriar committed Oct 14, 2022
1 parent 1bcb42f commit 5df36bc
Show file tree
Hide file tree
Showing 13 changed files with 30 additions and 2 deletions.
12 changes: 12 additions & 0 deletions src/vs/base/common/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ export namespace Event {
}
}

/**
* Given an event, returns another event which debounces calls and defers the listeners to a
* later task via a shared `setTimeout`. The event is converted into a signal (`Event<void>`) to
* avoid additional object creation as a result of merging events and to try prevent race
* conditions that could arise when using related deferred and non-deferred events.
*
* This is useful for deferring non-critical work (eg. general UI updates) to ensure it does not
* block critical work (eg. latency of keypress to text rendered).
*/
export function defer(event: Event<unknown>): Event<void> {
return debounce<unknown, void>(event, () => void 0, 0);
}

/**
* Given an event, returns another event which only fires once.
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/browser/mainThreadCustomEditors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ class MainThreadCustomEditorModel extends ResourceWorkingCopy implements ICustom

private readonly _onDidChangeContent: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeContent: Event<void> = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred: Event<void> = Event.defer(this._onDidChangeContent.event);

private readonly _onDidSave: Emitter<IWorkingCopySaveEvent> = this._register(new Emitter<IWorkingCopySaveEvent>());
readonly onDidSave: Event<IWorkingCopySaveEvent> = this._onDidSave.event;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export class ComplexNotebookEditorModel extends EditorModel implements INotebook
readonly capabilities = that._isUntitled() ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None;
readonly onDidChangeDirty = that.onDidChangeDirty;
readonly onDidChangeContent = that._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(that._onDidChangeContent.event);
readonly onDidSave = that.onDidSave;
isDirty(): boolean { return that.isDirty(); }
backup(token: CancellationToken): Promise<IWorkingCopyBackup> { return that.backup(token); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class SearchEditorInput extends EditorInput {
readonly capabilities = input.hasCapability(EditorInputCapabilities.Untitled) ? WorkingCopyCapabilities.Untitled : WorkingCopyCapabilities.None;
readonly onDidChangeDirty = input.onDidChangeDirty;
readonly onDidChangeContent = input.onDidChangeContent;
readonly onDidChangeContentDeferred = Event.defer(input.onDidChangeContent);
readonly onDidSave = input.onDidSave;
isDirty(): boolean { return input.isDirty(); }
backup(token: CancellationToken): Promise<IWorkingCopyBackup> { return input.backup(token); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { localize } from 'vs/nls';
import { Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { assertIsDefined, withNullAsUndefined } from 'vs/base/common/types';
import { EncodingMode, ITextFileService, TextFileEditorModelState, ITextFileEditorModel, ITextFileStreamContent, ITextFileResolveOptions, IResolvedTextFileEditorModel, ITextFileSaveOptions, TextFileResolveReason, ITextFileEditorModelSaveEvent } from 'vs/workbench/services/textfile/common/textfiles';
Expand Down Expand Up @@ -51,6 +51,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil

private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(this._onDidChangeContent.event);

private readonly _onDidResolve = this._register(new Emitter<TextFileResolveReason>());
readonly onDidResolve = this._onDidResolve.event;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class UntitledTextEditorModel extends BaseTextEditorModel implements IUnt

private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(this._onDidChangeContent.event);

private readonly _onDidChangeName = this._register(new Emitter<void>());
readonly onDidChangeName = this._onDidChangeName.event;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export abstract class ResourceWorkingCopy extends Disposable implements IResourc

abstract onDidChangeDirty: Event<void>;
abstract onDidChangeContent: Event<void>;
abstract onDidChangeContentDeferred: Event<void>;
abstract onDidSave: Event<IWorkingCopySaveEvent>;

abstract isDirty(): boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ export class StoredFileWorkingCopy<M extends IStoredFileWorkingCopyModel> extend

private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(this._onDidChangeContent.event);

private readonly _onDidResolve = this._register(new Emitter<void>());
readonly onDidResolve = this._onDidResolve.event;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export class UntitledFileWorkingCopy<M extends IUntitledFileWorkingCopyModel> ex

private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(this._onDidChangeContent.event);

private readonly _onDidChangeDirty = this._register(new Emitter<void>());
readonly onDidChangeDirty = this._onDidChangeDirty.event;
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/services/workingCopy/common/workingCopy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@ export interface IWorkingCopy extends IWorkingCopyIdentifier {
*/
readonly onDidChangeContent: Event<void>;

/**
* Used by the workbench e.g. to trigger auto-save
* (unless this working copy is untitled) and backups.
*/
readonly onDidChangeContentDeferred: Event<void>;

/**
* Used by the workbench e.g. to track local history
* (unless this working copy is untitled).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic

// Wire in Events
const disposables = new DisposableStore();
disposables.add(workingCopy.onDidChangeContent(() => this._onDidChangeContent.fire(workingCopy)));
disposables.add(workingCopy.onDidChangeContentDeferred(() => this._onDidChangeContent.fire(workingCopy)));
disposables.add(workingCopy.onDidChangeDirty(() => this._onDidChangeDirty.fire(workingCopy)));
disposables.add(workingCopy.onDidSave(e => this._onDidSave.fire({ workingCopy, ...e })));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ suite('ResourceWorkingCopy', function () {
capabilities = WorkingCopyCapabilities.None;
onDidChangeDirty = Event.None;
onDidChangeContent = Event.None;
onDidChangeContentDeferred = Event.None;
onDidSave = Event.None;
isDirty(): boolean { return false; }
async backup(token: CancellationToken): Promise<IWorkingCopyBackup> { throw new Error('Method not implemented.'); }
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/test/common/workbenchTestServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export class TestWorkingCopy extends Disposable implements IWorkingCopy {

private readonly _onDidChangeContent = this._register(new Emitter<void>());
readonly onDidChangeContent = this._onDidChangeContent.event;
readonly onDidChangeContentDeferred = Event.defer(this._onDidChangeContent.event);

private readonly _onDidSave = this._register(new Emitter<IStoredFileWorkingCopySaveEvent>());
readonly onDidSave = this._onDidSave.event;
Expand Down

0 comments on commit 5df36bc

Please sign in to comment.