@theia/terminal 1.45.1 → 1.46.0-next.72
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/README.md +30 -30
- package/lib/browser/base/terminal-service.d.ts +34 -34
- package/lib/browser/base/terminal-service.js +7 -7
- package/lib/browser/base/terminal-widget.d.ts +192 -192
- package/lib/browser/base/terminal-widget.js +34 -34
- package/lib/browser/index.d.ts +1 -1
- package/lib/browser/index.js +28 -28
- package/lib/browser/search/terminal-search-container.d.ts +3 -3
- package/lib/browser/search/terminal-search-container.js +28 -28
- package/lib/browser/search/terminal-search-widget.d.ts +30 -30
- package/lib/browser/search/terminal-search-widget.js +147 -147
- package/lib/browser/shell-terminal-profile.d.ts +20 -20
- package/lib/browser/shell-terminal-profile.js +42 -42
- package/lib/browser/terminal-contribution.d.ts +3 -3
- package/lib/browser/terminal-contribution.js +20 -20
- package/lib/browser/terminal-copy-on-selection-handler.d.ts +10 -10
- package/lib/browser/terminal-copy-on-selection-handler.js +103 -103
- package/lib/browser/terminal-file-link-provider.d.ts +24 -24
- package/lib/browser/terminal-file-link-provider.js +200 -200
- package/lib/browser/terminal-frontend-contribution.d.ts +115 -115
- package/lib/browser/terminal-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/terminal-frontend-contribution.js +1078 -1056
- package/lib/browser/terminal-frontend-contribution.js.map +1 -1
- package/lib/browser/terminal-frontend-module.d.ts +5 -5
- package/lib/browser/terminal-frontend-module.js +117 -117
- package/lib/browser/terminal-link-helpers.d.ts +27 -27
- package/lib/browser/terminal-link-helpers.js +155 -155
- package/lib/browser/terminal-link-provider.d.ts +51 -51
- package/lib/browser/terminal-link-provider.js +197 -197
- package/lib/browser/terminal-preferences.d.ts +61 -61
- package/lib/browser/terminal-preferences.d.ts.map +1 -1
- package/lib/browser/terminal-preferences.js +357 -356
- package/lib/browser/terminal-preferences.js.map +1 -1
- package/lib/browser/terminal-profile-service.d.ts +58 -58
- package/lib/browser/terminal-profile-service.js +158 -158
- package/lib/browser/terminal-quick-open-service.d.ts +36 -36
- package/lib/browser/terminal-quick-open-service.js +137 -137
- package/lib/browser/terminal-theme-service.d.ts +21 -21
- package/lib/browser/terminal-theme-service.d.ts.map +1 -1
- package/lib/browser/terminal-theme-service.js +222 -218
- package/lib/browser/terminal-theme-service.js.map +1 -1
- package/lib/browser/terminal-url-link-provider.d.ts +11 -11
- package/lib/browser/terminal-url-link-provider.js +76 -76
- package/lib/browser/terminal-widget-impl.d.ts +180 -187
- package/lib/browser/terminal-widget-impl.d.ts.map +1 -1
- package/lib/browser/terminal-widget-impl.js +857 -867
- package/lib/browser/terminal-widget-impl.js.map +1 -1
- package/lib/common/base-terminal-protocol.d.ts +55 -55
- package/lib/common/base-terminal-protocol.js +84 -84
- package/lib/common/shell-terminal-protocol.d.ts +66 -66
- package/lib/common/shell-terminal-protocol.js +35 -35
- package/lib/common/terminal-common-module.d.ts +7 -7
- package/lib/common/terminal-common-module.js +31 -31
- package/lib/common/terminal-protocol.d.ts +12 -12
- package/lib/common/terminal-protocol.js +21 -21
- package/lib/common/terminal-watcher.d.ts +13 -13
- package/lib/common/terminal-watcher.js +70 -70
- package/lib/node/base-terminal-server.d.ts +24 -24
- package/lib/node/base-terminal-server.js +168 -168
- package/lib/node/buffering-stream.d.ts +39 -39
- package/lib/node/buffering-stream.js +75 -75
- package/lib/node/buffering-stream.spec.d.ts +1 -1
- package/lib/node/buffering-stream.spec.js +43 -43
- package/lib/node/index.d.ts +1 -1
- package/lib/node/index.js +28 -28
- package/lib/node/shell-process.d.ts +27 -27
- package/lib/node/shell-process.js +107 -107
- package/lib/node/shell-process.js.map +1 -1
- package/lib/node/shell-terminal-server.d.ts +30 -30
- package/lib/node/shell-terminal-server.js +212 -212
- package/lib/node/shell-terminal-server.spec.d.ts +1 -1
- package/lib/node/shell-terminal-server.spec.js +35 -35
- package/lib/node/terminal-backend-contribution.d.ts +9 -9
- package/lib/node/terminal-backend-contribution.js +75 -75
- package/lib/node/terminal-backend-contribution.slow-spec.d.ts +1 -1
- package/lib/node/terminal-backend-contribution.slow-spec.js +54 -54
- package/lib/node/terminal-backend-module.d.ts +11 -11
- package/lib/node/terminal-backend-module.js +69 -69
- package/lib/node/terminal-server.d.ts +9 -9
- package/lib/node/terminal-server.js +64 -64
- package/lib/node/terminal-server.spec.d.ts +1 -1
- package/lib/node/terminal-server.spec.js +41 -41
- package/lib/node/test/terminal-test-container.d.ts +2 -2
- package/lib/node/test/terminal-test-container.js +40 -40
- package/lib/package.spec.js +25 -25
- package/package.json +12 -12
- package/src/browser/base/terminal-service.ts +60 -60
- package/src/browser/base/terminal-widget.ts +254 -254
- package/src/browser/index.ts +17 -17
- package/src/browser/search/terminal-search-container.ts +28 -28
- package/src/browser/search/terminal-search-widget.tsx +161 -161
- package/src/browser/shell-terminal-profile.ts +45 -45
- package/src/browser/style/terminal-search.css +99 -99
- package/src/browser/style/terminal.css +32 -32
- package/src/browser/terminal-contribution.ts +19 -19
- package/src/browser/terminal-copy-on-selection-handler.ts +92 -92
- package/src/browser/terminal-file-link-provider.ts +200 -200
- package/src/browser/terminal-frontend-contribution.ts +1120 -1098
- package/src/browser/terminal-frontend-module.ts +136 -136
- package/src/browser/terminal-link-helpers.ts +187 -187
- package/src/browser/terminal-link-provider.ts +203 -203
- package/src/browser/terminal-preferences.ts +428 -427
- package/src/browser/terminal-profile-service.ts +180 -180
- package/src/browser/terminal-quick-open-service.ts +132 -132
- package/src/browser/terminal-theme-service.ts +213 -209
- package/src/browser/terminal-url-link-provider.ts +66 -66
- package/src/browser/terminal-widget-impl.ts +939 -936
- package/src/common/base-terminal-protocol.ts +125 -125
- package/src/common/shell-terminal-protocol.ts +103 -103
- package/src/common/terminal-common-module.ts +30 -30
- package/src/common/terminal-protocol.ts +32 -32
- package/src/common/terminal-watcher.ts +69 -69
- package/src/node/base-terminal-server.ts +173 -173
- package/src/node/buffering-stream.spec.ts +46 -46
- package/src/node/buffering-stream.ts +95 -95
- package/src/node/index.ts +17 -17
- package/src/node/shell-process.ts +101 -101
- package/src/node/shell-terminal-server.spec.ts +40 -40
- package/src/node/shell-terminal-server.ts +221 -221
- package/src/node/terminal-backend-contribution.slow-spec.ts +63 -63
- package/src/node/terminal-backend-contribution.ts +60 -60
- package/src/node/terminal-backend-module.ts +82 -82
- package/src/node/terminal-server.spec.ts +47 -47
- package/src/node/terminal-server.ts +52 -52
- package/src/node/test/terminal-test-container.ts +39 -39
- package/src/package.spec.ts +28 -28
|
@@ -1,936 +1,939 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2017 TypeFox and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import { Terminal
|
|
18
|
-
import { FitAddon } from 'xterm-addon-fit';
|
|
19
|
-
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
|
|
20
|
-
import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core';
|
|
21
|
-
import {
|
|
22
|
-
Widget, Message, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer
|
|
23
|
-
} from '@theia/core/lib/browser';
|
|
24
|
-
import { isOSX } from '@theia/core/lib/common';
|
|
25
|
-
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
26
|
-
import { ShellTerminalServerProxy, IShellTerminalPreferences } from '../common/shell-terminal-protocol';
|
|
27
|
-
import { terminalsPath } from '../common/terminal-protocol';
|
|
28
|
-
import { IBaseTerminalServer, TerminalProcessInfo, TerminalExitReason } from '../common/base-terminal-protocol';
|
|
29
|
-
import { TerminalWatcher } from '../common/terminal-watcher';
|
|
30
|
-
import {
|
|
31
|
-
TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus, TerminalLocationOptions,
|
|
32
|
-
TerminalLocation
|
|
33
|
-
} from './base/terminal-widget';
|
|
34
|
-
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
35
|
-
import { TerminalPreferences
|
|
36
|
-
import URI from '@theia/core/lib/common/uri';
|
|
37
|
-
import { TerminalService } from './base/terminal-service';
|
|
38
|
-
import { TerminalSearchWidgetFactory, TerminalSearchWidget } from './search/terminal-search-widget';
|
|
39
|
-
import { TerminalCopyOnSelectionHandler } from './terminal-copy-on-selection-handler';
|
|
40
|
-
import { TerminalThemeService } from './terminal-theme-service';
|
|
41
|
-
import { CommandLineOptions, ShellCommandBuilder } from '@theia/process/lib/common/shell-command-builder';
|
|
42
|
-
import { Key } from '@theia/core/lib/browser/keys';
|
|
43
|
-
import { nls } from '@theia/core/lib/common/nls';
|
|
44
|
-
import { TerminalMenus } from './terminal-frontend-contribution';
|
|
45
|
-
import debounce = require('p-debounce');
|
|
46
|
-
import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string';
|
|
47
|
-
import { EnhancedPreviewWidget } from '@theia/core/lib/browser/widgets/enhanced-preview-widget';
|
|
48
|
-
import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
|
|
49
|
-
import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
|
|
50
|
-
|
|
51
|
-
export const TERMINAL_WIDGET_FACTORY_ID = 'terminal';
|
|
52
|
-
|
|
53
|
-
export interface TerminalWidgetFactoryOptions extends Partial<TerminalWidgetOptions> {
|
|
54
|
-
/* a unique string per terminal */
|
|
55
|
-
created: string
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export const TerminalContribution = Symbol('TerminalContribution');
|
|
59
|
-
export interface TerminalContribution {
|
|
60
|
-
onCreate(term: TerminalWidgetImpl): void;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
@injectable()
|
|
64
|
-
export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget, EnhancedPreviewWidget {
|
|
65
|
-
readonly isExtractable: boolean = true;
|
|
66
|
-
secondaryWindow: Window | undefined;
|
|
67
|
-
location: TerminalLocationOptions;
|
|
68
|
-
|
|
69
|
-
static LABEL = nls.localizeByDefault('Terminal');
|
|
70
|
-
|
|
71
|
-
exitStatus: TerminalExitStatus | undefined;
|
|
72
|
-
|
|
73
|
-
protected terminalKind = 'user';
|
|
74
|
-
protected _terminalId = -1;
|
|
75
|
-
protected readonly onTermDidClose = new Emitter<TerminalWidget>();
|
|
76
|
-
protected fitAddon: FitAddon;
|
|
77
|
-
protected term: Terminal;
|
|
78
|
-
protected searchBox: TerminalSearchWidget;
|
|
79
|
-
protected restored = false;
|
|
80
|
-
protected closeOnDispose = true;
|
|
81
|
-
protected waitForConnection: Deferred<Channel> | undefined;
|
|
82
|
-
protected linkHover: HTMLDivElement;
|
|
83
|
-
protected linkHoverButton: HTMLAnchorElement;
|
|
84
|
-
protected lastTouchEnd: TouchEvent | undefined;
|
|
85
|
-
protected lastMousePosition: { x: number, y: number } | undefined;
|
|
86
|
-
protected isAttachedCloseListener: boolean = false;
|
|
87
|
-
protected shown = false;
|
|
88
|
-
protected enhancedPreviewNode: Node | undefined;
|
|
89
|
-
override lastCwd = new URI();
|
|
90
|
-
|
|
91
|
-
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
|
|
92
|
-
@inject(RemoteConnectionProvider) protected readonly connectionProvider: ServiceConnectionProvider;
|
|
93
|
-
@inject(TerminalWidgetOptions) options: TerminalWidgetOptions;
|
|
94
|
-
@inject(ShellTerminalServerProxy) protected readonly shellTerminalServer: ShellTerminalServerProxy;
|
|
95
|
-
@inject(TerminalWatcher) protected readonly terminalWatcher: TerminalWatcher;
|
|
96
|
-
@inject(ILogger) @named('terminal') protected readonly logger: ILogger;
|
|
97
|
-
@inject('terminal-dom-id') override readonly id: string;
|
|
98
|
-
@inject(TerminalPreferences) protected readonly preferences: TerminalPreferences;
|
|
99
|
-
@inject(ContributionProvider) @named(TerminalContribution) protected readonly terminalContributionProvider: ContributionProvider<TerminalContribution>;
|
|
100
|
-
@inject(TerminalService) protected readonly terminalService: TerminalService;
|
|
101
|
-
@inject(TerminalSearchWidgetFactory) protected readonly terminalSearchBoxFactory: TerminalSearchWidgetFactory;
|
|
102
|
-
@inject(TerminalCopyOnSelectionHandler) protected readonly copyOnSelectionHandler: TerminalCopyOnSelectionHandler;
|
|
103
|
-
@inject(TerminalThemeService) protected readonly themeService: TerminalThemeService;
|
|
104
|
-
@inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder;
|
|
105
|
-
@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
|
|
106
|
-
@inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory;
|
|
107
|
-
|
|
108
|
-
protected _markdownRenderer: MarkdownRenderer | undefined;
|
|
109
|
-
protected get markdownRenderer(): MarkdownRenderer {
|
|
110
|
-
this._markdownRenderer ||= this.markdownRendererFactory();
|
|
111
|
-
return this._markdownRenderer;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
protected readonly onDidOpenEmitter = new Emitter<void>();
|
|
115
|
-
readonly onDidOpen: Event<void> = this.onDidOpenEmitter.event;
|
|
116
|
-
|
|
117
|
-
protected readonly onDidOpenFailureEmitter = new Emitter<void>();
|
|
118
|
-
readonly onDidOpenFailure: Event<void> = this.onDidOpenFailureEmitter.event;
|
|
119
|
-
|
|
120
|
-
protected readonly onSizeChangedEmitter = new Emitter<{ cols: number; rows: number; }>();
|
|
121
|
-
readonly onSizeChanged: Event<{ cols: number; rows: number; }> = this.onSizeChangedEmitter.event;
|
|
122
|
-
|
|
123
|
-
protected readonly onDataEmitter = new Emitter<string>();
|
|
124
|
-
readonly onData: Event<string> = this.onDataEmitter.event;
|
|
125
|
-
|
|
126
|
-
protected readonly onKeyEmitter = new Emitter<{ key: string, domEvent: KeyboardEvent }>();
|
|
127
|
-
readonly onKey: Event<{ key: string, domEvent: KeyboardEvent }> = this.onKeyEmitter.event;
|
|
128
|
-
|
|
129
|
-
protected readonly onMouseEnterLinkHoverEmitter = new Emitter<MouseEvent>();
|
|
130
|
-
readonly onMouseEnterLinkHover: Event<MouseEvent> = this.onMouseEnterLinkHoverEmitter.event;
|
|
131
|
-
|
|
132
|
-
protected readonly onMouseLeaveLinkHoverEmitter = new Emitter<MouseEvent>();
|
|
133
|
-
readonly onMouseLeaveLinkHover: Event<MouseEvent> = this.onMouseLeaveLinkHoverEmitter.event;
|
|
134
|
-
|
|
135
|
-
protected readonly toDisposeOnConnect = new DisposableCollection();
|
|
136
|
-
|
|
137
|
-
@postConstruct()
|
|
138
|
-
protected init(): void {
|
|
139
|
-
this.setTitle(this.options.title || TerminalWidgetImpl.LABEL);
|
|
140
|
-
|
|
141
|
-
if (this.options.iconClass) {
|
|
142
|
-
this.title.iconClass = this.options.iconClass;
|
|
143
|
-
} else {
|
|
144
|
-
this.title.iconClass = codicon('terminal');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (this.options.kind) {
|
|
148
|
-
this.terminalKind = this.options.kind;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (this.options.destroyTermOnClose === true) {
|
|
152
|
-
this.toDispose.push(Disposable.create(() =>
|
|
153
|
-
this.term.dispose()
|
|
154
|
-
));
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this.location = this.options.location || TerminalLocation.Panel;
|
|
158
|
-
|
|
159
|
-
this.title.closable = true;
|
|
160
|
-
this.addClass('terminal-container');
|
|
161
|
-
|
|
162
|
-
this.term = new Terminal({
|
|
163
|
-
cursorBlink: this.preferences['terminal.integrated.cursorBlinking'],
|
|
164
|
-
cursorStyle: this.
|
|
165
|
-
cursorWidth: this.preferences['terminal.integrated.cursorWidth'],
|
|
166
|
-
fontFamily: this.preferences['terminal.integrated.fontFamily'],
|
|
167
|
-
fontSize: this.preferences['terminal.integrated.fontSize'],
|
|
168
|
-
fontWeight: this.preferences['terminal.integrated.fontWeight'],
|
|
169
|
-
fontWeightBold: this.preferences['terminal.integrated.fontWeightBold'],
|
|
170
|
-
drawBoldTextInBrightColors: this.preferences['terminal.integrated.drawBoldTextInBrightColors'],
|
|
171
|
-
letterSpacing: this.preferences['terminal.integrated.letterSpacing'],
|
|
172
|
-
lineHeight: this.preferences['terminal.integrated.lineHeight'],
|
|
173
|
-
scrollback: this.preferences['terminal.integrated.scrollback'],
|
|
174
|
-
fastScrollSensitivity: this.preferences['terminal.integrated.fastScrollSensitivity'],
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.fitAddon
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
this.toDispose.push(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
this.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this.toDispose.push(this.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
this.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
this.
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
this.linkHover =
|
|
340
|
-
|
|
341
|
-
this.linkHover.style.
|
|
342
|
-
|
|
343
|
-
this.
|
|
344
|
-
this.
|
|
345
|
-
this.
|
|
346
|
-
this.linkHover.
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
this.
|
|
354
|
-
this.
|
|
355
|
-
|
|
356
|
-
this.
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
this.
|
|
368
|
-
this.
|
|
369
|
-
|
|
370
|
-
this.
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
this.linkHover.style.display = '
|
|
379
|
-
|
|
380
|
-
this.
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
this.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
rootURI
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
override
|
|
565
|
-
super.
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
super.
|
|
580
|
-
this.
|
|
581
|
-
}
|
|
582
|
-
protected override
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
protected
|
|
593
|
-
|
|
594
|
-
super.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
this.
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
this.term.
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
if (
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
//
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
)
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
if (
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2017 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { Terminal } from 'xterm';
|
|
18
|
+
import { FitAddon } from 'xterm-addon-fit';
|
|
19
|
+
import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
|
|
20
|
+
import { ContributionProvider, Disposable, Event, Emitter, ILogger, DisposableCollection, Channel, OS } from '@theia/core';
|
|
21
|
+
import {
|
|
22
|
+
Widget, Message, StatefulWidget, isFirefox, MessageLoop, KeyCode, codicon, ExtractableWidget, ContextMenuRenderer
|
|
23
|
+
} from '@theia/core/lib/browser';
|
|
24
|
+
import { isOSX } from '@theia/core/lib/common';
|
|
25
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
26
|
+
import { ShellTerminalServerProxy, IShellTerminalPreferences } from '../common/shell-terminal-protocol';
|
|
27
|
+
import { terminalsPath } from '../common/terminal-protocol';
|
|
28
|
+
import { IBaseTerminalServer, TerminalProcessInfo, TerminalExitReason } from '../common/base-terminal-protocol';
|
|
29
|
+
import { TerminalWatcher } from '../common/terminal-watcher';
|
|
30
|
+
import {
|
|
31
|
+
TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus, TerminalLocationOptions,
|
|
32
|
+
TerminalLocation
|
|
33
|
+
} from './base/terminal-widget';
|
|
34
|
+
import { Deferred } from '@theia/core/lib/common/promise-util';
|
|
35
|
+
import { TerminalPreferences } from './terminal-preferences';
|
|
36
|
+
import URI from '@theia/core/lib/common/uri';
|
|
37
|
+
import { TerminalService } from './base/terminal-service';
|
|
38
|
+
import { TerminalSearchWidgetFactory, TerminalSearchWidget } from './search/terminal-search-widget';
|
|
39
|
+
import { TerminalCopyOnSelectionHandler } from './terminal-copy-on-selection-handler';
|
|
40
|
+
import { TerminalThemeService } from './terminal-theme-service';
|
|
41
|
+
import { CommandLineOptions, ShellCommandBuilder } from '@theia/process/lib/common/shell-command-builder';
|
|
42
|
+
import { Key } from '@theia/core/lib/browser/keys';
|
|
43
|
+
import { nls } from '@theia/core/lib/common/nls';
|
|
44
|
+
import { TerminalMenus } from './terminal-frontend-contribution';
|
|
45
|
+
import debounce = require('p-debounce');
|
|
46
|
+
import { MarkdownString, MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering/markdown-string';
|
|
47
|
+
import { EnhancedPreviewWidget } from '@theia/core/lib/browser/widgets/enhanced-preview-widget';
|
|
48
|
+
import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
|
|
49
|
+
import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
|
|
50
|
+
|
|
51
|
+
export const TERMINAL_WIDGET_FACTORY_ID = 'terminal';
|
|
52
|
+
|
|
53
|
+
export interface TerminalWidgetFactoryOptions extends Partial<TerminalWidgetOptions> {
|
|
54
|
+
/* a unique string per terminal */
|
|
55
|
+
created: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const TerminalContribution = Symbol('TerminalContribution');
|
|
59
|
+
export interface TerminalContribution {
|
|
60
|
+
onCreate(term: TerminalWidgetImpl): void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@injectable()
|
|
64
|
+
export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget, ExtractableWidget, EnhancedPreviewWidget {
|
|
65
|
+
readonly isExtractable: boolean = true;
|
|
66
|
+
secondaryWindow: Window | undefined;
|
|
67
|
+
location: TerminalLocationOptions;
|
|
68
|
+
|
|
69
|
+
static LABEL = nls.localizeByDefault('Terminal');
|
|
70
|
+
|
|
71
|
+
exitStatus: TerminalExitStatus | undefined;
|
|
72
|
+
|
|
73
|
+
protected terminalKind = 'user';
|
|
74
|
+
protected _terminalId = -1;
|
|
75
|
+
protected readonly onTermDidClose = new Emitter<TerminalWidget>();
|
|
76
|
+
protected fitAddon: FitAddon;
|
|
77
|
+
protected term: Terminal;
|
|
78
|
+
protected searchBox: TerminalSearchWidget;
|
|
79
|
+
protected restored = false;
|
|
80
|
+
protected closeOnDispose = true;
|
|
81
|
+
protected waitForConnection: Deferred<Channel> | undefined;
|
|
82
|
+
protected linkHover: HTMLDivElement;
|
|
83
|
+
protected linkHoverButton: HTMLAnchorElement;
|
|
84
|
+
protected lastTouchEnd: TouchEvent | undefined;
|
|
85
|
+
protected lastMousePosition: { x: number, y: number } | undefined;
|
|
86
|
+
protected isAttachedCloseListener: boolean = false;
|
|
87
|
+
protected shown = false;
|
|
88
|
+
protected enhancedPreviewNode: Node | undefined;
|
|
89
|
+
override lastCwd = new URI();
|
|
90
|
+
|
|
91
|
+
@inject(WorkspaceService) protected readonly workspaceService: WorkspaceService;
|
|
92
|
+
@inject(RemoteConnectionProvider) protected readonly connectionProvider: ServiceConnectionProvider;
|
|
93
|
+
@inject(TerminalWidgetOptions) options: TerminalWidgetOptions;
|
|
94
|
+
@inject(ShellTerminalServerProxy) protected readonly shellTerminalServer: ShellTerminalServerProxy;
|
|
95
|
+
@inject(TerminalWatcher) protected readonly terminalWatcher: TerminalWatcher;
|
|
96
|
+
@inject(ILogger) @named('terminal') protected readonly logger: ILogger;
|
|
97
|
+
@inject('terminal-dom-id') override readonly id: string;
|
|
98
|
+
@inject(TerminalPreferences) protected readonly preferences: TerminalPreferences;
|
|
99
|
+
@inject(ContributionProvider) @named(TerminalContribution) protected readonly terminalContributionProvider: ContributionProvider<TerminalContribution>;
|
|
100
|
+
@inject(TerminalService) protected readonly terminalService: TerminalService;
|
|
101
|
+
@inject(TerminalSearchWidgetFactory) protected readonly terminalSearchBoxFactory: TerminalSearchWidgetFactory;
|
|
102
|
+
@inject(TerminalCopyOnSelectionHandler) protected readonly copyOnSelectionHandler: TerminalCopyOnSelectionHandler;
|
|
103
|
+
@inject(TerminalThemeService) protected readonly themeService: TerminalThemeService;
|
|
104
|
+
@inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder;
|
|
105
|
+
@inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
|
|
106
|
+
@inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory;
|
|
107
|
+
|
|
108
|
+
protected _markdownRenderer: MarkdownRenderer | undefined;
|
|
109
|
+
protected get markdownRenderer(): MarkdownRenderer {
|
|
110
|
+
this._markdownRenderer ||= this.markdownRendererFactory();
|
|
111
|
+
return this._markdownRenderer;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
protected readonly onDidOpenEmitter = new Emitter<void>();
|
|
115
|
+
readonly onDidOpen: Event<void> = this.onDidOpenEmitter.event;
|
|
116
|
+
|
|
117
|
+
protected readonly onDidOpenFailureEmitter = new Emitter<void>();
|
|
118
|
+
readonly onDidOpenFailure: Event<void> = this.onDidOpenFailureEmitter.event;
|
|
119
|
+
|
|
120
|
+
protected readonly onSizeChangedEmitter = new Emitter<{ cols: number; rows: number; }>();
|
|
121
|
+
readonly onSizeChanged: Event<{ cols: number; rows: number; }> = this.onSizeChangedEmitter.event;
|
|
122
|
+
|
|
123
|
+
protected readonly onDataEmitter = new Emitter<string>();
|
|
124
|
+
readonly onData: Event<string> = this.onDataEmitter.event;
|
|
125
|
+
|
|
126
|
+
protected readonly onKeyEmitter = new Emitter<{ key: string, domEvent: KeyboardEvent }>();
|
|
127
|
+
readonly onKey: Event<{ key: string, domEvent: KeyboardEvent }> = this.onKeyEmitter.event;
|
|
128
|
+
|
|
129
|
+
protected readonly onMouseEnterLinkHoverEmitter = new Emitter<MouseEvent>();
|
|
130
|
+
readonly onMouseEnterLinkHover: Event<MouseEvent> = this.onMouseEnterLinkHoverEmitter.event;
|
|
131
|
+
|
|
132
|
+
protected readonly onMouseLeaveLinkHoverEmitter = new Emitter<MouseEvent>();
|
|
133
|
+
readonly onMouseLeaveLinkHover: Event<MouseEvent> = this.onMouseLeaveLinkHoverEmitter.event;
|
|
134
|
+
|
|
135
|
+
protected readonly toDisposeOnConnect = new DisposableCollection();
|
|
136
|
+
|
|
137
|
+
@postConstruct()
|
|
138
|
+
protected init(): void {
|
|
139
|
+
this.setTitle(this.options.title || TerminalWidgetImpl.LABEL);
|
|
140
|
+
|
|
141
|
+
if (this.options.iconClass) {
|
|
142
|
+
this.title.iconClass = this.options.iconClass;
|
|
143
|
+
} else {
|
|
144
|
+
this.title.iconClass = codicon('terminal');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (this.options.kind) {
|
|
148
|
+
this.terminalKind = this.options.kind;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.options.destroyTermOnClose === true) {
|
|
152
|
+
this.toDispose.push(Disposable.create(() =>
|
|
153
|
+
this.term.dispose()
|
|
154
|
+
));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.location = this.options.location || TerminalLocation.Panel;
|
|
158
|
+
|
|
159
|
+
this.title.closable = true;
|
|
160
|
+
this.addClass('terminal-container');
|
|
161
|
+
|
|
162
|
+
this.term = new Terminal({
|
|
163
|
+
cursorBlink: this.preferences['terminal.integrated.cursorBlinking'],
|
|
164
|
+
cursorStyle: this.preferences['terminal.integrated.cursorStyle'] === 'line' ? 'bar' : this.preferences['terminal.integrated.cursorStyle'],
|
|
165
|
+
cursorWidth: this.preferences['terminal.integrated.cursorWidth'],
|
|
166
|
+
fontFamily: this.preferences['terminal.integrated.fontFamily'],
|
|
167
|
+
fontSize: this.preferences['terminal.integrated.fontSize'],
|
|
168
|
+
fontWeight: this.preferences['terminal.integrated.fontWeight'],
|
|
169
|
+
fontWeightBold: this.preferences['terminal.integrated.fontWeightBold'],
|
|
170
|
+
drawBoldTextInBrightColors: this.preferences['terminal.integrated.drawBoldTextInBrightColors'],
|
|
171
|
+
letterSpacing: this.preferences['terminal.integrated.letterSpacing'],
|
|
172
|
+
lineHeight: this.preferences['terminal.integrated.lineHeight'],
|
|
173
|
+
scrollback: this.preferences['terminal.integrated.scrollback'],
|
|
174
|
+
fastScrollSensitivity: this.preferences['terminal.integrated.fastScrollSensitivity'],
|
|
175
|
+
theme: this.themeService.theme
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
this.fitAddon = new FitAddon();
|
|
179
|
+
this.term.loadAddon(this.fitAddon);
|
|
180
|
+
|
|
181
|
+
this.initializeLinkHover();
|
|
182
|
+
|
|
183
|
+
this.toDispose.push(this.preferences.onPreferenceChanged(change => {
|
|
184
|
+
this.updateConfig();
|
|
185
|
+
this.needsResize = true;
|
|
186
|
+
this.update();
|
|
187
|
+
}));
|
|
188
|
+
|
|
189
|
+
this.toDispose.push(this.themeService.onDidChange(() => this.term.options.theme = this.themeService.theme));
|
|
190
|
+
this.attachCustomKeyEventHandler();
|
|
191
|
+
const titleChangeListenerDispose = this.term.onTitleChange((title: string) => {
|
|
192
|
+
if (this.options.useServerTitle) {
|
|
193
|
+
this.title.label = title;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
this.toDispose.push(titleChangeListenerDispose);
|
|
197
|
+
|
|
198
|
+
this.toDispose.push(this.terminalWatcher.onTerminalError(({ terminalId, error, attached }) => {
|
|
199
|
+
if (terminalId === this.terminalId) {
|
|
200
|
+
this.exitStatus = { code: undefined, reason: TerminalExitReason.Process };
|
|
201
|
+
this.logger.error(`The terminal process terminated. Cause: ${error}`);
|
|
202
|
+
if (!attached) {
|
|
203
|
+
this.dispose();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}));
|
|
207
|
+
this.toDispose.push(this.terminalWatcher.onTerminalExit(({ terminalId, code, reason, attached }) => {
|
|
208
|
+
if (terminalId === this.terminalId) {
|
|
209
|
+
if (reason) {
|
|
210
|
+
this.exitStatus = { code, reason };
|
|
211
|
+
} else {
|
|
212
|
+
this.exitStatus = { code, reason: TerminalExitReason.Process };
|
|
213
|
+
}
|
|
214
|
+
if (!attached) {
|
|
215
|
+
this.dispose();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}));
|
|
219
|
+
this.toDispose.push(this.toDisposeOnConnect);
|
|
220
|
+
this.toDispose.push(this.shellTerminalServer.onDidCloseConnection(() => {
|
|
221
|
+
const disposable = this.shellTerminalServer.onDidOpenConnection(() => {
|
|
222
|
+
disposable.dispose();
|
|
223
|
+
this.reconnectTerminalProcess();
|
|
224
|
+
});
|
|
225
|
+
this.toDispose.push(disposable);
|
|
226
|
+
}));
|
|
227
|
+
this.toDispose.push(this.onTermDidClose);
|
|
228
|
+
this.toDispose.push(this.onDidOpenEmitter);
|
|
229
|
+
this.toDispose.push(this.onDidOpenFailureEmitter);
|
|
230
|
+
this.toDispose.push(this.onSizeChangedEmitter);
|
|
231
|
+
this.toDispose.push(this.onDataEmitter);
|
|
232
|
+
this.toDispose.push(this.onKeyEmitter);
|
|
233
|
+
|
|
234
|
+
const touchEndListener = (event: TouchEvent) => {
|
|
235
|
+
if (this.node.contains(event.target as Node)) {
|
|
236
|
+
this.lastTouchEnd = event;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
document.addEventListener('touchend', touchEndListener, { passive: true });
|
|
240
|
+
this.onDispose(() => {
|
|
241
|
+
document.removeEventListener('touchend', touchEndListener);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const mouseListener = (event: MouseEvent) => {
|
|
245
|
+
this.lastMousePosition = { x: event.x, y: event.y };
|
|
246
|
+
};
|
|
247
|
+
this.node.addEventListener('mousemove', mouseListener);
|
|
248
|
+
this.onDispose(() => {
|
|
249
|
+
this.node.removeEventListener('mousemove', mouseListener);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const contextMenuListener = (event: MouseEvent) => {
|
|
253
|
+
event.preventDefault();
|
|
254
|
+
event.stopPropagation();
|
|
255
|
+
this.contextMenuRenderer.render({ menuPath: TerminalMenus.TERMINAL_CONTEXT_MENU, anchor: event });
|
|
256
|
+
};
|
|
257
|
+
this.node.addEventListener('contextmenu', contextMenuListener);
|
|
258
|
+
this.onDispose(() => this.node.removeEventListener('contextmenu', contextMenuListener));
|
|
259
|
+
|
|
260
|
+
this.toDispose.push(this.term.onSelectionChange(() => {
|
|
261
|
+
if (this.copyOnSelection) {
|
|
262
|
+
this.copyOnSelectionHandler.copy(this.term.getSelection());
|
|
263
|
+
}
|
|
264
|
+
}));
|
|
265
|
+
|
|
266
|
+
this.toDispose.push(this.term.onResize(data => {
|
|
267
|
+
this.onSizeChangedEmitter.fire(data);
|
|
268
|
+
}));
|
|
269
|
+
|
|
270
|
+
this.toDispose.push(this.term.onData(data => {
|
|
271
|
+
this.onDataEmitter.fire(data);
|
|
272
|
+
}));
|
|
273
|
+
|
|
274
|
+
this.toDispose.push(this.term.onBinary(data => {
|
|
275
|
+
this.onDataEmitter.fire(data);
|
|
276
|
+
}));
|
|
277
|
+
|
|
278
|
+
this.toDispose.push(this.term.onKey(data => {
|
|
279
|
+
this.onKeyEmitter.fire(data);
|
|
280
|
+
}));
|
|
281
|
+
|
|
282
|
+
for (const contribution of this.terminalContributionProvider.getContributions()) {
|
|
283
|
+
contribution.onCreate(this);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.searchBox = this.terminalSearchBoxFactory(this.term);
|
|
287
|
+
this.toDispose.push(this.searchBox);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
get kind(): 'user' | string {
|
|
291
|
+
return this.terminalKind;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
updateConfig(): void {
|
|
295
|
+
this.setCursorBlink(this.preferences.get('terminal.integrated.cursorBlinking'));
|
|
296
|
+
this.setCursorStyle(this.preferences.get('terminal.integrated.cursorStyle'));
|
|
297
|
+
this.setCursorWidth(this.preferences.get('terminal.integrated.cursorWidth'));
|
|
298
|
+
this.term.options.fontFamily = this.preferences.get('terminal.integrated.fontFamily');
|
|
299
|
+
this.term.options.fontSize = this.preferences.get('terminal.integrated.fontSize');
|
|
300
|
+
this.term.options.fontWeight = this.preferences.get('terminal.integrated.fontWeight');
|
|
301
|
+
this.term.options.fontWeightBold = this.preferences.get('terminal.integrated.fontWeightBold');
|
|
302
|
+
this.term.options.drawBoldTextInBrightColors = this.preferences.get('terminal.integrated.drawBoldTextInBrightColors');
|
|
303
|
+
this.term.options.letterSpacing = this.preferences.get('terminal.integrated.letterSpacing');
|
|
304
|
+
this.term.options.lineHeight = this.preferences.get('terminal.integrated.lineHeight');
|
|
305
|
+
this.term.options.scrollback = this.preferences.get('terminal.integrated.scrollback');
|
|
306
|
+
this.term.options.fastScrollSensitivity = this.preferences.get('terminal.integrated.fastScrollSensitivity');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private setCursorBlink(blink: boolean): void {
|
|
310
|
+
if (this.term.options.cursorBlink !== blink) {
|
|
311
|
+
this.term.options.cursorBlink = blink;
|
|
312
|
+
this.term.refresh(0, this.term.rows - 1);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private setCursorStyle(style: 'block' | 'underline' | 'bar' | 'line'): void {
|
|
317
|
+
if (this.term.options.cursorStyle !== style) {
|
|
318
|
+
this.term.options.cursorStyle = (style === 'line') ? 'bar' : style;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
private setCursorWidth(width: number): void {
|
|
323
|
+
if (this.term.options.cursorWidth !== width) {
|
|
324
|
+
this.term.options.cursorWidth = width;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
protected initializeLinkHover(): void {
|
|
329
|
+
this.linkHover = document.createElement('div');
|
|
330
|
+
this.linkHover.style.position = 'fixed';
|
|
331
|
+
this.linkHover.style.color = 'var(--theia-editorHoverWidget-foreground)';
|
|
332
|
+
this.linkHover.style.backgroundColor = 'var(--theia-editorHoverWidget-background)';
|
|
333
|
+
this.linkHover.style.borderColor = 'var(--theia-editorHoverWidget-border)';
|
|
334
|
+
this.linkHover.style.borderWidth = '0.5px';
|
|
335
|
+
this.linkHover.style.borderStyle = 'solid';
|
|
336
|
+
this.linkHover.style.padding = '5px';
|
|
337
|
+
// Above the xterm.js canvas layers:
|
|
338
|
+
// https://github.com/xtermjs/xterm.js/blob/ff790236c1b205469f17a21246141f512d844295/src/renderer/Renderer.ts#L41-L46
|
|
339
|
+
this.linkHover.style.zIndex = '10';
|
|
340
|
+
// Initially invisible:
|
|
341
|
+
this.linkHover.style.display = 'none';
|
|
342
|
+
|
|
343
|
+
this.linkHoverButton = document.createElement('a');
|
|
344
|
+
this.linkHoverButton.textContent = this.linkHoverMessage();
|
|
345
|
+
this.linkHoverButton.style.cursor = 'pointer';
|
|
346
|
+
this.linkHover.appendChild(this.linkHoverButton);
|
|
347
|
+
|
|
348
|
+
const cmdCtrl = isOSX ? 'cmd' : 'ctrl';
|
|
349
|
+
const cmdHint = document.createTextNode(` (${nls.localizeByDefault(`${cmdCtrl} + click`)})`);
|
|
350
|
+
this.linkHover.appendChild(cmdHint);
|
|
351
|
+
|
|
352
|
+
const onMouseEnter = (mouseEvent: MouseEvent) => this.onMouseEnterLinkHoverEmitter.fire(mouseEvent);
|
|
353
|
+
this.linkHover.addEventListener('mouseenter', onMouseEnter);
|
|
354
|
+
this.toDispose.push(Disposable.create(() => this.linkHover.removeEventListener('mouseenter', onMouseEnter)));
|
|
355
|
+
|
|
356
|
+
const onMouseLeave = (mouseEvent: MouseEvent) => this.onMouseLeaveLinkHoverEmitter.fire(mouseEvent);
|
|
357
|
+
this.linkHover.addEventListener('mouseleave', onMouseLeave);
|
|
358
|
+
this.toDispose.push(Disposable.create(() => this.linkHover.removeEventListener('mouseleave', onMouseLeave)));
|
|
359
|
+
|
|
360
|
+
this.node.appendChild(this.linkHover);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
showLinkHover(invokeAction: (event: MouseEvent) => void, x: number, y: number, message?: string): void {
|
|
364
|
+
const mouseY = this.lastMousePosition?.y ?? y;
|
|
365
|
+
const mouseX = this.lastMousePosition?.x ?? x;
|
|
366
|
+
this.linkHoverButton.textContent = this.linkHoverMessage(message);
|
|
367
|
+
this.linkHoverButton.onclick = (mouseEvent: MouseEvent) => invokeAction(mouseEvent);
|
|
368
|
+
this.linkHover.style.display = 'inline';
|
|
369
|
+
this.linkHover.style.top = `${mouseY - 30}px`;
|
|
370
|
+
this.linkHover.style.left = `${mouseX - 60}px`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
protected linkHoverMessage(message?: string): string {
|
|
374
|
+
return message ?? nls.localizeByDefault('Follow link');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
hideLinkHover(): void {
|
|
378
|
+
this.linkHover.style.display = 'none';
|
|
379
|
+
// eslint-disable-next-line no-null/no-null
|
|
380
|
+
this.linkHoverButton.onclick = null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
getTerminal(): Terminal {
|
|
384
|
+
return this.term;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
getSearchBox(): TerminalSearchWidget {
|
|
388
|
+
return this.searchBox;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
protected override onCloseRequest(msg: Message): void {
|
|
392
|
+
this.exitStatus = { code: undefined, reason: TerminalExitReason.User };
|
|
393
|
+
super.onCloseRequest(msg);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
get dimensions(): TerminalDimensions {
|
|
397
|
+
return {
|
|
398
|
+
cols: this.term.cols,
|
|
399
|
+
rows: this.term.rows,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
get cwd(): Promise<URI> {
|
|
404
|
+
if (!IBaseTerminalServer.validateId(this.terminalId)) {
|
|
405
|
+
return Promise.reject(new Error('terminal is not started'));
|
|
406
|
+
}
|
|
407
|
+
if (this.terminalService.getById(this.id)) {
|
|
408
|
+
return this.shellTerminalServer.getCwdURI(this.terminalId)
|
|
409
|
+
.then(cwdUrl => {
|
|
410
|
+
this.lastCwd = new URI(cwdUrl);
|
|
411
|
+
return this.lastCwd;
|
|
412
|
+
}).catch(() => this.lastCwd);
|
|
413
|
+
}
|
|
414
|
+
return Promise.resolve(new URI());
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
get processId(): Promise<number> {
|
|
418
|
+
if (!IBaseTerminalServer.validateId(this.terminalId)) {
|
|
419
|
+
return Promise.reject(new Error('terminal is not started'));
|
|
420
|
+
}
|
|
421
|
+
return this.shellTerminalServer.getProcessId(this.terminalId);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
get processInfo(): Promise<TerminalProcessInfo> {
|
|
425
|
+
if (!IBaseTerminalServer.validateId(this.terminalId)) {
|
|
426
|
+
return Promise.reject(new Error('terminal is not started'));
|
|
427
|
+
}
|
|
428
|
+
return this.shellTerminalServer.getProcessInfo(this.terminalId);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
get envVarCollectionDescriptionsByExtension(): Promise<Map<string, (string | MarkdownString | undefined)[]>> {
|
|
432
|
+
if (!IBaseTerminalServer.validateId(this.terminalId)) {
|
|
433
|
+
return Promise.reject(new Error('terminal is not started'));
|
|
434
|
+
}
|
|
435
|
+
return this.shellTerminalServer.getEnvVarCollectionDescriptionsByExtension(this.terminalId);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
get terminalId(): number {
|
|
439
|
+
return this._terminalId;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
get lastTouchEndEvent(): TouchEvent | undefined {
|
|
443
|
+
return this.lastTouchEnd;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
get hiddenFromUser(): boolean {
|
|
447
|
+
if (this.shown) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
return this.options.hideFromUser ?? false;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
get transient(): boolean {
|
|
454
|
+
// The terminal is transient if session persistence is disabled or it's explicitly marked as transient
|
|
455
|
+
return !this.preferences['terminal.integrated.enablePersistentSessions'] || !!this.options.isTransient;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
onDispose(onDispose: () => void): void {
|
|
459
|
+
this.toDispose.push(Disposable.create(onDispose));
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
clearOutput(): void {
|
|
463
|
+
this.term.clear();
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
selectAll(): void {
|
|
467
|
+
this.term.selectAll();
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
async hasChildProcesses(): Promise<boolean> {
|
|
471
|
+
return this.shellTerminalServer.hasChildProcesses(await this.processId);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
storeState(): object {
|
|
475
|
+
this.closeOnDispose = false;
|
|
476
|
+
if (this.transient || this.options.isPseudoTerminal) {
|
|
477
|
+
return {};
|
|
478
|
+
}
|
|
479
|
+
return { terminalId: this.terminalId, titleLabel: this.title.label };
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
restoreState(oldState: object): void {
|
|
483
|
+
// transient terminals and pseudo terminals are not restored
|
|
484
|
+
if (this.transient || this.options.isPseudoTerminal) {
|
|
485
|
+
this.dispose();
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (this.restored === false) {
|
|
489
|
+
const state = oldState as { terminalId: number, titleLabel: string };
|
|
490
|
+
/* This is a workaround to issue #879 */
|
|
491
|
+
this.restored = true;
|
|
492
|
+
this.title.label = state.titleLabel;
|
|
493
|
+
this.start(state.terminalId);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Create a new shell terminal in the back-end and attach it to a
|
|
499
|
+
* new terminal widget.
|
|
500
|
+
* If id is provided attach to the terminal for this id.
|
|
501
|
+
*/
|
|
502
|
+
async start(id?: number): Promise<number> {
|
|
503
|
+
this._terminalId = typeof id !== 'number' ? await this.createTerminal() : await this.attachTerminal(id);
|
|
504
|
+
this.resizeTerminalProcess();
|
|
505
|
+
this.connectTerminalProcess();
|
|
506
|
+
if (IBaseTerminalServer.validateId(this.terminalId)) {
|
|
507
|
+
this.onDidOpenEmitter.fire(undefined);
|
|
508
|
+
await this.shellTerminalServer.onAttachAttempted(this._terminalId);
|
|
509
|
+
return this.terminalId;
|
|
510
|
+
}
|
|
511
|
+
this.onDidOpenFailureEmitter.fire(undefined);
|
|
512
|
+
throw new Error('Failed to start terminal' + (id ? ` for id: ${id}.` : '.'));
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
protected async attachTerminal(id: number): Promise<number> {
|
|
516
|
+
const terminalId = await this.shellTerminalServer.attach(id);
|
|
517
|
+
if (IBaseTerminalServer.validateId(terminalId)) {
|
|
518
|
+
// reset exit status if a new terminal process is attached
|
|
519
|
+
this.exitStatus = undefined;
|
|
520
|
+
return terminalId;
|
|
521
|
+
}
|
|
522
|
+
this.logger.warn(`Failed attaching to terminal id ${id}, the terminal is most likely gone. Starting up a new terminal instead.`);
|
|
523
|
+
if (this.kind === 'user') {
|
|
524
|
+
return this.createTerminal();
|
|
525
|
+
} else {
|
|
526
|
+
return -1;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
protected async createTerminal(): Promise<number> {
|
|
531
|
+
let rootURI = this.options.cwd?.toString();
|
|
532
|
+
if (!rootURI) {
|
|
533
|
+
const root = (await this.workspaceService.roots)[0];
|
|
534
|
+
rootURI = root?.resource?.toString();
|
|
535
|
+
}
|
|
536
|
+
const { cols, rows } = this.term;
|
|
537
|
+
|
|
538
|
+
const terminalId = await this.shellTerminalServer.create({
|
|
539
|
+
shell: this.options.shellPath || this.shellPreferences.shell[OS.backend.type()],
|
|
540
|
+
args: this.options.shellArgs || this.shellPreferences.shellArgs[OS.backend.type()],
|
|
541
|
+
env: this.options.env,
|
|
542
|
+
strictEnv: this.options.strictEnv,
|
|
543
|
+
isPseudo: this.options.isPseudoTerminal,
|
|
544
|
+
rootURI,
|
|
545
|
+
cols,
|
|
546
|
+
rows
|
|
547
|
+
});
|
|
548
|
+
if (IBaseTerminalServer.validateId(terminalId)) {
|
|
549
|
+
return terminalId;
|
|
550
|
+
}
|
|
551
|
+
throw new Error('Error creating terminal widget, see the backend error log for more information.');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
override processMessage(msg: Message): void {
|
|
555
|
+
super.processMessage(msg);
|
|
556
|
+
switch (msg.type) {
|
|
557
|
+
case 'fit-request':
|
|
558
|
+
this.onFitRequest(msg);
|
|
559
|
+
break;
|
|
560
|
+
default:
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
protected override onFitRequest(msg: Message): void {
|
|
565
|
+
super.onFitRequest(msg);
|
|
566
|
+
MessageLoop.sendMessage(this, Widget.ResizeMessage.UnknownSize);
|
|
567
|
+
}
|
|
568
|
+
protected override onActivateRequest(msg: Message): void {
|
|
569
|
+
super.onActivateRequest(msg);
|
|
570
|
+
this.term.focus();
|
|
571
|
+
}
|
|
572
|
+
protected override onAfterShow(msg: Message): void {
|
|
573
|
+
super.onAfterShow(msg);
|
|
574
|
+
this.update();
|
|
575
|
+
this.shown = true;
|
|
576
|
+
}
|
|
577
|
+
protected override onAfterAttach(msg: Message): void {
|
|
578
|
+
Widget.attach(this.searchBox, this.node);
|
|
579
|
+
super.onAfterAttach(msg);
|
|
580
|
+
this.update();
|
|
581
|
+
}
|
|
582
|
+
protected override onBeforeDetach(msg: Message): void {
|
|
583
|
+
Widget.detach(this.searchBox);
|
|
584
|
+
super.onBeforeDetach(msg);
|
|
585
|
+
}
|
|
586
|
+
protected override onResize(msg: Widget.ResizeMessage): void {
|
|
587
|
+
super.onResize(msg);
|
|
588
|
+
this.needsResize = true;
|
|
589
|
+
this.update();
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
protected needsResize = true;
|
|
593
|
+
protected override onUpdateRequest(msg: Message): void {
|
|
594
|
+
super.onUpdateRequest(msg);
|
|
595
|
+
if (!this.isVisible || !this.isAttached) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
this.open();
|
|
600
|
+
|
|
601
|
+
if (this.needsResize) {
|
|
602
|
+
this.resizeTerminal();
|
|
603
|
+
this.needsResize = false;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Device status code emitted by Xterm.js
|
|
608
|
+
// Check: https://github.com/xtermjs/xterm.js/blob/release/3.14/src/InputHandler.ts#L1055-L1082
|
|
609
|
+
protected readonly deviceStatusCodes = new Set(['\u001B[>0;276;0c', '\u001B[>85;95;0c', '\u001B[>83;40003;0c', '\u001B[?1;2c', '\u001B[?6c']);
|
|
610
|
+
|
|
611
|
+
protected connectTerminalProcess(): void {
|
|
612
|
+
if (typeof this.terminalId !== 'number') {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (this.options.isPseudoTerminal) {
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
this.toDisposeOnConnect.dispose();
|
|
619
|
+
this.toDispose.push(this.toDisposeOnConnect);
|
|
620
|
+
const waitForConnection = this.waitForConnection = new Deferred<Channel>();
|
|
621
|
+
this.connectionProvider.listen(
|
|
622
|
+
`${terminalsPath}/${this.terminalId}`,
|
|
623
|
+
(path, connection) => {
|
|
624
|
+
connection.onMessage(e => {
|
|
625
|
+
this.write(e().readString());
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Excludes the device status code emitted by Xterm.js
|
|
629
|
+
const sendData = (data?: string) => {
|
|
630
|
+
if (data && !this.deviceStatusCodes.has(data) && !this.disableEnterWhenAttachCloseListener()) {
|
|
631
|
+
connection.getWriteBuffer().writeString(data).commit();
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const disposable = new DisposableCollection();
|
|
636
|
+
disposable.push(this.term.onData(sendData));
|
|
637
|
+
disposable.push(this.term.onBinary(sendData));
|
|
638
|
+
|
|
639
|
+
connection.onClose(() => disposable.dispose());
|
|
640
|
+
|
|
641
|
+
if (waitForConnection) {
|
|
642
|
+
waitForConnection.resolve(connection);
|
|
643
|
+
}
|
|
644
|
+
}, false);
|
|
645
|
+
}
|
|
646
|
+
protected async reconnectTerminalProcess(): Promise<void> {
|
|
647
|
+
if (this.options.isPseudoTerminal) {
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (typeof this.terminalId === 'number') {
|
|
651
|
+
await this.start(this.terminalId);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
protected termOpened = false;
|
|
656
|
+
protected initialData = '';
|
|
657
|
+
protected open(): void {
|
|
658
|
+
if (this.termOpened) {
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
this.term.open(this.node);
|
|
662
|
+
|
|
663
|
+
interface ViewportType {
|
|
664
|
+
register(d: Disposable): void;
|
|
665
|
+
_refreshAnimationFrame: number | null;
|
|
666
|
+
_coreBrowserService: {
|
|
667
|
+
window: Window;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Workaround for https://github.com/xtermjs/xterm.js/issues/4775. Can be removed for releases > 5.3.0
|
|
672
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
673
|
+
const viewPort: ViewportType = (this.term as any)._core.viewport;
|
|
674
|
+
viewPort.register(Disposable.create(() => {
|
|
675
|
+
if (typeof viewPort._refreshAnimationFrame === 'number') {
|
|
676
|
+
viewPort._coreBrowserService.window.cancelAnimationFrame(viewPort._refreshAnimationFrame);
|
|
677
|
+
}
|
|
678
|
+
}));
|
|
679
|
+
|
|
680
|
+
if (isFirefox) {
|
|
681
|
+
// monkey patching intersection observer handling for secondary window support
|
|
682
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
683
|
+
const renderService: any = (this.term as any)._core._renderService;
|
|
684
|
+
|
|
685
|
+
const originalFunc: (entry: IntersectionObserverEntry) => void = renderService._handleIntersectionChange.bind(renderService);
|
|
686
|
+
const replacement = function (entry: IntersectionObserverEntry): void {
|
|
687
|
+
if (entry.target.ownerDocument !== document) {
|
|
688
|
+
// in Firefox, the intersection observer always reports the widget as non-intersecting if the dom element
|
|
689
|
+
// is in a different document from when the IntersectionObserver started observing. Since we know
|
|
690
|
+
// that the widget is always "visible" when in a secondary window, so we refresh the rows ourselves
|
|
691
|
+
const patchedEvent: IntersectionObserverEntry = {
|
|
692
|
+
...entry,
|
|
693
|
+
isIntersecting: true,
|
|
694
|
+
};
|
|
695
|
+
originalFunc(patchedEvent);
|
|
696
|
+
} else {
|
|
697
|
+
originalFunc(entry);
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
renderService._handleIntersectionChange = replacement.bind(renderService);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
if (this.initialData) {
|
|
705
|
+
this.term.write(this.initialData);
|
|
706
|
+
}
|
|
707
|
+
this.termOpened = true;
|
|
708
|
+
this.initialData = '';
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
write(data: string): void {
|
|
712
|
+
if (this.termOpened) {
|
|
713
|
+
this.term.write(data);
|
|
714
|
+
} else {
|
|
715
|
+
this.initialData += data;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
resize(cols: number, rows: number): void {
|
|
720
|
+
this.term.resize(cols, rows);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
sendText(text: string): void {
|
|
724
|
+
if (this.waitForConnection) {
|
|
725
|
+
this.waitForConnection.promise.then(connection =>
|
|
726
|
+
connection.getWriteBuffer().writeString(text).commit()
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
async executeCommand(commandOptions: CommandLineOptions): Promise<void> {
|
|
732
|
+
this.sendText(this.shellCommandBuilder.buildCommand(await this.processInfo, commandOptions) + OS.backend.EOL);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
scrollLineUp(): void {
|
|
736
|
+
this.term.scrollLines(-1);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
scrollLineDown(): void {
|
|
740
|
+
this.term.scrollLines(1);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
scrollToTop(): void {
|
|
744
|
+
this.term.scrollToTop();
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
scrollToBottom(): void {
|
|
748
|
+
this.term.scrollToBottom();
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
scrollPageUp(): void {
|
|
752
|
+
this.term.scrollPages(-1);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
scrollPageDown(): void {
|
|
756
|
+
this.term.scrollPages(1);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
resetTerminal(): void {
|
|
760
|
+
this.term.reset();
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
writeLine(text: string): void {
|
|
764
|
+
this.term.writeln(text);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
get onTerminalDidClose(): Event<TerminalWidget> {
|
|
768
|
+
return this.onTermDidClose.event;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
override dispose(): void {
|
|
772
|
+
if (this.closeOnDispose === true && typeof this.terminalId === 'number' && !this.exitStatus) {
|
|
773
|
+
// Close the backend terminal only when explicitly closing the terminal
|
|
774
|
+
// a refresh for example won't close it.
|
|
775
|
+
this.shellTerminalServer.close(this.terminalId);
|
|
776
|
+
// Exit status is set when terminal is closed by user or by process, so most likely an extension closed it.
|
|
777
|
+
this.exitStatus = { code: undefined, reason: TerminalExitReason.Extension };
|
|
778
|
+
}
|
|
779
|
+
if (this.exitStatus) {
|
|
780
|
+
this.onTermDidClose.fire(this);
|
|
781
|
+
}
|
|
782
|
+
if (this.enhancedPreviewNode) {
|
|
783
|
+
// don't use preview node anymore. rendered markdown will be disposed on super call
|
|
784
|
+
this.enhancedPreviewNode = undefined;
|
|
785
|
+
}
|
|
786
|
+
super.dispose();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
protected resizeTerminal = debounce(() => this.doResizeTerminal(), 50);
|
|
790
|
+
|
|
791
|
+
protected doResizeTerminal(): void {
|
|
792
|
+
if (this.isDisposed) {
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
const geo = this.fitAddon.proposeDimensions();
|
|
796
|
+
if (geo) {
|
|
797
|
+
const cols = geo.cols;
|
|
798
|
+
const rows = geo.rows - 1; // subtract one row for margin
|
|
799
|
+
this.term.resize(cols, rows);
|
|
800
|
+
|
|
801
|
+
this.resizeTerminalProcess();
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
protected resizeTerminalProcess(): void {
|
|
806
|
+
if (this.options.isPseudoTerminal) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
if (!IBaseTerminalServer.validateId(this.terminalId)
|
|
810
|
+
|| this.exitStatus
|
|
811
|
+
|| !this.terminalService.getById(this.id)
|
|
812
|
+
) {
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
815
|
+
const { cols, rows } = this.term;
|
|
816
|
+
this.shellTerminalServer.resize(this.terminalId, cols, rows);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
protected get enableCopy(): boolean {
|
|
820
|
+
return this.preferences['terminal.enableCopy'];
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
protected get enablePaste(): boolean {
|
|
824
|
+
return this.preferences['terminal.enablePaste'];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
protected get shellPreferences(): IShellTerminalPreferences {
|
|
828
|
+
return {
|
|
829
|
+
shell: {
|
|
830
|
+
Windows: this.preferences['terminal.integrated.shell.windows'] ?? undefined,
|
|
831
|
+
Linux: this.preferences['terminal.integrated.shell.linux'] ?? undefined,
|
|
832
|
+
OSX: this.preferences['terminal.integrated.shell.osx'] ?? undefined,
|
|
833
|
+
},
|
|
834
|
+
shellArgs: {
|
|
835
|
+
Windows: this.preferences['terminal.integrated.shellArgs.windows'],
|
|
836
|
+
Linux: this.preferences['terminal.integrated.shellArgs.linux'],
|
|
837
|
+
OSX: this.preferences['terminal.integrated.shellArgs.osx'],
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
protected customKeyHandler(event: KeyboardEvent): boolean {
|
|
843
|
+
const keyBindings = KeyCode.createKeyCode(event).toString();
|
|
844
|
+
const ctrlCmdCopy = (isOSX && keyBindings === 'meta+c') || (!isOSX && keyBindings === 'ctrl+c');
|
|
845
|
+
const ctrlCmdPaste = (isOSX && keyBindings === 'meta+v') || (!isOSX && keyBindings === 'ctrl+v');
|
|
846
|
+
if (ctrlCmdCopy && this.enableCopy && this.term.hasSelection()) {
|
|
847
|
+
return false;
|
|
848
|
+
}
|
|
849
|
+
if (ctrlCmdPaste && this.enablePaste) {
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
return true;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
protected get copyOnSelection(): boolean {
|
|
856
|
+
return this.preferences['terminal.integrated.copyOnSelection'];
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
protected attachCustomKeyEventHandler(): void {
|
|
860
|
+
this.term.attachCustomKeyEventHandler(e => this.customKeyHandler(e));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
setTitle(title: string): void {
|
|
864
|
+
this.title.caption = title;
|
|
865
|
+
this.title.label = title;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
waitOnExit(waitOnExit?: boolean | string): void {
|
|
869
|
+
if (waitOnExit) {
|
|
870
|
+
if (typeof waitOnExit === 'string') {
|
|
871
|
+
let message = waitOnExit;
|
|
872
|
+
// Bold the message and add an extra new line to make it stand out from the rest of the output
|
|
873
|
+
message = `\r\n\x1b[1m${message}\x1b[0m`;
|
|
874
|
+
this.write(message);
|
|
875
|
+
}
|
|
876
|
+
this.attachPressEnterKeyToCloseListener(this.term);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
this.dispose();
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
private attachPressEnterKeyToCloseListener(term: Terminal): void {
|
|
883
|
+
if (term.textarea) {
|
|
884
|
+
this.isAttachedCloseListener = true;
|
|
885
|
+
this.addKeyListener(term.textarea, Key.ENTER, (event: KeyboardEvent) => {
|
|
886
|
+
this.dispose();
|
|
887
|
+
this.isAttachedCloseListener = false;
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
private disableEnterWhenAttachCloseListener(): boolean {
|
|
893
|
+
return this.isAttachedCloseListener;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
getEnhancedPreviewNode(): Node | undefined {
|
|
897
|
+
if (this.enhancedPreviewNode) {
|
|
898
|
+
return this.enhancedPreviewNode;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
this.enhancedPreviewNode = document.createElement('div');
|
|
902
|
+
|
|
903
|
+
Promise.all([this.envVarCollectionDescriptionsByExtension, this.processId, this.processInfo])
|
|
904
|
+
.then((values: [Map<string, (string | MarkdownString | undefined)[]>, number, TerminalProcessInfo]) => {
|
|
905
|
+
const extensions = values[0];
|
|
906
|
+
const processId = values[1];
|
|
907
|
+
const processInfo = values[2];
|
|
908
|
+
|
|
909
|
+
const markdown = new MarkdownStringImpl();
|
|
910
|
+
markdown.appendMarkdown('Process ID: ' + processId + '\\\n');
|
|
911
|
+
markdown.appendMarkdown('Command line: ' +
|
|
912
|
+
processInfo.executable +
|
|
913
|
+
' ' +
|
|
914
|
+
processInfo.arguments.join(' ') +
|
|
915
|
+
'\n\n---\n\n');
|
|
916
|
+
markdown.appendMarkdown('The following extensions have contributed to this terminal\'s environment:\n');
|
|
917
|
+
extensions.forEach((arr, key) => {
|
|
918
|
+
arr.forEach(value => {
|
|
919
|
+
if (value === undefined) {
|
|
920
|
+
markdown.appendMarkdown('* ' + key + '\n');
|
|
921
|
+
} else if (typeof value === 'string') {
|
|
922
|
+
markdown.appendMarkdown('* ' + key + ': ' + value + '\n');
|
|
923
|
+
} else {
|
|
924
|
+
markdown.appendMarkdown('* ' + key + ': ' + value.value + '\n');
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
const enhancedPreviewNode = this.enhancedPreviewNode;
|
|
930
|
+
if (!this.isDisposed && enhancedPreviewNode) {
|
|
931
|
+
const result = this.markdownRenderer.render(markdown);
|
|
932
|
+
this.toDispose.push(result);
|
|
933
|
+
enhancedPreviewNode.appendChild(result.element);
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
|
|
937
|
+
return this.enhancedPreviewNode;
|
|
938
|
+
}
|
|
939
|
+
}
|