// 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 Nova.Parsing;
using Nova.Rendering;
using Nova.Utilities;
namespace Nova.CodeDOM
{
/// <summary>
/// The common base class of <see cref="IfDirective"/>, <see cref="ElIfDirective"/>, and <see cref="ElseDirective"/>.
/// </summary>
public abstract class ConditionalDirective : ConditionalDirectiveBase
{
#region /* FIELDS */
protected bool _isActive;
protected string _skippedText;
#endregion
#region /* CONSTRUCTORS */
protected ConditionalDirective()
{ }
#endregion
#region /* PROPERTIES */
/// <summary>
/// True if this part of a chain of conditional directives is the active one.
/// </summary>
public bool IsActive
{
get { return _isActive; }
}
/// <summary>
/// The skipped text section if the <see cref="ConditionalDirective"/> expression evaluates to <c>false</c>.
/// </summary>
public string SkippedText
{
get { return _skippedText; }
set
{
_skippedText = value.Replace("\r\n", "\n"); // Normalize newlines
}
}
#endregion
#region /* PARSING */
protected ConditionalDirective(Parser parser, CodeObject parent)
: base(parser, parent)
{ }
protected void SkipSection(Parser parser)
{
int minimumPrefixedSpaces = int.MaxValue;
// First, associate any skipped EOL comment to the current directive
MoveEOLComment(parser.LastToken);
// Skip the section following the conditional directive, parsing all lines and storing them.
// Detect any directives, keeping track of if/endif groups, so we know when we're done, but
// just store all lines as plain text.
int nestLevel = 0;
while (parser.Token != null)
{
// Keep track of nested directives
if (parser.TokenText == ParseToken)
{
string next = parser.PeekNextTokenText();
if (next == IfDirective.ParseToken)
++nestLevel;
else if (next == ElIfDirective.ParseToken || next == ElseDirective.ParseToken)
{
if (nestLevel == 0)
break;
}
else if (next == EndIfDirective.ParseToken)
{
if (--nestLevel < 0)
break;
}
}
// Get any preceeding comments, the current token, and the entire line. GetCurrentLine() will include
// comments when it does a NextToken at the end - this preserves formatting and is more efficient, but
// the first time into this loop we might have trailing comments on the last token.
List<CommentBase> comments = parser.LastToken.TrailingComments;
Token token = parser.Token;
string line = parser.GetCurrentLine();
// Keep track of the minimum number of prefixed spaces across all lines
int prefixedSpaces = StringUtil.CharCount(line, ' ', 0);
if (prefixedSpaces < minimumPrefixedSpaces)
minimumPrefixedSpaces = prefixedSpaces;
// Emit any preceeding comments for the current line
if (comments != null)
{
foreach (CommentBase comment in comments)
{
// Emit any preceeding newlines on the comment
if (comment.NewLines > 1)
_skippedText += new string('\n', comment.NewLines - 1);
// Emit the comment, adding back any removed prefix spaces
string prefixSpaces = new string(' ', comment.PrefixSpaceCount);
_skippedText += prefixSpaces + comment.AsString().Replace("\n", "\n" + prefixSpaces) + '\n';
if (comment.PrefixSpaceCount < minimumPrefixedSpaces)
minimumPrefixedSpaces = comment.PrefixSpaceCount;
}
comments.Clear();
}
// Emit any preceeding newlines
if (token.NewLines > 1)
_skippedText += new string('\n', token.NewLines - 1);
// Special handling for multi-line verbatim strings
if (token.TokenType == TokenType.VerbatimString && StringUtil.Contains(token.Text, '\n'))
{
// Emit everything up to the last line
string text = token.Text;
int lastLF = text.LastIndexOf('\n');
_skippedText += text.Substring(0, lastLF + 1);
minimumPrefixedSpaces = 0;
}
// Emit the current line of text, and move to the next one
_skippedText += line;
}
if (!string.IsNullOrEmpty(_skippedText))
{
// Remove the trailing '\n'
_skippedText = _skippedText.Substring(0, _skippedText.Length - 1);
// Normalize space by removing the minimum from all lines
if (_skippedText[0] != '\n')
_skippedText = _skippedText.Substring(minimumPrefixedSpaces);
_skippedText = _skippedText.Replace("\n" + new string(' ', minimumPrefixedSpaces), "\n");
}
}
#endregion
#region /* RENDERING */
public override void AsText(CodeWriter writer, RenderFlags flags)
{
base.AsText(writer, flags);
if (!string.IsNullOrEmpty(_skippedText) && !flags.HasFlag(RenderFlags.Description))
{
bool isPrefix = flags.HasFlag(RenderFlags.IsPrefix);
string[] lines = _skippedText.Split('\n');
foreach (string line in lines)
{
if (!isPrefix)
writer.WriteLine();
writer.Write(line);
if (isPrefix)
writer.WriteLine();
}
}
}
#endregion
}
}