Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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.
Expand Down
19 changes: 14 additions & 5 deletions src/document/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,8 +18,12 @@ pub struct Document {
pub text: String,
/// Language if from lsp client
pub language: Language,
/// Tokens cache
pub tokens: Mutex<ResolvedPathCache>,
/// Cached tokens from parser (candidate paths)
/// This will never expire until the document content changes
pub candidate_path: Mutex<Option<Arc<Vec<Vec<PathCandidate>>>>>,
/// Cached exists paths (resolved from candidate paths)
/// This may expire to reflect file system changes
pub resolved_path: Mutex<Option<ResolvedPathCache>>,
/// Index for line/column -> offset calculations
index: LineIndex,
/// Tree-sitter AST tree for incremental parsing
Expand All @@ -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),
}
}
}
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
56 changes: 32 additions & 24 deletions src/resolver/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,30 @@ 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,
config: &Config,
workspace_roots: &[String],
doc_parent: &Option<String>,
) -> PathServerResult<Arc<Vec<ResolvedPath>>> {
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
{
Comment on lines +20 to 25
// 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)
}

Expand All @@ -42,20 +43,27 @@ async fn compute_tokens(
doc_parent: &Option<String>,
) -> PathServerResult<Vec<ResolvedPath>> {
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<Vec<Vec<PathCandidate>>> = Arc::new(parse_document(document)?);
*document.candidate_path.lock().await = Some(path_candidates.clone());
path_candidates
};
let tokens: Vec<ResolvedPath> =
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()
Expand All @@ -68,19 +76,19 @@ async fn compute_tokens(
}

async fn filter_exist_path(
candidates: Vec<PathCandidate>,
candidates: &[PathCandidate],
config: &Config,
workspace_roots: &[String],
parent: Option<&String>,
home: Option<&String>,
document: &Document,
) -> PathServerResult<Vec<ResolvedPath>> {
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 {
PathServerResult::Ok(vec![
candidate_to_resolved(&candidate, &path, document).await?,
candidate_to_resolved(candidate, &path, document).await?,
])
} else {
PathServerResult::Ok(vec![])
Expand Down
13 changes: 9 additions & 4 deletions src/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,15 +28,18 @@ impl ResolvedPath {

#[derive(Debug)]
pub struct ResolvedPathCache {
tokens: Option<Arc<Vec<ResolvedPath>>>,
tokens: Arc<Vec<ResolvedPath>>,
config_signature: String,
/// For expiration
created_at: std::time::Instant,
}

impl ResolvedPathCache {
pub fn new() -> Self {
pub fn new(tokens: Arc<Vec<ResolvedPath>>, config_signature: String) -> Self {
Self {
tokens: None,
config_signature: String::new(),
tokens,
config_signature,
created_at: std::time::Instant::now(),
}
}
}
Loading