use crate::symbols::{SymbolKind, SymbolLocation, Utf16Range};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ResultSetId(u64);
impl ResultSetId {
pub fn get(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResultSetKind {
References,
CallHierarchy,
TypeHierarchy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HierarchyItem {
pub name: String,
pub detail: Option<String>,
pub kind: SymbolKind,
pub location: SymbolLocation,
pub selection_range: Utf16Range,
pub data_json: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReferencesResultSet {
pub title: String,
pub locations: Vec<SymbolLocation>,
pub is_stale: bool,
}
impl ReferencesResultSet {
fn mentions_uri(&self, uri: &str) -> bool {
self.locations.iter().any(|loc| loc.uri == uri)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallHierarchyIncomingCall {
pub from: HierarchyItem,
pub from_ranges: Vec<Utf16Range>,
}
impl CallHierarchyIncomingCall {
fn mentions_uri(&self, uri: &str) -> bool {
self.from.location.uri == uri
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallHierarchyOutgoingCall {
pub to: HierarchyItem,
pub from_ranges: Vec<Utf16Range>,
}
impl CallHierarchyOutgoingCall {
fn mentions_uri(&self, uri: &str) -> bool {
self.to.location.uri == uri
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallHierarchyResultSet {
pub title: String,
pub roots: Vec<HierarchyItem>,
pub incoming: Vec<CallHierarchyIncomingCall>,
pub outgoing: Vec<CallHierarchyOutgoingCall>,
pub is_stale: bool,
}
impl CallHierarchyResultSet {
fn mentions_uri(&self, uri: &str) -> bool {
self.roots.iter().any(|it| it.location.uri == uri)
|| self.incoming.iter().any(|c| c.mentions_uri(uri))
|| self.outgoing.iter().any(|c| c.mentions_uri(uri))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeHierarchyResultSet {
pub title: String,
pub roots: Vec<HierarchyItem>,
pub supertypes: Vec<HierarchyItem>,
pub subtypes: Vec<HierarchyItem>,
pub is_stale: bool,
}
impl TypeHierarchyResultSet {
fn mentions_uri(&self, uri: &str) -> bool {
self.roots.iter().any(|it| it.location.uri == uri)
|| self.supertypes.iter().any(|it| it.location.uri == uri)
|| self.subtypes.iter().any(|it| it.location.uri == uri)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IntelligenceResultSet {
References(ReferencesResultSet),
CallHierarchy(CallHierarchyResultSet),
TypeHierarchy(TypeHierarchyResultSet),
}
impl IntelligenceResultSet {
pub fn kind(&self) -> ResultSetKind {
match self {
Self::References(_) => ResultSetKind::References,
Self::CallHierarchy(_) => ResultSetKind::CallHierarchy,
Self::TypeHierarchy(_) => ResultSetKind::TypeHierarchy,
}
}
pub fn title(&self) -> &str {
match self {
Self::References(s) => s.title.as_str(),
Self::CallHierarchy(s) => s.title.as_str(),
Self::TypeHierarchy(s) => s.title.as_str(),
}
}
pub fn is_stale(&self) -> bool {
match self {
Self::References(s) => s.is_stale,
Self::CallHierarchy(s) => s.is_stale,
Self::TypeHierarchy(s) => s.is_stale,
}
}
pub fn mark_stale(&mut self) {
match self {
Self::References(s) => s.is_stale = true,
Self::CallHierarchy(s) => s.is_stale = true,
Self::TypeHierarchy(s) => s.is_stale = true,
}
}
fn mentions_uri(&self, uri: &str) -> bool {
match self {
Self::References(s) => s.mentions_uri(uri),
Self::CallHierarchy(s) => s.mentions_uri(uri),
Self::TypeHierarchy(s) => s.mentions_uri(uri),
}
}
}
#[derive(Debug, Default)]
pub struct WorkspaceIntelligence {
next_id: u64,
sets: BTreeMap<ResultSetId, IntelligenceResultSet>,
}
impl WorkspaceIntelligence {
pub fn len(&self) -> usize {
self.sets.len()
}
pub fn is_empty(&self) -> bool {
self.sets.is_empty()
}
pub fn ids(&self) -> Vec<ResultSetId> {
self.sets.keys().cloned().collect()
}
pub fn get(&self, id: ResultSetId) -> Option<&IntelligenceResultSet> {
self.sets.get(&id)
}
pub fn get_mut(&mut self, id: ResultSetId) -> Option<&mut IntelligenceResultSet> {
self.sets.get_mut(&id)
}
pub fn remove(&mut self, id: ResultSetId) -> Option<IntelligenceResultSet> {
self.sets.remove(&id)
}
pub fn clear(&mut self) {
self.sets.clear();
}
pub fn create(&mut self, set: IntelligenceResultSet) -> ResultSetId {
let id = ResultSetId(self.next_id);
self.next_id = self.next_id.saturating_add(1);
self.sets.insert(id, set);
id
}
pub fn create_references(
&mut self,
title: impl Into<String>,
locations: Vec<SymbolLocation>,
) -> ResultSetId {
self.create(IntelligenceResultSet::References(ReferencesResultSet {
title: title.into(),
locations,
is_stale: false,
}))
}
pub fn mark_stale_for_uri(&mut self, uri: &str) -> bool {
let mut changed = false;
for set in self.sets.values_mut() {
if set.is_stale() {
continue;
}
if set.mentions_uri(uri) {
set.mark_stale();
changed = true;
}
}
changed
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::symbols::{Utf16Position, Utf16Range};
#[test]
fn mark_stale_for_uri_marks_matching_sets_only() {
let mut intel = WorkspaceIntelligence::default();
let a_loc = SymbolLocation {
uri: "file:///a.rs".to_string(),
range: Utf16Range::new(Utf16Position::new(0, 0), Utf16Position::new(0, 1)),
};
let b_loc = SymbolLocation {
uri: "file:///b.rs".to_string(),
range: Utf16Range::new(Utf16Position::new(1, 0), Utf16Position::new(1, 1)),
};
let a_id = intel.create_references("refs a", vec![a_loc.clone()]);
let b_id = intel.create_references("refs b", vec![b_loc.clone()]);
assert!(!intel.get(a_id).unwrap().is_stale());
assert!(!intel.get(b_id).unwrap().is_stale());
assert!(intel.mark_stale_for_uri("file:///a.rs"));
assert!(intel.get(a_id).unwrap().is_stale());
assert!(!intel.get(b_id).unwrap().is_stale());
assert!(!intel.mark_stale_for_uri("file:///a.rs"));
}
}