Revert "Call into the BuildSystemManager from SwiftLanguageServer to get build settings"

This reverts commit 9dd38798bb.
This commit is contained in:
Ben Barham
2023-09-28 15:51:07 -07:00
parent 212c497eb0
commit 15bdcc42e1
21 changed files with 137 additions and 221 deletions

View File

@@ -11,7 +11,7 @@
//===----------------------------------------------------------------------===//
public typealias CodeActionProviderCompletion = (LSPResult<[CodeAction]>) -> Void
public typealias CodeActionProvider = (CodeActionRequest, @escaping CodeActionProviderCompletion) async -> Void
public typealias CodeActionProvider = (CodeActionRequest, @escaping CodeActionProviderCompletion) -> Void
/// Request for returning all possible code actions for a given text document and range.
///

View File

@@ -65,9 +65,6 @@ public final class BuildServerBuildSystem: MessageHandler {
/// Delegate to handle any build system events.
public weak var delegate: BuildSystemDelegate?
/// The build settings that have been received from the build server.
private var buildSettings: [DocumentURI: FileBuildSettings] = [:]
public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) throws {
let configPath = projectRoot.appending(component: "buildServer.json")
let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem)
@@ -198,18 +195,7 @@ public final class BuildServerBuildSystem: MessageHandler {
let result = notification.params.updatedOptions
let settings = FileBuildSettings(
compilerArguments: result.options, workingDirectory: result.workingDirectory)
self.buildSettingsChanged(for: notification.params.uri, settings: settings)
}
/// Record the new build settings for the given document and inform the delegate
/// about the changed build settings.
private func buildSettingsChanged(for document: DocumentURI, settings: FileBuildSettings?) {
buildSettings[document] = settings
if let settings {
self.delegate?.fileBuildSettingsChanged([document: .modified(settings)])
} else {
self.delegate?.fileBuildSettingsChanged([document: .removedOrUnavailable])
}
self.delegate?.fileBuildSettingsChanged([notification.params.uri: .modified(settings)])
}
}
@@ -222,14 +208,19 @@ private func readReponseDataKey(data: LSPAny?, key: String) -> String? {
return nil
}
extension BuildServerBuildSystem: BuildSystem {
/// The build settings for the given file.
///
/// Returns `nil` if no build settings have been received from the build
/// server yet or if no build settings are available for this file.
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
return buildSettings[document]
extension BuildServerBuildSystem {
/// Exposed for *testing*.
public func _settings(for uri: DocumentURI) -> FileBuildSettings? {
if let response = try? self.buildServer?.sendSync(SourceKitOptions(uri: uri)) {
return FileBuildSettings(
compilerArguments: response.options,
workingDirectory: response.workingDirectory)
}
return nil
}
}
extension BuildServerBuildSystem: BuildSystem {
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
let request = RegisterForChanges(uri: uri, action: .register)
@@ -239,7 +230,7 @@ extension BuildServerBuildSystem: BuildSystem {
// BuildServer registration failed, so tell our delegate that no build
// settings are available.
self.buildSettingsChanged(for: uri, settings: nil)
self.delegate?.fileBuildSettingsChanged([uri: .removedOrUnavailable])
}
})
}

View File

@@ -51,13 +51,6 @@ public protocol BuildSystem: AnyObject {
/// initial reports as well as changes.
var delegate: BuildSystemDelegate? { get set }
/// Retrieve build settings for the given document with the given source
/// language.
///
/// Returns `nil` if the build system can't provide build settings for this
/// file or if it hasn't computed build settings for the file yet.
func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings?
/// Register the given file for build-system level change notifications, such
/// as command line flag changes, dependency changes, etc.
///

View File

@@ -146,41 +146,6 @@ extension BuildSystemManager {
set { queue.sync { _mainFilesProvider = newValue } }
}
/// Get the build settings for the given document, assuming it has the given
/// language.
///
/// Returns `nil` if no build settings are availabe in the build system and
/// no fallback build settings can be computed.
///
/// `isFallback` is `true` if the build settings couldn't be computed and
/// fallback settings are used. These fallback settings are most likely not
/// correct and provide limited semantic functionality.
public func buildSettings(
for document: DocumentURI,
language: Language
) async -> (buildSettings: FileBuildSettings, isFallback: Bool)? {
do {
// FIXME: (async) We should only wait `fallbackSettingsTimeout` for build
// settings and return fallback afterwards. I am not sure yet, how best to
// implement that with Swift concurrency.
// For now, this should be fine because all build systems return
// very quickly from `settings(for:language:)`.
if let settings = try await buildSystem?.buildSettings(for: document, language: language) {
return (buildSettings: settings, isFallback: false)
}
} catch {
log("Getting build settings failed: \(error)")
}
if let settings = fallbackBuildSystem?.buildSettings(for: document, language: language) {
// If there is no build system and we onlyl have the fallback build system,
// we will never get real build settings. Consider the build settings
// non-fallback.
return (buildSettings: settings, isFallback: buildSystem != nil)
} else {
return nil
}
}
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
return queue.async {
log("registerForChangeNotifications(\(uri.pseudoPath))")
@@ -257,7 +222,7 @@ extension BuildSystemManager {
} else if let fallback = self.fallbackBuildSystem {
// Only have a fallback build system. We consider it be a primary build
// system that functions synchronously.
if let settings = fallback.buildSettings(for: mainFile, language: language) {
if let settings = fallback.settings(for: mainFile, language) {
newStatus = .primary(settings)
} else {
newStatus = .unsupported
@@ -318,7 +283,7 @@ extension BuildSystemManager {
guard let status = self.mainFileStatuses[mainFile], status == .waiting else {
return
}
if let settings = fallback.buildSettings(for: mainFile, language: language) {
if let settings = fallback.settings(for: mainFile, language) {
self.updateAndNotifyStatuses(changes: [mainFile: .waitingUsingFallback(settings)])
} else {
// Keep the status as waiting.
@@ -373,7 +338,7 @@ extension BuildSystemManager: BuildSystemDelegate {
// FIXME: we need to stop threading the language everywhere, or we need the build system
// itself to pass it in here. Or alteratively cache the fallback settings/language earlier?
let language = firstWatch.value.language
if let settings = fallback.buildSettings(for: mainFile, language: language) {
if let settings = fallback.settings(for: mainFile, language) {
newStatus = .fallback(settings)
} else {
newStatus = .unsupported

View File

@@ -93,15 +93,6 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
public var indexPrefixMappings: [PathPrefixMapping] { return [] }
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
// FIXME: (async) Convert this to an async function once `CompilationDatabaseBuildSystem` is an actor.
return await withCheckedContinuation { continuation in
self.queue.async {
continuation.resume(returning: self.settings(for: document))
}
}
}
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
queue.async {
self.watchedFiles[uri] = language

View File

@@ -44,7 +44,7 @@ public final class FallbackBuildSystem: BuildSystem {
public var indexPrefixMappings: [PathPrefixMapping] { return [] }
public func buildSettings(for uri: DocumentURI, language: Language) -> FileBuildSettings? {
public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? {
switch language {
case .swift:
return settingsSwift(uri.pseudoPath)
@@ -58,7 +58,7 @@ public final class FallbackBuildSystem: BuildSystem {
public func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
guard let delegate = self.delegate else { return }
let settings = self.buildSettings(for: uri, language: language)
let settings = self.settings(for: uri, language)
DispatchQueue.global().async {
delegate.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
}

View File

@@ -290,18 +290,6 @@ extension SwiftPMWorkspace: SKCore.BuildSystem {
}
}
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
return try await withCheckedThrowingContinuation { continuation in
queue.async {
do {
continuation.resume(returning: try self.settings(for: document, language))
} catch {
continuation.resume(throwing: error)
}
}
}
}
/// Must only be called on `queue`.
private func settings(
for uri: DocumentURI,

View File

@@ -118,8 +118,7 @@ final class ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
toolchain: Toolchain,
options: SourceKitServer.Options,
workspace: Workspace,
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void,
workspaceForDocument: @escaping (DocumentURI) async -> Workspace?
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void
) throws {
guard let clangdPath = toolchain.clangd else {
return nil

View File

@@ -414,10 +414,6 @@ public actor SourceKitServer {
Task {
await self.reopenDocuments(for: toolchainLanguageServer)
}
},
workspaceForDocument: { [weak self] document in
guard let self else { return nil }
return await self.workspaceForDocument(uri: document)
}
)
@@ -2012,8 +2008,7 @@ func languageService(
options: SourceKitServer.Options,
client: MessageHandler,
in workspace: Workspace,
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void,
workspaceForDocument: @escaping (DocumentURI) async -> Workspace?
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void
) throws -> ToolchainLanguageServer? {
let connectionToClient = LocalConnection()
@@ -2022,8 +2017,7 @@ func languageService(
toolchain: toolchain,
options: options,
workspace: workspace,
reopenDocuments: reopenDocuments,
workspaceForDocument: workspaceForDocument
reopenDocuments: reopenDocuments
)
connectionToClient.start(handler: client)
return server

View File

@@ -17,7 +17,7 @@ import Foundation
extension SwiftLanguageServer {
public func completion(_ req: Request<CompletionRequest>) async {
public func completion(_ req: Request<CompletionRequest>) {
guard let snapshot = documentManager.latestSnapshot(req.params.textDocument.uri) else {
log("failed to find snapshot for url \(req.params.textDocument.uri)")
req.reply(CompletionList(isIncomplete: true, items: []))
@@ -39,13 +39,14 @@ extension SwiftLanguageServer {
let options = req.params.sourcekitlspOptions ?? serverOptions.completionOptions
if options.serverSideFiltering {
await _completionWithServerFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options)
_completionWithServerFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options)
} else {
await _completionWithClientFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options)
_completionWithClientFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options)
}
}
func _completionWithServerFiltering(offset: Int, completionPos: Position, snapshot: DocumentSnapshot, request req: Request<CompletionRequest>, options: SKCompletionOptions) async {
/// Must be called on `queue`.
func _completionWithServerFiltering(offset: Int, completionPos: Position, snapshot: DocumentSnapshot, request req: Request<CompletionRequest>, options: SKCompletionOptions) {
guard let start = snapshot.indexOf(utf8Offset: offset),
let end = snapshot.index(of: req.params.position) else {
log("invalid completion position \(req.params.position)")
@@ -73,7 +74,7 @@ extension SwiftLanguageServer {
snapshot: snapshot,
utf8Offset: offset,
position: completionPos,
compileCommand: await buildSettings(for: snapshot.document.uri))
compileCommand: commandsByFile[snapshot.document.uri])
currentCompletionSession?.close()
currentCompletionSession = session
@@ -82,7 +83,8 @@ extension SwiftLanguageServer {
session.update(filterText: filterText, position: req.params.position, in: snapshot, options: options, completion: req.reply)
}
func _completionWithClientFiltering(offset: Int, completionPos: Position, snapshot: DocumentSnapshot, request req: Request<CompletionRequest>, options: SKCompletionOptions) async {
/// Must be called on `queue`.
func _completionWithClientFiltering(offset: Int, completionPos: Position, snapshot: DocumentSnapshot, request req: Request<CompletionRequest>, options: SKCompletionOptions) {
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
skreq[keys.request] = requests.codecomplete
skreq[keys.offset] = offset
@@ -94,7 +96,7 @@ extension SwiftLanguageServer {
skreq[keys.codecomplete_options] = skreqOptions
// FIXME: SourceKit should probably cache this for us.
if let compileCommand = await buildSettings(for: snapshot.document.uri) {
if let compileCommand = commandsByFile[snapshot.document.uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -84,7 +84,7 @@ extension SwiftLanguageServer {
_ uri: DocumentURI,
_ range: Range<Position>,
additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil,
_ completion: @escaping (Swift.Result<CursorInfo?, CursorInfoError>) -> Void) async
_ completion: @escaping (Swift.Result<CursorInfo?, CursorInfoError>) -> Void)
{
guard let snapshot = documentManager.latestSnapshot(uri) else {
return completion(.failure(.unknownDocument(uri)))
@@ -105,7 +105,7 @@ extension SwiftLanguageServer {
skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath
// FIXME: SourceKit should probably cache this for us.
if let compileCommand = await self.buildSettings(for: uri) {
if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -57,7 +57,7 @@ extension SwiftLanguageServer {
func expressionTypeInfos(
_ uri: DocumentURI,
_ completion: @escaping (Swift.Result<[ExpressionTypeInfo], ExpressionTypeInfoError>) -> Void
) async {
) {
guard let snapshot = documentManager.latestSnapshot(uri) else {
return completion(.failure(.unknownDocument(uri)))
}
@@ -69,7 +69,7 @@ extension SwiftLanguageServer {
skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath
// FIXME: SourceKit should probably cache this for us.
if let compileCommand = await self.buildSettings(for: uri) {
if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -25,7 +25,7 @@ struct FindUSRInfo {
}
extension SwiftLanguageServer {
public func openInterface(_ request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>) async {
public func openInterface(_ request: LanguageServerProtocol.Request<LanguageServerProtocol.OpenInterfaceRequest>) {
let uri = request.params.textDocument.uri
let moduleName = request.params.moduleName
let name = request.params.name
@@ -37,7 +37,7 @@ extension SwiftLanguageServer {
self._findUSRAndRespond(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol)
} else {
// generate interface
await self._openInterface(request: request, uri: uri, name: moduleName, interfaceURI: interfaceDocURI) { result in
self._openInterface(request: request, uri: uri, name: moduleName, interfaceURI: interfaceDocURI) { result in
switch result {
case .success(let interfaceInfo):
do {
@@ -69,7 +69,7 @@ extension SwiftLanguageServer {
uri: DocumentURI,
name: String,
interfaceURI: DocumentURI,
completion: @escaping (Swift.Result<InterfaceInfo, SKDError>) -> Void) async {
completion: @escaping (Swift.Result<InterfaceInfo, SKDError>) -> Void) {
let keys = self.keys
let skreq = SKDRequestDictionary(sourcekitd: sourcekitd)
skreq[keys.request] = requests.editor_open_interface
@@ -79,7 +79,7 @@ extension SwiftLanguageServer {
}
skreq[keys.name] = interfaceURI.pseudoPath
skreq[keys.synthesizedextensions] = 1
if let compileCommand = await self.buildSettings(for: uri) {
if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -126,7 +126,7 @@ extension SwiftLanguageServer {
/// - completion: Completion block to asynchronously receive the SemanticRefactoring data, or error.
func semanticRefactoring(
_ refactorCommand: SemanticRefactorCommand,
_ completion: @escaping (Result<SemanticRefactoring, SemanticRefactoringError>) -> Void) async
_ completion: @escaping (Result<SemanticRefactoring, SemanticRefactoringError>) -> Void)
{
let keys = self.keys
@@ -156,7 +156,7 @@ extension SwiftLanguageServer {
skreq[keys.actionuid] = self.sourcekitd.api.uid_get_from_cstr(refactorCommand.actionString)!
// FIXME: SourceKit should probably cache this for us.
if let compileCommand = await self.buildSettings(for: snapshot.document.uri) {
if let compileCommand = self.commandsByFile[snapshot.document.uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -124,6 +124,8 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
var currentCompletionSession: CodeCompletionSession? = nil
var commandsByFile: [DocumentURI: SwiftCompileCommand] = [:]
/// *For Testing*
public var reusedNodeCallback: ReusedNodeCallback?
@@ -152,12 +154,6 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
/// A callback with which `SwiftLanguageServer` can request its owner to reopen all documents in case it has crashed.
private let reopenDocuments: (ToolchainLanguageServer) -> Void
/// Get the workspace that the document with the given URI belongs to.
///
/// This is used to find the `BuildSystemManager` that is able to deliver
/// build settings for this document.
private let workspaceForDocument: (DocumentURI) async -> Workspace?
/// Creates a language server for the given client using the sourcekitd dylib specified in `toolchain`.
/// `reopenDocuments` is a closure that will be called if sourcekitd crashes and the `SwiftLanguageServer` asks its parent server to reopen all of its documents.
/// Returns `nil` if `sourcektid` couldn't be found.
@@ -166,8 +162,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
toolchain: Toolchain,
options: SourceKitServer.Options,
workspace: Workspace,
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void,
workspaceForDocument: @escaping (DocumentURI) async -> Workspace?
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void
) throws {
guard let sourcekitd = toolchain.sourcekitd else { return nil }
self.client = client
@@ -177,22 +172,10 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
self.documentManager = DocumentManager()
self.state = .connected
self.reopenDocuments = reopenDocuments
self.workspaceForDocument = workspaceForDocument
self.generatedInterfacesPath = options.generatedInterfacesPath.asURL
try FileManager.default.createDirectory(at: generatedInterfacesPath, withIntermediateDirectories: true)
}
func buildSettings(for document: DocumentURI) async -> SwiftCompileCommand? {
guard let workspace = await self.workspaceForDocument(document) else {
return nil
}
if let settings = await workspace.buildSystemManager.buildSettings(for: document, language: .swift) {
return SwiftCompileCommand(settings.buildSettings, isFallback: settings.isFallback)
} else {
return nil
}
}
public nonisolated func canHandle(workspace: Workspace) -> Bool {
// We have a single sourcekitd instance for all workspaces.
return true
@@ -305,15 +288,10 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
/// Register the diagnostics returned from sourcekitd in `currentDiagnostics`
/// and returns the corresponding LSP diagnostics.
///
/// If `isFromFallbackBuildSettings` is `true`, then only parse diagnostics are
/// stored and any semantic diagnostics are ignored since they are probably
/// incorrect in the absence of build settings.
private func registerDiagnostics(
sourcekitdDiagnostics: SKDResponseArray?,
snapshot: DocumentSnapshot,
stage: DiagnosticStage,
isFromFallbackBuildSettings: Bool
stage: DiagnosticStage
) -> [Diagnostic] {
let supportsCodeDescription = capabilityRegistry.clientHasDiagnosticsCodeDescriptionSupport
@@ -329,7 +307,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
old: currentDiagnostics[snapshot.document.uri] ?? [],
new: newDiags,
stage: stage,
isFallback: isFromFallbackBuildSettings
isFallback: self.commandsByFile[snapshot.document.uri]?.isFallback ?? true
)
currentDiagnostics[snapshot.document.uri] = result
@@ -356,8 +334,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
let diagnostics = registerDiagnostics(
sourcekitdDiagnostics: response[keys.diagnostics],
snapshot: snapshot,
stage: stage,
isFromFallbackBuildSettings: compileCommand?.isFallback ?? true
stage: stage
)
client.send(
@@ -369,11 +346,11 @@ public actor SwiftLanguageServer: ToolchainLanguageServer {
)
}
func handleDocumentUpdate(uri: DocumentURI) async {
func handleDocumentUpdate(uri: DocumentURI) {
guard let snapshot = documentManager.latestSnapshot(uri) else {
return
}
let compileCommand = await self.buildSettings(for: uri)
let compileCommand = self.commandsByFile[uri]
// Make the magic 0,0 replacetext request to update diagnostics and semantic tokens.
@@ -488,7 +465,16 @@ extension SwiftLanguageServer {
self.updateSyntacticTokens(for: snapshot)
}
public func documentUpdatedBuildSettings(_ uri: DocumentURI, change: FileBuildSettingsChange) async {
public func documentUpdatedBuildSettings(_ uri: DocumentURI, change: FileBuildSettingsChange) {
let compileCommand = SwiftCompileCommand(change: change)
// Confirm that the compile commands actually changed, otherwise we don't need to do anything.
// This includes when the compiler arguments are the same but the command is no longer
// considered to be fallback.
guard self.commandsByFile[uri] != compileCommand else {
return
}
self.commandsByFile[uri] = compileCommand
// We may not have a snapshot if this is called just before `openDocument`.
guard let snapshot = self.documentManager.latestSnapshot(uri) else {
return
@@ -496,22 +482,22 @@ extension SwiftLanguageServer {
// Close and re-open the document internally to inform sourcekitd to update the compile
// command. At the moment there's no better way to do this.
self.reopenDocument(snapshot, await self.buildSettings(for: uri))
self.reopenDocument(snapshot, compileCommand)
}
public func documentDependenciesUpdated(_ uri: DocumentURI) async {
public func documentDependenciesUpdated(_ uri: DocumentURI) {
guard let snapshot = self.documentManager.latestSnapshot(uri) else {
return
}
// Forcefully reopen the document since the `BuildSystem` has informed us
// that the dependencies have changed and the AST needs to be reloaded.
await self.reopenDocument(snapshot, self.buildSettings(for: uri))
self.reopenDocument(snapshot, self.commandsByFile[uri])
}
// MARK: - Text synchronization
public func openDocument(_ note: DidOpenTextDocumentNotification) async {
public func openDocument(_ note: DidOpenTextDocumentNotification) {
let keys = self.keys
guard let snapshot = self.documentManager.open(note) else {
@@ -525,7 +511,7 @@ extension SwiftLanguageServer {
req[keys.name] = note.textDocument.uri.pseudoPath
req[keys.sourcetext] = snapshot.text
let compileCommand = await self.buildSettings(for: uri)
let compileCommand = self.commandsByFile[uri]
if let compilerArgs = compileCommand?.compilerArgs {
req[keys.compilerargs] = compilerArgs
@@ -551,12 +537,13 @@ extension SwiftLanguageServer {
req[keys.name] = uri.pseudoPath
// Clear settings that should not be cached for closed documents.
self.commandsByFile[uri] = nil
self.currentDiagnostics[uri] = nil
_ = try? self.sourcekitd.sendSync(req)
}
public func changeDocument(_ note: DidChangeTextDocumentNotification) async {
public func changeDocument(_ note: DidChangeTextDocumentNotification) {
let keys = self.keys
var edits: [IncrementalEdit] = []
@@ -602,7 +589,7 @@ extension SwiftLanguageServer {
}
if let dict = lastResponse, let snapshot = snapshot {
let compileCommand = await self.buildSettings(for: note.textDocument.uri)
let compileCommand = self.commandsByFile[note.textDocument.uri]
self.publishDiagnostics(response: dict, for: snapshot, compileCommand: compileCommand)
}
}
@@ -628,10 +615,10 @@ extension SwiftLanguageServer {
return false
}
public func hover(_ req: Request<HoverRequest>) async {
public func hover(_ req: Request<HoverRequest>) {
let uri = req.params.textDocument.uri
let position = req.params.position
await cursorInfo(uri, position..<position) { result in
cursorInfo(uri, position..<position) { result in
guard let cursorInfo: CursorInfo = result.success ?? nil else {
if let error = result.failure, error != .responseError(.serverCancelled) {
log("cursor info failed \(uri):\(position): \(error)", level: .warning)
@@ -668,10 +655,10 @@ extension SwiftLanguageServer {
}
}
public func symbolInfo(_ req: Request<SymbolInfoRequest>) async {
public func symbolInfo(_ req: Request<SymbolInfoRequest>) {
let uri = req.params.textDocument.uri
let position = req.params.position
await cursorInfo(uri, position..<position) { result in
cursorInfo(uri, position..<position) { result in
guard let cursorInfo: CursorInfo = result.success ?? nil else {
if let error = result.failure {
log("cursor info failed \(uri):\(position): \(error)", level: .warning)
@@ -928,7 +915,7 @@ extension SwiftLanguageServer {
req.reply([presentation])
}
public func documentSymbolHighlight(_ req: Request<DocumentHighlightRequest>) async {
public func documentSymbolHighlight(_ req: Request<DocumentHighlightRequest>) {
let keys = self.keys
guard let snapshot = self.documentManager.latestSnapshot(req.params.textDocument.uri) else {
@@ -949,7 +936,7 @@ extension SwiftLanguageServer {
skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath
// FIXME: SourceKit should probably cache this for us.
if let compileCommand = await self.buildSettings(for: snapshot.document.uri) {
if let compileCommand = self.commandsByFile[snapshot.document.uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}
@@ -1241,14 +1228,12 @@ extension SwiftLanguageServer {
// FIXME: (async) Migrate `CodeActionProvider` to be async so that we
// don't need to do the `withCheckedContinuation` dance here.
await withCheckedContinuation { continuation in
Task {
await provider(req.params) {
switch $0 {
case .success(let actions):
continuation.resume(returning: actions)
case .failure:
continuation.resume(returning: [])
}
provider(req.params) {
switch $0 {
case .success(let actions):
continuation.resume(returning: actions)
case .failure:
continuation.resume(returning: [])
}
}
}
@@ -1263,12 +1248,12 @@ extension SwiftLanguageServer {
completion(.success(codeActions))
}
func retrieveRefactorCodeActions(_ params: CodeActionRequest, completion: @escaping CodeActionProviderCompletion) async {
func retrieveRefactorCodeActions(_ params: CodeActionRequest, completion: @escaping CodeActionProviderCompletion) {
let additionalCursorInfoParameters: ((SKDRequestDictionary) -> Void) = { skreq in
skreq[self.keys.retrieve_refactor_actions] = 1
}
await cursorInfo(
cursorInfo(
params.textDocument.uri,
params.range,
additionalParameters: additionalCursorInfoParameters)
@@ -1356,9 +1341,9 @@ extension SwiftLanguageServer {
completion(.success(codeActions))
}
public func inlayHint(_ req: Request<InlayHintRequest>) async {
public func inlayHint(_ req: Request<InlayHintRequest>) {
let uri = req.params.textDocument.uri
await variableTypeInfos(uri, req.params.range) { infosResult in
variableTypeInfos(uri, req.params.range) { infosResult in
do {
let infos = try infosResult.get()
let hints = infos
@@ -1393,7 +1378,7 @@ extension SwiftLanguageServer {
public func documentDiagnostic(
_ uri: DocumentURI,
_ completion: @escaping (Result<[Diagnostic], ResponseError>) -> Void
) async {
) {
guard let snapshot = documentManager.latestSnapshot(uri) else {
let msg = "failed to find snapshot for url \(uri)"
log(msg)
@@ -1407,12 +1392,8 @@ extension SwiftLanguageServer {
skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath
// FIXME: SourceKit should probably cache this for us.
let areFallbackBuildSettings: Bool
if let buildSettings = await self.buildSettings(for: uri) {
skreq[keys.compilerargs] = buildSettings.compilerArgs
areFallbackBuildSettings = buildSettings.isFallback
} else {
areFallbackBuildSettings = true
if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}
let handle = self.sourcekitd.send(skreq, self.queue) { response in
@@ -1423,8 +1404,7 @@ extension SwiftLanguageServer {
let diagnostics = self.registerDiagnostics(
sourcekitdDiagnostics: dict[keys.diagnostics],
snapshot: snapshot,
stage: .sema,
isFromFallbackBuildSettings: areFallbackBuildSettings
stage: .sema
)
completion(.success(diagnostics))
@@ -1434,9 +1414,9 @@ extension SwiftLanguageServer {
_ = handle
}
public func documentDiagnostic(_ req: Request<DocumentDiagnosticsRequest>) async {
public func documentDiagnostic(_ req: Request<DocumentDiagnosticsRequest>) {
let uri = req.params.textDocument.uri
await documentDiagnostic(req.params.textDocument.uri) { result in
documentDiagnostic(req.params.textDocument.uri) { result in
switch result {
case .success(let diagnostics):
req.reply(.full(.init(items: diagnostics)))
@@ -1449,7 +1429,7 @@ extension SwiftLanguageServer {
}
}
public func executeCommand(_ req: Request<ExecuteCommandRequest>) async {
public func executeCommand(_ req: Request<ExecuteCommandRequest>) {
let params = req.params
//TODO: If there's support for several types of commands, we might need to structure this similarly to the code actions request.
guard let swiftCommand = params.swiftCommand(ofType: SemanticRefactorCommand.self) else {
@@ -1458,7 +1438,7 @@ extension SwiftLanguageServer {
return req.reply(.failure(.unknown(message)))
}
let uri = swiftCommand.textDocument.uri
await semanticRefactoring(swiftCommand) { result in
semanticRefactoring(swiftCommand) { result in
switch result {
case .success(let refactor):
let edit = refactor.edit
@@ -1511,7 +1491,7 @@ extension SwiftLanguageServer: SKDNotificationHandler {
}
}
public func notificationImpl(_ notification: SKDResponse) async {
public func notificationImpl(_ notification: SKDResponse) {
// Check if we need to update our `state` based on the contents of the notification.
if notification.value?[self.keys.notification] == self.values.notification_sema_enabled {
self.state = .connected
@@ -1569,7 +1549,7 @@ extension SwiftLanguageServer: SKDNotificationHandler {
} else {
uri = DocumentURI(string: name)
}
await self.handleDocumentUpdate(uri: uri)
self.handleDocumentUpdate(uri: uri)
}
}
}

View File

@@ -93,7 +93,7 @@ extension SwiftLanguageServer {
_ uri: DocumentURI,
_ range: Range<Position>? = nil,
_ completion: @escaping (Swift.Result<[VariableTypeInfo], VariableTypeInfoError>) -> Void
) async {
) {
guard let snapshot = documentManager.latestSnapshot(uri) else {
return completion(.failure(.unknownDocument(uri)))
}
@@ -112,7 +112,7 @@ extension SwiftLanguageServer {
}
// FIXME: SourceKit should probably cache this for us
if let compileCommand = await self.buildSettings(for: uri) {
if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
}

View File

@@ -34,8 +34,7 @@ public protocol ToolchainLanguageServer: AnyObject {
toolchain: Toolchain,
options: SourceKitServer.Options,
workspace: Workspace,
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void,
workspaceForDocument: @escaping (DocumentURI) async -> Workspace?
reopenDocuments: @escaping (ToolchainLanguageServer) -> Void
) throws
/// Returns `true` if this instance of the language server can handle opening documents in `workspace`.

View File

@@ -40,6 +40,24 @@ final class BuildServerBuildSystemTests: XCTestCase {
)
}
func testSettings() throws {
#if os(Windows)
try XCTSkipIf(true, "hang")
#endif
let buildSystem = try BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder)
// test settings with a response
let fileURL = URL(fileURLWithPath: "/path/to/some/file.swift")
let settings = buildSystem._settings(for: DocumentURI(fileURL))
XCTAssertNotNil(settings)
XCTAssertEqual(settings?.compilerArguments, ["-a", "-b"])
XCTAssertEqual(settings?.workingDirectory, fileURL.deletingLastPathComponent().path)
// test error
let missingFileURL = URL(fileURLWithPath: "/path/to/some/missingfile.missing")
XCTAssertNil(buildSystem._settings(for: DocumentURI(missingFileURL)))
}
func testFileRegistration() throws {
let buildSystem = try BuildServerBuildSystem(projectRoot: root, buildFolder: buildFolder)

View File

@@ -149,7 +149,7 @@ final class BuildSystemManagerTests: XCTestCase {
mainFilesProvider: mainFiles)
defer { withExtendedLifetime(bsm) {} } // Keep BSM alive for callbacks.
let del = BSMDelegate(bsm)
let fallbackSettings = fallback.buildSettings(for: a, language: .swift)
let fallbackSettings = fallback.settings(for: a, .swift)
let initial = expectation(description: "initial fallback settings")
del.expected = [(a, fallbackSettings, initial, #file, #line)]
bsm.registerForChangeNotifications(for: a, language: .swift)
@@ -475,12 +475,12 @@ class ManualBuildSystem: BuildSystem {
var delegate: BuildSystemDelegate? = nil
func buildSettings(for uri: DocumentURI, language: Language) -> FileBuildSettings? {
func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? {
return map[uri]
}
func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
let settings = self.buildSettings(for: uri, language: language)
let settings = self.settings(for: uri, language)
self.delegate?.fileBuildSettingsChanged([uri: FileBuildSettingsChange(settings)])
}

View File

@@ -29,7 +29,7 @@ final class FallbackBuildSystemTests: XCTestCase {
XCTAssertNil(bs.indexStorePath)
XCTAssertNil(bs.indexDatabasePath)
let settings = bs.buildSettings(for: source.asURI, language: .swift)!
let settings = bs.settings(for: source.asURI, .swift)!
XCTAssertNil(settings.workingDirectory)
let args = settings.compilerArguments
@@ -41,7 +41,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .swift)?.compilerArguments, [
source.pathString,
])
}
@@ -57,7 +57,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let bs = FallbackBuildSystem(buildSetup: buildSetup)
bs.sdkpath = sdk
let args = bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments
let args = bs.settings(for: source.asURI, .swift)?.compilerArguments
XCTAssertEqual(args, [
"-Xfrontend",
"-debug-constraints",
@@ -68,7 +68,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .swift)?.compilerArguments, [
"-Xfrontend",
"-debug-constraints",
source.pathString,
@@ -88,7 +88,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let bs = FallbackBuildSystem(buildSetup: buildSetup)
bs.sdkpath = sdk
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .swift)!.compilerArguments, [
"-sdk",
"/some/custom/sdk",
"-Xfrontend",
@@ -98,7 +98,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .swift)!.compilerArguments, [
"-sdk",
"/some/custom/sdk",
"-Xfrontend",
@@ -114,7 +114,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let bs = FallbackBuildSystem(buildSetup: .default)
bs.sdkpath = sdk
let settings = bs.buildSettings(for: source.asURI, language: .cpp)!
let settings = bs.settings(for: source.asURI, .cpp)!
XCTAssertNil(settings.workingDirectory)
let args = settings.compilerArguments
@@ -126,7 +126,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [
source.pathString,
])
}
@@ -141,7 +141,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let bs = FallbackBuildSystem(buildSetup: buildSetup)
bs.sdkpath = sdk
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [
"-v",
"-isysroot",
sdk.pathString,
@@ -150,7 +150,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [
"-v",
source.pathString,
])
@@ -168,7 +168,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let bs = FallbackBuildSystem(buildSetup: buildSetup)
bs.sdkpath = sdk
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [
"-isysroot",
"/my/custom/sdk",
"-v",
@@ -177,7 +177,7 @@ final class FallbackBuildSystemTests: XCTestCase {
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [
"-isysroot",
"/my/custom/sdk",
"-v",
@@ -189,7 +189,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let source = try AbsolutePath(validating: "/my/source.c")
let bs = FallbackBuildSystem(buildSetup: .default)
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .c)?.compilerArguments, [
source.pathString,
])
}
@@ -202,7 +202,7 @@ final class FallbackBuildSystemTests: XCTestCase {
]))
let bs = FallbackBuildSystem(buildSetup: buildSetup)
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .c)?.compilerArguments, [
"-v",
source.pathString,
])
@@ -212,7 +212,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let source = try AbsolutePath(validating: "/my/source.m")
let bs = FallbackBuildSystem(buildSetup: .default)
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .objective_c)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .objective_c)?.compilerArguments, [
source.pathString,
])
}
@@ -221,7 +221,7 @@ final class FallbackBuildSystemTests: XCTestCase {
let source = try AbsolutePath(validating: "/my/source.mm")
let bs = FallbackBuildSystem(buildSetup: .default)
bs.sdkpath = nil
XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .objective_cpp)?.compilerArguments, [
XCTAssertEqual(bs.settings(for: source.asURI, .objective_cpp)?.compilerArguments, [
source.pathString,
])
}
@@ -229,6 +229,6 @@ final class FallbackBuildSystemTests: XCTestCase {
func testUnknown() throws {
let source = try AbsolutePath(validating: "/my/source.mm")
let bs = FallbackBuildSystem(buildSetup: .default)
XCTAssertNil(bs.buildSettings(for: source.asURI, language: Language(rawValue: "unknown")))
XCTAssertNil(bs.settings(for: source.asURI, Language(rawValue: "unknown")))
}
}

View File

@@ -43,10 +43,6 @@ final class TestBuildSystem: BuildSystem {
/// Files currently being watched by our delegate.
var watchedFiles: Set<DocumentURI> = []
func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
return buildSettingsByFile[document]
}
func registerForChangeNotifications(for uri: DocumentURI, language: Language) {
watchedFiles.insert(uri)
@@ -200,7 +196,7 @@ final class BuildSystemTests: XCTestCase {
func testSwiftDocumentUpdatedBuildSettings() async throws {
let url = URL(fileURLWithPath: "/\(UUID())/a.swift")
let doc = DocumentURI(url)
let args = FallbackBuildSystem(buildSetup: .default).buildSettings(for: doc, language: .swift)!.compilerArguments
let args = FallbackBuildSystem(buildSetup: .default).settings(for: doc, .swift)!.compilerArguments
buildSystem.buildSettingsByFile[doc] = FileBuildSettings(compilerArguments: args)
@@ -310,7 +306,7 @@ final class BuildSystemTests: XCTestCase {
let doc = DocumentURI(url)
// Primary settings must be different than the fallback settings.
var primarySettings = FallbackBuildSystem(buildSetup: .default).buildSettings(for: doc, language: .swift)!
var primarySettings = FallbackBuildSystem(buildSetup: .default).settings(for: doc, .swift)!
primarySettings.compilerArguments.append("-DPRIMARY")
let text = """