use crate::ast::{Node, NodeKind};
use crate::position::{Position, Range};
use crate::workspace::workspace_index::{WorkspaceIndex, SymbolReference};
use lsp_types::*;
use std::collections::HashMap;
use url::Url;
#[derive(Debug, Clone)]
pub struct ReferenceProvider {
workspace_index: WorkspaceIndex,
reference_cache: HashMap<String, Vec<Location>>,
}
impl ReferenceProvider {
pub fn new() -> Self {
Self {
workspace_index: WorkspaceIndex::new(),
reference_cache: HashMap::new(),
}
}
pub fn with_index(workspace_index: WorkspaceIndex) -> Self {
Self {
workspace_index,
reference_cache: HashMap::new(),
}
}
pub fn find_references(&self, params: ReferenceParams) -> Option<Vec<Location>> {
let position = params.text_document_position.position;
let uri = params.text_document_position.text_document.uri;
let symbol_name = self.resolve_symbol_at_position(&uri, position)?;
if let Some(cached) = self.reference_cache.get(&symbol_name) {
return Some(cached.clone());
}
let mut references = self.workspace_index.find_references(&symbol_name);
if !params.context.include_declaration {
references.retain(|loc| {
true
});
}
self.reference_cache.insert(symbol_name.clone(), references.clone());
Some(references)
}
fn resolve_symbol_at_position(&self, uri: &Url, position: Position) -> Option<String> {
Some("example_symbol".to_string())
}
pub fn update_workspace_index(&mut self, workspace_index: WorkspaceIndex) {
self.workspace_index = workspace_index;
self.reference_cache.clear();
}
pub fn clear_cache(&mut self) {
self.reference_cache.clear();
}
pub fn cache_stats(&self) -> (usize, usize) {
let symbol_count = self.reference_cache.len();
let total_references: usize = self.reference_cache.values()
.map(|refs| refs.len())
.sum();
(symbol_count, total_references)
}
}
impl Default for ReferenceProvider {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use perl_tdd_support::must;
#[test]
fn test_reference_provider_creation() {
let provider = ReferenceProvider::new();
let (symbols, references) = provider.cache_stats();
assert_eq!(symbols, 0);
assert_eq!(references, 0);
}
#[test]
fn test_reference_provider_with_index() {
let index = WorkspaceIndex::new();
let provider = ReferenceProvider::with_index(index);
let (symbols, references) = provider.cache_stats();
assert_eq!(symbols, 0);
assert_eq!(references, 0);
}
#[test]
fn test_cache_operations() {
let mut provider = ReferenceProvider::new();
let (symbols, references) = provider.cache_stats();
assert_eq!(symbols, 0);
assert_eq!(references, 0);
provider.clear_cache();
let (symbols, references) = provider.cache_stats();
assert_eq!(symbols, 0);
assert_eq!(references, 0);
}
#[test]
fn test_workspace_index_update() {
let mut provider = ReferenceProvider::new();
let new_index = WorkspaceIndex::new();
provider.update_workspace_index(new_index);
let (symbols, references) = provider.cache_stats();
assert_eq!(symbols, 0);
assert_eq!(references, 0);
}
#[test]
fn test_symbol_resolution() {
let provider = ReferenceProvider::new();
let uri = must(Url::parse("file:///test.pl"));
let position = Position::new(0, 10);
let symbol = provider.resolve_symbol_at_position(&uri, position);
assert!(symbol.is_some());
}
#[test]
fn test_find_references() {
let provider = ReferenceProvider::new();
let params = ReferenceParams {
text_document_position: lsp_types::TextDocumentPositionParams {
text_document: lsp_types::TextDocumentIdentifier {
uri: must(Url::parse("file:///test.pl"))
},
position: Position::new(0, 10),
},
context: lsp_types::ReferenceContext {
include_declaration: true,
},
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
};
let references = provider.find_references(params);
assert!(references.is_some());
}
}