Fournisseur de serveur de langage d'extensibilité
Un fournisseur de serveur de langage implique un processus hébergé en dehors de Visual Studio, et qui fournit des fonctionnalités de langage non présentes dans Visual Studio.
Ces serveurs doivent adhérer au protocole de serveur de langage, rédigé par un projet d'extension, et implémenter LanguageServerProvider
.
Travailler avec des fournisseurs de serveur de langage
Cet aperçu couvre ces scénarios principaux pour travailler avec des fournisseurs de serveur de langage :
- Créer un fournisseur de serveur de langage
- Envoyer des données supplémentaires lors du démarrage d'un serveur de langage
- Définir des types de documents personnalisés
- Activer ou désactiver un serveur de langage
- Utiliser des ressources localisées
Créer un fournisseur de serveur de langage
Créer un fournisseur de serveur de langage implique d'ajouter une nouvelle classe qui étend Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider
et d'appliquer l'attribut VisualStudioContribution
à celle-ci.
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
}
}
Après avoir défini votre fournisseur, vous devez :
Configurer votre fournisseur en surchargeant la propriété
LanguageServerProviderConfiguration
. Cette propriété de configuration définit le nom d'affichage du serveur et les types de documents applicables.LanguageServerBaseDocumentType
est disponible pour tous les serveurs et se déclenche sur tous les types de documents. Consultez Définir un type de document personnalisé.public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server", new[] { DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType), });
Forcer la méthode
CreateServerConnectionAsync
, qui est appelée par Visual Studio pour notifier l'extension que le serveur LSP devrait être démarré.// Activate the language server and return a duplex pipe that communicates with the server. public override Task<IDuplexPipe?> CreateServerConnectionAsync(CancellationToken cancellationToken) { (Stream PipeToServer, Stream PipeToVS) = FullDuplexStream.CreatePair(); // Connect "PipeToServer" to the language server return Task.FromResult<IDuplexPipe?>(new DuplexPipe(PipeToVS.UsePipeReader(), PipeToVS.UsePipeWriter())); }
Forcer la méthode
OnServerInitializationResultAsync
, qui est appelée par Visual Studio après que le serveur LSP a complété ses étapes de démarrage et de configuration.ServerInitializationResult
fournit l'état résultant du serveur, etLanguageServerInitializationFailureInfo
fournit une exception le cas échéant.public override Task OnServerInitializationResultAsync(ServerInitializationResult startState,LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken) { // Method called when server activation was completed successfully or failed, denoted by "startState". return Task.CompletedTask; }
Voici à quoi ressemble notre exemple de fournisseur de serveur de langage après avoir complété toutes les étapes :
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
}
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("My Language Server",
new[]
{
DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType),
});
// Activate the language server and return a duplex pipe that communicates with the server.
public override Task<IDuplexPipe?> CreateServerConnectionAsync(CancellationToken cancellationToken)
{
(Stream PipeToServer, Stream PipeToVS) = FullDuplexStream.CreatePair();
// Connect "PipeToServer" to the language server
return Task.FromResult<IDuplexPipe?>(new DuplexPipe(PipeToVS.UsePipeReader(), PipeToVS.UsePipeWriter()));
}
public override Task OnServerInitializationResultAsync(ServerInitializationResult startState, LanguageServerInitializationFailureInfo? initializationFailureInfo, CancellationToken cancellationToken)
{
// Method called when server activation was completed successfully or failed, denoted by "startState".
return Task.CompletedTask;
}
}
Envoyer des données supplémentaires lors du démarrage d'un serveur de langage
LanguageServerOptions.InitializationOptions
peut être défini dans le constructeur pour LanguageServerProvider
pour envoyer des données supplémentaires au serveur avec le message de protocole « initialize ».
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
this.LanguageServerOptions.InitializationOptions = JToken.Parse(@"[{""server"":""initialize""}]");
}
Définir des types de documents personnalisés
Lorsqu'une extension prend en charge des types de fichiers qui ne sont pas nativement pris en charge par Visual Studio, les auteurs d'extensions peuvent implémenter des types de documents personnalisés. Ces types peuvent être utilisés lors de la définition de LanguageServerProviderConfiguration
pour spécifier les types de documents pris en charge.
[VisualStudioContribution]
internal static DocumentTypeConfiguration RustDocumentType => new("rust")
{
FileExtensions = new[] { ".rs", ".rust" },
BaseDocumentType = LanguageServerBaseDocumentType,
};
[VisualStudioContribution]
internal static DocumentTypeConfiguration MarkdownDocumentType => new("markdown")
{
FileExtensions = new[] { ".md" },
BaseDocumentType = LanguageServerBaseDocumentType,
};
Cet extrait définit deux nouveaux types de documents : rust
et markdown
. Ces types contiennent une liste d'extensions de fichiers et un type de base, qui peut être LanguageServerBaseDocumentType
pour couvrir tous les types.
Utilisez ces types dans LanguageServerProviderConfiguration
pour activer votre serveur lorsque ces types de documents sont ouverts :
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("My Language Server",
new[]
{
DocumentFilter.FromDocumentType(RustDocumentType),
DocumentFilter.FromDocumentType(MarkdownDocumentType),
});
Activer ou désactiver un serveur de langage
Un serveur de langage activé est autorisé à « s'activer » une fois qu'un type de document applicable est ouvert. Lorsqu'il est désactivé, un message d'arrêt est envoyé à tout serveur de langage actif applicable et empêche de nouvelles activations.
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
...
public override Task OnServerInitializationResultAsync(ServerInitializationResult startState, LanguageServerInitializationException? initializationFailureInfo, CancellationToken cancellationToken)
{
if (startState == ServerInitializationResult.Failed)
{
Telemetry.LogEvent(initializationFailureInfo.StatusMessage, initializationFailureInfo.Exception)
// Disable the language server.
this.Enabled = false;
}
}
}
Ce fragment de code désactive le serveur de langage en réglant this.Enabled
sur false
si ServerInitializationResult
est réglé sur Failed
après une initialisation échouée.
Remarque
Ce drapeau est public et s'il est réglé sur faux, tous les serveurs en cours sont arrêtés.
Utiliser des ressources localisées
Nous prenons en charge l'utilisation de la localisation en définissant un fichier string-resources.json
et en utilisant %tokens%
pour spécifier le contenu localisé.
string-resources.json
{
{ "LocalizedResource": "LangaugeServerLocalized" }
}
Accéder à une ressource localisée
[VisualStudioContribution]
public class MyLanguageServer : LanguageServerProvider
{
...
/// <inheritdoc/>
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("%LocalizedResource%",
new[]
{
DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType)
});
}
Étapes suivantes
- Suivez le tutoriel intitulé créer votre première extension pour commencer à créer une extension.
- Voir l'exemple de Fournisseur de serveur de langage Rust pour un exemple complet de création d'une extension avec un fournisseur de serveur de langage.