mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Cloudwatch: Add syntax highlighting and autocomplete for "Metric Search" (#43985)
* Create a "monarch" folder with everything you need to do syntax highlighting and autocompletion. * Use this new monarch folder with existing cloudwatch sql. * Add metric math syntax highlighting and autocomplete. * Make autocomplete "smarter": - search always inserts a string as first arg - strings can't contain predefined functions - operators follow the last closing ) * Add some tests for Metric Math's CompletionItemProvider. * Fixes After CR: - refactor CompletionItemProvider, so that it only requires args that are dynamic or outside of it's responsibility - Update and add tests with mocked monaco - Add more autocomplete suggestions for SEARCH expression functions - sort keywords and give different priority from function to make more visually distinctive. * Change QueryEditor to auto-resize and look more like the one in Prometheus. * Add autocomplete for time periods for the third arg of Search. * More CR fixes: - fix missing break - add unit tests for statementPosition - fix broken time period - sort time periods * Bug fix
This commit is contained in:
@@ -1,28 +1,84 @@
|
||||
import React from 'react';
|
||||
import { Input } from '@grafana/ui';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { CodeEditor, Monaco } from '@grafana/ui';
|
||||
import language from '../metric-math/definition';
|
||||
import { registerLanguage } from '../monarch/register';
|
||||
import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import { TRIGGER_SUGGEST } from '../monarch/commands';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
|
||||
export interface Props {
|
||||
onChange: (query: string) => void;
|
||||
onRunQuery: () => void;
|
||||
expression: string;
|
||||
datasource: CloudWatchDatasource;
|
||||
}
|
||||
|
||||
export function MathExpressionQueryField({ expression: query, onChange, onRunQuery }: React.PropsWithChildren<Props>) {
|
||||
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (event.key === 'Enter' && (event.shiftKey || event.ctrlKey)) {
|
||||
event.preventDefault();
|
||||
onRunQuery();
|
||||
}
|
||||
};
|
||||
export function MathExpressionQueryField({
|
||||
expression: query,
|
||||
onChange,
|
||||
onRunQuery,
|
||||
datasource,
|
||||
}: React.PropsWithChildren<Props>) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const onEditorMount = useCallback(
|
||||
(editor: monacoType.editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
||||
editor.onDidFocusEditorText(() => editor.trigger(TRIGGER_SUGGEST.id, TRIGGER_SUGGEST.id, {}));
|
||||
editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => {
|
||||
const text = editor.getValue();
|
||||
onChange(text);
|
||||
onRunQuery();
|
||||
});
|
||||
|
||||
// auto resizes the editor to be the height of the content it holds
|
||||
// this code comes from the Prometheus query editor.
|
||||
// We may wish to consider abstracting it into the grafana/ui repo in the future
|
||||
const updateElementHeight = () => {
|
||||
const containerDiv = containerRef.current;
|
||||
if (containerDiv !== null && editor.getContentHeight() < 200) {
|
||||
const pixelHeight = editor.getContentHeight();
|
||||
containerDiv.style.height = `${pixelHeight}px`;
|
||||
containerDiv.style.width = '100%';
|
||||
const pixelWidth = containerDiv.clientWidth;
|
||||
editor.layout({ width: pixelWidth, height: pixelHeight });
|
||||
}
|
||||
};
|
||||
|
||||
editor.onDidContentSizeChange(updateElementHeight);
|
||||
updateElementHeight();
|
||||
},
|
||||
[onChange, onRunQuery]
|
||||
);
|
||||
|
||||
return (
|
||||
<Input
|
||||
name="Query"
|
||||
value={query}
|
||||
placeholder="Enter a math expression"
|
||||
onBlur={onRunQuery}
|
||||
onChange={(e) => onChange(e.currentTarget.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<div ref={containerRef}>
|
||||
<CodeEditor
|
||||
monacoOptions={{
|
||||
// without this setting, the auto-resize functionality causes an infinite loop, don't remove it!
|
||||
scrollBeyondLastLine: false,
|
||||
|
||||
// These additional options are style focused and are a subset of those in the query editor in Prometheus
|
||||
fontSize: 14,
|
||||
lineNumbers: 'off',
|
||||
renderLineHighlight: 'none',
|
||||
scrollbar: {
|
||||
vertical: 'hidden',
|
||||
horizontal: 'hidden',
|
||||
},
|
||||
suggestFontSize: 12,
|
||||
wordWrap: 'on',
|
||||
}}
|
||||
language={language.id}
|
||||
value={query}
|
||||
onBlur={(value) => {
|
||||
if (value !== query) {
|
||||
onChange(value);
|
||||
}
|
||||
}}
|
||||
onBeforeEditorMount={(monaco: Monaco) =>
|
||||
registerLanguage(monaco, language, datasource.metricMathCompletionItemProvider)
|
||||
}
|
||||
onEditorDidMount={onEditorMount}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ export class MetricsQueryEditor extends PureComponent<Props, State> {
|
||||
onRunQuery={onRunQuery}
|
||||
expression={query.expression ?? ''}
|
||||
onChange={(expression) => this.props.onChange({ ...query, expression })}
|
||||
datasource={datasource}
|
||||
></MathExpressionQueryField>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -3,8 +3,8 @@ import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api';
|
||||
import { CodeEditor, Monaco } from '@grafana/ui';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import language from '../cloudwatch-sql/definition';
|
||||
import { TRIGGER_SUGGEST } from '../cloudwatch-sql/completion/commands';
|
||||
import { registerLanguage } from '../cloudwatch-sql/register';
|
||||
import { TRIGGER_SUGGEST } from '../monarch/commands';
|
||||
import { registerLanguage } from '../monarch/register';
|
||||
|
||||
export interface Props {
|
||||
region: string;
|
||||
@@ -43,7 +43,7 @@ export const SQLCodeEditor: FunctionComponent<Props> = ({ region, sql, onChange,
|
||||
}}
|
||||
showMiniMap={false}
|
||||
showLineNumbers={true}
|
||||
onBeforeEditorMount={(monaco: Monaco) => registerLanguage(monaco, datasource.sqlCompletionItemProvider)}
|
||||
onBeforeEditorMount={(monaco: Monaco) => registerLanguage(monaco, language, datasource.sqlCompletionItemProvider)}
|
||||
onEditorDidMount={onEditorMount}
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user