“049b60e6f88aadd26b70e21375ad2e610402c916”上不存在“git@gitcode.net:qq_62524377/unidocs-zh.git”
untitledTextEditorModel.ts 7.6 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import { IEncodingSupport, ISaveOptions, IModeSupport } from 'vs/workbench/common/editor';
7
import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel';
8
import { URI } from 'vs/base/common/uri';
9
import { IModeService } from 'vs/editor/common/services/modeService';
J
Johannes Rieken 已提交
10
import { IModelService } from 'vs/editor/common/services/modelService';
11
import { Emitter } from 'vs/base/common/event';
12
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
S
rename  
Sandeep Somavarapu 已提交
13
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
14 15
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
16
import { IResolvedTextEditorModel, ITextEditorModel } from 'vs/editor/common/services/resolverService';
17
import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
18
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
19
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
E
Erich Gamma 已提交
20

21 22 23
export interface IUntitledTextEditorModel extends ITextEditorModel, IModeSupport, IEncodingSupport, IWorkingCopy { }

export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel {
24

25 26
	private readonly _onDidChangeContent = this._register(new Emitter<void>());
	readonly onDidChangeContent = this._onDidChangeContent.event;
27

28 29 30
	private readonly _onDidChangeFirstLine = this._register(new Emitter<void>());
	readonly onDidChangeFirstLine = this._onDidChangeFirstLine.event;

31 32
	private readonly _onDidChangeDirty = this._register(new Emitter<void>());
	readonly onDidChangeDirty = this._onDidChangeDirty.event;
E
Erich Gamma 已提交
33

34 35
	private readonly _onDidChangeEncoding = this._register(new Emitter<void>());
	readonly onDidChangeEncoding = this._onDidChangeEncoding.event;
36

37
	readonly capabilities = WorkingCopyCapabilities.Untitled;
38 39 40

	private dirty = false;
	private versionId = 0;
41
	private configuredEncoding: string | undefined;
42

E
Erich Gamma 已提交
43
	constructor(
44
		private readonly preferredMode: string | undefined,
45 46
		public readonly resource: URI,
		public readonly hasAssociatedFilePath: boolean,
47 48
		private readonly initialValue: string | undefined,
		private preferredEncoding: string | undefined,
E
Erich Gamma 已提交
49 50
		@IModeService modeService: IModeService,
		@IModelService modelService: IModelService,
51
		@IBackupFileService private readonly backupFileService: IBackupFileService,
S
rename  
Sandeep Somavarapu 已提交
52
		@ITextResourceConfigurationService private readonly configurationService: ITextResourceConfigurationService,
53 54
		@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
		@ITextFileService private readonly textFileService: ITextFileService
E
Erich Gamma 已提交
55
	) {
56
		super(modelService, modeService);
E
Erich Gamma 已提交
57

58 59
		// Make known to working copy service
		this._register(this.workingCopyService.registerWorkingCopy(this));
60

D
Daniel Imms 已提交
61
		this.registerListeners();
62 63
	}

E
Erich Gamma 已提交
64 65 66
	private registerListeners(): void {

		// Config Changes
B
Benjamin Pasero 已提交
67
		this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange()));
E
Erich Gamma 已提交
68 69
	}

70
	private onConfigurationChange(): void {
71
		const configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
72 73 74 75 76 77 78 79

		if (this.configuredEncoding !== configuredEncoding) {
			this.configuredEncoding = configuredEncoding;

			if (!this.preferredEncoding) {
				this._onDidChangeEncoding.fire(); // do not fire event if we have a preferred encoding set
			}
		}
E
Erich Gamma 已提交
80 81
	}

B
Benjamin Pasero 已提交
82
	getVersionId(): number {
83 84 85
		return this.versionId;
	}

86
	getMode(): string | undefined {
E
Erich Gamma 已提交
87
		if (this.textEditorModel) {
B
Benjamin Pasero 已提交
88
			return this.textEditorModel.getModeId();
E
Erich Gamma 已提交
89 90
		}

91
		return this.preferredMode;
E
Erich Gamma 已提交
92 93
	}

94
	getEncoding(): string | undefined {
E
Erich Gamma 已提交
95 96 97
		return this.preferredEncoding || this.configuredEncoding;
	}

B
Benjamin Pasero 已提交
98
	setEncoding(encoding: string): void {
99
		const oldEncoding = this.getEncoding();
E
Erich Gamma 已提交
100 101 102 103
		this.preferredEncoding = encoding;

		// Emit if it changed
		if (oldEncoding !== this.preferredEncoding) {
104
			this._onDidChangeEncoding.fire();
E
Erich Gamma 已提交
105 106 107
		}
	}

B
Benjamin Pasero 已提交
108
	isDirty(): boolean {
E
Erich Gamma 已提交
109 110 111
		return this.dirty;
	}

112 113 114 115 116 117 118 119 120
	private setDirty(dirty: boolean): void {
		if (this.dirty === dirty) {
			return;
		}

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

121 122 123 124 125
	save(options?: ISaveOptions): Promise<boolean> {
		return this.textFileService.save(this.resource, options);
	}

	async revert(): Promise<boolean> {
126
		this.setDirty(false);
127

128
		return true;
129 130
	}

131
	async backup(): Promise<void> {
132 133
		if (this.isResolved()) {
			return this.backupFileService.backupResource(this.resource, this.createSnapshot(), this.versionId);
134 135 136
		}
	}

137 138 139 140
	hasBackup(): boolean {
		return this.backupFileService.hasBackupSync(this.resource, this.versionId);
	}

141
	async load(): Promise<UntitledTextEditorModel & IResolvedTextEditorModel> {
E
Erich Gamma 已提交
142

143
		// Check for backups first
144 145 146 147 148
		let backup: IResolvedBackup<object> | undefined = undefined;
		const backupResource = await this.backupFileService.loadBackupResource(this.resource);
		if (backupResource) {
			backup = await this.backupFileService.resolveBackupContent(backupResource);
		}
E
Erich Gamma 已提交
149

150
		// untitled associated to file path are dirty right away as well as untitled with content
151
		this.setDirty(this.hasAssociatedFilePath || !!backup || !!this.initialValue);
E
Erich Gamma 已提交
152

153 154 155 156 157 158
		let untitledContents: ITextBufferFactory;
		if (backup) {
			untitledContents = backup.value;
		} else {
			untitledContents = createTextBufferFactory(this.initialValue || '');
		}
159

160 161 162 163
		// Create text editor model if not yet done
		if (!this.textEditorModel) {
			this.createTextEditorModel(untitledContents, this.resource, this.preferredMode);
		}
164

165 166 167 168
		// Otherwise update
		else {
			this.updateTextEditorModel(untitledContents, this.preferredMode);
		}
169

170 171
		// Encoding
		this.configuredEncoding = this.configurationService.getValue<string>(this.resource, 'files.encoding');
172

173 174
		// We know for a fact there is a text editor model here
		const textEditorModel = this.textEditorModel!;
175

176
		// Listen to content changes
177
		this._register(textEditorModel.onDidChangeContent(e => this.onModelContentChanged(e)));
178

179 180
		// Listen to mode changes
		this._register(textEditorModel.onDidChangeLanguage(() => this.onConfigurationChange())); // mode change can have impact on config
181

182 183 184 185 186 187 188
		// If we have initial contents, make sure to emit this
		// as the appropiate events to the outside.
		if (backup || this.initialValue) {
			this._onDidChangeContent.fire();
			this._onDidChangeFirstLine.fire();
		}

189
		return this as UntitledTextEditorModel & IResolvedTextEditorModel;
190
	}
E
Erich Gamma 已提交
191

192
	private onModelContentChanged(e: IModelContentChangedEvent): void {
193 194 195 196
		if (!this.isResolved()) {
			return;
		}

197
		this.versionId++;
198

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

205 206 207
		// turn dirty otherwise
		else {
			this.setDirty(true);
208
		}
209

210
		// Emit as general content change event
211
		this._onDidChangeContent.fire();
212 213 214 215 216

		// Emit as first line change event depending on actual change
		if (e.changes.some(change => change.range.startLineNumber === 1 || change.range.endLineNumber === 1)) {
			this._onDidChangeFirstLine.fire();
		}
E
Erich Gamma 已提交
217
	}
B
Benjamin Pasero 已提交
218 219 220 221

	isReadonly(): boolean {
		return false;
	}
222
}