Collapse nested Java source candidates

This commit is contained in:
John Burwell 2026-06-02 16:16:01 -05:00
parent ba6edf6cc4
commit 6e728724bb
2 changed files with 69 additions and 0 deletions

View File

@ -684,6 +684,7 @@ enum JavaContentScanner {
var candidatesByID: [String: SourceCandidate] = [:] var candidatesByID: [String: SourceCandidate] = [:]
for root in roots { for root in roots {
let candidateFolders = boundedCandidateFolders(from: root, maxDepth: 4, maxFolderCount: 600, fileManager: fileManager) let candidateFolders = boundedCandidateFolders(from: root, maxDepth: 4, maxFolderCount: 600, fileManager: fileManager)
var candidatesForRoot: [SourceCandidate] = []
for folderURL in candidateFolders { for folderURL in candidateFolders {
guard let probe = probeLocalFolder(folderURL, providerID: providerID) else { guard let probe = probeLocalFolder(folderURL, providerID: providerID) else {
continue continue
@ -699,6 +700,14 @@ enum JavaContentScanner {
detectedKinds: probe.detectedKinds detectedKinds: probe.detectedKinds
) )
candidatesForRoot.append(candidate)
}
for candidate in collapsedCandidates(
candidatesForRoot,
under: root,
providerID: providerID
) {
if let existingCandidate = candidatesByID[candidate.id], if let existingCandidate = candidatesByID[candidate.id],
existingCandidate.confidence >= candidate.confidence { existingCandidate.confidence >= candidate.confidence {
continue continue
@ -938,6 +947,40 @@ enum JavaContentScanner {
return candidates return candidates
} }
nonisolated private static func collapsedCandidates(
_ candidates: [SourceCandidate],
under root: URL,
providerID: PlatformProviderID
) -> [SourceCandidate] {
let uniqueCandidates = Dictionary(grouping: candidates, by: \.sourceRootURL).compactMap { _, groupedCandidates in
groupedCandidates.max { lhs, rhs in
lhs.confidence < rhs.confidence
}
}
guard uniqueCandidates.count > 1 else {
return uniqueCandidates
}
let detectedKinds = uniqueCandidates.reduce(into: Set<MinecraftContentKind>()) { result, candidate in
result.formUnion(candidate.detectedKinds)
}
let confidence = uniqueCandidates.map(\.confidence).max() ?? .medium
let standardizedRoot = root.standardizedFileURL
return [
SourceCandidate(
providerID: providerID,
edition: .java,
sourceRootURL: standardizedRoot,
displayName: standardizedRoot.lastPathComponent,
confidence: confidence,
reason: "Found multiple Java sources under \(standardizedRoot.lastPathComponent)",
detectedKinds: detectedKinds
)
]
}
nonisolated private static func javaProbeScore(for url: URL, fileManager: FileManager) -> (value: Int, kinds: Set<MinecraftContentKind>) { nonisolated private static func javaProbeScore(for url: URL, fileManager: FileManager) -> (value: Int, kinds: Set<MinecraftContentKind>) {
var score = 0 var score = 0
var kinds = Set<MinecraftContentKind>() var kinds = Set<MinecraftContentKind>()

View File

@ -359,6 +359,32 @@ struct World_Manager_for_MinecraftTests {
}) })
} }
@Test func javaProviderCollapsesNestedSourceCandidatesToSearchRoot() async throws {
let fileManager = FileManager.default
let workingURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
let firstInstanceURL = workingURL.appendingPathComponent("a/b/c", isDirectory: true)
let secondInstanceURL = workingURL.appendingPathComponent("a/e/f", isDirectory: true)
defer { try? fileManager.removeItem(at: workingURL) }
for instanceURL in [firstInstanceURL, secondInstanceURL] {
try fileManager.createDirectory(
at: instanceURL.appendingPathComponent("mods", isDirectory: true),
withIntermediateDirectories: true
)
try Data("jar".utf8).write(to: instanceURL.appendingPathComponent("mods/ExampleMod.jar"))
}
let candidates = JavaContentScanner.discoverSourceCandidates(
providerID: JavaLocalFolderSourceAccess().accessorIdentifier,
searchRoots: [workingURL]
)
#expect(candidates.count == 1)
#expect(candidates.first?.sourceRootURL == workingURL.standardizedFileURL)
#expect(candidates.first?.displayName == workingURL.lastPathComponent)
#expect(candidates.first?.detectedKinds.contains(.mod) == true)
}
@Test func sourceLibraryAddSourceResolvesJavaWrapperFolder() async throws { @Test func sourceLibraryAddSourceResolvesJavaWrapperFolder() async throws {
let fileManager = FileManager.default let fileManager = FileManager.default
let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true) let rootURL = fileManager.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)