Bâtissons RDCore
This document is available in English
🚀 Démarrage
- Lisez et acceptez d'abord l'accord de licence contributeur (CLA) approprié (ça facilitera la suite);
- Prenez connaissance de la feuille de route du projet et sélectionnez un ticket;
- Démarrez un fork du référentiel RDCore sur votre compte GitHub;
- Téléchargez un clone du référentiel sur un poste libre de droits (donc pas sur un poste fourni par votre employeur);
- Ouvrez
RDCore.slnxdans Microsoft Visual Studio Community Edition 2026 (gratuit pour contributions open-source); à. 👉 Alternativement, utilisez les outils CLIdotnet buildpour compiler la solution. - Démarrez une nouvelle branche à partir de main, nommée en référence au ticket sélectionné;
- Effectuez et testez vos contributions dans votre branche locale;
- Lorsque tout fonctionne et est prêt pour revue, ouvrez une pull request en y référant le ticket sélectionné;
a. 👉 Mentionnez
Closes #suivi du numéro du ticket dans le corps de la pull request. b. 👉 Une telle mention dans un commit de votre historique apparaîtra sur l'historique de ce ticket dès que vos commits sont poussés dans votre fork, ce qui signale un travail en cours aux autres contributeurs. - Signez la CLA en copiant le texte de la signature dans un nouveau commentaire sur la PR. a. 👉 Ceci ne sera nécessaire qu'une seule fois.
- Une fois votre pull request complétée, détruisez la branche et resynchronisez main pour démarrer un nouveau développement. à. 👉 Le squash merge détruira le détail de l'historique de vos commits dans le référentiel central, ce qui complique rapidement les choses si des commits additionnels s'ajoutent à une branche déjà complétée; un correctif peut être soumis en démarrant une nouvelle branche à partir de main resynchronisé avec le merge commit.
🧩 Créer une extension RDCore
Il suffit de quelques lignes dans votre point d'entrée pour que votre application RDCore soit prise en charge :
public class Program
{
public static async Task<int> Main(string[] args)
{
using var host = new RDCoreConsoleClientHost();
return await host.RunAsync(args);
}
}
Hôte
Avant de pouvoir écrire ces lignes, il faudra définir votre hôte en héritant de RDCoreLanguageClientHost si vous construisez un 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>();
}
}
...ou alors en héritant de RDCorePlatformServerHost si vous construisez plutôt un serveur :
internal class CoreDiagnosticsAppHost() : RDCorePlatformServerHost<CoreDiagnosticsApp>()
{
protected override void ConfigureAdditionalExternalServices(IServiceCollection services, IConfiguration configuration)
{
}
}
Application
Dans les deux cas, le rôle de l'hôte est de fournir les services au IServiceCollection de sorte que l'application puisse être instanciée en lui injectant tous les services dont elle a besoin.
Ensuite pour un client on hérite l'application LSP de 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) { }
}
...et pour un serveur on hérite l'application LSP de 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
}
}
Dans tous les cas, le rôle de ce niveau d'abstraction est de configurer les capacités (LSP) de l'application et les handlers pour la prise en charge de requêtes et notifications LSP.
Client ou Serveur?
- Une application client est généralement une application de type IDE.
- Une application serveur peut être un serveur de langage satellite ou une extension (plug-in) de la plateforme.
- 🌐LSP 3.17 Specifications
Important
🧩 Les extensions de la plateforme RDCore requièrent un manifest (extension.manifest.json, schéma ExtensionInfo) pour que le serveur de langage puisse les découvrir et les démarrer lors de l'assemblage de la plateforme. Le manifest est généré par la CLI en mode commande :
rdc.exe describe-ext RDCore.Diagnostics.exe --description "…" --unsafe-dev-mode
describe-ext reflète les capacités déclarées par l'exécutable d'extension (ses déclarations [assembly: ProvidesCorePlatformClientCapability<T>]) dans le manifest. PlatformPublish.ps1 l'exécute une fois par extension lors de l'assemblage de la plateforme.
Important
📚 Les bibliothèques référencées (Excel, Word, Access, ADODB, Scripting, …) sont connues d'un projet par leur nom, comme si la bibliothèque était du code VBA, et ce que chacune déclare est livré avec la plateforme sous forme de description de bibliothèque dans Symbols/<Nom>.json (format : LibraryDescription). Un projet est vérifié contre la description : il peut donc être analysé sur une machine qui n'a pas la bibliothèque. Une bibliothèque dont la plateforme n'a pas de description est une référence qui ne peut pas être résolue, et les types qui la nomment sont des erreurs de compilation. Les bibliothèques qui dépendent les unes des autres sont rejetées, non résolues.
Une description est générée, jamais écrite à la main, sur une machine Windows qui a la bibliothèque, par la CLI en mode commande :
rdc.exe export-library Excel --dependencies --output Symbols
export-library accepte un fichier (scrrun.dll), l'identifiant que la machine a donné à la bibliothèque, ou son nom; quand plusieurs versions d'une bibliothèque sont enregistrées, la plus récente est décrite. --dependencies décrit aussi les bibliothèques dont elle dépend, qu'un projet doit avoir pour la référencer.
La description est ce contre quoi un projet est vérifié; ce contre quoi il s'exécute, ce sont les objets de la bibliothèque sur la machine qui l'exécute. New Scripting.Dictionary est un vrai dictionnaire, et New Excel.Application démarre un vrai Excel, piloté de l'extérieur par son modèle objet. Aujourd'hui, ce sont les serveurs d'automation de Windows; ailleurs, un programme qui crée un de ces objets apprend que la plateforme n'en a pas (erreur 429), alors que le même programme est analysé n'importe où. Chacun de ces appels passe par la même chaîne d'interception que tout appel à l'extérieur de l'espace de travail, et un administrateur peut tous les refuser avec "AllowAutomation": false dans appsettings.json (erreur 70, Permission denied).
Capacités
Les extensions de la plateforme RDCore avec un manifest valide qui leur permet d'initier un LSP handshake avec la couche d'orchestration LSP doit fournir des paramètres d'initialisation qui spécifient un jeu complet de capacités définies tant par le protocole (LSP) que définies par l'hôte de l'environnement. Une extension déclare une capacité de plateforme telle que CliCommand au moyen d'un attribut d'assembly :
[assembly: ProvidesCorePlatformClientCapability<CliCommand>]
👉 La liste complète et exhaustive des capacités de la plateforme sera documentée à la section RD-VBAL §2.0.2 à mesure que progresse son implémentation.
Note
Les extensions tant de première que de tierces parties distribuées à travers l'infranuagique RDCore PEUVENT utiliser un capability provider qui PEUT valider la disponibilité de certains capacités avancées en requérant une authentification 2FA, la validation d'une inscription active (gratuite ou payante), et la validation d'un build signé avec le build officiel du canal de distribution certifié.
🧩 Extension de la plateforme (SDK et coeur de langage)
Le coeur de langage est conçu pour être étendu à travers des extensions de la plateforme de type serveur, moyennant un échange de capacités donnant accès à des points d'extensions.
Voir RD-VBAL § 1.1 pour les détails et la philosophie d'extension de la plateforme à ce niveau.
ACCUEIL • HOME | ℹ️ BIENVENUE • WELCOME | 🧩 BÂTISSONS • BUILD | RD-VBAL | SDK | 🌐 rubberduckvba.ca