Skip to content

[red-knot] Implement basic LSP server #12624

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 6, 2024
Merged

[red-knot] Implement basic LSP server #12624

merged 11 commits into from
Aug 6, 2024

Conversation

dhruvmanila
Copy link
Member

@dhruvmanila dhruvmanila commented Aug 2, 2024

Summary

This PR adds basic LSP implementation for the Red Knot project.

This is basically a fork of the existing ruff_server crate into a red_knot_server crate. The following are the main differences:

  1. The Session stores a map from workspace root to the corresponding Red Knot database (RootDatabase).
  2. The database is initialized with the newly implemented LSPSystem (implementation of System trait)
  3. The LSPSystem contains the server index corresponding to each workspace and an underlying OS system implementation. For certain methods, the system first checks if there's an open document in LSP system and returns the information from that. Otherwise, it falls back to the OS system to get that information. These methods are path_metadata, read_to_string and read_to_notebook
  4. Add as_any_mut method for System

Why fork?

Forking allows us to experiment with the functionalities that are specific to Red Knot. The architecture is completely different and so the requirements for an LSP implementation are different as well. For example, Red Knot only supports a single workspace, so the LSP system needs to map the multi-workspace support to each Red Knot instance. In the end, the server code isn't too big, it will be easier to implement Red Knot specific functionality without worrying about existing server limitations and it shouldn't be difficult to port the existing server.

Review

Most of the server files hasn't been changed. I'm going to list down the files that have been changed along with highlight the specific part of the file that's changed from the existing server code.

Changed files:

Test Plan

First, configure VS Code to use the red_knot binary

  1. Build the red_knot binary by cargo build
  2. Update the VS Code extension to specify the path to this binary
{
	"ruff.path": ["/path/to/ruff/target/debug/red_knot"]
}
  1. Restart VS Code

Now, open a file containing red-knot specific diagnostics, close the file and validate that diagnostics disappear.

@dhruvmanila dhruvmanila added the ty Multi-file analysis & type inference label Aug 2, 2024
@dhruvmanila dhruvmanila force-pushed the dhruv/red-knot-lsp-0 branch from 15db000 to b046757 Compare August 2, 2024 11:14
Base automatically changed from dhruv/red-knot-lsp-0 to main August 2, 2024 11:24
@dhruvmanila dhruvmanila force-pushed the dhruv/red-knot-lsp-1 branch 5 times, most recently from afeb65c to 01b91db Compare August 4, 2024 09:33
Copy link
Contributor

github-actions bot commented Aug 4, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

warning: Detected debug build without --no-cache.
error: Failed to parse examples/Chat_finetuning_data_prep.ipynb:6:18:25: Unparenthesized generator expression cannot be used here
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_confluence.ipynb:15:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_gmail.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_jira.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_notion.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_doc.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_text.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sql_database.ipynb:2:2:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function.ipynb:37:1:13: Simple statements must be separated by newlines or semicolons

Formatter (preview)

ℹ️ ecosystem check encountered format errors. (no format changes; 1 project error)

openai/openai-cookbook (error)

ruff format --preview

warning: Detected debug build without --no-cache.
error: Failed to parse examples/Chat_finetuning_data_prep.ipynb:6:18:25: Unparenthesized generator expression cannot be used here
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_confluence.ipynb:15:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_gmail.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_jira.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_notion.ipynb:15:1:1: Expected an expression
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_doc.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sharepoint_text.ipynb:28:1:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_action_sql_database.ipynb:2:2:5: Simple statements must be separated by newlines or semicolons
error: Failed to parse examples/chatgpt/gpt_actions_library/gpt_middleware_azure_function.ipynb:37:1:13: Simple statements must be separated by newlines or semicolons

@dhruvmanila dhruvmanila marked this pull request as ready for review August 4, 2024 09:58
Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overall looks great. We may not have to add system_mut after all. I added an inline comment for that.

The other two things that we have to figure out are but I would recommend removing for now:

  • Settings
  • API exposed to the LSP: Calling system_path_to_file can panic if any other thread calls Handle::get_mut. I think the LSP should instead use a facade with a limited set of queries that wrapps the queries in RootDatabase::with_db, similar to rust analyzer. Any queries not exposed on the facade should be considered internal.

#[allow(clippy::cast_sign_loss)]
Ok(Metadata::new(
FileRevision::new(document.version() as u128),
None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to query the os file system to retrieve the permissions on disk.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it depends on how are the permissions being utilized? I don't see any usages currently. I think one potential use-case from an editor perspective would be to re-check the file when the permission is changed but the content remains the same. Is that so?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no immediate use case for it anymore now that we have shifted towards building a type checker. The only use we have for permissions are the permission-lints.

@dhruvmanila dhruvmanila force-pushed the dhruv/red-knot-lsp-1 branch from a434464 to df7ad47 Compare August 6, 2024 10:14
@dhruvmanila dhruvmanila force-pushed the dhruv/red-knot-lsp-1 branch from df7ad47 to f66546a Compare August 6, 2024 10:35
@dhruvmanila dhruvmanila requested a review from MichaReiser August 6, 2024 10:36
@dhruvmanila
Copy link
Member Author

I'll add a basic support for non-workspace files in a follow-up PR.

Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. This looks great!

If you can find the time, it would be great to have a follow up PR to store the file on DocumentController rather than looking it everytime we call check_file (or in close etc).

We should also add a TODO that the LSP shouldn't use any queries and instead should only use methods exposed on RootDatabase.

@dhruvmanila dhruvmanila force-pushed the dhruv/red-knot-lsp-1 branch from 8c1ab63 to 5c92d59 Compare August 6, 2024 11:22
@dhruvmanila dhruvmanila enabled auto-merge (squash) August 6, 2024 11:22
@dhruvmanila dhruvmanila merged commit e91a0fe into main Aug 6, 2024
17 checks passed
@dhruvmanila dhruvmanila deleted the dhruv/red-knot-lsp-1 branch August 6, 2024 11:27
dhruvmanila added a commit to astral-sh/ruff-vscode that referenced this pull request Aug 6, 2024
## Summary

This PR provides minimal support for the `red_knot` binary to run the
server without performing any version checks.

## Test Plan

I've been using this branch for my testing of
astral-sh/ruff#12624
dylwil3 pushed a commit to dylwil3/ruff that referenced this pull request Aug 7, 2024
## Summary

This PR adds basic LSP implementation for the Red Knot project.

This is basically a fork of the existing `ruff_server` crate into a
`red_knot_server` crate. The following are the main differences:
1. The `Session` stores a map from workspace root to the corresponding
Red Knot database (`RootDatabase`).
2. The database is initialized with the newly implemented `LSPSystem`
(implementation of `System` trait)
3. The `LSPSystem` contains the server index corresponding to each
workspace and an underlying OS system implementation. For certain
methods, the system first checks if there's an open document in LSP
system and returns the information from that. Otherwise, it falls back
to the OS system to get that information. These methods are
`path_metadata`, `read_to_string` and `read_to_notebook`
4. Add `as_any_mut` method for `System`

**Why fork?**

Forking allows us to experiment with the functionalities that are
specific to Red Knot. The architecture is completely different and so
the requirements for an LSP implementation are different as well. For
example, Red Knot only supports a single workspace, so the LSP system
needs to map the multi-workspace support to each Red Knot instance. In
the end, the server code isn't too big, it will be easier to implement
Red Knot specific functionality without worrying about existing server
limitations and it shouldn't be difficult to port the existing server.

## Review

Most of the server files hasn't been changed. I'm going to list down the
files that have been changed along with highlight the specific part of
the file that's changed from the existing server code.

Changed files:
* Red Knot CLI implementation:
https://github.com/astral-sh/ruff/pull/12624/files#diff-579596339a29d3212a641232e674778c339b446de33b890c7fdad905b5eb50e1
* In
https://github.com/astral-sh/ruff/pull/12624/files#diff-b9a9041a8a2bace014bf3687c3ef0512f25e0541f112fad6131b14242f408db6,
server capabilities have been updated, dynamic capability registration
is removed
* In
https://github.com/astral-sh/ruff/pull/12624/files#diff-b9a9041a8a2bace014bf3687c3ef0512f25e0541f112fad6131b14242f408db6,
the API for `clear_diagnostics` now take in a `Url` instead of
`DocumentQuery` as the document version doesn't matter when clearing
diagnostics after a document is closed
*
[`did_close`](https://github.com/astral-sh/ruff/pull/12624/files#diff-9271370102a6f3be8defaca40c82485b0048731942520b491a3bdd2ee0e25493),
[`did_close_notebook`](https://github.com/astral-sh/ruff/pull/12624/files#diff-96fb53ffb12c1694356e17313e4bb37b3f0931e887878b5d7c896c19ff60283b),
[`did_open`](https://github.com/astral-sh/ruff/pull/12624/files#diff-60e852cf1aa771e993131cabf98eb4c467963a8328f10eccdb43b3e8f0f1fb12),
[`did_open_notebook`](https://github.com/astral-sh/ruff/pull/12624/files#diff-ac356eb5e36c3b2c1c135eda9dfbcab5c12574d1cb77c71f7da8dbcfcfb2d2f1)
are updated to open / close file from the corresponding Red Knot
workspace
* The [diagnostic
handler](https://github.com/astral-sh/ruff/pull/12624/files#diff-4475f318fd0290d0292834569a7df5699debdcc0a453b411b8c3d329f1b879d9)
is updated to request diagnostics from Red Knot
* The [`Session::new`] method in
https://github.com/astral-sh/ruff/pull/12624/files#diff-55c96201296200c1cab37c8b0407b6c733381374b94be7ae50563bfe95264e4d
is updated to construct the Red Knot databases for each workspace. It
also contains the `index_mut` and `MutIndexGuard` implementation
* And, `LSPSystem` implementation is in
https://github.com/astral-sh/ruff/pull/12624/files#diff-4ed62bd359c43b0bf1a13f04349dcd954966934bb8d544de7813f974182b489e

## Test Plan

First, configure VS Code to use the `red_knot` binary

1. Build the `red_knot` binary by `cargo build`
2. Update the VS Code extension to specify the path to this binary
```json
{
	"ruff.path": ["/path/to/ruff/target/debug/red_knot"]
}
```
3. Restart VS Code

Now, open a file containing red-knot specific diagnostics, close the
file and validate that diagnostics disappear.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ty Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants