diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index dcf189b2..fba894d7 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -221,6 +221,26 @@ extension BuildServerBuildSystem: BuildSystem { } public func filesDidChange(_ events: [FileEvent]) {} + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + guard let fileUrl = uri.fileURL else { + return .unhandled + } + + // FIXME: We should not make any assumptions about which files the build server can handle. + // Instead we should query the build server which files it can handle (#492). + let path = AbsolutePath(fileUrl.path) + if projectRoot.isAncestorOfOrEqual(to: path) { + return .handled + } + + let realpath = resolveSymlinks(path) + if realpath != path, projectRoot.isAncestorOfOrEqual(to: realpath) { + return .handled + } + + return .unhandled + } } private func loadBuildServerConfig(path: AbsolutePath, fileSystem: FileSystem) throws -> BuildServerConfig { diff --git a/Sources/SKCore/BuildSystem.swift b/Sources/SKCore/BuildSystem.swift index e0725f1b..094d368c 100644 --- a/Sources/SKCore/BuildSystem.swift +++ b/Sources/SKCore/BuildSystem.swift @@ -14,6 +14,18 @@ import BuildServerProtocol import LanguageServerProtocol import TSCBasic +/// Defines how well a `BuildSystem` can handle a file with a given URI. +public enum FileHandlingCapability: Comparable { + /// The build system can't handle the file at all + case unhandled + + /// The build system has fallback build settings for the file + case fallback + + /// The build system knows how to handle the file + case handled +} + /// Provider of FileBuildSettings and other build-related information. /// /// The primary role of the build system is to answer queries for @@ -62,6 +74,8 @@ public protocol BuildSystem: AnyObject { /// Called when files in the project change. func filesDidChange(_ events: [FileEvent]) + + func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability } public let buildTargetsNotSupported = diff --git a/Sources/SKCore/BuildSystemManager.swift b/Sources/SKCore/BuildSystemManager.swift index 5f53a50e..84bc2d70 100644 --- a/Sources/SKCore/BuildSystemManager.swift +++ b/Sources/SKCore/BuildSystemManager.swift @@ -348,6 +348,10 @@ extension BuildSystemManager: BuildSystem { } } } + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + return max(buildSystem?.fileHandlingCapability(for: uri) ?? .unhandled, fallbackBuildSystem?.fileHandlingCapability(for: uri) ?? .unhandled) + } } extension BuildSystemManager: BuildSystemDelegate { diff --git a/Sources/SKCore/CompilationDatabaseBuildSystem.swift b/Sources/SKCore/CompilationDatabaseBuildSystem.swift index dcd7b8cf..1bb46fc6 100644 --- a/Sources/SKCore/CompilationDatabaseBuildSystem.swift +++ b/Sources/SKCore/CompilationDatabaseBuildSystem.swift @@ -187,6 +187,19 @@ extension CompilationDatabaseBuildSystem: BuildSystem { } } } + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + guard let fileUrl = uri.fileURL else { + return .unhandled + } + return queue.sync { + if database(for: fileUrl) != nil { + return .handled + } else { + return .unhandled + } + } + } } extension CompilationDatabaseBuildSystem { diff --git a/Sources/SKCore/FallbackBuildSystem.swift b/Sources/SKCore/FallbackBuildSystem.swift index 8faf8d3e..ceb6cade 100644 --- a/Sources/SKCore/FallbackBuildSystem.swift +++ b/Sources/SKCore/FallbackBuildSystem.swift @@ -100,4 +100,8 @@ public final class FallbackBuildSystem: BuildSystem { } public func filesDidChange(_ events: [FileEvent]) {} + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + return .fallback + } } diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index 6b083a4a..eb00ce45 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -361,6 +361,19 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { } } } + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + guard let fileUrl = uri.fileURL else { + return .unhandled + } + return self.queue.sync { + if targetDescription(for: AbsolutePath(fileUrl.path)) != nil { + return .handled + } else { + return .unhandled + } + } + } } extension SwiftPMWorkspace { diff --git a/Sources/SourceKitLSP/SourceKitServer.swift b/Sources/SourceKitLSP/SourceKitServer.swift index 718d14d3..84f2db6a 100644 --- a/Sources/SourceKitLSP/SourceKitServer.swift +++ b/Sources/SourceKitLSP/SourceKitServer.swift @@ -49,11 +49,7 @@ public final class SourceKitServer: LanguageServer { return documentManager } - private var workspaces: [Workspace] = [] { - didSet { - assert(workspaces.count <= 1, "Multiple workspaces aren't supported yet") - } - } + private var workspaces: [Workspace] = [] // **Public for testing** public var _workspaces: [Workspace] { @@ -81,7 +77,22 @@ public final class SourceKitServer: LanguageServer { } public func workspaceForDocument(uri: DocumentURI) -> Workspace? { - return workspaces.first + if workspaces.count == 1 { + // Special handling: If there is only one workspace, open all files in it. + // This retains the behavior of SourceKit-LSP before it supported multiple workspaces. + return workspaces.first + } + + // Pick the workspace with the best FileHandlingCapability for this file. + // If there is a tie, use the workspace that occurred first in the list. + var bestWorkspace: (workspace: Workspace?, fileHandlingCapability: FileHandlingCapability) = (nil, .unhandled) + for workspace in workspaces { + let fileHandlingCapability = workspace.buildSystemManager.fileHandlingCapability(for: uri) + if fileHandlingCapability > bestWorkspace.fileHandlingCapability { + bestWorkspace = (workspace, fileHandlingCapability) + } + } + return bestWorkspace.workspace } public override func _registerBuiltinHandlers() { diff --git a/Tests/SKCoreTests/BuildSystemManagerTests.swift b/Tests/SKCoreTests/BuildSystemManagerTests.swift index ea1d56ab..5ad31b49 100644 --- a/Tests/SKCoreTests/BuildSystemManagerTests.swift +++ b/Tests/SKCoreTests/BuildSystemManagerTests.swift @@ -505,6 +505,14 @@ class ManualBuildSystem: BuildSystem { } func filesDidChange(_ events: [FileEvent]) {} + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + if map[uri] != nil { + return .handled + } else { + return .unhandled + } + } } /// A `BuildSystemDelegate` setup for testing. diff --git a/Tests/SourceKitLSPTests/BuildSystemTests.swift b/Tests/SourceKitLSPTests/BuildSystemTests.swift index 9ae9ccdb..fbaadc83 100644 --- a/Tests/SourceKitLSPTests/BuildSystemTests.swift +++ b/Tests/SourceKitLSPTests/BuildSystemTests.swift @@ -67,6 +67,14 @@ final class TestBuildSystem: BuildSystem { } func filesDidChange(_ events: [FileEvent]) {} + + public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { + if buildSettingsByFile[uri] != nil { + return .handled + } else { + return .unhandled + } + } } final class BuildSystemTests: XCTestCase {