@theia/terminal 1.70.0-next.71 → 1.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/browser/base/terminal-widget.d.ts +40 -1
  2. package/lib/browser/base/terminal-widget.d.ts.map +1 -1
  3. package/lib/browser/base/terminal-widget.js.map +1 -1
  4. package/lib/browser/terminal-command-history.d.ts +27 -0
  5. package/lib/browser/terminal-command-history.d.ts.map +1 -0
  6. package/lib/browser/terminal-command-history.js +76 -0
  7. package/lib/browser/terminal-command-history.js.map +1 -0
  8. package/lib/browser/terminal-frontend-contribution.d.ts +1 -0
  9. package/lib/browser/terminal-frontend-contribution.d.ts.map +1 -1
  10. package/lib/browser/terminal-frontend-contribution.js +20 -0
  11. package/lib/browser/terminal-frontend-contribution.js.map +1 -1
  12. package/lib/browser/terminal-frontend-module.d.ts.map +1 -1
  13. package/lib/browser/terminal-frontend-module.js +3 -0
  14. package/lib/browser/terminal-frontend-module.js.map +1 -1
  15. package/lib/browser/terminal-widget-impl.d.ts +31 -2
  16. package/lib/browser/terminal-widget-impl.d.ts.map +1 -1
  17. package/lib/browser/terminal-widget-impl.js +147 -2
  18. package/lib/browser/terminal-widget-impl.js.map +1 -1
  19. package/lib/common/shell-terminal-protocol.d.ts +6 -0
  20. package/lib/common/shell-terminal-protocol.d.ts.map +1 -1
  21. package/lib/common/shell-terminal-protocol.js.map +1 -1
  22. package/lib/common/terminal-preferences.d.ts +2 -0
  23. package/lib/common/terminal-preferences.d.ts.map +1 -1
  24. package/lib/common/terminal-preferences.js +12 -0
  25. package/lib/common/terminal-preferences.js.map +1 -1
  26. package/lib/node/shell-integration-injector.d.ts +15 -0
  27. package/lib/node/shell-integration-injector.d.ts.map +1 -0
  28. package/lib/node/shell-integration-injector.js +97 -0
  29. package/lib/node/shell-integration-injector.js.map +1 -0
  30. package/lib/node/shell-process.d.ts +6 -0
  31. package/lib/node/shell-process.d.ts.map +1 -1
  32. package/lib/node/shell-process.js.map +1 -1
  33. package/lib/node/terminal-backend-module.d.ts.map +1 -1
  34. package/lib/node/terminal-backend-module.js +7 -1
  35. package/lib/node/terminal-backend-module.js.map +1 -1
  36. package/package.json +10 -10
  37. package/src/browser/base/terminal-widget.ts +52 -1
  38. package/src/browser/style/terminal.css +7 -0
  39. package/src/browser/terminal-command-history.ts +83 -0
  40. package/src/browser/terminal-frontend-contribution.ts +20 -0
  41. package/src/browser/terminal-frontend-module.ts +6 -0
  42. package/src/browser/terminal-widget-impl.ts +171 -4
  43. package/src/common/shell-terminal-protocol.ts +6 -0
  44. package/src/common/terminal-preferences.ts +14 -0
  45. package/src/node/shell-integration-injector.ts +94 -0
  46. package/src/node/shell-integrations/bash/bash-integration.bash +86 -0
  47. package/src/node/shell-integrations/bash/command-block-support.bash +195 -0
  48. package/src/node/shell-integrations/zsh/command-block-support.zsh +103 -0
  49. package/src/node/shell-integrations/zsh/zdotdir/.zlogin +45 -0
  50. package/src/node/shell-integrations/zsh/zdotdir/.zprofile +27 -0
  51. package/src/node/shell-integrations/zsh/zdotdir/.zshenv +56 -0
  52. package/src/node/shell-integrations/zsh/zdotdir/.zshrc +46 -0
  53. package/src/node/shell-integrations/zsh/zdotdir/source-original.zsh +61 -0
  54. package/src/node/shell-integrations/zsh/zsh-integration.zsh +28 -0
  55. package/src/node/shell-process.ts +6 -0
  56. package/src/node/terminal-backend-module.ts +9 -1
@@ -14,7 +14,7 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { Terminal } from 'xterm';
17
+ import { Terminal, IMarker } from 'xterm';
18
18
  import { FitAddon } from 'xterm-addon-fit';
19
19
  import { WebglAddon } from 'xterm-addon-webgl';
20
20
  import { inject, injectable, named, postConstruct } from '@theia/core/shared/inversify';
@@ -32,7 +32,9 @@ import { TerminalWatcher } from '../common/terminal-watcher';
32
32
  import {
33
33
  TerminalWidgetOptions, TerminalWidget, TerminalDimensions, TerminalExitStatus, TerminalLocationOptions,
34
34
  TerminalLocation,
35
- TerminalBuffer
35
+ TerminalBuffer,
36
+ TerminalBlock,
37
+ TerminalCommandHistoryState
36
38
  } from './base/terminal-widget';
37
39
  import { Deferred } from '@theia/core/lib/common/promise-util';
38
40
  import { TerminalPreferences } from '../common/terminal-preferences';
@@ -52,6 +54,7 @@ import { MarkdownRenderer, MarkdownRendererFactory } from '@theia/core/lib/brows
52
54
  import { RemoteConnectionProvider, ServiceConnectionProvider } from '@theia/core/lib/browser/messaging/service-connection-provider';
53
55
  import { ColorRegistry } from '@theia/core/lib/browser/color-registry';
54
56
  import { cleanTerminalTitle, guessShellTypeFromExecutable } from '../common/shell-type';
57
+ import { TerminalCommandHistoryStateFactory } from './terminal-command-history';
55
58
 
56
59
  export const TERMINAL_WIDGET_FACTORY_ID = 'terminal';
57
60
 
@@ -143,6 +146,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
143
146
  @inject(ShellCommandBuilder) protected readonly shellCommandBuilder: ShellCommandBuilder;
144
147
  @inject(ContextMenuRenderer) protected readonly contextMenuRenderer: ContextMenuRenderer;
145
148
  @inject(MarkdownRendererFactory) protected readonly markdownRendererFactory: MarkdownRendererFactory;
149
+ @inject(TerminalCommandHistoryStateFactory) protected readonly commandHistoryStateFactory: TerminalCommandHistoryStateFactory;
146
150
 
147
151
  protected _markdownRenderer: MarkdownRenderer | undefined;
148
152
  protected get markdownRenderer(): MarkdownRenderer {
@@ -179,6 +183,12 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
179
183
 
180
184
  protected readonly toDisposeOnConnect = new DisposableCollection();
181
185
 
186
+ protected readonly commandSeparatorDecorations = new DisposableCollection();
187
+
188
+ protected readonly toDisposeOnCommandHistory = new DisposableCollection();
189
+ protected outputStartMarker: IMarker | undefined;
190
+ protected promptStartMarker: IMarker | undefined;
191
+
182
192
  private _buffer: TerminalBuffer;
183
193
  override get buffer(): TerminalBuffer {
184
194
  return this._buffer;
@@ -186,6 +196,13 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
186
196
 
187
197
  private _currentTerminalOutput: string[];
188
198
 
199
+ private _commandHistoryState?: TerminalCommandHistoryState;
200
+ override get commandHistoryState(): TerminalCommandHistoryState | undefined {
201
+ return this._commandHistoryState;
202
+ }
203
+
204
+ protected enableCommandSeparator: boolean;
205
+
189
206
  @postConstruct()
190
207
  protected init(): void {
191
208
  this.id = this._terminalDOMId;
@@ -222,7 +239,9 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
222
239
  lineHeight: this.preferences['terminal.integrated.lineHeight'],
223
240
  scrollback: this.preferences['terminal.integrated.scrollback'],
224
241
  fastScrollSensitivity: this.preferences['terminal.integrated.fastScrollSensitivity'],
225
- theme: this.themeService.theme
242
+ theme: this.themeService.theme,
243
+ // Enables proposed API to allow parsing of OSC 133 sequences for command tracking.
244
+ allowProposedApi: this.preferences['terminal.integrated.enableCommandHistory']
226
245
  });
227
246
  this._buffer = new TerminalBufferImpl(this.term);
228
247
  this._currentTerminalOutput = [];
@@ -237,9 +256,14 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
237
256
 
238
257
  this.toDispose.push(this.preferences.onPreferenceChanged(change => {
239
258
  this.updateConfig();
259
+ if (change.preferenceName === 'terminal.integrated.enableCommandHistory') {
260
+ this.updateCommandHistoryHandlers();
261
+ }
240
262
  this.needsResize = true;
241
263
  this.update();
242
264
  }));
265
+ this.updateCommandHistoryConfig();
266
+ this.updateCommandHistoryHandlers();
243
267
 
244
268
  this.toDispose.push(this.themeService.onDidChange(() => {
245
269
  this.term.options.theme = this.themeService.theme;
@@ -282,12 +306,19 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
282
306
  } else {
283
307
  this.exitStatus = { code, reason: TerminalExitReason.Process };
284
308
  }
309
+ // Ensure any in-progress command block is closed even if the process exits
310
+ // before its OSC prompt_started bytes are flushed from the ring buffer.
311
+ if (this._commandHistoryState?.currentCommand) {
312
+ this.finishCurrentCommand();
313
+ }
285
314
  if (!attached) {
286
315
  this.dispose();
287
316
  }
288
317
  }
289
318
  }));
290
319
  this.toDispose.push(this.toDisposeOnConnect);
320
+ this.toDispose.push(this.commandSeparatorDecorations);
321
+ this.toDispose.push(this.toDisposeOnCommandHistory);
291
322
  this.toDispose.push(this.shellTerminalServer.onDidCloseConnection(() => {
292
323
  const disposable = this.shellTerminalServer.onDidOpenConnection(() => {
293
324
  disposable.dispose();
@@ -384,6 +415,135 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
384
415
  this.term.options.lineHeight = this.preferences.get('terminal.integrated.lineHeight');
385
416
  this.term.options.scrollback = this.preferences.get('terminal.integrated.scrollback');
386
417
  this.term.options.fastScrollSensitivity = this.preferences.get('terminal.integrated.fastScrollSensitivity');
418
+ this.updateCommandHistoryConfig();
419
+ }
420
+
421
+ protected updateCommandHistoryConfig(): void {
422
+ const enabled = this.preferences.get('terminal.integrated.enableCommandHistory', false);
423
+ this.term.options.allowProposedApi = enabled;
424
+ this.enableCommandSeparator = enabled
425
+ ? this.preferences.get('terminal.integrated.enableCommandSeparator', false)
426
+ : false;
427
+
428
+ if (enabled && !this._commandHistoryState) {
429
+ this._commandHistoryState = this.commandHistoryStateFactory();
430
+ this.toDispose.push(this._commandHistoryState);
431
+ } else if (!enabled && this._commandHistoryState) {
432
+ this._commandHistoryState.dispose();
433
+ this._commandHistoryState = undefined;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Registers or deregisters command history handlers based on the current preference state.
439
+ *
440
+ * Manages the OSC 133 handler that tracks command lifecycle events. OSC 133 is an iTerm2
441
+ * escape-sequence family marking events such as command start and prompt display
442
+ * (see https://iterm2.com/documentation-escape-codes.html). We use a customized subset:
443
+ *
444
+ * - prompt_started: emitted when the prompt is shown (command output ends)
445
+ * - command_started;<hex-encoded-command>: emitted when a command begins
446
+ *
447
+ * Command output is read directly from xterm's buffer using markers to track line positions,
448
+ * avoiding the need to intercept and sanitize raw terminal data.
449
+ */
450
+ protected updateCommandHistoryHandlers(): void {
451
+ this.toDisposeOnCommandHistory.dispose();
452
+ this.resetCommandOutputMarker();
453
+ this.resetCommandMarker();
454
+ if (!this._commandHistoryState) {
455
+ return;
456
+ }
457
+ this.toDisposeOnCommandHistory.push(
458
+ this.term.parser.registerOscHandler(133, (oscPayload: string) => {
459
+ if (!this._commandHistoryState) {
460
+ return false;
461
+ }
462
+ if (oscPayload === 'prompt_started') {
463
+ if (this._commandHistoryState.currentCommand) {
464
+ this.finishCurrentCommand();
465
+ }
466
+ this.promptStartMarker?.dispose();
467
+ this.promptStartMarker = this.term.registerMarker(0);
468
+ if (this.enableCommandSeparator) {
469
+ this.addCommandSeparator();
470
+ }
471
+ } else if (oscPayload.startsWith('command_started')) {
472
+ const command = oscPayload.split(';')[1];
473
+ if (!command) {
474
+ return false;
475
+ }
476
+ this.startNewCommand(command);
477
+ }
478
+ return true;
479
+ })
480
+ );
481
+ }
482
+
483
+ protected resetCommandOutputMarker(): void {
484
+ this.outputStartMarker?.dispose();
485
+ this.outputStartMarker = undefined;
486
+ }
487
+
488
+ protected resetCommandMarker(): void {
489
+ this.promptStartMarker?.dispose();
490
+ this.promptStartMarker = undefined;
491
+ }
492
+
493
+ protected startNewCommand(hexEncodedCommand: string): void {
494
+ if (!this._commandHistoryState) {
495
+ return;
496
+ }
497
+ this.outputStartMarker?.dispose();
498
+ this.outputStartMarker = this.term.registerMarker(0);
499
+ this._commandHistoryState.startCommand(hexEncodedCommand);
500
+ }
501
+
502
+ protected finishCurrentCommand(): void {
503
+ if (!this._commandHistoryState) {
504
+ return;
505
+ }
506
+ const startMarker = this.outputStartMarker;
507
+ const endMarker = this.term.registerMarker(0);
508
+ const startLine = startMarker?.line ?? -1;
509
+ const endLine = endMarker?.line ?? -1;
510
+ endMarker.dispose();
511
+
512
+ let output = '';
513
+ if (startLine >= 0 && endLine >= 0) {
514
+ output = this._buffer.getLines(startLine, endLine - startLine, true).join('\n');
515
+ }
516
+
517
+ const block: TerminalBlock = {
518
+ command: this._commandHistoryState.currentCommand,
519
+ output,
520
+ };
521
+
522
+ this.logger.debug('Terminal command result captured:', { command: block.command, output: block.output, outputLength: block.output.length });
523
+ this._commandHistoryState.finishCommand(block);
524
+ this.outputStartMarker = undefined;
525
+ this.promptStartMarker = undefined;
526
+ }
527
+
528
+ private addCommandSeparator(): void {
529
+ const marker = this.term.registerMarker(0);
530
+ if (!marker) {
531
+ return;
532
+ }
533
+
534
+ const deco = this.term.registerDecoration({ marker });
535
+ if (!deco) {
536
+ marker.dispose();
537
+ return;
538
+ }
539
+
540
+ const renderListener = deco.onRender(e => {
541
+ e.classList.add('terminal-command-separator');
542
+ });
543
+
544
+ this.commandSeparatorDecorations.push(marker);
545
+ this.commandSeparatorDecorations.push(deco);
546
+ this.commandSeparatorDecorations.push(renderListener);
387
547
  }
388
548
 
389
549
  protected setIconClass(): void {
@@ -649,7 +809,8 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
649
809
  isPseudo: this.options.isPseudoTerminal,
650
810
  rootURI,
651
811
  cols,
652
- rows
812
+ rows,
813
+ enableShellIntegration: this.preferences['terminal.integrated.enableCommandHistory'] ?? false,
653
814
  });
654
815
  if (IBaseTerminalServer.validateId(terminalId)) {
655
816
  const processInfo = await this.shellTerminalServer.getProcessInfo(terminalId);
@@ -847,6 +1008,10 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
847
1008
 
848
1009
  async executeCommand(commandOptions: CommandLineOptions): Promise<void> {
849
1010
  this.sendText(this.shellCommandBuilder.buildCommand(await this.processInfo, commandOptions) + OS.backend.EOL);
1011
+ this.resetCommandOutputMarker();
1012
+ if (this._commandHistoryState) {
1013
+ this._commandHistoryState.clearCommandCollectionState();
1014
+ }
850
1015
  }
851
1016
 
852
1017
  scrollLineUp(): void {
@@ -903,6 +1068,7 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
903
1068
  }
904
1069
  this.styleElement?.remove();
905
1070
  this.webglAddon?.dispose();
1071
+ this._commandHistoryState?.dispose();
906
1072
  super.dispose();
907
1073
  }
908
1074
 
@@ -1057,4 +1223,5 @@ export class TerminalWidgetImpl extends TerminalWidget implements StatefulWidget
1057
1223
 
1058
1224
  return this.enhancedPreviewNode;
1059
1225
  }
1226
+
1060
1227
  }
@@ -58,6 +58,12 @@ export interface IShellTerminalServerOptions extends IBaseTerminalServerOptions
58
58
  env?: { [key: string]: string | null },
59
59
  strictEnv?: boolean,
60
60
  isPseudo?: boolean,
61
+ /**
62
+ * Whether to inject shell integration scripts for command tracking.
63
+ * When enabled, shell integration scripts are injected to emit OSC 133 sequences.
64
+ * @default false
65
+ */
66
+ enableShellIntegration?: boolean;
61
67
  }
62
68
 
63
69
  export const ShellTerminalServerProxy = Symbol('ShellTerminalServerProxy');
@@ -372,6 +372,18 @@ export const TerminalConfigSchema: PreferenceSchema = {
372
372
  description: nls.localizeByDefault('Persist terminal sessions/history for the workspace across window reloads.'),
373
373
  default: true
374
374
  },
375
+ 'terminal.integrated.enableCommandHistory': {
376
+ type: 'boolean',
377
+ markdownDescription: nls.localize('theia/terminal/enableCommandHistory', 'Track terminal commands and their output separately using shell injection. This enables use cases such as visually distinguishing commands in the UI and giving AI agents more structured access to terminals. Toggling this setting will not affect terminals that are already open.\n\n&nbsp;\n\nThis feature is currently only supported by task terminals and user terminals running bash or zsh.'),
378
+ default: false,
379
+ tags: ['experimental']
380
+ },
381
+ 'terminal.integrated.enableCommandSeparator': {
382
+ type: 'boolean',
383
+ markdownDescription: nls.localize('theia/terminal/enableCommandSeparator', 'Enable a visual separator between executed commands and their output in the terminal. Changes only apply to commands executed after this setting is modified. Only works when {0} is enabled.', '`#terminal.integrated.enableCommandHistory#`'),
384
+ default: false,
385
+ tags: ['experimental']
386
+ },
375
387
  'terminal.integrated.defaultProfile.windows': {
376
388
  type: 'string',
377
389
  description: nls.localize('theia/terminal/defaultProfile', 'The default profile used on {0}', OS.Type.Windows.toString())
@@ -556,6 +568,8 @@ export interface TerminalConfiguration {
556
568
  'terminal.integrated.profiles.osx': Profiles,
557
569
  'terminal.integrated.confirmOnExit': ConfirmOnExitType
558
570
  'terminal.integrated.enablePersistentSessions': boolean
571
+ 'terminal.integrated.enableCommandHistory': boolean
572
+ 'terminal.integrated.enableCommandSeparator': boolean
559
573
  }
560
574
 
561
575
  type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
@@ -0,0 +1,94 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH 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 path = require('path');
18
+ import * as fs from 'fs';
19
+ import { GeneralShellType, guessShellTypeFromExecutable } from '../common/shell-type';
20
+ import { ShellProcess, ShellProcessOptions } from './shell-process';
21
+ import { injectable } from '@theia/core/shared/inversify';
22
+
23
+ @injectable()
24
+ export class ShellIntegrationInjector {
25
+
26
+ protected readonly INTEGRATION_ROOT_DIR = 'shell-integrations';
27
+
28
+ protected readonly BASH_RCFILE_FLAG = '--rcfile';
29
+ protected readonly BASH_INTEGRATION_SCRIPT_PATH = 'bash/bash-integration.bash';
30
+
31
+ protected readonly ZSH_INTEGRATION_ENV_VAR = 'THEIA_ZSH_DIR';
32
+ protected readonly ZSH_INTEGRATION_DIR = 'zsh';
33
+ protected readonly ZDOTDIR_ENV_VAR = 'ZDOTDIR';
34
+ protected readonly ZDOTDIR_RELATIVE_DIR = '/zsh/zdotdir/';
35
+ protected readonly ZDOTDIR_ORIGINAL_ENV_VAR = 'THEIA_ORIGINAL_ZDOTDIR';
36
+
37
+ injectShellIntegration(options: ShellProcessOptions): ShellProcessOptions {
38
+ const shellExecutable = options.shell ?? ShellProcess.getShellExecutablePath();
39
+ const shellType = guessShellTypeFromExecutable(shellExecutable);
40
+ if (shellType === GeneralShellType.Bash) {
41
+ const scriptPath = this.getShellIntegrationPath(this.BASH_INTEGRATION_SCRIPT_PATH);
42
+ if (!scriptPath) {
43
+ return options;
44
+ }
45
+ // strips the login flag if present to avoid conflicts with --rcfile
46
+ const filteredArgs = this.stripLoginFlag(options.args);
47
+ return {
48
+ ...options,
49
+ args: [
50
+ this.BASH_RCFILE_FLAG, scriptPath,
51
+ ...(filteredArgs ?? []),
52
+ ],
53
+ };
54
+ } else if (shellType === GeneralShellType.Zsh) {
55
+ const zdotdirPath = this.getShellIntegrationPath(this.ZDOTDIR_RELATIVE_DIR);
56
+ const zshDirPath = this.getShellIntegrationPath(this.ZSH_INTEGRATION_DIR);
57
+ if (!zdotdirPath || !zshDirPath) {
58
+ return options;
59
+ }
60
+ return {
61
+ ...options,
62
+ env: {
63
+ ...options.env,
64
+ [this.ZDOTDIR_ENV_VAR]: zdotdirPath,
65
+ [this.ZSH_INTEGRATION_ENV_VAR]: zshDirPath,
66
+ [this.ZDOTDIR_ORIGINAL_ENV_VAR]: options.env?.ZDOTDIR ?? process.env.ZDOTDIR ?? ''
67
+ },
68
+ };
69
+ } else {
70
+ return options;
71
+ }
72
+ }
73
+
74
+ protected getShellIntegrationPath(relativePath: string): string | undefined {
75
+ const fullPath = path.join(__dirname, this.INTEGRATION_ROOT_DIR, relativePath);
76
+ if (!fs.existsSync(fullPath)) {
77
+ console.warn(`Shell integration file not found (application may not be bundled correctly): ${fullPath}`);
78
+ return undefined;
79
+ }
80
+ return fullPath;
81
+ }
82
+
83
+ protected stripLoginFlag(args: string | string[] | undefined): string[] | undefined {
84
+ if (args === undefined) {
85
+ return args;
86
+ }
87
+ if (typeof args === 'string') {
88
+ // split string on any amount of whitespace into an array
89
+ return args.trim().split(/\s+/).filter(arg => arg !== '-l' && arg !== '--login');
90
+ }
91
+ return args.filter(arg => arg !== '-l' && arg !== '--login');
92
+ }
93
+
94
+ }
@@ -0,0 +1,86 @@
1
+ #!/bin/bash
2
+ # *****************************************************************************
3
+ # Copyright (C) 2000-2025 JetBrains s.r.o.
4
+ # Modifications (C) 2025 EclipseSource GmbH and others.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions
15
+ # and limitations under the License.
16
+ #
17
+ # Modifications:
18
+ # - Rebranded internals from JetBrains/IntelliJ to Theia (`JETBRAINS_INTELLIJ_BASH_DIR` -> `THEIA_BASH_DIR`,
19
+ # `__jetbrains_intellij_restore_posix_flag` -> `__theia_restore_posix_flag`).
20
+ # - Removed IntelliJ-provided environment injection (`_INTELLIJ_FORCE_SET_*` / `_INTELLIJ_FORCE_PREPEND_*` processing),
21
+ # plus optional sourcing of `JEDITERM_USER_RCFILE` and `JEDITERM_SOURCE` (+ args).
22
+ # - Dropped keybinding setup for Ctrl-left/right word movement.
23
+ # - Dropped IntelliJ command-history hook (`__INTELLIJ_COMMAND_HISTFILE__` + EXIT trap).
24
+ # - Only sources `command-block-support.bash` (no `command-block-support-reworked.bash`), without readability checks.
25
+ #
26
+ # Source:
27
+ # https://github.com/JetBrains/intellij-community/blob/8d02751ced444e5b70784fe0a757f960fe495a67/plugins/terminal/resources/shell-integrations/bash/bash-integration.bash
28
+ # *****************************************************************************
29
+
30
+ if [ -n "$LOGIN_SHELL" ]; then
31
+ unset LOGIN_SHELL
32
+
33
+ # When bash is invoked as an interactive login shell, or as a non-interac-
34
+ # tive shell with the --login option, it first reads and executes commands
35
+ # from the file /etc/profile, if that file exists. After reading that
36
+ # file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in
37
+ # that order, and reads and executes commands from the first one that
38
+ # exists and is readable.
39
+
40
+ if [ -f /etc/profile ]; then
41
+ source /etc/profile
42
+ fi
43
+
44
+ if [ -f ~/.bash_profile ]; then
45
+ source ~/.bash_profile
46
+ else
47
+ if [ -f ~/.bash_login ]; then
48
+ source ~/.bash_login
49
+ else
50
+ if [ -f ~/.profile ]; then
51
+ source ~/.profile
52
+ fi
53
+ fi
54
+ fi
55
+ else
56
+ if [ -f ~/.bashrc ]; then
57
+ source ~/.bashrc
58
+ fi
59
+ fi
60
+
61
+ function disable_posix() {
62
+ if shopt -qo posix
63
+ then
64
+ set +o posix
65
+ __theia_restore_posix_flag=1
66
+ fi
67
+ }
68
+
69
+ function restore_posix() {
70
+ if [ -n "${__theia_restore_posix_flag-}" ]
71
+ then
72
+ set -o posix
73
+ unset __theia_restore_posix_flag
74
+ fi
75
+ }
76
+
77
+ disable_posix
78
+
79
+ THEIA_BASH_DIR="$(dirname "${BASH_SOURCE[0]}")"
80
+ source "${THEIA_BASH_DIR}/command-block-support.bash"
81
+
82
+ unset THEIA_BASH_DIR
83
+
84
+ restore_posix
85
+ unset -f disable_posix
86
+ unset -f restore_posix