Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/cli-repl/src/mongosh-repl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { StubbedInstance, stubInterface } from 'ts-sinon';
import { promisify } from 'util';
import { expect, fakeTTYProps, tick, useTmpdir, waitEval } from '../test/repl-helpers';
import MongoshNodeRepl, { MongoshConfigProvider, MongoshNodeReplOptions } from './mongosh-repl';
import stripAnsi from 'strip-ansi';

const delay = promisify(setTimeout);

Expand Down Expand Up @@ -234,6 +235,19 @@ describe('MongoshNodeRepl', () => {
expect(output).to.include('65537');
});

it('does not stop input when autocompleting during .editor', async() => {
input.write('.editor\n');
await tick();
expect(output).to.include('Entering editor mode');
output = '';
input.write('db.\u0009\u0009');
await tick();
input.write('version()\n');
input.write('\u0004'); // Ctrl+D
await waitEval(bus);
expect(output).to.include('Error running command serverBuildInfo');
});

it('can enter multiline code', async() => {
for (const line of multilineCode.split('\n')) {
input.write(line + '\n');
Expand Down Expand Up @@ -297,6 +311,20 @@ describe('MongoshNodeRepl', () => {
await tick();
expect(output).to.include('somelongvariable');
});
it('autocompletion during .editor does not reset the prompt', async() => {
input.write('.editor\n');
await tick();
output = '';
expect((mongoshRepl.runtimeState().repl as any)._prompt).to.equal('');
input.write('db.\u0009\u0009');
await tick();
input.write('foo\nbar\n');
expect((mongoshRepl.runtimeState().repl as any)._prompt).to.equal('');
input.write('\u0003'); // Ctrl+C for abort
await tick();
expect((mongoshRepl.runtimeState().repl as any)._prompt).to.equal('> ');
expect(stripAnsi(output)).to.equal('ddbdb.db.\tdb.\tfdb.\tfodb.\tfoo\r\nbbabar\r\n\r\n> ');
});
});

context('history support', () => {
Expand Down
36 changes: 24 additions & 12 deletions packages/cli-repl/src/mongosh-repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class MongoshNodeRepl implements EvaluationListener {
shellCliOptions: Partial<MongoshCliOptions>;
configProvider: MongoshConfigProvider;
onClearCommand?: EvaluationListener['onClearCommand'];
insideAutoComplete: boolean;

constructor(options: MongoshNodeReplOptions) {
this.input = options.input;
Expand All @@ -74,6 +75,7 @@ class MongoshNodeRepl implements EvaluationListener {
this.nodeReplOptions = options.nodeReplOptions || {};
this.shellCliOptions = options.shellCliOptions || {};
this.configProvider = options.configProvider;
this.insideAutoComplete = false;
this._runtimeState = null;
}

Expand Down Expand Up @@ -123,16 +125,21 @@ class MongoshNodeRepl implements EvaluationListener {
completer.bind(null, internalState.getAutocompleteParameters());
(repl as Mutable<typeof repl>).completer =
callbackify(async(text: string): Promise<[string[], string]> => {
// Merge the results from the repl completer and the mongosh completer.
const [ [replResults], [mongoshResults] ] = await Promise.all([
(async() => await origReplCompleter(text) || [[]])(),
(async() => await mongoshCompleter(text))()
]);
this.bus.emit('mongosh:autocompletion-complete'); // For testing.
// Remove duplicates, because shell API methods might otherwise show
// up in both completions.
const deduped = [...new Set([...replResults, ...mongoshResults])];
return [deduped, text];
this.insideAutoComplete = true;
try {
// Merge the results from the repl completer and the mongosh completer.
const [ [replResults], [mongoshResults] ] = await Promise.all([
(async() => await origReplCompleter(text) || [[]])(),
(async() => await mongoshCompleter(text))()
]);
this.bus.emit('mongosh:autocompletion-complete'); // For testing.
// Remove duplicates, because shell API methods might otherwise show
// up in both completions.
const deduped = [...new Set([...replResults, ...mongoshResults])];
return [deduped, text];
} finally {
this.insideAutoComplete = false;
}
});

const originalDisplayPrompt = repl.displayPrompt.bind(repl);
Expand Down Expand Up @@ -283,13 +290,18 @@ class MongoshNodeRepl implements EvaluationListener {
}

async eval(originalEval: asyncRepl.OriginalEvalFunction, input: string, context: any, filename: string): Promise<any> {
this.lineByLineInput.enableBlockOnNewLine();
if (!this.insideAutoComplete) {
this.lineByLineInput.enableBlockOnNewLine();
}

const { internalState, repl, shellEvaluator } = this.runtimeState();

try {
return await shellEvaluator.customEval(originalEval, input, context, filename);
} finally {
repl.setPrompt(await this.getShellPrompt(internalState));
if (!this.insideAutoComplete) {
repl.setPrompt(await this.getShellPrompt(internalState));
}
this.bus.emit('mongosh:eval-complete'); // For testing purposes.
}
}
Expand Down