1use fml_syntax::{Diagnostic, TextRange, TextSize};
6
7use crate::types::{ElementRef, Type};
8
9#[derive(Debug, Clone, Default)]
11pub struct Analysis {
12 pub diagnostics: Vec<Diagnostic>,
14 pub bindings: Vec<Binding>,
16 pub references: Vec<Reference>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum BindingKind {
23 Constant,
25 SourceParameter,
27 TargetParameter,
28 Source,
30 Target,
32}
33
34impl BindingKind {
35 pub fn is_target(self) -> bool {
37 matches!(self, BindingKind::TargetParameter | BindingKind::Target)
38 }
39}
40
41#[derive(Debug, Clone)]
43pub struct Binding {
44 pub name: String,
45 pub kind: BindingKind,
46 pub definition: TextRange,
48 pub visible: TextRange,
51 pub ty: Type,
54}
55
56#[derive(Debug, Clone)]
58pub enum Referent {
59 Binding(usize),
61 Element(Box<ElementRef>),
63 Group(String),
65 Transform(String),
67 Type(Type),
69}
70
71#[derive(Debug, Clone)]
73pub struct Reference {
74 pub range: TextRange,
75 pub referent: Referent,
76 pub ty: Type,
78}
79
80impl Analysis {
81 pub fn reference_at(&self, offset: TextSize) -> Option<&Reference> {
83 let index = self.references.partition_point(|r| r.range.end() < offset);
84 self.references[index..]
85 .iter()
86 .take_while(|r| r.range.start() <= offset)
87 .find(|r| r.range.contains_inclusive(offset))
88 }
89
90 pub fn binding_at(&self, offset: TextSize) -> Option<(usize, &Binding)> {
92 self.bindings
93 .iter()
94 .enumerate()
95 .find(|(_, b)| b.definition.contains_inclusive(offset))
96 }
97
98 pub fn bindings_at(&self, offset: TextSize) -> Vec<&Binding> {
100 let mut visible: Vec<&Binding> = Vec::new();
101 for binding in self
102 .bindings
103 .iter()
104 .filter(|b| b.visible.contains_inclusive(offset))
105 {
106 match visible.iter_mut().find(|b| b.name == binding.name) {
107 Some(seen) if binding.visible.len() < seen.visible.len() => *seen = binding,
108 Some(_) => {}
109 None => visible.push(binding),
110 }
111 }
112 visible
113 }
114
115 pub fn references_to(&self, binding: usize) -> impl Iterator<Item = &Reference> {
117 self.references
118 .iter()
119 .filter(move |r| matches!(r.referent, Referent::Binding(b) if b == binding))
120 }
121}