@theia/plugin-ext 1.34.0-next.7 → 1.34.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 (175) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +30 -10
  2. package/lib/common/plugin-api-rpc.d.ts.map +1 -1
  3. package/lib/common/plugin-api-rpc.js +23 -11
  4. package/lib/common/plugin-api-rpc.js.map +1 -1
  5. package/lib/common/plugin-protocol.d.ts +15 -0
  6. package/lib/common/plugin-protocol.d.ts.map +1 -1
  7. package/lib/common/plugin-protocol.js.map +1 -1
  8. package/lib/common/rpc-protocol.d.ts.map +1 -1
  9. package/lib/common/rpc-protocol.js +3 -4
  10. package/lib/common/rpc-protocol.js.map +1 -1
  11. package/lib/common/types.d.ts +1 -1
  12. package/lib/common/types.d.ts.map +1 -1
  13. package/lib/common/types.js +2 -3
  14. package/lib/common/types.js.map +1 -1
  15. package/lib/hosted/browser/hosted-plugin.d.ts +1 -0
  16. package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
  17. package/lib/hosted/browser/hosted-plugin.js +3 -0
  18. package/lib/hosted/browser/hosted-plugin.js.map +1 -1
  19. package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
  20. package/lib/hosted/node/hosted-plugin-localization-service.js +2 -2
  21. package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
  22. package/lib/hosted/node/plugin-host.d.ts +1 -1
  23. package/lib/hosted/node/plugin-host.d.ts.map +1 -1
  24. package/lib/hosted/node/plugin-host.js +1 -2
  25. package/lib/hosted/node/plugin-host.js.map +1 -1
  26. package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -1
  27. package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
  28. package/lib/hosted/node/scanners/scanner-theia.js +13 -0
  29. package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
  30. package/lib/main/browser/authentication-main.js +1 -1
  31. package/lib/main/browser/authentication-main.js.map +1 -1
  32. package/lib/main/browser/commands.js +1 -1
  33. package/lib/main/browser/commands.js.map +1 -1
  34. package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
  35. package/lib/main/browser/debug/debug-main.js +1 -0
  36. package/lib/main/browser/debug/debug-main.js.map +1 -1
  37. package/lib/main/browser/dialogs-main.d.ts.map +1 -1
  38. package/lib/main/browser/dialogs-main.js +2 -1
  39. package/lib/main/browser/dialogs-main.js.map +1 -1
  40. package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts +2 -2
  41. package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts.map +1 -1
  42. package/lib/main/browser/menus/plugin-menu-command-adapter.js +6 -2
  43. package/lib/main/browser/menus/plugin-menu-command-adapter.js.map +1 -1
  44. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +2 -2
  45. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  46. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +6 -0
  47. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  48. package/lib/main/browser/plugin-contribution-handler.d.ts +6 -0
  49. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  50. package/lib/main/browser/plugin-contribution-handler.js +35 -0
  51. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  52. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  53. package/lib/main/browser/plugin-ext-frontend-module.js +8 -3
  54. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  55. package/lib/main/browser/plugin-terminal-registry.d.ts +5 -0
  56. package/lib/main/browser/plugin-terminal-registry.d.ts.map +1 -0
  57. package/lib/main/browser/plugin-terminal-registry.js +35 -0
  58. package/lib/main/browser/plugin-terminal-registry.js.map +1 -0
  59. package/lib/main/browser/scm-main.d.ts +1 -0
  60. package/lib/main/browser/scm-main.d.ts.map +1 -1
  61. package/lib/main/browser/scm-main.js +7 -0
  62. package/lib/main/browser/scm-main.js.map +1 -1
  63. package/lib/main/browser/terminal-main.d.ts +10 -4
  64. package/lib/main/browser/terminal-main.d.ts.map +1 -1
  65. package/lib/main/browser/terminal-main.js +51 -25
  66. package/lib/main/browser/terminal-main.js.map +1 -1
  67. package/lib/main/browser/view/dnd-file-content-store.d.ts +8 -0
  68. package/lib/main/browser/view/dnd-file-content-store.d.ts.map +1 -0
  69. package/lib/main/browser/view/dnd-file-content-store.js +52 -0
  70. package/lib/main/browser/view/dnd-file-content-store.js.map +1 -0
  71. package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
  72. package/lib/main/browser/view/plugin-view-registry.js +1 -1
  73. package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
  74. package/lib/main/browser/view/tree-view-decorator-service.d.ts +2 -4
  75. package/lib/main/browser/view/tree-view-decorator-service.d.ts.map +1 -1
  76. package/lib/main/browser/view/tree-view-decorator-service.js +1 -2
  77. package/lib/main/browser/view/tree-view-decorator-service.js.map +1 -1
  78. package/lib/main/browser/view/tree-view-widget.d.ts +25 -9
  79. package/lib/main/browser/view/tree-view-widget.d.ts.map +1 -1
  80. package/lib/main/browser/view/tree-view-widget.js +184 -38
  81. package/lib/main/browser/view/tree-view-widget.js.map +1 -1
  82. package/lib/main/browser/view/tree-views-main.d.ts +5 -2
  83. package/lib/main/browser/view/tree-views-main.d.ts.map +1 -1
  84. package/lib/main/browser/view/tree-views-main.js +16 -2
  85. package/lib/main/browser/view/tree-views-main.js.map +1 -1
  86. package/lib/main/browser/webview/webview.d.ts +1 -1
  87. package/lib/main/browser/webview/webview.d.ts.map +1 -1
  88. package/lib/main/browser/webview/webview.js +7 -2
  89. package/lib/main/browser/webview/webview.js.map +1 -1
  90. package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts +1 -1
  91. package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts.map +1 -1
  92. package/lib/plugin/debug/debug-ext.d.ts +3 -0
  93. package/lib/plugin/debug/debug-ext.d.ts.map +1 -1
  94. package/lib/plugin/debug/debug-ext.js +10 -0
  95. package/lib/plugin/debug/debug-ext.js.map +1 -1
  96. package/lib/plugin/languages/code-action.d.ts.map +1 -1
  97. package/lib/plugin/languages/code-action.js +8 -8
  98. package/lib/plugin/languages/code-action.js.map +1 -1
  99. package/lib/plugin/plugin-context.d.ts.map +1 -1
  100. package/lib/plugin/plugin-context.js +8 -0
  101. package/lib/plugin/plugin-context.js.map +1 -1
  102. package/lib/plugin/plugin-manager.d.ts.map +1 -1
  103. package/lib/plugin/plugin-manager.js +1 -0
  104. package/lib/plugin/plugin-manager.js.map +1 -1
  105. package/lib/plugin/preference-registry.d.ts.map +1 -1
  106. package/lib/plugin/preference-registry.js +4 -2
  107. package/lib/plugin/preference-registry.js.map +1 -1
  108. package/lib/plugin/quick-open.d.ts +3 -0
  109. package/lib/plugin/quick-open.d.ts.map +1 -1
  110. package/lib/plugin/quick-open.js +7 -0
  111. package/lib/plugin/quick-open.js.map +1 -1
  112. package/lib/plugin/scm.d.ts +3 -0
  113. package/lib/plugin/scm.d.ts.map +1 -1
  114. package/lib/plugin/scm.js +7 -0
  115. package/lib/plugin/scm.js.map +1 -1
  116. package/lib/plugin/tabs.js +2 -2
  117. package/lib/plugin/tabs.js.map +1 -1
  118. package/lib/plugin/terminal-ext.d.ts +9 -1
  119. package/lib/plugin/terminal-ext.d.ts.map +1 -1
  120. package/lib/plugin/terminal-ext.js +63 -7
  121. package/lib/plugin/terminal-ext.js.map +1 -1
  122. package/lib/plugin/tree/tree-views.d.ts +13 -7
  123. package/lib/plugin/tree/tree-views.d.ts.map +1 -1
  124. package/lib/plugin/tree/tree-views.js +93 -24
  125. package/lib/plugin/tree/tree-views.js.map +1 -1
  126. package/lib/plugin/type-converters.d.ts +4 -0
  127. package/lib/plugin/type-converters.d.ts.map +1 -1
  128. package/lib/plugin/type-converters.js +48 -46
  129. package/lib/plugin/type-converters.js.map +1 -1
  130. package/lib/plugin/type-converters.spec.js +19 -0
  131. package/lib/plugin/type-converters.spec.js.map +1 -1
  132. package/lib/plugin/types-impl.d.ts +61 -6
  133. package/lib/plugin/types-impl.d.ts.map +1 -1
  134. package/lib/plugin/types-impl.js +117 -17
  135. package/lib/plugin/types-impl.js.map +1 -1
  136. package/package.json +26 -26
  137. package/src/common/plugin-api-rpc.ts +36 -18
  138. package/src/common/plugin-protocol.ts +18 -0
  139. package/src/common/rpc-protocol.ts +2 -3
  140. package/src/common/types.ts +4 -4
  141. package/src/hosted/browser/hosted-plugin.ts +4 -0
  142. package/src/hosted/node/hosted-plugin-localization-service.ts +3 -3
  143. package/src/hosted/node/plugin-host.ts +1 -2
  144. package/src/hosted/node/scanners/scanner-theia.ts +15 -1
  145. package/src/main/browser/authentication-main.ts +1 -1
  146. package/src/main/browser/commands.ts +1 -1
  147. package/src/main/browser/debug/debug-main.ts +1 -0
  148. package/src/main/browser/dialogs-main.ts +2 -1
  149. package/src/main/browser/menus/plugin-menu-command-adapter.ts +7 -4
  150. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +6 -0
  151. package/src/main/browser/plugin-contribution-handler.ts +35 -0
  152. package/src/main/browser/plugin-ext-frontend-module.ts +10 -4
  153. package/src/main/browser/plugin-terminal-registry.ts +27 -0
  154. package/src/main/browser/scm-main.ts +10 -0
  155. package/src/main/browser/terminal-main.ts +55 -25
  156. package/src/main/browser/view/dnd-file-content-store.ts +42 -0
  157. package/src/main/browser/view/plugin-view-registry.ts +4 -1
  158. package/src/main/browser/view/tree-view-decorator-service.ts +4 -5
  159. package/src/main/browser/view/tree-view-widget.tsx +189 -35
  160. package/src/main/browser/view/tree-views-main.ts +20 -4
  161. package/src/main/browser/webview/pre/main.js +112 -111
  162. package/src/main/browser/webview/webview.ts +7 -3
  163. package/src/plugin/debug/debug-ext.ts +12 -0
  164. package/src/plugin/languages/code-action.ts +8 -8
  165. package/src/plugin/plugin-context.ts +16 -3
  166. package/src/plugin/plugin-manager.ts +1 -0
  167. package/src/plugin/preference-registry.ts +4 -2
  168. package/src/plugin/quick-open.ts +10 -0
  169. package/src/plugin/scm.ts +11 -0
  170. package/src/plugin/tabs.ts +2 -2
  171. package/src/plugin/terminal-ext.ts +68 -8
  172. package/src/plugin/tree/tree-views.ts +98 -31
  173. package/src/plugin/type-converters.spec.ts +20 -0
  174. package/src/plugin/type-converters.ts +51 -50
  175. package/src/plugin/types-impl.ts +143 -21
@@ -14,9 +14,8 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { URI } from '@theia/core/shared/vscode-uri';
18
17
  import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
19
- import { TreeViewsExt, TreeViewItemCollapsibleState, TreeViewItem, TreeViewSelection, ThemeIcon } from '../../../common/plugin-api-rpc';
18
+ import { TreeViewsExt, TreeViewItemCollapsibleState, TreeViewItem, TreeViewItemReference, ThemeIcon, DataTransferFileDTO } from '../../../common/plugin-api-rpc';
20
19
  import { Command } from '../../../common/plugin-api-rpc-model';
21
20
  import {
22
21
  TreeNode,
@@ -32,7 +31,8 @@ import {
32
31
  TreeViewWelcomeWidget,
33
32
  TooltipAttributes,
34
33
  TreeSelection,
35
- HoverService
34
+ HoverService,
35
+ ApplicationShell
36
36
  } from '@theia/core/lib/browser';
37
37
  import { MenuPath, MenuModelRegistry, ActionMenuNode } from '@theia/core/lib/common/menu';
38
38
  import * as React from '@theia/core/shared/react';
@@ -41,7 +41,7 @@ import { ACTION_ITEM, Widget } from '@theia/core/lib/browser/widgets/widget';
41
41
  import { Emitter, Event } from '@theia/core/lib/common/event';
42
42
  import { MessageService } from '@theia/core/lib/common/message-service';
43
43
  import { View } from '../../../common/plugin-protocol';
44
- import CoreURI from '@theia/core/lib/common/uri';
44
+ import { URI } from '@theia/core/lib/common/uri';
45
45
  import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
46
46
  import { MarkdownString } from '@theia/core/lib/common/markdown-rendering';
47
47
  import { LabelParser } from '@theia/core/lib/browser/label-parser';
@@ -52,6 +52,7 @@ import { WidgetDecoration } from '@theia/core/lib/browser/widget-decoration';
52
52
  import { CancellationTokenSource, CancellationToken } from '@theia/core/lib/common';
53
53
  import { mixin } from '../../../common/types';
54
54
  import { Deferred } from '@theia/core/lib/common/promise-util';
55
+ import { DnDFileContentStore } from './dnd-file-content-store';
55
56
 
56
57
  export const TREE_NODE_HYPERLINK = 'theia-TreeNodeHyperlink';
57
58
  export const VIEW_ITEM_CONTEXT_MENU: MenuPath = ['view-item-context-menu'];
@@ -161,8 +162,11 @@ export namespace CompositeTreeViewNode {
161
162
  }
162
163
 
163
164
  @injectable()
164
- export class TreeViewWidgetIdentifier {
165
+ export class TreeViewWidgetOptions {
165
166
  id: string;
167
+ multiSelect: boolean | undefined;
168
+ dragMimeTypes: string[] | undefined;
169
+ dropMimeTypes: string[] | undefined;
166
170
  }
167
171
 
168
172
  @injectable()
@@ -171,8 +175,8 @@ export class PluginTree extends TreeImpl {
171
175
  @inject(PluginSharedStyle)
172
176
  protected readonly sharedStyle: PluginSharedStyle;
173
177
 
174
- @inject(TreeViewWidgetIdentifier)
175
- protected readonly identifier: TreeViewWidgetIdentifier;
178
+ @inject(TreeViewWidgetOptions)
179
+ protected readonly options: TreeViewWidgetOptions;
176
180
 
177
181
  @inject(MessageService)
178
182
  protected readonly notification: MessageService;
@@ -188,7 +192,7 @@ export class PluginTree extends TreeImpl {
188
192
  set proxy(proxy: TreeViewsExt | undefined) {
189
193
  this._proxy = proxy;
190
194
  if (proxy) {
191
- this._hasTreeItemResolve = proxy.$hasResolveTreeItem(this.identifier.id);
195
+ this._hasTreeItemResolve = proxy.$hasResolveTreeItem(this.options.id);
192
196
  } else {
193
197
  this._hasTreeItemResolve = Promise.resolve(false);
194
198
  }
@@ -220,7 +224,7 @@ export class PluginTree extends TreeImpl {
220
224
 
221
225
  protected async fetchChildren(proxy: TreeViewsExt, parent: CompositeTreeNode): Promise<TreeViewItem[]> {
222
226
  try {
223
- const children = await proxy.$getChildren(this.identifier.id, parent.id);
227
+ const children = await proxy.$getChildren(this.options.id, parent.id);
224
228
  const oldEmpty = this._isEmpty;
225
229
  this._isEmpty = !parent.id && (!children || children.length === 0);
226
230
  if (oldEmpty !== this._isEmpty) {
@@ -229,8 +233,8 @@ export class PluginTree extends TreeImpl {
229
233
  return children || [];
230
234
  } catch (e) {
231
235
  if (e) {
232
- console.error(`Failed to fetch children for '${this.identifier.id}'`, e);
233
- const label = this._viewInfo ? this._viewInfo.name : this.identifier.id;
236
+ console.error(`Failed to fetch children for '${this.options.id}'`, e);
237
+ const label = this._viewInfo ? this._viewInfo.name : this.options.id;
234
238
  this.notification.error(`${label}: ${e.message}`);
235
239
  }
236
240
  return [];
@@ -288,7 +292,7 @@ export class PluginTree extends TreeImpl {
288
292
  children: [],
289
293
  command: item.command
290
294
  }, update);
291
- return new ResolvableCompositeTreeViewNode(compositeNode, async (token: CancellationToken) => this._proxy?.$resolveTreeItem(this.identifier.id, item.id, token));
295
+ return new ResolvableCompositeTreeViewNode(compositeNode, async (token: CancellationToken) => this._proxy?.$resolveTreeItem(this.options.id, item.id, token));
292
296
  }
293
297
 
294
298
  // Node is a leaf
@@ -304,13 +308,13 @@ export class PluginTree extends TreeImpl {
304
308
  selected: false,
305
309
  command: item.command,
306
310
  }, update);
307
- return new ResolvableTreeViewNode(treeNode, async (token: CancellationToken) => this._proxy?.$resolveTreeItem(this.identifier.id, item.id, token));
311
+ return new ResolvableTreeViewNode(treeNode, async (token: CancellationToken) => this._proxy?.$resolveTreeItem(this.options.id, item.id, token));
308
312
  }
309
313
 
310
314
  protected createTreeNodeUpdate(item: TreeViewItem): Partial<TreeViewNode> {
311
315
  const decorationData = this.toDecorationData(item);
312
316
  const icon = this.toIconClass(item);
313
- const resourceUri = item.resourceUri && URI.revive(item.resourceUri).toString();
317
+ const resourceUri = item.resourceUri && URI.fromComponents(item.resourceUri).toString();
314
318
  const themeIcon = item.themeIcon ? item.themeIcon : item.collapsibleState !== TreeViewItemCollapsibleState.None ? { id: 'folder' } : undefined;
315
319
  return {
316
320
  name: item.label,
@@ -393,14 +397,17 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
393
397
 
394
398
  protected _contextSelection = false;
395
399
 
400
+ @inject(ApplicationShell)
401
+ protected readonly applicationShell: ApplicationShell;
402
+
396
403
  @inject(MenuModelRegistry)
397
404
  protected readonly menus: MenuModelRegistry;
398
405
 
399
406
  @inject(ContextKeyService)
400
407
  protected readonly contextKeys: ContextKeyService;
401
408
 
402
- @inject(TreeViewWidgetIdentifier)
403
- readonly identifier: TreeViewWidgetIdentifier;
409
+ @inject(TreeViewWidgetOptions)
410
+ readonly options: TreeViewWidgetOptions;
404
411
 
405
412
  @inject(PluginTreeModel)
406
413
  override readonly model: PluginTreeModel;
@@ -417,16 +424,23 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
417
424
  @inject(ColorRegistry)
418
425
  protected readonly colorRegistry: ColorRegistry;
419
426
 
427
+ @inject(DnDFileContentStore)
428
+ protected readonly dndFileContentStore: DnDFileContentStore;
429
+
430
+ protected treeDragType: string;
431
+ protected readonly expansionTimeouts: Map<string, number> = new Map();
432
+
420
433
  @postConstruct()
421
434
  protected override init(): void {
422
435
  super.init();
423
- this.id = this.identifier.id;
436
+ this.id = this.options.id;
424
437
  this.addClass('theia-tree-view');
425
438
  this.node.style.height = '100%';
426
439
  this.model.onDidChangeWelcomeState(this.update, this);
427
440
  this.toDispose.push(this.model.onDidChangeWelcomeState(this.update, this));
428
441
  this.toDispose.push(this.onDidChangeVisibilityEmitter);
429
442
  this.toDispose.push(this.contextKeyService.onDidChange(() => this.update()));
443
+ this.treeDragType = `application/vnd.code.tree.${this.id.toLowerCase()}`;
430
444
  }
431
445
 
432
446
  protected override renderIcon(node: TreeNode, props: NodeProps): React.ReactNode {
@@ -498,7 +512,7 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
498
512
  });
499
513
  } else {
500
514
  const title = node.tooltip ||
501
- (node.resourceUri && this.labelProvider.getLongName(new CoreURI(node.resourceUri)))
515
+ (node.resourceUri && this.labelProvider.getLongName(new URI(node.resourceUri)))
502
516
  || this.toNodeName(node);
503
517
  event.currentTarget.title = title;
504
518
  }
@@ -518,7 +532,7 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
518
532
  };
519
533
  } else {
520
534
  const title = node.tooltip ||
521
- (node.resourceUri && this.labelProvider.getLongName(new CoreURI(node.resourceUri)))
535
+ (node.resourceUri && this.labelProvider.getLongName(new URI(node.resourceUri)))
522
536
  || this.toNodeName(node);
523
537
 
524
538
  attrs = {
@@ -546,41 +560,181 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
546
560
  return <div {...attrs}>{...children}</div>;
547
561
  }
548
562
 
549
- protected override renderTailDecorations(node: TreeViewNode, props: NodeProps): React.ReactNode {
550
- return this.contextKeys.with({ view: this.id, viewItem: node.contextValue }, () => {
563
+ protected override createNodeAttributes(node: TreeViewNode, props: NodeProps): React.Attributes & React.HTMLAttributes<HTMLElement> {
564
+ const attrs = super.createNodeAttributes(node, props);
565
+
566
+ if (this.options.dragMimeTypes) {
567
+ attrs.onDragStart = event => this.handleDragStartEvent(node, event);
568
+ attrs.onDragEnd = event => this.handleDragEnd(node, event);
569
+ attrs.draggable = true;
570
+ }
571
+
572
+ if (this.options.dropMimeTypes) {
573
+ attrs.onDrop = event => this.handleDropEvent(node, event);
574
+ attrs.onDragEnter = event => this.handleDragEnter(node, event);
575
+ attrs.onDragLeave = event => this.handleDragLeave(node, event);
576
+ attrs.onDragOver = event => this.handleDragOver(event);
577
+ }
578
+
579
+ return attrs;
580
+ }
581
+ handleDragLeave(node: TreeViewNode, event: React.DragEvent<HTMLElement>): void {
582
+ const timeout = this.expansionTimeouts.get(node.id);
583
+ if (typeof timeout !== 'undefined') {
584
+ console.debug(`dragleave ${node.id} canceling timeout`);
585
+ clearTimeout(timeout);
586
+ this.expansionTimeouts.delete(node.id);
587
+ }
588
+ }
589
+ handleDragEnter(node: TreeViewNode, event: React.DragEvent<HTMLElement>): void {
590
+ console.debug(`dragenter ${node.id}`);
591
+ if (ExpandableTreeNode.is(node)) {
592
+ console.debug(`dragenter ${node.id} starting timeout`);
593
+ this.expansionTimeouts.set(node.id, window.setTimeout(() => {
594
+ console.debug(`dragenter ${node.id} timeout reached`);
595
+ this.model.expandNode(node);
596
+ }, 500));
597
+ }
598
+ }
599
+
600
+ protected override createContainerAttributes(): React.HTMLAttributes<HTMLElement> {
601
+ const attrs = super.createContainerAttributes();
602
+ if (this.options.dropMimeTypes) {
603
+ attrs.onDrop = event => this.handleDropEvent(undefined, event);
604
+ attrs.onDragOver = event => this.handleDragOver(event);
605
+ }
606
+ return attrs;
607
+ }
608
+
609
+ protected handleDragStartEvent(node: TreeViewNode, event: React.DragEvent<HTMLElement>): void {
610
+ event.dataTransfer!.setData(this.treeDragType, '');
611
+ let selectedNodes: TreeViewNode[] = [];
612
+ if (this.model.selectedNodes.find(selected => TreeNode.equals(selected, node))) {
613
+ selectedNodes = this.model.selectedNodes.filter(TreeViewNode.is);
614
+ } else {
615
+ selectedNodes = [node];
616
+ }
617
+
618
+ this.options.dragMimeTypes!.forEach(type => {
619
+ if (type === 'text/uri-list') {
620
+ ApplicationShell.setDraggedEditorUris(event.dataTransfer, selectedNodes.filter(n => n.resourceUri).map(n => new URI(n.resourceUri)));
621
+ } else {
622
+ event.dataTransfer.setData(type, '');
623
+ }
624
+ });
625
+
626
+ this.model.proxy!.$dragStarted(this.options.id, selectedNodes.map(selected => selected.id), CancellationToken.None).then(maybeUris => {
627
+ if (maybeUris) {
628
+ this.applicationShell.addAdditionalDraggedEditorUris(maybeUris.map(URI.fromComponents));
629
+ }
630
+ });
631
+ }
632
+
633
+ handleDragEnd(node: TreeViewNode, event: React.DragEvent<HTMLElement>): void {
634
+ this.applicationShell.clearAdditionalDraggedEditorUris();
635
+ }
636
+
637
+ handleDragOver(event: React.DragEvent<HTMLElement>): void {
638
+ if (event.dataTransfer) {
639
+ const canDrop = event.dataTransfer.types.some(type => this.options.dropMimeTypes!.includes(type)) ||
640
+ event.dataTransfer.types.includes(this.treeDragType) ||
641
+ this.options.dropMimeTypes!.includes('files') && event.dataTransfer.files.length > 0;
642
+ if (canDrop) {
643
+ event.preventDefault();
644
+ event.dataTransfer.dropEffect = 'move';
645
+ } else {
646
+ event.dataTransfer.dropEffect = 'none';
647
+ }
648
+ event.stopPropagation();
649
+ }
650
+ }
651
+
652
+ protected handleDropEvent(node: TreeViewNode | undefined, event: React.DragEvent<HTMLElement>): void {
653
+ if (event.dataTransfer) {
654
+ const items: [string, string | DataTransferFileDTO][] = [];
655
+ let files: string[] = [];
656
+ try {
657
+ for (let i = 0; i < event.dataTransfer.items.length; i++) {
658
+ const transferItem = event.dataTransfer.items[i];
659
+ if (transferItem.type !== this.treeDragType) {
660
+ // do not pass the artificial drag data to the extension
661
+ const f = event.dataTransfer.items[i].getAsFile();
662
+ if (f) {
663
+ const fileId = this.dndFileContentStore.addFile(f);
664
+ files.push(fileId);
665
+ const uri = f.path ? {
666
+ scheme: 'file',
667
+ path: f.path,
668
+ authority: '',
669
+ query: '',
670
+ fragment: ''
671
+ } : undefined;
672
+ items.push([transferItem.type, new DataTransferFileDTO(f.name, fileId, uri)]);
673
+ } else {
674
+ const textData = event.dataTransfer.getData(transferItem.type);
675
+ if (textData) {
676
+ items.push([transferItem.type, textData]);
677
+ }
678
+ }
679
+ }
680
+ }
681
+ if (items.length > 0 || event.dataTransfer.types.includes(this.treeDragType)) {
682
+ event.preventDefault();
683
+ event.stopPropagation();
684
+ this.model.proxy?.$drop(this.id, node?.id, items, CancellationToken.None).finally(() => {
685
+ for (const file of files) {
686
+ this.dndFileContentStore.removeFile(file);
687
+ }
688
+ });
689
+ files = [];
690
+ }
691
+ } catch (e) {
692
+ for (const file of files) {
693
+ this.dndFileContentStore.removeFile(file);
694
+ }
695
+ throw e;
696
+ }
697
+ }
698
+ }
699
+
700
+ protected override renderTailDecorations(treeViewNode: TreeViewNode, props: NodeProps): React.ReactNode {
701
+ return this.contextKeys.with({ view: this.id, viewItem: treeViewNode.contextValue }, () => {
551
702
  const menu = this.menus.getMenu(VIEW_ITEM_INLINE_MENU);
552
- const arg = this.toTreeViewSelection(node);
703
+ const args = this.toContextMenuArgs(treeViewNode);
553
704
  const inlineCommands = menu.children.filter((item): item is ActionMenuNode => item instanceof ActionMenuNode);
554
- const tailDecorations = super.renderTailDecorations(node, props);
705
+ const tailDecorations = super.renderTailDecorations(treeViewNode, props);
555
706
  return <React.Fragment>
556
707
  {inlineCommands.length > 0 && <div className={TREE_NODE_SEGMENT_CLASS + ' flex'}>
557
- {inlineCommands.map((item, index) => this.renderInlineCommand(item, index, this.focusService.hasFocus(node), arg))}
708
+ {inlineCommands.map((item, index) => this.renderInlineCommand(item, index, this.focusService.hasFocus(treeViewNode), args))}
558
709
  </div>}
559
710
  {tailDecorations !== undefined && <div className={TREE_NODE_SEGMENT_CLASS + ' flex'}>{tailDecorations}</div>}
560
711
  </React.Fragment>;
561
712
  });
562
713
  }
563
714
 
564
- toTreeViewSelection(node: TreeNode): TreeViewSelection {
565
- return { treeViewId: this.id, treeItemId: node.id };
715
+ toTreeViewItemReference(treeNode: TreeNode): TreeViewItemReference {
716
+ return { viewId: this.id, itemId: treeNode.id };
566
717
  }
567
718
 
568
719
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
569
- protected renderInlineCommand(node: ActionMenuNode, index: number, tabbable: boolean, arg: any): React.ReactNode {
570
- const { icon } = node;
571
- if (!icon || !this.commands.isVisible(node.command, arg) || !node.when || !this.contextKeys.match(node.when)) {
720
+ protected renderInlineCommand(actionMenuNode: ActionMenuNode, index: number, tabbable: boolean, args: any[]): React.ReactNode {
721
+ if (!actionMenuNode.icon || !this.commands.isVisible(actionMenuNode.command, ...args) || !actionMenuNode.when || !this.contextKeys.match(actionMenuNode.when)) {
572
722
  return false;
573
723
  }
574
- const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS, icon, ACTION_ITEM, 'theia-tree-view-inline-action'].join(' ');
724
+ const className = [TREE_NODE_SEGMENT_CLASS, TREE_NODE_TAIL_CLASS, actionMenuNode.icon, ACTION_ITEM, 'theia-tree-view-inline-action'].join(' ');
575
725
  const tabIndex = tabbable ? 0 : undefined;
576
- return <div key={index} className={className} title={node.label} tabIndex={tabIndex} onClick={e => {
726
+ return <div key={index} className={className} title={actionMenuNode.label} tabIndex={tabIndex} onClick={e => {
577
727
  e.stopPropagation();
578
- this.commands.executeCommand(node.command, arg);
728
+ this.commands.executeCommand(actionMenuNode.command, ...args);
579
729
  }} />;
580
730
  }
581
731
 
582
- protected override toContextMenuArgs(node: SelectableTreeNode): [TreeViewSelection] {
583
- return [this.toTreeViewSelection(node)];
732
+ protected override toContextMenuArgs(target: SelectableTreeNode): [TreeViewItemReference, TreeViewItemReference[]] | [TreeViewItemReference] {
733
+ if (this.options.multiSelect) {
734
+ return [this.toTreeViewItemReference(target), this.model.selectedNodes.map(node => this.toTreeViewItemReference(node))];
735
+ } else {
736
+ return [this.toTreeViewItemReference(target)];
737
+ }
584
738
  }
585
739
 
586
740
  override setFlag(flag: Widget.Flag): void {
@@ -688,7 +842,7 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
688
842
  const args = this.toContextMenuArgs(node);
689
843
  const contextKeyService = this.contextKeyService.createOverlay([
690
844
  ['viewItem', (TreeViewNode.is(node) && node.contextValue) || undefined],
691
- ['view', this.identifier.id]
845
+ ['view', this.options.id]
692
846
  ]);
693
847
  setTimeout(() => this.contextMenuRenderer.render({
694
848
  menuPath: contextMenuPath,
@@ -15,7 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { interfaces } from '@theia/core/shared/inversify';
18
- import { MAIN_RPC_CONTEXT, TreeViewsMain, TreeViewsExt, TreeViewRevealOptions } from '../../../common/plugin-api-rpc';
18
+ import { MAIN_RPC_CONTEXT, TreeViewsMain, TreeViewsExt, TreeViewRevealOptions, RegisterTreeDataProviderOptions } from '../../../common/plugin-api-rpc';
19
19
  import { RPCProtocol } from '../../../common/rpc-protocol';
20
20
  import { PluginViewRegistry, PLUGIN_VIEW_DATA_FACTORY_ID } from './plugin-view-registry';
21
21
  import {
@@ -26,8 +26,10 @@ import {
26
26
  } from '@theia/core/lib/browser';
27
27
  import { ViewContextKeyService } from './view-context-key-service';
28
28
  import { Disposable, DisposableCollection } from '@theia/core';
29
- import { TreeViewWidget, TreeViewNode, PluginTreeModel } from './tree-view-widget';
29
+ import { TreeViewWidget, TreeViewNode, PluginTreeModel, TreeViewWidgetOptions } from './tree-view-widget';
30
30
  import { PluginViewWidget } from './plugin-view-widget';
31
+ import { BinaryBuffer } from '@theia/core/lib/common/buffer';
32
+ import { DnDFileContentStore } from './dnd-file-content-store';
31
33
 
32
34
  export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
33
35
 
@@ -35,6 +37,7 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
35
37
  private readonly viewRegistry: PluginViewRegistry;
36
38
  private readonly contextKeys: ViewContextKeyService;
37
39
  private readonly widgetManager: WidgetManager;
40
+ private readonly fileContentStore: DnDFileContentStore;
38
41
 
39
42
  private readonly treeViewProviders = new Map<string, Disposable>();
40
43
 
@@ -48,15 +51,22 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
48
51
 
49
52
  this.contextKeys = this.container.get(ViewContextKeyService);
50
53
  this.widgetManager = this.container.get(WidgetManager);
54
+ this.fileContentStore = this.container.get(DnDFileContentStore);
51
55
  }
52
56
 
53
57
  dispose(): void {
54
58
  this.toDispose.dispose();
55
59
  }
56
60
 
57
- async $registerTreeDataProvider(treeViewId: string): Promise<void> {
61
+ async $registerTreeDataProvider(treeViewId: string, $options: RegisterTreeDataProviderOptions): Promise<void> {
58
62
  this.treeViewProviders.set(treeViewId, this.viewRegistry.registerViewDataProvider(treeViewId, async ({ state, viewInfo }) => {
59
- const widget = await this.widgetManager.getOrCreateWidget<TreeViewWidget>(PLUGIN_VIEW_DATA_FACTORY_ID, { id: treeViewId });
63
+ const options: TreeViewWidgetOptions = {
64
+ id: treeViewId,
65
+ multiSelect: $options.canSelectMany,
66
+ dragMimeTypes: $options.dragMimeTypes,
67
+ dropMimeTypes: $options.dropMimeTypes
68
+ };
69
+ const widget = await this.widgetManager.getOrCreateWidget<TreeViewWidget>(PLUGIN_VIEW_DATA_FACTORY_ID, options);
60
70
  widget.model.viewInfo = viewInfo;
61
71
  if (state) {
62
72
  widget.restoreState(state);
@@ -94,6 +104,12 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
94
104
  }
95
105
  }
96
106
 
107
+ async $readDroppedFile(contentId: string): Promise<BinaryBuffer> {
108
+ const file = this.fileContentStore.getFile(contentId);
109
+ const buffer = await file.arrayBuffer();
110
+ return BinaryBuffer.wrap(new Uint8Array(buffer));
111
+ }
112
+
97
113
  async $refresh(treeViewId: string): Promise<void> {
98
114
  const viewPanel = await this.viewRegistry.getView(treeViewId);
99
115
  const widget = viewPanel && viewPanel.widgets[0];