// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php
using System.Collections.Generic;
using System.Windows.Media;
using Nova.CodeDOM;
namespace Nova.UI
{
/// <summary>
/// The view model for a <see cref="CodeDOM.Expression"/>.
/// </summary>
public abstract class ExpressionVM : CodeObjectVM
{
#region /* CONSTRUCTORS */
/// <summary>
/// Create a view model instance for the specified <see cref="CodeDOM.Expression"/>.
/// </summary>
protected ExpressionVM(Expression expression, Dictionary<CodeObject, CodeObjectVM> dictionary)
: base(expression, dictionary)
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// The underlying <see cref="CodeDOM.Expression"/> model.
/// </summary>
public Expression Expression
{
get { return (Expression)CodeObject; }
}
#endregion
#region /* METHODS */
/// <summary>
/// Get the <see cref="ExpressionVM"/> on the right of the right-most <see cref="LookupVM"/> or <see cref="DotVM"/> (bypass any '::' and '.' prefixes).
/// </summary>
public virtual ExpressionVM SkipPrefixes()
{
return this;
}
#endregion
#region /* RENDERING */
public static readonly Brush StaticBorderBrush = Brushes.PaleGoldenrod;
public static readonly Brush StaticBackgroundBrush = Brushes.LightYellow;
public override Brush BorderBrush
{
get { return StaticBorderBrush; }
}
public override Brush BackgroundBrush
{
get { return StaticBackgroundBrush; }
}
protected internal override bool HasBorder()
{
return CodeRenderer.MaximizeBorders;
}
/// <summary>
/// Render an expression, with optional border, parens, and comments.
/// </summary>
public override void Render(CodeRenderer renderer, RenderFlags flags)
{
Expression expression = Expression;
int newLines = expression.NewLines;
bool isPrefix = flags.HasFlag(RenderFlags.IsPrefix);
if (!isPrefix && newLines > 0)
{
if (!flags.HasFlag(RenderFlags.SuppressNewLine))
renderer.NewLines(newLines, ParentVM);
}
else if (flags.HasFlag(RenderFlags.PrefixSpace))
renderer.RenderText(" ", PUNC_BRUSH, ParentVM);
bool increaseIndent = (flags.HasFlag(RenderFlags.IncreaseIndent) && !flags.HasFlag(RenderFlags.NoIncreaseIndent));
bool hasBorder = (flags.HasFlag(RenderFlags.ForceBorder) || HasBorder()) && !flags.HasFlag(RenderFlags.NoBorder);
if (hasBorder)
{
CreateBorder(renderer, flags);
increaseIndent = true; // Always increase the indent level for wrapped lines inside a border
}
RenderFlags passFlags = (flags & RenderFlags.PassMask);
RenderBefore(renderer, passFlags | RenderFlags.IsPrefix);
// Increase the indent level for any newlines that occur within the expression if the flag is set
if (increaseIndent)
renderer.IndentOnNewLineBegin(this);
//bool hasParens = this is Operator && Parent is Operator && !(Parent is Cast) && !(Parent is ArgumentOperator && ((ArgumentOperator)Parent).HasArgumentParens); // Show extra parens
bool hasParens = expression.HasParens;
if (hasParens)
renderer.RenderText(Expression.ParseTokenStartGroup, PUNC_BRUSH, this);
RenderExpression(renderer, passFlags | (flags & (RenderFlags.Attribute | RenderFlags.HasDotPrefix | RenderFlags.Declaration)));
if (hasParens)
{
if (expression.IsEndFirstOnLine)
renderer.NewLine();
renderer.RenderText(Expression.ParseTokenEndGroup, PUNC_BRUSH, this);
}
if (expression.HasTerminator && !StatementVM.HideTerminators && !flags.HasFlag(RenderFlags.Description))
renderer.RenderText(Statement.ParseTokenTerminator, PUNC_BRUSH, this);
if (!flags.HasFlag(RenderFlags.NoEOLComments))
RenderEOLComments(renderer, flags);
// Determine if the indent can be ended after rendering the annotations instead of before, so that any post-comments are indented
// with the lines above them. If there are any post compiler directives, we can't do this.
bool hasPostCompilerDirectives = (Annotations != null && Annotations.Exists(delegate(AnnotationVM annotationVM) { return annotationVM is CompilerDirectiveVM; }));
if (hasPostCompilerDirectives && increaseIndent)
renderer.IndentOnNewLineEnd(this);
RenderAfter(renderer, passFlags | (flags & RenderFlags.NoPostAnnotations));
if (!hasPostCompilerDirectives && increaseIndent)
renderer.IndentOnNewLineEnd(this);
// If we have a border, return to the previous one
if (hasBorder)
renderer.ReturnToBorderParent(this);
if (isPrefix)
{
// If this object is rendered as a child prefix object of another, then any whitespace is
// rendered here *after* the object instead of before it.
if (newLines > 0)
renderer.NewLines(newLines, ParentVM);
else if (!flags.HasFlag(RenderFlags.NoSpaceSuffix))
renderer.RenderText(" ", PUNC_BRUSH, ParentVM);
}
}
public abstract void RenderExpression(CodeRenderer renderer, RenderFlags flags);
public override void RenderToolTip(CodeRenderer renderer, RenderFlags flags)
{
base.RenderToolTip(renderer, flags);
RenderImplicitConversions(renderer, flags); // Render any implicit conversion if being passed as an argument
}
public void RenderImplicitConversions(CodeRenderer renderer, RenderFlags flags)
{
if (_parentVM is ArgumentsOperatorVM)
{
// If the expression is being passed as a parameter to an arguments operator, check for implicit conversions
ArgumentsOperatorVM argumentsOperatorVM = (ArgumentsOperatorVM)_parentVM;
ChildListVM<ExpressionVM> arguments = argumentsOperatorVM.Arguments;
if (arguments != null)
{
for (int index = 0; index < arguments.Count; ++index)
{
ExpressionVM parameterVM = arguments[index];
if (parameterVM != null && parameterVM.CodeObject == CodeObject)
{
RenderImplicitConversion(renderer, argumentsOperatorVM.ArgumentsOperator.GetParameterType(index));
break;
}
}
}
}
else if (_parentVM is BinaryOperatorVM)
{
if (!(_parentVM is DotVM)) // Exclude DotVM
{
// If the expression is on the left or right side of a binary operator, check for implicit conversions
BinaryOperator binaryOperator = ((BinaryOperatorVM)_parentVM).BinaryOperator;
TypeRefBase typeRef;
if (binaryOperator.Left == CodeObject)
{
if (binaryOperator.HiddenRef is OperatorRef)
typeRef = ((OperatorRef)binaryOperator.HiddenRef).GetParameterType(0);
else
{
Expression right = binaryOperator.Right;
typeRef = (right != null ? right.EvaluateType() : null);
}
RenderImplicitConversion(renderer, typeRef);
}
else if (binaryOperator.Right == CodeObject)
{
if (binaryOperator.HiddenRef is OperatorRef)
typeRef = ((OperatorRef)binaryOperator.HiddenRef).GetParameterType(1);
else
{
Expression left = binaryOperator.Left;
typeRef = (left != null ? left.EvaluateType() : null);
}
RenderImplicitConversion(renderer, typeRef);
}
}
}
else if (_parentVM is UnaryOperatorVM)
{
// If the expression is on a user-defined unary operator, check for implicit conversions
UnaryOperator unaryOperator = ((UnaryOperatorVM)_parentVM).UnaryOperator;
if (unaryOperator.HiddenRef is OperatorRef && unaryOperator.Expression == CodeObject)
RenderImplicitConversion(renderer, ((OperatorRef)unaryOperator.HiddenRef).GetParameterType(0));
}
}
public void RenderImplicitConversions(CodeRenderer renderer)
{
RenderImplicitConversions(renderer, RenderFlags.None);
}
protected void RenderImplicitConversion(CodeRenderer renderer, TypeRefBase targetTypeRef)
{
if (targetTypeRef is TypeRef)
{
TypeRef expressionTypeRef = Expression.EvaluateType(true) as TypeRef;
if (expressionTypeRef != null && !expressionTypeRef.IsSameRef(targetTypeRef))
{
// If the type of the expression differs from the targetTypeRef, look for an implicit conversion operator
object conversionOperator = expressionTypeRef.FindUserDefinedImplicitConversion((TypeRef)targetTypeRef);
if (conversionOperator != null)
{
// Render the implicit conversion operator
renderer.NewLine();
renderer.RenderText("The following implicit conversion applies to this expression:", NORMAL_BRUSH, TextStyle.Proportional);
renderer.NewLine();
SymbolicRefVM.RenderDescription(renderer, conversionOperator, null);
}
}
}
}
#endregion
}
}