Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/common/extHostDocuments.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 { Emitter, Event } from '../../../base/common/event.js';
7
import { DisposableStore } from '../../../base/common/lifecycle.js';
8
import { URI, UriComponents } from '../../../base/common/uri.js';
9
import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from './extHost.protocol.js';
10
import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData.js';
11
import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js';
12
import * as TypeConverters from './extHostTypeConverters.js';
13
import type * as vscode from 'vscode';
14
import { assertReturnsDefined } from '../../../base/common/types.js';
15
import { deepFreeze } from '../../../base/common/objects.js';
16
import { TextDocumentChangeReason } from './extHostTypes.js';
17
import { ISerializedModelContentChangedEvent } from '../../../editor/common/textModelEvents.js';
18
19
export class ExtHostDocuments implements ExtHostDocumentsShape {
20
21
private readonly _onDidAddDocument = new Emitter<vscode.TextDocument>();
22
private readonly _onDidRemoveDocument = new Emitter<vscode.TextDocument>();
23
private readonly _onDidChangeDocument = new Emitter<Omit<vscode.TextDocumentChangeEvent, 'detailedReason'>>();
24
private readonly _onDidChangeDocumentWithReason = new Emitter<vscode.TextDocumentChangeEvent>();
25
private readonly _onDidSaveDocument = new Emitter<vscode.TextDocument>();
26
27
readonly onDidAddDocument: Event<vscode.TextDocument> = this._onDidAddDocument.event;
28
readonly onDidRemoveDocument: Event<vscode.TextDocument> = this._onDidRemoveDocument.event;
29
readonly onDidChangeDocument: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocument.event as Event<vscode.TextDocumentChangeEvent>;
30
readonly onDidChangeDocumentWithReason: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocumentWithReason.event;
31
readonly onDidSaveDocument: Event<vscode.TextDocument> = this._onDidSaveDocument.event;
32
33
private readonly _toDispose = new DisposableStore();
34
private _proxy: MainThreadDocumentsShape;
35
private _documentsAndEditors: ExtHostDocumentsAndEditors;
36
private _documentLoader = new Map<string, Promise<ExtHostDocumentData>>();
37
38
constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {
39
this._proxy = mainContext.getProxy(MainContext.MainThreadDocuments);
40
this._documentsAndEditors = documentsAndEditors;
41
42
this._documentsAndEditors.onDidRemoveDocuments(documents => {
43
for (const data of documents) {
44
this._onDidRemoveDocument.fire(data.document);
45
}
46
}, undefined, this._toDispose);
47
this._documentsAndEditors.onDidAddDocuments(documents => {
48
for (const data of documents) {
49
this._onDidAddDocument.fire(data.document);
50
}
51
}, undefined, this._toDispose);
52
}
53
54
public dispose(): void {
55
this._toDispose.dispose();
56
}
57
58
public getAllDocumentData(): ExtHostDocumentData[] {
59
return [...this._documentsAndEditors.allDocuments()];
60
}
61
62
public getDocumentData(resource: vscode.Uri): ExtHostDocumentData | undefined {
63
if (!resource) {
64
return undefined;
65
}
66
const data = this._documentsAndEditors.getDocument(resource);
67
if (data) {
68
return data;
69
}
70
return undefined;
71
}
72
73
public getDocument(resource: vscode.Uri): vscode.TextDocument {
74
const data = this.getDocumentData(resource);
75
if (!data?.document) {
76
throw new Error(`Unable to retrieve document from URI '${resource}'`);
77
}
78
return data.document;
79
}
80
81
public ensureDocumentData(uri: URI, options?: { encoding?: string }): Promise<ExtHostDocumentData> {
82
83
const cached = this._documentsAndEditors.getDocument(uri);
84
if (cached && (!options?.encoding || cached.document.encoding === options.encoding)) {
85
return Promise.resolve(cached);
86
}
87
88
let promise = this._documentLoader.get(uri.toString());
89
if (!promise) {
90
promise = this._proxy.$tryOpenDocument(uri, options).then(uriData => {
91
this._documentLoader.delete(uri.toString());
92
const canonicalUri = URI.revive(uriData);
93
return assertReturnsDefined(this._documentsAndEditors.getDocument(canonicalUri));
94
}, err => {
95
this._documentLoader.delete(uri.toString());
96
return Promise.reject(err);
97
});
98
this._documentLoader.set(uri.toString(), promise);
99
} else {
100
if (options?.encoding) {
101
promise = promise.then(data => {
102
if (data.document.encoding !== options.encoding) {
103
return this.ensureDocumentData(uri, options);
104
}
105
return data;
106
});
107
}
108
}
109
110
return promise;
111
}
112
113
public createDocumentData(options?: { language?: string; content?: string; encoding?: string }): Promise<URI> {
114
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
115
}
116
117
public $acceptModelLanguageChanged(uriComponents: UriComponents, newLanguageId: string): void {
118
const uri = URI.revive(uriComponents);
119
const data = this._documentsAndEditors.getDocument(uri);
120
if (!data) {
121
throw new Error('unknown document');
122
}
123
// Treat a language change as a remove + add
124
125
this._onDidRemoveDocument.fire(data.document);
126
data._acceptLanguageId(newLanguageId);
127
this._onDidAddDocument.fire(data.document);
128
}
129
130
public $acceptModelSaved(uriComponents: UriComponents): void {
131
const uri = URI.revive(uriComponents);
132
const data = this._documentsAndEditors.getDocument(uri);
133
if (!data) {
134
throw new Error('unknown document');
135
}
136
this.$acceptDirtyStateChanged(uriComponents, false);
137
this._onDidSaveDocument.fire(data.document);
138
}
139
140
public $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void {
141
const uri = URI.revive(uriComponents);
142
const data = this._documentsAndEditors.getDocument(uri);
143
if (!data) {
144
throw new Error('unknown document');
145
}
146
data._acceptIsDirty(isDirty);
147
this._onDidChangeDocument.fire({
148
document: data.document,
149
contentChanges: [],
150
reason: undefined,
151
});
152
this._onDidChangeDocumentWithReason.fire({
153
document: data.document,
154
contentChanges: [],
155
reason: undefined,
156
detailedReason: undefined,
157
});
158
}
159
160
public $acceptEncodingChanged(uriComponents: UriComponents, encoding: string): void {
161
const uri = URI.revive(uriComponents);
162
const data = this._documentsAndEditors.getDocument(uri);
163
if (!data) {
164
throw new Error('unknown document');
165
}
166
data._acceptEncoding(encoding);
167
this._onDidChangeDocument.fire({
168
document: data.document,
169
contentChanges: [],
170
reason: undefined,
171
});
172
this._onDidChangeDocumentWithReason.fire({
173
document: data.document,
174
contentChanges: [],
175
reason: undefined,
176
detailedReason: undefined,
177
});
178
}
179
180
public $acceptModelChanged(uriComponents: UriComponents, events: ISerializedModelContentChangedEvent, isDirty: boolean): void {
181
const uri = URI.revive(uriComponents);
182
const data = this._documentsAndEditors.getDocument(uri);
183
if (!data) {
184
throw new Error('unknown document');
185
}
186
data._acceptIsDirty(isDirty);
187
data.onEvents(events);
188
189
let reason: vscode.TextDocumentChangeReason | undefined = undefined;
190
if (events.isUndoing) {
191
reason = TextDocumentChangeReason.Undo;
192
} else if (events.isRedoing) {
193
reason = TextDocumentChangeReason.Redo;
194
}
195
196
this._onDidChangeDocument.fire(deepFreeze<Omit<vscode.TextDocumentChangeEvent, 'detailedReason'>>({
197
document: data.document,
198
contentChanges: events.changes.map((change) => {
199
return {
200
range: TypeConverters.Range.to(change.range),
201
rangeOffset: change.rangeOffset,
202
rangeLength: change.rangeLength,
203
text: change.text
204
};
205
}),
206
reason,
207
}));
208
this._onDidChangeDocumentWithReason.fire(deepFreeze<vscode.TextDocumentChangeEvent>({
209
document: data.document,
210
contentChanges: events.changes.map((change) => {
211
return {
212
range: TypeConverters.Range.to(change.range),
213
rangeOffset: change.rangeOffset,
214
rangeLength: change.rangeLength,
215
text: change.text
216
};
217
}),
218
reason,
219
detailedReason: events.detailedReason ? {
220
source: events.detailedReason.source as string,
221
metadata: events.detailedReason,
222
} : undefined,
223
}));
224
}
225
226
public setWordDefinitionFor(languageId: string, wordDefinition: RegExp | undefined): void {
227
setWordDefinitionFor(languageId, wordDefinition);
228
}
229
}
230
231