diff --git a/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx b/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx index 76b995b858a..9e9ed94f7d4 100644 --- a/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx +++ b/public/app/plugins/datasource/prometheus/components/monaco-query-field/MonacoQueryField.tsx @@ -56,7 +56,14 @@ const MonacoQueryField = (props: Props) => { const getAllMetricNames = () => { const { metricsMetadata } = lpRef.current; - const result = metricsMetadata == null ? [] : Object.keys(metricsMetadata); + const result = + metricsMetadata == null + ? [] + : Object.entries(metricsMetadata).map(([k, v]) => ({ + name: k, + help: v[0].help, + type: v[0].type, + })); return Promise.resolve(result); }; 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 507136cc20c..36f00ce4fa2 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 @@ -3,38 +3,56 @@ import { NeverCaseError } from './util'; // FIXME: we should not load this from the "outside", but we cannot do that while we have the "old" query-field too import { FUNCTIONS } from '../../../promql'; +export type CompletionType = 'HISTORY' | 'FUNCTION' | 'METRIC_NAME' | 'DURATION' | 'LABEL_NAME' | 'LABEL_VALUE'; + type Completion = { + type: CompletionType; label: string; insertText: string; + detail?: string; + documentation?: string; triggerOnInsert?: boolean; }; +type Metric = { + name: string; + help: string; + type: string; +}; + export type DataProvider = { getHistory: () => Promise; - getAllMetricNames: () => Promise; + getAllMetricNames: () => Promise; getSeries: (selector: string) => Promise>; }; // we order items like: history, functions, metrics async function getAllMetricNamesCompletions(dataProvider: DataProvider): Promise { - const names = await dataProvider.getAllMetricNames(); - return names.map((text) => ({ - label: text, - insertText: text, + const metrics = await dataProvider.getAllMetricNames(); + return metrics.map((metric) => ({ + type: 'METRIC_NAME', + label: metric.name, + insertText: metric.name, + detail: `${metric.name} : ${metric.type}`, + documentation: metric.help, })); } function getAllFunctionsCompletions(): Completion[] { return FUNCTIONS.map((f) => ({ + type: 'FUNCTION', label: f.label, insertText: f.insertText ?? '', // i don't know what to do when this is nullish. it should not be. + detail: f.detail, + documentation: f.documentation, })); } function getAllDurationsCompletions(): Completion[] { // FIXME: get a better list return ['5m', '1m', '30s', '15s'].map((text) => ({ + type: 'DURATION', label: text, insertText: text, })); @@ -46,6 +64,7 @@ async function getAllHistoryCompletions(dataProvider: DataProvider): Promise ({ + type: 'HISTORY', label: expr, insertText: expr, })); @@ -77,6 +96,7 @@ async function getLabelNamesForCompletions( const usedLabelNames = new Set(otherLabels.map((l) => l.name)); // names used in the query const labelNames = possibleLabelNames.filter((l) => !usedLabelNames.has(l)); return labelNames.map((text) => ({ + type: 'LABEL_NAME', label: text, insertText: `${text}${suffix}`, triggerOnInsert, @@ -108,6 +128,7 @@ async function getLabelValuesForMetricCompletions( const data = await dataProvider.getSeries(selector); const values = data[labelName] ?? []; return values.map((text) => ({ + type: 'LABEL_VALUE', label: text, insertText: `"${text}"`, // FIXME: escaping strange characters? })); 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 91cbaabfb7f..eeb8a3e040d 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 @@ -1,8 +1,27 @@ import type { Monaco, monacoTypes } from '@grafana/ui'; import { getIntent } from './intent'; -import { getCompletions, DataProvider } from './completions'; +import { getCompletions, DataProvider, CompletionType } from './completions'; +import { NeverCaseError } from './util'; +function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): monacoTypes.languages.CompletionItemKind { + switch (type) { + case 'DURATION': + return monaco.languages.CompletionItemKind.Unit; + case 'FUNCTION': + return monaco.languages.CompletionItemKind.Variable; + case 'HISTORY': + return monaco.languages.CompletionItemKind.Snippet; + case 'LABEL_NAME': + return monaco.languages.CompletionItemKind.Enum; + case 'LABEL_VALUE': + return monaco.languages.CompletionItemKind.EnumMember; + case 'METRIC_NAME': + return monaco.languages.CompletionItemKind.Constructor; + default: + throw new NeverCaseError(type); + } +} export function getCompletionProvider( monaco: Monaco, dataProvider: DataProvider @@ -35,10 +54,12 @@ export function getCompletionProvider( // to stop it, we use a number-as-string sortkey, // so that monaco keeps the order we use const maxIndexDigits = items.length.toString().length; - const suggestions = items.map((item, index) => ({ - kind: monaco.languages.CompletionItemKind.Text, + const suggestions: monacoTypes.languages.CompletionItem[] = items.map((item, index) => ({ + kind: getMonacoCompletionItemKind(item.type, monaco), label: item.label, insertText: item.insertText, + detail: item.detail, + documentation: item.documentation, sortText: index.toString().padStart(maxIndexDigits, '0'), // to force the order we have range, command: item.triggerOnInsert