use crate::core::{Position, Span};
use crate::syntax::sysml::ast::{
Definition, DefinitionMember, Element, Package, SysMLFile, Usage, UsageMember,
};
pub fn find_selection_spans(file: &SysMLFile, position: Position) -> Vec<Span> {
let mut spans: Vec<Span> = Vec::new();
for element in &file.elements {
if collect_containing_spans(element, position, &mut spans) {
break;
}
}
if spans.is_empty() {
return Vec::new();
}
spans.sort_by(|a, b| {
let size_a = range_size(a);
let size_b = range_size(b);
size_a.cmp(&size_b)
});
spans
}
fn range_size(span: &Span) -> usize {
let lines = span.end.line.saturating_sub(span.start.line);
let cols = if lines == 0 {
span.end.column.saturating_sub(span.start.column)
} else {
span.end.column + 100
};
lines * 100 + cols
}
fn try_push_span(span: &Option<Span>, position: Position, spans: &mut Vec<Span>) -> bool {
if let Some(span) = span
&& span.contains(position)
{
spans.push(*span);
return true;
}
false
}
fn collect_containing_spans(element: &Element, position: Position, spans: &mut Vec<Span>) -> bool {
match element {
Element::Package(p) => collect_package_spans(p, position, spans),
Element::Definition(d) => collect_definition_spans(d, position, spans),
Element::Usage(u) => collect_usage_spans(u, position, spans),
Element::Comment(c) => try_push_span(&c.span, position, spans),
Element::Import(i) => try_push_span(&i.span, position, spans),
Element::Alias(a) => try_push_span(&a.span, position, spans),
}
}
fn collect_package_spans(package: &Package, position: Position, spans: &mut Vec<Span>) -> bool {
if !try_push_span(&package.span, position, spans) {
return false;
}
for child in &package.elements {
if collect_containing_spans(child, position, spans) {
return true;
}
}
true
}
fn collect_definition_spans(def: &Definition, position: Position, spans: &mut Vec<Span>) -> bool {
if !try_push_span(&def.span, position, spans) {
return false;
}
for member in &def.body {
match member {
DefinitionMember::Usage(u) => {
if collect_usage_spans(u, position, spans) {
return true;
}
}
DefinitionMember::Comment(c) => {
if try_push_span(&c.span, position, spans) {
return true;
}
}
}
}
true
}
fn collect_usage_spans(usage: &Usage, position: Position, spans: &mut Vec<Span>) -> bool {
if !try_push_span(&usage.span, position, spans) {
return false;
}
for member in &usage.body {
match member {
UsageMember::Usage(u) => {
if collect_usage_spans(u, position, spans) {
return true;
}
}
UsageMember::Comment(c) => {
if try_push_span(&c.span, position, spans) {
return true;
}
}
}
}
true
}