From 5c839f8640275c661fcbfffa41c3ee65d96bde9d Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 18 Nov 2019 16:18:05 -0800 Subject: [PATCH] Add support for non-URL URIs According to the LSP specification, arbitrary URIs can be used as document identifiers. Instead of internally assuming that all URIs are URLs, use a DocumentURI enum to represent URIs. These can either be file URLs or other URIs whose value as treated as an opaque string. --- Package.swift | 1 + .../BuildServerProtocol/BuildTargets.swift | 3 + .../RegisterForChangeNotifications.swift | 10 +- .../LanguageServerProtocol/DocumentURI.swift | 72 +++++++++ .../InitializeRequest.swift | 14 +- Sources/LanguageServerProtocol/Location.swift | 16 +- .../PublishDiagnostics.swift | 17 +- .../TextDocumentIdentifier.swift | 31 +--- .../TextDocumentItem.swift | 8 +- .../VersionedTextDocumentIdentifier.swift | 18 +-- .../WorkspaceEdit.swift | 25 ++- .../WorkspaceFolder.swift | 27 ++-- Sources/SKCore/BuildServerBuildSystem.swift | 20 ++- Sources/SKCore/BuildSystem.swift | 11 +- Sources/SKCore/BuildSystemDelegate.swift | 2 +- Sources/SKCore/BuildSystemList.swift | 16 +- .../CompilationDatabaseBuildSystem.swift | 12 +- Sources/SKCore/FallbackBuildSystem.swift | 12 +- .../SKSwiftPMWorkspace/SwiftPMWorkspace.swift | 15 +- Sources/SKTestSupport/Extensions.swift | 26 ++++ .../SKSwiftPMTestWorkspace.swift | 4 +- .../SKTestSupport/SKTibsTestWorkspace.swift | 10 +- Sources/SourceKit/DocumentManager.swift | 44 +++--- Sources/SourceKit/SourceKitServer.swift | 69 ++++----- .../SourceKit/ToolchainLanguageServer.swift | 2 +- Sources/SourceKit/Workspace.swift | 34 ++-- .../clangd/ClangLanguageServer.swift | 13 +- Sources/SourceKit/sourcekitd/CursorInfo.swift | 20 +-- Sources/SourceKit/sourcekitd/Diagnostic.swift | 2 +- .../sourcekitd/SemanticRefactoring.swift | 18 +-- .../sourcekitd/SwiftLanguageServer.swift | 102 ++++++------ .../CodingTests.swift | 4 +- .../CodingTests.swift | 19 +-- .../BuildServerBuildSystemTests.swift | 20 +-- .../CompilationDatabaseTests.swift | 2 +- .../FallbackBuildSystemTests.swift | 16 +- .../SwiftPMWorkspaceTests.swift | 36 ++--- Tests/SourceKitTests/BuildSystemTests.swift | 58 +++---- Tests/SourceKitTests/DocumentColorTests.swift | 6 +- .../SourceKitTests/DocumentSymbolTests.swift | 4 +- .../SourceKitTests/ExecuteCommandTests.swift | 22 +-- Tests/SourceKitTests/FoldingRangeTests.swift | 12 +- Tests/SourceKitTests/LocalClangTests.swift | 6 +- Tests/SourceKitTests/LocalSwiftTests.swift | 146 ++++++++++++++---- Tests/SourceKitTests/SourceKitTests.swift | 6 +- .../SourceKitTests/SwiftCompletionTests.swift | 4 +- 46 files changed, 609 insertions(+), 426 deletions(-) create mode 100644 Sources/LanguageServerProtocol/DocumentURI.swift create mode 100644 Sources/SKTestSupport/Extensions.swift diff --git a/Package.swift b/Package.swift index 1c6d6eb3..ca71a295 100644 --- a/Package.swift +++ b/Package.swift @@ -40,6 +40,7 @@ let package = Package( "LSPTestSupport", "SourceKit", "tibs", // Never imported, needed at runtime + "TSCUtility", ] ), .testTarget( diff --git a/Sources/BuildServerProtocol/BuildTargets.swift b/Sources/BuildServerProtocol/BuildTargets.swift index e6e3455c..c77f9f89 100644 --- a/Sources/BuildServerProtocol/BuildTargets.swift +++ b/Sources/BuildServerProtocol/BuildTargets.swift @@ -10,6 +10,9 @@ // //===----------------------------------------------------------------------===// import LanguageServerProtocol +import struct Foundation.URL + +public typealias URL = Foundation.URL /// The workspace build targets request is sent from the client to the server to /// ask for the list of all available build targets in the workspace. diff --git a/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift b/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift index 9b536c43..3dc967b8 100644 --- a/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift +++ b/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift @@ -20,13 +20,13 @@ public struct RegisterForChanges: RequestType { public static let method: String = "textDocument/registerForChanges" public typealias Response = VoidResponse - /// The URL of the document to get options for. - public var uri: URL + /// The URI of the document to get options for. + public var uri: DocumentURI /// Whether to register or unregister for the file. public var action: RegisterAction - public init(uri: URL, action: RegisterAction) { + public init(uri: DocumentURI, action: RegisterAction) { self.uri = uri self.action = action } @@ -43,8 +43,8 @@ public enum RegisterAction: String, Hashable, Codable { public struct FileOptionsChangedNotification: NotificationType { public static let method: String = "build/sourceKitOptionsChanged" - /// The URL of the document that has changed settings. - public var uri: URL + /// The URI of the document that has changed settings. + public var uri: DocumentURI /// The updated options for the registered file. public var updatedOptions: SourceKitOptionsResult diff --git a/Sources/LanguageServerProtocol/DocumentURI.swift b/Sources/LanguageServerProtocol/DocumentURI.swift new file mode 100644 index 00000000..b52e3f4c --- /dev/null +++ b/Sources/LanguageServerProtocol/DocumentURI.swift @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Foundation + +public enum DocumentURI: Codable, Hashable { + case url(URL) + case other(String) + + /// Returns a filepath if the URI is a URL. If the URI is not a URL, returns + /// the full URI as a fallback. + /// This value is intended to be used when interacting with sourcekitd which + /// expects a file path but is able to handle arbitrary strings as well in a + /// fallback mode that drops semantic functionality. + public var pseudoPath: String { + switch self { + case .url(let url): + return url.path + case .other(let string): + return string + } + } + + /// Returns the URI as a string. + public var stringValue: String { + switch self { + case .url(let url): + return url.absoluteString + case .other(let string): + return string + } + } + + /// Construct a DocumentURI from the given URI string, automatically parsing + /// it either as a URL or an opaque URI. + public init(string: String) { + if string.starts(with: "file:"), let url = URL(string: string) { + // URL with a 'file:' protocol. Parse using URL(string:) + self = .url(url) + } else if string.starts(with: "/") { + // Absolute path. Technically this not part of the LSP specification + // but we want to support it anyway. + // Parse it using URL(fileURLWithPath:) + self = .url(URL(fileURLWithPath: string)) + } else { + // Can't parse URI as URL. Use it as an opaque value. + self = .other(string) + } + } + + public init(from decoder: Decoder) throws { + self.init(string: try decoder.singleValueContainer().decode(String.self)) + } + + public func encode(to encoder: Encoder) throws { + switch self { + case .url(let url): + try url.absoluteString.encode(to: encoder) + case .other(let string): + try string.encode(to: encoder) + } + } +} diff --git a/Sources/LanguageServerProtocol/InitializeRequest.swift b/Sources/LanguageServerProtocol/InitializeRequest.swift index 0a500bc2..a9a8bd59 100644 --- a/Sources/LanguageServerProtocol/InitializeRequest.swift +++ b/Sources/LanguageServerProtocol/InitializeRequest.swift @@ -19,7 +19,7 @@ /// /// - Parameters: /// - processId: The process identifier (pid) of the client process. -/// - rootURL: The workspace URL, or nil if no workspace is open. +/// - rootURI: The workspace URI, or nil if no workspace is open. /// - initializationOptions: User-provided options. /// - capabilities: The capabilities provided by the client editor. /// - trace: Whether to enable tracing. @@ -39,13 +39,13 @@ public struct InitializeRequest: RequestType, Hashable { /// The workspace path, or nil if no workspace is open. /// - /// - Note: deprecated in favour of `rootURL`. + /// - Note: deprecated in favour of `rootURI`. public var rootPath: String? = nil - /// The workspace URL, or nil if no workspace is open. + /// The workspace URI, or nil if no workspace is open. /// /// Takes precedence over the deprecated `rootPath`. - public var rootURL: URL? + public var rootURI: DocumentURI? /// User-provided options. public var initializationOptions: LSPAny? = nil @@ -68,7 +68,7 @@ public struct InitializeRequest: RequestType, Hashable { public init( processId: Int? = nil, rootPath: String? = nil, - rootURL: URL?, + rootURI: DocumentURI?, initializationOptions: LSPAny? = nil, capabilities: ClientCapabilities, trace: Tracing = .off, @@ -76,7 +76,7 @@ public struct InitializeRequest: RequestType, Hashable { { self.processId = processId self.rootPath = rootPath - self.rootURL = rootURL + self.rootURI = rootURI self.initializationOptions = initializationOptions self.capabilities = capabilities self.trace = trace @@ -88,7 +88,7 @@ extension InitializeRequest: Codable { private enum CodingKeys: String, CodingKey { case processId case rootPath - case rootURL = "rootUri" + case rootURI = "rootUri" case initializationOptions case capabilities case trace diff --git a/Sources/LanguageServerProtocol/Location.swift b/Sources/LanguageServerProtocol/Location.swift index 4030d919..9fdc6c7e 100644 --- a/Sources/LanguageServerProtocol/Location.swift +++ b/Sources/LanguageServerProtocol/Location.swift @@ -13,23 +13,15 @@ /// Range within a particular document. /// /// For a location where the document is implied, use `Position` or `Range`. -public struct Location: ResponseType, Hashable { +public struct Location: ResponseType, Hashable, Codable { - public var url: URL + public var uri: DocumentURI @CustomCodable public var range: Range - public init(url: URL, range: Range) { - self.url = url + public init(uri: DocumentURI, range: Range) { + self.uri = uri self._range = CustomCodable(wrappedValue: range) } } - -// Encode using the key "uri" to match LSP. -extension Location: Codable { - private enum CodingKeys: String, CodingKey { - case url = "uri" - case range - } -} diff --git a/Sources/LanguageServerProtocol/PublishDiagnostics.swift b/Sources/LanguageServerProtocol/PublishDiagnostics.swift index bc991087..7376c426 100644 --- a/Sources/LanguageServerProtocol/PublishDiagnostics.swift +++ b/Sources/LanguageServerProtocol/PublishDiagnostics.swift @@ -22,26 +22,19 @@ /// to coalesce the notification across multiple changes in a short period of time. /// /// - Parameters: -/// - url: The document in which the diagnostics should be shown. +/// - uri: The document in which the diagnostics should be shown. /// - diagnostics: The complete list of diagnostics in the document, if any. -public struct PublishDiagnostics: NotificationType, Hashable { +public struct PublishDiagnostics: NotificationType, Hashable, Codable { public static let method: String = "textDocument/publishDiagnostics" /// The document in which the diagnostics should be shown. - public var url: URL + public var uri: DocumentURI /// The complete list of diagnostics in the document, if any. public var diagnostics: [Diagnostic] - public init(url: URL, diagnostics: [Diagnostic]) { - self.url = url + public init(uri: DocumentURI, diagnostics: [Diagnostic]) { + self.uri = uri self.diagnostics = diagnostics } } - -extension PublishDiagnostics: Codable { - private enum CodingKeys: String, CodingKey { - case url = "uri" - case diagnostics - } -} diff --git a/Sources/LanguageServerProtocol/TextDocumentIdentifier.swift b/Sources/LanguageServerProtocol/TextDocumentIdentifier.swift index 6a13d885..0057092d 100644 --- a/Sources/LanguageServerProtocol/TextDocumentIdentifier.swift +++ b/Sources/LanguageServerProtocol/TextDocumentIdentifier.swift @@ -10,43 +10,28 @@ // //===----------------------------------------------------------------------===// -import struct Foundation.URL - -/// Convenience alias so that you don't need to add Foundation import to use. -public typealias URL = Foundation.URL - /// Unique identifier for a document. -public struct TextDocumentIdentifier: Hashable { +public struct TextDocumentIdentifier: Hashable, Codable { - /// A URL that uniquely identifies the document. - public var url: URL + /// A URI that uniquely identifies the document. + public var uri: DocumentURI - public init(_ url: URL) { - self.url = url - } -} - -// Encode using the key "uri" to match LSP. -extension TextDocumentIdentifier: Codable { - private enum CodingKeys: String, CodingKey { - case url = "uri" + public init(_ uri: DocumentURI) { + self.uri = uri } } extension TextDocumentIdentifier: LSPAnyCodable { public init?(fromLSPDictionary dictionary: [String : LSPAny]) { - guard case .string(let urlString)? = dictionary[CodingKeys.url.stringValue] else { + guard case .string(let uriString)? = dictionary[CodingKeys.uri.stringValue] else { return nil } - guard let url = URL(string: urlString) else { - return nil - } - self.url = url + self.uri = DocumentURI(string: uriString) } public func encodeToLSPAny() -> LSPAny { return .dictionary([ - CodingKeys.url.stringValue: .string(url.absoluteString) + CodingKeys.uri.stringValue: .string(uri.stringValue) ]) } } diff --git a/Sources/LanguageServerProtocol/TextDocumentItem.swift b/Sources/LanguageServerProtocol/TextDocumentItem.swift index b60017fc..29e640a9 100644 --- a/Sources/LanguageServerProtocol/TextDocumentItem.swift +++ b/Sources/LanguageServerProtocol/TextDocumentItem.swift @@ -13,7 +13,7 @@ /// The content and metadata of a text document. public struct TextDocumentItem: Hashable { - public var url: URL + public var uri: DocumentURI public var language: Language @@ -23,8 +23,8 @@ public struct TextDocumentItem: Hashable { /// The content of the document. public var text: String - public init(url: URL, language: Language, version: Int, text: String) { - self.url = url + public init(uri: DocumentURI, language: Language, version: Int, text: String) { + self.uri = uri self.language = language self.version = version self.text = text @@ -33,7 +33,7 @@ public struct TextDocumentItem: Hashable { extension TextDocumentItem: Codable { private enum CodingKeys: String, CodingKey { - case url = "uri" + case uri case language = "languageId" case version case text diff --git a/Sources/LanguageServerProtocol/VersionedTextDocumentIdentifier.swift b/Sources/LanguageServerProtocol/VersionedTextDocumentIdentifier.swift index 2c036089..7e650439 100644 --- a/Sources/LanguageServerProtocol/VersionedTextDocumentIdentifier.swift +++ b/Sources/LanguageServerProtocol/VersionedTextDocumentIdentifier.swift @@ -13,24 +13,16 @@ /// A document identifier representing a specific version of the document. /// /// Notionally a subtype of `TextDocumentIdentifier`. -public struct VersionedTextDocumentIdentifier: Hashable { +public struct VersionedTextDocumentIdentifier: Hashable, Codable { - /// A URL that uniquely identifies the document. - public var url: URL + /// A URI that uniquely identifies the document. + public var uri: DocumentURI /// The version number of this document, or nil if unknown. public var version: Int? - public init(_ url: URL, version: Int?) { - self.url = url + public init(_ uri: DocumentURI, version: Int?) { + self.uri = uri self.version = version } } - -// Encode using the key "uri" to match LSP. -extension VersionedTextDocumentIdentifier: Codable { - private enum CodingKeys: String, CodingKey { - case url = "uri" - case version - } -} diff --git a/Sources/LanguageServerProtocol/WorkspaceEdit.swift b/Sources/LanguageServerProtocol/WorkspaceEdit.swift index 6ce2b88b..17e04155 100644 --- a/Sources/LanguageServerProtocol/WorkspaceEdit.swift +++ b/Sources/LanguageServerProtocol/WorkspaceEdit.swift @@ -14,9 +14,9 @@ public struct WorkspaceEdit: Hashable, ResponseType { /// The edits to be applied to existing resources. - public var changes: [URL: [TextEdit]]? + public var changes: [DocumentURI: [TextEdit]]? - public init(changes: [URL: [TextEdit]]?) { + public init(changes: [DocumentURI: [TextEdit]]?) { self.changes = changes } } @@ -30,13 +30,10 @@ extension WorkspaceEdit: Codable { public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let changesDict = try container.decode([String: [TextEdit]].self, forKey: .changes) - var changes = [URL: [TextEdit]]() + var changes = [DocumentURI: [TextEdit]]() for change in changesDict { - guard let url = URL(string: change.key) else { - let error = "Changes key is not an URL." - throw DecodingError.dataCorruptedError(forKey: .changes, in: container, debugDescription: error) - } - changes[url] = change.value + let uri = DocumentURI(string: change.key) + changes[uri] = change.value } self.changes = changes } @@ -47,7 +44,7 @@ extension WorkspaceEdit: Codable { } var stringDictionary = [String: [TextEdit]]() for (key, value) in changes { - stringDictionary[key.absoluteString] = value + stringDictionary[key.stringValue] = value } var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(stringDictionary, forKey: .changes) @@ -59,15 +56,13 @@ extension WorkspaceEdit: LSPAnyCodable { guard case .dictionary(let lspDict) = dictionary[CodingKeys.changes.stringValue] else { return nil } - var dictionary = [URL: [TextEdit]]() + var dictionary = [DocumentURI: [TextEdit]]() for (key, value) in lspDict { - guard let url = URL(string: key) else { - return nil - } + let uri = DocumentURI(string: key) guard let edits = [TextEdit](fromLSPArray: value) else { return nil } - dictionary[url] = edits + dictionary[uri] = edits } self.changes = dictionary } @@ -77,7 +72,7 @@ extension WorkspaceEdit: LSPAnyCodable { return nil } let values = changes.map { - ($0.key.absoluteString, $0.value.encodeToLSPAny()) + ($0.key.stringValue, $0.value.encodeToLSPAny()) } let dictionary = Dictionary(uniqueKeysWithValues: values) return .dictionary([ diff --git a/Sources/LanguageServerProtocol/WorkspaceFolder.swift b/Sources/LanguageServerProtocol/WorkspaceFolder.swift index f49026c2..0852e55d 100644 --- a/Sources/LanguageServerProtocol/WorkspaceFolder.swift +++ b/Sources/LanguageServerProtocol/WorkspaceFolder.swift @@ -11,27 +11,26 @@ //===----------------------------------------------------------------------===// /// Unique identifier for a document. -public struct WorkspaceFolder: ResponseType, Hashable { +public struct WorkspaceFolder: ResponseType, Hashable, Codable { - /// A URL that uniquely identifies the workspace. - public var url: URL + /// A URI that uniquely identifies the workspace. + public var uri: DocumentURI /// The name of the workspace (default: basename of url). public var name: String - public init(url: URL, name: String? = nil) { - self.url = url - self.name = name ?? url.lastPathComponent + public init(uri: DocumentURI, name: String? = nil) { + self.uri = uri + + switch uri { + case .url(let url): + self.name = name ?? url.lastPathComponent + case .other(_): + self.name = name ?? "unknown_workspace" + } + if self.name.isEmpty { self.name = "unknown_workspace" } } } - -// Encode using the key "uri" to match LSP. -extension WorkspaceFolder: Codable { - private enum CodingKeys: String, CodingKey { - case url = "uri" - case name - } -} diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index 2dcf7b3d..307bfeb6 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -149,34 +149,38 @@ extension BuildServerBuildSystem: BuildSystem { /// Register the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func registerForChangeNotifications(for url: LanguageServerProtocol.URL) { - let request = RegisterForChanges(uri: url, action: .register) + public func registerForChangeNotifications(for uri: DocumentURI) { + let request = RegisterForChanges(uri: uri, action: .register) _ = self.buildServer?.send(request, queue: requestQueue, reply: { result in if let error = result.failure { - log("error registering \(url): \(error)", level: .error) + log("error registering \(uri): \(error)", level: .error) } }) } /// Unregister the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func unregisterForChangeNotifications(for url: LanguageServerProtocol.URL) { - let request = RegisterForChanges(uri: url, action: .unregister) + public func unregisterForChangeNotifications(for uri: DocumentURI) { + let request = RegisterForChanges(uri: uri, action: .unregister) _ = self.buildServer?.send(request, queue: requestQueue, reply: { result in if let error = result.failure { - log("error unregistering \(url): \(error)", level: .error) + log("error unregistering \(uri): \(error)", level: .error) } }) } - public func settings(for url: URL, _ language: Language) -> FileBuildSettings? { + public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { + guard case .url(let url) = uri else { + // We can't determine build settings for non-file URIs. + return nil + } if let response = try? self.buildServer?.sendSync(SourceKitOptions(uri: url)) { return FileBuildSettings(compilerArguments: response.options, workingDirectory: response.workingDirectory) } return nil } - public func toolchain(for: URL, _ language: Language) -> Toolchain? { + public func toolchain(for: DocumentURI, _ language: Language) -> Toolchain? { return nil } diff --git a/Sources/SKCore/BuildSystem.swift b/Sources/SKCore/BuildSystem.swift index deb135b7..001aa119 100644 --- a/Sources/SKCore/BuildSystem.swift +++ b/Sources/SKCore/BuildSystem.swift @@ -13,6 +13,9 @@ import BuildServerProtocol import LanguageServerProtocol import TSCBasic +import struct Foundation.URL + +public typealias URL = Foundation.URL /// Provider of FileBuildSettings and other build-related information. /// @@ -32,10 +35,10 @@ public protocol BuildSystem: AnyObject { var indexDatabasePath: AbsolutePath? { get } /// Returns the settings for the given url and language mode, if known. - func settings(for: URL, _ language: Language) -> FileBuildSettings? + func settings(for: DocumentURI, _ language: Language) -> FileBuildSettings? /// Returns the toolchain to use to compile this file - func toolchain(for: URL, _ language: Language) -> Toolchain? + func toolchain(for: DocumentURI, _ language: Language) -> Toolchain? /// Delegate to handle any build system events such as file build settings /// changing. @@ -43,11 +46,11 @@ public protocol BuildSystem: AnyObject { /// Register the given file for build-system level change notifications, such /// as command line flag changes, dependency changes, etc. - func registerForChangeNotifications(for: URL) + func registerForChangeNotifications(for: DocumentURI) /// Unregister the given file for build-system level change notifications, /// such as command line flag changes, dependency changes, etc. - func unregisterForChangeNotifications(for: URL) + func unregisterForChangeNotifications(for: DocumentURI) /// Returns the build targets in the workspace. If the build system does not /// support build targets the `buildTargetsNotSupported` error should be diff --git a/Sources/SKCore/BuildSystemDelegate.swift b/Sources/SKCore/BuildSystemDelegate.swift index 1eb2192f..1ff89e20 100644 --- a/Sources/SKCore/BuildSystemDelegate.swift +++ b/Sources/SKCore/BuildSystemDelegate.swift @@ -25,5 +25,5 @@ public protocol BuildSystemDelegate: AnyObject { /// /// The callee should request new build settings for any of the given files /// that they are interested in. - func fileBuildSettingsChanged(_ changedFiles: Set) + func fileBuildSettingsChanged(_ changedFiles: Set) } diff --git a/Sources/SKCore/BuildSystemList.swift b/Sources/SKCore/BuildSystemList.swift index b1852d83..153f35e9 100644 --- a/Sources/SKCore/BuildSystemList.swift +++ b/Sources/SKCore/BuildSystemList.swift @@ -36,9 +36,9 @@ extension BuildSystemList: BuildSystem { public var indexDatabasePath: AbsolutePath? { return providers.first?.indexDatabasePath } - public func settings(for url: URL, _ language: Language) -> FileBuildSettings? { + public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { for provider in providers { - if let settings = provider.settings(for: url, language) { + if let settings = provider.settings(for: uri, language) { return settings } } @@ -47,20 +47,20 @@ extension BuildSystemList: BuildSystem { /// Register the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func registerForChangeNotifications(for url: URL) { + public func registerForChangeNotifications(for uri: DocumentURI) { // Only register with the primary build system, since we only use its delegate. - providers.first?.registerForChangeNotifications(for: url) + providers.first?.registerForChangeNotifications(for: uri) } /// Unregister the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func unregisterForChangeNotifications(for url: URL) { + public func unregisterForChangeNotifications(for uri: DocumentURI) { // Only unregister with the primary build system, since we only use its delegate. - providers.first?.unregisterForChangeNotifications(for: url) + providers.first?.unregisterForChangeNotifications(for: uri) } - public func toolchain(for url: URL, _ language: Language) -> Toolchain? { - return providers.first?.toolchain(for: url, language) + public func toolchain(for uri: DocumentURI, _ language: Language) -> Toolchain? { + return providers.first?.toolchain(for: uri, language) } public func buildTargets(reply: @escaping (LSPResult<[BuildTarget]>) -> Void) { diff --git a/Sources/SKCore/CompilationDatabaseBuildSystem.swift b/Sources/SKCore/CompilationDatabaseBuildSystem.swift index c2631221..eae75e49 100644 --- a/Sources/SKCore/CompilationDatabaseBuildSystem.swift +++ b/Sources/SKCore/CompilationDatabaseBuildSystem.swift @@ -44,7 +44,11 @@ extension CompilationDatabaseBuildSystem: BuildSystem { public var indexStorePath: AbsolutePath? { return nil } public var indexDatabasePath: AbsolutePath? { return nil } - public func settings(for url: URL, _ language: Language) -> FileBuildSettings? { + public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { + guard case .url(let url) = uri else { + // We can't determine build settings for non-file URIs. + return nil + } guard let db = database(for: url), let cmd = db[url].first else { return nil } return FileBuildSettings( @@ -53,13 +57,13 @@ extension CompilationDatabaseBuildSystem: BuildSystem { ) } - public func toolchain(for: URL, _ language: Language) -> Toolchain? { return nil } + public func toolchain(for: DocumentURI, _ language: Language) -> Toolchain? { return nil } /// We don't support change watching. - public func registerForChangeNotifications(for: URL) {} + public func registerForChangeNotifications(for: DocumentURI) {} /// We don't support change watching. - public func unregisterForChangeNotifications(for: URL) {} + public func unregisterForChangeNotifications(for: DocumentURI) {} public func buildTargets(reply: @escaping (LSPResult<[BuildTarget]>) -> Void) { reply(.failure(buildTargetsNotSupported)) diff --git a/Sources/SKCore/FallbackBuildSystem.swift b/Sources/SKCore/FallbackBuildSystem.swift index b6a50d77..4c555d8e 100644 --- a/Sources/SKCore/FallbackBuildSystem.swift +++ b/Sources/SKCore/FallbackBuildSystem.swift @@ -39,7 +39,11 @@ public final class FallbackBuildSystem: BuildSystem { public var indexDatabasePath: AbsolutePath? { return nil } - public func settings(for url: URL, _ language: Language) -> FileBuildSettings? { + public func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { + guard case .url(let url) = uri else { + // We can't determine build settings for non-files URIs. + return nil + } guard let path = try? AbsolutePath(validating: url.path) else { return nil } @@ -55,12 +59,12 @@ public final class FallbackBuildSystem: BuildSystem { } /// We don't support change watching. - public func registerForChangeNotifications(for: URL) {} + public func registerForChangeNotifications(for: DocumentURI) {} /// We don't support change watching. - public func unregisterForChangeNotifications(for: URL) {} + public func unregisterForChangeNotifications(for: DocumentURI) {} - public func toolchain(for: URL, _ language: Language) -> Toolchain? { return nil } + public func toolchain(for: DocumentURI, _ language: Language) -> Toolchain? { return nil } public func buildTargets(reply: @escaping (LSPResult<[BuildTarget]>) -> Void) { reply(.failure(buildTargetsNotSupported)) diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index 45d0c494..54d560c6 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -20,6 +20,7 @@ import PackageModel import SKCore import SKSupport import Workspace +import struct Foundation.URL /// Swift Package Manager build system and workspace support. /// @@ -114,7 +115,7 @@ public final class SwiftPMWorkspace { /// Creates a build system using the Swift Package Manager, if this workspace is a package. /// /// - Returns: nil if `workspacePath` is not part of a package or there is an error. - public convenience init?(url: LanguageServerProtocol.URL, + public convenience init?(url: URL, toolchainRegistry: ToolchainRegistry, buildSetup: BuildSetup) { @@ -195,9 +196,13 @@ extension SwiftPMWorkspace: BuildSystem { } public func settings( - for url: LanguageServerProtocol.URL, + for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { + guard case .url(let url) = uri else { + // We can't determine build settings for non-file URIs. + return nil + } guard let path = try? AbsolutePath(validating: url.path) else { return nil } @@ -217,19 +222,19 @@ extension SwiftPMWorkspace: BuildSystem { return nil } - public func toolchain(for: LanguageServerProtocol.URL, _ language: Language) -> SKCore.Toolchain? { + public func toolchain(for: DocumentURI, _ language: Language) -> SKCore.Toolchain? { return nil } /// Register the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func registerForChangeNotifications(for url: LanguageServerProtocol.URL) { + public func registerForChangeNotifications(for uri: DocumentURI) { // TODO: Support for change detection (via file watching) } /// Unregister the given file for build-system level change notifications, such as command /// line flag changes, dependency changes, etc. - public func unregisterForChangeNotifications(for url: LanguageServerProtocol.URL) { + public func unregisterForChangeNotifications(for uri: DocumentURI) { // TODO: Support for change detection (via file watching) } diff --git a/Sources/SKTestSupport/Extensions.swift b/Sources/SKTestSupport/Extensions.swift new file mode 100644 index 00000000..9c6bff78 --- /dev/null +++ b/Sources/SKTestSupport/Extensions.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import LanguageServerProtocol +import TSCBasic + +public extension TextDocumentIdentifier { + init(_ url: URL) { + self.init(.url(url)) + } +} + +public extension AbsolutePath { + var asURI: DocumentURI { + return .url(asURL) + } +} diff --git a/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift b/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift index 3d5154cb..91f1e495 100644 --- a/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift +++ b/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift @@ -85,7 +85,7 @@ public final class SKSwiftPMTestWorkspace { listenToUnitEvents: false) testServer.server!.workspace = Workspace( - rootPath: sourcePath, + rootUri: .url(sources.rootDirectory), clientCapabilities: ClientCapabilities(), buildSettings: swiftpm, index: index, @@ -121,7 +121,7 @@ extension SKSwiftPMTestWorkspace { extension SKSwiftPMTestWorkspace { public func openDocument(_ url: URL, language: Language) throws { sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: language, version: 1, text: try sources.sourceCache.get(url)))) diff --git a/Sources/SKTestSupport/SKTibsTestWorkspace.swift b/Sources/SKTestSupport/SKTibsTestWorkspace.swift index 4e066d7e..c0177f9d 100644 --- a/Sources/SKTestSupport/SKTibsTestWorkspace.swift +++ b/Sources/SKTestSupport/SKTibsTestWorkspace.swift @@ -62,7 +62,7 @@ public final class SKTibsTestWorkspace { func initWorkspace(clientCapabilities: ClientCapabilities) { let buildPath = AbsolutePath(builder.buildRoot.path) testServer.server!.workspace = Workspace( - rootPath: AbsolutePath(sources.rootDirectory.path), + rootUri: .url(sources.rootDirectory), clientCapabilities: clientCapabilities, buildSettings: CompilationDatabaseBuildSystem(projectRoot: buildPath), index: index, @@ -82,7 +82,7 @@ extension SKTibsTestWorkspace { extension SKTibsTestWorkspace { public func openDocument(_ url: URL, language: Language) throws { sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: language, version: 1, text: try sources.sourceCache.get(url)))) @@ -134,13 +134,13 @@ extension Position { extension Location { public init(_ loc: TestLocation) { - self.init(url: loc.url, range: Range(Position(loc))) + self.init(uri: .url(loc.url), range: Range(Position(loc))) } /// Incorrectly use the UTF-8 column index in place of the UTF-16 one, to match the incorrect /// implementation in SourceKitServer when using the index. public init(badUTF16 loc: TestLocation) { - self.init(url: loc.url, range: Range(Position(badUTF16: loc))) + self.init(uri: .url(loc.url), range: Range(Position(badUTF16: loc))) } } @@ -162,5 +162,5 @@ extension TibsToolchain { } extension TestLocation { - public var docIdentifier: TextDocumentIdentifier { TextDocumentIdentifier(url) } + public var docIdentifier: TextDocumentIdentifier { TextDocumentIdentifier(.url(url)) } } diff --git a/Sources/SourceKit/DocumentManager.swift b/Sources/SourceKit/DocumentManager.swift index dd3828a4..d5162cbf 100644 --- a/Sources/SourceKit/DocumentManager.swift +++ b/Sources/SourceKit/DocumentManager.swift @@ -33,13 +33,13 @@ public struct DocumentSnapshot { } public final class Document { - public let url: URL + public let uri: DocumentURI public let language: Language var latestVersion: Int var latestLineTable: LineTable - init(url: URL, language: Language, version: Int, text: String) { - self.url = url + init(uri: DocumentURI, language: Language, version: Int, text: String) { + self.uri = uri self.language = language self.latestVersion = version self.latestLineTable = LineTable(text) @@ -54,16 +54,16 @@ public final class Document { public final class DocumentManager { public enum Error: Swift.Error { - case alreadyOpen(URL) - case missingDocument(URL) + case alreadyOpen(DocumentURI) + case missingDocument(DocumentURI) } let queue: DispatchQueue = DispatchQueue(label: "document-manager-queue") - var documents: [URL: Document] = [:] + var documents: [DocumentURI: Document] = [:] /// All currently opened documents. - public var openDocuments: Set { + public var openDocuments: Set { return queue.sync { return Set(documents.keys) } @@ -74,11 +74,11 @@ public final class DocumentManager { /// - returns: The initial contents of the file. /// - throws: Error.alreadyOpen if the document is already open. @discardableResult - public func open(_ url: URL, language: Language, version: Int, text: String) throws -> DocumentSnapshot { + public func open(_ uri: DocumentURI, language: Language, version: Int, text: String) throws -> DocumentSnapshot { return try queue.sync { - let document = Document(url: url, language: language, version: version, text: text) - if nil != documents.updateValue(document, forKey: url) { - throw Error.alreadyOpen(url) + let document = Document(uri: uri, language: language, version: version, text: text) + if nil != documents.updateValue(document, forKey: uri) { + throw Error.alreadyOpen(uri) } return document.latestSnapshot } @@ -88,10 +88,10 @@ public final class DocumentManager { /// /// - returns: The initial contents of the file. /// - throws: Error.missingDocument if the document is not open. - public func close(_ url: URL) throws { + public func close(_ uri: DocumentURI) throws { try queue.sync { - if nil == documents.removeValue(forKey: url) { - throw Error.missingDocument(url) + if nil == documents.removeValue(forKey: uri) { + throw Error.missingDocument(uri) } } } @@ -103,10 +103,10 @@ public final class DocumentManager { /// - returns: The contents of the file after all the edits are applied. /// - throws: Error.missingDocument if the document is not open. @discardableResult - public func edit(_ url: URL, newVersion: Int, edits: [TextDocumentContentChangeEvent], editCallback: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil) throws -> DocumentSnapshot { + public func edit(_ uri: DocumentURI, newVersion: Int, edits: [TextDocumentContentChangeEvent], editCallback: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil) throws -> DocumentSnapshot { return try queue.sync { - guard let document = documents[url] else { - throw Error.missingDocument(url) + guard let document = documents[uri] else { + throw Error.missingDocument(uri) } for edit in edits { @@ -135,9 +135,9 @@ public final class DocumentManager { } } - public func latestSnapshot(_ url: URL) -> DocumentSnapshot? { + public func latestSnapshot(_ uri: DocumentURI) -> DocumentSnapshot? { return queue.sync { - guard let document = documents[url] else { + guard let document = documents[uri] else { return nil } return document.latestSnapshot @@ -154,14 +154,14 @@ extension DocumentManager { func open(_ note: DidOpenTextDocument) -> DocumentSnapshot? { let doc = note.textDocument return orLog("failed to open document", level: .error) { - try open(doc.url, language: doc.language, version: doc.version, text: doc.text) + try open(doc.uri, language: doc.language, version: doc.version, text: doc.text) } } /// Convenience wrapper for `close(_:)` that logs on failure. func close(_ note: DidCloseTextDocument) { orLog("failed to close document", level: .error) { - try close(note.textDocument.url) + try close(note.textDocument.uri) } } @@ -169,7 +169,7 @@ extension DocumentManager { @discardableResult func edit(_ note: DidChangeTextDocument, editCallback: ((_ before: DocumentSnapshot, TextDocumentContentChangeEvent) -> Void)? = nil) -> DocumentSnapshot? { return orLog("failed to edit document", level: .error) { - try edit(note.textDocument.url, newVersion: note.textDocument.version ?? -1, edits: note.contentChanges, editCallback: editCallback) + try edit(note.textDocument.uri, newVersion: note.textDocument.version ?? -1, edits: note.contentChanges, editCallback: editCallback) } } } diff --git a/Sources/SourceKit/SourceKitServer.swift b/Sources/SourceKit/SourceKitServer.swift index 1d4b6f38..32c9e11f 100644 --- a/Sources/SourceKit/SourceKitServer.swift +++ b/Sources/SourceKit/SourceKitServer.swift @@ -99,7 +99,7 @@ public final class SourceKitServer: LanguageServer { guard let workspace = self.workspace else { return req.reply(.failure(.serverNotInitialized)) } - guard let languageService = workspace.documentService[req.params.textDocument.url] else { + guard let languageService = workspace.documentService[req.params.textDocument.uri] else { return req.reply(fallback) } requestHandler(self)(req, languageService) @@ -155,8 +155,8 @@ public final class SourceKitServer: LanguageServer { client.send(note.params) } - func toolchain(for url: URL, _ language: Language) -> Toolchain? { - if let toolchain = workspace?.buildSettings.toolchain(for: url, language) { + func toolchain(for uri: DocumentURI, _ language: Language) -> Toolchain? { + if let toolchain = workspace?.buildSettings.toolchain(for: uri, language) { return toolchain } @@ -201,7 +201,7 @@ public final class SourceKitServer: LanguageServer { let resp = try service.initializeSync(InitializeRequest( processId: pid, rootPath: nil, - rootURL: (workspace?.rootPath).map { URL(fileURLWithPath: $0.pathString) }, + rootURI: workspace?.rootUri, initializationOptions: nil, capabilities: workspace?.clientCapabilities ?? ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -219,20 +219,20 @@ public final class SourceKitServer: LanguageServer { } } - func languageService(for url: URL, _ language: Language, in workspace: Workspace) -> ToolchainLanguageServer? { - if let service = workspace.documentService[url] { + func languageService(for uri: DocumentURI, _ language: Language, in workspace: Workspace) -> ToolchainLanguageServer? { + if let service = workspace.documentService[uri] { return service } - guard let toolchain = toolchain(for: url, language), + guard let toolchain = toolchain(for: uri, language), let service = languageService(for: toolchain, language) else { return nil } - log("Using toolchain \(toolchain.displayName) (\(toolchain.identifier)) for \(url)") + log("Using toolchain \(toolchain.displayName) (\(toolchain.identifier)) for \(uri)") - workspace.documentService[url] = service + workspace.documentService[uri] = service return service } } @@ -244,21 +244,21 @@ extension SourceKitServer: BuildSystemDelegate { // TODO: do something with these changes once build target support is in place } - public func fileBuildSettingsChanged(_ changedFiles: Set) { + public func fileBuildSettingsChanged(_ changedFiles: Set) { guard let workspace = self.workspace else { return } let documentManager = workspace.documentManager let openDocuments = documentManager.openDocuments - for url in changedFiles { - guard openDocuments.contains(url) else { + for uri in changedFiles { + guard openDocuments.contains(uri) else { continue } - log("Build settings changed for opened file \(url)") - if let snapshot = documentManager.latestSnapshot(url), - let service = languageService(for: url, snapshot.document.language, in: workspace) { - service.documentUpdatedBuildSettings(url, language: snapshot.document.language) + log("Build settings changed for opened file \(uri)") + if let snapshot = documentManager.latestSnapshot(uri), + let service = languageService(for: uri, snapshot.document.language, in: workspace) { + service.documentUpdatedBuildSettings(uri, language: snapshot.document.language) } } } @@ -279,16 +279,16 @@ extension SourceKitServer { } } - if let url = req.params.rootURL { + if let uri = req.params.rootURI { self.workspace = try? Workspace( - url: url, + rootUri: uri, clientCapabilities: req.params.capabilities, toolchainRegistry: self.toolchainRegistry, buildSetup: self.options.buildSetup, indexOptions: indexOptions) } else if let path = req.params.rootPath { self.workspace = try? Workspace( - url: URL(fileURLWithPath: path), + rootUri: .url(URL(fileURLWithPath: path)), clientCapabilities: req.params.capabilities, toolchainRegistry: self.toolchainRegistry, buildSetup: self.options.buildSetup, @@ -299,7 +299,7 @@ extension SourceKitServer { log("no workspace found", level: .warning) self.workspace = Workspace( - rootPath: nil, + rootUri: nil, clientCapabilities: req.params.capabilities, buildSettings: BuildSystemList(), index: nil, @@ -366,9 +366,9 @@ extension SourceKitServer { workspace.documentManager.open(note.params) let textDocument = note.params.textDocument - workspace.buildSettings.registerForChangeNotifications(for: textDocument.url) + workspace.buildSettings.registerForChangeNotifications(for: textDocument.uri) - if let service = languageService(for: textDocument.url, textDocument.language, in: workspace) { + if let service = languageService(for: textDocument.uri, textDocument.language, in: workspace) { service.openDocument(note.params) } } @@ -376,10 +376,9 @@ extension SourceKitServer { func closeDocument(_ note: Notification, workspace: Workspace) { workspace.documentManager.close(note.params) - let url = note.params.textDocument.url - workspace.buildSettings.unregisterForChangeNotifications(for: url) + workspace.buildSettings.unregisterForChangeNotifications(for: note.params.textDocument.uri) - if let service = workspace.documentService[url] { + if let service = workspace.documentService[note.params.textDocument.uri] { service.closeDocument(note.params) } } @@ -387,19 +386,19 @@ extension SourceKitServer { func changeDocument(_ note: Notification, workspace: Workspace) { workspace.documentManager.edit(note.params) - if let service = workspace.documentService[note.params.textDocument.url] { + if let service = workspace.documentService[note.params.textDocument.uri] { service.changeDocument(note.params) } } func willSaveDocument(_ note: Notification, workspace: Workspace) { - if let service = workspace.documentService[note.params.textDocument.url] { + if let service = workspace.documentService[note.params.textDocument.uri] { service.willSaveDocument(note.params) } } func didSaveDocument(_ note: Notification, workspace: Workspace) { - if let service = workspace.documentService[note.params.textDocument.url] { + if let service = workspace.documentService[note.params.textDocument.uri] { service.didSaveDocument(note.params) } } @@ -445,7 +444,7 @@ extension SourceKitServer { utf16index: symbolOccurrence.location.utf8Column - 1) let symbolLocation = Location( - url: URL(fileURLWithPath: symbolOccurrence.location.path), + uri: .url(URL(fileURLWithPath: symbolOccurrence.location.path)), range: Range(symbolPosition)) return SymbolInformation( @@ -497,12 +496,12 @@ extension SourceKitServer { } func executeCommand(_ req: Request, workspace: Workspace) { - guard let url = req.params.textDocument?.url else { + guard let uri = req.params.textDocument?.uri else { log("attempted to perform executeCommand request without an url!", level: .error) req.reply(nil) return } - guard let languageService = workspace.documentService[url] else { + guard let languageService = workspace.documentService[uri] else { req.reply(nil) return } @@ -566,7 +565,7 @@ extension SourceKitServer { return nil } return Location( - url: URL(fileURLWithPath: occur.location.path), + uri: .url(URL(fileURLWithPath: occur.location.path)), range: Range(Position( line: occur.location.line - 1, // 1-based -> 0-based // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! @@ -613,7 +612,7 @@ extension SourceKitServer { return nil } return Location( - url: URL(fileURLWithPath: occur.location.path), + uri: .url(URL(fileURLWithPath: occur.location.path)), range: Range(Position( line: occur.location.line - 1, // 1-based -> 0-based // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! @@ -631,7 +630,7 @@ extension SourceKitServer { // FIXME: a lot of duplication with definition request func references(_ req: Request, workspace: Workspace) { - guard let service = workspace.documentService[req.params.textDocument.url] else { + guard let service = workspace.documentService[req.params.textDocument.uri] else { req.reply([]) return } @@ -666,7 +665,7 @@ extension SourceKitServer { return nil } return Location( - url: URL(fileURLWithPath: occur.location.path), + uri: .url(URL(fileURLWithPath: occur.location.path)), range: Range(Position( line: occur.location.line - 1, // 1-based -> 0-based // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! diff --git a/Sources/SourceKit/ToolchainLanguageServer.swift b/Sources/SourceKit/ToolchainLanguageServer.swift index 8f60e10f..7b1ba2d6 100644 --- a/Sources/SourceKit/ToolchainLanguageServer.swift +++ b/Sources/SourceKit/ToolchainLanguageServer.swift @@ -28,7 +28,7 @@ public protocol ToolchainLanguageServer: AnyObject { func changeDocument(_ note: DidChangeTextDocument) func willSaveDocument(_ note: WillSaveTextDocument) func didSaveDocument(_ note: DidSaveTextDocument) - func documentUpdatedBuildSettings(_ url: URL, language: Language) + func documentUpdatedBuildSettings(_ uri: DocumentURI, language: Language) // MARK: - Text Document diff --git a/Sources/SourceKit/Workspace.swift b/Sources/SourceKit/Workspace.swift index 4257c095..342efc2e 100644 --- a/Sources/SourceKit/Workspace.swift +++ b/Sources/SourceKit/Workspace.swift @@ -29,7 +29,7 @@ import TSCUtility public final class Workspace { /// The root directory of the workspace. - public let rootPath: AbsolutePath? + public let rootUri: DocumentURI? public let clientCapabilities: ClientCapabilities @@ -46,16 +46,16 @@ public final class Workspace { public let documentManager: DocumentManager = DocumentManager() /// Language service for an open document, if available. - var documentService: [URL: ToolchainLanguageServer] = [:] + var documentService: [DocumentURI: ToolchainLanguageServer] = [:] public init( - rootPath: AbsolutePath?, + rootUri: DocumentURI?, clientCapabilities: ClientCapabilities, buildSettings: BuildSystem, index: IndexStoreDB?, buildSetup: BuildSetup) { - self.rootPath = rootPath + self.rootUri = rootUri self.clientCapabilities = clientCapabilities self.buildSettings = buildSettings self.index = index @@ -69,7 +69,7 @@ public final class Workspace { /// - clientCapabilities: The client capabilities provided during server initialization. /// - toolchainRegistry: The toolchain registry. public init( - url: URL, + rootUri: DocumentURI, clientCapabilities: ClientCapabilities, toolchainRegistry: ToolchainRegistry, buildSetup: BuildSetup, @@ -78,20 +78,26 @@ public final class Workspace { self.buildSetup = buildSetup - self.rootPath = try AbsolutePath(validating: url.path) + self.rootUri = rootUri self.clientCapabilities = clientCapabilities let settings = BuildSystemList() self.buildSettings = settings - if let buildServer = BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) { - settings.providers.insert(buildServer, at: 0) - } else { - settings.providers.insert(CompilationDatabaseBuildSystem(projectRoot: rootPath), at: 0) - if let swiftpm = SwiftPMWorkspace(url: url, - toolchainRegistry: toolchainRegistry, - buildSetup: buildSetup) { - settings.providers.insert(swiftpm, at: 0) + if case .url(let rootUrl) = rootUri, let rootPath = try? AbsolutePath(validating: rootUrl.path) { + if let buildServer = BuildServerBuildSystem(projectRoot: rootPath, buildSetup: buildSetup) { + settings.providers.insert(buildServer, at: 0) + } else { + settings.providers.insert(CompilationDatabaseBuildSystem(projectRoot: rootPath), at: 0) + if let swiftpm = SwiftPMWorkspace(url: rootUrl, + toolchainRegistry: toolchainRegistry, + buildSetup: buildSetup) { + settings.providers.insert(swiftpm, at: 0) + } } + } else { + // We assume that workspaces are directories. This is only true for URLs not for URIs in general. + // Simply skip setting up the build integration in this case. + log("cannot setup build integration for workspace at URI \(rootUri) because the URI it is not a valid file URL") } if let storePath = buildSettings.indexStorePath, diff --git a/Sources/SourceKit/clangd/ClangLanguageServer.swift b/Sources/SourceKit/clangd/ClangLanguageServer.swift index 4f3312a6..34324e5e 100644 --- a/Sources/SourceKit/clangd/ClangLanguageServer.swift +++ b/Sources/SourceKit/clangd/ClangLanguageServer.swift @@ -86,7 +86,7 @@ extension ClangLanguageServerShim { public func openDocument(_ note: DidOpenTextDocument) { let textDocument = note.textDocument - documentUpdatedBuildSettings(textDocument.url, language: textDocument.language) + documentUpdatedBuildSettings(textDocument.uri, language: textDocument.language) clangd.send(note) } @@ -106,12 +106,17 @@ extension ClangLanguageServerShim { } - public func documentUpdatedBuildSettings(_ url: URL, language: Language) { - let settings = buildSystem.settings(for: url, language) + public func documentUpdatedBuildSettings(_ uri: DocumentURI, language: Language) { + guard case .url(let url) = uri else { + // FIXME: The clang workspace can probably be reworked to support non-file URIs. + log("Received updated build settings for non-file URI '\(uri)'. Ignoring the update.") + return + } + let settings = buildSystem.settings(for: uri, language) logAsync(level: settings == nil ? .warning : .debug) { _ in let settingsStr = settings == nil ? "nil" : settings!.compilerArguments.description - return "settings for \(url): \(settingsStr)" + return "settings for \(uri): \(settingsStr)" } if let settings = settings { diff --git a/Sources/SourceKit/sourcekitd/CursorInfo.swift b/Sources/SourceKit/sourcekitd/CursorInfo.swift index f486bb1f..51c11965 100644 --- a/Sources/SourceKit/sourcekitd/CursorInfo.swift +++ b/Sources/SourceKit/sourcekitd/CursorInfo.swift @@ -48,7 +48,7 @@ struct CursorInfo { enum CursorInfoError: Error { /// The given URL is not a known document. - case unknownDocument(URL) + case unknownDocument(DocumentURI) /// The given range is not valid in the document snapshot. case invalidRange(Range) @@ -74,13 +74,13 @@ extension SwiftLanguageServer { /// Must be called on self.queue. func _cursorInfo( - _ url: URL, + _ uri: DocumentURI, _ range: Range, additionalParameters appendAdditionalParameters: ((SKRequestDictionary) -> Void)? = nil, _ completion: @escaping (Swift.Result) -> Void) { - guard let snapshot = documentManager.latestSnapshot(url) else { - return completion(.failure(.unknownDocument(url))) + guard let snapshot = documentManager.latestSnapshot(uri) else { + return completion(.failure(.unknownDocument(uri))) } guard let offsetRange = snapshot.utf8OffsetRange(of: range) else { @@ -95,10 +95,10 @@ extension SwiftLanguageServer { if offsetRange.upperBound != offsetRange.lowerBound { skreq[keys.length] = offsetRange.count } - skreq[keys.sourcefile] = snapshot.document.url.path + skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath // FIXME: SourceKit should probably cache this for us. - if let settings = self.buildSettingsByFile[snapshot.document.url] { + if let settings = self.buildSettingsByFile[uri] { skreq[keys.compilerargs] = settings.compilerArguments } @@ -120,7 +120,7 @@ extension SwiftLanguageServer { let offset: Int = dict[keys.offset], let pos = snapshot.positionOf(utf8Offset: offset) { - location = Location(url: URL(fileURLWithPath: filepath), range: Range(pos)) + location = Location(uri: .url(URL(fileURLWithPath: filepath)), range: Range(pos)) } let refactorActionsArray: SKResponseArray? = dict[keys.refactor_actions] @@ -138,7 +138,7 @@ extension SwiftLanguageServer { [SemanticRefactorCommand]( array: refactorActionsArray, range: range, - textDocument: TextDocumentIdentifier(url), + textDocument: TextDocumentIdentifier(uri), keys, self.api) ))) @@ -158,13 +158,13 @@ extension SwiftLanguageServer { /// - range: The position range within the document to lookup the symbol at. /// - completion: Completion block to asynchronously receive the CursorInfo, or error. func cursorInfo( - _ url: URL, + _ uri: DocumentURI, _ range: Range, additionalParameters appendAdditionalParameters: ((SKRequestDictionary) -> Void)? = nil, _ completion: @escaping (Swift.Result) -> Void) { self.queue.async { - self._cursorInfo(url, range, + self._cursorInfo(uri, range, additionalParameters: appendAdditionalParameters, completion) } } diff --git a/Sources/SourceKit/sourcekitd/Diagnostic.swift b/Sources/SourceKit/sourcekitd/Diagnostic.swift index 64032398..db2d1fbf 100644 --- a/Sources/SourceKit/sourcekitd/Diagnostic.swift +++ b/Sources/SourceKit/sourcekitd/Diagnostic.swift @@ -58,7 +58,7 @@ extension Diagnostic { sknotes.forEach { (_, sknote) -> Bool in guard let note = Diagnostic(sknote, in: snapshot) else { return true } notes?.append(DiagnosticRelatedInformation( - location: Location(url: snapshot.document.url, range: note.range), + location: Location(uri: snapshot.document.uri, range: note.range), message: note.message )) return true diff --git a/Sources/SourceKit/sourcekitd/SemanticRefactoring.swift b/Sources/SourceKit/sourcekitd/SemanticRefactoring.swift index 3905d416..2f32f3ad 100644 --- a/Sources/SourceKit/sourcekitd/SemanticRefactoring.swift +++ b/Sources/SourceKit/sourcekitd/SemanticRefactoring.swift @@ -72,7 +72,7 @@ struct SemanticRefactoring { } self.title = title - self.edit = WorkspaceEdit(changes: [snapshot.document.url: textEdits]) + self.edit = WorkspaceEdit(changes: [snapshot.document.uri: textEdits]) } } @@ -80,7 +80,7 @@ struct SemanticRefactoring { enum SemanticRefactoringError: Error { /// The given URL is not a known document. - case unknownDocument(URL) + case unknownDocument(DocumentURI) /// The given position range is invalid. case invalidRange(Range) @@ -92,7 +92,7 @@ enum SemanticRefactoringError: Error { case responseError(ResponseError) /// The underlying sourcekitd reported no edits for this action. - case noEditsNeeded(URL) + case noEditsNeeded(DocumentURI) } extension SemanticRefactoringError: CustomStringConvertible { @@ -129,9 +129,9 @@ extension SwiftLanguageServer { let keys = self.keys queue.async { - let url = refactorCommand.textDocument.url - guard let snapshot = self.documentManager.latestSnapshot(url) else { - return completion(.failure(.unknownDocument(url))) + let uri = refactorCommand.textDocument.uri + guard let snapshot = self.documentManager.latestSnapshot(uri) else { + return completion(.failure(.unknownDocument(uri))) } guard let offsetRange = snapshot.utf8OffsetRange(of: refactorCommand.positionRange) else { return completion(.failure(.failedToRetrieveOffset(refactorCommand.positionRange))) @@ -147,7 +147,7 @@ extension SwiftLanguageServer { // Preferred name for e.g. an extracted variable. // Empty string means sourcekitd chooses a name automatically. skreq[keys.name] = "" - skreq[keys.sourcefile] = url.path + skreq[keys.sourcefile] = uri.pseudoPath // LSP is zero based, but this request is 1 based. skreq[keys.line] = line + 1 skreq[keys.column] = utf8Column + 1 @@ -155,7 +155,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 settings = self.buildSettingsByFile[snapshot.document.url] { + if let settings = self.buildSettingsByFile[snapshot.document.uri] { skreq[keys.compilerargs] = settings.compilerArguments } @@ -165,7 +165,7 @@ extension SwiftLanguageServer { return completion(.failure(.responseError(result.failure!))) } guard let refactor = SemanticRefactoring(refactorCommand.title, dict, snapshot, self.keys) else { - return completion(.failure(.noEditsNeeded(url))) + return completion(.failure(.noEditsNeeded(uri))) } completion(.success(refactor)) } diff --git a/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift b/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift index 24a1cbe2..7b1b0adb 100644 --- a/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift +++ b/Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift @@ -35,9 +35,9 @@ public final class SwiftLanguageServer: ToolchainLanguageServer { // FIXME: ideally we wouldn't need separate management from a parent server in the same process. var documentManager: DocumentManager - var currentDiagnostics: [URL: [CachedDiagnostic]] = [:] + var currentDiagnostics: [DocumentURI: [CachedDiagnostic]] = [:] - var buildSettingsByFile: [URL: FileBuildSettings] = [:] + var buildSettingsByFile: [DocumentURI: FileBuildSettings] = [:] let onExit: () -> Void @@ -73,19 +73,19 @@ public final class SwiftLanguageServer: ToolchainLanguageServer { return true } - let document = snapshot.document.url + let document = snapshot.document.uri let result = mergeDiagnostics( old: currentDiagnostics[document] ?? [], new: newDiags, stage: stage) currentDiagnostics[document] = result - client.send(PublishDiagnostics(url: document, diagnostics: result.map { $0.diagnostic })) + client.send(PublishDiagnostics(uri: document, diagnostics: result.map { $0.diagnostic })) } /// Should be called on self.queue. - func handleDocumentUpdate(url: URL) { - guard let snapshot = documentManager.latestSnapshot(url) else { + func handleDocumentUpdate(uri: DocumentURI) { + guard let snapshot = documentManager.latestSnapshot(uri) else { return } @@ -93,7 +93,7 @@ public final class SwiftLanguageServer: ToolchainLanguageServer { let req = SKRequestDictionary(sourcekitd: sourcekitd) req[keys.request] = requests.editor_replacetext - req[keys.name] = url.path + req[keys.name] = uri.pseudoPath req[keys.offset] = 0 req[keys.length] = 0 req[keys.sourcetext] = "" @@ -125,7 +125,7 @@ extension SwiftLanguageServer { let name: String = dict[self.keys.name] { self.queue.async { - self.handleDocumentUpdate(url: URL(fileURLWithPath: name)) + self.handleDocumentUpdate(uri: DocumentURI(string: name)) } } } @@ -172,19 +172,19 @@ extension SwiftLanguageServer { // MARK: - Workspace - public func documentUpdatedBuildSettings(_ url: URL, language: Language) { + public func documentUpdatedBuildSettings(_ uri: DocumentURI, language: Language) { self.queue.async { - guard let snapshot = self.documentManager.latestSnapshot(url) else { + guard let snapshot = self.documentManager.latestSnapshot(uri) else { return } // Confirm that the build settings actually changed, otherwise we don't // need to do anything. - let newSettings = self.buildSystem.settings(for: url, language) - guard self.buildSettingsByFile[url] != newSettings else { + let newSettings = self.buildSystem.settings(for: uri, language) + guard self.buildSettingsByFile[uri] != newSettings else { return } - self.buildSettingsByFile[url] = newSettings + self.buildSettingsByFile[uri] = newSettings let keys = self.keys @@ -192,12 +192,12 @@ extension SwiftLanguageServer { // update the settings. At the moment there's no better way to do this. let closeReq = SKRequestDictionary(sourcekitd: self.sourcekitd) closeReq[keys.request] = self.requests.editor_close - closeReq[keys.name] = url.path + closeReq[keys.name] = uri.pseudoPath _ = self.sourcekitd.sendSync(closeReq) let openReq = SKRequestDictionary(sourcekitd: self.sourcekitd) openReq[keys.request] = self.requests.editor_open - openReq[keys.name] = url.path + openReq[keys.name] = uri.pseudoPath openReq[keys.sourcetext] = snapshot.text if let settings = newSettings { openReq[keys.compilerargs] = settings.compilerArguments @@ -222,15 +222,15 @@ extension SwiftLanguageServer { return } - let url = snapshot.document.url + let uri = snapshot.document.uri // Cache the `BuildSystem`'s settings interally. - let settings = self.buildSystem.settings(for: url, snapshot.document.language) - self.buildSettingsByFile[url] = settings + let settings = self.buildSystem.settings(for: uri, snapshot.document.language) + self.buildSettingsByFile[uri] = settings let req = SKRequestDictionary(sourcekitd: self.sourcekitd) req[keys.request] = self.requests.editor_open - req[keys.name] = note.textDocument.url.path + req[keys.name] = note.textDocument.uri.pseudoPath req[keys.sourcetext] = snapshot.text if let settings = settings { @@ -252,15 +252,15 @@ extension SwiftLanguageServer { self.queue.async { self.documentManager.close(note) - let url = note.textDocument.url + let uri = note.textDocument.uri let req = SKRequestDictionary(sourcekitd: self.sourcekitd) req[keys.request] = self.requests.editor_close - req[keys.name] = url.path + req[keys.name] = uri.pseudoPath // Clear settings that should not be cached for closed documents. - self.buildSettingsByFile[url] = nil - self.currentDiagnostics[url] = nil + self.buildSettingsByFile[uri] = nil + self.currentDiagnostics[uri] = nil _ = self.sourcekitd.sendSync(req) } @@ -275,7 +275,7 @@ extension SwiftLanguageServer { let snapshot = self.documentManager.edit(note) { (before: DocumentSnapshot, edit: TextDocumentContentChangeEvent) in let req = SKRequestDictionary(sourcekitd: self.sourcekitd) req[keys.request] = self.requests.editor_replacetext - req[keys.name] = note.textDocument.url.path + req[keys.name] = note.textDocument.uri.pseudoPath if let range = edit.range { guard let offset = before.utf8Offset(of: range.lowerBound), let end = before.utf8Offset(of: range.upperBound) else { @@ -316,8 +316,8 @@ extension SwiftLanguageServer { let keys = self.keys queue.async { - guard let snapshot = self.documentManager.latestSnapshot(req.params.textDocument.url) else { - log("failed to find snapshot for url \(req.params.textDocument.url)") + guard let snapshot = self.documentManager.latestSnapshot(req.params.textDocument.uri) else { + log("failed to find snapshot for url \(req.params.textDocument.uri)") req.reply(CompletionList(isIncomplete: true, items: [])) return } @@ -337,11 +337,11 @@ extension SwiftLanguageServer { let skreq = SKRequestDictionary(sourcekitd: self.sourcekitd) skreq[keys.request] = self.requests.codecomplete skreq[keys.offset] = offset - skreq[keys.sourcefile] = snapshot.document.url.path + skreq[keys.sourcefile] = snapshot.document.uri.pseudoPath skreq[keys.sourcetext] = snapshot.text // FIXME: SourceKit should probably cache this for us. - if let settings = self.buildSettingsByFile[snapshot.document.url] { + if let settings = self.buildSettingsByFile[snapshot.document.uri] { skreq[keys.compilerargs] = settings.compilerArguments } @@ -455,12 +455,12 @@ extension SwiftLanguageServer { } public func hover(_ req: Request) { - let url = req.params.textDocument.url + let uri = req.params.textDocument.uri let position = req.params.position - cursorInfo(url, position..) { - let url = req.params.textDocument.url + let uri = req.params.textDocument.uri let position = req.params.position - cursorInfo(url, position.. uri - checkCoding(Location(url: url, range: range), json: """ + checkCoding(Location(uri: uri, range: range), json: """ { "range" : \(indent2rangejson), "uri" : "\(urljson)" @@ -54,26 +55,26 @@ final class CodingTests: XCTestCase { """) // url -> uri - checkCoding(TextDocumentIdentifier(url), json: """ + checkCoding(TextDocumentIdentifier(uri), json: """ { "uri" : "\(urljson)" } """) - checkCoding(VersionedTextDocumentIdentifier(url, version: nil), json: """ + checkCoding(VersionedTextDocumentIdentifier(uri, version: nil), json: """ { "uri" : "\(urljson)" } """) - checkCoding(VersionedTextDocumentIdentifier(url, version: 3), json: """ + checkCoding(VersionedTextDocumentIdentifier(uri, version: 3), json: """ { "uri" : "\(urljson)", "version" : 3 } """) - checkCoding(TextDocumentEdit(textDocument: VersionedTextDocumentIdentifier(url, version: 1), edits: [TextEdit(range: range, newText: "foo")]), json: """ + checkCoding(TextDocumentEdit(textDocument: VersionedTextDocumentIdentifier(uri, version: 1), edits: [TextEdit(range: range, newText: "foo")]), json: """ { "edits" : [ { @@ -89,21 +90,21 @@ final class CodingTests: XCTestCase { """) // url -> uri - checkCoding(WorkspaceFolder(url: url, name: "foo"), json: """ + checkCoding(WorkspaceFolder(uri: uri, name: "foo"), json: """ { "name" : "foo", "uri" : "\(urljson)" } """) - checkCoding(WorkspaceFolder(url: url), json: """ + checkCoding(WorkspaceFolder(uri: uri), json: """ { "name" : "foo.swift", "uri" : "\(urljson)" } """) - checkCoding(WorkspaceFolder(url: url, name: ""), json: """ + checkCoding(WorkspaceFolder(uri: uri, name: ""), json: """ { "name" : "unknown_workspace", "uri" : "\(urljson)" @@ -237,7 +238,7 @@ final class CodingTests: XCTestCase { "text" : "a" } """) - checkCoding(WorkspaceEdit(changes: [url: []]), json: """ + checkCoding(WorkspaceEdit(changes: [uri: []]), json: """ { "changes" : { "\(urljson)" : [ diff --git a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift index cfb9e29a..883cd72e 100644 --- a/Tests/SKCoreTests/BuildServerBuildSystemTests.swift +++ b/Tests/SKCoreTests/BuildServerBuildSystemTests.swift @@ -39,14 +39,14 @@ final class BuildServerBuildSystemTests: XCTestCase { // test settings with a response let fileURL = URL(fileURLWithPath: "/path/to/some/file.swift") - let settings = buildSystem.settings(for: fileURL, Language.swift) + let settings = buildSystem.settings(for: .url(fileURL), Language.swift) 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: missingFileURL, Language.swift)) + XCTAssertNil(buildSystem.settings(for: .url(missingFileURL), Language.swift)) } func testFileRegistration() throws { @@ -57,9 +57,9 @@ final class BuildServerBuildSystemTests: XCTestCase { let fileUrl = URL(fileURLWithPath: "/some/file/path") let expectation = XCTestExpectation(description: "\(fileUrl) settings updated") - let buildSystemDelegate = TestDelegate(fileExpectations: [fileUrl: expectation]) + let buildSystemDelegate = TestDelegate(fileExpectations: [.url(fileUrl): expectation]) buildSystem.delegate = buildSystemDelegate - buildSystem.registerForChangeNotifications(for: fileUrl) + buildSystem.registerForChangeNotifications(for: .url(fileUrl)) XCTAssertEqual(XCTWaiter.wait(for: [expectation], timeout: 15), .completed) } @@ -174,7 +174,7 @@ final class BuildServerBuildSystemTests: XCTestCase { data: .dictionary(["key": "value"])): expectation, ]) buildSystem.delegate = buildSystemDelegate - buildSystem.registerForChangeNotifications(for: fileUrl) + buildSystem.registerForChangeNotifications(for: .url(fileUrl)) let result = XCTWaiter.wait(for: [expectation], timeout: 15) if result != .completed { @@ -185,10 +185,10 @@ final class BuildServerBuildSystemTests: XCTestCase { final class TestDelegate: BuildSystemDelegate { - let fileExpectations: [URL:XCTestExpectation] + let fileExpectations: [DocumentURI:XCTestExpectation] let targetExpectations: [BuildTargetEvent:XCTestExpectation] - public init(fileExpectations: [URL:XCTestExpectation] = [:], + public init(fileExpectations: [DocumentURI:XCTestExpectation] = [:], targetExpectations: [BuildTargetEvent:XCTestExpectation] = [:]) { self.fileExpectations = fileExpectations self.targetExpectations = targetExpectations @@ -200,9 +200,9 @@ final class TestDelegate: BuildSystemDelegate { } } - func fileBuildSettingsChanged(_ changedFiles: Set) { - for url in changedFiles { - fileExpectations[url]?.fulfill() + func fileBuildSettingsChanged(_ changedFiles: Set) { + for uri in changedFiles { + fileExpectations[uri]?.fulfill() } } } diff --git a/Tests/SKCoreTests/CompilationDatabaseTests.swift b/Tests/SKCoreTests/CompilationDatabaseTests.swift index f61acbd2..b629b61b 100644 --- a/Tests/SKCoreTests/CompilationDatabaseTests.swift +++ b/Tests/SKCoreTests/CompilationDatabaseTests.swift @@ -200,7 +200,7 @@ final class CompilationDatabaseTests: XCTestCase { let buildSystem: BuildSystem = CompilationDatabaseBuildSystem( projectRoot: AbsolutePath("/a"), fileSystem: fs) - let settings = buildSystem.settings(for: URL(fileURLWithPath: "/a/a.swift"), .swift) + let settings = buildSystem.settings(for: .url(URL(fileURLWithPath: "/a/a.swift")), .swift) XCTAssertNotNil(settings) XCTAssertEqual(settings?.workingDirectory, "/a") XCTAssertEqual(settings?.compilerArguments, ["-swift-version", "4", "/a/a.swift"]) diff --git a/Tests/SKCoreTests/FallbackBuildSystemTests.swift b/Tests/SKCoreTests/FallbackBuildSystemTests.swift index f75b9cbe..d1a91116 100644 --- a/Tests/SKCoreTests/FallbackBuildSystemTests.swift +++ b/Tests/SKCoreTests/FallbackBuildSystemTests.swift @@ -27,7 +27,7 @@ final class FallbackBuildSystemTests: XCTestCase { XCTAssertNil(bs.indexStorePath) XCTAssertNil(bs.indexDatabasePath) - let settings = bs.settings(for: source.asURL, .swift)! + let settings = bs.settings(for: source.asURI, .swift)! XCTAssertNil(settings.workingDirectory) let args = settings.compilerArguments @@ -39,7 +39,7 @@ final class FallbackBuildSystemTests: XCTestCase { bs.sdkpath = nil - XCTAssertEqual(bs.settings(for: source.asURL, .swift)?.compilerArguments, [ + XCTAssertEqual(bs.settings(for: source.asURI, .swift)?.compilerArguments, [ source.pathString, ]) } @@ -51,7 +51,7 @@ final class FallbackBuildSystemTests: XCTestCase { let bs = FallbackBuildSystem() bs.sdkpath = sdk - let settings = bs.settings(for: source.asURL, .cpp)! + let settings = bs.settings(for: source.asURI, .cpp)! XCTAssertNil(settings.workingDirectory) let args = settings.compilerArguments @@ -63,7 +63,7 @@ final class FallbackBuildSystemTests: XCTestCase { bs.sdkpath = nil - XCTAssertEqual(bs.settings(for: source.asURL, .cpp)?.compilerArguments, [ + XCTAssertEqual(bs.settings(for: source.asURI, .cpp)?.compilerArguments, [ source.pathString, ]) } @@ -72,7 +72,7 @@ final class FallbackBuildSystemTests: XCTestCase { let source = AbsolutePath("/my/source.c") let bs = FallbackBuildSystem() bs.sdkpath = nil - XCTAssertEqual(bs.settings(for: source.asURL, .c)?.compilerArguments, [ + XCTAssertEqual(bs.settings(for: source.asURI, .c)?.compilerArguments, [ source.pathString, ]) } @@ -81,7 +81,7 @@ final class FallbackBuildSystemTests: XCTestCase { let source = AbsolutePath("/my/source.m") let bs = FallbackBuildSystem() bs.sdkpath = nil - XCTAssertEqual(bs.settings(for: source.asURL, .objective_c)?.compilerArguments, [ + XCTAssertEqual(bs.settings(for: source.asURI, .objective_c)?.compilerArguments, [ source.pathString, ]) } @@ -90,7 +90,7 @@ final class FallbackBuildSystemTests: XCTestCase { let source = AbsolutePath("/my/source.mm") let bs = FallbackBuildSystem() bs.sdkpath = nil - XCTAssertEqual(bs.settings(for: source.asURL, .objective_cpp)?.compilerArguments, [ + XCTAssertEqual(bs.settings(for: source.asURI, .objective_cpp)?.compilerArguments, [ source.pathString, ]) } @@ -98,6 +98,6 @@ final class FallbackBuildSystemTests: XCTestCase { func testUnknown() { let source = AbsolutePath("/my/source.mm") let bs = FallbackBuildSystem() - XCTAssertNil(bs.settings(for: source.asURL, Language(rawValue: "unknown"))) + XCTAssertNil(bs.settings(for: source.asURI, Language(rawValue: "unknown"))) } } diff --git a/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift b/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift index 53ec9cf6..b047df67 100644 --- a/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift +++ b/Tests/SKSwiftPMWorkspaceTests/SwiftPMWorkspaceTests.swift @@ -104,7 +104,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { XCTAssertEqual(ws.buildPath, build) XCTAssertNotNil(ws.indexStorePath) - let arguments = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let arguments = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check( "-module-name", "lib", "-incremental", "-emit-dependencies", @@ -157,7 +157,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { let build = buildPath(root: packageRoot, config: config) XCTAssertEqual(ws.buildPath, build) - let arguments = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let arguments = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check("-typecheck", arguments: arguments) check("-Xcc", "-m32", arguments: arguments) @@ -187,7 +187,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { buildSetup: TestSourceKitServer.serverOptions.buildSetup) let source = resolveSymlinks(packageRoot.appending(component: "Package.swift")) - let arguments = ws.settings(for: source.asURL, .swift)!.compilerArguments + let arguments = ws.settings(for: source.asURI, .swift)!.compilerArguments check("-swift-version", "4.2", arguments: arguments) check(source.pathString, arguments: arguments) @@ -219,10 +219,10 @@ final class SwiftPMWorkspaceTests: XCTestCase { let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") let bswift = packageRoot.appending(components: "Sources", "lib", "b.swift") - let argumentsA = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let argumentsA = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check(aswift.pathString, arguments: argumentsA) check(bswift.pathString, arguments: argumentsA) - let argumentsB = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let argumentsB = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check(aswift.pathString, arguments: argumentsB) check(bswift.pathString, arguments: argumentsB) } @@ -258,14 +258,14 @@ final class SwiftPMWorkspaceTests: XCTestCase { let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift") let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift") - let arguments = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let arguments = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check(aswift.pathString, arguments: arguments) checkNot(bswift.pathString, arguments: arguments) check( "-I", packageRoot.appending(components: "Sources", "libC", "include").pathString, arguments: arguments) - let argumentsB = ws.settings(for: bswift.asURL, .swift)!.compilerArguments + let argumentsB = ws.settings(for: bswift.asURI, .swift)!.compilerArguments check(bswift.pathString, arguments: argumentsB) checkNot(aswift.pathString, arguments: argumentsB) checkNot("-I", packageRoot.appending(components: "Sources", "libC", "include").pathString, @@ -299,9 +299,9 @@ final class SwiftPMWorkspaceTests: XCTestCase { let aswift = packageRoot.appending(components: "Sources", "libA", "a.swift") let bswift = packageRoot.appending(components: "Sources", "libB", "b.swift") - XCTAssertNotNil(ws.settings(for: aswift.asURL, .swift)) - XCTAssertNil(ws.settings(for: bswift.asURL, .swift)) - XCTAssertNil(ws.settings(for: URL(string: "https://www.apple.com")!, .swift)) + XCTAssertNotNil(ws.settings(for: aswift.asURI, .swift)) + XCTAssertNil(ws.settings(for: bswift.asURI, .swift)) + XCTAssertNil(ws.settings(for: .url(URL(string: "https://www.apple.com")!), .swift)) } } @@ -356,7 +356,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { checkNot(bcxx.pathString, arguments: arguments) } - let args = ws.settings(for: acxx.asURL, .cpp)!.compilerArguments + let args = ws.settings(for: acxx.asURI, .cpp)!.compilerArguments checkArgsCommon(args) check("-MD", "-MT", "dependencies", "-MF", build.appending(components: "lib.build", "a.cpp.d").pathString, @@ -365,7 +365,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { check("-o", build.appending(components: "lib.build", "a.cpp.o").pathString, arguments: args) let header = packageRoot.appending(components: "Sources", "lib", "include", "a.h") - let headerArgs = ws.settings(for: header.asURL, .cpp)!.compilerArguments + let headerArgs = ws.settings(for: header.asURI, .cpp)!.compilerArguments checkArgsCommon(headerArgs) check("-c", "-x", "c++-header", header.pathString, arguments: headerArgs) } @@ -394,7 +394,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { buildSetup: TestSourceKitServer.serverOptions.buildSetup) let aswift = packageRoot.appending(components: "Sources", "lib", "a.swift") - let arguments = ws.settings(for: aswift.asURL, .swift)!.compilerArguments + let arguments = ws.settings(for: aswift.asURI, .swift)!.compilerArguments check("-target", arguments: arguments) // Only one! #if os(macOS) check("-target", @@ -437,8 +437,8 @@ final class SwiftPMWorkspaceTests: XCTestCase { .appending(components: "Sources", "lib", "a.swift") let manifest = packageRoot.appending(components: "Package.swift") - let arguments1 = ws.settings(for: aswift1.asURL, .swift)?.compilerArguments - let arguments2 = ws.settings(for: aswift2.asURL, .swift)?.compilerArguments + let arguments1 = ws.settings(for: aswift1.asURI, .swift)?.compilerArguments + let arguments2 = ws.settings(for: aswift2.asURI, .swift)?.compilerArguments XCTAssertNotNil(arguments1) XCTAssertNotNil(arguments2) XCTAssertEqual(arguments1, arguments2) @@ -446,7 +446,7 @@ final class SwiftPMWorkspaceTests: XCTestCase { checkNot(aswift1.pathString, arguments: arguments1 ?? []) check(resolveSymlinks(aswift1).pathString, arguments: arguments1 ?? []) - let argsManifest = ws.settings(for: manifest.asURL, .swift)?.compilerArguments + let argsManifest = ws.settings(for: manifest.asURI, .swift)?.compilerArguments XCTAssertNotNil(argsManifest) checkNot(manifest.pathString, arguments: argsManifest ?? []) @@ -487,12 +487,12 @@ final class SwiftPMWorkspaceTests: XCTestCase { let acxx = packageRoot.appending(components: "Sources", "lib", "a.cpp") let ah = packageRoot.appending(components: "Sources", "lib", "include", "a.h") - let argsCxx = ws.settings(for: acxx.asURL, .cpp)?.compilerArguments + let argsCxx = ws.settings(for: acxx.asURI, .cpp)?.compilerArguments XCTAssertNotNil(argsCxx) check(acxx.pathString, arguments: argsCxx ?? []) checkNot(resolveSymlinks(acxx).pathString, arguments: argsCxx ?? []) - let argsH = ws.settings(for: ah.asURL, .cpp)?.compilerArguments + let argsH = ws.settings(for: ah.asURI, .cpp)?.compilerArguments XCTAssertNotNil(argsH) checkNot(ah.pathString, arguments: argsH ?? []) check(resolveSymlinks(ah).pathString, arguments: argsH ?? []) diff --git a/Tests/SourceKitTests/BuildSystemTests.swift b/Tests/SourceKitTests/BuildSystemTests.swift index 387356d9..d9a47e7f 100644 --- a/Tests/SourceKitTests/BuildSystemTests.swift +++ b/Tests/SourceKitTests/BuildSystemTests.swift @@ -31,28 +31,28 @@ final class TestBuildSystem: BuildSystem { weak var delegate: BuildSystemDelegate? /// Build settings by file. - var buildSettingsByFile: [URL: FileBuildSettings] = [:] + var buildSettingsByFile: [DocumentURI: FileBuildSettings] = [:] /// Toolchains by file. - var toolchainsByFile: [URL: Toolchain] = [:] + var toolchainsByFile: [DocumentURI: Toolchain] = [:] /// Files currently being watched by our delegate. - var watchedFiles: Set = [] + var watchedFiles: Set = [] - func settings(for url: URL, _ language: Language) -> FileBuildSettings? { - return buildSettingsByFile[url] + func settings(for uri: DocumentURI, _ language: Language) -> FileBuildSettings? { + return buildSettingsByFile[uri] } - func toolchain(for url: URL, _ language: Language) -> Toolchain? { - return toolchainsByFile[url] + func toolchain(for uri: DocumentURI, _ language: Language) -> Toolchain? { + return toolchainsByFile[uri] } - func registerForChangeNotifications(for url: URL) { - watchedFiles.insert(url) + func registerForChangeNotifications(for uri: DocumentURI) { + watchedFiles.insert(uri) } - func unregisterForChangeNotifications(for url: URL) { - watchedFiles.remove(url) + func unregisterForChangeNotifications(for uri: DocumentURI) { + watchedFiles.remove(uri) } func buildTargets(reply: @escaping (LSPResult<[BuildTarget]>) -> Void) { @@ -91,7 +91,7 @@ final class BuildSystemTests: XCTestCase { buildSystem = TestBuildSystem() self.workspace = Workspace( - rootPath: nil, + rootUri: nil, clientCapabilities: ClientCapabilities(), buildSettings: buildSystem, index: nil, @@ -102,7 +102,7 @@ final class BuildSystemTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -132,29 +132,29 @@ final class BuildSystemTests: XCTestCase { } """ - buildSystem.buildSettingsByFile[url] = FileBuildSettings(compilerArguments: args) + buildSystem.buildSettingsByFile[.url(url)] = FileBuildSettings(compilerArguments: args) sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .objective_c, version: 12, text: text )), { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(.url(url))!.text) }) // Modify the build settings and inform the delegate. // This should trigger a new publish diagnostics and we should no longer have errors. - buildSystem.buildSettingsByFile[url] = FileBuildSettings(compilerArguments: args + ["-DFOO"]) - testServer.server?.fileBuildSettingsChanged([url]) + buildSystem.buildSettingsByFile[.url(url)] = FileBuildSettings(compilerArguments: args + ["-DFOO"]) + testServer.server?.fileBuildSettingsChanged([.url(url)]) let expectation = XCTestExpectation(description: "refresh") sk.handleNextNotification { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 0) - XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(.url(url))!.text) expectation.fulfill() } @@ -166,9 +166,9 @@ final class BuildSystemTests: XCTestCase { func testSwiftDocumentUpdatedBuildSettings() { let url = URL(fileURLWithPath: "/a.swift") - let args = FallbackBuildSystem().settings(for: url, .swift)!.compilerArguments + let args = FallbackBuildSystem().settings(for: .url(url), .swift)!.compilerArguments - buildSystem.buildSettingsByFile[url] = FileBuildSettings(compilerArguments: args) + buildSystem.buildSettingsByFile[.url(url)] = FileBuildSettings(compilerArguments: args) let text = """ #if FOO @@ -181,14 +181,14 @@ final class BuildSystemTests: XCTestCase { sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 12, text: text )), { (note: Notification) in // Syntactic analysis - no expected errors here. XCTAssertEqual(note.params.diagnostics.count, 0) - XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual(text, self.workspace.documentManager.latestSnapshot(.url(url))!.text) }, { (note: Notification) in // Semantic analysis - expect one error here. XCTAssertEqual(note.params.diagnostics.count, 1) @@ -196,7 +196,7 @@ final class BuildSystemTests: XCTestCase { // Modify the build settings and inform the delegate. // This should trigger a new publish diagnostics and we should no longer have errors. - buildSystem.buildSettingsByFile[url] = FileBuildSettings(compilerArguments: args + ["-DFOO"]) + buildSystem.buildSettingsByFile[.url(url)] = FileBuildSettings(compilerArguments: args + ["-DFOO"]) let expectation = XCTestExpectation(description: "refresh") expectation.expectedFulfillmentCount = 2 @@ -210,7 +210,7 @@ final class BuildSystemTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 0) expectation.fulfill() } - testServer.server?.fileBuildSettingsChanged([url]) + testServer.server?.fileBuildSettingsChanged([.url(url)]) let result = XCTWaiter.wait(for: [expectation], timeout: 5) if result != .completed { @@ -224,7 +224,7 @@ final class BuildSystemTests: XCTestCase { sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 12, text: """ @@ -232,18 +232,18 @@ final class BuildSystemTests: XCTestCase { """ )), { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(.url(url))!.text) }) // Modify the build settings and inform the SourceKitServer. // This shouldn't trigger new diagnostics since nothing actually changed (false alarm). - testServer.server?.fileBuildSettingsChanged([url]) + testServer.server?.fileBuildSettingsChanged([.url(url)]) let expectation = XCTestExpectation(description: "refresh doesn't occur") expectation.isInverted = true sk.handleNextNotification { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(.url(url))!.text) expectation.fulfill() } diff --git a/Tests/SourceKitTests/DocumentColorTests.swift b/Tests/SourceKitTests/DocumentColorTests.swift index 8ebdb831..c7772521 100644 --- a/Tests/SourceKitTests/DocumentColorTests.swift +++ b/Tests/SourceKitTests/DocumentColorTests.swift @@ -39,7 +39,7 @@ final class DocumentColorTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), trace: .off, @@ -52,7 +52,7 @@ final class DocumentColorTests: XCTestCase { let url = URL(fileURLWithPath: "/a.swift") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 12, text: text))) @@ -65,7 +65,7 @@ final class DocumentColorTests: XCTestCase { let url = URL(fileURLWithPath: "/a.swift") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 12, text: text))) diff --git a/Tests/SourceKitTests/DocumentSymbolTests.swift b/Tests/SourceKitTests/DocumentSymbolTests.swift index faaa23b8..6c8965cb 100644 --- a/Tests/SourceKitTests/DocumentSymbolTests.swift +++ b/Tests/SourceKitTests/DocumentSymbolTests.swift @@ -42,7 +42,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), trace: .off, @@ -55,7 +55,7 @@ func initialize(capabilities: DocumentSymbolCapabilities) { let url = URL(fileURLWithPath: "/a.swift") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 17, text: text diff --git a/Tests/SourceKitTests/ExecuteCommandTests.swift b/Tests/SourceKitTests/ExecuteCommandTests.swift index 8ba2b36e..38014c25 100644 --- a/Tests/SourceKitTests/ExecuteCommandTests.swift +++ b/Tests/SourceKitTests/ExecuteCommandTests.swift @@ -39,7 +39,7 @@ final class ExecuteCommandTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -79,10 +79,12 @@ final class ExecuteCommandTests: XCTestCase { } XCTAssertEqual(WorkspaceEdit(fromLSPDictionary: resultDict), WorkspaceEdit(changes: [ - loc.url: [TextEdit(range: Position(line: 1, utf16index: 29).. String {\n/*sr:extractStart*/var a = \"/*sr:string*/\"\n return a\n}\n\n"), - TextEdit(range: Position(line: 1, utf16index: 2).. String {\n/*sr:extractStart*/var a = \"/*sr:string*/\"\n return a\n}\n\n"), + TextEdit(range: Position(line: 1, utf16index: 2).. (SKTibsTestWorkspace, URL)? { + func initializeWorkspace(withCapabilities capabilities: FoldingRangeCapabilities, testLoc: String) throws -> (SKTibsTestWorkspace, DocumentURI)? { var documentCapabilities = TextDocumentClientCapabilities() documentCapabilities.foldingRange = capabilities let capabilities = ClientCapabilities(workspace: nil, textDocument: documentCapabilities) @@ -26,16 +26,16 @@ final class FoldingRangeTests: XCTestCase { clientCapabilities: capabilities) else { return nil } let loc = ws.testLoc(testLoc) try ws.openDocument(loc.url, language: .swift) - return (ws, loc.url) + return (ws, .url(loc.url)) } func testPartialLineFolding() throws { var capabilities = FoldingRangeCapabilities() capabilities.lineFoldingOnly = false - guard let (ws, url) = try initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } + guard let (ws, uri) = try initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } - let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) + let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(uri)) let ranges = try ws.sk.sendSync(request) XCTAssertEqual(ranges, [ @@ -59,9 +59,9 @@ final class FoldingRangeTests: XCTestCase { var capabilities = FoldingRangeCapabilities() capabilities.lineFoldingOnly = true - guard let (ws, url) = try initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } + guard let (ws, uri) = try initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } - let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) + let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(uri)) let ranges = try ws.sk.sendSync(request) XCTAssertEqual(ranges, [ diff --git a/Tests/SourceKitTests/LocalClangTests.swift b/Tests/SourceKitTests/LocalClangTests.swift index ccd667d7..eea3d828 100644 --- a/Tests/SourceKitTests/LocalClangTests.swift +++ b/Tests/SourceKitTests/LocalClangTests.swift @@ -41,7 +41,7 @@ final class LocalClangTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -60,7 +60,7 @@ final class LocalClangTests: XCTestCase { let url = URL(fileURLWithPath: "/a.cpp") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .cpp, version: 1, text: """ @@ -124,7 +124,7 @@ final class LocalClangTests: XCTestCase { let url = URL(fileURLWithPath: "/a.cpp") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .cpp, version: 1, text: """ diff --git a/Tests/SourceKitTests/LocalSwiftTests.swift b/Tests/SourceKitTests/LocalSwiftTests.swift index 1fcbca2f..3c423924 100644 --- a/Tests/SourceKitTests/LocalSwiftTests.swift +++ b/Tests/SourceKitTests/LocalSwiftTests.swift @@ -37,7 +37,7 @@ final class LocalSwiftTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -54,11 +54,12 @@ final class LocalSwiftTests: XCTestCase { func testEditing() { let url = URL(fileURLWithPath: "/a.swift") + let uri = DocumentURI.url(url) sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: uri, language: .swift, version: 12, text: """ @@ -67,7 +68,7 @@ final class LocalSwiftTests: XCTestCase { )), { (note: Notification) in log("Received diagnostics for open - syntactic") XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for open - semantic") XCTAssertEqual(note.params.diagnostics.count, 1) @@ -76,20 +77,20 @@ final class LocalSwiftTests: XCTestCase { Position(line: 0, utf16index: 4)) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(url, version: 13), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 13), contentChanges: [ .init(range: Range(Position(line: 0, utf16index: 4)), text: " foo() {}\n") ]), { (note: Notification) in log("Received diagnostics for edit 1 - syntactic") // 1 = remaining semantic error // 0 = semantic update finished already XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func foo() {}\n", self.workspace.documentManager.latestSnapshot(url)!.text) + XCTAssertEqual("func foo() {}\n", self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for edit 1 - semantic") XCTAssertEqual(note.params.diagnostics.count, 0) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(url, version: 14), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 14), contentChanges: [ .init(range: Range(Position(line: 1, utf16index: 0)), text: "_ = bar()") ]), { (note: Notification) in log("Received diagnostics for edit 2 - syntactic") @@ -99,7 +100,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(""" func foo() {} _ = bar() - """, self.workspace.documentManager.latestSnapshot(url)!.text) + """, self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for edit 2 - semantic") XCTAssertEqual(note.params.diagnostics.count, 1) @@ -108,7 +109,7 @@ final class LocalSwiftTests: XCTestCase { Position(line: 1, utf16index: 4)) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(url, version: 14), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 14), contentChanges: [ .init(range: Position(line: 1, utf16index: 4)..) in log("Received diagnostics for edit 3 - syntactic") @@ -118,13 +119,13 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(""" func foo() {} _ = foo() - """, self.workspace.documentManager.latestSnapshot(url)!.text) + """, self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for edit 3 - semantic") XCTAssertEqual(note.params.diagnostics.count, 0) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(url, version: 15), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 15), contentChanges: [ .init(range: Position(line: 1, utf16index: 4)..) in log("Received diagnostics for edit 4 - syntactic") @@ -134,7 +135,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(""" func foo() {} _ = fooTypo() - """, self.workspace.documentManager.latestSnapshot(url)!.text) + """, self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for edit 4 - semantic") XCTAssertEqual(note.params.diagnostics.count, 1) @@ -143,7 +144,7 @@ final class LocalSwiftTests: XCTestCase { Position(line: 1, utf16index: 4)) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(url, version: 16), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 16), contentChanges: [ .init(range: nil, text: """ func bar() {} _ = foo() @@ -155,7 +156,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(""" func bar() {} _ = foo() - """, self.workspace.documentManager.latestSnapshot(url)!.text) + """, self.workspace.documentManager.latestSnapshot(uri)!.text) }, { (note: Notification) in log("Received diagnostics for edit 5 - semantic") XCTAssertEqual(note.params.diagnostics.count, 1) @@ -165,14 +166,99 @@ final class LocalSwiftTests: XCTestCase { }) } - func testCrossFileDiagnostics() { - let urlA = URL(fileURLWithPath: "/a.swift") - let urlB = URL(fileURLWithPath: "/b.swift") + func testEditingNonURL() { + let uri = DocumentURI.other("urn:uuid:A1B08909-E791-469E-BF0F-F5790977E051") sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: urlA, language: .swift, version: 12, + uri: uri, + language: .swift, + version: 12, + text: """ + func + """ + )), { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func", self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 13), contentChanges: [ + .init(range: Range(Position(line: 0, utf16index: 4)), text: " foo() {}\n") + ]), { (note: Notification) in + log("Received diagnostics for edit 1 - syntactic") + // 1 = remaining semantic error + // 0 = semantic update finished already + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func foo() {}\n", self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 14), contentChanges: [ + .init(range: Range(Position(line: 1, utf16index: 0)), text: "_ = bar()") + ]), { (note: Notification) in + log("Received diagnostics for edit 2 - syntactic") + // 1 = semantic update finished already + // 0 = only syntactic + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual(""" + func foo() {} + _ = bar() + """, self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 14), contentChanges: [ + .init(range: Position(line: 1, utf16index: 4)..) in + log("Received diagnostics for edit 3 - syntactic") + // 1 = remaining semantic error + // 0 = semantic update finished already + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual(""" + func foo() {} + _ = foo() + """, self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 15), contentChanges: [ + .init(range: Position(line: 1, utf16index: 4)..) in + log("Received diagnostics for edit 4 - syntactic") + // 1 = semantic update finished already + // 0 = only syntactic + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual(""" + func foo() {} + _ = fooTypo() + """, self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uri, version: 16), contentChanges: [ + .init(range: nil, text: """ + func bar() {} + _ = foo() + """) + ]), { (note: Notification) in + log("Received diagnostics for edit 5 - syntactic") + // Could be remaining semantic error or new one. + XCTAssertEqual(note.params.diagnostics.count, 0) + XCTAssertEqual(""" + func bar() {} + _ = foo() + """, self.workspace.documentManager.latestSnapshot(uri)!.text) + }) + } + + func testCrossFileDiagnostics() { + let urlA = URL(fileURLWithPath: "/a.swift") + let urlB = URL(fileURLWithPath: "/b.swift") + let uriA = DocumentURI.url(urlA) + let uriB = DocumentURI.url(urlB) + + sk.allowUnexpectedNotification = false + + sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( + uri: uriA, language: .swift, version: 12, text: """ _ = foo() """ @@ -190,7 +276,7 @@ final class LocalSwiftTests: XCTestCase { }) sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: urlB, language: .swift, version: 12, + uri: uriB, language: .swift, version: 12, text: """ _ = bar() """ @@ -207,7 +293,7 @@ final class LocalSwiftTests: XCTestCase { Position(line: 0, utf16index: 4)) }) - sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(urlA, version: 13), contentChanges: [ + sk.sendNoteSync(DidChangeTextDocument(textDocument: .init(uriA, version: 13), contentChanges: [ .init(range: nil, text: "_ = foo()\n") ]), { (note: Notification) in log("Received diagnostics for edit 1 - syntactic") @@ -220,10 +306,11 @@ final class LocalSwiftTests: XCTestCase { func testDiagnosticsReopen() { let urlA = URL(fileURLWithPath: "/a.swift") + let uriA = DocumentURI.url(urlA) sk.allowUnexpectedNotification = false sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: urlA, language: .swift, version: 12, + uri: uriA, language: .swift, version: 12, text: """ _ = foo() """ @@ -243,7 +330,7 @@ final class LocalSwiftTests: XCTestCase { sk.send(DidCloseTextDocument(textDocument: .init(urlA))) sk.sendNoteSync(DidOpenTextDocument(textDocument: TextDocumentItem( - url: urlA, language: .swift, version: 13, + uri: uriA, language: .swift, version: 13, text: """ var """ @@ -518,9 +605,10 @@ final class LocalSwiftTests: XCTestCase { func testSymbolInfo() { let url = URL(fileURLWithPath: "/a.swift") + let uri = DocumentURI.url(url) sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: uri, language: .swift, version: 1, text: """ @@ -541,7 +629,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(sym.name, "S") XCTAssertNil(sym.containerName) XCTAssertEqual(sym.usr, "s:1a1SV") - XCTAssertEqual(sym.bestLocalDeclaration?.url, url) + XCTAssertEqual(sym.bestLocalDeclaration?.uri, uri) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.line, 0) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.utf16index, 7) } @@ -557,7 +645,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(sym.name, "foo()") XCTAssertNil(sym.containerName) XCTAssertEqual(sym.usr, "s:1a1SV3fooyyF") - XCTAssertEqual(sym.bestLocalDeclaration?.url, url) + XCTAssertEqual(sym.bestLocalDeclaration?.uri, uri) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.line, 1) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.utf16index, 7) } @@ -573,7 +661,7 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(sym.name, "local") XCTAssertNil(sym.containerName) XCTAssertEqual(sym.usr, "s:1a1SV3fooyyF5localL_Sivp") - XCTAssertEqual(sym.bestLocalDeclaration?.url, url) + XCTAssertEqual(sym.bestLocalDeclaration?.uri, uri) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.line, 2) XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.utf16index, 8) } @@ -590,9 +678,10 @@ final class LocalSwiftTests: XCTestCase { func testHover() { let url = URL(fileURLWithPath: "/a.swift") + let uri = DocumentURI.url(url) sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: uri, language: .swift, version: 1, text: """ @@ -639,7 +728,7 @@ final class LocalSwiftTests: XCTestCase { let url = URL(fileURLWithPath: "/a.swift") sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 1, text: """ @@ -692,9 +781,10 @@ final class LocalSwiftTests: XCTestCase { func testDocumentSymbolHighlight() { let url = URL(fileURLWithPath: "/a.swift") + let uri = DocumentURI.url(url) sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: uri, language: .swift, version: 1, text: """ diff --git a/Tests/SourceKitTests/SourceKitTests.swift b/Tests/SourceKitTests/SourceKitTests.swift index a9277bb5..be0db9a2 100644 --- a/Tests/SourceKitTests/SourceKitTests.swift +++ b/Tests/SourceKitTests/SourceKitTests.swift @@ -26,7 +26,7 @@ final class SKTests: XCTestCase { let initResult = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -44,7 +44,7 @@ final class SKTests: XCTestCase { let initResult = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: nil), trace: .off, @@ -71,7 +71,7 @@ final class SKTests: XCTestCase { position: locRef.position)) XCTAssertEqual(jump.count, 1) - XCTAssertEqual(jump.first?.url, locDef.url) + XCTAssertEqual(jump.first?.uri, .url(locDef.url)) XCTAssertEqual(jump.first?.range.lowerBound, locDef.position) // MARK: Find references diff --git a/Tests/SourceKitTests/SwiftCompletionTests.swift b/Tests/SourceKitTests/SwiftCompletionTests.swift index 388fa3b8..e29fda46 100644 --- a/Tests/SourceKitTests/SwiftCompletionTests.swift +++ b/Tests/SourceKitTests/SwiftCompletionTests.swift @@ -67,7 +67,7 @@ final class SwiftCompletionTests: XCTestCase { _ = try! sk.sendSync(InitializeRequest( processId: nil, rootPath: nil, - rootURL: nil, + rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), trace: .off, @@ -78,7 +78,7 @@ final class SwiftCompletionTests: XCTestCase { func openDocument(text: String? = nil, url: URL) { sk.send(DidOpenTextDocument(textDocument: TextDocumentItem( - url: url, + uri: .url(url), language: .swift, version: 12, text: text ?? self.text)))