From b58cf31a6a9ae3ea6a7fd1c9f7c8b1dd503ae42f Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Mon, 12 Dec 2022 16:19:34 +0100 Subject: [PATCH 1/4] add context to monaco editors --- .../monaco-query-field/MonacoQueryField.tsx | 19 ++++++++++++------ .../monaco-query-field/MonacoQueryField.tsx | 20 +++++++++++++++---- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx index 7791a248702b..0d64dd399913 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/css'; import React, { useRef, useEffect } from 'react'; import { useLatest } from 'react-use'; +import { v4 as uuidv4 } from 'uuid'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -78,10 +79,10 @@ const getStyles = (theme: GrafanaTheme2) => { }; const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initialValue }: Props) => { + const id = uuidv4(); // we need only one instance of `overrideServices` during the lifetime of the react component const overrideServicesRef = useRef(getOverrideServices()); const containerRef = useRef(null); - const langProviderRef = useLatest(languageProvider); const historyRef = useLatest(history); const onRunQueryRef = useLatest(onRunQuery); @@ -115,8 +116,10 @@ const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initi ensureLogQL(monaco); }} onMount={(editor, monaco) => { + const isEditorFocused = editor.createContextKey('isEditorFocused' + id, false); // we setup on-blur editor.onDidBlurEditorWidget(() => { + isEditorFocused.set(false); onBlurRef.current(editor.getValue()); }); const dataProvider = new CompletionDataProvider(langProviderRef.current, historyRef.current); @@ -162,14 +165,18 @@ const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initi editor.onDidContentSizeChange(updateElementHeight); updateElementHeight(); - // handle: shift + enter // FIXME: maybe move this functionality into CodeEditor? - editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => { - onRunQueryRef.current(editor.getValue()); - }); - + editor.addCommand( + monaco.KeyMod.Shift | monaco.KeyCode.Enter, + () => { + onRunQueryRef.current(editor.getValue()); + }, + 'runQueryCondition' + id + ); + editor.onDidFocusEditorText(() => { + isEditorFocused.set(true); if (editor.getValue().trim() === '') { editor.trigger('', 'editor.action.triggerSuggest', {}); } 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 5c26a5682d20..97308c848306 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 @@ -2,6 +2,7 @@ import { css } from '@emotion/css'; import { promLanguageDefinition } from 'monaco-promql'; import React, { useRef, useEffect } from 'react'; import { useLatest } from 'react-use'; +import { v4 as uuidv4 } from 'uuid'; import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -87,11 +88,13 @@ const getStyles = (theme: GrafanaTheme2, placeholder: string) => { }; const MonacoQueryField = (props: Props) => { - // we need only one instance of `overrideServices` during the lifetime of the react component + // we need only one instance of `overrideSerices` during the lifetime of the react component const overrideServicesRef = useRef(getOverrideServices()); const containerRef = useRef(null); const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, onChange } = props; + const id = uuidv4(); + const lpRef = useLatest(languageProvider); const historyRef = useLatest(history); const onRunQueryRef = useLatest(onRunQuery); @@ -126,10 +129,15 @@ const MonacoQueryField = (props: Props) => { ensurePromQL(monaco); }} onMount={(editor, monaco) => { + const isEditorFocused = editor.createContextKey('isEditorFocused' + id, false); // we setup on-blur editor.onDidBlurEditorWidget(() => { + isEditorFocused.set(false); onBlurRef.current(editor.getValue()); }); + editor.onDidFocusEditorText(() => { + isEditorFocused.set(true); + }); // we construct a DataProvider object const getSeries = (selector: string) => lpRef.current.getSeries(selector); @@ -213,9 +221,13 @@ const MonacoQueryField = (props: Props) => { // handle: shift + enter // FIXME: maybe move this functionality into CodeEditor? - editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => { - onRunQueryRef.current(editor.getValue()); - }); + editor.addCommand( + monaco.KeyMod.Shift | monaco.KeyCode.Enter, + () => { + onRunQueryRef.current(editor.getValue()); + }, + 'isEditorFocused' + id + ); /* Something in this configuration of monaco doesn't bubble up [mod]+K, which the command palette uses. Pass the event out of monaco manually From 73e283a17f7a4e0610b9ba212a136cfdcf189da5 Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Mon, 12 Dec 2022 16:26:10 +0100 Subject: [PATCH 2/4] move id --- .../components/monaco-query-field/MonacoQueryField.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 97308c848306..138786648862 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 @@ -88,13 +88,13 @@ const getStyles = (theme: GrafanaTheme2, placeholder: string) => { }; const MonacoQueryField = (props: Props) => { - // we need only one instance of `overrideSerices` during the lifetime of the react component + const id = uuidv4(); + + // we need only one instance of `overrideServices` during the lifetime of the react component const overrideServicesRef = useRef(getOverrideServices()); const containerRef = useRef(null); const { languageProvider, history, onBlur, onRunQuery, initialValue, placeholder, onChange } = props; - const id = uuidv4(); - const lpRef = useLatest(languageProvider); const historyRef = useLatest(history); const onRunQueryRef = useLatest(onRunQuery); From debe866c3c5eec9eda278da2b15089f905133da1 Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Mon, 12 Dec 2022 16:31:39 +0100 Subject: [PATCH 3/4] use right key --- .../loki/components/monaco-query-field/MonacoQueryField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx index 0d64dd399913..e738280aeae8 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx @@ -172,7 +172,7 @@ const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initi () => { onRunQueryRef.current(editor.getValue()); }, - 'runQueryCondition' + id + 'isEditorFocused' + id ); editor.onDidFocusEditorText(() => { From e7643315853f6468871ce46a335d74a3bade9a86 Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Mon, 12 Dec 2022 16:54:33 +0100 Subject: [PATCH 4/4] add comment --- .../loki/components/monaco-query-field/MonacoQueryField.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx index e738280aeae8..f98a203a5757 100644 --- a/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx +++ b/public/app/plugins/datasource/loki/components/monaco-query-field/MonacoQueryField.tsx @@ -116,6 +116,7 @@ const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initi ensureLogQL(monaco); }} onMount={(editor, monaco) => { + // Monaco has a bug where it runs actions on all instances (https://github.com/microsoft/monaco-editor/issues/2947), so we ensure actions are executed on instance-level with this ContextKey. const isEditorFocused = editor.createContextKey('isEditorFocused' + id, false); // we setup on-blur editor.onDidBlurEditorWidget(() => { @@ -174,7 +175,7 @@ const MonacoQueryField = ({ languageProvider, history, onBlur, onRunQuery, initi }, 'isEditorFocused' + id ); - + editor.onDidFocusEditorText(() => { isEditorFocused.set(true); if (editor.getValue().trim() === '') {