5

A Drop-in Indented TextWriter in C#

 2 years ago
source link: https://www.codeproject.com/Tips/5315915/A-Drop-in-Indented-TextWriter-in-Csharp
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Introduction

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 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

Using the code involves dropping the following class wholesale into a new file in your code. Rather than provide a download, the entire implemention is simple enough to just provide here:

Shrink ▲   Copy Code
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):

Shrink ▲   Copy Code
// create an 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. I haven't tested nesting it by creating an IndentedTextWriter over another IndentedTextWriter, but in theory that should work, too in case you need that.

I hope this solves a thing for you.

History

23rd October, 2021 - Initial submission


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK