1313#if compiler(>=6)
1414package import BuildServerProtocol
1515import Dispatch
16- import Foundation
16+ package import Foundation
1717package import LanguageServerProtocol
1818package import LanguageServerProtocolExtensions
1919import SKLogging
@@ -23,8 +23,7 @@ package import SwiftExtensions
2323package import ToolchainRegistry
2424import TSCExtensions
2525
26- package import struct TSCBasic. AbsolutePath
27- package import struct TSCBasic. RelativePath
26+ import struct TSCBasic. RelativePath
2827#else
2928import BuildServerProtocol
3029import Dispatch
@@ -38,7 +37,6 @@ import SwiftExtensions
3837import ToolchainRegistry
3938import TSCExtensions
4039
41- import struct TSCBasic. AbsolutePath
4240import struct TSCBasic. RelativePath
4341#endif
4442
@@ -138,31 +136,31 @@ private enum BuildSystemAdapter {
138136
139137private extension BuildSystemSpec {
140138 private static func createBuiltInBuildSystemAdapter(
141- projectRoot: AbsolutePath ,
139+ projectRoot: URL ,
142140 messagesToSourceKitLSPHandler: any MessageHandler ,
143141 buildSystemTestHooks: BuildSystemTestHooks ,
144142 _ createBuildSystem: @Sendable ( _ connectionToSourceKitLSP: any Connection ) async throws -> BuiltInBuildSystem ?
145143 ) async -> BuildSystemAdapter ? {
146144 let connectionToSourceKitLSP = LocalConnection (
147- receiverName: " BuildSystemManager for \( projectRoot. asURL . lastPathComponent) "
145+ receiverName: " BuildSystemManager for \( projectRoot. lastPathComponent) "
148146 )
149147 connectionToSourceKitLSP. start ( handler: messagesToSourceKitLSPHandler)
150148
151149 let buildSystem = await orLog ( " Creating build system " ) {
152150 try await createBuildSystem ( connectionToSourceKitLSP)
153151 }
154152 guard let buildSystem else {
155- logger. log ( " Failed to create build system at \( projectRoot. pathString ) " )
153+ logger. log ( " Failed to create build system at \( projectRoot) " )
156154 return nil
157155 }
158- logger. log ( " Created \( type ( of: buildSystem) , privacy: . public) at \( projectRoot. pathString ) " )
156+ logger. log ( " Created \( type ( of: buildSystem) , privacy: . public) at \( projectRoot) " )
159157 let buildSystemAdapter = BuiltInBuildSystemAdapter (
160158 underlyingBuildSystem: buildSystem,
161159 connectionToSourceKitLSP: connectionToSourceKitLSP,
162160 buildSystemTestHooks: buildSystemTestHooks
163161 )
164162 let connectionToBuildSystem = LocalConnection (
165- receiverName: " \( type ( of: buildSystem) ) for \( projectRoot. asURL . lastPathComponent) "
163+ receiverName: " \( type ( of: buildSystem) ) for \( projectRoot. lastPathComponent) "
166164 )
167165 connectionToBuildSystem. start ( handler: buildSystemAdapter)
168166 return . builtIn( buildSystemAdapter, connectionToBuildSystem: connectionToBuildSystem)
@@ -185,10 +183,10 @@ private extension BuildSystemSpec {
185183 )
186184 }
187185 guard let buildSystem else {
188- logger. log ( " Failed to create external build system at \( projectRoot. pathString ) " )
186+ logger. log ( " Failed to create external build system at \( projectRoot) " )
189187 return nil
190188 }
191- logger. log ( " Created external build server at \( projectRoot. pathString ) " )
189+ logger. log ( " Created external build server at \( projectRoot) " )
192190 return . external( buildSystem)
193191 case . compilationDatabase:
194192 return await Self . createBuiltInBuildSystemAdapter (
@@ -245,7 +243,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
245243 /// For compilation databases it is the root folder based on which the compilation database was found.
246244 ///
247245 /// `nil` if the `BuildSystemManager` does not have an underlying build system.
248- package let projectRoot : AbsolutePath ?
246+ package let projectRoot : URL ?
249247
250248 /// The files for which the delegate has requested change notifications, ie. the files for which the delegate wants to
251249 /// get `fileBuildSettingsChanged` and `filesDependenciesUpdated` callbacks.
@@ -340,7 +338,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
340338 let files : [ DocumentURI : SourceFileInfo ]
341339
342340 /// The source directories in the workspace, ie. all `SourceItem`s that have `kind == .directory`.
343- let directories : [ DocumentURI : SourceFileInfo ]
341+ ///
342+ /// `pathComponents` is the result of `key.fileURL?.pathComponents`. We frequently need these path components to
343+ /// determine if a file is descendent of the directory and computing them from the `DocumentURI` is expensive.
344+ let directories : [ DocumentURI : ( pathComponents: [ String ] ? , info: SourceFileInfo ) ]
344345 }
345346
346347 private let cachedSourceFilesAndDirectories = Cache < SourceFilesAndDirectoriesKey , SourceFilesAndDirectories > ( )
@@ -402,7 +403,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
402403 displayName: " SourceKit-LSP " ,
403404 version: " " ,
404405 bspVersion: " 2.2.0 " ,
405- rootUri: URI ( buildSystemSpec. projectRoot. asURL ) ,
406+ rootUri: URI ( buildSystemSpec. projectRoot) ,
406407 capabilities: BuildClientCapabilities ( languageIds: [ . c, . cpp, . objective_c, . objective_cpp, . swift] )
407408 )
408409 )
@@ -608,7 +609,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
608609 in target: BuildTargetIdentifier ? ,
609610 language: Language
610611 ) async -> Toolchain ? {
611- let toolchainPath = await orLog ( " Getting toolchain from build targets " ) { ( ) -> AbsolutePath ? in
612+ let toolchainPath = await orLog ( " Getting toolchain from build targets " ) { ( ) -> URL ? in
612613 guard let target else {
613614 return nil
614615 }
@@ -624,7 +625,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
624625 logger. error ( " Toolchain is not a file URL " )
625626 return nil
626627 }
627- return try AbsolutePath ( validating : toolchainUrl. filePath )
628+ return toolchainUrl
628629 }
629630 if let toolchainPath {
630631 if let toolchain = await self . toolchainRegistry. toolchain ( withPath: toolchainPath) {
@@ -634,7 +635,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
634635 }
635636
636637 switch language {
637- case . swift:
638+ case . swift, . markdown , . tutorial :
638639 return await toolchainRegistry. preferredToolchain ( containing: [ \. sourcekitd, \. swift, \. swiftc] )
639640 case . c, . cpp, . objective_c, . objective_cpp:
640641 return await toolchainRegistry. preferredToolchain ( containing: [ \. clang, \. clangd] )
@@ -681,14 +682,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
681682 if let targets = filesAndDirectories. files [ document] ? . targets {
682683 result. formUnion ( targets)
683684 }
684- if !filesAndDirectories. directories. isEmpty,
685- let documentPath = AbsolutePath ( validatingOrNil: try ? document. fileURL? . filePath)
686- {
687- for (directory, info) in filesAndDirectories. directories {
688- guard let directoryPath = AbsolutePath ( validatingOrNil: try ? directory. fileURL? . filePath) else {
685+ if !filesAndDirectories. directories. isEmpty, let documentPathComponents = document. fileURL? . pathComponents {
686+ for (directory, ( directoryPathComponents, info) ) in filesAndDirectories. directories {
687+ guard let directoryPathComponents, let directoryPath = directory. fileURL else {
689688 continue
690689 }
691- if documentPath . isDescendant ( of: directoryPath ) {
690+ if isDescendant ( documentPathComponents , of: directoryPathComponents ) {
692691 result. formUnion ( info. targets)
693692 }
694693 }
@@ -1013,19 +1012,21 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10131012 return [ ]
10141013 }
10151014
1015+ let request = BuildTargetSourcesRequest ( targets: targets. sorted { $0. uri. stringValue < $1. uri. stringValue } )
1016+
10161017 // If we have a cached request for a superset of the targets, serve the result from that cache entry.
10171018 let fromSuperset = await orLog ( " Getting source files from superset request " ) {
1018- try await cachedTargetSources. get ( isolation: self ) { request in
1019- targets. isSubset ( of: request. targets)
1020- } transform: { response in
1021- return BuildTargetSourcesResponse ( items: response. items. filter { targets. contains ( $0. target) } )
1022- }
1019+ try await cachedTargetSources. getDerived (
1020+ isolation: self ,
1021+ request,
1022+ canReuseKey: { targets. isSubset ( of: $0. targets) } ,
1023+ transform: { BuildTargetSourcesResponse ( items: $0. items. filter { targets. contains ( $0. target) } ) }
1024+ )
10231025 }
10241026 if let fromSuperset {
10251027 return fromSuperset. items
10261028 }
10271029
1028- let request = BuildTargetSourcesRequest ( targets: targets. sorted { $0. uri. stringValue < $1. uri. stringValue } )
10291030 let response = try await cachedTargetSources. get ( request, isolation: self ) { request in
10301031 try await buildSystemAdapter. send ( request)
10311032 }
@@ -1059,7 +1060,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10591060
10601061 return try await cachedSourceFilesAndDirectories. get ( key, isolation: self ) { key in
10611062 var files : [ DocumentURI : SourceFileInfo ] = [ : ]
1062- var directories : [ DocumentURI : SourceFileInfo ] = [ : ]
1063+ var directories : [ DocumentURI : ( pathComponents : [ String ] ? , info : SourceFileInfo ) ] = [ : ]
10631064 for sourcesItem in key. sourcesItems {
10641065 let target = targets [ sourcesItem. target] ? . target
10651066 let isPartOfRootProject = !( target? . tags. contains ( . dependency) ?? false )
@@ -1081,7 +1082,9 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10811082 case . file:
10821083 files [ sourceItem. uri] = info. merging ( files [ sourceItem. uri] )
10831084 case . directory:
1084- directories [ sourceItem. uri] = info. merging ( directories [ sourceItem. uri] )
1085+ directories [ sourceItem. uri] = (
1086+ sourceItem. uri. fileURL? . pathComponents, info. merging ( directories [ sourceItem. uri] ? . info)
1087+ )
10851088 }
10861089 }
10871090 }
@@ -1230,3 +1233,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
12301233 }
12311234 }
12321235}
1236+
1237+ /// Returns `true` if the path components `selfPathComponents`, retrieved from `URL.pathComponents` are a descendent
1238+ /// of the other path components.
1239+ ///
1240+ /// This operates directly on path components instead of `URL`s because computing the path components of a URL is
1241+ /// expensive and this allows us to cache the path components.
1242+ private func isDescendant( _ selfPathComponents: [ String ] , of otherPathComponents: [ String ] ) -> Bool {
1243+ return selfPathComponents. dropLast ( ) . starts ( with: otherPathComponents)
1244+ }
0 commit comments