Skip to content

Commit 2305c75

Browse files
authored
feat(cli-repl): add isInteractive() support MONGOSH-620 (#887)
1 parent d7b361a commit 2305c75

File tree

9 files changed

+80
-0
lines changed

9 files changed

+80
-0
lines changed

packages/browser-runtime-core/src/open-context-runtime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class OpenContextRuntime implements Runtime {
3737
) {
3838
this.interpreterEnvironment = interpreterEnvironment;
3939
this.internalState = new ShellInternalState(serviceProvider, messageBus || new EventEmitter());
40+
this.internalState.isInteractive = true;
4041
this.shellEvaluator = new ShellEvaluator(this.internalState);
4142
this.internalState.setCtx(this.interpreterEnvironment.getContextObject());
4243
this.interpreter = new Interpreter(this.interpreterEnvironment);

packages/cli-repl/src/cli-repl.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,61 @@ describe('CliRepl', () => {
810810
expect(output).to.include('--eval requires an argument, but no argument was given');
811811
expect(exitCode).to.equal(0);
812812
});
813+
814+
it('isInteractive() is false for --eval without --shell', async() => {
815+
const filename1 = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'printisinteractive.js');
816+
cliReplOptions.shellCliOptions.eval = await fs.readFile(filename1, 'utf8');
817+
cliRepl = new CliRepl(cliReplOptions);
818+
await startWithExpectedImmediateExit(cliRepl, await testServer.connectionString());
819+
expect(output).to.match(/isInteractive=false/);
820+
expect(exitCode).to.equal(0);
821+
});
822+
823+
it('isInteractive() is true for --eval with --shell', async() => {
824+
const filename1 = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'printisinteractive.js');
825+
cliReplOptions.shellCliOptions.eval = await fs.readFile(filename1, 'utf8');
826+
cliReplOptions.shellCliOptions.shell = true;
827+
cliRepl = new CliRepl(cliReplOptions);
828+
await cliRepl.start(await testServer.connectionString(), {});
829+
expect(output).to.match(/isInteractive=true/);
830+
expect(exitCode).to.equal(null);
831+
832+
input.write('exit\n');
833+
await waitBus(cliRepl.bus, 'mongosh:closed');
834+
expect(exitCode).to.equal(0);
835+
});
836+
837+
it('isInteractive() is false for loaded file without --shell', async() => {
838+
const filename1 = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'printisinteractive.js');
839+
cliReplOptions.shellCliOptions._.push(filename1);
840+
cliRepl = new CliRepl(cliReplOptions);
841+
await startWithExpectedImmediateExit(cliRepl, await testServer.connectionString());
842+
expect(output).to.match(/isInteractive=false/);
843+
expect(exitCode).to.equal(0);
844+
});
845+
846+
it('isInteractive() is true for --eval with --shell', async() => {
847+
const filename1 = path.resolve(__dirname, '..', 'test', 'fixtures', 'load', 'printisinteractive.js');
848+
cliReplOptions.shellCliOptions._.push(filename1);
849+
cliReplOptions.shellCliOptions.shell = true;
850+
cliRepl = new CliRepl(cliReplOptions);
851+
await cliRepl.start(await testServer.connectionString(), {});
852+
expect(output).to.match(/isInteractive=true/);
853+
expect(exitCode).to.equal(null);
854+
855+
input.write('exit\n');
856+
await waitBus(cliRepl.bus, 'mongosh:closed');
857+
expect(exitCode).to.equal(0);
858+
});
859+
860+
it('isInteractive() is true for plain shell', async() => {
861+
cliRepl = new CliRepl(cliReplOptions);
862+
await cliRepl.start(await testServer.connectionString(), {});
863+
864+
input.write('print("isInteractive=" + isInteractive())\n');
865+
await waitEval(cliRepl.bus);
866+
expect(output).to.match(/isInteractive=true/);
867+
});
813868
});
814869

815870
context('with a user-provided prompt', () => {

packages/cli-repl/src/cli-repl.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,15 @@ class CliRepl {
168168
const initialized = await this.mongoshRepl.initialize(initialServiceProvider);
169169
const commandLineLoadFiles = this.listCommandLineLoadFiles();
170170
if (commandLineLoadFiles.length > 0 || this.cliOptions.eval !== undefined) {
171+
this.mongoshRepl.setIsInteractive(!!this.cliOptions.shell);
171172
this.bus.emit('mongosh:start-loading-cli-scripts', { usesShellOption: !!this.cliOptions.shell });
172173
await this.loadCommandLineFilesAndEval(commandLineLoadFiles);
173174
if (!this.cliOptions.shell) {
174175
await this.exit(0);
175176
return;
176177
}
178+
} else {
179+
this.mongoshRepl.setIsInteractive(true);
177180
}
178181
await this.loadRcFiles();
179182
this.bus.emit('mongosh:start-mongosh-repl', { version });

packages/cli-repl/src/mongosh-repl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class MongoshNodeRepl implements EvaluationListener {
123123
this._runtimeState = null;
124124
}
125125

126+
setIsInteractive(value: boolean): void {
127+
this.runtimeState().internalState.isInteractive = value;
128+
}
129+
126130
async initialize(serviceProvider: ServiceProvider): Promise<InitializationToken> {
127131
const internalState = new ShellInternalState(serviceProvider, this.bus, this.shellCliOptions);
128132
const shellEvaluator = new ShellEvaluator(internalState);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* eslint-disable */
2+
print(`isInteractive=${isInteractive()}`)

packages/i18n/src/locales/en_US.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ const translations: Catalog = {
154154
},
155155
printjson: {
156156
description: 'Alias for print()'
157+
},
158+
isInteractive: {
159+
description: 'Returns whether the shell will enter or has entered interactive mode'
157160
}
158161
}
159162
},

packages/shell-api/src/shell-api.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,13 @@ describe('ShellApi', () => {
519519
expect(version).to.equal(expected);
520520
});
521521
});
522+
describe('isInteractive', () => {
523+
it('returns a boolean', () => {
524+
expect(internalState.context.isInteractive()).to.equal(false);
525+
internalState.isInteractive = true;
526+
expect(internalState.context.isInteractive()).to.equal(true);
527+
});
528+
});
522529
for (const cmd of ['exit', 'quit']) {
523530
// eslint-disable-next-line no-loop-func
524531
describe(cmd, () => {

packages/shell-api/src/shell-api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,8 @@ export default class ShellApi extends ShellApiClass {
286286
const { evaluationListener } = this._internalState;
287287
await evaluationListener.onClearCommand?.();
288288
}
289+
290+
isInteractive(): boolean {
291+
return this._internalState.isInteractive;
292+
}
289293
}

packages/shell-api/src/shell-internal-state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export default class ShellInternalState {
106106
public evaluationListener: EvaluationListener;
107107
public mongocryptdSpawnPath: string | null;
108108
public batchSizeFromDBQuery: number | undefined = undefined;
109+
public isInteractive = false;
109110

110111
public readonly interrupted = new InterruptFlag();
111112
public resumeMongosAfterInterrupt: Array<{

0 commit comments

Comments
 (0)