prism-csharp.js 13 KB

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