mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Properly manage trigger chars with open editor box (#75461)
This commit is contained in:
parent
030e891b3f
commit
5fae64ccbe
@ -251,19 +251,14 @@ describe('CompletionProvider', () => {
|
||||
});
|
||||
|
||||
it.each([
|
||||
['{ .foo }', 6],
|
||||
['{ .foo }', 7],
|
||||
['{.foo 300}', 5],
|
||||
['{.foo 300}', 6],
|
||||
['{.foo 300}', 7],
|
||||
['{.foo 300}', 8],
|
||||
['{.foo 300 && .bar = 200}', 5],
|
||||
['{.foo 300 && .bar = 200}', 6],
|
||||
['{.foo 300 && .bar = 200}', 7],
|
||||
['{.foo 300 && .bar 200}', 18],
|
||||
['{.foo 300 && .bar 200}', 19],
|
||||
['{.foo 300 && .bar 200}', 20],
|
||||
['{ .foo = 1 && .bar }', 18],
|
||||
['{ .foo = 1 && .bar }', 19],
|
||||
['{ .foo = 1 && .bar }', 19],
|
||||
])('suggests with incomplete spanset - %s, %i', async (input: string, offset: number) => {
|
||||
@ -276,6 +271,28 @@ describe('CompletionProvider', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['{ .foo }', 6],
|
||||
['{.foo 300}', 5],
|
||||
['{.foo 300 && .bar = 200}', 5],
|
||||
['{ .foo = 1 && .bar }', 18],
|
||||
])('suggests with incomplete spanset with no space before cursor - %s, %i', async (input: string, offset: number) => {
|
||||
const { provider, model } = setup(input, offset);
|
||||
const result = await provider.provideCompletionItems(model, emptyPosition);
|
||||
expect((result! as monacoTypes.languages.CompletionList).suggestions).toEqual([]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['{ span.d }', 8],
|
||||
['{ span.db }', 9],
|
||||
])('suggests to complete attribute - %s, %i', async (input: string, offset: number) => {
|
||||
const { provider, model } = setup(input, offset, undefined, v2Tags);
|
||||
const result = await provider.provideCompletionItems(model, emptyPosition);
|
||||
expect((result! as monacoTypes.languages.CompletionList).suggestions).toEqual([
|
||||
expect.objectContaining({ label: 'db', insertText: 'db' }),
|
||||
]);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['{.foo=1} {.bar=2}', 8],
|
||||
['{.foo=1} {.bar=2}', 9],
|
||||
|
@ -25,6 +25,11 @@ describe('situation', () => {
|
||||
{
|
||||
query: '{.foo}',
|
||||
cursorPos: 5,
|
||||
expected: { type: 'SPANSET_IN_NAME_SCOPE', scope: '' },
|
||||
},
|
||||
{
|
||||
query: '{.foo }',
|
||||
cursorPos: 6,
|
||||
expected: { type: 'SPANSET_EXPRESSION_OPERATORS' },
|
||||
},
|
||||
{
|
||||
@ -49,7 +54,7 @@ describe('situation', () => {
|
||||
},
|
||||
{
|
||||
query: '{span.foo = 200 }',
|
||||
cursorPos: 15,
|
||||
cursorPos: 16,
|
||||
expected: { type: 'SPANFIELD_COMBINING_OPERATORS' },
|
||||
},
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ type Path = Array<[Direction, NodeType[]]>;
|
||||
|
||||
type Resolver = {
|
||||
path: NodeType[];
|
||||
fun: (node: SyntaxNode, text: string, pos: number) => SituationType | null;
|
||||
fun: (node: SyntaxNode, text: string, pos: number, originalPos: number) => SituationType | null;
|
||||
};
|
||||
|
||||
function getErrorNode(tree: Tree, cursorPos: number): SyntaxNode | null {
|
||||
@ -181,7 +181,7 @@ export function getSituation(text: string, offset: number): Situation | null {
|
||||
let situationType: SituationType | null = null;
|
||||
for (let resolver of RESOLVERS) {
|
||||
if (isPathMatch(resolver.path, ids)) {
|
||||
situationType = resolver.fun(currentNode, text, shiftedOffset);
|
||||
situationType = resolver.fun(currentNode, text, shiftedOffset, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,7 +191,7 @@ export function getSituation(text: string, offset: number): Situation | null {
|
||||
const ERROR_NODE_ID = 0;
|
||||
|
||||
const RESOLVERS: Resolver[] = [
|
||||
// Incomplete query cases
|
||||
// Curson on error node cases
|
||||
{
|
||||
path: [ERROR_NODE_ID, AttributeField],
|
||||
fun: resolveAttribute,
|
||||
@ -230,12 +230,10 @@ const RESOLVERS: Resolver[] = [
|
||||
};
|
||||
},
|
||||
},
|
||||
// Valid query cases
|
||||
// Curson on valid node cases (the whole query could contain errors nevertheless)
|
||||
{
|
||||
path: [FieldExpression],
|
||||
fun: () => ({
|
||||
type: 'SPANSET_EXPRESSION_OPERATORS',
|
||||
}),
|
||||
fun: resolveSpanset,
|
||||
},
|
||||
{
|
||||
path: [SpansetFilter],
|
||||
@ -251,30 +249,53 @@ const RESOLVERS: Resolver[] = [
|
||||
},
|
||||
];
|
||||
|
||||
function resolveSpanset(node: SyntaxNode): SituationType {
|
||||
const firstChild = walk(node, [
|
||||
const resolveAttributeCompletion = (node: SyntaxNode, text: string, pos: number): SituationType | void => {
|
||||
// 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 attributeFieldParentText = attributeFieldParent ? getNodeText(attributeFieldParent, text) : '';
|
||||
const indexOfDot = attributeFieldParentText.indexOf('.');
|
||||
const attributeFieldUpToDot = attributeFieldParentText.slice(0, indexOfDot);
|
||||
|
||||
return {
|
||||
type: 'SPANSET_IN_NAME_SCOPE',
|
||||
scope: attributeFieldUpToDot,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function resolveSpanset(node: SyntaxNode, text: string, _: number, originalPos: number): SituationType {
|
||||
const situation = resolveAttributeCompletion(node, text, originalPos);
|
||||
if (situation) {
|
||||
return situation;
|
||||
}
|
||||
|
||||
let endOfPathNode = walk(node, [
|
||||
['firstChild', [FieldExpression]],
|
||||
['firstChild', [AttributeField]],
|
||||
]);
|
||||
if (firstChild) {
|
||||
if (endOfPathNode) {
|
||||
return {
|
||||
type: 'SPANSET_EXPRESSION_OPERATORS',
|
||||
};
|
||||
}
|
||||
|
||||
const lastFieldExpression1 = walk(node, [
|
||||
endOfPathNode = walk(node, [
|
||||
['lastChild', [FieldExpression]],
|
||||
['lastChild', [FieldExpression]],
|
||||
['lastChild', [Static]],
|
||||
]);
|
||||
if (lastFieldExpression1) {
|
||||
if (endOfPathNode) {
|
||||
return {
|
||||
type: 'SPANFIELD_COMBINING_OPERATORS',
|
||||
};
|
||||
}
|
||||
|
||||
const lastFieldExpression = walk(node, [['lastChild', [FieldExpression]]]);
|
||||
if (lastFieldExpression) {
|
||||
endOfPathNode = walk(node, [['lastChild', [FieldExpression]]]);
|
||||
if (endOfPathNode) {
|
||||
return {
|
||||
type: 'SPANSET_EXPRESSION_OPERATORS',
|
||||
};
|
||||
@ -309,7 +330,12 @@ function resolveAttribute(node: SyntaxNode, text: string): SituationType {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveExpression(node: SyntaxNode, text: string): SituationType {
|
||||
function resolveExpression(node: SyntaxNode, text: string, _: number, originalPos: number): SituationType {
|
||||
const situation = resolveAttributeCompletion(node, text, originalPos);
|
||||
if (situation) {
|
||||
return situation;
|
||||
}
|
||||
|
||||
if (node.prevSibling?.type.id === FieldOp) {
|
||||
let attributeField = node.prevSibling.prevSibling;
|
||||
if (attributeField) {
|
||||
|
Loading…
Reference in New Issue
Block a user