use crate::{
LanguageService,
binder::{SymbolKey, SymbolKind, SymbolTable},
helpers, types_analyzer,
};
use lspt::{
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
SymbolKind as LspSymbolKind,
};
use wat_syntax::SyntaxKind;
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 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: 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: helpers::rowan_range_to_lsp_range(
line_index,
symbol.key.text_range(),
),
selection_range: helpers::create_selection_range(symbol, &root, line_index),
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: 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: helpers::rowan_range_to_lsp_range(
line_index,
symbol.key.text_range(),
),
selection_range: helpers::create_selection_range(
symbol, &root, line_index,
),
data: None,
}]
})
}
_ => None,
})
}
pub fn call_hierarchy_incoming_calls(
&self,
params: CallHierarchyIncomingCallsParams,
) -> Option<Vec<CallHierarchyIncomingCall>> {
let document = self.get_document(¶ms.item.uri)?;
let root = document.root_tree(self);
let symbol_table = SymbolTable::of(self, document);
let line_index = document.line_index(self);
let callee_def_range = helpers::lsp_range_to_rowan_range(line_index, params.item.range)?;
let callee_def = symbol_table
.symbols
.values()
.find(|symbol| symbol.key.text_range() == callee_def_range)?;
let items = symbol_table
.symbols
.values()
.filter(|symbol| {
symbol.kind == SymbolKind::Call
&& symbol.idx.is_defined_by(&callee_def.idx)
&& callee_def.region == symbol.region
})
.filter_map(|call_symbol| {
call_symbol
.key
.to_node(&root)
.ancestors()
.find(|node| node.kind() == SyntaxKind::MODULE_FIELD_FUNC)
.and_then(|node| symbol_table.symbols.get(&SymbolKey::new(&node)))
.map(|symbol| (call_symbol, symbol))
})
.map(|(call_symbol, func_symbol)| {
let plain_instr_range =
call_symbol.key.to_node(&root).parent().map(|call| {
helpers::rowan_range_to_lsp_range(line_index, call.text_range())
});
CallHierarchyIncomingCall {
from: CallHierarchyItem {
name: func_symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: None,
detail: Some(types_analyzer::render_func_header(
self,
func_symbol.idx.name,
types_analyzer::get_func_sig(
self,
document,
*func_symbol.key,
&func_symbol.green,
),
)),
uri: params.item.uri.clone(),
range: helpers::rowan_range_to_lsp_range(
line_index,
func_symbol.key.text_range(),
),
selection_range: helpers::create_selection_range(
func_symbol,
&root,
line_index,
),
data: None,
},
from_ranges: plain_instr_range.into_iter().collect(),
}
})
.collect();
Some(items)
}
pub fn call_hierarchy_outgoing_calls(
&self,
params: CallHierarchyOutgoingCallsParams,
) -> Option<Vec<CallHierarchyOutgoingCall>> {
let document = self.get_document(¶ms.item.uri)?;
let root = &document.root_tree(self);
let symbol_table = SymbolTable::of(self, document);
let line_index = document.line_index(self);
let call_def_range = helpers::lsp_range_to_rowan_range(line_index, params.item.range)?;
let call_def_symbol = symbol_table
.symbols
.values()
.find(|symbol| symbol.key.text_range() == call_def_range)?;
let func = call_def_symbol.key.to_node(root);
let items = func
.descendants()
.filter(|node| node.kind() == SyntaxKind::PLAIN_INSTR && helpers::ast::is_call(node))
.flat_map(|node| {
let plain_instr_range =
helpers::rowan_range_to_lsp_range(line_index, node.text_range());
let uri = ¶ms.item.uri;
node.children()
.filter(|child| child.kind() == SyntaxKind::IMMEDIATE)
.filter_map(|immediate| symbol_table.find_def(SymbolKey::new(&immediate)))
.filter(|symbol| symbol.kind == SymbolKind::Func)
.map(move |func_symbol| CallHierarchyOutgoingCall {
to: CallHierarchyItem {
name: func_symbol.idx.render(self).to_string(),
kind: LspSymbolKind::Function,
tags: None,
detail: Some(types_analyzer::render_func_header(
self,
func_symbol.idx.name,
types_analyzer::get_func_sig(
self,
document,
*func_symbol.key,
&func_symbol.green,
),
)),
uri: uri.clone(),
range: helpers::rowan_range_to_lsp_range(
line_index,
func_symbol.key.text_range(),
),
selection_range: helpers::create_selection_range(
func_symbol,
root,
line_index,
),
data: None,
},
from_ranges: vec![plain_instr_range],
})
})
.collect();
Some(items)
}
}