Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
81 changes: 50 additions & 31 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/autocomplete/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"dependencies": {
"@mongodb-js/mongodb-constants": "^0.10.1",
"@mongosh/shell-api": "^3.11.0",
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.4",
"semver": "^7.5.4"
}
}
52 changes: 52 additions & 0 deletions packages/autocomplete/src/direct-command-completer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
import type { TypeSignature } from '@mongosh/shell-api';
import { signatures as shellSignatures } from '@mongosh/shell-api';

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

export async function directCommandCompleter(
context: AutocompletionContext,
line: string
): Promise<string[]> {
const SHELL_COMPLETIONS = shellSignatures.ShellApi
.attributes as TypeSignatureAttributes;

// Split at space-to-non-space transitions when looking at this as a command,
// because multiple spaces (e.g. 'show collections') are valid in commands.
// This split keeps the spaces intact so we can join them back later.
const splitLineWhitespace = line.split(/(?<!\S)(?=\S)/);
const command = splitLineWhitespace[0].trim();
if (!SHELL_COMPLETIONS[command]?.isDirectShellCommand) {
return [];
}

// If the shell API provides us with a completer, use it.
// examples: use, show, snippet
const completer = SHELL_COMPLETIONS[command].newShellCommandCompleter;
if (!completer) {
// examples without a custom completer: exit, quit, it, cls
return [line];
}

if (splitLineWhitespace.length === 1) {
if (splitLineWhitespace[0].trimEnd() === splitLineWhitespace[0]) {
// Treat e.g. 'show' like 'show '.
splitLineWhitespace[0] += ' ';
}

// Complete the first argument after the command.
splitLineWhitespace.push('');
}
const hits =
(await completer(
context,
splitLineWhitespace.map((item) => item.trim())
)) || [];
// Adjust to full input, because `completer` only completed the last item
// in the line, e.g. ['profile'] -> ['show profile']
const fullLineHits = hits.map((hit) =>
[...splitLineWhitespace.slice(0, -1), hit].join('')
);

return fullLineHits;
}
2 changes: 1 addition & 1 deletion packages/autocomplete/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import completer, { BASE_COMPLETIONS } from './';
import { completer, BASE_COMPLETIONS } from './';
import { signatures as shellSignatures, Topologies } from '@mongosh/shell-api';

import { expect } from 'chai';
Expand Down
53 changes: 51 additions & 2 deletions packages/autocomplete/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Topologies, TypeSignature } from '@mongosh/shell-api';
import { signatures as shellSignatures } from '@mongosh/shell-api';
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';
import semver from 'semver';
import {
CONVERSION_OPERATORS,
Expand All @@ -13,6 +14,7 @@ import {
ON_PREM,
DATABASE,
} from '@mongodb-js/mongodb-constants';
import { directCommandCompleter } from './direct-command-completer';

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

Expand Down Expand Up @@ -78,7 +80,7 @@ const GROUP = '$group';
*
* @returns {array} Matching Completions, Current User Input.
*/
async function completer(
export async function completer(
params: AutocompleteParameters,
line: string
): Promise<[string[], string, 'exclusive'] | [string[], string]> {
Expand Down Expand Up @@ -398,4 +400,51 @@ function filterShellAPI(
return hits;
}

export default completer;
type AutocompleteShellInstanceState = {
getAutocompleteParameters: () => AutocompleteParameters;
getAutocompletionContext: () => AutocompletionContext;
};

function transformAutocompleteResults(
line: string,
results: { result: string }[]
): [string[], string] {
return [results.map((result) => result.result), line];
}

export type CompletionResults =
| [string[], string, 'exclusive']
| [string[], string];

export async function initNewAutocompleter(
instanceState: Pick<
AutocompleteShellInstanceState,
'getAutocompletionContext'
>
): Promise<(text: string) => Promise<CompletionResults>> {
// only import the autocompleter code the first time we need it to
// hide the time it takes from the initial startup time
const { MongoDBAutocompleter } = await import(
'@mongodb-js/mongodb-ts-autocomplete'
);

const autocompletionContext = instanceState.getAutocompletionContext();
const mongoDBCompleter = new MongoDBAutocompleter({
context: autocompletionContext,
});

return async (text: string): Promise<CompletionResults> => {
const directResults = await directCommandCompleter(
autocompletionContext,
text
);

if (directResults.length) {
return [directResults, text, 'exclusive'];
}

const results = await mongoDBCompleter.autocomplete(text);
const transformed = transformAutocompleteResults(text, results);
return transformed;
};
}
1 change: 1 addition & 0 deletions packages/browser-runtime-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@mongodb-js/eslint-config-mongosh": "^1.0.0",
"@mongodb-js/prettier-config-devtools": "^1.0.1",
"@mongodb-js/tsconfig-mongosh": "^1.0.0",
"@mongodb-js/mongodb-ts-autocomplete": "^0.2.4",
"@mongosh/types": "3.6.2",
"bson": "^6.10.3",
"depcheck": "^1.4.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ShellApiAutocompleter } from './shell-api-autocompleter';
import { expect } from 'chai';
import { Topologies } from '@mongosh/shell-api';
import type { AutocompleteParameters } from '@mongosh/autocomplete';
import type { AutocompletionContext } from '@mongodb-js/mongodb-ts-autocomplete';

const standalone440 = {
const standalone440Parameters: AutocompleteParameters = {
topology: () => Topologies.Standalone,
apiVersionInfo: () => undefined,
connectionInfo: () => ({
Expand All @@ -11,16 +13,31 @@ const standalone440 = {
server_version: '4.4.0',
is_local_atlas: false,
}),
getCollectionCompletionsForCurrentDb: () => ['bananas'],
getDatabaseCompletions: () => ['databaseOne'],
getCollectionCompletionsForCurrentDb: () => Promise.resolve(['bananas']),
getDatabaseCompletions: () => Promise.resolve(['databaseOne']),
};

const standalone440Context: AutocompletionContext = {
currentDatabaseAndConnection: () => ({
connectionId: 'connection-1',
databaseName: 'databaseOne',
}),
databasesForConnection: () => Promise.resolve(['databaseOne']),
collectionsForDatabase: () => Promise.resolve(['bananas', 'coll1']),
schemaInformationForCollection: () => Promise.resolve({}),
};

const shellInstanceState = {
getAutocompleteParameters: () => standalone440Parameters,
getAutocompletionContext: () => standalone440Context,
};

describe('Autocompleter', function () {
describe('getCompletions', function () {
let autocompleter: ShellApiAutocompleter;

beforeEach(function () {
autocompleter = new ShellApiAutocompleter(standalone440);
autocompleter = new ShellApiAutocompleter(shellInstanceState);
});

it('returns completions for text before cursor', async function () {
Expand Down
Loading