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 pattern: RegExp(character),
103 greedy: true,
104 alias: 'character'
105 }
106 ],
107 'class-name': [
108 {
109 // Using static
110 // using static System.Math;
111 pattern: re(/(\busing\s+static\s+)<<0>>(?=\s*;)/.source, [identifier]),
112 lookbehind: true,
113 inside: typeInside
114 },
115 {
116 // Using alias (type)
117 // using Project = PC.MyCompany.Project;
118 pattern: re(/(\busing\s+<<0>>\s*=\s*)<<1>>(?=\s*;)/.source, [name, typeExpression]),
119 lookbehind: true,
120 inside: typeInside
121 },
122 {
123 // Using alias (alias)
124 // using Project = PC.MyCompany.Project;
125 pattern: re(/(\busing\s+)<<0>>(?=\s*=)/.source, [name]),
126 lookbehind: true
127 },
128 {
129 // Type declarations
130 // class Foo<A, B>
131 // interface Foo<out A, B>
132 pattern: re(/(\b<<0>>\s+)<<1>>/.source, [typeDeclarationKeywords, genericName]),
133 lookbehind: true,
134 inside: typeInside
135 },
136 {
137 // Single catch exception declaration
138 // catch(Foo)
139 // (things like catch(Foo e) is covered by variable declaration)
140 pattern: re(/(\bcatch\s*\(\s*)<<0>>/.source, [identifier]),
141 lookbehind: true,
142 inside: typeInside
143 },
144 {
145 // Name of the type parameter of generic constraints
146 // where Foo : class
147 pattern: re(/(\bwhere\s+)<<0>>/.source, [name]),
148 lookbehind: true
149 },
150 {
151 // Casts and checks via as and is.
152 // as Foo<A>, is Bar<B>
153 // (things like if(a is Foo b) is covered by variable declaration)
154 pattern: re(/(\b(?:is(?:\s+not)?|as)\s+)<<0>>/.source, [typeExpressionWithoutTuple]),
155 lookbehind: true,
156 inside: typeInside
157 },
158 {
159 // Variable, field and parameter declaration
160 // (Foo bar, Bar baz, Foo[,,] bay, Foo<Bar, FooBar<Bar>> bax)
161 pattern: re(/\b<<0>>(?=\s+(?!<<1>>|with\s*\{)<<2>>(?:\s*[=,;:{)\]]|\s+(?:in|when)\b))/.source, [typeExpression, nonContextualKeywords, name]),
162 inside: typeInside
163 }
164 ],
165 'keyword': keywords,
166 // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/lexical-structure#literals
167 'number': /(?:\b0(?:x[\da-f_]*[\da-f]|b[01_]*[01])|(?:\B\.\d+(?:_+\d+)*|\b\d+(?:_+\d+)*(?:\.\d+(?:_+\d+)*)?)(?:e[-+]?\d+(?:_+\d+)*)?)(?:ul|lu|[dflmu])?\b/i,
168 'operator': />>=?|<<=?|[-=]>|([-+&|])\1|~|\?\?=?|[-+*/%&|^!=<>]=?/,
169 'punctuation': /\?\.?|::|[{}[\];(),.:]/
170 });
171
172 Prism.languages.insertBefore('csharp', 'number', {
173 'range': {
174 pattern: /\.\./,
175 alias: 'operator'
176 }
177 });
178
179 Prism.languages.insertBefore('csharp', 'punctuation', {
180 'named-parameter': {
181 pattern: re(/([(,]\s*)<<0>>(?=\s*:)/.source, [name]),
182 lookbehind: true,
183 alias: 'punctuation'
184 }
185 });
186
187 Prism.languages.insertBefore('csharp', 'class-name', {
188 'namespace': {
189 // namespace Foo.Bar {}
190 // using Foo.Bar;
191 pattern: re(/(\b(?:namespace|using)\s+)<<0>>(?:\s*\.\s*<<0>>)*(?=\s*[;{])/.source, [name]),
192 lookbehind: true,
193 inside: {
194 'punctuation': /\./
195 }
196 },
197 'type-expression': {
198 // default(Foo), typeof(Foo<Bar>), sizeof(int)
199 pattern: re(/(\b(?:default|typeof|sizeof)\s*\(\s*(?!\s))(?:[^()\s]|\s(?!\s)|<<0>>)*(?=\s*\))/.source, [nestedRound]),
200 lookbehind: true,
201 alias: 'class-name',
202 inside: typeInside
203 },
204 'return-type': {
205 // Foo<Bar> ForBar(); Foo IFoo.Bar() => 0
206 // int this[int index] => 0; T IReadOnlyList<T>.this[int index] => this[index];
207 // int Foo => 0; int Foo { get; set } = 0;
208 pattern: re(/<<0>>(?=\s+(?:<<1>>\s*(?:=>|[({]|\.\s*this\s*\[)|this\s*\[))/.source, [typeExpression, identifier]),
209 inside: typeInside,
210 alias: 'class-name'
211 },
212 'constructor-invocation': {
213 // new List<Foo<Bar[]>> { }
214 pattern: re(/(\bnew\s+)<<0>>(?=\s*[[({])/.source, [typeExpression]),
215 lookbehind: true,
216 inside: typeInside,
217 alias: 'class-name'
218 },
219 /*'explicit-implementation': {
220 // int IFoo<Foo>.Bar => 0; void IFoo<Foo<Foo>>.Foo<T>();
221 pattern: replace(/\b<<0>>(?=\.<<1>>)/, className, methodOrPropertyDeclaration),
222 inside: classNameInside,
223 alias: 'class-name'
224 },*/
225 'generic-method': {
226 // foo<Bar>()
227 pattern: re(/<<0>>\s*<<1>>(?=\s*\()/.source, [name, generic]),
228 inside: {
229 'function': re(/^<<0>>/.source, [name]),
230 'generic': {
231 pattern: RegExp(generic),
232 alias: 'class-name',
233 inside: typeInside
234 }
235 }
236 },
237 'type-list': {
238 // The list of types inherited or of generic constraints
239 // class Foo<F> : Bar, IList<FooBar>
240 // where F : Bar, IList<int>
241 pattern: re(
242 /\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,
243 [typeDeclarationKeywords, genericName, name, typeExpression, keywords.source, nestedRound, /\bnew\s*\(\s*\)/.source]
244 ),
245 lookbehind: true,
246 inside: {
247 'record-arguments': {
248 pattern: re(/(^(?!new\s*\()<<0>>\s*)<<1>>/.source, [genericName, nestedRound]),
249 lookbehind: true,
250 greedy: true,
251 inside: Prism.languages.csharp
252 },
253 'keyword': keywords,
254 'class-name': {
255 pattern: RegExp(typeExpression),
256 greedy: true,
257 inside: typeInside
258 },
259 'punctuation': /[,()]/
260 }
261 },
262 'preprocessor': {
263 pattern: /(^[\t ]*)#.*/m,
264 lookbehind: true,
265 alias: 'property',
266 inside: {
267 // highlight preprocessor directives as keywords
268 'directive': {
269 pattern: /(#)\b(?:define|elif|else|endif|endregion|error|if|line|nullable|pragma|region|undef|warning)\b/,
270 lookbehind: true,
271 alias: 'keyword'
272 }
273 }
274 }
275 });
276
277 // attributes
278 var regularStringOrCharacter = regularString + '|' + character;
279 var regularStringCharacterOrComment = replace(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source, [regularStringOrCharacter]);
280 var roundExpression = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2);
281
282 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/#attribute-targets
283 var attrTarget = /\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source;
284 var attr = replace(/<<0>>(?:\s*\(<<1>>*\))?/.source, [identifier, roundExpression]);
285
286 Prism.languages.insertBefore('csharp', 'class-name', {
287 'attribute': {
288 // Attributes
289 // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)]
290 pattern: re(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source, [attrTarget, attr]),
291 lookbehind: true,
292 greedy: true,
293 inside: {
294 'target': {
295 pattern: re(/^<<0>>(?=\s*:)/.source, [attrTarget]),
296 alias: 'keyword'
297 },
298 'attribute-arguments': {
299 pattern: re(/\(<<0>>*\)/.source, [roundExpression]),
300 inside: Prism.languages.csharp
301 },
302 'class-name': {
303 pattern: RegExp(identifier),
304 inside: {
305 'punctuation': /\./
306 }
307 },
308 'punctuation': /[:,]/
309 }
310 }
311 });
312
313
314 // string interpolation
315 var formatString = /:[^}\r\n]+/.source;
316 // multi line
317 var mInterpolationRound = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2);
318 var mInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [mInterpolationRound, formatString]);
319 // single line
320 var sInterpolationRound = nested(replace(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<<self>>*\)/.source, [regularStringOrCharacter]), 2);
321 var sInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [sInterpolationRound, formatString]);
322
323 function createInterpolationInside(interpolation, interpolationRound) {
324 return {
325 'interpolation': {
326 pattern: re(/((?:^|[^{])(?:\{\{)*)<<0>>/.source, [interpolation]),
327 lookbehind: true,
328 inside: {
329 'format-string': {
330 pattern: re(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source, [interpolationRound, formatString]),
331 lookbehind: true,
332 inside: {
333 'punctuation': /^:/
334 }
335 },
336 'punctuation': /^\{|\}$/,
337 'expression': {
338 pattern: /[\s\S]+/,
339 alias: 'language-csharp',
340 inside: Prism.languages.csharp
341 }
342 }
343 },
344 'string': /[\s\S]+/
345 };
346 }
347
348 Prism.languages.insertBefore('csharp', 'string', {
349 'interpolation-string': [
350 {
351 pattern: re(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, [mInterpolation]),
352 lookbehind: true,
353 greedy: true,
354 inside: createInterpolationInside(mInterpolation, mInterpolationRound),
355 },
356 {
357 pattern: re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [sInterpolation]),
358 lookbehind: true,
359 greedy: true,
360 inside: createInterpolationInside(sInterpolation, sInterpolationRound),
361 }
362 ]
363 });
364
365}(Prism));
366
367Prism.languages.dotnet = Prism.languages.cs = Prism.languages.csharp;