Skip to content

Commit d9cc7d2

Browse files
author
Marc Schlaeppi
committed
Add Kusto to all Milkdown code blocks
1 parent 1e4d815 commit d9cc7d2

3 files changed

Lines changed: 232 additions & 225 deletions

File tree

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import { LanguageDescription, LanguageSupport, StreamLanguage } from '@codemirror/language';
2+
import { languages } from '@codemirror/language-data';
3+
import { tags } from '@lezer/highlight';
4+
5+
const kqlControlKeywords = new Set([
6+
'as',
7+
'asc',
8+
'by',
9+
'consume',
10+
'datatable',
11+
'desc',
12+
'distinct',
13+
'evaluate',
14+
'extend',
15+
'facet',
16+
'find',
17+
'fork',
18+
'from',
19+
'getschema',
20+
'in',
21+
'into',
22+
'invoke',
23+
'join',
24+
'let',
25+
'limit',
26+
'lookup',
27+
'make-series',
28+
'materialize',
29+
'mv-apply',
30+
'mv-expand',
31+
'on',
32+
'order',
33+
'parse',
34+
'parse-where',
35+
'partition',
36+
'print',
37+
'project',
38+
'project-away',
39+
'project-keep',
40+
'project-rename',
41+
'project-reorder',
42+
'range',
43+
'render',
44+
'sample',
45+
'sample-distinct',
46+
'search',
47+
'serialize',
48+
'sort',
49+
'step',
50+
'summarize',
51+
'take',
52+
'to',
53+
'top',
54+
'top-nested',
55+
'union',
56+
'where',
57+
'with',
58+
]);
59+
60+
const kqlOperatorKeywords = new Set([
61+
'and',
62+
'between',
63+
'contains',
64+
'contains_cs',
65+
'endswith',
66+
'has',
67+
'has_cs',
68+
'hasprefix',
69+
'hassuffix',
70+
'in',
71+
'in~',
72+
'like',
73+
'matches',
74+
'not',
75+
'or',
76+
'regex',
77+
'startswith',
78+
]);
79+
80+
const kqlBuiltInFunctions = new Set([
81+
'ago',
82+
'arg_max',
83+
'arg_min',
84+
'avg',
85+
'bin',
86+
'case',
87+
'coalesce',
88+
'count',
89+
'countif',
90+
'datetime',
91+
'dcount',
92+
'dcountif',
93+
'extract',
94+
'extract_all',
95+
'floor',
96+
'iff',
97+
'iif',
98+
'isempty',
99+
'isnotempty',
100+
'isnotnull',
101+
'isnull',
102+
'make_list',
103+
'make_set',
104+
'max',
105+
'min',
106+
'next',
107+
'now',
108+
'percentile',
109+
'percentiles',
110+
'prev',
111+
'replace',
112+
'replace_string',
113+
'row_number',
114+
'series_stats',
115+
'split',
116+
'strcat',
117+
'strlen',
118+
'substring',
119+
'sum',
120+
'sumif',
121+
'todatetime',
122+
'todouble',
123+
'toint',
124+
'tolong',
125+
'tolower',
126+
'toreal',
127+
'tostring',
128+
'totimespan',
129+
'toupper',
130+
'trim',
131+
]);
132+
133+
function consumeKqlString(stream, state) {
134+
if (!state.stringQuote) {
135+
state.stringQuote = stream.next();
136+
}
137+
138+
let escaped = false;
139+
while (!stream.eol()) {
140+
const ch = stream.next();
141+
if (escaped) {
142+
escaped = false;
143+
} else if (ch === '\\') {
144+
escaped = true;
145+
} else if (ch === state.stringQuote) {
146+
state.stringQuote = null;
147+
break;
148+
}
149+
}
150+
151+
return 'string';
152+
}
153+
154+
const kqlParser = {
155+
name: 'kusto',
156+
startState: () => ({ stringQuote: null }),
157+
token(stream, state) {
158+
if (state.stringQuote) {
159+
return consumeKqlString(stream, state);
160+
}
161+
162+
if (stream.eatSpace()) {
163+
return null;
164+
}
165+
166+
if (stream.match('//')) {
167+
stream.skipToEnd();
168+
return 'comment';
169+
}
170+
171+
const ch = stream.peek();
172+
if (ch === '"' || ch === "'") {
173+
return consumeKqlString(stream, state);
174+
}
175+
176+
if (stream.match(/^(?:\d+(?:\.\d+)?|\.\d+)(?:ms|[dhms])?\b/i)) {
177+
return 'number';
178+
}
179+
180+
if (stream.match(/^(?:==|!=|<=|>=|=~|!~|[|=+\-*/%<>])/)) {
181+
return 'operator';
182+
}
183+
184+
const word = stream.match(/^[A-Za-z_][A-Za-z0-9_-]*(?:~)?/);
185+
if (word) {
186+
const value = word[0].toLowerCase();
187+
if (value === 'true' || value === 'false') {
188+
return 'bool';
189+
}
190+
if (value === 'null') {
191+
return 'null';
192+
}
193+
if (kqlOperatorKeywords.has(value)) {
194+
return 'operatorKeyword';
195+
}
196+
if (kqlControlKeywords.has(value)) {
197+
return 'keyword';
198+
}
199+
if (kqlBuiltInFunctions.has(value)) {
200+
return 'standardFunction';
201+
}
202+
return null;
203+
}
204+
205+
stream.next();
206+
return null;
207+
},
208+
blankLine(state) {
209+
state.stringQuote = null;
210+
},
211+
tokenTable: {
212+
standardFunction: tags.standard(tags.function(tags.variableName)),
213+
},
214+
languageData: {
215+
commentTokens: { line: '//' },
216+
},
217+
};
218+
219+
export const kustoLanguage = LanguageDescription.of({
220+
name: 'Kusto',
221+
alias: ['kql'],
222+
extensions: ['kql', 'kusto'],
223+
support: new LanguageSupport(StreamLanguage.define(kqlParser)),
224+
});
225+
226+
export const milkdownCodeLanguages = [...languages, kustoLanguage];

0 commit comments

Comments
 (0)