Skip to content

Support analysis / types in Rust LSP #881

@sonnyp

Description

@sonnyp
Contributor

In #868 we added rust-analyzer LSP support.

For some reason it only does syntax diagnostics, not types.

Activity

added a commit that references this issue on Feb 12, 2024
Hofer-Julian

Hofer-Julian commented on Feb 14, 2024

@Hofer-Julian
Contributor

I've created two small reproducer scripts in Python and JavaScript.
They show the same behavior:

Python

# %%
import json
from subprocess import Popen, PIPE
import re
from typing import Any
import uuid
from pathlib import Path

# Path to the Rust Analyzer executable and the file to analyze
rust_analyzer_path = "rust-analyzer"
file_path = Path("src/main.rs").absolute()


def extract_content_length(response: str) -> int:
    # Split the response into lines
    lines = response.split("\r\n")

    # Find the line that starts with "Content-Length:"
    content_length_line = next(
        (line for line in lines if line.startswith("Content-Length:")), None
    )

    if content_length_line is not None:
        # Extract the number from the line using a regular expression
        match = re.search(r"Content-Length: (\d+)", content_length_line)
        if match:
            return int(match.group(1))
        else:
            raise ValueError("No number found in Content-Length line")
    else:
        raise ValueError("No Content-Length line found")


def send_request(server: Popen, request: dict[str, Any]) -> Any:
    send_notification(server, request)
    return receive_response(server)


def receive_response(server: Popen) -> Any:
    response = ""
    assert server.stdout is not None
    while not response.endswith("\r\n\r\n"):
        response += server.stdout.read(1).decode()
    content_length = extract_content_length(response)
    return json.loads(server.stdout.read(content_length).decode())


def send_notification(server: Popen, request: dict[str, Any]) -> None:
    request_json = json.dumps(request)
    header = f"Content-Length: {len(request_json.encode())}\r\n\r\n"
    assert server.stdin is not None
    server.stdin.write(header.encode())
    server.stdin.write(request_json.encode())
    server.stdin.flush()


# %%
# Start the Rust Analyzer server
server = Popen(
    [rust_analyzer_path],
    stdin=PIPE,
    stdout=PIPE,
)

# %%
# Initialize the LSP client
initialize_params: dict[str, Any] = {
    "processId": None,
    "capabilities": {},
    "initializationOptions": {"checkOnSave": {"enable": False}},
    "rootUri": f"file://{file_path.parent}",
}
initalize_request_id = uuid.uuid4()
initialize_request = {
    "jsonrpc": "2.0",
    "id": str(initalize_request_id),
    "method": "initialize",
    "params": initialize_params,
}

result = send_request(server, initialize_request)
print(result)
# %%
send_notification(server, {"jsonrpc": "2.0", "method": "initialized"})

# # %%
# # Send textDocument/didOpen notification
did_open_params = {
    "textDocument": {
        "uri": f"file://{file_path}",
        "languageId": "rust",
        "version": 1,
        "text": file_path.read_text(),
    }
}
did_open_notification = {
    "jsonrpc": "2.0",
    "method": "textDocument/didOpen",
    "params": did_open_params,
}

send_notification(server, did_open_notification)
# %%
result = receive_response(server)
print(result)

# %%
# Send textDocument/didChange notification
did_change_params = {
    "textDocument": {
        "uri": f"file://{file_path}",
        "version": 2,
    },
    "contentChanges": [
        {
            "text": file_path.read_text(),
        }
    ],
}
did_change_notification = {
    "jsonrpc": "2.0",
    "method": "textDocument/didChange",
    "params": did_change_params,
}

send_notification(server, did_change_notification)

# %%
result = receive_response(server)
print(result)

# %%
exit_request = json.dumps(
    {
        "jsonrpc": "2.0",
        "method": "exit",
    }
)
send_notification(server, exit_request)

server.terminate()

NodeJs

const { spawn } = require('child_process');
const path = require('path');

// Path to the Rust Analyzer executable and the file to analyze
const rustAnalyzerPath = 'rust-analyzer';
const filePath = path.resolve('src/main.rs');

// Start the Rust Analyzer server
const server = spawn(rustAnalyzerPath, [], { stdio: ['pipe', 'pipe', process.stderr] });

// Function to send a JSON-RPC request to the server
function sendRequest(method, params, id) {
    const request = JSON.stringify({ jsonrpc: '2.0', method, params, id });
    const contentLength = Buffer.byteLength(request, 'utf8');
    server.stdin.write(`Content-Length: ${contentLength}\r\n\r\n${request}`);
}

// Function to send a JSON-RPC notification to the server
function sendNotification(method, params) {
    const notification = JSON.stringify({ jsonrpc: '2.0', method, params });
    const contentLength = Buffer.byteLength(notification, 'utf8');
    server.stdin.write(`Content-Length: ${contentLength}\r\n\r\n${notification}`);
}


// Initialize the LSP client
sendRequest('initialize', {
    processId: process.pid,
    rootPath: path.dirname(filePath),
    rootUri: `file://${path.dirname(filePath)}`,
    capabilities: {},
}, 1);

// Send the initialized notification
sendNotification('initialized', {});

// Open the file
sendNotification('textDocument/didOpen', {
    textDocument: {
        uri: `file://${filePath}`,
        languageId: 'rust',
        version: 1,
        text: require('fs').readFileSync(filePath, 'utf8'),
    },
});

// Read the server's responses
server.stdout.on('data', (data) => {
    console.log('Server response:', data.toString());
});

// Keep the Node.js process running
setInterval(() => {}, 1000);
Hofer-Julian

Hofer-Julian commented on Feb 14, 2024

@Hofer-Julian
Contributor
added a commit that references this issue on Feb 14, 2024
e06a7a3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @sonnyp@Hofer-Julian

      Issue actions

        Support analysis / types in Rust LSP · Issue #881 · workbenchdev/Workbench