diff --git a/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs b/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs
new file mode 100644
index 0000000000..5dee09f2f0
--- /dev/null
+++ b/ICSharpCode.NRefactory.CSharp/Analysis/SemanticHighlightingVisitor.cs
@@ -0,0 +1,367 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using ICSharpCode.NRefactory.CSharp.Resolver;
+using ICSharpCode.NRefactory.Semantics;
+using ICSharpCode.NRefactory.TypeSystem;
+
+namespace ICSharpCode.NRefactory.CSharp.Analysis
+{
+ ///
+ /// C# Semantic highlighter.
+ ///
+ public abstract class SemanticHighlightingVisitor : DepthFirstAstVisitor
+ {
+ protected TColor defaultTextColor;
+ protected TColor referenceTypeColor;
+ protected TColor valueTypeColor;
+ protected TColor methodCallColor;
+ protected TColor fieldAccessColor;
+ protected TColor valueKeywordColor;
+
+ ///
+ /// Used for 'in' modifiers on type parameters.
+ ///
+ ///
+ /// 'in' may have a different color when used with 'foreach'.
+ /// 'out' is not colored by semantic highlighting, as syntax highlighting can already detect it as a parameter modifier.
+ ///
+ protected TColor parameterModifierColor;
+
+ ///
+ /// Used for inactive code (excluded by preprocessor or ConditionalAttribute)
+ ///
+ protected TColor inactiveCodeColor;
+
+ protected TextLocation regionStart;
+ protected TextLocation regionEnd;
+
+ protected CSharpAstResolver resolver;
+ bool isInAccessor;
+
+ protected abstract void Colorize(TextLocation start, TextLocation end, TColor color);
+
+ #region Colorize helper methods
+ protected void Colorize(Identifier identifier, ResolveResult rr)
+ {
+ if (identifier.IsNull)
+ return;
+ if (rr is TypeResolveResult) {
+ if (rr.Type.IsReferenceType == false)
+ Colorize(identifier, valueTypeColor);
+ else
+ Colorize(identifier, referenceTypeColor);
+ return;
+ }
+ MemberResolveResult mrr = rr as MemberResolveResult;
+ if (mrr != null) {
+ if (mrr.Member is IField) {
+ Colorize(identifier, fieldAccessColor);
+ return;
+ }
+ }
+ VisitIdentifier(identifier); // un-colorize contextual keywords
+ }
+
+ protected void Colorize(AstNode node, TColor color)
+ {
+ if (node.IsNull)
+ return;
+ Colorize(node.StartLocation, node.EndLocation, color);
+ }
+ #endregion
+
+ protected override void VisitChildren(AstNode node)
+ {
+ for (var child = node.FirstChild; child != null; child = child.NextSibling) {
+ if (child.StartLocation < regionEnd && child.EndLocation > regionStart)
+ child.AcceptVisitor(this);
+ }
+ }
+
+ ///
+ /// Visit all children of node until (but excluding) end.
+ /// If end is a null node, nothing will be visited.
+ ///
+ protected void VisitChildrenUntil(AstNode node, AstNode end)
+ {
+ if (end.IsNull)
+ return;
+ Debug.Assert(node == end.Parent);
+ for (var child = node.FirstChild; child != end; child = child.NextSibling) {
+ if (child.StartLocation < regionEnd && child.EndLocation > regionStart)
+ child.AcceptVisitor(this);
+ }
+ }
+
+ ///
+ /// Visit all children of node after (excluding) start.
+ /// If start is a null node, all children will be visited.
+ ///
+ protected void VisitChildrenAfter(AstNode node, AstNode start)
+ {
+ Debug.Assert(start.IsNull || start.Parent == node);
+ for (var child = (start.IsNull ? node.FirstChild : start.NextSibling); child != null; child = child.NextSibling) {
+ if (child.StartLocation < regionEnd && child.EndLocation > regionStart)
+ child.AcceptVisitor(this);
+ }
+ }
+
+ public override void VisitIdentifier(Identifier identifier)
+ {
+ switch (identifier.Name) {
+ case "add":
+ case "async":
+ case "await":
+ case "get":
+ case "partial":
+ case "remove":
+ case "set":
+ case "where":
+ case "yield":
+ case "from":
+ case "select":
+ case "group":
+ case "into":
+ case "orderby":
+ case "join":
+ case "let":
+ case "on":
+ case "equals":
+ case "by":
+ case "ascending":
+ case "descending":
+ case "dynamic":
+ case "var":
+ // Reset color of contextual keyword to default if it's used as an identifier.
+ // Note that this method does not get called when 'var' or 'dynamic' is used as a type,
+ // because types get highlighted with valueTypeColor/referenceTypeColor instead.
+ Colorize(identifier, defaultTextColor);
+ break;
+ case "global":
+ // Reset color of 'global' keyword to default unless its used as part of 'global::'.
+ MemberType parentMemberType = identifier.Parent as MemberType;
+ if (parentMemberType == null || !parentMemberType.IsDoubleColon)
+ Colorize(identifier, defaultTextColor);
+ break;
+ }
+ // "value" is handled in VisitIdentifierExpression()
+ }
+
+ public override void VisitSimpleType(SimpleType simpleType)
+ {
+ var identifierToken = simpleType.IdentifierToken;
+ VisitChildrenUntil(simpleType, identifierToken);
+ Colorize(identifierToken, resolver.Resolve(simpleType));
+ VisitChildrenAfter(simpleType, identifierToken);
+ }
+
+ public override void VisitMemberType(MemberType memberType)
+ {
+ var memberNameToken = memberType.MemberNameToken;
+ VisitChildrenUntil(memberType, memberNameToken);
+ Colorize(memberNameToken, resolver.Resolve(memberType));
+ VisitChildrenAfter(memberType, memberNameToken);
+ }
+
+ public override void VisitIdentifierExpression(IdentifierExpression identifierExpression)
+ {
+ var identifier = identifierExpression.IdentifierToken;
+ VisitChildrenUntil(identifierExpression, identifier);
+ if (isInAccessor && identifierExpression.Identifier == "value") {
+ Colorize(identifier, valueKeywordColor);
+ } else {
+ Colorize(identifier, resolver.Resolve(identifierExpression));
+ }
+ VisitChildrenAfter(identifierExpression, identifier);
+ }
+
+ public override void VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression)
+ {
+ var memberNameToken = memberReferenceExpression.MemberNameToken;
+ VisitChildrenUntil(memberReferenceExpression, memberNameToken);
+ ResolveResult rr = resolver.Resolve(memberReferenceExpression);
+ Colorize(memberNameToken, rr);
+ VisitChildren(memberReferenceExpression);
+ VisitChildrenAfter(memberReferenceExpression, memberNameToken);
+ }
+
+ public override void VisitInvocationExpression(InvocationExpression invocationExpression)
+ {
+ Expression target = invocationExpression.Target;
+ if (target is IdentifierExpression || target is MemberReferenceExpression || target is PointerReferenceExpression) {
+ var invocationRR = resolver.Resolve(invocationExpression) as CSharpInvocationResolveResult;
+ if (invocationRR != null && IsInactiveConditionalMethod(invocationRR.Member)) {
+ // mark the whole invocation expression as inactive code
+ Colorize(invocationExpression, inactiveCodeColor);
+ return;
+ }
+
+ VisitChildrenUntil(invocationExpression, target);
+
+ // highlight the method call
+ var identifier = target.GetChildByRole(Roles.Identifier);
+ VisitChildrenUntil(target, identifier);
+ if (invocationRR != null && !invocationRR.IsDelegateInvocation) {
+ Colorize(identifier, methodCallColor);
+ } else {
+ ResolveResult targetRR = resolver.Resolve(target);
+ Colorize(identifier, targetRR);
+ }
+ VisitChildrenAfter(target, identifier);
+ VisitChildrenAfter(invocationExpression, target);
+ } else {
+ VisitChildren(invocationExpression);
+ }
+ }
+
+ #region IsInactiveConditional helper methods
+ bool IsInactiveConditionalMethod(IParameterizedMember member)
+ {
+ if (member.EntityType != EntityType.Method || member.ReturnType.Kind != TypeKind.Void)
+ return false;
+ while (member.IsOverride)
+ member = (IParameterizedMember)InheritanceHelper.GetBaseMember(member);
+ return IsInactiveConditional(member.Attributes);
+ }
+
+ bool IsInactiveConditional(IList attributes)
+ {
+ bool hasConditionalAttribute = false;
+ foreach (var attr in attributes) {
+ if (attr.AttributeType.Name == "ConditionalAttribute" && attr.AttributeType.Namespace == "System.Diagnostics" && attr.PositionalArguments.Count == 1) {
+ string symbol = attr.PositionalArguments[0].ConstantValue as string;
+ if (symbol != null) {
+ hasConditionalAttribute = true;
+ var cu = this.resolver.RootNode as SyntaxTree;
+ if (cu != null) {
+ if (cu.ConditionalSymbols.Contains(symbol))
+ return false; // conditional is active
+ }
+ }
+ }
+ }
+ return hasConditionalAttribute;
+ }
+ #endregion
+
+ public override void VisitAccessor(Accessor accessor)
+ {
+ isInAccessor = true;
+ try {
+ VisitChildren(accessor);
+ } finally {
+ isInAccessor = false;
+ }
+ }
+
+ public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration)
+ {
+ var nameToken = methodDeclaration.NameToken;
+ VisitChildrenUntil(methodDeclaration, nameToken);
+ Colorize(nameToken, methodCallColor);
+ VisitChildrenAfter(methodDeclaration, nameToken);
+ }
+
+ public override void VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration)
+ {
+ HandleConstructorOrDestructor(constructorDeclaration);
+ }
+
+ public override void VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration)
+ {
+ HandleConstructorOrDestructor(destructorDeclaration);
+ }
+
+ void HandleConstructorOrDestructor(AstNode constructorDeclaration)
+ {
+ Identifier nameToken = constructorDeclaration.GetChildByRole(Roles.Identifier);
+ VisitChildrenUntil(constructorDeclaration, nameToken);
+ var currentTypeDef = resolver.GetResolverStateBefore(constructorDeclaration).CurrentTypeDefinition;
+ if (currentTypeDef != null && nameToken.Name == currentTypeDef.Name) {
+ if (currentTypeDef.IsReferenceType == true)
+ Colorize(nameToken, referenceTypeColor);
+ else if (currentTypeDef.IsReferenceType == false)
+ Colorize(nameToken, valueTypeColor);
+ }
+ VisitChildrenAfter(constructorDeclaration, nameToken);
+ }
+
+ public override void VisitTypeDeclaration(TypeDeclaration typeDeclaration)
+ {
+ var nameToken = typeDeclaration.NameToken;
+ VisitChildrenUntil(typeDeclaration, nameToken);
+ if (typeDeclaration.ClassType == ClassType.Enum || typeDeclaration.ClassType == ClassType.Struct)
+ Colorize(nameToken, valueTypeColor);
+ else
+ Colorize(nameToken, referenceTypeColor);
+ VisitChildrenAfter(typeDeclaration, nameToken);
+ }
+
+ public override void VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration)
+ {
+ if (typeParameterDeclaration.Variance == VarianceModifier.Contravariant)
+ Colorize(typeParameterDeclaration.VarianceToken, parameterModifierColor);
+
+ bool isValueType = false;
+ if (typeParameterDeclaration.Parent != null) {
+ foreach (var constraint in typeParameterDeclaration.Parent.GetChildrenByRole(Roles.Constraint)) {
+ if (constraint.TypeParameter.Identifier == typeParameterDeclaration.Name) {
+ isValueType = constraint.BaseTypes.OfType().Any(p => p.Keyword == "struct");
+ }
+ }
+ }
+ var nameToken = typeParameterDeclaration.NameToken;
+ VisitChildrenUntil(typeParameterDeclaration, nameToken);
+ Colorize(nameToken, isValueType ? valueTypeColor : referenceTypeColor);
+ VisitChildrenAfter(typeParameterDeclaration, nameToken);
+ }
+
+ public override void VisitVariableInitializer(VariableInitializer variableInitializer)
+ {
+ if (variableInitializer.Parent is FieldDeclaration) {
+ VisitChildrenUntil(variableInitializer, variableInitializer.NameToken);
+ Colorize(variableInitializer.NameToken, fieldAccessColor);
+ VisitChildrenAfter(variableInitializer, variableInitializer.NameToken);
+ } else {
+ VisitChildren(variableInitializer);
+ }
+ }
+
+ public override void VisitComment(Comment comment)
+ {
+ if (comment.CommentType == CommentType.InactiveCode) {
+ Colorize(comment, inactiveCodeColor);
+ }
+ }
+
+ public override void VisitAttribute(ICSharpCode.NRefactory.CSharp.Attribute attribute)
+ {
+ ITypeDefinition attrDef = resolver.Resolve(attribute.Type).Type.GetDefinition();
+ if (attrDef != null && IsInactiveConditional(attrDef.Attributes)) {
+ Colorize(attribute, inactiveCodeColor);
+ } else {
+ VisitChildren(attribute);
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
index 63cdd74394..2fedc8bca6 100644
--- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
+++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj
@@ -88,6 +88,7 @@
+