Skip to content

Commit 93a6a2a

Browse files
authored
untitled - encapsulate and update model within input (#201712)
1 parent 12656dc commit 93a6a2a

File tree

6 files changed

+72
-26
lines changed

6 files changed

+72
-26
lines changed

src/vs/workbench/browser/parts/editor/textResourceEditor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
156156
}
157157

158158
private onDidEditorPaste(e: IPasteEvent, codeEditor: ICodeEditor): void {
159-
if (this.input instanceof UntitledTextEditorInput && this.input.model.hasLanguageSetExplicitly) {
159+
if (this.input instanceof UntitledTextEditorInput && this.input.hasLanguageSetExplicitly) {
160160
return; // do not override language if it was set explicitly
161161
}
162162

@@ -205,7 +205,7 @@ export class TextResourceEditor extends AbstractTextResourceEditor {
205205
if (candidateLanguage && candidateLanguage.id !== PLAINTEXT_LANGUAGE_ID) {
206206
if (this.input instanceof UntitledTextEditorInput && candidateLanguage.source === 'event') {
207207
// High confidence, set language id at TextEditorModel level to block future auto-detection
208-
this.input.model.setLanguageId(candidateLanguage.id);
208+
this.input.setLanguageId(candidateLanguage.id);
209209
} else {
210210
textModel.setLanguage(this.languageService.createById(candidateLanguage.id));
211211
}

src/vs/workbench/services/textfile/test/browser/textEditorService.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,14 +129,14 @@ suite('TextEditorService', () => {
129129
// Untyped Input (untitled with file path)
130130
input = disposables.add(service.createTextEditor({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }));
131131
assert(input instanceof UntitledTextEditorInput);
132-
assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath);
132+
assert.ok((input as UntitledTextEditorInput).hasAssociatedFilePath);
133133

134134
// Untyped Input (untitled with untitled resource)
135135
untypedInput = { resource: URI.parse('untitled://Untitled-1'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } };
136136
assert.ok(isUntitledResourceEditorInput(untypedInput));
137137
input = disposables.add(service.createTextEditor(untypedInput));
138138
assert(input instanceof UntitledTextEditorInput);
139-
assert.ok(!(input as UntitledTextEditorInput).model.hasAssociatedFilePath);
139+
assert.ok(!(input as UntitledTextEditorInput).hasAssociatedFilePath);
140140

141141
// Untyped input (untitled with custom resource, but forceUntitled)
142142
untypedInput = { resource: URI.file('/fake'), forceUntitled: true };
@@ -149,7 +149,7 @@ suite('TextEditorService', () => {
149149

150150
input = disposables.add(service.createTextEditor({ resource: URI.parse('untitled-custom://some/path'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }));
151151
assert(input instanceof UntitledTextEditorInput);
152-
assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath);
152+
assert.ok((input as UntitledTextEditorInput).hasAssociatedFilePath);
153153

154154
provider.dispose();
155155

src/vs/workbench/services/untitled/common/untitledTextEditorHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class UntitledTextEditorInputSerializer implements IEditorSerializer {
4747
const untitledTextEditorInput = editorInput as UntitledTextEditorInput;
4848

4949
let resource = untitledTextEditorInput.resource;
50-
if (untitledTextEditorInput.model.hasAssociatedFilePath) {
50+
if (untitledTextEditorInput.hasAssociatedFilePath) {
5151
resource = toLocalResource(resource, this.environmentService.remoteAuthority, this.pathService.defaultUriScheme); // untitled with associated file path use the local schema
5252
}
5353

@@ -59,7 +59,7 @@ export class UntitledTextEditorInputSerializer implements IEditorSerializer {
5959
const languageIdCandidate = untitledTextEditorInput.getLanguageId();
6060
if (languageIdCandidate !== PLAINTEXT_LANGUAGE_ID) {
6161
languageId = languageIdCandidate;
62-
} else if (untitledTextEditorInput.model.hasLanguageSetExplicitly) {
62+
} else if (untitledTextEditorInput.hasLanguageSetExplicitly) {
6363
languageId = languageIdCandidate;
6464
}
6565

src/vs/workbench/services/untitled/common/untitledTextEditorInput.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { IPathService } from 'vs/workbench/services/path/common/pathService';
1818
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
1919
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
2020
import { ITextModelService } from 'vs/editor/common/services/resolverService';
21-
import { dispose, IReference } from 'vs/base/common/lifecycle';
21+
import { DisposableStore, dispose, IReference } from 'vs/base/common/lifecycle';
2222
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration';
2323

2424
/**
@@ -37,10 +37,11 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
3737
}
3838

3939
private modelResolve: Promise<void> | undefined = undefined;
40+
private readonly modelDisposables = this._register(new DisposableStore());
4041
private cachedUntitledTextEditorModelReference: IReference<IUntitledTextEditorModel> | undefined = undefined;
4142

4243
constructor(
43-
readonly model: IUntitledTextEditorModel,
44+
protected model: IUntitledTextEditorModel,
4445
@ITextFileService textFileService: ITextFileService,
4546
@ILabelService labelService: ILabelService,
4647
@IEditorService editorService: IEditorService,
@@ -54,16 +55,31 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
5455
super(model.resource, undefined, editorService, textFileService, labelService, fileService, filesConfigurationService, textResourceConfigurationService);
5556

5657
this.registerModelListeners(model);
58+
59+
this._register(this.textFileService.untitled.onDidCreate(model => this.onDidCreateUntitledModel(model)));
5760
}
5861

5962
private registerModelListeners(model: IUntitledTextEditorModel): void {
63+
this.modelDisposables.clear();
6064

6165
// re-emit some events from the model
62-
this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
63-
this._register(model.onDidChangeName(() => this._onDidChangeLabel.fire()));
66+
this.modelDisposables.add(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
67+
this.modelDisposables.add(model.onDidChangeName(() => this._onDidChangeLabel.fire()));
6468

6569
// a reverted untitled text editor model renders this input disposed
66-
this._register(model.onDidRevert(() => this.dispose()));
70+
this.modelDisposables.add(model.onDidRevert(() => this.dispose()));
71+
}
72+
73+
private onDidCreateUntitledModel(model: IUntitledTextEditorModel): void {
74+
if (isEqual(model.resource, this.model.resource) && model !== this.model) {
75+
76+
// Ensure that we keep our model up to date with
77+
// the actual model from the service so that we
78+
// never get out of sync with the truth.
79+
80+
this.model = model;
81+
this.registerModelListeners(model);
82+
}
6783
}
6884

6985
override getName(): string {
@@ -116,6 +132,10 @@ export class UntitledTextEditorInput extends AbstractTextResourceEditorInput imp
116132
return this.model.setEncoding(encoding);
117133
}
118134

135+
get hasLanguageSetExplicitly() { return this.model.hasLanguageSetExplicitly; }
136+
137+
get hasAssociatedFilePath() { return this.model.hasAssociatedFilePath; }
138+
119139
setLanguageId(languageId: string, source?: string): void {
120140
this.model.setLanguageId(languageId, source);
121141
}

src/vs/workbench/services/untitled/common/untitledTextEditorService.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export interface IUntitledTextEditorModelManager {
7878
*/
7979
readonly onDidChangeLabel: Event<IUntitledTextEditorModel>;
8080

81+
/**
82+
* Events for when untitled text editor models are created.
83+
*/
84+
readonly onDidCreate: Event<IUntitledTextEditorModel>;
85+
8186
/**
8287
* Events for when untitled text editors are about to be disposed.
8388
*/
@@ -143,6 +148,9 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
143148
private readonly _onDidChangeEncoding = this._register(new Emitter<IUntitledTextEditorModel>());
144149
readonly onDidChangeEncoding = this._onDidChangeEncoding.event;
145150

151+
private readonly _onDidCreate = this._register(new Emitter<IUntitledTextEditorModel>());
152+
readonly onDidCreate = this._onDidCreate.event;
153+
146154
private readonly _onWillDispose = this._register(new Emitter<IUntitledTextEditorModel>());
147155
readonly onWillDispose = this._onWillDispose.event;
148156

@@ -267,6 +275,9 @@ export class UntitledTextEditorService extends Disposable implements IUntitledTe
267275
// Add to cache
268276
this.mapResourceToModel.set(model.resource, model);
269277

278+
// Emit as event
279+
this._onDidCreate.fire(model);
280+
270281
// If the model is dirty right from the beginning,
271282
// make sure to emit this as an event
272283
if (model.isDirty()) {

src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import { timeout } from 'vs/base/common/async';
2626

2727
suite('Untitled text editors', () => {
2828

29+
class TestUntitledTextEditorInput extends UntitledTextEditorInput {
30+
getModel() { return this.model; }
31+
}
32+
2933
const disposables = new DisposableStore();
3034
let instantiationService: IInstantiationService;
3135
let accessor: TestServiceAccessor;
@@ -44,11 +48,19 @@ suite('Untitled text editors', () => {
4448
const service = accessor.untitledTextEditorService;
4549
const workingCopyService = accessor.workingCopyService;
4650

47-
const input1 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
51+
const events: IUntitledTextEditorModel[] = [];
52+
disposables.add(service.onDidCreate(model => {
53+
events.push(model);
54+
}));
55+
56+
const input1 = instantiationService.createInstance(TestUntitledTextEditorInput, service.create());
4857
await input1.resolve();
49-
assert.strictEqual(service.get(input1.resource), input1.model);
58+
assert.strictEqual(service.get(input1.resource), input1.getModel());
5059
assert.ok(!accessor.untitledTextEditorService.isUntitledWithAssociatedResource(input1.resource));
5160

61+
assert.strictEqual(events.length, 1);
62+
assert.strictEqual(events[0].resource.toString(), input1.getModel().resource.toString());
63+
5264
assert.ok(service.get(input1.resource));
5365
assert.ok(!service.get(URI.file('testing')));
5466

@@ -59,16 +71,16 @@ suite('Untitled text editors', () => {
5971
assert.ok(!input1.hasCapability(EditorInputCapabilities.RequiresTrust));
6072
assert.ok(!input1.hasCapability(EditorInputCapabilities.Scratchpad));
6173

62-
const input2 = instantiationService.createInstance(UntitledTextEditorInput, service.create());
63-
assert.strictEqual(service.get(input2.resource), input2.model);
74+
const input2 = instantiationService.createInstance(TestUntitledTextEditorInput, service.create());
75+
assert.strictEqual(service.get(input2.resource), input2.getModel());
6476

6577
// toUntyped()
6678
const untypedInput = input1.toUntyped({ preserveViewState: 0 });
6779
assert.strictEqual(untypedInput.forceUntitled, true);
6880

6981
// get()
70-
assert.strictEqual(service.get(input1.resource), input1.model);
71-
assert.strictEqual(service.get(input2.resource), input2.model);
82+
assert.strictEqual(service.get(input1.resource), input1.getModel());
83+
assert.strictEqual(service.get(input2.resource), input2.getModel());
7284

7385
// revert()
7486
await input1.revert(0);
@@ -80,6 +92,9 @@ suite('Untitled text editors', () => {
8092
assert.strictEqual(await service.resolve({ untitledResource: input2.resource }), model);
8193
assert.ok(service.get(model.resource));
8294

95+
assert.strictEqual(events.length, 2);
96+
assert.strictEqual(events[1].resource.toString(), input2.resource.toString());
97+
8398
assert.ok(!input2.isDirty());
8499

85100
const resourcePromise = awaitDidChangeDirty(accessor.untitledTextEditorService);
@@ -214,10 +229,10 @@ suite('Untitled text editors', () => {
214229
const service = accessor.untitledTextEditorService;
215230
const workingCopyService = accessor.workingCopyService;
216231

217-
const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' })));
232+
const untitled = disposables.add(instantiationService.createInstance(TestUntitledTextEditorInput, service.create({ initialValue: 'Hello World' })));
218233
assert.ok(untitled.isDirty());
219234

220-
const backup = (await untitled.model.backup(CancellationToken.None)).content;
235+
const backup = (await untitled.getModel().backup(CancellationToken.None)).content;
221236
if (isReadableStream(backup)) {
222237
const value = await streamToBuffer(backup as VSBufferReadableStream);
223238
assert.strictEqual(value.toString(), 'Hello World');
@@ -307,9 +322,9 @@ suite('Untitled text editors', () => {
307322
const model = disposables.add(service.create());
308323
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
309324

310-
assert.ok(!input.model.hasLanguageSetExplicitly);
325+
assert.ok(!input.hasLanguageSetExplicitly);
311326
input.setLanguageId(PLAINTEXT_LANGUAGE_ID);
312-
assert.ok(input.model.hasLanguageSetExplicitly);
327+
assert.ok(input.hasLanguageSetExplicitly);
313328

314329
assert.strictEqual(input.getLanguageId(), PLAINTEXT_LANGUAGE_ID);
315330
});
@@ -327,9 +342,9 @@ suite('Untitled text editors', () => {
327342
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
328343
disposables.add(await input.resolve());
329344

330-
assert.ok(!input.model.hasLanguageSetExplicitly);
345+
assert.ok(!input.hasLanguageSetExplicitly);
331346
model.textEditorModel!.setLanguage(accessor.languageService.createById(language));
332-
assert.ok(input.model.hasLanguageSetExplicitly);
347+
assert.ok(input.hasLanguageSetExplicitly);
333348

334349
assert.strictEqual(model.getLanguageId(), language);
335350
});
@@ -346,12 +361,12 @@ suite('Untitled text editors', () => {
346361
const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model));
347362
await input.resolve();
348363

349-
assert.ok(!input.model.hasLanguageSetExplicitly);
364+
assert.ok(!input.hasLanguageSetExplicitly);
350365
model.textEditorModel!.setLanguage(
351366
accessor.languageService.createById(language),
352367
// This is really what this is testing
353368
LanguageDetectionLanguageEventSource);
354-
assert.ok(!input.model.hasLanguageSetExplicitly);
369+
assert.ok(!input.hasLanguageSetExplicitly);
355370

356371
assert.strictEqual(model.getLanguageId(), language);
357372
});

0 commit comments

Comments
 (0)