@@ -199,6 +199,129 @@ async fn test_goto_definition_cross_file_psr4() {
199199 }
200200}
201201
202+ #[ tokio:: test]
203+ async fn test_goto_definition_after_target_did_close ( ) {
204+ // Regression test for issue #99: go-to-definition stops working
205+ // after the target file is closed via textDocument/didClose.
206+ let backend = create_test_backend ( ) ;
207+ let dir = tempfile:: tempdir ( ) . expect ( "failed to create temp dir" ) ;
208+ let src_dir = dir. path ( ) . join ( "src" ) ;
209+ std:: fs:: create_dir_all ( & src_dir) . unwrap ( ) ;
210+
211+ let text_b = concat ! (
212+ "<?php\n " ,
213+ "namespace App;\n " ,
214+ "\n " ,
215+ "class ClassB {\n " ,
216+ " public function doSomething() {}\n " ,
217+ "}\n " ,
218+ ) ;
219+ std:: fs:: write ( src_dir. join ( "ClassB.php" ) , text_b) . unwrap ( ) ;
220+
221+ let uri_b = Url :: from_file_path ( src_dir. join ( "ClassB.php" ) ) . unwrap ( ) ;
222+
223+ // Open ClassB so it gets indexed.
224+ backend
225+ . did_open ( DidOpenTextDocumentParams {
226+ text_document : TextDocumentItem {
227+ uri : uri_b. clone ( ) ,
228+ language_id : "php" . to_string ( ) ,
229+ version : 1 ,
230+ text : text_b. to_string ( ) ,
231+ } ,
232+ } )
233+ . await ;
234+
235+ // Close ClassB — simulates VS Code peek preview closing.
236+ backend
237+ . did_close ( DidCloseTextDocumentParams {
238+ text_document : TextDocumentIdentifier { uri : uri_b. clone ( ) } ,
239+ } )
240+ . await ;
241+
242+ // Open ClassA which references ClassB.
243+ let uri_a = Url :: from_file_path ( src_dir. join ( "ClassA.php" ) ) . unwrap ( ) ;
244+ let text_a = concat ! (
245+ "<?php\n " ,
246+ "namespace App;\n " ,
247+ "\n " ,
248+ "class ClassA {\n " ,
249+ " public function test() {\n " ,
250+ " $b = new ClassB();\n " ,
251+ " $b->doSomething();\n " ,
252+ " }\n " ,
253+ "}\n " ,
254+ ) ;
255+ backend
256+ . did_open ( DidOpenTextDocumentParams {
257+ text_document : TextDocumentItem {
258+ uri : uri_a. clone ( ) ,
259+ language_id : "php" . to_string ( ) ,
260+ version : 1 ,
261+ text : text_a. to_string ( ) ,
262+ } ,
263+ } )
264+ . await ;
265+
266+ // Go-to-definition on "ClassB" (line 5, char 21 = inside "ClassB")
267+ let params = GotoDefinitionParams {
268+ text_document_position_params : TextDocumentPositionParams {
269+ text_document : TextDocumentIdentifier { uri : uri_a. clone ( ) } ,
270+ position : Position {
271+ line : 5 ,
272+ character : 21 ,
273+ } ,
274+ } ,
275+ work_done_progress_params : WorkDoneProgressParams :: default ( ) ,
276+ partial_result_params : PartialResultParams :: default ( ) ,
277+ } ;
278+
279+ let result = backend. goto_definition ( params) . await . unwrap ( ) ;
280+ assert ! (
281+ result. is_some( ) ,
282+ "Go-to-definition should work after target file is closed (issue #99)"
283+ ) ;
284+
285+ match result. unwrap ( ) {
286+ GotoDefinitionResponse :: Scalar ( location) => {
287+ assert_eq ! (
288+ location. uri, uri_b,
289+ "Should jump to ClassB.php"
290+ ) ;
291+ }
292+ other => panic ! ( "Expected Scalar location, got: {:?}" , other) ,
293+ }
294+
295+ // Also test member access: $b->doSomething() (line 6, char 14)
296+ let params = GotoDefinitionParams {
297+ text_document_position_params : TextDocumentPositionParams {
298+ text_document : TextDocumentIdentifier { uri : uri_a } ,
299+ position : Position {
300+ line : 6 ,
301+ character : 14 ,
302+ } ,
303+ } ,
304+ work_done_progress_params : WorkDoneProgressParams :: default ( ) ,
305+ partial_result_params : PartialResultParams :: default ( ) ,
306+ } ;
307+
308+ let result = backend. goto_definition ( params) . await . unwrap ( ) ;
309+ assert ! (
310+ result. is_some( ) ,
311+ "Go-to-definition on member should work after target file is closed (issue #99)"
312+ ) ;
313+
314+ match result. unwrap ( ) {
315+ GotoDefinitionResponse :: Scalar ( location) => {
316+ assert_eq ! (
317+ location. uri, uri_b,
318+ "Member definition should jump to ClassB.php"
319+ ) ;
320+ }
321+ other => panic ! ( "Expected Scalar location for member, got: {:?}" , other) ,
322+ }
323+ }
324+
202325#[ tokio:: test]
203326async fn test_goto_definition_cross_file_with_use_statement ( ) {
204327 let ( backend, _dir) = create_psr4_workspace (
0 commit comments