astral-sh/ruff-lsp

Provide a link to rule doc using `textDocument/documentLink`

harupy opened this issue · 4 comments

Is this more discoverable than hover?

Screen.Recording.2023-10-15.at.13.43.49.mov
Code change

diff --git a/ruff_lsp/server.py b/ruff_lsp/server.py
index ea7df2a..a1eb35a 100755
--- a/ruff_lsp/server.py
+++ b/ruff_lsp/server.py
@@ -23,6 +23,7 @@ from lsprotocol.types import (
     TEXT_DOCUMENT_DID_OPEN,
     TEXT_DOCUMENT_DID_SAVE,
     TEXT_DOCUMENT_FORMATTING,
+    TEXT_DOCUMENT_DOCUMENT_LINK,
     TEXT_DOCUMENT_HOVER,
     AnnotatedTextEdit,
     ClientCapabilities,
@@ -39,6 +40,8 @@ from lsprotocol.types import (
     DidOpenTextDocumentParams,
     DidSaveTextDocumentParams,
     DocumentFormattingParams,
+    DocumentLink,
+    DocumentLinkParams,
     Hover,
     HoverParams,
     InitializeParams,
@@ -397,6 +400,59 @@ async def hover(params: HoverParams) -> Hover | None:
     return None
 
 
+@LSP_SERVER.feature(TEXT_DOCUMENT_DOCUMENT_LINK)
+async def document_link(params: DocumentLinkParams) -> list[DocumentLink] | None:
+    """LSP handler for textDocument/documentLink request."""
+    document = LSP_SERVER.workspace.get_document(params.text_document.uri)
+
+    links: list[DocumentLink] = []
+    for line_index, line in enumerate(document.lines):
+        match = NOQA_REGEX.search(line)
+        if not match:
+            continue
+
+        codes = match.group("codes")
+        if not codes:
+            continue
+
+        codes_start = match.start("codes")
+        for match in CODE_REGEX.finditer(codes):
+            start, end = match.span()
+            start += codes_start
+            end += codes_start
+            code = match.group()
+            result = await _run_subcommand_on_document(
+                document,
+                VERSION_REQUIREMENT_LINTER,
+                args=[
+                    "--explain",
+                    code,
+                    "--format",
+                    "json",
+                ],
+            )
+            log_to_output(f"result.stdout: {result.stdout}")
+            if result.stdout:
+                name = json.loads(result.stdout.decode("utf-8")).get("name")
+                links.append(
+                    DocumentLink(
+                        range=Range(
+                            start=Position(
+                                line=line_index,
+                                character=start,
+                            ),
+                            end=Position(
+                                line=line_index,
+                                character=end,
+                            ),
+                        ),
+                        target=f"https://docs.astral.sh/ruff/rules/{name}",
+                    )
+                )
+
+    return links
+
+
 ###
 # Code Actions.
 ###

I think that seems good. What about you, @dhruvmanila?

Yeah, it's good. A use case would be to have a keybinding to open the website directly instead of hover.

@charliermarsh @dhruvmanila Thanks for the comments. Do you think the hover feature should be kept?

Yeah, I think the hover should still be kept.