Fork me on GitHub

Generating Code with ISourceWriter


This code was originally written and proven out in the related Marten and described in a post titled Using Roslyn for Runtime Code Generation in Marten. This code was ripped out of Marten itself, but it's happily running now in Lamar a couple years later.

Lamar provides the Lamar.Compilation.ISourceWriter service -- and a lot of related extension methods -- to help write common code constructs and maintain legible code indention just like you'd use if you were writing the code in an editor or IDE.

The Basics

To dip our toes into source generation, let's write a simple method to a string that would just write out "Hello" to the console:


            var writer = new SourceWriter();
            
            writer.Write(@"
BLOCK:public void SayHello()
Console.WriteLine('Hello');
END
".Replace("'", "\""));
            
            Console.WriteLine(writer.Code());

After that code, the value of the SourceWriter.Code() method is this text:

public void Go()
{
    Console.WriteLine("Hello");
}

A few notes on what SourceWriter.Write() is doing:

  • Starting a line with BLOCK: tells Lamar to write an open bracket '{' on the next line of code and to increment the leading spaces for subsequent lines
  • The Write() method is processing each line in the text one at a time, so the call to Console.WriteLine("Hello") would be indented because it is inside a code block for the method
  • The END text tells Lamar to write a closing '}' bracket on the next line, then decrement the leading spaces for the next lines of code

Other basic method usages are shown below:


var writer = new SourceWriter();

// Write an empty line into the code 
writer.BlankLine();

// Writes a single line into the code
// with the proper indention. Does NOT
// respect the BLOCK: and END directives
writer.WriteLine("// A comment");

// Writes a closing '}' character into the 
// next line and decrements the leading space
// indention for the following lines of code
writer.FinishBlock();

Advanced Usages

Note! All the usages in this section are from extension methods in the Lamar.Compilation namespace

Here are some of the advanced usages of ISourceWriter:


var writer = new SourceWriter();

// Add "using [namespace]; statements
writer.UsingNamespace(typeof(Console).Namespace);
writer.UsingNamespace<IOperation>();

writer.Namespace("GeneratedCode");
// Write new classes and code within the namespace
writer.FinishBlock();


// Helper to write using blocks in C# code
writer.UsingBlock("var conn = new SqlConnection()", w =>
{
    w.Write("conn.Open();");
    // other statements
});



// Write a comment text into the code at the correct indention
// level
writer.WriteComment("Some message");


// Start the declaration of a new public class named "MyClass"
// that implements the IDisposable interface
writer.StartClass("MyClass", typeof(IDisposable));