Let's Build RDCore
Ce document est disponible en français
🚀 Getting Started
- First read and accept the Contributor Licence Agreement (CLA) as appropriate for your situation (it will make the next steps smoother);
- Browse the project roadmap and pick a ticket under the current milestone;
- Fork the RDCore repository to your GitHub account;
- Download a clone of the repository on a workstation that is free of rights (i.e. NOT your employer's computer);
- Open
RDCore.slnxun Microsoft Visual Studio Community Editition 2026 (free for open-source contributions); a. 👉 Alternatively, usedotnet buildCLI tooling to build the solution. - Start a new branch from main, named in reference to the selected ticket;
- Implement and test your contributions in your local branch;
- When everything works and is ready for review, open a new pull request referring to the selected ticket;
a. 👉 Mention
Closes #followed by the ticket number in the body of the pull request. b. 👉 Such a mention in a commit of your branch history will appear in the ticket history as soon as your commits are pushed to your fork, which signals work in progress to other contributors. - Sign the CLA by copying the specified signature content into a comment on your PR. a. 👉 This will only be need to be done once.
- Once your pull request is completed, destroy your feature branch and resynchronize main to start work on a new ticket. a. 👉 The squash merge will destroy the details of your commit history in the central repository, which quickly complicates things if additional commits get added to an already-completed branch; a patch may be submitted by starting a new branch from main resynchronized to contain the merge commit.
🧩 Building a RDCore extension
It only takes a few lines in your entry point to make your application a RDCore app:
public class Program
{
public static async Task<int> Main(string[] args)
{
using var host = new RDCoreConsoleClientHost();
return await host.RunAsync(args);
}
}
Host
Before you can write these lines, you must define a host by inheriting RDCoreLanguageClientHost if you're writing a client :
internal class RDCoreConsoleClientHost() : RDCoreLanguageClientHost<RDCoreConsoleClientApp>()
{
protected override void ConfigureAdditionalExternalServices(IServiceCollection services, IConfiguration configuration)
{
services
.AddSingleton<IAppThemeService, AppThemeService>()
.AddSingleton<IAppThemeLoaderService, AppThemeLoaderService>()
.AddSingleton<IConsoleMessageWriter, DefaultConsoleMessageWriter>()
.AddSingleton<ILoggerProvider, RDCoreConsoleLoggerProvider>()
.AddSingleton<ShowSplashCommand>();
}
}
...or by inheriting RDCorePlatformServerHost if you're building a server app instead:
internal class CoreDiagnosticsAppHost() : RDCorePlatformServerHost<CoreDiagnosticsApp>()
{
protected override void ConfigureAdditionalExternalServices(IServiceCollection services, IConfiguration configuration)
{
}
}
Application
In both cases, the role of the host is to supply services to the IServiceCollection such that the application can be instantiated with injected services.
Then for a client you would inherit RDCoreClientApp :
internal class RDCoreConsoleClientApp(
IOptions<SdkAppOptions> options,
IChildConnectionFactory connectionFactory,
ILogger<RDCoreConsoleClientApp> logger)
: RDCoreClientApp(options, connectionFactory, logger)
{
public override CoreServerComponent PlatformComponent => CoreServerComponent.ClientApp;
protected override void ConfigureServices(IServiceCollection services)
{
}
protected override ClientCapabilities ConfigureClientCapabilities(ClientCapabilities capabilities)
{
// TODO
return capabilities;
}
protected override void ConfigureHandlers(IRDCoreLSPHandlerConfigurationBuilder builder)
{
// TODO
}
protected override async Task OnLanguageClientStartedAsync(ILanguageClient client, CancellationToken token)
{
// TODO
}
protected override void Dispose(bool disposing) { }
}
...and for a server app we instead inherit the LSP app from RDCoreServerApp:
internal class CoreDiagnosticsApp(
IOptions<SdkAppOptions> options,
IServerStateProvider serverStateProvider,
IHealthCheckService<CoreDiagnosticsApp> healthCheckService,
ILanguageServerProtocolTransportLayer transportLayer,
ILogger<CoreDiagnosticsApp> logger)
: RDCoreServerApp(options, serverStateProvider, healthCheckService, transportLayer, logger)
{
public override CoreServerComponent PlatformComponent => CoreServerComponent.Extension;
protected override void ConfigureHandlers(IRDCoreLSPHandlerConfigurationBuilder builder)
{
// TODO
}
protected override void RegisterServerCapabilities(ILanguageServer server, ClientCapabilities clientCapabilities)
{
// TODO
}
protected override void Dispose(bool disposing)
{
// TODO
}
}
In any case, the role of this abstraction layer is to configure the capabilities (LSP) of the application, along with the handlers that will be handling the LSP requests and notifications.
Client or Server?
- A client application is typically an IDE application.
- A server application could be a satellite language server, or a platform extension (plug-in).
- 🌐LSP 3.17 Specifications
Important
🧩 RDCore Platform Extensions need a manifest (extension.manifest.json, schema ExtensionInfo) for the language server to discover and bring them up during platform assembly. The manifest is generated by the CLI in command mode:
rdc.exe describe-ext RDCore.Diagnostics.exe --description "…" --unsafe-dev-mode
describe-ext reflects the extension executable's advertised capabilities (its [assembly: ProvidesCorePlatformClientCapability<T>] declarations) into the manifest. PlatformPublish.ps1 runs it once per extension while assembling the platform.
Important
📚 Referenced libraries (Excel, Word, Access, ADODB, Scripting, …) are known to a project by their name, as if the library were VBA code, and what each declares ships with the platform as a library description in Symbols/<Name>.json (format: LibraryDescription). A project is checked against the description, so it can be analyzed on a machine that does not have the library. A library that the platform has no description of is a reference that cannot be resolved, and the types that name it are compile errors. Libraries that depend on one another are rejected, not resolved.
A description is generated, never written by hand, on a Windows machine that has the library, by the CLI in command mode:
rdc.exe export-library Excel --dependencies --output Symbols
export-library takes a file (scrrun.dll), the identifier the machine gave the library, or its name; when several versions of a library are registered, the latest is described. --dependencies describes the libraries it depends on too, which a project needs in order to reference it.
The description is what a project is checked against; what it runs against are the objects of the library on the machine that runs it. New Scripting.Dictionary is a real dictionary, and New Excel.Application starts a real Excel, driven from the sidelines through its object model. Today that is the automation servers of Windows; elsewhere, a program that creates one of these objects is told that the platform has none (error 429), while the same program is analyzed anywhere. Every such call passes through the same interception chain as any call outside the workspace, and an administrator can refuse them all with "AllowAutomation": false in appsettings.json (error 70, Permission denied).
Capabilities
RDCore platform extensions with a valid manifest that gets them to initiate a LSP handshake with the LSP orchestration layer must supply initialization parameters that specify a complete set of both LSP (protocol) defined and environment host-defined capabilities. An extension advertises a platform capability such as CliCommand with an assembly attribute:
[assembly: ProvidesCorePlatformClientCapability<CliCommand>]
👉 The complete and exhaustive list of platform capabilities shall be documented in RD-VBAL §2.0.2 as its implementation progresses.
Note
First and third party extensions distributed through the RDCore Platform Cloud Infrastructure MAY use a capability provider that MAY validate the availability of certain advanced capabilities by requiring 2FA authentication, the validation of an active subscription (free or paid), and the validation of the signed build against the certified distribution channel build.
🧩 Platform-level extensions (SDK and language core)
The lgnauge core is engineered to be extended through server type extensions, via an exchange of capabilities giving access to extension points.
Please see RD-VBAL § 1.1 for more details and the platform's extension philosophy at that level.
ACCUEIL • HOME | ℹ️ BIENVENUE • WELCOME | 🧩 BÂTISSONS • BUILD | RD-VBAL | SDK | 🌐 rubberduckvba.ca