UNPKG

13.6 kBJavaScriptView Raw
1(function (Prism) {
2
3 /**
4 * Replaces all placeholders "<<n>>" of given pattern with the n-th replacement (zero based).
5 *
6 * Note: This is a simple text based replacement. Be careful when using backreferences!
7 *
8 * @param {string} pattern the given pattern.
9 * @param {string[]} replacements a list of replacement which can be inserted into the given pattern.
10 * @returns {string} the pattern with all placeholders replaced with their corresponding replacements.
11 * @example replace(/a<<0>>a/.source, [/b+/.source]) === /a(?:b+)a/.source
12 */
13 function replace(pattern, replacements) {
14 return pattern.replace(/<<(\d+)>>/g, function (m, index) {
15 return '(?:' + replacements[+index] + ')';
16 });
17 }
18 /**
19 * @param {string} pattern
20 * @param {string[]} replacements
21 * @param {string} [flags]
22 * @returns {RegExp}
23 */
24 function re(pattern, replacements, flags) {
25 return RegExp(replace(pattern, replacements), flags || '');
26 }
27
28 /**
29 * Creates a nested pattern where all occurrences of the string `<<self>>` are replaced with the pattern itself.
30 *
31 * @param {string} pattern
32 * @param {number} depthLog2
33 * @returns {string}
34 */
35 function nested(pattern, depthLog2) {
36 for (var i = 0; i < depthLog2; i++) {
37 pattern = pattern.replace(/<<self>>/g, function () { return '(?:' + pattern + ')'; });
38 }
39 return pattern.replace(/<<self>>/g, '[^\\s\\S]');
40 }
41
42 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
43 var keywordKinds = {
44 // keywords which represent a return or variable type
45 type: 'bool byte char decimal double dynamic float int long object sbyte short string uint ulong ushort var void',
46 // keywords which are used to declare a type
47 typeDeclaration: 'class enum interface record struct',
48 // contextual keywords
49 // ("var" and "dynamic" are missing because they are used like types)
50 contextual: 'add alias and ascending async await by descending from(?=\\s*(?:\\w|$)) get global group into init(?=\\s*;) join let nameof not notnull on or orderby partial remove select set unmanaged value when where with(?=\\s*{)',
51 // all other keywords
52 other: 'abstract as base break case catch checked const continue default delegate do else event explicit extern finally fixed for foreach goto if implicit in internal is lock namespace new null operator out override params private protected public readonly ref return sealed sizeof stackalloc static switch this throw try typeof unchecked unsafe using virtual volatile while yield'
53 };
54
55 // keywords
56 function keywordsToPattern(words) {
57 return '\\b(?:' + words.trim().replace(/ /g, '|') + ')\\b';
58 }
59 var typeDeclarationKeywords = keywordsToPattern(keywordKinds.typeDeclaration);
60 var keywords = RegExp(keywordsToPattern(keywordKinds.type + ' ' + keywordKinds.typeDeclaration + ' ' + keywordKinds.contextual + ' ' + keywordKinds.other));
61 var nonTypeKeywords = keywordsToPattern(keywordKinds.typeDeclaration + ' ' + keywordKinds.contextual + ' ' + keywordKinds.other);
62 var nonContextualKeywords = keywordsToPattern(keywordKinds.type + ' ' + keywordKinds.typeDeclaration + ' ' + keywordKinds.other);
63
64 // types
65 var generic = nested(/<(?:[^<>;=+\-*/%&|^]|<<self>>)*>/.source, 2); // the idea behind the other forbidden characters is to prevent false positives. Same for tupleElement.
66 var nestedRound = nested(/\((?:[^()]|<<self>>)*\)/.source, 2);
67 var name = /@?\b[A-Za-z_]\w*\b/.source;
68 var genericName = replace(/<<0>>(?:\s*<<1>>)?/.source, [name, generic]);
69 var identifier = replace(/(?!<<0>>)<<1>>(?:\s*\.\s*<<1>>)*/.source, [nonTypeKeywords, genericName]);
70 var array = /\[\s*(?:,\s*)*\]/.source;
71 var typeExpressionWithoutTuple = replace(/<<0>>(?:\s*(?:\?\s*)?<<1>>)*(?:\s*\?)?/.source, [identifier, array]);
72 var tupleElement = replace(/[^,()<>[\];=+\-*/%&|^]|<<0>>|<<1>>|<<2>>/.source, [generic, nestedRound, array]);
73 var tuple = replace(/\(<<0>>+(?:,<<0>>+)+\)/.source, [tupleElement]);
74 var typeExpression = replace(/(?:<<0>>|<<1>>)(?:\s*(?:\?\s*)?<<2>>)*(?:\s*\?)?/.source, [tuple, identifier, array]);
75
76 var typeInside = {
77 'keyword': keywords,
78 'punctuation': /[<>()?,.:[\]]/
79 };
80
81 // strings & characters
82 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#character-literals
83 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#string-literals
84 var character = /'(?:[^\r\n'\\]|\\.|\\[Uux][\da-fA-F]{1,8})'/.source; // simplified pattern
85 var regularString = /"(?:\\.|[^\\"\r\n])*"/.source;
86 var verbatimString = /@"(?:""|\\[\s\S]|[^\\"])*"(?!")/.source;
87
88
89 Prism.languages.csharp = Prism.languages.extend('clike', {
90 'string': [
91 {
92 pattern: re(/(^|[^$\\])<<0>>/.source, [verbatimString]),
93 lookbehind: true,
94 greedy: true
95 },
96 {
97 pattern: re(/(^|[^@$\\])<<0>>/.source, [regularString]),
98 lookbehind: true,
99 greedy: true
100 }
101 ],
102 'class-name': [
103 {
104 // Using static
105 // using static System.Math;
106 pattern: re(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source, [identifier]),
107 lookbehind: true,
108 inside: typeInside
109 },
110 {
111 // Using alias (type)
112 // using Project = PC.MyCompany.Project;
113 pattern: re(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source, [name, typeExpression]),
114 lookbehind: true,
115 inside: typeInside
116 },
117 {
118 // Using alias (alias)
119 // using Project = PC.MyCompany.Project;
120 pattern: re(/(\busing\s+)<<0>>(?=\s*=)/.source, [name]),
121 lookbehind: true
122 },
123 {
124 // Type declarations
125 // class Foo<A, B>
126 // interface Foo<out A, B>
127 pattern: re(/(\b<<0>>\s+)<<1>>/.source, [typeDeclarationKeywords, genericName]),
128 lookbehind: true,
129 inside: typeInside
130 },
131 {
132 // Single catch exception declaration
133 // catch(Foo)
134 // (things like catch(Foo e) is covered by variable declaration)
135 pattern: re(/(\bcatch\s*\(\s*)<<0>>/.source, [identifier]),
136 lookbehind: true,
137 inside: typeInside
138 },
139 {
140 // Name of the type parameter of generic constraints
141 // where Foo : class
142 pattern: re(/(\bwhere\s+)<<0>>/.source, [name]),
143 lookbehind: true
144 },
145 {
146 // Casts and checks via as and is.
147 // as Foo<A>, is Bar<B>
148 // (things like if(a is Foo b) is covered by variable declaration)
149 pattern: re(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source, [typeExpressionWithoutTuple]),
150 lookbehind: true,
151 inside: typeInside
152 },
153 {
154 // Variable, field and parameter declaration
155 // (Foo bar, Bar baz, Foo[,,] bay, Foo<Bar, FooBar<Bar>> bax)
156 pattern: re(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source, [typeExpression, nonContextualKeywords, name]),
157 inside: typeInside
158 }
159 ],
160 'keyword': keywords,
161 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#literals
162 'number': /(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:[dflmu]|lu|ul)?\b/i,
163 'operator': />>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,
164 'punctuation': /\?\.?|::|[{}[\];(),.:]/
165 });
166
167 Prism.languages.insertBefore('csharp', 'number', {
168 'range': {
169 pattern: /\.\./,
170 alias: 'operator'
171 }
172 });
173
174 Prism.languages.insertBefore('csharp', 'punctuation', {
175 'named-parameter': {
176 pattern: re(/([(,]\s*)<<0>>(?=\s*:)/.source, [name]),
177 lookbehind: true,
178 alias: 'punctuation'
179 }
180 });
181
182 Prism.languages.insertBefore('csharp', 'class-name', {
183 'namespace': {
184 // namespace Foo.Bar {}
185 // using Foo.Bar;
186 pattern: re(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source, [name]),
187 lookbehind: true,
188 inside: {
189 'punctuation': /\./
190 }
191 },
192 'type-expression': {
193 // default(Foo), typeof(Foo<Bar>), sizeof(int)
194 pattern: re(/(\b(?:default|sizeof|typeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source, [nestedRound]),
195 lookbehind: true,
196 alias: 'class-name',
197 inside: typeInside
198 },
199 'return-type': {
200 // Foo<Bar> ForBar(); Foo IFoo.Bar() => 0
201 // int this[int index] => 0; T IReadOnlyList<T>.this[int index] => this[index];
202 // int Foo => 0; int Foo { get; set } = 0;
203 pattern: re(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source, [typeExpression, identifier]),
204 inside: typeInside,
205 alias: 'class-name'
206 },
207 'constructor-invocation': {
208 // new List<Foo<Bar[]>> { }
209 pattern: re(/(\bnew\s+)<<0>>(?=\s*[[({])/.source, [typeExpression]),
210 lookbehind: true,
211 inside: typeInside,
212 alias: 'class-name'
213 },
214 /*'explicit-implementation': {
215 // int IFoo<Foo>.Bar => 0; void IFoo<Foo<Foo>>.Foo<T>();
216 pattern: replace(/\b<<0>>(?=\.<<1>>)/, className, methodOrPropertyDeclaration),
217 inside: classNameInside,
218 alias: 'class-name'
219 },*/
220 'generic-method': {
221 // foo<Bar>()
222 pattern: re(/<<0>>\s*<<1>>(?=\s*\()/.source, [name, generic]),
223 inside: {
224 'function': re(/^<<0>>/.source, [name]),
225 'generic': {
226 pattern: RegExp(generic),
227 alias: 'class-name',
228 inside: typeInside
229 }
230 }
231 },
232 'type-list': {
233 // The list of types inherited or of generic constraints
234 // class Foo<F> : Bar, IList<FooBar>
235 // where F : Bar, IList<int>
236 pattern: re(
237 /\b((?:<<0>>\s+<<1>>|record\s+<<1>>\s*<<5>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>|<<1>>\s*<<5>>|<<6>>)(?:\s*,\s*(?:<<3>>|<<4>>|<<6>>))*(?=\s*(?:where|[{;]|=>|$))/.source,
238 [typeDeclarationKeywords, genericName, name, typeExpression, keywords.source, nestedRound, /\bnew\s*\(\s*\)/.source]
239 ),
240 lookbehind: true,
241 inside: {
242 'record-arguments': {
243 pattern: re(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source, [genericName, nestedRound]),
244 lookbehind: true,
245 greedy: true,
246 inside: Prism.languages.csharp
247 },
248 'keyword': keywords,
249 'class-name': {
250 pattern: RegExp(typeExpression),
251 greedy: true,
252 inside: typeInside
253 },
254 'punctuation': /[,()]/
255 }
256 },
257 'preprocessor': {
258 pattern: /(^[\t ]*)#.*/m,
259 lookbehind: true,
260 alias: 'property',
261 inside: {
262 // highlight preprocessor directives as keywords
263 'directive': {
264 pattern: /(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,
265 lookbehind: true,
266 alias: 'keyword'
267 }
268 }
269 }
270 });
271
272 // attributes
273 var regularStringOrCharacter = regularString + '|' + character;
274 var regularStringCharacterOrComment = replace(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source, [regularStringOrCharacter]);
275 var roundExpression = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2);
276
277 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/#attribute-targets
278 var attrTarget = /\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source;
279 var attr = replace(/<<0>>(?:\s*\(<<1>>*\))?/.source, [identifier, roundExpression]);
280
281 Prism.languages.insertBefore('csharp', 'class-name', {
282 'attribute': {
283 // Attributes
284 // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)]
285 pattern: re(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source, [attrTarget, attr]),
286 lookbehind: true,
287 greedy: true,
288 inside: {
289 'target': {
290 pattern: re(/^<<0>>(?=\s*:)/.source, [attrTarget]),
291 alias: 'keyword'
292 },
293 'attribute-arguments': {
294 pattern: re(/\(<<0>>*\)/.source, [roundExpression]),
295 inside: Prism.languages.csharp
296 },
297 'class-name': {
298 pattern: RegExp(identifier),
299 inside: {
300 'punctuation': /\./
301 }
302 },
303 'punctuation': /[:,]/
304 }
305 }
306 });
307
308
309 // string interpolation
310 var formatString = /:[^}\r\n]+/.source;
311 // multi line
312 var mInterpolationRound = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2);
313 var mInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [mInterpolationRound, formatString]);
314 // single line
315 var sInterpolationRound = nested(replace(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<<self>>*\)/.source, [regularStringOrCharacter]), 2);
316 var sInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [sInterpolationRound, formatString]);
317
318 function createInterpolationInside(interpolation, interpolationRound) {
319 return {
320 'interpolation': {
321 pattern: re(/((?:^|[^{])(?:\{\{)*)<<0>>/.source, [interpolation]),
322 lookbehind: true,
323 inside: {
324 'format-string': {
325 pattern: re(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source, [interpolationRound, formatString]),
326 lookbehind: true,
327 inside: {
328 'punctuation': /^:/
329 }
330 },
331 'punctuation': /^\{|\}$/,
332 'expression': {
333 pattern: /[\s\S]+/,
334 alias: 'language-csharp',
335 inside: Prism.languages.csharp
336 }
337 }
338 },
339 'string': /[\s\S]+/
340 };
341 }
342
343 Prism.languages.insertBefore('csharp', 'string', {
344 'interpolation-string': [
345 {
346 pattern: re(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, [mInterpolation]),
347 lookbehind: true,
348 greedy: true,
349 inside: createInterpolationInside(mInterpolation, mInterpolationRound),
350 },
351 {
352 pattern: re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [sInterpolation]),
353 lookbehind: true,
354 greedy: true,
355 inside: createInterpolationInside(sInterpolation, sInterpolationRound),
356 }
357 ],
358 'char': {
359 pattern: RegExp(character),
360 greedy: true
361 }
362 });
363
364 Prism.languages.dotnet = Prism.languages.cs = Prism.languages.csharp;
365
366}(Prism));