Updated on 2021-10-23
A handy code snippet that can set indentation levels while rendering multipart documents with a TextWriter
I was recently writing a code generator using some of my old code from Rolex - a powerful multilanguage code generator that creates tokenizers/lexers given input specifications files that consist of a series of named regular expressions.
That tool uses something called the CodeDOM to do most of the heavy lifting in terms of code formatting, but these days, I'm about simplifying, and I don't need the CodeDOM and its ability to render code in multiple languages. I don't use Visual Basic so it seems kind of silly to go through all of the work of supporting it when someone can just link it as a library. So now I'm writing code out directly in C# instead. It's faster, but it leaves me missing some things, among them automatic code formatting.
This doesn't do automatic code formatting, but it does make it so I, or more the point, you - can easily indent blocks of code, or otherwise sections of text, like XML, HTML, or anything where indentation is important.
Using the code involves dropping the following class wholesale into a new file in your code. Rather than provide a download, the entire implementation is simple enough to just provide here:
using System;
using System.IO;
using System.Text;
// quick and dirty indented text writer
// by honey the codewitch
class IndentedTextWriter : TextWriter
{
bool _needIndent;
TextWriter _writer;
public IndentedTextWriter(TextWriter writer)
{
if (writer == null) throw new ArgumentNullException();
_writer = writer;
_needIndent = false;
}
public override Encoding Encoding => _writer.Encoding;
public override void Write(char value)
{
if(_needIndent)
{
_writer.Write(_Indent(IndentLevel));
_needIndent = false;
}
if (value == '\n')
{
_needIndent = true;
_writer.Write("\n");
}
else
_writer.Write(value);
}
public int IndentLevel { get; set; } = 0;
public string Indent { get; set; } = " ";
string _Indent(int level)
{
if (level<=0) return "";
var len = level * Indent.Length;
var sb = new StringBuilder(len, len) ;
for(var i = 0;i<level;++i)
{
sb.Append(Indent);
}
return sb.ToString();
}
}
Using it goes something like the following, which was adapted from an actual application of mine (in progress):
// create a text writer over the console
var tw = new IndentedTextWriter(Console.Out);
// write out "namespace {codenamespace} if
// codenamespace was specified
if(!string.IsNullOrEmpty(codenamespace))
{
tw.WriteLine("namespace {0}", codenamespace);
tw.WriteLine("{");
// increase the indent level for all
// subsequent writes
++tw.IndentLevel;
}
// this will be indented compared to the
// namespace declaration:
CodeGenerator.GenerateCodeAttribute(tw);
tw.WriteLine("partial class {0}", codeclass);
tw.WriteLine("{");
// increase the indent again
++tw.IndentLevel;
// generate a method
CodeGenerator.GenerateUnicodeFetchMethod(tw);
// decrease the indent to close the class
--tw.IndentLevel;
tw.WriteLine("}");
// if necessary decrease the indent and
// close the namespace
if (!string.IsNullOrEmpty(codenamespace))
{
--tw.IndentLevel;
tw.WriteLine("}");
}
As I said, the above code was adapted from an actual application. It's not all shown here, but enough is shown to illustrate how to use the IndentedTextWriter class.
Notice above we're just writing things out, and then we can increase the indent by manipulating IndentLevel. You can also change the string used as the indent by modifying the Indent property, which defaults to four spaces. Once the IndentLevel is modified, subsequent writes proceed at that level, allowing you to transparently indent portions of the written document. Above, you can see that if codenamespace is specified, only then does the rest of the code get indented, and then we can simply pass the IndentedTextWriter around like a regular TextWriter and anything that writes to it will write out at that indentation level. You can also nest it by creating an IndentedTextWriter over another IndentedTextWriter in case you need that.
I hope this solves a thing for you.