Skip to content

Defining custom directives

Adam Bajguz edited this page Apr 5, 2021 · 12 revisions

To define a custom directive, just create a new class that implements the IDirective or IPipelinedDirective interface and annotate it with [Directive] attribute:

[Directive(BuiltInDirectives.Debug, Description = "Starts a debugging mode. Application will wait for debugger to be attached before proceeding.")]
public sealed class DebugDirective : IPipelinedDirective
{
    /// <inheritdoc/>
    public ValueTask OnInitializedAsync(CancellationToken cancellationToken)
    {
        return default;
    }

    /// <inheritdoc/>
    public async ValueTask HandleAsync(ICliContext context, CommandPipelineHandlerDelegate next, CancellationToken cancellationToken)
    {
#if NET5_0
        int processId = Environment.ProcessId;
#else
        int processId = Process.GetCurrentProcess().Id;
#endif

        IConsole console = context.Console;

        console.Output.WithForegroundColor(ConsoleColor.Green, (output) => output.WriteLine($"Attach debugger to PID {processId} to continue."));

        Debugger.Launch();

        while (!Debugger.IsAttached)
            await Task.Delay(100, cancellationToken);

        await next();
    }
}

To facilitate both asynchronous and synchronous execution, OnInitializedAsync and HandleAsync methods return a ValueTask. In synchronous implementation, we can just put return default at the end, while in an asynchronous we can use the async/await keywords instead.

Similarly to commands, in every directive it is possible to define a description and a manual with [Directive] attribute. [Directive] attribute provides also an easy way for excluding a command from execution in normal mode through InteractiveModeOnly property.

Difference between IDirective and IPipelinedDirective

Clone this wiki locally