Skip to content

Commit 12eefe5

Browse files
feat: add monaco editor for InfluxQL (#6623)
* feat: add monaco editor for InfluxQL * feat: add syntax with the original monaco editor psql syntax * chore: update token for keywords and operators and add illegal and literals * chore: update theme and tokenizer defination * chore: update tokenizer regex to match with influxql doc * chore: fix the regex and theme color for duration * chore: add InfluxQL functions * chore: update regex for escaped single and double quote
1 parent 9b347ce commit 12eefe5

File tree

6 files changed

+572
-22
lines changed

6 files changed

+572
-22
lines changed

src/dataExplorer/components/ResultsPane.tsx

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {SubmitQueryButton} from 'src/timeMachine/components/SubmitQueryButton'
3232
import QueryTime from 'src/dataExplorer/components/QueryTime'
3333
import NewDatePicker from 'src/shared/components/dateRangePicker/NewDatePicker'
3434
import {SqlEditorMonaco} from 'src/shared/components/SqlMonacoEditor'
35+
import {InfluxQLMonacoEditor} from 'src/shared/components/InfluxQLMonacoEditor'
3536
import CSVExportButton from 'src/shared/components/CSVExportButton'
3637

3738
// Types
@@ -176,14 +177,44 @@ const ResultsPane: FC = () => {
176177
})
177178
}, [text, range, resource?.language, selection.bucket])
178179

179-
const timeVars = [
180-
getRangeVariable(TIME_RANGE_START, range),
181-
getRangeVariable(TIME_RANGE_STOP, range),
182-
]
183-
184-
const variables = timeVars.concat(
185-
getWindowPeriodVariableFromVariables(text, timeVars) || []
186-
)
180+
let monacoEditor: JSX.Element = null
181+
switch (resource?.language) {
182+
case LanguageType.SQL:
183+
monacoEditor = (
184+
<SqlEditorMonaco
185+
script={text}
186+
onChangeScript={setQuery}
187+
onSubmitScript={submit}
188+
/>
189+
)
190+
break
191+
case LanguageType.INFLUXQL:
192+
monacoEditor = (
193+
<InfluxQLMonacoEditor
194+
script={text}
195+
onChangeScript={setQuery}
196+
onSubmitScript={submit}
197+
/>
198+
)
199+
break
200+
default:
201+
// LanguageType.FLUX
202+
const timeVars = [
203+
getRangeVariable(TIME_RANGE_START, range),
204+
getRangeVariable(TIME_RANGE_STOP, range),
205+
]
206+
const variables = timeVars.concat(
207+
getWindowPeriodVariableFromVariables(text, timeVars) || []
208+
)
209+
monacoEditor = (
210+
<FluxMonacoEditor
211+
variables={variables}
212+
script={text}
213+
onChangeScript={setQuery}
214+
onSubmitScript={submit}
215+
/>
216+
)
217+
}
187218

188219
return (
189220
<DraggableResizer
@@ -208,20 +239,7 @@ const ResultsPane: FC = () => {
208239
/>
209240
}
210241
>
211-
{resource?.language == LanguageType.SQL ? (
212-
<SqlEditorMonaco
213-
script={text}
214-
onChangeScript={setQuery}
215-
onSubmitScript={submit}
216-
/>
217-
) : (
218-
<FluxMonacoEditor
219-
variables={variables}
220-
script={text}
221-
onChangeScript={setQuery}
222-
onSubmitScript={submit}
223-
/>
224-
)}
242+
{monacoEditor}
225243
</Suspense>
226244
</div>
227245
</div>
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
import * as allMonaco from 'monaco-editor/esm/vs/editor/editor.api'
2+
3+
// This file is a modified version of
4+
// https://github.com/microsoft/monaco-editor/blob/136ce723f73b8bd284565c0b7d6d851b52161015/src/basic-languages/pgsql/pgsql.ts
5+
6+
// The rules are referenced from the InfluxQL doc
7+
// https://docs.influxdata.com/influxdb/v2.6/reference/syntax/influxql/spec/
8+
9+
export const conf: allMonaco.languages.LanguageConfiguration = {
10+
comments: {
11+
lineComment: '--',
12+
blockComment: ['/*', '*/'],
13+
},
14+
brackets: [
15+
['{', '}'],
16+
['[', ']'],
17+
['(', ')'],
18+
],
19+
autoClosingPairs: [
20+
{open: '{', close: '}'},
21+
{open: '[', close: ']'},
22+
{open: '(', close: ')'},
23+
{open: '"', close: '"'},
24+
{open: "'", close: "'"},
25+
],
26+
surroundingPairs: [
27+
{open: '{', close: '}'},
28+
{open: '[', close: ']'},
29+
{open: '(', close: ')'},
30+
{open: '"', close: '"'},
31+
{open: "'", close: "'"},
32+
],
33+
}
34+
35+
export const language = <allMonaco.languages.IMonarchLanguage>{
36+
defaultToken: '',
37+
tokenPostfix: '.influxql',
38+
ignoreCase: true,
39+
40+
brackets: [
41+
{open: '[', close: ']', token: 'delimiter.square'},
42+
{open: '(', close: ')', token: 'delimiter.parenthesis'},
43+
],
44+
45+
keywords: [
46+
// InfluxQL Keywords
47+
// https://github.com/influxdata/influxql/blob/c87e0d6a754823381b1fc1016f40a40c86b23090/token.go#L68-L143
48+
'ALL',
49+
'ALTER',
50+
'ANALYZE',
51+
'ANY',
52+
'AS',
53+
'ASC',
54+
'BEGIN',
55+
'BY',
56+
'CARDINALITY',
57+
'CREATE',
58+
'CONTINUOUS',
59+
'DATABASE',
60+
'DATABASES',
61+
'DEFAULT',
62+
'DELETE',
63+
'DESC',
64+
'DESTINATIONS',
65+
'DIAGNOSTICS',
66+
'DISTINCT',
67+
'DROP',
68+
'DURATION',
69+
'END',
70+
'EVERY',
71+
'EXACT',
72+
'EXPLAIN',
73+
'FIELD',
74+
'FOR',
75+
'FROM',
76+
'GRANT',
77+
'GRANTS',
78+
'GROUP',
79+
'GROUPS',
80+
'IN',
81+
'INF',
82+
'INSERT',
83+
'INTO',
84+
'KEY',
85+
'KEYS',
86+
'KILL',
87+
'LIMIT',
88+
'MEASUREMENT',
89+
'MEASUREMENTS',
90+
'NAME',
91+
'OFFSET',
92+
'ON',
93+
'ORDER',
94+
'PASSWORD',
95+
'POLICY',
96+
'POLICIES',
97+
'PRIVILEGES',
98+
'QUERIES',
99+
'QUERY',
100+
'READ',
101+
'REPLICATION',
102+
'RESAMPLE',
103+
'RETENTION',
104+
'REVOKE',
105+
'SELECT',
106+
'SERIES',
107+
'SET',
108+
'SHOW',
109+
'SHARD',
110+
'SHARDS',
111+
'SLIMIT',
112+
'SOFFSET',
113+
'STATS',
114+
'SUBSCRIPTION',
115+
'SUBSCRIPTIONS',
116+
'TAG',
117+
'TO',
118+
'USER',
119+
'USERS',
120+
'VALUES',
121+
'WHERE',
122+
'WITH',
123+
'WRITE',
124+
],
125+
operators: [
126+
// InfluxQL Operators
127+
// https://github.com/influxdata/influxql/blob/c87e0d6a754823381b1fc1016f40a40c86b23090/token.go#L36-L55
128+
'ADD', // +
129+
'SUB', // -
130+
'MUL', // *
131+
'DIV', // /
132+
'MOD', // %
133+
'BITWISE_AND', // &
134+
'BITWISE_OR', // |
135+
'BITWISE_XOR', // ^
136+
'AND', // AND
137+
'OR', // OR
138+
'EQ', // =
139+
'NEQ', // !=
140+
'EQREGEX', // =~
141+
'NEQREGEX', // !~
142+
'LT', // <
143+
'LTE', // <=
144+
'GT', // >
145+
'GTE', // >=
146+
],
147+
illegal: [
148+
// ILLEGAL Token, EOF, WS are Special InfluxQL tokens
149+
// https://github.com/influxdata/influxql/blob/c87e0d6a754823381b1fc1016f40a40c86b23090/token.go#L13-L16
150+
'ILLEGAL',
151+
'EOF',
152+
'WS',
153+
'COMMENT',
154+
],
155+
literals: [
156+
// InfluxQL literal tokens
157+
// https://github.com/influxdata/influxql/blob/c87e0d6a754823381b1fc1016f40a40c86b23090/token.go#L20-L31
158+
'IDENT', // main
159+
'BOUNDPARAM', // $param
160+
'NUMBER', // 12345.67
161+
'INTEGER', // 12345
162+
'DURATIONVAL', // 13h
163+
'STRING', // "abc"
164+
'BADSTRING', // "abc
165+
'BADESCAPE', // \q
166+
'TRUE', // true
167+
'FALSE', // false
168+
'REGEX', // Regular expressions
169+
'BADREGEX', // `.*
170+
],
171+
builtinFunctions: [
172+
// InfluxQL functions
173+
// https://docs.influxdata.com/influxdb/v2.6/query-data/influxql/functions/
174+
175+
// Aggregates
176+
'COUNT',
177+
'DISTINCT',
178+
'INTEGRAL',
179+
'MEAN',
180+
'MEDIAN',
181+
'MODE',
182+
'SPREAD',
183+
'STDDEV',
184+
'SUM',
185+
// Selectors
186+
'BOTTOM',
187+
'FIRST',
188+
'LAST',
189+
'MAX',
190+
'MIN',
191+
'PERCENTILE',
192+
'SAMPLE',
193+
'TOP',
194+
// Transformations
195+
'ABS',
196+
'ACOS',
197+
'ASIN',
198+
'ATAN',
199+
'ATAN2',
200+
'CEIL',
201+
'COS',
202+
'CUMULATIVE_SUM',
203+
'DERIVATIVE',
204+
'DIFFERENCE',
205+
'ELAPSED',
206+
'EXP',
207+
'FLOOR',
208+
'HISTOGRAM',
209+
'LN',
210+
'LOG',
211+
'LOG2',
212+
'LOG10',
213+
'MOVING_AVERAGE',
214+
'NON_NEGATIVE_DERIVATIVE',
215+
'NON_NEGATIVE_DIFFERENCE',
216+
'POW',
217+
'ROUND',
218+
'SIN',
219+
'SQRT',
220+
'TAN',
221+
// Technical analysis
222+
'HOLT_WINTERS',
223+
'CHANDE_MOMENTUM_OSCILLATOR',
224+
'EXPONENTIAL_MOVING_AVERAGE',
225+
'DOUBLE_EXPONENTIAL_MOVING_AVERAGE',
226+
'KAUFMANS_EFFICIENCY_RATIO',
227+
'KAUFMANS_ADAPTIVE_MOVING_AVERAGE',
228+
'TRIPLE_EXPONENTIAL_MOVING_AVERAGE',
229+
'TRIPLE_EXPONENTIAL_DERIVATIVE',
230+
'RELATIVE_STRENGTH_INDEX',
231+
],
232+
tokenizer: {
233+
root: [
234+
// the order of the following operators in increasing precedence
235+
{include: '@comments'},
236+
{include: '@whitespace'},
237+
[/([0-9]+(y|mo|w|d|h|m|s|ms|us|µs|ns))+/, 'literal.duration'],
238+
{include: '@numbers'},
239+
{include: '@strings'},
240+
{include: '@complexIdentifiers'},
241+
{include: '@delimiters'},
242+
[/(TRUE|FALSE)/, 'literal.boolean'],
243+
[
244+
/[\w@#$]+/,
245+
{
246+
cases: {
247+
'@keywords': 'keyword',
248+
'@operators': 'operator',
249+
'@illegal': 'illegal',
250+
'@literals': 'predefined',
251+
'@builtinFunctions': 'builtin.function',
252+
'@default': 'identifier',
253+
},
254+
},
255+
],
256+
[/[<>=!%&+\-*/|~^]/, 'operator'],
257+
],
258+
comments: [
259+
[/--+.*/, 'comment'],
260+
[/\/\*/, {token: 'comment.quote', next: '@comment'}],
261+
],
262+
comment: [
263+
[/[^*/]+/, 'comment'],
264+
// InfluxQL does not support nested multi-line comments
265+
// https://docs.influxdata.com/influxdb/v2.6/reference/syntax/influxql/spec/#comments
266+
// [/\/\*/, { token: 'comment.quote', next: '@push' }], // nested comment not allowed :-(
267+
[/\*\//, {token: 'comment.quote', next: '@pop'}],
268+
[/./, 'comment'],
269+
],
270+
whitespace: [[/\s+/, 'white']],
271+
numbers: [
272+
[/0[xX][0-9a-fA-F]*/, 'literal.number'],
273+
[/[$][+-]*\d*(\.\d*)?/, 'literal.number'],
274+
[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/, 'literal.number'],
275+
],
276+
strings: [
277+
// can contain escaped ' (i.e. \')
278+
// https://docs.influxdata.com/influxdb/v2.6/reference/syntax/influxql/spec/#strings
279+
[/'((.*)[\\](.*))+'$/, 'literal.string'],
280+
[/'/, {token: 'literal.string', next: '@string'}],
281+
],
282+
string: [
283+
[/[^']+/, 'literal.string'],
284+
[/''/, 'literal.string'],
285+
[/'/, {token: 'literal.string', next: '@pop'}],
286+
],
287+
complexIdentifiers: [
288+
// can contain escaped " (i.e. \")
289+
// https://docs.influxdata.com/influxdb/v2.6/reference/syntax/influxql/spec/#identifiers
290+
[/"((.*)[\\](.*))+"$/, 'identifier.quote'],
291+
[/"/, {token: 'identifier.quote', next: '@quotedIdentifier'}],
292+
],
293+
quotedIdentifier: [
294+
[/[^"]+/, 'identifier.quote'],
295+
[/""/, 'identifier.quote'],
296+
[/"/, {token: 'identifier.quote', next: '@pop'}],
297+
],
298+
delimiters: [
299+
[/[,:;.]/, 'delimiter'],
300+
[/[\[\]]/, 'delimiter.square'],
301+
[/[()]/, 'delimiter.parenthesis'],
302+
],
303+
},
304+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import {EditorType} from 'src/types'
2+
3+
export function submit(editor: EditorType, submitFn: () => any) {
4+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
5+
submitFn()
6+
})
7+
}

0 commit comments

Comments
 (0)