@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.
- package/lib/common/plugin-api-rpc.d.ts +31 -11
- package/lib/common/plugin-api-rpc.d.ts.map +1 -1
- package/lib/common/plugin-api-rpc.js +23 -11
- package/lib/common/plugin-api-rpc.js.map +1 -1
- package/lib/common/plugin-protocol.d.ts +15 -0
- package/lib/common/plugin-protocol.d.ts.map +1 -1
- package/lib/common/plugin-protocol.js.map +1 -1
- package/lib/common/rpc-protocol.d.ts.map +1 -1
- package/lib/common/rpc-protocol.js +3 -4
- package/lib/common/rpc-protocol.js.map +1 -1
- package/lib/common/types.d.ts +1 -1
- package/lib/common/types.d.ts.map +1 -1
- package/lib/common/types.js +2 -3
- package/lib/common/types.js.map +1 -1
- package/lib/hosted/browser/hosted-plugin.d.ts +1 -0
- package/lib/hosted/browser/hosted-plugin.d.ts.map +1 -1
- package/lib/hosted/browser/hosted-plugin.js +3 -0
- package/lib/hosted/browser/hosted-plugin.js.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.d.ts.map +1 -1
- package/lib/hosted/node/hosted-plugin-localization-service.js +2 -2
- package/lib/hosted/node/hosted-plugin-localization-service.js.map +1 -1
- package/lib/hosted/node/plugin-host.d.ts +1 -1
- package/lib/hosted/node/plugin-host.d.ts.map +1 -1
- package/lib/hosted/node/plugin-host.js +1 -2
- package/lib/hosted/node/plugin-host.js.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts +2 -1
- package/lib/hosted/node/scanners/scanner-theia.d.ts.map +1 -1
- package/lib/hosted/node/scanners/scanner-theia.js +13 -0
- package/lib/hosted/node/scanners/scanner-theia.js.map +1 -1
- package/lib/main/browser/authentication-main.js +1 -1
- package/lib/main/browser/authentication-main.js.map +1 -1
- package/lib/main/browser/commands.js +1 -1
- package/lib/main/browser/commands.js.map +1 -1
- package/lib/main/browser/debug/debug-main.d.ts.map +1 -1
- package/lib/main/browser/debug/debug-main.js +1 -0
- package/lib/main/browser/debug/debug-main.js.map +1 -1
- package/lib/main/browser/dialogs-main.d.ts.map +1 -1
- package/lib/main/browser/dialogs-main.js +2 -1
- package/lib/main/browser/dialogs-main.js.map +1 -1
- package/lib/main/browser/main-context.d.ts.map +1 -1
- package/lib/main/browser/main-context.js +3 -0
- package/lib/main/browser/main-context.js.map +1 -1
- package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts +2 -2
- package/lib/main/browser/menus/plugin-menu-command-adapter.d.ts.map +1 -1
- package/lib/main/browser/menus/plugin-menu-command-adapter.js +6 -2
- package/lib/main/browser/menus/plugin-menu-command-adapter.js.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts +2 -2
- package/lib/main/browser/menus/vscode-theia-menu-mappings.d.ts.map +1 -1
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js +6 -0
- package/lib/main/browser/menus/vscode-theia-menu-mappings.js.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.d.ts +6 -0
- package/lib/main/browser/plugin-contribution-handler.d.ts.map +1 -1
- package/lib/main/browser/plugin-contribution-handler.js +35 -0
- package/lib/main/browser/plugin-contribution-handler.js.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.d.ts.map +1 -1
- package/lib/main/browser/plugin-ext-frontend-module.js +8 -3
- package/lib/main/browser/plugin-ext-frontend-module.js.map +1 -1
- package/lib/main/browser/plugin-terminal-registry.d.ts +5 -0
- package/lib/main/browser/plugin-terminal-registry.d.ts.map +1 -0
- package/lib/main/browser/plugin-terminal-registry.js +35 -0
- package/lib/main/browser/plugin-terminal-registry.js.map +1 -0
- package/lib/main/browser/scm-main.d.ts +1 -0
- package/lib/main/browser/scm-main.d.ts.map +1 -1
- package/lib/main/browser/scm-main.js +7 -0
- package/lib/main/browser/scm-main.js.map +1 -1
- package/lib/main/browser/tabs/tabs-main.d.ts +33 -2
- package/lib/main/browser/tabs/tabs-main.d.ts.map +1 -1
- package/lib/main/browser/tabs/tabs-main.js +256 -6
- package/lib/main/browser/tabs/tabs-main.js.map +1 -1
- package/lib/main/browser/terminal-main.d.ts +10 -4
- package/lib/main/browser/terminal-main.d.ts.map +1 -1
- package/lib/main/browser/terminal-main.js +51 -25
- package/lib/main/browser/terminal-main.js.map +1 -1
- package/lib/main/browser/view/dnd-file-content-store.d.ts +8 -0
- package/lib/main/browser/view/dnd-file-content-store.d.ts.map +1 -0
- package/lib/main/browser/view/dnd-file-content-store.js +52 -0
- package/lib/main/browser/view/dnd-file-content-store.js.map +1 -0
- package/lib/main/browser/view/plugin-view-registry.d.ts.map +1 -1
- package/lib/main/browser/view/plugin-view-registry.js +1 -1
- package/lib/main/browser/view/plugin-view-registry.js.map +1 -1
- package/lib/main/browser/view/tree-view-decorator-service.d.ts +2 -4
- package/lib/main/browser/view/tree-view-decorator-service.d.ts.map +1 -1
- package/lib/main/browser/view/tree-view-decorator-service.js +1 -2
- package/lib/main/browser/view/tree-view-decorator-service.js.map +1 -1
- package/lib/main/browser/view/tree-view-widget.d.ts +25 -9
- package/lib/main/browser/view/tree-view-widget.d.ts.map +1 -1
- package/lib/main/browser/view/tree-view-widget.js +184 -38
- package/lib/main/browser/view/tree-view-widget.js.map +1 -1
- package/lib/main/browser/view/tree-views-main.d.ts +5 -2
- package/lib/main/browser/view/tree-views-main.d.ts.map +1 -1
- package/lib/main/browser/view/tree-views-main.js +16 -2
- package/lib/main/browser/view/tree-views-main.js.map +1 -1
- package/lib/main/browser/webview/webview.d.ts +1 -1
- package/lib/main/browser/webview/webview.d.ts.map +1 -1
- package/lib/main/browser/webview/webview.js +7 -2
- package/lib/main/browser/webview/webview.js.map +1 -1
- package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts +1 -1
- package/lib/main/node/handlers/plugin-theia-directory-handler.d.ts.map +1 -1
- package/lib/plugin/debug/debug-ext.d.ts +3 -0
- package/lib/plugin/debug/debug-ext.d.ts.map +1 -1
- package/lib/plugin/debug/debug-ext.js +10 -0
- package/lib/plugin/debug/debug-ext.js.map +1 -1
- package/lib/plugin/languages/code-action.d.ts.map +1 -1
- package/lib/plugin/languages/code-action.js +8 -8
- package/lib/plugin/languages/code-action.js.map +1 -1
- package/lib/plugin/plugin-context.d.ts.map +1 -1
- package/lib/plugin/plugin-context.js +8 -0
- package/lib/plugin/plugin-context.js.map +1 -1
- package/lib/plugin/plugin-manager.d.ts.map +1 -1
- package/lib/plugin/plugin-manager.js +1 -0
- package/lib/plugin/plugin-manager.js.map +1 -1
- package/lib/plugin/preference-registry.d.ts.map +1 -1
- package/lib/plugin/preference-registry.js +4 -2
- package/lib/plugin/preference-registry.js.map +1 -1
- package/lib/plugin/quick-open.d.ts +3 -0
- package/lib/plugin/quick-open.d.ts.map +1 -1
- package/lib/plugin/quick-open.js +7 -0
- package/lib/plugin/quick-open.js.map +1 -1
- package/lib/plugin/scm.d.ts +3 -0
- package/lib/plugin/scm.d.ts.map +1 -1
- package/lib/plugin/scm.js +7 -0
- package/lib/plugin/scm.js.map +1 -1
- package/lib/plugin/tabs.d.ts.map +1 -1
- package/lib/plugin/tabs.js +12 -15
- package/lib/plugin/tabs.js.map +1 -1
- package/lib/plugin/terminal-ext.d.ts +9 -1
- package/lib/plugin/terminal-ext.d.ts.map +1 -1
- package/lib/plugin/terminal-ext.js +63 -7
- package/lib/plugin/terminal-ext.js.map +1 -1
- package/lib/plugin/tree/tree-views.d.ts +13 -7
- package/lib/plugin/tree/tree-views.d.ts.map +1 -1
- package/lib/plugin/tree/tree-views.js +93 -24
- package/lib/plugin/tree/tree-views.js.map +1 -1
- package/lib/plugin/type-converters.d.ts +4 -0
- package/lib/plugin/type-converters.d.ts.map +1 -1
- package/lib/plugin/type-converters.js +48 -46
- package/lib/plugin/type-converters.js.map +1 -1
- package/lib/plugin/type-converters.spec.js +19 -0
- package/lib/plugin/type-converters.spec.js.map +1 -1
- package/lib/plugin/types-impl.d.ts +61 -6
- package/lib/plugin/types-impl.d.ts.map +1 -1
- package/lib/plugin/types-impl.js +117 -17
- package/lib/plugin/types-impl.js.map +1 -1
- package/package.json +27 -26
- package/src/common/plugin-api-rpc.ts +37 -19
- package/src/common/plugin-protocol.ts +18 -0
- package/src/common/rpc-protocol.ts +2 -3
- package/src/common/types.ts +4 -4
- package/src/hosted/browser/hosted-plugin.ts +4 -0
- package/src/hosted/node/hosted-plugin-localization-service.ts +3 -3
- package/src/hosted/node/plugin-host.ts +1 -2
- package/src/hosted/node/scanners/scanner-theia.ts +15 -1
- package/src/main/browser/authentication-main.ts +1 -1
- package/src/main/browser/commands.ts +1 -1
- package/src/main/browser/debug/debug-main.ts +1 -0
- package/src/main/browser/dialogs-main.ts +2 -1
- package/src/main/browser/main-context.ts +4 -0
- package/src/main/browser/menus/plugin-menu-command-adapter.ts +7 -4
- package/src/main/browser/menus/vscode-theia-menu-mappings.ts +6 -0
- package/src/main/browser/plugin-contribution-handler.ts +35 -0
- package/src/main/browser/plugin-ext-frontend-module.ts +10 -4
- package/src/main/browser/plugin-terminal-registry.ts +27 -0
- package/src/main/browser/scm-main.ts +10 -0
- package/src/main/browser/tabs/tabs-main.ts +287 -6
- package/src/main/browser/terminal-main.ts +55 -25
- package/src/main/browser/view/dnd-file-content-store.ts +42 -0
- package/src/main/browser/view/plugin-view-registry.ts +4 -1
- package/src/main/browser/view/tree-view-decorator-service.ts +4 -5
- package/src/main/browser/view/tree-view-widget.tsx +189 -35
- package/src/main/browser/view/tree-views-main.ts +20 -4
- package/src/main/browser/webview/pre/main.js +112 -111
- package/src/main/browser/webview/webview.ts +7 -3
- package/src/plugin/debug/debug-ext.ts +12 -0
- package/src/plugin/languages/code-action.ts +8 -8
- package/src/plugin/plugin-context.ts +16 -3
- package/src/plugin/plugin-manager.ts +1 -0
- package/src/plugin/preference-registry.ts +4 -2
- package/src/plugin/quick-open.ts +10 -0
- package/src/plugin/scm.ts +11 -0
- package/src/plugin/tabs.ts +12 -16
- package/src/plugin/terminal-ext.ts +68 -8
- package/src/plugin/tree/tree-views.ts +98 -31
- package/src/plugin/type-converters.spec.ts +20 -0
- package/src/plugin/type-converters.ts +51 -50
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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(
|
|
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
|
|
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
|
|
36
|
-
|
|
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
|
|