From c05db3361dd933535270250fbdfab030a65f8e05 Mon Sep 17 00:00:00 2001 From: Felix Zeller Date: Wed, 6 Dec 2023 00:28:03 -0500 Subject: [PATCH] Semi working lsp references with new vault api --- TestFiles/Another Test.md | 2 ++ src/main.rs | 23 +++++++++++++++++++++++ src/references.rs | 37 +++++++++++++++++++++++++++++++++++++ src/vault.rs | 21 +++++++++++++++++++-- 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 src/references.rs diff --git a/TestFiles/Another Test.md b/TestFiles/Another Test.md index 410931cb..9b618b7a 100644 --- a/TestFiles/Another Test.md +++ b/TestFiles/Another Test.md @@ -10,3 +10,5 @@ This file is popppinnng, 2 links to it. [[Completion]] [[folder/File]] + +[[Test#Heading 1 2]] diff --git a/src/main.rs b/src/main.rs index 7d14cf1f..6418a0ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use std::ops::Deref; use std::path::Path; +use references::references; use tokio::sync::RwLock; use gotodef::goto_definition; @@ -11,6 +12,7 @@ use vault::{Vault, construct_vault, reconstruct_vault}; mod vault; mod gotodef; +mod references; #[derive(Debug)] @@ -102,6 +104,27 @@ impl LanguageServer for Backend { return Ok(result.map(|l| GotoDefinitionResponse::Scalar(l))) } + + async fn references(&self, params: ReferenceParams) -> Result>> { + let position = params.text_document_position.position; + + let vault_option = self.vault.read().await; + let Some(vault) = vault_option.deref() else { + self.client.log_message(MessageType::ERROR, "Vault is not initialized").await; + return Err(Error::new(ErrorCode::ServerError(0))); + }; + let Ok(path) = params.text_document_position.text_document.uri.to_file_path() else { + self.client.log_message(MessageType::ERROR, "Failed to parse URI path").await; + return Err(Error::new(ErrorCode::ServerError(0))); + }; + self.client.log_message(MessageType::INFO, format!( "Path: {:?}", path )).await; + + let locations = references(vault, position, &path); + // log locations + self.client.log_message(MessageType::INFO, format!("Result {:?}", locations)).await; + Ok(locations) + } + async fn shutdown(&self) -> Result<()> { Ok(()) } diff --git a/src/references.rs b/src/references.rs new file mode 100644 index 00000000..e36019f5 --- /dev/null +++ b/src/references.rs @@ -0,0 +1,37 @@ +use std::path::Path; + +use itertools::Itertools; +use tower_lsp::lsp_types::{Position, Location, Url}; + +use crate::vault::Vault; + +pub fn references(vault: &Vault, cursor_position: Position, path: &Path) -> Option> { + // First we need to get the linkable node under the cursor + let path = path.to_path_buf(); + let linkable_nodes = vault.select_linkable_nodes_for_path(&path); + let linkable = linkable_nodes + .iter() + .find(|&l| + l.get_range().start.line <= cursor_position.line && + l.get_range().end.line >= cursor_position.line && + l.get_range().start.character <= cursor_position.character && + l.get_range().end.character >= cursor_position.character + )?; + + println!("Linkable: {:?}", linkable); + + let reference_text = linkable.get_refname(&vault.root_dir())?; + + let references = vault.select_links(None)?; + let locations = references.into_iter() + .filter(|&r| r.1.reference_text == reference_text) + .map(|link| Url::from_file_path(link.0).map(|good| Location {uri: good, range: link.1.range})) + .flat_map(|l| match l.is_ok() { + true => Some(l), + false => None + }) + .flatten() + .collect_vec(); + + Some(locations) +} diff --git a/src/vault.rs b/src/vault.rs index 9f239e31..82ab859b 100644 --- a/src/vault.rs +++ b/src/vault.rs @@ -141,6 +141,7 @@ pub struct Vault { root_dir: PathBuf, } +#[derive(Debug)] /// Linkable algebreic type that easily allows for new linkable nodes to be added if necessary and everything in it should live the same amount because it is all from vault /// These will also use the current obsidian syntax to come up with reference names for the linkables. These are the things that are using in links ([[Refname]]) pub enum Linkable<'a> { @@ -181,7 +182,7 @@ impl Linkable<'_> { impl Vault { /// Select all links ([[Link]]) in a file - pub fn select_links(&self, path: Option<&Path>) -> Option> { + pub fn select_links<'a>(&'a self, path: Option<&'a Path>) -> Option> { match path { Some(path) => self.files.get(path).map(|md| &md.links).map(|vec| vec.iter().map(|i| (path, i)).collect()), None => Some(self.files.iter().map(|(path, md)| md.links.iter().map(|link| (path.as_path(), link))).flatten().collect()) @@ -205,6 +206,22 @@ impl Vault { return files.into_iter().chain(headings).into_iter().chain(indexed_blocks).collect() } + pub fn select_linkable_nodes_for_path<'a>(&'a self, path: &'a PathBuf) -> Vec> { + let files = self.files.get(path); + let file_linkables = files.iter() + .map(|md| Linkable::MDFile(path, md)); + + let headings = self.files.iter() + .flat_map(|(path, md)| md.headings.iter()) + .map(|h| Linkable::Heading(path, h)); + + let indexed_blocks = self.files.iter() + .flat_map(|(path, md)| md.indexed_blocks.iter()) + .map(|ib| Linkable::IndexedBlock(path, ib)); + + return file_linkables.into_iter().chain(headings).into_iter().chain(indexed_blocks).collect() + } + pub fn root_dir(&self) -> &PathBuf { &self.root_dir } @@ -217,7 +234,7 @@ pub struct MDFile { indexed_blocks: Vec } -#[derive(Debug, PartialEq, Eq, Default)] +#[derive(Debug, PartialEq, Eq, Default, Clone)] pub struct Link { pub reference_text: String, pub display_text: Option,