1mod build;
6
7use nifty_syntax::ast::File;
8use nifty_syntax::{FileId, Span};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11pub(crate) struct SymbolId(usize);
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub(crate) enum SymbolKind {
15 State,
16 Struct,
17 Field,
18 Function,
19 View,
20 Window,
21 Parameter,
22 Local,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub(crate) enum Visibility {
28 Everywhere,
30 Within(Span),
32 After {
34 file: FileId,
35 start: usize,
36 end: Option<usize>,
37 },
38}
39
40impl Visibility {
41 fn contains(self, file: FileId, offset: usize) -> bool {
42 match self {
43 Self::Everywhere => true,
44 Self::Within(span) => span.file == file && span.start < offset && offset < span.end,
45 Self::After {
46 file: declared_in,
47 start,
48 end,
49 } => declared_in == file && start <= offset && end.is_none_or(|end| offset < end),
50 }
51 }
52}
53
54#[derive(Debug)]
55pub(crate) struct Symbol {
56 pub name: String,
57 pub kind: SymbolKind,
58 pub parent: Option<SymbolId>,
60 pub visibility: Visibility,
61}
62
63#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub(crate) enum Unresolved {
66 Function,
67 Type,
68 Variable,
69 Field,
70 Method,
71 ImplicitMember,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub(crate) enum Role {
76 Declaration(SymbolId),
77 Reference(SymbolId),
78 Unresolved(Unresolved),
79}
80
81impl Role {
82 pub(crate) fn symbol(self) -> Option<SymbolId> {
83 match self {
84 Self::Declaration(id) | Self::Reference(id) => Some(id),
85 Self::Unresolved(_) => None,
86 }
87 }
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
91pub(crate) struct Occurrence {
92 pub span: Span,
93 pub role: Role,
94}
95
96#[derive(Debug)]
97pub(crate) struct Index {
98 symbols: Vec<Symbol>,
99 occurrences: Vec<Occurrence>,
102}
103
104impl Index {
105 pub(crate) fn new(files: &[&File]) -> Self {
107 build::build(files)
108 }
109
110 pub(crate) fn symbol(&self, id: SymbolId) -> &Symbol {
111 &self.symbols[id.0]
112 }
113
114 pub(crate) fn occurrences_in(&self, file: FileId) -> &[Occurrence] {
116 let start = self
117 .occurrences
118 .partition_point(|occurrence| occurrence.span.file < file);
119 let end = self
120 .occurrences
121 .partition_point(|occurrence| occurrence.span.file <= file);
122 &self.occurrences[start..end]
123 }
124
125 pub(crate) fn occurrence_at(&self, file: FileId, offset: usize) -> Option<Occurrence> {
128 let in_file = self.occurrences_in(file);
129 let first_ending_at_or_after =
130 in_file.partition_point(|occurrence| occurrence.span.end < offset);
131 in_file
132 .get(first_ending_at_or_after)
133 .filter(|occurrence| occurrence.span.start <= offset)
134 .copied()
135 }
136
137 pub(crate) fn occurrences_of(&self, id: SymbolId) -> impl Iterator<Item = Occurrence> {
139 self.occurrences
140 .iter()
141 .filter(move |occurrence| occurrence.role.symbol() == Some(id))
142 .copied()
143 }
144
145 pub(crate) fn in_scope(&self, file: FileId, offset: usize) -> impl Iterator<Item = &Symbol> {
148 self.symbols.iter().filter(move |symbol| {
149 symbol.kind != SymbolKind::Field && symbol.visibility.contains(file, offset)
150 })
151 }
152
153 pub(crate) fn declaration_of(&self, id: SymbolId) -> Option<Span> {
154 self.occurrences
155 .iter()
156 .find(|occurrence| occurrence.role == Role::Declaration(id))
157 .map(|occurrence| occurrence.span)
158 }
159}
160
161#[cfg(test)]
162mod tests;