@theia/plugin-ext 1.34.0-next.7 → 1.34.1

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 (185) hide show
  1. package/lib/common/plugin-api-rpc.d.ts +31 -11
  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/main-context.d.ts.map +1 -1
  41. package/lib/main/browser/main-context.js +3 -0
  42. package/lib/main/browser/main-context.js.map +1 -1
  43. package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts +2 -2
  44. package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts.map +1 -1
  45. package/lib/main/browser/menus/plugin-menu-command-adapter.js +6 -2
  46. package/lib/main/browser/menus/plugin-menu-command-adapter.js.map +1 -1
  47. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +2 -2
  48. package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
  49. package/lib/main/browser/menus/vscode-theia-menu-mappings.js +6 -0
  50. package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
  51. package/lib/main/browser/plugin-contribution-handler.d.ts +6 -0
  52. package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
  53. package/lib/main/browser/plugin-contribution-handler.js +35 -0
  54. package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
  55. package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
  56. package/lib/main/browser/plugin-ext-frontend-module.js +8 -3
  57. package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
  58. package/lib/main/browser/plugin-terminal-registry.d.ts +5 -0
  59. package/lib/main/browser/plugin-terminal-registry.d.ts.map +1 -0
  60. package/lib/main/browser/plugin-terminal-registry.js +35 -0
  61. package/lib/main/browser/plugin-terminal-registry.js.map +1 -0
  62. package/lib/main/browser/scm-main.d.ts +1 -0
  63. package/lib/main/browser/scm-main.d.ts.map +1 -1
  64. package/lib/main/browser/scm-main.js +7 -0
  65. package/lib/main/browser/scm-main.js.map +1 -1
  66. package/lib/main/browser/tabs/tabs-main.d.ts +33 -2
  67. package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
  68. package/lib/main/browser/tabs/tabs-main.js +256 -6
  69. package/lib/main/browser/tabs/tabs-main.js.map +1 -1
  70. package/lib/main/browser/terminal-main.d.ts +10 -4
  71. package/lib/main/browser/terminal-main.d.ts.map +1 -1
  72. package/lib/main/browser/terminal-main.js +51 -25
  73. package/lib/main/browser/terminal-main.js.map +1 -1
  74. package/lib/main/browser/view/dnd-file-content-store.d.ts +8 -0
  75. package/lib/main/browser/view/dnd-file-content-store.d.ts.map +1 -0
  76. package/lib/main/browser/view/dnd-file-content-store.js +52 -0
  77. package/lib/main/browser/view/dnd-file-content-store.js.map +1 -0
  78. package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
  79. package/lib/main/browser/view/plugin-view-registry.js +1 -1
  80. package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
  81. package/lib/main/browser/view/tree-view-decorator-service.d.ts +2 -4
  82. package/lib/main/browser/view/tree-view-decorator-service.d.ts.map +1 -1
  83. package/lib/main/browser/view/tree-view-decorator-service.js +1 -2
  84. package/lib/main/browser/view/tree-view-decorator-service.js.map +1 -1
  85. package/lib/main/browser/view/tree-view-widget.d.ts +25 -9
  86. package/lib/main/browser/view/tree-view-widget.d.ts.map +1 -1
  87. package/lib/main/browser/view/tree-view-widget.js +184 -38
  88. package/lib/main/browser/view/tree-view-widget.js.map +1 -1
  89. package/lib/main/browser/view/tree-views-main.d.ts +5 -2
  90. package/lib/main/browser/view/tree-views-main.d.ts.map +1 -1
  91. package/lib/main/browser/view/tree-views-main.js +16 -2
  92. package/lib/main/browser/view/tree-views-main.js.map +1 -1
  93. package/lib/main/browser/webview/webview.d.ts +1 -1
  94. package/lib/main/browser/webview/webview.d.ts.map +1 -1
  95. package/lib/main/browser/webview/webview.js +7 -2
  96. package/lib/main/browser/webview/webview.js.map +1 -1
  97. package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts +1 -1
  98. package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts.map +1 -1
  99. package/lib/plugin/debug/debug-ext.d.ts +3 -0
  100. package/lib/plugin/debug/debug-ext.d.ts.map +1 -1
  101. package/lib/plugin/debug/debug-ext.js +10 -0
  102. package/lib/plugin/debug/debug-ext.js.map +1 -1
  103. package/lib/plugin/languages/code-action.d.ts.map +1 -1
  104. package/lib/plugin/languages/code-action.js +8 -8
  105. package/lib/plugin/languages/code-action.js.map +1 -1
  106. package/lib/plugin/plugin-context.d.ts.map +1 -1
  107. package/lib/plugin/plugin-context.js +8 -0
  108. package/lib/plugin/plugin-context.js.map +1 -1
  109. package/lib/plugin/plugin-manager.d.ts.map +1 -1
  110. package/lib/plugin/plugin-manager.js +1 -0
  111. package/lib/plugin/plugin-manager.js.map +1 -1
  112. package/lib/plugin/preference-registry.d.ts.map +1 -1
  113. package/lib/plugin/preference-registry.js +4 -2
  114. package/lib/plugin/preference-registry.js.map +1 -1
  115. package/lib/plugin/quick-open.d.ts +3 -0
  116. package/lib/plugin/quick-open.d.ts.map +1 -1
  117. package/lib/plugin/quick-open.js +7 -0
  118. package/lib/plugin/quick-open.js.map +1 -1
  119. package/lib/plugin/scm.d.ts +3 -0
  120. package/lib/plugin/scm.d.ts.map +1 -1
  121. package/lib/plugin/scm.js +7 -0
  122. package/lib/plugin/scm.js.map +1 -1
  123. package/lib/plugin/tabs.d.ts.map +1 -1
  124. package/lib/plugin/tabs.js +12 -15
  125. package/lib/plugin/tabs.js.map +1 -1
  126. package/lib/plugin/terminal-ext.d.ts +9 -1
  127. package/lib/plugin/terminal-ext.d.ts.map +1 -1
  128. package/lib/plugin/terminal-ext.js +63 -7
  129. package/lib/plugin/terminal-ext.js.map +1 -1
  130. package/lib/plugin/tree/tree-views.d.ts +13 -7
  131. package/lib/plugin/tree/tree-views.d.ts.map +1 -1
  132. package/lib/plugin/tree/tree-views.js +93 -24
  133. package/lib/plugin/tree/tree-views.js.map +1 -1
  134. package/lib/plugin/type-converters.d.ts +4 -0
  135. package/lib/plugin/type-converters.d.ts.map +1 -1
  136. package/lib/plugin/type-converters.js +48 -46
  137. package/lib/plugin/type-converters.js.map +1 -1
  138. package/lib/plugin/type-converters.spec.js +19 -0
  139. package/lib/plugin/type-converters.spec.js.map +1 -1
  140. package/lib/plugin/types-impl.d.ts +61 -6
  141. package/lib/plugin/types-impl.d.ts.map +1 -1
  142. package/lib/plugin/types-impl.js +117 -17
  143. package/lib/plugin/types-impl.js.map +1 -1
  144. package/package.json +27 -26
  145. package/src/common/plugin-api-rpc.ts +37 -19
  146. package/src/common/plugin-protocol.ts +18 -0
  147. package/src/common/rpc-protocol.ts +2 -3
  148. package/src/common/types.ts +4 -4
  149. package/src/hosted/browser/hosted-plugin.ts +4 -0
  150. package/src/hosted/node/hosted-plugin-localization-service.ts +3 -3
  151. package/src/hosted/node/plugin-host.ts +1 -2
  152. package/src/hosted/node/scanners/scanner-theia.ts +15 -1
  153. package/src/main/browser/authentication-main.ts +1 -1
  154. package/src/main/browser/commands.ts +1 -1
  155. package/src/main/browser/debug/debug-main.ts +1 -0
  156. package/src/main/browser/dialogs-main.ts +2 -1
  157. package/src/main/browser/main-context.ts +4 -0
  158. package/src/main/browser/menus/plugin-menu-command-adapter.ts +7 -4
  159. package/src/main/browser/menus/vscode-theia-menu-mappings.ts +6 -0
  160. package/src/main/browser/plugin-contribution-handler.ts +35 -0
  161. package/src/main/browser/plugin-ext-frontend-module.ts +10 -4
  162. package/src/main/browser/plugin-terminal-registry.ts +27 -0
  163. package/src/main/browser/scm-main.ts +10 -0
  164. package/src/main/browser/tabs/tabs-main.ts +287 -6
  165. package/src/main/browser/terminal-main.ts +55 -25
  166. package/src/main/browser/view/dnd-file-content-store.ts +42 -0
  167. package/src/main/browser/view/plugin-view-registry.ts +4 -1
  168. package/src/main/browser/view/tree-view-decorator-service.ts +4 -5
  169. package/src/main/browser/view/tree-view-widget.tsx +189 -35
  170. package/src/main/browser/view/tree-views-main.ts +20 -4
  171. package/src/main/browser/webview/pre/main.js +112 -111
  172. package/src/main/browser/webview/webview.ts +7 -3
  173. package/src/plugin/debug/debug-ext.ts +12 -0
  174. package/src/plugin/languages/code-action.ts +8 -8
  175. package/src/plugin/plugin-context.ts +16 -3
  176. package/src/plugin/plugin-manager.ts +1 -0
  177. package/src/plugin/preference-registry.ts +4 -2
  178. package/src/plugin/quick-open.ts +10 -0
  179. package/src/plugin/scm.ts +11 -0
  180. package/src/plugin/tabs.ts +12 -16
  181. package/src/plugin/terminal-ext.ts +68 -8
  182. package/src/plugin/tree/tree-views.ts +98 -31
  183. package/src/plugin/type-converters.spec.ts +20 -0
  184. package/src/plugin/type-converters.ts +51 -50
  185. package/src/plugin/types-impl.ts +143 -21
@@ -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
  }
@@ -16,9 +16,8 @@
16
16
 
17
17
  import { interfaces } from '@theia/core/shared/inversify';
18
18
  import { ApplicationShell, WidgetOpenerOptions } from '@theia/core/lib/browser';
19
- import { TerminalOptions } from '@theia/plugin';
20
- import { CancellationToken } from '@theia/core/shared/vscode-languageserver-protocol';
21
- import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
19
+ import { TerminalEditorLocationOptions, TerminalOptions } from '@theia/plugin';
20
+ import { TerminalLocation, TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
22
21
  import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service';
23
22
  import { TerminalServiceMain, TerminalServiceExt, MAIN_RPC_CONTEXT } from '../../common/plugin-api-rpc';
24
23
  import { RPCProtocol } from '../../common/rpc-protocol';
@@ -27,6 +26,10 @@ import { SerializableEnvironmentVariableCollection } from '@theia/terminal/lib/c
27
26
  import { ShellTerminalServerProxy } from '@theia/terminal/lib/common/shell-terminal-protocol';
28
27
  import { TerminalLink, TerminalLinkProvider } from '@theia/terminal/lib/browser/terminal-link-provider';
29
28
  import { URI } from '@theia/core/lib/common/uri';
29
+ import { getIconClass } from '../../plugin/terminal-ext';
30
+ import { PluginTerminalRegistry } from './plugin-terminal-registry';
31
+ import { CancellationToken } from '@theia/core';
32
+ import { HostedPluginSupport } from '../../hosted/browser/hosted-plugin';
30
33
 
31
34
  /**
32
35
  * Plugin api service allows working with terminal emulator.
@@ -34,6 +37,8 @@ import { URI } from '@theia/core/lib/common/uri';
34
37
  export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLinkProvider, Disposable {
35
38
 
36
39
  private readonly terminals: TerminalService;
40
+ private readonly pluginTerminalRegistry: PluginTerminalRegistry;
41
+ private readonly hostedPluginSupport: HostedPluginSupport;
37
42
  private readonly shell: ApplicationShell;
38
43
  private readonly extProxy: TerminalServiceExt;
39
44
  private readonly shellTerminalServer: ShellTerminalServerProxy;
@@ -43,6 +48,8 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
43
48
 
44
49
  constructor(rpc: RPCProtocol, container: interfaces.Container) {
45
50
  this.terminals = container.get(TerminalService);
51
+ this.pluginTerminalRegistry = container.get(PluginTerminalRegistry);
52
+ this.hostedPluginSupport = container.get(HostedPluginSupport);
46
53
  this.shell = container.get(ApplicationShell);
47
54
  this.shellTerminalServer = container.get(ShellTerminalServerProxy);
48
55
  this.extProxy = rpc.getProxy(MAIN_RPC_CONTEXT.TERMINAL_EXT);
@@ -58,9 +65,16 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
58
65
  this.extProxy.$initEnvironmentVariableCollections(serializedCollections);
59
66
  }
60
67
 
68
+ this.pluginTerminalRegistry.startCallback = id => this.startProfile(id);
69
+
61
70
  container.bind(TerminalLinkProvider).toDynamicValue(() => this);
62
71
  }
63
72
 
73
+ async startProfile(id: string): Promise<string> {
74
+ await this.hostedPluginSupport.activateByTerminalProfile(id);
75
+ return this.extProxy.$startProfile(id, CancellationToken.None);
76
+ }
77
+
64
78
  $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: SerializableEnvironmentVariableCollection | undefined): void {
65
79
  if (collection) {
66
80
  this.shellTerminalServer.setCollection(extensionIdentifier, persistent, collection);
@@ -122,30 +136,46 @@ export class TerminalServiceMainImpl implements TerminalServiceMain, TerminalLin
122
136
  terminal.resize(cols, rows);
123
137
  }
124
138
 
125
- async $createTerminal(id: string, options: TerminalOptions, isPseudoTerminal?: boolean): Promise<string> {
126
- try {
127
- const terminal = await this.terminals.newTerminal({
128
- id,
129
- title: options.name,
130
- shellPath: options.shellPath,
131
- shellArgs: options.shellArgs,
132
- cwd: options.cwd ? new URI(options.cwd) : undefined,
133
- env: options.env,
134
- strictEnv: options.strictEnv,
135
- destroyTermOnClose: true,
136
- useServerTitle: false,
137
- attributes: options.attributes,
138
- hideFromUser: options.hideFromUser,
139
- isPseudoTerminal
140
- });
141
- if (options.message) {
142
- terminal.writeLine(options.message);
139
+ async $createTerminal(id: string, options: TerminalOptions, parentId?: string, isPseudoTerminal?: boolean): Promise<string> {
140
+ const terminal = await this.terminals.newTerminal({
141
+ id,
142
+ title: options.name,
143
+ iconClass: getIconClass(options),
144
+ shellPath: options.shellPath,
145
+ shellArgs: options.shellArgs,
146
+ cwd: options.cwd ? new URI(options.cwd) : undefined,
147
+ env: options.env,
148
+ strictEnv: options.strictEnv,
149
+ destroyTermOnClose: true,
150
+ useServerTitle: false,
151
+ attributes: options.attributes,
152
+ hideFromUser: options.hideFromUser,
153
+ location: this.getTerminalLocation(options, parentId),
154
+ isPseudoTerminal,
155
+ isTransient: options.isTransient
156
+ });
157
+ if (options.message) {
158
+ terminal.writeLine(options.message);
159
+ }
160
+ terminal.start();
161
+ return terminal.id;
162
+ }
163
+
164
+ protected getTerminalLocation(options: TerminalOptions, parentId?: string): TerminalLocation | TerminalEditorLocationOptions | { parentTerminal: string; } | undefined {
165
+ if (typeof options.location === 'number' && Object.values(TerminalLocation).includes(options.location)) {
166
+ return options.location;
167
+ } else if (options.location && typeof options.location === 'object') {
168
+ if ('parentTerminal' in options.location) {
169
+ if (!parentId) {
170
+ throw new Error('parentTerminal is set but no parentId is provided');
171
+ }
172
+ return { 'parentTerminal': parentId };
173
+ } else {
174
+ return options.location;
143
175
  }
144
- terminal.start();
145
- return terminal.id;
146
- } catch (error) {
147
- throw new Error('Failed to create terminal. Cause: ' + error);
148
176
  }
177
+
178
+ return undefined;
149
179
  }
150
180
 
151
181
  $sendText(id: string, text: string, addNewLine?: boolean): void {
@@ -0,0 +1,42 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2022 STMicroelectronics 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 { injectable } from '@theia/core/shared/inversify';
18
+
19
+ @injectable()
20
+ export class DnDFileContentStore {
21
+ private static id: number = 0;
22
+ private files: Map<string, File> = new Map();
23
+
24
+ addFile(f: File): string {
25
+ const id = (DnDFileContentStore.id++).toString();
26
+ this.files.set(id, f);
27
+ return id;
28
+ }
29
+
30
+ removeFile(id: string): boolean {
31
+ return this.files.delete(id);
32
+ }
33
+
34
+ getFile(id: string): File {
35
+ const file = this.files.get(id);
36
+ if (file) {
37
+ return file;
38
+ }
39
+
40
+ throw new Error(`File with id ${id} not found in dnd operation`);
41
+ }
42
+ }
@@ -148,7 +148,10 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
148
148
  const disposable = new DisposableCollection();
149
149
  disposable.push(this.registerViewWelcome({
150
150
  view: 'explorer',
151
- content: nls.localizeByDefault('You have not yet opened a folder.\n[Open Folder](command:{0})', 'workbench.action.files.openFolder'),
151
+ content: nls.localizeByDefault(
152
+ 'You have not yet opened a folder.\n{0}',
153
+ `[${nls.localizeByDefault('Open Folder')}](command:workbench.action.files.openFolder)`
154
+ ),
152
155
  order: 0
153
156
  }));
154
157
  disposable.push(event.widget.onDidDispose(() => disposable.dispose()));
@@ -16,9 +16,9 @@
16
16
 
17
17
  import { inject, injectable, interfaces, named } from '@theia/core/shared/inversify';
18
18
  import { AbstractTreeDecoratorService, TreeDecorator } from '@theia/core/lib/browser/tree/tree-decorator';
19
- import { bindContributionProvider, ContributionProvider } from '@theia/core';
19
+ import { bindContributionProvider, ContributionProvider, isObject } from '@theia/core';
20
20
  import { TreeNode } from '@theia/core/lib/browser';
21
- import { TreeItem, Uri } from '@theia/plugin';
21
+ import { TreeItem } from '@theia/plugin';
22
22
  import URI from '@theia/core/lib/common/uri';
23
23
  import { FileTreeDecoratorAdapter } from '@theia/filesystem/lib/browser';
24
24
 
@@ -32,9 +32,8 @@ export class TreeViewDecoratorAdapter extends FileTreeDecoratorAdapter {
32
32
  }
33
33
  }
34
34
 
35
- protected isTreeItem(node: unknown): node is TreeItem & { resourceUri: Uri } {
36
- const candidate = node as TreeItem;
37
- return !!candidate && typeof node === 'object' && 'resourceUri' in candidate && !!candidate.resourceUri;
35
+ protected isTreeItem(node: unknown): node is TreeItem {
36
+ return isObject<TreeItem>(node) && !!node.resourceUri;
38
37
  }
39
38
  }
40
39