use std::collections::{HashMap, HashSet};
use lsp_types::{Position, Range};
use crate::{parol_ls_grammar::OwnedToken, utils::location_to_range};
#[derive(Debug, Clone, Default)]
pub(crate) struct DefsAndRefs {
definitions: HashSet<Range>,
references: HashSet<Range>,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct SymbolDefs {
pub(crate) symbols: HashMap<String, DefsAndRefs>,
}
impl SymbolDefs {
pub(crate) fn add_definition(&mut self, name: String, range: Range) {
self.symbols
.entry(name)
.or_default()
.definitions
.insert(range);
}
pub(crate) fn add_definition_by_token(&mut self, token: &OwnedToken) {
self.symbols
.entry(token.text().to_string())
.or_default()
.definitions
.insert(location_to_range(&token.location));
}
pub(crate) fn add_reference(&mut self, range: Range, name: &str) {
self.symbols
.entry(name.to_string())
.or_default()
.references
.insert(range);
}
pub(crate) fn add_reference_by_token(&mut self, token: &OwnedToken) {
self.symbols
.entry(token.text().to_string())
.or_default()
.references
.insert(location_to_range(&token.location));
}
pub(crate) fn find_reference(&self, position: Position) -> Option<&str> {
for (name, dr) in &self.symbols {
for range in &dr.references {
if range.start <= position && range.end > position {
return Some(name);
}
}
}
None
}
pub(crate) fn find_definitions(&self, name: &str) -> Option<Vec<Range>> {
self.symbols.get(name).and_then(|s| {
let definitions: Vec<Range> = s.definitions.iter().cloned().collect();
if definitions.is_empty() {
None
} else {
Some(definitions)
}
})
}
pub(crate) fn find_references(&self, name: &str) -> Vec<&Range> {
self.symbols
.get(name)
.map_or_else(Vec::new, |s| s.references.iter().collect())
}
pub(crate) fn find_reference_range(&self, name: &str, position: Position) -> Option<&Range> {
self.symbols.get(name).and_then(|s| {
s.references
.iter()
.find(|range| range.start <= position && range.end > position)
})
}
}