untitledEditorModel.ts 7.5 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7 8
import { IDisposable } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
B
Benjamin Pasero 已提交
9
import { IEncodingSupport } from 'vs/workbench/common/editor';
10
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
E
Erich Gamma 已提交
11
import URI from 'vs/base/common/uri';
J
Johannes Rieken 已提交
12 13
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
14
import { IFilesConfiguration, CONTENT_CHANGE_EVENT_BUFFER_DELAY } from 'vs/platform/files/common/files';
J
Johannes Rieken 已提交
15 16 17 18 19
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IMode } from 'vs/editor/common/modes';
import Event, { Emitter } from 'vs/base/common/event';
20
import { RunOnceScheduler } from 'vs/base/common/async';
21
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
22
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
E
Erich Gamma 已提交
23

24
export class UntitledEditorModel extends BaseTextEditorModel implements IEncodingSupport {
25

26
	public static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY;
27

A
Alex Dima 已提交
28
	private textModelChangeListener: IDisposable;
29
	private configurationChangeListener: IDisposable;
E
Erich Gamma 已提交
30 31

	private dirty: boolean;
32
	private _onDidChangeContent: Emitter<void>;
33
	private _onDidChangeDirty: Emitter<void>;
34
	private _onDidChangeEncoding: Emitter<void>;
35

36 37
	private versionId: number;

38 39
	private contentChangeEventScheduler: RunOnceScheduler;

E
Erich Gamma 已提交
40
	private configuredEncoding: string;
41

E
Erich Gamma 已提交
42
	constructor(
43 44
		private modeId: string,
		private resource: URI,
45 46 47
		private hasAssociatedFilePath: boolean,
		private initialValue: string,
		private preferredEncoding: string,
E
Erich Gamma 已提交
48 49
		@IModeService modeService: IModeService,
		@IModelService modelService: IModelService,
50 51
		@IBackupFileService private backupFileService: IBackupFileService,
		@ITextFileService private textFileService: ITextFileService,
52
		@IConfigurationService private configurationService: IConfigurationService
E
Erich Gamma 已提交
53
	) {
54
		super(modelService, modeService);
E
Erich Gamma 已提交
55

56
		this.dirty = false;
57
		this.versionId = 0;
E
Erich Gamma 已提交
58

59
		this._onDidChangeContent = new Emitter<void>();
60
		this._onDidChangeDirty = new Emitter<void>();
61
		this._onDidChangeEncoding = new Emitter<void>();
62

63 64
		this.contentChangeEventScheduler = new RunOnceScheduler(() => this._onDidChangeContent.fire(), UntitledEditorModel.DEFAULT_CONTENT_CHANGE_BUFFER_DELAY);

D
Daniel Imms 已提交
65
		this.registerListeners();
66 67
	}

68 69 70 71
	public get onDidChangeContent(): Event<void> {
		return this._onDidChangeContent.event;
	}

72 73 74 75
	public get onDidChangeDirty(): Event<void> {
		return this._onDidChangeDirty.event;
	}

76 77 78 79
	public get onDidChangeEncoding(): Event<void> {
		return this._onDidChangeEncoding.event;
	}

80 81 82
	protected getOrCreateMode(modeService: IModeService, modeId: string, firstLineText?: string): TPromise<IMode> {
		if (!modeId || modeId === PLAINTEXT_MODE_ID) {
			return modeService.getOrCreateModeByFilenameOrFirstLine(this.resource.fsPath, firstLineText); // lookup mode via resource path if the provided modeId is unspecific
83 84
		}

85
		return super.getOrCreateMode(modeService, modeId, firstLineText);
86 87
	}

E
Erich Gamma 已提交
88 89 90
	private registerListeners(): void {

		// Config Changes
91
		this.configurationChangeListener = this.configurationService.onDidUpdateConfiguration(e => this.onConfigurationChange(this.configurationService.getConfiguration<IFilesConfiguration>()));
E
Erich Gamma 已提交
92 93 94 95 96 97
	}

	private onConfigurationChange(configuration: IFilesConfiguration): void {
		this.configuredEncoding = configuration && configuration.files && configuration.files.encoding;
	}

98 99 100 101
	public getVersionId(): number {
		return this.versionId;
	}

E
Erich Gamma 已提交
102 103 104 105 106 107 108 109 110 111
	public getValue(): string {
		if (this.textEditorModel) {
			return this.textEditorModel.getValue(EndOfLinePreference.TextDefined, true /* Preserve BOM */);
		}

		return null;
	}

	public getModeId(): string {
		if (this.textEditorModel) {
A
Alex Dima 已提交
112
			return this.textEditorModel.getLanguageIdentifier().language;
E
Erich Gamma 已提交
113 114 115 116 117 118 119 120 121 122
		}

		return null;
	}

	public getEncoding(): string {
		return this.preferredEncoding || this.configuredEncoding;
	}

	public setEncoding(encoding: string): void {
123
		const oldEncoding = this.getEncoding();
E
Erich Gamma 已提交
124 125 126 127
		this.preferredEncoding = encoding;

		// Emit if it changed
		if (oldEncoding !== this.preferredEncoding) {
128
			this._onDidChangeEncoding.fire();
E
Erich Gamma 已提交
129 130 131 132 133 134 135
		}
	}

	public isDirty(): boolean {
		return this.dirty;
	}

136 137 138 139 140 141 142 143 144
	private setDirty(dirty: boolean): void {
		if (this.dirty === dirty) {
			return;
		}

		this.dirty = dirty;
		this._onDidChangeDirty.fire();
	}

D
Daniel Imms 已提交
145 146 147 148
	public getResource(): URI {
		return this.resource;
	}

149
	public revert(): void {
150
		this.setDirty(false);
151 152 153

		// Handle content change event buffered
		this.contentChangeEventScheduler.schedule();
154 155
	}

B
Benjamin Pasero 已提交
156
	public load(): TPromise<UntitledEditorModel> {
E
Erich Gamma 已提交
157

158
		// Check for backups first
B
Benjamin Pasero 已提交
159 160
		return this.backupFileService.loadBackupResource(this.resource).then(backupResource => {
			if (backupResource) {
161
				return this.textFileService.resolveTextContent(backupResource, BACKUP_FILE_RESOLVE_OPTIONS).then(rawTextContent => {
162
					return this.backupFileService.parseBackupContent(rawTextContent.value);
163
				});
164 165 166 167
			}

			return null;
		}).then(backupContent => {
E
Erich Gamma 已提交
168

169 170
			// untitled associated to file path are dirty right away as well as untitled with content
			this.setDirty(this.hasAssociatedFilePath || !!backupContent);
E
Erich Gamma 已提交
171

172
			return this.doLoad(backupContent || this.initialValue || '').then(model => {
173
				const configuration = this.configurationService.getConfiguration<IFilesConfiguration>();
E
Erich Gamma 已提交
174

175 176
				// Encoding
				this.configuredEncoding = configuration && configuration.files && configuration.files.encoding;
E
Erich Gamma 已提交
177

178
				// Listen to content changes
A
Alex Dima 已提交
179
				this.textModelChangeListener = this.textEditorModel.onDidChangeContent(() => this.onModelContentChanged());
180 181 182

				return model;
			});
E
Erich Gamma 已提交
183 184
		});
	}
185

B
Benjamin Pasero 已提交
186
	private doLoad(content: string): TPromise<UntitledEditorModel> {
187 188 189

		// Create text editor model if not yet done
		if (!this.textEditorModel) {
B
Benjamin Pasero 已提交
190
			return this.createTextEditorModel(content, this.resource, this.modeId).then(model => this);
191 192 193 194 195 196 197
		}

		// Otherwise update
		else {
			this.updateTextEditorModel(content);
		}

B
Benjamin Pasero 已提交
198
		return TPromise.as<UntitledEditorModel>(this);
199
	}
E
Erich Gamma 已提交
200

B
Benjamin Pasero 已提交
201
	private onModelContentChanged(): void {
202
		this.versionId++;
203 204

		// mark the untitled editor as non-dirty once its content becomes empty and we do
B
Benjamin Pasero 已提交
205 206
		// not have an associated path set. we never want dirty indicator in that case.
		if (!this.hasAssociatedFilePath && this.textEditorModel.getLineCount() === 1 && this.textEditorModel.getLineContent(1) === '') {
207
			this.setDirty(false);
E
Erich Gamma 已提交
208
		}
209

210 211 212
		// turn dirty otherwise
		else {
			this.setDirty(true);
213
		}
214

215 216
		// Handle content change event buffered
		this.contentChangeEventScheduler.schedule();
E
Erich Gamma 已提交
217 218 219 220 221 222
	}

	public dispose(): void {
		super.dispose();

		if (this.textModelChangeListener) {
A
Alex Dima 已提交
223
			this.textModelChangeListener.dispose();
E
Erich Gamma 已提交
224 225 226
			this.textModelChangeListener = null;
		}

227 228 229
		if (this.configurationChangeListener) {
			this.configurationChangeListener.dispose();
			this.configurationChangeListener = null;
E
Erich Gamma 已提交
230
		}
231

232 233
		this.contentChangeEventScheduler.dispose();

234
		this._onDidChangeContent.dispose();
235 236
		this._onDidChangeDirty.dispose();
		this._onDidChangeEncoding.dispose();
E
Erich Gamma 已提交
237 238
	}
}