diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 69ca1e4..c25d4ec 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -197,15 +197,14 @@ impl LanguageServer for Backend { &self, params: GotoDefinitionParams, ) -> Result> { - let mut source = fs::read_to_string( - params - .text_document_position_params - .text_document - .uri - .to_file_path() - .unwrap(), - ) - .expect("Failed to read source"); + let file_path = params + .text_document_position_params + .text_document + .uri + .to_file_path() + .unwrap(); + + let mut source = fs::read_to_string(&file_path).expect("Failed to read source"); let inject = "__mist_23"; @@ -223,10 +222,32 @@ impl LanguageServer for Backend { .expect("Didn't find injection") .saturating_sub(1); + let (line, character) = find_row_col(&output, inject).unwrap(); + self.client .log_message(MessageType::INFO, format!("Found at {position}")) .await; + self.rust_analyzer + .lock() + .await + .request::(lsp_types::GotoDefinitionParams { + text_document_position_params: lsp_types::TextDocumentPositionParams { + position: lsp_types::Position { + line: line as u32, + character: character as u32, + }, + text_document: lsp_types::TextDocumentIdentifier { + uri: Url::from_file_path(from_mist_to_rust(file_path)) + .expect("failed to generate rs url"), + }, + }, + partial_result_params: lsp_types::PartialResultParams::default(), + work_done_progress_params: lsp_types::WorkDoneProgressParams::default(), + }) + .await + .expect("Failed to send to rust"); + Ok(None) } } @@ -286,3 +307,20 @@ fn insert_at_position(s: &str, line: usize, col: usize, insert: &str) -> String lines.join("\n") } + +fn find_row_col(output: &str, inject: &str) -> Option<(usize, usize)> { + // 1. Find the flat byte index just like your original code + let byte_idx = output.find(inject)?; + + // 2. Slice the string up to the match point + let prefix = &output[..byte_idx]; + + // 3. Row = number of newlines found before the match + 1 (1-indexed) + let row = prefix.lines().count(); + + // 4. Column = character count of the remaining text on the current line + 1 + // (Using .chars().count() ensures it works with multi-byte UTF-8 symbols) + let col = prefix.lines().last().unwrap_or("").chars().count() + 1; + + Some((row, col)) +} diff --git a/crates/analyzer/src/rust_analyzer.rs b/crates/analyzer/src/rust_analyzer.rs index 5ceef93..c168ecb 100644 --- a/crates/analyzer/src/rust_analyzer.rs +++ b/crates/analyzer/src/rust_analyzer.rs @@ -15,92 +15,6 @@ pub struct RustAnalyzer { id: usize, } -impl RustAnalyzer { - pub fn new() -> Result> { - let mut child = tokio::process::Command::new("rust-analyzer") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) // Ignore logs for simplicity - .spawn()?; - - let stdin = child.stdin.take().unwrap(); - let stdout = BufReader::new(child.stdout.take().unwrap()); - - Ok(Self { - stdin, - stdout, - id: 0, - }) - } - - pub async fn init(&mut self, root: &PathBuf) -> Result<(), Box> { - 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()), - root_uri: Some(project_uri.clone()), - 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 _response = self.request::(init_params); - - eprintln!("<- Received: initialize response"); - - Ok(()) - } - - pub async fn request( - &mut self, - params: R::Params, - ) -> Result> { - self.send(R::METHOD, params).await?; - self.read().await - } - - pub async fn send(&mut self, method: &str, req: T) -> std::io::Result<()> { - let id = { - self.id += 1; - self.id - }; - - send_lsp_message( - &mut self.stdin, - &json!({ - "jsonrpc": "2.0", - "id": id, - "method": method, - "params": req, - }), - ) - .await - } - - pub async fn read(&mut self) -> Result> - where - T: for<'de> serde::Deserialize<'de>, - { - read_lsp_message(&mut self.stdout) - .await - .map(|v| serde_json::from_str(&v).unwrap()) - } -} - async fn send_lsp_message( writer: &mut W, value: &serde_json::Value, @@ -140,3 +54,91 @@ async fn read_lsp_message( Ok(String::from_utf8(buffer)?) } + +impl RustAnalyzer { + pub fn new() -> Result> { + let mut child = tokio::process::Command::new("rust-analyzer") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) // Ignore logs for simplicity + .spawn()?; + + let stdin = child.stdin.take().unwrap(); + let stdout = BufReader::new(child.stdout.take().unwrap()); + + Ok(Self { + stdin, + stdout, + id: 0, + }) + } + + pub async fn request( + &mut self, + params: R::Params, + ) -> Result> { + self.send(R::METHOD, params).await?; + self.read().await + } + + pub async fn send(&mut self, method: &str, req: T) -> std::io::Result<()> { + let id = { + self.id += 1; + self.id + }; + + send_lsp_message( + &mut self.stdin, + &json!({ + "jsonrpc": "2.0", + "id": id, + "method": method, + "params": req, + }), + ) + .await + } + + pub async fn read(&mut self) -> Result> + where + T: for<'de> serde::Deserialize<'de>, + { + read_lsp_message(&mut self.stdout) + .await + .map(|v| serde_json::from_str(&v).unwrap()) + } +} + +impl RustAnalyzer { + pub async fn init(&mut self, root: &PathBuf) -> Result<(), Box> { + 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()), + root_uri: Some(project_uri.clone()), + 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 _response = self.request::(init_params); + + eprintln!("<- Received: initialize response"); + + Ok(()) + } +}