Skip to content

Commit b081995

Browse files
author
Benjamin Pasero
committed
typed events for untitled (for microsoft#7176)
1 parent e5d1400 commit b081995

12 files changed

Lines changed: 88 additions & 66 deletions

File tree

src/vs/test/utils/servicesTestUtils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import {IModeService} from 'vs/editor/common/services/modeService';
4141
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
4242
import {ITextFileService} from 'vs/workbench/parts/files/common/files';
4343
import {IHistoryService} from 'vs/workbench/services/history/common/history';
44+
import {UntitledEditorEvent} from 'vs/workbench/common/events';
4445

4546
export const TestWorkspace: IWorkspace = {
4647
resource: URI.file('C:\\testWorkspace'),
@@ -283,6 +284,12 @@ export class TestStorageService extends EventEmitter implements IStorageService
283284
export class TestUntitledEditorService implements IUntitledEditorService {
284285
public _serviceBrand: any;
285286

287+
private _onDidChangeDirty = new Emitter<UntitledEditorEvent>();
288+
289+
public get onDidChangeDirty(): Event<UntitledEditorEvent> {
290+
return this._onDidChangeDirty.event;
291+
}
292+
286293
public get(resource: URI) {
287294
return null;
288295
}

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {TextEditorOptions, EditorModel, EditorInput, EditorOptions} from 'vs/wor
1313
import {BaseTextEditorModel} from 'vs/workbench/common/editor/textEditorModel';
1414
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
1515
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
16-
import {UntitledEditorEvent, EventType} from 'vs/workbench/common/events';
16+
import {UntitledEditorEvent} from 'vs/workbench/common/events';
1717
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
1818
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
1919
import {IStorageService} from 'vs/platform/storage/common/storage';
@@ -23,6 +23,7 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat
2323
import {IMessageService} from 'vs/platform/message/common/message';
2424
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
2525
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
26+
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
2627

2728
/**
2829
* An editor implementation that is capable of showing string inputs or promise inputs that resolve to a string.
@@ -43,17 +44,20 @@ export class StringEditor extends BaseTextEditor {
4344
@IConfigurationService configurationService: IConfigurationService,
4445
@IEventService eventService: IEventService,
4546
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
46-
@IThemeService themeService: IThemeService
47+
@IThemeService themeService: IThemeService,
48+
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
4749
) {
4850
super(StringEditor.ID, telemetryService, instantiationService, contextService, storageService, messageService, configurationService, eventService, editorService, themeService);
4951

5052
this.mapResourceToEditorViewState = Object.create(null);
5153

52-
this.toUnbind.push(this.eventService.addListener2(EventType.UNTITLED_FILE_SAVED, (e: UntitledEditorEvent) => this.onUntitledSavedEvent(e)));
54+
this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDirtyChange(e)));
5355
}
5456

55-
private onUntitledSavedEvent(e: UntitledEditorEvent): void {
56-
delete this.mapResourceToEditorViewState[e.resource.toString()];
57+
private onUntitledDirtyChange(e: UntitledEditorEvent): void {
58+
if (!this.untitledEditorService.isDirty(e.resource)) {
59+
delete this.mapResourceToEditorViewState[e.resource.toString()]; // untitled file got reverted, so remove view state
60+
}
5761
}
5862

5963
public getTitle(): string {

src/vs/workbench/common/editor.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ export interface IEditorInputFactory {
121121
* Each editor input is mapped to an editor that is capable of opening it through the Platform facade.
122122
*/
123123
export abstract class EditorInput extends EventEmitter implements IEditorInput {
124+
124125
protected _onDidChangeDirty: Emitter<void>;
126+
125127
private disposed: boolean;
126128

127129
constructor() {

src/vs/workbench/common/editor/untitledEditorInput.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
1717
import {IModeService} from 'vs/editor/common/services/modeService';
1818
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
1919
import {IEventService} from 'vs/platform/event/common/event';
20-
import {EventType as WorkbenchEventType, UntitledEditorEvent} from 'vs/workbench/common/events';
2120

2221
import {ITextFileService} from 'vs/workbench/parts/files/common/files'; // TODO@Ben layer breaker
2322

@@ -53,19 +52,6 @@ export class UntitledEditorInput extends AbstractUntitledEditorInput {
5352
this.hasAssociatedFilePath = hasAssociatedFilePath;
5453
this.modeId = modeId;
5554
this.toUnbind = [];
56-
57-
this.registerListeners();
58-
}
59-
60-
private registerListeners(): void {
61-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_SAVED, (e: UntitledEditorEvent) => this.onDirtyStateChange(e)));
62-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_DIRTY, (e: UntitledEditorEvent) => this.onDirtyStateChange(e)));
63-
}
64-
65-
private onDirtyStateChange(e: UntitledEditorEvent): void {
66-
if (e.resource.toString() === this.resource.toString()) {
67-
this._onDidChangeDirty.fire();
68-
}
6955
}
7056

7157
public getTypeId(): string {
@@ -163,7 +149,12 @@ export class UntitledEditorInput extends AbstractUntitledEditorInput {
163149
}
164150
}
165151

166-
return this.instantiationService.createInstance(UntitledEditorModel, content, mime || MIME_TEXT, this.resource, this.hasAssociatedFilePath);
152+
const model = this.instantiationService.createInstance(UntitledEditorModel, content, mime || MIME_TEXT, this.resource, this.hasAssociatedFilePath);
153+
154+
// detect dirty state changes on model and re-emit
155+
this.toUnbind.push(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
156+
157+
return model;
167158
}
168159

169160
public matches(otherInput: any): boolean {

src/vs/workbench/common/editor/untitledEditorModel.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,23 @@ import {EditorModel, IEncodingSupport} from 'vs/workbench/common/editor';
1010
import {StringEditorModel} from 'vs/workbench/common/editor/stringEditorModel';
1111
import URI from 'vs/base/common/uri';
1212
import {EventType, EndOfLinePreference} from 'vs/editor/common/editorCommon';
13-
import {EventType as WorkbenchEventType, UntitledEditorEvent, ResourceEvent} from 'vs/workbench/common/events';
13+
import {EventType as WorkbenchEventType, ResourceEvent} from 'vs/workbench/common/events';
1414
import {IFilesConfiguration} from 'vs/platform/files/common/files';
1515
import {IConfigurationService} from 'vs/platform/configuration/common/configuration';
1616
import {IEventService} from 'vs/platform/event/common/event';
1717
import {IModeService} from 'vs/editor/common/services/modeService';
1818
import {IModelService} from 'vs/editor/common/services/modelService';
1919
import {IMode} from 'vs/editor/common/modes';
2020
import {isUnspecific} from 'vs/base/common/mime';
21+
import Event, {Emitter} from 'vs/base/common/event';
2122

2223
export class UntitledEditorModel extends StringEditorModel implements IEncodingSupport {
2324
private textModelChangeListener: IDisposable;
2425
private configurationChangeListener: IDisposable;
2526

2627
private dirty: boolean;
28+
private _onDidChangeDirty: Emitter<void>;
29+
2730
private configuredEncoding: string;
2831
private preferredEncoding: string;
2932

@@ -41,9 +44,15 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS
4144

4245
this.dirty = hasAssociatedFilePath; // untitled associated to file path are dirty right away
4346

47+
this._onDidChangeDirty = new Emitter<void>();
48+
4449
this.registerListeners();
4550
}
4651

52+
public get onDidChangeDirty(): Event<void> {
53+
return this._onDidChangeDirty.event;
54+
}
55+
4756
protected getOrCreateMode(modeService: IModeService, mime: string, firstLineText?: string): TPromise<IMode> {
4857
if (isUnspecific(mime)) {
4958
return modeService.getOrCreateModeByFilenameOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided mime is unspecific
@@ -99,7 +108,7 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS
99108
public revert(): void {
100109
this.dirty = false;
101110

102-
this.eventService.emit(WorkbenchEventType.UNTITLED_FILE_SAVED, new UntitledEditorEvent(this.resource));
111+
this._onDidChangeDirty.fire();
103112
}
104113

105114
public load(): TPromise<EditorModel> {
@@ -115,7 +124,7 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS
115124
// Emit initial dirty event if we are
116125
if (this.dirty) {
117126
setTimeout(() => {
118-
this.eventService.emit(WorkbenchEventType.UNTITLED_FILE_DIRTY, new UntitledEditorEvent(this.resource));
127+
this._onDidChangeDirty.fire();
119128
}, 0 /* prevent race condition between creating model and emitting dirty event */);
120129
}
121130

@@ -126,7 +135,7 @@ export class UntitledEditorModel extends StringEditorModel implements IEncodingS
126135
private onModelContentChanged(): void {
127136
if (!this.dirty) {
128137
this.dirty = true;
129-
this.eventService.emit(WorkbenchEventType.UNTITLED_FILE_DIRTY, new UntitledEditorEvent(this.resource));
138+
this._onDidChangeDirty.fire();
130139
}
131140
}
132141

src/vs/workbench/common/events.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,6 @@ import {Event} from 'vs/base/common/events';
1212
*/
1313
export class EventType {
1414

15-
/**
16-
* Event type for when an untitled file is becoming dirty.
17-
*/
18-
static UNTITLED_FILE_DIRTY = 'untitledFileDirty';
19-
20-
/**
21-
* Event type for when an untitled file is saved.
22-
*/
23-
static UNTITLED_FILE_SAVED = 'untitledFileSaved';
24-
2515
/**
2616
* Event type for when a resources encoding changes.
2717
*/

src/vs/workbench/parts/files/browser/fileActions.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import {Action, IAction} from 'vs/base/common/actions';
2323
import {MessageType, IInputValidator} from 'vs/base/browser/ui/inputbox/inputBox';
2424
import {ITree, IHighlightEvent} from 'vs/base/parts/tree/browser/tree';
2525
import {dispose, IDisposable} from 'vs/base/common/lifecycle';
26-
import {EventType as WorkbenchEventType} from 'vs/workbench/common/events';
2726
import {LocalFileChangeEvent, VIEWLET_ID, ITextFileService, TextFileChangeEvent, EventType as FileEventType} from 'vs/workbench/parts/files/common/files';
2827
import {IFileService, IFileStat, IImportResult} from 'vs/platform/files/common/files';
2928
import {DiffEditorInput, toDiffLabel} from 'vs/workbench/common/editor/diffEditorInput';
@@ -1565,8 +1564,7 @@ export abstract class BaseSaveAllAction extends BaseActionWithErrorReporting {
15651564
this.toDispose.push(this.eventService.addListener2(FileEventType.FILE_SAVE_ERROR, (e: TextFileChangeEvent) => this.updateEnablement(true)));
15661565

15671566
if (this.includeUntitled()) {
1568-
this.toDispose.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_DIRTY, () => this.updateEnablement(true)));
1569-
this.toDispose.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_SAVED, () => this.updateEnablement(false)));
1567+
this.toDispose.push(this.untitledEditorService.onDidChangeDirty(e => this.updateEnablement(this.untitledEditorService.isDirty(e.resource))));
15701568
}
15711569
}
15721570

src/vs/workbench/parts/files/browser/fileTracker.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {LocalFileChangeEvent, TextFileChangeEvent, VIEWLET_ID, BINARY_FILE_EDITO
2222
import {FileChangeType, FileChangesEvent, EventType as CommonFileEventType, IFileService} from 'vs/platform/files/common/files';
2323
import {FileEditorInput} from 'vs/workbench/parts/files/common/editors/fileEditorInput';
2424
import {TextFileEditorModel, CACHE} from 'vs/workbench/parts/files/common/editors/textFileEditorModel';
25-
import {EventType as WorkbenchEventType, UntitledEditorEvent} from 'vs/workbench/common/events';
25+
import {UntitledEditorEvent} from 'vs/workbench/common/events';
2626
import {IEditorGroupService} from 'vs/workbench/services/group/common/groupService';
2727
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
2828
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
@@ -78,8 +78,7 @@ export class FileTracker implements IWorkbenchContribution {
7878

7979
// Update editors and inputs from local changes and saves
8080
this.toUnbind.push(this.editorGroupService.onEditorsChanged(() => this.onEditorsChanged()));
81-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_SAVED, (e: UntitledEditorEvent) => this.onUntitledEditorSaved(e)));
82-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_DIRTY, (e: UntitledEditorEvent) => this.onUntitledEditorDirty(e)));
81+
this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDidChangeDirty(e)));
8382
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_DIRTY, (e: TextFileChangeEvent) => this.onTextFileDirty(e)));
8483
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_SAVE_ERROR, (e: TextFileChangeEvent) => this.onTextFileSaveError(e)));
8584
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_SAVED, (e: TextFileChangeEvent) => this.onTextFileSaved(e)));
@@ -151,12 +150,10 @@ export class FileTracker implements IWorkbenchContribution {
151150
}
152151
}
153152

154-
private onUntitledEditorDirty(e: UntitledEditorEvent): void {
155-
this.updateActivityBadge();
156-
}
153+
private onUntitledDidChangeDirty(e: UntitledEditorEvent): void {
154+
const gotDirty = this.untitledEditorService.isDirty(e.resource);
157155

158-
private onUntitledEditorSaved(e: UntitledEditorEvent): void {
159-
if (this.lastDirtyCount > 0) {
156+
if (gotDirty || this.lastDirtyCount > 0) {
160157
this.updateActivityBadge();
161158
}
162159
}

src/vs/workbench/parts/files/electron-browser/macIntegration.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ import {IWorkbenchContribution} from 'vs/workbench/common/contributions';
99
import {TextFileChangeEvent, EventType as FileEventType, ITextFileService, AutoSaveMode} from 'vs/workbench/parts/files/common/files';
1010
import {platform, Platform} from 'vs/base/common/platform';
1111
import {IWindowService} from 'vs/workbench/services/window/electron-browser/windowService';
12-
import {EventType as WorkbenchEventType} from 'vs/workbench/common/events';
1312
import {IEventService} from 'vs/platform/event/common/event';
1413
import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle';
1514
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
1615
import {ipcRenderer as ipc} from 'electron';
16+
import {UntitledEditorEvent} from 'vs/workbench/common/events';
17+
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
1718

1819
export class MacIntegration implements IWorkbenchContribution {
1920
private isDocumentedEdited: boolean;
@@ -23,7 +24,8 @@ export class MacIntegration implements IWorkbenchContribution {
2324
@IEventService private eventService: IEventService,
2425
@ITextFileService private textFileService: ITextFileService,
2526
@ILifecycleService private lifecycleService: ILifecycleService,
26-
@IWindowService private windowService: IWindowService
27+
@IWindowService private windowService: IWindowService,
28+
@IUntitledEditorService private untitledEditorService: IUntitledEditorService
2729
) {
2830
this.toUnbind = [];
2931
this.isDocumentedEdited = false;
@@ -34,8 +36,7 @@ export class MacIntegration implements IWorkbenchContribution {
3436
private registerListeners(): void {
3537

3638
// Local text file changes
37-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_SAVED, () => this.onUntitledSavedEvent()));
38-
this.toUnbind.push(this.eventService.addListener2(WorkbenchEventType.UNTITLED_FILE_DIRTY, () => this.onUntitledDirtyEvent()));
39+
this.toUnbind.push(this.untitledEditorService.onDidChangeDirty(e => this.onUntitledDidChangeDirty(e)));
3940
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_DIRTY, (e: TextFileChangeEvent) => this.onTextFileDirty(e)));
4041
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_SAVED, (e: TextFileChangeEvent) => this.onTextFileSaved(e)));
4142
this.toUnbind.push(this.eventService.addListener2(FileEventType.FILE_SAVE_ERROR, (e: TextFileChangeEvent) => this.onTextFileSaveError(e)));
@@ -45,14 +46,10 @@ export class MacIntegration implements IWorkbenchContribution {
4546
this.lifecycleService.onShutdown(this.dispose, this);
4647
}
4748

48-
private onUntitledDirtyEvent(): void {
49-
if (!this.isDocumentedEdited) {
50-
this.updateDocumentEdited();
51-
}
52-
}
49+
private onUntitledDidChangeDirty(e: UntitledEditorEvent): void {
50+
const gotDirty = this.untitledEditorService.isDirty(e.resource);
5351

54-
private onUntitledSavedEvent(): void {
55-
if (this.isDocumentedEdited) {
52+
if ((!this.isDocumentedEdited && gotDirty) || (this.isDocumentedEdited && !gotDirty)) {
5653
this.updateDocumentEdited();
5754
}
5855
}

src/vs/workbench/parts/output/browser/outputPanel.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {SwitchOutputAction, SwitchOutputActionItem, ClearOutputAction} from 'vs/
2424
import {IWorkspaceContextService} from 'vs/platform/workspace/common/workspace';
2525
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
2626
import {IThemeService} from 'vs/workbench/services/themes/common/themeService';
27+
import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService';
2728

2829
export class OutputPanel extends StringEditor {
2930

@@ -40,10 +41,11 @@ export class OutputPanel extends StringEditor {
4041
@IEventService eventService: IEventService,
4142
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
4243
@IThemeService themeService: IThemeService,
43-
@IOutputService private outputService: IOutputService
44+
@IOutputService private outputService: IOutputService,
45+
@IUntitledEditorService untitledEditorService: IUntitledEditorService
4446
) {
4547
super(telemetryService, instantiationService, contextService, storageService,
46-
messageService, configurationService, eventService, editorService, themeService);
48+
messageService, configurationService, eventService, editorService, themeService, untitledEditorService);
4749
this.toDispose = [];
4850
}
4951

0 commit comments

Comments
 (0)