Path: blob/main/src/vs/workbench/api/common/extHostDocuments.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter, Event } from '../../../base/common/event.js';6import { DisposableStore } from '../../../base/common/lifecycle.js';7import { URI, UriComponents } from '../../../base/common/uri.js';8import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from './extHost.protocol.js';9import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData.js';10import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js';11import * as TypeConverters from './extHostTypeConverters.js';12import type * as vscode from 'vscode';13import { assertReturnsDefined } from '../../../base/common/types.js';14import { deepFreeze } from '../../../base/common/objects.js';15import { TextDocumentChangeReason } from './extHostTypes.js';16import { ISerializedModelContentChangedEvent } from '../../../editor/common/textModelEvents.js';1718export class ExtHostDocuments implements ExtHostDocumentsShape {1920private readonly _onDidAddDocument = new Emitter<vscode.TextDocument>();21private readonly _onDidRemoveDocument = new Emitter<vscode.TextDocument>();22private readonly _onDidChangeDocument = new Emitter<Omit<vscode.TextDocumentChangeEvent, 'detailedReason'>>();23private readonly _onDidChangeDocumentWithReason = new Emitter<vscode.TextDocumentChangeEvent>();24private readonly _onDidSaveDocument = new Emitter<vscode.TextDocument>();2526readonly onDidAddDocument: Event<vscode.TextDocument> = this._onDidAddDocument.event;27readonly onDidRemoveDocument: Event<vscode.TextDocument> = this._onDidRemoveDocument.event;28readonly onDidChangeDocument: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocument.event as Event<vscode.TextDocumentChangeEvent>;29readonly onDidChangeDocumentWithReason: Event<vscode.TextDocumentChangeEvent> = this._onDidChangeDocumentWithReason.event;30readonly onDidSaveDocument: Event<vscode.TextDocument> = this._onDidSaveDocument.event;3132private readonly _toDispose = new DisposableStore();33private _proxy: MainThreadDocumentsShape;34private _documentsAndEditors: ExtHostDocumentsAndEditors;35private _documentLoader = new Map<string, Promise<ExtHostDocumentData>>();3637constructor(mainContext: IMainContext, documentsAndEditors: ExtHostDocumentsAndEditors) {38this._proxy = mainContext.getProxy(MainContext.MainThreadDocuments);39this._documentsAndEditors = documentsAndEditors;4041this._documentsAndEditors.onDidRemoveDocuments(documents => {42for (const data of documents) {43this._onDidRemoveDocument.fire(data.document);44}45}, undefined, this._toDispose);46this._documentsAndEditors.onDidAddDocuments(documents => {47for (const data of documents) {48this._onDidAddDocument.fire(data.document);49}50}, undefined, this._toDispose);51}5253public dispose(): void {54this._toDispose.dispose();55}5657public getAllDocumentData(): ExtHostDocumentData[] {58return [...this._documentsAndEditors.allDocuments()];59}6061public getDocumentData(resource: vscode.Uri): ExtHostDocumentData | undefined {62if (!resource) {63return undefined;64}65const data = this._documentsAndEditors.getDocument(resource);66if (data) {67return data;68}69return undefined;70}7172public getDocument(resource: vscode.Uri): vscode.TextDocument {73const data = this.getDocumentData(resource);74if (!data?.document) {75throw new Error(`Unable to retrieve document from URI '${resource}'`);76}77return data.document;78}7980public ensureDocumentData(uri: URI, options?: { encoding?: string }): Promise<ExtHostDocumentData> {8182const cached = this._documentsAndEditors.getDocument(uri);83if (cached && (!options?.encoding || cached.document.encoding === options.encoding)) {84return Promise.resolve(cached);85}8687let promise = this._documentLoader.get(uri.toString());88if (!promise) {89promise = this._proxy.$tryOpenDocument(uri, options).then(uriData => {90this._documentLoader.delete(uri.toString());91const canonicalUri = URI.revive(uriData);92return assertReturnsDefined(this._documentsAndEditors.getDocument(canonicalUri));93}, err => {94this._documentLoader.delete(uri.toString());95return Promise.reject(err);96});97this._documentLoader.set(uri.toString(), promise);98} else {99if (options?.encoding) {100promise = promise.then(data => {101if (data.document.encoding !== options.encoding) {102return this.ensureDocumentData(uri, options);103}104return data;105});106}107}108109return promise;110}111112public createDocumentData(options?: { language?: string; content?: string; encoding?: string }): Promise<URI> {113return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));114}115116public $acceptModelLanguageChanged(uriComponents: UriComponents, newLanguageId: string): void {117const uri = URI.revive(uriComponents);118const data = this._documentsAndEditors.getDocument(uri);119if (!data) {120throw new Error('unknown document');121}122// Treat a language change as a remove + add123124this._onDidRemoveDocument.fire(data.document);125data._acceptLanguageId(newLanguageId);126this._onDidAddDocument.fire(data.document);127}128129public $acceptModelSaved(uriComponents: UriComponents): void {130const uri = URI.revive(uriComponents);131const data = this._documentsAndEditors.getDocument(uri);132if (!data) {133throw new Error('unknown document');134}135this.$acceptDirtyStateChanged(uriComponents, false);136this._onDidSaveDocument.fire(data.document);137}138139public $acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void {140const uri = URI.revive(uriComponents);141const data = this._documentsAndEditors.getDocument(uri);142if (!data) {143throw new Error('unknown document');144}145data._acceptIsDirty(isDirty);146this._onDidChangeDocument.fire({147document: data.document,148contentChanges: [],149reason: undefined,150});151this._onDidChangeDocumentWithReason.fire({152document: data.document,153contentChanges: [],154reason: undefined,155detailedReason: undefined,156});157}158159public $acceptEncodingChanged(uriComponents: UriComponents, encoding: string): void {160const uri = URI.revive(uriComponents);161const data = this._documentsAndEditors.getDocument(uri);162if (!data) {163throw new Error('unknown document');164}165data._acceptEncoding(encoding);166this._onDidChangeDocument.fire({167document: data.document,168contentChanges: [],169reason: undefined,170});171this._onDidChangeDocumentWithReason.fire({172document: data.document,173contentChanges: [],174reason: undefined,175detailedReason: undefined,176});177}178179public $acceptModelChanged(uriComponents: UriComponents, events: ISerializedModelContentChangedEvent, isDirty: boolean): void {180const uri = URI.revive(uriComponents);181const data = this._documentsAndEditors.getDocument(uri);182if (!data) {183throw new Error('unknown document');184}185data._acceptIsDirty(isDirty);186data.onEvents(events);187188let reason: vscode.TextDocumentChangeReason | undefined = undefined;189if (events.isUndoing) {190reason = TextDocumentChangeReason.Undo;191} else if (events.isRedoing) {192reason = TextDocumentChangeReason.Redo;193}194195this._onDidChangeDocument.fire(deepFreeze<Omit<vscode.TextDocumentChangeEvent, 'detailedReason'>>({196document: data.document,197contentChanges: events.changes.map((change) => {198return {199range: TypeConverters.Range.to(change.range),200rangeOffset: change.rangeOffset,201rangeLength: change.rangeLength,202text: change.text203};204}),205reason,206}));207this._onDidChangeDocumentWithReason.fire(deepFreeze<vscode.TextDocumentChangeEvent>({208document: data.document,209contentChanges: events.changes.map((change) => {210return {211range: TypeConverters.Range.to(change.range),212rangeOffset: change.rangeOffset,213rangeLength: change.rangeLength,214text: change.text215};216}),217reason,218detailedReason: events.detailedReason ? {219source: events.detailedReason.source as string,220metadata: events.detailedReason,221} : undefined,222}));223}224225public setWordDefinitionFor(languageId: string, wordDefinition: RegExp | undefined): void {226setWordDefinitionFor(languageId, wordDefinition);227}228}229230231