@theia/terminal 1.77.0-next.0 → 1.77.0-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/node/shell-integration-injector.d.ts +14 -2
- package/lib/node/shell-integration-injector.d.ts.map +1 -1
- package/lib/node/shell-integration-injector.js +34 -1
- package/lib/node/shell-integration-injector.js.map +1 -1
- package/lib/node/shell-integration-injector.spec.d.ts +2 -0
- package/lib/node/shell-integration-injector.spec.d.ts.map +1 -0
- package/lib/node/shell-integration-injector.spec.js +85 -0
- package/lib/node/shell-integration-injector.spec.js.map +1 -0
- package/lib/node/terminal-backend-module.d.ts.map +1 -1
- package/lib/node/terminal-backend-module.js +2 -0
- package/lib/node/terminal-backend-module.js.map +1 -1
- package/package.json +9 -9
- package/src/node/shell-integration-injector.spec.ts +95 -0
- package/src/node/shell-integration-injector.ts +40 -3
- package/src/node/terminal-backend-module.ts +2 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ShellProcessOptions } from './shell-process';
|
|
2
|
-
import { ILogger } from '@theia/core';
|
|
3
|
-
|
|
2
|
+
import { ILogger, MaybePromise } from '@theia/core';
|
|
3
|
+
import { BackendApplicationContribution, BundledResourceProvider } from '@theia/core/lib/node';
|
|
4
|
+
export declare class ShellIntegrationInjector implements BackendApplicationContribution {
|
|
4
5
|
protected readonly logger: ILogger;
|
|
6
|
+
protected readonly bundledResourceProvider: BundledResourceProvider;
|
|
5
7
|
protected readonly INTEGRATION_ROOT_DIR = "shell-integrations";
|
|
6
8
|
protected readonly BASH_RCFILE_FLAG = "--rcfile";
|
|
7
9
|
protected readonly BASH_INTEGRATION_SCRIPT_PATH = "bash/bash-integration.bash";
|
|
@@ -10,7 +12,17 @@ export declare class ShellIntegrationInjector {
|
|
|
10
12
|
protected readonly ZDOTDIR_ENV_VAR = "ZDOTDIR";
|
|
11
13
|
protected readonly ZDOTDIR_RELATIVE_DIR = "/zsh/zdotdir/";
|
|
12
14
|
protected readonly ZDOTDIR_ORIGINAL_ENV_VAR = "THEIA_ORIGINAL_ZDOTDIR";
|
|
15
|
+
/** The location of the shell integration scripts, as seen by the spawned shells. */
|
|
16
|
+
protected integrationRootPath: string | undefined;
|
|
17
|
+
protected resolvingIntegrationRoot: Promise<void> | undefined;
|
|
18
|
+
initialize(): MaybePromise<void>;
|
|
13
19
|
injectShellIntegration(options: ShellProcessOptions): ShellProcessOptions;
|
|
20
|
+
/**
|
|
21
|
+
* Make the bundled shell integration scripts available to the shells that we spawn. In a packaged Electron
|
|
22
|
+
* application, the scripts are stored in an archive that only the application itself can read, so they have
|
|
23
|
+
* to be resolved to a location outside of that archive.
|
|
24
|
+
*/
|
|
25
|
+
protected resolveIntegrationRoot(): Promise<void>;
|
|
14
26
|
protected getShellIntegrationPath(relativePath: string): string | undefined;
|
|
15
27
|
protected stripLoginFlag(args: string | string[] | undefined): string[] | undefined;
|
|
16
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-integration-injector.d.ts","sourceRoot":"","sources":["../../src/node/shell-integration-injector.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"shell-integration-injector.d.ts","sourceRoot":"","sources":["../../src/node/shell-integration-injector.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAE/F,qBACa,wBAAyB,YAAW,8BAA8B;IAG3E,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAGnC,SAAS,CAAC,QAAQ,CAAC,uBAAuB,EAAE,uBAAuB,CAAC;IAEpE,SAAS,CAAC,QAAQ,CAAC,oBAAoB,wBAAwB;IAE/D,SAAS,CAAC,QAAQ,CAAC,gBAAgB,cAAc;IACjD,SAAS,CAAC,QAAQ,CAAC,4BAA4B,gCAAgC;IAE/E,SAAS,CAAC,QAAQ,CAAC,uBAAuB,mBAAmB;IAC7D,SAAS,CAAC,QAAQ,CAAC,mBAAmB,SAAS;IAC/C,SAAS,CAAC,QAAQ,CAAC,eAAe,aAAa;IAC/C,SAAS,CAAC,QAAQ,CAAC,oBAAoB,mBAAmB;IAC1D,SAAS,CAAC,QAAQ,CAAC,wBAAwB,4BAA4B;IAEvE,oFAAoF;IACpF,SAAS,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAC;IAClD,SAAS,CAAC,wBAAwB,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;IAE9D,UAAU,IAAI,YAAY,CAAC,IAAI,CAAC;IAKhC,sBAAsB,CAAC,OAAO,EAAE,mBAAmB,GAAG,mBAAmB;IAqCzE;;;;OAIG;IACH,SAAS,CAAC,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAajD,SAAS,CAAC,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAe3E,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,GAAG,MAAM,EAAE,GAAG,SAAS;CAWtF"}
|
|
@@ -23,6 +23,7 @@ const shell_type_1 = require("../common/shell-type");
|
|
|
23
23
|
const shell_process_1 = require("./shell-process");
|
|
24
24
|
const inversify_1 = require("@theia/core/shared/inversify");
|
|
25
25
|
const core_1 = require("@theia/core");
|
|
26
|
+
const node_1 = require("@theia/core/lib/node");
|
|
26
27
|
let ShellIntegrationInjector = class ShellIntegrationInjector {
|
|
27
28
|
constructor() {
|
|
28
29
|
this.INTEGRATION_ROOT_DIR = 'shell-integrations';
|
|
@@ -34,6 +35,10 @@ let ShellIntegrationInjector = class ShellIntegrationInjector {
|
|
|
34
35
|
this.ZDOTDIR_RELATIVE_DIR = '/zsh/zdotdir/';
|
|
35
36
|
this.ZDOTDIR_ORIGINAL_ENV_VAR = 'THEIA_ORIGINAL_ZDOTDIR';
|
|
36
37
|
}
|
|
38
|
+
initialize() {
|
|
39
|
+
// Resolve the scripts up-front, because the shells are spawned synchronously.
|
|
40
|
+
return this.resolveIntegrationRoot();
|
|
41
|
+
}
|
|
37
42
|
injectShellIntegration(options) {
|
|
38
43
|
const shellExecutable = options.shell ?? shell_process_1.ShellProcess.getShellExecutablePath();
|
|
39
44
|
const shellType = (0, shell_type_1.guessShellTypeFromExecutable)(shellExecutable);
|
|
@@ -72,8 +77,32 @@ let ShellIntegrationInjector = class ShellIntegrationInjector {
|
|
|
72
77
|
return options;
|
|
73
78
|
}
|
|
74
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Make the bundled shell integration scripts available to the shells that we spawn. In a packaged Electron
|
|
82
|
+
* application, the scripts are stored in an archive that only the application itself can read, so they have
|
|
83
|
+
* to be resolved to a location outside of that archive.
|
|
84
|
+
*/
|
|
85
|
+
resolveIntegrationRoot() {
|
|
86
|
+
return this.resolvingIntegrationRoot ??= (async () => {
|
|
87
|
+
const bundledPath = path.join(__dirname, this.INTEGRATION_ROOT_DIR);
|
|
88
|
+
try {
|
|
89
|
+
this.integrationRootPath = await this.bundledResourceProvider.resolveExternalPath(bundledPath);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
this.logger.warn(`Shell integration scripts not available (application may not be bundled correctly): ${bundledPath}`, error);
|
|
93
|
+
// allow a later attempt to recover, e.g. when the configuration directory was not writable yet
|
|
94
|
+
this.resolvingIntegrationRoot = undefined;
|
|
95
|
+
}
|
|
96
|
+
})();
|
|
97
|
+
}
|
|
75
98
|
getShellIntegrationPath(relativePath) {
|
|
76
|
-
|
|
99
|
+
if (this.integrationRootPath === undefined) {
|
|
100
|
+
// The scripts are resolved on start-up, so this is only reached if that has failed or has not run.
|
|
101
|
+
this.resolveIntegrationRoot();
|
|
102
|
+
this.logger.warn('Shell integration scripts are not available yet.');
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const fullPath = path.join(this.integrationRootPath, relativePath);
|
|
77
106
|
if (!fs.existsSync(fullPath)) {
|
|
78
107
|
this.logger.warn(`Shell integration file not found (application may not be bundled correctly): ${fullPath}`);
|
|
79
108
|
return undefined;
|
|
@@ -97,6 +126,10 @@ tslib_1.__decorate([
|
|
|
97
126
|
(0, inversify_1.named)('terminal:ShellIntegrationInjector'),
|
|
98
127
|
tslib_1.__metadata("design:type", Object)
|
|
99
128
|
], ShellIntegrationInjector.prototype, "logger", void 0);
|
|
129
|
+
tslib_1.__decorate([
|
|
130
|
+
(0, inversify_1.inject)(node_1.BundledResourceProvider),
|
|
131
|
+
tslib_1.__metadata("design:type", Object)
|
|
132
|
+
], ShellIntegrationInjector.prototype, "bundledResourceProvider", void 0);
|
|
100
133
|
exports.ShellIntegrationInjector = ShellIntegrationInjector = tslib_1.__decorate([
|
|
101
134
|
(0, inversify_1.injectable)()
|
|
102
135
|
], ShellIntegrationInjector);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-integration-injector.js","sourceRoot":"","sources":["../../src/node/shell-integration-injector.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,oDAAoD;AACpD,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;;;AAEhF,6BAA8B;AAC9B,yBAAyB;AACzB,qDAAsF;AACtF,mDAAoE;AACpE,4DAAyE;AACzE,
|
|
1
|
+
{"version":3,"file":"shell-integration-injector.js","sourceRoot":"","sources":["../../src/node/shell-integration-injector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-integration-injector.spec.d.ts","sourceRoot":"","sources":["../../src/node/shell-integration-injector.spec.ts"],"names":[],"mappings":"AAgBA,OAAO,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2026 EclipseSource GmbH and others.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
require("reflect-metadata");
|
|
19
|
+
const chai_1 = require("chai");
|
|
20
|
+
const fs = require("fs");
|
|
21
|
+
const path = require("path");
|
|
22
|
+
const temp = require("temp");
|
|
23
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
24
|
+
const core_1 = require("@theia/core");
|
|
25
|
+
const logger_1 = require("@theia/core/lib/common/logger");
|
|
26
|
+
const mock_logger_1 = require("@theia/core/lib/common/test/mock-logger");
|
|
27
|
+
const node_1 = require("@theia/core/lib/node");
|
|
28
|
+
const shell_integration_injector_1 = require("./shell-integration-injector");
|
|
29
|
+
describe('ShellIntegrationInjector', () => {
|
|
30
|
+
const track = temp.track();
|
|
31
|
+
/** Stands for the location outside of the application bundle at which the scripts are made available. */
|
|
32
|
+
let externalRoot;
|
|
33
|
+
let requestedPath;
|
|
34
|
+
let injector;
|
|
35
|
+
/**
|
|
36
|
+
* The injector dispatches on the shell type that `guessShellTypeFromExecutable` derives, which depends on
|
|
37
|
+
* the platform: on Windows, `bash` is taken to be Git Bash, which the injector does not handle. Pin the
|
|
38
|
+
* platform so that the tests cover the same branches everywhere. Restore it per test rather than once for
|
|
39
|
+
* the suite, because `shell-type.spec.ts` installs a root hook that resets it after every test.
|
|
40
|
+
*/
|
|
41
|
+
const originalIsWindows = core_1.OS.backend.isWindows;
|
|
42
|
+
beforeEach(() => Object.defineProperty(core_1.OS.backend, 'isWindows', { value: false }));
|
|
43
|
+
afterEach(() => Object.defineProperty(core_1.OS.backend, 'isWindows', { value: originalIsWindows }));
|
|
44
|
+
beforeEach(async () => {
|
|
45
|
+
externalRoot = track.mkdirSync();
|
|
46
|
+
for (const file of ['bash/bash-integration.bash', 'zsh/zsh-integration.zsh', 'zsh/zdotdir/.zshrc']) {
|
|
47
|
+
const filePath = path.join(externalRoot, file);
|
|
48
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
49
|
+
fs.writeFileSync(filePath, '');
|
|
50
|
+
}
|
|
51
|
+
requestedPath = undefined;
|
|
52
|
+
injector = createInjector();
|
|
53
|
+
await injector.initialize();
|
|
54
|
+
});
|
|
55
|
+
afterEach(() => track.cleanupSync());
|
|
56
|
+
function createInjector() {
|
|
57
|
+
const container = new inversify_1.Container();
|
|
58
|
+
container.bind(logger_1.ILogger).toConstantValue(new mock_logger_1.MockLogger());
|
|
59
|
+
container.bind(node_1.BundledResourceProvider).toConstantValue({
|
|
60
|
+
resolveExternalPath: async (resourcePath) => {
|
|
61
|
+
requestedPath = resourcePath;
|
|
62
|
+
return externalRoot;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
container.bind(shell_integration_injector_1.ShellIntegrationInjector).toSelf().inSingletonScope();
|
|
66
|
+
return container.get(shell_integration_injector_1.ShellIntegrationInjector);
|
|
67
|
+
}
|
|
68
|
+
it('resolves the bundled scripts to a location that shells can read', () => {
|
|
69
|
+
(0, chai_1.expect)(requestedPath).to.equal(path.join(__dirname, 'shell-integrations'));
|
|
70
|
+
});
|
|
71
|
+
it('passes the resolved bash script to bash', () => {
|
|
72
|
+
const options = injector.injectShellIntegration({ shell: '/bin/bash', args: ['--login'] });
|
|
73
|
+
(0, chai_1.expect)(options.args).to.deep.equal(['--rcfile', path.join(externalRoot, 'bash', 'bash-integration.bash')]);
|
|
74
|
+
});
|
|
75
|
+
it('points zsh at the resolved script directories', () => {
|
|
76
|
+
const options = injector.injectShellIntegration({ shell: '/bin/zsh' });
|
|
77
|
+
(0, chai_1.expect)(options.env?.ZDOTDIR).to.equal(path.join(externalRoot, 'zsh/zdotdir/'));
|
|
78
|
+
(0, chai_1.expect)(options.env?.THEIA_ZSH_DIR).to.equal(path.join(externalRoot, 'zsh'));
|
|
79
|
+
});
|
|
80
|
+
it('leaves the options untouched while the scripts are not available', () => {
|
|
81
|
+
const options = { shell: '/bin/bash', args: ['--login'] };
|
|
82
|
+
(0, chai_1.expect)(createInjector().injectShellIntegration(options)).to.deep.equal(options);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=shell-integration-injector.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-integration-injector.spec.js","sourceRoot":"","sources":["../../src/node/shell-integration-injector.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-backend-module.d.ts","sourceRoot":"","sources":["../../src/node/terminal-backend-module.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,eAAe,EAAa,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAKtF,OAAO,EAAsD,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"terminal-backend-module.d.ts","sourceRoot":"","sources":["../../src/node/terminal-backend-module.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,eAAe,EAAa,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAKtF,OAAO,EAAsD,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAU3H,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,EAAE;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;IAC9D,WAAW,EAAE;QAET,KAAI,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,mBAAmB,CAAC;KAC5C,CAAA;CACJ,GAAG,IAAI,CAiBP;;AAED,wBAiCG"}
|
|
@@ -29,6 +29,7 @@ const terminal_watcher_1 = require("../common/terminal-watcher");
|
|
|
29
29
|
const messaging_service_1 = require("@theia/core/lib/node/messaging/messaging-service");
|
|
30
30
|
const terminal_preferences_1 = require("../common/terminal-preferences");
|
|
31
31
|
const shell_integration_injector_1 = require("./shell-integration-injector");
|
|
32
|
+
const node_1 = require("@theia/core/lib/node");
|
|
32
33
|
function bindTerminalServer(bind, { path, identifier, constructor }) {
|
|
33
34
|
const dispatchingClient = new base_terminal_protocol_1.DispatchingBaseTerminalClient();
|
|
34
35
|
bind(identifier).to(constructor).inSingletonScope().onActivation((context, terminalServer) => {
|
|
@@ -49,6 +50,7 @@ exports.default = new inversify_1.ContainerModule(bind => {
|
|
|
49
50
|
bind(messaging_service_1.MessagingService.Contribution).to(terminal_backend_contribution_1.TerminalBackendContribution).inSingletonScope();
|
|
50
51
|
bind(shell_process_1.ShellProcess).toSelf().inTransientScope();
|
|
51
52
|
bind(shell_integration_injector_1.ShellIntegrationInjector).toSelf().inSingletonScope();
|
|
53
|
+
bind(node_1.BackendApplicationContribution).toService(shell_integration_injector_1.ShellIntegrationInjector);
|
|
52
54
|
bind(shell_process_1.ShellProcessFactory).toFactory(ctx => (options) => {
|
|
53
55
|
const child = new inversify_1.Container({ defaultScope: 'Singleton' });
|
|
54
56
|
child.parent = ctx.container;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal-backend-module.js","sourceRoot":"","sources":["../../src/node/terminal-backend-module.ts"],"names":[],"mappings":";AAAA,gFAAgF;AAChF,yCAAyC;AACzC,EAAE;AACF,2EAA2E;AAC3E,mEAAmE;AACnE,wCAAwC;AACxC,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,uDAAuD;AACvD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;;
|
|
1
|
+
{"version":3,"file":"terminal-backend-module.js","sourceRoot":"","sources":["../../src/node/terminal-backend-module.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/terminal",
|
|
3
|
-
"version": "1.77.0-next.
|
|
3
|
+
"version": "1.77.0-next.4+4f24ee401",
|
|
4
4
|
"description": "Theia - Terminal Extension",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.77.0-next.
|
|
7
|
-
"@theia/editor": "1.77.0-next.
|
|
8
|
-
"@theia/file-search": "1.77.0-next.
|
|
9
|
-
"@theia/filesystem": "1.77.0-next.
|
|
10
|
-
"@theia/process": "1.77.0-next.
|
|
11
|
-
"@theia/variable-resolver": "1.77.0-next.
|
|
12
|
-
"@theia/workspace": "1.77.0-next.
|
|
6
|
+
"@theia/core": "1.77.0-next.4+4f24ee401",
|
|
7
|
+
"@theia/editor": "1.77.0-next.4+4f24ee401",
|
|
8
|
+
"@theia/file-search": "1.77.0-next.4+4f24ee401",
|
|
9
|
+
"@theia/filesystem": "1.77.0-next.4+4f24ee401",
|
|
10
|
+
"@theia/process": "1.77.0-next.4+4f24ee401",
|
|
11
|
+
"@theia/variable-resolver": "1.77.0-next.4+4f24ee401",
|
|
12
|
+
"@theia/workspace": "1.77.0-next.4+4f24ee401",
|
|
13
13
|
"tslib": "^2.8.1",
|
|
14
14
|
"xterm": "^5.3.0",
|
|
15
15
|
"xterm-addon-fit": "^0.8.0",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"nyc": {
|
|
60
60
|
"extends": "../../configs/nyc.json"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "4f24ee40105b8c53808c8293c41387304d15960c"
|
|
63
63
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 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 'reflect-metadata';
|
|
18
|
+
import { expect } from 'chai';
|
|
19
|
+
import * as fs from 'fs';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import * as temp from 'temp';
|
|
22
|
+
import { Container } from '@theia/core/shared/inversify';
|
|
23
|
+
import { OS } from '@theia/core';
|
|
24
|
+
import { ILogger } from '@theia/core/lib/common/logger';
|
|
25
|
+
import { MockLogger } from '@theia/core/lib/common/test/mock-logger';
|
|
26
|
+
import { BundledResourceProvider } from '@theia/core/lib/node';
|
|
27
|
+
import { ShellIntegrationInjector } from './shell-integration-injector';
|
|
28
|
+
|
|
29
|
+
describe('ShellIntegrationInjector', () => {
|
|
30
|
+
|
|
31
|
+
const track = temp.track();
|
|
32
|
+
|
|
33
|
+
/** Stands for the location outside of the application bundle at which the scripts are made available. */
|
|
34
|
+
let externalRoot: string;
|
|
35
|
+
let requestedPath: string | undefined;
|
|
36
|
+
let injector: ShellIntegrationInjector;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The injector dispatches on the shell type that `guessShellTypeFromExecutable` derives, which depends on
|
|
40
|
+
* the platform: on Windows, `bash` is taken to be Git Bash, which the injector does not handle. Pin the
|
|
41
|
+
* platform so that the tests cover the same branches everywhere. Restore it per test rather than once for
|
|
42
|
+
* the suite, because `shell-type.spec.ts` installs a root hook that resets it after every test.
|
|
43
|
+
*/
|
|
44
|
+
const originalIsWindows = OS.backend.isWindows;
|
|
45
|
+
beforeEach(() => Object.defineProperty(OS.backend, 'isWindows', { value: false }));
|
|
46
|
+
afterEach(() => Object.defineProperty(OS.backend, 'isWindows', { value: originalIsWindows }));
|
|
47
|
+
|
|
48
|
+
beforeEach(async () => {
|
|
49
|
+
externalRoot = track.mkdirSync();
|
|
50
|
+
for (const file of ['bash/bash-integration.bash', 'zsh/zsh-integration.zsh', 'zsh/zdotdir/.zshrc']) {
|
|
51
|
+
const filePath = path.join(externalRoot, file);
|
|
52
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
53
|
+
fs.writeFileSync(filePath, '');
|
|
54
|
+
}
|
|
55
|
+
requestedPath = undefined;
|
|
56
|
+
injector = createInjector();
|
|
57
|
+
await injector.initialize();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => track.cleanupSync());
|
|
61
|
+
|
|
62
|
+
function createInjector(): ShellIntegrationInjector {
|
|
63
|
+
const container = new Container();
|
|
64
|
+
container.bind(ILogger).toConstantValue(new MockLogger());
|
|
65
|
+
container.bind(BundledResourceProvider).toConstantValue({
|
|
66
|
+
resolveExternalPath: async (resourcePath: string) => {
|
|
67
|
+
requestedPath = resourcePath;
|
|
68
|
+
return externalRoot;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
container.bind(ShellIntegrationInjector).toSelf().inSingletonScope();
|
|
72
|
+
return container.get(ShellIntegrationInjector);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
it('resolves the bundled scripts to a location that shells can read', () => {
|
|
76
|
+
expect(requestedPath).to.equal(path.join(__dirname, 'shell-integrations'));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('passes the resolved bash script to bash', () => {
|
|
80
|
+
const options = injector.injectShellIntegration({ shell: '/bin/bash', args: ['--login'] });
|
|
81
|
+
expect(options.args).to.deep.equal(['--rcfile', path.join(externalRoot, 'bash', 'bash-integration.bash')]);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('points zsh at the resolved script directories', () => {
|
|
85
|
+
const options = injector.injectShellIntegration({ shell: '/bin/zsh' });
|
|
86
|
+
expect(options.env?.ZDOTDIR).to.equal(path.join(externalRoot, 'zsh/zdotdir/'));
|
|
87
|
+
expect(options.env?.THEIA_ZSH_DIR).to.equal(path.join(externalRoot, 'zsh'));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('leaves the options untouched while the scripts are not available', () => {
|
|
91
|
+
const options = { shell: '/bin/bash', args: ['--login'] };
|
|
92
|
+
expect(createInjector().injectShellIntegration(options)).to.deep.equal(options);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
});
|
|
@@ -19,14 +19,18 @@ import * as fs from 'fs';
|
|
|
19
19
|
import { GeneralShellType, guessShellTypeFromExecutable } from '../common/shell-type';
|
|
20
20
|
import { ShellProcess, ShellProcessOptions } from './shell-process';
|
|
21
21
|
import { injectable, inject, named } from '@theia/core/shared/inversify';
|
|
22
|
-
import { ILogger } from '@theia/core';
|
|
22
|
+
import { ILogger, MaybePromise } from '@theia/core';
|
|
23
|
+
import { BackendApplicationContribution, BundledResourceProvider } from '@theia/core/lib/node';
|
|
23
24
|
|
|
24
25
|
@injectable()
|
|
25
|
-
export class ShellIntegrationInjector {
|
|
26
|
+
export class ShellIntegrationInjector implements BackendApplicationContribution {
|
|
26
27
|
|
|
27
28
|
@inject(ILogger) @named('terminal:ShellIntegrationInjector')
|
|
28
29
|
protected readonly logger: ILogger;
|
|
29
30
|
|
|
31
|
+
@inject(BundledResourceProvider)
|
|
32
|
+
protected readonly bundledResourceProvider: BundledResourceProvider;
|
|
33
|
+
|
|
30
34
|
protected readonly INTEGRATION_ROOT_DIR = 'shell-integrations';
|
|
31
35
|
|
|
32
36
|
protected readonly BASH_RCFILE_FLAG = '--rcfile';
|
|
@@ -38,6 +42,15 @@ export class ShellIntegrationInjector {
|
|
|
38
42
|
protected readonly ZDOTDIR_RELATIVE_DIR = '/zsh/zdotdir/';
|
|
39
43
|
protected readonly ZDOTDIR_ORIGINAL_ENV_VAR = 'THEIA_ORIGINAL_ZDOTDIR';
|
|
40
44
|
|
|
45
|
+
/** The location of the shell integration scripts, as seen by the spawned shells. */
|
|
46
|
+
protected integrationRootPath: string | undefined;
|
|
47
|
+
protected resolvingIntegrationRoot: Promise<void> | undefined;
|
|
48
|
+
|
|
49
|
+
initialize(): MaybePromise<void> {
|
|
50
|
+
// Resolve the scripts up-front, because the shells are spawned synchronously.
|
|
51
|
+
return this.resolveIntegrationRoot();
|
|
52
|
+
}
|
|
53
|
+
|
|
41
54
|
injectShellIntegration(options: ShellProcessOptions): ShellProcessOptions {
|
|
42
55
|
const shellExecutable = options.shell ?? ShellProcess.getShellExecutablePath();
|
|
43
56
|
const shellType = guessShellTypeFromExecutable(shellExecutable);
|
|
@@ -75,8 +88,32 @@ export class ShellIntegrationInjector {
|
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Make the bundled shell integration scripts available to the shells that we spawn. In a packaged Electron
|
|
93
|
+
* application, the scripts are stored in an archive that only the application itself can read, so they have
|
|
94
|
+
* to be resolved to a location outside of that archive.
|
|
95
|
+
*/
|
|
96
|
+
protected resolveIntegrationRoot(): Promise<void> {
|
|
97
|
+
return this.resolvingIntegrationRoot ??= (async () => {
|
|
98
|
+
const bundledPath = path.join(__dirname, this.INTEGRATION_ROOT_DIR);
|
|
99
|
+
try {
|
|
100
|
+
this.integrationRootPath = await this.bundledResourceProvider.resolveExternalPath(bundledPath);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.warn(`Shell integration scripts not available (application may not be bundled correctly): ${bundledPath}`, error);
|
|
103
|
+
// allow a later attempt to recover, e.g. when the configuration directory was not writable yet
|
|
104
|
+
this.resolvingIntegrationRoot = undefined;
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
protected getShellIntegrationPath(relativePath: string): string | undefined {
|
|
79
|
-
|
|
110
|
+
if (this.integrationRootPath === undefined) {
|
|
111
|
+
// The scripts are resolved on start-up, so this is only reached if that has failed or has not run.
|
|
112
|
+
this.resolveIntegrationRoot();
|
|
113
|
+
this.logger.warn('Shell integration scripts are not available yet.');
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
const fullPath = path.join(this.integrationRootPath, relativePath);
|
|
80
117
|
if (!fs.existsSync(fullPath)) {
|
|
81
118
|
this.logger.warn(`Shell integration file not found (application may not be bundled correctly): ${fullPath}`);
|
|
82
119
|
return undefined;
|
|
@@ -27,6 +27,7 @@ import { TerminalWatcher } from '../common/terminal-watcher';
|
|
|
27
27
|
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
|
|
28
28
|
import { bindTerminalPreferences } from '../common/terminal-preferences';
|
|
29
29
|
import { ShellIntegrationInjector } from './shell-integration-injector';
|
|
30
|
+
import { BackendApplicationContribution } from '@theia/core/lib/node';
|
|
30
31
|
|
|
31
32
|
export function bindTerminalServer(bind: interfaces.Bind, { path, identifier, constructor }: {
|
|
32
33
|
path: string,
|
|
@@ -59,6 +60,7 @@ export default new ContainerModule(bind => {
|
|
|
59
60
|
|
|
60
61
|
bind(ShellProcess).toSelf().inTransientScope();
|
|
61
62
|
bind(ShellIntegrationInjector).toSelf().inSingletonScope();
|
|
63
|
+
bind(BackendApplicationContribution).toService(ShellIntegrationInjector);
|
|
62
64
|
bind(ShellProcessFactory).toFactory(ctx =>
|
|
63
65
|
(options: ShellProcessOptions) => {
|
|
64
66
|
const child = new Container({ defaultScope: 'Singleton' });
|