use crate::core::Position;
use crate::semantic::resolver::Resolver;
use crate::semantic::symbol_table::SymbolTable;
use crate::semantic::types::{InlayHint, InlayHintKind};
use crate::syntax::kerml::ast::{Element, Feature, FeatureMember, KerMLFile};
pub fn extract_inlay_hints(
file: &KerMLFile,
symbol_table: &SymbolTable,
range: Option<(Position, Position)>,
) -> Vec<InlayHint> {
let mut hints = Vec::new();
for element in &file.elements {
collect_hints(element, symbol_table, range, &mut hints);
}
hints
}
fn collect_hints(
element: &Element,
symbol_table: &SymbolTable,
range: Option<(Position, Position)>,
hints: &mut Vec<InlayHint>,
) {
match element {
Element::Package(p) => {
for child in &p.elements {
collect_hints(child, symbol_table, range, hints);
}
}
Element::Feature(f) => {
collect_feature_hints(f, symbol_table, range, hints);
}
_ => {}
}
}
fn collect_feature_hints(
feature: &Feature,
symbol_table: &SymbolTable,
range: Option<(Position, Position)>,
hints: &mut Vec<InlayHint>,
) {
if let Some(((start, end), span)) = range.zip(feature.span.as_ref())
&& (span.start < start || span.end > end)
{
return;
}
if let Some(name) = &feature.name {
let has_typing = feature
.body
.iter()
.any(|member| matches!(member, FeatureMember::Typing(_)));
if !has_typing && feature.span.is_some() {
let resolver = Resolver::new(symbol_table);
if let Some(symbol) = resolver.resolve(name) {
let type_name = match symbol {
crate::semantic::symbol_table::Symbol::Feature { feature_type, .. } => {
feature_type.as_ref()
}
_ => None,
};
if let (Some(type_name), Some(span)) = (type_name, &feature.span) {
let hint_pos = Position {
line: span.start.line,
column: span.start.column + name.len(),
};
hints.push(InlayHint {
position: hint_pos,
label: format!(": {type_name}"),
kind: InlayHintKind::Type,
padding_left: false,
padding_right: true,
});
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_hints() {
let file = KerMLFile {
namespace: None,
elements: vec![],
};
let symbol_table = SymbolTable::new();
let hints = extract_inlay_hints(&file, &symbol_table, None);
assert!(hints.is_empty());
}
}