Code Generation

Generator overview

The Beef IDE allows for generating source files through generators contained in the source code itself. The simplest example of this is NewClassGenerator. They can be accessed through the “Generate File…” option when right-clicking inside the workspace panel. Classes inheriting from Compiler.Generator will automatically show up as an option of the “Generator” dropdown in the “Generate File” panel. The generator’s methods are directly executed through the compiler, so there is no need to compile manually.

Generators can be used for creating custom file templates, but also for generating an entire file of complete source code.

public class NewClassGenerator : Compiler.Generator
{
    public override String Name => "New Class"; // This is was the generator will show up as in the "Generator" dropdown

    public override void InitUI()
    {
        AddEdit("name", "Class Name", "");
    }

    public override void Generate(String outFileName, String outText, ref Flags generateFlags)
    {
        var name = mParams["name"];
        if (name.EndsWith(".bf", .OrdinalIgnoreCase))
          name.RemoveFromEnd(3);

    outFileName.Append(name);
    outText.AppendF(
        $"""
        namespace {Namespace}
        {{
            class {name}
            {{
            }}
        }}
        """);
    }
}

Fail(...) may be called in any of the two methods to report back errors.

Generator UI

InitUI() is called when the generator is selected from the “Generator” dropdown and is used to prompt the creation of specific UI as input for the generator. The first argument passed to these methods is the name of the parameter, which can later be used by the Generate method to gain access to the values passed in.

AddEdit(...); // Adds an input field
AddCombo(...); // Adds a dropdown selection
AddCheckbox(...); // Adds a checkbox

// For example, a dropdown selection with the options A, B and C, where B is the default selection.
AddCombo("type", "Type", "B", StringView[?]("A", "B", "C"));

// ... in Generate, we can retrieve this input from mParams
let type = mParams["type"];

Source Generation

In Generate(), we return output by appending to the outFileName and outFile strings passed into the method. outFileName should not include a ‘.bf’ file extension.

All inputs are StringViews retrieved with mParams[...], by passing in the name of the parameter (as specified by adding UI elements in InitUI). Note that for checkboxes, the returned string will be equal to bool.TrueString or bool.FalseString. GetString(...) can also be used to append the StringView to a passed in string.

// For example...
let type = mParams["type"];
bool option = (mParams["option"] == bool.TrueString);

A number of built-in parameters can be accessed with getter methods. For example, the example above uses Namespace to get the name of the namespace based on the project and file location. The following parameters can be used: - ProjectName - the name of the current project - ProjectDir - the path to the current project - FolderDir - the path to the folder the file is generated into - Namespace - the namespace of the file (includes the relative folder path of the file as sub-namespaces) - DefaultNamespace - the default namespace of the current project - WorkspaceName - the name of the current workspace - WorkspaceDir - the path to the current workspace - DateTime - the current time as of generation - IsRegenerating - indicates if the generator is run to regenerate a file

Regeneration

A generator can enable regeneration of a file by setting .AllowRegenerate on generateFlags during the Generate call. When generating fully functional source code, previously generated files can easily be regenerated after the generator is updated. Note that regenerating a file discards all manual changes as the file content is rewritten from scratch.

Generators marked for regeneration will save the generator configuration in a comment at the top of the regenerated file. Its existence is necessary for the file to be recognized as regeneratable. The comment may be edited, for example to include newly added generator inputs or change them. GetString(...) may be used to determine if an input is given (as older files do not know of more recently added generator parameters) in order to use a default value in that case. mParams[...] is a dictionary and will simply error when an unknown key is passed in.

Files can be regenerated by right-clicking on them and choosing the “Regenrate” option.