1use std::sync::Arc;
10
11use tower_lsp::lsp_types::{Location, Position, Url};
12
13use crate::document::ast::ParsedDoc;
14use crate::text::{strip_variable_sigil, utf16_code_units, word_at_position};
15use crate::types::resolve::{Container, Declaration, resolve_declaration};
16
17pub fn goto_declaration(
20 source: &str,
21 all_docs: &[(Url, Arc<ParsedDoc>)],
22 position: Position,
23) -> Option<Location> {
24 let word = word_at_position(source, position)?;
25
26 for (uri, doc) in all_docs {
28 let sv = doc.view();
29 if let Some(decl) =
30 resolve_declaration(&doc.program().stmts, &word, &is_abstract_declaration)
31 {
32 return Some(Location {
33 uri: uri.clone(),
34 range: sv.name_range_in_span(decl.name(), decl.span()),
35 });
36 }
37 }
38
39 for (uri, doc) in all_docs {
41 let sv = doc.view();
42 if let Some(decl) = resolve_declaration(&doc.program().stmts, &word, &is_any_declaration) {
43 return Some(Location {
44 uri: uri.clone(),
45 range: sv.name_range_in_span(decl.name(), decl.span()),
46 });
47 }
48 }
49
50 None
51}
52
53fn is_abstract_declaration(decl: &Declaration<'_>) -> bool {
61 match decl {
62 Declaration::Interface { .. } => true,
63 Declaration::Method {
64 container: Container::Interface,
65 ..
66 } => true,
67 Declaration::Method {
68 method,
69 container: Container::Class | Container::Trait,
70 ..
71 } => method.is_abstract,
72 _ => false,
73 }
74}
75
76fn is_any_declaration(decl: &Declaration<'_>) -> bool {
79 !matches!(decl, Declaration::PromotedParam { .. })
80}
81
82pub fn goto_declaration_from_index(
87 source: &str,
88 indexes: &[(
89 tower_lsp::lsp_types::Url,
90 std::sync::Arc<crate::index::file_index::FileIndex>,
91 )],
92 position: tower_lsp::lsp_types::Position,
93) -> Option<Location> {
94 use crate::index::file_index::ClassKind;
95 use crate::text::word_at_position;
96 let word = word_at_position(source, position)?;
97 let bare = strip_variable_sigil(&word);
98
99 let precise_range = |line: u32, name_char: u32, name: &str| -> tower_lsp::lsp_types::Range {
100 let end_char = name_char + utf16_code_units(name);
101 tower_lsp::lsp_types::Range {
102 start: tower_lsp::lsp_types::Position {
103 line,
104 character: name_char,
105 },
106 end: tower_lsp::lsp_types::Position {
107 line,
108 character: end_char,
109 },
110 }
111 };
112
113 for (uri, idx) in indexes {
115 for cls in &idx.classes {
116 match cls.kind {
117 ClassKind::Interface => {
118 if cls.name.as_ref() == word {
120 return Some(Location {
121 uri: uri.clone(),
122 range: precise_range(cls.start_line, cls.name_char, &cls.name),
123 });
124 }
125 for m in &cls.methods {
127 if m.name.as_ref() == word {
128 return Some(Location {
129 uri: uri.clone(),
130 range: precise_range(m.start_line, m.name_char, &m.name),
131 });
132 }
133 }
134 }
135 ClassKind::Trait => {
136 for m in &cls.methods {
138 if m.is_abstract && m.name.as_ref() == word {
139 return Some(Location {
140 uri: uri.clone(),
141 range: precise_range(m.start_line, m.name_char, &m.name),
142 });
143 }
144 }
145 }
146 _ if cls.is_abstract => {
147 for m in &cls.methods {
149 if m.is_abstract && m.name.as_ref() == word {
150 return Some(Location {
151 uri: uri.clone(),
152 range: precise_range(m.start_line, m.name_char, &m.name),
153 });
154 }
155 }
156 }
157 _ => {}
158 }
159 }
160 }
161
162 for (uri, idx) in indexes {
164 for f in &idx.functions {
166 if f.name.as_ref() == word {
167 return Some(Location {
168 uri: uri.clone(),
169 range: precise_range(f.start_line, f.name_char, &f.name),
170 });
171 }
172 }
173
174 for cls in &idx.classes {
175 if cls.name.as_ref() == word {
177 return Some(Location {
178 uri: uri.clone(),
179 range: precise_range(cls.start_line, cls.name_char, &cls.name),
180 });
181 }
182
183 for m in &cls.methods {
185 if m.name.as_ref() == word {
186 return Some(Location {
187 uri: uri.clone(),
188 range: precise_range(m.start_line, m.name_char, &m.name),
189 });
190 }
191 }
192
193 for dm in &cls.doc_methods {
195 if dm.name.as_ref() == word {
196 return Some(Location {
197 uri: uri.clone(),
198 range: crate::text::zero_width_range(dm.start_line),
199 });
200 }
201 }
202
203 for p in &cls.properties {
205 if p.name.as_ref() == bare {
206 return Some(Location {
207 uri: uri.clone(),
208 range: precise_range(p.start_line, p.name_char, &p.name),
209 });
210 }
211 }
212
213 for c in &cls.constants {
215 if c.as_ref() == word {
216 return Some(Location {
217 uri: uri.clone(),
218 range: precise_range(cls.start_line, cls.name_char, &cls.name),
219 });
220 }
221 }
222
223 if cls.kind == ClassKind::Enum {
225 for case_name in &cls.cases {
226 if case_name.as_ref() == word {
227 return Some(Location {
228 uri: uri.clone(),
229 range: precise_range(cls.start_line, cls.name_char, &cls.name),
230 });
231 }
232 }
233 }
234 }
235 }
236 None
237}