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

[ES|QL] new pattern for SORT autocomplete #193595

Merged
merged 10 commits into from
Sep 23, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,101 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { setup, getFieldNamesByType } from './helpers';
import {
setup,
getFieldNamesByType,
attachTriggerCommand,
getFunctionSignaturesByReturnType,
} from './helpers';

describe('autocomplete.suggest', () => {
describe('SORT ( <column> [ ASC / DESC ] [ NULLS FIST / NULLS LAST ] )+', () => {
describe('SORT <column> ...', () => {
test('suggests command on first character', async () => {
const expectedFieldSuggestions = getFieldNamesByType('any').map(attachTriggerCommand);
const expectedFunctionSuggestions = getFunctionSignaturesByReturnType('sort', 'any', {
scalar: true,
}).map(attachTriggerCommand);

test('suggests column', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort /', [
...expectedFieldSuggestions,
...expectedFunctionSuggestions,
]);
await assertSuggestions('from a | sort keyw/', [
...expectedFieldSuggestions,
...expectedFunctionSuggestions,
]);
await assertSuggestions(
'from a | sort /',
[...getFieldNamesByType('any')].map((field) => `${field} `)
'from a | sort keywordField/',
[
{
filterText: 'keywordField',
text: 'keywordField, ',
},
{
filterText: 'keywordField',
text: 'keywordField | ',
},
{
filterText: 'keywordField',
text: 'keywordField ASC',
},
{
filterText: 'keywordField',
text: 'keywordField DESC',
},
{
filterText: 'keywordField',
text: 'keywordField NULLS FIRST',
},
{
filterText: 'keywordField',
text: 'keywordField NULLS LAST',
},
].map(attachTriggerCommand)
);
});
it('suggests subsequent column after comma', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort keywordField, /', [
...expectedFieldSuggestions,
...expectedFunctionSuggestions,
]);
await assertSuggestions('from a | sort keywordField, doubl/', [
...expectedFieldSuggestions,
...expectedFunctionSuggestions,
]);
await assertSuggestions(
'from a | sort column, /',
[...getFieldNamesByType('any')].map((field) => `${field} `)
'from a | sort keywordField, doubleField/',
[
{
filterText: 'doubleField',
text: 'doubleField, ',
},
{
filterText: 'doubleField',
text: 'doubleField | ',
},
{
filterText: 'doubleField',
text: 'doubleField ASC',
},
{
filterText: 'doubleField',
text: 'doubleField DESC',
},
{
filterText: 'doubleField',
text: 'doubleField NULLS FIRST',
},
{
filterText: 'doubleField',
text: 'doubleField NULLS LAST',
},
].map(attachTriggerCommand)
);
});
});
Expand All @@ -30,76 +110,134 @@ describe('autocomplete.suggest', () => {
test('suggests all modifiers on first space', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField /', [
'ASC ',
'DESC ',
'NULLS FIRST ',
'NULLS LAST ',
',',
'| ',
]);
await assertSuggestions(
'from a | sort stringField /',
['ASC', 'DESC', 'NULLS FIRST', 'NULLS LAST', ', ', '| '].map(attachTriggerCommand)
);
});

test('when user starts to type ASC modifier', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField A/', ['ASC ']);
await assertSuggestions(
'from a | sort stringField A/',
['ASC', 'DESC', 'NULLS FIRST', 'NULLS LAST'].map(attachTriggerCommand)
);
await assertSuggestions(
'from a | sort stringField ASC/',
['ASC NULLS FIRST', 'ASC NULLS LAST', 'ASC, ', 'ASC | '].map(attachTriggerCommand)
);
await assertSuggestions(
'from a | sort stringField asc/',
['asc NULLS FIRST', 'asc NULLS LAST', 'asc, ', 'asc | '].map(attachTriggerCommand)
);
});

test('when user starts to type DESC modifier', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField d/', ['DESC ']);
await assertSuggestions('from a | sort stringField des/', ['DESC ']);
await assertSuggestions('from a | sort stringField DES/', ['DESC ']);
await assertSuggestions(
'from a | sort stringField D/',
['ASC', 'DESC', 'NULLS FIRST', 'NULLS LAST'].map(attachTriggerCommand)
);
await assertSuggestions(
'from a | sort stringField DESC/',
['DESC NULLS FIRST', 'DESC NULLS LAST', 'DESC, ', 'DESC | '].map(attachTriggerCommand)
);
await assertSuggestions('from a | sort stringField desc/', [
'desc NULLS FIRST',
'desc NULLS LAST',
'desc, ',
'desc | ',
]);
});
});

describe('... [ NULLS FIST / NULLS LAST ]', () => {
test('suggests command on first character', async () => {
describe('... [ NULLS FIRST / NULLS LAST ]', () => {
test('suggests nulls modifier after order modifier + space', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField ASC /', [
'NULLS FIRST ',
'NULLS LAST ',
',',
'NULLS FIRST',
'NULLS LAST',
', ',
'| ',
]);
});

test('when user starts to type NULLS modifiers', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField N/', ['NULLS FIRST ', 'NULLS LAST ']);
await assertSuggestions('from a | sort stringField null/', ['NULLS FIRST ', 'NULLS LAST ']);
// @TODO check for replacement range
await assertSuggestions('from a | sort stringField N/', [
'ASC',
'DESC',
'NULLS FIRST',
'NULLS LAST',
]);
await assertSuggestions('from a | sort stringField null/', [
'ASC',
'DESC',
'NULLS FIRST',
'NULLS LAST',
]);
await assertSuggestions('from a | sort stringField nulls/', [
'NULLS FIRST ',
'NULLS LAST ',
'ASC',
'DESC',
'NULLS FIRST',
'NULLS LAST',
]);
await assertSuggestions('from a | sort stringField nulls /', [
'NULLS FIRST ',
'NULLS LAST ',
'ASC',
'DESC',
'NULLS FIRST',
'NULLS LAST',
]);
});

test('when user types NULLS FIRST', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField NULLS F/', ['NULLS FIRST ']);
await assertSuggestions('from a | sort stringField NULLS FI/', ['NULLS FIRST ']);
await assertSuggestions(
'from a | sort stringField NULLS F/',
[
'ASC',
'DESC',
{ text: 'NULLS LAST', rangeToReplace: { start: 27, end: 34 } },
{ text: 'NULLS FIRST', rangeToReplace: { start: 27, end: 34 } },
].map(attachTriggerCommand)
);
await assertSuggestions(
'from a | sort stringField NULLS FI/',
[
'ASC',
'DESC',
{ text: 'NULLS LAST', rangeToReplace: { start: 27, end: 35 } },
{ text: 'NULLS FIRST', rangeToReplace: { start: 27, end: 35 } },
].map(attachTriggerCommand)
);
});

test('when user types NULLS LAST', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField NULLS L/', ['NULLS LAST ']);
await assertSuggestions('from a | sort stringField NULLS LAS/', ['NULLS LAST ']);
await assertSuggestions(
'from a | sort stringField NULLS L/',
['ASC', 'DESC', 'NULLS LAST', 'NULLS FIRST'].map(attachTriggerCommand)
);
await assertSuggestions(
'from a | sort stringField NULLS LAS/',
['ASC', 'DESC', 'NULLS LAST', 'NULLS FIRST'].map(attachTriggerCommand)
);
});

test('after nulls are entered, suggests comma or pipe', async () => {
const { assertSuggestions } = await setup();

await assertSuggestions('from a | sort stringField NULLS LAST /', [',', '| ']);
await assertSuggestions(
'from a | sort stringField NULLS LAST /',
[', ', '| '].map(attachTriggerCommand)
);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { groupingFunctionDefinitions } from '../../definitions/grouping';
import * as autocomplete from '../autocomplete';
import type { ESQLCallbacks } from '../../shared/types';
import type { EditorContext, SuggestionRawDefinition } from '../types';
import { TIME_SYSTEM_PARAMS, getSafeInsertText } from '../factories';
import { TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND, getSafeInsertText } from '../factories';
import { getFunctionSignatures } from '../../definitions/helpers';
import { ESQLRealField } from '../../validation/types';
import {
Expand Down Expand Up @@ -348,3 +348,17 @@ export const setup = async (caret = '/') => {
assertSuggestions,
};
};

/**
* Attaches the trigger command to an expected suggestion to make
* sure the suggestions menu will be opened when the suggestion is accepted.
*/
export const attachTriggerCommand = (
s: string | PartialSuggestionWithText
): PartialSuggestionWithText =>
typeof s === 'string'
? {
text: s,
command: TRIGGER_SUGGESTION_COMMAND,
}
: { ...s, command: TRIGGER_SUGGESTION_COMMAND };
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
PartialSuggestionWithText,
TIME_PICKER_SUGGESTION,
setup,
attachTriggerCommand,
} from './__tests__/helpers';
import { METADATA_FIELDS } from '../shared/constants';
import { ESQL_COMMON_NUMERIC_TYPES, ESQL_STRING_TYPES } from '../shared/esql_types';
Expand Down Expand Up @@ -287,7 +288,10 @@ describe('autocomplete', () => {
'from a | grok key/',
getFieldNamesByType(ESQL_STRING_TYPES).map((name) => `${name} `)
);
testSuggestions('from a | grok keywordField/', []);
testSuggestions(
'from a | grok keywordField/',
['keywordField ', 'textField '].map(attachTriggerCommand)
);
});

describe('dissect', () => {
Expand Down Expand Up @@ -327,7 +331,10 @@ describe('autocomplete', () => {
'from a | dissect key/',
getFieldNamesByType(ESQL_STRING_TYPES).map((name) => `${name} `)
);
testSuggestions('from a | dissect keywordField/', []);
testSuggestions(
'from a | dissect keywordField/',
['keywordField ', 'textField '].map(attachTriggerCommand)
);
});

describe('limit', () => {
Expand Down Expand Up @@ -699,16 +706,6 @@ describe('autocomplete', () => {
* NOTE: Monaco uses an Invoke trigger kind when the show suggestions action is triggered (e.g. accepting the "FROM" suggestion)
*/

const attachTriggerCommand = (
s: string | PartialSuggestionWithText
): PartialSuggestionWithText =>
typeof s === 'string'
? {
text: s,
command: TRIGGER_SUGGESTION_COMMAND,
}
: { ...s, command: TRIGGER_SUGGESTION_COMMAND };

const attachAsSnippet = (s: PartialSuggestionWithText): PartialSuggestionWithText => ({
...s,
asSnippet: true,
Expand Down
Loading