UNPKG

13.2 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 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 get global group into join let nameof not notnull on or orderby partial remove select set unmanaged value when where where',
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>>)<<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*\))|<<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>>|where\s+<<2>>)\s*:\s*)(?:<<3>>|<<4>>)(?:\s*,\s*(?:<<3>>|<<4>>))*(?=\s*(?:where|[{;]|=>|$))/.source,
243 [typeDeclarationKeywords, genericName, name, typeExpression, keywords.source]
244 ),
245 lookbehind: true,
246 inside: {
247 'keyword': keywords,
248 'class-name': {
249 pattern: RegExp(typeExpression),
250 greedy: true,
251 inside: typeInside
252 },
253 'punctuation': /,/
254 }
255 },
256 'preprocessor': {
257 pattern: /(^\s*)#.*/m,
258 lookbehind: true,
259 alias: 'property',
260 inside: {
261 // highlight preprocessor directives as keywords
262 'directive': {
263 pattern: /(\s*#)\b(?:define|elif|else|endif|endregion|error|if|line|pragma|region|undef|warning)\b/,
264 lookbehind: true,
265 alias: 'keyword'
266 }
267 }
268 }
269 });
270
271 // attributes
272 var regularStringOrCharacter = regularString + '|' + character;
273 var regularStringCharacterOrComment = replace(/\/(?![*/])|\/\/[^\r\n]*[\r\n]|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>/.source, [regularStringOrCharacter]);
274 var roundExpression = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2);
275
276 // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/attributes/#attribute-targets
277 var attrTarget = /\b(?:assembly|event|field|method|module|param|property|return|type)\b/.source;
278 var attr = replace(/<<0>>(?:\s*\(<<1>>*\))?/.source, [identifier, roundExpression]);
279
280 Prism.languages.insertBefore('csharp', 'class-name', {
281 'attribute': {
282 // Attributes
283 // [Foo], [Foo(1), Bar(2, Prop = "foo")], [return: Foo(1), Bar(2)], [assembly: Foo(Bar)]
284 pattern: re(/((?:^|[^\s\w>)?])\s*\[\s*)(?:<<0>>\s*:\s*)?<<1>>(?:\s*,\s*<<1>>)*(?=\s*\])/.source, [attrTarget, attr]),
285 lookbehind: true,
286 greedy: true,
287 inside: {
288 'target': {
289 pattern: re(/^<<0>>(?=\s*:)/.source, [attrTarget]),
290 alias: 'keyword'
291 },
292 'attribute-arguments': {
293 pattern: re(/\(<<0>>*\)/.source, [roundExpression]),
294 inside: Prism.languages.csharp
295 },
296 'class-name': {
297 pattern: RegExp(identifier),
298 inside: {
299 'punctuation': /\./
300 }
301 },
302 'punctuation': /[:,]/
303 }
304 }
305 });
306
307
308 // string interpolation
309 var formatString = /:[^}\r\n]+/.source;
310 // multi line
311 var mInterpolationRound = nested(replace(/[^"'/()]|<<0>>|\(<<self>>*\)/.source, [regularStringCharacterOrComment]), 2)
312 var mInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [mInterpolationRound, formatString]);
313 // single line
314 var sInterpolationRound = nested(replace(/[^"'/()]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|<<0>>|\(<<self>>*\)/.source, [regularStringOrCharacter]), 2)
315 var sInterpolation = replace(/\{(?!\{)(?:(?![}:])<<0>>)*<<1>>?\}/.source, [sInterpolationRound, formatString]);
316
317 function createInterpolationInside(interpolation, interpolationRound) {
318 return {
319 'interpolation': {
320 pattern: re(/((?:^|[^{])(?:\{\{)*)<<0>>/.source, [interpolation]),
321 lookbehind: true,
322 inside: {
323 'format-string': {
324 pattern: re(/(^\{(?:(?![}:])<<0>>)*)<<1>>(?=\}$)/.source, [interpolationRound, formatString]),
325 lookbehind: true,
326 inside: {
327 'punctuation': /^:/
328 }
329 },
330 'punctuation': /^\{|\}$/,
331 'expression': {
332 pattern: /[\s\S]+/,
333 alias: 'language-csharp',
334 inside: Prism.languages.csharp
335 }
336 }
337 },
338 'string': /[\s\S]+/
339 };
340 }
341
342 Prism.languages.insertBefore('csharp', 'string', {
343 'interpolation-string': [
344 {
345 pattern: re(/(^|[^\\])(?:\$@|@\$)"(?:""|\\[\s\S]|\{\{|<<0>>|[^\\{"])*"/.source, [mInterpolation]),
346 lookbehind: true,
347 greedy: true,
348 inside: createInterpolationInside(mInterpolation, mInterpolationRound),
349 },
350 {
351 pattern: re(/(^|[^@\\])\$"(?:\\.|\{\{|<<0>>|[^\\"{])*"/.source, [sInterpolation]),
352 lookbehind: true,
353 greedy: true,
354 inside: createInterpolationInside(sInterpolation, sInterpolationRound),
355 }
356 ]
357 });
358
359}(Prism));
360
361Prism.languages.dotnet = Prism.languages.cs = Prism.languages.csharp;