use fml_syntax::{Diagnostic, TextRange, TextSize};
use crate::types::{ElementRef, Type};
#[derive(Debug, Clone, Default)]
pub struct Analysis {
pub diagnostics: Vec<Diagnostic>,
pub bindings: Vec<Binding>,
pub references: Vec<Reference>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BindingKind {
Constant,
SourceParameter,
TargetParameter,
Source,
Target,
}
impl BindingKind {
pub fn is_target(self) -> bool {
matches!(self, BindingKind::TargetParameter | BindingKind::Target)
}
}
#[derive(Debug, Clone)]
pub struct Binding {
pub name: String,
pub kind: BindingKind,
pub definition: TextRange,
pub visible: TextRange,
pub ty: Type,
}
#[derive(Debug, Clone)]
pub enum Referent {
Binding(usize),
Element(Box<ElementRef>),
Group(String),
Transform(String),
Type(Type),
}
#[derive(Debug, Clone)]
pub struct Reference {
pub range: TextRange,
pub referent: Referent,
pub ty: Type,
}
impl Analysis {
pub fn reference_at(&self, offset: TextSize) -> Option<&Reference> {
let index = self.references.partition_point(|r| r.range.end() < offset);
self.references[index..]
.iter()
.take_while(|r| r.range.start() <= offset)
.find(|r| r.range.contains_inclusive(offset))
}
pub fn binding_at(&self, offset: TextSize) -> Option<(usize, &Binding)> {
self.bindings
.iter()
.enumerate()
.find(|(_, b)| b.definition.contains_inclusive(offset))
}
pub fn bindings_at(&self, offset: TextSize) -> Vec<&Binding> {
let mut visible: Vec<&Binding> = Vec::new();
for binding in self
.bindings
.iter()
.filter(|b| b.visible.contains_inclusive(offset))
{
match visible.iter_mut().find(|b| b.name == binding.name) {
Some(seen) if binding.visible.len() < seen.visible.len() => *seen = binding,
Some(_) => {}
None => visible.push(binding),
}
}
visible
}
pub fn references_to(&self, binding: usize) -> impl Iterator<Item = &Reference> {
self.references
.iter()
.filter(move |r| matches!(r.referent, Referent::Binding(b) if b == binding))
}
}