Skip to content

Commit 82bdf25

Browse files
authored
feat: port autocomplete direct commands and browser-runtime-core to the new autocomplete MONGOSH-2205 MONGOSH-2035 MONGOSH-2206 (#2464)
* port autocomplete direct commands and browser-runtime-core * test fixes * just use the type * bump mongodb-ts-autocomplete, return and check undefined * another assertion
1 parent c8941c1 commit 82bdf25

34 files changed

+390
-145
lines changed

package-lock.json

Lines changed: 50 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/autocomplete/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"dependencies": {
4646
"@mongodb-js/mongodb-constants": "^0.10.1",
4747
"@mongosh/shell-api": "^3.11.0",
48+
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.5",
4849
"semver": "^7.5.4"
4950
}
5051
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
2+
import type { TypeSignature } from '@mongosh/shell-api';
3+
import { signatures as shellSignatures } from '@mongosh/shell-api';
4+
5+
type TypeSignatureAttributes = { [key: string]: TypeSignature };
6+
7+
export async function directCommandCompleter(
8+
context: AutocompletionContext,
9+
line: string
10+
): Promise<string[]> {
11+
const SHELL_COMPLETIONS = shellSignatures.ShellApi
12+
.attributes as TypeSignatureAttributes;
13+
14+
// Split at space-to-non-space transitions when looking at this as a command,
15+
// because multiple spaces (e.g. 'show collections') are valid in commands.
16+
// This split keeps the spaces intact so we can join them back later.
17+
const splitLineWhitespace = line.split(/(?<!\S)(?=\S)/);
18+
const command = splitLineWhitespace[0].trim();
19+
if (!SHELL_COMPLETIONS[command]?.isDirectShellCommand) {
20+
return [];
21+
}
22+
23+
// If the shell API provides us with a completer, use it.
24+
// examples: use, show, snippet
25+
const completer = SHELL_COMPLETIONS[command].newShellCommandCompleter;
26+
if (!completer) {
27+
// examples without a custom completer: exit, quit, it, cls
28+
return [line];
29+
}
30+
31+
if (splitLineWhitespace.length === 1) {
32+
if (splitLineWhitespace[0].trimEnd() === splitLineWhitespace[0]) {
33+
// Treat e.g. 'show' like 'show '.
34+
splitLineWhitespace[0] += ' ';
35+
}
36+
37+
// Complete the first argument after the command.
38+
splitLineWhitespace.push('');
39+
}
40+
const hits =
41+
(await completer(
42+
context,
43+
splitLineWhitespace.map((item) => item.trim())
44+
)) || [];
45+
// Adjust to full input, because `completer` only completed the last item
46+
// in the line, e.g. ['profile'] -> ['show profile']
47+
const fullLineHits = hits.map((hit) =>
48+
[...splitLineWhitespace.slice(0, -1), hit].join('')
49+
);
50+
51+
return fullLineHits;
52+
}

packages/autocomplete/src/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import completer, { BASE_COMPLETIONS } from './';
1+
import { completer, BASE_COMPLETIONS } from './';
22
import { signatures as shellSignatures, Topologies } from '@mongosh/shell-api';
33

44
import { expect } from 'chai';

packages/autocomplete/src/index.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Topologies, TypeSignature } from '@mongosh/shell-api';
22
import { signatures as shellSignatures } from '@mongosh/shell-api';
3+
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
34
import semver from 'semver';
45
import {
56
CONVERSION_OPERATORS,
@@ -13,6 +14,7 @@ import {
1314
ON_PREM,
1415
DATABASE,
1516
} from '@mongodb-js/mongodb-constants';
17+
import { directCommandCompleter } from './direct-command-completer';
1618

1719
type TypeSignatureAttributes = { [key: string]: TypeSignature };
1820

@@ -78,7 +80,7 @@ const GROUP = '$group';
7880
*
7981
* @returns {array} Matching Completions, Current User Input.
8082
*/
81-
async function completer(
83+
export async function completer(
8284
params: AutocompleteParameters,
8385
line: string
8486
): Promise<[string[], string, 'exclusive'] | [string[], string]> {
@@ -398,4 +400,51 @@ function filterShellAPI(
398400
return hits;
399401
}
400402

401-
export default completer;
403+
type AutocompleteShellInstanceState = {
404+
getAutocompleteParameters: () => AutocompleteParameters;
405+
getAutocompletionContext: () => AutocompletionContext;
406+
};
407+
408+
function transformAutocompleteResults(
409+
line: string,
410+
results: { result: string }[]
411+
): [string[], string] {
412+
return [results.map((result) => result.result), line];
413+
}
414+
415+
export type CompletionResults =
416+
| [string[], string, 'exclusive']
417+
| [string[], string];
418+
419+
export async function initNewAutocompleter(
420+
instanceState: Pick<
421+
AutocompleteShellInstanceState,
422+
'getAutocompletionContext'
423+
>
424+
): Promise<(text: string) => Promise<CompletionResults>> {
425+
// only import the autocompleter code the first time we need it to
426+
// hide the time it takes from the initial startup time
427+
const { MongoDBAutocompleter } = await import(
428+
'@mongodb-js/mongodb-ts-autocomplete'
429+
);
430+
431+
const autocompletionContext = instanceState.getAutocompletionContext();
432+
const mongoDBCompleter = new MongoDBAutocompleter({
433+
context: autocompletionContext,
434+
});
435+
436+
return async (text: string): Promise<CompletionResults> => {
437+
const directResults = await directCommandCompleter(
438+
autocompletionContext,
439+
text
440+
);
441+
442+
if (directResults.length) {
443+
return [directResults, text, 'exclusive'];
444+
}
445+
446+
const results = await mongoDBCompleter.autocomplete(text);
447+
const transformed = transformAutocompleteResults(text, results);
448+
return transformed;
449+
};
450+
}

packages/browser-runtime-core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"@mongodb-js/eslint-config-mongosh": "^1.0.0",
4242
"@mongodb-js/prettier-config-devtools": "^1.0.1",
4343
"@mongodb-js/tsconfig-mongosh": "^1.0.0",
44+
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.5",
4445
"@mongosh/types": "3.6.2",
4546
"bson": "^6.10.3",
4647
"depcheck": "^1.4.7",

packages/browser-runtime-core/src/autocompleter/shell-api-autocompleter.spec.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { ShellApiAutocompleter } from './shell-api-autocompleter';
22
import { expect } from 'chai';
33
import { Topologies } from '@mongosh/shell-api';
4+
import type { AutocompleteParameters } from '@mongosh/autocomplete';
5+
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
46

5-
const standalone440 = {
7+
const standalone440Parameters: AutocompleteParameters = {
68
topology: () => Topologies.Standalone,
79
apiVersionInfo: () => undefined,
810
connectionInfo: () => ({
@@ -11,16 +13,31 @@ const standalone440 = {
1113
server_version: '4.4.0',
1214
is_local_atlas: false,
1315
}),
14-
getCollectionCompletionsForCurrentDb: () => ['bananas'],
15-
getDatabaseCompletions: () => ['databaseOne'],
16+
getCollectionCompletionsForCurrentDb: () => Promise.resolve(['bananas']),
17+
getDatabaseCompletions: () => Promise.resolve(['databaseOne']),
18+
};
19+
20+
const standalone440Context: AutocompletionContext = {
21+
currentDatabaseAndConnection: () => ({
22+
connectionId: 'connection-1',
23+
databaseName: 'databaseOne',
24+
}),
25+
databasesForConnection: () => Promise.resolve(['databaseOne']),
26+
collectionsForDatabase: () => Promise.resolve(['bananas', 'coll1']),
27+
schemaInformationForCollection: () => Promise.resolve({}),
28+
};
29+
30+
const shellInstanceState = {
31+
getAutocompleteParameters: () => standalone440Parameters,
32+
getAutocompletionContext: () => standalone440Context,
1633
};
1734

1835
describe('Autocompleter', function () {
1936
describe('getCompletions', function () {
2037
let autocompleter: ShellApiAutocompleter;
2138

2239
beforeEach(function () {
23-
autocompleter = new ShellApiAutocompleter(standalone440);
40+
autocompleter = new ShellApiAutocompleter(shellInstanceState);
2441
});
2542

2643
it('returns completions for text before cursor', async function () {

0 commit comments

Comments
 (0)