TL;DR
I have a VSCode extension acting as a client for an LSP server written in Python (using the pygls library) and I can't seem to get basic requests sent to my LSP server from the extension.
The Longer Version
I'm working on an LSP server for a custom YAML-based language and have run into some issues I can't seem to resolve. Specifically, our tool is written in Python, so I'm using pygls to ease the creation of the language server and am creating a VSCode extension to handle the client side of things.
At this stage, my goal is to get a very basic hover functionality to work where I've hard-coded the hover text to be displayed to the user. Unfortunately, I have yet to be able to get this to work as it doesn't seem like my client is correctly sending the request to the server, or my server is not correctly handling it.
To try and resolve this, I have:
- Looked through several examples of extensions to see how my VSCode extension differs (to name a few):
- Gone through the LSP Specification to know which parameters need to be passed for the
Hover
request. - Gone through the VSCode Langage Server Extension Guide
- Looked through the vscode-Languageserver-node code base for hints as to why my hover handler (in the server) is never getting called, etc.
Some things I noticed that were different with what I'm doing versus what others have:
- Many extensions are using TypeScript to write the server; I'm using Python.
- Some extensions do not add the call to
client.start()
to thecontext.subscriptions
; I do.
Bonus: I get the sense that I should be sending the initialize
request and the initialized
notification before expecting any of the hovering to work but, from what I've seen, no other extension I've come across explicitly sends either of those. (Additionally, just because I was curious, I tried and it still didn't provide any different results.)
At this point, I'm really not sure where I'm going wrong - any insights/pointers are greatly appreciated. Thanks!
The relevant parts of my server implementation are as follows:
server.py
# imports elided
# we start the server a bit differently, but the essence is this:
server = LanguageServer()
server.start_ws("127.0.0.1", 8080)
@server.feature(methods.HOVER)
async def handle_hover(ls: LanguageServer, params: HoverParams):
"""Handle a hover event."""
logger.info(f"received hover request\nparams are: {params.text_document.uri}")
ls.show_message("received hover request")
ls.show_message(f"file: {params.text_document.uri}; line: {params.position.line}; character: {params.position.character}")
return Hover(contents="Hello from your friendly AaC LSP server!")
The relevant parts of the client (I think, I don't use VSCode so I may be missing something) are as follows:
AacLanguageServer.ts
// imports elided
// extension.ts (not this file) contains a call to the startLspClient(...) function
export class AacLanguageServerClient {
private static instance: AacLanguageServerClient;
private aacLspClient!: LanguageClient;
// certain checks are elided for brevity
private startLspClient(context: ExtensionContext, aacPath: string, host: string, port: number): void {
if (this.aacLspClient) { return; }
this.aacLspClient = new LanguageClient(
"aac",
"AaC Language Client",
this.getServerOptions(aacPath, "start-lsp", "--host", host, "--port", `${port}`),
this.getClientOptions(),
);
this.aacLspClient.trace = Trace.Verbose;
context.subscriptions.push(this.aacLspClient.start());
this.registerHoverProvider(context);
}
private async registerHoverProvider(context: ExtensionContext): Promise<void> {
const client = this.aacLspClient;
context.subscriptions.push(languages.registerHoverProvider({ scheme: "file", language: "aac", pattern: "**/*.yaml" }, {
provideHover(document, position, token): ProviderResult<Hover> {
window.showInformationMessage(
`File: ${document.uri.path}; Line: ${position.line}; Character: ${position.character}`
);
return client.sendRequest("textDocument/hover", {
textDocument: document,
position: position,
}, token);
}
}));
}
private getServerOptions(command: string, ...args: any[]): ServerOptions {
return {
args,
command,
};
}
private getClientOptions(): LanguageClientOptions {
return {
documentSelector: [
{ scheme: "file", language: "aac", pattern: "**/*.aac" },
{ scheme: "file", language: "aac", pattern: "**/*.yaml" },
],
diagnosticCollectionName: "aac",
outputChannelName: "Architecture-as-Code",
synchronize: {
fileEvents: workspace.createFileSystemWatcher("**/.clientrc"),
}
};
}
}
When I debug this in VSCode, I can see that the server is started in the session but if I hover over any symbol in the shown file, nothing happens. Can someone
Finally, all the code (in it's current state as of the time of posting this question) can be found in this repository, if a more complete code reference is desired. The LSP server-related code is located in the python/src/aac/lang/server.py
file; and the client-related code is located in the vscode_extension/src/AacLanguageServer.ts
file.