use std::sync::Arc;
use tower_lsp::lsp_types::{Position, SymbolKind, TypeHierarchyItem, Url};
use crate::text::zero_width_range;
fn make_item_from_index(
name: &str,
kind: SymbolKind,
uri: &Url,
start_line: u32,
) -> TypeHierarchyItem {
let range = zero_width_range(start_line);
TypeHierarchyItem {
name: name.to_string(),
kind,
tags: None,
detail: None,
uri: uri.clone(),
range,
selection_range: range,
data: None,
}
}
pub fn prepare_type_hierarchy_from_workspace(
source: &str,
wi: &crate::db::workspace_index::WorkspaceIndexData,
position: Position,
) -> Option<TypeHierarchyItem> {
use crate::index::file_index::ClassKind;
use crate::text::word_at_position;
let word = word_at_position(source, position)?;
let refs = wi.classes_by_name.get(&word)?;
let (uri, cls) = wi.at(*refs.first()?)?;
let kind = match cls.kind {
ClassKind::Class | ClassKind::Trait => SymbolKind::CLASS,
ClassKind::Interface => SymbolKind::INTERFACE,
ClassKind::Enum => SymbolKind::ENUM,
};
Some(make_item_from_index(&cls.name, kind, uri, cls.start_line))
}
pub fn supertypes_of_from_workspace(
item: &TypeHierarchyItem,
wi: &crate::db::workspace_index::WorkspaceIndexData,
) -> Vec<TypeHierarchyItem> {
use crate::index::file_index::ClassKind;
let mut super_pairs: Vec<(Arc<str>, Option<&crate::index::file_index::FileIndex>)> = Vec::new();
if let Some(refs) = wi.classes_by_name.get(&item.name) {
for r in refs {
if let Some((_, cls)) = wi.at(*r) {
let file_idx = wi.files.get(r.file as usize).map(|(_, idx)| idx.as_ref());
if let Some(p) = &cls.parent {
super_pairs.push((Arc::clone(p), file_idx));
}
for iface in &cls.implements {
super_pairs.push((Arc::clone(iface), file_idx));
}
}
}
}
let mut result = Vec::new();
for (name, file_idx) in super_pairs {
let canonical = if wi.classes_by_name.contains_key(name.as_ref()) {
Some(name.as_ref().to_string())
} else {
file_idx.and_then(|idx| {
idx.use_imports
.iter()
.find(|(alias, _)| alias.as_ref() == name.as_ref())
.map(|(_, fqn)| crate::text::fqn_short_name(fqn).to_string())
})
};
let Some(canonical_name) = canonical else {
continue;
};
if let Some(refs) = wi.classes_by_name.get(&canonical_name)
&& let Some((uri, cls)) = refs.first().and_then(|r| wi.at(*r))
{
let kind = match cls.kind {
ClassKind::Class | ClassKind::Trait => SymbolKind::CLASS,
ClassKind::Interface => SymbolKind::INTERFACE,
ClassKind::Enum => SymbolKind::ENUM,
};
result.push(make_item_from_index(&cls.name, kind, uri, cls.start_line));
}
}
result
}
pub fn subtypes_of_from_workspace(
item: &TypeHierarchyItem,
wi: &crate::db::workspace_index::WorkspaceIndexData,
) -> Vec<TypeHierarchyItem> {
use crate::index::file_index::ClassKind;
let Some(refs) = wi.subtypes_of.get(item.name.as_str()) else {
return Vec::new();
};
refs.iter()
.filter_map(|r| wi.at(*r))
.map(|(uri, cls)| {
let kind = match cls.kind {
ClassKind::Class | ClassKind::Trait => SymbolKind::CLASS,
ClassKind::Interface => SymbolKind::INTERFACE,
ClassKind::Enum => SymbolKind::ENUM,
};
make_item_from_index(&cls.name, kind, uri, cls.start_line)
})
.collect()
}