use std::sync::Arc;
use tower_lsp::lsp_types::{Location, Position, Url};
use crate::ast::ParsedDoc;
use crate::resolve::{Container, Declaration, resolve_declaration};
use crate::util::{strip_variable_sigil, utf16_code_units, word_at_position};
pub fn goto_declaration(
source: &str,
all_docs: &[(Url, Arc<ParsedDoc>)],
position: Position,
) -> Option<Location> {
let word = word_at_position(source, position)?;
for (uri, doc) in all_docs {
let sv = doc.view();
if let Some(decl) =
resolve_declaration(&doc.program().stmts, &word, &is_abstract_declaration)
{
return Some(Location {
uri: uri.clone(),
range: sv.name_range_in_span(decl.name(), decl.span()),
});
}
}
for (uri, doc) in all_docs {
let sv = doc.view();
if let Some(decl) = resolve_declaration(&doc.program().stmts, &word, &is_any_declaration) {
return Some(Location {
uri: uri.clone(),
range: sv.name_range_in_span(decl.name(), decl.span()),
});
}
}
None
}
fn is_abstract_declaration(decl: &Declaration<'_>) -> bool {
match decl {
Declaration::Interface { .. } => true,
Declaration::Method {
container: Container::Interface,
..
} => true,
Declaration::Method {
method,
container: Container::Class | Container::Trait,
..
} => method.is_abstract,
_ => false,
}
}
fn is_any_declaration(decl: &Declaration<'_>) -> bool {
!matches!(decl, Declaration::PromotedParam { .. })
}
pub fn goto_declaration_from_index(
source: &str,
indexes: &[(
tower_lsp::lsp_types::Url,
std::sync::Arc<crate::file_index::FileIndex>,
)],
position: tower_lsp::lsp_types::Position,
) -> Option<Location> {
use crate::file_index::ClassKind;
use crate::util::word_at_position;
let word = word_at_position(source, position)?;
let bare = strip_variable_sigil(&word);
let precise_range = |line: u32, name_char: u32, name: &str| -> tower_lsp::lsp_types::Range {
let end_char = name_char + utf16_code_units(name);
tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position {
line,
character: name_char,
},
end: tower_lsp::lsp_types::Position {
line,
character: end_char,
},
}
};
for (uri, idx) in indexes {
for cls in &idx.classes {
match cls.kind {
ClassKind::Interface => {
if cls.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(cls.start_line, cls.name_char, &cls.name),
});
}
for m in &cls.methods {
if m.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(m.start_line, m.name_char, &m.name),
});
}
}
}
ClassKind::Trait => {
for m in &cls.methods {
if m.is_abstract && m.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(m.start_line, m.name_char, &m.name),
});
}
}
}
_ if cls.is_abstract => {
for m in &cls.methods {
if m.is_abstract && m.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(m.start_line, m.name_char, &m.name),
});
}
}
}
_ => {}
}
}
}
for (uri, idx) in indexes {
for f in &idx.functions {
if f.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(f.start_line, f.name_char, &f.name),
});
}
}
for cls in &idx.classes {
if cls.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(cls.start_line, cls.name_char, &cls.name),
});
}
for m in &cls.methods {
if m.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(m.start_line, m.name_char, &m.name),
});
}
}
for dm in &cls.doc_methods {
if dm.name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: crate::util::zero_width_range(dm.start_line),
});
}
}
for p in &cls.properties {
if p.name.as_ref() == bare {
return Some(Location {
uri: uri.clone(),
range: precise_range(p.start_line, p.name_char, &p.name),
});
}
}
for c in &cls.constants {
if c.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(cls.start_line, cls.name_char, &cls.name),
});
}
}
if cls.kind == ClassKind::Enum {
for case_name in &cls.cases {
if case_name.as_ref() == word {
return Some(Location {
uri: uri.clone(),
range: precise_range(cls.start_line, cls.name_char, &cls.name),
});
}
}
}
}
}
None
}