@theia/plugin-ext 1.73.0-next.9 → 1.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/common/lm-tool-protocol.d.ts +58 -0
- package/lib/common/lm-tool-protocol.d.ts.map +1 -0
- package/lib/common/lm-tool-protocol.js +38 -0
- package/lib/common/lm-tool-protocol.js.map +1 -0
- package/lib/common/lm-tool-protocol.spec.d.ts +2 -0
- package/lib/common/lm-tool-protocol.spec.d.ts.map +1 -0
- package/lib/common/lm-tool-protocol.spec.js +80 -0
- package/lib/common/lm-tool-protocol.spec.js.map +1 -0
- package/lib/common/plugin-api-rpc-model.d.ts +4 -0
- package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc-model.js.map +1 -1
- package/lib/common/plugin-api-rpc.d.ts +6 -1
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +4 -2
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/common/plugin-protocol.d.ts +8 -0
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts +2 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js +6 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
- package/lib/hosted/node/plugin-deployer-handler-impl.js +4 -3
- package/lib/hosted/node/plugin-deployer-handler-impl.js.map +1 -1
- package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
- package/lib/hosted/node/plugin-host-module.js +2 -0
- package/lib/hosted/node/plugin-host-module.js.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -0
- package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.js +55 -49
- package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
- package/lib/main/browser/comments/comment-thread-widget.d.ts +3 -3
- package/lib/main/browser/comments/comment-thread-widget.d.ts.map +1 -1
- package/lib/main/browser/comments/comment-thread-widget.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-opener.js +1 -0
- package/lib/main/browser/custom-editors/custom-editor-opener.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts +1 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editor-widget.js +3 -0
- package/lib/main/browser/custom-editors/custom-editor-widget.js.map +1 -1
- package/lib/main/browser/custom-editors/custom-editors-main.d.ts.map +1 -1
- package/lib/main/browser/custom-editors/custom-editors-main.js +2 -0
- package/lib/main/browser/custom-editors/custom-editors-main.js.map +1 -1
- package/lib/main/browser/debug/debug-main.d.ts +1 -11
- package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
- package/lib/main/browser/debug/debug-main.js +3 -24
- package/lib/main/browser/debug/debug-main.js.map +1 -1
- package/lib/main/browser/debug/plugin-debug-service.d.ts +2 -1
- package/lib/main/browser/debug/plugin-debug-service.d.ts.map +1 -1
- package/lib/main/browser/debug/plugin-debug-service.js +8 -3
- package/lib/main/browser/debug/plugin-debug-service.js.map +1 -1
- package/lib/main/browser/debug/plugin-debug-session-factory.d.ts +7 -44
- package/lib/main/browser/debug/plugin-debug-session-factory.d.ts.map +1 -1
- package/lib/main/browser/debug/plugin-debug-session-factory.js +29 -35
- package/lib/main/browser/debug/plugin-debug-session-factory.js.map +1 -1
- package/lib/main/browser/languages-main.d.ts +6 -1
- package/lib/main/browser/languages-main.d.ts.map +1 -1
- package/lib/main/browser/languages-main.js +39 -2
- package/lib/main/browser/languages-main.js.map +1 -1
- package/lib/main/browser/lm-main.js +2 -2
- package/lib/main/browser/lm-main.js.map +1 -1
- package/lib/main/browser/lm-tool-main.d.ts +14 -0
- package/lib/main/browser/lm-tool-main.d.ts.map +1 -0
- package/lib/main/browser/lm-tool-main.js +101 -0
- package/lib/main/browser/lm-tool-main.js.map +1 -0
- package/lib/main/browser/lm-tool-main.spec.d.ts +2 -0
- package/lib/main/browser/lm-tool-main.spec.d.ts.map +1 -0
- package/lib/main/browser/lm-tool-main.spec.js +100 -0
- package/lib/main/browser/lm-tool-main.spec.js.map +1 -0
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +3 -0
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/main-file-system-event-service.d.ts +21 -4
- package/lib/main/browser/main-file-system-event-service.d.ts.map +1 -1
- package/lib/main/browser/main-file-system-event-service.js +47 -17
- package/lib/main/browser/main-file-system-event-service.js.map +1 -1
- package/lib/main/browser/main-file-system-event-service.spec.js +61 -26
- package/lib/main/browser/main-file-system-event-service.spec.js.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts +2 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js +11 -5
- package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
- package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.js +10 -4
- package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.d.ts +1 -0
- package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.js +28 -3
- package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.spec.js +40 -0
- package/lib/main/browser/view/plugin-view-registry.spec.js.map +1 -1
- package/lib/main/browser/webview/webview-resource-cache.d.ts +2 -0
- package/lib/main/browser/webview/webview-resource-cache.d.ts.map +1 -1
- package/lib/main/browser/webview/webview-resource-cache.js +7 -1
- package/lib/main/browser/webview/webview-resource-cache.js.map +1 -1
- package/lib/main/browser/webview/webview.d.ts +2 -1
- package/lib/main/browser/webview/webview.d.ts.map +1 -1
- package/lib/main/browser/webview/webview.js +7 -1
- package/lib/main/browser/webview/webview.js.map +1 -1
- package/lib/main/node/plugin-deployer-impl.js +2 -1
- package/lib/main/node/plugin-deployer-impl.js.map +1 -1
- package/lib/plugin/languages/diagnostics.d.ts +4 -1
- package/lib/plugin/languages/diagnostics.d.ts.map +1 -1
- package/lib/plugin/languages/diagnostics.js +22 -9
- package/lib/plugin/languages/diagnostics.js.map +1 -1
- package/lib/plugin/languages/multi-document-highlight.d.ts +14 -0
- package/lib/plugin/languages/multi-document-highlight.d.ts.map +1 -0
- package/lib/plugin/languages/multi-document-highlight.js +60 -0
- package/lib/plugin/languages/multi-document-highlight.js.map +1 -0
- package/lib/plugin/languages.d.ts +3 -1
- package/lib/plugin/languages.d.ts.map +1 -1
- package/lib/plugin/languages.js +11 -0
- package/lib/plugin/languages.js.map +1 -1
- package/lib/plugin/lm-tool-ext.d.ts +28 -0
- package/lib/plugin/lm-tool-ext.d.ts.map +1 -0
- package/lib/plugin/lm-tool-ext.js +171 -0
- package/lib/plugin/lm-tool-ext.js.map +1 -0
- package/lib/plugin/lm-tool-ext.spec.d.ts +2 -0
- package/lib/plugin/lm-tool-ext.spec.d.ts.map +1 -0
- package/lib/plugin/lm-tool-ext.spec.js +124 -0
- package/lib/plugin/lm-tool-ext.spec.js.map +1 -0
- package/lib/plugin/plugin-context.d.ts.map +1 -1
- package/lib/plugin/plugin-context.js +10 -11
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/types-impl.d.ts +0 -9
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/types-impl.js +22 -15
- package/lib/plugin/types-impl.js.map +1 -1
- package/package.json +32 -31
- package/src/common/lm-tool-protocol.spec.ts +89 -0
- package/src/common/lm-tool-protocol.ts +102 -0
- package/src/common/plugin-api-rpc-model.ts +5 -0
- package/src/common/plugin-api-rpc.ts +9 -2
- package/src/common/plugin-protocol.ts +9 -0
- package/src/hosted/node/hosted-plugin-localization-service.ts +6 -3
- package/src/hosted/node/plugin-deployer-handler-impl.ts +5 -5
- package/src/hosted/node/plugin-host-module.ts +2 -0
- package/src/hosted/node/scanners/scanner-theia.ts +54 -50
- package/src/main/browser/comments/comment-thread-widget.tsx +5 -5
- package/src/main/browser/custom-editors/custom-editor-opener.tsx +1 -0
- package/src/main/browser/custom-editors/custom-editor-widget.ts +4 -0
- package/src/main/browser/custom-editors/custom-editors-main.ts +2 -0
- package/src/main/browser/debug/debug-main.ts +3 -45
- package/src/main/browser/debug/plugin-debug-service.ts +7 -5
- package/src/main/browser/debug/plugin-debug-session-factory.ts +25 -71
- package/src/main/browser/languages-main.ts +49 -4
- package/src/main/browser/lm-main.ts +2 -2
- package/src/main/browser/lm-tool-main.spec.ts +126 -0
- package/src/main/browser/lm-tool-main.ts +116 -0
- package/src/main/browser/main-context.ts +4 -0
- package/src/main/browser/main-file-system-event-service.spec.ts +76 -29
- package/src/main/browser/main-file-system-event-service.ts +46 -16
- package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +11 -7
- package/src/main/browser/plugin-contribution-handler.ts +8 -4
- package/src/main/browser/plugin-ext-widget.tsx +1 -1
- package/src/main/browser/view/plugin-view-registry.spec.ts +69 -1
- package/src/main/browser/view/plugin-view-registry.ts +30 -3
- package/src/main/browser/webview/webview-resource-cache.ts +6 -2
- package/src/main/browser/webview/webview.ts +6 -3
- package/src/main/node/plugin-deployer-impl.ts +2 -2
- package/src/plugin/languages/diagnostics.ts +28 -10
- package/src/plugin/languages/multi-document-highlight.ts +75 -0
- package/src/plugin/languages.ts +20 -0
- package/src/plugin/lm-tool-ext.spec.ts +152 -0
- package/src/plugin/lm-tool-ext.ts +197 -0
- package/src/plugin/plugin-context.ts +11 -11
- package/src/plugin/types-impl.ts +22 -15
|
@@ -27,18 +27,20 @@ import { URI } from '@theia/core';
|
|
|
27
27
|
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
28
28
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
29
29
|
import { FileChangeType, WatchOptions } from '@theia/filesystem/lib/common/files';
|
|
30
|
-
import {
|
|
30
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
31
31
|
|
|
32
32
|
export class MainFileSystemEventService implements MainFileSystemEventServiceShape {
|
|
33
33
|
|
|
34
34
|
private readonly toDispose = new DisposableCollection();
|
|
35
35
|
private readonly watches = new Map<number, Disposable>();
|
|
36
|
+
/** Ancestor-of-workspace roots already skipped, to avoid logging on every re-registration. */
|
|
37
|
+
private readonly skippedWatchRoots = new Set<string>();
|
|
36
38
|
|
|
37
39
|
constructor(
|
|
38
40
|
rpc: RPCProtocol,
|
|
39
41
|
container: interfaces.Container,
|
|
40
42
|
private readonly fileService = container.get(FileService),
|
|
41
|
-
private readonly
|
|
43
|
+
private readonly workspaceService = container.get(WorkspaceService)
|
|
42
44
|
) {
|
|
43
45
|
const proxy = rpc.getProxy(MAIN_RPC_CONTEXT.ExtHostFileSystemEventService);
|
|
44
46
|
|
|
@@ -84,26 +86,54 @@ export class MainFileSystemEventService implements MainFileSystemEventServiceSha
|
|
|
84
86
|
throw new Error(`There is already a watch request for the key ${session}`);
|
|
85
87
|
}
|
|
86
88
|
const uri = URI.fromComponents(resource);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
if (this.shouldSkipWatch(uri, options)) {
|
|
90
|
+
// Register a no-op disposable so the session is tracked and `$unwatch` still works.
|
|
91
|
+
this.watches.set(session, Disposable.NULL);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Plugin/language-server watchers (`vscode.workspace.createFileSystemWatcher`) arrive here
|
|
95
|
+
// with an empty `excludes` list; `FileService.watch` applies `files.watcherExclude` centrally
|
|
96
|
+
// for all watchers, so they stay bounded without merging the excludes here.
|
|
97
|
+
const watch = this.fileService.watch(uri, options);
|
|
92
98
|
this.toDispose.push(watch);
|
|
93
99
|
this.watches.set(session, watch);
|
|
94
100
|
}
|
|
95
101
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Whether a plugin-requested watch should not be registered at all.
|
|
104
|
+
*
|
|
105
|
+
* Theia's backend ignores the `recursive` flag and always watches recursively. A NON-recursive
|
|
106
|
+
* watch rooted at a strict ancestor of a workspace root - e.g. a language server (such as
|
|
107
|
+
* `redhat.java` / JDT-LS) watching the PARENT of the workspace folder via
|
|
108
|
+
* `RelativePattern(parentDir, folderName)` purely to detect deletion of the folder itself -
|
|
109
|
+
* would therefore be turned into a recursive crawl of every sibling subtree under that parent,
|
|
110
|
+
* i.e. thousands of inodes the workspace does not own, which can exhaust the OS file-watch
|
|
111
|
+
* budget. `files.watcherExclude` cannot bound it because the root is outside the workspace, so
|
|
112
|
+
* the only effective mitigation is to not register the watch.
|
|
113
|
+
*
|
|
114
|
+
* Explicit recursive requests are honored as-is, and watches on or inside a workspace root are
|
|
115
|
+
* left untouched.
|
|
116
|
+
*/
|
|
117
|
+
protected shouldSkipWatch(uri: URI, options: WatchOptions): boolean {
|
|
118
|
+
if (options.recursive) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const roots = this.workspaceService.tryGetRoots();
|
|
122
|
+
// A folder that is itself a workspace root must always be watched, even if it also happens to
|
|
123
|
+
// be a (strict) ancestor of another root in a multi-root workspace where one root is nested
|
|
124
|
+
// inside another. Only watches rooted strictly above every root are dropped.
|
|
125
|
+
const isWorkspaceRoot = roots.some(root => uri.isEqual(root.resource));
|
|
126
|
+
const isAncestorOfWorkspace = !isWorkspaceRoot && roots.some(root => uri.isEqualOrParent(root.resource));
|
|
127
|
+
if (isAncestorOfWorkspace) {
|
|
128
|
+
const key = uri.toString();
|
|
129
|
+
if (!this.skippedWatchRoots.has(key)) {
|
|
130
|
+
this.skippedWatchRoots.add(key);
|
|
131
|
+
console.warn('[MainFileSystemEventService] skipping non-recursive watch rooted at an ancestor of the '
|
|
132
|
+
+ `workspace (the backend would recursively crawl sibling trees): ${key}`);
|
|
104
133
|
}
|
|
134
|
+
return true;
|
|
105
135
|
}
|
|
106
|
-
return
|
|
136
|
+
return false;
|
|
107
137
|
}
|
|
108
138
|
|
|
109
139
|
$unwatch(session: number): void {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*--------------------------------------------------------------------------------------------*/
|
|
20
20
|
|
|
21
21
|
import * as React from '@theia/core/shared/react';
|
|
22
|
-
import { inject, injectable, interfaces } from '@theia/core/shared/inversify';
|
|
22
|
+
import { inject, injectable, interfaces, named } from '@theia/core/shared/inversify';
|
|
23
23
|
import { generateUuid } from '@theia/core/lib/common/uuid';
|
|
24
24
|
import {
|
|
25
25
|
NotebookRendererMessagingService, CellOutputWebview, NotebookRendererRegistry,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
CellOutputChange, CellsChangedMessage, CellsMoved, CellsSpliced,
|
|
37
37
|
ChangePreferredMimetypeMessage, FromWebviewMessage, Output, OutputChangedMessage
|
|
38
38
|
} from './webview-communication';
|
|
39
|
-
import { Disposable, DisposableCollection, Emitter, QuickPickService, nls } from '@theia/core';
|
|
39
|
+
import { Disposable, DisposableCollection, Emitter, ILogger, QuickPickService, nls } from '@theia/core';
|
|
40
40
|
import { NotebookModel } from '@theia/notebook/lib/browser/view-model/notebook-model';
|
|
41
41
|
import { NotebookOptionsService, NotebookOutputOptions } from '@theia/notebook/lib/browser/service/notebook-options';
|
|
42
42
|
import { NotebookCellModel } from '@theia/notebook/lib/browser/view-model/notebook-cell-model';
|
|
@@ -224,6 +224,9 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
|
|
|
224
224
|
@inject(QuickPickService)
|
|
225
225
|
protected readonly quickPickService: QuickPickService;
|
|
226
226
|
|
|
227
|
+
@inject(ILogger) @named('plugin-ext:CellOutputWebviewImpl')
|
|
228
|
+
protected readonly logger: ILogger;
|
|
229
|
+
|
|
227
230
|
@inject(AdditionalNotebookCellOutputCss)
|
|
228
231
|
protected readonly additionalOutputCss: string;
|
|
229
232
|
|
|
@@ -341,17 +344,18 @@ export class CellOutputWebviewImpl implements CellOutputWebview, Disposable {
|
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
render(): React.JSX.Element {
|
|
344
|
-
return <div className='theia-notebook-cell-output-webview' ref={
|
|
347
|
+
return <div className='theia-notebook-cell-output-webview' ref={element => {
|
|
348
|
+
this.element = element ?? undefined;
|
|
345
349
|
if (element) {
|
|
346
|
-
this.
|
|
347
|
-
|
|
348
|
-
|
|
350
|
+
this.webviewWidgetInitialized.promise
|
|
351
|
+
.then(() => this.attachWebview())
|
|
352
|
+
.catch(error => this.logger.error('Failed to attach notebook output webview.', error));
|
|
349
353
|
}
|
|
350
354
|
}}></div>;
|
|
351
355
|
}
|
|
352
356
|
|
|
353
357
|
protected attachWebview(): void {
|
|
354
|
-
if (this.element) {
|
|
358
|
+
if (this.element && !this.isAttached()) {
|
|
355
359
|
this.webviewWidget.processMessage(new Message('before-attach'));
|
|
356
360
|
this.element.appendChild(this.webviewWidget.node);
|
|
357
361
|
this.webviewWidget.processMessage(new Message('after-attach'));
|
|
@@ -52,6 +52,7 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
|
52
52
|
import { LanguageService } from '@theia/core/lib/browser/language-service';
|
|
53
53
|
import { ThemeIcon } from '@theia/monaco-editor-core/esm/vs/base/common/themables';
|
|
54
54
|
import { JSONObject, JSONValue } from '@theia/core/shared/@lumino/coreutils';
|
|
55
|
+
import { ILogger } from '@theia/core';
|
|
55
56
|
|
|
56
57
|
// The enum export is missing from `vscode-textmate@9.2.0`
|
|
57
58
|
const enum StandardTokenType {
|
|
@@ -147,6 +148,9 @@ export class PluginContributionHandler {
|
|
|
147
148
|
@inject(ContextKeyService)
|
|
148
149
|
protected readonly contextKeyService: ContextKeyService;
|
|
149
150
|
|
|
151
|
+
@inject(ILogger) @named('plugin-ext:PluginContributionHandler')
|
|
152
|
+
protected readonly logger: ILogger;
|
|
153
|
+
|
|
150
154
|
protected readonly commandHandlers = new Map<string, CommandHandler['execute'] | undefined>();
|
|
151
155
|
|
|
152
156
|
protected readonly onDidRegisterCommandHandlerEmitter = new Emitter<string>();
|
|
@@ -164,8 +168,8 @@ export class PluginContributionHandler {
|
|
|
164
168
|
}
|
|
165
169
|
const toDispose = new DisposableCollection(Disposable.create(() => { /* mark as not disposed */ }));
|
|
166
170
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
167
|
-
const logError = (message: string, ...args: any[]) =>
|
|
168
|
-
const logWarning = (message: string, ...args: any[]) =>
|
|
171
|
+
const logError = (message: string, ...args: any[]) => this.logger.error(`[${clientId}][${plugin.metadata.model.id}]: ${message}`, ...args);
|
|
172
|
+
const logWarning = (message: string, ...args: any[]) => this.logger.warn(`[${clientId}][${plugin.metadata.model.id}]: ${message}`, ...args);
|
|
169
173
|
const pushContribution = (id: string, contribute: () => Disposable) => {
|
|
170
174
|
if (toDispose.disposed) {
|
|
171
175
|
return;
|
|
@@ -517,7 +521,7 @@ export class PluginContributionHandler {
|
|
|
517
521
|
|
|
518
522
|
registerCommand(command: Command, enablement?: string): Disposable {
|
|
519
523
|
if (this.hasCommand(command.id)) {
|
|
520
|
-
|
|
524
|
+
this.logger.warn(`command '${command.id}' already registered`);
|
|
521
525
|
return Disposable.NULL;
|
|
522
526
|
}
|
|
523
527
|
|
|
@@ -565,7 +569,7 @@ export class PluginContributionHandler {
|
|
|
565
569
|
|
|
566
570
|
registerCommandHandler(id: string, execute: CommandHandler['execute']): Disposable {
|
|
567
571
|
if (this.hasCommandHandler(id)) {
|
|
568
|
-
|
|
572
|
+
this.logger.warn(`command handler '${id}' already registered`);
|
|
569
573
|
return Disposable.NULL;
|
|
570
574
|
}
|
|
571
575
|
|
|
@@ -86,7 +86,7 @@ export class PluginWidget extends ReactWidget {
|
|
|
86
86
|
</div>;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
private renderPlugin(plugin: PluginMetadata): JSX.Element {
|
|
89
|
+
private renderPlugin(plugin: PluginMetadata): React.JSX.Element {
|
|
90
90
|
const unversionedId = PluginIdentifiers.componentsToUnversionedId(plugin.model);
|
|
91
91
|
const isRestrictedByTrust = this.pluginService.disabledByTrust.has(unversionedId);
|
|
92
92
|
return <div key={plugin.model.name} className={this.createPluginClassName(plugin, isRestrictedByTrust)}>
|
|
@@ -24,7 +24,8 @@ FrontendApplicationConfigProvider.set({});
|
|
|
24
24
|
|
|
25
25
|
import { expect } from 'chai';
|
|
26
26
|
import { Disposable } from '@theia/core/lib/common';
|
|
27
|
-
import { PluginViewRegistry, ViewContainerInfo } from './plugin-view-registry';
|
|
27
|
+
import { PluginViewRegistry, ViewContainerInfo, PLUGIN_VIEW_DATA_FACTORY_ID } from './plugin-view-registry';
|
|
28
|
+
import type { ViewWelcome } from '../../../common';
|
|
28
29
|
|
|
29
30
|
disableJSDOM();
|
|
30
31
|
|
|
@@ -103,3 +104,70 @@ describe('PluginViewRegistry - view menu labels', () => {
|
|
|
103
104
|
});
|
|
104
105
|
|
|
105
106
|
});
|
|
107
|
+
|
|
108
|
+
describe('PluginViewRegistry - welcome-only views', () => {
|
|
109
|
+
|
|
110
|
+
interface FakeTreeViewWidget {
|
|
111
|
+
model: { root?: unknown };
|
|
112
|
+
welcomeArg?: ViewWelcome[];
|
|
113
|
+
handleViewWelcomeContentChange(welcomes: ViewWelcome[]): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let registry: PluginViewRegistry;
|
|
117
|
+
let createdWidget: FakeTreeViewWidget;
|
|
118
|
+
let getOrCreateCalls: Array<{ factoryId: string; options: unknown }>;
|
|
119
|
+
|
|
120
|
+
const welcome = (view: string): ViewWelcome => ({ view, content: `[Hi](command:${view}.hi)`, order: 0 });
|
|
121
|
+
|
|
122
|
+
const internals = (): {
|
|
123
|
+
views: Map<string, [string, { type?: unknown }]>;
|
|
124
|
+
viewDataProviders: Map<string, unknown>;
|
|
125
|
+
viewsWelcome: Map<string, ViewWelcome[]>;
|
|
126
|
+
createViewDataWidget(viewId: string): Promise<unknown>;
|
|
127
|
+
} => registry as unknown as {
|
|
128
|
+
views: Map<string, [string, { type?: unknown }]>;
|
|
129
|
+
viewDataProviders: Map<string, unknown>;
|
|
130
|
+
viewsWelcome: Map<string, ViewWelcome[]>;
|
|
131
|
+
createViewDataWidget(viewId: string): Promise<unknown>;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
registry = new PluginViewRegistry();
|
|
136
|
+
getOrCreateCalls = [];
|
|
137
|
+
createdWidget = {
|
|
138
|
+
model: {},
|
|
139
|
+
handleViewWelcomeContentChange(welcomes: ViewWelcome[]): void {
|
|
140
|
+
this.welcomeArg = welcomes;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
(registry as unknown as { widgetManager: unknown }).widgetManager = {
|
|
144
|
+
getOrCreateWidget: async (factoryId: string, options: unknown) => {
|
|
145
|
+
getOrCreateCalls.push({ factoryId, options });
|
|
146
|
+
return createdWidget;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
internals().views.set('actions', ['container', {}]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('creates a welcome widget for a view with welcomes but no data provider', async () => {
|
|
153
|
+
const welcomes = [welcome('actions')];
|
|
154
|
+
internals().viewsWelcome.set('actions', welcomes);
|
|
155
|
+
|
|
156
|
+
const widget = await internals().createViewDataWidget('actions');
|
|
157
|
+
|
|
158
|
+
expect(widget).to.equal(createdWidget);
|
|
159
|
+
expect(getOrCreateCalls).to.have.lengthOf(1);
|
|
160
|
+
expect(getOrCreateCalls[0].factoryId).to.equal(PLUGIN_VIEW_DATA_FACTORY_ID);
|
|
161
|
+
expect(getOrCreateCalls[0].options).to.deep.equal({ id: 'actions' });
|
|
162
|
+
expect(createdWidget.welcomeArg).to.equal(welcomes);
|
|
163
|
+
expect(createdWidget.model.root).to.not.equal(undefined);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('returns undefined for a view with neither a provider nor welcomes', async () => {
|
|
167
|
+
const widget = await internals().createViewDataWidget('actions');
|
|
168
|
+
|
|
169
|
+
expect(widget).to.equal(undefined);
|
|
170
|
+
expect(getOrCreateCalls).to.have.lengthOf(0);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
});
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
ApplicationShell, ViewContainer as ViewContainerWidget, WidgetManager, QuickViewService,
|
|
20
20
|
ViewContainerIdentifier, ViewContainerTitleOptions, Widget, FrontendApplicationContribution,
|
|
21
21
|
StatefulWidget, CommonMenus, TreeViewWelcomeWidget, ViewContainerPart, BaseWidget,
|
|
22
|
+
CompositeTreeNode, ExpandableTreeNode,
|
|
22
23
|
} from '@theia/core/lib/browser';
|
|
23
24
|
import { ViewContainer, View, ViewWelcome, PluginViewType } from '../../../common';
|
|
24
25
|
import { PluginSharedStyle } from '../plugin-shared-style';
|
|
@@ -643,7 +644,9 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
|
|
|
643
644
|
});
|
|
644
645
|
return _pendingResolution;
|
|
645
646
|
},
|
|
646
|
-
show:
|
|
647
|
+
show: (preserveFocus: boolean) => {
|
|
648
|
+
this.openView(viewId, preserveFocus ? { reveal: true } : { activate: true });
|
|
649
|
+
}
|
|
647
650
|
};
|
|
648
651
|
|
|
649
652
|
const toDispose = this.onNewResolverRegistered(resolver => {
|
|
@@ -1053,10 +1056,15 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
|
|
|
1053
1056
|
if (view?.[1]?.type === PluginViewType.Webview) {
|
|
1054
1057
|
return this.createWebviewWidget(viewId, webviewId);
|
|
1055
1058
|
}
|
|
1056
|
-
|
|
1057
|
-
if (!view || !provider) {
|
|
1059
|
+
if (!view) {
|
|
1058
1060
|
return undefined;
|
|
1059
1061
|
}
|
|
1062
|
+
const provider = this.viewDataProviders.get(viewId);
|
|
1063
|
+
if (!provider) {
|
|
1064
|
+
return this.getViewWelcomes(viewId).length > 0
|
|
1065
|
+
? this.createViewWelcomeWidget(viewId)
|
|
1066
|
+
: undefined;
|
|
1067
|
+
}
|
|
1060
1068
|
const [, viewInfo] = view;
|
|
1061
1069
|
const state = this.viewDataState.get(viewId);
|
|
1062
1070
|
const widget = await provider({ state, viewInfo });
|
|
@@ -1069,6 +1077,25 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
|
|
|
1069
1077
|
return widget;
|
|
1070
1078
|
}
|
|
1071
1079
|
|
|
1080
|
+
protected async createViewWelcomeWidget(viewId: string): Promise<TreeViewWidget> {
|
|
1081
|
+
const widget = await this.widgetManager.getOrCreateWidget<TreeViewWidget>(PLUGIN_VIEW_DATA_FACTORY_ID, { id: viewId });
|
|
1082
|
+
if (!widget.model.root) {
|
|
1083
|
+
const root: CompositeTreeNode & ExpandableTreeNode = {
|
|
1084
|
+
id: '',
|
|
1085
|
+
parent: undefined,
|
|
1086
|
+
name: '',
|
|
1087
|
+
visible: false,
|
|
1088
|
+
expanded: true,
|
|
1089
|
+
children: []
|
|
1090
|
+
};
|
|
1091
|
+
widget.model.root = root;
|
|
1092
|
+
}
|
|
1093
|
+
// An undefined `model.proxy` keeps `shouldShowWelcomeView()` true, so the welcome content
|
|
1094
|
+
// renders instead of an empty node list.
|
|
1095
|
+
widget.handleViewWelcomeContentChange(this.getViewWelcomes(viewId));
|
|
1096
|
+
return widget;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1072
1099
|
protected async createWebviewWidget(viewId: string, webviewId?: string): Promise<Widget | undefined> {
|
|
1073
1100
|
if (!webviewId) {
|
|
1074
1101
|
const webviewView = await this.createNewWebviewView(viewId);
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import { injectable } from '@theia/core/shared/inversify';
|
|
17
|
+
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
18
18
|
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
19
19
|
import { MaybePromise } from '@theia/core/lib/common/types';
|
|
20
|
+
import { ILogger } from '@theia/core';
|
|
20
21
|
|
|
21
22
|
export interface WebviewResourceResponse {
|
|
22
23
|
eTag: string | undefined,
|
|
@@ -29,6 +30,9 @@ export interface WebviewResourceResponse {
|
|
|
29
30
|
@injectable()
|
|
30
31
|
export class WebviewResourceCache {
|
|
31
32
|
|
|
33
|
+
@inject(ILogger) @named('plugin-ext:WebviewResourceCache')
|
|
34
|
+
protected readonly logger: ILogger;
|
|
35
|
+
|
|
32
36
|
protected readonly cache = new Deferred<Cache | undefined>();
|
|
33
37
|
|
|
34
38
|
constructor() {
|
|
@@ -39,7 +43,7 @@ export class WebviewResourceCache {
|
|
|
39
43
|
try {
|
|
40
44
|
this.cache.resolve(await caches.open('webview:v1'));
|
|
41
45
|
} catch (e) {
|
|
42
|
-
|
|
46
|
+
this.logger.error('Failed to enable webview caching: ', e);
|
|
43
47
|
this.cache.resolve(undefined);
|
|
44
48
|
}
|
|
45
49
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
import * as mime from 'mime';
|
|
24
24
|
import { JSONExt } from '@theia/core/shared/@lumino/coreutils';
|
|
25
|
-
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
|
|
25
|
+
import { injectable, inject, postConstruct, named } from '@theia/core/shared/inversify';
|
|
26
26
|
import { WebviewPanelOptions, WebviewPortMapping } from '@theia/plugin';
|
|
27
27
|
import { BaseWidget, Message, codicon } from '@theia/core/lib/browser/widgets/widget';
|
|
28
28
|
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
|
|
@@ -49,7 +49,7 @@ import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
|
49
49
|
import { FileOperationError, FileOperationResult } from '@theia/filesystem/lib/common/files';
|
|
50
50
|
import { BinaryBufferReadableStream } from '@theia/core/lib/common/buffer';
|
|
51
51
|
import { ExtractableWidget } from '@theia/core/lib/browser/widgets/extractable-widget';
|
|
52
|
-
import { MenuPath } from '@theia/core';
|
|
52
|
+
import { MenuPath, ILogger } from '@theia/core';
|
|
53
53
|
import { ContextMenuRenderer } from '@theia/core/lib/browser';
|
|
54
54
|
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
|
|
55
55
|
import { PluginViewWidget } from '../view/plugin-view-widget';
|
|
@@ -166,6 +166,9 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract
|
|
|
166
166
|
@inject(ContextKeyService)
|
|
167
167
|
protected readonly contextKeyService: ContextKeyService;
|
|
168
168
|
|
|
169
|
+
@inject(ILogger) @named('plugin-ext:WebviewWidget')
|
|
170
|
+
protected readonly logger: ILogger;
|
|
171
|
+
|
|
169
172
|
viewState: WebviewPanelViewState = {
|
|
170
173
|
visible: false,
|
|
171
174
|
active: false,
|
|
@@ -626,7 +629,7 @@ export class WebviewWidget extends BaseWidget implements StatefulWidget, Extract
|
|
|
626
629
|
await this.ready.promise;
|
|
627
630
|
this.postMessage(channel, data);
|
|
628
631
|
} catch (e) {
|
|
629
|
-
|
|
632
|
+
this.logger.error(e);
|
|
630
633
|
}
|
|
631
634
|
}
|
|
632
635
|
|
|
@@ -42,7 +42,7 @@ export class PluginDeployerImpl implements PluginDeployer {
|
|
|
42
42
|
protected readonly onDidDeployEmitter = new Emitter<void>();
|
|
43
43
|
readonly onDidDeploy = this.onDidDeployEmitter.event;
|
|
44
44
|
|
|
45
|
-
@inject(ILogger)
|
|
45
|
+
@inject(ILogger) @named('plugin-ext:PluginDeployerImpl')
|
|
46
46
|
protected readonly logger: ILogger;
|
|
47
47
|
|
|
48
48
|
@inject(PluginDeployerHandler)
|
|
@@ -215,7 +215,7 @@ export class PluginDeployerImpl implements PluginDeployer {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
} catch (e) {
|
|
218
|
-
|
|
218
|
+
this.logger.error(`Failed to resolve plugins from '${entry.id}'`, e);
|
|
219
219
|
errors.push(e instanceof Error ? e : new Error(String(e)));
|
|
220
220
|
}
|
|
221
221
|
}));
|
|
@@ -30,6 +30,7 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
30
30
|
];
|
|
31
31
|
|
|
32
32
|
private collectionName: string;
|
|
33
|
+
private ownerId: string;
|
|
33
34
|
private diagnosticsLimitPerResource: number;
|
|
34
35
|
private proxy: LanguagesMain;
|
|
35
36
|
private onDidChangeDiagnosticsEmitter: Emitter<theia.DiagnosticChangeEvent>;
|
|
@@ -38,8 +39,9 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
38
39
|
private isDisposed: boolean;
|
|
39
40
|
private onDisposeCallback: (() => void) | undefined;
|
|
40
41
|
|
|
41
|
-
constructor(name: string, maxCountPerFile: number, proxy: LanguagesMain, onDidChangeDiagnosticsEmitter: Emitter<theia.DiagnosticChangeEvent>) {
|
|
42
|
+
constructor(name: string, owner: string, maxCountPerFile: number, proxy: LanguagesMain, onDidChangeDiagnosticsEmitter: Emitter<theia.DiagnosticChangeEvent>) {
|
|
42
43
|
this.collectionName = name;
|
|
44
|
+
this.ownerId = owner;
|
|
43
45
|
this.diagnosticsLimitPerResource = maxCountPerFile;
|
|
44
46
|
this.proxy = proxy;
|
|
45
47
|
this.onDidChangeDiagnosticsEmitter = onDidChangeDiagnosticsEmitter;
|
|
@@ -53,6 +55,10 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
53
55
|
return this.collectionName;
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
get owner(): string {
|
|
59
|
+
return this.ownerId;
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
set(uri: theia.Uri, diagnostics: theia.Diagnostic[] | undefined): void;
|
|
57
63
|
set(entries: [theia.Uri, theia.Diagnostic[] | undefined][]): void;
|
|
58
64
|
set(arg: theia.Uri | [theia.Uri, theia.Diagnostic[] | undefined][], diagnostics?: theia.Diagnostic[] | undefined): void {
|
|
@@ -115,7 +121,7 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
115
121
|
if (this.has(uri)) {
|
|
116
122
|
this.fireDiagnosticChangeEvent(uri);
|
|
117
123
|
this.diagnostics.delete(uri.toString());
|
|
118
|
-
this.proxy.$changeDiagnostics(this.
|
|
124
|
+
this.proxy.$changeDiagnostics(this.ownerId, [[uri.toString(), []]]);
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
127
|
|
|
@@ -123,7 +129,7 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
123
129
|
this.ensureNotDisposed();
|
|
124
130
|
this.fireDiagnosticChangeEvent(this.getAllResourcesUris());
|
|
125
131
|
this.diagnostics.clear();
|
|
126
|
-
this.proxy.$clearDiagnostics(this.
|
|
132
|
+
this.proxy.$clearDiagnostics(this.ownerId);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
135
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -247,7 +253,7 @@ export class DiagnosticCollection implements theia.DiagnosticCollection {
|
|
|
247
253
|
}
|
|
248
254
|
}
|
|
249
255
|
|
|
250
|
-
this.proxy.$changeDiagnostics(this.
|
|
256
|
+
this.proxy.$changeDiagnostics(this.ownerId, markers);
|
|
251
257
|
}
|
|
252
258
|
}
|
|
253
259
|
|
|
@@ -256,7 +262,12 @@ export class Diagnostics {
|
|
|
256
262
|
private static GENERATED_DIAGNOSTIC_COLLECTION_NAME_PREFIX = '_generated_diagnostic_collection_name_#';
|
|
257
263
|
|
|
258
264
|
private proxy: LanguagesMain;
|
|
259
|
-
|
|
265
|
+
// Collections are keyed by their unique `owner` id (which is the same as `name` for the first
|
|
266
|
+
// collection with that name, but is suffixed when a duplicate name is requested). This matches
|
|
267
|
+
// VS Code's behavior and prevents collisions on the main side's marker owner namespace as well
|
|
268
|
+
// as a latent bug where disposing the first collection would evict the second from the map.
|
|
269
|
+
private diagnosticCollections: Map<string, DiagnosticCollection>;
|
|
270
|
+
private nextOwnerSuffix = 1;
|
|
260
271
|
|
|
261
272
|
private diagnosticsChangedEmitter = new Emitter<theia.DiagnosticChangeEvent>();
|
|
262
273
|
public readonly onDidChangeDiagnostics: Event<theia.DiagnosticChangeEvent> = this.diagnosticsChangedEmitter.event;
|
|
@@ -278,19 +289,26 @@ export class Diagnostics {
|
|
|
278
289
|
}
|
|
279
290
|
|
|
280
291
|
createDiagnosticCollection(name?: string): theia.DiagnosticCollection {
|
|
292
|
+
let owner: string;
|
|
281
293
|
if (!name) {
|
|
282
294
|
do {
|
|
283
295
|
name = Diagnostics.GENERATED_DIAGNOSTIC_COLLECTION_NAME_PREFIX + this.getNextId();
|
|
284
296
|
} while (this.diagnosticCollections.has(name));
|
|
285
|
-
|
|
286
|
-
|
|
297
|
+
owner = name;
|
|
298
|
+
} else if (!this.diagnosticCollections.has(name)) {
|
|
299
|
+
owner = name;
|
|
300
|
+
} else {
|
|
301
|
+
console.warn(`Diagnostic collection with name '${name}' already exists.`);
|
|
302
|
+
do {
|
|
303
|
+
owner = name + this.nextOwnerSuffix++;
|
|
304
|
+
} while (this.diagnosticCollections.has(owner));
|
|
287
305
|
}
|
|
288
306
|
|
|
289
|
-
const diagnosticCollection = new DiagnosticCollection(name, Diagnostics.MAX_DIAGNOSTICS_PER_FILE, this.proxy, this.diagnosticsChangedEmitter);
|
|
307
|
+
const diagnosticCollection = new DiagnosticCollection(name, owner, Diagnostics.MAX_DIAGNOSTICS_PER_FILE, this.proxy, this.diagnosticsChangedEmitter);
|
|
290
308
|
diagnosticCollection.setOnDisposeCallback(() => {
|
|
291
|
-
this.diagnosticCollections.delete(
|
|
309
|
+
this.diagnosticCollections.delete(owner);
|
|
292
310
|
});
|
|
293
|
-
this.diagnosticCollections.set(
|
|
311
|
+
this.diagnosticCollections.set(owner, diagnosticCollection);
|
|
294
312
|
return diagnosticCollection;
|
|
295
313
|
}
|
|
296
314
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { URI } from '@theia/core/shared/vscode-uri';
|
|
18
|
+
import * as theia from '@theia/plugin';
|
|
19
|
+
import { DocumentsExtImpl } from '../documents';
|
|
20
|
+
import * as types from '../types-impl';
|
|
21
|
+
import * as Converter from '../type-converters';
|
|
22
|
+
import { Position } from '../../common/plugin-api-rpc';
|
|
23
|
+
import { MultiDocumentHighlightDto } from '../../common/plugin-api-rpc-model';
|
|
24
|
+
import { UriComponents } from '../../common/uri-components';
|
|
25
|
+
|
|
26
|
+
export class MultiDocumentHighlightAdapter {
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
private readonly provider: theia.MultiDocumentHighlightProvider,
|
|
30
|
+
private readonly documents: DocumentsExtImpl) {
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
provideMultiDocumentHighlights(
|
|
34
|
+
resource: URI,
|
|
35
|
+
position: Position,
|
|
36
|
+
otherResources: UriComponents[],
|
|
37
|
+
token: theia.CancellationToken
|
|
38
|
+
): Promise<MultiDocumentHighlightDto[] | undefined> {
|
|
39
|
+
const documentData = this.documents.getDocumentData(resource);
|
|
40
|
+
if (!documentData) {
|
|
41
|
+
return Promise.reject(new Error(`There is no document for ${resource}`));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const document = documentData.document;
|
|
45
|
+
const zeroBasedPosition = Converter.toPosition(position);
|
|
46
|
+
|
|
47
|
+
const otherDocuments: theia.TextDocument[] = [];
|
|
48
|
+
for (const otherResource of otherResources) {
|
|
49
|
+
const otherUri = URI.revive(otherResource);
|
|
50
|
+
const otherDocData = this.documents.getDocumentData(otherUri);
|
|
51
|
+
if (otherDocData) {
|
|
52
|
+
otherDocuments.push(otherDocData.document);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Promise.resolve(this.provider.provideMultiDocumentHighlights(document, zeroBasedPosition, otherDocuments, token)).then(result => {
|
|
57
|
+
if (!result) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!this.isMultiDocumentHighlightArray(result)) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return result.map(multiHighlight => ({
|
|
66
|
+
uri: multiHighlight.uri,
|
|
67
|
+
highlights: multiHighlight.highlights.map(h => Converter.fromDocumentHighlight(h))
|
|
68
|
+
}));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private isMultiDocumentHighlightArray(array: unknown): array is types.MultiDocumentHighlight[] {
|
|
73
|
+
return Array.isArray(array) && array.length > 0 && array[0] instanceof types.MultiDocumentHighlight;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/plugin/languages.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
SerializedDocumentFilter,
|
|
45
45
|
SignatureHelp,
|
|
46
46
|
DocumentHighlight,
|
|
47
|
+
MultiDocumentHighlightDto,
|
|
47
48
|
Range,
|
|
48
49
|
TextEdit,
|
|
49
50
|
FormattingOptions,
|
|
@@ -81,6 +82,7 @@ import { HoverAdapter } from './languages/hover';
|
|
|
81
82
|
import { EvaluatableExpressionAdapter } from './languages/evaluatable-expression';
|
|
82
83
|
import { InlineValuesAdapter } from './languages/inline-values';
|
|
83
84
|
import { DocumentHighlightAdapter } from './languages/document-highlight';
|
|
85
|
+
import { MultiDocumentHighlightAdapter } from './languages/multi-document-highlight';
|
|
84
86
|
import { DocumentFormattingAdapter } from './languages/document-formatting';
|
|
85
87
|
import { RangeFormattingAdapter } from './languages/range-formatting';
|
|
86
88
|
import { OnTypeFormattingAdapter } from './languages/on-type-formatting';
|
|
@@ -122,6 +124,7 @@ type Adapter = CompletionAdapter |
|
|
|
122
124
|
EvaluatableExpressionAdapter |
|
|
123
125
|
InlineValuesAdapter |
|
|
124
126
|
DocumentHighlightAdapter |
|
|
127
|
+
MultiDocumentHighlightAdapter |
|
|
125
128
|
DocumentFormattingAdapter |
|
|
126
129
|
RangeFormattingAdapter |
|
|
127
130
|
OnTypeFormattingAdapter |
|
|
@@ -466,6 +469,23 @@ export class LanguagesExtImpl implements LanguagesExt {
|
|
|
466
469
|
}
|
|
467
470
|
// ### Document Highlight Provider end
|
|
468
471
|
|
|
472
|
+
// ### Multi Document Highlight Provider begin
|
|
473
|
+
registerMultiDocumentHighlightProvider(
|
|
474
|
+
selector: theia.DocumentSelector, provider: theia.MultiDocumentHighlightProvider, pluginInfo: PluginInfo
|
|
475
|
+
): theia.Disposable {
|
|
476
|
+
const callId = this.addNewAdapter(new MultiDocumentHighlightAdapter(provider, this.documents));
|
|
477
|
+
this.proxy.$registerMultiDocumentHighlightProvider(callId, pluginInfo, this.transformDocumentSelector(selector));
|
|
478
|
+
return this.createDisposable(callId);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
$provideMultiDocumentHighlights(
|
|
482
|
+
handle: number, resource: UriComponents, position: Position, otherResources: UriComponents[], token: theia.CancellationToken
|
|
483
|
+
): Promise<MultiDocumentHighlightDto[] | undefined> {
|
|
484
|
+
return this.withAdapter(handle, MultiDocumentHighlightAdapter,
|
|
485
|
+
adapter => adapter.provideMultiDocumentHighlights(URI.revive(resource), position, otherResources, token), undefined);
|
|
486
|
+
}
|
|
487
|
+
// ### Multi Document Highlight Provider end
|
|
488
|
+
|
|
469
489
|
// ### WorkspaceSymbol Provider begin
|
|
470
490
|
registerWorkspaceSymbolProvider(provider: theia.WorkspaceSymbolProvider, pluginInfo: PluginInfo): theia.Disposable {
|
|
471
491
|
const callId = this.addNewAdapter(new WorkspaceSymbolAdapter(provider));
|