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(); } } }