Skip to content

Commit 0d38b7c

Browse files
authored
Merge pull request #840 from ahoppen/ahoppen/re-apply-pr
Re-apply #834
2 parents fd166e4 + 66a2748 commit 0d38b7c

26 files changed

+1274
-1241
lines changed

Sources/LSPLogging/Logging.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ public func orLog<R>(
6262
}
6363
}
6464

65+
/// Like `try?`, but logs the error on failure.
66+
public func orLog<R>(
67+
_ prefix: String = "",
68+
level: LogLevel = .default,
69+
logger: Logger = Logger.shared,
70+
_ block: () async throws -> R?) async -> R?
71+
{
72+
do {
73+
return try await block()
74+
} catch {
75+
logger.log("\(prefix)\(prefix.isEmpty ? "" : " ")\(error)", level: level)
76+
return nil
77+
}
78+
}
79+
80+
6581
/// Logs the time that the given block takes to execute in milliseconds.
6682
public func logExecutionTime<R>(
6783
_ prefix: String = #function,

Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
public typealias CodeActionProviderCompletion = (LSPResult<[CodeAction]>) -> Void
14-
public typealias CodeActionProvider = (CodeActionRequest, @escaping CodeActionProviderCompletion) -> Void
14+
public typealias CodeActionProvider = (CodeActionRequest, @escaping CodeActionProviderCompletion) async -> Void
1515

1616
/// Request for returning all possible code actions for a given text document and range.
1717
///

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public final class BuildServerBuildSystem: MessageHandler {
6565
/// Delegate to handle any build system events.
6666
public weak var delegate: BuildSystemDelegate?
6767

68+
/// The build settings that have been received from the build server.
69+
private var buildSettings: [DocumentURI: FileBuildSettings] = [:]
70+
6871
public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) throws {
6972
let configPath = projectRoot.appending(component: "buildServer.json")
7073
let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem)
@@ -195,7 +198,18 @@ public final class BuildServerBuildSystem: MessageHandler {
195198
let result = notification.params.updatedOptions
196199
let settings = FileBuildSettings(
197200
compilerArguments: result.options, workingDirectory: result.workingDirectory)
198-
self.delegate?.fileBuildSettingsChanged([notification.params.uri: .modified(settings)])
201+
self.buildSettingsChanged(for: notification.params.uri, settings: settings)
202+
}
203+
204+
/// Record the new build settings for the given document and inform the delegate
205+
/// about the changed build settings.
206+
private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) {
207+
buildSettings[document] = settings
208+
if let settings {
209+
self.delegate?.fileBuildSettingsChanged([document: .modified(settings)])
210+
} else {
211+
self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable])
212+
}
199213
}
200214
}
201215

@@ -208,19 +222,14 @@ private func readReponseDataKey(data: LSPAny?, key: String) -> String? {
208222
return nil
209223
}
210224

211-
extension BuildServerBuildSystem {
212-
/// Exposed for *testing*.
213-
public func _settings(for uri: DocumentURI) -> FileBuildSettings? {
214-
if let response = try? self.buildServer?.sendSync(SourceKitOptions(uri: uri)) {
215-
return FileBuildSettings(
216-
compilerArguments: response.options,
217-
workingDirectory: response.workingDirectory)
218-
}
219-
return nil
220-
}
221-
}
222-
223225
extension BuildServerBuildSystem: BuildSystem {
226+
/// The build settings for the given file.
227+
///
228+
/// Returns `nil` if no build settings have been received from the build
229+
/// server yet or if no build settings are available for this file.
230+
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
231+
return buildSettings[document]
232+
}
224233

225234
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
226235
let request = RegisterForChanges(uri: uri, action: .register)
@@ -230,7 +239,7 @@ extension BuildServerBuildSystem: BuildSystem {
230239

231240
// BuildServer registration failed, so tell our delegate that no build
232241
// settings are available.
233-
self.delegate?.fileBuildSettingsChanged([uri: .removedOrUnavailable])
242+
self.buildSettingsChanged(for: uri, settings: nil)
234243
}
235244
})
236245
}

Sources/SKCore/BuildSystem.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ public protocol BuildSystem: AnyObject {
5151
/// initial reports as well as changes.
5252
var delegate: BuildSystemDelegate? { get set }
5353

54+
/// Retrieve build settings for the given document with the given source
55+
/// language.
56+
///
57+
/// Returns `nil` if the build system can't provide build settings for this
58+
/// file or if it hasn't computed build settings for the file yet.
59+
func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings?
60+
5461
/// Register the given file for build-system level change notifications, such
5562
/// as command line flag changes, dependency changes, etc.
5663
///

Sources/SKCore/BuildSystemManager.swift

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,41 @@ extension BuildSystemManager {
146146
set { queue.sync { _mainFilesProvider = newValue } }
147147
}
148148

149+
/// Get the build settings for the given document, assuming it has the given
150+
/// language.
151+
///
152+
/// Returns `nil` if no build settings are available in the build system and
153+
/// no fallback build settings can be computed.
154+
///
155+
/// `isFallback` is `true` if the build settings couldn't be computed and
156+
/// fallback settings are used. These fallback settings are most likely not
157+
/// correct and provide limited semantic functionality.
158+
public func buildSettings(
159+
for document: DocumentURI,
160+
language: Language
161+
) async -> (buildSettings: FileBuildSettings, isFallback: Bool)? {
162+
do {
163+
// FIXME: (async) We should only wait `fallbackSettingsTimeout` for build
164+
// settings and return fallback afterwards. I am not sure yet, how best to
165+
// implement that with Swift concurrency.
166+
// For now, this should be fine because all build systems return
167+
// very quickly from `settings(for:language:)`.
168+
if let settings = try await buildSystem?.buildSettings(for: document, language: language) {
169+
return (buildSettings: settings, isFallback: false)
170+
}
171+
} catch {
172+
log("Getting build settings failed: \(error)")
173+
}
174+
if let settings = fallbackBuildSystem?.buildSettings(for: document, language: language) {
175+
// If there is no build system and we only have the fallback build system,
176+
// we will never get real build settings. Consider the build settings
177+
// non-fallback.
178+
return (buildSettings: settings, isFallback: buildSystem != nil)
179+
} else {
180+
return nil
181+
}
182+
}
183+
149184
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
150185
return queue.async {
151186
log("registerForChangeNotifications(\(uri.pseudoPath))")
@@ -222,7 +257,7 @@ extension BuildSystemManager {
222257
} else if let fallback = self.fallbackBuildSystem {
223258
// Only have a fallback build system. We consider it be a primary build
224259
// system that functions synchronously.
225-
if let settings = fallback.settings(for: mainFile, language) {
260+
if let settings = fallback.buildSettings(for: mainFile, language: language) {
226261
newStatus = .primary(settings)
227262
} else {
228263
newStatus = .unsupported
@@ -235,7 +270,7 @@ extension BuildSystemManager {
235270
}
236271

237272
/// *Must be called on queue*. Update and notify our delegate for the given
238-
/// main file changes if they are convertable into `FileBuildSettingsChange`.
273+
/// main file changes if they are convertible into `FileBuildSettingsChange`.
239274
func updateAndNotifyStatuses(changes: [DocumentURI: MainFileStatus]) {
240275
var changedWatchedFiles = [DocumentURI: FileBuildSettingsChange]()
241276
for (mainFile, status) in changes {
@@ -279,11 +314,11 @@ extension BuildSystemManager {
279314
_ fallback: FallbackBuildSystem
280315
) {
281316
// There won't be a current status if it's unreferenced by any watched file.
282-
// Simiarly, if the status isn't `waiting` then there's nothing to do.
317+
// Similarly, if the status isn't `waiting` then there's nothing to do.
283318
guard let status = self.mainFileStatuses[mainFile], status == .waiting else {
284319
return
285320
}
286-
if let settings = fallback.settings(for: mainFile, language) {
321+
if let settings = fallback.buildSettings(for: mainFile, language: language) {
287322
self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)])
288323
} else {
289324
// Keep the status as waiting.
@@ -336,9 +371,9 @@ extension BuildSystemManager: BuildSystemDelegate {
336371
newStatus = settingsChange.isFallback ? .fallback(newSettings) : .primary(newSettings)
337372
} else if let fallback = self.fallbackBuildSystem {
338373
// FIXME: we need to stop threading the language everywhere, or we need the build system
339-
// itself to pass it in here. Or alteratively cache the fallback settings/language earlier?
374+
// itself to pass it in here. Or alternatively cache the fallback settings/language earlier?
340375
let language = firstWatch.value.language
341-
if let settings = fallback.settings(for: mainFile, language) {
376+
if let settings = fallback.buildSettings(for: mainFile, language: language) {
342377
newStatus = .fallback(settings)
343378
} else {
344379
newStatus = .unsupported

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
9393

9494
public var indexPrefixMappings: [PathPrefixMapping] { return [] }
9595

96+
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
97+
// FIXME: (async) Convert this to an async function once `CompilationDatabaseBuildSystem` is an actor.
98+
return await withCheckedContinuation { continuation in
99+
self.queue.async {
100+
continuation.resume(returning: self.settings(for: document))
101+
}
102+
}
103+
}
104+
96105
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
97106
queue.async {
98107
self.watchedFiles[uri] = language

Sources/SKCore/FallbackBuildSystem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public final class FallbackBuildSystem: BuildSystem {
4444

4545
public var indexPrefixMappings: [PathPrefixMapping] { return [] }
4646

47-
public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? {
47+
public func buildSettings(for uri: DocumentURI, language: Language) -> FileBuildSettings? {
4848
switch language {
4949
case .swift:
5050
return settingsSwift(uri.pseudoPath)
@@ -58,7 +58,7 @@ public final class FallbackBuildSystem: BuildSystem {
5858
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
5959
guard let delegate = self.delegate else { return }
6060

61-
let settings = self.settings(for: uri, language)
61+
let settings = self.buildSettings(for: uri, language: language)
6262
DispatchQueue.global().async {
6363
delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
6464
}

Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,18 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
290290
}
291291
}
292292

293+
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
294+
return try await withCheckedThrowingContinuation { continuation in
295+
queue.async {
296+
do {
297+
continuation.resume(returning: try self.settings(for: document, language))
298+
} catch {
299+
continuation.resume(throwing: error)
300+
}
301+
}
302+
}
303+
}
304+
293305
/// Must only be called on `queue`.
294306
private func settings(
295307
for uri: DocumentURI,

0 commit comments

Comments
 (0)