use super::super::index::*;
use super::common::{predicate_op_project_field_value, string_project_field_value};
use super::{WorkspaceDocumentSymbol, WorkspaceDocumentSymbolKind};
pub(crate) fn document_symbols(index: &SemanticIndex, path: &str) -> Vec<WorkspaceDocumentSymbol> {
let mut symbols = Vec::new();
if let Some(manifest) = &index.manifest
&& manifest.location.path == path
&& let Some(symbol) = workspace_extends_symbol(&manifest.extends)
{
symbols.push(symbol);
}
for qualifier in index.qualifiers.values() {
if qualifier.location.path == path {
symbols.push(qualifier_document_symbol(qualifier));
}
}
for variable in index.variables.values() {
if variable.location.path == path {
symbols.push(variable_document_symbol(variable));
}
}
for resource in index.resources.values() {
if resource.location.path == path {
symbols.push(resource_document_symbol(resource));
}
}
for objects in index.resource_objects.values() {
for object in objects.values() {
if object.location.path == path {
symbols.push(resource_object_document_symbol(object));
}
}
}
sort_workspace_document_symbols(&mut symbols);
symbols
}
fn workspace_extends_symbol(
extends: &WorkspaceExtendsCollection,
) -> Option<WorkspaceDocumentSymbol> {
match extends {
WorkspaceExtendsCollection::Missing => None,
WorkspaceExtendsCollection::Invalid { location } => Some(WorkspaceDocumentSymbol::new(
"extends",
WorkspaceDocumentSymbolKind::WorkspaceExtends,
location.clone(),
Vec::new(),
)),
WorkspaceExtendsCollection::Sources { location, values } => {
Some(WorkspaceDocumentSymbol::new(
"extends",
WorkspaceDocumentSymbolKind::WorkspaceExtends,
location.clone(),
values
.iter()
.map(|source| {
WorkspaceDocumentSymbol::new(
source.source.clone(),
WorkspaceDocumentSymbolKind::WorkspaceExtendSource,
source.location.clone(),
Vec::new(),
)
})
.collect(),
))
}
}
}
fn qualifier_document_symbol(qualifier: &QualifierNode) -> WorkspaceDocumentSymbol {
let children = match &qualifier.predicates {
PredicateCollection::Predicates(predicates) => predicates
.iter()
.map(predicate_document_symbol)
.collect::<Vec<_>>(),
PredicateCollection::Missing { .. } | PredicateCollection::Invalid { .. } => Vec::new(),
};
WorkspaceDocumentSymbol::new(
qualifier.id.clone(),
WorkspaceDocumentSymbolKind::Qualifier,
qualifier.location.clone(),
children,
)
}
fn predicate_document_symbol(predicate: &PredicateNode) -> WorkspaceDocumentSymbol {
WorkspaceDocumentSymbol::new(
predicate_symbol_name(predicate),
WorkspaceDocumentSymbolKind::Predicate,
predicate.location.clone(),
Vec::new(),
)
}
fn variable_document_symbol(variable: &VariableNode) -> WorkspaceDocumentSymbol {
let mut children = Vec::new();
if let Some(values) = variable_values_document_symbol(variable) {
children.push(values);
}
if let Some(resolve) = variable_resolve_document_symbol(variable) {
children.push(resolve);
}
WorkspaceDocumentSymbol::new(
variable.id.clone(),
WorkspaceDocumentSymbolKind::Variable,
variable.location.clone(),
children,
)
}
fn variable_values_document_symbol(variable: &VariableNode) -> Option<WorkspaceDocumentSymbol> {
if variable.values.inline_values.is_empty() && !variable.values.invalid_shape {
return None;
}
Some(WorkspaceDocumentSymbol::new(
"values",
WorkspaceDocumentSymbolKind::Values,
variable.values.location.clone(),
variable
.values
.inline_values
.values()
.map(value_document_symbol)
.collect(),
))
}
fn variable_resolve_document_symbol(variable: &VariableNode) -> Option<WorkspaceDocumentSymbol> {
let ResolveNode::Resolve {
location, rules, ..
} = &variable.resolve
else {
return None;
};
let children = match rules {
RuleCollection::Rules(rules) => rules
.iter()
.map(variable_rule_document_symbol)
.collect::<Vec<_>>(),
RuleCollection::Invalid { .. } => Vec::new(),
};
Some(WorkspaceDocumentSymbol::new(
"resolve",
WorkspaceDocumentSymbolKind::Resolve,
location.clone(),
children,
))
}
fn variable_rule_document_symbol(rule: &VariableRuleNode) -> WorkspaceDocumentSymbol {
WorkspaceDocumentSymbol::new(
variable_rule_symbol_name(rule),
WorkspaceDocumentSymbolKind::Rule,
rule.location.clone(),
Vec::new(),
)
}
fn resource_document_symbol(resource: &ResourceNode) -> WorkspaceDocumentSymbol {
WorkspaceDocumentSymbol::new(
resource.id.clone(),
WorkspaceDocumentSymbolKind::Resource,
resource.location.clone(),
Vec::new(),
)
}
fn resource_object_document_symbol(object: &ResourceObjectNode) -> WorkspaceDocumentSymbol {
WorkspaceDocumentSymbol::new(
format!("{}.{}", object.resource_id, object.key),
WorkspaceDocumentSymbolKind::ResourceObject,
object.location.clone(),
Vec::new(),
)
}
fn value_document_symbol(value: &ValueNode) -> WorkspaceDocumentSymbol {
WorkspaceDocumentSymbol::new(
value.key.clone(),
WorkspaceDocumentSymbolKind::Value,
value.location.clone(),
Vec::new(),
)
}
fn predicate_symbol_name(predicate: &PredicateNode) -> String {
let index = predicate.index + 1;
let Some(attribute) = string_project_field_value(&predicate.attribute) else {
return format!("predicate {index}");
};
let Some(op) = predicate_op_project_field_value(&predicate.op) else {
return format!("predicate {index}: {attribute}");
};
format!("predicate {index}: {attribute} {op}")
}
fn variable_rule_symbol_name(rule: &VariableRuleNode) -> String {
let index = rule.index + 1;
match (
string_project_field_value(&rule.qualifier),
string_project_field_value(&rule.value),
) {
(Some(qualifier), Some(value)) => format!("rule {index}: {qualifier} -> {value}"),
(Some(qualifier), None) => format!("rule {index}: {qualifier}"),
(None, Some(value)) => format!("rule {index}: {value}"),
(None, None) => format!("rule {index}"),
}
}
fn sort_workspace_document_symbols(symbols: &mut [WorkspaceDocumentSymbol]) {
for symbol in symbols.iter_mut() {
sort_workspace_document_symbols(&mut symbol.children);
}
symbols.sort_by(|left, right| {
symbol_position(left)
.cmp(&symbol_position(right))
.then_with(|| left.name.cmp(&right.name))
});
}
fn symbol_position(symbol: &WorkspaceDocumentSymbol) -> (usize, usize) {
symbol
.location
.range
.map(|range| (range.start.line, range.start.character))
.unwrap_or((0, 0))
}