@@ -338,7 +338,10 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
338338 let files : [ DocumentURI : SourceFileInfo ]
339339
340340 /// The source directories in the workspace, ie. all `SourceItem`s that have `kind == .directory`.
341- 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 ) ]
342345 }
343346
344347 private let cachedSourceFilesAndDirectories = Cache < SourceFilesAndDirectoriesKey , SourceFilesAndDirectories > ( )
@@ -679,12 +682,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
679682 if let targets = filesAndDirectories. files [ document] ? . targets {
680683 result. formUnion ( targets)
681684 }
682- if !filesAndDirectories. directories. isEmpty, let documentPath = document. fileURL {
683- for (directory, info) in filesAndDirectories. directories {
684- guard let directoryPath = directory. fileURL 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 {
685688 continue
686689 }
687- if documentPath . isDescendant ( of: directoryPath ) {
690+ if isDescendant ( documentPathComponents , of: directoryPathComponents ) {
688691 result. formUnion ( info. targets)
689692 }
690693 }
@@ -1055,7 +1058,7 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10551058
10561059 return try await cachedSourceFilesAndDirectories. get ( key, isolation: self ) { key in
10571060 var files : [ DocumentURI : SourceFileInfo ] = [ : ]
1058- var directories : [ DocumentURI : SourceFileInfo ] = [ : ]
1061+ var directories : [ DocumentURI : ( pathComponents : [ String ] ? , info : SourceFileInfo ) ] = [ : ]
10591062 for sourcesItem in key. sourcesItems {
10601063 let target = targets [ sourcesItem. target] ? . target
10611064 let isPartOfRootProject = !( target? . tags. contains ( . dependency) ?? false )
@@ -1077,7 +1080,9 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
10771080 case . file:
10781081 files [ sourceItem. uri] = info. merging ( files [ sourceItem. uri] )
10791082 case . directory:
1080- directories [ sourceItem. uri] = info. merging ( directories [ sourceItem. uri] )
1083+ directories [ sourceItem. uri] = (
1084+ sourceItem. uri. fileURL? . pathComponents, info. merging ( directories [ sourceItem. uri] ? . info)
1085+ )
10811086 }
10821087 }
10831088 }
@@ -1226,3 +1231,12 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
12261231 }
12271232 }
12281233}
1234+
1235+ /// Returns `true` if the path components `selfPathComponents`, retrieved from `URL.pathComponents` are a descendent
1236+ /// of the other path components.
1237+ ///
1238+ /// This operates directly on path components instead of `URL`s because computing the path components of a URL is
1239+ /// expensive and this allows us to cache the path components.
1240+ private func isDescendant( _ selfPathComponents: [ String ] , of otherPathComponents: [ String ] ) -> Bool {
1241+ return selfPathComponents. dropLast ( ) . starts ( with: otherPathComponents)
1242+ }
0 commit comments