cursorless-dev / cursorless

Don't let the cursor slow you down
https://www.cursorless.org/
MIT License
1.13k stars 78 forks source link

Migrate Cursorless to language server #946

Open pokey opened 2 years ago

pokey commented 2 years ago

Overview

One possible approach to #435 is to just turn the core of Cursorless into a language server. On the surface, it seems like it may not be a great fit, given that Cursorless doesn't do pretty much any of the things expected by a language server (eg jump to definition, find references, rename, list workspace symbols, autocomplete, etc). However, the lsp protocol is actually quite general, and Cursorless could just define custom requests and notifications for its own purposes. Here are the benefits of this approach:

The approach

Phase I: preliminaries

There are two main directions at the start, that can happen somewhat in parallel

Abstract away references to vscode in Cursorless

We will gradually move everything VSCode-specific behind the ide abstraction. We will make some of these function calls asynchronous. Those async function calls will eventually become round-trips to the lsp client.

Change sidecar extension to be an LSP server

The sidecar extension will use the node LSP server library to implement the LSP protocol. This library will keep an in-memory version of documents. We can listen to these changes and propagate the changes to VSCode. We could either propagate these changes by clobbering editor text, or support incremental changes. Incremental changes probably won't actually be too hard to support from client to server to VSCode, as the change notifications are very similar in format to the edits that we'd apply to VSCode. See server.ts from the VSCode extension sample for example code, keeping in mind that in that repo the server runs as a node subprocess, whereas here we're running it directly in the sidecar extension. Note that the work of getting the sidecar to implement this synchronisation from lsp server in-memory state to VSCode editor state is throw-away work.

The sidecar extension will need to start supporting our custom LSP messages, such as cursorless/command, selection / cursor synchronisation, hat updates, etc. This work is not throw-away work, as we will want to fold it into Cursorless eventually.

The editor clients will also need to be changed to be LSP clients. The document synchronisation should come for free from an LSP client library. They will need to handle / send our custom messages for cursor synchronisation, hats, and Cursorless command messages.

None of the code to do this LSP server implementation should require any changes to Cursorless itself in the short term.

Here are some things that will need to happen along the way:

Initial work in Cursorless extension

In addition to the above two directions, we will create an implementation of the ide abstraction that defines how Cursorless will interact with a client using the lsp protocol. At the outset, it will just directly call a method on the sidecar to ask it to send an lsp message to the client, but that method call should be easy to replace with something that directly ends the lsp message in the future. This implementation of the ide abstraction will be used in place of the VSCode implementation when we're using the sidecar. Not sure how this will work where we need functionality from both, because the sidecar setup is still running in VSCode. We can see how this works out in the code. It might need to keep its own VSCode ide object and forward some things to it.

We'll first use this setup to implement the "follow" action and hat synchronisation

Phase II: proper language server

Once all of the references to VSCode are behind the ide abstraction, and we have https://github.com/cursorless-dev/cursorless/issues/945, we can

References

Old stuff

Actions

We'd need to look at our actions to figure out how to handle the fact that they sometimes go back and forth multiple times with the editor. Here are a few examples:

Possible approaches

For updating the "that" mark, could possibly have Cursorless set intermediate "that" mark and then keep it up to date with document changes.

We probably want a barrier no matter what, so that commands can linearise properly. Maybe look into hooking into synchronisation code so we can wait for it to happen. Possibly could use custom notifications or requests

pokey commented 10 months ago

update from meet-up 2023-10-31: