@@ -52,6 +52,10 @@ struct ExecutableNotFoundError: Error {
5252 let executableName : String
5353}
5454
55+ enum BuildServerNotFoundError : Error {
56+ case fileNotFound
57+ }
58+
5559private struct BuildServerConfig : Codable {
5660 /// The name of the build tool.
5761 let name : String
@@ -94,7 +98,7 @@ actor ExternalBuildSystemAdapter {
9498 private var lastRestart : Date ?
9599
96100 static package func projectRoot( for workspaceFolder: AbsolutePath , options: SourceKitLSPOptions ) -> AbsolutePath ? {
97- guard localFileSystem . isFile ( workspaceFolder . appending ( component : " buildServer.json " ) ) else {
101+ guard getConfigPath ( for : workspaceFolder ) != nil else {
98102 return nil
99103 }
100104 return workspaceFolder
@@ -142,7 +146,10 @@ actor ExternalBuildSystemAdapter {
142146
143147 /// Create a new JSONRPCConnection to the build server.
144148 private func createConnectionToBspServer( ) async throws -> JSONRPCConnection {
145- let configPath = projectRoot. appending ( component: " buildServer.json " )
149+ guard let configPath = ExternalBuildSystemAdapter . getConfigPath ( for: self . projectRoot) else {
150+ throw BuildServerNotFoundError . fileNotFound
151+ }
152+
146153 let serverConfig = try BuildServerConfig . load ( from: configPath)
147154 var serverPath = try AbsolutePath ( validating: serverConfig. argv [ 0 ] , relativeTo: projectRoot)
148155 var serverArgs = Array ( serverConfig. argv [ 1 ... ] )
@@ -178,6 +185,62 @@ actor ExternalBuildSystemAdapter {
178185 ) . connection
179186 }
180187
188+ private static func getConfigPath( for workspaceFolder: AbsolutePath ? = nil ) -> AbsolutePath ? {
189+ var buildServerConfigLocations : [ URL ? ] = [ ]
190+ if let workspaceFolder = workspaceFolder {
191+ buildServerConfigLocations. append ( workspaceFolder. appending ( component: " .bsp " ) . asURL)
192+ }
193+
194+ #if os(Windows)
195+ if let localAppData = ProcessInfo . processInfo. environment [ " LOCALAPPDATA " ] {
196+ buildServerConfigLocations. append ( URL ( fileURLWithPath: localAppData) . appendingPathComponent ( " bsp " ) )
197+ }
198+ if let programData = ProcessInfo . processInfo. environment [ " PROGRAMDATA " ] {
199+ buildServerConfigLocations. append ( URL ( fileURLWithPath: programData) . appendingPathComponent ( " bsp " ) )
200+ }
201+ #else
202+ if let xdgDataHome = ProcessInfo . processInfo. environment [ " XDG_DATA_HOME " ] {
203+ buildServerConfigLocations. append ( URL ( fileURLWithPath: xdgDataHome) . appendingPathComponent ( " bsp " ) )
204+ }
205+
206+ if let libraryUrl = FileManager . default. urls ( for: . applicationSupportDirectory, in: . userDomainMask) . first {
207+ buildServerConfigLocations. append ( libraryUrl. appendingPathComponent ( " bsp " ) )
208+ }
209+
210+ if let xdgDataDirs = ProcessInfo . processInfo. environment [ " XDG_DATA_DIRS " ] {
211+ buildServerConfigLocations += xdgDataDirs. split ( separator: " : " ) . map { xdgDataDir in
212+ URL ( fileURLWithPath: String ( xdgDataDir) ) . appendingPathComponent ( " bsp " )
213+ }
214+ }
215+
216+ if let libraryUrl = FileManager . default. urls ( for: . applicationSupportDirectory, in: . systemDomainMask) . first {
217+ buildServerConfigLocations. append ( libraryUrl. appendingPathComponent ( " bsp " ) )
218+ }
219+ #endif
220+
221+ for case let buildServerConfigLocation? in buildServerConfigLocations {
222+ let jsonFiles =
223+ try ? FileManager . default. contentsOfDirectory ( at: buildServerConfigLocation, includingPropertiesForKeys: nil )
224+ . filter { $0. pathExtension == " json " }
225+
226+ if let configFileURL = jsonFiles? . sorted ( by: { $0. lastPathComponent < $1. lastPathComponent } ) . first,
227+ let configFilePath = AbsolutePath ( validatingOrNil: configFileURL. path)
228+ {
229+ return configFilePath
230+ }
231+ }
232+
233+ // Pre Swift 6.1 SourceKit-LSP looked for `buildServer.json` in the project root. Maintain this search location for
234+ // compatibility even though it's not a standard BSP search location.
235+ if let workspaceFolder = workspaceFolder,
236+ localFileSystem. isFile ( workspaceFolder. appending ( component: " buildServer.json " ) )
237+ {
238+ return workspaceFolder. appending ( component: " buildServer.json " )
239+ }
240+
241+ return nil
242+ }
243+
181244 /// Restart the BSP server after it has crashed.
182245 private func handleBspServerCrash( ) async throws {
183246 // Set `connectionToBuildServer` to `nil` to indicate that there is currently no BSP server running.
0 commit comments