Skip to content

Commit 4c1b4e0

Browse files
authored
Merge pull request #54 from haohanyang/code-editor-enhance
Add Hover and Codelense in code editor
2 parents 07026fd + 3cc4df0 commit 4c1b4e0

File tree

9 files changed

+369
-182
lines changed

9 files changed

+369
-182
lines changed

package-lock.json

Lines changed: 31 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"name": "mongodb-datasource",
33
"version": "0.3.1",
44
"scripts": {
5-
"build": "webpack -c ./.config/webpack/webpack.config.ts --env production",
6-
"dev": "webpack -w -c ./.config/webpack/webpack.config.ts --env development",
5+
"build": "webpack -c ./webpack.config.ts --env production",
6+
"dev": "webpack -w -c ./webpack.config.ts --env development",
77
"test": "jest --watch --onlyChanged",
88
"test:ci": "jest --passWithNoTests --maxWorkers 4",
99
"typecheck": "tsc --noEmit",
@@ -68,6 +68,8 @@
6868
"@grafana/runtime": "^11.2.0",
6969
"@grafana/schema": "^10.4.0",
7070
"@grafana/ui": "^11.2.0",
71+
"@mongodb-js/mongodb-constants": "^0.11.1",
72+
"jsonc-parser": "^3.3.1",
7173
"react": "18.2.0",
7274
"react-dom": "18.2.0",
7375
"shadowrealm-api": "^0.8.3",

src/aggregation.json

Lines changed: 0 additions & 157 deletions
This file was deleted.

src/components/QueryEditorRaw.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import React, { useRef, useCallback } from 'react';
22
import { CodeEditor, type MonacoEditor } from '@grafana/ui';
3-
import { useAutocomplete } from 'autocomplete';
3+
import { useAutocomplete } from '../editor/autocomplete';
4+
import { useValidation } from '../editor/validation';
5+
import { useHover } from '../editor/hover';
6+
import { useCodeLens } from '../editor/codelens';
47

58
interface QueryEditorRawProps {
69
query: string;
@@ -14,7 +17,11 @@ interface QueryEditorRawProps {
1417

1518
export function QueryEditorRaw({ query, onBlur, language, width, height, fontSize, children }: QueryEditorRawProps) {
1619
const monacoRef = useRef<MonacoEditor | null>(null);
20+
1721
const setupAutocompleteFn = useAutocomplete();
22+
const setupHoverFn = useHover();
23+
const setupValidationFn = useValidation();
24+
const setupCodeLensFn = useCodeLens();
1825

1926
const formatQuery = useCallback(() => {
2027
if (monacoRef.current) {
@@ -27,7 +34,16 @@ export function QueryEditorRaw({ query, onBlur, language, width, height, fontSiz
2734
<CodeEditor
2835
onEditorDidMount={(editor, monaco) => {
2936
monacoRef.current = editor;
37+
setupValidationFn(editor, monaco);
3038
setupAutocompleteFn(editor, monaco);
39+
setupHoverFn(editor, monaco);
40+
41+
const updateTextCommandId = editor.addCommand(0, (_ctx, ...args) => {
42+
const text = args[0];
43+
onBlur?.(text);
44+
});
45+
46+
setupCodeLensFn(editor, monaco, updateTextCommandId!);
3147
}}
3248
height={height || '240px'}
3349
width={width ? `${width - 2}px` : undefined}
@@ -36,7 +52,7 @@ export function QueryEditorRaw({ query, onBlur, language, width, height, fontSiz
3652
value={query}
3753
showMiniMap={false}
3854
showLineNumbers={true}
39-
monacoOptions={fontSize ? { fontSize: fontSize } : undefined}
55+
monacoOptions={fontSize ? { fontSize: fontSize, codeLens: true } : undefined}
4056
/>
4157
{children && children({ formatQuery })}
4258
</div>

src/autocomplete.tsx renamed to src/editor/autocomplete.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useRef, useEffect } from 'react';
2-
import { type Monaco, type monacoTypes } from '@grafana/ui';
2+
import { type Monaco, type monacoTypes, type MonacoEditor } from '@grafana/ui';
33
import { languages } from 'monaco-editor';
4-
import aggregationData from './aggregation.json';
4+
import { STAGE_OPERATORS, EXPRESSION_OPERATORS, ACCUMULATORS, CONVERSION_OPERATORS, QUERY_OPERATORS } from '@mongodb-js/mongodb-constants'
55

66
// Supports JSON only right now
77
class CompletionProvider implements monacoTypes.languages.CompletionItemProvider {
8-
constructor(private readonly editor: monacoTypes.editor.IStandaloneCodeEditor) {}
8+
constructor(private readonly editor: MonacoEditor) { }
99

1010
provideCompletionItems(
1111
model: monacoTypes.editor.ITextModel,
@@ -40,18 +40,30 @@ class CompletionProvider implements monacoTypes.languages.CompletionItemProvider
4040
endColumn: word.endColumn,
4141
};
4242

43-
const suggestions: languages.CompletionItem[] = aggregationData['stages'].map((s) => ({
44-
label: `"${s['name']}"`,
43+
const stageSuggestions: languages.CompletionItem[] = STAGE_OPERATORS.map((stage) => {
44+
// Add double quotation marks
45+
const snippet = stage.snippet.replace(/(\s*)([a-zA-Z]+)\s*: /g, '$1"$2": ');
46+
return {
47+
label: `"${stage.name}"`,
48+
kind: languages.CompletionItemKind.Function,
49+
insertText: `"\\${stage.name}": ${snippet}`,
50+
range: range,
51+
detail: stage.meta,
52+
documentation: stage.description,
53+
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
54+
}});
55+
56+
const expressionSuggestions: languages.CompletionItem[] = [...EXPRESSION_OPERATORS, ...ACCUMULATORS, ...CONVERSION_OPERATORS, ...QUERY_OPERATORS].map((expression) => ({
57+
label: `"${expression.name}"`,
4558
kind: languages.CompletionItemKind.Function,
46-
insertText: s['insertText'] ? s['insertText'] : `"\\${s['name']}": {\n\t$0\n}`,
59+
insertText: `"\\${expression.name}": \${1:expression}`,
4760
range: range,
48-
detail: 'stage',
49-
documentation: s['description'],
61+
detail: expression.meta,
5062
insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet,
5163
}));
5264

5365
return {
54-
suggestions: suggestions,
66+
suggestions: [...stageSuggestions, ...expressionSuggestions],
5567
};
5668
}
5769
}
@@ -64,7 +76,7 @@ export function useAutocomplete() {
6476
};
6577
}, []);
6678

67-
return (editor: monacoTypes.editor.IStandaloneCodeEditor, monaco: Monaco) => {
79+
return (editor: MonacoEditor, monaco: Monaco) => {
6880
const provider = new CompletionProvider(editor);
6981
const { dispose } = monaco.languages.registerCompletionItemProvider('json', provider);
7082
autocompleteDisposeFun.current = dispose;

0 commit comments

Comments
 (0)