Rust analyzer init
This commit is contained in:
Generated
+17
-1
@@ -423,6 +423,19 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lsp-types"
|
||||||
|
version = "0.95.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e34d33a8e9b006cd3fc4fe69a921affa097bae4bb65f76271f4644f9a334365"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.8.0"
|
version = "2.8.0"
|
||||||
@@ -446,8 +459,11 @@ version = "0.0.2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cargo_metadata",
|
"cargo_metadata",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
|
"lsp-types 0.95.1",
|
||||||
"mist-codegen",
|
"mist-codegen",
|
||||||
"mist-parser",
|
"mist-parser",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-lsp",
|
"tower-lsp",
|
||||||
]
|
]
|
||||||
@@ -865,7 +881,7 @@ dependencies = [
|
|||||||
"dashmap 5.5.3",
|
"dashmap 5.5.3",
|
||||||
"futures",
|
"futures",
|
||||||
"httparse",
|
"httparse",
|
||||||
"lsp-types",
|
"lsp-types 0.94.1",
|
||||||
"memchr",
|
"memchr",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -16,3 +16,7 @@ dashmap = "6.1.0"
|
|||||||
tokio = { version = "1.52.3", features = ["full", "macros", "rt", "rt-multi-thread"] }
|
tokio = { version = "1.52.3", features = ["full", "macros", "rt", "rt-multi-thread"] }
|
||||||
tower-lsp = "0.20.0"
|
tower-lsp = "0.20.0"
|
||||||
cargo_metadata = "0.23.1"
|
cargo_metadata = "0.23.1"
|
||||||
|
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
lsp-types = "0.95"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
pub mod rust_analyzer;
|
||||||
pub mod transpiler;
|
pub mod transpiler;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -72,6 +73,9 @@ impl LanguageServer for Backend {
|
|||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Some(root) = &*workspace_folder.lock().await {
|
if let Some(root) = &*workspace_folder.lock().await {
|
||||||
transpiler::build(root);
|
transpiler::build(root);
|
||||||
|
rust_analyzer::initialize(root)
|
||||||
|
.await
|
||||||
|
.expect("Failed to initialize rust analyzer");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use std::{path::PathBuf, process::Stdio};
|
||||||
|
|
||||||
|
use lsp_types::{ClientCapabilities, InitializeParams, InitializeResult, Url, WorkspaceFolder};
|
||||||
|
use serde_json::json;
|
||||||
|
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
||||||
|
|
||||||
|
async fn send_lsp_message<W: AsyncWriteExt + Unpin>(
|
||||||
|
writer: &mut W,
|
||||||
|
value: &serde_json::Value,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let payload = serde_json::to_string(value)?;
|
||||||
|
let frame = format!("Content-Length: {}\r\n\r\n{}", payload.len(), payload);
|
||||||
|
writer.write_all(frame.as_bytes()).await?;
|
||||||
|
writer.flush().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_lsp_message<R: AsyncBufReadExt + Unpin>(
|
||||||
|
reader: &mut R,
|
||||||
|
) -> Result<String, Box<dyn std::error::Error>> {
|
||||||
|
let mut line = String::new();
|
||||||
|
let mut content_length = 0;
|
||||||
|
|
||||||
|
// Read headers until we hit the empty separator line (\r\n)
|
||||||
|
loop {
|
||||||
|
line.clear();
|
||||||
|
reader.read_line(&mut line).await?;
|
||||||
|
if line == "\r\n" || line.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if line.to_lowercase().starts_with("content-length:") {
|
||||||
|
content_length = line["content-length:".len()..].trim().parse::<usize>()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if content_length == 0 {
|
||||||
|
return Err("Missing or invalid Content-Length header".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the exact byte buffer payload
|
||||||
|
let mut buffer = vec![0u8; content_length];
|
||||||
|
reader.read_exact(&mut buffer).await?;
|
||||||
|
|
||||||
|
Ok(String::from_utf8(buffer)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialize(root: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Step 1: Spawn rust-analyzer
|
||||||
|
let mut child = tokio::process::Command::new("rust-analyzer")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::null()) // Ignore logs for simplicity
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let mut stdin = child.stdin.take().unwrap();
|
||||||
|
let mut stdout = BufReader::new(child.stdout.take().unwrap());
|
||||||
|
|
||||||
|
let project_uri = Url::from_directory_path(root)
|
||||||
|
.map_err(|_| "Failed to convert path to valid file:// URL")?;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let init_params = InitializeParams {
|
||||||
|
process_id: Some(std::process::id()), // Highly recommended so RA knows if your client dies
|
||||||
|
root_uri: Some(project_uri.clone()), // Provide root_uri as a fallback alongside folders
|
||||||
|
workspace_folders: Some(vec![WorkspaceFolder {
|
||||||
|
uri: project_uri,
|
||||||
|
name: "workspace".to_string(),
|
||||||
|
}]),
|
||||||
|
capabilities: ClientCapabilities {
|
||||||
|
workspace: Some(lsp_types::WorkspaceClientCapabilities {
|
||||||
|
workspace_folders: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
text_document: Some(lsp_types::TextDocumentClientCapabilities {
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let init_request = json!({
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": 1,
|
||||||
|
"method": "initialize",
|
||||||
|
"params": init_params
|
||||||
|
});
|
||||||
|
|
||||||
|
send_lsp_message(&mut stdin, &init_request).await?;
|
||||||
|
eprintln!("-> Sent: initialize");
|
||||||
|
|
||||||
|
// Step 3: Read 'initialize' response
|
||||||
|
let response_str = read_lsp_message(&mut stdout).await?;
|
||||||
|
|
||||||
|
// DEBUG: If it fails again, this print will show you exactly what rust-analyzer sent back!
|
||||||
|
let json_parsed: serde_json::Value = serde_json::from_str(&response_str)?;
|
||||||
|
if let Some(error) = json_parsed.get("error") {
|
||||||
|
panic!(
|
||||||
|
"Rust-analyzer explicitly rejected our request! Error: {:#?}",
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no explicit error, try parsing into the strong type
|
||||||
|
let _response: InitializeResult =
|
||||||
|
serde_json::from_value(json_parsed.get("result").cloned().unwrap_or(json_parsed))?;
|
||||||
|
eprintln!("<- Received: initialize response");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user