1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::core::Span;
use crate::semantic::graphs::ReferenceIndex;
use crate::semantic::symbol_table::SymbolTable;
use crate::semantic::types::SemanticError;
use std::path::PathBuf;
pub struct SysmlAdapter<'a> {
pub(super) symbol_table: &'a mut SymbolTable,
pub(super) reference_index: Option<&'a mut ReferenceIndex>,
pub(super) current_namespace: Vec<String>,
pub(super) errors: Vec<SemanticError>,
}
impl<'a> SysmlAdapter<'a> {
pub fn new(symbol_table: &'a mut SymbolTable) -> Self {
Self {
symbol_table,
reference_index: None,
current_namespace: Vec::new(),
errors: Vec::new(),
}
}
pub fn with_index(
symbol_table: &'a mut SymbolTable,
reference_index: &'a mut ReferenceIndex,
) -> Self {
Self {
symbol_table,
reference_index: Some(reference_index),
current_namespace: Vec::new(),
errors: Vec::new(),
}
}
/// Index a reference from source to target for reverse lookups.
///
/// The target is resolved to its full qualified name by trying:
/// 1. The target as-is (already fully qualified)
/// 2. Prefixed with the current namespace (relative to current scope)
/// 3. Walking up the namespace chain to find the symbol
pub(super) fn index_reference(&mut self, source_qname: &str, target: &str, span: Option<Span>) {
self.index_reference_with_type(source_qname, target, span, None);
}
/// Index a reference with an explicit token type for semantic highlighting.
///
/// Use this for references where the default Type token is not appropriate:
/// - Property for redefines/subsets targets (they reference usages/features)
/// - Type for typed_by targets (they reference definitions/classifiers)
pub(super) fn index_reference_with_type(
&mut self,
source_qname: &str,
target: &str,
span: Option<Span>,
token_type: Option<crate::semantic::types::TokenType>,
) {
// First resolve the target (needs immutable borrow of self)
let resolved_target = self.resolve_reference_target(target);
let file = self.symbol_table.current_file().map(PathBuf::from);
// Then add to index (needs mutable borrow of reference_index)
if let Some(index) = &mut self.reference_index {
index.add_reference_with_type(
source_qname,
&resolved_target,
file.as_ref(),
span,
token_type,
);
}
}
/// Resolve a reference target to its full qualified name.
///
/// Tries multiple resolution strategies:
/// 1. Check if target already exists as a fully qualified name
/// 2. Try prefixing with current namespace parts (walking up the chain)
fn resolve_reference_target(&self, target: &str) -> String {
// Strategy 1: Check if it's already a valid fully qualified name
if self.symbol_table.find_by_qualified_name(target).is_some() {
return target.to_string();
}
// Strategy 2: Try prefixing with current namespace, walking up the chain
// e.g., if we're in Outer and target is Inner::Vehicle,
// try Outer::Inner::Vehicle
let mut namespace = self.current_namespace.clone();
while !namespace.is_empty() {
let candidate = format!("{}::{}", namespace.join("::"), target);
if self
.symbol_table
.find_by_qualified_name(&candidate)
.is_some()
{
return candidate;
}
namespace.pop();
}
// Strategy 3: If target contains ::, try resolving just the first segment
// and build the full path from there
if let Some(first_segment) = target.split("::").next() {
let mut ns = self.current_namespace.clone();
while !ns.is_empty() {
let candidate_prefix = format!("{}::{}", ns.join("::"), first_segment);
if self
.symbol_table
.find_by_qualified_name(&candidate_prefix)
.is_some()
{
// Found the first segment, now build the full path
let full_target = format!("{}::{}", ns.join("::"), target);
if self
.symbol_table
.find_by_qualified_name(&full_target)
.is_some()
{
return full_target;
}
}
ns.pop();
}
}
// Fallback: return original target (unresolved references)
target.to_string()
}
}