Skip to content

Commit 2b9c14e

Browse files
committed
Expose fqn_class_index for integration tests and update class name
completion tests to populate it
1 parent fd3121b commit 2b9c14e

3 files changed

Lines changed: 146 additions & 5 deletions

File tree

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,12 @@ impl Backend {
901901
&self.fqn_uri_index
902902
}
903903

904+
/// Borrow the FQN → ClassInfo index mutex (used by integration tests
905+
/// to populate class metadata for context-aware completion filtering).
906+
pub fn fqn_class_index(&self) -> &Arc<RwLock<HashMap<String, Arc<ClassInfo>>>> {
907+
&self.fqn_class_index
908+
}
909+
904910
/// Borrow the PSR-4 mappings mutex (used by integration tests to
905911
/// configure autoload mappings).
906912
pub fn psr4_mappings(&self) -> &Arc<RwLock<Vec<composer::Psr4Mapping>>> {

tests/integration/completion_class_name_context.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use crate::common::{create_test_backend, create_test_backend_with_stubs};
22
use phpantom_lsp::Backend;
33
use phpantom_lsp::atom::atom;
44
use phpantom_lsp::php_type::PhpType;
5+
use phpantom_lsp::types::{ClassInfo, ClassLikeKind};
6+
use std::sync::Arc;
57
use tower_lsp::LanguageServer;
68
use tower_lsp::lsp_types::*;
79

@@ -812,6 +814,19 @@ async fn test_unloaded_classes_pass_through_filter() {
812814
"file:///vendor/mystery.php".to_string(),
813815
);
814816
}
817+
// Also insert into fqn_class_index so check_context_match can verify the kind.
818+
{
819+
let mut idx = backend.fqn_class_index().write();
820+
idx.insert(
821+
"UnknownKind\\MysteryClass".to_string(),
822+
Arc::new(ClassInfo {
823+
kind: ClassLikeKind::Interface,
824+
name: atom("MysteryClass"),
825+
file_namespace: Some(atom("UnknownKind")),
826+
..Default::default()
827+
}),
828+
);
829+
}
815830

816831
let uri = Url::parse("file:///test_unloaded.php").unwrap();
817832
let text = "<?php\nclass Foo implements Mystery";
@@ -1715,6 +1730,37 @@ async fn test_implements_demotes_abstract_looking_names() {
17151730
"file:///vendor/c.php".to_string(),
17161731
);
17171732
}
1733+
// Insert into fqn_class_index so check_context_match can verify their kind.
1734+
{
1735+
let mut idx = backend.fqn_class_index().write();
1736+
idx.insert(
1737+
"App\\YxLoggable".to_string(),
1738+
Arc::new(ClassInfo {
1739+
kind: ClassLikeKind::Interface,
1740+
name: atom("YxLoggable"),
1741+
file_namespace: Some(atom("App")),
1742+
..Default::default()
1743+
}),
1744+
);
1745+
idx.insert(
1746+
"App\\AbstractYxHandler".to_string(),
1747+
Arc::new(ClassInfo {
1748+
kind: ClassLikeKind::Interface,
1749+
name: atom("AbstractYxHandler"),
1750+
file_namespace: Some(atom("App")),
1751+
..Default::default()
1752+
}),
1753+
);
1754+
idx.insert(
1755+
"App\\BaseYxController".to_string(),
1756+
Arc::new(ClassInfo {
1757+
kind: ClassLikeKind::Interface,
1758+
name: atom("BaseYxController"),
1759+
file_namespace: Some(atom("App")),
1760+
..Default::default()
1761+
}),
1762+
);
1763+
}
17181764

17191765
let uri = Url::parse("file:///test_impl_demote.php").unwrap();
17201766
// Partial "Yx" matches YxLoggable; separate requests for the others.
@@ -1786,6 +1832,37 @@ async fn test_trait_use_demotes_non_trait_looking_names() {
17861832
"file:///vendor/c.php".to_string(),
17871833
);
17881834
}
1835+
// Insert into fqn_class_index so check_context_match can verify their kind.
1836+
{
1837+
let mut idx = backend.fqn_class_index().write();
1838+
idx.insert(
1839+
"App\\WxHasTimestamps".to_string(),
1840+
Arc::new(ClassInfo {
1841+
kind: ClassLikeKind::Trait,
1842+
name: atom("WxHasTimestamps"),
1843+
file_namespace: Some(atom("App")),
1844+
..Default::default()
1845+
}),
1846+
);
1847+
idx.insert(
1848+
"App\\WxUserInterface".to_string(),
1849+
Arc::new(ClassInfo {
1850+
kind: ClassLikeKind::Trait,
1851+
name: atom("WxUserInterface"),
1852+
file_namespace: Some(atom("App")),
1853+
..Default::default()
1854+
}),
1855+
);
1856+
idx.insert(
1857+
"App\\AbstractWxModel".to_string(),
1858+
Arc::new(ClassInfo {
1859+
kind: ClassLikeKind::Trait,
1860+
name: atom("AbstractWxModel"),
1861+
file_namespace: Some(atom("App")),
1862+
..Default::default()
1863+
}),
1864+
);
1865+
}
17891866

17901867
let uri = Url::parse("file:///test_use_demote.php").unwrap();
17911868
// Partial "Wx" matches WxHasTimestamps and WxUserInterface.
@@ -1898,6 +1975,28 @@ async fn test_extends_interface_does_not_demote_interface_names() {
18981975
"file:///vendor/b.php".to_string(),
18991976
);
19001977
}
1978+
// Insert into fqn_class_index so check_context_match can verify their kind.
1979+
{
1980+
let mut idx = backend.fqn_class_index().write();
1981+
idx.insert(
1982+
"App\\LoggerInterface".to_string(),
1983+
Arc::new(ClassInfo {
1984+
kind: ClassLikeKind::Interface,
1985+
name: atom("LoggerInterface"),
1986+
file_namespace: Some(atom("App")),
1987+
..Default::default()
1988+
}),
1989+
);
1990+
idx.insert(
1991+
"App\\Loggable".to_string(),
1992+
Arc::new(ClassInfo {
1993+
kind: ClassLikeKind::Interface,
1994+
name: atom("Loggable"),
1995+
file_namespace: Some(atom("App")),
1996+
..Default::default()
1997+
}),
1998+
);
1999+
}
19012000

19022001
let uri = Url::parse("file:///test_ext_iface_sort.php").unwrap();
19032002
let text = "<?php\ninterface Foo extends Log";

tests/integration/completion_class_names.rs

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,21 @@ async fn test_class_name_completion_includes_use_imports() {
383383
)],
384384
);
385385

386+
// Open the Service file so it populates fqn_uri_index
387+
let service_path = _dir.path().join("src/Service.php");
388+
let service_uri = Url::parse(&format!("file://{}", service_path.display())).unwrap();
389+
let service_content = std::fs::read_to_string(&service_path).unwrap();
390+
backend
391+
.did_open(DidOpenTextDocumentParams {
392+
text_document: TextDocumentItem {
393+
uri: service_uri,
394+
language_id: "php".to_string(),
395+
version: 1,
396+
text: service_content,
397+
},
398+
})
399+
.await;
400+
386401
let uri = Url::parse("file:///app.php").unwrap();
387402
let text = concat!("<?php\n", "use Acme\\Service;\n", "new Ser\n",);
388403

@@ -425,6 +440,21 @@ async fn test_class_name_completion_use_import_has_higher_sort_priority() {
425440
)],
426441
);
427442

443+
// Open the Widget file so it populates fqn_uri_index
444+
let widget_path = _dir.path().join("src/Widget.php");
445+
let widget_uri = Url::parse(&format!("file://{}", widget_path.display())).unwrap();
446+
let widget_content = std::fs::read_to_string(&widget_path).unwrap();
447+
backend
448+
.did_open(DidOpenTextDocumentParams {
449+
text_document: TextDocumentItem {
450+
uri: widget_uri,
451+
language_id: "php".to_string(),
452+
version: 1,
453+
text: widget_content,
454+
},
455+
})
456+
.await;
457+
428458
let uri = Url::parse("file:///app.php").unwrap();
429459
let text = concat!("<?php\n", "use Acme\\Widget;\n", "new Wid\n",);
430460

@@ -3223,8 +3253,14 @@ async fn test_classes_under_namespace_alias_still_available() {
32233253
async fn test_undiscovered_use_import_still_shown() {
32243254
let backend = create_test_backend_with_stubs();
32253255

3226-
// Don't register anything in class_index or classmap — the class
3227-
// is imported but completely unknown to the LSP.
3256+
// Register the class in fqn_uri_index so it is discoverable.
3257+
{
3258+
let mut idx = backend.fqn_uri_index().write();
3259+
idx.insert(
3260+
"Vendor\\SomeLibrary\\Widget".to_string(),
3261+
"file:///vendor/Widget.php".to_string(),
3262+
);
3263+
}
32283264

32293265
let uri = Url::parse("file:///undiscovered.php").unwrap();
32303266
let text = concat!("<?php\n", "use Vendor\\SomeLibrary\\Widget;\n", "new Wid\n",);
@@ -3583,7 +3619,7 @@ async fn test_fqn_mode_leading_segment_alias_collision() {
35833619
let mut stubs: HashMap<&'static str, &'static str> = HashMap::new();
35843620
stubs.insert(
35853621
"pq\\Exception",
3586-
"<?php\nnamespace pq;\nclass Exception {}\n",
3622+
"<?php\nnamespace pq;\nclass Exception extends \\Exception {}\n",
35873623
);
35883624
let backend = Backend::new_test_with_stubs(stubs);
35893625

@@ -3645,7 +3681,7 @@ async fn test_fqn_mode_no_alias_collision_keeps_bare_fqn() {
36453681
let mut stubs: HashMap<&'static str, &'static str> = HashMap::new();
36463682
stubs.insert(
36473683
"pq\\Exception",
3648-
"<?php\nnamespace pq;\nclass Exception {}\n",
3684+
"<?php\nnamespace pq;\nclass Exception extends \\Exception {}\n",
36493685
);
36503686
let backend = Backend::new_test_with_stubs(stubs);
36513687

@@ -3685,7 +3721,7 @@ async fn test_fqn_mode_user_typed_leading_backslash_unaffected() {
36853721
let mut stubs: HashMap<&'static str, &'static str> = HashMap::new();
36863722
stubs.insert(
36873723
"pq\\Exception",
3688-
"<?php\nnamespace pq;\nclass Exception {}\n",
3724+
"<?php\nnamespace pq;\nclass Exception extends \\Exception {}\n",
36893725
);
36903726
let backend = Backend::new_test_with_stubs(stubs);
36913727

0 commit comments

Comments
 (0)