From d9443e0cb21195e2f38f2fdc341217720fca0f80 Mon Sep 17 00:00:00 2001 From: kunlinglio <142668432+kunlinglio@users.noreply.github.com> Date: Fri, 8 May 2026 10:43:40 +0800 Subject: [PATCH 1/4] Enhance path resolution caching with TTL and update Document structure --- CHANGELOG.md | 1 + src/document/mod.rs | 19 ++++++++++---- src/resolver/collect.rs | 55 ++++++++++++++++++++++++----------------- src/resolver/mod.rs | 13 +++++++--- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d06b0..d71d22e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ Path Server is now published to crates.io! You can now install it via `cargo ins ### Added - Added new step to release workflow to publish to crates.io. +- Added TTL to resolved path cache to keep path highlighting update with file system changes. ### Improved - **VS Code**: Updated icon color from #007fd4 to #0098ff to keep consistent with VS Code's color palette. diff --git a/src/document/mod.rs b/src/document/mod.rs index 5baad6a..325219f 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -2,11 +2,13 @@ mod language; pub use language::Language; use line_index::{LineIndex, TextSize, WideEncoding, WideLineCol}; +use std::sync::Arc; use tokio::sync::Mutex; use tower_lsp_server::ls_types; use tree_sitter::Tree; use crate::error::*; +use crate::parser::PathCandidate; use crate::parser::{new_tree, update_tree}; use crate::resolver::ResolvedPathCache; @@ -16,8 +18,12 @@ pub struct Document { pub text: String, /// Language if from lsp client pub language: Language, - /// Tokens cache - pub tokens: Mutex, + /// Cached tokens from parser (candidate paths) + /// This will never expired until the document content changes + pub candidate_path: Mutex>>>>, + /// Cached exists paths (resolved from candidate paths) + /// This may expired to avoid file system change + pub resolved_path: Mutex>, /// Index for line/column -> offset calculations index: LineIndex, /// Tree-sitter AST tree for incremental parsing @@ -31,7 +37,8 @@ impl Default for Document { index: LineIndex::new(""), language: Language::Unknown("".into()), tree: None, - tokens: Mutex::new(ResolvedPathCache::new()), + candidate_path: Mutex::new(None), + resolved_path: Mutex::new(None), } } } @@ -43,7 +50,8 @@ impl Document { text, language: Language::from_id(language_id), tree: None, - tokens: Mutex::new(ResolvedPathCache::new()), + candidate_path: Mutex::new(None), + resolved_path: Mutex::new(None), }; doc.tree = new_tree(&doc)?; Ok(doc) @@ -74,7 +82,8 @@ impl Document { index: new_index, tree: None, language: old_document.language.clone(), - tokens: Mutex::new(ResolvedPathCache::new()), + candidate_path: Mutex::new(None), + resolved_path: Mutex::new(None), }; // update tree diff --git a/src/resolver/collect.rs b/src/resolver/collect.rs index aed2f0b..fb38426 100644 --- a/src/resolver/collect.rs +++ b/src/resolver/collect.rs @@ -9,7 +9,7 @@ use crate::error::*; use crate::fs; use crate::parser::{PathCandidate, parse_document}; -use super::{ResolvedPath, ResolvedPathCache}; +use super::{RESOLVE_CACHE_TTL, ResolvedPath, ResolvedPathCache}; pub async fn resolve_all( document: &Document, @@ -17,21 +17,22 @@ pub async fn resolve_all( workspace_roots: &[String], doc_parent: &Option, ) -> PathServerResult>> { - let mut cache = document.tokens.lock().await; + let mut cache = document.resolved_path.lock().await; let signature = config.signature()?; - if let Some(tokens) = &cache.tokens + if let Some(cache) = &*cache && cache.config_signature == signature + && cache.created_at.elapsed() < RESOLVE_CACHE_TTL { // hit - return Ok(tokens.clone()); + return Ok(cache.tokens.clone()); } // miss let tokens = compute_tokens(document, config, workspace_roots, doc_parent).await?; let shared_tokens = Arc::new(tokens); - *cache = ResolvedPathCache { - tokens: Some(Arc::clone(&shared_tokens)), - config_signature: signature, - }; + *cache = Some(ResolvedPathCache::new( + Arc::clone(&shared_tokens), + signature, + )); Ok(shared_tokens) } @@ -42,20 +43,28 @@ async fn compute_tokens( doc_parent: &Option, ) -> PathServerResult> { let home = std::env::var("HOME").ok(); + let path_candidates = if let Some(cache) = &*document.candidate_path.lock().await { + // hit + cache.clone() + } else { + // miss + let path_candidates: Arc>> = + Arc::new(parse_document(document).into_iter().flatten().collect()); + *document.candidate_path.lock().await = Some(path_candidates.clone()); + path_candidates + }; let tokens: Vec = - future::try_join_all(parse_document(document).into_iter().flatten().map( - |candidates| async { - filter_exist_path( - candidates, - config, - workspace_roots, - doc_parent.as_ref(), - home.as_ref(), - document, - ) - .await - }, - )) + future::try_join_all(path_candidates.iter().map(|candidates| async { + filter_exist_path( + candidates, + config, + workspace_roots, + doc_parent.as_ref(), + home.as_ref(), + document, + ) + .await + })) .await? .into_iter() .flatten() @@ -68,14 +77,14 @@ async fn compute_tokens( } async fn filter_exist_path( - candidates: Vec, + candidates: &Vec, config: &Config, workspace_roots: &[String], parent: Option<&String>, home: Option<&String>, document: &Document, ) -> PathServerResult> { - let resolved = future::try_join_all(candidates.into_iter().map(|candidate| async move { + let resolved = future::try_join_all(candidates.iter().map(|candidate| async move { let path = PathBuf::from(&candidate.content); if path.is_absolute() { if fs::exists(&path).await { diff --git a/src/resolver/mod.rs b/src/resolver/mod.rs index 6af0701..687c4ed 100644 --- a/src/resolver/mod.rs +++ b/src/resolver/mod.rs @@ -7,6 +7,8 @@ pub use query::resolve_at_pos; use std::path::PathBuf; use std::sync::Arc; +const RESOLVE_CACHE_TTL: std::time::Duration = std::time::Duration::from_secs(10); + #[derive(Debug, Clone)] pub struct ResolvedPath { pub start: (usize, usize), // (line, character) in utf16 @@ -26,15 +28,18 @@ impl ResolvedPath { #[derive(Debug)] pub struct ResolvedPathCache { - tokens: Option>>, + tokens: Arc>, config_signature: String, + /// For expiration + created_at: std::time::Instant, } impl ResolvedPathCache { - pub fn new() -> Self { + pub fn new(tokens: Arc>, config_signature: String) -> Self { Self { - tokens: None, - config_signature: String::new(), + tokens, + config_signature, + created_at: std::time::Instant::now(), } } } From b8deeda76613c97768d2871a109d990ec95f3951 Mon Sep 17 00:00:00 2001 From: kunlinglio <142668432+kunlinglio@users.noreply.github.com> Date: Fri, 8 May 2026 10:45:55 +0800 Subject: [PATCH 2/4] Fix clippy complaint --- src/resolver/collect.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resolver/collect.rs b/src/resolver/collect.rs index fb38426..2f682bf 100644 --- a/src/resolver/collect.rs +++ b/src/resolver/collect.rs @@ -77,7 +77,7 @@ async fn compute_tokens( } async fn filter_exist_path( - candidates: &Vec, + candidates: &[PathCandidate], config: &Config, workspace_roots: &[String], parent: Option<&String>, @@ -89,7 +89,7 @@ async fn filter_exist_path( if path.is_absolute() { if fs::exists(&path).await { PathServerResult::Ok(vec![ - candidate_to_resolved(&candidate, &path, document).await?, + candidate_to_resolved(candidate, &path, document).await?, ]) } else { PathServerResult::Ok(vec![]) From 2822723055bba97245fdb28e31e788601d3b3362 Mon Sep 17 00:00:00 2001 From: kunlinglio <142668432+kunlinglio@users.noreply.github.com> Date: Fri, 8 May 2026 10:56:29 +0800 Subject: [PATCH 3/4] Enhance error handling for compute_tokens() --- src/resolver/collect.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/resolver/collect.rs b/src/resolver/collect.rs index 2f682bf..615a4d6 100644 --- a/src/resolver/collect.rs +++ b/src/resolver/collect.rs @@ -48,8 +48,7 @@ async fn compute_tokens( cache.clone() } else { // miss - let path_candidates: Arc>> = - Arc::new(parse_document(document).into_iter().flatten().collect()); + let path_candidates: Arc>> = Arc::new(parse_document(document)?); *document.candidate_path.lock().await = Some(path_candidates.clone()); path_candidates }; From 35a8dbe3acbb85ed87c4e66a673c02b6d24d2fbf Mon Sep 17 00:00:00 2001 From: kunlinglio <142668432+kunlinglio@users.noreply.github.com> Date: Fri, 8 May 2026 10:58:04 +0800 Subject: [PATCH 4/4] Fix grammar issues --- CHANGELOG.md | 2 +- src/document/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d71d22e..fc0a128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ Path Server is now published to crates.io! You can now install it via `cargo ins ### Added - Added new step to release workflow to publish to crates.io. -- Added TTL to resolved path cache to keep path highlighting update with file system changes. +- Added TTL to resolved path cache to keep path highlighting up to date with file system changes. ### Improved - **VS Code**: Updated icon color from #007fd4 to #0098ff to keep consistent with VS Code's color palette. diff --git a/src/document/mod.rs b/src/document/mod.rs index 325219f..866cb88 100644 --- a/src/document/mod.rs +++ b/src/document/mod.rs @@ -19,10 +19,10 @@ pub struct Document { /// Language if from lsp client pub language: Language, /// Cached tokens from parser (candidate paths) - /// This will never expired until the document content changes + /// This will never expire until the document content changes pub candidate_path: Mutex>>>>, /// Cached exists paths (resolved from candidate paths) - /// This may expired to avoid file system change + /// This may expire to reflect file system changes pub resolved_path: Mutex>, /// Index for line/column -> offset calculations index: LineIndex,