Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compass-editor, compass-query-bar): provide auto completed query history matching user input COMPASS-8018 #6040

Merged
Show file tree
Hide file tree
Changes from 11 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
76 changes: 38 additions & 38 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/compass-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"typescript": "^5.0.4"
},
"dependencies": {
"@codemirror/autocomplete": "^6.4.0",
"@codemirror/autocomplete": "^6.17.0",
"@codemirror/commands": "^6.1.2",
"@codemirror/lang-javascript": "^6.1.2",
"@codemirror/lang-json": "^6.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@ describe('query autocompleter', function () {

context('when autocompleting outside of stage', function () {
context('with empty pipeline', function () {
it('should return stages', function () {
it('should return stages', async function () {
const completions = getCompletions('[{ $');

expect(
completions.map((completion) => completion.label).sort()
(await completions).map((completion) => completion.label).sort()
).to.deep.eq([...STAGE_OPERATOR_NAMES].sort());
});
});

context('with other stages in the pipeline', function () {
it('should return stages', function () {
it('should return stages', async function () {
const completions = getCompletions('[{$match:{foo: 1}},{$');

expect(
completions.map((completion) => completion.label).sort()
(await completions).map((completion) => completion.label).sort()
).to.deep.eq([...STAGE_OPERATOR_NAMES].sort());
});
});

context('inside block', function () {
it('should not suggest blocks in snippets', function () {
it('should not suggest blocks in snippets', async function () {
const completions = getCompletions(`[{ /** comment */ $`);

completions.forEach((completion) => {
(await completions).forEach((completion) => {
const snippet = applySnippet(completion);
expect(snippet).to.match(
/^[^{]/,
Expand All @@ -46,10 +46,10 @@ describe('query autocompleter', function () {
});

context('outside block', function () {
it('should have blocks in snippets', function () {
it('should have blocks in snippets', async function () {
const completions = getCompletions(`[{ $match: {foo: 1} }, $`);

completions.forEach((completion) => {
(await completions).forEach((completion) => {
const snippet = applySnippet(completion);
expect(snippet).to.match(
/^{/,
Expand All @@ -61,15 +61,14 @@ describe('query autocompleter', function () {
});

context('when autocompleting inside the stage', function () {
it('should return stage completer results', function () {
it('should return stage completer results', async function () {
const completions = getCompletions('[{$bucket: { _id: "$', {
fields: [{ name: 'foo' }, { name: 'bar' }],
});

expect(completions.map((completion) => completion.label)).to.deep.eq([
'$foo',
'$bar',
]);
expect(
(await completions).map((completion) => completion.label)
).to.deep.eq(['$foo', '$bar']);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { expect } from 'chai';
import { createQueryWithHistoryAutocompleter } from './query-autocompleter-with-history';
import { setupCodemirrorCompleter } from '../../test/completer';
import type { SavedQuery } from '../../dist/codemirror/query-history-autocompleter';
import { createQuery } from './query-history-autocompleter';

describe('query history autocompleter', function () {
const { getCompletions, cleanup } = setupCodemirrorCompleter(
Expand Down Expand Up @@ -65,26 +66,31 @@ describe('query history autocompleter', function () {

const mockOnApply: (query: SavedQuery['queryProperties']) => any = () => {};

it('returns all saved queries as completions on click', function () {
it('returns all saved queries as completions on click', async function () {
expect(
getCompletions('{}', savedQueries, undefined, mockOnApply)
await getCompletions('{}', savedQueries, undefined, mockOnApply)
).to.have.lengthOf(5);
});

it('returns normal autocompletion when user starts typing', function () {
it('returns combined completions when user starts typing', async function () {
expect(
getCompletions('foo', savedQueries, undefined, mockOnApply)
).to.have.lengthOf(45);
await getCompletions('foo', savedQueries, undefined, mockOnApply)
).to.have.lengthOf(50);
});

it('completes "any text" when inside a string', function () {
it('completes "any text" when inside a string', async function () {
const prettifiedSavedQueries = savedQueries.map((query) =>
createQuery(query)
);
expect(
getCompletions(
'{ bar: 1, buz: 2, foo: "b',
savedQueries,
undefined,
mockOnApply
(
await getCompletions(
'{ bar: 1, buz: 2, foo: "b',
savedQueries,
undefined,
mockOnApply
)
).map((completion) => completion.label)
).to.deep.eq(['bar', '1', 'buz', '2', 'foo']);
).to.deep.eq(['bar', '1', 'buz', '2', 'foo', ...prettifiedSavedQueries]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import {
createQueryHistoryAutocompleter,
} from './query-history-autocompleter';
import { createQueryAutocompleter } from './query-autocompleter';

import type {
CompletionSource,
CompletionContext,
CompletionSection,
Completion,
} from '@codemirror/autocomplete';
import type { CompletionOptions } from '../autocompleter';
import { css } from '@mongodb-js/compass-components';

export const createQueryWithHistoryAutocompleter = (
recentQueries: SavedQuery[],
Expand All @@ -21,10 +23,55 @@ export const createQueryWithHistoryAutocompleter = (
);

const originalQueryAutocompleter = createQueryAutocompleter(options);
const historySection: CompletionSection = {
name: 'Query History',
header: renderDottedLine,
};

return async function fullCompletions(context: CompletionContext) {
if (context.state.doc.toString() === '{}') {
return queryHistoryAutocompleter(context);
}

const combinedOptions: Completion[] = [];
// completions assigned to a section appear below ones that are not assigned
const baseCompletions = await originalQueryAutocompleter(context);
const historyCompletions = await queryHistoryAutocompleter(context);
Anemy marked this conversation as resolved.
Show resolved Hide resolved

return function fullCompletions(context: CompletionContext) {
if (context.state.doc.toString() !== '{}')
return originalQueryAutocompleter(context);
return queryHistoryAutocompleter(context);
if (baseCompletions) {
combinedOptions.push(
...baseCompletions.options.map((option) => ({
...option,
}))
);
}
if (historyCompletions) {
combinedOptions.push(
...historyCompletions.options.map((option) => ({
...option,
section: historySection,
}))
);
}

return {
from: Math.min(
historyCompletions?.from ?? context.pos,
baseCompletions?.from ?? context.pos
),
options: combinedOptions,
};
};
};

const sectionHeaderStyles = css({
display: 'list-item',
borderBottom: '1px dashed #ccc',
margin: `5px 0`,
});

function renderDottedLine(): HTMLElement {
const header = document.createElement('div');
header.className = sectionHeaderStyles;
return header;
}
Loading
Loading