You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

575 lines
16 KiB

  1. // Copyright (c) 2018 Siegfried Pammer
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using ICSharpCode.AvalonEdit.Highlighting;
  21. using ICSharpCode.Decompiler.CSharp;
  22. using ICSharpCode.Decompiler.CSharp.OutputVisitor;
  23. using ICSharpCode.Decompiler.CSharp.Syntax;
  24. using ICSharpCode.Decompiler.IL;
  25. using ICSharpCode.Decompiler.TypeSystem;
  26. using ICSharpCode.ILSpyX.Extensions;
  27. namespace ICSharpCode.ILSpy
  28. {
  29. class CSharpHighlightingTokenWriter : DecoratingTokenWriter
  30. {
  31. HighlightingColor visibilityKeywordsColor;
  32. HighlightingColor namespaceKeywordsColor;
  33. HighlightingColor structureKeywordsColor;
  34. HighlightingColor gotoKeywordsColor;
  35. HighlightingColor queryKeywordsColor;
  36. HighlightingColor exceptionKeywordsColor;
  37. HighlightingColor checkedKeywordColor;
  38. HighlightingColor unsafeKeywordsColor;
  39. HighlightingColor valueTypeKeywordsColor;
  40. HighlightingColor referenceTypeKeywordsColor;
  41. HighlightingColor operatorKeywordsColor;
  42. HighlightingColor parameterModifierColor;
  43. HighlightingColor modifiersColor;
  44. HighlightingColor accessorKeywordsColor;
  45. HighlightingColor attributeKeywordsColor;
  46. HighlightingColor referenceTypeColor;
  47. HighlightingColor valueTypeColor;
  48. HighlightingColor interfaceTypeColor;
  49. HighlightingColor enumerationTypeColor;
  50. HighlightingColor typeParameterTypeColor;
  51. HighlightingColor delegateTypeColor;
  52. HighlightingColor methodCallColor;
  53. HighlightingColor methodDeclarationColor;
  54. HighlightingColor fieldDeclarationColor;
  55. HighlightingColor fieldAccessColor;
  56. HighlightingColor propertyDeclarationColor;
  57. HighlightingColor propertyAccessColor;
  58. HighlightingColor eventDeclarationColor;
  59. HighlightingColor eventAccessColor;
  60. HighlightingColor variableColor;
  61. HighlightingColor parameterColor;
  62. HighlightingColor valueKeywordColor;
  63. HighlightingColor thisKeywordColor;
  64. HighlightingColor trueKeywordColor;
  65. HighlightingColor typeKeywordsColor;
  66. public RichTextModel HighlightingModel { get; } = new RichTextModel();
  67. public CSharpHighlightingTokenWriter(TokenWriter decoratedWriter, ISmartTextOutput textOutput = null, ILocatable locatable = null)
  68. : base(decoratedWriter)
  69. {
  70. var highlighting = HighlightingManager.Instance.GetDefinition("C#");
  71. this.locatable = locatable;
  72. this.textOutput = textOutput;
  73. this.visibilityKeywordsColor = highlighting.GetNamedColor("Visibility");
  74. this.namespaceKeywordsColor = highlighting.GetNamedColor("NamespaceKeywords");
  75. this.structureKeywordsColor = highlighting.GetNamedColor("Keywords");
  76. this.gotoKeywordsColor = highlighting.GetNamedColor("GotoKeywords");
  77. this.queryKeywordsColor = highlighting.GetNamedColor("QueryKeywords");
  78. this.exceptionKeywordsColor = highlighting.GetNamedColor("ExceptionKeywords");
  79. this.checkedKeywordColor = highlighting.GetNamedColor("CheckedKeyword");
  80. this.unsafeKeywordsColor = highlighting.GetNamedColor("UnsafeKeywords");
  81. this.valueTypeKeywordsColor = highlighting.GetNamedColor("ValueTypeKeywords");
  82. this.referenceTypeKeywordsColor = highlighting.GetNamedColor("ReferenceTypeKeywords");
  83. this.operatorKeywordsColor = highlighting.GetNamedColor("OperatorKeywords");
  84. this.parameterModifierColor = highlighting.GetNamedColor("ParameterModifiers");
  85. this.modifiersColor = highlighting.GetNamedColor("Modifiers");
  86. this.accessorKeywordsColor = highlighting.GetNamedColor("GetSetAddRemove");
  87. this.referenceTypeColor = highlighting.GetNamedColor("ReferenceTypes");
  88. this.valueTypeColor = highlighting.GetNamedColor("ValueTypes");
  89. this.interfaceTypeColor = highlighting.GetNamedColor("InterfaceTypes");
  90. this.enumerationTypeColor = highlighting.GetNamedColor("EnumTypes");
  91. this.typeParameterTypeColor = highlighting.GetNamedColor("TypeParameters");
  92. this.delegateTypeColor = highlighting.GetNamedColor("DelegateTypes");
  93. this.methodDeclarationColor = highlighting.GetNamedColor("MethodDeclaration");
  94. this.methodCallColor = highlighting.GetNamedColor("MethodCall");
  95. this.fieldDeclarationColor = highlighting.GetNamedColor("FieldDeclaration");
  96. this.fieldAccessColor = highlighting.GetNamedColor("FieldAccess");
  97. this.propertyDeclarationColor = highlighting.GetNamedColor("PropertyDeclaration");
  98. this.propertyAccessColor = highlighting.GetNamedColor("PropertyAccess");
  99. this.eventDeclarationColor = highlighting.GetNamedColor("EventDeclaration");
  100. this.eventAccessColor = highlighting.GetNamedColor("EventAccess");
  101. this.variableColor = highlighting.GetNamedColor("Variable");
  102. this.parameterColor = highlighting.GetNamedColor("Parameter");
  103. this.valueKeywordColor = highlighting.GetNamedColor("NullOrValueKeywords");
  104. this.thisKeywordColor = highlighting.GetNamedColor("ThisOrBaseReference");
  105. this.trueKeywordColor = highlighting.GetNamedColor("TrueFalse");
  106. this.typeKeywordsColor = highlighting.GetNamedColor("TypeKeywords");
  107. this.attributeKeywordsColor = highlighting.GetNamedColor("AttributeKeywords");
  108. //this.externAliasKeywordColor = ...;
  109. }
  110. public override void WriteKeyword(Role role, string keyword)
  111. {
  112. HighlightingColor color = null;
  113. switch (keyword)
  114. {
  115. case "namespace":
  116. case "using":
  117. if (role == UsingStatement.UsingKeywordRole)
  118. color = structureKeywordsColor;
  119. else
  120. color = namespaceKeywordsColor;
  121. break;
  122. case "this":
  123. case "base":
  124. color = thisKeywordColor;
  125. break;
  126. case "true":
  127. case "false":
  128. color = trueKeywordColor;
  129. break;
  130. case "public":
  131. case "internal":
  132. case "protected":
  133. case "private":
  134. color = visibilityKeywordsColor;
  135. break;
  136. case "if":
  137. case "else":
  138. case "switch":
  139. case "case":
  140. case "default":
  141. case "while":
  142. case "do":
  143. case "for":
  144. case "foreach":
  145. case "lock":
  146. case "await":
  147. color = structureKeywordsColor;
  148. break;
  149. case "where":
  150. if (nodeStack.PeekOrDefault() is QueryClause)
  151. color = queryKeywordsColor;
  152. else
  153. color = structureKeywordsColor;
  154. break;
  155. case "in":
  156. if (nodeStack.PeekOrDefault() is ForeachStatement)
  157. color = structureKeywordsColor;
  158. else if (nodeStack.PeekOrDefault() is QueryClause)
  159. color = queryKeywordsColor;
  160. else
  161. color = parameterModifierColor;
  162. break;
  163. case "as":
  164. case "is":
  165. case "new":
  166. case "sizeof":
  167. case "typeof":
  168. case "nameof":
  169. case "stackalloc":
  170. color = typeKeywordsColor;
  171. break;
  172. case "with":
  173. if (role == WithInitializerExpression.WithKeywordRole)
  174. color = typeKeywordsColor;
  175. break;
  176. case "try":
  177. case "throw":
  178. case "catch":
  179. case "finally":
  180. color = exceptionKeywordsColor;
  181. break;
  182. case "when":
  183. if (role == CatchClause.WhenKeywordRole)
  184. color = exceptionKeywordsColor;
  185. break;
  186. case "get":
  187. case "set":
  188. case "add":
  189. case "remove":
  190. case "init":
  191. if (role == PropertyDeclaration.GetKeywordRole ||
  192. role == PropertyDeclaration.SetKeywordRole ||
  193. role == PropertyDeclaration.InitKeywordRole ||
  194. role == CustomEventDeclaration.AddKeywordRole ||
  195. role == CustomEventDeclaration.RemoveKeywordRole)
  196. color = accessorKeywordsColor;
  197. break;
  198. case "abstract":
  199. case "const":
  200. case "event":
  201. case "extern":
  202. case "override":
  203. case "sealed":
  204. case "static":
  205. case "virtual":
  206. case "volatile":
  207. case "async":
  208. case "partial":
  209. color = modifiersColor;
  210. break;
  211. case "readonly":
  212. if (role == ComposedType.ReadonlyRole)
  213. color = parameterModifierColor;
  214. else
  215. color = modifiersColor;
  216. break;
  217. case "checked":
  218. case "unchecked":
  219. color = checkedKeywordColor;
  220. break;
  221. case "fixed":
  222. case "unsafe":
  223. color = unsafeKeywordsColor;
  224. break;
  225. case "enum":
  226. case "struct":
  227. color = valueTypeKeywordsColor;
  228. break;
  229. case "class":
  230. case "interface":
  231. case "delegate":
  232. color = referenceTypeKeywordsColor;
  233. break;
  234. case "record":
  235. color = role == Roles.RecordKeyword ? referenceTypeKeywordsColor : valueTypeKeywordsColor;
  236. break;
  237. case "select":
  238. case "group":
  239. case "by":
  240. case "into":
  241. case "from":
  242. case "orderby":
  243. case "let":
  244. case "join":
  245. case "on":
  246. case "equals":
  247. if (nodeStack.PeekOrDefault() is QueryClause)
  248. color = queryKeywordsColor;
  249. break;
  250. case "ascending":
  251. case "descending":
  252. if (nodeStack.PeekOrDefault() is QueryOrdering)
  253. color = queryKeywordsColor;
  254. break;
  255. case "explicit":
  256. case "implicit":
  257. case "operator":
  258. color = operatorKeywordsColor;
  259. break;
  260. case "params":
  261. case "ref":
  262. case "out":
  263. case "scoped":
  264. color = parameterModifierColor;
  265. break;
  266. case "break":
  267. case "continue":
  268. case "goto":
  269. case "yield":
  270. case "return":
  271. color = gotoKeywordsColor;
  272. break;
  273. }
  274. if (nodeStack.PeekOrDefault() is AttributeSection)
  275. color = attributeKeywordsColor;
  276. if (color != null)
  277. {
  278. BeginSpan(color);
  279. }
  280. base.WriteKeyword(role, keyword);
  281. if (color != null)
  282. {
  283. EndSpan();
  284. }
  285. }
  286. public override void WritePrimitiveType(string type)
  287. {
  288. HighlightingColor color = null;
  289. switch (type)
  290. {
  291. case "new":
  292. case "notnull":
  293. // Not sure if reference type or value type
  294. color = referenceTypeKeywordsColor;
  295. break;
  296. case "bool":
  297. case "byte":
  298. case "char":
  299. case "decimal":
  300. case "double":
  301. case "enum":
  302. case "float":
  303. case "int":
  304. case "long":
  305. case "sbyte":
  306. case "short":
  307. case "struct":
  308. case "uint":
  309. case "ushort":
  310. case "ulong":
  311. case "unmanaged":
  312. case "nint":
  313. case "nuint":
  314. color = valueTypeKeywordsColor;
  315. break;
  316. case "class":
  317. case "object":
  318. case "string":
  319. case "void":
  320. case "dynamic":
  321. color = referenceTypeKeywordsColor;
  322. break;
  323. }
  324. if (color != null)
  325. {
  326. BeginSpan(color);
  327. }
  328. base.WritePrimitiveType(type);
  329. if (color != null)
  330. {
  331. EndSpan();
  332. }
  333. }
  334. public override void WriteIdentifier(Identifier identifier)
  335. {
  336. HighlightingColor color = null;
  337. if (identifier.Parent?.GetResolveResult() is ILVariableResolveResult rr)
  338. {
  339. if (rr.Variable.Kind == VariableKind.Parameter)
  340. {
  341. if (identifier.Name == "value"
  342. && identifier.Ancestors.OfType<Accessor>().FirstOrDefault() is { } accessor
  343. && accessor.Role != PropertyDeclaration.GetterRole)
  344. {
  345. color = valueKeywordColor;
  346. }
  347. else
  348. {
  349. color = parameterColor;
  350. }
  351. }
  352. else
  353. {
  354. color = variableColor;
  355. }
  356. }
  357. if (identifier.Parent is AstType)
  358. {
  359. switch (identifier.Name)
  360. {
  361. case "var":
  362. color = queryKeywordsColor;
  363. break;
  364. case "global":
  365. color = structureKeywordsColor;
  366. break;
  367. }
  368. }
  369. switch (GetCurrentDefinition())
  370. {
  371. case ITypeDefinition t:
  372. switch (t.Kind)
  373. {
  374. case TypeKind.Delegate:
  375. color = delegateTypeColor;
  376. break;
  377. case TypeKind.Class:
  378. color = referenceTypeColor;
  379. break;
  380. case TypeKind.Interface:
  381. color = interfaceTypeColor;
  382. break;
  383. case TypeKind.Enum:
  384. color = enumerationTypeColor;
  385. break;
  386. case TypeKind.Struct:
  387. color = valueTypeColor;
  388. break;
  389. }
  390. break;
  391. case IMethod:
  392. color = methodDeclarationColor;
  393. break;
  394. case IField:
  395. color = fieldDeclarationColor;
  396. break;
  397. case IProperty:
  398. color = propertyDeclarationColor;
  399. break;
  400. case IEvent:
  401. color = eventDeclarationColor;
  402. break;
  403. }
  404. switch (GetCurrentMemberReference())
  405. {
  406. case IType t:
  407. switch (t.Kind)
  408. {
  409. case TypeKind.Delegate:
  410. color = delegateTypeColor;
  411. break;
  412. case TypeKind.Class:
  413. color = referenceTypeColor;
  414. break;
  415. case TypeKind.Interface:
  416. color = interfaceTypeColor;
  417. break;
  418. case TypeKind.Enum:
  419. color = enumerationTypeColor;
  420. break;
  421. case TypeKind.Struct:
  422. color = valueTypeColor;
  423. break;
  424. }
  425. break;
  426. case IMethod:
  427. color = methodCallColor;
  428. break;
  429. case IField:
  430. color = fieldAccessColor;
  431. break;
  432. case IProperty:
  433. color = propertyAccessColor;
  434. break;
  435. case IEvent:
  436. color = eventAccessColor;
  437. break;
  438. }
  439. if (color != null)
  440. {
  441. BeginSpan(color);
  442. }
  443. base.WriteIdentifier(identifier);
  444. if (color != null)
  445. {
  446. EndSpan();
  447. }
  448. }
  449. public override void WritePrimitiveValue(object value, Decompiler.CSharp.Syntax.LiteralFormat format)
  450. {
  451. HighlightingColor color = null;
  452. if (value is null)
  453. {
  454. color = valueKeywordColor;
  455. }
  456. if (value is true || value is false)
  457. {
  458. color = trueKeywordColor;
  459. }
  460. if (color != null)
  461. {
  462. BeginSpan(color);
  463. }
  464. base.WritePrimitiveValue(value, format);
  465. if (color != null)
  466. {
  467. EndSpan();
  468. }
  469. }
  470. ISymbol GetCurrentDefinition()
  471. {
  472. if (nodeStack == null || nodeStack.Count == 0)
  473. return null;
  474. var node = nodeStack.Peek();
  475. if (node is Identifier)
  476. node = node.Parent;
  477. if (Decompiler.TextTokenWriter.IsDefinition(ref node))
  478. return node.GetSymbol();
  479. return null;
  480. }
  481. ISymbol GetCurrentMemberReference()
  482. {
  483. if (nodeStack == null || nodeStack.Count == 0)
  484. return null;
  485. AstNode node = nodeStack.Peek();
  486. var symbol = node.GetSymbol();
  487. if (symbol == null && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression)
  488. {
  489. symbol = node.Parent.GetSymbol();
  490. }
  491. if (symbol != null && node.Parent is ObjectCreateExpression)
  492. {
  493. symbol = node.Parent.GetSymbol();
  494. }
  495. if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && symbol is IMember member)
  496. {
  497. var declaringType = member.DeclaringType;
  498. if (declaringType != null && declaringType.Kind == TypeKind.Delegate)
  499. return null;
  500. }
  501. return symbol;
  502. }
  503. readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
  504. public override void StartNode(AstNode node)
  505. {
  506. nodeStack.Push(node);
  507. base.StartNode(node);
  508. }
  509. public override void EndNode(AstNode node)
  510. {
  511. base.EndNode(node);
  512. nodeStack.Pop();
  513. }
  514. readonly Stack<HighlightingColor> colorStack = new Stack<HighlightingColor>();
  515. HighlightingColor currentColor = new HighlightingColor();
  516. int currentColorBegin = -1;
  517. readonly ILocatable locatable;
  518. readonly ISmartTextOutput textOutput;
  519. private void BeginSpan(HighlightingColor highlightingColor)
  520. {
  521. if (textOutput != null)
  522. {
  523. textOutput.BeginSpan(highlightingColor);
  524. return;
  525. }
  526. if (currentColorBegin > -1)
  527. HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
  528. colorStack.Push(currentColor);
  529. currentColor = currentColor.Clone();
  530. currentColorBegin = locatable.Length;
  531. currentColor.MergeWith(highlightingColor);
  532. currentColor.Freeze();
  533. }
  534. private void EndSpan()
  535. {
  536. if (textOutput != null)
  537. {
  538. textOutput.EndSpan();
  539. return;
  540. }
  541. HighlightingModel.SetHighlighting(currentColorBegin, locatable.Length - currentColorBegin, currentColor);
  542. currentColor = colorStack.Pop();
  543. currentColorBegin = locatable.Length;
  544. }
  545. }
  546. }