Skip to content

Commit e547453

Browse files
committed
Fix type hierarchy registration and subtype expansion
1 parent 8a288cd commit e547453

2 files changed

Lines changed: 59 additions & 8 deletions

File tree

src/server.rs

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -397,11 +397,7 @@ impl LanguageServer for Backend {
397397
.supports_type_hierarchy_dynamic_registration
398398
.load(Ordering::Acquire)
399399
{
400-
registrations.push(Registration {
401-
id: "type-hierarchy".to_string(),
402-
method: "textDocument/prepareTypeHierarchy".to_string(),
403-
register_options: None,
404-
});
400+
registrations.push(type_hierarchy_registration());
405401
}
406402

407403
// Register file watchers for staleness detection. The client
@@ -1588,6 +1584,27 @@ impl LanguageServer for Backend {
15881584
}
15891585
}
15901586

1587+
fn type_hierarchy_registration() -> Registration {
1588+
Registration {
1589+
id: "type-hierarchy".to_string(),
1590+
method: "textDocument/prepareTypeHierarchy".to_string(),
1591+
register_options: Some(
1592+
serde_json::to_value(TypeHierarchyRegistrationOptions {
1593+
text_document_registration_options: TextDocumentRegistrationOptions {
1594+
document_selector: Some(vec![DocumentFilter {
1595+
language: Some("php".to_string()),
1596+
scheme: None,
1597+
pattern: None,
1598+
}]),
1599+
},
1600+
type_hierarchy_options: TypeHierarchyOptions::default(),
1601+
static_registration_options: StaticRegistrationOptions::default(),
1602+
})
1603+
.expect("type hierarchy registration options serialize"),
1604+
),
1605+
}
1606+
}
1607+
15911608
/// Convert a `Vec<Location>` into a `GotoDefinitionResponse`.
15921609
///
15931610
/// Returns `Scalar` for a single location, `Array` for multiple, and
@@ -1603,6 +1620,26 @@ fn wrap_locations(locations: Vec<Location>) -> Option<GotoDefinitionResponse> {
16031620
}
16041621
}
16051622

1623+
#[cfg(test)]
1624+
mod tests {
1625+
use super::*;
1626+
1627+
#[test]
1628+
fn type_hierarchy_registration_includes_php_document_selector() {
1629+
let registration = type_hierarchy_registration();
1630+
1631+
assert_eq!(registration.id, "type-hierarchy");
1632+
assert_eq!(registration.method, "textDocument/prepareTypeHierarchy");
1633+
1634+
let options = registration
1635+
.register_options
1636+
.expect("type hierarchy registration should include options");
1637+
assert_eq!(options["documentSelector"][0]["language"], "php");
1638+
assert!(options["documentSelector"][0].get("scheme").is_none());
1639+
assert!(options["documentSelector"][0].get("pattern").is_none());
1640+
}
1641+
}
1642+
16061643
// ─── Self-scan helpers ──────────────────────────────────────────────────────
16071644

16081645
impl Backend {

src/type_hierarchy.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,23 @@ impl Backend {
137137
let ctx = self.file_context(item_uri);
138138
let class_loader = self.class_loader(&ctx);
139139

140-
// direct_only = true so only immediate children are returned;
141-
// the client walks the tree one level at a time.
142-
let implementors = self.find_implementors(short, &fqn, &class_loader, true, true);
140+
// Subtypes are walked one level at a time by the client. Prefer
141+
// the reverse inheritance index for known children so expanding an
142+
// already-discovered hierarchy node does not re-run the broad
143+
// go-to-implementation scanner over the workspace.
144+
let indexed_children = self.gti_index.read().get(&fqn).cloned();
145+
let implementors: Vec<ClassInfo> = if let Some(children) = indexed_children {
146+
children
147+
.into_iter()
148+
.filter_map(|child_fqn| {
149+
class_loader(&child_fqn).map(std::sync::Arc::unwrap_or_clone)
150+
})
151+
.collect()
152+
} else {
153+
// Fall back to the wider scan for first discovery of unopened
154+
// files that are known only through the FQN index.
155+
self.find_implementors(short, &fqn, &class_loader, true, true)
156+
};
143157

144158
let mut result = Vec::new();
145159
for imp in &implementors {

0 commit comments

Comments
 (0)