From eec7b9d325f836a16a7c159130522848fe8e75da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Farkas?= Date: Fri, 5 Nov 2021 10:13:55 +0100 Subject: [PATCH] prometheus: monaco: better suggestions inside quotes (#41262) * prometheus: monaco: better suggestions inside quotes * add double-quote as trigger character * moved comment to correct place --- .../monaco-completion-provider/completions.ts | 4 +++- .../monaco-completion-provider/index.ts | 2 +- .../situation.test.ts | 15 ++++++++++++ .../monaco-completion-provider/situation.ts | 23 +++++++++++++++---- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/completions.ts b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/completions.ts index 9d1b9394fbe..719705d66af 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/completions.ts +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/completions.ts @@ -162,6 +162,7 @@ async function getLabelValues( async function getLabelValuesForMetricCompletions( metric: string | undefined, labelName: string, + betweenQuotes: boolean, otherLabels: Label[], dataProvider: DataProvider ): Promise { @@ -169,7 +170,7 @@ async function getLabelValuesForMetricCompletions( return values.map((text) => ({ type: 'LABEL_VALUE', label: text, - insertText: `"${text}"`, // FIXME: escaping strange characters? + insertText: betweenQuotes ? text : `"${text}"`, // FIXME: escaping strange characters? })); } @@ -195,6 +196,7 @@ export async function getCompletions(situation: Situation, dataProvider: DataPro return getLabelValuesForMetricCompletions( situation.metricName, situation.labelName, + situation.betweenQuotes, situation.otherLabels, dataProvider ); diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts index 4685390705a..3075a9b4570 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/index.ts @@ -95,7 +95,7 @@ export function getCompletionProvider( }; return { - triggerCharacters: ['{', ',', '[', '(', '=', '~', ' '], + triggerCharacters: ['{', ',', '[', '(', '=', '~', ' ', '"'], provideCompletionItems, }; } diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.test.ts b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.test.ts index 299e363a273..128650c4c22 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.test.ts +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.test.ts @@ -96,6 +96,7 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'job', + betweenQuotes: false, otherLabels: [], }); @@ -103,6 +104,7 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'job', + betweenQuotes: false, otherLabels: [], }); @@ -110,6 +112,7 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'job', + betweenQuotes: false, otherLabels: [], }); @@ -117,6 +120,7 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'job', + betweenQuotes: false, otherLabels: [], }); @@ -124,12 +128,22 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'job', + betweenQuotes: false, otherLabels: [{ name: 'host', value: 'h1', op: '=' }], }); + assertSituation('something{job="j1",host="^"}', { + type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', + metricName: 'something', + labelName: 'host', + betweenQuotes: true, + otherLabels: [{ name: 'job', value: 'j1', op: '=' }], + }); + assertSituation('{job=^,host="h1"}', { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', labelName: 'job', + betweenQuotes: false, otherLabels: [{ name: 'host', value: 'h1', op: '=' }], }); @@ -137,6 +151,7 @@ describe('situation', () => { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName: 'something', labelName: 'three', + betweenQuotes: false, otherLabels: [ { name: 'one', value: 'val1', op: '=' }, { name: 'two', value: 'val2', op: '!=' }, diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.ts b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.ts index 74eabeaec44..f00fc8c7e80 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.ts +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/monaco-completion-provider/situation.ts @@ -111,6 +111,7 @@ export type Situation = type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME'; metricName?: string; labelName: string; + betweenQuotes: boolean; otherLabels: Label[]; }; @@ -138,9 +139,13 @@ const RESOLVERS: Resolver[] = [ path: ['FunctionCallBody'], fun: resolveInFunction, }, + { + path: ['StringLiteral', 'LabelMatcher'], + fun: resolveLabelMatcher, + }, { path: [ERROR_NODE_NAME, 'LabelMatcher'], - fun: resolveLabelMatcherError, + fun: resolveLabelMatcher, }, { path: [ERROR_NODE_NAME, 'MatrixSelector'], @@ -290,9 +295,12 @@ function resolveLabelsForGrouping(node: SyntaxNode, text: string, pos: number): }; } -function resolveLabelMatcherError(node: SyntaxNode, text: string, pos: number): Situation | null { - // we are probably in the scenario where the user is before entering the - // label-value, like `{job=^}` (^ marks the cursor) +function resolveLabelMatcher(node: SyntaxNode, text: string, pos: number): Situation | null { + // we can arrive here in two situation. `node` is either: + // - a StringNode (like in `{job="^"}`) + // - or an error node (like in `{job=^}`) + const inStringNode = !node.type.isError; + const parent = walk(node, [['parent', 'LabelMatcher']]); if (parent === null) { return null; @@ -344,7 +352,10 @@ function resolveLabelMatcherError(node: SyntaxNode, text: string, pos: number): } // now we need to find the other names - const otherLabels = getLabels(labelMatchersNode, text); + const allLabels = getLabels(labelMatchersNode, text); + + // we need to remove "our" label from all-labels, if it is in there + const otherLabels = allLabels.filter((label) => label.name !== labelName); const metricNameNode = walk(labelMatchersNode, [ ['parent', 'VectorSelector'], @@ -357,6 +368,7 @@ function resolveLabelMatcherError(node: SyntaxNode, text: string, pos: number): return { type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', labelName, + betweenQuotes: inStringNode, otherLabels, }; } @@ -367,6 +379,7 @@ function resolveLabelMatcherError(node: SyntaxNode, text: string, pos: number): type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME', metricName, labelName, + betweenQuotes: inStringNode, otherLabels, }; }