mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Tempo: Fix TraceQL autocompletion with missing } (#77365)
This commit is contained in:
@@ -342,6 +342,27 @@ describe('CompletionProvider', () => {
|
||||
[...scopes, ...intrinsics].map((s) => expect.objectContaining({ label: s }))
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['{span.ht', 8],
|
||||
['{span.http', 10],
|
||||
['{span.http.', 11],
|
||||
['{span.http.status', 17],
|
||||
])(
|
||||
'suggests attributes when containing trigger characters and missing `}`- %s, %i',
|
||||
async (input: string, offset: number) => {
|
||||
const { provider, model } = setup(input, offset, undefined, [
|
||||
{
|
||||
name: 'span',
|
||||
tags: ['http.status_code'],
|
||||
},
|
||||
]);
|
||||
const result = await provider.provideCompletionItems(model, emptyPosition);
|
||||
expect((result! as monacoTypes.languages.CompletionList).suggestions).toEqual([
|
||||
expect.objectContaining({ label: 'http.status_code', insertText: 'http.status_code' }),
|
||||
]);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function setup(value: string, offset: number, tagsV1?: string[], tagsV2?: Scope[]) {
|
||||
|
||||
@@ -202,9 +202,7 @@ const RESOLVERS: Resolver[] = [
|
||||
},
|
||||
{
|
||||
path: [ERROR_NODE_ID, SpansetFilter],
|
||||
fun: () => ({
|
||||
type: 'SPANSET_EXPRESSION_OPERATORS_WITH_MISSING_CLOSED_BRACE',
|
||||
}),
|
||||
fun: resolveSpansetWithNoClosedBrace,
|
||||
},
|
||||
{
|
||||
path: [ERROR_NODE_ID, Aggregate],
|
||||
@@ -253,16 +251,35 @@ const resolveAttributeCompletion = (node: SyntaxNode, text: string, pos: number)
|
||||
// The user is completing an expression. We can take advantage of the fact that the Monaco editor is smart
|
||||
// enough to automatically detect that there are some characters before the cursor and to take them into
|
||||
// account when providing suggestions.
|
||||
const endOfPathNode = walk(node, [['firstChild', [FieldExpression]]]);
|
||||
if (endOfPathNode && text[pos - 1] !== ' ') {
|
||||
const attributeFieldParent = walk(endOfPathNode, [['firstChild', [AttributeField]]]);
|
||||
const getAttributeFieldUpToDot = (node: SyntaxNode) => {
|
||||
const attributeFieldParent = walk(node, [['firstChild', [AttributeField]]]);
|
||||
const attributeFieldParentText = attributeFieldParent ? getNodeText(attributeFieldParent, text) : '';
|
||||
const indexOfDot = attributeFieldParentText.indexOf('.');
|
||||
const attributeFieldUpToDot = attributeFieldParentText.slice(0, indexOfDot);
|
||||
return attributeFieldParentText.slice(0, indexOfDot);
|
||||
};
|
||||
|
||||
// If there is a space, for sure the attribute is completed and no suggestions to complete it should be provided
|
||||
if (text[pos - 1] === ' ') {
|
||||
return;
|
||||
}
|
||||
|
||||
const endOfPathNode = walk(node, [['firstChild', [FieldExpression]]]);
|
||||
if (endOfPathNode) {
|
||||
return {
|
||||
type: 'SPANSET_IN_NAME_SCOPE',
|
||||
scope: attributeFieldUpToDot,
|
||||
scope: getAttributeFieldUpToDot(endOfPathNode),
|
||||
};
|
||||
}
|
||||
|
||||
const endOfPathNode2 = walk(node, [
|
||||
['parent', [SpansetFilter]],
|
||||
['firstChild', [FieldExpression]],
|
||||
]);
|
||||
// In this case, we also need to check the character at `pos`
|
||||
if (endOfPathNode2 && text[pos] !== ' ') {
|
||||
return {
|
||||
type: 'SPANSET_IN_NAME_SCOPE',
|
||||
scope: getAttributeFieldUpToDot(endOfPathNode2),
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -415,3 +432,14 @@ function resolveSpansetPipeline(node: SyntaxNode, _1: string, _2: number): Situa
|
||||
type: 'NEW_SPANSET',
|
||||
};
|
||||
}
|
||||
|
||||
function resolveSpansetWithNoClosedBrace(node: SyntaxNode, text: string, originalPos: number): SituationType {
|
||||
const situation = resolveAttributeCompletion(node, text, originalPos);
|
||||
if (situation) {
|
||||
return situation;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'SPANSET_EXPRESSION_OPERATORS_WITH_MISSING_CLOSED_BRACE',
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user