Skip to content

Commit 1607426

Browse files
committed
last cleanup
1 parent 04385b0 commit 1607426

7 files changed

Lines changed: 63 additions & 27 deletions

File tree

src/platform/packages/shared/kbn-esql-language/src/commands/definitions/utils/ast.test.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
import { PromQLParser } from '@elastic/esql';
10+
import { Parser, PromQLParser, Walker } from '@elastic/esql';
11+
import type { ESQLAstItem, ESQLAstQueryExpression } from '@elastic/esql/types';
1112
import { EDITOR_MARKER } from '../constants';
1213
import {
1314
addAutocompleteMarker,
1415
correctPromqlQuerySyntax,
1516
correctQuerySyntax,
1617
getBracketsToClose,
18+
isMarkerNode,
1719
removeAutocompleteMarkers,
1820
} from './ast';
1921

@@ -134,3 +136,37 @@ describe('correctPromqlQuerySyntax', () => {
134136
expect(JSON.stringify(normalizedRoot)).not.toContain(EDITOR_MARKER);
135137
});
136138
});
139+
140+
describe('removeAutocompleteMarkers', () => {
141+
const parseAutocomplete = (innerText: string): ESQLAstQueryExpression => {
142+
const corrected = correctQuerySyntax(addAutocompleteMarker(innerText));
143+
return Parser.parse(corrected, { withFormatting: true }).root;
144+
};
145+
146+
const countMarkers = (node: ESQLAstQueryExpression): number => {
147+
let count = 0;
148+
Walker.walk(node, {
149+
visitAny: (current) => {
150+
if (isMarkerNode(current as ESQLAstItem)) {
151+
count++;
152+
}
153+
},
154+
});
155+
return count;
156+
};
157+
158+
it('drops marker-only nodes nested in args arrays', () => {
159+
const root = parseAutocomplete('FROM index | EVAL result = ROUND(doubleField, ');
160+
161+
expect(countMarkers(root)).toBeGreaterThan(0);
162+
expect(countMarkers(removeAutocompleteMarkers(root))).toBe(0);
163+
});
164+
165+
it('strips the marker from the inline cast type, not only from text', () => {
166+
const root = parseAutocomplete('FROM index | EVAL casted = keywordField::');
167+
168+
// Before cleaning, the marker leaks into inlineCast.castType (a plain string property).
169+
expect(JSON.stringify(root)).toContain(EDITOR_MARKER);
170+
expect(JSON.stringify(removeAutocompleteMarkers(root))).not.toContain(EDITOR_MARKER);
171+
});
172+
});

src/platform/packages/shared/kbn-esql-language/src/commands/definitions/utils/ast.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ export function removeAutocompleteMarkers<T>(value: T): T {
6868
.map((item) => removeAutocompleteMarkers(item)) as T;
6969
}
7070

71+
// Strip the marker from any string value, not only `text` (e.g. inline cast `castType`).
72+
if (typeof value === 'string') {
73+
return (value.includes(EDITOR_MARKER) ? value.replace(EDITOR_MARKER, '') : value) as T;
74+
}
75+
7176
if (!value || typeof value !== 'object') {
7277
return value;
7378
}
@@ -82,14 +87,6 @@ export function removeAutocompleteMarkers<T>(value: T): T {
8287
cleanedValue[key as keyof typeof cleanedValue] = removeAutocompleteMarkers(child);
8388
}
8489

85-
if (
86-
'text' in cleanedValue &&
87-
typeof cleanedValue.text === 'string' &&
88-
cleanedValue.text.includes(EDITOR_MARKER)
89-
) {
90-
cleanedValue.text = cleanedValue.text.replace(EDITOR_MARKER, '');
91-
}
92-
9390
return cleanedValue;
9491
}
9592

src/platform/packages/shared/kbn-esql-language/src/language/autocomplete/autocomplete.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ import { getColumnsByTypeRetriever } from '../shared/columns_retrieval_helpers';
3737
import { getUnmappedFieldsStrategy } from '../../commands/definitions/utils/settings';
3838
import { isTimeseriesSourceCommand } from '../../commands/definitions/utils/timeseries_check';
3939
import { attachReplacementRanges, ReplacementRangeStrategyKind } from './utils/prefix_range';
40-
import { correctQuerySyntax } from '../../commands/definitions/utils/ast';
4140

4241
function isSourceCommandSuggestion({ label }: { label: string }) {
4342
const sourceCommands = esqlCommandRegistry
@@ -73,10 +72,9 @@ export async function suggest(
7372
// Use the appropriate AST context for field retrieval
7473
// When in a subquery, use the subquery's AST to get only its fields
7574
const astForFields = astContext.astForContext;
76-
const correctedQuery = correctQuerySyntax(innerText);
7775

7876
const { getColumnsByType, getColumnMap } = getColumnsByTypeRetriever(
79-
getQueryForFields(correctedQuery, astForFields),
77+
getQueryForFields(innerText, astForFields),
8078
innerText,
8179
resourceRetriever
8280
);

src/platform/packages/shared/kbn-esql-language/src/language/shared/get_query_for_fields.test.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { BasicPrettyPrinter } from '@elastic/esql';
1111
import { getQueryForFields } from './get_query_for_fields';
1212
import { EDITOR_MARKER } from '../../commands/definitions/constants';
1313
import { parseAutocompleteQuery } from './parse_for_autocomplete_query';
14-
import { correctQuerySyntax } from '../../commands/definitions/utils/ast';
1514

1615
describe('getQueryForFields', () => {
1716
const assert = (queryBeforeCursor: string, expected: string) => {
@@ -56,13 +55,6 @@ describe('getQueryForFields', () => {
5655
});
5756

5857
it('should not treat a comma inside an incomplete function call as an EVAL separator', () => {
59-
const query = 'FROM index | EVAL result = ROUND(field, ';
60-
const { root } = parseAutocompleteQuery(query, query.length);
61-
62-
const result = getQueryForFields(correctQuerySyntax(query), root);
63-
const printedResult = BasicPrettyPrinter.print(result);
64-
65-
expect(printedResult).toEqual('FROM index');
66-
expect(printedResult).not.toContain(EDITOR_MARKER);
58+
assert('FROM index | EVAL result = ROUND(field, ', 'FROM index');
6759
});
6860
});

src/platform/packages/shared/kbn-esql-language/src/language/shared/get_query_for_fields.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import type { ESQLAstQueryExpression, ESQLAstForkCommand } from '@elastic/esql/types';
11+
import { getBracketsToClose } from '../../commands/definitions/utils/ast';
1112
import { expandEvals } from './expand_evals';
1213

1314
/**
@@ -21,7 +22,7 @@ import { expandEvals } from './expand_evals';
2122
* IMPORTANT: the AST nodes in the new query still reference locations in the original query text
2223
*
2324
* @param queryString The original query string
24-
* @param commands
25+
* @param root
2526
* @returns
2627
*/
2728
export function getQueryForFields(
@@ -49,8 +50,10 @@ export function getQueryForFields(
4950
}
5051

5152
if (lastCommand && lastCommand.name === 'eval') {
52-
const endsWithComma = queryString.trim().endsWith(',');
53-
if (lastCommand.args.length > 1 || endsWithComma) {
53+
const hasTrailingSeparatorComma =
54+
getBracketsToClose(queryString).length === 0 && queryString.trimEnd().endsWith(',');
55+
56+
if (lastCommand.args.length > 1 || hasTrailingSeparatorComma) {
5457
/**
5558
* If we get here, we know that we have a multi-expression EVAL statement.
5659
*
@@ -64,7 +67,7 @@ export function getQueryForFields(
6467
* Simplified: FROM lolz | EVAL foo = 1 | EVAL bar = foo + 1 | EVAL baz = bar + 1
6568
*/
6669
const expanded = expandEvals(commands);
67-
const newCommands = expanded.slice(0, endsWithComma ? undefined : -1);
70+
const newCommands = expanded.slice(0, hasTrailingSeparatorComma ? undefined : -1);
6871
return { ...root, commands: newCommands };
6972
}
7073
}

src/platform/packages/shared/kbn-esql-language/src/language/shared/parse_for_autocomplete_query.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe('getAutocompleteCursorContext', () => {
4747
['timeseries source command after comma', 'TS timeseries_index, '],
4848
['row assignment', 'ROW total = '],
4949
['eval assignment', 'FROM employees | EVAL total = '],
50+
['inline cast type', 'FROM employees | EVAL casted = keywordField::'],
5051
['function argument', 'FROM employees | EVAL total = ROUND(salary, '],
5152
['where binary expression', 'FROM employees | WHERE age > '],
5253
['where list value', 'FROM employees | WHERE age IN (1, '],
@@ -87,4 +88,15 @@ describe('getAutocompleteCursorContext', () => {
8788
assertNoMarker(astContext.containingFunction, query, 'containingFunction');
8889
}
8990
);
91+
92+
it.each([
93+
'FROM employees | EVAL total = ',
94+
'ROW total = ',
95+
'FROM employees | EVAL total = ROUND(salary, ',
96+
'FROM employees | WHERE age IN (1, ',
97+
])('resolves an expression context (not just marker-free) for %s', (query) => {
98+
const { astContext } = getAutocompleteCursorContext(query, query.length);
99+
100+
expect(astContext.type).toBe('expression');
101+
});
90102
});

src/platform/packages/shared/kbn-esql-language/src/language/shared/parse_for_autocomplete_query.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { getCursorContext } from './get_cursor_context';
1919

2020
interface ParsedAutocompleteQuery {
2121
innerText: string;
22-
correctedQuery: string;
2322
root: ESQLAstQueryExpression;
2423
}
2524

@@ -34,7 +33,6 @@ export function parseAutocompleteQuery(fullText: string, offset: number): Parsed
3433

3534
return {
3635
innerText,
37-
correctedQuery,
3836
root: removeAutocompleteMarkers(root),
3937
};
4038
}

0 commit comments

Comments
 (0)