@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.
Files changed (169) hide show
  1. package/lib/common/lm-tool-protocol.d.ts +58 -0
  2. package/lib/common/lm-tool-protocol.d.ts.map +1 -0
  3. package/lib/common/lm-tool-protocol.js +38 -0
  4. package/lib/common/lm-tool-protocol.js.map +1 -0
  5. package/lib/common/lm-tool-protocol.spec.d.ts +2 -0
  6. package/lib/common/lm-tool-protocol.spec.d.ts.map +1 -0
  7. package/lib/common/lm-tool-protocol.spec.js +80 -0
  8. package/lib/common/lm-tool-protocol.spec.js.map +1 -0
  9. package/lib/common/plugin-api-rpc-model.d.ts +4 -0
  10. package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
  11. package/lib/common/plugin-api-rpc-model.js.map +1 -1
  12. package/lib/common/plugin-api-rpc.d.ts +6 -1
  13. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  14. package/lib/common/plugin-api-rpc.js +4 -2
  15. package/lib/common/plugin-api-rpc.js.map +1 -1
  16. package/lib/common/plugin-protocol.d.ts +8 -0
  17. package/lib/common/plugin-protocol.d.ts.map +1 -1
  18. package/lib/common/plugin-protocol.js.map +1 -1
  19. package/lib/hosted/node/hosted-plugin-localization-service.d.ts +2 -1
  20. package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
  21. package/lib/hosted/node/hosted-plugin-localization-service.js +6 -1
  22. package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
  23. package/lib/hosted/node/plugin-deployer-handler-impl.js +4 -3
  24. package/lib/hosted/node/plugin-deployer-handler-impl.js.map +1 -1
  25. package/lib/hosted/node/plugin-host-module.d.ts.map +1 -1
  26. package/lib/hosted/node/plugin-host-module.js +2 -0
  27. package/lib/hosted/node/plugin-host-module.js.map +1 -1
  28. package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -0
  29. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  30. package/lib/hosted/node/scanners/scanner-theia.js +55 -49
  31. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  32. package/lib/main/browser/comments/comment-thread-widget.d.ts +3 -3
  33. package/lib/main/browser/comments/comment-thread-widget.d.ts.map +1 -1
  34. package/lib/main/browser/comments/comment-thread-widget.js.map +1 -1
  35. package/lib/main/browser/custom-editors/custom-editor-opener.d.ts.map +1 -1
  36. package/lib/main/browser/custom-editors/custom-editor-opener.js +1 -0
  37. package/lib/main/browser/custom-editors/custom-editor-opener.js.map +1 -1
  38. package/lib/main/browser/custom-editors/custom-editor-widget.d.ts +1 -0
  39. package/lib/main/browser/custom-editors/custom-editor-widget.d.ts.map +1 -1
  40. package/lib/main/browser/custom-editors/custom-editor-widget.js +3 -0
  41. package/lib/main/browser/custom-editors/custom-editor-widget.js.map +1 -1
  42. package/lib/main/browser/custom-editors/custom-editors-main.d.ts.map +1 -1
  43. package/lib/main/browser/custom-editors/custom-editors-main.js +2 -0
  44. package/lib/main/browser/custom-editors/custom-editors-main.js.map +1 -1
  45. package/lib/main/browser/debug/debug-main.d.ts +1 -11
  46. package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
  47. package/lib/main/browser/debug/debug-main.js +3 -24
  48. package/lib/main/browser/debug/debug-main.js.map +1 -1
  49. package/lib/main/browser/debug/plugin-debug-service.d.ts +2 -1
  50. package/lib/main/browser/debug/plugin-debug-service.d.ts.map +1 -1
  51. package/lib/main/browser/debug/plugin-debug-service.js +8 -3
  52. package/lib/main/browser/debug/plugin-debug-service.js.map +1 -1
  53. package/lib/main/browser/debug/plugin-debug-session-factory.d.ts +7 -44
  54. package/lib/main/browser/debug/plugin-debug-session-factory.d.ts.map +1 -1
  55. package/lib/main/browser/debug/plugin-debug-session-factory.js +29 -35
  56. package/lib/main/browser/debug/plugin-debug-session-factory.js.map +1 -1
  57. package/lib/main/browser/languages-main.d.ts +6 -1
  58. package/lib/main/browser/languages-main.d.ts.map +1 -1
  59. package/lib/main/browser/languages-main.js +39 -2
  60. package/lib/main/browser/languages-main.js.map +1 -1
  61. package/lib/main/browser/lm-main.js +2 -2
  62. package/lib/main/browser/lm-main.js.map +1 -1
  63. package/lib/main/browser/lm-tool-main.d.ts +14 -0
  64. package/lib/main/browser/lm-tool-main.d.ts.map +1 -0
  65. package/lib/main/browser/lm-tool-main.js +101 -0
  66. package/lib/main/browser/lm-tool-main.js.map +1 -0
  67. package/lib/main/browser/lm-tool-main.spec.d.ts +2 -0
  68. package/lib/main/browser/lm-tool-main.spec.d.ts.map +1 -0
  69. package/lib/main/browser/lm-tool-main.spec.js +100 -0
  70. package/lib/main/browser/lm-tool-main.spec.js.map +1 -0
  71. package/lib/main/browser/main-context.d.ts.map +1 -1
  72. package/lib/main/browser/main-context.js +3 -0
  73. package/lib/main/browser/main-context.js.map +1 -1
  74. package/lib/main/browser/main-file-system-event-service.d.ts +21 -4
  75. package/lib/main/browser/main-file-system-event-service.d.ts.map +1 -1
  76. package/lib/main/browser/main-file-system-event-service.js +47 -17
  77. package/lib/main/browser/main-file-system-event-service.js.map +1 -1
  78. package/lib/main/browser/main-file-system-event-service.spec.js +61 -26
  79. package/lib/main/browser/main-file-system-event-service.spec.js.map +1 -1
  80. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts +2 -1
  81. package/lib/main/browser/notebooks/renderers/cell-output-webview.d.ts.map +1 -1
  82. package/lib/main/browser/notebooks/renderers/cell-output-webview.js +11 -5
  83. package/lib/main/browser/notebooks/renderers/cell-output-webview.js.map +1 -1
  84. package/lib/main/browser/plugin-contribution-handler.d.ts +2 -0
  85. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  86. package/lib/main/browser/plugin-contribution-handler.js +10 -4
  87. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  88. package/lib/main/browser/view/plugin-view-registry.d.ts +1 -0
  89. package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
  90. package/lib/main/browser/view/plugin-view-registry.js +28 -3
  91. package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
  92. package/lib/main/browser/view/plugin-view-registry.spec.js +40 -0
  93. package/lib/main/browser/view/plugin-view-registry.spec.js.map +1 -1
  94. package/lib/main/browser/webview/webview-resource-cache.d.ts +2 -0
  95. package/lib/main/browser/webview/webview-resource-cache.d.ts.map +1 -1
  96. package/lib/main/browser/webview/webview-resource-cache.js +7 -1
  97. package/lib/main/browser/webview/webview-resource-cache.js.map +1 -1
  98. package/lib/main/browser/webview/webview.d.ts +2 -1
  99. package/lib/main/browser/webview/webview.d.ts.map +1 -1
  100. package/lib/main/browser/webview/webview.js +7 -1
  101. package/lib/main/browser/webview/webview.js.map +1 -1
  102. package/lib/main/node/plugin-deployer-impl.js +2 -1
  103. package/lib/main/node/plugin-deployer-impl.js.map +1 -1
  104. package/lib/plugin/languages/diagnostics.d.ts +4 -1
  105. package/lib/plugin/languages/diagnostics.d.ts.map +1 -1
  106. package/lib/plugin/languages/diagnostics.js +22 -9
  107. package/lib/plugin/languages/diagnostics.js.map +1 -1
  108. package/lib/plugin/languages/multi-document-highlight.d.ts +14 -0
  109. package/lib/plugin/languages/multi-document-highlight.d.ts.map +1 -0
  110. package/lib/plugin/languages/multi-document-highlight.js +60 -0
  111. package/lib/plugin/languages/multi-document-highlight.js.map +1 -0
  112. package/lib/plugin/languages.d.ts +3 -1
  113. package/lib/plugin/languages.d.ts.map +1 -1
  114. package/lib/plugin/languages.js +11 -0
  115. package/lib/plugin/languages.js.map +1 -1
  116. package/lib/plugin/lm-tool-ext.d.ts +28 -0
  117. package/lib/plugin/lm-tool-ext.d.ts.map +1 -0
  118. package/lib/plugin/lm-tool-ext.js +171 -0
  119. package/lib/plugin/lm-tool-ext.js.map +1 -0
  120. package/lib/plugin/lm-tool-ext.spec.d.ts +2 -0
  121. package/lib/plugin/lm-tool-ext.spec.d.ts.map +1 -0
  122. package/lib/plugin/lm-tool-ext.spec.js +124 -0
  123. package/lib/plugin/lm-tool-ext.spec.js.map +1 -0
  124. package/lib/plugin/plugin-context.d.ts.map +1 -1
  125. package/lib/plugin/plugin-context.js +10 -11
  126. package/lib/plugin/plugin-context.js.map +1 -1
  127. package/lib/plugin/types-impl.d.ts +0 -9
  128. package/lib/plugin/types-impl.d.ts.map +1 -1
  129. package/lib/plugin/types-impl.js +22 -15
  130. package/lib/plugin/types-impl.js.map +1 -1
  131. package/package.json +32 -31
  132. package/src/common/lm-tool-protocol.spec.ts +89 -0
  133. package/src/common/lm-tool-protocol.ts +102 -0
  134. package/src/common/plugin-api-rpc-model.ts +5 -0
  135. package/src/common/plugin-api-rpc.ts +9 -2
  136. package/src/common/plugin-protocol.ts +9 -0
  137. package/src/hosted/node/hosted-plugin-localization-service.ts +6 -3
  138. package/src/hosted/node/plugin-deployer-handler-impl.ts +5 -5
  139. package/src/hosted/node/plugin-host-module.ts +2 -0
  140. package/src/hosted/node/scanners/scanner-theia.ts +54 -50
  141. package/src/main/browser/comments/comment-thread-widget.tsx +5 -5
  142. package/src/main/browser/custom-editors/custom-editor-opener.tsx +1 -0
  143. package/src/main/browser/custom-editors/custom-editor-widget.ts +4 -0
  144. package/src/main/browser/custom-editors/custom-editors-main.ts +2 -0
  145. package/src/main/browser/debug/debug-main.ts +3 -45
  146. package/src/main/browser/debug/plugin-debug-service.ts +7 -5
  147. package/src/main/browser/debug/plugin-debug-session-factory.ts +25 -71
  148. package/src/main/browser/languages-main.ts +49 -4
  149. package/src/main/browser/lm-main.ts +2 -2
  150. package/src/main/browser/lm-tool-main.spec.ts +126 -0
  151. package/src/main/browser/lm-tool-main.ts +116 -0
  152. package/src/main/browser/main-context.ts +4 -0
  153. package/src/main/browser/main-file-system-event-service.spec.ts +76 -29
  154. package/src/main/browser/main-file-system-event-service.ts +46 -16
  155. package/src/main/browser/notebooks/renderers/cell-output-webview.tsx +11 -7
  156. package/src/main/browser/plugin-contribution-handler.ts +8 -4
  157. package/src/main/browser/plugin-ext-widget.tsx +1 -1
  158. package/src/main/browser/view/plugin-view-registry.spec.ts +69 -1
  159. package/src/main/browser/view/plugin-view-registry.ts +30 -3
  160. package/src/main/browser/webview/webview-resource-cache.ts +6 -2
  161. package/src/main/browser/webview/webview.ts +6 -3
  162. package/src/main/node/plugin-deployer-impl.ts +2 -2
  163. package/src/plugin/languages/diagnostics.ts +28 -10
  164. package/src/plugin/languages/multi-document-highlight.ts +75 -0
  165. package/src/plugin/languages.ts +20 -0
  166. package/src/plugin/lm-tool-ext.spec.ts +152 -0
  167. package/src/plugin/lm-tool-ext.ts +197 -0
  168. package/src/plugin/plugin-context.ts +11 -11
  169. package/src/plugin/types-impl.ts +22 -15
@@ -14,53 +14,32 @@
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, inject, interfaces } from '@theia/core/shared/inversify';
17
18
  import { DefaultDebugSessionFactory } from '@theia/debug/lib/browser/debug-session-contribution';
18
- import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
19
- import { EditorManager } from '@theia/editor/lib/browser/editor-manager';
20
- import { BreakpointManager } from '@theia/debug/lib/browser/breakpoint/breakpoint-manager';
21
- import { LabelProvider } from '@theia/core/lib/browser/label-provider';
22
- import { MessageClient } from '@theia/core/lib/common/message-service-protocol';
23
- import { OutputChannelManager } from '@theia/output/lib/browser/output-channel';
24
- import { DebugPreferences } from '@theia/debug/lib/common/debug-preferences';
25
- import { DebugConfigurationSessionOptions, TestRunReference } from '@theia/debug/lib/browser/debug-session-options';
26
- import { DebugSession } from '@theia/debug/lib/browser/debug-session';
19
+ import { DebugConfigurationSessionOptions } from '@theia/debug/lib/browser/debug-session-options';
20
+ import { DebugSession, DebugSessionData } from '@theia/debug/lib/browser/debug-session';
27
21
  import { DebugSessionConnection } from '@theia/debug/lib/browser/debug-session-connection';
28
22
  import { TerminalWidgetOptions, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
29
23
  import { TerminalOptionsExt } from '../../../common/plugin-api-rpc';
30
- import { FileService } from '@theia/filesystem/lib/browser/file-service';
31
- import { DebugContribution } from '@theia/debug/lib/browser/debug-contribution';
32
- import { ContributionProvider } from '@theia/core/lib/common/contribution-provider';
33
- import { WorkspaceService } from '@theia/workspace/lib/browser';
34
24
  import { PluginChannel } from '../../../common/connection';
35
- import { TestService } from '@theia/test/lib/browser/test-service';
36
- import { DebugSessionManager } from '@theia/debug/lib/browser/debug-session-manager';
37
- import { CommandService } from '@theia/core';
38
25
 
26
+ export const PluginTerminalOptionsExt = Symbol('PluginTerminalOptionsExt');
27
+
28
+ @injectable()
39
29
  export class PluginDebugSession extends DebugSession {
40
- constructor(
41
- override readonly id: string,
42
- override readonly options: DebugConfigurationSessionOptions,
43
- override readonly parentSession: DebugSession | undefined,
44
- testService: TestService,
45
- testRun: TestRunReference | undefined,
46
- sessionManager: DebugSessionManager,
47
- protected override readonly connection: DebugSessionConnection,
48
- protected override readonly terminalServer: TerminalService,
49
- protected override readonly editorManager: EditorManager,
50
- protected override readonly breakpoints: BreakpointManager,
51
- protected override readonly labelProvider: LabelProvider,
52
- protected override readonly messages: MessageClient,
53
- protected override readonly fileService: FileService,
54
- protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
55
- protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
56
- protected override readonly workspaceService: WorkspaceService,
57
- debugPreferences: DebugPreferences,
58
- protected override readonly commandService: CommandService) {
59
- super(id, options, parentSession, testService, testRun, sessionManager, connection, terminalServer, editorManager, breakpoints,
60
- labelProvider, messages, fileService, debugContributionProvider,
61
- workspaceService, debugPreferences, commandService);
30
+
31
+ static override createContainer(
32
+ parent: interfaces.Container, data: DebugSessionData, connection: DebugSessionConnection, terminalOptionsExt?: TerminalOptionsExt
33
+ ): interfaces.Container {
34
+ const child = DebugSession.createContainer(parent, data, connection);
35
+ child.rebind(DebugSession).to(PluginDebugSession);
36
+ child.bind(PluginTerminalOptionsExt).toConstantValue(terminalOptionsExt);
37
+ return child;
62
38
  }
63
39
 
40
+ @inject(PluginTerminalOptionsExt)
41
+ protected readonly terminalOptionsExt: TerminalOptionsExt | undefined;
42
+
64
43
  protected override async doCreateTerminal(terminalWidgetOptions: TerminalWidgetOptions): Promise<TerminalWidget> {
65
44
  terminalWidgetOptions = Object.assign({}, terminalWidgetOptions, this.terminalOptionsExt);
66
45
  return super.doCreateTerminal(terminalWidgetOptions);
@@ -73,49 +52,24 @@ export class PluginDebugSession extends DebugSession {
73
52
  */
74
53
  export class PluginDebugSessionFactory extends DefaultDebugSessionFactory {
75
54
  constructor(
76
- protected override readonly terminalService: TerminalService,
77
- protected override readonly editorManager: EditorManager,
78
- protected override readonly breakpoints: BreakpointManager,
79
- protected override readonly labelProvider: LabelProvider,
80
- protected override readonly messages: MessageClient,
81
- protected override readonly outputChannelManager: OutputChannelManager,
82
- protected override readonly debugPreferences: DebugPreferences,
83
55
  protected readonly connectionFactory: (sessionId: string) => Promise<PluginChannel>,
84
- protected override readonly fileService: FileService,
85
56
  protected readonly terminalOptionsExt: TerminalOptionsExt | undefined,
86
- protected override readonly debugContributionProvider: ContributionProvider<DebugContribution>,
87
- protected override readonly testService: TestService,
88
- protected override readonly workspaceService: WorkspaceService,
89
- protected override readonly commandService: CommandService,
57
+ container: interfaces.Container,
90
58
  ) {
91
- super();
59
+ super(container);
92
60
  }
93
61
 
94
- override get(manager: DebugSessionManager, sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession {
62
+ override createSession(sessionId: string, options: DebugConfigurationSessionOptions, parentSession?: DebugSession): DebugSession {
95
63
  const connection = new DebugSessionConnection(
96
64
  sessionId,
97
65
  this.connectionFactory,
98
66
  this.getTraceOutputChannel());
99
-
100
- return new PluginDebugSession(
101
- sessionId,
67
+ const data: DebugSessionData = {
68
+ id: sessionId,
102
69
  options,
103
70
  parentSession,
104
- this.testService,
105
- options.testRun,
106
- manager,
107
- connection,
108
- this.terminalService,
109
- this.editorManager,
110
- this.breakpoints,
111
- this.labelProvider,
112
- this.messages,
113
- this.fileService,
114
- this.terminalOptionsExt,
115
- this.debugContributionProvider,
116
- this.workspaceService,
117
- this.debugPreferences,
118
- this.commandService
119
- );
71
+ };
72
+ const child = PluginDebugSession.createContainer(this.container, data, connection, this.terminalOptionsExt);
73
+ return child.get(DebugSession);
120
74
  }
121
75
  }
@@ -39,7 +39,7 @@ import {
39
39
  IdentifiableInlineCompletions,
40
40
  HoverWithId
41
41
  } from '../../common/plugin-api-rpc';
42
- import { injectable, inject } from '@theia/core/shared/inversify';
42
+ import { injectable, inject, named } from '@theia/core/shared/inversify';
43
43
  import {
44
44
  SerializedDocumentFilter, MarkerData, Range, RelatedInformation,
45
45
  MarkerSeverity, DocumentLink, WorkspaceSymbolParams, CodeAction, CompletionDto,
@@ -80,12 +80,14 @@ import {
80
80
  EvaluatableExpressionProvider,
81
81
  InlineValue,
82
82
  InlineValueContext,
83
- InlineValuesProvider
83
+ InlineValuesProvider,
84
+ MultiDocumentHighlightProvider
84
85
  } from '@theia/monaco-editor-core/esm/vs/editor/common/languages';
85
86
  import { ITextModel } from '@theia/monaco-editor-core/esm/vs/editor/common/model';
86
87
  import { CodeActionTriggerKind } from '../../plugin/types-impl';
87
88
  import { IReadonlyVSDataTransfer } from '@theia/monaco-editor-core/esm/vs/base/common/dataTransfer';
88
89
  import { FileUploadService } from '@theia/filesystem/lib/common/upload/file-upload';
90
+ import { ILogger } from '@theia/core';
89
91
 
90
92
  @injectable()
91
93
  export class LanguagesMainImpl implements LanguagesMain, Disposable {
@@ -108,6 +110,9 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
108
110
  @inject(FileUploadService)
109
111
  protected readonly fileUploadService: FileUploadService;
110
112
 
113
+ @inject(ILogger) @named('plugin-ext:LanguagesMainImpl')
114
+ protected readonly logger: ILogger;
115
+
111
116
  private readonly proxy: LanguagesExt;
112
117
  private readonly services = new Map<number, Disposable>();
113
118
  private readonly toDispose = new DisposableCollection();
@@ -349,7 +354,8 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
349
354
 
350
355
  protected createHoverProvider(handle: number): monaco.languages.HoverProvider {
351
356
  return {
352
- provideHover: (model, position, token) => this.provideHover(handle, model, position, token)
357
+ provideHover: (model, position, token, context) =>
358
+ this.provideHover(handle, model, position, token, context as monaco.languages.HoverContext<HoverWithId> | undefined)
353
359
  };
354
360
  }
355
361
 
@@ -445,6 +451,45 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
445
451
  });
446
452
  }
447
453
 
454
+ $registerMultiDocumentHighlightProvider(handle: number, _pluginInfo: PluginInfo, selector: SerializedDocumentFilter[]): void {
455
+ const languageSelector = this.toLanguageSelector(selector);
456
+ const multiDocumentHighlightProvider = this.createMultiDocumentHighlightProvider(handle, languageSelector);
457
+ this.register(handle,
458
+ StandaloneServices.get(ILanguageFeaturesService).multiDocumentHighlightProvider.register(languageSelector, multiDocumentHighlightProvider));
459
+ }
460
+
461
+ protected createMultiDocumentHighlightProvider(handle: number, selector: LanguageSelector): MultiDocumentHighlightProvider {
462
+ return {
463
+ selector,
464
+ provideMultiDocumentHighlights: (model, position, otherModels, token) =>
465
+ // @monaco-uplift: cast due to ITextModel enum incompatibility between internal and standalone API types
466
+ this.provideMultiDocumentHighlights(handle, model as unknown as monaco.editor.ITextModel, position,
467
+ otherModels as unknown as monaco.editor.ITextModel[], token)
468
+ };
469
+ }
470
+
471
+ protected provideMultiDocumentHighlights(
472
+ handle: number, model: monaco.editor.ITextModel, position: monaco.Position,
473
+ otherModels: monaco.editor.ITextModel[], token: monaco.CancellationToken
474
+ ): monaco.languages.ProviderResult<Map<monaco.Uri, monaco.languages.DocumentHighlight[]>> {
475
+ const otherResources = otherModels.map(m => m.uri);
476
+ return this.proxy.$provideMultiDocumentHighlights(handle, model.uri, position, otherResources, token).then(result => {
477
+ if (!result) {
478
+ return undefined;
479
+ }
480
+ const map = new Map<monaco.Uri, monaco.languages.DocumentHighlight[]>();
481
+ for (const entry of result) {
482
+ const uri = monaco.Uri.revive(entry.uri);
483
+ const highlights: monaco.languages.DocumentHighlight[] = entry.highlights.map(item => ({
484
+ ...item,
485
+ kind: item.kind ?? monaco.languages.DocumentHighlightKind.Text
486
+ }));
487
+ map.set(uri, highlights);
488
+ }
489
+ return map;
490
+ });
491
+ }
492
+
448
493
  $registerWorkspaceSymbolProvider(handle: number, pluginInfo: PluginInfo): void {
449
494
  const workspaceSymbolProvider = this.createWorkspaceSymbolProvider(handle);
450
495
  this.register(handle, this.monacoLanguages.registerWorkspaceSymbolProvider(workspaceSymbolProvider));
@@ -958,7 +1003,7 @@ export class LanguagesMainImpl implements LanguagesMain, Disposable {
958
1003
  dispose: () => this.proxy.$releaseCodeActions(handle, actions.map(a => a.cacheId))
959
1004
  };
960
1005
  } catch (e) {
961
- console.error(e);
1006
+ this.logger.error(e);
962
1007
  return undefined;
963
1008
  }
964
1009
  }
@@ -64,7 +64,7 @@ export class McpServerDefinitionRegistryMainImpl implements McpServerDefinitionR
64
64
  try {
65
65
  const definitions = await this.$getServerDefinitions(handle);
66
66
  for (const definition of definitions) {
67
- this.mcpServerManager.removeServer(definition.label);
67
+ await this.mcpServerManager.removeServer(definition.label);
68
68
  }
69
69
  } catch (error) {
70
70
  console.error('Error getting server definitions for removal:', error);
@@ -107,7 +107,7 @@ export class McpServerDefinitionRegistryMainImpl implements McpServerDefinitionR
107
107
 
108
108
  for (const definition of definitions) {
109
109
  const mcpServerDescription = this.convertToMcpServerDescription(handle, definition);
110
- this.mcpServerManager.addOrUpdateServer(mcpServerDescription);
110
+ await this.mcpServerManager.addOrUpdateServer(mcpServerDescription);
111
111
  }
112
112
  } catch (error) {
113
113
  console.error('Error loading MCP server definitions:', error);
@@ -0,0 +1,126 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 EclipseSource GmbH 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 { expect } from 'chai';
18
+ import { LanguageModelToolsMainImpl } from './lm-tool-main';
19
+ import {
20
+ ToolResultTextPartDto,
21
+ ToolResultDataPartDto,
22
+ ToolResultPromptTsxPartDto,
23
+ ToolResultUnknownPartDto,
24
+ ToolResultPartDto,
25
+ uint8ArrayToBase64,
26
+ } from '../../common/lm-tool-protocol';
27
+ import { ToolCallContentResult } from '@theia/ai-core/lib/common/language-model';
28
+ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
29
+
30
+ // Access private methods for testing
31
+ interface LmToolMainInternals {
32
+ convertDtoToToolCallResult(part: ToolResultPartDto): ToolCallContentResult;
33
+ convertDataPart(base64: string, mimeType: string): ToolCallContentResult;
34
+ }
35
+
36
+ function createInstance(): LmToolMainInternals {
37
+ const impl = Object.create(LanguageModelToolsMainImpl.prototype) as LanguageModelToolsMainImpl;
38
+ return impl as unknown as LmToolMainInternals;
39
+ }
40
+
41
+ describe('LanguageModelToolsMainImpl - DTO to ToolCallContentResult conversion', () => {
42
+ let instance: LmToolMainInternals;
43
+
44
+ beforeEach(() => {
45
+ instance = createInstance();
46
+ });
47
+
48
+ describe('convertDtoToToolCallResult', () => {
49
+ it('should convert text part DTO to ToolCallTextResult', () => {
50
+ const part: ToolResultTextPartDto = { type: 'text', value: 'hello world' };
51
+ const result = instance.convertDtoToToolCallResult(part);
52
+ expect(result).to.deep.equal({ type: 'text', text: 'hello world' });
53
+ });
54
+
55
+ it('should convert prompt-tsx part DTO to ToolCallTextResult with JSON', () => {
56
+ const part: ToolResultPromptTsxPartDto = { type: 'prompt-tsx', value: { foo: 'bar' } };
57
+ const result = instance.convertDtoToToolCallResult(part);
58
+ expect(result).to.deep.equal({ type: 'text', text: '{"foo":"bar"}' });
59
+ });
60
+
61
+ it('should convert unknown part DTO to ToolCallTextResult', () => {
62
+ const part: ToolResultUnknownPartDto = { type: 'unknown', json: '{"custom":"data"}' };
63
+ const result = instance.convertDtoToToolCallResult(part);
64
+ expect(result).to.deep.equal({ type: 'text', text: '{"custom":"data"}' });
65
+ });
66
+
67
+ it('should convert data part with image mimeType to ToolCallImageResult', () => {
68
+ const base64 = btoa('fake-image-data');
69
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'image/png' };
70
+ const result = instance.convertDtoToToolCallResult(part);
71
+ expect(result).to.deep.equal({ type: 'image', base64data: base64, mimeType: 'image/png' });
72
+ });
73
+
74
+ it('should convert data part with audio mimeType to ToolCallAudioResult', () => {
75
+ const base64 = btoa('fake-audio-data');
76
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'audio/mp3' };
77
+ const result = instance.convertDtoToToolCallResult(part);
78
+ expect(result).to.deep.equal({ type: 'audio', data: base64, mimeType: 'audio/mp3' });
79
+ });
80
+
81
+ it('should convert data part with text/plain mimeType to ToolCallTextResult', () => {
82
+ const base64 = btoa('plain text content');
83
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'text/plain' };
84
+ const result = instance.convertDtoToToolCallResult(part);
85
+ expect(result).to.deep.equal({ type: 'text', text: 'plain text content' });
86
+ });
87
+
88
+ it('should convert data part with application/json mimeType to ToolCallTextResult', () => {
89
+ const jsonString = '{"key":"value"}';
90
+ const base64 = btoa(jsonString);
91
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'application/json' };
92
+ const result = instance.convertDtoToToolCallResult(part);
93
+ expect(result).to.deep.equal({ type: 'text', text: jsonString });
94
+ });
95
+
96
+ it('should convert data part with text/x-json mimeType to ToolCallTextResult', () => {
97
+ const jsonString = '{"data":123}';
98
+ const base64 = btoa(jsonString);
99
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'text/x-json' };
100
+ const result = instance.convertDtoToToolCallResult(part);
101
+ expect(result).to.deep.equal({ type: 'text', text: jsonString });
102
+ });
103
+
104
+ it('should convert data part with image/jpeg mimeType to ToolCallImageResult', () => {
105
+ const base64 = btoa('jpeg-data');
106
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'image/jpeg' };
107
+ const result = instance.convertDtoToToolCallResult(part);
108
+ expect(result).to.deep.equal({ type: 'image', base64data: base64, mimeType: 'image/jpeg' });
109
+ });
110
+
111
+ it('should convert data part with audio/wav mimeType to ToolCallAudioResult', () => {
112
+ const base64 = btoa('wav-data');
113
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'audio/wav' };
114
+ const result = instance.convertDtoToToolCallResult(part);
115
+ expect(result).to.deep.equal({ type: 'audio', data: base64, mimeType: 'audio/wav' });
116
+ });
117
+
118
+ it('should correctly decode UTF-8 text from base64 data part', () => {
119
+ const utf8Text = 'café 日本語';
120
+ const base64 = uint8ArrayToBase64(BinaryBuffer.fromString(utf8Text).buffer);
121
+ const part: ToolResultDataPartDto = { type: 'data', base64, mimeType: 'text/plain' };
122
+ const result = instance.convertDtoToToolCallResult(part);
123
+ expect(result).to.deep.equal({ type: 'text', text: utf8Text });
124
+ });
125
+ });
126
+ });
@@ -0,0 +1,116 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 EclipseSource
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 { interfaces } from '@theia/core/shared/inversify';
18
+ import { RPCProtocol } from '../../common/rpc-protocol';
19
+ import {
20
+ LanguageModelToolsMain,
21
+ LanguageModelToolsExt,
22
+ LanguageModelToolDto,
23
+ ToolResultPartDto,
24
+ isToolInvocationError,
25
+ base64ToUint8Array,
26
+ } from '../../common/lm-tool-protocol';
27
+ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
28
+ import { MAIN_RPC_CONTEXT } from '../../common/plugin-api-rpc';
29
+ import { ToolInvocationRegistry } from '@theia/ai-core/lib/common';
30
+ import { ToolRequest, ToolRequestParameters, ToolInvocationContext, ToolCallContent, ToolCallContentResult, createToolCallError } from '@theia/ai-core/lib/common/language-model';
31
+
32
+ export class LanguageModelToolsMainImpl implements LanguageModelToolsMain {
33
+ private readonly proxy: LanguageModelToolsExt;
34
+ private readonly toolInvocationRegistry: ToolInvocationRegistry | undefined;
35
+ private readonly toolHandleToName = new Map<number, string>();
36
+
37
+ constructor(
38
+ rpc: RPCProtocol,
39
+ container: interfaces.Container
40
+ ) {
41
+ this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.LM_TOOLS_EXT);
42
+ try {
43
+ this.toolInvocationRegistry = container.get(ToolInvocationRegistry);
44
+ } catch {
45
+ this.toolInvocationRegistry = undefined;
46
+ }
47
+ }
48
+
49
+ async $registerTool(handle: number, name: string, metadata: LanguageModelToolDto, pluginId: string): Promise<void> {
50
+ if (!this.toolInvocationRegistry) {
51
+ console.warn('ToolInvocationRegistry not available - tool will not be registered');
52
+ return;
53
+ }
54
+ if (this.toolInvocationRegistry.getFunction(name)) {
55
+ throw new Error(`Tool '${name}' is already registered.`);
56
+ }
57
+ const parameters: ToolRequestParameters = ToolRequest.isToolRequestParameters(metadata.inputSchema)
58
+ ? metadata.inputSchema as ToolRequestParameters
59
+ : { type: 'object', properties: {} };
60
+ // Note: metadata.tags is not forwarded to ToolRequest as ToolRequest has no tags field.
61
+ // Tags are exposed to extensions on the Ext side via lm.tools (LanguageModelToolInformation).
62
+ const toolRequest: ToolRequest = {
63
+ id: name,
64
+ name: name,
65
+ providerName: `plugin_lm_tools_${pluginId}`,
66
+ description: metadata.description,
67
+ parameters,
68
+ handler: async (argString: string, ctx?: ToolInvocationContext): Promise<ToolCallContent> => {
69
+ const result = await this.proxy.$invokeTool(handle, argString, ctx?.cancellationToken);
70
+ if (isToolInvocationError(result)) {
71
+ return createToolCallError(result.error);
72
+ }
73
+ const content: ToolCallContentResult[] = result.content.map(part => this.convertDtoToToolCallResult(part));
74
+ return { content };
75
+ }
76
+ };
77
+ this.toolHandleToName.set(handle, name);
78
+ this.toolInvocationRegistry.registerTool(toolRequest);
79
+ }
80
+
81
+ $unregisterTool(handle: number): void {
82
+ if (!this.toolInvocationRegistry) {
83
+ return;
84
+ }
85
+ const name = this.toolHandleToName.get(handle);
86
+ if (name) {
87
+ this.toolInvocationRegistry.unregisterTool(name);
88
+ this.toolHandleToName.delete(handle);
89
+ }
90
+ }
91
+
92
+ private convertDtoToToolCallResult(part: ToolResultPartDto): ToolCallContentResult {
93
+ switch (part.type) {
94
+ case 'text':
95
+ return { type: 'text', text: part.value };
96
+ case 'data':
97
+ return this.convertDataPart(part.base64, part.mimeType);
98
+ case 'prompt-tsx':
99
+ return { type: 'text', text: JSON.stringify(part.value) };
100
+ case 'unknown':
101
+ return { type: 'text', text: part.json };
102
+ }
103
+ }
104
+
105
+ private convertDataPart(base64: string, mimeType: string): ToolCallContentResult {
106
+ if (mimeType.startsWith('image/')) {
107
+ return { type: 'image', base64data: base64, mimeType };
108
+ }
109
+ if (mimeType.startsWith('audio/')) {
110
+ return { type: 'audio', data: base64, mimeType };
111
+ }
112
+ // Text-like MIME types: decode base64 to string
113
+ const decoded = BinaryBuffer.wrap(base64ToUint8Array(base64)).toString();
114
+ return { type: 'text', text: decoded };
115
+ }
116
+ }
@@ -67,6 +67,7 @@ import { TestingMainImpl } from './test-main';
67
67
  import { UriMainImpl } from './uri-main';
68
68
  import { LoggerMainImpl } from './logger-main';
69
69
  import { McpServerDefinitionRegistryMainImpl } from './lm-main';
70
+ import { LanguageModelToolsMainImpl } from './lm-tool-main';
70
71
 
71
72
  export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container): void {
72
73
  const loggerMain = new LoggerMainImpl(container);
@@ -211,4 +212,7 @@ export function setUpPluginApi(rpc: RPCProtocol, container: interfaces.Container
211
212
 
212
213
  const mcpServerDefinitionRegistryMain = new McpServerDefinitionRegistryMainImpl(rpc, container);
213
214
  rpc.set(PLUGIN_RPC_CONTEXT.MCP_SERVER_DEFINITION_REGISTRY_MAIN, mcpServerDefinitionRegistryMain);
215
+
216
+ const languageModelToolsMain = new LanguageModelToolsMainImpl(rpc, container);
217
+ rpc.set(PLUGIN_RPC_CONTEXT.LM_TOOLS_MAIN, languageModelToolsMain);
214
218
  }
@@ -17,9 +17,12 @@
17
17
  import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom';
18
18
  const disableJSDOM = enableJSDOM();
19
19
 
20
+ import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
21
+ FrontendApplicationConfigProvider.set({});
22
+
20
23
  import * as assert from 'assert';
24
+ import { URI } from '@theia/core';
21
25
  import { Disposable } from '@theia/core/lib/common/disposable';
22
- import { WatchOptions } from '@theia/filesystem/lib/common/files';
23
26
  import { UriComponents } from '../../common/uri-components';
24
27
  import { MainFileSystemEventService } from './main-file-system-event-service';
25
28
 
@@ -27,52 +30,96 @@ disableJSDOM();
27
30
 
28
31
  /* eslint-disable @typescript-eslint/no-explicit-any */
29
32
 
30
- // A VS Code plugin (e.g. `redhat.java` hosting JDT-LS) that calls
31
- // `vscode.workspace.createFileSystemWatcher` with a `RelativePattern` rooted at an absolute base
32
- // triggers `ExtHostFileSystemEventService.ensureWatching`, which sends `$watch` to this main-side
33
- // service with an EMPTY `excludes` list. As a result the user's `files.watcherExclude` preference
34
- // was never applied to these (often sibling-of-workspace) recursive watches, so they crawled and
35
- // watched whole external trees, exhausting the OS file-watch budget.
33
+ // A language server (e.g. `redhat.java` / JDT-LS) registers a watcher rooted at the PARENT of the
34
+ // workspace folder - `RelativePattern(parentDir, folderName)` - purely to detect deletion of the
35
+ // workspace folder itself. The pattern has no globstar, so `ensureWatching` sends it as a
36
+ // NON-recursive `$watch` on `parentDir`. Theia's backend ignores the `recursive` flag and always
37
+ // watches recursively, so this turns into a recursive crawl of every sibling subtree under the
38
+ // parent - thousands of inodes the workspace does not own - which can exhaust the OS file-watch
39
+ // budget. `files.watcherExclude` cannot bound it because the root is outside the workspace.
36
40
  //
37
- // This test pins the fix: the main side must merge the configured `files.watcherExclude` patterns
38
- // into the watch options before delegating to the `FileService`.
39
- describe('MainFileSystemEventService files.watcherExclude handling', () => {
41
+ // These tests pin the fix: a non-recursive watch rooted at a strict ancestor of a workspace root is
42
+ // not registered, while watches on/inside the workspace and explicit recursive requests are
43
+ // untouched.
44
+ describe('MainFileSystemEventService ancestor-of-workspace watch handling', () => {
40
45
 
41
46
  function componentsFor(path: string): UriComponents {
42
47
  return { scheme: 'file', authority: '', path, query: '', fragment: '' };
43
48
  }
44
49
 
45
- it('applies files.watcherExclude to plugin-created watches that arrive with empty excludes', () => {
46
- const watchCalls: WatchOptions[] = [];
47
-
50
+ function createService(rootUris: string[], watchCalls: UriComponents[]): MainFileSystemEventService {
48
51
  const fileService: any = {
49
52
  onDidFilesChange: () => Disposable.NULL,
50
53
  onDidRunUserOperation: () => Disposable.NULL,
51
54
  addFileOperationParticipant: () => Disposable.NULL,
52
- watch: (_resource: any, options: WatchOptions) => {
53
- watchCalls.push(options);
55
+ watch: (resource: UriComponents) => {
56
+ watchCalls.push(resource);
54
57
  return Disposable.NULL;
55
58
  }
56
59
  };
60
+ const workspaceService: any = { tryGetRoots: () => rootUris.map(uri => ({ resource: new URI(uri) })) };
61
+ const rpc: any = { getProxy: () => ({}) };
62
+ return new MainFileSystemEventService(rpc, {} as any, fileService, workspaceService);
63
+ }
57
64
 
58
- const preferences: any = {
59
- get: (preferenceName: string) => preferenceName === 'files.watcherExclude'
60
- ? { '**/node_modules/**': true, '**/.git/objects/**': true, '**/disabled-exclude/**': false }
61
- : undefined
62
- };
65
+ it('skips a non-recursive watch rooted at a strict ancestor of a workspace root', () => {
66
+ const watchCalls: UriComponents[] = [];
67
+ const service = createService(['file:///projects/my-app'], watchCalls);
63
68
 
64
- const rpc: any = { getProxy: () => ({}) };
69
+ service.$watch(1, componentsFor('/projects'), { recursive: false, excludes: [] });
70
+
71
+ assert.strictEqual(watchCalls.length, 0, 'ancestor-of-workspace watch must not be registered');
72
+ });
73
+
74
+ it('still registers a non-recursive watch on the workspace root itself', () => {
75
+ const watchCalls: UriComponents[] = [];
76
+ const service = createService(['file:///projects/my-app'], watchCalls);
77
+
78
+ service.$watch(1, componentsFor('/projects/my-app'), { recursive: false, excludes: [] });
79
+
80
+ assert.strictEqual(watchCalls.length, 1);
81
+ });
82
+
83
+ it('still registers a non-recursive watch on an outer root that is itself the parent of another root', () => {
84
+ const watchCalls: UriComponents[] = [];
85
+ // Multi-root workspace where `/projects` is a root AND the parent of the `/projects/my-app` root.
86
+ // The outer root is explicitly opened by the user, so its watch must not be dropped as an
87
+ // "ancestor of the workspace".
88
+ const service = createService(['file:///projects', 'file:///projects/my-app'], watchCalls);
89
+
90
+ service.$watch(1, componentsFor('/projects'), { recursive: false, excludes: [] });
91
+
92
+ assert.strictEqual(watchCalls.length, 1, 'a folder that is itself a workspace root must be watched, even if it is an ancestor of another root');
93
+ });
94
+
95
+ it('still registers a non-recursive watch inside the workspace', () => {
96
+ const watchCalls: UriComponents[] = [];
97
+ const service = createService(['file:///projects/my-app'], watchCalls);
98
+
99
+ service.$watch(1, componentsFor('/projects/my-app/src'), { recursive: false, excludes: [] });
100
+
101
+ assert.strictEqual(watchCalls.length, 1);
102
+ });
103
+
104
+ it('does not skip an explicit recursive watch on an ancestor (honored as requested)', () => {
105
+ const watchCalls: UriComponents[] = [];
106
+ const service = createService(['file:///projects/my-app'], watchCalls);
107
+
108
+ service.$watch(1, componentsFor('/projects'), { recursive: true, excludes: [] });
109
+
110
+ assert.strictEqual(watchCalls.length, 1);
111
+ });
65
112
 
66
- const service = new MainFileSystemEventService(rpc, {} as any, fileService, preferences);
113
+ it('frees the session for a skipped watch so $unwatch and re-watch do not throw', () => {
114
+ const watchCalls: UriComponents[] = [];
115
+ const service = createService(['file:///projects/my-app'], watchCalls);
67
116
 
68
- // Mirrors what `ensureWatching` sends for an absolute RelativePattern base outside the workspace.
69
- service.$watch(1, componentsFor('/outside/workspace/storage'), { recursive: true, excludes: [] });
117
+ service.$watch(1, componentsFor('/projects'), { recursive: false, excludes: [] });
118
+ service.$unwatch(1);
119
+ // Re-using the same session id must not throw "already a watch request".
120
+ service.$watch(1, componentsFor('/projects'), { recursive: false, excludes: [] });
70
121
 
71
- assert.strictEqual(watchCalls.length, 1, 'FileService.watch should have been called once');
72
- const excludes = watchCalls[0].excludes;
73
- assert.ok(excludes.includes('**/node_modules/**'), 'enabled watcherExclude pattern should be applied');
74
- assert.ok(excludes.includes('**/.git/objects/**'), 'enabled watcherExclude pattern should be applied');
75
- assert.ok(!excludes.includes('**/disabled-exclude/**'), 'patterns set to `false` must not be applied');
122
+ assert.strictEqual(watchCalls.length, 0);
76
123
  });
77
124
 
78
125
  });