@@ -71,6 +71,7 @@ public final class SourceKitServer: LanguageServer {
71
71
registerWorkspaceNotfication ( SourceKitServer . didSaveDocument)
72
72
registerWorkspaceRequest ( SourceKitServer . completion)
73
73
registerWorkspaceRequest ( SourceKitServer . hover)
74
+ registerWorkspaceRequest ( SourceKitServer . workspaceSymbols)
74
75
registerWorkspaceRequest ( SourceKitServer . definition)
75
76
registerWorkspaceRequest ( SourceKitServer . references)
76
77
registerWorkspaceRequest ( SourceKitServer . documentSymbolHighlight)
@@ -269,7 +270,8 @@ extension SourceKitServer {
269
270
clientCapabilities: req. params. capabilities. textDocument? . codeAction,
270
271
codeActionOptions: CodeActionOptions ( codeActionKinds: nil ) ,
271
272
supportsCodeActions: false // TODO: Turn it on after a provider is implemented.
272
- )
273
+ ) ,
274
+ workspaceSymbolProvider: true
273
275
) ) )
274
276
}
275
277
@@ -338,6 +340,51 @@ extension SourceKitServer {
338
340
toolchainTextDocumentRequest ( req, workspace: workspace, fallback: nil )
339
341
}
340
342
343
+ /// Find all symbols in the workspace that include a string in their name.
344
+ /// - returns: An array of SymbolOccurrences that match the string.
345
+ func findWorkspaceSymbols( matching: String ) -> [ SymbolOccurrence ] {
346
+ var symbolOccurenceResults : [ SymbolOccurrence ] = [ ]
347
+ workspace? . index? . forEachCanonicalSymbolOccurrence (
348
+ containing: matching,
349
+ anchorStart: false ,
350
+ anchorEnd: false ,
351
+ subsequence: true ,
352
+ ignoreCase: true
353
+ ) { symbol in
354
+ if !symbol. location. isSystem && !symbol. roles. contains ( . accessorOf) {
355
+ symbolOccurenceResults. append ( symbol)
356
+ }
357
+ return true
358
+ }
359
+ return symbolOccurenceResults
360
+ }
361
+
362
+ /// Handle a workspace/symbols request, returning the SymbolInformation.
363
+ /// - returns: An array with SymbolInformation for each matching symbol in the workspace.
364
+ func workspaceSymbols( _ req: Request < WorkspaceSymbolsRequest > , workspace: Workspace ) {
365
+ let symbols = findWorkspaceSymbols (
366
+ matching: req. params. query
367
+ ) . map ( { symbolOccurrence -> SymbolInformation in
368
+ let symbolPosition = Position (
369
+ line: symbolOccurrence. location. line - 1 , // 1-based -> 0-based
370
+ // FIXME: we need to convert the utf8/utf16 column, which may require reading the file!
371
+ utf16index: symbolOccurrence. location. utf8Column - 1 )
372
+
373
+ let symbolLocation = Location (
374
+ url: URL ( fileURLWithPath: symbolOccurrence. location. path) ,
375
+ range: Range ( symbolPosition) )
376
+
377
+ return SymbolInformation (
378
+ name: symbolOccurrence. symbol. name,
379
+ kind: symbolOccurrence. symbol. kind. asLspSymbolKind ( ) ,
380
+ deprecated: nil ,
381
+ location: symbolLocation,
382
+ containerName: symbolOccurrence. getContainerName ( )
383
+ )
384
+ } )
385
+ req. reply ( symbols)
386
+ }
387
+
341
388
/// Forwards a SymbolInfoRequest to the appropriate toolchain service for this document.
342
389
func symbolInfo( _ req: Request < SymbolInfoRequest > , workspace: Workspace ) {
343
390
toolchainTextDocumentRequest ( req, workspace: workspace, fallback: [ ] )
@@ -523,3 +570,48 @@ public func languageService(
523
570
524
571
public typealias Notification = LanguageServerProtocol . Notification
525
572
public typealias Diagnostic = LanguageServerProtocol . Diagnostic
573
+
574
+ extension IndexSymbolKind {
575
+ func asLspSymbolKind( ) -> SymbolKind {
576
+ switch self {
577
+ case . class:
578
+ return . class
579
+ case . classMethod, . instanceMethod, . staticMethod:
580
+ return . method
581
+ case . instanceProperty, . staticProperty, . classProperty:
582
+ return . property
583
+ case . enum:
584
+ return . enum
585
+ case . enumConstant:
586
+ return . enumMember
587
+ case . protocol:
588
+ return . interface
589
+ case . function, . conversionFunction:
590
+ return . function
591
+ case . variable:
592
+ return . variable
593
+ case . struct:
594
+ return . struct
595
+ case . parameter:
596
+ return . typeParameter
597
+
598
+ default :
599
+ return . null
600
+ }
601
+ }
602
+ }
603
+
604
+ extension SymbolOccurrence {
605
+ /// Get the name of the symbol that is a parent of this symbol, if one exists
606
+ func getContainerName( ) -> String ? {
607
+ var foundChildRelation : SymbolRelation ?
608
+ self . forEachRelation { relation in
609
+ if relation. roles. contains ( . childOf) {
610
+ foundChildRelation = relation
611
+ return false
612
+ }
613
+ return true
614
+ }
615
+ return foundChildRelation? . symbol. name
616
+ }
617
+ }
0 commit comments