Skip to content

Commit d01e111

Browse files
author
Benjamin Pasero
committed
debt - model reload should happen from model manager
1 parent fb80de9 commit d01e111

4 files changed

Lines changed: 90 additions & 92 deletions

File tree

src/vs/platform/files/node/diskFileSystemProvider.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -484,15 +484,15 @@ export class DiskFileSystemProvider extends Disposable implements
484484

485485
//#region File Watching
486486

487-
private _onDidWatchErrorOccur: Emitter<string> = this._register(new Emitter<string>());
488-
readonly onDidErrorOccur: Event<string> = this._onDidWatchErrorOccur.event;
487+
private _onDidWatchErrorOccur = this._register(new Emitter<string>());
488+
readonly onDidErrorOccur = this._onDidWatchErrorOccur.event;
489489

490490
private _onDidChangeFile = this._register(new Emitter<readonly IFileChange[]>());
491-
get onDidChangeFile(): Event<readonly IFileChange[]> { return this._onDidChangeFile.event; }
491+
readonly onDidChangeFile = this._onDidChangeFile.event;
492492

493493
private recursiveWatcher: WindowsWatcherService | UnixWatcherService | NsfwWatcherService | undefined;
494494
private recursiveFoldersToWatch: { path: string, excludes: string[] }[] = [];
495-
private recursiveWatchRequestDelayer: ThrottledDelayer<void> = this._register(new ThrottledDelayer<void>(0));
495+
private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer<void>(0));
496496

497497
private recursiveWatcherLogLevelListener: IDisposable | undefined;
498498

src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts

Lines changed: 21 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri';
88
import * as resources from 'vs/base/common/resources';
99
import { IEditorViewState } from 'vs/editor/common/editorCommon';
1010
import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor';
11-
import { ITextFileService, ITextFileEditorModel, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
11+
import { ITextFileService, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles';
1212
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
1313
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
1414
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
@@ -22,17 +22,17 @@ import { isCodeEditor } from 'vs/editor/browser/editorBrowser';
2222
import { IHostService } from 'vs/workbench/services/host/browser/host';
2323
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
2424
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
25-
import { ResourceQueue, timeout } from 'vs/base/common/async';
26-
import { onUnexpectedError } from 'vs/base/common/errors';
25+
import { timeout } from 'vs/base/common/async';
2726
import { withNullAsUndefined } from 'vs/base/common/types';
2827
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
2928
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
3029

3130
export class FileEditorTracker extends Disposable implements IWorkbenchContribution {
32-
private closeOnFileDelete: boolean = false;
33-
private readonly modelLoadQueue = new ResourceQueue();
31+
3432
private readonly activeOutOfWorkspaceWatchers = new ResourceMap<IDisposable>();
3533

34+
private closeOnFileDelete: boolean = false;
35+
3636
constructor(
3737
@IEditorService private readonly editorService: IEditorService,
3838
@ITextFileService private readonly textFileService: ITextFileService,
@@ -177,8 +177,18 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
177177
return undefined;
178178
}
179179

180+
//#endregion
181+
182+
//#region File Changes: Close editors of deleted files
183+
184+
private onFileChanges(e: FileChangesEvent): void {
185+
if (e.gotDeleted()) {
186+
this.handleDeletes(e, true);
187+
}
188+
}
189+
180190
private handleDeletes(arg1: URI | FileChangesEvent, isExternal: boolean, movedTo?: URI): void {
181-
const nonDirtyFileEditors = this.getOpenedFileEditors(false /* non-dirty only */);
191+
const nonDirtyFileEditors = this.getNonDirtyFileEditors();
182192
nonDirtyFileEditors.forEach(async editor => {
183193
const resource = editor.getResource();
184194

@@ -227,26 +237,26 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
227237
});
228238
}
229239

230-
private getOpenedFileEditors(dirtyState: boolean): FileEditorInput[] {
240+
private getNonDirtyFileEditors(): FileEditorInput[] {
231241
const editors: FileEditorInput[] = [];
232242

233243
this.editorService.editors.forEach(editor => {
234244
if (editor instanceof FileEditorInput) {
235-
if (!!editor.isDirty() === dirtyState) {
245+
if (!editor.isDirty()) {
236246
editors.push(editor);
237247
}
238248
} else if (editor instanceof SideBySideEditorInput) {
239249
const master = editor.master;
240250
const details = editor.details;
241251

242252
if (master instanceof FileEditorInput) {
243-
if (!!master.isDirty() === dirtyState) {
253+
if (!master.isDirty()) {
244254
editors.push(master);
245255
}
246256
}
247257

248258
if (details instanceof FileEditorInput) {
249-
if (!!details.isDirty() === dirtyState) {
259+
if (!details.isDirty()) {
250260
editors.push(details);
251261
}
252262
}
@@ -258,52 +268,6 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
258268

259269
//#endregion
260270

261-
//#region File Changes: Update text models and binary editors
262-
263-
private onFileChanges(e: FileChangesEvent): void {
264-
265-
// Handle updates
266-
if (e.gotAdded() || e.gotUpdated()) {
267-
this.handleUpdates(e);
268-
}
269-
270-
// Handle deletes
271-
if (e.gotDeleted()) {
272-
this.handleDeletes(e, true);
273-
}
274-
}
275-
276-
private handleUpdates(e: FileChangesEvent): void {
277-
278-
// Handle updates to text models
279-
this.handleUpdatesToTextModels(e);
280-
}
281-
282-
private handleUpdatesToTextModels(e: FileChangesEvent): void {
283-
284-
// Collect distinct (saved) models to update.
285-
//
286-
// Note: we also consider the added event because it could be that a file was added
287-
// and updated right after.
288-
distinct(coalesce([...e.getUpdated(), ...e.getAdded()]
289-
.map(u => this.textFileService.models.get(u.resource)))
290-
.filter(model => model && !model.isDirty()), model => model.resource.toString())
291-
.forEach(model => this.queueModelLoad(model));
292-
}
293-
294-
private queueModelLoad(model: ITextFileEditorModel): void {
295-
296-
// Load model to update (use a queue to prevent accumulation of loads
297-
// when the load actually takes long. At most we only want the queue
298-
// to have a size of 2 (1 running load and 1 queued load).
299-
const queue = this.modelLoadQueue.queueFor(model.resource);
300-
if (queue.size <= 1) {
301-
queue.queue(() => model.load().then(undefined, onUnexpectedError));
302-
}
303-
}
304-
305-
//#endregion
306-
307271
//#region Text File Dirty: Ensure every dirty text file is opened in an editor
308272

309273
private onTextFilesDirty(events: ReadonlyArray<TextFileModelChangeEvent>): void {
@@ -396,7 +360,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
396360
return model;
397361
})),
398362
model => model.resource.toString()
399-
).forEach(model => this.queueModelLoad(model));
363+
).forEach(model => model.load());
400364
}
401365
}
402366

src/vs/workbench/services/textfile/common/textFileEditorModel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil
6363

6464
static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
6565
static DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 100;
66+
6667
static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json'];
6768
static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json'];
6869

src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,78 +11,85 @@ import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeE
1111
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
1212
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
1313
import { ResourceMap } from 'vs/base/common/map';
14+
import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files';
15+
import { distinct, coalesce } from 'vs/base/common/arrays';
16+
import { ResourceQueue } from 'vs/base/common/async';
17+
import { onUnexpectedError } from 'vs/base/common/errors';
1418

1519
export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager {
1620

17-
private readonly _onModelDisposed: Emitter<URI> = this._register(new Emitter<URI>());
18-
readonly onModelDisposed: Event<URI> = this._onModelDisposed.event;
21+
private readonly _onModelDisposed = this._register(new Emitter<URI>());
22+
readonly onModelDisposed = this._onModelDisposed.event;
1923

20-
private readonly _onModelContentChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
21-
readonly onModelContentChanged: Event<TextFileModelChangeEvent> = this._onModelContentChanged.event;
24+
private readonly _onModelContentChanged = this._register(new Emitter<TextFileModelChangeEvent>());
25+
readonly onModelContentChanged = this._onModelContentChanged.event;
2226

23-
private readonly _onModelDirty: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
24-
readonly onModelDirty: Event<TextFileModelChangeEvent> = this._onModelDirty.event;
27+
private readonly _onModelDirty = this._register(new Emitter<TextFileModelChangeEvent>());
28+
readonly onModelDirty = this._onModelDirty.event;
2529

26-
private readonly _onModelSaveError: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
27-
readonly onModelSaveError: Event<TextFileModelChangeEvent> = this._onModelSaveError.event;
30+
private readonly _onModelSaveError = this._register(new Emitter<TextFileModelChangeEvent>());
31+
readonly onModelSaveError = this._onModelSaveError.event;
2832

29-
private readonly _onModelSaved: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
30-
readonly onModelSaved: Event<TextFileModelChangeEvent> = this._onModelSaved.event;
33+
private readonly _onModelSaved = this._register(new Emitter<TextFileModelChangeEvent>());
34+
readonly onModelSaved = this._onModelSaved.event;
3135

32-
private readonly _onModelReverted: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
33-
readonly onModelReverted: Event<TextFileModelChangeEvent> = this._onModelReverted.event;
36+
private readonly _onModelReverted = this._register(new Emitter<TextFileModelChangeEvent>());
37+
readonly onModelReverted = this._onModelReverted.event;
3438

35-
private readonly _onModelEncodingChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
36-
readonly onModelEncodingChanged: Event<TextFileModelChangeEvent> = this._onModelEncodingChanged.event;
39+
private readonly _onModelEncodingChanged = this._register(new Emitter<TextFileModelChangeEvent>());
40+
readonly onModelEncodingChanged = this._onModelEncodingChanged.event;
3741

38-
private readonly _onModelOrphanedChanged: Emitter<TextFileModelChangeEvent> = this._register(new Emitter<TextFileModelChangeEvent>());
39-
readonly onModelOrphanedChanged: Event<TextFileModelChangeEvent> = this._onModelOrphanedChanged.event;
42+
private readonly _onModelOrphanedChanged = this._register(new Emitter<TextFileModelChangeEvent>());
43+
readonly onModelOrphanedChanged = this._onModelOrphanedChanged.event;
4044

41-
private _onModelsDirty: Event<readonly TextFileModelChangeEvent[]> | undefined;
42-
get onModelsDirty(): Event<readonly TextFileModelChangeEvent[]> {
45+
private _onModelsDirty: Event<ReadonlyArray<TextFileModelChangeEvent>> | undefined;
46+
get onModelsDirty(): Event<ReadonlyArray<TextFileModelChangeEvent>> {
4347
if (!this._onModelsDirty) {
4448
this._onModelsDirty = this.debounce(this.onModelDirty);
4549
}
4650

4751
return this._onModelsDirty;
4852
}
4953

50-
private _onModelsSaveError: Event<readonly TextFileModelChangeEvent[]> | undefined;
51-
get onModelsSaveError(): Event<readonly TextFileModelChangeEvent[]> {
54+
private _onModelsSaveError: Event<ReadonlyArray<TextFileModelChangeEvent>> | undefined;
55+
get onModelsSaveError(): Event<ReadonlyArray<TextFileModelChangeEvent>> {
5256
if (!this._onModelsSaveError) {
5357
this._onModelsSaveError = this.debounce(this.onModelSaveError);
5458
}
5559

5660
return this._onModelsSaveError;
5761
}
5862

59-
private _onModelsSaved: Event<readonly TextFileModelChangeEvent[]> | undefined;
60-
get onModelsSaved(): Event<readonly TextFileModelChangeEvent[]> {
63+
private _onModelsSaved: Event<ReadonlyArray<TextFileModelChangeEvent>> | undefined;
64+
get onModelsSaved(): Event<ReadonlyArray<TextFileModelChangeEvent>> {
6165
if (!this._onModelsSaved) {
6266
this._onModelsSaved = this.debounce(this.onModelSaved);
6367
}
6468

6569
return this._onModelsSaved;
6670
}
6771

68-
private _onModelsReverted: Event<readonly TextFileModelChangeEvent[]> | undefined;
69-
get onModelsReverted(): Event<readonly TextFileModelChangeEvent[]> {
72+
private _onModelsReverted: Event<ReadonlyArray<TextFileModelChangeEvent>> | undefined;
73+
get onModelsReverted(): Event<ReadonlyArray<TextFileModelChangeEvent>> {
7074
if (!this._onModelsReverted) {
7175
this._onModelsReverted = this.debounce(this.onModelReverted);
7276
}
7377

7478
return this._onModelsReverted;
7579
}
7680

77-
private mapResourceToDisposeListener = new ResourceMap<IDisposable>();
78-
private mapResourceToStateChangeListener = new ResourceMap<IDisposable>();
79-
private mapResourceToModelContentChangeListener = new ResourceMap<IDisposable>();
80-
private mapResourceToModel = new ResourceMap<ITextFileEditorModel>();
81-
private mapResourceToPendingModelLoaders = new ResourceMap<Promise<ITextFileEditorModel>>();
81+
private readonly mapResourceToDisposeListener = new ResourceMap<IDisposable>();
82+
private readonly mapResourceToStateChangeListener = new ResourceMap<IDisposable>();
83+
private readonly mapResourceToModelContentChangeListener = new ResourceMap<IDisposable>();
84+
private readonly mapResourceToModel = new ResourceMap<ITextFileEditorModel>();
85+
private readonly mapResourceToPendingModelLoaders = new ResourceMap<Promise<ITextFileEditorModel>>();
86+
87+
private readonly modelLoadQueue = new ResourceQueue();
8288

8389
constructor(
8490
@ILifecycleService private readonly lifecycleService: ILifecycleService,
85-
@IInstantiationService private readonly instantiationService: IInstantiationService
91+
@IInstantiationService private readonly instantiationService: IInstantiationService,
92+
@IFileService private readonly fileService: IFileService
8693
) {
8794
super();
8895

@@ -91,11 +98,37 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
9198

9299
private registerListeners(): void {
93100

101+
// Update models from file change events
102+
this._register(this.fileService.onFileChanges(e => this.onFileChanges(e)));
103+
94104
// Lifecycle
95105
this.lifecycleService.onShutdown(this.dispose, this);
96106
}
97107

98-
private debounce(event: Event<TextFileModelChangeEvent>): Event<readonly TextFileModelChangeEvent[]> {
108+
private onFileChanges(e: FileChangesEvent): void {
109+
110+
// Collect distinct (saved) models to update.
111+
//
112+
// Note: we also consider the added event because it could be that a file was added
113+
// and updated right after.
114+
distinct(coalesce([...e.getUpdated(), ...e.getAdded()]
115+
.map(({ resource }) => this.get(resource)))
116+
.filter(model => model && !model.isDirty()), model => model.resource.toString())
117+
.forEach(model => this.queueModelLoad(model));
118+
}
119+
120+
private queueModelLoad(model: ITextFileEditorModel): void {
121+
122+
// Load model to update (use a queue to prevent accumulation of loads
123+
// when the load actually takes long. At most we only want the queue
124+
// to have a size of 2 (1 running load and 1 queued load).
125+
const queue = this.modelLoadQueue.queueFor(model.resource);
126+
if (queue.size <= 1) {
127+
queue.queue(() => model.load().then(undefined, onUnexpectedError));
128+
}
129+
}
130+
131+
private debounce(event: Event<TextFileModelChangeEvent>): Event<ReadonlyArray<TextFileModelChangeEvent>> {
99132
return Event.debounce(event, (prev: TextFileModelChangeEvent[], cur: TextFileModelChangeEvent) => {
100133
if (!prev) {
101134
prev = [cur];

0 commit comments

Comments
 (0)