//
//
//
//
// $Revision$
//
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using Boo.Lang.Compiler;
using Boo.Lang.Compiler.Ast;
using Boo.Lang.Compiler.Ast.Visitors;
using ICSharpCode.NRefactory.Parser;
namespace NRefactoryToBooConverter
{
public class BooPrinterVisitorWithComments : BooPrinterVisitor, ISpecialVisitor
{
IEnumerator enumerator;
bool available; // true when more specials are available
public BooPrinterVisitorWithComments(IEnumerable specials, TextWriter writer)
: base(writer)
{
if (specials == null) throw new ArgumentNullException("specials");
enumerator = specials.GetEnumerator();
available = enumerator.MoveNext();
}
void AcceptPoint(LexicalInfo lex)
{
if (lex != null && lex.IsValid) {
AcceptPoint(lex.Line, lex.Column);
}
}
void AcceptPoint(SourceLocation loc)
{
if (loc != null && loc.IsValid) {
AcceptPoint(loc.Line, loc.Column);
}
}
///
/// Writes all specials up to the specified location.
///
void AcceptPoint(int line, int column)
{
while (available) {
Point b = enumerator.Current.StartPosition;
if (b.Y < line || (b.Y == line && b.X <= column)) {
WriteCurrent();
} else {
break;
}
}
}
void WriteCurrent()
{
enumerator.Current.AcceptVisitor(this, null);
available = enumerator.MoveNext();
}
///
/// Outputs all missing specials to the writer.
///
public void Finish()
{
while (available) {
WriteCurrent();
}
}
Node currentDocuNode;
bool isInEndMode = false;
public override bool Visit(Node node)
{
if (node == null) return base.Visit(node);
currentDocuNode = node;
AcceptPoint(node.LexicalInfo);
currentDocuNode = null;
bool result = base.Visit(node);
isInEndMode = true;
AcceptPoint(node.EndSourceLocation);
isInEndMode = false;
oldIndentation = _indent;
return result;
}
#region ICSharpCode.NRefactory.Parser.ISpecialVisitor interface implementation
int oldIndentation;
struct DelayedSpecial {
public readonly int Indentation;
public readonly string Format;
public readonly object[] Args;
public DelayedSpecial(int indentation, string Format, object[] Args) {
this.Indentation = indentation;
this.Format = Format;
this.Args = Args;
}
}
List delayedSpecials = new List();
bool writingDelayedSpecials;
public override void WriteLine()
{
if (_disableNewLine == 0) {
base.WriteLine();
if (!writingDelayedSpecials) {
writingDelayedSpecials = true;
int tmp = _indent;
foreach (DelayedSpecial special in delayedSpecials) {
_indent = special.Indentation;
WriteLine(special.Format, special.Args);
}
delayedSpecials.Clear();
writingDelayedSpecials = false;
_indent = tmp;
}
oldIndentation = _indent;
}
}
void WriteSpecialText(bool writeInline, string format, params object[] args)
{
// write comment in it's own line
if (_needsIndenting || writeInline) {
int tmp = _indent;
if (isInEndMode) {
_indent = oldIndentation;
}
if (writeInline && !_needsIndenting) {
WriteIndented(format, args);
} else {
WriteLine(format, args);
}
if (isInEndMode) {
_indent = tmp;
}
} else {
// comment is in the middle of line
// put it after the next line
delayedSpecials.Add(new DelayedSpecial(isInEndMode ? oldIndentation : _indent, format, args));
}
}
object ISpecialVisitor.Visit(ISpecial special, object data)
{
throw new NotImplementedException();
}
object ISpecialVisitor.Visit(BlankLine special, object data)
{
WriteSpecialText(false, "");
return null;
}
object ISpecialVisitor.Visit(Comment special, object data)
{
switch (special.CommentType) {
case CommentType.Documentation:
if (currentDocuNode == null)
goto default;
currentDocuNode.Documentation += special.CommentText;
break;
case CommentType.Block:
WriteSpecialText(true, "/*{0}*/", special.CommentText);
break;
default:
WriteSpecialText(false, "//{0}", special.CommentText);
break;
}
return null;
}
object ISpecialVisitor.Visit(PreProcessingDirective special, object data)
{
WriteSpecialText(false, "{0} {1}", special.Cmd, special.Arg);
return null;
}
#endregion
}
}