[ES|QL] Separate ROW and SHOW autocomplete routines (#210934)

## Summary

Part of https://github.com/elastic/kibana/issues/195418

Gives `ROW` and `SHOW` autocomplete logic its own home 🏡

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

### Identify risks

- [ ] As with any refactor, there's a possibility this will introduce a
regression in the behavior of FROM. However, all automated tests are
passing and I have tested the behavior manually and can detect no
regression.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
Drew Tate 2025-02-13 06:35:10 -07:00 committed by GitHub
parent 526ff0516e
commit 79f1144974
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 162 additions and 2 deletions

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { getNewVariableSuggestion } from '../factories';
import { attachTriggerCommand, getFunctionSignaturesByReturnType, setup } from './helpers';
describe('autocomplete.suggest', () => {
describe('ROW column1 = value1[, ..., columnN = valueN]', () => {
const functions = getFunctionSignaturesByReturnType('row', 'any', { scalar: true });
it('suggests functions and an assignment for new expressions', async () => {
const { assertSuggestions } = await setup();
const expectedSuggestions = [getNewVariableSuggestion('var0'), ...functions];
await assertSuggestions('ROW /', expectedSuggestions);
await assertSuggestions('ROW foo = "bar", /', expectedSuggestions);
});
it('suggests only functions after an assignment', async () => {
const { assertSuggestions } = await setup();
await assertSuggestions('ROW var0 = /', functions);
});
it('suggests a comma and a pipe after a complete expression', async () => {
const { assertSuggestions } = await setup();
const expected = [', ', '| '].map(attachTriggerCommand);
await assertSuggestions('ROW var0 = 23 /', expected);
await assertSuggestions('ROW ABS(23) /', expected);
await assertSuggestions('ROW ABS(23), var0=234 /', expected);
});
});
});

View file

@ -0,0 +1,35 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { attachTriggerCommand, setup } from './helpers';
describe('autocomplete.suggest', () => {
describe('SHOW INFO', () => {
it('suggests INFO', async () => {
const { assertSuggestions } = await setup();
await assertSuggestions('SHOW /', ['INFO']);
});
it('suggests pipe after INFO', async () => {
const { assertSuggestions } = await setup();
await assertSuggestions('SHOW INFO /', [attachTriggerCommand('| ')]);
await assertSuggestions('SHOW INFO\t/', [attachTriggerCommand('| ')]);
await assertSuggestions('SHOW info /', [attachTriggerCommand('| ')]);
});
it('suggests nothing after a random word', async () => {
const { assertSuggestions } = await setup();
await assertSuggestions('SHOW lolz /', []);
await assertSuggestions('SHOW inof /', []);
});
});
});

View file

@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { isRestartingExpression } from '../../../shared/helpers';
import { CommandSuggestParams } from '../../../definitions/types';
import type { SuggestionRawDefinition } from '../../types';
import {
TRIGGER_SUGGESTION_COMMAND,
getFunctionSuggestions,
getNewVariableSuggestion,
} from '../../factories';
import { commaCompleteItem, pipeCompleteItem } from '../../complete_items';
export async function suggest({
getSuggestedVariableName,
command,
innerText,
}: CommandSuggestParams<'row'>): Promise<SuggestionRawDefinition[]> {
// ROW var0 = /
if (/=\s*$/.test(innerText)) {
return getFunctionSuggestions({ command: 'row' });
}
// ROW var0 = 23 /
else if (command.args.length > 0 && !isRestartingExpression(innerText)) {
return [
{ ...pipeCompleteItem, command: TRIGGER_SUGGESTION_COMMAND },
{ ...commaCompleteItem, text: ', ', command: TRIGGER_SUGGESTION_COMMAND },
];
}
// ROW /
// ROW foo = "bar", /
return [
getNewVariableSuggestion(getSuggestedVariableName()),
...getFunctionSuggestions({ command: 'row' }),
];
}

View file

@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
import { i18n } from '@kbn/i18n';
import { CommandSuggestParams } from '../../../definitions/types';
import type { SuggestionRawDefinition } from '../../types';
import { pipeCompleteItem } from '../../complete_items';
import { TRIGGER_SUGGESTION_COMMAND } from '../../factories';
export async function suggest({
innerText,
}: CommandSuggestParams<'show'>): Promise<SuggestionRawDefinition[]> {
// SHOW INFO /
if (/INFO\s+$/i.test(innerText)) {
return [{ ...pipeCompleteItem, command: TRIGGER_SUGGESTION_COMMAND }];
}
// SHOW INSOF /
else if (/SHOW\s+\S+\s+$/i.test(innerText)) {
return [];
}
// SHOW /
return [
{
text: 'INFO',
detail: i18n.translate('kbn-esql-validation-autocomplete.esql.show.info.detail', {
defaultMessage: 'Get information about the Elasticsearch cluster.',
}),
kind: 'Method',
label: 'INFO',
},
];
}

View file

@ -44,6 +44,8 @@ import { suggest as suggestForStats } from '../autocomplete/commands/stats';
import { suggest as suggestForWhere } from '../autocomplete/commands/where';
import { suggest as suggestForJoin } from '../autocomplete/commands/join';
import { suggest as suggestForFrom } from '../autocomplete/commands/from';
import { suggest as suggestForRow } from '../autocomplete/commands/row';
import { suggest as suggestForShow } from '../autocomplete/commands/show';
const statsValidator = (command: ESQLCommand) => {
const messages: ESQLMessage[] = [];
@ -191,12 +193,13 @@ export const commandDefinitions: Array<CommandDefinition<any>> = [
defaultMessage:
'Produces a row with one or more columns with values that you specify. This can be useful for testing.',
}),
examples: ['row a=1', 'row a=1, b=2'],
examples: ['ROW a=1', 'ROW a=1, b=2'],
signature: {
multipleParams: true,
// syntax check already validates part of this
params: [{ name: 'assignment', type: 'any' }],
},
suggest: suggestForRow,
options: [],
modes: [],
},
@ -220,13 +223,14 @@ export const commandDefinitions: Array<CommandDefinition<any>> = [
description: i18n.translate('kbn-esql-validation-autocomplete.esql.definitions.showDoc', {
defaultMessage: 'Returns information about the deployment and its capabilities',
}),
examples: ['show info'],
examples: ['SHOW INFO'],
options: [],
modes: [],
signature: {
multipleParams: false,
params: [{ name: 'functions', type: 'function' }],
},
suggest: suggestForShow,
},
{
name: 'metrics',