@@ -42,10 +42,20 @@ pub fn goto_definition<'a>(
42
42
let end = convert_point_to_position ( contents, node. end_position ( ) ) ;
43
43
let range = Range { start, end } ;
44
44
45
- // search for a reference in the document index
45
+ // Search for a reference in the document index
46
46
if node. is_identifier ( ) {
47
47
let symbol = document. contents . node_slice ( & node) ?. to_string ( ) ;
48
- if let Some ( ( path, entry) ) = indexer:: find ( symbol. as_str ( ) ) {
48
+
49
+ let uri = & params. text_document_position_params . text_document . uri ;
50
+ let info = if let Ok ( preferred_path) = uri. to_file_path ( ) {
51
+ // First search in current file, then in all files
52
+ indexer:: find_in_file ( symbol. as_str ( ) , & preferred_path)
53
+ . or_else ( || indexer:: find ( symbol. as_str ( ) ) )
54
+ } else {
55
+ indexer:: find ( symbol. as_str ( ) )
56
+ } ;
57
+
58
+ if let Some ( ( path, entry) ) = info {
49
59
let link = LocationLink {
50
60
origin_selection_range : None ,
51
61
target_uri : Url :: from_file_path ( path) . unwrap ( ) ,
@@ -95,7 +105,7 @@ foo <- 42
95
105
print(foo)
96
106
"# ;
97
107
let doc = Document :: new ( code, None ) ;
98
- let ( path, uri) = test_path ( ) ;
108
+ let ( path, uri) = test_path ( "test.R" ) ;
99
109
100
110
indexer:: update ( & doc, & path) . unwrap ( ) ;
101
111
@@ -132,7 +142,7 @@ foo <- 1
132
142
print(foo)
133
143
"# ;
134
144
let doc = Document :: new ( code, None ) ;
135
- let ( path, uri) = test_path ( ) ;
145
+ let ( path, uri) = test_path ( "test.R" ) ;
136
146
137
147
indexer:: update ( & doc, & path) . unwrap ( ) ;
138
148
@@ -159,4 +169,110 @@ print(foo)
159
169
}
160
170
) ;
161
171
}
172
+
173
+ #[ test]
174
+ fn test_goto_definition_prefers_local_symbol ( ) {
175
+ let _guard = indexer:: ResetIndexerGuard ;
176
+
177
+ // Both files define the same symbol
178
+ let code1 = r#"
179
+ foo <- 1
180
+ foo
181
+ "# ;
182
+ let code2 = r#"
183
+ foo <- 2
184
+ foo
185
+ "# ;
186
+
187
+ let doc1 = Document :: new ( code1, None ) ;
188
+ let doc2 = Document :: new ( code2, None ) ;
189
+
190
+ let ( path1, uri1) = test_path ( "file1.R" ) ;
191
+ let ( path2, uri2) = test_path ( "file2.R" ) ;
192
+
193
+ indexer:: update ( & doc1, & path1) . unwrap ( ) ;
194
+ indexer:: update ( & doc2, & path2) . unwrap ( ) ;
195
+
196
+ // Go to definition for foo in file1
197
+ let params1 = GotoDefinitionParams {
198
+ text_document_position_params : lsp_types:: TextDocumentPositionParams {
199
+ text_document : lsp_types:: TextDocumentIdentifier { uri : uri1. clone ( ) } ,
200
+ position : lsp_types:: Position :: new ( 2 , 0 ) ,
201
+ } ,
202
+ work_done_progress_params : Default :: default ( ) ,
203
+ partial_result_params : Default :: default ( ) ,
204
+ } ;
205
+ assert_matches ! (
206
+ goto_definition( & doc1, params1) . unwrap( ) ,
207
+ Some ( GotoDefinitionResponse :: Link ( ref links) ) => {
208
+ // Should jump to foo in file1
209
+ assert_eq!( links[ 0 ] . target_uri, uri1) ;
210
+ }
211
+ ) ;
212
+
213
+ // Go to definition for foo in file2
214
+ let params2 = GotoDefinitionParams {
215
+ text_document_position_params : lsp_types:: TextDocumentPositionParams {
216
+ text_document : lsp_types:: TextDocumentIdentifier { uri : uri2. clone ( ) } ,
217
+ position : lsp_types:: Position :: new ( 2 , 0 ) ,
218
+ } ,
219
+ work_done_progress_params : Default :: default ( ) ,
220
+ partial_result_params : Default :: default ( ) ,
221
+ } ;
222
+ assert_matches ! (
223
+ goto_definition( & doc2, params2) . unwrap( ) ,
224
+ Some ( GotoDefinitionResponse :: Link ( ref links) ) => {
225
+ // Should jump to foo in file2
226
+ assert_eq!( links[ 0 ] . target_uri, uri2) ;
227
+ }
228
+ ) ;
229
+ }
230
+
231
+ #[ test]
232
+ fn test_goto_definition_falls_back_to_other_file ( ) {
233
+ let _guard = indexer:: ResetIndexerGuard ;
234
+
235
+ // file1 defines foo, file2 does not
236
+ let code1 = r#"
237
+ foo <- 1
238
+ "# ;
239
+ let code2 = r#"
240
+ foo
241
+ "# ;
242
+
243
+ let doc1 = Document :: new ( code1, None ) ;
244
+ let doc2 = Document :: new ( code2, None ) ;
245
+
246
+ // Use test_path for cross-platform compatibility
247
+ let ( path1, uri1) = crate :: lsp:: util:: test_path ( "file1.R" ) ;
248
+ let ( path2, uri2) = crate :: lsp:: util:: test_path ( "file2.R" ) ;
249
+
250
+ indexer:: update ( & doc1, & path1) . unwrap ( ) ;
251
+ indexer:: update ( & doc2, & path2) . unwrap ( ) ;
252
+
253
+ // Go to definition for foo in file2 (should jump to file1)
254
+ let params2 = GotoDefinitionParams {
255
+ text_document_position_params : lsp_types:: TextDocumentPositionParams {
256
+ text_document : lsp_types:: TextDocumentIdentifier { uri : uri2. clone ( ) } ,
257
+ position : lsp_types:: Position :: new ( 1 , 0 ) ,
258
+ } ,
259
+ work_done_progress_params : Default :: default ( ) ,
260
+ partial_result_params : Default :: default ( ) ,
261
+ } ;
262
+ let result2 = goto_definition ( & doc2, params2) . unwrap ( ) ;
263
+ assert_matches ! (
264
+ result2,
265
+ Some ( GotoDefinitionResponse :: Link ( ref links) ) => {
266
+ // Should jump to foo in file1
267
+ assert_eq!( links[ 0 ] . target_uri, uri1) ;
268
+ assert_eq!(
269
+ links[ 0 ] . target_range,
270
+ lsp_types:: Range {
271
+ start: lsp_types:: Position :: new( 1 , 0 ) ,
272
+ end: lsp_types:: Position :: new( 1 , 3 ) ,
273
+ }
274
+ ) ;
275
+ }
276
+ ) ;
277
+ }
162
278
}
0 commit comments