@@ -209,6 +209,157 @@ describe("tool.document-symbol", () => {
209
209
expect ( result . output ) . toContain ( " Method: processData async (data: string[]): Promise<void> [line 11:2-16:3]" )
210
210
} )
211
211
212
+ test ( "filters symbols by single type" , async ( ) => {
213
+ // Mock response with mixed symbol types
214
+ documentSymbolSpy . mockResolvedValue ( [
215
+ {
216
+ name : "MyClass" ,
217
+ kind : SymbolKind . Class ,
218
+ range : { start : { line : 9 , character : 0 } , end : { line : 29 , character : 1 } } ,
219
+ selectionRange : { start : { line : 9 , character : 6 } , end : { line : 9 , character : 13 } } ,
220
+ children : [
221
+ {
222
+ name : "myMethod" ,
223
+ kind : SymbolKind . Method ,
224
+ range : { start : { line : 10 , character : 2 } , end : { line : 12 , character : 3 } } ,
225
+ selectionRange : { start : { line : 10 , character : 2 } , end : { line : 10 , character : 10 } } ,
226
+ } ,
227
+ ] ,
228
+ } ,
229
+ {
230
+ name : "myFunction" ,
231
+ kind : SymbolKind . Function ,
232
+ range : { start : { line : 31 , character : 0 } , end : { line : 33 , character : 1 } } ,
233
+ selectionRange : { start : { line : 31 , character : 9 } , end : { line : 31 , character : 19 } } ,
234
+ } ,
235
+ {
236
+ name : "MyInterface" ,
237
+ kind : SymbolKind . Interface ,
238
+ range : { start : { line : 35 , character : 0 } , end : { line : 40 , character : 1 } } ,
239
+ selectionRange : { start : { line : 35 , character : 10 } , end : { line : 35 , character : 21 } } ,
240
+ } ,
241
+ ] )
242
+
243
+ const result = await App . provide ( { cwd : projectRoot } , async ( ) => {
244
+ return await tool . execute ( { filePath : "test.ts" , symbolTypes : [ "Function" ] } , ctx )
245
+ } )
246
+
247
+ expect ( result . output ) . toContain ( "Found 1 symbols matching [Function] (3 total):" )
248
+ expect ( result . output ) . toContain ( "Function: myFunction" )
249
+ expect ( result . output ) . not . toContain ( "Class: MyClass" )
250
+ expect ( result . output ) . not . toContain ( "Interface: MyInterface" )
251
+ } )
252
+
253
+ test ( "filters symbols by multiple types" , async ( ) => {
254
+ documentSymbolSpy . mockResolvedValue ( [
255
+ {
256
+ name : "MyClass" ,
257
+ kind : SymbolKind . Class ,
258
+ range : { start : { line : 9 , character : 0 } , end : { line : 29 , character : 1 } } ,
259
+ selectionRange : { start : { line : 9 , character : 6 } , end : { line : 9 , character : 13 } } ,
260
+ } ,
261
+ {
262
+ name : "myFunction" ,
263
+ kind : SymbolKind . Function ,
264
+ range : { start : { line : 31 , character : 0 } , end : { line : 33 , character : 1 } } ,
265
+ selectionRange : { start : { line : 31 , character : 9 } , end : { line : 31 , character : 19 } } ,
266
+ } ,
267
+ {
268
+ name : "MyInterface" ,
269
+ kind : SymbolKind . Interface ,
270
+ range : { start : { line : 35 , character : 0 } , end : { line : 40 , character : 1 } } ,
271
+ selectionRange : { start : { line : 35 , character : 10 } , end : { line : 35 , character : 21 } } ,
272
+ } ,
273
+ ] )
274
+
275
+ const result = await App . provide ( { cwd : projectRoot } , async ( ) => {
276
+ return await tool . execute ( { filePath : "test.ts" , symbolTypes : [ "Class" , "Interface" ] } , ctx )
277
+ } )
278
+
279
+ expect ( result . output ) . toContain ( "Found 2 symbols matching [Class, Interface] (3 total):" )
280
+ expect ( result . output ) . toContain ( "Class: MyClass" )
281
+ expect ( result . output ) . toContain ( "Interface: MyInterface" )
282
+ expect ( result . output ) . not . toContain ( "Function: myFunction" )
283
+ } )
284
+
285
+ test ( "preserves parent when child matches filter" , async ( ) => {
286
+ documentSymbolSpy . mockResolvedValue ( [
287
+ {
288
+ name : "MyClass" ,
289
+ kind : SymbolKind . Class ,
290
+ range : { start : { line : 9 , character : 0 } , end : { line : 29 , character : 1 } } ,
291
+ selectionRange : { start : { line : 9 , character : 6 } , end : { line : 9 , character : 13 } } ,
292
+ children : [
293
+ {
294
+ name : "myMethod" ,
295
+ kind : SymbolKind . Method ,
296
+ range : { start : { line : 10 , character : 2 } , end : { line : 12 , character : 3 } } ,
297
+ selectionRange : { start : { line : 10 , character : 2 } , end : { line : 10 , character : 10 } } ,
298
+ } ,
299
+ {
300
+ name : "myProperty" ,
301
+ kind : SymbolKind . Property ,
302
+ range : { start : { line : 14 , character : 2 } , end : { line : 14 , character : 15 } } ,
303
+ selectionRange : { start : { line : 14 , character : 2 } , end : { line : 14 , character : 12 } } ,
304
+ } ,
305
+ ] ,
306
+ } ,
307
+ {
308
+ name : "standaloneFunction" ,
309
+ kind : SymbolKind . Function ,
310
+ range : { start : { line : 31 , character : 0 } , end : { line : 33 , character : 1 } } ,
311
+ selectionRange : { start : { line : 31 , character : 9 } , end : { line : 31 , character : 27 } } ,
312
+ } ,
313
+ ] )
314
+
315
+ const result = await App . provide ( { cwd : projectRoot } , async ( ) => {
316
+ return await tool . execute ( { filePath : "test.ts" , symbolTypes : [ "Method" ] } , ctx )
317
+ } )
318
+
319
+ // Parent class should be shown because it has a matching child
320
+ expect ( result . output ) . toContain ( "Found 1 symbols matching [Method] (2 total):" )
321
+ expect ( result . output ) . toContain ( "Class: MyClass" )
322
+ expect ( result . output ) . toContain ( "Method: myMethod" )
323
+ expect ( result . output ) . not . toContain ( "Property: myProperty" )
324
+ expect ( result . output ) . not . toContain ( "Function: standaloneFunction" )
325
+ } )
326
+
327
+ test ( "handles non-matching filter" , async ( ) => {
328
+ documentSymbolSpy . mockResolvedValue ( [
329
+ {
330
+ name : "myFunction" ,
331
+ kind : SymbolKind . Function ,
332
+ range : { start : { line : 0 , character : 0 } , end : { line : 2 , character : 1 } } ,
333
+ selectionRange : { start : { line : 0 , character : 9 } , end : { line : 0 , character : 19 } } ,
334
+ } ,
335
+ ] )
336
+
337
+ const result = await App . provide ( { cwd : projectRoot } , async ( ) => {
338
+ return await tool . execute ( { filePath : "test.ts" , symbolTypes : [ "Class" ] } , ctx )
339
+ } )
340
+
341
+ expect ( result . output ) . toBe ( "No symbols found matching filter: Class (1 total symbols in file)" )
342
+ } )
343
+
344
+ test ( "handles empty filter array (shows all)" , async ( ) => {
345
+ documentSymbolSpy . mockResolvedValue ( [
346
+ {
347
+ name : "myFunction" ,
348
+ kind : SymbolKind . Function ,
349
+ range : { start : { line : 0 , character : 0 } , end : { line : 2 , character : 1 } } ,
350
+ selectionRange : { start : { line : 0 , character : 9 } , end : { line : 0 , character : 19 } } ,
351
+ } ,
352
+ ] )
353
+
354
+ const result = await App . provide ( { cwd : projectRoot } , async ( ) => {
355
+ return await tool . execute ( { filePath : "test.ts" , symbolTypes : [ ] } , ctx )
356
+ } )
357
+
358
+ // Empty filter should show all symbols
359
+ expect ( result . output ) . toContain ( "Function: myFunction" )
360
+ expect ( result . output ) . not . toContain ( "matching" )
361
+ } )
362
+
212
363
test ( "sorts symbols by line number" , async ( ) => {
213
364
// Provide symbols in non-sorted order
214
365
documentSymbolSpy . mockResolvedValue ( [
0 commit comments