Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/common/editor/textResourceEditorInput.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { DEFAULT_EDITOR_ASSOCIATION, GroupIdentifier, IRevertOptions, isResourceEditorInput, IUntypedEditorInput } from '../editor.js';
7
import { EditorInput } from './editorInput.js';
8
import { AbstractResourceEditorInput } from './resourceEditorInput.js';
9
import { URI } from '../../../base/common/uri.js';
10
import { ITextFileService, ITextFileSaveOptions, ILanguageSupport } from '../../services/textfile/common/textfiles.js';
11
import { IEditorService } from '../../services/editor/common/editorService.js';
12
import { IFileService } from '../../../platform/files/common/files.js';
13
import { ILabelService } from '../../../platform/label/common/label.js';
14
import { Schemas } from '../../../base/common/network.js';
15
import { isEqual } from '../../../base/common/resources.js';
16
import { ITextEditorModel, ITextModelService } from '../../../editor/common/services/resolverService.js';
17
import { TextResourceEditorModel } from './textResourceEditorModel.js';
18
import { IReference } from '../../../base/common/lifecycle.js';
19
import { createTextBufferFactory } from '../../../editor/common/model/textModel.js';
20
import { IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js';
21
import { ITextResourceConfigurationService } from '../../../editor/common/services/textResourceConfiguration.js';
22
import { ICustomEditorLabelService } from '../../services/editor/common/customEditorLabelService.js';
23
24
/**
25
* The base class for all editor inputs that open in text editors.
26
*/
27
export abstract class AbstractTextResourceEditorInput extends AbstractResourceEditorInput {
28
29
constructor(
30
resource: URI,
31
preferredResource: URI | undefined,
32
@IEditorService protected readonly editorService: IEditorService,
33
@ITextFileService protected readonly textFileService: ITextFileService,
34
@ILabelService labelService: ILabelService,
35
@IFileService fileService: IFileService,
36
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService,
37
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
38
@ICustomEditorLabelService customEditorLabelService: ICustomEditorLabelService
39
) {
40
super(resource, preferredResource, labelService, fileService, filesConfigurationService, textResourceConfigurationService, customEditorLabelService);
41
}
42
43
override save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<IUntypedEditorInput | undefined> {
44
45
// If this is neither an `untitled` resource, nor a resource
46
// we can handle with the file service, we can only "Save As..."
47
if (this.resource.scheme !== Schemas.untitled && !this.fileService.hasProvider(this.resource)) {
48
return this.saveAs(group, options);
49
}
50
51
// Normal save
52
return this.doSave(options, false, group);
53
}
54
55
override saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise<IUntypedEditorInput | undefined> {
56
return this.doSave(options, true, group);
57
}
58
59
private async doSave(options: ITextFileSaveOptions | undefined, saveAs: boolean, group: GroupIdentifier | undefined): Promise<IUntypedEditorInput | undefined> {
60
61
// Save / Save As
62
let target: URI | undefined;
63
if (saveAs) {
64
target = await this.textFileService.saveAs(this.resource, undefined, { ...options, suggestedTarget: this.preferredResource });
65
} else {
66
target = await this.textFileService.save(this.resource, options);
67
}
68
69
if (!target) {
70
return undefined; // save cancelled
71
}
72
73
return { resource: target };
74
}
75
76
override async revert(group: GroupIdentifier, options?: IRevertOptions): Promise<void> {
77
await this.textFileService.revert(this.resource, options);
78
}
79
}
80
81
/**
82
* A read-only text editor input whos contents are made of the provided resource that points to an existing
83
* code editor model.
84
*/
85
export class TextResourceEditorInput extends AbstractTextResourceEditorInput implements ILanguageSupport {
86
87
static readonly ID: string = 'workbench.editors.resourceEditorInput';
88
89
override get typeId(): string {
90
return TextResourceEditorInput.ID;
91
}
92
93
override get editorId(): string | undefined {
94
return DEFAULT_EDITOR_ASSOCIATION.id;
95
}
96
97
private cachedModel: TextResourceEditorModel | undefined = undefined;
98
private modelReference: Promise<IReference<ITextEditorModel>> | undefined = undefined;
99
100
constructor(
101
resource: URI,
102
private name: string | undefined,
103
private description: string | undefined,
104
private preferredLanguageId: string | undefined,
105
private preferredContents: string | undefined,
106
@ITextModelService private readonly textModelService: ITextModelService,
107
@ITextFileService textFileService: ITextFileService,
108
@IEditorService editorService: IEditorService,
109
@IFileService fileService: IFileService,
110
@ILabelService labelService: ILabelService,
111
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService,
112
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService,
113
@ICustomEditorLabelService customEditorLabelService: ICustomEditorLabelService
114
) {
115
super(resource, undefined, editorService, textFileService, labelService, fileService, filesConfigurationService, textResourceConfigurationService, customEditorLabelService);
116
}
117
118
override getName(): string {
119
return this.name || super.getName();
120
}
121
122
setName(name: string): void {
123
if (this.name !== name) {
124
this.name = name;
125
126
this._onDidChangeLabel.fire();
127
}
128
}
129
130
override getDescription(): string | undefined {
131
return this.description;
132
}
133
134
setDescription(description: string): void {
135
if (this.description !== description) {
136
this.description = description;
137
138
this._onDidChangeLabel.fire();
139
}
140
}
141
142
setLanguageId(languageId: string, source?: string): void {
143
this.setPreferredLanguageId(languageId);
144
145
this.cachedModel?.setLanguageId(languageId, source);
146
}
147
148
setPreferredLanguageId(languageId: string): void {
149
this.preferredLanguageId = languageId;
150
}
151
152
setPreferredContents(contents: string): void {
153
this.preferredContents = contents;
154
}
155
156
override async resolve(): Promise<ITextEditorModel> {
157
158
// Unset preferred contents and language after resolving
159
// once to prevent these properties to stick. We still
160
// want the user to change the language in the editor
161
// and want to show updated contents (if any) in future
162
// `resolve` calls.
163
const preferredContents = this.preferredContents;
164
const preferredLanguageId = this.preferredLanguageId;
165
this.preferredContents = undefined;
166
this.preferredLanguageId = undefined;
167
168
if (!this.modelReference) {
169
this.modelReference = this.textModelService.createModelReference(this.resource);
170
}
171
172
const ref = await this.modelReference;
173
174
// Ensure the resolved model is of expected type
175
const model = ref.object;
176
if (!(model instanceof TextResourceEditorModel)) {
177
ref.dispose();
178
this.modelReference = undefined;
179
180
throw new Error(`Unexpected model for TextResourceEditorInput: ${this.resource}`);
181
}
182
183
this.cachedModel = model;
184
185
// Set contents and language if preferred
186
if (typeof preferredContents === 'string' || typeof preferredLanguageId === 'string') {
187
model.updateTextEditorModel(typeof preferredContents === 'string' ? createTextBufferFactory(preferredContents) : undefined, preferredLanguageId);
188
}
189
190
return model;
191
}
192
193
override matches(otherInput: EditorInput | IUntypedEditorInput): boolean {
194
if (this === otherInput) {
195
return true;
196
}
197
198
if (otherInput instanceof TextResourceEditorInput) {
199
return isEqual(otherInput.resource, this.resource);
200
}
201
202
if (isResourceEditorInput(otherInput)) {
203
return super.matches(otherInput);
204
}
205
206
return false;
207
}
208
209
override dispose(): void {
210
if (this.modelReference) {
211
this.modelReference.then(ref => ref.dispose());
212
this.modelReference = undefined;
213
}
214
215
this.cachedModel = undefined;
216
217
super.dispose();
218
}
219
}
220
221