UNPKG

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