11/// Completion item building.
22///
33/// This module contains the logic for constructing LSP `CompletionItem`s from
4- /// resolved `ClassInfo`, filtered by the `AccessKind` (arrow vs double-colon).
4+ /// resolved `ClassInfo`, filtered by the `AccessKind` (arrow, double-colon,
5+ /// or parent double-colon).
56use tower_lsp:: lsp_types:: * ;
67
78use crate :: Backend ;
9+ use crate :: types:: Visibility ;
810use crate :: types:: * ;
911
1012/// PHP magic methods that should not appear in completion results.
@@ -78,6 +80,8 @@ impl Backend {
7880 ///
7981 /// - `Arrow` access: returns only non-static methods and properties.
8082 /// - `DoubleColon` access: returns only static methods, static properties, and constants.
83+ /// - `ParentDoubleColon` access: returns both static and non-static methods,
84+ /// static properties, and constants — but excludes private members.
8185 /// - `Other` access: returns all members.
8286 pub ( crate ) fn build_completion_items (
8387 target_class : & ClassInfo ,
@@ -91,9 +95,18 @@ impl Backend {
9195 continue ;
9296 }
9397
98+ // parent:: excludes private members
99+ if access_kind == AccessKind :: ParentDoubleColon
100+ && method. visibility == Visibility :: Private
101+ {
102+ continue ;
103+ }
104+
94105 let include = match access_kind {
95106 AccessKind :: Arrow => !method. is_static ,
96107 AccessKind :: DoubleColon => method. is_static ,
108+ // parent:: shows both static and non-static methods
109+ AccessKind :: ParentDoubleColon => true ,
97110 AccessKind :: Other => true ,
98111 } ;
99112 if !include {
@@ -113,19 +126,28 @@ impl Backend {
113126
114127 // Properties — filtered by static / instance
115128 for property in & target_class. properties {
129+ // parent:: excludes private members
130+ if access_kind == AccessKind :: ParentDoubleColon
131+ && property. visibility == Visibility :: Private
132+ {
133+ continue ;
134+ }
135+
116136 let include = match access_kind {
117137 AccessKind :: Arrow => !property. is_static ,
118- AccessKind :: DoubleColon => property. is_static ,
138+ AccessKind :: DoubleColon | AccessKind :: ParentDoubleColon => property. is_static ,
119139 AccessKind :: Other => true ,
120140 } ;
121141 if !include {
122142 continue ;
123143 }
124144
125- // Static properties accessed via `::` need the `$` prefix
126- // (e.g. `self::$path`), while instance properties via `->`
145+ // Static properties accessed via `::` or `parent::` need the `$`
146+ // prefix (e.g. `self::$path`), while instance properties via `->`
127147 // use the bare name (e.g. `$this->path`).
128- let display_name = if access_kind == AccessKind :: DoubleColon {
148+ let display_name = if access_kind == AccessKind :: DoubleColon
149+ || access_kind == AccessKind :: ParentDoubleColon
150+ {
129151 format ! ( "${}" , property. name)
130152 } else {
131153 property. name . clone ( )
@@ -147,9 +169,19 @@ impl Backend {
147169 } ) ;
148170 }
149171
150- // Constants — only for `::` or unqualified access
151- if access_kind == AccessKind :: DoubleColon || access_kind == AccessKind :: Other {
172+ // Constants — only for `::`, `parent::`, or unqualified access
173+ if access_kind == AccessKind :: DoubleColon
174+ || access_kind == AccessKind :: ParentDoubleColon
175+ || access_kind == AccessKind :: Other
176+ {
152177 for constant in & target_class. constants {
178+ // parent:: excludes private members
179+ if access_kind == AccessKind :: ParentDoubleColon
180+ && constant. visibility == Visibility :: Private
181+ {
182+ continue ;
183+ }
184+
153185 let detail = if let Some ( ref th) = constant. type_hint {
154186 format ! ( "Class: {} — {}" , target_class. name, th)
155187 } else {
0 commit comments