0

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:

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 the context.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

Debug Session

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.

Cameron
  • 53
  • 10

2 Answers2

2

Just creating a new document and setting the language to 'aac' wasn't enough.

But saving it with the extension .aac made the server active and "data" and "import" were suggested, so I tried to hover a word, and look - I got a response:

Hover shows text

And the output to the channel: AAC output channel


By the way, I think that I found a bug. Shouldn't the error messages in lines 39-40 in AacLanguageServerClient.ts use variable name instead of item?

        assertTrue(item.length > 0, `Cannot start Language Server; '${name}' is not configured!`);
        assertTrue(fs.existsSync(item), `Cannot use ${name} as it does not exist!`);
md2perpe
  • 3,372
  • 2
  • 18
  • 22
  • The reason this works (now) is because in [server.py](https://github.com/jondavid-black/AaC/blob/main/python/src/aac/lang/server.py#L47) I'm using `server.start_io()` as opposed to `server.start_ws(...)` or `server.start_tcp(...)`. For some reason, when using the TCP and WS servers, the `initialize` message was never sent (or received). – Cameron Feb 24 '22 at 22:47
  • @rayzinnz. Can't you add that suggestion in the GitHub repository or at least create an issue about it? – md2perpe Jul 25 '23 at 22:22
0

NOTE: This is still an incomplete answer! That's why I haven't accepted it.

I will update this when I figure out how to get the TCP and WebSockets servers working, but apparently a "good-enough" way to get the VSCode extension and my LSP server to communicate is by using the pygls I/O server for everything regardless of being in development mode.

Cameron
  • 53
  • 10
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 24 '22 at 09:38