mirror of
https://github.com/elastic/kibana.git
synced 2025-04-18 23:21:39 -04:00
[ES|QL] remove worker (#218006)
## Summary Fix https://github.com/elastic/kibana/issues/217923 Investigations in https://github.com/elastic/kibana/issues/217368 showed that there was basically no performance impact to passing the AST across a thread boundary. But we also didn't detect a pressing reason to remove the worker. Since then, however, we noticed another cost associated with the worker: it's a hefty Javascript file, even in production builds. In addition, we are doing parsing on the main thread _and_ the worker, so the `kbn-esql-ast` package is actually being loaded and parsed twice by the browser, once for the main thread and once for the worker. This PR removes our worker. Our parsing associated with validation and autocomplete will still be done asynchronously, but on the main thread. I do not see any regression in perceived performance. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> Co-authored-by: Stratoula Kalafateli <efstratia.kalafateli@elastic.co>
This commit is contained in:
parent
f660e0140e
commit
9b4403b7dc
30 changed files with 119 additions and 407 deletions
|
@ -25,7 +25,6 @@ import {
|
|||
import type { CoreStart } from '@kbn/core/public';
|
||||
|
||||
import { ESQLCallbacks, ESQLRealField, validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import type { StartDependencies } from './plugin';
|
||||
import { CodeSnippet } from './code_snippet';
|
||||
|
||||
|
@ -77,16 +76,13 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => {
|
|||
if (currentQuery === '') {
|
||||
return;
|
||||
}
|
||||
validateQuery(
|
||||
currentQuery,
|
||||
getAstAndSyntaxErrors,
|
||||
{ ignoreOnMissingCallbacks: ignoreErrors },
|
||||
callbacks
|
||||
).then(({ errors: validationErrors, warnings: validationWarnings }) => {
|
||||
// syntax errors come with a slight different format than other validation errors
|
||||
setErrors(validationErrors.map((e) => ('severity' in e ? e.message : e.text)));
|
||||
setWarnings(validationWarnings.map((e) => e.text));
|
||||
});
|
||||
validateQuery(currentQuery, { ignoreOnMissingCallbacks: ignoreErrors }, callbacks).then(
|
||||
({ errors: validationErrors, warnings: validationWarnings }) => {
|
||||
// syntax errors come with a slight different format than other validation errors
|
||||
setErrors(validationErrors.map((e) => ('severity' in e ? e.message : e.text)));
|
||||
setWarnings(validationWarnings.map((e) => e.text));
|
||||
}
|
||||
);
|
||||
}, [currentQuery, ignoreErrors, callbacks]);
|
||||
|
||||
const checkboxes = [
|
||||
|
@ -106,7 +102,7 @@ export const App = (props: { core: CoreStart; plugins: StartDependencies }) => {
|
|||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody style={{ maxWidth: 800, margin: '0 auto' }}>
|
||||
<EuiPageBody css={{ maxWidth: 800, margin: '0 auto' }}>
|
||||
<EuiPageHeader paddingSize="s" bottomBorder={true} pageTitle="ES|QL validation example" />
|
||||
<EuiPageSection paddingSize="s">
|
||||
<p>This app shows how to use the ES|QL validation API with all its options</p>
|
||||
|
|
|
@ -56,7 +56,7 @@ export function CodeSnippet({ currentQuery, callbacks, ignoreErrors }: CodeSnipp
|
|||
<EuiCodeBlock language="typescript" isCopyable>
|
||||
{`
|
||||
import { ESQLCallbacks, validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
|
||||
const currentQuery = "${currentQuery}";
|
||||
|
||||
|
@ -64,7 +64,6 @@ const callbacks: ESQLCallbacks = () => ${getCallbacksCode(callbacks)};
|
|||
|
||||
const {errors, warnings} = validateQuery(
|
||||
currentQuery,
|
||||
getAstAndSyntaxErrors,
|
||||
{ ignoreOnMissingCallbacks: ${Boolean(ignoreErrors)} },
|
||||
callbacks
|
||||
);
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
"@kbn/data-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/developer-examples-plugin",
|
||||
"@kbn/esql-ast",
|
||||
"@kbn/esql-validation-autocomplete",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ export type {
|
|||
ESQLColumn,
|
||||
ESQLLiteral,
|
||||
ESQLParamLiteral,
|
||||
AstProviderFn,
|
||||
EditorError,
|
||||
ESQLAstNode,
|
||||
ESQLInlineCast,
|
||||
|
@ -57,7 +56,6 @@ export {
|
|||
parseErrors,
|
||||
type ParseOptions,
|
||||
type ParseResult,
|
||||
getAstAndSyntaxErrors,
|
||||
ESQLErrorListener,
|
||||
} from './src/parser';
|
||||
|
||||
|
|
|
@ -15,9 +15,6 @@ export {
|
|||
parseErrors,
|
||||
type ParseOptions,
|
||||
type ParseResult,
|
||||
|
||||
/** @deprecated Use `parse` instead. */
|
||||
parse as getAstAndSyntaxErrors,
|
||||
} from './parser';
|
||||
|
||||
export { ESQLErrorListener } from './esql_error_listener';
|
||||
|
|
|
@ -489,13 +489,6 @@ export interface ESQLMessage {
|
|||
code: string;
|
||||
}
|
||||
|
||||
export type AstProviderFn = (text: string | undefined) =>
|
||||
| Promise<{
|
||||
ast: ESQLAst;
|
||||
errors: EditorError[];
|
||||
}>
|
||||
| { ast: ESQLAst; errors: EditorError[] };
|
||||
|
||||
export interface EditorError {
|
||||
startLineNumber: number;
|
||||
endLineNumber: number;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse, mutate, BasicPrettyPrinter } from '@kbn/esql-ast';
|
||||
import { sanitazeESQLInput } from './sanitaze_input';
|
||||
|
||||
|
@ -61,7 +60,7 @@ export function appendWhereClauseToESQLQuery(
|
|||
filterValue = '';
|
||||
}
|
||||
|
||||
const { ast } = getAstAndSyntaxErrors(baseESQLQuery);
|
||||
const { ast } = parse(baseESQLQuery);
|
||||
|
||||
const lastCommandIsWhere = ast[ast.length - 1].name === 'where';
|
||||
// if where command already exists in the end of the query:
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
|
||||
export function getESQLWithSafeLimit(esql: string, limit: number): string {
|
||||
const { ast } = getAstAndSyntaxErrors(esql);
|
||||
const { ast } = parse(esql);
|
||||
const sourceCommand = ast.find(({ name }) => ['from', 'metrics'].includes(name));
|
||||
if (!sourceCommand) {
|
||||
return esql;
|
||||
|
|
|
@ -25,7 +25,7 @@ For instance, not passing the `getSources` callback will report all index mentio
|
|||
##### Usage
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
// define all callbacks
|
||||
|
@ -35,13 +35,13 @@ const myCallbacks = {
|
|||
};
|
||||
|
||||
// Full validation performed
|
||||
const { errors, warnings } = await validateQuery("from index | stats 1 + avg(myColumn)", getAstAndSyntaxErrors, undefined, myCallbacks);
|
||||
const { errors, warnings } = await validateQuery("from index | stats 1 + avg(myColumn)", parse, undefined, myCallbacks);
|
||||
```
|
||||
|
||||
If not all callbacks are available it is possible to gracefully degrade the validation experience with the `ignoreOnMissingCallbacks` option:
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
// define only the getSources callback
|
||||
|
@ -52,7 +52,7 @@ const myCallbacks = {
|
|||
// ignore errors that might be triggered by the lack of some callbacks (i.e. "Unknown columns", etc...)
|
||||
const { errors, warnings } = await validateQuery(
|
||||
'from index | stats 1 + avg(myColumn)',
|
||||
getAstAndSyntaxErrors,
|
||||
parse,
|
||||
{ ignoreOnMissingCallbacks: true },
|
||||
myCallbacks
|
||||
);
|
||||
|
@ -63,7 +63,7 @@ const { errors, warnings } = await validateQuery(
|
|||
This is the complete logic for the ES|QL autocomplete language, it is completely independent from the actual editor (i.e. Monaco) and the suggestions reported need to be wrapped against the specific editor shape.
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { suggest } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
const queryString = "from index | stats 1 + avg(myColumn) ";
|
||||
|
@ -76,7 +76,7 @@ const suggestions = await suggest(
|
|||
queryString,
|
||||
queryString.length - 1, // the cursor position in a single line context
|
||||
{ triggerCharacter: " "; triggerKind: 1 }, // kind = 0 is a programmatic trigger, while other values are ignored
|
||||
getAstAndSyntaxErrors,
|
||||
parse,
|
||||
myCallbacks
|
||||
);
|
||||
|
||||
|
@ -102,7 +102,7 @@ This feature provides a list of suggestions to propose as fixes for a subset of
|
|||
The feature works in combination with the validation service.
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { validateQuery, getActions } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
const queryString = "from index2 | stats 1 + avg(myColumn)"
|
||||
|
@ -111,12 +111,12 @@ const myCallbacks = {
|
|||
getSources: async () => [{name: 'index', hidden: false}],
|
||||
...
|
||||
};
|
||||
const { errors, warnings } = await validateQuery(queryString, getAstAndSyntaxErrors, undefined, myCallbacks);
|
||||
const { errors, warnings } = await validateQuery(queryString, parse, undefined, myCallbacks);
|
||||
|
||||
const {title, edits} = await getActions(
|
||||
queryString,
|
||||
errors,
|
||||
getAstAndSyntaxErrors,
|
||||
parse,
|
||||
undefined,
|
||||
myCallbacks
|
||||
);
|
||||
|
@ -129,7 +129,7 @@ console.log({ title, edits });
|
|||
Like with validation also `getActions` can 'relax' its internal checks when no callbacks, either all or specific ones, are passed.
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { validateQuery, getActions } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
const queryString = "from index2 | keep unquoted-field"
|
||||
|
@ -138,12 +138,12 @@ const myCallbacks = {
|
|||
getSources: async () => [{name: 'index', hidden: false}],
|
||||
...
|
||||
};
|
||||
const { errors, warnings } = await validateQuery(queryString, getAstAndSyntaxErrors, undefined, myCallbacks);
|
||||
const { errors, warnings } = await validateQuery(queryString, parse, undefined, myCallbacks);
|
||||
|
||||
const {title, edits} = await getActions(
|
||||
queryString,
|
||||
errors,
|
||||
getAstAndSyntaxErrors,
|
||||
parse,
|
||||
{ relaxOnMissingCallbacks: true },
|
||||
myCallbacks
|
||||
);
|
||||
|
@ -159,13 +159,13 @@ This is an important function in order to build more features on top of the exis
|
|||
For instance to show contextual information on Hover the `getAstContext` function can be leveraged to get the correct context for the cursor position:
|
||||
|
||||
```js
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { getAstContext } from '@kbn/esql-validation-autocomplete';
|
||||
|
||||
const queryString = 'from index2 | stats 1 + avg(myColumn)';
|
||||
const offset = queryString.indexOf('avg');
|
||||
|
||||
const astContext = getAstContext(queryString, getAstAndSyntaxErrors(queryString), offset);
|
||||
const astContext = getAstContext(queryString, parse(queryString), offset);
|
||||
|
||||
if (astContext.type === 'function') {
|
||||
const fnNode = astContext.node;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { ESQLCallbacks } from '../../shared/types';
|
||||
import * as autocomplete from '../autocomplete';
|
||||
import { getCallbackMocks } from '../../__tests__/helpers';
|
||||
|
@ -26,7 +25,7 @@ const setup = async (caret = '?') => {
|
|||
const pos = query.indexOf(caret);
|
||||
if (pos < 0) throw new Error(`User cursor/caret "${caret}" not found in query: ${query}`);
|
||||
const querySansCaret = query.slice(0, pos) + query.slice(pos + 1);
|
||||
return await autocomplete.suggest(querySansCaret, pos, ctx, getAstAndSyntaxErrors, cb);
|
||||
return await autocomplete.suggest(querySansCaret, pos, ctx, cb);
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import { camelCase } from 'lodash';
|
||||
import { parse } from '@kbn/esql-ast';
|
||||
import { scalarFunctionDefinitions } from '../../definitions/generated/scalar_functions';
|
||||
import { operatorsDefinitions } from '../../definitions/all_operators';
|
||||
import { NOT_SUGGESTED_TYPES } from '../../shared/resources_helpers';
|
||||
|
@ -353,13 +352,7 @@ export const setup = async (caret = '/') => {
|
|||
? { triggerKind: 1, triggerCharacter: opts.triggerCharacter }
|
||||
: { triggerKind: 0 };
|
||||
|
||||
return await autocomplete.suggest(
|
||||
querySansCaret,
|
||||
pos,
|
||||
ctx,
|
||||
(_query: string | undefined) => parse(_query, { withFormatting: true }),
|
||||
opts.callbacks ?? callbacks
|
||||
);
|
||||
return await autocomplete.suggest(querySansCaret, pos, ctx, opts.callbacks ?? callbacks);
|
||||
};
|
||||
|
||||
const assertSuggestions: AssertSuggestionsFn = async (query, expected, opts) => {
|
||||
|
|
|
@ -7,31 +7,30 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { suggest } from './autocomplete';
|
||||
import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands';
|
||||
import { scalarFunctionDefinitions } from '../definitions/generated/scalar_functions';
|
||||
import { timeUnitsToSuggest } from '../definitions/literals';
|
||||
import { commandDefinitions as unmodifiedCommandDefinitions } from '../definitions/commands';
|
||||
import { getSafeInsertText, TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND } from './factories';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import {
|
||||
policies,
|
||||
getFunctionSignaturesByReturnType,
|
||||
getFieldNamesByType,
|
||||
createCustomCallbackMocks,
|
||||
createCompletionContext,
|
||||
getPolicyFields,
|
||||
PartialSuggestionWithText,
|
||||
TIME_PICKER_SUGGESTION,
|
||||
setup,
|
||||
attachTriggerCommand,
|
||||
SuggestOptions,
|
||||
fields,
|
||||
} from './__tests__/helpers';
|
||||
import { Location } from '../definitions/types';
|
||||
import { METADATA_FIELDS } from '../shared/constants';
|
||||
import { ESQL_STRING_TYPES } from '../shared/esql_types';
|
||||
import { getRecommendedQueries } from './recommended_queries/templates';
|
||||
import {
|
||||
attachTriggerCommand,
|
||||
createCompletionContext,
|
||||
createCustomCallbackMocks,
|
||||
fields,
|
||||
getFieldNamesByType,
|
||||
getFunctionSignaturesByReturnType,
|
||||
getPolicyFields,
|
||||
PartialSuggestionWithText,
|
||||
policies,
|
||||
setup,
|
||||
SuggestOptions,
|
||||
TIME_PICKER_SUGGESTION,
|
||||
} from './__tests__/helpers';
|
||||
import { suggest } from './autocomplete';
|
||||
import { getDateHistogramCompletionItem } from './commands/stats/util';
|
||||
import { Location } from '../definitions/types';
|
||||
import { getSafeInsertText, TIME_SYSTEM_PARAMS, TRIGGER_SUGGESTION_COMMAND } from './factories';
|
||||
import { getRecommendedQueries } from './recommended_queries/templates';
|
||||
|
||||
const commandDefinitions = unmodifiedCommandDefinitions.filter(({ hidden }) => !hidden);
|
||||
|
||||
|
@ -213,13 +212,7 @@ describe('autocomplete', () => {
|
|||
const statement = 'from a | drop keywordField | eval var0 = abs(doubleField) ';
|
||||
const triggerOffset = statement.lastIndexOf(' ');
|
||||
const context = createCompletionContext(statement[triggerOffset]);
|
||||
await suggest(
|
||||
statement,
|
||||
triggerOffset + 1,
|
||||
context,
|
||||
async (text) => (text ? getAstAndSyntaxErrors(text) : { ast: [], errors: [] }),
|
||||
callbackMocks
|
||||
);
|
||||
await suggest(statement, triggerOffset + 1, context, callbackMocks);
|
||||
expect(callbackMocks.getColumnsFor).toHaveBeenCalledWith({
|
||||
query: 'from a | drop keywordField',
|
||||
});
|
||||
|
@ -229,13 +222,7 @@ describe('autocomplete', () => {
|
|||
const statement = 'from a | drop | eval var0 = abs(doubleField) ';
|
||||
const triggerOffset = statement.lastIndexOf('p') + 1; // drop <here>
|
||||
const context = createCompletionContext(statement[triggerOffset]);
|
||||
await suggest(
|
||||
statement,
|
||||
triggerOffset + 1,
|
||||
context,
|
||||
async (text) => (text ? getAstAndSyntaxErrors(text) : { ast: [], errors: [] }),
|
||||
callbackMocks
|
||||
);
|
||||
await suggest(statement, triggerOffset + 1, context, callbackMocks);
|
||||
expect(callbackMocks.getColumnsFor).toHaveBeenCalledWith({ query: 'from a' });
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
import { uniq } from 'lodash';
|
||||
import {
|
||||
type AstProviderFn,
|
||||
parse,
|
||||
type ESQLAstItem,
|
||||
type ESQLCommand,
|
||||
type ESQLCommandOption,
|
||||
|
@ -87,13 +87,12 @@ export async function suggest(
|
|||
fullText: string,
|
||||
offset: number,
|
||||
context: EditorContext,
|
||||
astProvider: AstProviderFn,
|
||||
resourceRetriever?: ESQLCallbacks
|
||||
): Promise<SuggestionRawDefinition[]> {
|
||||
// Partition out to inner ast / ast context for the latest command
|
||||
const innerText = fullText.substring(0, offset);
|
||||
const correctedQuery = correctQuerySyntax(innerText, context);
|
||||
const { ast } = await astProvider(correctedQuery);
|
||||
const { ast } = parse(correctedQuery, { withFormatting: true });
|
||||
const astContext = getAstContext(innerText, ast, offset);
|
||||
|
||||
if (astContext.type === 'comment') {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EditorError, ESQLMessage, getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { EditorError, ESQLMessage } from '@kbn/esql-ast';
|
||||
import { ESQLCallbacks } from '../../shared/types';
|
||||
import { getCallbackMocks } from '../../__tests__/helpers';
|
||||
import { ValidationOptions } from '../types';
|
||||
|
@ -29,7 +29,7 @@ export const setup = async () => {
|
|||
opts: ValidationOptions = {},
|
||||
cb: ESQLCallbacks = callbacks
|
||||
) => {
|
||||
return await validateQuery(query, getAstAndSyntaxErrors, opts, cb);
|
||||
return await validateQuery(query, opts, cb);
|
||||
};
|
||||
|
||||
const assertErrors = (errors: unknown[], expectedErrors: string[], query?: string) => {
|
||||
|
@ -66,7 +66,7 @@ export const setup = async () => {
|
|||
opts: ValidationOptions = {},
|
||||
cb: ESQLCallbacks = callbacks
|
||||
) => {
|
||||
const { errors, warnings } = await validateQuery(query, getAstAndSyntaxErrors, opts, cb);
|
||||
const { errors, warnings } = await validateQuery(query, opts, cb);
|
||||
assertErrors(errors, expectedErrors, query);
|
||||
if (expectedWarnings) {
|
||||
assertErrors(warnings, expectedWarnings, query);
|
||||
|
|
|
@ -7,34 +7,33 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { join } from 'path';
|
||||
import { writeFile, readFile } from 'fs/promises';
|
||||
import { ignoreErrorsMap, validateQuery } from './validation';
|
||||
import { scalarFunctionDefinitions } from '../definitions/generated/scalar_functions';
|
||||
import { getFunctionSignatures } from '../definitions/helpers';
|
||||
import {
|
||||
FieldType,
|
||||
FunctionDefinition,
|
||||
SupportedDataType,
|
||||
dataTypes,
|
||||
fieldTypes as _fieldTypes,
|
||||
} from '../definitions/types';
|
||||
import { timeUnits, timeUnitsToSuggest } from '../definitions/literals';
|
||||
import { aggFunctionDefinitions } from '../definitions/generated/aggregation_functions';
|
||||
import capitalize from 'lodash/capitalize';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import { camelCase } from 'lodash';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { nonNullable } from '../shared/helpers';
|
||||
import capitalize from 'lodash/capitalize';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
fields,
|
||||
enrichFields,
|
||||
fields,
|
||||
getCallbackMocks,
|
||||
indexes,
|
||||
policies,
|
||||
unsupported_field,
|
||||
} from '../__tests__/helpers';
|
||||
import { validationFromCommandTestSuite as runFromTestSuite } from './__tests__/test_suites/validation.command.from';
|
||||
import { aggFunctionDefinitions } from '../definitions/generated/aggregation_functions';
|
||||
import { scalarFunctionDefinitions } from '../definitions/generated/scalar_functions';
|
||||
import { getFunctionSignatures } from '../definitions/helpers';
|
||||
import { timeUnits, timeUnitsToSuggest } from '../definitions/literals';
|
||||
import {
|
||||
FieldType,
|
||||
FunctionDefinition,
|
||||
SupportedDataType,
|
||||
fieldTypes as _fieldTypes,
|
||||
dataTypes,
|
||||
} from '../definitions/types';
|
||||
import { nonNullable } from '../shared/helpers';
|
||||
import { Setup, setup } from './__tests__/helpers';
|
||||
import { validationFromCommandTestSuite as runFromTestSuite } from './__tests__/test_suites/validation.command.from';
|
||||
import { ignoreErrorsMap, validateQuery } from './validation';
|
||||
|
||||
const fieldTypes = _fieldTypes.filter((type) => type !== 'unsupported');
|
||||
|
||||
|
@ -226,7 +225,7 @@ describe('validation logic', () => {
|
|||
const callbackMocks = getCallbackMocks();
|
||||
const { warnings, errors } = await validateQuery(
|
||||
statement,
|
||||
getAstAndSyntaxErrors,
|
||||
|
||||
undefined,
|
||||
callbackMocks
|
||||
);
|
||||
|
@ -1547,20 +1546,20 @@ describe('validation logic', () => {
|
|||
describe('callbacks', () => {
|
||||
it(`should not fetch source and fields list when a row command is set`, async () => {
|
||||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(`row a = 1 | eval a`, getAstAndSyntaxErrors, undefined, callbackMocks);
|
||||
await validateQuery(`row a = 1 | eval a`, undefined, callbackMocks);
|
||||
expect(callbackMocks.getColumnsFor).not.toHaveBeenCalled();
|
||||
expect(callbackMocks.getSources).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`should not fetch policies if no enrich command is found`, async () => {
|
||||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(`row a = 1 | eval a`, getAstAndSyntaxErrors, undefined, callbackMocks);
|
||||
await validateQuery(`row a = 1 | eval a`, undefined, callbackMocks);
|
||||
expect(callbackMocks.getPolicies).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it(`should not fetch source and fields for empty command`, async () => {
|
||||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(` `, getAstAndSyntaxErrors, undefined, callbackMocks);
|
||||
await validateQuery(` `, undefined, callbackMocks);
|
||||
expect(callbackMocks.getColumnsFor).not.toHaveBeenCalled();
|
||||
expect(callbackMocks.getSources).not.toHaveBeenCalled();
|
||||
});
|
||||
|
@ -1569,7 +1568,7 @@ describe('validation logic', () => {
|
|||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(
|
||||
`row a = 1 | eval b = a | enrich policy`,
|
||||
getAstAndSyntaxErrors,
|
||||
|
||||
undefined,
|
||||
callbackMocks
|
||||
);
|
||||
|
@ -1583,12 +1582,7 @@ describe('validation logic', () => {
|
|||
|
||||
it('should call fields callbacks also for show command', async () => {
|
||||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(
|
||||
`show info | keep name`,
|
||||
getAstAndSyntaxErrors,
|
||||
undefined,
|
||||
callbackMocks
|
||||
);
|
||||
await validateQuery(`show info | keep name`, undefined, callbackMocks);
|
||||
expect(callbackMocks.getSources).not.toHaveBeenCalled();
|
||||
expect(callbackMocks.getPolicies).not.toHaveBeenCalled();
|
||||
expect(callbackMocks.getColumnsFor).toHaveBeenCalledTimes(1);
|
||||
|
@ -1601,7 +1595,7 @@ describe('validation logic', () => {
|
|||
const callbackMocks = getCallbackMocks();
|
||||
await validateQuery(
|
||||
`from a_index | eval b = a | enrich policy`,
|
||||
getAstAndSyntaxErrors,
|
||||
|
||||
undefined,
|
||||
callbackMocks
|
||||
);
|
||||
|
@ -1617,7 +1611,7 @@ describe('validation logic', () => {
|
|||
try {
|
||||
await validateQuery(
|
||||
`from a_index | eval b = a | enrich policy | dissect textField "%{firstWord}"`,
|
||||
getAstAndSyntaxErrors,
|
||||
|
||||
undefined,
|
||||
{
|
||||
getColumnsFor: undefined,
|
||||
|
@ -1633,8 +1627,7 @@ describe('validation logic', () => {
|
|||
it(`should not crash if no callbacks are passed`, async () => {
|
||||
try {
|
||||
await validateQuery(
|
||||
`from a_index | eval b = a | enrich policy | dissect textField "%{firstWord}"`,
|
||||
getAstAndSyntaxErrors
|
||||
`from a_index | eval b = a | enrich policy | dissect textField "%{firstWord}"`
|
||||
);
|
||||
} catch {
|
||||
fail('Should not throw');
|
||||
|
@ -1773,7 +1766,7 @@ describe('validation logic', () => {
|
|||
.map(({ query }) =>
|
||||
validateQuery(
|
||||
query,
|
||||
getAstAndSyntaxErrors,
|
||||
|
||||
{ ignoreOnMissingCallbacks: true },
|
||||
getCallbackMocks()
|
||||
)
|
||||
|
@ -1801,7 +1794,6 @@ describe('validation logic', () => {
|
|||
filteredTestCases.map(({ query }) =>
|
||||
validateQuery(
|
||||
query,
|
||||
getAstAndSyntaxErrors,
|
||||
{ ignoreOnMissingCallbacks: true },
|
||||
getPartialCallbackMocks(excludedCallback)
|
||||
)
|
||||
|
@ -1826,7 +1818,7 @@ describe('validation logic', () => {
|
|||
excludeErrorsByContent(excludedCallbacks).every((regexp) => regexp?.test(message))
|
||||
)
|
||||
)) {
|
||||
const { errors } = await validateQuery(testCase.query, getAstAndSyntaxErrors, {
|
||||
const { errors } = await validateQuery(testCase.query, {
|
||||
ignoreOnMissingCallbacks: true,
|
||||
});
|
||||
expect(
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
AstProviderFn,
|
||||
ESQLAst,
|
||||
ESQLAstTimeseriesCommand,
|
||||
ESQLColumn,
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
ESQLMessage,
|
||||
ESQLSource,
|
||||
isIdentifier,
|
||||
parse,
|
||||
walk,
|
||||
} from '@kbn/esql-ast';
|
||||
import type { ESQLAstJoinCommand, ESQLIdentifier } from '@kbn/esql-ast/src/types';
|
||||
|
@ -65,11 +65,10 @@ import { validate as validateTimeseriesCommand } from './commands/metrics';
|
|||
*/
|
||||
export async function validateQuery(
|
||||
queryString: string,
|
||||
astProvider: AstProviderFn,
|
||||
options: ValidationOptions = {},
|
||||
callbacks?: ESQLCallbacks
|
||||
): Promise<ValidationResult> {
|
||||
const result = await validateAst(queryString, astProvider, callbacks);
|
||||
const result = await validateAst(queryString, callbacks);
|
||||
// early return if we do not want to ignore errors
|
||||
if (!options.ignoreOnMissingCallbacks) {
|
||||
return result;
|
||||
|
@ -128,12 +127,11 @@ export const ignoreErrorsMap: Record<keyof ESQLCallbacks, ErrorTypes[]> = {
|
|||
*/
|
||||
async function validateAst(
|
||||
queryString: string,
|
||||
astProvider: AstProviderFn,
|
||||
callbacks?: ESQLCallbacks
|
||||
): Promise<ValidationResult> {
|
||||
const messages: ESQLMessage[] = [];
|
||||
|
||||
const parsingResult = await astProvider(queryString);
|
||||
const parsingResult = parse(queryString);
|
||||
|
||||
const { ast } = parsingResult;
|
||||
|
||||
|
|
|
@ -7,20 +7,19 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { ESQLCallbacks } from '@kbn/esql-validation-autocomplete';
|
||||
import { validateQuery, type ESQLCallbacks, suggest } from '@kbn/esql-validation-autocomplete';
|
||||
import { monaco } from '../../monaco_imports';
|
||||
|
||||
import { ESQL_LANG_ID } from './lib/constants';
|
||||
|
||||
import type { CustomLangModuleType } from '../../types';
|
||||
import type { ESQLWorker } from './worker/esql_worker';
|
||||
|
||||
import { WorkerProxyService } from '../../common/worker_proxy';
|
||||
import { buildESQLTheme } from './lib/esql_theme';
|
||||
import { ESQLAstAdapter } from './lib/esql_ast_provider';
|
||||
import { wrapAsMonacoSuggestions } from './lib/converters/suggestions';
|
||||
import { wrapAsMonacoMessages } from './lib/converters/positions';
|
||||
import { getHoverItem } from './lib/hover/hover';
|
||||
import { monacoPositionToOffset } from './lib/shared/utils';
|
||||
|
||||
const workerProxyService = new WorkerProxyService<ESQLWorker>();
|
||||
const removeKeywordSuffix = (name: string) => {
|
||||
return name.endsWith('.keyword') ? name.slice(0, -8) : name;
|
||||
};
|
||||
|
@ -30,8 +29,6 @@ export const ESQLLang: CustomLangModuleType<ESQLCallbacks> = {
|
|||
async onLanguage() {
|
||||
const { ESQLTokensProvider } = await import('./lib');
|
||||
|
||||
workerProxyService.setup(ESQL_LANG_ID);
|
||||
|
||||
monaco.languages.setTokensProvider(ESQL_LANG_ID, new ESQLTokensProvider());
|
||||
},
|
||||
languageThemeResolver: buildESQLTheme,
|
||||
|
@ -55,28 +52,11 @@ export const ESQLLang: CustomLangModuleType<ESQLCallbacks> = {
|
|||
],
|
||||
},
|
||||
validate: async (model: monaco.editor.ITextModel, code: string, callbacks?: ESQLCallbacks) => {
|
||||
const astAdapter = new ESQLAstAdapter(
|
||||
(...uris) => workerProxyService.getWorker(uris),
|
||||
callbacks
|
||||
);
|
||||
return await astAdapter.validate(model, code);
|
||||
},
|
||||
getSignatureProvider: (callbacks?: ESQLCallbacks): monaco.languages.SignatureHelpProvider => {
|
||||
return {
|
||||
signatureHelpTriggerCharacters: [' ', '('],
|
||||
async provideSignatureHelp(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
_token: monaco.CancellationToken,
|
||||
context: monaco.languages.SignatureHelpContext
|
||||
) {
|
||||
const astAdapter = new ESQLAstAdapter(
|
||||
(...uris) => workerProxyService.getWorker(uris),
|
||||
callbacks
|
||||
);
|
||||
return astAdapter.suggestSignature(model, position, context);
|
||||
},
|
||||
};
|
||||
const text = code ?? model.getValue();
|
||||
const { errors, warnings } = await validateQuery(text, undefined, callbacks);
|
||||
const monacoErrors = wrapAsMonacoMessages(text, errors);
|
||||
const monacoWarnings = wrapAsMonacoMessages(text, warnings);
|
||||
return { errors: monacoErrors, warnings: monacoWarnings };
|
||||
},
|
||||
getHoverProvider: (callbacks?: ESQLCallbacks): monaco.languages.HoverProvider => {
|
||||
return {
|
||||
|
@ -85,11 +65,7 @@ export const ESQLLang: CustomLangModuleType<ESQLCallbacks> = {
|
|||
position: monaco.Position,
|
||||
token: monaco.CancellationToken
|
||||
) {
|
||||
const astAdapter = new ESQLAstAdapter(
|
||||
(...uris) => workerProxyService.getWorker(uris),
|
||||
callbacks
|
||||
);
|
||||
return astAdapter.getHover(model, position, token);
|
||||
return getHoverItem(model, position, token, callbacks);
|
||||
},
|
||||
};
|
||||
},
|
||||
|
@ -101,14 +77,12 @@ export const ESQLLang: CustomLangModuleType<ESQLCallbacks> = {
|
|||
position: monaco.Position,
|
||||
context: monaco.languages.CompletionContext
|
||||
): Promise<monaco.languages.CompletionList> {
|
||||
const astAdapter = new ESQLAstAdapter(
|
||||
(...uris) => workerProxyService.getWorker(uris),
|
||||
callbacks
|
||||
);
|
||||
const suggestions = await astAdapter.autocomplete(model, position, context);
|
||||
const fullText = model.getValue();
|
||||
const offset = monacoPositionToOffset(fullText, position);
|
||||
const suggestions = await suggest(fullText, offset, context, callbacks);
|
||||
return {
|
||||
// @ts-expect-error because of range typing: https://github.com/microsoft/monaco-editor/issues/4638
|
||||
suggestions: wrapAsMonacoSuggestions(suggestions),
|
||||
suggestions: wrapAsMonacoSuggestions(suggestions, fullText),
|
||||
};
|
||||
},
|
||||
async resolveCompletionItem(item, token): Promise<monaco.languages.CompletionItem> {
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { SuggestionRawDefinition } from '@kbn/esql-validation-autocomplete';
|
||||
import { monaco } from '../../../../monaco_imports';
|
||||
import {
|
||||
MonacoAutocompleteCommandDefinition,
|
||||
SuggestionRawDefinitionWithMonacoRange,
|
||||
} from '../types';
|
||||
import { MonacoAutocompleteCommandDefinition } from '../types';
|
||||
import { offsetRangeToMonacoRange } from '../shared/utils';
|
||||
|
||||
export function wrapAsMonacoSuggestions(
|
||||
suggestions: SuggestionRawDefinitionWithMonacoRange[]
|
||||
suggestions: SuggestionRawDefinition[],
|
||||
fullText: string
|
||||
): MonacoAutocompleteCommandDefinition[] {
|
||||
return suggestions.map<MonacoAutocompleteCommandDefinition>(
|
||||
({
|
||||
|
@ -27,7 +27,7 @@ export function wrapAsMonacoSuggestions(
|
|||
sortText,
|
||||
filterText,
|
||||
command,
|
||||
range,
|
||||
rangeToReplace,
|
||||
}) => {
|
||||
const monacoSuggestion: MonacoAutocompleteCommandDefinition = {
|
||||
label,
|
||||
|
@ -44,7 +44,7 @@ export function wrapAsMonacoSuggestions(
|
|||
insertTextRules: asSnippet
|
||||
? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
|
||||
: undefined,
|
||||
range,
|
||||
range: rangeToReplace ? offsetRangeToMonacoRange(fullText, rangeToReplace) : undefined,
|
||||
};
|
||||
return monacoSuggestion;
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* 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 { type ESQLCallbacks, suggest, validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import { monaco } from '../../../monaco_imports';
|
||||
import type { ESQLWorker } from '../worker/esql_worker';
|
||||
import { wrapAsMonacoMessages } from './converters/positions';
|
||||
import { getHoverItem } from './hover/hover';
|
||||
import { monacoPositionToOffset, offsetRangeToMonacoRange } from './shared/utils';
|
||||
import { getSignatureHelp } from './signature';
|
||||
import { SuggestionRawDefinitionWithMonacoRange } from './types';
|
||||
|
||||
export class ESQLAstAdapter {
|
||||
constructor(
|
||||
private worker: (...uris: monaco.Uri[]) => Promise<ESQLWorker>,
|
||||
private callbacks?: ESQLCallbacks
|
||||
) {}
|
||||
|
||||
private async getAstWorker(model: monaco.editor.ITextModel) {
|
||||
const worker = await this.worker(model.uri);
|
||||
return worker.getAst;
|
||||
}
|
||||
|
||||
async getAst(model: monaco.editor.ITextModel, code?: string) {
|
||||
const getAstFn = await this.getAstWorker(model);
|
||||
return getAstFn(code ?? model.getValue());
|
||||
}
|
||||
|
||||
async validate(model: monaco.editor.ITextModel, code: string) {
|
||||
const getAstFn = await this.getAstWorker(model);
|
||||
const text = code ?? model.getValue();
|
||||
const { errors, warnings } = await validateQuery(text, getAstFn, undefined, this.callbacks);
|
||||
const monacoErrors = wrapAsMonacoMessages(text, errors);
|
||||
const monacoWarnings = wrapAsMonacoMessages(text, warnings);
|
||||
return { errors: monacoErrors, warnings: monacoWarnings };
|
||||
}
|
||||
|
||||
async suggestSignature(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
context: monaco.languages.SignatureHelpContext
|
||||
) {
|
||||
const getAstFn = await this.getAstWorker(model);
|
||||
return getSignatureHelp(model, position, context, getAstFn);
|
||||
}
|
||||
|
||||
async getHover(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
token: monaco.CancellationToken
|
||||
) {
|
||||
const getAstFn = await this.getAstWorker(model);
|
||||
return getHoverItem(model, position, token, getAstFn, this.callbacks);
|
||||
}
|
||||
|
||||
async autocomplete(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
context: monaco.languages.CompletionContext
|
||||
): Promise<SuggestionRawDefinitionWithMonacoRange[]> {
|
||||
const getAstFn = await this.getAstWorker(model);
|
||||
const fullText = model.getValue();
|
||||
const offset = monacoPositionToOffset(fullText, position);
|
||||
const suggestions = await suggest(fullText, offset, context, getAstFn, this.callbacks);
|
||||
for (const s of suggestions) {
|
||||
(s as SuggestionRawDefinitionWithMonacoRange).range = s.rangeToReplace
|
||||
? offsetRangeToMonacoRange(fullText, s.rangeToReplace)
|
||||
: undefined;
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import { monaco } from '../../../../monaco_imports';
|
||||
import { getHoverItem } from './hover';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import {
|
||||
ESQLRealField,
|
||||
getFunctionDefinition,
|
||||
|
@ -129,13 +128,7 @@ describe('hover', () => {
|
|||
})=> ["${expected.join('","')}"]`,
|
||||
async () => {
|
||||
const callbackMocks = createCustomCallbackMocks(...customCallbacksArgs);
|
||||
const { contents } = await getHoverItem(
|
||||
model,
|
||||
position,
|
||||
token,
|
||||
async (text) => (text ? getAstAndSyntaxErrors(text) : { ast: [], errors: [] }),
|
||||
callbackMocks
|
||||
);
|
||||
const { contents } = await getHoverItem(model, position, token, callbackMocks);
|
||||
expect(contents.map(({ value }) => value)).toEqual(expected);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { AstProviderFn, ESQLAstItem } from '@kbn/esql-ast';
|
||||
import { parse, type ESQLAstItem } from '@kbn/esql-ast';
|
||||
import {
|
||||
getAstContext,
|
||||
getFunctionDefinition,
|
||||
|
@ -45,8 +45,6 @@ const ACCEPTABLE_TYPES_HOVER = i18n.translate('monaco.esql.hover.acceptableTypes
|
|||
async function getHoverItemForFunction(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
token: monaco.CancellationToken,
|
||||
astProvider: AstProviderFn,
|
||||
resourceRetriever?: ESQLCallbacks
|
||||
) {
|
||||
const context: EditorContext = {
|
||||
|
@ -59,7 +57,7 @@ async function getHoverItemForFunction(
|
|||
const innerText = fullText.substring(0, offset);
|
||||
|
||||
const correctedQuery = correctQuerySyntax(innerText, context);
|
||||
const { ast } = await astProvider(correctedQuery);
|
||||
const { ast } = parse(correctedQuery);
|
||||
const astContext = getAstContext(innerText, ast, offset);
|
||||
|
||||
const { node } = astContext;
|
||||
|
@ -140,14 +138,13 @@ async function getHoverItemForFunction(
|
|||
export async function getHoverItem(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
token: monaco.CancellationToken,
|
||||
astProvider: AstProviderFn,
|
||||
_token: monaco.CancellationToken,
|
||||
resourceRetriever?: ESQLCallbacks
|
||||
) {
|
||||
const fullText = model.getValue();
|
||||
const offset = monacoPositionToOffset(fullText, position);
|
||||
|
||||
const { ast } = await astProvider(fullText);
|
||||
const { ast } = parse(fullText);
|
||||
const astContext = getAstContext(fullText, ast, offset);
|
||||
const { getPolicyMetadata } = getPolicyHelper(resourceRetriever);
|
||||
|
||||
|
@ -162,13 +159,7 @@ export async function getHoverItem(
|
|||
hoverContent.contents.push(...variablesContent);
|
||||
}
|
||||
|
||||
const hoverItemsForFunction = await getHoverItemForFunction(
|
||||
model,
|
||||
position,
|
||||
token,
|
||||
astProvider,
|
||||
resourceRetriever
|
||||
);
|
||||
const hoverItemsForFunction = await getHoverItemForFunction(model, position, resourceRetriever);
|
||||
if (hoverItemsForFunction) {
|
||||
hoverContent.contents.push(...hoverItemsForFunction.contents);
|
||||
hoverContent.range = hoverItemsForFunction.range;
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* 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 type { AstProviderFn } from '@kbn/esql-ast';
|
||||
import type { monaco } from '../../../../monaco_imports';
|
||||
|
||||
export function getSignatureHelp(
|
||||
model: monaco.editor.ITextModel,
|
||||
position: monaco.Position,
|
||||
context: monaco.languages.SignatureHelpContext,
|
||||
astProvider: AstProviderFn
|
||||
): monaco.languages.SignatureHelpResult {
|
||||
return {
|
||||
value: { signatures: [], activeParameter: 0, activeSignature: 0 },
|
||||
dispose: () => {},
|
||||
};
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { SuggestionRawDefinition } from '@kbn/esql-validation-autocomplete';
|
||||
import { monaco } from '../../../monaco_imports';
|
||||
|
||||
export type MonacoAutocompleteCommandDefinition = Pick<
|
||||
|
@ -24,10 +23,3 @@ export type MonacoAutocompleteCommandDefinition = Pick<
|
|||
> & { range?: monaco.IRange };
|
||||
|
||||
export type MonacoCodeAction = monaco.languages.CodeAction;
|
||||
|
||||
export type SuggestionRawDefinitionWithMonacoRange = Omit<
|
||||
SuggestionRawDefinition,
|
||||
'rangeToReplace'
|
||||
> & {
|
||||
range?: monaco.IRange;
|
||||
};
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* 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".
|
||||
*/
|
||||
|
||||
// This module is intended to be run inside of a webworker
|
||||
/* eslint-disable @kbn/eslint/module_migration */
|
||||
|
||||
import '@babel/runtime/regenerator';
|
||||
|
||||
// @ts-ignore
|
||||
import * as worker from 'monaco-editor/esm/vs/editor/editor.worker';
|
||||
|
||||
import { monaco } from '../../../monaco_imports';
|
||||
import { ESQLWorker } from './esql_worker';
|
||||
|
||||
self.onmessage = () => {
|
||||
worker.initialize((ctx: monaco.worker.IWorkerContext, createData: any) => {
|
||||
return new ESQLWorker(ctx);
|
||||
});
|
||||
};
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* 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 { parse, parseErrors, type EditorError } from '@kbn/esql-ast';
|
||||
import type { monaco } from '../../../monaco_imports';
|
||||
import type { BaseWorkerDefinition } from '../../../types';
|
||||
|
||||
/**
|
||||
* While this function looks similar to the wrapAsMonacoMessages one, it prevents from
|
||||
* loading the whole monaco stuff within the WebWorker.
|
||||
* Given that we're dealing only with EditorError objects here, and not other types, it is
|
||||
* possible to use this simpler inline function to work.
|
||||
*/
|
||||
function inlineToMonacoErrors({ severity, ...error }: EditorError) {
|
||||
return {
|
||||
...error,
|
||||
severity: severity === 'error' ? 8 : 4, // monaco.MarkerSeverity.Error : monaco.MarkerSeverity.Warning
|
||||
};
|
||||
}
|
||||
|
||||
export class ESQLWorker implements BaseWorkerDefinition {
|
||||
private readonly _ctx: monaco.worker.IWorkerContext;
|
||||
|
||||
constructor(ctx: monaco.worker.IWorkerContext) {
|
||||
this._ctx = ctx;
|
||||
}
|
||||
|
||||
public async getSyntaxErrors(modelUri: string) {
|
||||
const model = this._ctx.getMirrorModels().find((m) => m.uri.toString() === modelUri);
|
||||
const text = model?.getValue();
|
||||
|
||||
if (!text) return [];
|
||||
|
||||
const errors = parseErrors(text);
|
||||
|
||||
return errors.map(inlineToMonacoErrors);
|
||||
}
|
||||
|
||||
getAst(text: string | undefined) {
|
||||
const rawAst = parse(text, { withFormatting: true });
|
||||
return {
|
||||
ast: rawAst.root.commands,
|
||||
errors: rawAst.errors.map(inlineToMonacoErrors),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -7,13 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import {
|
||||
XJSON_LANG_ID,
|
||||
PAINLESS_LANG_ID,
|
||||
ESQL_LANG_ID,
|
||||
CONSOLE_LANG_ID,
|
||||
YAML_LANG_ID,
|
||||
} from './languages';
|
||||
import { XJSON_LANG_ID, PAINLESS_LANG_ID, CONSOLE_LANG_ID, YAML_LANG_ID } from './languages';
|
||||
import { monaco } from './monaco_imports';
|
||||
|
||||
export const DEFAULT_WORKER_ID = 'default' as const;
|
||||
|
@ -22,7 +16,6 @@ const langSpecificWorkerIds = [
|
|||
monaco.languages.json.jsonDefaults.languageId,
|
||||
XJSON_LANG_ID,
|
||||
PAINLESS_LANG_ID,
|
||||
ESQL_LANG_ID,
|
||||
YAML_LANG_ID,
|
||||
CONSOLE_LANG_ID,
|
||||
] as const;
|
||||
|
|
|
@ -104,4 +104,4 @@ const workerConfig = (languages) => ({
|
|||
},
|
||||
});
|
||||
|
||||
module.exports = workerConfig(['default', 'json', 'xjson', 'painless', 'esql', 'yaml', 'console']);
|
||||
module.exports = workerConfig(['default', 'json', 'xjson', 'painless', 'yaml', 'console']);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { run } from '@kbn/dev-cli-runner';
|
||||
import { ESQLMessage, EditorError, getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import { ESQLMessage, EditorError } from '@kbn/esql-ast';
|
||||
import { validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import Fs from 'fs/promises';
|
||||
import Path from 'path';
|
||||
|
@ -156,7 +156,7 @@ const findEsqlSyntaxError = async (doc: FileToWrite): Promise<SyntaxError[]> =>
|
|||
return Array.from(doc.content.matchAll(INLINE_ESQL_QUERY_REGEX)).reduce(
|
||||
async (listP, [match, query]) => {
|
||||
const list = await listP;
|
||||
const { errors, warnings } = await validateQuery(query, getAstAndSyntaxErrors, {
|
||||
const { errors, warnings } = await validateQuery(query, {
|
||||
// setting this to true, we don't want to validate the index / fields existence
|
||||
ignoreOnMissingCallbacks: true,
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { ESQLSearchResponse, ESQLRow } from '@kbn/es-types';
|
||||
import { esFieldTypeToKibanaFieldType } from '@kbn/field-types';
|
||||
|
@ -25,7 +24,7 @@ export async function runAndValidateEsqlQuery({
|
|||
error?: Error;
|
||||
errorMessages?: string[];
|
||||
}> {
|
||||
const { errors } = await validateQuery(query, getAstAndSyntaxErrors, {
|
||||
const { errors } = await validateQuery(query, {
|
||||
// setting this to true, we don't want to validate the index / fields existence
|
||||
ignoreOnMissingCallbacks: true,
|
||||
});
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { validateQuery } from '@kbn/esql-validation-autocomplete';
|
||||
import { getAstAndSyntaxErrors } from '@kbn/esql-ast';
|
||||
import type { ElasticsearchClient } from '@kbn/core/server';
|
||||
import { ESQLSearchResponse, ESQLRow } from '@kbn/es-types';
|
||||
import { esFieldTypeToKibanaFieldType } from '@kbn/field-types';
|
||||
|
@ -29,7 +28,7 @@ export async function runAndValidateEsqlQuery({
|
|||
}> {
|
||||
const queryWithoutLineBreaks = query.replaceAll(/\n/g, '');
|
||||
|
||||
const { errors } = await validateQuery(queryWithoutLineBreaks, getAstAndSyntaxErrors, {
|
||||
const { errors } = await validateQuery(queryWithoutLineBreaks, {
|
||||
// setting this to true, we don't want to validate the index / fields existence
|
||||
ignoreOnMissingCallbacks: true,
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue