Skip to content

Commit 4e278b6

Browse files
Merge pull request #1 from hi2gage/fix/directory-tree-filtering
Fix directory tree filtering and path handling
2 parents 78de427 + f02615c commit 4e278b6

1 file changed

Lines changed: 46 additions & 3 deletions

File tree

BuildSignal/UI/Explorer/ProjectDetailViewModel.swift

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,22 @@ final class ProjectDetailViewModel: ObservableObject {
163163
url.contains("/deriveddata/") && url.contains("/sourcepackages/")
164164
}
165165

166+
/// Checks if a warning is from a generated or system source (e.g., Swift macros, SDK headers).
167+
func isGeneratedSource(_ warning: Notice) -> Bool {
168+
let url = warning.documentURL.lowercased()
169+
return url.contains("/var/folders/") ||
170+
url.contains("swift-generated-sources") ||
171+
url.contains("@__swiftmacro_") ||
172+
url.contains("/library/developer/xcode/deriveddata/") ||
173+
url.contains(".framework/headers/")
174+
}
175+
166176
private func buildDirectoryTree() -> [DirectoryNode] {
167-
let projectWarnings = activeFilteredWarnings.filter { !isPackageDependency($0) && !$0.documentURL.isEmpty }
177+
let projectWarnings = activeFilteredWarnings.filter {
178+
!isPackageDependency($0) &&
179+
!$0.documentURL.isEmpty &&
180+
!isGeneratedSource($0)
181+
}
168182
guard !projectWarnings.isEmpty else { return [] }
169183

170184
// Count warnings per file (not directory)
@@ -242,7 +256,12 @@ final class ProjectDetailViewModel: ObservableObject {
242256
var currentPath = commonPrefix
243257

244258
for (index, component) in components.enumerated() {
245-
currentPath += "/" + component
259+
// Avoid double slashes when commonPrefix is just "/"
260+
if currentPath == "/" {
261+
currentPath = "/" + component
262+
} else {
263+
currentPath += "/" + component
264+
}
246265
let isLastComponent = index == components.count - 1
247266
currentNode = currentNode.getOrCreateChild(
248267
name: component,
@@ -256,14 +275,38 @@ final class ProjectDetailViewModel: ObservableObject {
256275
}
257276
}
258277

259-
return rootContainer.children.values
278+
var topLevelNodes = rootContainer.children.values
260279
.map { $0.toDirectoryNode() }
261280
.sorted {
262281
if $0.warningCount != $1.warningCount {
263282
return $0.warningCount > $1.warningCount
264283
}
265284
return $0.name < $1.name
266285
}
286+
287+
// Collapse single-child chains to start at the first meaningful branching point
288+
topLevelNodes = collapseSingleChildChains(topLevelNodes)
289+
290+
return topLevelNodes
291+
}
292+
293+
/// Collapses single-child directory chains to show the first meaningful branching point.
294+
/// For example: Users/g.halverson/Dev/project with one child each becomes just "project".
295+
private func collapseSingleChildChains(_ nodes: [DirectoryNode]) -> [DirectoryNode] {
296+
// If we have exactly one node and it has children, check if we should skip it
297+
guard nodes.count == 1,
298+
let singleNode = nodes.first,
299+
!singleNode.children.isEmpty else {
300+
return nodes
301+
}
302+
303+
// If this single node has multiple children, this is our branching point - return it
304+
if singleNode.children.count > 1 {
305+
return nodes
306+
}
307+
308+
// Single node with single child - recurse to collapse further
309+
return collapseSingleChildChains(singleNode.children)
267310
}
268311

269312
private func getFilePath(from documentURL: String) -> String {

0 commit comments

Comments
 (0)