use crate::{
LanguageService,
binder::{SymbolKind, SymbolTable},
deprecation,
helpers::LineIndexExt,
types_analyzer,
};
use lspt::{
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem, CallHierarchyOutgoingCall,
CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams, SymbolKind as LspSymbolKind, SymbolTag,
};
impl LanguageService {
pub fn prepare_call_hierarchy(&self, params: CallHierarchyPrepareParams) -> Option<Vec<CallHierarchyItem>> {
let document = self.get_document(¶ms.text_document.uri)?;
let line_index = document.line_index(self);
let root = document.root_tree(self);
let symbol_table = SymbolTable::of(self, document);
let deprecation = deprecation::get_deprecation(self, document);
let token = super::find_meaningful_token(self, document, &root, params.position)?;
let parent_range = token.parent().text_range();
symbol_table.symbols.values().find_map(|symbol| match symbol.kind {
SymbolKind::Func if symbol.key.text_range() == parent_range => Some(vec![CallHierarchyItem {
name: symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: if deprecation.contains_key(&symbol.key) {
Some(vec![SymbolTag::Deprecated])
} else {
None
},
detail: Some(types_analyzer::render_func_header(
self,
symbol.idx.name,
types_analyzer::get_func_sig(self, document, symbol.key, &symbol.green),
)),
uri: params.text_document.uri.clone(),
range: line_index.convert(symbol.key.text_range()),
selection_range: line_index.convert(
symbol_table
.def_poi
.get(&symbol.key)
.copied()
.unwrap_or_else(|| symbol.key.text_range()),
),
data: None,
}]),
SymbolKind::Call if symbol.key.text_range() == parent_range => {
symbol_table.find_def(symbol.key).map(|symbol| {
vec![CallHierarchyItem {
name: symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: if deprecation.contains_key(&symbol.key) {
Some(vec![SymbolTag::Deprecated])
} else {
None
},
detail: Some(types_analyzer::render_func_header(
self,
symbol.idx.name,
types_analyzer::get_func_sig(self, document, symbol.key, &symbol.green),
)),
uri: params.text_document.uri.clone(),
range: line_index.convert(symbol.key.text_range()),
selection_range: line_index.convert(
symbol_table
.def_poi
.get(&symbol.key)
.copied()
.unwrap_or_else(|| symbol.key.text_range()),
),
data: None,
}]
})
}
_ => None,
})
}
pub fn call_hierarchy_incoming_calls(
&self,
params: CallHierarchyIncomingCallsParams,
) -> Option<Vec<CallHierarchyIncomingCall>> {
let document = self.get_document(¶ms.item.uri)?;
let line_index = document.line_index(self);
let symbol_table = SymbolTable::of(self, document);
let deprecation = deprecation::get_deprecation(self, document);
let callee_def_range = line_index.convert(params.item.range)?;
let mut items = symbol_table
.resolved
.iter()
.filter_map(|(ref_key, def_key)| {
if def_key.text_range() == callee_def_range {
Some(ref_key)
} else {
None
}
})
.filter_map(|call_key| {
symbol_table
.symbols
.values()
.find(|symbol| {
symbol.kind == SymbolKind::Func && symbol.key.text_range().contains_range(call_key.text_range())
})
.map(|symbol| CallHierarchyIncomingCall {
from: CallHierarchyItem {
name: symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: if deprecation.contains_key(&symbol.key) {
Some(vec![SymbolTag::Deprecated])
} else {
None
},
detail: Some(types_analyzer::render_func_header(
self,
symbol.idx.name,
types_analyzer::get_func_sig(self, document, symbol.key, &symbol.green),
)),
uri: params.item.uri.clone(),
range: line_index.convert(symbol.key.text_range()),
selection_range: line_index.convert(
symbol_table
.def_poi
.get(&symbol.key)
.copied()
.unwrap_or_else(|| symbol.key.text_range()),
),
data: None,
},
from_ranges: vec![line_index.convert(call_key.text_range())],
})
})
.collect::<Vec<_>>();
items.sort_unstable_by_key(|item| item.from.range.start);
Some(items)
}
pub fn call_hierarchy_outgoing_calls(
&self,
params: CallHierarchyOutgoingCallsParams,
) -> Option<Vec<CallHierarchyOutgoingCall>> {
let document = self.get_document(¶ms.item.uri)?;
let line_index = document.line_index(self);
let symbol_table = SymbolTable::of(self, document);
let deprecation = deprecation::get_deprecation(self, document);
let call_def_range = line_index.convert(params.item.range)?;
let mut items = symbol_table
.symbols
.values()
.filter(|symbol| symbol.kind == SymbolKind::Call && call_def_range.contains_range(symbol.key.text_range()))
.filter_map(|symbol| symbol_table.find_def(symbol.key).map(|def_symbol| (def_symbol, symbol)))
.map(|(def_symbol, ref_symbol)| CallHierarchyOutgoingCall {
to: CallHierarchyItem {
name: def_symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: if deprecation.contains_key(&def_symbol.key) {
Some(vec![SymbolTag::Deprecated])
} else {
None
},
detail: Some(types_analyzer::render_func_header(
self,
def_symbol.idx.name,
types_analyzer::get_func_sig(self, document, def_symbol.key, &def_symbol.green),
)),
uri: params.item.uri.clone(),
range: line_index.convert(def_symbol.key.text_range()),
selection_range: line_index.convert(
symbol_table
.def_poi
.get(&def_symbol.key)
.copied()
.unwrap_or_else(|| def_symbol.key.text_range()),
),
data: None,
},
from_ranges: vec![line_index.convert(ref_symbol.key.text_range())],
})
.collect::<Vec<_>>();
items.sort_unstable_by_key(|item| item.to.range.start);
Some(items)
}
}