mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
prometheus: monaco: handle multiple query fields at the same time (#39286)
This commit is contained in:
parent
c457c03a9c
commit
8489575c84
@ -1,5 +1,5 @@
|
||||
import React, { useRef } from 'react';
|
||||
import { CodeEditor, CodeEditorMonacoOptions } from '@grafana/ui';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { CodeEditor, CodeEditorMonacoOptions, Monaco, monacoTypes } from '@grafana/ui';
|
||||
import { useLatest } from 'react-use';
|
||||
import { promLanguageDefinition } from 'monaco-promql';
|
||||
import { getCompletionProvider } from './monaco-completion-provider';
|
||||
@ -22,6 +22,24 @@ const options: CodeEditorMonacoOptions = {
|
||||
fixedOverflowWidgets: true,
|
||||
};
|
||||
|
||||
const PROMQL_LANG_ID = promLanguageDefinition.id;
|
||||
|
||||
// we must only run the promql-setup code once
|
||||
let PROMQL_SETUP_STARTED = false;
|
||||
|
||||
function ensurePromQL(monaco: Monaco) {
|
||||
if (PROMQL_SETUP_STARTED === false) {
|
||||
PROMQL_SETUP_STARTED = true;
|
||||
const { aliases, extensions, mimetypes, loader } = promLanguageDefinition;
|
||||
monaco.languages.register({ id: PROMQL_LANG_ID, aliases, extensions, mimetypes });
|
||||
|
||||
loader().then((mod) => {
|
||||
monaco.languages.setMonarchTokensProvider(PROMQL_LANG_ID, mod.language);
|
||||
monaco.languages.setLanguageConfiguration(PROMQL_LANG_ID, mod.languageConfiguration);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const MonacoQueryField = (props: Props) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const { languageProvider, history, onChange, initialValue } = props;
|
||||
@ -29,6 +47,15 @@ const MonacoQueryField = (props: Props) => {
|
||||
const lpRef = useLatest(languageProvider);
|
||||
const historyRef = useLatest(history);
|
||||
|
||||
const autocompleteDisposeFun = useRef<(() => void) | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// when we unmount, we unregister the autocomplete-function, if it was registered
|
||||
return () => {
|
||||
autocompleteDisposeFun.current?.();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
// NOTE: we will be setting inline-style-width/height on this element
|
||||
@ -48,7 +75,8 @@ const MonacoQueryField = (props: Props) => {
|
||||
monacoOptions={options}
|
||||
language="promql"
|
||||
value={initialValue}
|
||||
onBeforeEditorMount={(monaco) => {
|
||||
onBeforeEditorMount={ensurePromQL}
|
||||
onEditorDidMount={(editor, monaco) => {
|
||||
// we construct a DataProvider object
|
||||
const getSeries = (selector: string) => lpRef.current.getSeries(selector);
|
||||
|
||||
@ -69,19 +97,34 @@ const MonacoQueryField = (props: Props) => {
|
||||
};
|
||||
|
||||
const dataProvider = { getSeries, getHistory, getAllMetricNames };
|
||||
const completionProvider = getCompletionProvider(monaco, dataProvider);
|
||||
|
||||
const langId = promLanguageDefinition.id;
|
||||
monaco.languages.register(promLanguageDefinition);
|
||||
promLanguageDefinition.loader().then((mod) => {
|
||||
monaco.languages.setMonarchTokensProvider(langId, mod.language);
|
||||
monaco.languages.setLanguageConfiguration(langId, mod.languageConfiguration);
|
||||
const completionProvider = getCompletionProvider(monaco, dataProvider);
|
||||
monaco.languages.registerCompletionItemProvider(langId, completionProvider);
|
||||
});
|
||||
// completion-providers in monaco are not registered directly to editor-instances,
|
||||
// they are registerd to languages. this makes it hard for us to have
|
||||
// separate completion-providers for every query-field-instance
|
||||
// (but we need that, because they might connect to different datasources).
|
||||
// the trick we do is, we wrap the callback in a "proxy",
|
||||
// and in the proxy, the first thing is, we check if we are called from
|
||||
// "our editor instance", and if not, we just return nothing. if yes,
|
||||
// we call the completion-provider.
|
||||
const filteringCompletionProvider: monacoTypes.languages.CompletionItemProvider = {
|
||||
...completionProvider,
|
||||
provideCompletionItems: (model, position, context, token) => {
|
||||
// if the model-id does not match, then this call is from a different editor-instance,
|
||||
// not "our instance", so return nothing
|
||||
if (editor.getModel()?.id !== model.id) {
|
||||
return { suggestions: [] };
|
||||
}
|
||||
return completionProvider.provideCompletionItems(model, position, context, token);
|
||||
},
|
||||
};
|
||||
|
||||
// FIXME: should we unregister this at end end?
|
||||
}}
|
||||
onEditorDidMount={(editor, monaco) => {
|
||||
const { dispose } = monaco.languages.registerCompletionItemProvider(
|
||||
PROMQL_LANG_ID,
|
||||
filteringCompletionProvider
|
||||
);
|
||||
|
||||
autocompleteDisposeFun.current = dispose;
|
||||
// this code makes the editor resize itself so that the content fits
|
||||
// (it will grow taller when necessary)
|
||||
// FIXME: maybe move this functionality into CodeEditor, like:
|
||||
|
Loading…
Reference in New Issue
Block a user