Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts
5263 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 { URI } from '../../../../base/common/uri.js';
7
import { RunOnceScheduler } from '../../../../base/common/async.js';
8
import { IModelService } from '../../../../editor/common/services/model.js';
9
import { ILink } from '../../../../editor/common/languages.js';
10
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
11
import { OUTPUT_MODE_ID, LOG_MODE_ID } from '../../../services/output/common/output.js';
12
import { OutputLinkComputer } from '../common/outputLinkComputer.js';
13
import { IDisposable, dispose, Disposable } from '../../../../base/common/lifecycle.js';
14
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js';
15
import { WebWorkerDescriptor } from '../../../../platform/webWorker/browser/webWorkerDescriptor.js';
16
import { IWebWorkerService } from '../../../../platform/webWorker/browser/webWorkerService.js';
17
import { IWebWorkerClient } from '../../../../base/common/worker/webWorker.js';
18
import { WorkerTextModelSyncClient } from '../../../../editor/common/services/textModelSync/textModelSync.impl.js';
19
import { FileAccess } from '../../../../base/common/network.js';
20
21
export class OutputLinkProvider extends Disposable {
22
23
private static readonly DISPOSE_WORKER_TIME = 3 * 60 * 1000; // dispose worker after 3 minutes of inactivity
24
25
private worker?: OutputLinkWorkerClient;
26
private disposeWorkerScheduler: RunOnceScheduler;
27
private linkProviderRegistration: IDisposable | undefined;
28
29
constructor(
30
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
31
@IModelService private readonly modelService: IModelService,
32
@ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService,
33
@IWebWorkerService private readonly webWorkerService: IWebWorkerService,
34
) {
35
super();
36
37
this.disposeWorkerScheduler = this._register(new RunOnceScheduler(() => this.disposeWorker(), OutputLinkProvider.DISPOSE_WORKER_TIME));
38
39
this.registerListeners();
40
this.updateLinkProviderWorker();
41
}
42
43
private registerListeners(): void {
44
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.updateLinkProviderWorker()));
45
}
46
47
private updateLinkProviderWorker(): void {
48
49
// Setup link provider depending on folders being opened or not
50
const folders = this.contextService.getWorkspace().folders;
51
if (folders.length > 0) {
52
if (!this.linkProviderRegistration) {
53
this.linkProviderRegistration = this.languageFeaturesService.linkProvider.register([{ language: OUTPUT_MODE_ID, scheme: '*' }, { language: LOG_MODE_ID, scheme: '*' }], {
54
provideLinks: async model => {
55
const links = await this.provideLinks(model.uri);
56
57
return links && { links };
58
}
59
});
60
}
61
} else {
62
dispose(this.linkProviderRegistration);
63
this.linkProviderRegistration = undefined;
64
}
65
66
// Dispose worker to recreate with folders on next provideLinks request
67
this.disposeWorker();
68
this.disposeWorkerScheduler.cancel();
69
}
70
71
private getOrCreateWorker(): OutputLinkWorkerClient {
72
this.disposeWorkerScheduler.schedule();
73
74
if (!this.worker) {
75
this.worker = new OutputLinkWorkerClient(this.contextService, this.modelService, this.webWorkerService);
76
}
77
78
return this.worker;
79
}
80
81
private async provideLinks(modelUri: URI): Promise<ILink[]> {
82
return this.getOrCreateWorker().provideLinks(modelUri);
83
}
84
85
private disposeWorker(): void {
86
if (this.worker) {
87
this.worker.dispose();
88
this.worker = undefined;
89
}
90
}
91
}
92
93
class OutputLinkWorkerClient extends Disposable {
94
private readonly _workerClient: IWebWorkerClient<OutputLinkComputer>;
95
private readonly _workerTextModelSyncClient: WorkerTextModelSyncClient;
96
private readonly _initializeBarrier: Promise<void>;
97
98
constructor(
99
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
100
@IModelService modelService: IModelService,
101
@IWebWorkerService webWorkerService: IWebWorkerService,
102
) {
103
super();
104
this._workerClient = this._register(webWorkerService.createWorkerClient<OutputLinkComputer>(
105
new WebWorkerDescriptor({
106
esmModuleLocation: FileAccess.asBrowserUri('vs/workbench/contrib/output/common/outputLinkComputerMain.js'),
107
label: 'OutputLinkDetectionWorker'
108
})
109
));
110
this._workerTextModelSyncClient = this._register(WorkerTextModelSyncClient.create(this._workerClient, modelService));
111
this._initializeBarrier = this._ensureWorkspaceFolders();
112
}
113
114
private async _ensureWorkspaceFolders(): Promise<void> {
115
await this._workerClient.proxy.$setWorkspaceFolders(this.contextService.getWorkspace().folders.map(folder => folder.uri.toString()));
116
}
117
118
public async provideLinks(modelUri: URI): Promise<ILink[]> {
119
await this._initializeBarrier;
120
this._workerTextModelSyncClient.ensureSyncedResources([modelUri]);
121
return this._workerClient.proxy.$computeLinks(modelUri.toString());
122
}
123
}
124
125