using System;
using System.Text;
namespace Ariadne.Generator
{
///
/// Writes generated code a line at a time, tracking the indentation.
///
public sealed class CodeWriter
{
private const string IndentUnit = " ";
// Generated files use CRLF, matching what the rest of the tree checks in.
private const string LineEnding = "\r\n";
private readonly StringBuilder text = new StringBuilder();
private int depth;
public CodeWriter(int depth = 0)
{
if (depth < 0)
{
throw new ArgumentOutOfRangeException(nameof(depth), depth, "A depth can't be negative.");
}
this.depth = depth;
}
public int Depth
{
get { return depth; }
}
///
/// Gets how much has been written, which takes as a place to insert.
///
public int Length
{
get { return text.Length; }
}
public void Indent()
{
++depth;
}
public void Dedent()
{
if (depth == 0)
{
throw new InvalidOperationException("Dedent without a matching Indent.");
}
--depth;
}
public void NewLine()
{
text.Append(LineEnding);
}
// Copied statements arrive with whatever line endings the script used.
private void AppendWithLineEndings(string content)
{
int start = 0;
for (int newline = content.IndexOf('\n'); newline >= 0; newline = content.IndexOf('\n', start))
{
if (newline > 0 && content[newline - 1] == '\r')
{
text.Append(content, start, newline + 1 - start);
}
else
{
text.Append(content, start, newline - start);
text.Append(LineEnding);
}
start = newline + 1;
}
text.Append(content, start, content.Length - start);
}
///
/// Writes on its own line at exactly , whatever the
/// indentation. Empty content writes a blank line.
///
public void AddLineAt(int column, string content)
{
if (content.Length == 0)
{
NewLine();
return;
}
text.Append(' ', column);
AppendWithLineEndings(content);
text.Append(LineEnding);
}
///
/// Writes , already laid out with its own indentation and line endings, as it is.
///
public void AddText(string content)
{
AppendWithLineEndings(content);
}
public void AddLine(string content)
{
AddLineAt(depth * IndentUnit.Length, content);
}
public void AddDirective(string content)
{
AddLineAt(0, content);
}
public void OpenNewBlock()
{
AddLine("{");
Indent();
}
public void OpenNewBlock(string header)
{
AddLine(header);
OpenNewBlock();
}
public void CloseBlock()
{
Dedent();
AddLine("}");
}
///
/// Writes everything holds, which keeps the indentation it was written with.
///
public void AddLines(CodeWriter lines)
{
text.Append(lines.text.ToString());
}
///
/// Puts everything holds at , a from
/// earlier. It's for text that can't be written until what follows it is known.
///
public void InsertLines(int position, CodeWriter lines)
{
text.Insert(position, lines.text.ToString());
}
///
/// Returns everything written so far.
///
public override string ToString()
{
return text.ToString();
}
}
}