Proveedor de servidor de lenguaje de extensibilidad
Un proveedor de servidor de lenguaje es un proceso que se hospeda fuera de Visual Studio y que proporciona funciones de lenguaje que no están presentes en Visual Studio.
Estos servidores deben cumplir el protocolo de servidor de lenguaje, creado por un proyecto de extensión e implementar LanguageServerProvider
.
Trabajo con proveedores de servidor de lenguaje
En esta introducción se describen estos principales escenarios para trabajar con proveedores de servidores de lenguaje:
- Creación de un proveedor de servidor de lenguaje
- Envío de datos adicionales al iniciar un servidor de lenguaje
- Definición de tipos de documentos personalizados
- Habilitación o deshabilitación de un servidor de lenguaje
- Uso de recursos localizados
Creación de un proveedor de servidor de lenguaje
La creación de un proveedor de servidor de lenguaje implica agregar una nueva clase que extienda Microsoft.VisualStudio.Extensibility.LanguageServer.LanguageServerProvider
y le aplique el atributo VisualStudioContribution
.
[VisualStudioContribution]
public class MyLanguageServerProvider : LanguageServerProvider
{
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
}
}
Después de definir el proveedor, debe hacer lo siguiente:
Configure el proveedor anulando la propiedad
LanguageServerProviderConfiguration
. Esta propiedad de configuración define el nombre para mostrar del servidor y los tipos de documento aplicables.LanguageServerBaseDocumentType
está disponible para todos los servidores y desencadenadores en todos los tipos de documento. Consulte Definición de un tipo de documento personalizado.public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new("My Language Server", new[] { DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType), });
Anule el método
CreateServerConnectionAsync
, al que llama Visual Studio para notificar a la extensión que se debe iniciar el servidor LSP.// 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())); }
Anule el método
OnServerInitializationResultAsync
, al que llama Visual Studio después de que el servidor LSP haya completado sus pasos de inicio y configuración.ServerInitializationResult
proporciona el estado resultante del servidor yLanguageServerInitializationFailureInfo
proporciona una excepción si existe.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; }
Este es el aspecto de nuestro proveedor de servidor de lenguaje de muestra después de completar todos los pasos:
[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;
}
}
Envío de datos adicionales al iniciar un servidor de lenguaje
LanguageServerOptions.InitializationOptions
se puede establecer en el constructor para que LanguageServerProvider
envíe datos adicionales al servidor con el mensaje de protocolo "initialize".
public MyLanguageServerProvider(ExtensionCore container, VisualStudioExtensibility extensibilityObject, TraceSource traceSource)
: base(container, extensibilityObject)
{
this.LanguageServerOptions.InitializationOptions = JToken.Parse(@"[{""server"":""initialize""}]");
}
Definición de tipos de documentos personalizados
Cuando una extensión admite tipos de archivo que no son compatibles de forma nativa con Visual Studio, los autores de extensiones pueden implementar tipos de documentos personalizados. Estos tipos se pueden usar al definir LanguageServerProviderConfiguration
para especificar los tipos de documento admitidos.
[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,
};
Este fragmento de código define dos nuevos tipos de documento: rust
y markdown
. Estos tipos contienen una lista de extensiones de archivo y un tipo base, que puede ser LanguageServerBaseDocumentType
para cubrir todos los tipos.
Use estos tipos en LanguageServerProviderConfiguration
para activar el servidor cuando se abran estos tipos de documento:
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("My Language Server",
new[]
{
DocumentFilter.FromDocumentType(RustDocumentType),
DocumentFilter.FromDocumentType(MarkdownDocumentType),
});
Habilitación o deshabilitación de un servidor de lenguaje
Un servidor de lenguaje habilitado se puede "activar" una vez abierto un tipo de documento aplicable. Cuando está deshabilitado, se envía un mensaje de detención a cualquier servidor de lenguaje activo aplicable y se evitan más activaciones.
[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;
}
}
}
Este fragmento de código deshabilita el servidor de lenguaje estableciendo this.Enabled
en false
si ServerInitializationResult
se establece en Failed
después de que falle la inicialización.
Nota:
Esta marca es pública y, si se establece en falsa, se detienen los servidores en ejecución.
Uso de recursos localizados
Se admite el uso de la localización mediante la definición de un archivo string-resources.json
y el uso de %tokens%
para especificar contenido localizado.
string-resources.json
{
{ "LocalizedResource": "LangaugeServerLocalized" }
}
Acceso a un recurso localizado
[VisualStudioContribution]
public class MyLanguageServer : LanguageServerProvider
{
...
/// <inheritdoc/>
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration =>
new("%LocalizedResource%",
new[]
{
DocumentFilter.FromDocumentType(LanguageServerBaseDocumentType)
});
}
Pasos siguientes
- Siga el tutorial de creación de la primera extensión para comenzar a crear una extensión.
- Consulte el ejemplo Proveedor de servidor de lenguaje de Rust para obtener un ejemplo completo sobre cómo crear una extensión con un proveedor de servidor de lenguaje.