@theia/plugin-ext 1.34.0 → 1.35.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 (123) hide show
  1. package/lib/common/plugin-api-rpc-model.d.ts +18 -1
  2. package/lib/common/plugin-api-rpc-model.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc-model.js.map +1 -1
  4. package/lib/common/plugin-api-rpc.d.ts +11 -6
  5. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  6. package/lib/common/plugin-api-rpc.js.map +1 -1
  7. package/lib/common/plugin-protocol.d.ts +30 -0
  8. package/lib/common/plugin-protocol.d.ts.map +1 -1
  9. package/lib/common/plugin-protocol.js.map +1 -1
  10. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  11. package/lib/hosted/browser/hosted-plugin.js +10 -17
  12. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  13. package/lib/hosted/node/hosted-plugin-process.js.map +1 -1
  14. package/lib/hosted/node/plugin-activation-events.d.ts +7 -0
  15. package/lib/hosted/node/plugin-activation-events.d.ts.map +1 -0
  16. package/lib/hosted/node/plugin-activation-events.js +96 -0
  17. package/lib/hosted/node/plugin-activation-events.js.map +1 -0
  18. package/lib/hosted/node/plugin-manifest-loader.d.ts +2 -1
  19. package/lib/hosted/node/plugin-manifest-loader.d.ts.map +1 -1
  20. package/lib/hosted/node/plugin-manifest-loader.js +2 -1
  21. package/lib/hosted/node/plugin-manifest-loader.js.map +1 -1
  22. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  23. package/lib/hosted/node/scanners/scanner-theia.js +27 -36
  24. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  25. package/lib/main/browser/data-transfer/data-transfer-type-converters.d.ts +9 -0
  26. package/lib/main/browser/data-transfer/data-transfer-type-converters.d.ts.map +1 -0
  27. package/lib/main/browser/data-transfer/data-transfer-type-converters.js +65 -0
  28. package/lib/main/browser/data-transfer/data-transfer-type-converters.js.map +1 -0
  29. package/lib/main/browser/dialogs/modal-notification.d.ts.map +1 -1
  30. package/lib/main/browser/dialogs/modal-notification.js +4 -2
  31. package/lib/main/browser/dialogs/modal-notification.js.map +1 -1
  32. package/lib/main/browser/languages-main.d.ts +8 -1
  33. package/lib/main/browser/languages-main.d.ts.map +1 -1
  34. package/lib/main/browser/languages-main.js +26 -2
  35. package/lib/main/browser/languages-main.js.map +1 -1
  36. package/lib/main/browser/main-context.d.ts.map +1 -1
  37. package/lib/main/browser/main-context.js +3 -0
  38. package/lib/main/browser/main-context.js.map +1 -1
  39. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  40. package/lib/main/browser/plugin-ext-frontend-module.js +3 -0
  41. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  42. package/lib/main/browser/tabs/tabs-main.d.ts +33 -2
  43. package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
  44. package/lib/main/browser/tabs/tabs-main.js +256 -6
  45. package/lib/main/browser/tabs/tabs-main.js.map +1 -1
  46. package/lib/main/browser/text-editors-main.d.ts +2 -2
  47. package/lib/main/browser/text-editors-main.d.ts.map +1 -1
  48. package/lib/main/browser/text-editors-main.js +2 -2
  49. package/lib/main/browser/text-editors-main.js.map +1 -1
  50. package/lib/main/browser/webview/webview-context-keys.d.ts +13 -0
  51. package/lib/main/browser/webview/webview-context-keys.d.ts.map +1 -0
  52. package/lib/main/browser/webview/webview-context-keys.js +64 -0
  53. package/lib/main/browser/webview/webview-context-keys.js.map +1 -0
  54. package/lib/plugin/dialogs.js +2 -2
  55. package/lib/plugin/dialogs.js.map +1 -1
  56. package/lib/plugin/file-system-ext-impl.d.ts +11 -5
  57. package/lib/plugin/file-system-ext-impl.d.ts.map +1 -1
  58. package/lib/plugin/file-system-ext-impl.js +8 -16
  59. package/lib/plugin/file-system-ext-impl.js.map +1 -1
  60. package/lib/plugin/known-commands.d.ts.map +1 -1
  61. package/lib/plugin/known-commands.js +13 -0
  62. package/lib/plugin/known-commands.js.map +1 -1
  63. package/lib/plugin/languages/document-drop-edit.d.ts +16 -0
  64. package/lib/plugin/languages/document-drop-edit.d.ts.map +1 -0
  65. package/lib/plugin/languages/document-drop-edit.js +23 -0
  66. package/lib/plugin/languages/document-drop-edit.js.map +1 -0
  67. package/lib/plugin/languages.d.ts +9 -2
  68. package/lib/plugin/languages.d.ts.map +1 -1
  69. package/lib/plugin/languages.js +27 -1
  70. package/lib/plugin/languages.js.map +1 -1
  71. package/lib/plugin/plugin-context.d.ts.map +1 -1
  72. package/lib/plugin/plugin-context.js +8 -4
  73. package/lib/plugin/plugin-context.js.map +1 -1
  74. package/lib/plugin/preference-registry.d.ts +5 -3
  75. package/lib/plugin/preference-registry.d.ts.map +1 -1
  76. package/lib/plugin/preference-registry.js +31 -42
  77. package/lib/plugin/preference-registry.js.map +1 -1
  78. package/lib/plugin/preference-registry.spec.js +29 -41
  79. package/lib/plugin/preference-registry.spec.js.map +1 -1
  80. package/lib/plugin/tabs.d.ts.map +1 -1
  81. package/lib/plugin/tabs.js +10 -13
  82. package/lib/plugin/tabs.js.map +1 -1
  83. package/lib/plugin/text-editors.d.ts +1 -1
  84. package/lib/plugin/text-editors.d.ts.map +1 -1
  85. package/lib/plugin/text-editors.js +2 -2
  86. package/lib/plugin/text-editors.js.map +1 -1
  87. package/lib/plugin/type-converters.d.ts +6 -0
  88. package/lib/plugin/type-converters.d.ts.map +1 -1
  89. package/lib/plugin/type-converters.js +37 -1
  90. package/lib/plugin/type-converters.js.map +1 -1
  91. package/lib/plugin/types-impl.d.ts +5 -0
  92. package/lib/plugin/types-impl.d.ts.map +1 -1
  93. package/lib/plugin/types-impl.js +13 -3
  94. package/lib/plugin/types-impl.js.map +1 -1
  95. package/package.json +27 -26
  96. package/src/common/plugin-api-rpc-model.ts +22 -1
  97. package/src/common/plugin-api-rpc.ts +19 -6
  98. package/src/common/plugin-protocol.ts +32 -4
  99. package/src/hosted/browser/hosted-plugin.ts +9 -16
  100. package/src/hosted/node/hosted-plugin-process.ts +2 -2
  101. package/src/hosted/node/plugin-activation-events.ts +111 -0
  102. package/src/hosted/node/plugin-manifest-loader.ts +4 -3
  103. package/src/hosted/node/scanners/scanner-theia.ts +59 -75
  104. package/src/main/browser/data-transfer/data-transfer-type-converters.ts +70 -0
  105. package/src/main/browser/dialogs/modal-notification.ts +4 -2
  106. package/src/main/browser/languages-main.ts +34 -4
  107. package/src/main/browser/main-context.ts +4 -0
  108. package/src/main/browser/plugin-ext-frontend-module.ts +3 -0
  109. package/src/main/browser/tabs/tabs-main.ts +287 -6
  110. package/src/main/browser/text-editors-main.ts +3 -2
  111. package/src/main/browser/webview/webview-context-keys.ts +49 -0
  112. package/src/plugin/dialogs.ts +2 -2
  113. package/src/plugin/file-system-ext-impl.ts +8 -18
  114. package/src/plugin/known-commands.ts +16 -1
  115. package/src/plugin/languages/document-drop-edit.ts +44 -0
  116. package/src/plugin/languages.ts +41 -3
  117. package/src/plugin/plugin-context.ts +9 -4
  118. package/src/plugin/preference-registry.spec.ts +29 -45
  119. package/src/plugin/preference-registry.ts +33 -45
  120. package/src/plugin/tabs.ts +10 -14
  121. package/src/plugin/text-editors.ts +2 -2
  122. package/src/plugin/type-converters.ts +37 -0
  123. package/src/plugin/types-impl.ts +11 -0
@@ -15,16 +15,275 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { interfaces } from '@theia/core/shared/inversify';
18
-
19
- import { TabsMain } from '../../../common/plugin-api-rpc';
18
+ import { ApplicationShell, PINNED_CLASS, Saveable, TabBar, Title, ViewContainer, Widget } from '@theia/core/lib/browser';
19
+ import { AnyInputDto, MAIN_RPC_CONTEXT, TabDto, TabGroupDto, TabInputKind, TabModelOperationKind, TabsExt, TabsMain } from '../../../common/plugin-api-rpc';
20
20
  import { RPCProtocol } from '../../../common/rpc-protocol';
21
+ import { EditorPreviewWidget } from '@theia/editor-preview/lib/browser/editor-preview-widget';
22
+ import { Disposable } from '@theia/core/shared/vscode-languageserver-protocol';
23
+ import { MonacoDiffEditor } from '@theia/monaco/lib/browser/monaco-diff-editor';
24
+ import { toUriComponents } from '../hierarchy/hierarchy-types-converters';
25
+ import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
26
+ import { DisposableCollection } from '@theia/core';
27
+
28
+ interface TabInfo {
29
+ tab: TabDto;
30
+ tabIndex: number;
31
+ group: TabGroupDto;
32
+ }
33
+
34
+ export class TabsMainImpl implements TabsMain, Disposable {
35
+
36
+ private readonly proxy: TabsExt;
37
+ private tabGroupModel = new Map<TabBar<Widget>, TabGroupDto>();
38
+ private tabInfoLookup = new Map<Title<Widget>, TabInfo>();
39
+
40
+ private applicationShell: ApplicationShell;
41
+
42
+ private disposableTabBarListeners: DisposableCollection = new DisposableCollection();
43
+ private toDisposeOnDestroy: DisposableCollection = new DisposableCollection();
21
44
 
22
- export class TabsMainImp implements TabsMain {
45
+ private groupIdCounter = 0;
46
+ private currentActiveGroup: TabGroupDto;
47
+
48
+ private tabGroupChanged: boolean = false;
23
49
 
24
50
  constructor(
25
51
  rpc: RPCProtocol,
26
52
  container: interfaces.Container
27
- ) {}
53
+ ) {
54
+ this.proxy = rpc.getProxy(MAIN_RPC_CONTEXT.TABS_EXT);
55
+
56
+ this.applicationShell = container.get(ApplicationShell);
57
+ this.createTabsModel();
58
+
59
+ const tabBars = this.applicationShell.mainPanel.tabBars();
60
+ for (let tabBar; tabBar = tabBars.next();) {
61
+ this.attachListenersToTabBar(tabBar);
62
+ }
63
+
64
+ this.toDisposeOnDestroy.push(
65
+ this.applicationShell.mainPanelRenderer.onDidCreateTabBar(tabBar => {
66
+ this.attachListenersToTabBar(tabBar);
67
+ this.onTabGroupCreated(tabBar);
68
+ })
69
+ );
70
+
71
+ this.connectToSignal(this.toDisposeOnDestroy, this.applicationShell.mainPanel.widgetAdded, (mainPanel, widget) => {
72
+ if (this.tabGroupChanged || this.tabGroupModel.size === 0) {
73
+ this.tabGroupChanged = false;
74
+ this.createTabsModel();
75
+ // tab Open event is done in backend
76
+ } else {
77
+ const tabBar = mainPanel.findTabBar(widget.title)!;
78
+ const oldTabInfo = this.tabInfoLookup.get(widget.title);
79
+ const group = this.tabGroupModel.get(tabBar);
80
+ if (group !== oldTabInfo?.group) {
81
+ if (oldTabInfo) {
82
+ this.onTabClosed(oldTabInfo, widget.title);
83
+ }
84
+
85
+ this.onTabCreated(tabBar, { index: tabBar.titles.indexOf(widget.title), title: widget.title });
86
+ }
87
+ }
88
+ });
89
+
90
+ this.connectToSignal(this.toDisposeOnDestroy, this.applicationShell.mainPanel.widgetRemoved, (mainPanel, widget) => {
91
+ if (!(widget instanceof TabBar)) {
92
+ const tabInfo = this.getOrRebuildModel(this.tabInfoLookup, widget.title)!;
93
+ this.onTabClosed(tabInfo, widget.title);
94
+ if (this.tabGroupChanged) {
95
+ this.tabGroupChanged = false;
96
+ this.createTabsModel();
97
+ }
98
+ }
99
+ });
100
+ }
101
+
102
+ protected createTabsModel(): void {
103
+ const newTabGroupModel = new Map<TabBar<Widget>, TabGroupDto>();
104
+ this.tabInfoLookup.clear();
105
+ this.disposableTabBarListeners.dispose();
106
+ this.applicationShell.mainAreaTabBars.forEach(tabBar => {
107
+ this.attachListenersToTabBar(tabBar);
108
+ const groupDto = this.createTabGroupDto(tabBar);
109
+ tabBar.titles.forEach((title, index) => this.tabInfoLookup.set(title, { group: groupDto, tab: groupDto.tabs[index], tabIndex: index }));
110
+ newTabGroupModel.set(tabBar, groupDto);
111
+ });
112
+ if (newTabGroupModel.size > 0 && Array.from(newTabGroupModel.values()).indexOf(this.currentActiveGroup) < 0) {
113
+ this.currentActiveGroup = this.tabInfoLookup.get(this.applicationShell.mainPanel.currentTitle!)?.group ?? newTabGroupModel.values().next().value;
114
+ this.currentActiveGroup.isActive = true;
115
+ }
116
+ this.tabGroupModel = newTabGroupModel;
117
+ this.proxy.$acceptEditorTabModel(Array.from(this.tabGroupModel.values()));
118
+ }
119
+
120
+ protected createTabDto(tabTitle: Title<Widget>, groupId: number): TabDto {
121
+ const widget = tabTitle.owner;
122
+ return {
123
+ id: this.createTabId(tabTitle, groupId),
124
+ label: tabTitle.label,
125
+ input: this.evaluateTabDtoInput(widget),
126
+ isActive: tabTitle.owner.isVisible,
127
+ isPinned: tabTitle.className.includes(PINNED_CLASS),
128
+ isDirty: Saveable.isDirty(widget),
129
+ isPreview: widget instanceof EditorPreviewWidget && widget.isPreview
130
+ };
131
+ }
132
+
133
+ protected createTabId(tabTitle: Title<Widget>, groupId: number): string {
134
+ return `${groupId}~${tabTitle.owner.id}`;
135
+ }
136
+
137
+ protected createTabGroupDto(tabBar: TabBar<Widget>): TabGroupDto {
138
+ const oldDto = this.tabGroupModel.get(tabBar);
139
+ const groupId = oldDto?.groupId ?? this.groupIdCounter++;
140
+ const tabs = tabBar.titles.map(title => this.createTabDto(title, groupId));
141
+ return {
142
+ groupId,
143
+ tabs,
144
+ isActive: false,
145
+ viewColumn: 1
146
+ };
147
+ }
148
+
149
+ protected attachListenersToTabBar(tabBar: TabBar<Widget> | undefined): void {
150
+ if (!tabBar) {
151
+ return;
152
+ }
153
+ tabBar.titles.forEach(title => {
154
+ this.connectToSignal(this.disposableTabBarListeners, title.changed, this.onTabTitleChanged);
155
+ });
156
+
157
+ this.connectToSignal(this.disposableTabBarListeners, tabBar.tabMoved, this.onTabMoved);
158
+ this.connectToSignal(this.disposableTabBarListeners, tabBar.disposed, this.onTabGroupClosed);
159
+ }
160
+
161
+ protected evaluateTabDtoInput(widget: Widget): AnyInputDto {
162
+ if (widget instanceof EditorPreviewWidget) {
163
+ if (widget.editor instanceof MonacoDiffEditor) {
164
+ return {
165
+ kind: TabInputKind.TextDiffInput,
166
+ original: toUriComponents(widget.editor.originalModel.uri),
167
+ modified: toUriComponents(widget.editor.modifiedModel.uri)
168
+ };
169
+ } else {
170
+ return {
171
+ kind: TabInputKind.TextInput,
172
+ uri: toUriComponents(widget.editor.uri.toString())
173
+ };
174
+ }
175
+ // TODO notebook support when implemented
176
+ } else if (widget instanceof ViewContainer) {
177
+ return {
178
+ kind: TabInputKind.WebviewEditorInput,
179
+ viewType: widget.id
180
+ };
181
+ } else if (widget instanceof TerminalWidget) {
182
+ return {
183
+ kind: TabInputKind.TerminalEditorInput
184
+ };
185
+ }
186
+
187
+ return { kind: TabInputKind.UnknownInput };
188
+ }
189
+
190
+ protected connectToSignal<T>(disposableList: DisposableCollection, signal: { connect(listener: T, context: unknown): void, disconnect(listener: T): void }, listener: T): void {
191
+ signal.connect(listener, this);
192
+ disposableList.push(Disposable.create(() => signal.disconnect(listener)));
193
+ }
194
+
195
+ protected tabDtosEqual(a: TabDto, b: TabDto): boolean {
196
+ return a.isActive === b.isActive &&
197
+ a.isDirty === b.isDirty &&
198
+ a.isPinned === b.isPinned &&
199
+ a.isPreview === b.isPreview &&
200
+ a.id === b.id;
201
+ }
202
+
203
+ protected getOrRebuildModel<T, R>(map: Map<T, R>, key: T): R {
204
+ // something broke so we rebuild the model
205
+ let item = map.get(key);
206
+ if (!item) {
207
+ this.createTabsModel();
208
+ item = map.get(key)!;
209
+ }
210
+ return item;
211
+ }
212
+
213
+ // #region event listeners
214
+ private onTabCreated(tabBar: TabBar<Widget>, args: TabBar.ITabActivateRequestedArgs<Widget>): void {
215
+ const group = this.getOrRebuildModel(this.tabGroupModel, tabBar);
216
+ this.connectToSignal(this.disposableTabBarListeners, args.title.changed, this.onTabTitleChanged);
217
+ const tabDto = this.createTabDto(args.title, group.groupId);
218
+ this.tabInfoLookup.set(args.title, { group, tab: tabDto, tabIndex: args.index });
219
+ group.tabs.splice(args.index, 0, tabDto);
220
+ this.proxy.$acceptTabOperation({
221
+ kind: TabModelOperationKind.TAB_OPEN,
222
+ index: args.index,
223
+ tabDto,
224
+ groupId: group.groupId
225
+ });
226
+ }
227
+
228
+ private onTabTitleChanged(title: Title<Widget>): void {
229
+ const tabInfo = this.getOrRebuildModel(this.tabInfoLookup, title);
230
+ if (!tabInfo) {
231
+ return;
232
+ }
233
+ const oldTabDto = tabInfo.tab;
234
+ const newTabDto = this.createTabDto(title, tabInfo.group.groupId);
235
+ if (newTabDto.isActive && !tabInfo.group.isActive) {
236
+ tabInfo.group.isActive = true;
237
+ this.currentActiveGroup.isActive = false;
238
+ this.currentActiveGroup = tabInfo.group;
239
+ this.proxy.$acceptTabGroupUpdate(tabInfo.group);
240
+ }
241
+ if (!this.tabDtosEqual(oldTabDto, newTabDto)) {
242
+ tabInfo.group.tabs[tabInfo.tabIndex] = newTabDto;
243
+ tabInfo.tab = newTabDto;
244
+ this.proxy.$acceptTabOperation({
245
+ kind: TabModelOperationKind.TAB_UPDATE,
246
+ index: tabInfo.tabIndex,
247
+ tabDto: newTabDto,
248
+ groupId: tabInfo.group.groupId
249
+ });
250
+ }
251
+ }
252
+
253
+ private onTabClosed(tabInfo: TabInfo, title: Title<Widget>): void {
254
+ tabInfo.group.tabs.splice(tabInfo.tabIndex, 1);
255
+ this.tabInfoLookup.delete(title);
256
+ this.proxy.$acceptTabOperation({
257
+ kind: TabModelOperationKind.TAB_CLOSE,
258
+ index: tabInfo.tabIndex,
259
+ tabDto: this.createTabDto(title, tabInfo.group.groupId),
260
+ groupId: tabInfo.group.groupId
261
+ });
262
+ }
263
+
264
+ private onTabMoved(tabBar: TabBar<Widget>, args: TabBar.ITabMovedArgs<Widget>): void {
265
+ const tabInfo = this.getOrRebuildModel(this.tabInfoLookup, args.title)!;
266
+ tabInfo.tabIndex = args.toIndex;
267
+ const tabDto = this.createTabDto(args.title, tabInfo.group.groupId);
268
+ tabInfo.group.tabs.splice(args.fromIndex, 1);
269
+ tabInfo.group.tabs.splice(args.toIndex, 0, tabDto);
270
+ this.proxy.$acceptTabOperation({
271
+ kind: TabModelOperationKind.TAB_MOVE,
272
+ index: args.toIndex,
273
+ tabDto,
274
+ groupId: tabInfo.group.groupId,
275
+ oldIndex: args.fromIndex
276
+ });
277
+ }
278
+
279
+ private onTabGroupCreated(tabBar: TabBar<Widget>): void {
280
+ this.tabGroupChanged = true;
281
+ }
282
+
283
+ private onTabGroupClosed(tabBar: TabBar<Widget>): void {
284
+ this.tabGroupChanged = true;
285
+ }
286
+ // #endregion
28
287
 
29
288
  // #region Messages received from Ext Host
30
289
  $moveTab(tabId: string, index: number, viewColumn: number, preserveFocus?: boolean): void {
@@ -32,11 +291,33 @@ export class TabsMainImp implements TabsMain {
32
291
  }
33
292
 
34
293
  async $closeTab(tabIds: string[], preserveFocus?: boolean): Promise<boolean> {
35
- return false;
294
+ const widgets: Widget[] = [];
295
+ for (const tabId of tabIds) {
296
+ const cleanedId = tabId.substring(tabId.indexOf('~') + 1);
297
+ const widget = this.applicationShell.getWidgetById(cleanedId);
298
+ if (widget) {
299
+ widgets.push(widget);
300
+ }
301
+ }
302
+ await this.applicationShell.closeMany(widgets);
303
+ return true;
36
304
  }
37
305
 
38
306
  async $closeGroup(groupIds: number[], preserveFocus?: boolean): Promise<boolean> {
39
- return false;
307
+ for (const groupId of groupIds) {
308
+ tabGroupModel: for (const [bar, groupDto] of this.tabGroupModel) {
309
+ if (groupDto.groupId === groupId) {
310
+ this.applicationShell.closeTabs(bar);
311
+ break tabGroupModel;
312
+ }
313
+ }
314
+ }
315
+ return true;
40
316
  }
41
317
  // #endregion
318
+
319
+ dispose(): void {
320
+ this.toDisposeOnDestroy.dispose();
321
+ this.disposableTabBarListeners.dispose();
322
+ }
42
323
  }
@@ -30,6 +30,7 @@ import {
30
30
  DecorationOptions,
31
31
  WorkspaceEditDto,
32
32
  DocumentsMain,
33
+ WorkspaceEditMetadataDto,
33
34
  } from '../../common/plugin-api-rpc';
34
35
  import { Range, TextDocumentShowOptions } from '../../common/plugin-api-rpc-model';
35
36
  import { EditorsAndDocumentsMain } from './editors-and-documents-main';
@@ -126,11 +127,11 @@ export class TextEditorsMainImpl implements TextEditorsMain, Disposable {
126
127
  return Promise.resolve(this.editorsAndDocuments.getEditor(id)!.applyEdits(modelVersionId, edits, opts));
127
128
  }
128
129
 
129
- async $tryApplyWorkspaceEdit(dto: WorkspaceEditDto): Promise<boolean> {
130
+ async $tryApplyWorkspaceEdit(dto: WorkspaceEditDto, metadata?: WorkspaceEditMetadataDto): Promise<boolean> {
130
131
  const workspaceEdit = toMonacoWorkspaceEdit(dto);
131
132
  try {
132
133
  const edits = ResourceEdit.convert(workspaceEdit);
133
- const { success } = await this.bulkEditService.apply(edits);
134
+ const { success } = await this.bulkEditService.apply(edits, { respectAutoSaveConfig: metadata?.isRefactoring });
134
135
  return success;
135
136
  } catch {
136
137
  return false;
@@ -0,0 +1,49 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2023 Ericsson 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 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
18
+ import { ContextKey, ContextKeyService } from '@theia/core/lib/browser/context-key-service';
19
+ import { ApplicationShell, FocusTracker, Widget } from '@theia/core/lib/browser';
20
+ import { WebviewWidget } from './webview';
21
+
22
+ @injectable()
23
+ export class WebviewContextKeys {
24
+
25
+ /**
26
+ * Context key representing the `viewType` of the active `WebviewWidget`, if any.
27
+ */
28
+ activeWebviewPanelId: ContextKey<string>;
29
+
30
+ @inject(ApplicationShell)
31
+ protected applicationShell: ApplicationShell;
32
+
33
+ @inject(ContextKeyService)
34
+ protected contextKeyService: ContextKeyService;
35
+
36
+ @postConstruct()
37
+ protected postConstruct(): void {
38
+ this.activeWebviewPanelId = this.contextKeyService.createKey('activeWebviewPanelId', '');
39
+ this.applicationShell.onDidChangeCurrentWidget(this.handleDidChangeCurrentWidget, this);
40
+ }
41
+
42
+ protected handleDidChangeCurrentWidget(change: FocusTracker.IChangedArgs<Widget>): void {
43
+ if (change.newValue instanceof WebviewWidget) {
44
+ this.activeWebviewPanelId.set(change.newValue.viewType);
45
+ } else {
46
+ this.activeWebviewPanelId.set('');
47
+ }
48
+ }
49
+ }
@@ -30,8 +30,8 @@ export class DialogsExtImpl {
30
30
  title: options.title,
31
31
  openLabel: options.openLabel,
32
32
  defaultUri: options.defaultUri ? options.defaultUri.path : undefined,
33
- canSelectFiles: options.canSelectFiles ? options.canSelectFiles : true,
34
- canSelectFolders: options.canSelectFolders ? options.canSelectFolders : false,
33
+ canSelectFiles: typeof options.canSelectFiles === 'boolean' ? options.canSelectFiles : true,
34
+ canSelectFolders: typeof options.canSelectFolders === 'boolean' ? options.canSelectFolders : false,
35
35
  canSelectMany: options.canSelectMany,
36
36
  filters: options.filters
37
37
  } as OpenDialogOptionsMain;
@@ -35,16 +35,16 @@ import { PLUGIN_RPC_CONTEXT, FileSystemExt, FileSystemMain, IFileChangeDto } fro
35
35
  import * as vscode from '@theia/plugin';
36
36
  import * as files from '@theia/filesystem/lib/common/files';
37
37
  import * as typeConverter from './type-converters';
38
- import { LanguagesExtImpl } from './languages';
39
38
  import { Schemes as Schemas } from '../common/uri-components';
40
39
  import { State, StateMachine, LinkComputer, Edge } from '../common/link-computer';
41
40
  import { commonPrefixLength } from '@theia/core/lib/common/strings';
42
41
  import { CharCode } from '@theia/core/lib/common/char-code';
43
42
  import { BinaryBuffer } from '@theia/core/lib/common/buffer';
43
+ import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol';
44
44
 
45
45
  type IDisposable = vscode.Disposable;
46
46
 
47
- class FsLinkProvider {
47
+ export class FsLinkProvider {
48
48
 
49
49
  private _schemes: string[] = [];
50
50
  private _stateMachine?: StateMachine;
@@ -204,12 +204,14 @@ export class FileSystemExtImpl implements FileSystemExt {
204
204
  private readonly _usedSchemes = new Set<string>();
205
205
  private readonly _watches = new Map<number, IDisposable>();
206
206
 
207
- private _linkProviderRegistration?: IDisposable;
207
+ private readonly onWillRegisterFileSystemProviderEmitter = new Emitter<FsLinkProvider>();
208
+ readonly onWillRegisterFileSystemProvider = this.onWillRegisterFileSystemProviderEmitter.event;
209
+
208
210
  private _handlePool: number = 0;
209
211
 
210
212
  readonly fileSystem: vscode.FileSystem;
211
213
 
212
- constructor(rpc: RPCProtocol, private _extHostLanguageFeatures: LanguagesExtImpl) {
214
+ constructor(rpc: RPCProtocol) {
213
215
  this._proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.FILE_SYSTEM_MAIN);
214
216
  this.fileSystem = new ConsumerFileSystem(this._proxy, this._capabilities);
215
217
 
@@ -218,18 +220,7 @@ export class FileSystemExtImpl implements FileSystemExt {
218
220
  }
219
221
 
220
222
  dispose(): void {
221
- if (this._linkProviderRegistration) {
222
- this._linkProviderRegistration.dispose();
223
- }
224
- }
225
-
226
- private _registerLinkProviderIfNotYetRegistered(): void {
227
- if (!this._linkProviderRegistration) {
228
- this._linkProviderRegistration = this._extHostLanguageFeatures.registerDocumentLinkProvider('*', this._linkProvider, {
229
- id: 'theia.fs-ext-impl',
230
- name: 'fs-ext-impl'
231
- });
232
- }
223
+ this.onWillRegisterFileSystemProviderEmitter.dispose();
233
224
  }
234
225
 
235
226
  registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) {
@@ -238,8 +229,7 @@ export class FileSystemExtImpl implements FileSystemExt {
238
229
  throw new Error(`a provider for the scheme '${scheme}' is already registered`);
239
230
  }
240
231
 
241
- //
242
- this._registerLinkProviderIfNotYetRegistered();
232
+ this.onWillRegisterFileSystemProviderEmitter.fire(this._linkProvider);
243
233
 
244
234
  const handle = this._handlePool++;
245
235
  this._linkProvider.add(scheme);
@@ -14,8 +14,9 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { Range as R, Position as P, Location as L } from '@theia/core/shared/vscode-languageserver-protocol';
18
17
  import * as theia from '@theia/plugin';
18
+ import { Range as R, Position as P, Location as L } from '@theia/core/shared/vscode-languageserver-protocol';
19
+ import { URI as TheiaURI } from '@theia/core/lib/common/uri';
19
20
  import { cloneAndChange } from '../common/objects';
20
21
  import { Position, Range, Location, CallHierarchyItem, TypeHierarchyItem, URI, TextDocumentShowOptions } from './types-impl';
21
22
  import {
@@ -63,6 +64,9 @@ export namespace KnownCommands {
63
64
  }
64
65
  };
65
66
 
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ const identity = (args: any[]) => args;
69
+
66
70
  mappings['editor.action.select.all'] = ['editor.action.select.all', CONVERT_VSCODE_TO_MONACO];
67
71
  mappings['editor.action.toggleHighContrast'] = ['editor.action.toggleHighContrast', CONVERT_VSCODE_TO_MONACO];
68
72
  mappings['editor.action.moveCarretLeftAction'] = ['editor.action.moveCarretLeftAction', CONVERT_VSCODE_TO_MONACO];
@@ -302,6 +306,17 @@ export namespace KnownCommands {
302
306
  mappings['vscode.open'] = ['vscode.open', CONVERT_VSCODE_TO_MONACO];
303
307
  mappings['vscode.diff'] = ['vscode.diff', CONVERT_VSCODE_TO_MONACO];
304
308
 
309
+ // terminal commands
310
+ mappings['workbench.action.terminal.new'] = ['terminal:new', identity];
311
+ mappings['workbench.action.terminal.newWithProfile'] = ['terminal:new:profile', identity];
312
+ mappings['workbench.action.terminal.selectDefaultShell'] = ['terminal:profile:default', identity];
313
+ mappings['workbench.action.terminal.newInActiveWorkspace'] = ['terminal:new:active:workspace', identity];
314
+ mappings['workbench.action.terminal.clear'] = ['terminal:clear', identity];
315
+ mappings['openInTerminal'] = ['terminal:context', createConversionFunction((uri: URI) => new TheiaURI(uri))];
316
+ mappings['workbench.action.terminal.split'] = ['terminal:split', identity];
317
+ mappings['workbench.action.terminal.focusFind'] = ['terminal:find', identity];
318
+ mappings['workbench.action.terminal.hideFind'] = ['terminal:find:cancel', identity];
319
+
305
320
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
306
321
  export function map<T>(id: string, args: any[] | undefined, toDo: (mappedId: string, mappedArgs: any[] | undefined, mappedResult: ConversionFunction | undefined) => T): T {
307
322
  if (mappings[id]) {
@@ -0,0 +1,44 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 Red Hat, Inc. 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 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+ import * as theia from '@theia/plugin';
17
+ import { DataTransferDTO, DocumentDropEdit } from '../../common/plugin-api-rpc-model';
18
+ import { CancellationToken } from '@theia/core/shared/vscode-languageserver-protocol';
19
+ import { Position } from '../../common/plugin-api-rpc';
20
+ import * as Converter from '../type-converters';
21
+ import { DocumentsExtImpl } from '../documents';
22
+ import { URI } from '@theia/core/shared/vscode-uri';
23
+ import { FileSystemExtImpl } from '../file-system-ext-impl';
24
+ import * as os from 'os';
25
+ import * as path from 'path';
26
+
27
+ export class DocumentDropEditAdapter {
28
+ constructor(private readonly provider: theia.DocumentDropEditProvider,
29
+ private readonly documents: DocumentsExtImpl,
30
+ private readonly fileSystem: FileSystemExtImpl) { }
31
+
32
+ async provideDocumentDropEdits(resource: URI, position: Position, dataTransfer: DataTransferDTO, token: CancellationToken): Promise<DocumentDropEdit | undefined> {
33
+ return this.provider.provideDocumentDropEdits(
34
+ this.documents.getDocument(resource),
35
+ Converter.toPosition(position),
36
+ Converter.DataTransfer.toDataTransfer(dataTransfer, itemId => this.resolveFileData(itemId)),
37
+ token) as DocumentDropEdit | undefined;
38
+ }
39
+
40
+ private async resolveFileData(itemId: string): Promise<Uint8Array> {
41
+ const filePath = URI.file(path.resolve(os.tmpdir(), 'theia_upload', itemId));
42
+ return this.fileSystem.fileSystem.readFile(filePath);
43
+ }
44
+ }
@@ -69,7 +69,9 @@ import {
69
69
  InlineValue,
70
70
  InlineValueContext,
71
71
  TypeHierarchyItem,
72
- InlineCompletionContext
72
+ InlineCompletionContext,
73
+ DocumentDropEdit,
74
+ DataTransferDTO
73
75
  } from '../common/plugin-api-rpc-model';
74
76
  import { CompletionAdapter } from './languages/completion';
75
77
  import { Diagnostics } from './languages/diagnostics';
@@ -109,6 +111,9 @@ import { LinkedEditingRangeAdapter } from './languages/linked-editing-range';
109
111
  import { serializeEnterRules, serializeIndentation, serializeRegExp } from './languages-utils';
110
112
  import { InlayHintsAdapter } from './languages/inlay-hints';
111
113
  import { InlineCompletionAdapter, InlineCompletionAdapterBase } from './languages/inline-completion';
114
+ import { DocumentDropEditAdapter } from './languages/document-drop-edit';
115
+ import { IDisposable } from '@theia/monaco-editor-core';
116
+ import { FileSystemExtImpl, FsLinkProvider } from './file-system-ext-impl';
112
117
 
113
118
  type Adapter = CompletionAdapter |
114
119
  SignatureHelpAdapter |
@@ -139,7 +144,8 @@ type Adapter = CompletionAdapter |
139
144
  DocumentSemanticTokensAdapter |
140
145
  LinkedEditingRangeAdapter |
141
146
  TypeHierarchyAdapter |
142
- InlineCompletionAdapter;
147
+ InlineCompletionAdapter |
148
+ DocumentDropEditAdapter;
143
149
 
144
150
  export class LanguagesExtImpl implements LanguagesExt {
145
151
 
@@ -147,15 +153,25 @@ export class LanguagesExtImpl implements LanguagesExt {
147
153
 
148
154
  private readonly diagnostics: Diagnostics;
149
155
 
156
+ private linkProviderRegistration?: IDisposable;
157
+
150
158
  private callId = 0;
151
159
  private adaptersMap = new Map<number, Adapter>();
152
160
 
153
161
  constructor(
154
162
  rpc: RPCProtocol,
155
163
  private readonly documents: DocumentsExtImpl,
156
- private readonly commands: CommandRegistryImpl) {
164
+ private readonly commands: CommandRegistryImpl,
165
+ private readonly filesSystem: FileSystemExtImpl) {
157
166
  this.proxy = rpc.getProxy(PLUGIN_RPC_CONTEXT.LANGUAGES_MAIN);
158
167
  this.diagnostics = new Diagnostics(rpc);
168
+ filesSystem.onWillRegisterFileSystemProvider(linkProvider => this.registerLinkProviderIfNotYetRegistered(linkProvider));
169
+ }
170
+
171
+ dispose(): void {
172
+ if (this.linkProviderRegistration) {
173
+ this.linkProviderRegistration.dispose();
174
+ }
159
175
  }
160
176
 
161
177
  get onDidChangeDiagnostics(): Event<theia.DiagnosticChangeEvent> {
@@ -258,6 +274,15 @@ export class LanguagesExtImpl implements LanguagesExt {
258
274
  return undefined;
259
275
  }
260
276
 
277
+ private registerLinkProviderIfNotYetRegistered(linkProvider: FsLinkProvider): void {
278
+ if (!this.linkProviderRegistration) {
279
+ this.linkProviderRegistration = this.registerDocumentLinkProvider('*', linkProvider, {
280
+ id: 'theia.fs-ext-impl',
281
+ name: 'fs-ext-impl'
282
+ });
283
+ }
284
+ }
285
+
261
286
  // ### Completion begin
262
287
  $provideCompletionItems(handle: number, resource: UriComponents, position: Position,
263
288
  context: CompletionContext, token: theia.CancellationToken): Promise<CompletionResultDto | undefined> {
@@ -463,6 +488,19 @@ export class LanguagesExtImpl implements LanguagesExt {
463
488
  }
464
489
  // ### Document Formatting Edit end
465
490
 
491
+ // ### Drop Edit Provider start
492
+ $provideDocumentDropEdits(handle: number, resource: UriComponents, position: Position,
493
+ dataTransfer: DataTransferDTO, token: theia.CancellationToken): Promise<DocumentDropEdit | undefined> {
494
+ return this.withAdapter(handle, DocumentDropEditAdapter, adapter => adapter.provideDocumentDropEdits(URI.revive(resource), position, dataTransfer, token), undefined);
495
+ }
496
+
497
+ registerDocumentDropEditProvider(selector: theia.DocumentSelector, provider: theia.DocumentDropEditProvider): theia.Disposable {
498
+ const callId = this.addNewAdapter(new DocumentDropEditAdapter(provider, this.documents, this.filesSystem));
499
+ this.proxy.$registerDocumentDropEditProvider(callId, this.transformDocumentSelector(selector));
500
+ return this.createDisposable(callId);
501
+ }
502
+ // ### Drop Edit Provider end
503
+
466
504
  // ### Document Range Formatting Edit begin
467
505
  registerDocumentRangeFormattingEditProvider(selector: theia.DocumentSelector, provider: theia.DocumentRangeFormattingEditProvider,
468
506
  pluginInfo: PluginInfo): theia.Disposable {