diff --git a/.swift-format b/.swift-format new file mode 100644 index 000000000..22b48e090 --- /dev/null +++ b/.swift-format @@ -0,0 +1,17 @@ +{ + "version": 1, + "lineLength": 120, + "indentation": { + "spaces": 2 + }, + "lineBreakBeforeEachArgument": true, + "indentConditionalCompilationBlocks": false, + "rules": { + "AlwaysUseLowerCamelCase": false, + "AmbiguousTrailingClosureOverload": false, + "NoBlockComments": false, + "OrderedImports": true, + "UseLetInEveryBoundCaseVariable": false, + "UseSynthesizedInitializer": false + } +} diff --git a/Package.swift b/Package.swift index 1b62c6b53..40b6e19e7 100644 --- a/Package.swift +++ b/Package.swift @@ -1,241 +1,253 @@ // swift-tools-version:5.7 -import PackageDescription import Foundation +import PackageDescription // When building the toolchain on the CI, don't add the CI's runpath for the // final build before installing. -let sourcekitLSPLinkSettings : [LinkerSetting] +let sourcekitLSPLinkSettings: [LinkerSetting] if ProcessInfo.processInfo.environment["SOURCEKIT_LSP_CI_INSTALL"] != nil { - sourcekitLSPLinkSettings = [ .unsafeFlags(["-no-toolchain-stdlib-rpath"], .when(platforms: [.linux, .android])) ] + sourcekitLSPLinkSettings = [.unsafeFlags(["-no-toolchain-stdlib-rpath"], .when(platforms: [.linux, .android]))] } else { sourcekitLSPLinkSettings = [] } let package = Package( - name: "SourceKitLSP", - platforms: [.macOS("12.0")], - products: [ - .executable( - name: "sourcekit-lsp", - targets: ["sourcekit-lsp"] - ), - .library( - name: "_SourceKitLSP", - targets: ["SourceKitLSP"] - ), - .library( - name: "LSPBindings", - targets: [ - "LanguageServerProtocol", - "LanguageServerProtocolJSONRPC", - ] - ) - ], - dependencies: [ - // See 'Dependencies' below. - ], - targets: [ - .executableTarget( - name: "sourcekit-lsp", - dependencies: [ - "LanguageServerProtocolJSONRPC", - "SourceKitLSP", - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - exclude: ["CMakeLists.txt"], - linkerSettings: sourcekitLSPLinkSettings), + name: "SourceKitLSP", + platforms: [.macOS("12.0")], + products: [ + .executable( + name: "sourcekit-lsp", + targets: ["sourcekit-lsp"] + ), + .library( + name: "_SourceKitLSP", + targets: ["SourceKitLSP"] + ), + .library( + name: "LSPBindings", + targets: [ + "LanguageServerProtocol", + "LanguageServerProtocolJSONRPC", + ] + ), + ], + dependencies: [ + // See 'Dependencies' below. + ], + targets: [ + .executableTarget( + name: "sourcekit-lsp", + dependencies: [ + "LanguageServerProtocolJSONRPC", + "SourceKitLSP", + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + ], + exclude: ["CMakeLists.txt"], + linkerSettings: sourcekitLSPLinkSettings + ), - .target( - name: "SourceKitLSP", - dependencies: [ - "BuildServerProtocol", - .product(name: "IndexStoreDB", package: "indexstore-db"), - "LanguageServerProtocol", - "LanguageServerProtocolJSONRPC", - "SKCore", - "SourceKitD", - "SKSwiftPMWorkspace", - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - .product(name: "SwiftSyntax", package: "swift-syntax"), - .product(name: "SwiftParser", package: "swift-syntax"), - .product(name: "SwiftIDEUtils", package: "swift-syntax"), - ], - exclude: ["CMakeLists.txt"]), + .target( + name: "SourceKitLSP", + dependencies: [ + "BuildServerProtocol", + .product(name: "IndexStoreDB", package: "indexstore-db"), + "LanguageServerProtocol", + "LanguageServerProtocolJSONRPC", + "SKCore", + "SourceKitD", + "SKSwiftPMWorkspace", + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftParser", package: "swift-syntax"), + .product(name: "SwiftIDEUtils", package: "swift-syntax"), + ], + exclude: ["CMakeLists.txt"] + ), - .target( - name: "CSKTestSupport", - dependencies: []), - .target( - name: "SKTestSupport", - dependencies: [ - "CSKTestSupport", - "LSPTestSupport", - "SourceKitLSP", - .product(name: "ISDBTestSupport", package: "indexstore-db"), - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - resources: [ - .copy("INPUTS"), - ] - ), - .testTarget( - name: "SourceKitLSPTests", - dependencies: [ - "SKTestSupport", - "SourceKitLSP", - ] - ), + .target( + name: "CSKTestSupport", + dependencies: [] + ), + .target( + name: "SKTestSupport", + dependencies: [ + "CSKTestSupport", + "LSPTestSupport", + "SourceKitLSP", + .product(name: "ISDBTestSupport", package: "indexstore-db"), + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + ], + resources: [ + .copy("INPUTS") + ] + ), + .testTarget( + name: "SourceKitLSPTests", + dependencies: [ + "SKTestSupport", + "SourceKitLSP", + ] + ), - .target( - name: "SKSwiftPMWorkspace", - dependencies: [ - "BuildServerProtocol", - "LanguageServerProtocol", - "SKCore", - .product(name: "SwiftPM-auto", package: "swift-package-manager") - ], - exclude: ["CMakeLists.txt"]), + .target( + name: "SKSwiftPMWorkspace", + dependencies: [ + "BuildServerProtocol", + "LanguageServerProtocol", + "SKCore", + .product(name: "SwiftPM-auto", package: "swift-package-manager"), + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "SKSwiftPMWorkspaceTests", - dependencies: [ - "SKSwiftPMWorkspace", - "SKTestSupport", - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ] - ), + .testTarget( + name: "SKSwiftPMWorkspaceTests", + dependencies: [ + "SKSwiftPMWorkspace", + "SKTestSupport", + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + ] + ), - // SKCore: Data structures and algorithms useful across the project, but not necessarily - // suitable for use in other packages. - .target( - name: "SKCore", - dependencies: [ - "SourceKitD", - "BuildServerProtocol", - "LanguageServerProtocol", - "LanguageServerProtocolJSONRPC", - "SKSupport", - .product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"), - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - exclude: ["CMakeLists.txt"]), + // SKCore: Data structures and algorithms useful across the project, but not necessarily + // suitable for use in other packages. + .target( + name: "SKCore", + dependencies: [ + "SourceKitD", + "BuildServerProtocol", + "LanguageServerProtocol", + "LanguageServerProtocolJSONRPC", + "SKSupport", + .product(name: "SwiftPMDataModel-auto", package: "swift-package-manager"), + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "SKCoreTests", - dependencies: [ - "SKCore", - "SKTestSupport", - ] - ), + .testTarget( + name: "SKCoreTests", + dependencies: [ + "SKCore", + "SKTestSupport", + ] + ), - // SourceKitD: Swift bindings for sourcekitd. - .target( - name: "SourceKitD", - dependencies: [ - "Csourcekitd", - "LSPLogging", - "SKSupport", - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - exclude: ["CMakeLists.txt"]), + // SourceKitD: Swift bindings for sourcekitd. + .target( + name: "SourceKitD", + dependencies: [ + "Csourcekitd", + "LSPLogging", + "SKSupport", + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "SourceKitDTests", - dependencies: [ - "SourceKitD", - "SKCore", - "SKTestSupport", - ] - ), + .testTarget( + name: "SourceKitDTests", + dependencies: [ + "SourceKitD", + "SKCore", + "SKTestSupport", + ] + ), - // Csourcekitd: C modules wrapper for sourcekitd. - .target( - name: "Csourcekitd", - dependencies: [], - exclude: ["CMakeLists.txt"]), + // Csourcekitd: C modules wrapper for sourcekitd. + .target( + name: "Csourcekitd", + dependencies: [], + exclude: ["CMakeLists.txt"] + ), - // Logging support used in LSP modules. - .target( - name: "LSPLogging", - dependencies: [], - exclude: ["CMakeLists.txt"]), + // Logging support used in LSP modules. + .target( + name: "LSPLogging", + dependencies: [], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "LSPLoggingTests", - dependencies: [ - "LSPLogging", - ] - ), + .testTarget( + name: "LSPLoggingTests", + dependencies: [ + "LSPLogging" + ] + ), - .target( - name: "LSPTestSupport", - dependencies: [ - "LanguageServerProtocol", - "LanguageServerProtocolJSONRPC" - ] - ), + .target( + name: "LSPTestSupport", + dependencies: [ + "LanguageServerProtocol", + "LanguageServerProtocolJSONRPC", + ] + ), - // jsonrpc: LSP connection using jsonrpc over pipes. - .target( - name: "LanguageServerProtocolJSONRPC", - dependencies: [ - "LanguageServerProtocol", - "LSPLogging", - ], - exclude: ["CMakeLists.txt"]), + // jsonrpc: LSP connection using jsonrpc over pipes. + .target( + name: "LanguageServerProtocolJSONRPC", + dependencies: [ + "LanguageServerProtocol", + "LSPLogging", + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "LanguageServerProtocolJSONRPCTests", - dependencies: [ - "LanguageServerProtocolJSONRPC", - "LSPTestSupport" - ] - ), + .testTarget( + name: "LanguageServerProtocolJSONRPCTests", + dependencies: [ + "LanguageServerProtocolJSONRPC", + "LSPTestSupport", + ] + ), - // LanguageServerProtocol: The core LSP types, suitable for any LSP implementation. - .target( - name: "LanguageServerProtocol", - dependencies: [ - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - exclude: ["CMakeLists.txt"]), + // LanguageServerProtocol: The core LSP types, suitable for any LSP implementation. + .target( + name: "LanguageServerProtocol", + dependencies: [ + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core") + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "LanguageServerProtocolTests", - dependencies: [ - "LanguageServerProtocol", - "LSPTestSupport", - ] - ), + .testTarget( + name: "LanguageServerProtocolTests", + dependencies: [ + "LanguageServerProtocol", + "LSPTestSupport", + ] + ), - // BuildServerProtocol: connection between build server and language server to provide build and index info - .target( - name: "BuildServerProtocol", - dependencies: [ - "LanguageServerProtocol" - ], - exclude: ["CMakeLists.txt"]), + // BuildServerProtocol: connection between build server and language server to provide build and index info + .target( + name: "BuildServerProtocol", + dependencies: [ + "LanguageServerProtocol" + ], + exclude: ["CMakeLists.txt"] + ), - // SKSupport: Data structures, algorithms and platform-abstraction code that might be generally - // useful to any Swift package. Similar in spirit to SwiftPM's Basic module. - .target( - name: "SKSupport", - dependencies: [ - .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core"), - ], - exclude: ["CMakeLists.txt"]), + // SKSupport: Data structures, algorithms and platform-abstraction code that might be generally + // useful to any Swift package. Similar in spirit to SwiftPM's Basic module. + .target( + name: "SKSupport", + dependencies: [ + .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core") + ], + exclude: ["CMakeLists.txt"] + ), - .testTarget( - name: "SKSupportTests", - dependencies: [ - "LSPTestSupport", - "SKSupport", - "SKTestSupport", - ] - ), - ] + .testTarget( + name: "SKSupportTests", + dependencies: [ + "LSPTestSupport", + "SKSupport", + "SKTestSupport", + ] + ), + ] ) // MARK: Dependencies @@ -261,6 +273,6 @@ if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] == nil { .package(name: "swift-package-manager", path: "../swiftpm"), .package(path: "../swift-tools-support-core"), .package(path: "../swift-argument-parser"), - .package(path: "../swift-syntax") + .package(path: "../swift-syntax"), ] } diff --git a/Sources/BuildServerProtocol/BuildTargets.swift b/Sources/BuildServerProtocol/BuildTargets.swift index 33a353bcb..c13af740a 100644 --- a/Sources/BuildServerProtocol/BuildTargets.swift +++ b/Sources/BuildServerProtocol/BuildTargets.swift @@ -62,13 +62,15 @@ public struct BuildTarget: Codable, Hashable { /// The direct upstream build target dependencies of this build target public var dependencies: [BuildTargetIdentifier] - public init(id: BuildTargetIdentifier, - displayName: String?, - baseDirectory: URI?, - tags: [BuildTargetTag], - capabilities: BuildTargetCapabilities, - languageIds: [Language], - dependencies: [BuildTargetIdentifier]) { + public init( + id: BuildTargetIdentifier, + displayName: String?, + baseDirectory: URI?, + tags: [BuildTargetTag], + capabilities: BuildTargetCapabilities, + languageIds: [Language], + dependencies: [BuildTargetIdentifier] + ) { self.id = id self.displayName = displayName self.baseDirectory = baseDirectory diff --git a/Sources/BuildServerProtocol/InitializeBuild.swift b/Sources/BuildServerProtocol/InitializeBuild.swift index f4e16297b..de2839ea6 100644 --- a/Sources/BuildServerProtocol/InitializeBuild.swift +++ b/Sources/BuildServerProtocol/InitializeBuild.swift @@ -44,7 +44,13 @@ public struct InitializeBuild: RequestType, Hashable { /// The capabilities of the client public var capabilities: BuildClientCapabilities - public init(displayName: String, version: String, bspVersion: String, rootUri: URI, capabilities: BuildClientCapabilities) { + public init( + displayName: String, + version: String, + bspVersion: String, + rootUri: URI, + capabilities: BuildClientCapabilities + ) { self.displayName = displayName self.version = version self.bspVersion = bspVersion @@ -66,22 +72,28 @@ public struct BuildClientCapabilities: Codable, Hashable { } public struct InitializeBuildResult: ResponseType, Hashable { - /// Name of the server + /// Name of the server public var displayName: String - /// The version of the server + /// The version of the server public var version: String - /// The BSP version that the server speaks + /// The BSP version that the server speaks public var bspVersion: String - /// The capabilities of the build server + /// The capabilities of the build server public var capabilities: BuildServerCapabilities /// Optional metadata about the server public var data: LSPAny? - public init(displayName: String, version: String, bspVersion: String, capabilities: BuildServerCapabilities, data: LSPAny? = nil) { + public init( + displayName: String, + version: String, + bspVersion: String, + capabilities: BuildServerCapabilities, + data: LSPAny? = nil + ) { self.displayName = displayName self.version = version self.bspVersion = bspVersion diff --git a/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift b/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift index 8fdc44dd0..2ac63b161 100644 --- a/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift +++ b/Sources/BuildServerProtocol/RegisterForChangeNotifications.swift @@ -11,7 +11,6 @@ //===----------------------------------------------------------------------===// import LanguageServerProtocol - /// The register for changes request is sent from the language /// server to the build server to register or unregister for /// changes in file options or dependencies. On changes a diff --git a/Sources/LSPLogging/LogLevel.swift b/Sources/LSPLogging/LogLevel.swift index 32f0303f7..84971d745 100644 --- a/Sources/LSPLogging/LogLevel.swift +++ b/Sources/LSPLogging/LogLevel.swift @@ -65,7 +65,7 @@ extension LogLevel { if let level = LogLevel(rawValue: value) { self = level - } else if value > LogLevel.debug.rawValue { + } else if value > LogLevel.debug.rawValue { self = .debug } else { return nil diff --git a/Sources/LSPLogging/Logging.swift b/Sources/LSPLogging/Logging.swift index 7b2643fcd..0ba7473a9 100644 --- a/Sources/LSPLogging/Logging.swift +++ b/Sources/LSPLogging/Logging.swift @@ -13,7 +13,7 @@ import Foundation #if canImport(os) -import os // os_log +import os // os_log #endif /// Log the given message. @@ -52,8 +52,8 @@ public func orLog( _ prefix: String = "", level: LogLevel = .default, logger: Logger = Logger.shared, - _ block: () throws -> R?) -> R? -{ + _ block: () throws -> R? +) -> R? { do { return try block() } catch { @@ -67,8 +67,8 @@ public func orLog( _ prefix: String = "", level: LogLevel = .default, logger: Logger = Logger.shared, - _ block: () async throws -> R?) async -> R? -{ + _ block: () async throws -> R? +) async -> R? { do { return try await block() } catch { @@ -77,7 +77,6 @@ public func orLog( } } - /// Logs the time that the given block takes to execute in milliseconds. public func logExecutionTime( _ prefix: String = #function, @@ -197,7 +196,7 @@ public final class Logger { let currentLevel = self.currentLevel var usedOSLog = false -#if canImport(os) + #if canImport(os) if !disableOSLog { // If os_log is available, we call it unconditionally since it has its own log-level handling that we respect. if #available(macOS 11.0, *) { @@ -207,7 +206,7 @@ public final class Logger { } usedOSLog = true } -#endif + #endif if level > currentLevel { return diff --git a/Sources/LSPTestSupport/Assertions.swift b/Sources/LSPTestSupport/Assertions.swift index 9e2ab4d4a..26d80e9f5 100644 --- a/Sources/LSPTestSupport/Assertions.swift +++ b/Sources/LSPTestSupport/Assertions.swift @@ -82,11 +82,14 @@ extension XCTestCase { var expecatations: [XCTestExpectation] var description: String { - return "One of the expectation was not fulfilled within timeout: \(expecatations.map(\.description).joined(separator: ", "))" + return """ + One of the expectation was not fulfilled within timeout: \ + \(expecatations.map(\.description).joined(separator: ", ")) + """ } } - /// Wait for the given expectations to be fulfilled. If the expectations aren't + /// Wait for the given expectations to be fulfilled. If the expectations aren't /// fulfilled within `timeout`, throw an error, aborting the test execution. public func fulfillmentOfOrThrow( _ expectations: [XCTestExpectation], diff --git a/Sources/LSPTestSupport/CheckCoding.swift b/Sources/LSPTestSupport/CheckCoding.swift index e9d7dc3ad..34454107a 100644 --- a/Sources/LSPTestSupport/CheckCoding.swift +++ b/Sources/LSPTestSupport/CheckCoding.swift @@ -17,11 +17,16 @@ import XCTest /// /// - parameter value: The value to encode/decode. /// - parameter json: The expected json encoding. -public func checkCoding(_ value: T, json: String, file: StaticString = #filePath, line: UInt = #line) where T: Codable & Equatable { +public func checkCoding( + _ value: T, + json: String, + file: StaticString = #filePath, + line: UInt = #line +) { let encoder = JSONEncoder() encoder.outputFormatting.insert(.prettyPrinted) encoder.outputFormatting.insert(.sortedKeys) - + let data = try! encoder.encode(WrapFragment(value: value)) let wrappedStr = String(data: data, encoding: .utf8)! @@ -52,7 +57,12 @@ private struct WrapFragment: Equatable, Codable where T: Equatable & Codable /// /// - parameter value: The value to encode/decode. /// - parameter json: The expected json encoding. -public func checkDecoding(json: String, expected value: T, file: StaticString = #filePath, line: UInt = #line) where T: Codable & Equatable { +public func checkDecoding( + json: String, + expected value: T, + file: StaticString = #filePath, + line: UInt = #line +) { let wrappedStr = "{\"value\":\(json)}" let data = wrappedStr.data(using: .utf8)! @@ -62,7 +72,14 @@ public func checkDecoding(json: String, expected value: T, file: StaticString XCTAssertEqual(value, decodedValue, file: file, line: line) } -public func checkCoding(_ value: T, json: String, userInfo: [CodingUserInfoKey: Any] = [:], file: StaticString = #filePath, line: UInt = #line, body: (T) -> Void) where T: Codable { +public func checkCoding( + _ value: T, + json: String, + userInfo: [CodingUserInfoKey: Any] = [:], + file: StaticString = #filePath, + line: UInt = #line, + body: (T) -> Void +) where T: Codable { let encoder = JSONEncoder() encoder.outputFormatting.insert(.prettyPrinted) encoder.outputFormatting.insert(.sortedKeys) @@ -71,7 +88,7 @@ public func checkCoding(_ value: T, json: String, userInfo: [CodingUserInfoKe // Remove trailing whitespace to normalize between corelibs and Apple Foundation. .trimmingTrailingWhitespace() XCTAssertEqual(json, str, file: file, line: line) - + let decoder = JSONDecoder() decoder.userInfo = userInfo let decodedValue = try! decoder.decode(T.self, from: data) diff --git a/Sources/LSPTestSupport/PerfTestCase.swift b/Sources/LSPTestSupport/PerfTestCase.swift index ac3c5f646..1b696898f 100644 --- a/Sources/LSPTestSupport/PerfTestCase.swift +++ b/Sources/LSPTestSupport/PerfTestCase.swift @@ -21,34 +21,34 @@ import XCTest /// continuous integration. open class PerfTestCase: XCTestCase { -#if !ENABLE_PERF_TESTS + #if !ENABLE_PERF_TESTS #if os(macOS) - open override func startMeasuring() {} - open override func stopMeasuring() {} - open override func measureMetrics( - _: [XCTPerformanceMetric], - automaticallyStartMeasuring: Bool, - for block: () -> Void) - { - block() - } + open override func startMeasuring() {} + open override func stopMeasuring() {} + open override func measureMetrics( + _: [XCTPerformanceMetric], + automaticallyStartMeasuring: Bool, + for block: () -> Void + ) { + block() + } #else - // In corelibs-xctest, these methods are public, not open, so we can only - // shadow them. - public func startMeasuring() {} - public func stopMeasuring() {} - public func measureMetrics( - _: [XCTPerformanceMetric], - automaticallyStartMeasuring: Bool, - for block: () -> Void) - { - block() - } - public func measure(block: () -> Void) { - block() - } + // In corelibs-xctest, these methods are public, not open, so we can only + // shadow them. + public func startMeasuring() {} + public func stopMeasuring() {} + public func measureMetrics( + _: [XCTPerformanceMetric], + automaticallyStartMeasuring: Bool, + for block: () -> Void + ) { + block() + } + public func measure(block: () -> Void) { + block() + } + #endif #endif -#endif } diff --git a/Sources/LSPTestSupport/String+TrimTrailingWhitespace.swift b/Sources/LSPTestSupport/String+TrimTrailingWhitespace.swift index 6a1628351..4e4784b17 100644 --- a/Sources/LSPTestSupport/String+TrimTrailingWhitespace.swift +++ b/Sources/LSPTestSupport/String+TrimTrailingWhitespace.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// - public extension String { // This implementation is really slow; to use it outside a test it should be optimized. func trimmingTrailingWhitespace() -> String { diff --git a/Sources/LSPTestSupport/TestJSONRPCConnection.swift b/Sources/LSPTestSupport/TestJSONRPCConnection.swift index fc86eb436..e6de86a3e 100644 --- a/Sources/LSPTestSupport/TestJSONRPCConnection.swift +++ b/Sources/LSPTestSupport/TestJSONRPCConnection.swift @@ -12,9 +12,10 @@ import LanguageServerProtocol import LanguageServerProtocolJSONRPC -import class Foundation.Pipe import XCTest +import class Foundation.Pipe + // Workaround ambiguity with Foundation. public typealias Notification = LanguageServerProtocol.Notification @@ -135,7 +136,12 @@ public final class TestClient: MessageHandler { handler(notification) } - public func handle(_ params: R, id: RequestID, from clientID: ObjectIdentifier, reply: @escaping (LSPResult) -> Void) { + public func handle( + _ params: R, + id: RequestID, + from clientID: ObjectIdentifier, + reply: @escaping (LSPResult) -> Void + ) { let cancellationToken = CancellationToken() let request = Request(params, id: id, clientID: clientID, cancellation: cancellationToken, reply: reply) @@ -156,14 +162,19 @@ extension TestClient: Connection { } /// Send a request to the language server and (asynchronously) receive a reply. - public func send(_ request: Request, reply: @escaping (LSPResult) -> Void) -> RequestID where Request: RequestType { + public func send( + _ request: Request, + reply: @escaping (LSPResult) -> Void + ) -> RequestID { return server.send(request, reply: reply) } - /// Send a notification and expect a notification in reply synchronously. /// For testing notifications that behave like requests - e.g. didChange & publishDiagnostics. - public func sendNoteSync(_ notification: NSend, _ handler: @escaping (Notification) -> Void) where NSend: NotificationType { + public func sendNoteSync( + _ notification: some NotificationType, + _ handler: @escaping (Notification) -> Void + ) { let expectation = XCTestExpectation(description: "sendNoteSync - note received") @@ -227,18 +238,35 @@ public final class TestServer: MessageHandler { } } - public func handle(_ params: R, id: RequestID, from clientID: ObjectIdentifier, reply: @escaping (LSPResult) -> Void) { + public func handle( + _ params: R, + id: RequestID, + from clientID: ObjectIdentifier, + reply: @escaping (LSPResult) -> Void + ) { let cancellationToken = CancellationToken() if let params = params as? EchoRequest { - let req = Request(params, id: id, clientID: clientID, cancellation: cancellationToken, reply: { result in - reply(result.map({ $0 as! R.Response })) - }) + let req = Request( + params, + id: id, + clientID: clientID, + cancellation: cancellationToken, + reply: { result in + reply(result.map({ $0 as! R.Response })) + } + ) req.reply(req.params.string) } else if let params = params as? EchoError { - let req = Request(params, id: id, clientID: clientID, cancellation: cancellationToken, reply: { result in - reply(result.map({ $0 as! R.Response })) - }) + let req = Request( + params, + id: id, + clientID: clientID, + cancellation: cancellationToken, + reply: { result in + reply(result.map({ $0 as! R.Response })) + } + ) if let code = req.params.code { req.reply(.failure(ResponseError(code: code, message: req.params.message!))) } else { @@ -254,7 +282,8 @@ public final class TestServer: MessageHandler { private let testMessageRegistry = MessageRegistry( requests: [EchoRequest.self, EchoError.self], - notifications: [EchoNotification.self]) + notifications: [EchoNotification.self] +) extension String: ResponseType {} diff --git a/Sources/LSPTestSupport/Timeouts.swift b/Sources/LSPTestSupport/Timeouts.swift index db457fb5c..b3f4538f6 100644 --- a/Sources/LSPTestSupport/Timeouts.swift +++ b/Sources/LSPTestSupport/Timeouts.swift @@ -13,5 +13,5 @@ import Foundation /// The default duration how long tests should wait for responses from -/// SourceKit-LSP / sourcekitd / clangd. +/// SourceKit-LSP / sourcekitd / clangd. public let defaultTimeout: TimeInterval = 60 diff --git a/Sources/LanguageServerProtocol/AsyncQueue.swift b/Sources/LanguageServerProtocol/AsyncQueue.swift index 5a54fc28c..0537d9a3c 100644 --- a/Sources/LanguageServerProtocol/AsyncQueue.swift +++ b/Sources/LanguageServerProtocol/AsyncQueue.swift @@ -124,7 +124,6 @@ public final class AsyncQueue { dependencies = [pendingTasks.last].compactMap { $0 } } - // Schedule the task. let task = Task { // IMPORTANT: The only throwing call in here must be the call to diff --git a/Sources/LanguageServerProtocol/Connection.swift b/Sources/LanguageServerProtocol/Connection.swift index 6580d6710..8552cc5f7 100644 --- a/Sources/LanguageServerProtocol/Connection.swift +++ b/Sources/LanguageServerProtocol/Connection.swift @@ -19,7 +19,10 @@ public protocol Connection: AnyObject { func send(_: Notification) where Notification: NotificationType /// Send a request and (asynchronously) receive a reply. - func send(_: Request, reply: @escaping (LSPResult) -> Void) -> RequestID where Request: RequestType + func send( + _: Request, + reply: @escaping (LSPResult) -> Void + ) -> RequestID /// Send a request synchronously. **Use wisely**. func sendSync(_: Request) throws -> Request.Response where Request: RequestType @@ -54,7 +57,12 @@ public protocol MessageHandler: AnyObject { /// handled to avoid out-of-order requests, e.g. once the corresponding /// request has been sent to sourcekitd. The actual semantic computation /// should occur after the method returns and report the result via `reply`. - func handle(_ params: Request, id: RequestID, from clientID: ObjectIdentifier, reply: @escaping (LSPResult) -> Void) + func handle( + _ params: Request, + id: RequestID, + from clientID: ObjectIdentifier, + reply: @escaping (LSPResult) -> Void + ) } /// A connection between two message handlers in the same process. @@ -77,7 +85,7 @@ public final class LocalConnection { /// The queue guarding `_nextRequestID`. let queue: DispatchQueue = DispatchQueue(label: "local-connection-queue") - + var _nextRequestID: Int = 0 var state: State = .ready diff --git a/Sources/LanguageServerProtocol/CustomCodable.swift b/Sources/LanguageServerProtocol/CustomCodable.swift index a0ca4e23e..71ca06188 100644 --- a/Sources/LanguageServerProtocol/CustomCodable.swift +++ b/Sources/LanguageServerProtocol/CustomCodable.swift @@ -103,7 +103,11 @@ extension KeyedEncodingContainer { _ value: CustomCodable>, forKey key: Key ) throws { - try encodeIfPresent(value.wrappedValue.map { - type(of: value).CustomCoder(wrappedValue: $0) }, forKey: key) + try encodeIfPresent( + value.wrappedValue.map { + type(of: value).CustomCoder(wrappedValue: $0) + }, + forKey: key + ) } } diff --git a/Sources/LanguageServerProtocol/Error.swift b/Sources/LanguageServerProtocol/Error.swift index 90f481f16..c2de5b598 100644 --- a/Sources/LanguageServerProtocol/Error.swift +++ b/Sources/LanguageServerProtocol/Error.swift @@ -29,7 +29,6 @@ public struct ErrorCode: RawRepresentable, Codable, Hashable { public static let invalidParams: ErrorCode = ErrorCode(rawValue: -32602) public static let internalError: ErrorCode = ErrorCode(rawValue: -32603) - /// This is the start range of JSON-RPC reserved error codes. /// It doesn't denote a real error code. No LSP error codes should /// be defined between the start and end range. For backwards @@ -103,7 +102,10 @@ extension ResponseError { public static var cancelled: ResponseError = ResponseError(code: .cancelled, message: "request cancelled by client") - public static var serverCancelled: ResponseError = ResponseError(code: .serverCancelled, message: "request cancelled by server") + public static var serverCancelled: ResponseError = ResponseError( + code: .serverCancelled, + message: "request cancelled by server" + ) public static func workspaceNotOpen(_ uri: DocumentURI) -> ResponseError { return ResponseError(code: .workspaceNotOpen, message: "No workspace containing '\(uri)' found") @@ -149,19 +151,40 @@ public struct MessageDecodingError: Error, Hashable { } extension MessageDecodingError { - public static func methodNotFound(_ method: String, id: RequestID? = nil, messageKind: MessageKind = .unknown) -> MessageDecodingError { - return MessageDecodingError(code: .methodNotFound, message: "method not found: \(method)", id: id, messageKind: messageKind) + public static func methodNotFound( + _ method: String, + id: RequestID? = nil, + messageKind: MessageKind = .unknown + ) -> MessageDecodingError { + return MessageDecodingError( + code: .methodNotFound, + message: "method not found: \(method)", + id: id, + messageKind: messageKind + ) } - public static func invalidRequest(_ reason: String, id: RequestID? = nil, messageKind: MessageKind = .unknown) -> MessageDecodingError { + public static func invalidRequest( + _ reason: String, + id: RequestID? = nil, + messageKind: MessageKind = .unknown + ) -> MessageDecodingError { return MessageDecodingError(code: .invalidRequest, message: reason, id: id, messageKind: messageKind) } - public static func invalidParams(_ reason: String, id: RequestID? = nil, messageKind: MessageKind = .unknown) -> MessageDecodingError { + public static func invalidParams( + _ reason: String, + id: RequestID? = nil, + messageKind: MessageKind = .unknown + ) -> MessageDecodingError { return MessageDecodingError(code: .invalidParams, message: reason, id: id, messageKind: messageKind) } - public static func parseError(_ reason: String, id: RequestID? = nil, messageKind: MessageKind = .unknown) -> MessageDecodingError { + public static func parseError( + _ reason: String, + id: RequestID? = nil, + messageKind: MessageKind = .unknown + ) -> MessageDecodingError { return MessageDecodingError(code: .parseError, message: reason, id: id, messageKind: messageKind) } } diff --git a/Sources/LanguageServerProtocol/MessageRegistry.swift b/Sources/LanguageServerProtocol/MessageRegistry.swift index f1e16e982..b9bc86b75 100644 --- a/Sources/LanguageServerProtocol/MessageRegistry.swift +++ b/Sources/LanguageServerProtocol/MessageRegistry.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// - public final class MessageRegistry { public static let lspProtocol: MessageRegistry = diff --git a/Sources/LanguageServerProtocol/Notifications/DidChangeWorkspaceFoldersNotification.swift b/Sources/LanguageServerProtocol/Notifications/DidChangeWorkspaceFoldersNotification.swift index 37bba314c..1c2c73bae 100644 --- a/Sources/LanguageServerProtocol/Notifications/DidChangeWorkspaceFoldersNotification.swift +++ b/Sources/LanguageServerProtocol/Notifications/DidChangeWorkspaceFoldersNotification.swift @@ -16,27 +16,27 @@ /// /// Requires the `workspaceFolders` capability on both the client and server. public struct DidChangeWorkspaceFoldersNotification: NotificationType { - public static let method: String = "workspace/didChangeWorkspaceFolders" + public static let method: String = "workspace/didChangeWorkspaceFolders" - /// The set of changes. - public var event: WorkspaceFoldersChangeEvent + /// The set of changes. + public var event: WorkspaceFoldersChangeEvent - public init(event: WorkspaceFoldersChangeEvent) { - self.event = event - } + public init(event: WorkspaceFoldersChangeEvent) { + self.event = event + } } /// The workspace folder change event. public struct WorkspaceFoldersChangeEvent: Codable, Hashable { - /// The array of added workspace folders - public var added: [WorkspaceFolder]? + /// The array of added workspace folders + public var added: [WorkspaceFolder]? - /// The array of the removed workspace folders - public var removed: [WorkspaceFolder]? + /// The array of the removed workspace folders + public var removed: [WorkspaceFolder]? - public init(added: [WorkspaceFolder]? = nil, removed: [WorkspaceFolder]? = nil) { - self.added = added - self.removed = removed - } + public init(added: [WorkspaceFolder]? = nil, removed: [WorkspaceFolder]? = nil) { + self.added = added + self.removed = removed + } } diff --git a/Sources/LanguageServerProtocol/Notifications/ExitNotification.swift b/Sources/LanguageServerProtocol/Notifications/ExitNotification.swift index 83ef71592..e47e7b5f4 100644 --- a/Sources/LanguageServerProtocol/Notifications/ExitNotification.swift +++ b/Sources/LanguageServerProtocol/Notifications/ExitNotification.swift @@ -15,5 +15,5 @@ /// This notification will come after the shutdown request finishes. public struct ExitNotification: NotificationType, Hashable { public static let method: String = "exit" - public init() { } + public init() {} } diff --git a/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift b/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift index fb92f3d24..070aeba0e 100644 --- a/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift +++ b/Sources/LanguageServerProtocol/Notifications/TextSynchronizationNotifications.swift @@ -96,8 +96,8 @@ public struct DidChangeTextDocumentNotification: NotificationType, Hashable { public init( textDocument: VersionedTextDocumentIdentifier, contentChanges: [TextDocumentContentChangeEvent], - forceRebuild: Bool? = nil) - { + forceRebuild: Bool? = nil + ) { self.textDocument = textDocument self.contentChanges = contentChanges self.forceRebuild = forceRebuild @@ -165,7 +165,7 @@ public struct DidOpenNotebookDocumentNotification: NotificationType, Hashable { /// The change notification is sent from the client to the server when a notebook document changes. It is only sent by a client if the server requested the synchronization mode `notebook` in its `notebookDocumentSync` capability. public struct DidChangeNotebookDocumentNotification: NotificationType, Hashable { public static var method: String = "notebookDocument/didChange" - + /// The notebook document that did change. The version number points /// to the version after all provided changes have been applied. public var notebookDocument: VersionedNotebookDocumentIdentifier diff --git a/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift b/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift index 03922c738..f87961a66 100644 --- a/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift +++ b/Sources/LanguageServerProtocol/Notifications/WorkDoneProgress.swift @@ -38,7 +38,10 @@ public enum WorkDoneProgressKind: Codable, Hashable { } else if let end = try? WorkDoneProgressEnd(from: decoder) { self = .end(end) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected WorkDoneProgressBegin, WorkDoneProgressReport, or WorkDoneProgressEnd") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected WorkDoneProgressBegin, WorkDoneProgressReport, or WorkDoneProgressEnd" + ) throw DecodingError.dataCorrupted(context) } } @@ -101,7 +104,11 @@ public struct WorkDoneProgressBegin: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "begin" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressBegin is not 'begin'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of WorkDoneProgressBegin is not 'begin'" + ) } self.title = try container.decode(String.self, forKey: .title) @@ -110,7 +117,6 @@ public struct WorkDoneProgressBegin: Codable, Hashable { self.percentage = try container.decodeIfPresent(Int.self, forKey: .percentage) } - public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode("begin", forKey: .kind) @@ -161,7 +167,11 @@ public struct WorkDoneProgressReport: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "report" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressReport is not 'report'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of WorkDoneProgressReport is not 'report'" + ) } self.cancellable = try container.decodeIfPresent(Bool.self, forKey: .cancellable) @@ -169,7 +179,6 @@ public struct WorkDoneProgressReport: Codable, Hashable { self.percentage = try container.decodeIfPresent(Int.self, forKey: .percentage) } - public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode("report", forKey: .kind) @@ -197,13 +206,16 @@ public struct WorkDoneProgressEnd: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "end" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of WorkDoneProgressReport is not 'end'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of WorkDoneProgressReport is not 'end'" + ) } self.message = try container.decodeIfPresent(String.self, forKey: .message) } - public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode("end", forKey: .kind) diff --git a/Sources/LanguageServerProtocol/PositionRange.swift b/Sources/LanguageServerProtocol/PositionRange.swift index 73893bf85..8f9c50186 100644 --- a/Sources/LanguageServerProtocol/PositionRange.swift +++ b/Sources/LanguageServerProtocol/PositionRange.swift @@ -14,7 +14,7 @@ extension Range where Bound == Position { /// Create a range for a single position. public init(_ pos: Position) { - self = pos ..< pos + self = pos.. LSPAny { return .dictionary([ PositionRange.CodingKeys.lowerBound.stringValue: lowerBound.encodeToLSPAny(), - PositionRange.CodingKeys.upperBound.stringValue: upperBound.encodeToLSPAny() + PositionRange.CodingKeys.upperBound.stringValue: upperBound.encodeToLSPAny(), ]) } } diff --git a/Sources/LanguageServerProtocol/Request.swift b/Sources/LanguageServerProtocol/Request.swift index ce981e89f..f7dec8afa 100644 --- a/Sources/LanguageServerProtocol/Request.swift +++ b/Sources/LanguageServerProtocol/Request.swift @@ -38,7 +38,13 @@ public final class Request { /// The request's cancellation state. public let cancellationToken: CancellationToken - public init(_ request: Params, id: RequestID, clientID: ObjectIdentifier, cancellation: CancellationToken, reply: @escaping (LSPResult) -> Void) { + public init( + _ request: Params, + id: RequestID, + clientID: ObjectIdentifier, + cancellation: CancellationToken, + reply: @escaping (LSPResult) -> Void + ) { self.id = id self.clientID = clientID self.params = request @@ -87,22 +93,22 @@ public final class Notification { extension Request: CustomStringConvertible { public var description: String { return """ - Request<\(R.method)>( - id: \(id), - clientID: \(clientID), - params: \(params) - ) - """ + Request<\(R.method)>( + id: \(id), + clientID: \(clientID), + params: \(params) + ) + """ } } extension Notification: CustomStringConvertible { public var description: String { return """ - Notification<\(N.method)>( - clientID: \(clientID), - params: \(params) - ) - """ + Notification<\(N.method)>( + clientID: \(clientID), + params: \(params) + ) + """ } } diff --git a/Sources/LanguageServerProtocol/RequestID.swift b/Sources/LanguageServerProtocol/RequestID.swift index d99949f75..8852d1987 100644 --- a/Sources/LanguageServerProtocol/RequestID.swift +++ b/Sources/LanguageServerProtocol/RequestID.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -public enum RequestID: Hashable{ +public enum RequestID: Hashable { case string(String) case number(Int) } diff --git a/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift index 7d36d3061..7376c7fc2 100644 --- a/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CodeActionRequest.swift @@ -119,7 +119,11 @@ public struct CodeActionContext: Codable, Hashable { /// The reason why code actions were requested. public var triggerKind: CodeActionTriggerKind? - public init(diagnostics: [Diagnostic] = [], only: [CodeActionKind]? = nil, triggerKind: CodeActionTriggerKind? = nil) { + public init( + diagnostics: [Diagnostic] = [], + only: [CodeActionKind]? = nil, + triggerKind: CodeActionTriggerKind? = nil + ) { self.diagnostics = diagnostics self.only = only self.triggerKind = triggerKind @@ -145,7 +149,13 @@ public struct CodeAction: ResponseType, Hashable { /// first the edit is executed and then the command. public var command: Command? - public init(title: String, kind: CodeActionKind? = nil, diagnostics: [Diagnostic]? = nil, edit: WorkspaceEdit? = nil, command: Command? = nil) { + public init( + title: String, + kind: CodeActionKind? = nil, + diagnostics: [Diagnostic]? = nil, + edit: WorkspaceEdit? = nil, + command: Command? = nil + ) { self.title = title self.kind = kind self.diagnostics = diagnostics diff --git a/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift index fc6abac3e..743775258 100644 --- a/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CodeLensRefreshRequest.swift @@ -15,5 +15,5 @@ public struct CodeLensRefreshRequest: RequestType { public typealias Response = VoidResponse - public init() { } + public init() {} } diff --git a/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift b/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift index 2381e95a5..ea95736ac 100644 --- a/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CodeLensRequest.swift @@ -35,7 +35,7 @@ public struct CodeLens: ResponseType, Hashable { @CustomCodable public var range: Range - /// The command this code lens represents. + /// The command this code lens represents. public var command: Command? /// A data entry field that is preserved on a code lens item between diff --git a/Sources/LanguageServerProtocol/Requests/ColorPresentationRequest.swift b/Sources/LanguageServerProtocol/Requests/ColorPresentationRequest.swift index e64a2a8c0..ff993b5e7 100644 --- a/Sources/LanguageServerProtocol/Requests/ColorPresentationRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/ColorPresentationRequest.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -/// The color presentation request is sent from the client to the server to obtain -/// a list of presentations for a color value at a given location. Clients can -/// use the result to modify a color reference, or show in a color picker +/// The color presentation request is sent from the client to the server to obtain +/// a list of presentations for a color value at a given location. Clients can +/// use the result to modify a color reference, or show in a color picker /// and let users pick one of the presentations /// /// - Parameters: @@ -44,7 +44,7 @@ public struct ColorPresentationRequest: TextDocumentRequest, Hashable { public struct ColorPresentation: ResponseType, Hashable { /// The label of this color presentation. It will be shown on the color - /// picker header. By default this is also the text that is inserted when + /// picker header. By default this is also the text that is inserted when /// selecting this color presentation. public var label: String @@ -53,7 +53,7 @@ public struct ColorPresentation: ResponseType, Hashable { public var textEdit: TextEdit? /// An optional array of additional text edits that are applied when - /// selecting this color presentation. Edits must not overlap with + /// selecting this color presentation. Edits must not overlap with /// the main edit nor with themselves. public var additionalTextEdits: [TextEdit]? diff --git a/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift b/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift index a50eeae4e..30254dade 100644 --- a/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/CompletionRequest.swift @@ -42,8 +42,8 @@ public struct CompletionRequest: TextDocumentRequest, Hashable { textDocument: TextDocumentIdentifier, position: Position, context: CompletionContext? = nil, - sourcekitlspOptions: SKCompletionOptions? = nil) - { + sourcekitlspOptions: SKCompletionOptions? = nil + ) { self.textDocument = textDocument self.position = position self.context = context @@ -108,7 +108,10 @@ public struct CompletionList: ResponseType, Hashable { } else if let insertReplaceRange = try? InsertReplaceRanges(from: decoder) { self = .insertReplaceRanges(insertReplaceRange) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Range or InsertReplaceRanges") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected Range or InsertReplaceRanges" + ) throw DecodingError.dataCorrupted(context) } } @@ -139,7 +142,13 @@ public struct CompletionList: ResponseType, Hashable { /// A default data value. public var data: LSPAny? - public init(commitCharacters: [String]? = nil, editRange: ItemDefaultsEditRange? = nil, insertTextFormat: InsertTextFormat? = nil, insertTextMode: InsertTextMode? = nil, data: LSPAny? = nil) { + public init( + commitCharacters: [String]? = nil, + editRange: ItemDefaultsEditRange? = nil, + insertTextFormat: InsertTextFormat? = nil, + insertTextMode: InsertTextMode? = nil, + data: LSPAny? = nil + ) { self.commitCharacters = commitCharacters self.editRange = editRange self.insertTextFormat = insertTextFormat @@ -148,7 +157,6 @@ public struct CompletionList: ResponseType, Hashable { } } - /// Whether the list of completions is "complete" or not. /// /// When this value is `true`, the client should re-query the server when doing further filtering. @@ -192,7 +200,10 @@ public struct CompletionList: ResponseType, Hashable { return } catch {} - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected CompletionList or [CompletionItem]") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected CompletionList or [CompletionItem]" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift index 3a31873e7..bc50dbb96 100644 --- a/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/DiagnosticsRefreshRequest.swift @@ -14,5 +14,5 @@ public struct DiagnosticsRefreshRequest: RequestType { public static var method: String = "workspace/diagnostic/refresh" public typealias Response = VoidResponse - public init() { } + public init() {} } diff --git a/Sources/LanguageServerProtocol/Requests/DocumentColorRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentColorRequest.swift index 25b162093..865852bbd 100644 --- a/Sources/LanguageServerProtocol/Requests/DocumentColorRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/DocumentColorRequest.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// /// The document color request is sent from the client to the server to list -/// all color references found in a given text document. Along with the range, +/// all color references found in a given text document. Along with the range, /// a color value in RGB is returned. -/// Clients can use the result to decorate color references in an editor. +/// Clients can use the result to decorate color references in an editor. /// /// - Parameters: /// - textDocument: The document to search for color references. diff --git a/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift index 479ba2a1b..ccbd46955 100644 --- a/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/DocumentDiagnosticsRequest.swift @@ -45,7 +45,10 @@ public enum DocumentDiagnosticReport: ResponseType, Codable, Hashable { } else if let unchanged = try? RelatedUnchangedDocumentDiagnosticReport(from: decoder) { self = .unchanged(unchanged) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected RelatedFullDocumentDiagnosticReport or RelatedUnchangedDocumentDiagnosticReport") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected RelatedFullDocumentDiagnosticReport or RelatedUnchangedDocumentDiagnosticReport" + ) throw DecodingError.dataCorrupted(context) } } @@ -94,7 +97,11 @@ public struct RelatedFullDocumentDiagnosticReport: Codable, Hashable { /// a.cpp and result in errors in a header file b.hpp. public var relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? - public init(resultId: String? = nil, items: [Diagnostic], relatedDocuments: [DocumentURI : DocumentDiagnosticReport]? = nil) { + public init( + resultId: String? = nil, + items: [Diagnostic], + relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? = nil + ) { self.resultId = resultId self.items = items self.relatedDocuments = relatedDocuments @@ -111,11 +118,18 @@ public struct RelatedFullDocumentDiagnosticReport: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) guard kind == .full else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'" + ) } self.resultId = try container.decodeIfPresent(String.self, forKey: .resultId) self.items = try container.decode([Diagnostic].self, forKey: .items) - self.relatedDocuments = try container.decodeIfPresent([DocumentURI: DocumentDiagnosticReport].self, forKey: .relatedDocuments) + self.relatedDocuments = try container.decodeIfPresent( + [DocumentURI: DocumentDiagnosticReport].self, + forKey: .relatedDocuments + ) } public func encode(to encoder: Encoder) throws { @@ -141,7 +155,7 @@ public struct RelatedUnchangedDocumentDiagnosticReport: Codable, Hashable { /// a.cpp and result in errors in a header file b.hpp. public var relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? - public init(resultId: String, relatedDocuments: [DocumentURI : DocumentDiagnosticReport]? = nil) { + public init(resultId: String, relatedDocuments: [DocumentURI: DocumentDiagnosticReport]? = nil) { self.resultId = resultId self.relatedDocuments = relatedDocuments } @@ -156,10 +170,17 @@ public struct RelatedUnchangedDocumentDiagnosticReport: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) guard kind == .unchanged else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'" + ) } self.resultId = try container.decode(String.self, forKey: .resultId) - self.relatedDocuments = try container.decodeIfPresent([DocumentURI: DocumentDiagnosticReport].self, forKey: .relatedDocuments) + self.relatedDocuments = try container.decodeIfPresent( + [DocumentURI: DocumentDiagnosticReport].self, + forKey: .relatedDocuments + ) } public func encode(to encoder: Encoder) throws { diff --git a/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift b/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift index fa77bc69a..db8ac9c9e 100644 --- a/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/DocumentSymbolRequest.swift @@ -12,7 +12,7 @@ /// Request for symbols to display in the document outline. /// -/// This is used to provide list of all symbols in the document and display inside which +/// This is used to provide list of all symbols in the document and display inside which /// type or function the cursor is currently in. /// /// Servers that provide document highlights should set the `documentSymbolProvider` server @@ -44,7 +44,10 @@ public enum DocumentSymbolResponse: ResponseType, Hashable { } else if let symbolInformation = try? [SymbolInformation](from: decoder) { self = .symbolInformation(symbolInformation) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected [DocumentSymbol] or [SymbolInformation]") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected [DocumentSymbol] or [SymbolInformation]" + ) throw DecodingError.dataCorrupted(context) } } @@ -59,11 +62,11 @@ public enum DocumentSymbolResponse: ResponseType, Hashable { } } -/// Represents programming constructs like variables, classes, interfaces etc. that appear +/// Represents programming constructs like variables, classes, interfaces etc. that appear /// in a document. Document symbols can be hierarchical and they have two ranges: one that encloses /// its definition and one that points to its most interesting range, e.g. the range of an identifier. public struct DocumentSymbol: Hashable, Codable { - + /// The name of this symbol. Will be displayed in the user interface and therefore must not be /// an empty string or a string only consisting of white spaces. public var name: String @@ -86,7 +89,7 @@ public struct DocumentSymbol: Hashable, Codable { @CustomCodable public var range: Range - /// The range that should be selected and revealed when this symbol is being picked, + /// The range that should be selected and revealed when this symbol is being picked, /// e.g the name of a function. /// /// Must be contained by the `range`. @@ -104,8 +107,8 @@ public struct DocumentSymbol: Hashable, Codable { deprecated: Bool? = nil, range: Range, selectionRange: Range, - children: [DocumentSymbol]? = nil) - { + children: [DocumentSymbol]? = nil + ) { self.name = name self.detail = detail self.kind = kind diff --git a/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift b/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift index 278e26793..63bac96c9 100644 --- a/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/FoldingRangeRequest.swift @@ -64,8 +64,7 @@ public struct FoldingRange: ResponseType, Hashable { endUTF16Index: Int? = nil, kind: FoldingRangeKind? = nil, collapsedText: String? = nil - ) - { + ) { self.startLine = startLine self.startUTF16Index = startUTF16Index self.endLine = endLine @@ -87,7 +86,7 @@ extension FoldingRange: Codable { extension FoldingRange: Comparable { - public static func <(lhs: FoldingRange, rhs: FoldingRange) -> Bool { + public static func < (lhs: FoldingRange, rhs: FoldingRange) -> Bool { return lhs.comparableKey < rhs.comparableKey } @@ -96,6 +95,7 @@ extension FoldingRange: Comparable { startLine, startUTF16Index ?? Int.max, endLine, endUTF16Index ?? Int.max, - kind?.rawValue ?? "") + kind?.rawValue ?? "" + ) } } diff --git a/Sources/LanguageServerProtocol/Requests/HoverRequest.swift b/Sources/LanguageServerProtocol/Requests/HoverRequest.swift index d4f3f12f7..4623f09f1 100644 --- a/Sources/LanguageServerProtocol/Requests/HoverRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/HoverRequest.swift @@ -77,7 +77,10 @@ extension MarkedString: Codable { } else if let codeBlock = try? MarkdownCodeBlock(from: decoder) { self = .codeBlock(language: codeBlock.language, value: codeBlock.value) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkdownCodeBlock") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected String or MarkdownCodeBlock" + ) throw DecodingError.dataCorrupted(context) } } @@ -95,13 +98,16 @@ extension MarkedString: Codable { extension HoverResponseContents: Codable { public init(from decoder: Decoder) throws { if let value = try? MarkupContent(from: decoder) { - self = .markupContent(value) + self = .markupContent(value) } else if let value = try? [MarkedString](from: decoder) { - self = .markedStrings(value) + self = .markedStrings(value) } else if let value = try? MarkedString(from: decoder) { self = .markedStrings([value]) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected MarkedString, [MarkedString], or MarkupContent") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected MarkedString, [MarkedString], or MarkupContent" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/Requests/ImplementationRequest.swift b/Sources/LanguageServerProtocol/Requests/ImplementationRequest.swift index aecfa4e7e..347e6a966 100644 --- a/Sources/LanguageServerProtocol/Requests/ImplementationRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/ImplementationRequest.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -/// The go to implementation request is sent from the client to the server -/// to resolve the implementation location of a symbol at a given +/// The go to implementation request is sent from the client to the server +/// to resolve the implementation location of a symbol at a given /// text document position. /// -/// Servers that provide Goto Implementation support should set +/// Servers that provide Goto Implementation support should set /// the `implementationProvider` server capability. /// /// - Parameters: diff --git a/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift b/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift index 33ee90b08..4cf425853 100644 --- a/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InitializeRequest.swift @@ -93,8 +93,8 @@ public struct InitializeRequest: RequestType, Hashable { initializationOptions: LSPAny? = nil, capabilities: ClientCapabilities, trace: Tracing = .off, - workspaceFolders: [WorkspaceFolder]?) - { + workspaceFolders: [WorkspaceFolder]? + ) { self.processId = processId self.clientInfo = clientInfo self.locale = locale diff --git a/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift index e4a229afe..b3812c3c8 100644 --- a/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InlayHintRefreshRequest.swift @@ -14,5 +14,5 @@ public struct InlayHintRefreshRequest: RequestType { public static var method: String = "workspace/inlayHint/refresh" public typealias Response = VoidResponse - public init() { } + public init() {} } diff --git a/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift b/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift index 8b6cdba3e..be93611eb 100644 --- a/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InlayHintRequest.swift @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// /// Request for inline annotations to be displayed in the editor. -/// +/// /// - Parameters: /// - textDocument: The document for which to provide the inlay hints. /// diff --git a/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift b/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift index dc2b10071..6e4ce3262 100644 --- a/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InlineValueRefreshRequest.swift @@ -14,5 +14,5 @@ struct InlineValueRefreshRequest: RequestType { static var method: String = "workspace/inlineValue/refresh" typealias Response = VoidResponse - public init() { } + public init() {} } diff --git a/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift b/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift index ed770da0b..e07320cc7 100644 --- a/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/InlineValueRequest.swift @@ -130,7 +130,11 @@ public enum InlineValue: ResponseType, Hashable { } else if let evaluatableExpression = try? InlineValueEvaluatableExpression(from: decoder) { self = .evaluatableExpression(evaluatableExpression) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected InlineValueText, InlineValueEvaluatableExpression or InlineValueEvaluatableExpression") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: + "Expected InlineValueText, InlineValueEvaluatableExpression or InlineValueEvaluatableExpression" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift b/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift index d10370d9e..d92e71218 100644 --- a/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/LinkedEditingRangeRequest.swift @@ -27,15 +27,15 @@ public struct LinkedEditingRangeRequest: TextDocumentRequest { } public struct LinkedEditingRanges: ResponseType { - /// A list of ranges that can be renamed together. The ranges must have - /// identical length and contain identical text content. The ranges cannot - /// overlap. + /// A list of ranges that can be renamed together. The ranges must have + /// identical length and contain identical text content. The ranges cannot + /// overlap. @CustomCodable public var ranges: [Range] - /// An optional word pattern (regular expression) that describes valid - /// contents for the given ranges. If no pattern is provided, the client - /// configuration's word pattern will be used. + /// An optional word pattern (regular expression) that describes valid + /// contents for the given ranges. If no pattern is provided, the client + /// configuration's word pattern will be used. public var wordPattern: String? public init(ranges: [Range], wordPattern: String? = nil) { diff --git a/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift b/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift index d41f1e11f..c73a07419 100644 --- a/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/MonikersRequest.swift @@ -71,8 +71,6 @@ public struct Moniker: ResponseType, Hashable { public static let local = Kind(rawValue: "local") } - - /// The scheme of the moniker. For example tsc or .Net public var scheme: String diff --git a/Sources/LanguageServerProtocol/Requests/PrepareRenameRequest.swift b/Sources/LanguageServerProtocol/Requests/PrepareRenameRequest.swift index 6ee75f60d..a5e7ffeae 100644 --- a/Sources/LanguageServerProtocol/Requests/PrepareRenameRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/PrepareRenameRequest.swift @@ -42,7 +42,7 @@ public struct PrepareRenameResponse: ResponseType, Hashable { /// The range of the symbol @CustomCodable public var range: Range - + /// A placeholder text of the string content to be renamed public var placeholder: String? @@ -50,7 +50,7 @@ public struct PrepareRenameResponse: ResponseType, Hashable { self.range = range self.placeholder = placeholder } - + public init(from decoder: Decoder) throws { // Try decoding as PrepareRenameResponse do { @@ -59,18 +59,21 @@ public struct PrepareRenameResponse: ResponseType, Hashable { self.placeholder = try container.decode(String.self, forKey: .placeholder) return } catch {} - + // Try decoding as PositionRange do { self.range = try PositionRange(from: decoder).wrappedValue self.placeholder = nil return } catch {} - - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected PrepareRenameResponse or PositionRange") + + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected PrepareRenameResponse or PositionRange" + ) throw DecodingError.dataCorrupted(context) } - + public func encode(to encoder: Encoder) throws { if let placeholder = placeholder { var container = encoder.container(keyedBy: CodingKeys.self) @@ -80,7 +83,7 @@ public struct PrepareRenameResponse: ResponseType, Hashable { try _range.encode(to: encoder) } } - + private enum CodingKeys: String, CodingKey { case range case placeholder diff --git a/Sources/LanguageServerProtocol/Requests/RenameRequest.swift b/Sources/LanguageServerProtocol/Requests/RenameRequest.swift index ee3e13321..624b02e53 100644 --- a/Sources/LanguageServerProtocol/Requests/RenameRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/RenameRequest.swift @@ -32,7 +32,7 @@ public struct RenameRequest: TextDocumentRequest, Hashable { /// The document location at which the selected symbol is. public var position: Position - + /// The new name of the symbol. If the given name is not valid the request must return /// a ResponseError with an appropriate message set. public var newName: String diff --git a/Sources/LanguageServerProtocol/Requests/ShowMessageRequest.swift b/Sources/LanguageServerProtocol/Requests/ShowMessageRequest.swift index a1f1e82d2..3b7334d46 100644 --- a/Sources/LanguageServerProtocol/Requests/ShowMessageRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/ShowMessageRequest.swift @@ -34,8 +34,8 @@ public struct ShowMessageRequest: RequestType, Hashable { public init( type: WindowMessageType, message: String, - actions: [MessageActionItem]?) - { + actions: [MessageActionItem]? + ) { self.type = type self.message = message self.actions = actions diff --git a/Sources/LanguageServerProtocol/Requests/ShutdownRequest.swift b/Sources/LanguageServerProtocol/Requests/ShutdownRequest.swift index 5f5402dd5..aca90bcfa 100644 --- a/Sources/LanguageServerProtocol/Requests/ShutdownRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/ShutdownRequest.swift @@ -20,7 +20,7 @@ public struct ShutdownRequest: RequestType, Hashable { public static let method: String = "shutdown" public typealias Response = VoidResponse - public init() { } + public init() {} } @available(*, deprecated, renamed: "ShutdownRequest") diff --git a/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift b/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift index 9a800a74d..9bd58a353 100644 --- a/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/SignatureHelpRequest.swift @@ -32,7 +32,6 @@ public struct SignatureHelpRequest: TextDocumentRequest { } } - /// How a signature help was triggered. public struct SignatureHelpTriggerKind: RawRepresentable, Codable, Hashable { public var rawValue: Int @@ -77,7 +76,12 @@ public struct SignatureHelpContext: Codable, Hashable { /// updated based on the user navigating through available signatures. public var activeSignatureHelp: SignatureHelp? - public init(triggerKind: SignatureHelpTriggerKind, triggerCharacter: String? = nil, isRetrigger: Bool, activeSignatureHelp: SignatureHelp? = nil) { + public init( + triggerKind: SignatureHelpTriggerKind, + triggerCharacter: String? = nil, + isRetrigger: Bool, + activeSignatureHelp: SignatureHelp? = nil + ) { self.triggerKind = triggerKind self.triggerCharacter = triggerCharacter self.isRetrigger = isRetrigger @@ -140,7 +144,12 @@ public struct SignatureInformation: Codable, Hashable { /// If provided, this is used in place of `SignatureHelp.activeParameter`. public var activeParameter: Int? - public init(label: String, documentation: StringOrMarkupContent? = nil, parameters: [ParameterInformation]? = nil, activeParameter: Int? = nil) { + public init( + label: String, + documentation: StringOrMarkupContent? = nil, + parameters: [ParameterInformation]? = nil, + activeParameter: Int? = nil + ) { self.label = label self.documentation = documentation self.parameters = parameters @@ -161,7 +170,10 @@ public struct ParameterInformation: Codable, Hashable { } else if let offsets = try? Array(from: decoder), offsets.count == 2 { self = .offsets(start: offsets[0], end: offsets[1]) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or an array containing two integers") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected String or an array containing two integers" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/Requests/SymbolInfoRequest.swift b/Sources/LanguageServerProtocol/Requests/SymbolInfoRequest.swift index fb00dbb1f..dba8abb6d 100644 --- a/Sources/LanguageServerProtocol/Requests/SymbolInfoRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/SymbolInfoRequest.swift @@ -86,8 +86,8 @@ public struct SymbolDetails: ResponseType, Hashable { containerName: String? = nil, usr: String?, bestLocalDeclaration: Location? = nil, - kind: SymbolKind? = nil) - { + kind: SymbolKind? = nil + ) { self.name = name self.containerName = containerName self.usr = usr diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift index fed362bc0..104c3b8e9 100644 --- a/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceDiagnosticsRequest.swift @@ -64,7 +64,7 @@ public struct WorkspaceFullDocumentDiagnosticReport: Codable, Hashable { public var uri: DocumentURI /// The version number for which the diagnostics are reported. - /// If the document is not marked as open `null` can be provided. + /// If the document is not marked as open `null` can be provided. public var version: Int? public init(resultId: String? = nil, items: [Diagnostic], uri: DocumentURI, version: Int? = nil) { @@ -86,7 +86,11 @@ public struct WorkspaceFullDocumentDiagnosticReport: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) guard kind == .full else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of FullDocumentDiagnosticReport is not 'full'" + ) } self.resultId = try container.decodeIfPresent(String.self, forKey: .resultId) self.items = try container.decode([Diagnostic].self, forKey: .items) @@ -110,7 +114,6 @@ public struct WorkspaceUnchangedDocumentDiagnosticReport: Codable, Hashable { /// diagnostic request for the same document. public var resultId: String - /// The URI for which diagnostic information is reported. public var uri: DocumentURI @@ -135,7 +138,11 @@ public struct WorkspaceUnchangedDocumentDiagnosticReport: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(DocumentDiagnosticReportKind.self, forKey: .kind) guard kind == .unchanged else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of FullDocumentDiagnosticReport is not 'unchanged'" + ) } self.resultId = try container.decode(String.self, forKey: .resultId) self.uri = try container.decode(DocumentURI.self, forKey: .uri) @@ -162,7 +169,10 @@ public enum WorkspaceDocumentDiagnosticReport: Codable, Hashable { } else if let unchanged = try? WorkspaceUnchangedDocumentDiagnosticReport(from: decoder) { self = .unchanged(unchanged) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected WorkspaceFullDocumentDiagnosticReport or WorkspaceUnchangedDocumentDiagnosticReport") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected WorkspaceFullDocumentDiagnosticReport or WorkspaceUnchangedDocumentDiagnosticReport" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceFoldersRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceFoldersRequest.swift index 06f85eef3..f38eb3b53 100644 --- a/Sources/LanguageServerProtocol/Requests/WorkspaceFoldersRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceFoldersRequest.swift @@ -18,6 +18,6 @@ /// - Returns: The set of currently open workspace folders. Returns nil if only a single file is /// open. Returns an empty array if a workspace is open but no folders are configured. public struct WorkspaceFoldersRequest: RequestType, Hashable { - public static let method: String = "workspace/workspaceFolders" - public typealias Response = [WorkspaceFolder]? + public static let method: String = "workspace/workspaceFolders" + public typealias Response = [WorkspaceFolder]? } diff --git a/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift index cecd71944..4696c2daf 100644 --- a/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift +++ b/Sources/LanguageServerProtocol/Requests/WorkspaceSymbolsRequest.swift @@ -44,7 +44,10 @@ public enum WorkspaceSymbolItem: ResponseType, Hashable { } else if let workspaceSymbol = try? WorkspaceSymbol(from: decoder) { self = .workspaceSymbol(workspaceSymbol) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected SymbolInformation or WorkspaceSymbol") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected SymbolInformation or WorkspaceSymbol" + ) throw DecodingError.dataCorrupted(context) } } @@ -72,12 +75,14 @@ public struct SymbolInformation: Hashable, ResponseType { public var containerName: String? - public init(name: String, - kind: SymbolKind, - tags: [SymbolTag]? = nil, - deprecated: Bool? = nil, - location: Location, - containerName: String? = nil) { + public init( + name: String, + kind: SymbolKind, + tags: [SymbolTag]? = nil, + deprecated: Bool? = nil, + location: Location, + containerName: String? = nil + ) { self.name = name self.kind = kind self.tags = tags @@ -107,7 +112,10 @@ public struct WorkspaceSymbol: ResponseType, Hashable { } else if let uri = try? WorkspaceSymbolLocation.URI(from: decoder) { self = .uri(uri) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Location or object containing a URI") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected Location or object containing a URI" + ) throw DecodingError.dataCorrupted(context) } } @@ -148,7 +156,14 @@ public struct WorkspaceSymbol: ResponseType, Hashable { /// workspace symbol request and a workspace symbol resolve request. public var data: LSPAny? - public init(name: String, kind: SymbolKind, tags: [SymbolTag]? = nil, containerName: String? = nil, location: WorkspaceSymbolLocation, data: LSPAny? = nil) { + public init( + name: String, + kind: SymbolKind, + tags: [SymbolTag]? = nil, + containerName: String? = nil, + location: WorkspaceSymbolLocation, + data: LSPAny? = nil + ) { self.name = name self.kind = kind self.tags = tags diff --git a/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift b/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift index a49f96a23..7de05323e 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift @@ -241,7 +241,12 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { /// Whether the client supports the did-save notification. public var didSave: Bool? = nil - public init(dynamicRegistration: Bool? = nil, willSave: Bool? = nil, willSaveWaitUntil: Bool? = nil, didSave: Bool? = nil) { + public init( + dynamicRegistration: Bool? = nil, + willSave: Bool? = nil, + willSaveWaitUntil: Bool? = nil, + didSave: Bool? = nil + ) { self.dynamicRegistration = dynamicRegistration self.willSave = willSave self.willSaveWaitUntil = willSaveWaitUntil @@ -270,7 +275,13 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { /// Whether the client supports the `preselect` property on a CompletionItem. public var preselectSupport: Bool? = nil - public init(snippetSupport: Bool? = nil, commitCharactersSupport: Bool? = nil, documentationFormat: [MarkupKind]? = nil, deprecatedSupport: Bool? = nil, preselectSupport: Bool? = nil) { + public init( + snippetSupport: Bool? = nil, + commitCharactersSupport: Bool? = nil, + documentationFormat: [MarkupKind]? = nil, + deprecatedSupport: Bool? = nil, + preselectSupport: Bool? = nil + ) { self.snippetSupport = snippetSupport self.commitCharactersSupport = commitCharactersSupport self.documentationFormat = documentationFormat @@ -306,7 +317,12 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { /// Whether the client supports sending context information in a `textDocument/completion` request. public var contextSupport: Bool? = nil - public init(dynamicRegistration: Bool? = nil, completionItem: CompletionItem? = nil, completionItemKind: CompletionItemKind? = nil, contextSupport: Bool? = nil) { + public init( + dynamicRegistration: Bool? = nil, + completionItem: CompletionItem? = nil, + completionItemKind: CompletionItemKind? = nil, + contextSupport: Bool? = nil + ) { self.dynamicRegistration = dynamicRegistration self.completionItem = completionItem self.completionItemKind = completionItemKind @@ -390,7 +406,11 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { public var hierarchicalDocumentSymbolSupport: Bool? = nil - public init(dynamicRegistration: Bool? = nil, symbolKind: SymbolKind? = nil, hierarchicalDocumentSymbolSupport: Bool? = nil) { + public init( + dynamicRegistration: Bool? = nil, + symbolKind: SymbolKind? = nil, + hierarchicalDocumentSymbolSupport: Bool? = nil + ) { self.dynamicRegistration = dynamicRegistration self.symbolKind = symbolKind self.hierarchicalDocumentSymbolSupport = hierarchicalDocumentSymbolSupport @@ -472,9 +492,11 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { /// Whether the client supports a `codeDescription` property. public var codeDescriptionSupport: Bool? = nil - public init(relatedInformation: Bool? = nil, - codeActionsInline: Bool? = nil, - codeDescriptionSupport: Bool? = nil) { + public init( + relatedInformation: Bool? = nil, + codeActionsInline: Bool? = nil, + codeDescriptionSupport: Bool? = nil + ) { self.relatedInformation = relatedInformation self.codeActionsInline = codeActionsInline self.codeDescriptionSupport = codeDescriptionSupport @@ -672,7 +694,7 @@ public struct TextDocumentClientCapabilities: Hashable, Codable { public var inlineValue: DynamicRegistrationCapability? = nil public var inlayHint: InlayHint? = nil - + public var diagnostic: Diagnostic? = nil public init( @@ -769,7 +791,6 @@ public struct NotebookDocumentClientCapabilities: Hashable, Codable { } } - /// Window specific client capabilities. public struct WindowClientCapabilities: Hashable, Codable { /// Show message request client capabilities @@ -785,7 +806,7 @@ public struct WindowClientCapabilities: Hashable, Codable { } } - /// Capabilities specific to the `MessageActionItem` type. + /// Capabilities specific to the `MessageActionItem` type. public var messageActionItem: MessageActionItem? public init(messageActionItem: MessageActionItem? = nil) { @@ -829,13 +850,12 @@ public struct WindowClientCapabilities: Hashable, Codable { } } - /// General client capabilities. public struct GeneralClientCapabilities: Hashable, Codable { public struct StaleRequestSupport: Hashable, Codable { /// The client will actively cancel the request. public var cancel: Bool - + /// The list of requests for which the client /// will retry the request if it receives a /// response with error code `ContentModified`` @@ -879,24 +899,24 @@ public struct GeneralClientCapabilities: Hashable, Codable { } } - /// A type indicating how positions are encoded, - /// specifically what column offsets mean. - public enum PositionEncodingKind: String, Hashable, Codable { + /// A type indicating how positions are encoded, + /// specifically what column offsets mean. + public enum PositionEncodingKind: String, Hashable, Codable { - /// Character offsets count UTF-8 code units (e.g bytes). + /// Character offsets count UTF-8 code units (e.g bytes). case utf8 = "utf-8" - /// Character offsets count UTF-16 code units. - /// - /// This is the default and must always be supported - /// by servers + /// Character offsets count UTF-16 code units. + /// + /// This is the default and must always be supported + /// by servers case utf16 = "utf-16" - /// Character offsets count UTF-32 code units. - /// - /// Implementation note: these are the same as Unicode code points, - /// so this `PositionEncodingKind` may also be used for an - /// encoding-agnostic representation of character offsets. + /// Character offsets count UTF-32 code units. + /// + /// Implementation note: these are the same as Unicode code points, + /// so this `PositionEncodingKind` may also be used for an + /// encoding-agnostic representation of character offsets. case utf32 = "utf-32" } @@ -905,10 +925,10 @@ public struct GeneralClientCapabilities: Hashable, Codable { /// for which the client will not process the response /// anymore since the information is outdated). public var staleRequestSupport: StaleRequestSupport? - + /// Client capabilities specific to regular expressions. public var regularExpressions: RegularExpressions? - + /// Client capabilities specific to the client's markdown parser. public var markdown: Markdown? diff --git a/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift b/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift index d6023c086..f82429c8e 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/CodeActionKind.swift @@ -47,10 +47,10 @@ public struct CodeActionKind: RawRepresentable, Codable, Hashable { /// Organize imports action. public static let sourceOrganizeImports: CodeActionKind = CodeActionKind(rawValue: "source.organizeImports") - /// Base kind for a 'fix all' source action: `source.fixAll`. - /// - /// 'Fix all' actions automatically fix errors that have a clear fix that - /// do not require user input. They should not suppress errors or perform - /// unsafe fixes such as generating new types or classes. + /// Base kind for a 'fix all' source action: `source.fixAll`. + /// + /// 'Fix all' actions automatically fix errors that have a clear fix that + /// do not require user input. They should not suppress errors or perform + /// unsafe fixes such as generating new types or classes. public static let sourceFixAll: CodeActionKind = CodeActionKind(rawValue: "source.fixAll") } diff --git a/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift b/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift index 26e9ea335..282a7a0cc 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/CompletionItem.swift @@ -52,7 +52,10 @@ public enum CompletionItemEdit: Codable, Hashable { } else if let insertReplaceEdit = try? InsertReplaceEdit(from: decoder) { self = .insertReplaceEdit(insertReplaceEdit) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected TextEdit or InsertReplaceEdit") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected TextEdit or InsertReplaceEdit" + ) throw DecodingError.dataCorrupted(context) } } @@ -220,19 +223,19 @@ public struct InsertTextMode: RawRepresentable, Codable, Hashable { self.rawValue = rawValue } - /// The insertion or replace strings is taken as it is. If the - /// value is multi line the lines below the cursor will be - /// inserted using the indentation defined in the string value. - /// The client will not apply any kind of adjustments to the - /// string. + /// The insertion or replace strings is taken as it is. If the + /// value is multi line the lines below the cursor will be + /// inserted using the indentation defined in the string value. + /// The client will not apply any kind of adjustments to the + /// string. public static let asIs = InsertTextMode(rawValue: 1) - /// The editor adjusts leading whitespace of new lines so that - /// they match the indentation up to the cursor of the line for - /// which the item is accepted. - /// - /// Consider a line like this: <2tabs><3tabs>foo. Accepting a - /// multi line completion item is indented using 2 tabs and all - /// following lines inserted will be indented using 2 tabs as well. + /// The editor adjusts leading whitespace of new lines so that + /// they match the indentation up to the cursor of the line for + /// which the item is accepted. + /// + /// Consider a line like this: <2tabs><3tabs>foo. Accepting a + /// multi line completion item is indented using 2 tabs and all + /// following lines inserted will be indented using 2 tabs as well. public static let adjustIndentation = InsertTextMode(rawValue: 2) } diff --git a/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift b/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift index fa7d80df9..d0b240e72 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/Diagnostic.swift @@ -83,8 +83,8 @@ public struct Diagnostic: Codable, Hashable { tags: [DiagnosticTag]? = nil, relatedInformation: [DiagnosticRelatedInformation]? = nil, data: LSPAny? = nil, - codeActions: [CodeAction]? = nil) - { + codeActions: [CodeAction]? = nil + ) { self._range = CustomCodable(wrappedValue: range) self.severity = severity self.code = code diff --git a/Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift b/Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift index f268784ec..ee738b47e 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/DocumentURI.swift @@ -12,17 +12,17 @@ import Foundation -import func TSCBasic.resolveSymlinks import struct TSCBasic.AbsolutePath +import func TSCBasic.resolveSymlinks public struct DocumentURI: Codable, Hashable { /// The URL that store the URIs value private let storage: URL public var nativeURI: Self { - get throws { - DocumentURI(URL(fileURLWithPath: try resolveSymlinks(AbsolutePath(validating: self.pseudoPath)).pathString)) - } + get throws { + DocumentURI(URL(fileURLWithPath: try resolveSymlinks(AbsolutePath(validating: self.pseudoPath)).pathString)) + } } public var fileURL: URL? { diff --git a/Sources/LanguageServerProtocol/SupportTypes/FileSystemWatcher.swift b/Sources/LanguageServerProtocol/SupportTypes/FileSystemWatcher.swift index a6acc7a27..c35318885 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/FileSystemWatcher.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/FileSystemWatcher.swift @@ -26,7 +26,7 @@ public struct FileSystemWatcher: Codable, Hashable { } extension FileSystemWatcher: LSPAnyCodable { - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard let globPatternAny = dictionary[CodingKeys.globPattern.stringValue] else { return nil } guard case .string(let globPattern) = globPatternAny else { return nil } self.globPattern = globPattern diff --git a/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift b/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift index 08f45a2c3..946b8fbd3 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/InlayHint.swift @@ -84,7 +84,10 @@ public enum InlayHintLabel: Codable, Hashable { } else if let string = try? String(from: decoder) { self = .string(string) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected [InlayHintLabelPart] or String") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected [InlayHintLabelPart] or String" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift b/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift index 913a07537..5f154a807 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift @@ -112,7 +112,7 @@ extension LSPAny: ExpressibleByArrayLiteral { extension LSPAny: ExpressibleByDictionaryLiteral { public init(dictionaryLiteral elements: (String, LSPAny)...) { - let dict = [String: LSPAny](elements, uniquingKeysWith: { first, _ in first }) + let dict = [String: LSPAny](elements, uniquingKeysWith: { first, _ in first }) self = .dictionary(dict) } } @@ -137,7 +137,7 @@ extension Optional: LSPAnyCodable where Wrapped: LSPAnyCodable { self = .some(wrapped) } - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { return nil } @@ -162,7 +162,7 @@ extension Array: LSPAnyCodable where Element: LSPAnyCodable { self = result } - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { return nil } diff --git a/Sources/LanguageServerProtocol/SupportTypes/Language.swift b/Sources/LanguageServerProtocol/SupportTypes/Language.swift index d931cef38..3fe7a2a88 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/Language.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/Language.swift @@ -22,12 +22,12 @@ public struct Language: RawRepresentable, Codable, Equatable, Hashable { /// Clang-compatible language name suitable for use with `-x `. public var xflag: String? { switch self { - case .swift: return "swift" - case .c: return "c" - case .cpp: return "c++" - case .objective_c: return "objective-c" - case .objective_cpp: return "objective-c++" - default: return nil + case .swift: return "swift" + case .c: return "c" + case .cpp: return "c++" + case .objective_c: return "objective-c" + case .objective_cpp: return "objective-c++" + default: return nil } } @@ -106,12 +106,12 @@ extension Language: CustomStringConvertible, CustomDebugStringConvertible { public extension Language { static let abap = Language(rawValue: "abap") - static let bat = Language(rawValue: "bat") // Windows Bat + static let bat = Language(rawValue: "bat") // Windows Bat static let bibtex = Language(rawValue: "bibtex") static let clojure = Language(rawValue: "clojure") static let coffeescript = Language(rawValue: "coffeescript") static let c = Language(rawValue: "c") - static let cpp = Language(rawValue: "cpp") // C++, not C preprocessor + static let cpp = Language(rawValue: "cpp") // C++, not C preprocessor static let csharp = Language(rawValue: "csharp") static let css = Language(rawValue: "css") static let diff = Language(rawValue: "diff") @@ -143,20 +143,20 @@ public extension Language { static let jade = Language(rawValue: "jade") static let python = Language(rawValue: "python") static let r = Language(rawValue: "r") - static let razor = Language(rawValue: "razor") // Razor (cshtml) + static let razor = Language(rawValue: "razor") // Razor (cshtml) static let ruby = Language(rawValue: "ruby") static let rust = Language(rawValue: "rust") - static let scss = Language(rawValue: "scss") // SCSS (syntax using curly brackets) - static let sass = Language(rawValue: "sass") // SCSS (indented syntax) + static let scss = Language(rawValue: "scss") // SCSS (syntax using curly brackets) + static let sass = Language(rawValue: "sass") // SCSS (indented syntax) static let scala = Language(rawValue: "scala") static let shaderLab = Language(rawValue: "shaderlab") - static let shellScript = Language(rawValue: "shellscript") // Shell Script (Bash) + static let shellScript = Language(rawValue: "shellscript") // Shell Script (Bash) static let sql = Language(rawValue: "sql") static let swift = Language(rawValue: "swift") static let typeScript = Language(rawValue: "typescript") - static let typeScriptReact = Language(rawValue: "typescriptreact") // TypeScript React + static let typeScriptReact = Language(rawValue: "typescriptreact") // TypeScript React static let tex = Language(rawValue: "tex") - static let vb = Language(rawValue: "vb") // Visual Basic + static let vb = Language(rawValue: "vb") // Visual Basic static let xml = Language(rawValue: "xml") static let xsl = Language(rawValue: "xsl") static let yaml = Language(rawValue: "yaml") diff --git a/Sources/LanguageServerProtocol/SupportTypes/LocationLink.swift b/Sources/LanguageServerProtocol/SupportTypes/LocationLink.swift index 003d68554..05796db42 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/LocationLink.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/LocationLink.swift @@ -16,25 +16,27 @@ public struct LocationLink: Codable, Hashable { /// Used as the underlined span for mouse interaction. Defaults to the word range at the mouse position. @CustomCodable public var originSelectionRange: Range? - + /// The target resource identifier of this link. public var targetUri: DocumentURI - + /// The full target range of this link. If the target for example is a symbol then target range is the /// range enclosing this symbol not including leading/trailing whitespace but everything else /// like comments. This information is typically used to highlight the range in the editor. @CustomCodable public var targetRange: Range - + /// The range that should be selected and revealed when this link is being followed, e.g the name of a function. /// Must be contained by the the `targetRange`. See also `DocumentSymbol#range` @CustomCodable public var targetSelectionRange: Range - - public init(originSelectionRange: Range? = nil, - targetUri: DocumentURI, - targetRange: Range, - targetSelectionRange: Range) { + + public init( + originSelectionRange: Range? = nil, + targetUri: DocumentURI, + targetRange: Range, + targetSelectionRange: Range + ) { self._originSelectionRange = CustomCodable(wrappedValue: originSelectionRange) self.targetUri = targetUri self._targetRange = CustomCodable(wrappedValue: targetRange) diff --git a/Sources/LanguageServerProtocol/SupportTypes/LocationsOrLocationLinksResponse.swift b/Sources/LanguageServerProtocol/SupportTypes/LocationsOrLocationLinksResponse.swift index 3edf18815..dd6522ae4 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/LocationsOrLocationLinksResponse.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/LocationsOrLocationLinksResponse.swift @@ -23,7 +23,10 @@ public enum LocationsOrLocationLinksResponse: ResponseType, Hashable { // Fallback: Decode single location as array with one element self = .locations([location]) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected [Location], [LocationLink], or Location") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected [Location], [LocationLink], or Location" + ) throw DecodingError.dataCorrupted(context) } } @@ -37,4 +40,3 @@ public enum LocationsOrLocationLinksResponse: ResponseType, Hashable { } } } - diff --git a/Sources/LanguageServerProtocol/SupportTypes/MarkupContent.swift b/Sources/LanguageServerProtocol/SupportTypes/MarkupContent.swift index 0423bba2b..5e5f25ec6 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/MarkupContent.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/MarkupContent.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// - /// The kind of markup (plaintext or markdown). /// /// In LSP, this is a string, so we don't use a closed set. diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift index 2aa9d1a0f..dd8993ce4 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookCellTextDocumentFilter.swift @@ -41,7 +41,10 @@ public struct NotebookCellTextDocumentFilter: Codable, Hashable { } else if let filter = try? NotebookDocumentFilter(from: decoder) { self = .notebookDocumentFilter(filter) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "NotebookFilter must be either a String or NotebookDocumentFilter") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "NotebookFilter must be either a String or NotebookDocumentFilter" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift index 81cdc3466..11bdaa280 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocument.swift @@ -88,7 +88,12 @@ public struct NotebookCell: Codable, Hashable { /// Additional execution summary information if supported by the client. public var executionSummary: ExecutionSummary? - public init(kind: NotebookCellKind, document: DocumentURI, metadata: LSPObject? = nil, executionSummary: ExecutionSummary? = nil) { + public init( + kind: NotebookCellKind, + document: DocumentURI, + metadata: LSPObject? = nil, + executionSummary: ExecutionSummary? = nil + ) { self.kind = kind self.document = document self.metadata = metadata diff --git a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift index 220141f05..8611e2114 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/NotebookDocumentChangeEvent.swift @@ -41,7 +41,11 @@ public struct NotebookDocumentChangeEvent: Codable, Hashable { /// Additional closed cell text documents. public var didClose: [TextDocumentIdentifier]? - public init(array: NotebookCellArrayChange, didOpen: [TextDocumentItem]? = nil, didClose: [TextDocumentIdentifier]? = nil) { + public init( + array: NotebookCellArrayChange, + didOpen: [TextDocumentItem]? = nil, + didClose: [TextDocumentIdentifier]? = nil + ) { self.array = array self.didOpen = didOpen self.didClose = didClose diff --git a/Sources/LanguageServerProtocol/SupportTypes/Position.swift b/Sources/LanguageServerProtocol/SupportTypes/Position.swift index 9afcefc67..153278f52 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/Position.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/Position.swift @@ -39,10 +39,10 @@ extension Position: Comparable { } extension Position: LSPAnyCodable { - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .int(let line) = dictionary[CodingKeys.line.stringValue], - case .int(let utf16index) = dictionary[CodingKeys.utf16index.stringValue] else - { + case .int(let utf16index) = dictionary[CodingKeys.utf16index.stringValue] + else { return nil } self.line = line @@ -52,7 +52,7 @@ extension Position: LSPAnyCodable { public func encodeToLSPAny() -> LSPAny { return .dictionary([ CodingKeys.line.stringValue: .int(line), - CodingKeys.utf16index.stringValue: .int(utf16index) + CodingKeys.utf16index.stringValue: .int(utf16index), ]) } } diff --git a/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift b/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift index 6d25cb5f8..baab40d10 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/PositionEncoding.swift @@ -27,10 +27,10 @@ public struct PositionEncodingKind: RawRepresentable, Codable, Hashable { /// by servers public static let utf16: PositionEncodingKind = PositionEncodingKind(rawValue: "utf-16") - /// Character offsets count UTF-32 code units. - /// - /// Implementation note: these are the same as Unicode code points, - /// so this `PositionEncodingKind` may also be used for an - /// encoding-agnostic representation of character offsets. + /// Character offsets count UTF-32 code units. + /// + /// Implementation note: these are the same as Unicode code points, + /// so this `PositionEncodingKind` may also be used for an + /// encoding-agnostic representation of character offsets. public static let utf32: PositionEncodingKind = PositionEncodingKind(rawValue: "utf-32") } diff --git a/Sources/LanguageServerProtocol/SupportTypes/RegistrationOptions.swift b/Sources/LanguageServerProtocol/SupportTypes/RegistrationOptions.swift index 8e1f2bf1b..94b60242f 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/RegistrationOptions.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/RegistrationOptions.swift @@ -45,7 +45,7 @@ public struct TextDocumentRegistrationOptions: RegistrationOptions, Hashable { /// Protocol for a type which structurally represents`TextDocumentRegistrationOptions`. public protocol TextDocumentRegistrationOptionsProtocol { - var textDocumentRegistrationOptions: TextDocumentRegistrationOptions {get} + var textDocumentRegistrationOptions: TextDocumentRegistrationOptions { get } } /// Code completiion registration options. @@ -55,7 +55,7 @@ public struct CompletionRegistrationOptions: RegistrationOptions, TextDocumentRe public init(documentSelector: DocumentSelector? = nil, completionOptions: CompletionOptions) { self.textDocumentRegistrationOptions = - TextDocumentRegistrationOptions(documentSelector: documentSelector) + TextDocumentRegistrationOptions(documentSelector: documentSelector) self.completionOptions = completionOptions } @@ -81,7 +81,7 @@ public struct FoldingRangeRegistrationOptions: RegistrationOptions, TextDocument public init(documentSelector: DocumentSelector? = nil, foldingRangeOptions: FoldingRangeOptions) { self.textDocumentRegistrationOptions = - TextDocumentRegistrationOptions(documentSelector: documentSelector) + TextDocumentRegistrationOptions(documentSelector: documentSelector) self.foldingRangeOptions = foldingRangeOptions } @@ -91,7 +91,8 @@ public struct FoldingRangeRegistrationOptions: RegistrationOptions, TextDocument } } -public struct SemanticTokensRegistrationOptions: RegistrationOptions, TextDocumentRegistrationOptionsProtocol, Hashable { +public struct SemanticTokensRegistrationOptions: RegistrationOptions, TextDocumentRegistrationOptionsProtocol, Hashable +{ /// Method for registration, which defers from the actual requests' methods /// since this registration handles multiple requests. public static let method: String = "textDocument/semanticTokens" @@ -101,7 +102,7 @@ public struct SemanticTokensRegistrationOptions: RegistrationOptions, TextDocume public init(documentSelector: DocumentSelector? = nil, semanticTokenOptions: SemanticTokensOptions) { self.textDocumentRegistrationOptions = - TextDocumentRegistrationOptions(documentSelector: documentSelector) + TextDocumentRegistrationOptions(documentSelector: documentSelector) self.semanticTokenOptions = semanticTokenOptions } @@ -110,7 +111,7 @@ public struct SemanticTokensRegistrationOptions: RegistrationOptions, TextDocume let legend = semanticTokenOptions.legend dict["legend"] = .dictionary([ "tokenTypes": encode(strings: legend.tokenTypes), - "tokenModifiers": encode(strings: legend.tokenModifiers) + "tokenModifiers": encode(strings: legend.tokenModifiers), ]) if let range = semanticTokenOptions.range { let encodedRange: LSPAny @@ -160,7 +161,7 @@ public struct InlayHintRegistrationOptions: RegistrationOptions, TextDocumentReg public struct DiagnosticRegistrationOptions: RegistrationOptions, TextDocumentRegistrationOptionsProtocol { public var textDocumentRegistrationOptions: TextDocumentRegistrationOptions public var diagnosticOptions: DiagnosticOptions - + public init( documentSelector: DocumentSelector? = nil, diagnosticOptions: DiagnosticOptions diff --git a/Sources/LanguageServerProtocol/SupportTypes/SemanticTokens.swift b/Sources/LanguageServerProtocol/SupportTypes/SemanticTokens.swift index 099a755e5..dd80774a1 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/SemanticTokens.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/SemanticTokens.swift @@ -33,7 +33,6 @@ public struct SemanticTokensLegend: Codable, Hashable { } } - /// The encoding format for semantic tokens. Currently only `relative` is supported. public struct TokenFormat: RawRepresentable, Codable, Hashable { public var rawValue: String diff --git a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift index f683e7305..6fce3a240 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/ServerCapabilities.swift @@ -161,8 +161,7 @@ public struct ServerCapabilities: Codable, Hashable { monikerProvider: ValueOrBool? = nil, inlineValueProvider: ValueOrBool? = nil, experimental: LSPAny? = nil - ) - { + ) { self.positionEncoding = positionEncoding self.textDocumentSync = textDocumentSync self.notebookDocumentSync = notebookDocumentSync @@ -221,7 +220,10 @@ public enum ValueOrBool: Codable, Hashable where ValueType: } else if let value = try? ValueType(from: decoder) { self = .value(value) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected Bool or \(ValueType.self)") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected Bool or \(ValueType.self)" + ) throw DecodingError.dataCorrupted(context) } } @@ -246,7 +248,10 @@ public enum TextDocumentSync: Codable, Hashable { } else if let kind = try? TextDocumentSyncKind(from: decoder) { self = .kind(kind) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected TextDocumentSyncOptions or TextDocumentSyncKind") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected TextDocumentSyncOptions or TextDocumentSyncKind" + ) throw DecodingError.dataCorrupted(context) } } @@ -298,11 +303,13 @@ public struct TextDocumentSyncOptions: Codable, Hashable { /// Whether save notifications should be sent to the server. public var save: ValueOrBool? - public init(openClose: Bool? = true, - change: TextDocumentSyncKind? = .incremental, - willSave: Bool? = true, - willSaveWaitUntil: Bool? = false, - save: ValueOrBool? = .value(SaveOptions(includeText: false))) { + public init( + openClose: Bool? = true, + change: TextDocumentSyncKind? = .incremental, + willSave: Bool? = true, + willSaveWaitUntil: Bool? = false, + save: ValueOrBool? = .value(SaveOptions(includeText: false)) + ) { self.openClose = openClose self.change = change self.willSave = willSave @@ -325,7 +332,10 @@ public struct TextDocumentSyncOptions: Codable, Hashable { self.change = try TextDocumentSyncKind(from: decoder) return } catch {} - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected TextDocumentSyncOptions or TextDocumentSyncKind") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected TextDocumentSyncOptions or TextDocumentSyncKind" + ) throw DecodingError.dataCorrupted(context) } } @@ -353,7 +363,10 @@ public enum NotebookFilter: Codable, Hashable { } else if let documentFilter = try? DocumentFilter(from: decoder) { self = .documentFilter(documentFilter) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or DocumentFilter") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected String or DocumentFilter" + ) throw DecodingError.dataCorrupted(context) } } @@ -392,7 +405,7 @@ public struct NotebookDocumentSyncAndStaticRegistrationOptions: Codable, Hashabl } public protocol WorkDoneProgressOptions { - var workDoneProgress: Bool? { get } + var workDoneProgress: Bool? { get } } public struct HoverOptions: WorkDoneProgressOptions, Codable, Hashable { @@ -642,9 +655,11 @@ public enum CodeActionServerCapabilities: Codable, Hashable { case supportsCodeActionRequests(Bool) case supportsCodeActionRequestsWithLiterals(CodeActionOptions) - public init(clientCapabilities: TextDocumentClientCapabilities.CodeAction?, - codeActionOptions: CodeActionOptions, - supportsCodeActions: Bool) { + public init( + clientCapabilities: TextDocumentClientCapabilities.CodeAction?, + codeActionOptions: CodeActionOptions, + supportsCodeActions: Bool + ) { if clientCapabilities?.codeActionLiteralSupport != nil { self = .supportsCodeActionRequestsWithLiterals(codeActionOptions) } else { diff --git a/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift b/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift index c6be75b2e..633d01999 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/StringOrMarkupContent.swift @@ -20,7 +20,10 @@ public enum StringOrMarkupContent: Codable, Hashable { } else if let markupContent = try? MarkupContent(from: decoder) { self = .markupContent(markupContent) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected String or MarkupContent") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected String or MarkupContent" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift index 65bac7ebd..5a9695187 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentEdit.swift @@ -24,7 +24,10 @@ public struct TextDocumentEdit: Hashable, Codable { } else if let edit = try? TextEdit(from: decoder) { self = .textEdit(edit) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected AnnotatedTextEdit or TextEdit") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected AnnotatedTextEdit or TextEdit" + ) throw DecodingError.dataCorrupted(context) } } diff --git a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentIdentifier.swift b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentIdentifier.swift index 0057092d8..104a77881 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/TextDocumentIdentifier.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/TextDocumentIdentifier.swift @@ -22,7 +22,7 @@ public struct TextDocumentIdentifier: Hashable, Codable { } extension TextDocumentIdentifier: LSPAnyCodable { - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .string(let uriString)? = dictionary[CodingKeys.uri.stringValue] else { return nil } diff --git a/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift index b60b8e6bc..dbeda25ad 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/TextEdit.swift @@ -27,10 +27,10 @@ public struct TextEdit: ResponseType, Hashable { } extension TextEdit: LSPAnyCodable { - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .dictionary(let rangeDict) = dictionary[CodingKeys.range.stringValue], - case .string(let newText) = dictionary[CodingKeys.newText.stringValue] else - { + case .string(let newText) = dictionary[CodingKeys.newText.stringValue] + else { return nil } guard let range = Range(fromLSPDictionary: rangeDict) else { @@ -43,7 +43,7 @@ extension TextEdit: LSPAnyCodable { public func encodeToLSPAny() -> LSPAny { return .dictionary([ CodingKeys.range.stringValue: range.encodeToLSPAny(), - CodingKeys.newText.stringValue: .string(newText) + CodingKeys.newText.stringValue: .string(newText), ]) } } @@ -69,7 +69,6 @@ public struct ChangeAnnotation: Codable, Hashable { } } - /// An identifier referring to a change annotation managed by a workspace /// edit. public typealias ChangeAnnotationIdentifier = String diff --git a/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift b/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift index 745c2656a..01033adbc 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/Tracing.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -public enum Tracing: String, Codable { +public enum Tracing: String, Codable { case off case messages case verbose diff --git a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift index f345ab2b2..695190ec2 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceEdit.swift @@ -26,9 +26,11 @@ public struct WorkspaceEdit: Hashable, ResponseType { /// `workspace.changeAnnotationSupport`. public var changeAnnotations: [ChangeAnnotationIdentifier: ChangeAnnotation]? - public init(changes: [DocumentURI: [TextEdit]]? = nil, - documentChanges: [WorkspaceEditDocumentChange]? = nil, - changeAnnotation: [ChangeAnnotationIdentifier: ChangeAnnotation]? = nil) { + public init( + changes: [DocumentURI: [TextEdit]]? = nil, + documentChanges: [WorkspaceEditDocumentChange]? = nil, + changeAnnotation: [ChangeAnnotationIdentifier: ChangeAnnotation]? = nil + ) { self.changes = changes self.documentChanges = documentChanges self.changeAnnotations = changeAnnotation @@ -86,7 +88,10 @@ public enum WorkspaceEditDocumentChange: Codable, Hashable { } else if let deleteFile = try? DeleteFile(from: decoder) { self = .deleteFile(deleteFile) } else { - let context = DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Expected TextDocumentEdit, CreateFile, RenameFile, or DeleteFile") + let context = DecodingError.Context( + codingPath: decoder.codingPath, + debugDescription: "Expected TextDocumentEdit, CreateFile, RenameFile, or DeleteFile" + ) throw DecodingError.dataCorrupted(context) } } @@ -105,11 +110,11 @@ public enum WorkspaceEditDocumentChange: Codable, Hashable { } } - /// Options to create a file. +/// Options to create a file. public struct CreateFileOptions: Codable, Hashable { - /// Overwrite existing file. Overwrite wins over `ignoreIfExists` + /// Overwrite existing file. Overwrite wins over `ignoreIfExists` public var overwrite: Bool? - /// Ignore if exists. + /// Ignore if exists. public var ignoreIfExists: Bool? public init(overwrite: Bool? = nil, ignoreIfExists: Bool? = nil) { @@ -118,11 +123,11 @@ public struct CreateFileOptions: Codable, Hashable { } } - /// Create file operation +/// Create file operation public struct CreateFile: Codable, Hashable { - /// The resource to create. + /// The resource to create. public var uri: DocumentURI - /// Additional options + /// Additional options public var options: CreateFileOptions? /// An optional annotation identifier describing the operation. public var annotationId: ChangeAnnotationIdentifier? @@ -146,7 +151,11 @@ public struct CreateFile: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "create" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of CreateFile is not 'create'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of CreateFile is not 'create'" + ) } self.uri = try container.decode(DocumentURI.self, forKey: .uri) self.options = try container.decodeIfPresent(CreateFileOptions.self, forKey: .options) @@ -162,11 +171,11 @@ public struct CreateFile: Codable, Hashable { } } - /// Rename file options +/// Rename file options public struct RenameFileOptions: Codable, Hashable { - /// Overwrite target if existing. Overwrite wins over `ignoreIfExists` + /// Overwrite target if existing. Overwrite wins over `ignoreIfExists` public var overwrite: Bool? - /// Ignores if target exists. + /// Ignores if target exists. public var ignoreIfExists: Bool? public init(overwrite: Bool? = nil, ignoreIfExists: Bool? = nil) { @@ -175,18 +184,23 @@ public struct RenameFileOptions: Codable, Hashable { } } - /// Rename file operation +/// Rename file operation public struct RenameFile: Codable, Hashable { - /// The old (existing) location. + /// The old (existing) location. public var oldUri: DocumentURI - /// The new location. + /// The new location. public var newUri: DocumentURI - /// Rename options. + /// Rename options. public var options: RenameFileOptions? /// An optional annotation identifier describing the operation. public var annotationId: ChangeAnnotationIdentifier? - public init(oldUri: DocumentURI, newUri: DocumentURI, options: RenameFileOptions? = nil, annotationId: ChangeAnnotationIdentifier? = nil) { + public init( + oldUri: DocumentURI, + newUri: DocumentURI, + options: RenameFileOptions? = nil, + annotationId: ChangeAnnotationIdentifier? = nil + ) { self.oldUri = oldUri self.newUri = newUri self.options = options @@ -207,7 +221,11 @@ public struct RenameFile: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "rename" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of RenameFile is not 'rename'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of RenameFile is not 'rename'" + ) } self.oldUri = try container.decode(DocumentURI.self, forKey: .oldUri) self.newUri = try container.decode(DocumentURI.self, forKey: .newUri) @@ -225,11 +243,11 @@ public struct RenameFile: Codable, Hashable { } } - /// Delete file options +/// Delete file options public struct DeleteFileOptions: Codable, Hashable { - /// Delete the content recursively if a folder is denoted. + /// Delete the content recursively if a folder is denoted. public var recursive: Bool? - /// Ignore the operation if the file doesn't exist. + /// Ignore the operation if the file doesn't exist. public var ignoreIfNotExists: Bool? public init(recursive: Bool? = nil, ignoreIfNotExists: Bool? = nil) { @@ -238,11 +256,11 @@ public struct DeleteFileOptions: Codable, Hashable { } } - /// Delete file operation +/// Delete file operation public struct DeleteFile: Codable, Hashable { - /// The file to delete. + /// The file to delete. public var uri: DocumentURI - /// Delete options. + /// Delete options. public var options: DeleteFileOptions? /// An optional annotation identifier describing the operation. public var annotationId: ChangeAnnotationIdentifier? @@ -266,7 +284,11 @@ public struct DeleteFile: Codable, Hashable { let container = try decoder.container(keyedBy: CodingKeys.self) let kind = try container.decode(String.self, forKey: .kind) guard kind == "delete" else { - throw DecodingError.dataCorruptedError(forKey: .kind, in: container, debugDescription: "Kind of DeleteFile is not 'delete'") + throw DecodingError.dataCorruptedError( + forKey: .kind, + in: container, + debugDescription: "Kind of DeleteFile is not 'delete'" + ) } self.uri = try container.decode(DocumentURI.self, forKey: .uri) self.options = try container.decodeIfPresent(DeleteFileOptions.self, forKey: .options) @@ -283,7 +305,7 @@ public struct DeleteFile: Codable, Hashable { } extension WorkspaceEdit: LSPAnyCodable { - public init?(fromLSPDictionary dictionary: [String : LSPAny]) { + public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .dictionary(let lspDict) = dictionary[CodingKeys.changes.stringValue] else { return nil } diff --git a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceFolder.swift b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceFolder.swift index 9276930a3..99a85af6f 100644 --- a/Sources/LanguageServerProtocol/SupportTypes/WorkspaceFolder.swift +++ b/Sources/LanguageServerProtocol/SupportTypes/WorkspaceFolder.swift @@ -23,7 +23,7 @@ public struct WorkspaceFolder: ResponseType, Hashable, Codable { self.uri = uri self.name = name ?? uri.fileURL?.lastPathComponent ?? "unknown_workspace" - + if self.name.isEmpty { self.name = "unknown_workspace" } diff --git a/Sources/LanguageServerProtocolJSONRPC/DisableSigpipe.swift b/Sources/LanguageServerProtocolJSONRPC/DisableSigpipe.swift index 6d8267a86..e9967f64c 100644 --- a/Sources/LanguageServerProtocolJSONRPC/DisableSigpipe.swift +++ b/Sources/LanguageServerProtocolJSONRPC/DisableSigpipe.swift @@ -10,7 +10,6 @@ // //===----------------------------------------------------------------------===// - #if canImport(Glibc) import Glibc #elseif canImport(Musl) @@ -20,9 +19,9 @@ import Musl #if canImport(Glibc) || canImport(Musl) // This is a lazily initialised global variable that when read for the first time, will ignore SIGPIPE. private let globallyIgnoredSIGPIPE: Bool = { - /* no F_SETNOSIGPIPE on Linux :( */ - _ = signal(SIGPIPE, SIG_IGN) - return true + /* no F_SETNOSIGPIPE on Linux :( */ + _ = signal(SIGPIPE, SIG_IGN) + return true }() internal func globallyDisableSigpipe() { diff --git a/Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift b/Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift index 8e560c00f..0878de813 100644 --- a/Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift +++ b/Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift @@ -10,13 +10,14 @@ // //===----------------------------------------------------------------------===// -#if canImport(CDispatch) -import struct CDispatch.dispatch_fd_t -#endif import Dispatch import Foundation -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol + +#if canImport(CDispatch) +import struct CDispatch.dispatch_fd_t +#endif /// A connection between a message handler (e.g. language server) in the same process as the connection object and a remote message handler (e.g. language client) that may run in another process using JSON RPC messages sent over a pair of in/out file descriptors. /// @@ -24,7 +25,7 @@ import LSPLogging public final class JSONRPCConnection { var receiveHandler: MessageHandler? = nil - + /// The queue on which we read the data let queue: DispatchQueue = DispatchQueue(label: "jsonrpc-queue", qos: .userInitiated) @@ -68,25 +69,25 @@ public final class JSONRPCConnection { protocol messageRegistry: MessageRegistry, inFD: FileHandle, outFD: FileHandle, - syncRequests: Bool = false) - { -#if os(Linux) || os(Android) + syncRequests: Bool = false + ) { + #if os(Linux) || os(Android) // We receive a `SIGPIPE` if we write to a pipe that points to a crashed process. This in particular happens if the target of a `JSONRPCConnection` has crashed and we try to send it a message. // On Darwin, `DispatchIO` ignores `SIGPIPE` for the pipes handled by it, but that features is not available on Linux. // Instead, globally ignore `SIGPIPE` on Linux to prevent us from crashing if the `JSONRPCConnection`'s target crashes. globallyDisableSigpipe() -#endif + #endif state = .created self.messageRegistry = messageRegistry self.syncRequests = syncRequests let ioGroup = DispatchGroup() -#if os(Windows) + #if os(Windows) let rawInFD = dispatch_fd_t(bitPattern: inFD._handle) -#else + #else let rawInFD = inFD.fileDescriptor -#endif + #endif ioGroup.enter() receiveIO = DispatchIO(type: .stream, fileDescriptor: rawInFD, queue: queue) { (error: Int32) in @@ -96,11 +97,11 @@ public final class JSONRPCConnection { ioGroup.leave() } -#if os(Windows) + #if os(Windows) let rawOutFD = dispatch_fd_t(bitPattern: outFD._handle) -#else + #else let rawOutFD = outFD.fileDescriptor -#endif + #endif ioGroup.enter() sendIO = DispatchIO(type: .stream, fileDescriptor: rawOutFD, queue: sendQueue) { (error: Int32) in @@ -114,7 +115,7 @@ public final class JSONRPCConnection { guard let self = self else { return } Task { await self.closeHandler?() - self.receiveHandler = nil // break retain cycle + self.receiveHandler = nil // break retain cycle } } @@ -141,11 +142,11 @@ public final class JSONRPCConnection { receiveIO.read(offset: 0, length: Int.max, queue: queue) { done, data, errorCode in guard errorCode == 0 else { -#if !os(Windows) + #if !os(Windows) if errorCode != POSIXError.ECANCELED.rawValue { log("IO error reading \(errorCode)", level: .error) } -#endif + #endif if done { self._close() } return } @@ -188,7 +189,7 @@ public final class JSONRPCConnection { } return ready } - + /// *Public for testing* public func _send(_ message: JSONRPCMessage, async: Bool = true) { send(async: async) { encoder in @@ -205,13 +206,14 @@ public final class JSONRPCConnection { decoder.userInfo[.messageRegistryKey] = messageRegistry // Setup callback for response type. - decoder.userInfo[.responseTypeCallbackKey] = { id in - guard let outstanding = self.outstandingRequests[id] else { - log("Unknown request for \(id)", level: .error) - return nil - } - return outstanding.responseType - } as JSONRPCMessage.ResponseTypeCallback + decoder.userInfo[.responseTypeCallbackKey] = + { id in + guard let outstanding = self.outstandingRequests[id] else { + log("Unknown request for \(id)", level: .error) + return nil + } + return outstanding.responseType + } as JSONRPCMessage.ResponseTypeCallback var bytes = bytes[...] @@ -223,45 +225,54 @@ public final class JSONRPCConnection { bytes = rest let pointer = UnsafeMutableRawPointer(mutating: UnsafeBufferPointer(rebasing: messageBytes).baseAddress!) - let message = try decoder.decode(JSONRPCMessage.self, from: Data(bytesNoCopy: pointer, count: messageBytes.count, deallocator: .none)) + let message = try decoder.decode( + JSONRPCMessage.self, + from: Data(bytesNoCopy: pointer, count: messageBytes.count, deallocator: .none) + ) handle(message) } catch let error as MessageDecodingError { switch error.messageKind { - case .request: - if let id = error.id { - _send(.errorResponse(ResponseError(error), id: id)) - continue MESSAGE_LOOP - } - case .response: - if let id = error.id { - if let outstanding = self.outstandingRequests.removeValue(forKey: id) { - outstanding.replyHandler(.failure(ResponseError(error))) - } else { - log("error in response to unknown request \(id) \(error)", level: .error) - } - continue MESSAGE_LOOP - } - case .notification: - if error.code == .methodNotFound { - log("ignoring unknown notification \(error)") - continue MESSAGE_LOOP + case .request: + if let id = error.id { + _send(.errorResponse(ResponseError(error), id: id)) + continue MESSAGE_LOOP + } + case .response: + if let id = error.id { + if let outstanding = self.outstandingRequests.removeValue(forKey: id) { + outstanding.replyHandler(.failure(ResponseError(error))) + } else { + log("error in response to unknown request \(id) \(error)", level: .error) } - case .unknown: - _send(.errorResponse(ResponseError(error), id: nil), - async: false) // synchronous because the following fatalError - break + continue MESSAGE_LOOP + } + case .notification: + if error.code == .methodNotFound { + log("ignoring unknown notification \(error)") + continue MESSAGE_LOOP + } + case .unknown: + _send( + .errorResponse(ResponseError(error), id: nil), + async: false + ) // synchronous because the following fatalError + break } // FIXME: graceful shutdown? fatalError("fatal error encountered decoding message \(error)") } catch { - let responseError = ResponseError(code: .parseError, - message: "Failed to decode message. \(error.localizedDescription)") - _send(.errorResponse(responseError, id: nil), - async: false) // synchronous because the following fatalError + let responseError = ResponseError( + code: .parseError, + message: "Failed to decode message. \(error.localizedDescription)" + ) + _send( + .errorResponse(responseError, id: nil), + async: false + ) // synchronous because the following fatalError // FIXME: graceful shutdown? fatalError("fatal error encountered decoding message \(error)") } @@ -302,8 +313,10 @@ public final class JSONRPCConnection { } /// *Public for testing*. - public func send(_rawData dispatchData: DispatchData, - handleCompletion: (() -> Void)? = nil) { + public func send( + _rawData dispatchData: DispatchData, + handleCompletion: (() -> Void)? = nil + ) { guard readyToSend() else { return } sendIO.write(offset: 0, data: dispatchData, queue: sendQueue) { [weak self] done, _, errorCode in @@ -325,7 +338,7 @@ public final class JSONRPCConnection { var dispatchData = DispatchData.empty let header = "Content-Length: \(messageData.count)\r\n\r\n" - header.utf8.map{$0}.withUnsafeBytes { buffer in + header.utf8.map { $0 }.withUnsafeBytes { buffer in dispatchData.append(buffer) } messageData.withUnsafeBytes { rawBufferPointer in @@ -335,18 +348,20 @@ public final class JSONRPCConnection { send(_rawData: dispatchData, handleCompletion: handleCompletion) } - private func sendMessageSynchronously(_ messageData: Data, - timeoutInSeconds seconds: Int) { + private func sendMessageSynchronously( + _ messageData: Data, + timeoutInSeconds seconds: Int + ) { let synchronizationSemaphore = DispatchSemaphore(value: 0) - + send(messageData: messageData) { - synchronizationSemaphore.signal() + synchronizationSemaphore.signal() } - + // blocks until timeout expires or message sending completes _ = synchronizationSemaphore.wait(timeout: .now() + .seconds(seconds)) } - + func send(async: Bool = true, encoding: (JSONEncoder) throws -> Data) { guard readyToSend() else { return } @@ -408,7 +423,8 @@ extension JSONRPCConnection: Connection { } } - public func send(_ request: Request, reply: @escaping (LSPResult) -> Void) -> RequestID where Request: RequestType { + public func send(_ request: Request, reply: @escaping (LSPResult) -> Void) -> RequestID + where Request: RequestType { let id: RequestID = self.queue.sync { let id = nextRequestID() diff --git a/Sources/LanguageServerProtocolJSONRPC/MessageCoding.swift b/Sources/LanguageServerProtocolJSONRPC/MessageCoding.swift index f109b9306..2aaf377bf 100644 --- a/Sources/LanguageServerProtocolJSONRPC/MessageCoding.swift +++ b/Sources/LanguageServerProtocolJSONRPC/MessageCoding.swift @@ -21,7 +21,9 @@ public enum JSONRPCMessage { } extension CodingUserInfoKey { - public static let responseTypeCallbackKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "lsp.jsonrpc.responseTypeCallback")! + public static let responseTypeCallbackKey: CodingUserInfoKey = CodingUserInfoKey( + rawValue: "lsp.jsonrpc.responseTypeCallback" + )! public static let messageRegistryKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "lsp.jsonrpc.messageRegistry")! } @@ -78,7 +80,7 @@ extension JSONRPCMessage: Codable { } let params = try messageType.init(from: container.superDecoder(forKey: .params)) - + self = .request(params, id: id) case (let id?, nil, true, nil): @@ -111,14 +113,26 @@ extension JSONRPCMessage: Codable { throw error } catch DecodingError.keyNotFound(let key, _) { - throw MessageDecodingError.invalidParams("missing expected parameter: \(key.stringValue)", id: id, messageKind: msgKind) + throw MessageDecodingError.invalidParams( + "missing expected parameter: \(key.stringValue)", + id: id, + messageKind: msgKind + ) } catch DecodingError.valueNotFound(_, let context) { - throw MessageDecodingError.invalidParams("missing expected parameter: \(context.codingPath.last?.stringValue ?? "unknown")", id: id, messageKind: msgKind) + throw MessageDecodingError.invalidParams( + "missing expected parameter: \(context.codingPath.last?.stringValue ?? "unknown")", + id: id, + messageKind: msgKind + ) } catch DecodingError.typeMismatch(_, let context) { let path = context.codingPath.map { $0.stringValue }.joined(separator: ".") - throw MessageDecodingError.invalidParams("type mismatch at \(path) : \(context.debugDescription)", id: id, messageKind: msgKind) + throw MessageDecodingError.invalidParams( + "type mismatch at \(path) : \(context.debugDescription)", + id: id, + messageKind: msgKind + ) } catch { throw MessageDecodingError.parseError(error.localizedDescription, id: id, messageKind: msgKind) diff --git a/Sources/LanguageServerProtocolJSONRPC/MessageSplitting.swift b/Sources/LanguageServerProtocolJSONRPC/MessageSplitting.swift index 513ede335..0c1bdd6f4 100644 --- a/Sources/LanguageServerProtocolJSONRPC/MessageSplitting.swift +++ b/Sources/LanguageServerProtocolJSONRPC/MessageSplitting.swift @@ -50,7 +50,9 @@ extension RandomAccessCollection where Element == UInt8 { if key.elementsEqual(JSONRPCMessageHeader.contentLengthKey) { guard let count = Int(ascii: value) else { - throw MessageDecodingError.parseError("expected integer value in \(String(bytes: value, encoding: .utf8) ?? "")") + throw MessageDecodingError.parseError( + "expected integer value in \(String(bytes: value, encoding: .utf8) ?? "")" + ) } header.contentLength = count } @@ -73,7 +75,7 @@ extension RandomAccessCollection where Element == UInt8 { if self[keyEnd] != JSONRPCMessageHeader.colon { throw MessageDecodingError.parseError("expected ':' in message header") } - let valueStart = index(after:keyEnd) + let valueStart = index(after: keyEnd) guard let valueEnd = self[valueStart...].firstIndex(of: JSONRPCMessageHeader.separator) else { return nil } @@ -86,7 +88,7 @@ extension RandomAccessCollection where Element: Equatable { /// Returns the first index where the specified subsequence appears or nil. @inlinable - public func firstIndex(of pattern: Pattern) -> Index? where Pattern: RandomAccessCollection, Pattern.Element == Element { + public func firstIndex(of pattern: some RandomAccessCollection) -> Index? { if pattern.isEmpty { return startIndex @@ -97,7 +99,7 @@ extension RandomAccessCollection where Element: Equatable { // FIXME: use a better algorithm (e.g. Boyer-Moore-Horspool). var i = startIndex - for _ in 0 ..< (count - pattern.count + 1) { + for _ in 0..<(count - pattern.count + 1) { if self[i...].starts(with: pattern) { return i } @@ -112,7 +114,7 @@ extension UInt8 { @inlinable public var isSpace: Bool { switch self { - case UInt8(ascii: " "), UInt8(ascii: "\t"), /*LF*/0xa, /*VT*/0xb, /*FF*/0xc, /*CR*/0xd: + case UInt8(ascii: " "), UInt8(ascii: "\t"), /*LF*/ 0xa, /*VT*/ 0xb, /*FF*/ 0xc, /*CR*/ 0xd: return true default: return false diff --git a/Sources/SKCore/BuildServerBuildSystem.swift b/Sources/SKCore/BuildServerBuildSystem.swift index 11e40dcc1..8dfdf8faf 100644 --- a/Sources/SKCore/BuildServerBuildSystem.swift +++ b/Sources/SKCore/BuildServerBuildSystem.swift @@ -12,33 +12,32 @@ import BuildServerProtocol import Foundation +import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC -import LSPLogging import SKSupport -import func TSCBasic.getEnvSearchPaths -import func TSCBasic.lookupExecutablePath -import func TSCBasic.resolveSymlinks -import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath +import protocol TSCBasic.FileSystem import struct TSCBasic.FileSystemError +import func TSCBasic.getEnvSearchPaths import var TSCBasic.localFileSystem +import func TSCBasic.lookupExecutablePath +import func TSCBasic.resolveSymlinks enum BuildServerTestError: Error { - case executableNotFound(String) + case executableNotFound(String) } func executable(_ name: String) -> String { -#if os(Windows) + #if os(Windows) guard !name.hasSuffix(".exe") else { return name } return "\(name).exe" -#else + #else return name -#endif + #endif } - /// A `BuildSystem` based on communicating with a build server /// /// Provides build settings from a build server launched based on a @@ -47,7 +46,7 @@ public actor BuildServerBuildSystem: MessageHandler { let projectRoot: AbsolutePath let buildFolder: AbsolutePath? let serverConfig: BuildServerConfig - + var buildServer: JSONRPCConnection? /// The queue on which all messages that originate from the build server are @@ -80,18 +79,26 @@ public actor BuildServerBuildSystem: MessageHandler { /// The build settings that have been received from the build server. private var buildSettings: [DocumentURI: FileBuildSettings] = [:] - public init(projectRoot: AbsolutePath, buildFolder: AbsolutePath?, fileSystem: FileSystem = localFileSystem) async throws { + public init( + projectRoot: AbsolutePath, + buildFolder: AbsolutePath?, + fileSystem: FileSystem = localFileSystem + ) async throws { let configPath = projectRoot.appending(component: "buildServer.json") let config = try loadBuildServerConfig(path: configPath, fileSystem: fileSystem) -#if os(Windows) + #if os(Windows) self.searchPaths = - getEnvSearchPaths(pathString: ProcessInfo.processInfo.environment["Path"], - currentWorkingDirectory: fileSystem.currentWorkingDirectory) -#else + getEnvSearchPaths( + pathString: ProcessInfo.processInfo.environment["Path"], + currentWorkingDirectory: fileSystem.currentWorkingDirectory + ) + #else self.searchPaths = - getEnvSearchPaths(pathString: ProcessInfo.processInfo.environment["PATH"], - currentWorkingDirectory: fileSystem.currentWorkingDirectory) -#endif + getEnvSearchPaths( + pathString: ProcessInfo.processInfo.environment["PATH"], + currentWorkingDirectory: fileSystem.currentWorkingDirectory + ) + #endif self.buildFolder = buildFolder self.projectRoot = projectRoot self.serverConfig = config @@ -132,11 +139,17 @@ public actor BuildServerBuildSystem: MessageHandler { var flags = Array(serverConfig.argv[1...]) if serverPath.suffix == ".py" { flags = [serverPath.pathString] + flags - guard let interpreterPath = - lookupExecutablePath(filename: executable("python3"), - searchPaths: searchPaths) ?? - lookupExecutablePath(filename: executable("python"), - searchPaths: searchPaths) else { + guard + let interpreterPath = + lookupExecutablePath( + filename: executable("python3"), + searchPaths: searchPaths + ) + ?? lookupExecutablePath( + filename: executable("python"), + searchPaths: searchPaths + ) + else { throw BuildServerTestError.executableNotFound("python3") } @@ -155,7 +168,8 @@ public actor BuildServerBuildSystem: MessageHandler { version: "1.0", bspVersion: "2.0", rootUri: URI(self.projectRoot.asURL), - capabilities: BuildClientCapabilities(languageIds: languages)) + capabilities: BuildClientCapabilities(languageIds: languages) + ) let buildServer = try makeJSONRPCBuildServer(client: self, serverPath: serverPath, serverFlags: flags) let response = try buildServer.sendSync(initializeRequest) @@ -198,14 +212,20 @@ public actor BuildServerBuildSystem: MessageHandler { reply(.failure(ResponseError.methodNotFound(R.method))) } - func handleBuildTargetsChanged(_ notification: LanguageServerProtocol.Notification) async { + func handleBuildTargetsChanged( + _ notification: LanguageServerProtocol.Notification + ) async { await self.delegate?.buildTargetsChanged(notification.params.changes) } - func handleFileOptionsChanged(_ notification: LanguageServerProtocol.Notification) async { + func handleFileOptionsChanged( + _ notification: LanguageServerProtocol.Notification + ) async { let result = notification.params.updatedOptions let settings = FileBuildSettings( - compilerArguments: result.options, workingDirectory: result.workingDirectory) + compilerArguments: result.options, + workingDirectory: result.workingDirectory + ) await self.buildSettingsChanged(for: notification.params.uri, settings: settings) } @@ -219,7 +239,8 @@ public actor BuildServerBuildSystem: MessageHandler { private func readReponseDataKey(data: LSPAny?, key: String) -> String? { if case .dictionary(let dataDict)? = data, - case .string(let stringVal)? = dataDict[key] { + case .string(let stringVal)? = dataDict[key] + { return stringVal } @@ -241,7 +262,7 @@ extension BuildServerBuildSystem: BuildSystem { Task { if let error = result.failure { log("error registering \(uri): \(error)", level: .error) - + // BuildServer registration failed, so tell our delegate that no build // settings are available. await self.buildSettingsChanged(for: uri, settings: nil) @@ -264,16 +285,16 @@ extension BuildServerBuildSystem: BuildSystem { public func filesDidChange(_ events: [FileEvent]) {} public func fileHandlingCapability(for uri: DocumentURI) -> FileHandlingCapability { - guard - let fileUrl = uri.fileURL, - let path = try? AbsolutePath(validating: fileUrl.path) + guard + let fileUrl = uri.fileURL, + let path = try? AbsolutePath(validating: fileUrl.path) else { return .unhandled } // FIXME: We should not make any assumptions about which files the build server can handle. // Instead we should query the build server which files it can handle (#492). - + if projectRoot.isAncestorOfOrEqual(to: path) { return .handled } @@ -309,7 +330,11 @@ struct BuildServerConfig: Codable { let argv: [String] } -private func makeJSONRPCBuildServer(client: MessageHandler, serverPath: AbsolutePath, serverFlags: [String]?) throws -> JSONRPCConnection { +private func makeJSONRPCBuildServer( + client: MessageHandler, + serverPath: AbsolutePath, + serverFlags: [String]? +) throws -> JSONRPCConnection { let clientToServer = Pipe() let serverToClient = Pipe() diff --git a/Sources/SKCore/BuildSetup.swift b/Sources/SKCore/BuildSetup.swift index 00e23daae..39675d336 100644 --- a/Sources/SKCore/BuildSetup.swift +++ b/Sources/SKCore/BuildSetup.swift @@ -12,16 +12,18 @@ import SKSupport -import struct TSCBasic.AbsolutePath import struct PackageModel.BuildFlags +import struct TSCBasic.AbsolutePath /// Build configuration public struct BuildSetup { /// Default configuration - public static let `default` = BuildSetup(configuration: .debug, - path: nil, - flags: BuildFlags()) + public static let `default` = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags() + ) /// Build configuration (debug|release). public var configuration: BuildConfiguration diff --git a/Sources/SKCore/BuildSystemManager.swift b/Sources/SKCore/BuildSystemManager.swift index eeb95f047..5e8109d56 100644 --- a/Sources/SKCore/BuildSystemManager.swift +++ b/Sources/SKCore/BuildSystemManager.swift @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import BuildServerProtocol -import LSPLogging import Dispatch +import LSPLogging +import LanguageServerProtocol import struct TSCBasic.AbsolutePath @@ -52,8 +52,12 @@ public actor BuildSystemManager { /// Create a BuildSystemManager that wraps the given build system. The new /// manager will modify the delegate of the underlying build system. - public init(buildSystem: BuildSystem?, fallbackBuildSystem: FallbackBuildSystem?, - mainFilesProvider: MainFilesProvider?, fallbackSettingsTimeout: DispatchTimeInterval = .seconds(3)) async { + public init( + buildSystem: BuildSystem?, + fallbackBuildSystem: FallbackBuildSystem?, + mainFilesProvider: MainFilesProvider?, + fallbackSettingsTimeout: DispatchTimeInterval = .seconds(3) + ) async { let buildSystemHasDelegate = await buildSystem?.delegate != nil precondition(!buildSystemHasDelegate) self.buildSystem = buildSystem @@ -80,7 +84,7 @@ extension BuildSystemManager { } public var mainFilesProvider: MainFilesProvider? { - get { _mainFilesProvider} + get { _mainFilesProvider } set { _mainFilesProvider = newValue } } @@ -172,13 +176,15 @@ extension BuildSystemManager { extension BuildSystemManager: BuildSystemDelegate { private func watchedFilesReferencing(mainFiles: Set) -> Set { - return Set(watchedFiles.compactMap { (watchedFile, mainFileAndLanguage) in - if mainFiles.contains(mainFileAndLanguage.mainFile) { - return watchedFile - } else { - return nil + return Set( + watchedFiles.compactMap { (watchedFile, mainFileAndLanguage) in + if mainFiles.contains(mainFileAndLanguage.mainFile) { + return watchedFile + } else { + return nil + } } - }) + ) } public func fileBuildSettingsChanged(_ changedFiles: Set) async { @@ -251,11 +257,11 @@ extension BuildSystemManager: MainFilesDelegate { await delegate.fileBuildSettingsChanged(changedMainFileAssociations) } } - + /// Return the main file that should be used to get build settings for `uri`. /// /// For Swift or normal C files, this will be the file itself. For header - /// files, we pick a main file that includes the header since header files + /// files, we pick a main file that includes the header since header files /// don't have build settings by themselves. private func mainFile(for uri: DocumentURI, useCache: Bool = true) -> DocumentURI { if useCache, let mainFile = self.watchedFiles[uri]?.mainFile { @@ -272,7 +278,7 @@ extension BuildSystemManager: MainFilesDelegate { // If the main files contain the file itself, prefer to use that one return uri } else if let mainFile = mainFiles.min(by: { $0.pseudoPath < $1.pseudoPath }) { - // Pick the lexicographically first main file if it exists. + // Pick the lexicographically first main file if it exists. // This makes sure that picking a main file is deterministic. return mainFile } else { diff --git a/Sources/SKCore/CompilationDatabase.swift b/Sources/SKCore/CompilationDatabase.swift index efdd1752b..a95d76c15 100644 --- a/Sources/SKCore/CompilationDatabase.swift +++ b/Sources/SKCore/CompilationDatabase.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -import SKSupport import Foundation +import SKSupport -import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath +import protocol TSCBasic.FileSystem import var TSCBasic.localFileSystem import func TSCBasic.resolveSymlinks @@ -89,7 +89,7 @@ public func tryLoadCompilationDatabase( /// See https://clang.llvm.org/docs/JSONCompilationDatabase.html under Alternatives public struct FixedCompilationDatabase: CompilationDatabase, Equatable { public var allCommands: AnySequence { AnySequence([]) } - + private let fixedArgs: [String] private let directory: String @@ -113,7 +113,7 @@ extension FixedCompilationDatabase { guard let fileContents = String(data: data, encoding: .utf8) else { throw CompilationDatabaseDecodingError.fixedDatabaseDecordingError } - + fileContents.enumerateLines { line, _ in fixedArgs.append(line.trimmingCharacters(in: .whitespacesAndNewlines)) } @@ -122,7 +122,6 @@ extension FixedCompilationDatabase { } } - /// The JSON clang-compatible compilation database. /// /// Example: @@ -194,7 +193,7 @@ extension JSONCompilationDatabase { public init(file: AbsolutePath, _ fileSystem: FileSystem = localFileSystem) throws { let bytes = try fileSystem.readFileContents(file) try bytes.withUnsafeData { data in - self = try JSONDecoder().decode(JSONCompilationDatabase.self, from: data) + self = try JSONDecoder().decode(JSONCompilationDatabase.self, from: data) } } } @@ -274,8 +273,8 @@ public func splitShellEscapedCommand(_ cmd: String) -> [String] { mutating func parse() -> [String] { while !done { switch ch { - case UInt8(ascii: " "): next() - default: parseString() + case UInt8(ascii: " "): next() + default: parseString() } } return result diff --git a/Sources/SKCore/CompilationDatabaseBuildSystem.swift b/Sources/SKCore/CompilationDatabaseBuildSystem.swift index 45c5546c7..af1ac1d01 100644 --- a/Sources/SKCore/CompilationDatabaseBuildSystem.swift +++ b/Sources/SKCore/CompilationDatabaseBuildSystem.swift @@ -11,14 +11,14 @@ //===----------------------------------------------------------------------===// import BuildServerProtocol -import LanguageServerProtocol +import Dispatch import LSPLogging +import LanguageServerProtocol import SKSupport -import Dispatch -import struct Foundation.URL -import protocol TSCBasic.FileSystem +import struct Foundation.URL import struct TSCBasic.AbsolutePath +import protocol TSCBasic.FileSystem import var TSCBasic.localFileSystem /// A `BuildSystem` based on loading clang-compatible compilation database(s). @@ -61,7 +61,7 @@ public actor CompilationDatabaseBuildSystem { let args = command.commandLine for i in args.indices.reversed() { if args[i] == "-index-store-path" && i != args.endIndex - 1 { - _indexStorePath = try? AbsolutePath(validating: args[i+1]) + _indexStorePath = try? AbsolutePath(validating: args[i + 1]) return _indexStorePath } } @@ -93,10 +93,12 @@ extension CompilationDatabaseBuildSystem: BuildSystem { return nil } guard let db = database(for: url), - let cmd = db[url].first else { return nil } + let cmd = db[url].first + else { return nil } return FileBuildSettings( compilerArguments: Array(cmd.commandLine.dropFirst()), - workingDirectory: cmd.directory) + workingDirectory: cmd.directory + ) } public func registerForChangeNotifications(for uri: DocumentURI, language: Language) async { diff --git a/Sources/SKCore/FallbackBuildSystem.swift b/Sources/SKCore/FallbackBuildSystem.swift index 039cdf8ed..d799d4d70 100644 --- a/Sources/SKCore/FallbackBuildSystem.swift +++ b/Sources/SKCore/FallbackBuildSystem.swift @@ -11,14 +11,14 @@ //===----------------------------------------------------------------------===// import BuildServerProtocol +import Dispatch +import Foundation import LanguageServerProtocol import SKSupport -import Foundation -import Dispatch import enum PackageLoading.Platform -import class TSCBasic.Process import struct TSCBasic.AbsolutePath +import class TSCBasic.Process /// A simple BuildSystem suitable as a fallback when accurate settings are unknown. public final class FallbackBuildSystem { @@ -32,7 +32,10 @@ public final class FallbackBuildSystem { /// The path to the SDK. public lazy var sdkpath: AbsolutePath? = { guard Platform.current == .darwin else { return nil } - return try? AbsolutePath(validating: Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx").trimmingCharacters(in: .whitespacesAndNewlines)) + return try? AbsolutePath( + validating: Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx") + .trimmingCharacters(in: .whitespacesAndNewlines) + ) }() /// Delegate to handle any build system events. diff --git a/Sources/SKCore/Toolchain.swift b/Sources/SKCore/Toolchain.swift index a33037d90..2a2397275 100644 --- a/Sources/SKCore/Toolchain.swift +++ b/Sources/SKCore/Toolchain.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKSupport import enum PackageLoading.Platform @@ -69,8 +69,8 @@ public final class Toolchain { swiftc: AbsolutePath? = nil, clangd: AbsolutePath? = nil, sourcekitd: AbsolutePath? = nil, - libIndexStore: AbsolutePath? = nil) - { + libIndexStore: AbsolutePath? = nil + ) { self.identifier = identifier self.displayName = displayName self.path = path @@ -122,9 +122,8 @@ extension Toolchain { @discardableResult func searchForTools(_ path: AbsolutePath, _ fs: FileSystem = localFileSystem) -> Bool { return - searchForTools(binPath: path, fs) || - searchForTools(binPath: path.appending(components: "bin"), fs) || - searchForTools(binPath: path.appending(components: "usr", "bin"), fs) + searchForTools(binPath: path, fs) || searchForTools(binPath: path.appending(components: "bin"), fs) + || searchForTools(binPath: path.appending(components: "usr", "bin"), fs) } private func searchForTools(binPath: AbsolutePath, _ fs: FileSystem) -> Bool { @@ -174,22 +173,22 @@ extension Toolchain { self.sourcekitd = sourcekitdPath foundAny = true } else { -#if os(Windows) + #if os(Windows) let sourcekitdPath = binPath.appending(component: "sourcekitdInProc\(dylibExt)") -#else + #else let sourcekitdPath = libPath.appending(component: "libsourcekitdInProc\(dylibExt)") -#endif + #endif if fs.isFile(sourcekitdPath) { self.sourcekitd = sourcekitdPath foundAny = true } } -#if os(Windows) + #if os(Windows) let libIndexStore = binPath.appending(components: "libIndexStore\(dylibExt)") -#else + #else let libIndexStore = libPath.appending(components: "libIndexStore\(dylibExt)") -#endif + #endif if fs.isFile(libIndexStore) { self.libIndexStore = libIndexStore foundAny = true @@ -202,8 +201,8 @@ extension Toolchain { /// Find a containing xctoolchain with plist, if available. func containingXCToolchain( _ path: AbsolutePath, - _ fileSystem: FileSystem) -> (XCToolchainPlist, AbsolutePath)? -{ + _ fileSystem: FileSystem +) -> (XCToolchainPlist, AbsolutePath)? { var path = path while !path.isRoot { if path.extension == "xctoolchain" { diff --git a/Sources/SKCore/ToolchainRegistry.swift b/Sources/SKCore/ToolchainRegistry.swift index ed3e1d314..69c822cad 100644 --- a/Sources/SKCore/ToolchainRegistry.swift +++ b/Sources/SKCore/ToolchainRegistry.swift @@ -10,15 +10,15 @@ // //===----------------------------------------------------------------------===// -import SKSupport import Dispatch import Foundation +import SKSupport -import func TSCBasic.getEnvSearchPaths +import struct TSCBasic.AbsolutePath +import protocol TSCBasic.FileSystem import class TSCBasic.Process import enum TSCBasic.ProcessEnv -import protocol TSCBasic.FileSystem -import struct TSCBasic.AbsolutePath +import func TSCBasic.getEnvSearchPaths import var TSCBasic.localFileSystem /// Set of known toolchains. @@ -84,7 +84,7 @@ extension ToolchainRegistry { /// tr.scanForToolchains() /// ``` public convenience init( - installPath: AbsolutePath? = nil, + installPath: AbsolutePath? = nil, _ fileSystem: FileSystem ) { self.init() @@ -121,8 +121,10 @@ extension ToolchainRegistry { _default = nil return } - precondition(_toolchains.contains { $0 === toolchain }, - "default toolchain must be registered first") + precondition( + _toolchains.contains { $0 === toolchain }, + "default toolchain must be registered first" + ) _default = toolchain } } @@ -210,8 +212,8 @@ extension ToolchainRegistry { /// already been seen. public func registerToolchain( _ path: AbsolutePath, - _ fileSystem: FileSystem = localFileSystem) throws -> Toolchain - { + _ fileSystem: FileSystem = localFileSystem + ) throws -> Toolchain { return try queue.sync { try _registerToolchain(path, fileSystem) } } @@ -238,15 +240,16 @@ extension ToolchainRegistry { public func scanForToolchains( installPath: AbsolutePath? = nil, environmentVariables: [String] = ["SOURCEKIT_TOOLCHAIN_PATH"], - xcodes: [AbsolutePath] = [currentXcodeDeveloperPath].compactMap({$0}), + xcodes: [AbsolutePath] = [currentXcodeDeveloperPath].compactMap({ $0 }), xctoolchainSearchPaths: [AbsolutePath]? = nil, pathVariables: [String] = ["SOURCEKIT_PATH", "PATH", "Path"], _ fileSystem: FileSystem ) { - let xctoolchainSearchPaths = try! xctoolchainSearchPaths ?? [ - AbsolutePath(expandingTilde: "~/Library/Developer/Toolchains"), - AbsolutePath(validating: "/Library/Developer/Toolchains"), - ] + let xctoolchainSearchPaths = + try! xctoolchainSearchPaths ?? [ + AbsolutePath(expandingTilde: "~/Library/Developer/Toolchains"), + AbsolutePath(validating: "/Library/Developer/Toolchains"), + ] queue.sync { _scanForToolchains(environmentVariables: environmentVariables, setDefault: true, fileSystem) @@ -271,27 +274,28 @@ extension ToolchainRegistry { public func scanForToolchains( environmentVariables: [String], setDefault: Bool, - _ fileSystem: FileSystem = localFileSystem) - { + _ fileSystem: FileSystem = localFileSystem + ) { queue.sync { _scanForToolchains( environmentVariables: environmentVariables, setDefault: setDefault, - fileSystem) + fileSystem + ) } } func _scanForToolchains( environmentVariables: [String], setDefault: Bool, - _ fileSystem: FileSystem) - { + _ fileSystem: FileSystem + ) { var shouldSetDefault = setDefault for envVar in environmentVariables { if let pathStr = ProcessEnv.vars[envVar], - let path = try? AbsolutePath(validating: pathStr), - let toolchain = try? _registerToolchain(path, fileSystem), - shouldSetDefault + let path = try? AbsolutePath(validating: pathStr), + let toolchain = try? _registerToolchain(path, fileSystem), + shouldSetDefault { shouldSetDefault = false _default = toolchain @@ -305,7 +309,8 @@ extension ToolchainRegistry { /// - pathVariables: A list of PATH-like environment variable names to search. /// - setDefault: If true, the first toolchain found will be set as the default. public - func scanForToolchains(pathVariables: [String], _ fileSystem: FileSystem = localFileSystem) { + func scanForToolchains(pathVariables: [String], _ fileSystem: FileSystem = localFileSystem) + { queue.sync { _scanForToolchains(pathVariables: pathVariables, fileSystem) } } @@ -339,12 +344,12 @@ extension ToolchainRegistry { /// - parameter toolchains: Directory containing xctoolchains, e.g. /Library/Developer/Toolchains public func scanForToolchains( xctoolchainSearchPath searchPath: AbsolutePath, - _ fileSystem: FileSystem = localFileSystem) - { + _ fileSystem: FileSystem = localFileSystem + ) { queue.sync { _scanForToolchains(xctoolchainSearchPath: searchPath, fileSystem) } } - func _scanForToolchains(xctoolchainSearchPath searchPath: AbsolutePath, _ fileSystem: FileSystem){ + func _scanForToolchains(xctoolchainSearchPath searchPath: AbsolutePath, _ fileSystem: FileSystem) { guard let direntries = try? fileSystem.getDirectoryContents(searchPath) else { return } for name in direntries { let path = searchPath.appending(component: name) diff --git a/Sources/SKCore/XCToolchainPlist.swift b/Sources/SKCore/XCToolchainPlist.swift index 1df041898..7860d7d18 100644 --- a/Sources/SKCore/XCToolchainPlist.swift +++ b/Sources/SKCore/XCToolchainPlist.swift @@ -12,9 +12,10 @@ import Foundation -import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath +import protocol TSCBasic.FileSystem import var TSCBasic.localFileSystem + #if os(macOS) import struct TSCBasic.RelativePath import struct TSCBasic.FileSystemError @@ -47,10 +48,10 @@ extension XCToolchainPlist { /// - parameter path: The directory to search. /// - throws: If there is not plist file or it cannot be read. init(fromDirectory path: AbsolutePath, _ fileSystem: FileSystem = localFileSystem) throws { -#if os(macOS) + #if os(macOS) let plistNames = [ - try RelativePath(validating: "ToolchainInfo.plist"), // Xcode - try RelativePath(validating: "Info.plist"), // Swift.org + try RelativePath(validating: "ToolchainInfo.plist"), // Xcode + try RelativePath(validating: "Info.plist"), // Swift.org ] var missingPlistPath: AbsolutePath? @@ -64,25 +65,25 @@ extension XCToolchainPlist { } throw FileSystemError(.noEntry, missingPlistPath) -#else + #else throw Error.unsupportedPlatform -#endif + #endif } /// Returns the plist contents from the xctoolchain at `path`. /// /// - parameter path: The directory to search. init(path: AbsolutePath, _ fileSystem: FileSystem = localFileSystem) throws { -#if os(macOS) + #if os(macOS) let bytes = try fileSystem.readFileContents(path) self = try bytes.withUnsafeData { data in let decoder = PropertyListDecoder() var format = PropertyListSerialization.PropertyListFormat.binary return try decoder.decode(XCToolchainPlist.self, from: data, format: &format) } -#else + #else throw Error.unsupportedPlatform -#endif + #endif } } diff --git a/Sources/SKSupport/LineTable.swift b/Sources/SKSupport/LineTable.swift index 096244fb2..5c22557ab 100644 --- a/Sources/SKSupport/LineTable.swift +++ b/Sources/SKSupport/LineTable.swift @@ -39,7 +39,7 @@ public struct LineTable: Hashable { /// - parameter line: Line number (zero-based). @inlinable public subscript(line: Int) -> Substring { - return content[impl[line] ..< (line == count - 1 ? content.endIndex : impl[line + 1])] + return content[impl[line]..<(line == count - 1 ? content.endIndex : impl[line + 1])] } /// Translate String.Index to logical line/utf16 pair. @@ -94,8 +94,8 @@ extension LineTable { utf16Offset fromOff: Int, toLine: Int, utf16Offset toOff: Int, - with replacement: String) - { + with replacement: String + ) { let start = content.utf16.index(impl[fromLine], offsetBy: fromOff) let end = content.utf16.index(impl[toLine], offsetBy: toOff) @@ -115,8 +115,8 @@ extension LineTable { fromLine: Int, utf16Offset fromOff: Int, utf16Length: Int, - with replacement: String) - { + with replacement: String + ) { let start = content.utf16.index(impl[fromLine], offsetBy: fromOff) let end = content.utf16.index(start, offsetBy: utf16Length) let (toLine, toOff) = lineAndUTF16ColumnOf(end, fromLine: fromLine) @@ -176,7 +176,8 @@ extension LineTable { line: line, column: utf8Column, indexFunction: content.utf8.index(_:offsetBy:limitedBy:), - distanceFunction: content.utf16.distance(from:to:)) + distanceFunction: content.utf16.distance(from:to:) + ) } /// Returns UTF8 column offset at UTF16 version of logical position. @@ -189,11 +190,17 @@ extension LineTable { line: line, column: utf16Column, indexFunction: content.utf16.index(_:offsetBy:limitedBy:), - distanceFunction: content.utf8.distance(from:to:)) + distanceFunction: content.utf8.distance(from:to:) + ) } @inlinable - func convertColumn(line: Int, column: Int, indexFunction: (Substring.Index, Int, Substring.Index) -> Substring.Index?, distanceFunction: (Substring.Index, Substring.Index) -> Int) -> Int? { + func convertColumn( + line: Int, + column: Int, + indexFunction: (Substring.Index, Int, Substring.Index) -> Substring.Index?, + distanceFunction: (Substring.Index, Substring.Index) -> Int + ) -> Int? { guard line < count else { // Line out of range. return nil diff --git a/Sources/SKSupport/dlopen.swift b/Sources/SKSupport/dlopen.swift index d8841ef66..7c57182fe 100644 --- a/Sources/SKSupport/dlopen.swift +++ b/Sources/SKSupport/dlopen.swift @@ -23,9 +23,9 @@ import Musl public final class DLHandle { #if os(Windows) - typealias Handle = HMODULE + typealias Handle = HMODULE #else - typealias Handle = UnsafeMutableRawPointer + typealias Handle = UnsafeMutableRawPointer #endif var rawValue: Handle? = nil @@ -40,13 +40,13 @@ public final class DLHandle { public func close() throws { if let handle = rawValue { #if os(Windows) - guard FreeLibrary(handle) else { - throw DLError.close("Failed to FreeLibrary: \(GetLastError())") - } + guard FreeLibrary(handle) else { + throw DLError.close("Failed to FreeLibrary: \(GetLastError())") + } #else - guard dlclose(handle) == 0 else { - throw DLError.close(dlerror() ?? "unknown error") - } + guard dlclose(handle) == 0 else { + throw DLError.close(dlerror() ?? "unknown error") + } #endif } rawValue = nil @@ -67,9 +67,9 @@ public struct DLOpenFlags: RawRepresentable, OptionSet { // Platform-specific flags. #if os(macOS) - public static let first: DLOpenFlags = DLOpenFlags(rawValue: RTLD_FIRST) + public static let first: DLOpenFlags = DLOpenFlags(rawValue: RTLD_FIRST) #else - public static let first: DLOpenFlags = DLOpenFlags(rawValue: 0) + public static let first: DLOpenFlags = DLOpenFlags(rawValue: 0) #endif #endif @@ -87,13 +87,13 @@ public enum DLError: Swift.Error { public func dlopen(_ path: String?, mode: DLOpenFlags) throws -> DLHandle { #if os(Windows) - guard let handle = path?.withCString(encodedAs: UTF16.self, LoadLibraryW) else { - throw DLError.open("LoadLibraryW failed: \(GetLastError())") - } + guard let handle = path?.withCString(encodedAs: UTF16.self, LoadLibraryW) else { + throw DLError.open("LoadLibraryW failed: \(GetLastError())") + } #else - guard let handle = dlopen(path, mode.rawValue) else { - throw DLError.open(dlerror() ?? "unknown error") - } + guard let handle = dlopen(path, mode.rawValue) else { + throw DLError.open(dlerror() ?? "unknown error") + } #endif return DLHandle(rawValue: handle) } diff --git a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift index 71b9917df..64b74a3e3 100644 --- a/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift +++ b/Sources/SKSwiftPMWorkspace/SwiftPMWorkspace.swift @@ -10,30 +10,30 @@ // //===----------------------------------------------------------------------===// -#if canImport(SPMBuildCore) -import SPMBuildCore -#endif import Basics import Build import BuildServerProtocol -import LanguageServerProtocol +import Dispatch import LSPLogging +import LanguageServerProtocol import PackageGraph import PackageLoading import PackageModel -import SourceControl import SKCore import SKSupport +import SourceControl import Workspace -import Dispatch -import struct Foundation.URL import struct Basics.AbsolutePath import struct Basics.TSCAbsolutePath - -import func TSCBasic.resolveSymlinks +import struct Foundation.URL import protocol TSCBasic.FileSystem import var TSCBasic.localFileSystem +import func TSCBasic.resolveSymlinks + +#if canImport(SPMBuildCore) +import SPMBuildCore +#endif /// Parameter of `reloadPackageStatusCallback` in ``SwiftPMWorkspace``. /// @@ -85,7 +85,6 @@ public actor SwiftPMWorkspace { /// This callback is informed when `reloadPackage` starts and ends executing. var reloadPackageStatusCallback: (ReloadPackageStatus) async -> Void - /// Creates a build system using the Swift Package Manager, if this workspace is a package. /// /// - Parameters: @@ -111,28 +110,29 @@ public actor SwiftPMWorkspace { self.packageRoot = try resolveSymlinks(packageRoot) guard let destinationToolchainBinDir = toolchainRegistry.default?.swiftc?.parentDirectory else { - throw Error.cannotDetermineHostToolchain + throw Error.cannotDetermineHostToolchain } let swiftSDK = try SwiftSDK.hostSwiftSDK(AbsolutePath(destinationToolchainBinDir)) let toolchain = try UserToolchain(swiftSDK: swiftSDK) var location = try Workspace.Location( - forRootPackage: AbsolutePath(packageRoot), - fileSystem: fileSystem + forRootPackage: AbsolutePath(packageRoot), + fileSystem: fileSystem ) if let scratchDirectory = buildSetup.path { - location.scratchDirectory = AbsolutePath(scratchDirectory) + location.scratchDirectory = AbsolutePath(scratchDirectory) } var configuration = WorkspaceConfiguration.default configuration.skipDependenciesUpdates = true self.workspace = try Workspace( - fileSystem: fileSystem, - location: location, - configuration: configuration, - customHostToolchain: toolchain) + fileSystem: fileSystem, + location: location, + configuration: configuration, + customHostToolchain: toolchain + ) let buildConfiguration: PackageModel.BuildConfiguration switch buildSetup.configuration { @@ -143,10 +143,10 @@ public actor SwiftPMWorkspace { } self.buildParameters = try BuildParameters( - dataPath: location.scratchDirectory.appending(component: toolchain.targetTriple.platformBuildPathComponent), - configuration: buildConfiguration, - toolchain: toolchain, - flags: buildSetup.flags + dataPath: location.scratchDirectory.appending(component: toolchain.targetTriple.platformBuildPathComponent), + configuration: buildConfiguration, + toolchain: toolchain, + flags: buildSetup.flags ) self.packageGraph = try PackageGraph(rootPackages: [], dependencies: [], binaryArtifacts: [:]) @@ -156,7 +156,7 @@ public actor SwiftPMWorkspace { } /// Creates a build system using the Swift Package Manager, if this workspace is a package. - /// + /// /// - Parameters: /// - reloadPackageStatusCallback: Will be informed when `reloadPackage` starts and ends executing. /// - Returns: nil if `workspacePath` is not part of a package or there is an error. @@ -192,7 +192,7 @@ extension SwiftPMWorkspace { await reloadPackageStatusCallback(.start) let observabilitySystem = ObservabilitySystem({ scope, diagnostic in - log(diagnostic.description, level: diagnostic.severity.asLogLevel) + log(diagnostic.description, level: diagnostic.severity.asLogLevel) }) let packageGraph = try self.workspace.loadPackageGraph( @@ -221,10 +221,12 @@ extension SwiftPMWorkspace { } return (key: $0, value: td) } - }, uniquingKeysWith: { td, _ in + }, + uniquingKeysWith: { td, _ in // FIXME: is there a preferred target? return td - }) + } + ) self.sourceDirToTarget = [AbsolutePath: TargetBuildDescription]( packageGraph.allTargets.compactMap { target in @@ -232,10 +234,12 @@ extension SwiftPMWorkspace { return nil } return (key: target.sources.root, value: td) - }, uniquingKeysWith: { td, _ in + }, + uniquingKeysWith: { td, _ in // FIXME: is there a preferred target? return td - }) + } + ) guard let delegate = self.delegate else { await reloadPackageStatusCallback(.end) @@ -267,8 +271,8 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { /// **Public for testing only** public func _settings( for uri: DocumentURI, - _ language: Language) throws -> FileBuildSettings? - { + _ language: Language + ) throws -> FileBuildSettings? { try self.buildSettings(for: uri, language: language) } @@ -334,12 +338,12 @@ extension SwiftPMWorkspace: SKCore.BuildSystem { } return self.workspace.fileAffectsSwiftOrClangBuildSettings( - filePath: path, + filePath: path, packageGraph: self.packageGraph ) case .changed: return fileURL.lastPathComponent == "Package.swift" - default: // Unknown file change type + default: // Unknown file change type return false } } @@ -373,8 +377,8 @@ extension SwiftPMWorkspace { public func settings( for path: AbsolutePath, _ language: Language, - _ td: TargetBuildDescription) throws -> FileBuildSettings? - { + _ td: TargetBuildDescription + ) throws -> FileBuildSettings? { switch (td, language) { case (.swift(let td), .swift): return try settings(forSwiftFile: path, td) @@ -429,8 +433,8 @@ extension SwiftPMWorkspace { /// Retrieve settings for the given swift file, which is part of a known target build description. public func settings( forSwiftFile path: AbsolutePath, - _ td: SwiftTargetBuildDescription) throws -> FileBuildSettings - { + _ td: SwiftTargetBuildDescription + ) throws -> FileBuildSettings { // FIXME: this is re-implementing llbuild's constructCommandLineArgs. var args: [String] = [ "-module-name", @@ -439,7 +443,7 @@ extension SwiftPMWorkspace { "-emit-dependencies", "-emit-module", "-emit-module-path", - buildPath.appending(component: "\(td.target.c99name).swiftmodule").pathString + buildPath.appending(component: "\(td.target.c99name).swiftmodule").pathString, // -output-file-map ] if td.target.type == .library || td.target.type == .test { @@ -452,7 +456,8 @@ extension SwiftPMWorkspace { return FileBuildSettings( compilerArguments: args, - workingDirectory: workspacePath.pathString) + workingDirectory: workspacePath.pathString + ) } /// Retrieve settings for the given C-family language file, which is part of a known target build @@ -462,16 +467,16 @@ extension SwiftPMWorkspace { public func settings( forClangFile path: AbsolutePath, _ language: Language, - _ td: ClangTargetBuildDescription) throws -> FileBuildSettings - { + _ td: ClangTargetBuildDescription + ) throws -> FileBuildSettings { // FIXME: this is re-implementing things from swiftpm's createClangCompileTarget var args = try td.basicArguments() let nativePath: AbsolutePath = - try URL(fileURLWithPath: path.pathString).withUnsafeFileSystemRepresentation { - try AbsolutePath(validating: String(cString: $0!)) - } + try URL(fileURLWithPath: path.pathString).withUnsafeFileSystemRepresentation { + try AbsolutePath(validating: String(cString: $0!)) + } let compilePath = try td.compilePaths().first(where: { $0.source == nativePath }) if let compilePath = compilePath { args += [ @@ -501,7 +506,7 @@ extension SwiftPMWorkspace { "-c", compilePath.source.pathString, "-o", - compilePath.object.pathString + compilePath.object.pathString, ] } else if path.extension == "h" { args += ["-c"] @@ -518,14 +523,16 @@ extension SwiftPMWorkspace { return FileBuildSettings( compilerArguments: args, - workingDirectory: workspacePath.pathString) + workingDirectory: workspacePath.pathString + ) } } /// Find a Swift Package root directory that contains the given path, if any. private func findPackageDirectory( containing path: TSCAbsolutePath, - _ fileSystem: FileSystem) -> TSCAbsolutePath? { + _ fileSystem: FileSystem +) -> TSCAbsolutePath? { var path = path while true { let packagePath = path.appending(component: "Package.swift") diff --git a/Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift b/Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift index 3d5f83f7d..92f7692fc 100644 --- a/Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift +++ b/Sources/SKTestSupport/Array+SyntaxHighlightingToken.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SourceKitLSP import LanguageServerProtocol +import SourceKitLSP extension Array where Element == SyntaxHighlightingToken { /// Decodes the LSP representation of syntax highlighting tokens @@ -40,12 +40,14 @@ extension Array where Element == SyntaxHighlightingToken { guard let kind = SyntaxHighlightingToken.Kind(rawValue: rawKind) else { continue } let modifiers = SyntaxHighlightingToken.Modifiers(rawValue: rawModifiers) - append(SyntaxHighlightingToken( - start: current, - utf16length: length, - kind: kind, - modifiers: modifiers - )) + append( + SyntaxHighlightingToken( + start: current, + utf16length: length, + kind: kind, + modifiers: modifiers + ) + ) } } } diff --git a/Sources/SKTestSupport/FileSystem.swift b/Sources/SKTestSupport/FileSystem.swift index 6a78f0aa5..f20a1238e 100644 --- a/Sources/SKTestSupport/FileSystem.swift +++ b/Sources/SKTestSupport/FileSystem.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -import protocol TSCBasic.FileSystem import struct TSCBasic.AbsolutePath import struct TSCBasic.ByteString +import protocol TSCBasic.FileSystem extension FileSystem { diff --git a/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift b/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift index f1187c3c5..b8d19a057 100644 --- a/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift +++ b/Sources/SKTestSupport/SKSwiftPMTestWorkspace.swift @@ -10,21 +10,21 @@ // //===----------------------------------------------------------------------===// -import SourceKitLSP -import SKSwiftPMWorkspace +import Foundation +import ISDBTestSupport +import ISDBTibs +import IndexStoreDB +import LSPTestSupport import LanguageServerProtocol import SKCore -import IndexStoreDB -import ISDBTibs -import ISDBTestSupport +import SKSwiftPMWorkspace +import SourceKitLSP import XCTest -import Foundation -import LSPTestSupport +import struct PackageModel.BuildFlags +import struct TSCBasic.AbsolutePath import class TSCBasic.Process import func TSCBasic.resolveSymlinks -import struct TSCBasic.AbsolutePath -import struct PackageModel.BuildFlags fileprivate extension SourceKitServer { func addWorkspace(_ workspace: Workspace) { @@ -88,7 +88,8 @@ public final class SKSwiftPMTestWorkspace { let swiftpm = try await SwiftPMWorkspace( workspacePath: sourcePath, toolchainRegistry: ToolchainRegistry.shared, - buildSetup: buildSetup) + buildSetup: buildSetup + ) let libIndexStore = try IndexStoreLibrary(dylibPath: toolchain.libIndexStore!.pathString) @@ -101,7 +102,8 @@ public final class SKSwiftPMTestWorkspace { databasePath: tmpDir.path, library: libIndexStore, delegate: indexDelegate, - listenToUnitEvents: false) + listenToUnitEvents: false + ) let server = self.testServer.server! let workspace = await Workspace( @@ -112,7 +114,8 @@ public final class SKSwiftPMTestWorkspace { buildSetup: buildSetup, underlyingBuildSystem: swiftpm, index: index, - indexDelegate: indexDelegate) + indexDelegate: indexDelegate + ) await workspace.buildSystemManager.setDelegate(server) await server.addWorkspace(workspace) } @@ -150,11 +153,16 @@ extension SKSwiftPMTestWorkspace { extension SKSwiftPMTestWorkspace { public func openDocument(_ url: URL, language: Language) throws { - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: language, - version: 1, - text: try sources.sourceCache.get(url)))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: language, + version: 1, + text: try sources.sourceCache.get(url) + ) + ) + ) } public func closeDocument(_ url: URL) { @@ -164,7 +172,10 @@ extension SKSwiftPMTestWorkspace { extension XCTestCase { - public func staticSourceKitSwiftPMWorkspace(name: String, server: TestSourceKitServer? = nil) async throws -> SKSwiftPMTestWorkspace? { + public func staticSourceKitSwiftPMWorkspace( + name: String, + server: TestSourceKitServer? = nil + ) async throws -> SKSwiftPMTestWorkspace? { let testDirName = testDirectoryName let toolchain = ToolchainRegistry.shared.default! let workspace = try await SKSwiftPMTestWorkspace( @@ -181,13 +192,17 @@ extension XCTestCase { if hasClangFile { if toolchain.libIndexStore == nil { - fputs("warning: skipping test because libIndexStore is missing;" + - "install Clang's IndexStore component\n", stderr) + fputs( + "warning: skipping test because libIndexStore is missing;" + "install Clang's IndexStore component\n", + stderr + ) return nil } if !TibsToolchain(toolchain).clangHasIndexSupport { - fputs("warning: skipping test because '\(toolchain.clang!)' does not " + - "have indexstore support; use swift-clang\n", stderr) + fputs( + "warning: skipping test because '\(toolchain.clang!)' does not have indexstore support; use swift-clang\n", + stderr + ) return nil } } diff --git a/Sources/SKTestSupport/SKTibsTestWorkspace.swift b/Sources/SKTestSupport/SKTibsTestWorkspace.swift index 7b7f033ad..bab0208bc 100644 --- a/Sources/SKTestSupport/SKTibsTestWorkspace.swift +++ b/Sources/SKTestSupport/SKTibsTestWorkspace.swift @@ -10,20 +10,20 @@ // //===----------------------------------------------------------------------===// -import SourceKitLSP +import Foundation +import ISDBTestSupport +import ISDBTibs +import IndexStoreDB +import LSPTestSupport import LanguageServerProtocol import SKCore import SKSupport -import IndexStoreDB -import ISDBTibs -import ISDBTestSupport +import SourceKitLSP import XCTest -import Foundation -import LSPTestSupport import enum PackageLoading.Platform -import struct TSCBasic.AbsolutePath import struct PackageModel.BuildFlags +import struct TSCBasic.AbsolutePath public typealias URL = Foundation.URL @@ -51,15 +51,15 @@ public final class SKTibsTestWorkspace { toolchain: Toolchain, clientCapabilities: ClientCapabilities, testServer: TestSourceKitServer? = nil - ) async throws - { + ) async throws { self.testServer = testServer ?? TestSourceKitServer(connectionKind: .local) self.tibsWorkspace = try TibsTestWorkspace( immutableProjectDir: immutableProjectDir, persistentBuildDir: persistentBuildDir, tmpDir: tmpDir, removeTmpDir: removeTmpDir, - toolchain: TibsToolchain(toolchain)) + toolchain: TibsToolchain(toolchain) + ) try await initWorkspace(clientCapabilities: clientCapabilities) } @@ -76,7 +76,8 @@ public final class SKTibsTestWorkspace { self.tibsWorkspace = try TibsTestWorkspace( projectDir: projectDir, tmpDir: tmpDir, - toolchain: TibsToolchain(toolchain)) + toolchain: TibsToolchain(toolchain) + ) try await initWorkspace(clientCapabilities: clientCapabilities) } @@ -95,7 +96,8 @@ public final class SKTibsTestWorkspace { buildSetup: BuildSetup(configuration: .debug, path: buildPath, flags: BuildFlags()), underlyingBuildSystem: buildSystem, index: index, - indexDelegate: indexDelegate) + indexDelegate: indexDelegate + ) await workspace.buildSystemManager.setDelegate(testServer.server!) await testServer.server!.setWorkspaces([workspace]) @@ -113,19 +115,24 @@ extension SKTibsTestWorkspace { /// Perform a group of edits to the project sources and optionally rebuild. public func edit( rebuild: Bool = false, - _ block: (inout TestSources.ChangeBuilder, _ current: SourceFileCache) throws -> ()) throws - { + _ block: (inout TestSources.ChangeBuilder, _ current: SourceFileCache) throws -> () + ) throws { try tibsWorkspace.edit(rebuild: rebuild, block) } } extension SKTibsTestWorkspace { public func openDocument(_ url: URL, language: Language) throws { - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: language, - version: 1, - text: try sources.sourceCache.get(url)))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: language, + version: 1, + text: try sources.sourceCache.get(url) + ) + ) + ) } } @@ -144,7 +151,8 @@ extension XCTestCase { .appendingPathComponent(name, isDirectory: true), persistentBuildDir: XCTestCase.productsDirectory .appendingPathComponent("sk-tests/\(testDirName)", isDirectory: true), - tmpDir: tmpDir ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + tmpDir: tmpDir + ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent("sk-test-data/\(testDirName)", isDirectory: true), removeTmpDir: removeTmpDir, toolchain: ToolchainRegistry.shared.default!, @@ -153,9 +161,13 @@ extension XCTestCase { ) if workspace.builder.targets.contains(where: { target in !target.clangTUs.isEmpty }) - && !workspace.builder.toolchain.clangHasIndexSupport { - fputs("warning: skipping test because '\(workspace.builder.toolchain.clang.path)' does not " + - "have indexstore support; use swift-clang\n", stderr) + && !workspace.builder.toolchain.clangHasIndexSupport + { + fputs( + "warning: skipping test because '\(workspace.builder.toolchain.clang.path)' does not " + + "have indexstore support; use swift-clang\n", + stderr + ) return nil } @@ -173,15 +185,21 @@ extension XCTestCase { let testDirName = testDirectoryName let workspace = try await SKTibsTestWorkspace( projectDir: XCTestCase.sklspInputsDirectory.appendingPathComponent(name, isDirectory: true), - tmpDir: tmpDir ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) + tmpDir: tmpDir + ?? URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) .appendingPathComponent("sk-test-data/\(testDirName)", isDirectory: true), toolchain: ToolchainRegistry.shared.default!, - clientCapabilities: clientCapabilities) + clientCapabilities: clientCapabilities + ) if workspace.builder.targets.contains(where: { target in !target.clangTUs.isEmpty }) - && !workspace.builder.toolchain.clangHasIndexSupport { - fputs("warning: skipping test because '\(workspace.builder.toolchain.clang.path)' does not " + - "have indexstore support; use swift-clang\n", stderr) + && !workspace.builder.toolchain.clangHasIndexSupport + { + fputs( + "warning: skipping test because '\(workspace.builder.toolchain.clang.path)' does not " + + "have indexstore support; use swift-clang\n", + stderr + ) return nil } @@ -257,7 +275,8 @@ extension TibsToolchain { clang: sktc.clang!.asURL, libIndexStore: sktc.libIndexStore!.asURL, tibs: XCTestCase.productsDirectory.appendingPathComponent("tibs\(execExt)", isDirectory: false), - ninja: ninja) + ninja: ninja + ) } } diff --git a/Sources/SKTestSupport/TestServer.swift b/Sources/SKTestSupport/TestServer.swift index de0053220..dc7aeb8e5 100644 --- a/Sources/SKTestSupport/TestServer.swift +++ b/Sources/SKTestSupport/TestServer.swift @@ -10,13 +10,13 @@ // //===----------------------------------------------------------------------===// -import SKSupport -import SKCore +import Foundation +import LSPTestSupport import LanguageServerProtocol import LanguageServerProtocolJSONRPC +import SKCore +import SKSupport import SourceKitLSP -import Foundation -import LSPTestSupport public final class TestSourceKitServer { public enum ConnectionKind { @@ -26,12 +26,14 @@ public final class TestSourceKitServer { enum ConnectionImpl { case local( clientConnection: LocalConnection, - serverConnection: LocalConnection) + serverConnection: LocalConnection + ) case jsonrpc( clientToServer: Pipe, serverToClient: Pipe, clientConnection: JSONRPCConnection, - serverConnection: JSONRPCConnection) + serverConnection: JSONRPCConnection + ) } public static let serverOptions: SourceKitServer.Options = SourceKitServer.Options() @@ -49,7 +51,7 @@ public final class TestSourceKitServer { /// The server, if it is in the same process. public let server: SourceKitServer? - + /// - Parameters: /// - useGlobalModuleCache: If `false`, the server will use its own module /// cache in an empty temporary directory instead of the global module cache. @@ -65,55 +67,64 @@ public final class TestSourceKitServer { } switch connectionKind { - case .local: - let clientConnection = LocalConnection() - let serverConnection = LocalConnection() - client = TestClient(server: serverConnection) - server = SourceKitServer(client: clientConnection, options: serverOptions, onExit: { + case .local: + let clientConnection = LocalConnection() + let serverConnection = LocalConnection() + client = TestClient(server: serverConnection) + server = SourceKitServer( + client: clientConnection, + options: serverOptions, + onExit: { clientConnection.close() - }) - - clientConnection.start(handler: client) - serverConnection.start(handler: server!) - - connImpl = .local(clientConnection: clientConnection, serverConnection: serverConnection) - - case .jsonrpc: - let clientToServer: Pipe = Pipe() - let serverToClient: Pipe = Pipe() - - let clientConnection = JSONRPCConnection( - protocol: MessageRegistry.lspProtocol, - inFD: serverToClient.fileHandleForReading, - outFD: clientToServer.fileHandleForWriting - ) - let serverConnection = JSONRPCConnection( - protocol: MessageRegistry.lspProtocol, - inFD: clientToServer.fileHandleForReading, - outFD: serverToClient.fileHandleForWriting - ) - - client = TestClient(server: clientConnection) - server = SourceKitServer(client: serverConnection, options: serverOptions, onExit: { - serverConnection.close() - }) - - clientConnection.start(receiveHandler: client) { - // FIXME: keep the pipes alive until we close the connection. This - // should be fixed systemically. - withExtendedLifetime((clientToServer, serverToClient)) {} } - serverConnection.start(receiveHandler: server!) { - // FIXME: keep the pipes alive until we close the connection. This - // should be fixed systemically. - withExtendedLifetime((clientToServer, serverToClient)) {} + ) + + clientConnection.start(handler: client) + serverConnection.start(handler: server!) + + connImpl = .local(clientConnection: clientConnection, serverConnection: serverConnection) + + case .jsonrpc: + let clientToServer: Pipe = Pipe() + let serverToClient: Pipe = Pipe() + + let clientConnection = JSONRPCConnection( + protocol: MessageRegistry.lspProtocol, + inFD: serverToClient.fileHandleForReading, + outFD: clientToServer.fileHandleForWriting + ) + let serverConnection = JSONRPCConnection( + protocol: MessageRegistry.lspProtocol, + inFD: clientToServer.fileHandleForReading, + outFD: serverToClient.fileHandleForWriting + ) + + client = TestClient(server: clientConnection) + server = SourceKitServer( + client: serverConnection, + options: serverOptions, + onExit: { + serverConnection.close() } - - connImpl = .jsonrpc( - clientToServer: clientToServer, - serverToClient: serverToClient, - clientConnection: clientConnection, - serverConnection: serverConnection) + ) + + clientConnection.start(receiveHandler: client) { + // FIXME: keep the pipes alive until we close the connection. This + // should be fixed systemically. + withExtendedLifetime((clientToServer, serverToClient)) {} + } + serverConnection.start(receiveHandler: server!) { + // FIXME: keep the pipes alive until we close the connection. This + // should be fixed systemically. + withExtendedLifetime((clientToServer, serverToClient)) {} + } + + connImpl = .jsonrpc( + clientToServer: clientToServer, + serverToClient: serverToClient, + clientConnection: clientConnection, + serverConnection: serverConnection + ) } } diff --git a/Sources/SourceKitD/SKDRequestArray.swift b/Sources/SourceKitD/SKDRequestArray.swift index d7c8bbb37..28d3d75bd 100644 --- a/Sources/SourceKitD/SKDRequestArray.swift +++ b/Sources/SourceKitD/SKDRequestArray.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Csourcekitd + #if canImport(Glibc) import Glibc #elseif canImport(Musl) diff --git a/Sources/SourceKitD/SKDRequestDictionary.swift b/Sources/SourceKitD/SKDRequestDictionary.swift index 9505a2c6a..911ef3258 100644 --- a/Sources/SourceKitD/SKDRequestDictionary.swift +++ b/Sources/SourceKitD/SKDRequestDictionary.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Csourcekitd + #if canImport(Glibc) import Glibc #elseif canImport(Musl) diff --git a/Sources/SourceKitD/SKDResponse.swift b/Sources/SourceKitD/SKDResponse.swift index aac2af3c1..8f72d4247 100644 --- a/Sources/SourceKitD/SKDResponse.swift +++ b/Sources/SourceKitD/SKDResponse.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Csourcekitd + #if canImport(Glibc) import Glibc #elseif canImport(Musl) @@ -37,11 +38,11 @@ public final class SKDResponse { return nil } switch sourcekitd.api.response_error_get_kind(response) { - case SOURCEKITD_ERROR_CONNECTION_INTERRUPTED: return .connectionInterrupted - case SOURCEKITD_ERROR_REQUEST_INVALID: return .requestInvalid(description) - case SOURCEKITD_ERROR_REQUEST_FAILED: return .requestFailed(description) - case SOURCEKITD_ERROR_REQUEST_CANCELLED: return .requestCancelled - default: return .requestFailed(description) + case SOURCEKITD_ERROR_CONNECTION_INTERRUPTED: return .connectionInterrupted + case SOURCEKITD_ERROR_REQUEST_INVALID: return .requestInvalid(description) + case SOURCEKITD_ERROR_REQUEST_FAILED: return .requestFailed(description) + case SOURCEKITD_ERROR_REQUEST_CANCELLED: return .requestCancelled + default: return .requestFailed(description) } } diff --git a/Sources/SourceKitD/SKDResponseArray.swift b/Sources/SourceKitD/SKDResponseArray.swift index 6b6076d0e..876df15df 100644 --- a/Sources/SourceKitD/SKDResponseArray.swift +++ b/Sources/SourceKitD/SKDResponseArray.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Csourcekitd + #if canImport(Glibc) import Glibc #elseif canImport(Musl) diff --git a/Sources/SourceKitD/SKDResponseDictionary.swift b/Sources/SourceKitD/SKDResponseDictionary.swift index 55b0117b5..014c00509 100644 --- a/Sources/SourceKitD/SKDResponseDictionary.swift +++ b/Sources/SourceKitD/SKDResponseDictionary.swift @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// import Csourcekitd + #if canImport(Glibc) import Glibc #elseif canImport(Musl) diff --git a/Sources/SourceKitD/SourceKitD.swift b/Sources/SourceKitD/SourceKitD.swift index 5b5662f7a..977153387 100644 --- a/Sources/SourceKitD/SourceKitD.swift +++ b/Sources/SourceKitD/SourceKitD.swift @@ -11,11 +11,10 @@ //===----------------------------------------------------------------------===// @_exported import Csourcekitd - -import SKSupport -import LSPLogging import Dispatch import Foundation +import LSPLogging +import SKSupport /// Access to sourcekitd API, taking care of initialization, shutdown, and notification handler /// multiplexing. diff --git a/Sources/SourceKitD/SourceKitDImpl.swift b/Sources/SourceKitD/SourceKitDImpl.swift index f83081aa7..6bd4352f9 100644 --- a/Sources/SourceKitD/SourceKitDImpl.swift +++ b/Sources/SourceKitD/SourceKitDImpl.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SKSupport import Foundation +import SKSupport import struct TSCBasic.AbsolutePath @@ -99,7 +99,7 @@ public final class SourceKitDImpl: SourceKitD { /// Removes a previously registered notification handler. public func removeNotificationHandler(_ handler: SKDNotificationHandler) { lock.withLock { - _notificationHandlers.removeAll(where: { $0.value == nil || $0.value === handler}) + _notificationHandlers.removeAll(where: { $0.value == nil || $0.value === handler }) } } } diff --git a/Sources/SourceKitD/sourcekitd_functions.swift b/Sources/SourceKitD/sourcekitd_functions.swift index 668241f2f..c9b37c1b9 100644 --- a/Sources/SourceKitD/sourcekitd_functions.swift +++ b/Sources/SourceKitD/sourcekitd_functions.swift @@ -21,7 +21,7 @@ extension sourcekitd_functions_t { // MARK: Optional Methods self.variant_data_get_size = dlsym(sourcekitd, symbol: "sourcekitd_variant_data_get_size") - self.variant_data_get_ptr = dlsym(sourcekitd, symbol:"sourcekitd_variant_data_get_ptr") + self.variant_data_get_ptr = dlsym(sourcekitd, symbol: "sourcekitd_variant_data_get_ptr") // MARK: Required Methods diff --git a/Sources/SourceKitLSP/CapabilityRegistry.swift b/Sources/SourceKitLSP/CapabilityRegistry.swift index 6cac156c0..21d4c6cfd 100644 --- a/Sources/SourceKitLSP/CapabilityRegistry.swift +++ b/Sources/SourceKitLSP/CapabilityRegistry.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol /// Handler responsible for registering a capability with the client. public typealias ClientRegistrationHandler = (CapabilityRegistration) -> Void @@ -31,7 +31,7 @@ public final class CapabilityRegistry { /// Dynamically registered inlay hint options. private var inlayHint: [CapabilityRegistration: InlayHintRegistrationOptions] = [:] - + /// Dynamically registered pull diagnostics options. private var pullDiagnostics: [CapabilityRegistration: DiagnosticRegistrationOptions] = [:] @@ -66,7 +66,7 @@ public final class CapabilityRegistry { public var clientHasDynamicDocumentDiagnosticsRegistration: Bool { clientCapabilities.textDocument?.diagnostic?.dynamicRegistration == true } - + public var clientHasDynamicExecuteCommandRegistration: Bool { clientCapabilities.workspace?.executeCommand?.dynamicRegistration == true } @@ -94,17 +94,24 @@ public final class CapabilityRegistry { guard clientHasDynamicCompletionRegistration else { return } if let registration = registration(for: languages, in: completion) { if options != registration.completionOptions { - log("Unable to register new completion options \(options) for " + - "\(languages) due to pre-existing options \(registration.completionOptions)", level: .warning) + log( + """ + Unable to register new completion options \(options) for \ + "\(languages) due to pre-existing options \(registration.completionOptions) + """, + level: .warning + ) } return } let registrationOptions = CompletionRegistrationOptions( documentSelector: self.documentSelector(for: languages), - completionOptions: options) + completionOptions: options + ) let registration = CapabilityRegistration( method: CompletionRequest.method, - registerOptions: self.encode(registrationOptions)) + registerOptions: self.encode(registrationOptions) + ) self.completion[registration] = registrationOptions @@ -118,15 +125,20 @@ public final class CapabilityRegistry { guard clientHasDynamicDidChangeWatchedFilesRegistration else { return } if let registration = didChangeWatchedFiles { if watchers != registration.watchers { - log("Unable to register new file system watchers \(watchers) due to pre-existing options \(registration.watchers)", level: .warning) + log( + "Unable to register new file system watchers \(watchers) due to pre-existing options \(registration.watchers)", + level: .warning + ) } return } let registrationOptions = DidChangeWatchedFilesRegistrationOptions( - watchers: watchers) + watchers: watchers + ) let registration = CapabilityRegistration( method: DidChangeWatchedFilesNotification.method, - registerOptions: self.encode(registrationOptions)) + registerOptions: self.encode(registrationOptions) + ) self.didChangeWatchedFiles = registrationOptions @@ -144,17 +156,24 @@ public final class CapabilityRegistry { guard clientHasDynamicFoldingRangeRegistration else { return } if let registration = registration(for: languages, in: foldingRange) { if options != registration.foldingRangeOptions { - log("Unable to register new folding range options \(options) for " + - "\(languages) due to pre-existing options \(registration.foldingRangeOptions)", level: .warning) + log( + """ + Unable to register new folding range options \(options) for \ + \(languages) due to pre-existing options \(registration.foldingRangeOptions) + """, + level: .warning + ) } return } let registrationOptions = FoldingRangeRegistrationOptions( documentSelector: self.documentSelector(for: languages), - foldingRangeOptions: options) + foldingRangeOptions: options + ) let registration = CapabilityRegistration( method: FoldingRangeRequest.method, - registerOptions: self.encode(registrationOptions)) + registerOptions: self.encode(registrationOptions) + ) self.foldingRange[registration] = registrationOptions @@ -172,17 +191,24 @@ public final class CapabilityRegistry { guard clientHasDynamicSemanticTokensRegistration else { return } if let registration = registration(for: languages, in: semanticTokens) { if options != registration.semanticTokenOptions { - log("Unable to register new semantic tokens options \(options) for " + - "\(languages) due to pre-existing options \(registration.semanticTokenOptions)", level: .warning) + log( + """ + Unable to register new semantic tokens options \(options) for \ + \(languages) due to pre-existing options \(registration.semanticTokenOptions) + """, + level: .warning + ) } return } let registrationOptions = SemanticTokensRegistrationOptions( documentSelector: self.documentSelector(for: languages), - semanticTokenOptions: options) + semanticTokenOptions: options + ) let registration = CapabilityRegistration( method: SemanticTokensRegistrationOptions.method, - registerOptions: self.encode(registrationOptions)) + registerOptions: self.encode(registrationOptions) + ) self.semanticTokens[registration] = registrationOptions @@ -200,18 +226,25 @@ public final class CapabilityRegistry { guard clientHasDynamicInlayHintRegistration else { return } if let registration = registration(for: languages, in: inlayHint) { if options != registration.inlayHintOptions { - log("Unable to register new inlay hint options \(options) for " + - "\(languages) due to pre-existing options \(registration.inlayHintOptions)", level: .warning) + log( + """ + Unable to register new inlay hint options \(options) for \ + \(languages) due to pre-existing options \(registration.inlayHintOptions) + """, + level: .warning + ) } return } let registrationOptions = InlayHintRegistrationOptions( documentSelector: self.documentSelector(for: languages), - inlayHintOptions: options) + inlayHintOptions: options + ) let registration = CapabilityRegistration( method: InlayHintRequest.method, - registerOptions: self.encode(registrationOptions)) - + registerOptions: self.encode(registrationOptions) + ) + self.inlayHint[registration] = registrationOptions registerOnClient(registration) @@ -227,18 +260,25 @@ public final class CapabilityRegistry { guard clientHasDynamicDocumentDiagnosticsRegistration else { return } if let registration = registration(for: languages, in: pullDiagnostics) { if options != registration.diagnosticOptions { - log("Unable to register new pull diagnostics options \(options) for " + - "\(languages) due to pre-existing options \(registration.diagnosticOptions)", level: .warning) + log( + """ + Unable to register new pull diagnostics options \(options) for \ + \(languages) due to pre-existing options \(registration.diagnosticOptions) + """, + level: .warning + ) } return } let registrationOptions = DiagnosticRegistrationOptions( documentSelector: self.documentSelector(for: languages), - diagnosticOptions: options) + diagnosticOptions: options + ) let registration = CapabilityRegistration( method: DocumentDiagnosticsRequest.method, - registerOptions: self.encode(registrationOptions)) - + registerOptions: self.encode(registrationOptions) + ) + self.pullDiagnostics[registration] = registrationOptions registerOnClient(registration) @@ -263,7 +303,8 @@ public final class CapabilityRegistry { let registrationOptions = ExecuteCommandRegistrationOptions(commands: Array(newCommands)) let registration = CapabilityRegistration( method: ExecuteCommandRequest.method, - registerOptions: self.encode(registrationOptions)) + registerOptions: self.encode(registrationOptions) + ) registerOnClient(registration) } diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift b/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift index 3fe9726f8..609427f77 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageServer.swift @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// import Foundation +import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC -import LSPLogging import SKCore import SKSupport @@ -64,10 +64,10 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { /// Path to the clang binary. let clangPath: AbsolutePath? - + /// Path to the `clangd` binary. let clangdPath: AbsolutePath - + let clangdOptions: [String] /// The current state of the `clangd` language server. @@ -79,17 +79,17 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { } } } - + private var stateChangeHandlers: [(_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void] = [] - + /// The date at which `clangd` was last restarted. /// Used to delay restarting in case of a crash loop. private var lastClangdRestart: Date? - + /// Whether or not a restart of `clangd` has been scheduled. /// Used to make sure we are not restarting `clangd` twice. private var clangRestartScheduled = false - + /// The `InitializeRequest` with which `clangd` was originally initialized. /// Stored so we can replay the initialization when clangd crashes. private var initializeRequest: InitializeRequest? @@ -104,11 +104,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { private var openDocuments: [DocumentURI: Language] = [:] /// While `clangd` is running, its PID. -#if os(Windows) + #if os(Windows) private var hClangd: HANDLE = INVALID_HANDLE_VALUE -#else + #else private var clangdPid: Int32? -#endif + #endif /// Creates a language server for the given client referencing the clang binary specified in `toolchain`. /// Returns `nil` if `clangd` can't be found. @@ -134,7 +134,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { guard let workspace = workspace.value, let language = openDocuments[document] else { return nil } - guard let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile(for: document, language: language) else { + let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile( + for: document, + language: language + ) + guard let settings else { return nil } return ClangBuildSettings(settings.buildSettings, clangPath: clangdPath, isFallback: settings.isFallback) @@ -155,11 +159,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { /// /// - Parameter terminationStatus: The exit code of `clangd`. private func handleClangdTermination(terminationStatus: Int32) { -#if os(Windows) + #if os(Windows) self.hClangd = INVALID_HANDLE_VALUE -#else + #else self.clangdPid = nil -#endif + #endif if terminationStatus != 0 { self.state = .connectionInterrupted self.restartClangd() @@ -189,11 +193,12 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { let process = Foundation.Process() process.executableURL = clangdPath.asURL - process.arguments = [ - "-compile_args_from=lsp", // Provide compiler args programmatically. - "-background-index=false", // Disable clangd indexing, we use the build - "-index=false" // system index store instead. - ] + clangdOptions + process.arguments = + [ + "-compile_args_from=lsp", // Provide compiler args programmatically. + "-background-index=false", // Disable clangd indexing, we use the build + "-index=false", // system index store instead. + ] + clangdOptions process.standardOutput = clangdToUs process.standardInput = usToClangd @@ -206,11 +211,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { } } try process.run() -#if os(Windows) + #if os(Windows) self.hClangd = process.processHandle -#else + #else self.clangdPid = process.processIdentifier -#endif + #endif } /// Restart `clangd` after it has crashed. @@ -228,7 +233,10 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { let restartDelay: Int if let lastClangdRestart = self.lastClangdRestart, Date().timeIntervalSince(lastClangdRestart) < 30 { - log("clangd has already been restarted in the last 30 seconds. Delaying another restart by 10 seconds.", level: .info) + log( + "clangd has already been restarted in the last 30 seconds. Delaying another restart by 10 seconds.", + level: .info + ) restartDelay = 10 } else { restartDelay = 0 @@ -254,7 +262,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { } catch { log("Failed to restart clangd after a crash.", level: .error) } - } + } } /// Handler for notifications received **from** clangd, ie. **clangd** is @@ -285,9 +293,15 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { reply: @escaping (LSPResult) -> Void ) { clangdMessageHandlingQueue.async { - let request = Request(params, id: id, clientID: clientID, cancellation: CancellationToken(), reply: { result in - reply(result) - }) + let request = Request( + params, + id: id, + clientID: clientID, + cancellation: CancellationToken(), + reply: { result in + reply(result) + } + ) guard let sourceKitServer = await self.sourceKitServer else { // `SourceKitServer` has been destructed. We are tearing down the language // server. Nothing left to do. @@ -303,7 +317,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { } } } - + /// Forward the given request to `clangd`. /// /// This method calls `readyToHandleNextRequest` once the request has been @@ -327,7 +341,7 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { func _crash() { // Since `clangd` doesn't have a method to crash it, kill it. -#if os(Windows) + #if os(Windows) if self.hClangd != INVALID_HANDLE_VALUE { // FIXME(compnerd) this is a bad idea - we can potentially deadlock the // process if a kobject is a pending state. Unfortunately, the @@ -338,11 +352,11 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler { // deadlock and resource leaks. _ = TerminateProcess(self.hClangd, 0) } -#else + #else if let pid = self.clangdPid { kill(pid, SIGKILL) } -#endif + #endif } } @@ -459,13 +473,15 @@ extension ClangLanguageServerShim { // The compile command changed, send over the new one. // FIXME: what should we do if we no longer have valid build settings? - if - let compileCommand = clangBuildSettings?.compileCommand, - let pathString = (try? AbsolutePath(validating: url.path))?.pathString + if let compileCommand = clangBuildSettings?.compileCommand, + let pathString = (try? AbsolutePath(validating: url.path))?.pathString { - let note = DidChangeConfigurationNotification(settings: .clangd( - ClangWorkspaceSettings( - compilationDatabaseChanges: [pathString: compileCommand]))) + let note = DidChangeConfigurationNotification( + settings: .clangd( + ClangWorkspaceSettings( + compilationDatabaseChanges: [pathString: compileCommand]) + ) + ) clangd.send(note) } } @@ -477,13 +493,13 @@ extension ClangLanguageServerShim { let note = DidChangeTextDocumentNotification( textDocument: VersionedTextDocumentIdentifier(uri, version: 0), contentChanges: [], - forceRebuild: true) + forceRebuild: true + ) clangd.send(note) } // MARK: - Text Document - /// Returns true if the `ToolchainLanguageServer` will take ownership of the request. public func definition(_ req: DefinitionRequest) async throws -> LocationsOrLocationLinksResponse? { // We handle it to provide jump-to-header support for #import/#include. @@ -525,11 +541,15 @@ extension ClangLanguageServerShim { return try await forwardRequestToClangd(req) } - func documentSemanticTokensDelta(_ req: DocumentSemanticTokensDeltaRequest) async throws -> DocumentSemanticTokensDeltaResponse? { + func documentSemanticTokensDelta( + _ req: DocumentSemanticTokensDeltaRequest + ) async throws -> DocumentSemanticTokensDeltaResponse? { return try await forwardRequestToClangd(req) } - func documentSemanticTokensRange(_ req: DocumentSemanticTokensRangeRequest) async throws -> DocumentSemanticTokensResponse? { + func documentSemanticTokensRange( + _ req: DocumentSemanticTokensRangeRequest + ) async throws -> DocumentSemanticTokensResponse? { return try await forwardRequestToClangd(req) } @@ -587,7 +607,7 @@ private struct ClangBuildSettings: Equatable { if arguments.contains("-fmodules") { // Clangd is not built with support for the 'obj' format. arguments.append(contentsOf: [ - "-Xclang", "-fmodule-format=raw" + "-Xclang", "-fmodule-format=raw", ]) } if let workingDirectory = settings.workingDirectory { @@ -595,7 +615,7 @@ private struct ClangBuildSettings: Equatable { // database's "directory" field for relative -fmodules-cache-path. // rdar://63984913 arguments.append(contentsOf: [ - "-working-directory", workingDirectory + "-working-directory", workingDirectory, ]) } @@ -606,6 +626,8 @@ private struct ClangBuildSettings: Equatable { public var compileCommand: ClangCompileCommand { return ClangCompileCommand( - compilationCommand: self.compilerArgs, workingDirectory: self.workingDirectory) + compilationCommand: self.compilerArgs, + workingDirectory: self.workingDirectory + ) } } diff --git a/Sources/SourceKitLSP/DocumentManager.swift b/Sources/SourceKitLSP/DocumentManager.swift index fb2d6bcee..d4820465d 100644 --- a/Sources/SourceKitLSP/DocumentManager.swift +++ b/Sources/SourceKitLSP/DocumentManager.swift @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// import Dispatch -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKSupport /// An immutable snapshot of a document at a given time. @@ -140,7 +140,7 @@ public final class DocumentManager { /// - newVersion: The new version of the document. Must be greater than the /// latest version of the document. /// - edits: The edits to apply to the document - /// - willEditDocument: Optional closure to call before each edit. Will be + /// - willEditDocument: Optional closure to call before each edit. Will be /// called multiple times if there are multiple edits. /// - Returns: The snapshot of the document before the edit and the snapshot /// of the document after the edit. @@ -162,13 +162,14 @@ public final class DocumentManager { willEditDocument(document.latestLineTable, edit) } - if let range = edit.range { + if let range = edit.range { document.latestLineTable.replace( fromLine: range.lowerBound.line, utf16Offset: range.lowerBound.utf16index, toLine: range.upperBound.line, utf16Offset: range.upperBound.utf16index, - with: edit.text) + with: edit.text + ) } else { // Full text replacement. document.latestLineTable = LineTable(edit.text) diff --git a/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift b/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift index d67149d36..3c1ea1bce 100644 --- a/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift +++ b/Sources/SourceKitLSP/IndexStoreDB+MainFilesProvider.swift @@ -11,16 +11,18 @@ //===----------------------------------------------------------------------===// import IndexStoreDB -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKCore extension IndexStoreDB: MainFilesProvider { public func mainFilesContainingFile(_ uri: DocumentURI) -> Set { let mainFiles: Set if let url = uri.fileURL { - mainFiles = Set(self.mainFilesContainingFile(path: url.path) - .lazy.map({ DocumentURI(URL(fileURLWithPath: $0, isDirectory: false)) })) + mainFiles = Set( + self.mainFilesContainingFile(path: url.path) + .lazy.map({ DocumentURI(URL(fileURLWithPath: $0, isDirectory: false)) }) + ) } else { mainFiles = [] } diff --git a/Sources/SourceKitLSP/RangeAdjuster.swift b/Sources/SourceKitLSP/RangeAdjuster.swift index b5b172978..b6f0143ca 100644 --- a/Sources/SourceKitLSP/RangeAdjuster.swift +++ b/Sources/SourceKitLSP/RangeAdjuster.swift @@ -32,11 +32,10 @@ struct RangeAdjuster { let replacedLineCount = 1 + editRange.upperBound.line - editRange.lowerBound.line let newLines = edit.text.split(separator: "\n", omittingEmptySubsequences: false) - let upperUtf16IndexAfterEdit = ( - newLines.count == 1 ? editRange.lowerBound.utf16index : 0 - ) + newLines.last!.utf16.count + let upperUtf16IndexAfterEdit = + (newLines.count == 1 ? editRange.lowerBound.utf16index : 0) + newLines.last!.utf16.count self.lastLineCharDelta = upperUtf16IndexAfterEdit - editRange.upperBound.utf16index - self.lineDelta = newLines.count - replacedLineCount // may be negative + self.lineDelta = newLines.count - replacedLineCount // may be negative } /// Adjust the pre-edit `range` to the corresponding range in the post-edit document. @@ -48,7 +47,8 @@ struct RangeAdjuster { let lineOffset: Int let charOffset: Int if range.lowerBound.line == editRange.upperBound.line, - range.lowerBound.utf16index >= editRange.upperBound.utf16index { + range.lowerBound.utf16index >= editRange.upperBound.utf16index + { lineOffset = lineDelta charOffset = lastLineCharDelta } else if range.lowerBound.line > editRange.upperBound.line { @@ -58,8 +58,14 @@ struct RangeAdjuster { lineOffset = 0 charOffset = 0 } - let newStart = Position(line: range.lowerBound.line + lineOffset, utf16index: range.lowerBound.utf16index + charOffset) - let newEnd = Position(line: range.upperBound.line + lineOffset, utf16index: range.upperBound.utf16index + charOffset) + let newStart = Position( + line: range.lowerBound.line + lineOffset, + utf16index: range.lowerBound.utf16index + charOffset + ) + let newEnd = Position( + line: range.upperBound.line + lineOffset, + utf16index: range.upperBound.utf16index + charOffset + ) return newStart.. Void /// Creates a language server for the given client. - public init(client: Connection, fileSystem: FileSystem = localFileSystem, options: Options, onExit: @escaping () -> Void = {}) { + public init( + client: Connection, + fileSystem: FileSystem = localFileSystem, + options: Options, + onExit: @escaping () -> Void = {} + ) { self.fs = fileSystem self.toolchainRegistry = ToolchainRegistry.shared self.options = options @@ -267,7 +276,6 @@ public actor SourceKitServer { } } - /// Send the given notification to the editor. public func sendNotificationToClient(_ notification: some NotificationType) { client.send(notification) @@ -308,7 +316,7 @@ public actor SourceKitServer { return nil } - + /// After the language service has crashed, send `DidOpenTextDocumentNotification`s to a newly instantiated language service for previously open documents. func reopenDocuments(for languageService: ToolchainLanguageServer) async { for documentUri in self.documentManager.openDocuments { @@ -327,17 +335,22 @@ public actor SourceKitServer { let closeNotification = DidCloseTextDocumentNotification(textDocument: TextDocumentIdentifier(documentUri)) await self.closeDocument(closeNotification, workspace: workspace) - let textDocument = TextDocumentItem(uri: documentUri, - language: snapshot.language, - version: snapshot.version, - text: snapshot.text) + let textDocument = TextDocumentItem( + uri: documentUri, + language: snapshot.language, + version: snapshot.version, + text: snapshot.text + ) await self.openDocument(DidOpenTextDocumentNotification(textDocument: textDocument), workspace: workspace) } } /// If a language service of type `serverType` that can handle `workspace` has /// already been started, return it, otherwise return `nil`. - private func existingLanguageService(_ serverType: LanguageServerType, workspace: Workspace) -> ToolchainLanguageServer? { + private func existingLanguageService( + _ serverType: LanguageServerType, + workspace: Workspace + ) -> ToolchainLanguageServer? { for languageService in languageServices[serverType, default: []] { if languageService.canHandle(workspace: workspace) { return languageService @@ -373,17 +386,23 @@ public actor SourceKitServer { } let pid = Int(ProcessInfo.processInfo.processIdentifier) - let resp = try await service.initializeSync(InitializeRequest( - processId: pid, - rootPath: nil, - rootURI: workspace.rootUri, - initializationOptions: nil, - capabilities: workspace.capabilityRegistry.clientCapabilities, - trace: .off, - workspaceFolders: nil)) + let resp = try await service.initializeSync( + InitializeRequest( + processId: pid, + rootPath: nil, + rootURI: workspace.rootUri, + initializationOptions: nil, + capabilities: workspace.capabilityRegistry.clientCapabilities, + trace: .off, + workspaceFolders: nil + ) + ) let languages = languageClass(for: language) self.registerCapabilities( - for: resp.capabilities, languages: languages, registry: workspace.capabilityRegistry) + for: resp.capabilities, + languages: languages, + registry: workspace.capabilityRegistry + ) // FIXME: store the server capabilities. var syncKind: TextDocumentSyncKind @@ -417,17 +436,25 @@ public actor SourceKitServer { } /// **Public for testing purposes only** - public func _languageService(for uri: DocumentURI, _ language: Language, in workspace: Workspace) async -> ToolchainLanguageServer? { + public func _languageService( + for uri: DocumentURI, + _ language: Language, + in workspace: Workspace + ) async -> ToolchainLanguageServer? { return await languageService(for: uri, language, in: workspace) } - func languageService(for uri: DocumentURI, _ language: Language, in workspace: Workspace) async -> ToolchainLanguageServer? { + func languageService( + for uri: DocumentURI, + _ language: Language, + in workspace: Workspace + ) async -> ToolchainLanguageServer? { if let service = workspace.documentService[uri] { return service } guard let toolchain = toolchain(for: uri, language), - let service = await languageService(for: toolchain, language, in: workspace) + let service = await languageService(for: toolchain, language, in: workspace) else { return nil } @@ -458,7 +485,7 @@ extension SourceKitServer: MessageHandler { // // Technically, we could optimize this further by having an `AsyncQueue` for // each file, because edits on one file should not block requests on another - // file from executing but, at least in Swift, this would get us any real + // file from executing but, at least in Swift, this would get us any real // benefits at the moment because sourcekitd only has a single, global queue, // instead of a queue per file. // Additionally, usually you are editing one file in a source editor, which @@ -494,7 +521,12 @@ extension SourceKitServer: MessageHandler { } } - public nonisolated func handle(_ params: R, id: RequestID, from clientID: ObjectIdentifier, reply: @escaping (LSPResult) -> Void) { + public nonisolated func handle( + _ params: R, + id: RequestID, + from clientID: ObjectIdentifier, + reply: @escaping (LSPResult) -> Void + ) { // All of the requests sourcekit-lsp do not modify global state or require // the client to wait for the result before using the modified global state. // For example @@ -506,14 +538,20 @@ extension SourceKitServer: MessageHandler { messageHandlingQueue.async(barrier: false) { let cancellationToken = CancellationToken() - let request = Request(params, id: id, clientID: clientID, cancellation: cancellationToken, reply: { [weak self] result in - reply(result) - if let self { - Task { - await self._logResponse(result, id: id, method: R.method) + let request = Request( + params, + id: id, + clientID: clientID, + cancellation: cancellationToken, + reply: { [weak self] result in + reply(result) + if let self { + Task { + await self._logResponse(result, id: id, method: R.method) + } } } - }) + ) self._logRequest(request) @@ -537,7 +575,11 @@ extension SourceKitServer: MessageHandler { case let request as Request: await self.handleRequest(request, handler: self.subtypes) case let request as Request: - await self.handleRequest(for: request, requestHandler: self.completion, fallback: CompletionList(isIncomplete: false, items: [])) + await self.handleRequest( + for: request, + requestHandler: self.completion, + fallback: CompletionList(isIncomplete: false, items: []) + ) case let request as Request: await self.handleRequest(for: request, requestHandler: self.hover, fallback: nil) case let request as Request: @@ -577,7 +619,11 @@ extension SourceKitServer: MessageHandler { case let request as Request: await self.handleRequest(for: request, requestHandler: self.inlayHint, fallback: []) case let request as Request: - await self.handleRequest(for: request, requestHandler: self.documentDiagnostic, fallback: .full(.init(items: []))) + await self.handleRequest( + for: request, + requestHandler: self.documentDiagnostic, + fallback: .full(.init(items: [])) + ) default: reply(.failure(ResponseError.methodNotFound(R.method))) } @@ -608,10 +654,10 @@ extension SourceKitServer: MessageHandler { return "\(type(of: self)): Response<\(method)(\(id))>" } return """ - \(type(of: self)): Response<\(method)(\(id))>( - \(result) - ) - """ + \(type(of: self)): Response<\(method)(\(id))>( + \(result) + ) + """ } } } @@ -807,10 +853,12 @@ extension SourceKitServer { executeCommandOptions = ExecuteCommandOptions(commands: builtinSwiftCommands) } return ServerCapabilities( - textDocumentSync: .options(TextDocumentSyncOptions( - openClose: true, - change: .incremental - )), + textDocumentSync: .options( + TextDocumentSyncOptions( + openClose: true, + change: .incremental + ) + ), hoverProvider: .bool(true), completionProvider: completionOptions, definitionProvider: .bool(true), @@ -819,19 +867,23 @@ extension SourceKitServer { documentHighlightProvider: .bool(true), documentSymbolProvider: .bool(true), workspaceSymbolProvider: .bool(true), - codeActionProvider: .value(CodeActionServerCapabilities( - clientCapabilities: client.textDocument?.codeAction, - codeActionOptions: CodeActionOptions(codeActionKinds: nil), - supportsCodeActions: true - )), + codeActionProvider: .value( + CodeActionServerCapabilities( + clientCapabilities: client.textDocument?.codeAction, + codeActionOptions: CodeActionOptions(codeActionKinds: nil), + supportsCodeActions: true + ) + ), colorProvider: .bool(true), foldingRangeProvider: .bool(!registry.clientHasDynamicFoldingRangeRegistration), declarationProvider: .bool(true), executeCommandProvider: executeCommandOptions, - workspace: WorkspaceServerCapabilities(workspaceFolders: .init( - supported: true, - changeNotifications: .bool(true) - )), + workspace: WorkspaceServerCapabilities( + workspaceFolders: .init( + supported: true, + changeNotifications: .bool(true) + ) + ), callHierarchyProvider: .bool(true), typeHierarchyProvider: .bool(true) ) @@ -858,7 +910,8 @@ extension SourceKitServer { } } if let inlayHintProvider = server.inlayHintProvider, - inlayHintProvider.isSupported { + inlayHintProvider.isSupported + { let options: InlayHintOptions switch inlayHintProvider { case .bool(_): @@ -937,7 +990,6 @@ extension SourceKitServer { } } - func shutdown(_ request: ShutdownRequest) async throws -> VoidResponse { await prepareForExit() @@ -1026,7 +1078,10 @@ extension SourceKitServer { let uri = notification.textDocument.uri guard let workspace = await workspaceForDocument(uri: uri) else { - log("received change notification for file '\(uri)' without a corresponding workspace, ignoring...", level: .error) + log( + "received change notification for file '\(uri)' without a corresponding workspace, ignoring...", + level: .error + ) return } @@ -1089,17 +1144,25 @@ extension SourceKitServer { continue } if let oldWorkspace = oldWorkspace { - await self.closeDocument(DidCloseTextDocumentNotification( - textDocument: TextDocumentIdentifier(docUri) - ), workspace: oldWorkspace) + await self.closeDocument( + DidCloseTextDocumentNotification( + textDocument: TextDocumentIdentifier(docUri) + ), + workspace: oldWorkspace + ) } if let newWorkspace = newWorkspace { - await self.openDocument(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: docUri, - language: snapshot.language, - version: snapshot.version, - text: snapshot.text - )), workspace: newWorkspace) + await self.openDocument( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: docUri, + language: snapshot.language, + version: snapshot.version, + text: snapshot.text + ) + ), + workspace: newWorkspace + ) } } } @@ -1133,7 +1196,7 @@ extension SourceKitServer { ) async throws -> HoverResponse? { return try await languageService.hover(req) } - + func openInterface( _ req: OpenInterfaceRequest, workspace: Workspace, @@ -1181,23 +1244,27 @@ extension SourceKitServer { func workspaceSymbols(_ req: WorkspaceSymbolsRequest) async throws -> [WorkspaceSymbolItem]? { let symbols = findWorkspaceSymbols( matching: req.query - ).map({symbolOccurrence -> WorkspaceSymbolItem in + ).map({ symbolOccurrence -> WorkspaceSymbolItem in let symbolPosition = Position( - line: symbolOccurrence.location.line - 1, // 1-based -> 0-based + line: symbolOccurrence.location.line - 1, // 1-based -> 0-based // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! - utf16index: symbolOccurrence.location.utf8Column - 1) + utf16index: symbolOccurrence.location.utf8Column - 1 + ) let symbolLocation = Location( uri: DocumentURI(URL(fileURLWithPath: symbolOccurrence.location.path)), - range: Range(symbolPosition)) - - return .symbolInformation(SymbolInformation( - name: symbolOccurrence.symbol.name, - kind: symbolOccurrence.symbol.kind.asLspSymbolKind(), - deprecated: nil, - location: symbolLocation, - containerName: symbolOccurrence.getContainerName() - )) + range: Range(symbolPosition) + ) + + return .symbolInformation( + SymbolInformation( + name: symbolOccurrence.symbol.name, + kind: symbolOccurrence.symbol.kind.asLspSymbolKind(), + deprecated: nil, + location: symbolLocation, + containerName: symbolOccurrence.getContainerName() + ) + ) }) return symbols } @@ -1231,7 +1298,7 @@ extension SourceKitServer { _ req: DocumentSymbolRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> DocumentSymbolResponse?{ + ) async throws -> DocumentSymbolResponse? { return try await languageService.documentSymbol(req) } @@ -1239,7 +1306,7 @@ extension SourceKitServer { _ req: DocumentColorRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> [ColorInformation]{ + ) async throws -> [ColorInformation] { return try await languageService.documentColor(req) } @@ -1247,7 +1314,7 @@ extension SourceKitServer { _ req: DocumentSemanticTokensRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> DocumentSemanticTokensResponse?{ + ) async throws -> DocumentSemanticTokensResponse? { return try await languageService.documentSemanticTokens(req) } @@ -1255,7 +1322,7 @@ extension SourceKitServer { _ req: DocumentSemanticTokensDeltaRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> DocumentSemanticTokensDeltaResponse?{ + ) async throws -> DocumentSemanticTokensDeltaResponse? { return try await languageService.documentSemanticTokensDelta(req) } @@ -1263,7 +1330,7 @@ extension SourceKitServer { _ req: DocumentSemanticTokensRangeRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> DocumentSemanticTokensResponse?{ + ) async throws -> DocumentSemanticTokensResponse? { return try await languageService.documentSemanticTokensRange(req) } @@ -1315,31 +1382,33 @@ extension SourceKitServer { _ req: DocumentDiagnosticsRequest, workspace: Workspace, languageService: ToolchainLanguageServer - ) async throws -> DocumentDiagnosticReport{ + ) async throws -> DocumentDiagnosticReport { return try await languageService.documentDiagnostic(req) } /// Converts a location from the symbol index to an LSP location. - /// + /// /// - Parameter location: The symbol index location /// - Returns: The LSP location private func indexToLSPLocation(_ location: SymbolLocation) -> Location? { guard !location.path.isEmpty else { return nil } return Location( uri: DocumentURI(URL(fileURLWithPath: location.path)), - range: Range(Position( - // 1-based -> 0-based - // Note that we still use max(0, ...) as a fallback if the location is zero. - line: max(0, location.line - 1), - // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! - utf16index: max(0, location.utf8Column - 1) - )) + range: Range( + Position( + // 1-based -> 0-based + // Note that we still use max(0, ...) as a fallback if the location is zero. + line: max(0, location.line - 1), + // FIXME: we need to convert the utf8/utf16 column, which may require reading the file! + utf16index: max(0, location.utf8Column - 1) + ) + ) ) } /// Extracts the locations of an indexed symbol's occurrences, /// e.g. for definition or reference lookups. - /// + /// /// - Parameters: /// - result: The symbol to look up /// - index: The index in which the occurrences will be looked up @@ -1399,10 +1468,16 @@ extension SourceKitServer { let index = await self.workspaceForDocument(uri: req.textDocument.uri)?.index // If this symbol is a module then generate a textual interface if let symbol = symbols.first, symbol.kind == .module, let name = symbol.name { - return try await self.definitionInInterface(req, moduleName: name, symbolUSR: nil, languageService: languageService) + return try await self.definitionInInterface( + req, + moduleName: name, + symbolUSR: nil, + languageService: languageService + ) } - let resolved = self.extractIndexedOccurrences(symbols: symbols, index: index, useLocalFallback: true) { (usr, index) in + let resolved = self.extractIndexedOccurrences(symbols: symbols, index: index, useLocalFallback: true) { + (usr, index) in log("performing indexed jump-to-def with usr \(usr)") var occurs = index.occurrences(ofUSR: usr, roles: [.definition]) if occurs.isEmpty { @@ -1414,8 +1489,9 @@ extension SourceKitServer { // if first resolved location is in `.swiftinterface` file. Use moduleName to return // textual interface if let firstResolved = resolved.first, - let moduleName = firstResolved.occurrence?.location.moduleName, - firstResolved.location.uri.fileURL?.pathExtension == "swiftinterface" { + let moduleName = firstResolved.occurrence?.location.moduleName, + firstResolved.location.uri.fileURL?.pathExtension == "swiftinterface" + { return try await self.definitionInInterface( req, moduleName: moduleName, @@ -1546,13 +1622,14 @@ extension SourceKitServer { /// Extracts our implementation-specific data about a call hierarchy /// item as encoded in `indexToLSPCallHierarchyItem`. - /// + /// /// - Parameter data: The opaque data structure to extract /// - Returns: The extracted data if successful or nil otherwise private nonisolated func extractCallHierarchyItemData(_ rawData: LSPAny?) -> (uri: DocumentURI, usr: String)? { guard case let .dictionary(data) = rawData, - case let .string(uriString) = data["uri"], - case let .string(usr) = data["usr"] else { + case let .string(uriString) = data["uri"], + case let .string(usr) = data["usr"] + else { return nil } return ( @@ -1563,13 +1640,15 @@ extension SourceKitServer { func incomingCalls(_ req: CallHierarchyIncomingCallsRequest) async throws -> [CallHierarchyIncomingCall]? { guard let data = extractCallHierarchyItemData(req.item.data), - let index = await self.workspaceForDocument(uri: data.uri)?.index else { + let index = await self.workspaceForDocument(uri: data.uri)?.index + else { return [] } let occurs = index.occurrences(ofUSR: data.usr, roles: .calledBy) let calls = occurs.compactMap { occurrence -> CallHierarchyIncomingCall? in guard let location = indexToLSPLocation(occurrence.location), - let related = occurrence.relations.first else { + let related = occurrence.relations.first + else { return nil } @@ -1582,7 +1661,7 @@ extension SourceKitServer { from: indexToLSPCallHierarchyItem( symbol: related.symbol, moduleName: definitionSymbolLocation?.moduleName, - location: definitionLocation ?? location // Use occurrence location as fallback + location: definitionLocation ?? location // Use occurrence location as fallback ), fromRanges: [location.range] ) @@ -1592,7 +1671,8 @@ extension SourceKitServer { func outgoingCalls(_ req: CallHierarchyOutgoingCallsRequest) async throws -> [CallHierarchyOutgoingCall]? { guard let data = extractCallHierarchyItemData(req.item.data), - let index = await self.workspaceForDocument(uri: data.uri)?.index else { + let index = await self.workspaceForDocument(uri: data.uri)?.index + else { return [] } let occurs = index.occurrences(relatedToUSR: data.usr, roles: .calledBy) @@ -1610,7 +1690,7 @@ extension SourceKitServer { to: indexToLSPCallHierarchyItem( symbol: occurrence.symbol, moduleName: definitionSymbolLocation?.moduleName, - location: definitionLocation ?? location // Use occurrence location as fallback + location: definitionLocation ?? location // Use occurrence location as fallback ), fromRanges: [location.range] ) @@ -1637,9 +1717,8 @@ extension SourceKitServer { name = "\(symbol.name): \(conformances.map(\.symbol.name).joined(separator: ", "))" } // Add the file name and line to the detail string - if - let url = location.uri.fileURL, - let basename = (try? AbsolutePath(validating: url.path))?.basename + if let url = location.uri.fileURL, + let basename = (try? AbsolutePath(validating: url.path))?.basename { detail = "Extension at \(basename):\(location.range.lowerBound.line + 1)" } else if let moduleName = moduleName { @@ -1701,13 +1780,14 @@ extension SourceKitServer { /// Extracts our implementation-specific data about a type hierarchy /// item as encoded in `indexToLSPTypeHierarchyItem`. - /// + /// /// - Parameter data: The opaque data structure to extract /// - Returns: The extracted data if successful or nil otherwise private nonisolated func extractTypeHierarchyItemData(_ rawData: LSPAny?) -> (uri: DocumentURI, usr: String)? { guard case let .dictionary(data) = rawData, - case let .string(uriString) = data["uri"], - case let .string(usr) = data["usr"] else { + case let .string(uriString) = data["uri"], + case let .string(usr) = data["usr"] + else { return nil } return ( @@ -1718,7 +1798,8 @@ extension SourceKitServer { func supertypes(_ req: TypeHierarchySupertypesRequest) async throws -> [TypeHierarchyItem]? { guard let data = extractTypeHierarchyItemData(req.item.data), - let index = await self.workspaceForDocument(uri: data.uri)?.index else { + let index = await self.workspaceForDocument(uri: data.uri)?.index + else { return [] } @@ -1749,7 +1830,7 @@ extension SourceKitServer { return indexToLSPTypeHierarchyItem( symbol: occurrence.symbol, moduleName: definitionSymbolLocation?.moduleName, - location: definitionLocation ?? location, // Use occurrence location as fallback + location: definitionLocation ?? location, // Use occurrence location as fallback index: index ) } @@ -1758,7 +1839,8 @@ extension SourceKitServer { func subtypes(_ req: TypeHierarchySubtypesRequest) async throws -> [TypeHierarchyItem]? { guard let data = extractTypeHierarchyItemData(req.item.data), - let index = await self.workspaceForDocument(uri: data.uri)?.index else { + let index = await self.workspaceForDocument(uri: data.uri)?.index + else { return [] } @@ -1768,7 +1850,8 @@ extension SourceKitServer { // Convert occurrences to type hierarchy items let types = occurs.compactMap { occurrence -> TypeHierarchyItem? in guard let location = indexToLSPLocation(occurrence.location), - let related = occurrence.relations.first else { + let related = occurrence.relations.first + else { return nil } @@ -1780,7 +1863,7 @@ extension SourceKitServer { return indexToLSPTypeHierarchyItem( symbol: related.symbol, moduleName: definitionSymbolLocation?.moduleName, - location: definitionLocation ?? location, // Use occurrence location as fallback + location: definitionLocation ?? location, // Use occurrence location as fallback index: index ) } @@ -1819,25 +1902,25 @@ public typealias Diagnostic = LanguageServerProtocol.Diagnostic extension IndexSymbolKind { func asLspSymbolKind() -> SymbolKind { switch self { - case .class: + case .class: return .class - case .classMethod, .instanceMethod, .staticMethod: + case .classMethod, .instanceMethod, .staticMethod: return .method - case .instanceProperty, .staticProperty, .classProperty: + case .instanceProperty, .staticProperty, .classProperty: return .property - case .enum: + case .enum: return .enum - case .enumConstant: + case .enumConstant: return .enumMember - case .protocol: + case .protocol: return .interface - case .function, .conversionFunction: + case .function, .conversionFunction: return .function - case .variable: + case .variable: return .variable - case .struct: + case .struct: return .struct - case .parameter: + case .parameter: return .typeParameter default: diff --git a/Sources/SourceKitLSP/Swift/CodeCompletion.swift b/Sources/SourceKitLSP/Swift/CodeCompletion.swift index 60d928496..f583957f0 100644 --- a/Sources/SourceKitLSP/Swift/CodeCompletion.swift +++ b/Sources/SourceKitLSP/Swift/CodeCompletion.swift @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol +import Foundation import LSPLogging +import LanguageServerProtocol import SourceKitD -import Foundation extension SwiftLanguageServer { @@ -36,9 +36,21 @@ extension SwiftLanguageServer { let options = req.sourcekitlspOptions ?? serverOptions.completionOptions if options.serverSideFiltering { - return try await completionWithServerFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options) + return try await completionWithServerFiltering( + offset: offset, + completionPos: completionPos, + snapshot: snapshot, + request: req, + options: options + ) } else { - return try await completionWithClientFiltering(offset: offset, completionPos: completionPos, snapshot: snapshot, request: req, options: options) + return try await completionWithClientFiltering( + offset: offset, + completionPos: completionPos, + snapshot: snapshot, + request: req, + options: options + ) } } @@ -50,7 +62,8 @@ extension SwiftLanguageServer { options: SKCompletionOptions ) async throws -> CompletionList { guard let start = snapshot.indexOf(utf8Offset: offset), - let end = snapshot.index(of: req.position) else { + let end = snapshot.index(of: req.position) + else { log("invalid completion position \(req.position)") return CompletionList(isIncomplete: true, items: []) } @@ -64,7 +77,10 @@ extension SwiftLanguageServer { throw ResponseError.serverCancelled } guard currentSession.uri == snapshot.uri, currentSession.utf8StartOffset == offset else { - log("triggerFromIncompleteCompletions with incompatible completion session; expected \(currentSession.uri)@\(currentSession.utf8StartOffset), but got \(snapshot.uri)@\(offset)", level: .warning) + log( + "triggerFromIncompleteCompletions with incompatible completion session; expected \(currentSession.uri)@\(currentSession.utf8StartOffset), but got \(snapshot.uri)@\(offset)", + level: .warning + ) throw ResponseError.serverCancelled } session = currentSession @@ -76,7 +92,8 @@ extension SwiftLanguageServer { snapshot: snapshot, utf8Offset: offset, position: completionPos, - compileCommand: await buildSettings(for: snapshot.uri)) + compileCommand: await buildSettings(for: snapshot.uri) + ) await currentCompletionSession?.close() currentCompletionSession = session @@ -115,7 +132,13 @@ extension SwiftLanguageServer { try Task.checkCancellation() - return self.completionsFromSKDResponse(completions, in: snapshot, completionPos: completionPos, requestPosition: req.position, isIncomplete: false) + return self.completionsFromSKDResponse( + completions, + in: snapshot, + completionPos: completionPos, + requestPosition: req.position, + isIncomplete: false + ) } nonisolated func completionsFromSKDResponse( @@ -129,7 +152,7 @@ extension SwiftLanguageServer { completions.forEach { (i, value) -> Bool in guard let name: String = value[self.keys.description] else { - return true // continue + return true // continue } var filterName: String? = value[self.keys.name] @@ -148,15 +171,27 @@ extension SwiftLanguageServer { if let text = text { let utf8CodeUnitsToErase: Int = value[self.keys.num_bytes_to_erase] ?? 0 - textEdit = self.computeCompletionTextEdit(completionPos: completionPos, requestPosition: requestPosition, utf8CodeUnitsToErase: utf8CodeUnitsToErase, newText: text, snapshot: snapshot) + textEdit = self.computeCompletionTextEdit( + completionPos: completionPos, + requestPosition: requestPosition, + utf8CodeUnitsToErase: utf8CodeUnitsToErase, + newText: text, + snapshot: snapshot + ) if utf8CodeUnitsToErase != 0, filterName != nil, let textEdit = textEdit { // To support the case where the client is doing prefix matching on the TextEdit range, // we need to prepend the deleted text to filterText. // This also works around a behaviour in VS Code that causes completions to not show up // if a '.' is being replaced for Optional completion. - let startIndex = snapshot.lineTable.stringIndexOf(line: textEdit.range.lowerBound.line, utf16Column: textEdit.range.lowerBound.utf16index)! - let endIndex = snapshot.lineTable.stringIndexOf(line: completionPos.line, utf16Column: completionPos.utf16index)! + let startIndex = snapshot.lineTable.stringIndexOf( + line: textEdit.range.lowerBound.line, + utf16Column: textEdit.range.lowerBound.utf16index + )! + let endIndex = snapshot.lineTable.stringIndexOf( + line: completionPos.line, + utf16Column: completionPos.utf16index + )! let filterPrefix = snapshot.text[startIndex.. TextEdit { + private nonisolated func computeCompletionTextEdit( + completionPos: Position, + requestPosition: Position, + utf8CodeUnitsToErase: Int, + newText: String, + snapshot: DocumentSnapshot + ) -> TextEdit { let textEditRangeStart: Position // Compute the TextEdit @@ -241,7 +284,10 @@ extension SwiftLanguageServer { assert(completionPos.line == requestPosition.line) // Construct a string index for the edit range start by subtracting the UTF-8 code units to erase from the completion position. let line = snapshot.lineTable[completionPos.line] - let completionPosStringIndex = snapshot.lineTable.stringIndexOf(line: completionPos.line, utf16Column: completionPos.utf16index)! + let completionPosStringIndex = snapshot.lineTable.stringIndexOf( + line: completionPos.line, + utf16Column: completionPos.utf16index + )! let deletionStartStringIndex = line.utf8.index(completionPosStringIndex, offsetBy: -utf8CodeUnitsToErase) // Compute the UTF-16 offset of the deletion start range. If the start lies in a previous line, this will be negative diff --git a/Sources/SourceKitLSP/Swift/CodeCompletionSession.swift b/Sources/SourceKitLSP/Swift/CodeCompletionSession.swift index 46809b242..34a56d11f 100644 --- a/Sources/SourceKitLSP/Swift/CodeCompletionSession.swift +++ b/Sources/SourceKitLSP/Swift/CodeCompletionSession.swift @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol +import Dispatch import LSPLogging +import LanguageServerProtocol import SourceKitD -import Dispatch /// Represents a code-completion session for a given source location that can be efficiently /// re-filtered by calling `update()`. @@ -48,8 +48,8 @@ actor CodeCompletionSession { snapshot: DocumentSnapshot, utf8Offset: Int, position: Position, - compileCommand: SwiftCompileCommand?) - { + compileCommand: SwiftCompileCommand? + ) { self.server = server self.snapshot = snapshot self.utf8StartOffset = utf8Offset @@ -145,7 +145,13 @@ actor CodeCompletionSession { return CompletionList(isIncomplete: false, items: []) } - return self.server.completionsFromSKDResponse(completions, in: snapshot, completionPos: self.position, requestPosition: position, isIncomplete: true) + return self.server.completionsFromSKDResponse( + completions, + in: snapshot, + completionPos: self.position, + requestPosition: position, + isIncomplete: true + ) } private func optionsDictionary( @@ -185,12 +191,12 @@ actor CodeCompletionSession { queue.async { switch self.state { - case .closed: - // Already closed, nothing to do. - break - case .open: - self.sendClose(server) - self.state = .closed + case .closed: + // Already closed, nothing to do. + break + case .open: + self.sendClose(server) + self.state = .closed } } } diff --git a/Sources/SourceKitLSP/Swift/CommentXML.swift b/Sources/SourceKitLSP/Swift/CommentXML.swift index ab818d1b1..b6f78b567 100644 --- a/Sources/SourceKitLSP/Swift/CommentXML.swift +++ b/Sources/SourceKitLSP/Swift/CommentXML.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SKSupport import Foundation +import SKSupport #if canImport(FoundationXML) import FoundationXML @@ -78,7 +78,7 @@ private struct XMLToMarkdown { out += "```swift\n" toMarkdown(node.children) out += "\n```\n\n---\n" - + case "Name", "USR", "Direction": break @@ -120,7 +120,7 @@ private struct XMLToMarkdown { toMarkdown(discussion.children) inParam = false } - // FIXME: closure parameters would go here. + // FIXME: closure parameters would go here. case "CodeListing": lineNumber = 0 @@ -160,7 +160,7 @@ private struct XMLToMarkdown { case "Link": if let href = node.attributes?.first(where: { $0.name == "href" })?.stringValue { - out += "[" + out += "[" toMarkdown(node.children) out += "](\(href))" } else { diff --git a/Sources/SourceKitLSP/Swift/CursorInfo.swift b/Sources/SourceKitLSP/Swift/CursorInfo.swift index 7f4375937..95ab32d44 100644 --- a/Sources/SourceKitLSP/Swift/CursorInfo.swift +++ b/Sources/SourceKitLSP/Swift/CursorInfo.swift @@ -35,10 +35,15 @@ struct CursorInfo { /// The refactor actions available at this position. var refactorActions: [SemanticRefactorCommand]? = nil - init(_ symbolInfo: SymbolDetails, annotatedDeclaration: String?, documentationXML: String?, refactorActions: [SemanticRefactorCommand]? = nil) { + init( + _ symbolInfo: SymbolDetails, + annotatedDeclaration: String?, + documentationXML: String?, + refactorActions: [SemanticRefactorCommand]? = nil + ) { self.symbolInfo = symbolInfo self.annotatedDeclaration = annotatedDeclaration - self.documentationXML = documentationXML + self.documentationXML = documentationXML self.refactorActions = refactorActions } } @@ -86,8 +91,8 @@ extension SwiftLanguageServer { additionalParameters appendAdditionalParameters: ((SKDRequestDictionary) -> Void)? = nil ) async throws -> CursorInfo? { guard let snapshot = documentManager.latestSnapshot(uri) else { - throw CursorInfoError.unknownDocument(uri) - } + throw CursorInfoError.unknownDocument(uri) + } guard let offsetRange = snapshot.utf8OffsetRange(of: range) else { throw CursorInfoError.invalidRange(range) @@ -119,8 +124,8 @@ extension SwiftLanguageServer { var location: Location? = nil if let filepath: String = dict[keys.filepath], - let offset: Int = dict[keys.offset], - let pos = snapshot.positionOf(utf8Offset: offset) + let offset: Int = dict[keys.offset], + let pos = snapshot.positionOf(utf8Offset: offset) { location = Location(uri: DocumentURI(URL(fileURLWithPath: filepath)), range: Range(pos)) } diff --git a/Sources/SourceKitLSP/Swift/Diagnostic.swift b/Sources/SourceKitLSP/Swift/Diagnostic.swift index 59fa89716..914457757 100644 --- a/Sources/SourceKitLSP/Swift/Diagnostic.swift +++ b/Sources/SourceKitLSP/Swift/Diagnostic.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKSupport import SourceKitD @@ -44,10 +44,10 @@ extension CodeAction { title = fromNote } else { guard let startIndex = snapshot.index(of: edits[0].range.lowerBound), - let endIndex = snapshot.index(of: edits[0].range.upperBound), - startIndex <= endIndex, - snapshot.text.indices.contains(startIndex), - endIndex <= snapshot.text.endIndex + let endIndex = snapshot.index(of: edits[0].range.upperBound), + startIndex <= endIndex, + snapshot.text.indices.contains(startIndex), + endIndex <= snapshot.text.endIndex else { logAssertionFailure("position mapped, but indices failed for edit range \(edits[0])") return nil @@ -65,7 +65,8 @@ extension CodeAction { title: title, kind: .quickFix, diagnostics: nil, - edit: WorkspaceEdit(changes: [snapshot.uri:edits])) + edit: WorkspaceEdit(changes: [snapshot.uri: edits]) + ) } /// Describe a fixit's edit briefly. @@ -91,15 +92,18 @@ extension TextEdit { init?(fixit: SKDResponseDictionary, in snapshot: DocumentSnapshot) { let keys = fixit.sourcekitd.keys if let utf8Offset: Int = fixit[keys.offset], - let length: Int = fixit[keys.length], - let replacement: String = fixit[keys.sourcetext], - let position = snapshot.positionOf(utf8Offset: utf8Offset), - let endPosition = snapshot.positionOf(utf8Offset: utf8Offset + length), - length > 0 || !replacement.isEmpty + let length: Int = fixit[keys.length], + let replacement: String = fixit[keys.sourcetext], + let position = snapshot.positionOf(utf8Offset: utf8Offset), + let endPosition = snapshot.positionOf(utf8Offset: utf8Offset + length), + length > 0 || !replacement.isEmpty { // Snippets are only suppored in code completion. // Remove SourceKit placeholders from Fix-Its because they can't be represented in the editor properly. - let replacementWithoutPlaceholders = rewriteSourceKitPlaceholders(inString: replacement, clientSupportsSnippets: false) + let replacementWithoutPlaceholders = rewriteSourceKitPlaceholders( + inString: replacement, + clientSupportsSnippets: false + ) // If both the replacement without placeholders and the fixit are empty, no TextEdit should be created. if (replacementWithoutPlaceholders.isEmpty && length == 0) { @@ -116,9 +120,11 @@ extension TextEdit { extension Diagnostic { /// Creates a diagnostic from a sourcekitd response dictionary. - init?(_ diag: SKDResponseDictionary, - in snapshot: DocumentSnapshot, - useEducationalNoteAsCode: Bool) { + init?( + _ diag: SKDResponseDictionary, + in snapshot: DocumentSnapshot, + useEducationalNoteAsCode: Bool + ) { // FIXME: this assumes that the diagnostics are all in the same file. let keys = diag.sourcekitd.keys @@ -128,8 +134,8 @@ extension Diagnostic { var range: Range? = nil if let line: Int = diag[keys.line], - let utf8Column: Int = diag[keys.column], - line > 0, utf8Column > 0 + let utf8Column: Int = diag[keys.column], + line > 0, utf8Column > 0 { range = snapshot.positionOf(zeroBasedLine: line - 1, utf8Column: utf8Column - 1).map(Range.init) } else if let utf8Offset: Int = diag[keys.offset] { @@ -139,10 +145,10 @@ extension Diagnostic { // If the diagnostic has a range associated with it that starts at the same location as the diagnostics position, use it to retrieve a proper range for the diagnostic, instead of just reporting a zero-length range. (diag[keys.ranges] as SKDResponseArray?)?.forEach { index, skRange in if let utf8Offset: Int = skRange[keys.offset], - let start = snapshot.positionOf(utf8Offset: utf8Offset), - start == range?.lowerBound, - let length: Int = skRange[keys.length], - let end = snapshot.positionOf(utf8Offset: utf8Offset + length) + let start = snapshot.positionOf(utf8Offset: utf8Offset), + start == range?.lowerBound, + let length: Int = skRange[keys.length], + let end = snapshot.positionOf(utf8Offset: utf8Offset + length) { range = start.. 0, - let primaryPath = educationalNotePaths[0] + let educationalNotePaths: SKDResponseArray = diag[keys.educational_note_paths], + educationalNotePaths.count > 0, + let primaryPath = educationalNotePaths[0] { let url = URL(fileURLWithPath: primaryPath) let name = url.deletingPathExtension().lastPathComponent @@ -186,7 +192,8 @@ extension Diagnostic { var actions: [CodeAction]? = nil if let skfixits: SKDResponseArray = diag[keys.fixits], - let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: nil) { + let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: nil) + { actions = [action] } @@ -224,7 +231,8 @@ extension Diagnostic { message: message, tags: tags, relatedInformation: notes, - codeActions: actions) + codeActions: actions + ) } } @@ -236,8 +244,8 @@ extension DiagnosticRelatedInformation { var position: Position? = nil if let line: Int = diag[keys.line], - let utf8Column: Int = diag[keys.column], - line > 0, utf8Column > 0 + let utf8Column: Int = diag[keys.column], + line > 0, utf8Column > 0 { position = snapshot.positionOf(zeroBasedLine: line - 1, utf8Column: utf8Column - 1) } else if let utf8Offset: Int = diag[keys.offset] { @@ -252,14 +260,16 @@ extension DiagnosticRelatedInformation { var actions: [CodeAction]? = nil if let skfixits: SKDResponseArray = diag[keys.fixits], - let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: message) { + let action = CodeAction(fixits: skfixits, in: snapshot, fromNote: message) + { actions = [action] } self.init( location: Location(uri: snapshot.uri, range: Range(position!)), message: message, - codeActions: actions) + codeActions: actions + ) } } @@ -284,13 +294,19 @@ struct CachedDiagnostic { } extension CachedDiagnostic { - init?(_ diag: SKDResponseDictionary, - in snapshot: DocumentSnapshot, - useEducationalNoteAsCode: Bool) { + init?( + _ diag: SKDResponseDictionary, + in snapshot: DocumentSnapshot, + useEducationalNoteAsCode: Bool + ) { let sk = diag.sourcekitd - guard let diagnostic = Diagnostic(diag, - in: snapshot, - useEducationalNoteAsCode: useEducationalNoteAsCode) else { + guard + let diagnostic = Diagnostic( + diag, + in: snapshot, + useEducationalNoteAsCode: useEducationalNoteAsCode + ) + else { return nil } self.diagnostic = diagnostic @@ -317,11 +333,11 @@ func mergeDiagnostics( return isFallback ? old.filter { $0.stage == .parse } : new } -#if DEBUG + #if DEBUG if let sema = new.first(where: { $0.stage == .sema }) { log("unexpected semantic diagnostic in parse diagnostics \(sema.diagnostic)", level: .warning) } -#endif + #endif return new.filter { $0.stage == .parse } + old.filter { $0.stage == .sema } } @@ -334,14 +350,14 @@ enum DiagnosticStage: Hashable { extension DiagnosticStage { init?(_ uid: sourcekitd_uid_t, sourcekitd: SourceKitD) { switch uid { - case sourcekitd.values.diag_stage_parse: - self = .parse - case sourcekitd.values.diag_stage_sema: - self = .sema - default: - let desc = sourcekitd.api.uid_get_string_ptr(uid).map { String(cString: $0) } - log("unknown diagnostic stage \(desc ?? "nil")", level: .warning) - return nil + case sourcekitd.values.diag_stage_parse: + self = .parse + case sourcekitd.values.diag_stage_sema: + self = .sema + default: + let desc = sourcekitd.api.uid_get_string_ptr(uid).map { String(cString: $0) } + log("unknown diagnostic stage \(desc ?? "nil")", level: .warning) + return nil } } } diff --git a/Sources/SourceKitLSP/Swift/EditorPlaceholder.swift b/Sources/SourceKitLSP/Swift/EditorPlaceholder.swift index d922ae647..ff31676de 100644 --- a/Sources/SourceKitLSP/Swift/EditorPlaceholder.swift +++ b/Sources/SourceKitLSP/Swift/EditorPlaceholder.swift @@ -20,8 +20,10 @@ public enum EditorPlaceholder: Hashable { static let placeholderSuffix: String = "#>" public init?(_ skPlaceholder: String) { - guard skPlaceholder.hasPrefix(EditorPlaceholder.placeholderPrefix) && - skPlaceholder.hasSuffix(EditorPlaceholder.placeholderSuffix) else { + guard + skPlaceholder.hasPrefix(EditorPlaceholder.placeholderPrefix) + && skPlaceholder.hasSuffix(EditorPlaceholder.placeholderSuffix) + else { return nil } var skPlaceholder = skPlaceholder.dropFirst(2).dropLast(2) diff --git a/Sources/SourceKitLSP/Swift/OpenInterface.swift b/Sources/SourceKitLSP/Swift/OpenInterface.swift index 14b418d98..c19926e15 100644 --- a/Sources/SourceKitLSP/Swift/OpenInterface.swift +++ b/Sources/SourceKitLSP/Swift/OpenInterface.swift @@ -11,10 +11,10 @@ //===----------------------------------------------------------------------===// import Foundation -import SourceKitD -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKSupport +import SourceKitD struct InterfaceInfo { var contents: String @@ -33,12 +33,22 @@ extension SwiftLanguageServer { return await self.interfaceDetails(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol) } else { // generate interface - let interfaceInfo = try await self.openInterface(request: request, uri: uri, name: moduleName, interfaceURI: interfaceDocURI) + let interfaceInfo = try await self.openInterface( + request: request, + uri: uri, + name: moduleName, + interfaceURI: interfaceDocURI + ) do { // write to file try interfaceInfo.contents.write(to: interfaceFilePath, atomically: true, encoding: String.Encoding.utf8) // store snapshot - let snapshot = try self.documentManager.open(interfaceDocURI, language: .swift, version: 0, text: interfaceInfo.contents) + let snapshot = try self.documentManager.open( + interfaceDocURI, + language: .swift, + version: 0, + text: interfaceInfo.contents + ) return await self.interfaceDetails(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol) } catch { throw ResponseError.unknown(error.localizedDescription) @@ -71,11 +81,11 @@ extension SwiftLanguageServer { if let compileCommand = await self.buildSettings(for: uri) { skreq[keys.compilerargs] = compileCommand.compilerArgs } - + let dict = try await self.sourcekitd.send(skreq) return InterfaceInfo(contents: dict[keys.sourcetext] ?? "") } - + private func interfaceDetails( request: OpenInterfaceRequest, uri: DocumentURI, @@ -95,7 +105,8 @@ extension SwiftLanguageServer { let dict = try await self.sourcekitd.send(skreq) if let offset: Int = dict[keys.offset], - let position = snapshot.positionOf(utf8Offset: offset) { + let position = snapshot.positionOf(utf8Offset: offset) + { return InterfaceDetails(uri: uri, position: position) } else { return InterfaceDetails(uri: uri, position: nil) diff --git a/Sources/SourceKitLSP/Swift/SemanticRefactorCommand.swift b/Sources/SourceKitLSP/Swift/SemanticRefactorCommand.swift index 11754b813..721b5196a 100644 --- a/Sources/SourceKitLSP/Swift/SemanticRefactorCommand.swift +++ b/Sources/SourceKitLSP/Swift/SemanticRefactorCommand.swift @@ -30,23 +30,27 @@ public struct SemanticRefactorCommand: SwiftCommand { public init?(fromLSPDictionary dictionary: [String: LSPAny]) { guard case .dictionary(let documentDict)? = dictionary[CodingKeys.textDocument.stringValue], - case .string(let title)? = dictionary[CodingKeys.title.stringValue], - case .string(let actionString)? = dictionary[CodingKeys.actionString.stringValue], - case .dictionary(let rangeDict)? = dictionary[CodingKeys.positionRange.stringValue] else - { + case .string(let title)? = dictionary[CodingKeys.title.stringValue], + case .string(let actionString)? = dictionary[CodingKeys.actionString.stringValue], + case .dictionary(let rangeDict)? = dictionary[CodingKeys.positionRange.stringValue] + else { return nil } guard let positionRange = Range(fromLSPDictionary: rangeDict), - let textDocument = TextDocumentIdentifier(fromLSPDictionary: documentDict) else { + let textDocument = TextDocumentIdentifier(fromLSPDictionary: documentDict) + else { return nil } - self.init(title: title, - actionString: actionString, - positionRange: positionRange, - textDocument: textDocument) + self.init( + title: title, + actionString: actionString, + positionRange: positionRange, + textDocument: textDocument + ) } - public init(title: String, actionString: String, positionRange: Range, textDocument: TextDocumentIdentifier) { + public init(title: String, actionString: String, positionRange: Range, textDocument: TextDocumentIdentifier) + { self.title = title self.actionString = actionString self.positionRange = positionRange @@ -58,32 +62,40 @@ public struct SemanticRefactorCommand: SwiftCommand { CodingKeys.title.stringValue: .string(title), CodingKeys.actionString.stringValue: .string(actionString), CodingKeys.positionRange.stringValue: positionRange.encodeToLSPAny(), - CodingKeys.textDocument.stringValue: textDocument.encodeToLSPAny() + CodingKeys.textDocument.stringValue: textDocument.encodeToLSPAny(), ]) } } extension Array where Element == SemanticRefactorCommand { - init?(array: SKDResponseArray?, range: Range, textDocument: TextDocumentIdentifier, _ keys: sourcekitd_keys, _ api: sourcekitd_functions_t) { + init?( + array: SKDResponseArray?, + range: Range, + textDocument: TextDocumentIdentifier, + _ keys: sourcekitd_keys, + _ api: sourcekitd_functions_t + ) { guard let results = array else { return nil } var commands = [SemanticRefactorCommand]() results.forEach { _, value in if let name: String = value[keys.actionname], - let actionuid: sourcekitd_uid_t = value[keys.actionuid], - let ptr = api.uid_get_string_ptr(actionuid) + let actionuid: sourcekitd_uid_t = value[keys.actionuid], + let ptr = api.uid_get_string_ptr(actionuid) { let actionName = String(cString: ptr) guard !actionName.hasPrefix("source.refactoring.kind.rename.") else { // TODO: Rename. return true } - commands.append(SemanticRefactorCommand( - title: name, - actionString: actionName, - positionRange: range, - textDocument: textDocument) + commands.append( + SemanticRefactorCommand( + title: name, + actionString: actionName, + positionRange: range, + textDocument: textDocument + ) ) } return true diff --git a/Sources/SourceKitLSP/Swift/SemanticRefactoring.swift b/Sources/SourceKitLSP/Swift/SemanticRefactoring.swift index ace81fbbc..4cdacd08b 100644 --- a/Sources/SourceKitLSP/Swift/SemanticRefactoring.swift +++ b/Sources/SourceKitLSP/Swift/SemanticRefactoring.swift @@ -49,14 +49,18 @@ struct SemanticRefactoring { edits.forEach { _, value in // The LSP is zero based, but semantic_refactoring is one based. if let startLine: Int = value[keys.line], - let startColumn: Int = value[keys.column], - let startPosition = snapshot.positionOf(zeroBasedLine: startLine - 1, - utf8Column: startColumn - 1), - let endLine: Int = value[keys.endline], - let endColumn: Int = value[keys.endcolumn], - let endPosition = snapshot.positionOf(zeroBasedLine: endLine - 1, - utf8Column: endColumn - 1), - let text: String = value[keys.text] + let startColumn: Int = value[keys.column], + let startPosition = snapshot.positionOf( + zeroBasedLine: startLine - 1, + utf8Column: startColumn - 1 + ), + let endLine: Int = value[keys.endline], + let endColumn: Int = value[keys.endcolumn], + let endPosition = snapshot.positionOf( + zeroBasedLine: endLine - 1, + utf8Column: endColumn - 1 + ), + let text: String = value[keys.text] { // Snippets are only suppored in code completion. // Remove SourceKit placeholders in refactoring actions because they can't be represented in the editor properly. diff --git a/Sources/SourceKitLSP/Swift/SemanticTokens.swift b/Sources/SourceKitLSP/Swift/SemanticTokens.swift index 1c17e2bc9..5fe39f24b 100644 --- a/Sources/SourceKitLSP/Swift/SemanticTokens.swift +++ b/Sources/SourceKitLSP/Swift/SemanticTokens.swift @@ -10,11 +10,11 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPLogging -import SwiftSyntax +import LanguageServerProtocol import SwiftIDEUtils import SwiftParser +import SwiftSyntax extension SwiftLanguageServer { /// Computes an array of syntax highlighting tokens from the syntax tree that @@ -31,16 +31,20 @@ extension SwiftLanguageServer { ) async -> [SyntaxHighlightingToken] { let tree = await syntaxTreeManager.syntaxTree(for: snapshot) let semanticTokens = await semanticTokensManager.semanticTokens(for: snapshot.id) - let range = range.flatMap({ $0.byteSourceRange(in: snapshot) }) - ?? ByteSourceRange(offset: 0, length: tree.totalLength.utf8Length) - return tree + let range = + range.flatMap({ $0.byteSourceRange(in: snapshot) }) + ?? ByteSourceRange(offset: 0, length: tree.totalLength.utf8Length) + return + tree .classifications(in: range) .flatMap({ $0.highlightingTokens(in: snapshot) }) .mergingTokens(with: semanticTokens ?? []) .sorted { $0.start < $1.start } } - public func documentSemanticTokens(_ req: DocumentSemanticTokensRequest) async throws -> DocumentSemanticTokensResponse? { + public func documentSemanticTokens( + _ req: DocumentSemanticTokensRequest + ) async throws -> DocumentSemanticTokensResponse? { let uri = req.textDocument.uri guard let snapshot = self.documentManager.latestSnapshot(uri) else { @@ -54,11 +58,15 @@ extension SwiftLanguageServer { return DocumentSemanticTokensResponse(data: encodedTokens) } - public func documentSemanticTokensDelta(_ req: DocumentSemanticTokensDeltaRequest) async throws -> DocumentSemanticTokensDeltaResponse? { + public func documentSemanticTokensDelta( + _ req: DocumentSemanticTokensDeltaRequest + ) async throws -> DocumentSemanticTokensDeltaResponse? { return nil } - public func documentSemanticTokensRange(_ req: DocumentSemanticTokensRangeRequest) async throws -> DocumentSemanticTokensResponse? { + public func documentSemanticTokensRange( + _ req: DocumentSemanticTokensRangeRequest + ) async throws -> DocumentSemanticTokensResponse? { let uri = req.textDocument.uri let range = req.range diff --git a/Sources/SourceKitLSP/Swift/SourceKitD+ResponseError.swift b/Sources/SourceKitLSP/Swift/SourceKitD+ResponseError.swift index 272f2a12d..6b1717fd0 100644 --- a/Sources/SourceKitLSP/Swift/SourceKitD+ResponseError.swift +++ b/Sources/SourceKitLSP/Swift/SourceKitD+ResponseError.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SourceKitD import LanguageServerProtocol +import SourceKitD extension ResponseError { public init(_ value: SKDError) { diff --git a/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift b/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift index 8828ae91f..bb48be76d 100644 --- a/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift +++ b/Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift @@ -10,15 +10,15 @@ // //===----------------------------------------------------------------------===// -import Foundation import Dispatch -import LanguageServerProtocol +import Foundation import LSPLogging +import LanguageServerProtocol import SKCore import SKSupport import SourceKitD -import SwiftSyntax import SwiftParser +import SwiftSyntax #if os(Windows) import WinSDK @@ -99,7 +99,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer { let capabilityRegistry: CapabilityRegistry let serverOptions: SourceKitServer.Options - + /// Directory where generated Swift interfaces will be stored. let generatedInterfacesPath: URL @@ -124,7 +124,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer { // and we should disable the publish notifications to avoid double-reporting. return capabilityRegistry.pullDiagnosticsRegistration(for: .swift) == nil } - + private var state: LanguageServerState { didSet { for handler in stateChangeHandlers { @@ -132,7 +132,7 @@ public actor SwiftLanguageServer: ToolchainLanguageServer { } } } - + private var stateChangeHandlers: [(_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void] = [] /// Creates a language server for the given client using the sourcekitd dylib specified in `toolchain`. @@ -168,7 +168,10 @@ public actor SwiftLanguageServer: ToolchainLanguageServer { guard let workspace = await sourceKitServer.workspaceForDocument(uri: document) else { return nil } - if let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile(for: document, language: .swift) { + if let settings = await workspace.buildSystemManager.buildSettingsInferredFromMainFile( + for: document, + language: .swift + ) { return SwiftCompileCommand(settings.buildSettings, isFallback: settings.isFallback) } else { return nil @@ -180,7 +183,9 @@ public actor SwiftLanguageServer: ToolchainLanguageServer { return true } - public func addStateChangeHandler(handler: @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void) { + public func addStateChangeHandler( + handler: @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void + ) { self.stateChangeHandlers.append(handler) } @@ -334,40 +339,55 @@ extension SwiftLanguageServer { public func initializeSync(_ initialize: InitializeRequest) throws -> InitializeResult { sourcekitd.addNotificationHandler(self) - return InitializeResult(capabilities: ServerCapabilities( - textDocumentSync: .options(TextDocumentSyncOptions( - openClose: true, - change: .incremental - )), - hoverProvider: .bool(true), - completionProvider: CompletionOptions( - resolveProvider: false, - triggerCharacters: [".", "("]), - definitionProvider: nil, - implementationProvider: .bool(true), - referencesProvider: nil, - documentHighlightProvider: .bool(true), - documentSymbolProvider: .bool(true), - codeActionProvider: .value(CodeActionServerCapabilities( - clientCapabilities: initialize.capabilities.textDocument?.codeAction, - codeActionOptions: CodeActionOptions(codeActionKinds: [.quickFix, .refactor]), - supportsCodeActions: true)), - colorProvider: .bool(true), - foldingRangeProvider: .bool(true), - executeCommandProvider: ExecuteCommandOptions( - commands: builtinSwiftCommands), - semanticTokensProvider: SemanticTokensOptions( - legend: SemanticTokensLegend( - tokenTypes: SyntaxHighlightingToken.Kind.allCases.map(\.lspName), - tokenModifiers: SyntaxHighlightingToken.Modifiers.allModifiers.map { $0.lspName! }), - range: .bool(true), - full: .bool(true)), - inlayHintProvider: .value(InlayHintOptions( - resolveProvider: false)), - diagnosticProvider: DiagnosticOptions( - interFileDependencies: true, - workspaceDiagnostics: false) - )) + return InitializeResult( + capabilities: ServerCapabilities( + textDocumentSync: .options( + TextDocumentSyncOptions( + openClose: true, + change: .incremental + ) + ), + hoverProvider: .bool(true), + completionProvider: CompletionOptions( + resolveProvider: false, + triggerCharacters: [".", "("] + ), + definitionProvider: nil, + implementationProvider: .bool(true), + referencesProvider: nil, + documentHighlightProvider: .bool(true), + documentSymbolProvider: .bool(true), + codeActionProvider: .value( + CodeActionServerCapabilities( + clientCapabilities: initialize.capabilities.textDocument?.codeAction, + codeActionOptions: CodeActionOptions(codeActionKinds: [.quickFix, .refactor]), + supportsCodeActions: true + ) + ), + colorProvider: .bool(true), + foldingRangeProvider: .bool(true), + executeCommandProvider: ExecuteCommandOptions( + commands: builtinSwiftCommands + ), + semanticTokensProvider: SemanticTokensOptions( + legend: SemanticTokensLegend( + tokenTypes: SyntaxHighlightingToken.Kind.allCases.map(\.lspName), + tokenModifiers: SyntaxHighlightingToken.Modifiers.allModifiers.map { $0.lspName! } + ), + range: .bool(true), + full: .bool(true) + ), + inlayHintProvider: .value( + InlayHintOptions( + resolveProvider: false + ) + ), + diagnosticProvider: DiagnosticOptions( + interFileDependencies: true, + workspaceDiagnostics: false + ) + ) + ) } public func clientInitialized(_: InitializedNotification) { @@ -388,7 +408,7 @@ extension SwiftLanguageServer { req[sourcekitd.keys.request] = sourcekitd.requests.crash_exit _ = try? sourcekitd.sendSync(req) } - + // MARK: - Build System Integration private func reopenDocument(_ snapshot: DocumentSnapshot, _ compileCmd: SwiftCompileCommand?) async { @@ -413,7 +433,10 @@ extension SwiftLanguageServer { return } await self.publishDiagnostics( - response: dict, for: snapshot, compileCommand: compileCmd) + response: dict, + for: snapshot, + compileCommand: compileCmd + ) } public func documentUpdatedBuildSettings(_ uri: DocumentURI) async { @@ -498,7 +521,7 @@ extension SwiftLanguageServer { if let range = edit.range { guard let offset = before.utf8OffsetOf(line: range.lowerBound.line, utf16Column: range.lowerBound.utf16index), - let end = before.utf8OffsetOf(line: range.upperBound.line, utf16Column: range.upperBound.utf16index) + let end = before.utf8OffsetOf(line: range.upperBound.line, utf16Column: range.upperBound.utf16index) else { fatalError("invalid edit \(range)") } @@ -623,12 +646,12 @@ extension SwiftLanguageServer { let closeHelperReq = SKDRequestDictionary(sourcekitd: self.sourcekitd) closeHelperReq[self.keys.request] = self.requests.editor_close closeHelperReq[self.keys.name] = helperDocumentName - // FIXME: (async) We might receive two concurrent document symbol requests for the - // same document, in which race to open/close a document with the same name in + // FIXME: (async) We might receive two concurrent document symbol requests for the + // same document, in which race to open/close a document with the same name in // sourcekitd. The solution is to either - // - Not open the helper document and instead rely on the document that is already + // - Not open the helper document and instead rely on the document that is already // open or - // - Prefix the helper document with a UUID to make sure the two concurrent + // - Prefix the helper document with a UUID to make sure the two concurrent // requests operate on different documents as far as sourcekitd is concerned. Task { _ = try await self.sourcekitd.send(closeHelperReq) @@ -641,21 +664,23 @@ extension SwiftLanguageServer { func documentSymbol(value: SKDResponseDictionary) -> DocumentSymbol? { guard let name: String = value[self.keys.name], - let uid: sourcekitd_uid_t = value[self.keys.kind], - let kind: SymbolKind = uid.asSymbolKind(self.values), - let offset: Int = value[self.keys.offset], - let start: Position = snapshot.positionOf(utf8Offset: offset), - let length: Int = value[self.keys.length], - let end: Position = snapshot.positionOf(utf8Offset: offset + length) else { + let uid: sourcekitd_uid_t = value[self.keys.kind], + let kind: SymbolKind = uid.asSymbolKind(self.values), + let offset: Int = value[self.keys.offset], + let start: Position = snapshot.positionOf(utf8Offset: offset), + let length: Int = value[self.keys.length], + let end: Position = snapshot.positionOf(utf8Offset: offset + length) + else { return nil } let range = start.. if let nameOffset: Int = value[self.keys.nameoffset], - let nameStart: Position = snapshot.positionOf(utf8Offset: nameOffset), - let nameLength: Int = value[self.keys.namelength], - let nameEnd: Position = snapshot.positionOf(utf8Offset: nameOffset + nameLength) { + let nameStart: Position = snapshot.positionOf(utf8Offset: nameOffset), + let nameLength: Int = value[self.keys.namelength], + let nameEnd: Position = snapshot.positionOf(utf8Offset: nameOffset + nameLength) + { selectionRange = nameStart.. [DocumentSymbol] { @@ -712,12 +739,12 @@ extension SwiftLanguageServer { let closeHelperReq = SKDRequestDictionary(sourcekitd: self.sourcekitd) closeHelperReq[keys.request] = self.requests.editor_close closeHelperReq[keys.name] = helperDocumentName - // FIXME: (async) We might receive two concurrent document color requests for the - // same document, in which race to open/close a document with the same name in + // FIXME: (async) We might receive two concurrent document color requests for the + // same document, in which race to open/close a document with the same name in // sourcekitd. The solution is to either - // - Not open the helper document and instead rely on the document that is already + // - Not open the helper document and instead rely on the document that is already // open or - // - Prefix the helper document with a UUID to make sure the two concurrent + // - Prefix the helper document with a UUID to make sure the two concurrent // requests operate on different documents as far as sourcekitd is concerned. Task { _ = try? await self.sourcekitd.send(closeHelperReq) @@ -730,45 +757,48 @@ extension SwiftLanguageServer { func colorInformation(dict: SKDResponseDictionary) -> ColorInformation? { guard let kind: sourcekitd_uid_t = dict[self.keys.kind], - kind == self.values.expr_object_literal, - let name: String = dict[self.keys.name], - name == "colorLiteral", - let offset: Int = dict[self.keys.offset], - let start: Position = snapshot.positionOf(utf8Offset: offset), - let length: Int = dict[self.keys.length], - let end: Position = snapshot.positionOf(utf8Offset: offset + length), - let substructure: SKDResponseArray = dict[self.keys.substructure] else { + kind == self.values.expr_object_literal, + let name: String = dict[self.keys.name], + name == "colorLiteral", + let offset: Int = dict[self.keys.offset], + let start: Position = snapshot.positionOf(utf8Offset: offset), + let length: Int = dict[self.keys.length], + let end: Position = snapshot.positionOf(utf8Offset: offset + length), + let substructure: SKDResponseArray = dict[self.keys.substructure] + else { return nil } var red, green, blue, alpha: Double? - substructure.forEach{ (i: Int, value: SKDResponseDictionary) in + substructure.forEach { (i: Int, value: SKDResponseDictionary) in guard let name: String = value[self.keys.name], - let bodyoffset: Int = value[self.keys.bodyoffset], - let bodylength: Int = value[self.keys.bodylength] else { + let bodyoffset: Int = value[self.keys.bodyoffset], + let bodylength: Int = value[self.keys.bodylength] + else { return true } let view = snapshot.text.utf8 let bodyStart = view.index(view.startIndex, offsetBy: bodyoffset) - let bodyEnd = view.index(view.startIndex, offsetBy: bodyoffset+bodylength) + let bodyEnd = view.index(view.startIndex, offsetBy: bodyoffset + bodylength) let value = String(view[bodyStart.. SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.statements.position.utf8Offset, - end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset) + end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset + ) } override func visit(_ node: MemberBlockSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.members.position.utf8Offset, - end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset) + end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset + ) } override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.statements.position.utf8Offset, - end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset) + end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset + ) } override func visit(_ node: AccessorBlockSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.accessors.position.utf8Offset, - end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset) + end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset + ) } override func visit(_ node: SwitchExprSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.cases.position.utf8Offset, - end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset) + end: node.rightBrace.positionAfterSkippingLeadingTrivia.utf8Offset + ) } override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.arguments.position.utf8Offset, - end: node.arguments.endPosition.utf8Offset) + end: node.arguments.endPosition.utf8Offset + ) } override func visit(_ node: SubscriptCallExprSyntax) -> SyntaxVisitorContinueKind { return self.addFoldingRange( start: node.arguments.position.utf8Offset, - end: node.arguments.endPosition.utf8Offset) + end: node.arguments.endPosition.utf8Offset + ) } __consuming func finalize() -> Set { @@ -1024,7 +1063,8 @@ extension SwiftLanguageServer { } guard let start: Position = snapshot.positionOf(utf8Offset: start), - let end: Position = snapshot.positionOf(utf8Offset: end) else { + let end: Position = snapshot.positionOf(utf8Offset: end) + else { log("folding range failed to retrieve position of \(snapshot.uri): \(start)-\(end)", level: .warning) return .visitChildren } @@ -1038,17 +1078,21 @@ extension SwiftLanguageServer { // If the client only supports folding full lines, don't report // the end of the range since there's nothing they could do with it. - range = FoldingRange(startLine: start.line, - startUTF16Index: nil, - endLine: end.line, - endUTF16Index: nil, - kind: kind) + range = FoldingRange( + startLine: start.line, + startUTF16Index: nil, + endLine: end.line, + endUTF16Index: nil, + kind: kind + ) } else { - range = FoldingRange(startLine: start.line, - startUTF16Index: start.utf16index, - endLine: end.line, - endUTF16Index: end.utf16index, - kind: kind) + range = FoldingRange( + startLine: start.line, + startUTF16Index: start.utf16index, + endLine: end.line, + endUTF16Index: end.utf16index, + kind: kind + ) } ranges.insert(range) return .visitChildren @@ -1065,7 +1109,8 @@ extension SwiftLanguageServer { let rangeFinder = FoldingRangeFinder( snapshot: snapshot, rangeLimit: foldingRangeCapabilities?.rangeLimit, - lineFoldingOnly: foldingRangeCapabilities?.lineFoldingOnly ?? false) + lineFoldingOnly: foldingRangeCapabilities?.lineFoldingOnly ?? false + ) rangeFinder.walk(sourceFile) let ranges = rangeFinder.finalize() @@ -1075,7 +1120,7 @@ extension SwiftLanguageServer { public func codeAction(_ req: CodeActionRequest) async throws -> CodeActionRequestResponse? { let providersAndKinds: [(provider: CodeActionProvider, kind: CodeActionKind)] = [ (retrieveRefactorCodeActions, .refactor), - (retrieveQuickFixCodeActions, .quickFix) + (retrieveQuickFixCodeActions, .quickFix), ] let wantedActionKinds = req.context.only let providers = providersAndKinds.filter { wantedActionKinds?.contains($0.1) != false } @@ -1120,7 +1165,8 @@ extension SwiftLanguageServer { let cursorInfoResponse = try await cursorInfo( params.textDocument.uri, params.range, - additionalParameters: additionalCursorInfoParameters) + additionalParameters: additionalCursorInfoParameters + ) guard let cursorInfoResponse else { throw ResponseError.unknown("CursorInfo failed.") @@ -1149,8 +1195,7 @@ extension SwiftLanguageServer { let diag = cachedDiag.diagnostic let codeActions: [CodeAction] = - (diag.codeActions ?? []) + - (diag.relatedInformation?.flatMap{ $0.codeActions ?? [] } ?? []) + (diag.codeActions ?? []) + (diag.relatedInformation?.flatMap { $0.codeActions ?? [] } ?? []) if codeActions.isEmpty { // The diagnostic doesn't have fix-its. Don't return anything. @@ -1165,13 +1210,13 @@ extension SwiftLanguageServer { // Check if the set of diagnostics provided by the request contains this diagnostic. // For this, only compare the 'basic' properties of the diagnostics, excluding related information and code actions since // code actions are only defined in an LSP extension and might not be sent back to us. - guard params.context.diagnostics.contains(where: { (contextDiag) -> Bool in - return contextDiag.range == diag.range && - contextDiag.severity == diag.severity && - contextDiag.code == diag.code && - contextDiag.source == diag.source && - contextDiag.message == diag.message - }) else { + guard + params.context.diagnostics.contains(where: { (contextDiag) -> Bool in + return contextDiag.range == diag.range && contextDiag.severity == diag.severity + && contextDiag.code == diag.code && contextDiag.source == diag.source + && contextDiag.message == diag.message + }) + else { return [] } @@ -1316,7 +1361,7 @@ extension SwiftLanguageServer: SKDNotificationHandler { // Reset the document manager to reflect that. self.documentManager = DocumentManager() } - + guard let dict = notification.value else { log(notification.description, level: .error) return @@ -1325,8 +1370,9 @@ extension SwiftLanguageServer: SKDNotificationHandler { logAsync(level: .debug) { _ in notification.description } if let kind: sourcekitd_uid_t = dict[self.keys.notification], - kind == self.values.notification_documentupdate, - let name: String = dict[self.keys.name] { + kind == self.values.notification_documentupdate, + let name: String = dict[self.keys.name] + { let uri: DocumentURI @@ -1339,13 +1385,13 @@ extension SwiftLanguageServer: SKDNotificationHandler { // TODO: this is not completely portable, e.g. MacOS 9 HFS paths are // unhandled. -#if os(Windows) + #if os(Windows) let isPath: Bool = name.withCString(encodedAs: UTF16.self) { !PathIsURLW($0) } -#else + #else let isPath: Bool = name.starts(with: "/") -#endif + #endif if isPath { // If sourcekitd returns us a path, translate it back into a URL uri = DocumentURI(URL(fileURLWithPath: name)) @@ -1365,8 +1411,8 @@ extension DocumentSnapshot { func utf8OffsetRange(of range: Range) -> Range? { guard let startOffset = utf8Offset(of: range.lowerBound), - let endOffset = utf8Offset(of: range.upperBound) else - { + let endOffset = utf8Offset(of: range.upperBound) + else { return nil } return startOffset.. Bool { switch self { - case vals.syntaxtype_comment, vals.syntaxtype_comment_marker, vals.syntaxtype_comment_url: - return true - default: - return isDocCommentKind(vals) + case vals.syntaxtype_comment, vals.syntaxtype_comment_marker, vals.syntaxtype_comment_url: + return true + default: + return isDocCommentKind(vals) } } @@ -1405,91 +1451,91 @@ extension sourcekitd_uid_t { func asCompletionItemKind(_ vals: sourcekitd_values) -> CompletionItemKind? { switch self { - case vals.kind_keyword: - return .keyword - case vals.decl_module: - return .module - case vals.decl_class: - return .class - case vals.decl_struct: - return .struct - case vals.decl_enum: - return .enum - case vals.decl_enumelement: - return .enumMember - case vals.decl_protocol: - return .interface - case vals.decl_associatedtype: - return .typeParameter - case vals.decl_typealias: - return .typeParameter // FIXME: is there a better choice? - case vals.decl_generic_type_param: - return .typeParameter - case vals.decl_function_constructor: - return .constructor - case vals.decl_function_destructor: - return .value // FIXME: is there a better choice? - case vals.decl_function_subscript: - return .method // FIXME: is there a better choice? - case vals.decl_function_method_static: - return .method - case vals.decl_function_method_instance: - return .method - case vals.decl_function_operator_prefix, - vals.decl_function_operator_postfix, - vals.decl_function_operator_infix: - return .operator - case vals.decl_precedencegroup: - return .value - case vals.decl_function_free: - return .function - case vals.decl_var_static, vals.decl_var_class: - return .property - case vals.decl_var_instance: - return .property - case vals.decl_var_local, - vals.decl_var_global, - vals.decl_var_parameter: - return .variable - default: - return nil + case vals.kind_keyword: + return .keyword + case vals.decl_module: + return .module + case vals.decl_class: + return .class + case vals.decl_struct: + return .struct + case vals.decl_enum: + return .enum + case vals.decl_enumelement: + return .enumMember + case vals.decl_protocol: + return .interface + case vals.decl_associatedtype: + return .typeParameter + case vals.decl_typealias: + return .typeParameter // FIXME: is there a better choice? + case vals.decl_generic_type_param: + return .typeParameter + case vals.decl_function_constructor: + return .constructor + case vals.decl_function_destructor: + return .value // FIXME: is there a better choice? + case vals.decl_function_subscript: + return .method // FIXME: is there a better choice? + case vals.decl_function_method_static: + return .method + case vals.decl_function_method_instance: + return .method + case vals.decl_function_operator_prefix, + vals.decl_function_operator_postfix, + vals.decl_function_operator_infix: + return .operator + case vals.decl_precedencegroup: + return .value + case vals.decl_function_free: + return .function + case vals.decl_var_static, vals.decl_var_class: + return .property + case vals.decl_var_instance: + return .property + case vals.decl_var_local, + vals.decl_var_global, + vals.decl_var_parameter: + return .variable + default: + return nil } } func asSymbolKind(_ vals: sourcekitd_values) -> SymbolKind? { switch self { - case vals.decl_class: - return .class - case vals.decl_function_method_instance, - vals.decl_function_method_static, - vals.decl_function_method_class: - return .method - case vals.decl_var_instance, - vals.decl_var_static, - vals.decl_var_class: - return .property - case vals.decl_enum: - return .enum - case vals.decl_enumelement: - return .enumMember - case vals.decl_protocol: - return .interface - case vals.decl_function_free: - return .function - case vals.decl_var_global, - vals.decl_var_local: - return .variable - case vals.decl_struct: - return .struct - case vals.decl_generic_type_param: - return .typeParameter - case vals.decl_extension: - // There are no extensions in LSP, so I return something vaguely similar - return .namespace - case vals.ref_module: - return .module - default: - return nil + case vals.decl_class: + return .class + case vals.decl_function_method_instance, + vals.decl_function_method_static, + vals.decl_function_method_class: + return .method + case vals.decl_var_instance, + vals.decl_var_static, + vals.decl_var_class: + return .property + case vals.decl_enum: + return .enum + case vals.decl_enumelement: + return .enumMember + case vals.decl_protocol: + return .interface + case vals.decl_function_free: + return .function + case vals.decl_var_global, + vals.decl_var_local: + return .variable + case vals.decl_struct: + return .struct + case vals.decl_generic_type_param: + return .typeParameter + case vals.decl_extension: + // There are no extensions in LSP, so I return something vaguely similar + return .namespace + case vals.ref_module: + return .module + default: + return nil } } } diff --git a/Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift b/Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift index 68a06f085..50b3186b0 100644 --- a/Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift +++ b/Sources/SourceKitLSP/Swift/SyntaxHighlightingToken.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -import SourceKitD -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol +import SourceKitD /// A ranged token in the document used for syntax highlighting. public struct SyntaxHighlightingToken: Hashable { @@ -92,7 +92,7 @@ public struct SyntaxHighlightingToken: Hashable { switch self { case .namespace: return "namespace" case .type: return "type" - case .actor: return "class" // LSP doesn’t know about actors. Display actors as classes. + case .actor: return "class" // LSP doesn’t know about actors. Display actors as classes. case .class: return "class" case .enum: return "enum" case .interface: return "interface" @@ -199,11 +199,11 @@ extension Array where Element == SyntaxHighlightingToken { for token in self { let lineDelta = token.start.line - previous.line - let charDelta = token.start.utf16index - ( - // The character delta is relative to the previous token's start - // only if the token is on the previous token's line. - previous.line == token.start.line ? previous.utf16index : 0 - ) + let charDelta = + token.start.utf16index - ( + // The character delta is relative to the previous token's start + // only if the token is on the previous token's line. + previous.line == token.start.line ? previous.utf16index : 0) // We assert that the tokens are actually sorted assert(lineDelta >= 0) @@ -215,7 +215,7 @@ extension Array where Element == SyntaxHighlightingToken { UInt32(charDelta), UInt32(token.utf16length), token.kind.rawValue, - token.modifiers.rawValue + token.modifiers.rawValue, ] } diff --git a/Sources/SourceKitLSP/Swift/SyntaxHighlightingTokenParser.swift b/Sources/SourceKitLSP/Swift/SyntaxHighlightingTokenParser.swift index 5bc84be05..8a5066175 100644 --- a/Sources/SourceKitLSP/Swift/SyntaxHighlightingTokenParser.swift +++ b/Sources/SourceKitLSP/Swift/SyntaxHighlightingTokenParser.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -import SourceKitD -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol +import SourceKitD /// Parses tokens from sourcekitd response dictionaries. struct SyntaxHighlightingTokenParser { @@ -22,19 +22,25 @@ struct SyntaxHighlightingTokenParser { self.sourcekitd = sourcekitd } - func parseTokens(_ response: SKDResponseDictionary, in snapshot: DocumentSnapshot, into tokens: inout [SyntaxHighlightingToken]) { + func parseTokens( + _ response: SKDResponseDictionary, + in snapshot: DocumentSnapshot, + into tokens: inout [SyntaxHighlightingToken] + ) { let keys = sourcekitd.keys if let offset: Int = response[keys.offset], - var length: Int = response[keys.length], - let start: Position = snapshot.positionOf(utf8Offset: offset), - let skKind: sourcekitd_uid_t = response[keys.kind], - case (let kind, var modifiers)? = parseKindAndModifiers(skKind) { + var length: Int = response[keys.length], + let start: Position = snapshot.positionOf(utf8Offset: offset), + let skKind: sourcekitd_uid_t = response[keys.kind], + case (let kind, var modifiers)? = parseKindAndModifiers(skKind) + { // If the name is escaped in backticks, we need to add two characters to the // length for the backticks. if modifiers.contains(.declaration), - let index = snapshot.indexOf(utf8Offset: offset), snapshot.text[index] == "`" { + let index = snapshot.indexOf(utf8Offset: offset), snapshot.text[index] == "`" + { length += 2 } @@ -61,19 +67,25 @@ struct SyntaxHighlightingTokenParser { } } - func parseTokens(_ response: SKDResponseArray, in snapshot: DocumentSnapshot, into tokens: inout [SyntaxHighlightingToken]) { + func parseTokens( + _ response: SKDResponseArray, + in snapshot: DocumentSnapshot, + into tokens: inout [SyntaxHighlightingToken] + ) { response.forEach { (_, value) in parseTokens(value, in: snapshot, into: &tokens) return true } } - private func parseKindAndModifiers(_ uid: sourcekitd_uid_t) -> (SyntaxHighlightingToken.Kind, SyntaxHighlightingToken.Modifiers)? { + private func parseKindAndModifiers( + _ uid: sourcekitd_uid_t + ) -> (SyntaxHighlightingToken.Kind, SyntaxHighlightingToken.Modifiers)? { let api = sourcekitd.api let values = sourcekitd.values switch uid { case values.kind_keyword, - values.syntaxtype_keyword: + values.syntaxtype_keyword: return (.keyword, []) case values.syntaxtype_attribute_builtin: return (.modifier, []) @@ -104,46 +116,46 @@ struct SyntaxHighlightingTokenParser { case values.ref_protocol: return (.interface, []) case values.decl_associatedtype, - values.decl_typealias, - values.decl_generic_type_param: + values.decl_typealias, + values.decl_generic_type_param: return (.typeParameter, [.declaration]) case values.ref_associatedtype, - values.ref_typealias, - values.ref_generic_type_param: + values.ref_typealias, + values.ref_generic_type_param: return (.typeParameter, []) case values.decl_function_free: return (.function, [.declaration]) case values.decl_function_method_static, - values.decl_function_method_class, - values.decl_function_constructor: + values.decl_function_method_class, + values.decl_function_constructor: return (.method, [.declaration, .static]) case values.decl_function_method_instance, - values.decl_function_destructor, - values.decl_function_subscript: + values.decl_function_destructor, + values.decl_function_subscript: return (.method, [.declaration]) case values.ref_function_free: return (.function, []) case values.ref_function_method_static, - values.ref_function_method_class, - values.ref_function_constructor: + values.ref_function_method_class, + values.ref_function_constructor: return (.method, [.static]) case values.ref_function_method_instance, - values.ref_function_destructor, - values.ref_function_subscript: + values.ref_function_destructor, + values.ref_function_subscript: return (.method, []) case values.syntaxtype_operator: return (.operator, []) case values.decl_function_operator_prefix, - values.decl_function_operator_postfix, - values.decl_function_operator_infix: + values.decl_function_operator_postfix, + values.decl_function_operator_infix: return (.operator, [.declaration]) case values.ref_function_operator_prefix, - values.ref_function_operator_postfix, - values.ref_function_operator_infix: + values.ref_function_operator_postfix, + values.ref_function_operator_infix: return (.operator, []) case values.decl_var_static, - values.decl_var_class, - values.decl_var_instance: + values.decl_var_class, + values.decl_var_instance: return (.property, [.declaration]) case values.decl_var_parameter: // SourceKit seems to use these to refer to parameter labels, @@ -152,21 +164,21 @@ struct SyntaxHighlightingTokenParser { // causing a 'wrong highlighting' e.g. of `x` in `f(x y: Int) {}`) return (.function, [.declaration]) case values.ref_var_static, - values.ref_var_class, - values.ref_var_instance: + values.ref_var_class, + values.ref_var_instance: return (.property, []) case values.decl_var_local, - values.decl_var_global: + values.decl_var_global: return (.variable, [.declaration]) case values.ref_var_local, - values.ref_var_global: + values.ref_var_global: return (.variable, []) case values.syntaxtype_comment, - values.syntaxtype_comment_marker, - values.syntaxtype_comment_url: + values.syntaxtype_comment_marker, + values.syntaxtype_comment_url: return (.comment, []) case values.syntaxtype_doccomment, - values.syntaxtype_doccomment_field: + values.syntaxtype_doccomment_field: return (.comment, [.documentation]) case values.syntaxtype_type_identifier: return (.type, []) @@ -178,7 +190,7 @@ struct SyntaxHighlightingTokenParser { return (.identifier, []) default: let ignoredKinds: Set = [ - values.syntaxtype_string_interpolation_anchor, + values.syntaxtype_string_interpolation_anchor ] if !ignoredKinds.contains(uid) { let name = api.uid_get_string_ptr(uid).map(String.init(cString:)) @@ -201,14 +213,16 @@ extension Range where Bound == Position { } guard let startIndex = snapshot.index(of: lowerBound), - let endIndex = snapshot.index(of: upperBound) else { + let endIndex = snapshot.index(of: upperBound) + else { fatalError("Range \(self) reaches outside of the document") } let text = snapshot.text[startIndex.. InitializeResult func clientInitialized(_ initialized: InitializedNotification) async - + /// Shut the server down and return once the server has finished shutting down func shutdown() async /// Add a handler that is called whenever the state of the language server changes. - func addStateChangeHandler(handler: @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void) async + func addStateChangeHandler( + handler: @escaping (_ oldState: LanguageServerState, _ newState: LanguageServerState) -> Void + ) async // MARK: - Text synchronization @@ -92,8 +94,12 @@ public protocol ToolchainLanguageServer: AnyObject { func documentSymbol(_ req: DocumentSymbolRequest) async throws -> DocumentSymbolResponse? func documentColor(_ req: DocumentColorRequest) async throws -> [ColorInformation] func documentSemanticTokens(_ req: DocumentSemanticTokensRequest) async throws -> DocumentSemanticTokensResponse? - func documentSemanticTokensDelta(_ req: DocumentSemanticTokensDeltaRequest) async throws -> DocumentSemanticTokensDeltaResponse? - func documentSemanticTokensRange(_ req: DocumentSemanticTokensRangeRequest) async throws -> DocumentSemanticTokensResponse? + func documentSemanticTokensDelta( + _ req: DocumentSemanticTokensDeltaRequest + ) async throws -> DocumentSemanticTokensDeltaResponse? + func documentSemanticTokensRange( + _ req: DocumentSemanticTokensRangeRequest + ) async throws -> DocumentSemanticTokensResponse? func colorPresentation(_ req: ColorPresentationRequest) async throws -> [ColorPresentation] func codeAction(_ req: CodeActionRequest) async throws -> CodeActionRequestResponse? func inlayHint(_ req: InlayHintRequest) async throws -> [InlayHint] diff --git a/Sources/SourceKitLSP/Workspace.swift b/Sources/SourceKitLSP/Workspace.swift index d874f4a01..162386ce1 100644 --- a/Sources/SourceKitLSP/Workspace.swift +++ b/Sources/SourceKitLSP/Workspace.swift @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// import IndexStoreDB -import LanguageServerProtocol import LSPLogging +import LanguageServerProtocol import SKCore import SKSupport import SKSwiftPMWorkspace @@ -27,7 +27,10 @@ fileprivate func firstNonNil(_ optional: T?, _ defaultValue: @autoclosure () return try await defaultValue() } -fileprivate func firstNonNil(_ optional: T?, _ defaultValue: @autoclosure () async throws -> T?) async rethrows -> T? { +fileprivate func firstNonNil( + _ optional: T?, + _ defaultValue: @autoclosure () async throws -> T? +) async rethrows -> T? { if let optional { return optional } @@ -82,7 +85,8 @@ public final class Workspace { self.buildSystemManager = await BuildSystemManager( buildSystem: underlyingBuildSystem, fallbackBuildSystem: FallbackBuildSystem(buildSetup: buildSetup), - mainFilesProvider: index) + mainFilesProvider: index + ) indexDelegate?.registerMainFileChanged(buildSystemManager) } @@ -125,20 +129,22 @@ public final class Workspace { var indexDelegate: SourceKitIndexDelegate? = nil if let storePath = await firstNonNil(indexOptions.indexStorePath, await buildSystem?.indexStorePath), - let dbPath = await firstNonNil(indexOptions.indexDatabasePath, await buildSystem?.indexDatabasePath), - let libPath = toolchainRegistry.default?.libIndexStore + let dbPath = await firstNonNil(indexOptions.indexDatabasePath, await buildSystem?.indexDatabasePath), + let libPath = toolchainRegistry.default?.libIndexStore { do { let lib = try IndexStoreLibrary(dylibPath: libPath.pathString) indexDelegate = SourceKitIndexDelegate() - let prefixMappings = await firstNonNil(indexOptions.indexPrefixMappings, await buildSystem?.indexPrefixMappings) ?? [] + let prefixMappings = + await firstNonNil(indexOptions.indexPrefixMappings, await buildSystem?.indexPrefixMappings) ?? [] index = try IndexStoreDB( storePath: storePath.pathString, databasePath: dbPath.pathString, library: lib, delegate: indexDelegate, listenToUnitEvents: indexOptions.listenToUnitEvents, - prefixMappings: prefixMappings.map { PathMapping(original: $0.original, replacement: $0.replacement) }) + prefixMappings: prefixMappings.map { PathMapping(original: $0.original, replacement: $0.replacement) } + ) log("opened IndexStoreDB at \(dbPath) with store path \(storePath)") } catch { log("failed to open IndexStoreDB: \(error.localizedDescription)", level: .error) @@ -153,7 +159,8 @@ public final class Workspace { buildSetup: buildSetup, underlyingBuildSystem: buildSystem, index: index, - indexDelegate: indexDelegate) + indexDelegate: indexDelegate + ) } } diff --git a/Sources/sourcekit-lsp/SourceKitLSP.swift b/Sources/sourcekit-lsp/SourceKitLSP.swift index 68d754f49..fd42617a4 100644 --- a/Sources/sourcekit-lsp/SourceKitLSP.swift +++ b/Sources/sourcekit-lsp/SourceKitLSP.swift @@ -11,12 +11,12 @@ //===----------------------------------------------------------------------===// import ArgumentParser -import Csourcekitd // Not needed here, but fixes debugging... +import Csourcekitd // Not needed here, but fixes debugging... import Dispatch import Foundation +import LSPLogging import LanguageServerProtocol import LanguageServerProtocolJSONRPC -import LSPLogging import SKCore import SKSupport import SourceKitLSP @@ -51,8 +51,10 @@ extension AbsolutePath: ExpressibleByArgument { extension PathPrefixMapping: ExpressibleByArgument { public init?(argument: String) { guard let eqIndex = argument.firstIndex(of: "=") else { return nil } - self.init(original: String(argument[..(_ value: Request, id: RequestID, json: String, file: StaticString = #filePath, line: UInt = #line) where Request: RequestType & Equatable { +private func checkMessageCoding( + _ value: Request, + id: RequestID, + json: String, + file: StaticString = #filePath, + line: UInt = #line +) { checkCoding(JSONRPCMessage.request(value, id: id), json: json, userInfo: defaultCodingInfo, file: file, line: line) { - - guard case JSONRPCMessage.request(let decodedValueOpaque, let decodedID) = $0, let decodedValue = decodedValueOpaque as? Request else { + guard case JSONRPCMessage.request(let decodedValueOpaque, let decodedID) = $0, + let decodedValue = decodedValueOpaque as? Request + else { XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line) return } @@ -258,10 +389,16 @@ private func checkMessageCoding(_ value: Request, id: RequestID, json: } } -private func checkMessageCoding(_ value: Notification, json: String, file: StaticString = #filePath, line: UInt = #line) where Notification: NotificationType & Equatable { +private func checkMessageCoding( + _ value: Notification, + json: String, + file: StaticString = #filePath, + line: UInt = #line +) { checkCoding(JSONRPCMessage.notification(value), json: json, userInfo: defaultCodingInfo, file: file, line: line) { - - guard case JSONRPCMessage.notification(let decodedValueOpaque) = $0, let decodedValue = decodedValueOpaque as? Notification else { + guard case JSONRPCMessage.notification(let decodedValueOpaque) = $0, + let decodedValue = decodedValueOpaque as? Notification + else { XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line) return } @@ -270,8 +407,13 @@ private func checkMessageCoding(_ value: Notification, json: Strin } } -private func checkMessageCoding(_ value: Response, id: RequestID, json: String, file: StaticString = #filePath, line: UInt = #line) where Response: ResponseType & Equatable { - +private func checkMessageCoding( + _ value: Response, + id: RequestID, + json: String, + file: StaticString = #filePath, + line: UInt = #line +) { let callback: JSONRPCMessage.ResponseTypeCallback = { return $0 == .string("unknown") ? nil : Response.self } @@ -281,7 +423,9 @@ private func checkMessageCoding(_ value: Response, id: RequestID, json checkCoding(JSONRPCMessage.response(value, id: id), json: json, userInfo: codingInfo, file: file, line: line) { - guard case JSONRPCMessage.response(let decodedValueOpaque, let decodedID) = $0, let decodedValue = decodedValueOpaque as? Response else { + guard case JSONRPCMessage.response(let decodedValueOpaque, let decodedID) = $0, + let decodedValue = decodedValueOpaque as? Response + else { XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line) return } @@ -291,8 +435,20 @@ private func checkMessageCoding(_ value: Response, id: RequestID, json } } -private func checkMessageCoding(_ value: ResponseError, id: RequestID?, json: String, file: StaticString = #filePath, line: UInt = #line) { - checkCoding(JSONRPCMessage.errorResponse(value, id: id), json: json, userInfo: defaultCodingInfo, file: file, line: line) { +private func checkMessageCoding( + _ value: ResponseError, + id: RequestID?, + json: String, + file: StaticString = #filePath, + line: UInt = #line +) { + checkCoding( + JSONRPCMessage.errorResponse(value, id: id), + json: json, + userInfo: defaultCodingInfo, + file: file, + line: line + ) { guard case JSONRPCMessage.errorResponse(let decodedValue, let decodedID) = $0 else { XCTFail("decodedValue \($0) does not match expected \(value)", file: file, line: line) @@ -304,7 +460,13 @@ private func checkMessageCoding(_ value: ResponseError, id: RequestID?, json: St } } -private func checkMessageDecodingError(_ expected: MessageDecodingError, json: String, userInfo: [CodingUserInfoKey: Any] = defaultCodingInfo, file: StaticString = #filePath, line: UInt = #line) { +private func checkMessageDecodingError( + _ expected: MessageDecodingError, + json: String, + userInfo: [CodingUserInfoKey: Any] = defaultCodingInfo, + file: StaticString = #filePath, + line: UInt = #line +) { let data = json.data(using: .utf8)! let decoder = JSONDecoder() decoder.userInfo = userInfo @@ -315,8 +477,12 @@ private func checkMessageDecodingError(_ expected: MessageDecodingError, json: S } catch let error as MessageDecodingError { XCTAssertEqual(expected.code, error.code, file: file, line: line) XCTAssertEqual(expected.id, error.id, file: file, line: line) - XCTAssertTrue(error.message.hasPrefix(expected.message), - "message expected to start with \(expected.message); got \(error.message)", file: file, line: line) + XCTAssertTrue( + error.message.hasPrefix(expected.message), + "message expected to start with \(expected.message); got \(error.message)", + file: file, + line: line + ) } catch { XCTFail("incorrect error seen \(error)", file: file, line: line) } diff --git a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionPerfTests.swift b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionPerfTests.swift index 874fd6922..cb01c9686 100644 --- a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionPerfTests.swift +++ b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionPerfTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocolJSONRPC import LSPTestSupport +import LanguageServerProtocolJSONRPC import XCTest class ConnectionPerfTests: PerfTestCase { @@ -57,11 +57,14 @@ class ConnectionPerfTests: PerfTestCase { let client = connection.client let sema = DispatchSemaphore(value: 0) self.measure { - DispatchQueue.concurrentPerform(iterations: 100, execute: { _ in - _ = client.send(EchoRequest(string: "hello!")) { _ in - sema.signal() + DispatchQueue.concurrentPerform( + iterations: 100, + execute: { _ in + _ = client.send(EchoRequest(string: "hello!")) { _ in + sema.signal() + } } - }) + ) for _ in 1...100 { XCTAssertEqual(sema.wait(timeout: .now() + .seconds(Int(defaultTimeout))), .success) } diff --git a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift index 794646599..56f3631f9 100644 --- a/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift +++ b/Tests/LanguageServerProtocolJSONRPCTests/ConnectionTests.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// +import LSPTestSupport import LanguageServerProtocol import LanguageServerProtocolJSONRPC -import LSPTestSupport import XCTest #if os(Windows) @@ -75,7 +75,9 @@ class ConnectionTests: XCTestCase { clientConnection.send(_rawData: [b].withUnsafeBytes { DispatchData(bytes: $0) }) } - clientConnection.send(_rawData: [note1Str.utf8.last!, note2Str.utf8.first!].withUnsafeBytes { DispatchData(bytes: $0) }) + clientConnection.send( + _rawData: [note1Str.utf8.last!, note2Str.utf8.first!].withUnsafeBytes { DispatchData(bytes: $0) } + ) waitForExpectations(timeout: defaultTimeout) @@ -225,7 +227,7 @@ class ConnectionTests: XCTestCase { waitForExpectations(timeout: defaultTimeout) } - + func testSendSynchronouslyBeforeClose() { let client = connection.client @@ -255,30 +257,39 @@ class ConnectionTests: XCTestCase { let conn = JSONRPCConnection( protocol: MessageRegistry(requests: [], notifications: []), inFD: to.fileHandleForReading, - outFD: from.fileHandleForWriting) + outFD: from.fileHandleForWriting + ) final class DummyHandler: MessageHandler { func handle(_: N, from: ObjectIdentifier) {} - func handle(_: R, id: RequestID, from: ObjectIdentifier, reply: @escaping (LSPResult) -> Void) {} + func handle( + _: R, + id: RequestID, + from: ObjectIdentifier, + reply: @escaping (LSPResult) -> Void + ) {} } - conn.start(receiveHandler: DummyHandler(), closeHandler: { - // We get an error from XCTest if this is fulfilled more than once. - expectation.fulfill() + conn.start( + receiveHandler: DummyHandler(), + closeHandler: { + // We get an error from XCTest if this is fulfilled more than once. + expectation.fulfill() - // FIXME: keep the pipes alive until we close the connection. This - // should be fixed systemically. - withExtendedLifetime((to, from)) {} - }) + // FIXME: keep the pipes alive until we close the connection. This + // should be fixed systemically. + withExtendedLifetime((to, from)) {} + } + ) to.fileHandleForWriting.closeFile() -#if os(Windows) + #if os(Windows) // 1 ms was chosen for simplicity. Sleep(1) -#else + #else // 100 us was chosen empirically to encourage races. usleep(100) -#endif + #endif conn.close() withExtendedLifetime(conn) { diff --git a/Tests/LanguageServerProtocolJSONRPCTests/MessageParsingTests.swift b/Tests/LanguageServerProtocolJSONRPCTests/MessageParsingTests.swift index 347f5b430..9e9ea9b16 100644 --- a/Tests/LanguageServerProtocolJSONRPCTests/MessageParsingTests.swift +++ b/Tests/LanguageServerProtocolJSONRPCTests/MessageParsingTests.swift @@ -17,7 +17,13 @@ import XCTest final class MessageParsingTests: XCTestCase { func testSplitMessage() throws { - func check(_ string: String, contentLen: Int? = nil, restLen: Int?, file: StaticString = #filePath, line: UInt = #line) throws { + func check( + _ string: String, + contentLen: Int? = nil, + restLen: Int?, + file: StaticString = #filePath, + line: UInt = #line + ) throws { let bytes: [UInt8] = [UInt8](string.utf8) guard let ((content, header), rest) = try bytes.jsonrpcSplitMessage() else { XCTAssert(restLen == nil, "expected non-empty field", file: file, line: line) @@ -28,7 +34,12 @@ final class MessageParsingTests: XCTestCase { XCTAssertEqual(header.contentLength, contentLen, file: file, line: line) } - func checkError(_ string: String, _ expected: MessageDecodingError, file: StaticString = #filePath, line: UInt = #line) { + func checkError( + _ string: String, + _ expected: MessageDecodingError, + file: StaticString = #filePath, + line: UInt = #line + ) { do { _ = try [UInt8](string.utf8).jsonrpcSplitMessage() XCTFail("missing expected error", file: file, line: line) @@ -43,17 +54,23 @@ final class MessageParsingTests: XCTestCase { try check("Content-Length: 1\r\n\r\n", restLen: nil) try check("Content-Length: 2\r\n\r\n{", restLen: nil) - try check("Content-Length: 0\r\n\r\n", contentLen: 0, restLen: 0) - try check("Content-Length: 0\r\n\r\n{}", contentLen: 0, restLen: 2) - try check("Content-Length: 1\r\n\r\n{}", contentLen: 1, restLen: 1) - try check("Content-Length: 2\r\n\r\n{}", contentLen: 2, restLen: 0) - try check("Content-Length: 2\r\n\r\n{}Co", contentLen: 2, restLen: 2) + try check("Content-Length: 0\r\n\r\n", contentLen: 0, restLen: 0) + try check("Content-Length: 0\r\n\r\n{}", contentLen: 0, restLen: 2) + try check("Content-Length: 1\r\n\r\n{}", contentLen: 1, restLen: 1) + try check("Content-Length: 2\r\n\r\n{}", contentLen: 2, restLen: 0) + try check("Content-Length: 2\r\n\r\n{}Co", contentLen: 2, restLen: 2) checkError("\r\n\r\n{}", MessageDecodingError.parseError("missing Content-Length header")) } func testParseHeader() throws { - func check(_ string: String, header expected: JSONRPCMessageHeader? = nil, restLen: Int?, file: StaticString = #filePath, line: UInt = #line) throws { + func check( + _ string: String, + header expected: JSONRPCMessageHeader? = nil, + restLen: Int?, + file: StaticString = #filePath, + line: UInt = #line + ) throws { let bytes: [UInt8] = [UInt8](string.utf8) guard let (header, rest) = try bytes.jsonrcpParseHeader() else { XCTAssert(restLen == nil, "expected non-empty field", file: file, line: line) @@ -63,7 +80,12 @@ final class MessageParsingTests: XCTestCase { XCTAssertEqual(header, expected, file: file, line: line) } - func checkErrorBytes(_ bytes: [UInt8], _ expected: MessageDecodingError, file: StaticString = #filePath, line: UInt = #line) { + func checkErrorBytes( + _ bytes: [UInt8], + _ expected: MessageDecodingError, + file: StaticString = #filePath, + line: UInt = #line + ) { do { _ = try bytes.jsonrcpParseHeader() XCTFail("missing expected error", file: file, line: line) @@ -74,7 +96,12 @@ final class MessageParsingTests: XCTestCase { } } - func checkError(_ string: String, _ expected: MessageDecodingError, file: StaticString = #filePath, line: UInt = #line) { + func checkError( + _ string: String, + _ expected: MessageDecodingError, + file: StaticString = #filePath, + line: UInt = #line + ) { checkErrorBytes([UInt8](string.utf8), expected, file: file, line: line) } @@ -91,11 +118,21 @@ final class MessageParsingTests: XCTestCase { checkError("Content-Length:0x1\r\n\r\n", MessageDecodingError.parseError("expected integer value in 0x1")) checkError("Content-Length:a123\r\n\r\n", MessageDecodingError.parseError("expected integer value in a123")) - checkErrorBytes([UInt8]("Content-Length: ".utf8) + [0xFF] + [UInt8]("\r\n".utf8), MessageDecodingError.parseError("expected integer value in ")) + checkErrorBytes( + [UInt8]("Content-Length: ".utf8) + [0xFF] + [UInt8]("\r\n".utf8), + MessageDecodingError.parseError("expected integer value in ") + ) } func testParseHeaderField() throws { - func check(_ string: String, keyLen: Int? = nil, valueLen: Int? = nil, restLen: Int?, file: StaticString = #filePath, line: UInt = #line) throws { + func check( + _ string: String, + keyLen: Int? = nil, + valueLen: Int? = nil, + restLen: Int?, + file: StaticString = #filePath, + line: UInt = #line + ) throws { let bytes: [UInt8] = [UInt8](string.utf8) guard let (kv, rest) = try bytes.jsonrpcParseHeaderField() else { XCTAssert(restLen == nil, "expected non-empty field", file: file, line: line) @@ -108,11 +145,16 @@ final class MessageParsingTests: XCTestCase { } XCTAssertEqual(kv?.value.count, valueLen, "value", file: file, line: line) if let value = kv?.value { - XCTAssertEqual(value, bytes.dropFirst(kv!.key.count+1).prefix(value.count), file: file, line: line) + XCTAssertEqual(value, bytes.dropFirst(kv!.key.count + 1).prefix(value.count), file: file, line: line) } } - func checkError(_ string: String, _ expected: MessageDecodingError, file: StaticString = #filePath, line: UInt = #line) { + func checkError( + _ string: String, + _ expected: MessageDecodingError, + file: StaticString = #filePath, + line: UInt = #line + ) { do { _ = try [UInt8](string.utf8).jsonrpcParseHeaderField() XCTFail("missing expected error", file: file, line: line) @@ -190,9 +232,9 @@ final class MessageParsingTests: XCTestCase { XCTAssertEqual(Int(ascii: "45"), 45) XCTAssertEqual(Int(ascii: " 45 "), 45) XCTAssertEqual(Int(ascii: "\(Int.max)"), Int.max) - XCTAssertEqual(Int(ascii: "\(Int.max-1)"), Int.max-1) + XCTAssertEqual(Int(ascii: "\(Int.max-1)"), Int.max - 1) XCTAssertEqual(Int(ascii: "\(Int.min)"), Int.min) - XCTAssertEqual(Int(ascii: "\(Int.min+1)"), Int.min+1) + XCTAssertEqual(Int(ascii: "\(Int.min+1)"), Int.min + 1) XCTAssertEqual(Int(ascii: "+0"), 0) XCTAssertEqual(Int(ascii: "+1"), 1) @@ -203,9 +245,9 @@ final class MessageParsingTests: XCTestCase { XCTAssertEqual(Int(ascii: "-45"), -45) XCTAssertEqual(Int(ascii: " -45 "), -45) XCTAssertEqual(Int(ascii: "+\(Int.max)"), Int.max) - XCTAssertEqual(Int(ascii: "+\(Int.max-1)"), Int.max-1) + XCTAssertEqual(Int(ascii: "+\(Int.max-1)"), Int.max - 1) XCTAssertEqual(Int(ascii: "\(Int.min)"), Int.min) - XCTAssertEqual(Int(ascii: "\(Int.min+1)"), Int.min+1) + XCTAssertEqual(Int(ascii: "\(Int.min+1)"), Int.min + 1) XCTAssertEqual(Int(ascii: "1234567890"), 1234567890) XCTAssertEqual(Int(ascii: "\n\r \u{b}\u{d}\t45\n\t\r\u{c}"), 45) diff --git a/Tests/LanguageServerProtocolTests/CodingTests.swift b/Tests/LanguageServerProtocolTests/CodingTests.swift index eba951522..ca59d60b6 100644 --- a/Tests/LanguageServerProtocolTests/CodingTests.swift +++ b/Tests/LanguageServerProtocolTests/CodingTests.swift @@ -10,9 +10,9 @@ // //===----------------------------------------------------------------------===// -import XCTest -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol +import XCTest final class CodingTests: XCTestCase { @@ -22,7 +22,7 @@ final class CodingTests: XCTestCase { // The \\/\\/\\/ is escaping file:// + /foo.swift, which is silly but allowed by json. let urljson = "file:\\/\\/\\/foo.swift" - let range = Position(line: 5, utf16index: 23) ..< Position(line: 6, utf16index: 0) + let range = Position(line: 5, utf16index: 23).. start, Range.upperBound -> end let rangejson = """ { @@ -40,39 +40,54 @@ final class CodingTests: XCTestCase { let indent2rangejson = rangejson.indented(2, skipFirstLine: true) // url -> uri - checkCoding(Location(uri: uri, range: range), json: """ - { - "range" : \(indent2rangejson), - "uri" : "\(urljson)" - } - """) + checkCoding( + Location(uri: uri, range: range), + json: """ + { + "range" : \(indent2rangejson), + "uri" : "\(urljson)" + } + """ + ) - checkCoding(TextEdit(range: range, newText: "foo"), json: """ - { - "newText" : "foo", - "range" : \(indent2rangejson) - } - """) + checkCoding( + TextEdit(range: range, newText: "foo"), + json: """ + { + "newText" : "foo", + "range" : \(indent2rangejson) + } + """ + ) // url -> uri - checkCoding(TextDocumentIdentifier(uri), json: """ - { - "uri" : "\(urljson)" - } - """) + checkCoding( + TextDocumentIdentifier(uri), + json: """ + { + "uri" : "\(urljson)" + } + """ + ) - checkCoding(OptionalVersionedTextDocumentIdentifier(uri, version: nil), json: """ - { - "uri" : "\(urljson)" - } - """) + checkCoding( + OptionalVersionedTextDocumentIdentifier(uri, version: nil), + json: """ + { + "uri" : "\(urljson)" + } + """ + ) - checkCoding(VersionedTextDocumentIdentifier(uri, version: 3), json: """ - { - "uri" : "\(urljson)", - "version" : 3 - } - """) + checkCoding( + VersionedTextDocumentIdentifier(uri, version: 3), + json: """ + { + "uri" : "\(urljson)", + "version" : 3 + } + """ + ) checkCoding( TextDocumentEdit( @@ -80,42 +95,53 @@ final class CodingTests: XCTestCase { edits: [ .textEdit(TextEdit(range: range, newText: "foo")) ] - ), json: """ - { - "edits" : [ - { - "newText" : "foo", - "range" : \(rangejson.indented(6, skipFirstLine: true)) + ), + json: """ + { + "edits" : [ + { + "newText" : "foo", + "range" : \(rangejson.indented(6, skipFirstLine: true)) + } + ], + "textDocument" : { + "uri" : "\(urljson)", + "version" : 1 } - ], - "textDocument" : { - "uri" : "\(urljson)", - "version" : 1 } - } - """) + """ + ) // url -> uri - checkCoding(WorkspaceFolder(uri: uri, name: "foo"), json: """ - { - "name" : "foo", - "uri" : "\(urljson)" - } - """) + checkCoding( + WorkspaceFolder(uri: uri, name: "foo"), + json: """ + { + "name" : "foo", + "uri" : "\(urljson)" + } + """ + ) - checkCoding(WorkspaceFolder(uri: uri), json: """ - { - "name" : "foo.swift", - "uri" : "\(urljson)" - } - """) + checkCoding( + WorkspaceFolder(uri: uri), + json: """ + { + "name" : "foo.swift", + "uri" : "\(urljson)" + } + """ + ) - checkCoding(WorkspaceFolder(uri: uri, name: ""), json: """ - { - "name" : "unknown_workspace", - "uri" : "\(urljson)" - } - """) + checkCoding( + WorkspaceFolder(uri: uri, name: ""), + json: """ + { + "name" : "unknown_workspace", + "uri" : "\(urljson)" + } + """ + ) checkCoding(MarkupKind.markdown, json: "\"markdown\"") checkCoding(MarkupKind.plaintext, json: "\"plaintext\"") @@ -136,68 +162,86 @@ final class CodingTests: XCTestCase { checkCoding( ClientCapabilities( workspace: with(WorkspaceClientCapabilities()) { $0.applyEdit = true }, - textDocument: nil), + textDocument: nil + ), json: """ - { - "workspace" : { - "applyEdit" : true + { + "workspace" : { + "applyEdit" : true + } } - } - """) + """ + ) checkCoding( WorkspaceSettingsChange.clangd(ClangWorkspaceSettings(compilationDatabasePath: nil)), - json: "{\n\n}") + json: "{\n\n}" + ) checkCoding( WorkspaceSettingsChange.clangd(ClangWorkspaceSettings(compilationDatabasePath: "foo")), json: """ - { - "compilationDatabasePath" : "foo" - } - """) + { + "compilationDatabasePath" : "foo" + } + """ + ) // FIXME: should probably be "unknown"; see comment in WorkspaceSettingsChange decoder. - checkDecoding(json: """ - { - "hi": "there" - } - """, expected: WorkspaceSettingsChange.clangd(ClangWorkspaceSettings(compilationDatabasePath: nil))) + checkDecoding( + json: """ + { + "hi": "there" + } + """, + expected: WorkspaceSettingsChange.clangd(ClangWorkspaceSettings(compilationDatabasePath: nil)) + ) // experimental can be anything - checkDecoding(json: """ - { - "experimenal": [1] - } - """, expected: ClientCapabilities(workspace: nil, textDocument: nil)) + checkDecoding( + json: """ + { + "experimenal": [1] + } + """, + expected: ClientCapabilities(workspace: nil, textDocument: nil) + ) - checkDecoding(json: """ - { - "workspace": { - "workspaceEdit": { - "documentChanges": false + checkDecoding( + json: """ + { + "workspace": { + "workspaceEdit": { + "documentChanges": false + } } } - } - """, expected: ClientCapabilities( + """, + expected: ClientCapabilities( workspace: with(WorkspaceClientCapabilities()) { $0.workspaceEdit = WorkspaceClientCapabilities.WorkspaceEdit(documentChanges: false) }, - textDocument: nil)) + textDocument: nil + ) + ) // ignore unknown keys - checkDecoding(json: """ - { - "workspace": { - "workspaceEdit": { - "ben's unlikley opton": false + checkDecoding( + json: """ + { + "workspace": { + "workspaceEdit": { + "ben's unlikley opton": false + } } } - } - """, expected: ClientCapabilities( + """, + expected: ClientCapabilities( workspace: with(WorkspaceClientCapabilities()) { $0.workspaceEdit = WorkspaceClientCapabilities.WorkspaceEdit(documentChanges: nil) }, - textDocument: nil)) + textDocument: nil + ) + ) checkCoding(RequestID.number(100), json: "100") checkCoding(RequestID.string("100"), json: "\"100\"") @@ -212,227 +256,317 @@ final class CodingTests: XCTestCase { checkCoding(DiagnosticCode.number(123), json: "123") checkCoding(DiagnosticCode.string("hi"), json: "\"hi\"") - checkCoding(CodeDescription(href: DocumentURI(string: "file:///some/path")), json: """ - { - "href" : "file:\\/\\/\\/some\\/path" - } - """) + checkCoding( + CodeDescription(href: DocumentURI(string: "file:///some/path")), + json: """ + { + "href" : "file:\\/\\/\\/some\\/path" + } + """ + ) let markup = MarkupContent(kind: .plaintext, value: "a") - checkCoding(HoverResponse(contents: .markupContent(markup), range: nil), json: """ - { - "contents" : { - "kind" : "plaintext", - "value" : "a" + checkCoding( + HoverResponse(contents: .markupContent(markup), range: nil), + json: """ + { + "contents" : { + "kind" : "plaintext", + "value" : "a" + } } - } - """) + """ + ) - checkDecoding(json: """ - { - "contents" : "test" - } - """, expected: HoverResponse(contents: .markedStrings([.markdown(value: "test")]), range: nil)) + checkDecoding( + json: """ + { + "contents" : "test" + } + """, + expected: HoverResponse(contents: .markedStrings([.markdown(value: "test")]), range: nil) + ) - checkCoding(HoverResponse(contents: .markedStrings([.markdown(value: "test"), .codeBlock(language: "swift", value: "let foo = 2")]), range: nil), json: """ - { - "contents" : [ - "test", - { - "language" : "swift", - "value" : "let foo = 2" - } - ] - } - """) + checkCoding( + HoverResponse( + contents: .markedStrings([.markdown(value: "test"), .codeBlock(language: "swift", value: "let foo = 2")]), + range: nil + ), + json: """ + { + "contents" : [ + "test", + { + "language" : "swift", + "value" : "let foo = 2" + } + ] + } + """ + ) - checkCoding(HoverResponse(contents: .markupContent(markup), range: range), json: """ - { - "contents" : { - "kind" : "plaintext", - "value" : "a" - }, - "range" : \(rangejson.indented(2, skipFirstLine: true)) - } - """) + checkCoding( + HoverResponse(contents: .markupContent(markup), range: range), + json: """ + { + "contents" : { + "kind" : "plaintext", + "value" : "a" + }, + "range" : \(rangejson.indented(2, skipFirstLine: true)) + } + """ + ) - checkCoding(TextDocumentContentChangeEvent(text: "a"), json: """ - { - "text" : "a" - } - """) - checkCoding(TextDocumentContentChangeEvent(range: range, rangeLength: 10, text: "a"), json: """ - { - "range" : \(rangejson.indented(2, skipFirstLine: true)), - "rangeLength" : 10, - "text" : "a" - } - """) - checkCoding(WorkspaceEdit(changes: [uri: []]), json: """ - { - "changes" : { - "\(urljson)" : [ + checkCoding( + TextDocumentContentChangeEvent(text: "a"), + json: """ + { + "text" : "a" + } + """ + ) + checkCoding( + TextDocumentContentChangeEvent(range: range, rangeLength: 10, text: "a"), + json: """ + { + "range" : \(rangejson.indented(2, skipFirstLine: true)), + "rangeLength" : 10, + "text" : "a" + } + """ + ) + checkCoding( + WorkspaceEdit(changes: [uri: []]), + json: """ + { + "changes" : { + "\(urljson)" : [ + + ] + } + } + """ + ) + checkCoding( + CompletionList(isIncomplete: true, items: [CompletionItem(label: "abc", kind: .function)]), + json: """ + { + "isIncomplete" : true, + "items" : [ + { + "kind" : 3, + "label" : "abc" + } ] } - } - """) + """ + ) - checkCoding(CompletionList(isIncomplete: true, items: [CompletionItem(label: "abc", kind: .function)]), json: """ - { - "isIncomplete" : true, - "items" : [ + checkDecoding( + json: """ + [ { "kind" : 3, "label" : "abc" } ] - } - """) + """, + expected: CompletionList(isIncomplete: false, items: [CompletionItem(label: "abc", kind: .function)]) + ) - checkDecoding(json: """ - [ + checkCoding( + StringOrMarkupContent.markupContent(MarkupContent(kind: .markdown, value: "some **Markdown***")), + json: """ { - "kind" : 3, - "label" : "abc" + "kind" : "markdown", + "value" : "some **Markdown***" } - ] - """, expected: CompletionList(isIncomplete: false, items: [CompletionItem(label: "abc", kind: .function)])) + """ + ) - checkCoding(StringOrMarkupContent.markupContent(MarkupContent(kind: .markdown, value: "some **Markdown***")), json: """ - { - "kind" : "markdown", - "value" : "some **Markdown***" - } - """) + checkCoding( + StringOrMarkupContent.string("Some documentation"), + json: """ + "Some documentation" + """ + ) - checkCoding(StringOrMarkupContent.string("Some documentation"), json: """ - "Some documentation" - """) - checkCoding(PrepareRenameResponse(range: range), json: rangejson) - - checkCoding(PrepareRenameResponse(range: range, placeholder: "somePlaceholder"), json: """ - { - "placeholder" : "somePlaceholder", - "range" : \(rangejson.indented(2, skipFirstLine: true)) - } - """) - checkCoding(LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)]), json: """ - [ + checkCoding( + PrepareRenameResponse(range: range, placeholder: "somePlaceholder"), + json: """ { - "range" : \(rangejson.indented(4, skipFirstLine: true)), - "uri" : "\(urljson)" + "placeholder" : "somePlaceholder", + "range" : \(rangejson.indented(2, skipFirstLine: true)) } - ] - """) + """ + ) - checkCoding(LocationsOrLocationLinksResponse.locationLinks([LocationLink(targetUri: uri, targetRange: range, targetSelectionRange: range)]), json: """ - [ - { - "targetRange" : \(rangejson.indented(4, skipFirstLine: true)), - "targetSelectionRange" : \(rangejson.indented(4, skipFirstLine: true)), - "targetUri" : "\(urljson)" - } - ] - """) + checkCoding( + LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)]), + json: """ + [ + { + "range" : \(rangejson.indented(4, skipFirstLine: true)), + "uri" : "\(urljson)" + } + ] + """ + ) - checkDecoding(json: """ - { - "range" : \(rangejson.indented(2, skipFirstLine: true)), - "uri" : "\(urljson)" - } - """, expected: LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)])) + checkCoding( + LocationsOrLocationLinksResponse.locationLinks([ + LocationLink(targetUri: uri, targetRange: range, targetSelectionRange: range) + ]), + json: """ + [ + { + "targetRange" : \(rangejson.indented(4, skipFirstLine: true)), + "targetSelectionRange" : \(rangejson.indented(4, skipFirstLine: true)), + "targetUri" : "\(urljson)" + } + ] + """ + ) - checkCoding(DocumentSymbolResponse.documentSymbols([DocumentSymbol(name: "mySymbol", kind: .function, range: range, selectionRange: range)]), json: """ - [ + checkDecoding( + json: """ { - "kind" : 12, - "name" : "mySymbol", - "range" : \(rangejson.indented(4, skipFirstLine: true)), - "selectionRange" : \(rangejson.indented(4, skipFirstLine: true)) + "range" : \(rangejson.indented(2, skipFirstLine: true)), + "uri" : "\(urljson)" } - ] - """) + """, + expected: LocationsOrLocationLinksResponse.locations([Location(uri: uri, range: range)]) + ) - checkCoding(DocumentSymbolResponse.symbolInformation([SymbolInformation(name: "mySymbol", kind: .function, location: Location(uri: uri, range: range))]), json: """ - [ - { - "kind" : 12, - "location" : { - "range" : \(rangejson.indented(6, skipFirstLine: true)), - "uri" : "\(urljson)" - }, - "name" : "mySymbol" - } - ] - """) + checkCoding( + DocumentSymbolResponse.documentSymbols([ + DocumentSymbol(name: "mySymbol", kind: .function, range: range, selectionRange: range) + ]), + json: """ + [ + { + "kind" : 12, + "name" : "mySymbol", + "range" : \(rangejson.indented(4, skipFirstLine: true)), + "selectionRange" : \(rangejson.indented(4, skipFirstLine: true)) + } + ] + """ + ) + + checkCoding( + DocumentSymbolResponse.symbolInformation([ + SymbolInformation(name: "mySymbol", kind: .function, location: Location(uri: uri, range: range)) + ]), + json: """ + [ + { + "kind" : 12, + "location" : { + "range" : \(rangejson.indented(6, skipFirstLine: true)), + "uri" : "\(urljson)" + }, + "name" : "mySymbol" + } + ] + """ + ) checkCoding(ValueOrBool.value(5), json: "5") checkCoding(ValueOrBool.bool(false), json: "false") - checkDecoding(json: "2", expected: TextDocumentSyncOptions(openClose: nil, change: .incremental, willSave: nil, willSaveWaitUntil: nil, save: nil)) - - checkCoding(TextDocumentSyncOptions(), json: """ - { - "change" : 2, - "openClose" : true, - "save" : { - "includeText" : false - }, - "willSave" : true, - "willSaveWaitUntil" : false - } - """) - - checkCoding(WorkspaceEdit(documentChanges: [.textDocumentEdit(TextDocumentEdit(textDocument: OptionalVersionedTextDocumentIdentifier(uri, version: 2), edits: []))]), json: """ - { - "documentChanges" : [ - { - "edits" : [ + checkDecoding( + json: "2", + expected: TextDocumentSyncOptions( + openClose: nil, + change: .incremental, + willSave: nil, + willSaveWaitUntil: nil, + save: nil + ) + ) - ], - "textDocument" : { - "uri" : "\(urljson)", - "version" : 2 - } - } - ] - } - """) - checkCoding(WorkspaceEdit(documentChanges: [.createFile(CreateFile(uri: uri))]), json: """ - { - "documentChanges" : [ + checkCoding( + TextDocumentSyncOptions(), + json: """ { - "kind" : "create", - "uri" : "\(urljson)" + "change" : 2, + "openClose" : true, + "save" : { + "includeText" : false + }, + "willSave" : true, + "willSaveWaitUntil" : false } - ] - } - """) - checkCoding(WorkspaceEdit(documentChanges: [.renameFile(RenameFile(oldUri: uri, newUri: uri))]), json: """ - { - "documentChanges" : [ + """ + ) + + checkCoding( + WorkspaceEdit(documentChanges: [ + .textDocumentEdit( + TextDocumentEdit(textDocument: OptionalVersionedTextDocumentIdentifier(uri, version: 2), edits: []) + ) + ]), + json: """ { - "kind" : "rename", - "newUri" : "\(urljson)", - "oldUri" : "\(urljson)" + "documentChanges" : [ + { + "edits" : [ + + ], + "textDocument" : { + "uri" : "\(urljson)", + "version" : 2 + } + } + ] } - ] - } - """) - checkCoding(WorkspaceEdit(documentChanges: [.deleteFile(DeleteFile(uri: uri))]), json: """ - { - "documentChanges" : [ + """ + ) + checkCoding( + WorkspaceEdit(documentChanges: [.createFile(CreateFile(uri: uri))]), + json: """ { - "kind" : "delete", - "uri" : "\(urljson)" + "documentChanges" : [ + { + "kind" : "create", + "uri" : "\(urljson)" + } + ] } - ] - } - """) - + """ + ) + checkCoding( + WorkspaceEdit(documentChanges: [.renameFile(RenameFile(oldUri: uri, newUri: uri))]), + json: """ + { + "documentChanges" : [ + { + "kind" : "rename", + "newUri" : "\(urljson)", + "oldUri" : "\(urljson)" + } + ] + } + """ + ) + checkCoding( + WorkspaceEdit(documentChanges: [.deleteFile(DeleteFile(uri: uri))]), + json: """ + { + "documentChanges" : [ + { + "kind" : "delete", + "uri" : "\(urljson)" + } + ] + } + """ + ) } @@ -449,21 +583,24 @@ final class CodingTests: XCTestCase { var range: Range } - let range = Position(line: 5, utf16index: 23) ..< Position(line: 6, utf16index: 0) - checkCoding(WithPosRange(range: range), json: """ - { - "range" : { - "end" : { - "character" : 0, - "line" : 6 - }, - "start" : { - "character" : 23, - "line" : 5 + let range = Position(line: 5, utf16index: 23)..] } let ranges = [ - Position(line: 1, utf16index: 0) ..< Position(line: 1, utf16index: 10), - Position(line: 2, utf16index: 2) ..< Position(line: 3, utf16index: 0), - Position(line: 70, utf16index: 8) ..< Position(line: 70, utf16index: 11) + Position(line: 1, utf16index: 0)..? } - let range = Position(line: 5, utf16index: 23) ..< Position(line: 6, utf16index: 0) - checkCoding(WithPosRange(range: range), json: """ - { - "range" : { - "end" : { - "character" : 0, - "line" : 6 - }, - "start" : { - "character" : 23, - "line" : 5 + let range = Position(line: 5, utf16index: 23).. ()) async throws { +private func checkCompilationDatabaseBuildSystem( + _ compdb: ByteString, + block: (CompilationDatabaseBuildSystem) async throws -> () +) async throws { let fs = InMemoryFileSystem() try fs.createDirectory(AbsolutePath(validating: "/a")) try fs.writeFileContents(AbsolutePath(validating: "/a/compile_commands.json"), bytes: compdb) diff --git a/Tests/SKCoreTests/FallbackBuildSystemTests.swift b/Tests/SKCoreTests/FallbackBuildSystemTests.swift index 6d103a3bf..b9441e4c9 100644 --- a/Tests/SKCoreTests/FallbackBuildSystemTests.swift +++ b/Tests/SKCoreTests/FallbackBuildSystemTests.swift @@ -33,78 +33,104 @@ final class FallbackBuildSystemTests: XCTestCase { XCTAssertNil(settings.workingDirectory) let args = settings.compilerArguments - XCTAssertEqual(args, [ - "-sdk", - sdk.pathString, - source.pathString, - ]) + XCTAssertEqual( + args, + [ + "-sdk", + sdk.pathString, + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, [ - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, + [ + source.pathString + ] + ) } func testSwiftWithCustomFlags() throws { let sdk = try AbsolutePath(validating: "/my/sdk") let source = try AbsolutePath(validating: "/my/source.swift") - let buildSetup = BuildSetup(configuration: .debug, path: nil, flags: BuildFlags(swiftCompilerFlags: [ - "-Xfrontend", - "-debug-constraints" - ])) + let buildSetup = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags(swiftCompilerFlags: [ + "-Xfrontend", + "-debug-constraints", + ]) + ) let bs = FallbackBuildSystem(buildSetup: buildSetup) bs.sdkpath = sdk let args = bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments - XCTAssertEqual(args, [ - "-Xfrontend", - "-debug-constraints", - "-sdk", - sdk.pathString, - source.pathString, - ]) + XCTAssertEqual( + args, + [ + "-Xfrontend", + "-debug-constraints", + "-sdk", + sdk.pathString, + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, [ - "-Xfrontend", - "-debug-constraints", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .swift)?.compilerArguments, + [ + "-Xfrontend", + "-debug-constraints", + source.pathString, + ] + ) } func testSwiftWithCustomSDKFlag() throws { let sdk = try AbsolutePath(validating: "/my/sdk") let source = try AbsolutePath(validating: "/my/source.swift") - let buildSetup = BuildSetup(configuration: .debug, path: nil, flags: BuildFlags(swiftCompilerFlags: [ - "-sdk", - "/some/custom/sdk", - "-Xfrontend", - "-debug-constraints", - ])) + let buildSetup = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags(swiftCompilerFlags: [ + "-sdk", + "/some/custom/sdk", + "-Xfrontend", + "-debug-constraints", + ]) + ) let bs = FallbackBuildSystem(buildSetup: buildSetup) bs.sdkpath = sdk - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, [ - "-sdk", - "/some/custom/sdk", - "-Xfrontend", - "-debug-constraints", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, + [ + "-sdk", + "/some/custom/sdk", + "-Xfrontend", + "-debug-constraints", + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, [ - "-sdk", - "/some/custom/sdk", - "-Xfrontend", - "-debug-constraints", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .swift)!.compilerArguments, + [ + "-sdk", + "/some/custom/sdk", + "-Xfrontend", + "-debug-constraints", + source.pathString, + ] + ) } func testCXX() throws { @@ -118,112 +144,154 @@ final class FallbackBuildSystemTests: XCTestCase { XCTAssertNil(settings.workingDirectory) let args = settings.compilerArguments - XCTAssertEqual(args, [ - "-isysroot", - sdk.pathString, - source.pathString, - ]) + XCTAssertEqual( + args, + [ + "-isysroot", + sdk.pathString, + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [ - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, + [ + source.pathString + ] + ) } func testCXXWithCustomFlags() throws { let sdk = try AbsolutePath(validating: "/my/sdk") let source = try AbsolutePath(validating: "/my/source.cpp") - let buildSetup = BuildSetup(configuration: .debug, path: nil, flags: BuildFlags(cxxCompilerFlags: [ - "-v" - ])) + let buildSetup = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags(cxxCompilerFlags: [ + "-v" + ]) + ) let bs = FallbackBuildSystem(buildSetup: buildSetup) bs.sdkpath = sdk - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [ - "-v", - "-isysroot", - sdk.pathString, - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, + [ + "-v", + "-isysroot", + sdk.pathString, + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [ - "-v", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, + [ + "-v", + source.pathString, + ] + ) } func testCXXWithCustomIsysroot() throws { let sdk = try AbsolutePath(validating: "/my/sdk") let source = try AbsolutePath(validating: "/my/source.cpp") - let buildSetup = BuildSetup(configuration: .debug, path: nil, flags: BuildFlags(cxxCompilerFlags: [ - "-isysroot", - "/my/custom/sdk", - "-v" - ])) + let buildSetup = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags(cxxCompilerFlags: [ + "-isysroot", + "/my/custom/sdk", + "-v", + ]) + ) let bs = FallbackBuildSystem(buildSetup: buildSetup) bs.sdkpath = sdk - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [ - "-isysroot", - "/my/custom/sdk", - "-v", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, + [ + "-isysroot", + "/my/custom/sdk", + "-v", + source.pathString, + ] + ) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, [ - "-isysroot", - "/my/custom/sdk", - "-v", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .cpp)?.compilerArguments, + [ + "-isysroot", + "/my/custom/sdk", + "-v", + source.pathString, + ] + ) } func testC() throws { let source = try AbsolutePath(validating: "/my/source.c") let bs = FallbackBuildSystem(buildSetup: .default) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, [ - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, + [ + source.pathString + ] + ) } func testCWithCustomFlags() throws { let source = try AbsolutePath(validating: "/my/source.c") - let buildSetup = BuildSetup(configuration: .debug, path: nil, flags: BuildFlags(cCompilerFlags: [ - "-v" - ])) + let buildSetup = BuildSetup( + configuration: .debug, + path: nil, + flags: BuildFlags(cCompilerFlags: [ + "-v" + ]) + ) let bs = FallbackBuildSystem(buildSetup: buildSetup) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, [ - "-v", - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .c)?.compilerArguments, + [ + "-v", + source.pathString, + ] + ) } func testObjC() throws { let source = try AbsolutePath(validating: "/my/source.m") let bs = FallbackBuildSystem(buildSetup: .default) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .objective_c)?.compilerArguments, [ - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .objective_c)?.compilerArguments, + [ + source.pathString + ] + ) } func testObjCXX() throws { let source = try AbsolutePath(validating: "/my/source.mm") let bs = FallbackBuildSystem(buildSetup: .default) bs.sdkpath = nil - XCTAssertEqual(bs.buildSettings(for: source.asURI, language: .objective_cpp)?.compilerArguments, [ - source.pathString, - ]) + XCTAssertEqual( + bs.buildSettings(for: source.asURI, language: .objective_cpp)?.compilerArguments, + [ + source.pathString + ] + ) } func testUnknown() throws { diff --git a/Tests/SKCoreTests/ToolchainRegistryTests.swift b/Tests/SKCoreTests/ToolchainRegistryTests.swift index 179dbbbba..b5d8fada5 100644 --- a/Tests/SKCoreTests/ToolchainRegistryTests.swift +++ b/Tests/SKCoreTests/ToolchainRegistryTests.swift @@ -43,7 +43,9 @@ final class ToolchainRegistryTests: XCTestCase { XCTAssertNil(tr.default) let a = Toolchain(identifier: "a", displayName: "a", path: nil) try tr.registerToolchain(a) - try tr.registerToolchain(Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, displayName: "a", path: nil)) + try tr.registerToolchain( + Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, displayName: "a", path: nil) + ) XCTAssertEqual(tr.default?.identifier, ToolchainRegistry.darwinDefaultToolchainIdentifier) tr.default = a XCTAssertEqual(tr.default?.identifier, "a") @@ -68,8 +70,8 @@ final class ToolchainRegistryTests: XCTestCase { } func testSearchDarwin() throws { -// FIXME: requires PropertyListEncoder -#if os(macOS) + // FIXME: requires PropertyListEncoder + #if os(macOS) let fs = InMemoryFileSystem() let tr1 = ToolchainRegistry(fs) tr1.darwinToolchainOverride = nil @@ -80,8 +82,10 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, opensource: false, - toolchains.appending(component: "XcodeDefault.xctoolchain"), fs, - sourcekitd: true) + toolchains.appending(component: "XcodeDefault.xctoolchain"), + fs, + sourcekitd: true + ) XCTAssertNil(tr1.default) XCTAssert(tr1.toolchains.isEmpty) @@ -108,13 +112,17 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: "com.apple.fake.A", opensource: false, - toolchains.appending(component: "A.xctoolchain"), fs, - sourcekitd: true) + toolchains.appending(component: "A.xctoolchain"), + fs, + sourcekitd: true + ) try makeXCToolchain( identifier: "com.apple.fake.B", opensource: false, - toolchains.appending(component: "B.xctoolchain"), fs, - sourcekitd: true) + toolchains.appending(component: "B.xctoolchain"), + fs, + sourcekitd: true + ) tr.scanForToolchains(fs) XCTAssertEqual(tr.toolchains.count, 3) @@ -122,13 +130,17 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: "com.apple.fake.C", opensource: false, - toolchains.appending(component: "C.wrong_extension"), fs, - sourcekitd: true) + toolchains.appending(component: "C.wrong_extension"), + fs, + sourcekitd: true + ) try makeXCToolchain( identifier: "com.apple.fake.D", opensource: false, - toolchains.appending(component: "D_no_extension"), fs, - sourcekitd: true) + toolchains.appending(component: "D_no_extension"), + fs, + sourcekitd: true + ) tr.scanForToolchains(fs) XCTAssertEqual(tr.toolchains.count, 3) @@ -136,8 +148,10 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: "com.apple.fake.A", opensource: false, - toolchains.appending(component: "E.xctoolchain"), fs, - sourcekitd: true) + toolchains.appending(component: "E.xctoolchain"), + fs, + sourcekitd: true + ) tr.scanForToolchains(fs) XCTAssertEqual(tr.toolchains.count, 3) @@ -145,13 +159,17 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: "org.fake.global.A", opensource: true, - try AbsolutePath(validating: "/Library/Developer/Toolchains/A.xctoolchain"), fs, - sourcekitd: true) + try AbsolutePath(validating: "/Library/Developer/Toolchains/A.xctoolchain"), + fs, + sourcekitd: true + ) try makeXCToolchain( identifier: "org.fake.global.B", opensource: true, - try AbsolutePath(expandingTilde: "~/Library/Developer/Toolchains/B.xctoolchain"), fs, - sourcekitd: true) + try AbsolutePath(expandingTilde: "~/Library/Developer/Toolchains/B.xctoolchain"), + fs, + sourcekitd: true + ) tr.scanForToolchains(fs) XCTAssertEqual(tr.toolchains.count, 5) @@ -160,8 +178,10 @@ final class ToolchainRegistryTests: XCTestCase { try makeXCToolchain( identifier: "org.fake.explicit", opensource: false, - toolchains.appending(component: "Explicit.xctoolchain"), fs, - sourcekitd: true) + toolchains.appending(component: "Explicit.xctoolchain"), + fs, + sourcekitd: true + ) let tc = Toolchain(path, fs) XCTAssertNotNil(tc) @@ -173,9 +193,15 @@ final class ToolchainRegistryTests: XCTestCase { XCTAssertEqual(tc?.path, tcBin?.path) XCTAssertEqual(tc?.displayName, tcBin?.displayName) - let trInstall = ToolchainRegistry() - trInstall.scanForToolchains(installPath: path.appending(components: "usr", "bin"), environmentVariables: [], xcodes: [], xctoolchainSearchPaths: [], pathVariables: [], fs) + trInstall.scanForToolchains( + installPath: path.appending(components: "usr", "bin"), + environmentVariables: [], + xcodes: [], + xctoolchainSearchPaths: [], + pathVariables: [], + fs + ) XCTAssertEqual(trInstall.default?.identifier, "org.fake.explicit") XCTAssertEqual(trInstall.default?.path, path) @@ -187,7 +213,7 @@ final class ToolchainRegistryTests: XCTestCase { let checkByDir = ToolchainRegistry() checkByDir.scanForToolchains(xctoolchainSearchPath: toolchains, fs) XCTAssertEqual(checkByDir.toolchains.count, 4) -#endif + #endif } func testSearchPATH() throws { @@ -199,13 +225,16 @@ final class ToolchainRegistryTests: XCTestCase { XCTAssertNil(tr.default) XCTAssert(tr.toolchains.isEmpty) -#if os(Windows) + #if os(Windows) let separator: String = ";" -#else + #else let separator: String = ":" -#endif + #endif - try ProcessEnv.setVar("SOURCEKIT_PATH", value: ["/bogus", binPath.pathString, "/bogus2"].joined(separator: separator)) + try ProcessEnv.setVar( + "SOURCEKIT_PATH", + value: ["/bogus", binPath.pathString, "/bogus2"].joined(separator: separator) + ) defer { try! ProcessEnv.setVar("SOURCEKIT_PATH", value: "") } tr.scanForToolchains(fs) @@ -224,7 +253,10 @@ final class ToolchainRegistryTests: XCTestCase { XCTAssertNil(tc.libIndexStore) let binPath2 = try AbsolutePath(validating: "/other/my_toolchain/bin") - try ProcessEnv.setVar("SOME_TEST_ENV_PATH", value: ["/bogus", binPath2.pathString, "bogus2"].joined(separator: separator)) + try ProcessEnv.setVar( + "SOME_TEST_ENV_PATH", + value: ["/bogus", binPath2.pathString, "bogus2"].joined(separator: separator) + ) try makeToolchain(binPath: binPath2, fs, sourcekitd: true) tr.scanForToolchains(pathVariables: ["NOPE", "SOME_TEST_ENV_PATH", "MORE_NOPE"], fs) @@ -279,7 +311,8 @@ final class ToolchainRegistryTests: XCTestCase { tr.scanForToolchains( environmentVariables: ["TEST_ENV_SOURCEKIT_TOOLCHAIN_PATH_2"], setDefault: false, - fs) + fs + ) guard let tc = tr.toolchains.first(where: { tc in tc.path == binPath.parentDirectory }) else { XCTFail("couldn't find expected toolchain") @@ -300,30 +333,32 @@ final class ToolchainRegistryTests: XCTestCase { try withTemporaryDirectory(removeTreeOnDeinit: true) { tempDir in let path = tempDir.appending(components: "A.xctoolchain", "usr") try makeToolchain( - binPath: path.appending(component: "bin"), fs, + binPath: path.appending(component: "bin"), + fs, clang: true, clangd: true, swiftc: true, shouldChmod: false, - sourcekitd: true) + sourcekitd: true + ) - try fs.writeFileContents(path.appending(components: "bin", "other") , bytes: "") + try fs.writeFileContents(path.appending(components: "bin", "other"), bytes: "") let t1 = Toolchain(path.parentDirectory, fs)! XCTAssertNotNil(t1.sourcekitd) -#if os(Windows) + #if os(Windows) // Windows does not have file permissions but rather checks the contents // which have been written out. XCTAssertNotNil(t1.clang) XCTAssertNotNil(t1.clangd) XCTAssertNotNil(t1.swiftc) -#else + #else XCTAssertNil(t1.clang) XCTAssertNil(t1.clangd) XCTAssertNil(t1.swiftc) -#endif + #endif -#if !os(Windows) + #if !os(Windows) func chmodRX(_ path: AbsolutePath) { XCTAssertEqual(chmod(path.pathString, S_IRUSR | S_IXUSR), 0) } @@ -332,7 +367,7 @@ final class ToolchainRegistryTests: XCTestCase { chmodRX(path.appending(components: "bin", "clangd")) chmodRX(path.appending(components: "bin", "swiftc")) chmodRX(path.appending(components: "bin", "other")) -#endif + #endif let t2 = Toolchain(path.parentDirectory, fs)! XCTAssertNotNil(t2.sourcekitd) @@ -383,8 +418,10 @@ final class ToolchainRegistryTests: XCTestCase { let tr = ToolchainRegistry() let toolchain = Toolchain(identifier: "a", displayName: "a", path: nil) try tr.registerToolchain(toolchain) - XCTAssertThrowsError(try tr.registerToolchain(toolchain), - "Expected error registering toolchain twice") { e in + XCTAssertThrowsError( + try tr.registerToolchain(toolchain), + "Expected error registering toolchain twice" + ) { e in guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainIdentifier else { XCTFail("Expected .duplicateToolchainIdentifier not \(e)") return @@ -398,8 +435,10 @@ final class ToolchainRegistryTests: XCTestCase { let first = Toolchain(identifier: "a", displayName: "a", path: path) let second = Toolchain(identifier: "b", displayName: "b", path: path) try tr.registerToolchain(first) - XCTAssertThrowsError(try tr.registerToolchain(second), - "Expected error registering toolchain twice") { e in + XCTAssertThrowsError( + try tr.registerToolchain(second), + "Expected error registering toolchain twice" + ) { e in guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainPath else { XCTFail("Error mismatch: expected duplicateToolchainPath not \(e)") return @@ -409,12 +448,16 @@ final class ToolchainRegistryTests: XCTestCase { func testDuplicateXcodeError() throws { let tr = ToolchainRegistry() - let xcodeToolchain = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, - displayName: "a", - path: try AbsolutePath(validating: "/versionA")) + let xcodeToolchain = Toolchain( + identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, + displayName: "a", + path: try AbsolutePath(validating: "/versionA") + ) try tr.registerToolchain(xcodeToolchain) - XCTAssertThrowsError(try tr.registerToolchain(xcodeToolchain), - "Expected error registering toolchain twice") { e in + XCTAssertThrowsError( + try tr.registerToolchain(xcodeToolchain), + "Expected error registering toolchain twice" + ) { e in guard let error = e as? ToolchainRegistry.Error, error == .duplicateToolchainPath else { XCTFail("Error mismatch: expected duplicateToolchainPath not \(e)") return @@ -425,13 +468,17 @@ final class ToolchainRegistryTests: XCTestCase { func testMultipleXcodes() throws { let tr = ToolchainRegistry() let pathA = try AbsolutePath(validating: "/versionA") - let xcodeA = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, - displayName: "a", - path: pathA) + let xcodeA = Toolchain( + identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, + displayName: "a", + path: pathA + ) let pathB = try AbsolutePath(validating: "/versionB") - let xcodeB = Toolchain(identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, - displayName: "b", - path: pathB) + let xcodeB = Toolchain( + identifier: ToolchainRegistry.darwinDefaultToolchainIdentifier, + displayName: "b", + path: pathB + ) try tr.registerToolchain(xcodeA) try tr.registerToolchain(xcodeB) XCTAssert(tr.toolchain(path: pathA) === xcodeA) @@ -466,7 +513,11 @@ final class ToolchainRegistryTests: XCTestCase { try ProcessEnv.setVar("TEST_SOURCEKIT_TOOLCHAIN_PATH_1", value: "/t2/bin") let tr = ToolchainRegistry() - tr.scanForToolchains(installPath: try AbsolutePath(validating: "/t1/bin"), environmentVariables: ["TEST_SOURCEKIT_TOOLCHAIN_PATH_1"], fs) + tr.scanForToolchains( + installPath: try AbsolutePath(validating: "/t1/bin"), + environmentVariables: ["TEST_SOURCEKIT_TOOLCHAIN_PATH_1"], + fs + ) XCTAssertEqual(tr.toolchains.count, 2) // Env variable wins. @@ -483,7 +534,7 @@ private func makeXCToolchain( clang: Bool = false, clangd: Bool = false, swiftc: Bool = false, - shouldChmod: Bool = true, // whether to mark exec + shouldChmod: Bool = true, // whether to mark exec sourcekitd: Bool = false, sourcekitdInProc: Bool = false, libIndexStore: Bool = false @@ -491,10 +542,14 @@ private func makeXCToolchain( try fs.createDirectory(path, recursive: true) let infoPlistPath = path.appending(component: opensource ? "Info.plist" : "ToolchainInfo.plist") let infoPlist = try PropertyListEncoder().encode( - XCToolchainPlist(identifier: identifier, displayName: "name-\(identifier)")) - try fs.writeFileContents(infoPlistPath, body: { stream in - stream.write(infoPlist) - }) + XCToolchainPlist(identifier: identifier, displayName: "name-\(identifier)") + ) + try fs.writeFileContents( + infoPlistPath, + body: { stream in + stream.write(infoPlist) + } + ) try makeToolchain( binPath: path.appending(components: "usr", "bin"), @@ -505,7 +560,8 @@ private func makeXCToolchain( shouldChmod: shouldChmod, sourcekitd: sourcekitd, sourcekitdInProc: sourcekitdInProc, - libIndexStore: libIndexStore) + libIndexStore: libIndexStore + ) } #endif @@ -515,13 +571,15 @@ private func makeToolchain( clang: Bool = false, clangd: Bool = false, swiftc: Bool = false, - shouldChmod: Bool = true, // whether to mark exec + shouldChmod: Bool = true, // whether to mark exec sourcekitd: Bool = false, sourcekitdInProc: Bool = false, libIndexStore: Bool = false ) throws { - precondition(!clang && !swiftc && !clangd || !shouldChmod, - "Cannot make toolchain binaries exectuable with InMemoryFileSystem") + precondition( + !clang && !swiftc && !clangd || !shouldChmod, + "Cannot make toolchain binaries exectuable with InMemoryFileSystem" + ) // tiny PE binary from: https://archive.is/w01DO let contents: [UInt8] = [ @@ -533,7 +591,7 @@ private func makeToolchain( 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02 + 0x02, ] let libPath = binPath.parentDirectory.appending(component: "lib") @@ -542,11 +600,11 @@ private func makeToolchain( let makeExec = { (path: AbsolutePath) in try fs.writeFileContents(path, bytes: ByteString(contents)) -#if !os(Windows) + #if !os(Windows) if shouldChmod { XCTAssertEqual(chmod(path.pathString, S_IRUSR | S_IXUSR), 0) } -#endif + #endif } let execExt = Platform.current?.executableExtension ?? "" @@ -565,21 +623,21 @@ private func makeToolchain( if sourcekitd { try fs.createDirectory(libPath.appending(component: "sourcekitd.framework")) - try fs.writeFileContents(libPath.appending(components: "sourcekitd.framework", "sourcekitd") , bytes: "") + try fs.writeFileContents(libPath.appending(components: "sourcekitd.framework", "sourcekitd"), bytes: "") } if sourcekitdInProc { -#if os(Windows) - try fs.writeFileContents(binPath.appending(component: "sourcekitdInProc\(dylibSuffix)") , bytes: "") -#else - try fs.writeFileContents(libPath.appending(component: "libsourcekitdInProc\(dylibSuffix)") , bytes: "") -#endif + #if os(Windows) + try fs.writeFileContents(binPath.appending(component: "sourcekitdInProc\(dylibSuffix)"), bytes: "") + #else + try fs.writeFileContents(libPath.appending(component: "libsourcekitdInProc\(dylibSuffix)"), bytes: "") + #endif } if libIndexStore { -#if os(Windows) + #if os(Windows) // Windows has a prefix of `lib` on this particular library ... - try fs.writeFileContents(binPath.appending(component: "libIndexStore\(dylibSuffix)") , bytes: "") -#else - try fs.writeFileContents(libPath.appending(component: "libIndexStore\(dylibSuffix)") , bytes: "") -#endif + try fs.writeFileContents(binPath.appending(component: "libIndexStore\(dylibSuffix)"), bytes: "") + #else + try fs.writeFileContents(libPath.appending(component: "libIndexStore\(dylibSuffix)"), bytes: "") + #endif } } diff --git a/Tests/SKSupportTests/SupportPerfTests.swift b/Tests/SKSupportTests/SupportPerfTests.swift index 10e824d05..c62907ac6 100644 --- a/Tests/SKSupportTests/SupportPerfTests.swift +++ b/Tests/SKSupportTests/SupportPerfTests.swift @@ -19,9 +19,10 @@ final class SupportPerfTests: PerfTestCase { func testLineTableAppendPerf() { - let characters: [Character] = [ - "\t", "\n" - ] + (32...126).map { Character(UnicodeScalar($0)) } + let characters: [Character] = + [ + "\t", "\n", + ] + (32...126).map { Character(UnicodeScalar($0)) } #if DEBUG || !ENABLE_PERF_TESTS let iterations = 1_000 @@ -47,8 +48,9 @@ final class SupportPerfTests: PerfTestCase { } func testLineTableSingleCharEditPerf() { - let characters: [Character] = [ - "\t", "\n" + let characters: [Character] = + [ + "\t", "\n", ] + (32...126).map { Character(UnicodeScalar($0)) } #if DEBUG || !ENABLE_PERF_TESTS @@ -71,12 +73,12 @@ final class SupportPerfTests: PerfTestCase { self.startMeasuring() for _ in 1...iterations { - let line = (0 ..< (t.count-1)).randomElement(using: &lcg) ?? 0 - let col = (0 ..< t[line].utf16.count).randomElement(using: &lcg) ?? 0 + let line = (0..<(t.count - 1)).randomElement(using: &lcg) ?? 0 + let col = (0.. AbsolutePath -{ + platform: String +) -> AbsolutePath { let buildPath = config.path ?? root.appending(component: ".build") return buildPath.appending(components: platform, "\(config.configuration)") } diff --git a/Tests/SourceKitDTests/CrashRecoveryTests.swift b/Tests/SourceKitDTests/CrashRecoveryTests.swift index 062541eae..bacf74156 100644 --- a/Tests/SourceKitDTests/CrashRecoveryTests.swift +++ b/Tests/SourceKitDTests/CrashRecoveryTests.swift @@ -11,13 +11,13 @@ //===----------------------------------------------------------------------===// import ISDBTestSupport -import LanguageServerProtocol -import LSPTestSupport import LSPLogging -import SourceKitLSP -import SourceKitD +import LSPTestSupport +import LanguageServerProtocol import SKSupport import SKTestSupport +import SourceKitD +import SourceKitLSP import XCTest import enum PackageLoading.Platform @@ -59,7 +59,8 @@ final class CrashRecoveryTests: XCTestCase { log("Received diagnostics for open - syntactic") documentOpened.fulfill() }) - ws.sk.appendOneShotNotificationHandler({ (note: LanguageServerProtocol.Notification) in + ws.sk.appendOneShotNotificationHandler({ + (note: LanguageServerProtocol.Notification) in log("Received diagnostics for open - semantic") documentOpened.fulfill() }) @@ -68,31 +69,53 @@ final class CrashRecoveryTests: XCTestCase { // Make a change to the file that's not saved to disk. This way we can check that we re-open the correct in-memory state. - let addFuncChange = TextDocumentContentChangeEvent(range: loc.position..) -> Void in + log("Received diagnostics for text edit - syntactic") + }, + { (note: LanguageServerProtocol.Notification) -> Void in + log("Received diagnostics for text edit - semantic") } - """) - ws.sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: VersionedTextDocumentIdentifier(loc.docUri, version: 2), contentChanges: [addFuncChange]), { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for text edit - syntactic") - }, { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for text edit - semantic") - }) + ) // Do a sanity check and verify that we get the expected result from a hover response before crashing sourcekitd. let hoverRequest = HoverRequest(textDocument: loc.docIdentifier, position: Position(line: 1, utf16index: 6)) let preCrashHoverResponse = try ws.sk.sendSync(hoverRequest) - precondition(preCrashHoverResponse?.contains(string: "foo()") ?? false, "Sanity check failed. The Hover response did not contain foo(), even before crashing sourcekitd. Received response: \(String(describing: preCrashHoverResponse))") + precondition( + preCrashHoverResponse?.contains(string: "foo()") ?? false, + "Sanity check failed. The Hover response did not contain foo(), even before crashing sourcekitd. Received response: \(String(describing: preCrashHoverResponse))" + ) // Crash sourcekitd - let sourcekitdServer = await ws.testServer.server!._languageService(for: loc.docUri, .swift, in: ws.testServer.server!.workspaceForDocument(uri: loc.docUri)!) as! SwiftLanguageServer + let sourcekitdServer = + await ws.testServer.server!._languageService( + for: loc.docUri, + .swift, + in: ws.testServer.server!.workspaceForDocument(uri: loc.docUri)! + ) + as! SwiftLanguageServer let sourcekitdCrashed = expectation(description: "sourcekitd has crashed") let sourcekitdRestarted = expectation(description: "sourcekitd has been restarted (syntactic only)") - let semanticFunctionalityRestored = expectation(description: "sourcekitd has restored semantic language functionality") + let semanticFunctionalityRestored = expectation( + description: "sourcekitd has restored semantic language functionality" + ) await sourcekitdServer.addStateChangeHandler { (oldState, newState) in switch newState { @@ -127,13 +150,17 @@ final class CrashRecoveryTests: XCTestCase { XCTAssertTrue(postCrashHoverResponse?.contains(string: "foo()") ?? false) } } - + /// Crashes clangd and waits for it to restart /// - Parameters: /// - ws: The workspace for which the clangd server shall be crashed /// - document: The URI of a C/C++/... document in the workspace private func crashClangd(for ws: SKTibsTestWorkspace, document docUri: DocumentURI) async throws { - let clangdServer = await ws.testServer.server!._languageService(for: docUri, .cpp, in: ws.testServer.server!.workspaceForDocument(uri: docUri)!)! + let clangdServer = await ws.testServer.server!._languageService( + for: docUri, + .cpp, + in: ws.testServer.server!.workspaceForDocument(uri: docUri)! + )! let clangdCrashed = self.expectation(description: "clangd crashed") let clangdRestarted = self.expectation(description: "clangd restarted") @@ -165,12 +192,21 @@ final class CrashRecoveryTests: XCTestCase { // Make a change to the file that's not saved to disk. This way we can check that we re-open the correct in-memory state. - let addFuncChange = TextDocumentContentChangeEvent(range: loc.position.. 5, "Clangd restarted too quickly after crashing twice in a row. We are not preventing crash loops.") + XCTAssert( + Date().timeIntervalSince(firstRestartDate) > 5, + "Clangd restarted too quickly after crashing twice in a row. We are not preventing crash loops." + ) } } - diff --git a/Tests/SourceKitDTests/SourceKitDTests.swift b/Tests/SourceKitDTests/SourceKitDTests.swift index ae7fa86ce..d13c226ef 100644 --- a/Tests/SourceKitDTests/SourceKitDTests.swift +++ b/Tests/SourceKitDTests/SourceKitDTests.swift @@ -10,14 +10,14 @@ // //===----------------------------------------------------------------------===// +import Foundation +import ISDBTestSupport +import ISDBTibs import LSPTestSupport -import SourceKitD import SKCore import SKSupport +import SourceKitD import TSCBasic -import ISDBTibs -import ISDBTestSupport -import Foundation import XCTest import enum PackageLoading.Platform @@ -30,7 +30,8 @@ final class SourceKitDTests: XCTestCase { override class func setUp() { sourcekitdPath = ToolchainRegistry.shared.default!.sourcekitd! guard case .darwin? = Platform.current else { return } - sdkpath = try? Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx").trimmingCharacters(in: .whitespacesAndNewlines) + sdkpath = try? Process.checkNonZeroExit(args: "/usr/bin/xcrun", "--show-sdk-path", "--sdk", "macosx") + .trimmingCharacters(in: .whitespacesAndNewlines) } func testMultipleNotificationHandlers() throws { @@ -41,7 +42,7 @@ final class SourceKitDTests: XCTestCase { let isExpectedNotification = { (response: SKDResponse) -> Bool in if let notification: sourcekitd_uid_t = response.value?[keys.notification], - let name: String = response.value?[keys.name] + let name: String = response.value?[keys.name] { return name == path && notification == sourcekitd.values.notification_documentupdate } diff --git a/Tests/SourceKitLSPTests/BuildSystemTests.swift b/Tests/SourceKitLSPTests/BuildSystemTests.swift index ae32c9000..1178d1ff6 100644 --- a/Tests/SourceKitLSPTests/BuildSystemTests.swift +++ b/Tests/SourceKitLSPTests/BuildSystemTests.swift @@ -11,8 +11,8 @@ //===----------------------------------------------------------------------===// import BuildServerProtocol -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKCore import SKTestSupport import SourceKitLSP @@ -108,21 +108,24 @@ final class BuildSystemTests: XCTestCase { buildSetup: TestSourceKitServer.serverOptions.buildSetup, underlyingBuildSystem: buildSystem, index: nil, - indexDelegate: nil) - + indexDelegate: nil + ) await server.setWorkspaces([workspace]) await workspace.buildSystemManager.setDelegate(server) sk = testServer.client - _ = try! sk.sendSync(InitializeRequest( - processId: nil, - rootPath: nil, - rootURI: nil, - initializationOptions: nil, - capabilities: ClientCapabilities(workspace: nil, textDocument: nil), - trace: .off, - workspaceFolders: nil)) + _ = try! sk.sendSync( + InitializeRequest( + processId: nil, + rootPath: nil, + rootURI: nil, + initializationOptions: nil, + capabilities: ClientCapabilities(workspace: nil, textDocument: nil), + trace: .off, + workspaceFolders: nil + ) + ) setUpCompleted.fulfill() } if XCTWaiter.wait(for: [setUpCompleted], timeout: defaultTimeout) != .completed { @@ -142,23 +145,23 @@ final class BuildSystemTests: XCTestCase { guard haveClangd else { return } -#if os(Windows) + #if os(Windows) let url = URL(fileURLWithPath: "C:/\(UUID())/file.m") -#else + #else let url = URL(fileURLWithPath: "/\(UUID())/file.m") -#endif + #endif let doc = DocumentURI(url) let args = [url.path, "-DDEBUG"] let text = """ - #ifdef FOO - static void foo() {} - #endif + #ifdef FOO + static void foo() {} + #endif - int main() { - foo(); - return 0; - } - """ + int main() { + foo(); + return 0; + } + """ buildSystem.buildSettingsByFile[doc] = FileBuildSettings(compilerArguments: args) @@ -166,19 +169,24 @@ final class BuildSystemTests: XCTestCase { let documentManager = await self.testServer.server!._documentManager - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: doc, - language: .objective_c, - version: 12, - text: text - )), { (note: Notification) in - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: doc, + language: .objective_c, + version: 12, + text: text + ) + ), + { (note: Notification) in + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) + } + ) // Modify the build settings and inform the delegate. // This should trigger a new publish diagnostics and we should no longer have errors. - let newSettings = FileBuildSettings(compilerArguments: args + ["-DFOO"]) + let newSettings = FileBuildSettings(compilerArguments: args + ["-DFOO"]) buildSystem.buildSettingsByFile[doc] = newSettings let expectation = XCTestExpectation(description: "refresh") @@ -201,30 +209,36 @@ final class BuildSystemTests: XCTestCase { buildSystem.buildSettingsByFile[doc] = FileBuildSettings(compilerArguments: args) let text = """ - #if FOO - func foo() {} - #endif + #if FOO + func foo() {} + #endif - foo() - """ + foo() + """ sk.allowUnexpectedNotification = false let documentManager = await self.testServer.server!._documentManager - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: doc, - language: .swift, - version: 12, - text: text - )), { (note: Notification) in - // Syntactic analysis - no expected errors here. - XCTAssertEqual(note.params.diagnostics.count, 0) - XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) - }, { (note: Notification) in - // Semantic analysis - expect one error here. - XCTAssertEqual(note.params.diagnostics.count, 1) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: doc, + language: .swift, + version: 12, + text: text + ) + ), + { (note: Notification) in + // Syntactic analysis - no expected errors here. + XCTAssertEqual(note.params.diagnostics.count, 0) + XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) + }, + { (note: Notification) in + // Semantic analysis - expect one error here. + XCTAssertEqual(note.params.diagnostics.count, 1) + } + ) // Modify the build settings and inform the delegate. // This should trigger a new publish diagnostics and we should no longer have errors. @@ -238,7 +252,7 @@ final class BuildSystemTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 1) expectation.fulfill() } - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in // Semantic analysis - no expected errors here because we fixed the settings. XCTAssertEqual(note.params.diagnostics.count, 0) expectation.fulfill() @@ -251,38 +265,43 @@ final class BuildSystemTests: XCTestCase { func testClangdDocumentFallbackWithholdsDiagnostics() async throws { try XCTSkipIf(!haveClangd) -#if os(Windows) + #if os(Windows) let url = URL(fileURLWithPath: "C:/\(UUID())/file.m") -#else + #else let url = URL(fileURLWithPath: "/\(UUID())/file.m") -#endif + #endif let doc = DocumentURI(url) let args = [url.path, "-DDEBUG"] let text = """ - #ifdef FOO - static void foo() {} - #endif + #ifdef FOO + static void foo() {} + #endif - int main() { - foo(); - return 0; - } - """ + int main() { + foo(); + return 0; + } + """ sk.allowUnexpectedNotification = false let documentManager = await self.testServer.server!._documentManager - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: doc, - language: .objective_c, - version: 12, - text: text - )), { (note: Notification) in - // Expect diagnostics to be withheld. - XCTAssertEqual(note.params.diagnostics.count, 0) - XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: doc, + language: .objective_c, + version: 12, + text: text + ) + ), + { (note: Notification) in + // Expect diagnostics to be withheld. + XCTAssertEqual(note.params.diagnostics.count, 0) + XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) + } + ) // Modify the build settings and inform the delegate. // This should trigger a new publish diagnostics and we should see a diagnostic. @@ -310,31 +329,37 @@ final class BuildSystemTests: XCTestCase { primarySettings.compilerArguments.append("-DPRIMARY") let text = """ - #if FOO - func foo() {} - #endif + #if FOO + func foo() {} + #endif - foo() - func - """ + foo() + func + """ sk.allowUnexpectedNotification = false let documentManager = await self.testServer.server!._documentManager - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: doc, - language: .swift, - version: 12, - text: text - )), { (note: Notification) in - // Syntactic analysis - one expected errors here (for `func`). - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) - }, { (note: Notification) in - // Should be the same syntactic analysis since we are using fallback arguments - XCTAssertEqual(note.params.diagnostics.count, 1) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: doc, + language: .swift, + version: 12, + text: text + ) + ), + { (note: Notification) in + // Syntactic analysis - one expected errors here (for `func`). + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual(text, documentManager.latestSnapshot(doc)!.text) + }, + { (note: Notification) in + // Should be the same syntactic analysis since we are using fallback arguments + XCTAssertEqual(note.params.diagnostics.count, 1) + } + ) // Swap from fallback settings to primary build system settings. buildSystem.buildSettingsByFile[doc] = primarySettings @@ -345,7 +370,7 @@ final class BuildSystemTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 1) expectation.fulfill() } - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in // Semantic analysis - two errors since `-DFOO` was not passed. XCTAssertEqual(note.params.diagnostics.count, 2) expectation.fulfill() @@ -397,12 +422,18 @@ final class BuildSystemTests: XCTestCase { } try ws.edit(rebuild: true) { (changes, _) in - changes.write(""" + changes.write( + """ // empty - """, to: ws.testLoc("d_func").url) - changes.write(""" + """, + to: ws.testLoc("d_func").url + ) + changes.write( + """ #include "unique.h" - """, to: ws.testLoc("c_func").url) + """, + to: ws.testLoc("c_func").url + ) } try await fulfillmentOfOrThrow([use_c]) diff --git a/Tests/SourceKitLSPTests/CallHierarchyTests.swift b/Tests/SourceKitLSPTests/CallHierarchyTests.swift index 42a20dd27..44fb28bf6 100644 --- a/Tests/SourceKitLSPTests/CallHierarchyTests.swift +++ b/Tests/SourceKitLSPTests/CallHierarchyTests.swift @@ -57,7 +57,8 @@ final class CallHierarchyTests: XCTestCase { return "" } guard case let .dictionary(data) = item.data, - case let .string(usr) = data["usr"] else { + case let .string(usr) = data["usr"] + else { XCTFail("unable to find usr in call hierarchy in item data dictionary") return "" } @@ -68,13 +69,19 @@ final class CallHierarchyTests: XCTestCase { func testLoc(_ name: String) -> TestLocation { ws.testLoc(name) - } + } func loc(_ name: String) -> Location { Location(badUTF16: ws.testLoc(name)) } - func item(_ name: String, _ kind: SymbolKind, detail: String = "main", usr: String, at locName: String) throws -> CallHierarchyItem { + func item( + _ name: String, + _ kind: SymbolKind, + detail: String = "main", + usr: String, + at locName: String + ) throws -> CallHierarchyItem { let location = loc(locName) return CallHierarchyItem( name: name, @@ -86,7 +93,7 @@ final class CallHierarchyTests: XCTestCase { selectionRange: location.range, data: .dictionary([ "usr": .string(usr), - "uri": .string(try location.uri.nativeURI.stringValue) + "uri": .string(try location.uri.nativeURI.stringValue), ]) ) } @@ -113,28 +120,43 @@ final class CallHierarchyTests: XCTestCase { // Test outgoing call hierarchy XCTAssertEqual(try outgoingCalls(at: testLoc("a")), []) - XCTAssertEqual(try outgoingCalls(at: testLoc("b")), [ - outCall(try item("a()", .function, usr: aUsr, at: "a"), at: "b->a"), - outCall(try item("c()", .function, usr: cUsr, at: "c"), at: "b->c"), - outCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->b"), - ]) - XCTAssertEqual(try outgoingCalls(at: testLoc("c")), [ - outCall(try item("a()", .function, usr: aUsr, at: "a"), at: "c->a"), - outCall(try item("d()", .function, usr: dUsr, at: "d"), at: "c->d"), - outCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->c"), - ]) + XCTAssertEqual( + try outgoingCalls(at: testLoc("b")), + [ + outCall(try item("a()", .function, usr: aUsr, at: "a"), at: "b->a"), + outCall(try item("c()", .function, usr: cUsr, at: "c"), at: "b->c"), + outCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->b"), + ] + ) + XCTAssertEqual( + try outgoingCalls(at: testLoc("c")), + [ + outCall(try item("a()", .function, usr: aUsr, at: "a"), at: "c->a"), + outCall(try item("d()", .function, usr: dUsr, at: "d"), at: "c->d"), + outCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->c"), + ] + ) // Test incoming call hierarchy - XCTAssertEqual(try incomingCalls(at: testLoc("a")), [ - inCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->a"), - inCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->a"), - ]) - XCTAssertEqual(try incomingCalls(at: testLoc("b")), [ - inCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->b"), - ]) - XCTAssertEqual(try incomingCalls(at: testLoc("d")), [ - inCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->d"), - ]) + XCTAssertEqual( + try incomingCalls(at: testLoc("a")), + [ + inCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->a"), + inCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->a"), + ] + ) + XCTAssertEqual( + try incomingCalls(at: testLoc("b")), + [ + inCall(try item("b(x:)", .function, usr: bUsr, at: "b"), at: "b->b") + ] + ) + XCTAssertEqual( + try incomingCalls(at: testLoc("d")), + [ + inCall(try item("c()", .function, usr: cUsr, at: "c"), at: "c->d") + ] + ) } } diff --git a/Tests/SourceKitLSPTests/CodeActionTests.swift b/Tests/SourceKitLSPTests/CodeActionTests.swift index 7b8bf5a75..7bc1dc253 100644 --- a/Tests/SourceKitLSPTests/CodeActionTests.swift +++ b/Tests/SourceKitLSPTests/CodeActionTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKTestSupport import SourceKitLSP import XCTest @@ -48,32 +48,36 @@ final class CodeActionTests: XCTestCase { var data: Data var response: CodeActionRequestResponse capabilityJson = - """ - { - "dynamicRegistration": true, - "codeActionLiteralSupport" : { - "codeActionKind": { - "valueSet": [] + """ + { + "dynamicRegistration": true, + "codeActionLiteralSupport" : { + "codeActionKind": { + "valueSet": [] + } } } - } - """ + """ data = capabilityJson.data(using: .utf8)! - capabilities = try JSONDecoder().decode(TextDocumentClientCapabilities.CodeAction.self, - from: data) + capabilities = try JSONDecoder().decode( + TextDocumentClientCapabilities.CodeAction.self, + from: data + ) response = .init(codeActions: [codeAction, codeAction2], clientCapabilities: capabilities) let actions = try JSONDecoder().decode([CodeAction].self, from: JSONEncoder().encode(response)) XCTAssertEqual(actions, [codeAction, codeAction2]) capabilityJson = - """ - { - "dynamicRegistration": true - } - """ + """ + { + "dynamicRegistration": true + } + """ data = capabilityJson.data(using: .utf8)! - capabilities = try JSONDecoder().decode(TextDocumentClientCapabilities.CodeAction.self, - from: data) + capabilities = try JSONDecoder().decode( + TextDocumentClientCapabilities.CodeAction.self, + from: data + ) response = .init(codeActions: [codeAction, codeAction2], clientCapabilities: capabilities) let commands = try JSONDecoder().decode([Command].self, from: JSONEncoder().encode(response)) XCTAssertEqual(commands, [command]) @@ -95,37 +99,41 @@ final class CodeActionTests: XCTestCase { var data: Data var response: CodeActionRequestResponse capabilityJson = - """ - { - "dynamicRegistration": true, - "codeActionLiteralSupport" : { - "codeActionKind": { - "valueSet": ["refactor"] + """ + { + "dynamicRegistration": true, + "codeActionLiteralSupport" : { + "codeActionKind": { + "valueSet": ["refactor"] + } } } - } - """ + """ data = capabilityJson.data(using: .utf8)! - capabilities = try JSONDecoder().decode(TextDocumentClientCapabilities.CodeAction.self, - from: data) + capabilities = try JSONDecoder().decode( + TextDocumentClientCapabilities.CodeAction.self, + from: data + ) response = .init(codeActions: actions, clientCapabilities: capabilities) XCTAssertEqual(response, .codeActions([unspecifiedAction, refactorAction, quickfixAction])) capabilityJson = - """ - { - "dynamicRegistration": true, - "codeActionLiteralSupport" : { - "codeActionKind": { - "valueSet": [] + """ + { + "dynamicRegistration": true, + "codeActionLiteralSupport" : { + "codeActionKind": { + "valueSet": [] + } } } - } - """ + """ data = capabilityJson.data(using: .utf8)! - capabilities = try JSONDecoder().decode(TextDocumentClientCapabilities.CodeAction.self, - from: data) + capabilities = try JSONDecoder().decode( + TextDocumentClientCapabilities.CodeAction.self, + from: data + ) response = .init(codeActions: actions, clientCapabilities: capabilities) XCTAssertEqual(response, .codeActions([unspecifiedAction, refactorAction, quickfixAction])) @@ -143,25 +151,36 @@ final class CodeActionTests: XCTestCase { let command = Command(title: "Title", command: "Command", arguments: [1, "text", 2.2, nil]) let codeAction = CodeAction(title: "1") let codeAction2 = CodeAction(title: "2", command: command) - let request = CodeActionRequest(range: Position(line: 0, utf16index: 0)..) in + ws.sk.appendOneShotNotificationHandler { (note: Notification) in // syntactic diagnostics XCTAssertEqual(note.params.uri, def.docUri) XCTAssertEqual(note.params.diagnostics, []) @@ -268,7 +309,7 @@ final class CodeActionTests: XCTestCase { } var diags: [Diagnostic]! = nil - ws.sk.appendOneShotNotificationHandler { (note: Notification) in + ws.sk.appendOneShotNotificationHandler { (note: Notification) in // semantic diagnostics XCTAssertEqual(note.params.uri, def.docUri) XCTAssertEqual(note.params.diagnostics.count, 1) @@ -279,7 +320,11 @@ final class CodeActionTests: XCTestCase { try await fulfillmentOfOrThrow([syntacticDiagnosticsReceived, semanticDiagnosticsReceived]) let textDocument = TextDocumentIdentifier(def.url) - let actionsRequest = CodeActionRequest(range: def.position.. CompilationDatabaseCompileCommand in + let newCommands = compilationDatabase.allCommands.map { + (command: CompilationDatabaseCompileCommand) -> CompilationDatabaseCompileCommand in var command = command command.commandLine.removeAll(where: { $0 == "-DFOO" }) return command @@ -49,15 +56,17 @@ final class CompilationDatabaseTests: XCTestCase { builder.write(newCompilationDatabaseStr, to: compilationDatabaseUrl) }) - ws.sk.send(DidChangeWatchedFilesNotification(changes: [ - FileEvent(uri: DocumentURI(compilationDatabaseUrl), type: .changed) - ])) + ws.sk.send( + DidChangeWatchedFilesNotification(changes: [ + FileEvent(uri: DocumentURI(compilationDatabaseUrl), type: .changed) + ]) + ) // DocumentHighlight should now point to the definition in the `#else` block. let expectedPostEditHighlight = [ DocumentHighlight(range: Position(line: 5, utf16index: 5).. [ColorInformation] { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 12, - text: text))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 12, + text: text + ) + ) + ) let request = DocumentColorRequest(textDocument: TextDocumentIdentifier(url)) return try sk.sendSync(request) } - func performColorPresentationRequest(text: String, color: Color, range: Range) throws -> [ColorPresentation] { + func performColorPresentationRequest(text: String, color: Color, range: Range) throws -> [ColorPresentation] + { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 12, - text: text))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 12, + text: text + ) + ) + ) let request = ColorPresentationRequest( - textDocument: TextDocumentIdentifier(url), - color: color, - range: range) + textDocument: TextDocumentIdentifier(url), + color: color, + range: range + ) return try sk.sendSync(request) } @@ -80,57 +95,66 @@ final class DocumentColorTests: XCTestCase { func testSimple() throws { try initialize() let text = #""" - #colorLiteral(red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4) - """# + #colorLiteral(red: 0.1, green: 0.2, blue: 0.3, alpha: 0.4) + """# let colors = try performDocumentColorRequest(text: text) - XCTAssertEqual(colors, [ - ColorInformation( - range: Position(line: 0, utf16index: 0).. DocumentSymbolResponse { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 17, - text: text - ))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 17, + text: text + ) + ) + ) let request = DocumentSymbolRequest(textDocument: TextDocumentIdentifier(url)) return try sk.sendSync(request)! @@ -79,21 +86,24 @@ final class DocumentSymbolTests: XCTestCase { try initialize(capabilities: capabilities) let text = """ - struct Foo { } - """ + struct Foo { } + """ let symbols = try performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, .documentSymbols([ - DocumentSymbol( - name: "Foo", - detail: nil, - kind: .struct, - deprecated: nil, - range: range(from: (0, 0), to: (0, 14)), - selectionRange: range(from: (0, 7), to: (0, 10)), - children: [] - ), - ])) + XCTAssertEqual( + symbols, + .documentSymbols([ + DocumentSymbol( + name: "Foo", + detail: nil, + kind: .struct, + deprecated: nil, + range: range(from: (0, 0), to: (0, 14)), + selectionRange: range(from: (0, 7), to: (0, 10)), + children: [] + ) + ]) + ) } func testUnicode() throws { @@ -101,9 +111,9 @@ final class DocumentSymbolTests: XCTestCase { try initialize(capabilities: capabilities) let text = """ - struct Żółć { } - struct 🍰 { } - """ + struct Żółć { } + struct 🍰 { } + """ let symbols = try performDocumentSymbolRequest(text: text) // while not ascii, these are still single code unit @@ -112,26 +122,29 @@ final class DocumentSymbolTests: XCTestCase { // but cake is two utf-16 code units let cakeRange = range(from: (1, 0), to: (1, 13)) let cakeSelectionRange = range(from: (1, 7), to: (1, 9)) - XCTAssertEqual(symbols, .documentSymbols([ - DocumentSymbol( - name: "Żółć", - detail: nil, - kind: .struct, - deprecated: nil, - range: żółćRange, - selectionRange: żółćSelectionRange, - children: [] - ), - DocumentSymbol( - name: "🍰", - detail: nil, - kind: .struct, - deprecated: nil, - range: cakeRange, - selectionRange: cakeSelectionRange, - children: [] - ), - ])) + XCTAssertEqual( + symbols, + .documentSymbols([ + DocumentSymbol( + name: "Żółć", + detail: nil, + kind: .struct, + deprecated: nil, + range: żółćRange, + selectionRange: żółćSelectionRange, + children: [] + ), + DocumentSymbol( + name: "🍰", + detail: nil, + kind: .struct, + deprecated: nil, + range: cakeRange, + selectionRange: cakeSelectionRange, + children: [] + ), + ]) + ) } func testEnum() throws { @@ -139,75 +152,77 @@ final class DocumentSymbolTests: XCTestCase { try initialize(capabilities: capabilities) let text = """ - enum Foo { - case first - case second, third - case fourth(Int), fifth - func notACase() { }; case sixth - case seventh, eight(Int, String) - enum SubEnum { - case a, b + enum Foo { + case first + case second, third + case fourth(Int), fifth + func notACase() { }; case sixth + case seventh, eight(Int, String) + enum SubEnum { + case a, b + } + case ninth(someName: Int) } - case ninth(someName: Int) - } - """ + """ let symbols = try performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, .documentSymbols([ - DocumentSymbol( - name: "Foo", - detail: nil, - kind: .enum, - deprecated: nil, - range: range(from: (0, 0), to: (10, 1)), - selectionRange: range(from: (0, 5), to: (0, 8)), - children: [ - DocumentSymbol( - name: "first", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (1, 7), to: (1, 12)), - selectionRange: range(from: (1, 7), to: (1, 12)), - children: [] - ), - DocumentSymbol( - name: "second", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (2, 7), to: (2, 13)), - selectionRange: range(from: (2, 7), to: (2, 13)), - children: [] - ), - DocumentSymbol( - name: "third", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (2, 15), to: (2, 20)), - selectionRange: range(from: (2, 15), to: (2, 20)), - children: [] - ), - DocumentSymbol( - name: "fourth(_:)", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (3, 7), to: (3, 18)), - selectionRange: range(from: (3, 7), to: (3, 18)), - children: [] - ), - DocumentSymbol( - name: "fifth", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (3, 20), to: (3, 25)), - selectionRange: range(from: (3, 20), to: (3, 25)), - children: [] - ), - DocumentSymbol( + XCTAssertEqual( + symbols, + .documentSymbols([ + DocumentSymbol( + name: "Foo", + detail: nil, + kind: .enum, + deprecated: nil, + range: range(from: (0, 0), to: (10, 1)), + selectionRange: range(from: (0, 5), to: (0, 8)), + children: [ + DocumentSymbol( + name: "first", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (1, 7), to: (1, 12)), + selectionRange: range(from: (1, 7), to: (1, 12)), + children: [] + ), + DocumentSymbol( + name: "second", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (2, 7), to: (2, 13)), + selectionRange: range(from: (2, 7), to: (2, 13)), + children: [] + ), + DocumentSymbol( + name: "third", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (2, 15), to: (2, 20)), + selectionRange: range(from: (2, 15), to: (2, 20)), + children: [] + ), + DocumentSymbol( + name: "fourth(_:)", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (3, 7), to: (3, 18)), + selectionRange: range(from: (3, 7), to: (3, 18)), + children: [] + ), + DocumentSymbol( + name: "fifth", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (3, 20), to: (3, 25)), + selectionRange: range(from: (3, 20), to: (3, 25)), + children: [] + ), + DocumentSymbol( name: "notACase()", detail: nil, kind: .method, @@ -215,8 +230,8 @@ final class DocumentSymbolTests: XCTestCase { range: range(from: (4, 2), to: (4, 21)), selectionRange: range(from: (4, 7), to: (4, 17)), children: [] - ), - DocumentSymbol( + ), + DocumentSymbol( name: "sixth", detail: nil, kind: .enumMember, @@ -224,65 +239,66 @@ final class DocumentSymbolTests: XCTestCase { range: range(from: (4, 28), to: (4, 33)), selectionRange: range(from: (4, 28), to: (4, 33)), children: [] - ), - DocumentSymbol( - name: "seventh", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (5, 7), to: (5, 14)), - selectionRange: range(from: (5, 7), to: (5, 14)), - children: [] - ), - DocumentSymbol( - name: "eight(_:_:)", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (5, 16), to: (5, 34)), - selectionRange: range(from: (5, 16), to: (5, 34)), - children: [] - ), - DocumentSymbol( - name: "SubEnum", - detail: nil, - kind: .enum, - deprecated: nil, - range: range(from: (6, 2), to: (8, 3)), - selectionRange: range(from: (6, 7), to: (6, 14)), - children: [ - DocumentSymbol( - name: "a", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (7, 9), to: (7, 10)), - selectionRange: range(from: (7, 9), to: (7, 10)), - children: [] - ), - DocumentSymbol( - name: "b", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (7, 12), to: (7, 13)), - selectionRange: range(from: (7, 12), to: (7, 13)), - children: [] - ) - ] - ), - DocumentSymbol( - name: "ninth(someName:)", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (9, 7), to: (9, 27)), - selectionRange: range(from: (9, 7), to: (9, 27)), - children: [] - ) - ] - ) - ])) + ), + DocumentSymbol( + name: "seventh", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (5, 7), to: (5, 14)), + selectionRange: range(from: (5, 7), to: (5, 14)), + children: [] + ), + DocumentSymbol( + name: "eight(_:_:)", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (5, 16), to: (5, 34)), + selectionRange: range(from: (5, 16), to: (5, 34)), + children: [] + ), + DocumentSymbol( + name: "SubEnum", + detail: nil, + kind: .enum, + deprecated: nil, + range: range(from: (6, 2), to: (8, 3)), + selectionRange: range(from: (6, 7), to: (6, 14)), + children: [ + DocumentSymbol( + name: "a", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (7, 9), to: (7, 10)), + selectionRange: range(from: (7, 9), to: (7, 10)), + children: [] + ), + DocumentSymbol( + name: "b", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (7, 12), to: (7, 13)), + selectionRange: range(from: (7, 12), to: (7, 13)), + children: [] + ), + ] + ), + DocumentSymbol( + name: "ninth(someName:)", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (9, 7), to: (9, 27)), + selectionRange: range(from: (9, 7), to: (9, 27)), + children: [] + ), + ] + ) + ]) + ) } func testAll() throws { @@ -290,356 +306,359 @@ final class DocumentSymbolTests: XCTestCase { try initialize(capabilities: capabilities) let text = """ - // struct ThisIsCommentedOut { } - /* struct ThisOneToo { } */ - extension Int { } - struct Struct { } - class Class { } - enum Enum { case enumMember } - protocol Interface { func f() } - func function() { } - var variable = 0 - let constant = 0 - var computedVariable: Int { return 0 } - func +(lhs: Struct, rhs: Struct) { } - prefix func -(rhs: Struct) { } - func f(type: TypeParameter.Type) { } - struct S { } - class Foo { - func method() { } - static func staticMethod() { } - class func classMethod() { } - var property = 0 - let constantProperty = 0 - var computedProperty: Int { return 0 } - init() { } - static var staticProperty = 0 - static let staticConstantProperty = 0 - static var staticComputedProperty: Int { return 0 } - class var classProperty: Int { return 0 } - func f() { - let localConstant = 0 - var localVariable = 0 - var localComputedVariable: Int { return 0 } - func localFunction() { } + // struct ThisIsCommentedOut { } + /* struct ThisOneToo { } */ + extension Int { } + struct Struct { } + class Class { } + enum Enum { case enumMember } + protocol Interface { func f() } + func function() { } + var variable = 0 + let constant = 0 + var computedVariable: Int { return 0 } + func +(lhs: Struct, rhs: Struct) { } + prefix func -(rhs: Struct) { } + func f(type: TypeParameter.Type) { } + struct S { } + class Foo { + func method() { } + static func staticMethod() { } + class func classMethod() { } + var property = 0 + let constantProperty = 0 + var computedProperty: Int { return 0 } + init() { } + static var staticProperty = 0 + static let staticConstantProperty = 0 + static var staticComputedProperty: Int { return 0 } + class var classProperty: Int { return 0 } + func f() { + let localConstant = 0 + var localVariable = 0 + var localComputedVariable: Int { return 0 } + func localFunction() { } + } } - } - """ + """ let symbols = try performDocumentSymbolRequest(text: text) - XCTAssertEqual(symbols, .documentSymbols([ - DocumentSymbol( - name: "Int", - detail: nil, - kind: .namespace, - deprecated: nil, - range: range(from: (2, 0), to: (2, 17)), - selectionRange: range(from: (2, 10), to: (2, 13)), - children: [] - ), - DocumentSymbol( - name: "Struct", - detail: nil, - kind: .struct, - deprecated: nil, - range: range(from: (3, 0), to: (3, 17)), - selectionRange: range(from: (3, 7), to: (3, 13)), - children: [] - ), - DocumentSymbol( - name: "Class", - detail: nil, - kind: .class, - deprecated: nil, - range: range(from: (4, 0), to: (4, 15)), - selectionRange: range(from: (4, 6), to: (4, 11)), - children: [] - ), - DocumentSymbol( - name: "Enum", - detail: nil, - kind: .enum, - deprecated: nil, - range: range(from: (5, 0), to: (5, 29)), - selectionRange: range(from: (5, 5), to: (5, 9)), - children: [ - DocumentSymbol( - name: "enumMember", - detail: nil, - kind: .enumMember, - deprecated: nil, - range: range(from: (5, 17), to: (5, 27)), - selectionRange: range(from: (5, 17), to: (5, 27)), - children: [] - ) - ] - ), - DocumentSymbol( - name: "Interface", - detail: nil, - kind: .interface, - deprecated: nil, - range: range(from: (6, 0), to: (6, 31)), - selectionRange: range(from: (6, 9), to: (6, 18)), - children: [ - DocumentSymbol( - name: "f()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (6, 21), to: (6, 29)), - selectionRange: range(from: (6, 26), to: (6, 29)), - children: [] - ) - ] - ), - DocumentSymbol( - name: "function()", - detail: nil, - kind: .function, - deprecated: nil, - range: range(from: (7, 0), to: (7, 19)), - selectionRange: range(from: (7, 5), to: (7, 15)), - children: [] - ), - DocumentSymbol( - name: "variable", - detail: nil, - kind: .variable, - deprecated: nil, - range: range(from: (8, 0), to: (8, 16)), - selectionRange: range(from: (8, 4), to: (8, 12)), - children: [] - ), - DocumentSymbol( - name: "constant", - detail: nil, - kind: .variable, - deprecated: nil, - range: range(from: (9, 0), to: (9, 16)), - selectionRange: range(from: (9, 4), to: (9, 12)), - children: [] - ), - DocumentSymbol( - name: "computedVariable", - detail: "Int", - kind: .variable, - deprecated: nil, - range: range(from: (10, 0), to: (10, 38)), - selectionRange: range(from: (10, 4), to: (10, 20)), - children: [] - ), - DocumentSymbol( - name: "+(_:_:)", - detail: nil, - kind: .function, - deprecated: nil, - range: range(from: (11, 0), to: (11, 36)), - selectionRange: range(from: (11, 5), to: (11, 32)), - children: [] - ), - DocumentSymbol( - name: "-(_:)", - detail: nil, - kind: .function, - deprecated: nil, - range: range(from: (12, 7), to: (12, 30)), - selectionRange: range(from: (12, 12), to: (12, 26)), - children: [] - ), - DocumentSymbol( - name: "f(type:)", - detail: nil, - kind: .function, - deprecated: nil, - range: range(from: (13, 0), to: (13, 51)), - selectionRange: range(from: (13, 5), to: (13, 47)), - children: [ - DocumentSymbol( - name: "TypeParameter", - detail: nil, - kind: .typeParameter, - deprecated: nil, - range: range(from: (13, 7), to: (13, 20)), - selectionRange: range(from: (13, 7), to: (13, 20)), - children: [] - ) - ] - ), - DocumentSymbol( - name: "S", - detail: nil, - kind: .struct, - deprecated: nil, - range: range(from: (14, 0), to: (14, 27)), - selectionRange: range(from: (14, 7), to: (14, 8)), - children: [ - DocumentSymbol( - name: "TypeParameter", - detail: nil, - kind: .typeParameter, - deprecated: nil, - range: range(from: (14, 9), to: (14, 22)), - selectionRange: range(from: (14, 9), to: (14, 22)), - children: [] - ) - ] - ), - DocumentSymbol( - name: "Foo", - detail: nil, - kind: .class, - deprecated: nil, - range: range(from: (15, 0), to: (33, 1)), - selectionRange: range(from: (15, 6), to: (15, 9)), - children: [ - DocumentSymbol( - name: "method()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (16, 2), to: (16, 19)), - selectionRange: range(from: (16, 7), to: (16, 15)), - children: [] - ), - DocumentSymbol( - name: "staticMethod()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (17, 2), to: (17, 32)), - selectionRange: range(from: (17, 14), to: (17, 28)), - children: [] - ), - DocumentSymbol( - name: "classMethod()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (18, 2), to: (18, 30)), - selectionRange: range(from: (18, 13), to: (18, 26)), - children: [] - ), - DocumentSymbol( - name: "property", - detail: nil, - kind: .property, - deprecated: nil, - range: range(from: (19, 2), to: (19, 18)), - selectionRange: range(from: (19, 6), to: (19, 14)), - children: [] - ), - DocumentSymbol( - name: "constantProperty", - detail: nil, - kind: .property, - deprecated: nil, - range: range(from: (20, 2), to: (20, 26)), - selectionRange: range(from: (20, 6), to: (20, 22)), - children: [] - ), - DocumentSymbol( - name: "computedProperty", - detail: "Int", - kind: .property, - deprecated: nil, - range: range(from: (21, 2), to: (21, 40)), - selectionRange: range(from: (21, 6), to: (21, 22)), - children: [] - ), - DocumentSymbol( - name: "init()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (22, 2), to: (22, 12)), - selectionRange: range(from: (22, 2), to: (22, 8)), - children: [] - ), - DocumentSymbol( - name: "staticProperty", - detail: nil, - kind: .property, - deprecated: nil, - range: range(from: (23, 2), to: (23, 31)), - selectionRange: range(from: (23, 13), to: (23, 27)), - children: [] - ), - DocumentSymbol( - name: "staticConstantProperty", - detail: nil, - kind: .property, - deprecated: nil, - range: range(from: (24, 2), to: (24, 39)), - selectionRange: range(from: (24, 13), to: (24, 35)), - children: [] - ), - DocumentSymbol( - name: "staticComputedProperty", - detail: "Int", - kind: .property, - deprecated: nil, - range: range(from: (25, 2), to: (25, 53)), - selectionRange: range(from: (25, 13), to: (25, 35)), - children: [] - ), - DocumentSymbol( - name: "classProperty", - detail: "Int", - kind: .property, - deprecated: nil, - range: range(from: (26, 2), to: (26, 43)), - selectionRange: range(from: (26, 12), to: (26, 25)), - children: [] - ), - DocumentSymbol( - name: "f()", - detail: nil, - kind: .method, - deprecated: nil, - range: range(from: (27, 2), to: (32, 3)), - selectionRange: range(from: (27, 7), to: (27, 10)), - children: [ - DocumentSymbol( - name: "localConstant", - detail: nil, - kind: .variable, - deprecated: nil, - range: range(from: (28, 4), to: (28, 25)), - selectionRange: range(from: (28, 8), to: (28, 21)), - children: [] - ), - DocumentSymbol( - name: "localVariable", - detail: nil, - kind: .variable, - deprecated: nil, - range: range(from: (29, 4), to: (29, 25)), - selectionRange: range(from: (29, 8), to: (29, 21)), - children: [] - ), - DocumentSymbol( - name: "localComputedVariable", - detail: "Int", - kind: .variable, - deprecated: nil, - range: range(from: (30, 4), to: (30, 47)), - selectionRange: range(from: (30, 8), to: (30, 29)), - children: [] - ), - DocumentSymbol( - name: "localFunction()", - detail: nil, - kind: .function, - deprecated: nil, - range: range(from: (31, 4), to: (31, 28)), - selectionRange: range(from: (31, 9), to: (31, 24)), - children: [] - ) - ] - ) - ] - ) - ])) + XCTAssertEqual( + symbols, + .documentSymbols([ + DocumentSymbol( + name: "Int", + detail: nil, + kind: .namespace, + deprecated: nil, + range: range(from: (2, 0), to: (2, 17)), + selectionRange: range(from: (2, 10), to: (2, 13)), + children: [] + ), + DocumentSymbol( + name: "Struct", + detail: nil, + kind: .struct, + deprecated: nil, + range: range(from: (3, 0), to: (3, 17)), + selectionRange: range(from: (3, 7), to: (3, 13)), + children: [] + ), + DocumentSymbol( + name: "Class", + detail: nil, + kind: .class, + deprecated: nil, + range: range(from: (4, 0), to: (4, 15)), + selectionRange: range(from: (4, 6), to: (4, 11)), + children: [] + ), + DocumentSymbol( + name: "Enum", + detail: nil, + kind: .enum, + deprecated: nil, + range: range(from: (5, 0), to: (5, 29)), + selectionRange: range(from: (5, 5), to: (5, 9)), + children: [ + DocumentSymbol( + name: "enumMember", + detail: nil, + kind: .enumMember, + deprecated: nil, + range: range(from: (5, 17), to: (5, 27)), + selectionRange: range(from: (5, 17), to: (5, 27)), + children: [] + ) + ] + ), + DocumentSymbol( + name: "Interface", + detail: nil, + kind: .interface, + deprecated: nil, + range: range(from: (6, 0), to: (6, 31)), + selectionRange: range(from: (6, 9), to: (6, 18)), + children: [ + DocumentSymbol( + name: "f()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (6, 21), to: (6, 29)), + selectionRange: range(from: (6, 26), to: (6, 29)), + children: [] + ) + ] + ), + DocumentSymbol( + name: "function()", + detail: nil, + kind: .function, + deprecated: nil, + range: range(from: (7, 0), to: (7, 19)), + selectionRange: range(from: (7, 5), to: (7, 15)), + children: [] + ), + DocumentSymbol( + name: "variable", + detail: nil, + kind: .variable, + deprecated: nil, + range: range(from: (8, 0), to: (8, 16)), + selectionRange: range(from: (8, 4), to: (8, 12)), + children: [] + ), + DocumentSymbol( + name: "constant", + detail: nil, + kind: .variable, + deprecated: nil, + range: range(from: (9, 0), to: (9, 16)), + selectionRange: range(from: (9, 4), to: (9, 12)), + children: [] + ), + DocumentSymbol( + name: "computedVariable", + detail: "Int", + kind: .variable, + deprecated: nil, + range: range(from: (10, 0), to: (10, 38)), + selectionRange: range(from: (10, 4), to: (10, 20)), + children: [] + ), + DocumentSymbol( + name: "+(_:_:)", + detail: nil, + kind: .function, + deprecated: nil, + range: range(from: (11, 0), to: (11, 36)), + selectionRange: range(from: (11, 5), to: (11, 32)), + children: [] + ), + DocumentSymbol( + name: "-(_:)", + detail: nil, + kind: .function, + deprecated: nil, + range: range(from: (12, 7), to: (12, 30)), + selectionRange: range(from: (12, 12), to: (12, 26)), + children: [] + ), + DocumentSymbol( + name: "f(type:)", + detail: nil, + kind: .function, + deprecated: nil, + range: range(from: (13, 0), to: (13, 51)), + selectionRange: range(from: (13, 5), to: (13, 47)), + children: [ + DocumentSymbol( + name: "TypeParameter", + detail: nil, + kind: .typeParameter, + deprecated: nil, + range: range(from: (13, 7), to: (13, 20)), + selectionRange: range(from: (13, 7), to: (13, 20)), + children: [] + ) + ] + ), + DocumentSymbol( + name: "S", + detail: nil, + kind: .struct, + deprecated: nil, + range: range(from: (14, 0), to: (14, 27)), + selectionRange: range(from: (14, 7), to: (14, 8)), + children: [ + DocumentSymbol( + name: "TypeParameter", + detail: nil, + kind: .typeParameter, + deprecated: nil, + range: range(from: (14, 9), to: (14, 22)), + selectionRange: range(from: (14, 9), to: (14, 22)), + children: [] + ) + ] + ), + DocumentSymbol( + name: "Foo", + detail: nil, + kind: .class, + deprecated: nil, + range: range(from: (15, 0), to: (33, 1)), + selectionRange: range(from: (15, 6), to: (15, 9)), + children: [ + DocumentSymbol( + name: "method()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (16, 2), to: (16, 19)), + selectionRange: range(from: (16, 7), to: (16, 15)), + children: [] + ), + DocumentSymbol( + name: "staticMethod()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (17, 2), to: (17, 32)), + selectionRange: range(from: (17, 14), to: (17, 28)), + children: [] + ), + DocumentSymbol( + name: "classMethod()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (18, 2), to: (18, 30)), + selectionRange: range(from: (18, 13), to: (18, 26)), + children: [] + ), + DocumentSymbol( + name: "property", + detail: nil, + kind: .property, + deprecated: nil, + range: range(from: (19, 2), to: (19, 18)), + selectionRange: range(from: (19, 6), to: (19, 14)), + children: [] + ), + DocumentSymbol( + name: "constantProperty", + detail: nil, + kind: .property, + deprecated: nil, + range: range(from: (20, 2), to: (20, 26)), + selectionRange: range(from: (20, 6), to: (20, 22)), + children: [] + ), + DocumentSymbol( + name: "computedProperty", + detail: "Int", + kind: .property, + deprecated: nil, + range: range(from: (21, 2), to: (21, 40)), + selectionRange: range(from: (21, 6), to: (21, 22)), + children: [] + ), + DocumentSymbol( + name: "init()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (22, 2), to: (22, 12)), + selectionRange: range(from: (22, 2), to: (22, 8)), + children: [] + ), + DocumentSymbol( + name: "staticProperty", + detail: nil, + kind: .property, + deprecated: nil, + range: range(from: (23, 2), to: (23, 31)), + selectionRange: range(from: (23, 13), to: (23, 27)), + children: [] + ), + DocumentSymbol( + name: "staticConstantProperty", + detail: nil, + kind: .property, + deprecated: nil, + range: range(from: (24, 2), to: (24, 39)), + selectionRange: range(from: (24, 13), to: (24, 35)), + children: [] + ), + DocumentSymbol( + name: "staticComputedProperty", + detail: "Int", + kind: .property, + deprecated: nil, + range: range(from: (25, 2), to: (25, 53)), + selectionRange: range(from: (25, 13), to: (25, 35)), + children: [] + ), + DocumentSymbol( + name: "classProperty", + detail: "Int", + kind: .property, + deprecated: nil, + range: range(from: (26, 2), to: (26, 43)), + selectionRange: range(from: (26, 12), to: (26, 25)), + children: [] + ), + DocumentSymbol( + name: "f()", + detail: nil, + kind: .method, + deprecated: nil, + range: range(from: (27, 2), to: (32, 3)), + selectionRange: range(from: (27, 7), to: (27, 10)), + children: [ + DocumentSymbol( + name: "localConstant", + detail: nil, + kind: .variable, + deprecated: nil, + range: range(from: (28, 4), to: (28, 25)), + selectionRange: range(from: (28, 8), to: (28, 21)), + children: [] + ), + DocumentSymbol( + name: "localVariable", + detail: nil, + kind: .variable, + deprecated: nil, + range: range(from: (29, 4), to: (29, 25)), + selectionRange: range(from: (29, 8), to: (29, 21)), + children: [] + ), + DocumentSymbol( + name: "localComputedVariable", + detail: "Int", + kind: .variable, + deprecated: nil, + range: range(from: (30, 4), to: (30, 47)), + selectionRange: range(from: (30, 8), to: (30, 29)), + children: [] + ), + DocumentSymbol( + name: "localFunction()", + detail: nil, + kind: .function, + deprecated: nil, + range: range(from: (31, 4), to: (31, 28)), + selectionRange: range(from: (31, 9), to: (31, 24)), + children: [] + ), + ] + ), + ] + ), + ]) + ) } } diff --git a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift index 89a85514c..aadbe61a9 100644 --- a/Tests/SourceKitLSPTests/ExecuteCommandTests.swift +++ b/Tests/SourceKitLSPTests/ExecuteCommandTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKTestSupport import SourceKitLSP import XCTest @@ -32,14 +32,17 @@ final class ExecuteCommandTests: XCTestCase { override func setUp() { connection = TestSourceKitServer() sk = connection.client - _ = try! sk.sendSync(InitializeRequest( - processId: nil, - rootPath: nil, - rootURI: nil, - initializationOptions: nil, - capabilities: ClientCapabilities(workspace: nil, textDocument: nil), - trace: .off, - workspaceFolders: nil)) + _ = try! sk.sendSync( + InitializeRequest( + processId: nil, + rootPath: nil, + rootURI: nil, + initializationOptions: nil, + capabilities: ClientCapabilities(workspace: nil, textDocument: nil), + trace: .off, + workspaceFolders: nil + ) + ) } func testLocationSemanticRefactoring() async throws { @@ -49,10 +52,12 @@ final class ExecuteCommandTests: XCTestCase { let textDocument = TextDocumentIdentifier(loc.url) - let args = SemanticRefactorCommand(title: "Localize String", - actionString: "source.refactoring.kind.localize.string", - positionRange: loc.position.. 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, DocumentURI)? { + func initializeWorkspace(withCapabilities capabilities: FoldingRangeCapabilities, testLoc: String) async throws -> ( + SKTibsTestWorkspace, DocumentURI + )? { var documentCapabilities = TextDocumentClientCapabilities() documentCapabilities.foldingRange = capabilities let capabilities = ClientCapabilities(workspace: nil, textDocument: documentCapabilities) - guard let ws = try await staticSourceKitTibsWorkspace(name: "FoldingRange", - clientCapabilities: capabilities) else { return nil } + guard + let ws = try await staticSourceKitTibsWorkspace( + name: "FoldingRange", + clientCapabilities: capabilities + ) + else { return nil } let loc = ws.testLoc(testLoc) try ws.openDocument(loc.url, language: .swift) return (ws, DocumentURI(loc.url)) @@ -33,7 +39,9 @@ final class FoldingRangeTests: XCTestCase { var capabilities = FoldingRangeCapabilities() capabilities.lineFoldingOnly = false - guard let (ws, uri) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } + guard let (ws, uri) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { + return + } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(uri)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } @@ -60,7 +68,9 @@ final class FoldingRangeTests: XCTestCase { var capabilities = FoldingRangeCapabilities() capabilities.lineFoldingOnly = true - guard let (ws, uri) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } + guard let (ws, uri) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { + return + } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(uri)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } @@ -85,7 +95,9 @@ final class FoldingRangeTests: XCTestCase { var capabilities = FoldingRangeCapabilities() capabilities.lineFoldingOnly = false capabilities.rangeLimit = limit - guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { return } + guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:base") else { + return + } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } XCTAssertEqual(ranges?.count, expectedRanges, "Failed rangeLimit test at line \(line)") @@ -101,7 +113,9 @@ final class FoldingRangeTests: XCTestCase { func testNoRanges() async throws { let capabilities = FoldingRangeCapabilities() - guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:empty") else { return } + guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:empty") else { + return + } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } @@ -114,7 +128,12 @@ final class FoldingRangeTests: XCTestCase { // Test that we only report the folding range once. let capabilities = FoldingRangeCapabilities() - guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:multilineDocLineComment") else { return } + guard + let (ws, url) = try await initializeWorkspace( + withCapabilities: capabilities, + testLoc: "fr:multilineDocLineComment" + ) + else { return } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } @@ -125,7 +144,7 @@ final class FoldingRangeTests: XCTestCase { FoldingRange(startLine: 7, startUTF16Index: 0, endLine: 8, endUTF16Index: 21, kind: .comment), FoldingRange(startLine: 10, startUTF16Index: 0, endLine: 10, endUTF16Index: 44, kind: .comment), FoldingRange(startLine: 11, startUTF16Index: 12, endLine: 11, endUTF16Index: 12), - FoldingRange(startLine: 13, startUTF16Index: 0, endLine: 13, endUTF16Index: 30, kind: .comment) + FoldingRange(startLine: 13, startUTF16Index: 0, endLine: 13, endUTF16Index: 30, kind: .comment), ] XCTAssertEqual(ranges, expected) @@ -136,7 +155,8 @@ final class FoldingRangeTests: XCTestCase { // Test that we only report the folding range once. let capabilities = FoldingRangeCapabilities() - guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:duplicateRanges") else { return } + guard let (ws, url) = try await initializeWorkspace(withCapabilities: capabilities, testLoc: "fr:duplicateRanges") + else { return } let request = FoldingRangeRequest(textDocument: TextDocumentIdentifier(url)) let ranges = try withExtendedLifetime(ws) { try ws.sk.sendSync(request) } diff --git a/Tests/SourceKitLSPTests/ImplementationTests.swift b/Tests/SourceKitLSPTests/ImplementationTests.swift index 060540a18..13933d121 100644 --- a/Tests/SourceKitLSPTests/ImplementationTests.swift +++ b/Tests/SourceKitLSPTests/ImplementationTests.swift @@ -35,12 +35,19 @@ final class ImplementationTests: XCTestCase { } func testLoc(_ name: String) -> TestLocation { ws.testLoc(name) - } + } func loc(_ name: String) throws -> Location { let location: TestLocation = ws.testLoc(name) - return Location(badUTF16: TestLocation(url: try location.docUri.nativeURI.fileURL!, line: location.line, utf8Column: location.utf8Column, utf16Column: location.utf16Column)) + return Location( + badUTF16: TestLocation( + url: try location.docUri.nativeURI.fileURL!, + line: location.line, + utf8Column: location.utf8Column, + utf16Column: location.utf16Column + ) + ) } - + try XCTAssertEqual(impls(at: testLoc("Protocol")), [loc("StructConformance")]) try XCTAssertEqual(impls(at: testLoc("ProtocolStaticVar")), [loc("StructStaticVar")]) try XCTAssertEqual(impls(at: testLoc("ProtocolStaticFunction")), [loc("StructStaticFunction")]) @@ -52,18 +59,33 @@ final class ImplementationTests: XCTestCase { try XCTAssertEqual(impls(at: testLoc("ClassVariable")), [loc("SubclassVariable")]) try XCTAssertEqual(impls(at: testLoc("ClassFunction")), [loc("SubclassFunction")]) - try XCTAssertEqual(impls(at: testLoc("Sepulcidae")), [loc("ParapamphiliinaeConformance"), loc("XyelulinaeConformance"), loc("TrematothoracinaeConformance")]) - try XCTAssertEqual(impls(at: testLoc("Parapamphiliinae")), [loc("MicramphiliusConformance"), loc("PamparaphiliusConformance")]) + try XCTAssertEqual( + impls(at: testLoc("Sepulcidae")), + [loc("ParapamphiliinaeConformance"), loc("XyelulinaeConformance"), loc("TrematothoracinaeConformance")] + ) + try XCTAssertEqual( + impls(at: testLoc("Parapamphiliinae")), + [loc("MicramphiliusConformance"), loc("PamparaphiliusConformance")] + ) try XCTAssertEqual(impls(at: testLoc("Xyelulinae")), [loc("XyelulaConformance")]) try XCTAssertEqual(impls(at: testLoc("Trematothoracinae")), []) try XCTAssertEqual(impls(at: testLoc("Prozaiczne")), [loc("MurkwiaConformance2"), loc("SepulkaConformance1")]) - try XCTAssertEqual(impls(at: testLoc("Sepulkowate")), [loc("MurkwiaConformance1"), loc("SepulkaConformance2"), loc("PćmaŁagodnaConformance"), loc("PćmaZwyczajnaConformance")]) + try XCTAssertEqual( + impls(at: testLoc("Sepulkowate")), + [ + loc("MurkwiaConformance1"), loc("SepulkaConformance2"), loc("PćmaŁagodnaConformance"), + loc("PćmaZwyczajnaConformance"), + ] + ) // FIXME: sourcekit returns wrong locations for the function (subclasses that don't override it, and extensions that don't implement it) // try XCTAssertEqual(impls(at: testLoc("rozpocznijSepulenie")), [loc("MurkwiaFunc"), loc("SepulkaFunc"), loc("PćmaŁagodnaFunc"), loc("PćmaZwyczajnaFunc")]) try XCTAssertEqual(impls(at: testLoc("Murkwia")), []) try XCTAssertEqual(impls(at: testLoc("MurkwiaFunc")), []) - try XCTAssertEqual(impls(at: testLoc("Sepulka")), [loc("SepulkaDwuusznaConformance"), loc("SepulkaPrzechylnaConformance")]) + try XCTAssertEqual( + impls(at: testLoc("Sepulka")), + [loc("SepulkaDwuusznaConformance"), loc("SepulkaPrzechylnaConformance")] + ) try XCTAssertEqual(impls(at: testLoc("SepulkaVar")), [loc("SepulkaDwuusznaVar"), loc("SepulkaPrzechylnaVar")]) try XCTAssertEqual(impls(at: testLoc("SepulkaFunc")), []) } diff --git a/Tests/SourceKitLSPTests/InlayHintTests.swift b/Tests/SourceKitLSPTests/InlayHintTests.swift index 969bf994f..fbb001833 100644 --- a/Tests/SourceKitLSPTests/InlayHintTests.swift +++ b/Tests/SourceKitLSPTests/InlayHintTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKTestSupport import SourceKitLSP import XCTest @@ -31,26 +31,32 @@ final class InlayHintTests: XCTestCase { override func setUp() { connection = TestSourceKitServer() sk = connection.client - _ = try! sk.sendSync(InitializeRequest( - processId: nil, - rootPath: nil, - rootURI: nil, - initializationOptions: nil, - capabilities: ClientCapabilities(workspace: nil, textDocument: nil), - trace: .off, - workspaceFolders: nil - )) + _ = try! sk.sendSync( + InitializeRequest( + processId: nil, + rootPath: nil, + rootURI: nil, + initializationOptions: nil, + capabilities: ClientCapabilities(workspace: nil, textDocument: nil), + trace: .off, + workspaceFolders: nil + ) + ) } func performInlayHintRequest(text: String, range: Range? = nil) throws -> [InlayHint] { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 17, - text: text - ))) + + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 17, + text: text + ) + ) + ) let request = InlayHintRequest(textDocument: TextDocumentIdentifier(url), range: range) @@ -61,7 +67,8 @@ final class InlayHintTests: XCTestCase { } } - private func makeInlayHint(position: Position, kind: InlayHintKind, label: String, hasEdit: Bool = true) -> InlayHint { + private func makeInlayHint(position: Position, kind: InlayHintKind, label: String, hasEdit: Bool = true) -> InlayHint + { let textEdits: [TextEdit]? if hasEdit { textEdits = [TextEdit(range: position.. Double { - let result = x * x - return result - } - - func collatz(_ n: Int) -> Int { - let even = n % 2 == 0 - let result = even ? (n / 2) : (3 * n + 1) - return result - } - """ + func square(_ x: Double) -> Double { + let result = x * x + return result + } + + func collatz(_ n: Int) -> Int { + let even = n % 2 == 0 + let result = even ? (n / 2) : (3 * n + 1) + return result + } + """ let range = Position(line: 6, utf16index: 0).. String = { x in x } - let i: (Double, Double) -> Double = { (x, y) in - x + y - } - """ + func f(x: Int) {} + + let g = { (x: Int) in } + let h: (String) -> String = { x in x } + let i: (Double, Double) -> Double = { (x, y) in + x + y + } + """ let hints = try performInlayHintRequest(text: text) - XCTAssertEqual(hints, [ - makeInlayHint( - position: Position(line: 2, utf16index: 5), - kind: .type, - label: ": (Int) -> ()" - ), - makeInlayHint( - position: Position(line: 3, utf16index: 31), - kind: .type, - label: ": String", - hasEdit: false - ), - makeInlayHint( - position: Position(line: 4, utf16index: 40), - kind: .type, - label: ": Double" - ), - makeInlayHint( - position: Position(line: 4, utf16index: 43), - kind: .type, - label: ": Double" - ) - ]) + XCTAssertEqual( + hints, + [ + makeInlayHint( + position: Position(line: 2, utf16index: 5), + kind: .type, + label: ": (Int) -> ()" + ), + makeInlayHint( + position: Position(line: 3, utf16index: 31), + kind: .type, + label: ": String", + hasEdit: false + ), + makeInlayHint( + position: Position(line: 4, utf16index: 40), + kind: .type, + label: ": Double" + ), + makeInlayHint( + position: Position(line: 4, utf16index: 43), + kind: .type, + label: ": Double" + ), + ] + ) } } diff --git a/Tests/SourceKitLSPTests/LocalClangTests.swift b/Tests/SourceKitLSPTests/LocalClangTests.swift index 7e860ac93..c6ab9f9ab 100644 --- a/Tests/SourceKitLSPTests/LocalClangTests.swift +++ b/Tests/SourceKitLSPTests/LocalClangTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKCore import SKTestSupport import XCTest @@ -19,7 +19,7 @@ import XCTest final class LocalClangTests: XCTestCase { /// Whether to fail tests if clangd cannot be found. - static let requireClangd: Bool = false // Note: Swift CI doesn't build clangd on all jobs + static let requireClangd: Bool = false // Note: Swift CI doesn't build clangd on all jobs /// Whether clangd exists in the toolchain. var haveClangd: Bool = false @@ -44,14 +44,17 @@ final class LocalClangTests: XCTestCase { hierarchicalDocumentSymbolSupport: true ) let textDocument = TextDocumentClientCapabilities(documentSymbol: documentSymbol) - _ = try! sk.sendSync(InitializeRequest( + _ = try! sk.sendSync( + InitializeRequest( processId: nil, rootPath: nil, rootURI: nil, initializationOptions: nil, capabilities: ClientCapabilities(workspace: nil, textDocument: textDocument), trace: .off, - workspaceFolders: nil)) + workspaceFolders: nil + ) + ) } override func tearDown() { @@ -63,28 +66,36 @@ final class LocalClangTests: XCTestCase { func testSymbolInfo() throws { guard haveClangd else { return } -#if os(Windows) + #if os(Windows) let url = URL(fileURLWithPath: "C:/a.cpp") -#else + #else let url = URL(fileURLWithPath: "/a.cpp") -#endif - - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .cpp, - version: 1, - text: """ - struct S { - void foo() { - int local = 1; - } - }; - """))) + #endif + + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .cpp, + version: 1, + text: """ + struct S { + void foo() { + int local = 1; + } + }; + """ + ) + ) + ) do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 0, utf16index: 7))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 0, utf16index: 7) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -95,9 +106,12 @@ final class LocalClangTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 1, utf16index: 7))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 1, utf16index: 7) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -108,9 +122,12 @@ final class LocalClangTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 2, utf16index: 8))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 2, utf16index: 8) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -121,9 +138,12 @@ final class LocalClangTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 0))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 0) + ) + ) XCTAssertEqual(resp.count, 0) } @@ -131,52 +151,65 @@ final class LocalClangTests: XCTestCase { func testFoldingRange() throws { guard haveClangd else { return } -#if os(Windows) + #if os(Windows) let url = URL(fileURLWithPath: "C:/a.cpp") -#else + #else let url = URL(fileURLWithPath: "/a.cpp") -#endif - - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .cpp, - version: 1, - text: """ - struct S { - void foo() { - int local = 1; - } - }; - """))) + #endif + + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .cpp, + version: 1, + text: """ + struct S { + void foo() { + int local = 1; + } + }; + """ + ) + ) + ) let resp = try sk.sendSync(FoldingRangeRequest(textDocument: TextDocumentIdentifier(url))) if let resp = resp { - XCTAssertEqual(resp, [ - FoldingRange(startLine: 0, startUTF16Index: 10, endLine: 4, kind: .region), - FoldingRange(startLine: 1, startUTF16Index: 14, endLine: 3, endUTF16Index: 2, kind: .region), - ]) + XCTAssertEqual( + resp, + [ + FoldingRange(startLine: 0, startUTF16Index: 10, endLine: 4, kind: .region), + FoldingRange(startLine: 1, startUTF16Index: 14, endLine: 3, endUTF16Index: 2, kind: .region), + ] + ) } } func testDocumentSymbols() throws { guard haveClangd else { return } -#if os(Windows) + #if os(Windows) let url = URL(fileURLWithPath: "C:/a.cpp") -#else + #else let url = URL(fileURLWithPath: "/a.cpp") -#endif - - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .cpp, - version: 1, - text: """ - struct S { - void foo() { - int local = 1; - } - }; - """))) + #endif + + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .cpp, + version: 1, + text: """ + struct S { + void foo() { + int local = 1; + } + }; + """ + ) + ) + ) guard let resp = try sk.sendSync(DocumentSymbolRequest(textDocument: TextDocumentIdentifier(url))) else { XCTFail("Invalid document symbol response") @@ -204,9 +237,10 @@ final class LocalClangTests: XCTestCase { let diagnostics = note.params.diagnostics // It seems we either get no diagnostics or a `-Wswitch` warning. Either is fine // as long as our code action works properly. - XCTAssert(diagnostics.isEmpty || - (diagnostics.count == 1 && diagnostics.first?.code == .string("-Wswitch")), - "Unexpected diagnostics \(diagnostics)") + XCTAssert( + diagnostics.isEmpty || (diagnostics.count == 1 && diagnostics.first?.code == .string("-Wswitch")), + "Unexpected diagnostics \(diagnostics)" + ) expectation.fulfill() } @@ -241,7 +275,9 @@ final class LocalClangTests: XCTestCase { } let executeCommand = ExecuteCommandRequest( - command: command.command, arguments: command.arguments) + command: command.command, + arguments: command.arguments + ) _ = try ws.sk.sendSync(executeCommand) try await fulfillmentOfOrThrow([applyEdit]) @@ -258,8 +294,10 @@ final class LocalClangTests: XCTestCase { ws.sk.handleNextNotification { (note: Notification) in // Don't use exact equality because of differences in recent clang. XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, - Position(loc) ..< Position(ws.testLoc("unused_b:end"))) + XCTAssertEqual( + note.params.diagnostics.first?.range, + Position(loc)..) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func", documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 4)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(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 - XCTAssertEqual(note.params.version, 13) - XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func foo() {}\n", documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 1 - semantic") - XCTAssertEqual(note.params.version, 13) - XCTAssertEqual(note.params.diagnostics.count, 0) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 14), contentChanges: [ - .init(range: Range(Position(line: 1, utf16index: 0)), text: "bar()") - ]), { (note: Notification) in + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 12, + text: """ + func + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func", documentManager.latestSnapshot(uri)!.text) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 4) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + 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 + XCTAssertEqual(note.params.version, 13) + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func foo() {}\n", documentManager.latestSnapshot(uri)!.text) + }, + { (note: Notification) in + log("Received diagnostics for edit 1 - semantic") + XCTAssertEqual(note.params.version, 13) + XCTAssertEqual(note.params.diagnostics.count, 0) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + 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") XCTAssertEqual(note.params.version, 14) // 1 = semantic update finished already // 0 = only syntactic XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - bar() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 2 - semantic") - XCTAssertEqual(note.params.version, 14) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 14), contentChanges: [ - .init(range: Position(line: 1, utf16index: 0)..) in + XCTAssertEqual( + """ + func foo() {} + bar() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 2 - semantic") + XCTAssertEqual(note.params.version, 14) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 14), + contentChanges: [ + .init(range: Position(line: 1, utf16index: 0)..) in log("Received diagnostics for edit 3 - syntactic") // 1 = remaining semantic error // 0 = semantic update finished already XCTAssertEqual(note.params.version, 14) XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - foo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 3 - semantic") - XCTAssertEqual(note.params.version, 14) - XCTAssertEqual(note.params.diagnostics.count, 0) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 15), contentChanges: [ - .init(range: Position(line: 1, utf16index: 0)..) in + XCTAssertEqual( + """ + func foo() {} + foo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 3 - semantic") + XCTAssertEqual(note.params.version, 14) + XCTAssertEqual(note.params.diagnostics.count, 0) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 15), + contentChanges: [ + .init(range: Position(line: 1, utf16index: 0)..) in log("Received diagnostics for edit 4 - syntactic") XCTAssertEqual(note.params.version, 15) // 1 = semantic update finished already // 0 = only syntactic XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - fooTypo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 4 - semantic") - XCTAssertEqual(note.params.version, 15) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 16), contentChanges: [ - .init(range: nil, text: """ - func bar() {} - foo() - """) - ]), { (note: Notification) in + XCTAssertEqual( + """ + func foo() {} + fooTypo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 4 - semantic") + XCTAssertEqual(note.params.version, 15) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 16), + contentChanges: [ + .init( + range: nil, + text: """ + func bar() {} + foo() + """ + ) + ] + ), + { (note: Notification) in log("Received diagnostics for edit 5 - syntactic") XCTAssertEqual(note.params.version, 16) // Could be remaining semantic error or new one. XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func bar() {} - foo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 5 - semantic") - XCTAssertEqual(note.params.version, 16) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) + XCTAssertEqual( + """ + func bar() {} + foo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 5 - semantic") + XCTAssertEqual(note.params.version, 16) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) } func testEditingNonURL() async { @@ -190,124 +256,184 @@ final class LocalSwiftTests: XCTestCase { let documentManager = await connection.server!._documentManager - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 12, - text: """ - func - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func", documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 4)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(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") - XCTAssertEqual(note.params.version, 13) - // 1 = remaining semantic error - // 0 = semantic update finished already - XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual("func foo() {}\n", documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 1 - semantic") - XCTAssertEqual(note.params.version, 13) - XCTAssertEqual(note.params.diagnostics.count, 0) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 14), contentChanges: [ - .init(range: Range(Position(line: 1, utf16index: 0)), text: "bar()") - ]), { (note: Notification) in + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 12, + text: """ + func + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func", documentManager.latestSnapshot(uri)!.text) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 4) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + 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") + XCTAssertEqual(note.params.version, 13) + // 1 = remaining semantic error + // 0 = semantic update finished already + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + XCTAssertEqual("func foo() {}\n", documentManager.latestSnapshot(uri)!.text) + }, + { (note: Notification) in + log("Received diagnostics for edit 1 - semantic") + XCTAssertEqual(note.params.version, 13) + XCTAssertEqual(note.params.diagnostics.count, 0) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + 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") XCTAssertEqual(note.params.version, 14) // 1 = semantic update finished already // 0 = only syntactic XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - bar() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 2 - semantic") - XCTAssertEqual(note.params.version, 14) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 14), contentChanges: [ - .init(range: Position(line: 1, utf16index: 0)..) in + XCTAssertEqual( + """ + func foo() {} + bar() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 2 - semantic") + XCTAssertEqual(note.params.version, 14) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 14), + contentChanges: [ + .init(range: Position(line: 1, utf16index: 0)..) in log("Received diagnostics for edit 3 - syntactic") XCTAssertEqual(note.params.version, 14) // 1 = remaining semantic error // 0 = semantic update finished already XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - foo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 3 - semantic") - XCTAssertEqual(note.params.version, 14) - XCTAssertEqual(note.params.diagnostics.count, 0) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 15), contentChanges: [ - .init(range: Position(line: 1, utf16index: 0)..) in + XCTAssertEqual( + """ + func foo() {} + foo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 3 - semantic") + XCTAssertEqual(note.params.version, 14) + XCTAssertEqual(note.params.diagnostics.count, 0) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 15), + contentChanges: [ + .init(range: Position(line: 1, utf16index: 0)..) in log("Received diagnostics for edit 4 - syntactic") XCTAssertEqual(note.params.version, 15) // 1 = semantic update finished already // 0 = only syntactic XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func foo() {} - fooTypo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 4 - semantic") - XCTAssertEqual(note.params.version, 15) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 16), contentChanges: [ - .init(range: nil, text: """ - func bar() {} - foo() - """) - ]), { (note: Notification) in + XCTAssertEqual( + """ + func foo() {} + fooTypo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 4 - semantic") + XCTAssertEqual(note.params.version, 15) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 16), + contentChanges: [ + .init( + range: nil, + text: """ + func bar() {} + foo() + """ + ) + ] + ), + { (note: Notification) in log("Received diagnostics for edit 5 - syntactic") XCTAssertEqual(note.params.version, 16) - // Could be remaining semantic error or new one. + // Could be remaining semantic error or new one. + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + """ + func bar() {} + foo() + """, + documentManager.latestSnapshot(uri)!.text + ) + }, + { (note: Notification) in + log("Received diagnostics for edit 5 - semantic") + XCTAssertEqual(note.params.version, 16) XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(""" - func bar() {} - foo() - """, documentManager.latestSnapshot(uri)!.text) - }, { (note: Notification) in - log("Received diagnostics for edit 5 - semantic") - XCTAssertEqual(note.params.version, 16) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 1, utf16index: 0)) - }) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 1, utf16index: 0) + ) + } + ) } func testExcludedDocumentSchemeDiagnostics() { @@ -317,32 +443,42 @@ final class LocalSwiftTests: XCTestCase { let excludedURI = DocumentURI(string: "git:/a.swift") let text = """ - func - """ + func + """ sk.allowUnexpectedNotification = false // Open the excluded URI first so our later notification handlers can confirm // that no diagnostics were emitted for this excluded URI. - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: excludedURI, - language: .swift, - version: 1, - text: text - ))) - - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: includedURI, - language: .swift, - version: 1, - text: text - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.uri, includedURI) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.uri, includedURI) - }) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: excludedURI, + language: .swift, + version: 1, + text: text + ) + ) + ) + + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: includedURI, + language: .swift, + version: 1, + text: text + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.uri, includedURI) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.uri, includedURI) + } + ) } func testCrossFileDiagnostics() { @@ -353,57 +489,82 @@ final class LocalSwiftTests: XCTestCase { sk.allowUnexpectedNotification = false - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uriA, language: .swift, version: 12, - text: """ - foo() - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 12) - // 1 = semantic update finished already - // 0 = only syntactic - XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 0)) - }) - - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uriB, language: .swift, version: 12, - text: """ - bar() - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 12) - // 1 = semantic update finished already - // 0 = only syntactic - XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 0)) - }) - - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uriA, version: 13), contentChanges: [ - .init(range: nil, text: "foo()\n") - ]), { (note: Notification) in - log("Received diagnostics for edit 1 - syntactic") - XCTAssertEqual(note.params.version, 13) - XCTAssertEqual(note.params.diagnostics.count, 1) - }, { (note: Notification) in - log("Received diagnostics for edit 1 - semantic") - XCTAssertEqual(note.params.version, 13) - XCTAssertEqual(note.params.diagnostics.count, 1) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uriA, + language: .swift, + version: 12, + text: """ + foo() + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 12) + // 1 = semantic update finished already + // 0 = only syntactic + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uriB, + language: .swift, + version: 12, + text: """ + bar() + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 12) + // 1 = semantic update finished already + // 0 = only syntactic + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 0) + ) + } + ) + + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uriA, version: 13), + contentChanges: [ + .init(range: nil, text: "foo()\n") + ] + ), + { (note: Notification) in + log("Received diagnostics for edit 1 - syntactic") + XCTAssertEqual(note.params.version, 13) + XCTAssertEqual(note.params.diagnostics.count, 1) + }, + { (note: Notification) in + log("Received diagnostics for edit 1 - semantic") + XCTAssertEqual(note.params.version, 13) + XCTAssertEqual(note.params.diagnostics.count, 1) + } + ) } func testDiagnosticsReopen() { @@ -411,49 +572,68 @@ final class LocalSwiftTests: XCTestCase { let uriA = DocumentURI(urlA) sk.allowUnexpectedNotification = false - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uriA, language: .swift, version: 12, - text: """ - foo() - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 12) - // 1 = semantic update finished already - // 0 = only syntactic - XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 12) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 0)) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uriA, + language: .swift, + version: 12, + text: """ + foo() + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 12) + // 1 = semantic update finished already + // 0 = only syntactic + XCTAssertLessThanOrEqual(note.params.diagnostics.count, 1) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 12) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 0) + ) + } + ) sk.send(DidCloseTextDocumentNotification(textDocument: .init(urlA))) - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uriA, language: .swift, version: 13, - text: """ - var - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - XCTAssertEqual(note.params.version, 13) - // 1 = syntactic, no cached semantic diagnostic from previous version - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 3)) - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.version, 13) - XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual( - note.params.diagnostics.first?.range.lowerBound, - Position(line: 0, utf16index: 3)) - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uriA, + language: .swift, + version: 13, + text: """ + var + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + XCTAssertEqual(note.params.version, 13) + // 1 = syntactic, no cached semantic diagnostic from previous version + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 3) + ) + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.version, 13) + XCTAssertEqual(note.params.diagnostics.count, 1) + XCTAssertEqual( + note.params.diagnostics.first?.range.lowerBound, + Position(line: 0, utf16index: 3) + ) + } + ) } func testEducationalNotesAreUsedAsDiagnosticCodes() { @@ -462,18 +642,26 @@ final class LocalSwiftTests: XCTestCase { sk.allowUnexpectedNotification = false - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, language: .swift, version: 12, - text: "@propertyWrapper struct Bar {}" - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - let diag = note.params.diagnostics.first! - XCTAssertEqual(diag.code, .string("property-wrapper-requirements")) - XCTAssertEqual(diag.codeDescription?.href.fileURL?.lastPathComponent, "property-wrapper-requirements.md") - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 12, + text: "@propertyWrapper struct Bar {}" + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + let diag = note.params.diagnostics.first! + XCTAssertEqual(diag.code, .string("property-wrapper-requirements")) + XCTAssertEqual(diag.codeDescription?.href.fileURL?.lastPathComponent, "property-wrapper-requirements.md") + } + ) } func testFixitsAreIncludedInPublishDiagnostics() { @@ -482,32 +670,47 @@ final class LocalSwiftTests: XCTestCase { sk.allowUnexpectedNotification = false - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, language: .swift, version: 12, - text: """ - func foo() { - let a = 2 + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 12, + text: """ + func foo() { + let a = 2 + } + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + let diag = note.params.diagnostics.first! + XCTAssertNotNil(diag.codeActions) + XCTAssertEqual(diag.codeActions!.count, 1) + let fixit = diag.codeActions!.first! + + // Expected Fix-it: Replace `let a` with `_` because it's never used + let expectedTextEdit = TextEdit( + range: Position(line: 1, utf16index: 2)..) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - let diag = note.params.diagnostics.first! - XCTAssertNotNil(diag.codeActions) - XCTAssertEqual(diag.codeActions!.count, 1) - let fixit = diag.codeActions!.first! - - // Expected Fix-it: Replace `let a` with `_` because it's never used - let expectedTextEdit = TextEdit(range: Position(line: 1, utf16index: 2)..) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - let diag = note.params.diagnostics.first! - XCTAssertEqual(diag.relatedInformation?.count, 2) - if let note1 = diag.relatedInformation?.first(where: { $0.message.contains("'?'") }) { - XCTAssertEqual(note1.codeActions?.count, 1) - if let fixit = note1.codeActions?.first { - // Expected Fix-it: Replace `let a` with `_` because it's never used - let expectedTextEdit = TextEdit(range: Position(line: 1, utf16index: 7)..) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + let diag = note.params.diagnostics.first! + XCTAssertEqual(diag.relatedInformation?.count, 2) + if let note1 = diag.relatedInformation?.first(where: { $0.message.contains("'?'") }) { + XCTAssertEqual(note1.codeActions?.count, 1) + if let fixit = note1.codeActions?.first { + // Expected Fix-it: Replace `let a` with `_` because it's never used + let expectedTextEdit = TextEdit( + range: Position(line: 1, utf16index: 7)..) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + let diag = note.params.diagnostics.first! + XCTAssertNotNil(diag.codeActions) + XCTAssertEqual(diag.codeActions!.count, 1) + let fixit = diag.codeActions!.first! + + // Expected Fix-it: Insert `;` + let expectedTextEdit = TextEdit( + range: Position(line: 1, utf16index: 11)..) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - let diag = note.params.diagnostics.first! - XCTAssertNotNil(diag.codeActions) - XCTAssertEqual(diag.codeActions!.count, 1) - let fixit = diag.codeActions!.first! - - // Expected Fix-it: Insert `;` - let expectedTextEdit = TextEdit(range: Position(line: 1, utf16index: 11)..) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + diagnostic = note.params.diagnostics.first } - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - diagnostic = note.params.diagnostics.first - }) + ) let request = CodeActionRequest( range: Position(line: 1, utf16index: 0)..) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + diagnostic = note.params.diagnostics.first } - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - diagnostic = note.params.diagnostics.first - }) + ) let request = CodeActionRequest( range: Position(line: 1, utf16index: 0)..) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - diagnostic = note.params.diagnostics.first - }) + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 12, + text: """ + @available(*, introduced: 10, deprecated: 11) + func foo() {} + """ + ) + ), + { (note: Notification) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + diagnostic = note.params.diagnostics.first + } + ) let request = CodeActionRequest( range: Position(line: 0, utf16index: 1)..) in + log("Received diagnostics for open - syntactic") + }, + { (note: Notification) in + log("Received diagnostics for open - semantic") + XCTAssertEqual(note.params.diagnostics.count, 1) + diagnostic = note.params.diagnostics.first! } - """ - )), { (note: Notification) in - log("Received diagnostics for open - syntactic") - }, { (note: Notification) in - log("Received diagnostics for open - semantic") - XCTAssertEqual(note.params.diagnostics.count, 1) - diagnostic = note.params.diagnostics.first! - }) + ) let request = CodeActionRequest( range: Position(line: 3, utf16index: 2)..func foo(_ bar: Baz) - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + func foo(_ bar: Baz) + """ + ), + """ ```swift func foo(_ bar: Baz) ``` --- - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - func foo() -> Bar - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + func foo() -> Bar + """ + ), + """ ```swift func foo() -> Bar ``` --- - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - My Link - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + My Link + """ + ), + """ [My Link](https://example.com) - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - My Invalid Link - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + My Invalid Link + """ + ), + """ My Invalid Link - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - func replacingOccurrences<Target, Replacement>(of target: Target, with replacement: Replacement, options: String.CompareOptions = default, range searchRange: Range<String.Index>? = default) -> String where Target : StringProtocol, Replacement : StringProtocol - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + func replacingOccurrences<Target, Replacement>(of target: Target, with replacement: Replacement, options: String.CompareOptions = default, range searchRange: Range<String.Index>? = default) -> String where Target : StringProtocol, Replacement : StringProtocol + """ + ), + """ ```swift func replacingOccurrences(of target: Target, with replacement: Replacement, options: String.CompareOptions = default, range searchRange: Range? = default) -> String where Target : StringProtocol, Replacement : StringProtocol ``` --- - """) + """ + ) } func testXMLToMarkdownComment() { - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - var foo - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + var foo + """ + ), + """ ```swift var foo ``` --- - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - foovar foo - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + foovar foo + """ + ), + """ ```swift var foo ``` --- - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - asdfvar foofoo - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + asdfvar foofoo + """ + ), + """ ```swift var foo ``` --- - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - FOO - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + FOO + """ + ), + """ FOO - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - FOOvar foo - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + FOOvar foo + """ + ), + """ FOO ```swift @@ -891,60 +1240,90 @@ final class LocalSwiftTests: XCTestCase { --- - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - FOOBAR - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + FOOBAR + """ + ), + """ FOO ### Discussion BAR - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - ABC - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + ABC + """ + ), + """ A B C - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a + """ + ), + """ ``` a ``` - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a + """ + ), + """ ``` 1.\ta ``` - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - ab - """), """ + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + ab + """ + ), + """ ``` 1.\ta 2.\tb ``` - - """) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - abcd - """), """ + + """ + ) + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + abcd + """ + ), + """ ``` 1.\ta 2.\tb @@ -956,74 +1335,90 @@ final class LocalSwiftTests: XCTestCase { ``` - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a b c d e f g h i - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a b c d e f g h i + """ + ), + """ a b c `d e f` g h i - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a b c d e f g h i - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a b c d e f g h i + """ + ), + """ a b c *d e f* g h i - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a b c d e f g h i - """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a b c d e f g h i + """ + ), + """ a b c **d e f** g h i - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a b c

d e f

g h i
- """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a b c

d e f

g h i
+ """ + ), + """ a b c # d e f g h i - """) + """ + ) - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - a b c

d e f

g h i
- """), """ + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + a b c

d e f

g h i
+ """ + ), + """ a b c ### d e f g h i - """) - - XCTAssertEqual(try xmlDocumentationToMarkdown( - "" + - "String" + - "s:SS" + - "struct String" + - "" + - "" + - "A Unicode s" + - "" + - "" + - "A string is a series of characters, such as "Swift", that forms a collection. " + - "The String type bridges with the Objective-C class NSString and offers" + - "" + - "You can create new strings A string literal i" + - "" + - "" + - "" + - "" + - "" + - "..." + - "" + - "" + - "" + - "" + - "" + - "" + - "" - ), """ + """ + ) + + XCTAssertEqual( + try xmlDocumentationToMarkdown( + "" + "String" + "s:SS" + "struct String" + + "" + "" + + "A Unicode s" + "" + "" + + "A string is a series of characters, such as "Swift", that forms a collection. " + + "The String type bridges with the Objective-C class NSString and offers" + + "" + + "You can create new strings A string literal i" + "" + + "" + + "" + + "" + "" + + "..." + "" + + "" + + "" + "" + "" + "" + + "" + ), + """ ```swift struct String ``` @@ -1050,110 +1445,115 @@ final class LocalSwiftTests: XCTestCase { ``` - """) - - XCTAssertEqual(try xmlDocumentationToMarkdown( - "" + - "" + - "Applies the given edits to the document." + - "" + - "" + - "editCallback" + - "in" + - "Optional closure to call for each edit." + - "" + - "" + - "before" + - "in" + - "The document contents before the edit is applied." + - "" + - "" + - "" + - ""), """ - Applies the given edits to the document. - - - Parameters: - - editCallback: Optional closure to call for each edit. - - before: The document contents *before* the edit is applied. - """) - - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - The contents of the file after all the edits are applied. - """), """ - ### Returns - - The contents of the file after all the edits are applied. - """) - - XCTAssertEqual(try xmlDocumentationToMarkdown(""" - Error.missingDocument if the document is not open. - """), """ - ### Throws - - Error.missingDocument if the document is not open. - """) - - XCTAssertEqual(try xmlDocumentationToMarkdown( - "" + - "S" + - "s:1a1SV" + - "struct S" + - "" + - "" + - #""# + - "" + - "" + - "" + - "" + - "" + - "" + - "]]>" + - "Title" + - "]]>" + - "" + - "Details." + - "" + - "" + - ""), """ - ```swift - struct S - ``` - - --- - ### Discussion - - ```swift - 1.\tlet S = 12456 - 2.\t - ``` - -

Title

- - Details. - """) + """ + ) + + XCTAssertEqual( + try xmlDocumentationToMarkdown( + "" + "" + + "Applies the given edits to the document." + "" + + "" + "editCallback" + + "in" + + "Optional closure to call for each edit." + "" + + "" + "before" + "in" + + "The document contents before the edit is applied." + + "" + "" + + "" + "" + ), + """ + Applies the given edits to the document. + + - Parameters: + - editCallback: Optional closure to call for each edit. + - before: The document contents *before* the edit is applied. + """ + ) + + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + The contents of the file after all the edits are applied. + """ + ), + """ + ### Returns + + The contents of the file after all the edits are applied. + """ + ) + + XCTAssertEqual( + try xmlDocumentationToMarkdown( + """ + Error.missingDocument if the document is not open. + """ + ), + """ + ### Throws + + Error.missingDocument if the document is not open. + """ + ) + + XCTAssertEqual( + try xmlDocumentationToMarkdown( + "" + "S" + "s:1a1SV" + "struct S" + "" + + "" + + #""# + "" + "" + + "" + + "" + "" + "" + "]]>" + + "Title" + "]]>" + + "" + "Details." + "" + "" + "" + ), + """ + ```swift + struct S + ``` + + --- + ### Discussion + + ```swift + 1.\tlet S = 12456 + 2.\t + ``` + +

Title

+ + Details. + """ + ) } func testSymbolInfo() throws { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") let uri = DocumentURI(url) - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 1, - text: """ - import Foundation - struct S { - func foo() { - var local = 1 - } - } - """))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 1, + text: """ + import Foundation + struct S { + func foo() { + var local = 1 + } + } + """ + ) + ) + ) do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 0, utf16index: 7))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 0, utf16index: 7) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -1166,11 +1566,14 @@ final class LocalSwiftTests: XCTestCase { XCTAssertEqual(sym.bestLocalDeclaration?.range.lowerBound.utf16index, nil) } } - + do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 1, utf16index: 7))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 1, utf16index: 7) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -1184,9 +1587,12 @@ final class LocalSwiftTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 2, utf16index: 7))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 2, utf16index: 7) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -1200,9 +1606,12 @@ final class LocalSwiftTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 8))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 8) + ) + ) XCTAssertEqual(resp.count, 1) if let sym = resp.first { @@ -1216,9 +1625,12 @@ final class LocalSwiftTests: XCTestCase { } do { - let resp = try sk.sendSync(SymbolInfoRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 0))) + let resp = try sk.sendSync( + SymbolInfoRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 0) + ) + ) XCTAssertEqual(resp.count, 0) } @@ -1228,21 +1640,29 @@ final class LocalSwiftTests: XCTestCase { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") let uri = DocumentURI(url) - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 1, - text: """ - /// This is a doc comment for S. - /// - /// Details. - struct S {} - """))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 1, + text: """ + /// This is a doc comment for S. + /// + /// Details. + struct S {} + """ + ) + ) + ) do { - let resp = try sk.sendSync(HoverRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 7))) + let resp = try sk.sendSync( + HoverRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 7) + ) + ) XCTAssertNotNil(resp) if let hover = resp { @@ -1252,7 +1672,9 @@ final class LocalSwiftTests: XCTestCase { return } XCTAssertEqual(content.kind, .markdown) - XCTAssertEqual(content.value, """ + XCTAssertEqual( + content.value, + """ S ```swift struct S @@ -1264,14 +1686,18 @@ final class LocalSwiftTests: XCTestCase { ### Discussion Details. - """) + """ + ) } } do { - let resp = try sk.sendSync(HoverRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 0, utf16index: 7))) + let resp = try sk.sendSync( + HoverRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 0, utf16index: 7) + ) + ) XCTAssertNil(resp) } @@ -1280,21 +1706,29 @@ final class LocalSwiftTests: XCTestCase { func testHoverNameEscaping() throws { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 1, - text: """ - /// this is **bold** documentation - func test(_ a: Int, _ b: Int) { } - /// this is *italic* documentation - func *%*(lhs: String, rhs: String) { } - """))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 1, + text: """ + /// this is **bold** documentation + func test(_ a: Int, _ b: Int) { } + /// this is *italic* documentation + func *%*(lhs: String, rhs: String) { } + """ + ) + ) + ) do { - let resp = try sk.sendSync(HoverRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 1, utf16index: 7))) + let resp = try sk.sendSync( + HoverRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 1, utf16index: 7) + ) + ) XCTAssertNotNil(resp) if let hover = resp { @@ -1304,7 +1738,9 @@ final class LocalSwiftTests: XCTestCase { return } XCTAssertEqual(content.kind, .markdown) - XCTAssertEqual(content.value, ##""" + XCTAssertEqual( + content.value, + ##""" test(\_:\_:) ```swift func test(_ a: Int, _ b: Int) @@ -1312,14 +1748,18 @@ final class LocalSwiftTests: XCTestCase { --- this is **bold** documentation - """##) + """## + ) } } do { - let resp = try sk.sendSync(HoverRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 7))) + let resp = try sk.sendSync( + HoverRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 7) + ) + ) XCTAssertNotNil(resp) if let hover = resp { @@ -1329,7 +1769,9 @@ final class LocalSwiftTests: XCTestCase { return } XCTAssertEqual(content.kind, .markdown) - XCTAssertEqual(content.value, ##""" + XCTAssertEqual( + content.value, + ##""" \*%\*(\_:\_:) ```swift func *%* (lhs: String, rhs: String) @@ -1337,40 +1779,52 @@ final class LocalSwiftTests: XCTestCase { --- this is *italic* documentation - """##) + """## + ) } } } - + func testDocumentSymbolHighlight() throws { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") let uri = DocumentURI(url) - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 1, - text: """ - func test() { - let a = 1 - let b = 2 - let ccc = 3 - _ = b - _ = ccc + ccc - } - """))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 1, + text: """ + func test() { + let a = 1 + let b = 2 + let ccc = 3 + _ = b + _ = ccc + ccc + } + """ + ) + ) + ) do { - let resp = try sk.sendSync(DocumentHighlightRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 0, utf16index: 0))) + let resp = try sk.sendSync( + DocumentHighlightRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 0, utf16index: 0) + ) + ) XCTAssertEqual(resp?.count, 0) } do { - let resp = try sk.sendSync(DocumentHighlightRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 1, utf16index: 6))) + let resp = try sk.sendSync( + DocumentHighlightRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 1, utf16index: 6) + ) + ) XCTAssertEqual(resp?.count, 1) if let highlight = resp?.first { XCTAssertEqual(highlight.range.lowerBound.line, 1) @@ -1381,9 +1835,12 @@ final class LocalSwiftTests: XCTestCase { } do { - let resp = try sk.sendSync(DocumentHighlightRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 2, utf16index: 6))) + let resp = try sk.sendSync( + DocumentHighlightRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 2, utf16index: 6) + ) + ) XCTAssertEqual(resp?.count, 2) if let highlight = resp?.first { XCTAssertEqual(highlight.range.lowerBound.line, 2) @@ -1400,9 +1857,12 @@ final class LocalSwiftTests: XCTestCase { } do { - let resp = try sk.sendSync(DocumentHighlightRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 3, utf16index: 6))) + let resp = try sk.sendSync( + DocumentHighlightRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 3, utf16index: 6) + ) + ) XCTAssertEqual(resp?.count, 3) if let highlight = resp?.first { XCTAssertEqual(highlight.range.lowerBound.line, 3) @@ -1426,32 +1886,28 @@ final class LocalSwiftTests: XCTestCase { } func testEditorPlaceholderParsing() { - var text = "<#basic placeholder" + - "#>" // Need to end this in another line so Xcode doesn't treat it as a real placeholder + var text = "<#basic placeholder" + "#>" // Need to end this in another line so Xcode doesn't treat it as a real placeholder var data = EditorPlaceholder(text) XCTAssertNotNil(data) if let data = data { XCTAssertEqual(data, .basic("basic placeholder")) XCTAssertEqual(data.displayName, "basic placeholder") } - text = "<#T##x: Int##Int" + - "#>" + text = "<#T##x: Int##Int" + "#>" data = EditorPlaceholder(text) XCTAssertNotNil(data) if let data = data { XCTAssertEqual(data, .typed(displayName: "x: Int", type: "Int", typeForExpansion: "Int")) XCTAssertEqual(data.displayName, "x: Int") } - text = "<#T##x: Int##Blah##()->Int" + - "#>" + text = "<#T##x: Int##Blah##()->Int" + "#>" data = EditorPlaceholder(text) XCTAssertNotNil(data) if let data = data { XCTAssertEqual(data, .typed(displayName: "x: Int", type: "Blah", typeForExpansion: "()->Int")) XCTAssertEqual(data.displayName, "x: Int") } - text = "<#T##Int" + - "#>" + text = "<#T##Int" + "#>" data = EditorPlaceholder(text) XCTAssertNotNil(data) if let data = data { @@ -1473,52 +1929,70 @@ final class LocalSwiftTests: XCTestCase { text = "<#foo#" data = EditorPlaceholder(text) XCTAssertNil(data) - text = " <#foo" + - "#>" + text = " <#foo" + "#>" data = EditorPlaceholder(text) XCTAssertNil(data) } - + func testIncrementalParse() async throws { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") let uri = DocumentURI(url) var reusedNodes: [Syntax] = [] - let swiftLanguageServer = await connection.server!._languageService(for: uri, .swift, in: connection.server!.workspaceForDocument(uri: uri)!) as! SwiftLanguageServer + let swiftLanguageServer = + await connection.server!._languageService( + for: uri, + .swift, + in: connection.server!.workspaceForDocument(uri: uri)! + ) as! SwiftLanguageServer await swiftLanguageServer.setReusedNodeCallback({ reusedNodes.append($0) }) sk.allowUnexpectedNotification = false - - sk.sendNoteSync(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 0, - text: """ - func foo() { - } - class bar { + + sk.sendNoteSync( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 0, + text: """ + func foo() { + } + class bar { + } + """ + ) + ), + { (note: LanguageServerProtocol.Notification) -> Void in + log("Received diagnostics for open - syntactic") + }, + { (note: LanguageServerProtocol.Notification) -> Void in + log("Received diagnostics for open - semantic") } - """ - )), { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for open - syntactic") - }, { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for open - semantic") - }) + ) // Send a request that triggers a syntax tree to be built. _ = try sk.sendSync(FoldingRangeRequest(textDocument: .init(uri))) - sk.sendNoteSync(DidChangeTextDocumentNotification(textDocument: .init(uri, version: 1), contentChanges: [ - .init(range: Range(Position(line: 2, utf16index: 7)), text: "a"), - ]), { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for text edit - syntactic") - }, { (note: LanguageServerProtocol.Notification) -> Void in - log("Received diagnostics for text edit - semantic") - }) - + sk.sendNoteSync( + DidChangeTextDocumentNotification( + textDocument: .init(uri, version: 1), + contentChanges: [ + .init(range: Range(Position(line: 2, utf16index: 7)), text: "a") + ] + ), + { (note: LanguageServerProtocol.Notification) -> Void in + log("Received diagnostics for text edit - syntactic") + }, + { (note: LanguageServerProtocol.Notification) -> Void in + log("Received diagnostics for text edit - semantic") + } + ) + XCTAssertEqual(reusedNodes.count, 1) - + let firstNode = try XCTUnwrap(reusedNodes.first) - XCTAssertEqual(firstNode.description, + XCTAssertEqual( + firstNode.description, """ func foo() { } diff --git a/Tests/SourceKitLSPTests/MainFilesProviderTests.swift b/Tests/SourceKitLSPTests/MainFilesProviderTests.swift index 130dfa901..5cd3dddc4 100644 --- a/Tests/SourceKitLSPTests/MainFilesProviderTests.swift +++ b/Tests/SourceKitLSPTests/MainFilesProviderTests.swift @@ -10,12 +10,12 @@ // //===----------------------------------------------------------------------===// -import SourceKitLSP +import IndexStoreDB +import LSPTestSupport +import LanguageServerProtocol import SKCore import SKTestSupport -import LanguageServerProtocol -import LSPTestSupport -import IndexStoreDB +import SourceKitLSP import XCTest final class MainFilesProviderTests: XCTestCase { @@ -65,15 +65,21 @@ final class MainFilesProviderTests: XCTestCase { try await fulfillmentOfOrThrow([mainFilesDelegate.expectation]) try ws.edit { changes, _ in - changes.write(""" + changes.write( + """ #include "bridging.h" void d_new(void) { bridging(); } - """, to: d.fileURL!) - - changes.write(""" - #include "unique.h" - void c_new(void) { unique(); } - """, to: c.fileURL!) + """, + to: d.fileURL! + ) + + changes.write( + """ + #include "unique.h" + void c_new(void) { unique(); } + """, + to: c.fileURL! + ) } mainFilesDelegate.expectation = expectation(description: "main files changed after edit") diff --git a/Tests/SourceKitLSPTests/PublishDiagnosticsTests.swift b/Tests/SourceKitLSPTests/PublishDiagnosticsTests.swift index cc5286338..5db0735da 100644 --- a/Tests/SourceKitLSPTests/PublishDiagnosticsTests.swift +++ b/Tests/SourceKitLSPTests/PublishDiagnosticsTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKTestSupport import XCTest @@ -32,156 +32,208 @@ final class PublishDiagnosticsTests: XCTestCase { connection = TestSourceKitServer() sk = connection.client let documentCapabilities = TextDocumentClientCapabilities() - _ = try! sk.sendSync(InitializeRequest( - processId: nil, - rootPath: nil, - rootURI: nil, - initializationOptions: nil, - capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), - trace: .off, - workspaceFolders: nil)) + _ = try! sk.sendSync( + InitializeRequest( + processId: nil, + rootPath: nil, + rootURI: nil, + initializationOptions: nil, + capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), + trace: .off, + workspaceFolders: nil + ) + ) } override func tearDown() { sk = nil connection = nil } - + private func openDocument(text: String) { - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: version, - text: text - ))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: version, + text: text + ) + ) + ) version += 1 } private func editDocument(changes: [TextDocumentContentChangeEvent]) { - sk.send(DidChangeTextDocumentNotification( - textDocument: VersionedTextDocumentIdentifier( - uri, - version: version - ), - contentChanges: changes - )) + sk.send( + DidChangeTextDocumentNotification( + textDocument: VersionedTextDocumentIdentifier( + uri, + version: version + ), + contentChanges: changes + ) + ) } func testUnknownIdentifierDiagnostic() { let syntacticDiagnosticsReceived = self.expectation(description: "Syntactic diagnotistics received") let semanticDiagnosticsReceived = self.expectation(description: "Semantic diagnotistics received") - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in // Unresolved identifier is not a syntactic diagnostic. XCTAssertEqual(note.params.diagnostics, []) syntacticDiagnosticsReceived.fulfill() } - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 1, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in // Unresolved identifier is not a syntactic diagnostic. XCTAssertEqual(note.params.diagnostics, []) initialSyntacticDiagnosticsReceived.fulfill() } - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 1, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in // We should report the semantic diagnostic reported by the edit range-shifted XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 2, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 2, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in // Unresolved identifier is not a syntactic diagnostic. XCTAssertEqual(note.params.diagnostics, []) initialSyntacticDiagnosticsReceived.fulfill() } - sk.appendOneShotNotificationHandler { (note: Notification) in + sk.appendOneShotNotificationHandler { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 2, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in // We should report the semantic diagnostic reported by the edit range-shifted XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 1, utf16index: 2)..) in + sk.appendOneShotNotificationHandler { (note: Notification) in XCTAssertEqual(note.params.diagnostics.count, 1) - XCTAssertEqual(note.params.diagnostics.first?.range, Position(line: 1, utf16index: 2).. [Diagnostic] { let url = URL(fileURLWithPath: "/PullDiagnostics/\(UUID()).swift") - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 17, - text: text - ))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 17, + text: text + ) + ) + ) let request = DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(url)) @@ -75,11 +83,13 @@ final class PullDiagnosticsTests: XCTestCase { } func testUnknownIdentifierDiagnostic() throws { - let diagnostics = try performDiagnosticRequest(text: """ - func foo() { - invalid - } - """) + let diagnostics = try performDiagnosticRequest( + text: """ + func foo() { + invalid + } + """ + ) XCTAssertEqual(diagnostics.count, 1) let diagnostic = try XCTUnwrap(diagnostics.first) XCTAssertEqual(diagnostic.range, Position(line: 1, utf16index: 2).. XCTestExpectation { @@ -84,9 +86,11 @@ final class SemanticTokensTests: XCTestCase { let registerCapabilityExpectation = expectation(description: "\(#function) - register semantic tokens capability") sk.appendOneShotRequestHandler { (req: Request) in let registrations = req.params.registrations - XCTAssert(registrations.contains { reg in - reg.method == SemanticTokensRegistrationOptions.method - }) + XCTAssert( + registrations.contains { reg in + reg.method == SemanticTokensRegistrationOptions.method + } + ) req.reply(VoidResponse()) registerCapabilityExpectation.fulfill() } @@ -95,12 +99,16 @@ final class SemanticTokensTests: XCTestCase { let refreshExpectation = expectSemanticTokensRefresh() - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: version, - text: text - ))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: version, + text: text + ) + ) + ) version += 1 wait(for: [registerCapabilityExpectation, refreshExpectation], timeout: defaultTimeout) @@ -116,25 +124,30 @@ final class SemanticTokensTests: XCTestCase { expectations.append(expectSemanticTokensRefresh()) } - sk.send(DidChangeTextDocumentNotification( - textDocument: VersionedTextDocumentIdentifier( - uri, - version: version - ), - contentChanges: changes - )) + sk.send( + DidChangeTextDocumentNotification( + textDocument: VersionedTextDocumentIdentifier( + uri, + version: version + ), + contentChanges: changes + ) + ) version += 1 wait(for: expectations, timeout: defaultTimeout) } private func editDocument(range: Range, text: String, expectRefresh: Bool = true) { - editDocument(changes: [ - TextDocumentContentChangeEvent( - range: range, - text: text - ) - ], expectRefresh: expectRefresh) + editDocument( + changes: [ + TextDocumentContentChangeEvent( + range: range, + text: text + ) + ], + expectRefresh: expectRefresh + ) } private func performSemanticTokensRequest(range: Range? = nil) throws -> [Token] { @@ -170,19 +183,22 @@ final class SemanticTokensTests: XCTestCase { ] let encoded = tokens.lspEncoded - XCTAssertEqual(encoded, [ - 2, // line delta - 3, // char delta - 5, // length - Token.Kind.string.rawValue, // kind - 0, // modifiers - - 2, // line delta - 2, // char delta - 1, // length - Token.Kind.interface.rawValue, // kind - Token.Modifiers.deprecated.rawValue | Token.Modifiers.definition.rawValue, // modifiers - ]) + XCTAssertEqual( + encoded, + [ + 2, // line delta + 3, // char delta + 5, // length + Token.Kind.string.rawValue, // kind + 0, // modifiers + + 2, // line delta + 2, // char delta + 1, // length + Token.Kind.interface.rawValue, // kind + Token.Modifiers.deprecated.rawValue | Token.Modifiers.definition.rawValue, // modifiers + ] + ) let decoded = [Token](lspEncodedTokens: encoded) XCTAssertEqual(decoded, tokens) @@ -190,13 +206,13 @@ final class SemanticTokensTests: XCTestCase { func testRangeSplitting() async { let text = """ - struct X { - let x: Int - let y: String + struct X { + let x: Int + let y: String - } - """ + } + """ openDocument(text: text) guard let snapshot = await connection.server?._documentManager.latestSnapshot(uri) else { @@ -207,16 +223,22 @@ final class SemanticTokensTests: XCTestCase { XCTAssertEqual(empty._splitToSingleLineRanges(in: snapshot), []) let multiLine = Position(line: 1, utf16index: 6)..() {} - """ + func f() {} + """ let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - // protocol X {} - Token(line: 0, utf16index: 0, length: 8, kind: .keyword), - Token(line: 0, utf16index: 9, length: 1, kind: .identifier), - // class Y: X {} - Token(line: 1, utf16index: 0, length: 5, kind: .keyword), - Token(line: 1, utf16index: 6, length: 1, kind: .identifier), - Token(line: 1, utf16index: 9, length: 1, kind: .interface), - // let y: Y = X() - Token(line: 3, utf16index: 0, length: 3, kind: .keyword), - Token(line: 3, utf16index: 4, length: 1, kind: .identifier), - Token(line: 3, utf16index: 7, length: 1, kind: .class), - Token(line: 3, utf16index: 11, length: 1, kind: .interface), - // func f() {} - Token(line: 5, utf16index: 0, length: 4, kind: .keyword), - Token(line: 5, utf16index: 5, length: 1, kind: .identifier), - Token(line: 5, utf16index: 7, length: 1, kind: .identifier), - Token(line: 5, utf16index: 10, length: 1, kind: .interface), - ]) + XCTAssertEqual( + tokens, + [ + // protocol X {} + Token(line: 0, utf16index: 0, length: 8, kind: .keyword), + Token(line: 0, utf16index: 9, length: 1, kind: .identifier), + // class Y: X {} + Token(line: 1, utf16index: 0, length: 5, kind: .keyword), + Token(line: 1, utf16index: 6, length: 1, kind: .identifier), + Token(line: 1, utf16index: 9, length: 1, kind: .interface), + // let y: Y = X() + Token(line: 3, utf16index: 0, length: 3, kind: .keyword), + Token(line: 3, utf16index: 4, length: 1, kind: .identifier), + Token(line: 3, utf16index: 7, length: 1, kind: .class), + Token(line: 3, utf16index: 11, length: 1, kind: .interface), + // func f() {} + Token(line: 5, utf16index: 0, length: 4, kind: .keyword), + Token(line: 5, utf16index: 5, length: 1, kind: .identifier), + Token(line: 5, utf16index: 7, length: 1, kind: .identifier), + Token(line: 5, utf16index: 10, length: 1, kind: .interface), + ] + ) } func testSemanticTokensForFunctionSignatures() throws { let text = "func f(x: Int, _ y: String) {}" let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - Token(line: 0, utf16index: 0, length: 4, kind: .keyword), - Token(line: 0, utf16index: 5, length: 1, kind: .identifier), - Token(line: 0, utf16index: 7, length: 1, kind: .identifier), - Token(line: 0, utf16index: 10, length: 3, kind: .struct, modifiers: .defaultLibrary), - Token(line: 0, utf16index: 17, length: 1, kind: .identifier), - Token(line: 0, utf16index: 20, length: 6, kind: .struct, modifiers: .defaultLibrary), - ]) + XCTAssertEqual( + tokens, + [ + Token(line: 0, utf16index: 0, length: 4, kind: .keyword), + Token(line: 0, utf16index: 5, length: 1, kind: .identifier), + Token(line: 0, utf16index: 7, length: 1, kind: .identifier), + Token(line: 0, utf16index: 10, length: 3, kind: .struct, modifiers: .defaultLibrary), + Token(line: 0, utf16index: 17, length: 1, kind: .identifier), + Token(line: 0, utf16index: 20, length: 6, kind: .struct, modifiers: .defaultLibrary), + ] + ) } func testSemanticTokensForFunctionSignaturesWithEmoji() throws { let text = "func x👍y() {}" let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - Token(line: 0, utf16index: 0, length: 4, kind: .keyword), - Token(line: 0, utf16index: 5, length: 4, kind: .identifier), - ]) + XCTAssertEqual( + tokens, + [ + Token(line: 0, utf16index: 0, length: 4, kind: .keyword), + Token(line: 0, utf16index: 5, length: 4, kind: .identifier), + ] + ) } func testSemanticTokensForStaticMethods() throws { let text = """ - class X { - deinit {} - static func f() {} - class func g() {} - } - X.f() - X.g() - """ + class X { + deinit {} + static func f() {} + class func g() {} + } + X.f() + X.g() + """ let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - // class X - Token(line: 0, utf16index: 0, length: 5, kind: .keyword), - Token(line: 0, utf16index: 6, length: 1, kind: .identifier), - // deinit {} - Token(line: 1, utf16index: 2, length: 6, kind: .keyword), - // static func f() {} - Token(line: 2, utf16index: 2, length: 6, kind: .keyword), - Token(line: 2, utf16index: 9, length: 4, kind: .keyword), - Token(line: 2, utf16index: 14, length: 1, kind: .identifier), - // class func g() {} - Token(line: 3, utf16index: 2, length: 5, kind: .keyword), - Token(line: 3, utf16index: 8, length: 4, kind: .keyword), - Token(line: 3, utf16index: 13, length: 1, kind: .identifier), - // X.f() - Token(line: 5, utf16index: 0, length: 1, kind: .class), - Token(line: 5, utf16index: 2, length: 1, kind: .method, modifiers: [.static]), - // X.g() - Token(line: 6, utf16index: 0, length: 1, kind: .class), - Token(line: 6, utf16index: 2, length: 1, kind: .method, modifiers: [.static]), - ]) + XCTAssertEqual( + tokens, + [ + // class X + Token(line: 0, utf16index: 0, length: 5, kind: .keyword), + Token(line: 0, utf16index: 6, length: 1, kind: .identifier), + // deinit {} + Token(line: 1, utf16index: 2, length: 6, kind: .keyword), + // static func f() {} + Token(line: 2, utf16index: 2, length: 6, kind: .keyword), + Token(line: 2, utf16index: 9, length: 4, kind: .keyword), + Token(line: 2, utf16index: 14, length: 1, kind: .identifier), + // class func g() {} + Token(line: 3, utf16index: 2, length: 5, kind: .keyword), + Token(line: 3, utf16index: 8, length: 4, kind: .keyword), + Token(line: 3, utf16index: 13, length: 1, kind: .identifier), + // X.f() + Token(line: 5, utf16index: 0, length: 1, kind: .class), + Token(line: 5, utf16index: 2, length: 1, kind: .method, modifiers: [.static]), + // X.g() + Token(line: 6, utf16index: 0, length: 1, kind: .class), + Token(line: 6, utf16index: 2, length: 1, kind: .method, modifiers: [.static]), + ] + ) } func testSemanticTokensForEnumMembers() throws { let text = """ - enum Maybe { - case none - case some(T) - } + enum Maybe { + case none + case some(T) + } - let x = Maybe.none - let y: Maybe = .some(42) - """ + let x = Maybe.none + let y: Maybe = .some(42) + """ let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - // enum Maybe - Token(line: 0, utf16index: 0, length: 4, kind: .keyword), - Token(line: 0, utf16index: 5, length: 5, kind: .identifier), - Token(line: 0, utf16index: 11, length: 1, kind: .identifier), - // case none - Token(line: 1, utf16index: 2, length: 4, kind: .keyword), - Token(line: 1, utf16index: 7, length: 4, kind: .identifier), - // case some - Token(line: 2, utf16index: 2, length: 4, kind: .keyword), - Token(line: 2, utf16index: 7, length: 4, kind: .identifier), - Token(line: 2, utf16index: 12, length: 1, kind: .typeParameter), - // let x = Maybe.none - Token(line: 5, utf16index: 0, length: 3, kind: .keyword), - Token(line: 5, utf16index: 4, length: 1, kind: .identifier), - Token(line: 5, utf16index: 8, length: 5, kind: .enum), - Token(line: 5, utf16index: 14, length: 6, kind: .struct, modifiers: .defaultLibrary), - Token(line: 5, utf16index: 22, length: 4, kind: .enumMember), - // let y: Maybe = .some(42) - Token(line: 6, utf16index: 0, length: 3, kind: .keyword), - Token(line: 6, utf16index: 4, length: 1, kind: .identifier), - Token(line: 6, utf16index: 7, length: 5, kind: .enum), - Token(line: 6, utf16index: 16, length: 4, kind: .enumMember), - Token(line: 6, utf16index: 21, length: 2, kind: .number), - ]) + XCTAssertEqual( + tokens, + [ + // enum Maybe + Token(line: 0, utf16index: 0, length: 4, kind: .keyword), + Token(line: 0, utf16index: 5, length: 5, kind: .identifier), + Token(line: 0, utf16index: 11, length: 1, kind: .identifier), + // case none + Token(line: 1, utf16index: 2, length: 4, kind: .keyword), + Token(line: 1, utf16index: 7, length: 4, kind: .identifier), + // case some + Token(line: 2, utf16index: 2, length: 4, kind: .keyword), + Token(line: 2, utf16index: 7, length: 4, kind: .identifier), + Token(line: 2, utf16index: 12, length: 1, kind: .typeParameter), + // let x = Maybe.none + Token(line: 5, utf16index: 0, length: 3, kind: .keyword), + Token(line: 5, utf16index: 4, length: 1, kind: .identifier), + Token(line: 5, utf16index: 8, length: 5, kind: .enum), + Token(line: 5, utf16index: 14, length: 6, kind: .struct, modifiers: .defaultLibrary), + Token(line: 5, utf16index: 22, length: 4, kind: .enumMember), + // let y: Maybe = .some(42) + Token(line: 6, utf16index: 0, length: 3, kind: .keyword), + Token(line: 6, utf16index: 4, length: 1, kind: .identifier), + Token(line: 6, utf16index: 7, length: 5, kind: .enum), + Token(line: 6, utf16index: 16, length: 4, kind: .enumMember), + Token(line: 6, utf16index: 21, length: 2, kind: .number), + ] + ) } func testRegexSemanticTokens() throws { @@ -493,31 +548,37 @@ final class SemanticTokensTests: XCTestCase { let r = /a[bc]*/ """ let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - Token(line: 0, utf16index: 0, length: 3, kind: .keyword), - Token(line: 0, utf16index: 4, length: 1, kind: .identifier), - Token(line: 0, utf16index: 8, length: 8, kind: .regexp), - ]) + XCTAssertEqual( + tokens, + [ + Token(line: 0, utf16index: 0, length: 3, kind: .keyword), + Token(line: 0, utf16index: 4, length: 1, kind: .identifier), + Token(line: 0, utf16index: 8, length: 8, kind: .regexp), + ] + ) } func testOperatorDeclaration() throws { let text = """ - infix operator ?= :ComparisonPrecedence - """ + infix operator ?= :ComparisonPrecedence + """ let tokens = try openAndPerformSemanticTokensRequest(text: text) - XCTAssertEqual(tokens, [ - Token(line: 0, utf16index: 0, length: 5, kind: .keyword), - Token(line: 0, utf16index: 6, length: 8, kind: .keyword), - Token(line: 0, utf16index: 15, length: 2, kind: .operator), - Token(line: 0, utf16index: 19, length: 20, kind: .identifier), - ]) + XCTAssertEqual( + tokens, + [ + Token(line: 0, utf16index: 0, length: 5, kind: .keyword), + Token(line: 0, utf16index: 6, length: 8, kind: .keyword), + Token(line: 0, utf16index: 15, length: 2, kind: .operator), + Token(line: 0, utf16index: 19, length: 20, kind: .identifier), + ] + ) } func testEmptyEdit() throws { let text = """ - let x: String = "test" - var y = 123 - """ + let x: String = "test" + var y = 123 + """ openDocument(text: text) let before = try performSemanticTokensRequest() @@ -531,8 +592,8 @@ final class SemanticTokensTests: XCTestCase { func testReplaceUntilMiddleOfToken() throws { let text = """ - var test = 4567 - """ + var test = 4567 + """ openDocument(text: text) let before = try performSemanticTokensRequest() @@ -540,54 +601,66 @@ final class SemanticTokensTests: XCTestCase { Token(line: 0, utf16index: 0, length: 3, kind: .keyword), Token(line: 0, utf16index: 4, length: 4, kind: .identifier), ] - XCTAssertEqual(before, expectedLeading + [ - Token(line: 0, utf16index: 11, length: 4, kind: .number), - ]) + XCTAssertEqual( + before, + expectedLeading + [ + Token(line: 0, utf16index: 11, length: 4, kind: .number) + ] + ) let start = Position(line: 0, utf16index: 10) let end = Position(line: 0, utf16index: 13) editDocument(range: start.. [URL] { try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) } func checkRunningIndex(build: Bool) async throws -> URL? { - guard let ws = try await staticSourceKitTibsWorkspace( - name: "SwiftModules", tmpDir: tmpDir, removeTmpDir: false) + guard + let ws = try await staticSourceKitTibsWorkspace( + name: "SwiftModules", + tmpDir: tmpDir, + removeTmpDir: false + ) else { return nil } @@ -131,9 +171,12 @@ final class SKTests: XCTestCase { let locDef = ws.testLoc("aaa:def") let locRef = ws.testLoc("aaa:call:c") try ws.openDocument(locRef.url, language: .swift) - let response = try ws.sk.sendSync(DefinitionRequest( - textDocument: locRef.docIdentifier, - position: locRef.position)) + let response = try ws.sk.sendSync( + DefinitionRequest( + textDocument: locRef.docIdentifier, + position: locRef.position + ) + ) guard case .locations(let jump) = response else { XCTFail("Response is not locations") return nil @@ -174,35 +217,51 @@ final class SKTests: XCTestCase { let loc = ws.testLoc("cc:A") try ws.openDocument(loc.url, language: .swift) - let results = try withExtendedLifetime(ws) { try ws.sk.sendSync( - CompletionRequest(textDocument: loc.docIdentifier, position: loc.position)) + let results = try withExtendedLifetime(ws) { + try ws.sk.sendSync( + CompletionRequest(textDocument: loc.docIdentifier, position: loc.position) + ) } - XCTAssertEqual(results.items, [ - CompletionItem( - label: "method(a: Int)", - kind: .method, - detail: "Void", - deprecated: false, sortText: nil, - filterText: "method(a:)", - insertText: "method(a: )", - insertTextFormat: .plain, - textEdit: .textEdit(TextEdit(range: Position(line: 1, utf16index: 14)..) in + ws.sk.appendOneShotNotificationHandler { (note: Notification) in // Semantic analysis: expect module import error. XCTAssertEqual(note.params.diagnostics.count, 1) if let diagnostic = note.params.diagnostics.first { - XCTAssert(diagnostic.message.contains("no such module"), - "expected module import error but found \"\(diagnostic.message)\"") + XCTAssert( + diagnostic.message.contains("no such module"), + "expected module import error but found \"\(diagnostic.message)\"" + ) } startExpectation.fulfill() } @@ -238,7 +299,7 @@ final class SKTests: XCTestCase { XCTAssertEqual(note.params.diagnostics.count, 1) finishExpectation.fulfill() } - ws.sk.appendOneShotNotificationHandler { (note: Notification) in + ws.sk.appendOneShotNotificationHandler { (note: Notification) in // Semantic analysis: no more errors expected, import should resolve since we built. XCTAssertEqual(note.params.diagnostics.count, 0) finishExpectation.fulfill() @@ -250,7 +311,7 @@ final class SKTests: XCTestCase { func testDependenciesUpdatedCXXTibs() async throws { guard let ws = try await mutableSourceKitTibsTestWorkspace(name: "GeneratedHeader") else { return } - defer { withExtendedLifetime(ws) {} } // Keep workspace alive for callbacks. + defer { withExtendedLifetime(ws) {} } // Keep workspace alive for callbacks. guard let server = ws.testServer.server else { XCTFail("Unable to fetch SourceKitServer to notify for build system events.") return @@ -266,7 +327,7 @@ final class SKTests: XCTestCase { } let generatedHeaderURL = moduleRef.url.deletingLastPathComponent() - .appendingPathComponent("lib-generated.h", isDirectory: false) + .appendingPathComponent("lib-generated.h", isDirectory: false) // Write an empty header file first since clangd doesn't handle missing header // files without a recently upstreamed extension. @@ -298,12 +359,14 @@ final class SKTests: XCTestCase { let mainLoc = ws.testLoc("Object:include:main") let expectedDoc = ws.testLoc("Object").docIdentifier.uri let includePosition = - Position(line: mainLoc.position.line, utf16index: mainLoc.utf16Column + 2) + Position(line: mainLoc.position.line, utf16index: mainLoc.utf16Column + 2) try ws.openDocument(mainLoc.url, language: .c) let goToInclude = DefinitionRequest( - textDocument: mainLoc.docIdentifier, position: includePosition) + textDocument: mainLoc.docIdentifier, + position: includePosition + ) let resp = try withExtendedLifetime(ws) { try ws.sk.sendSync(goToInclude) } let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-#include") @@ -332,7 +395,9 @@ final class SKTests: XCTestCase { try ws.openDocument(refLoc.url, language: .c) let goToDefinition = DefinitionRequest( - textDocument: refLoc.docIdentifier, position: refPos) + textDocument: refLoc.docIdentifier, + position: refPos + ) let resp = try withExtendedLifetime(ws) { try ws.sk.sendSync(goToDefinition) } let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-definition") @@ -357,12 +422,14 @@ final class SKTests: XCTestCase { let mainLoc = ws.testLoc("Object:ref:newObject") let expectedDoc = ws.testLoc("Object:decl:newObject").docIdentifier.uri let includePosition = - Position(line: mainLoc.position.line, utf16index: mainLoc.utf16Column + 2) + Position(line: mainLoc.position.line, utf16index: mainLoc.utf16Column + 2) try ws.openDocument(mainLoc.url, language: .c) let goToInclude = DeclarationRequest( - textDocument: mainLoc.docIdentifier, position: includePosition) + textDocument: mainLoc.docIdentifier, + position: includePosition + ) let resp = try ws.sk.sendSync(goToInclude) let locationsOrLinks = try XCTUnwrap(resp, "No response for go-to-declaration") diff --git a/Tests/SourceKitLSPTests/SwiftCompileCommandsTest.swift b/Tests/SourceKitLSPTests/SwiftCompileCommandsTest.swift index 04cb4de7b..b25201c88 100644 --- a/Tests/SourceKitLSPTests/SwiftCompileCommandsTest.swift +++ b/Tests/SourceKitLSPTests/SwiftCompileCommandsTest.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import SourceKitLSP import SKCore +import SourceKitLSP import XCTest final class SwiftCompileCommandsTest: XCTestCase { diff --git a/Tests/SourceKitLSPTests/SwiftCompletionTests.swift b/Tests/SourceKitLSPTests/SwiftCompletionTests.swift index 7bc451377..0e4e035d8 100644 --- a/Tests/SourceKitLSPTests/SwiftCompletionTests.swift +++ b/Tests/SourceKitLSPTests/SwiftCompletionTests.swift @@ -10,8 +10,8 @@ // //===----------------------------------------------------------------------===// -import LanguageServerProtocol import LSPTestSupport +import LanguageServerProtocol import SKTestSupport import SourceKitLSP import XCTest @@ -70,19 +70,26 @@ final class SwiftCompletionTests: XCTestCase { "completion": .dictionary([ "serverSideFiltering": .bool(options.serverSideFiltering), "maxResults": options.maxResults == nil ? .null : .int(options.maxResults!), - ]), + ]) ]), capabilities: ClientCapabilities(workspace: nil, textDocument: documentCapabilities), trace: .off, - workspaceFolders: nil)) + workspaceFolders: nil + ) + ) } func openDocument(text: String? = nil, url: URL) { - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: DocumentURI(url), - language: .swift, - version: 12, - text: text ?? self.text))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: DocumentURI(url), + language: .swift, + version: 12, + text: text ?? self.text + ) + ) + ) } func testCompletionClientFilter() throws { @@ -102,9 +109,12 @@ final class SwiftCompletionTests: XCTestCase { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") openDocument(url: url) - let selfDot = try sk.sendSync(CompletionRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 5, utf16index: 9))) + let selfDot = try sk.sendSync( + CompletionRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 5, utf16index: 9) + ) + ) XCTAssertEqual(selfDot.isIncomplete, options.serverSideFiltering) XCTAssertGreaterThanOrEqual(selfDot.items.count, 2) @@ -115,15 +125,21 @@ final class SwiftCompletionTests: XCTestCase { XCTAssertEqual(abc.detail, "Int") XCTAssertEqual(abc.documentation, .markupContent(MarkupContent(kind: .markdown, value: "Documentation for abc."))) XCTAssertEqual(abc.filterText, "abc") - XCTAssertEqual(abc.textEdit, .textEdit(TextEdit(range: Position(line: 5, utf16index: 9).. CompletionItem? { - let selfDot = try sk.sendSync(CompletionRequest( - textDocument: TextDocumentIdentifier(url), - position: position)) + let selfDot = try sk.sendSync( + CompletionRequest( + textDocument: TextDocumentIdentifier(url), + position: position + ) + ) return selfDot.items.first { $0.label == label } } @@ -174,7 +201,15 @@ final class SwiftCompletionTests: XCTestCase { XCTAssertEqual(test.kind, .method) XCTAssertEqual(test.detail, "Void") XCTAssertEqual(test.filterText, "test(a:)") - XCTAssertEqual(test.textEdit, .textEdit(TextEdit(range: Position(line: 5, utf16index: 9).. OK (20 is maxResults since we're outside the member completion) - XCTAssertEqual(20, try sk.sendSync(CompletionRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 7, utf16index: 0), - context:CompletionContext(triggerKind: .invoked))).items.count) + XCTAssertEqual( + 20, + try sk.sendSync( + CompletionRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 7, utf16index: 0), + context: CompletionContext(triggerKind: .invoked) + ) + ).items.count + ) } func testRefilterAfterIncompleteResultsWithEdits() throws { try initializeServer(options: SKCompletionOptions(serverSideFiltering: true, maxResults: nil)) let url = URL(fileURLWithPath: "/\(UUID())/a.swift") - openDocument(text: """ - struct S { - func fooAbc() {} - func fooBcd() {} - func fooCde() {} - func fooDef() {} - func fooGoop() {} - func test() { - self.fz + openDocument( + text: """ + struct S { + func fooAbc() {} + func fooBcd() {} + func fooCde() {} + func fooDef() {} + func fooGoop() {} + func test() { + self.fz + } } - } - """, url: url) + """, + url: url + ) // 'f' - XCTAssertEqual(5, countFs(try sk.sendSync(CompletionRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 7, utf16index: 10), - context:CompletionContext(triggerKind: .invoked))))) + XCTAssertEqual( + 5, + countFs( + try sk.sendSync( + CompletionRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 7, utf16index: 10), + context: CompletionContext(triggerKind: .invoked) + ) + ) + ) + ) // 'fz' - XCTAssertEqual(0, countFs(try sk.sendSync(CompletionRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 7, utf16index: 11), - context:CompletionContext(triggerKind: .triggerFromIncompleteCompletions))))) - - sk.send(DidChangeTextDocumentNotification( - textDocument: VersionedTextDocumentIdentifier(DocumentURI(url), version: 1), - contentChanges: [ - .init(range: Position(line: 7, utf16index: 10).. Int { - return response.items.filter{$0.label.hasPrefix("f")}.count + return response.items.filter { $0.label.hasPrefix("f") }.count } diff --git a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift index 27c62b9ce..5ea689eb5 100644 --- a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift +++ b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift @@ -12,9 +12,9 @@ import Foundation import ISDBTestSupport -import LanguageServerProtocol -import LSPTestSupport import LSPLogging +import LSPTestSupport +import LanguageServerProtocol import SKSupport import SKTestSupport import SourceKitLSP @@ -36,23 +36,29 @@ final class SwiftInterfaceTests: XCTestCase { // local module cache, we define away that source of bugs. connection = TestSourceKitServer(useGlobalModuleCache: false) sk = connection.client - _ = try! sk.sendSync(InitializeRequest( - processId: nil, - rootPath: nil, - rootURI: nil, - initializationOptions: nil, - capabilities: ClientCapabilities(workspace: nil, - textDocument: TextDocumentClientCapabilities( - codeAction: .init( - codeActionLiteralSupport: .init( - codeActionKind: .init(valueSet: [.quickFix]) - )), - publishDiagnostics: .init(codeDescriptionSupport: true) - )), - trace: .off, - workspaceFolders: nil)) + _ = try! sk.sendSync( + InitializeRequest( + processId: nil, + rootPath: nil, + rootURI: nil, + initializationOptions: nil, + capabilities: ClientCapabilities( + workspace: nil, + textDocument: TextDocumentClientCapabilities( + codeAction: .init( + codeActionLiteralSupport: .init( + codeActionKind: .init(valueSet: [.quickFix]) + ) + ), + publishDiagnostics: .init(codeDescriptionSupport: true) + ) + ), + trace: .off, + workspaceFolders: nil + ) + ) } - + override func tearDown() { sk = nil connection = nil @@ -62,17 +68,25 @@ final class SwiftInterfaceTests: XCTestCase { let url = URL(fileURLWithPath: "/\(UUID())/a.swift") let uri = DocumentURI(url) - sk.send(DidOpenTextDocumentNotification(textDocument: TextDocumentItem( - uri: uri, - language: .swift, - version: 1, - text: """ - import Foundation - """))) - - let _resp = try sk.sendSync(DefinitionRequest( - textDocument: TextDocumentIdentifier(url), - position: Position(line: 0, utf16index: 10))) + sk.send( + DidOpenTextDocumentNotification( + textDocument: TextDocumentItem( + uri: uri, + language: .swift, + version: 1, + text: """ + import Foundation + """ + ) + ) + ) + + let _resp = try sk.sendSync( + DefinitionRequest( + textDocument: TextDocumentIdentifier(url), + position: Position(line: 0, utf16index: 10) + ) + ) let resp = try XCTUnwrap(_resp) guard case .locations(let locations) = resp else { XCTFail("Unexpected response: \(resp)") @@ -83,9 +97,12 @@ final class SwiftInterfaceTests: XCTestCase { XCTAssertTrue(location.uri.pseudoPath.hasSuffix("/Foundation.swiftinterface")) let fileContents = try XCTUnwrap(location.uri.fileURL.flatMap({ try String(contentsOf: $0, encoding: .utf8) })) // Sanity-check that the generated Swift Interface contains Swift code - XCTAssert(fileContents.hasPrefix("import "), "Expected that the foundation swift interface starts with 'import ' but got '\(fileContents.prefix(100))'") + XCTAssert( + fileContents.hasPrefix("import "), + "Expected that the foundation swift interface starts with 'import ' but got '\(fileContents.prefix(100))'" + ) } - + func testOpenInterface() async throws { guard let ws = try await staticSourceKitSwiftPMWorkspace(name: "SwiftPMPackage") else { return } try ws.buildAndIndex() @@ -94,28 +111,37 @@ final class SwiftInterfaceTests: XCTestCase { let openInterface = OpenInterfaceRequest(textDocument: importedModule.docIdentifier, name: "lib", symbolUSR: nil) let interfaceDetails = try XCTUnwrap(ws.sk.sendSync(openInterface)) XCTAssertTrue(interfaceDetails.uri.pseudoPath.hasSuffix("/lib.swiftinterface")) - let fileContents = try XCTUnwrap(interfaceDetails.uri.fileURL.flatMap({ try String(contentsOf: $0, encoding: .utf8) })) - XCTAssertTrue(fileContents.contains(""" - public struct Lib { + let fileContents = try XCTUnwrap( + interfaceDetails.uri.fileURL.flatMap({ try String(contentsOf: $0, encoding: .utf8) }) + ) + XCTAssertTrue( + fileContents.contains( + """ + public struct Lib { - public func foo() + public func foo() - public init() - } - """)) + public init() + } + """ + ) + ) } - + /// Used by testDefinitionInSystemModuleInterface func testSystemSwiftInterface( - _ testLoc: TestLocation, - ws: SKSwiftPMTestWorkspace, - swiftInterfaceFile: String, + _ testLoc: TestLocation, + ws: SKSwiftPMTestWorkspace, + swiftInterfaceFile: String, linePrefix: String ) throws { try ws.openDocument(testLoc.url, language: .swift) - let definition = try ws.sk.sendSync(DefinitionRequest( - textDocument: testLoc.docIdentifier, - position: testLoc.position)) + let definition = try ws.sk.sendSync( + DefinitionRequest( + textDocument: testLoc.docIdentifier, + position: testLoc.position + ) + ) guard case .locations(let jump) = definition else { XCTFail("Response is not locations") return @@ -139,36 +165,39 @@ final class SwiftInterfaceTests: XCTestCase { // Test stdlib with one submodule try testSystemSwiftInterface( - stringRef, - ws: ws, - swiftInterfaceFile: "/Swift.String.swiftinterface", + stringRef, + ws: ws, + swiftInterfaceFile: "/Swift.String.swiftinterface", linePrefix: "@frozen public struct String" ) // Test stdlib with two submodules try testSystemSwiftInterface( - intRef, - ws: ws, - swiftInterfaceFile: "/Swift.Math.Integers.swiftinterface", + intRef, + ws: ws, + swiftInterfaceFile: "/Swift.Math.Integers.swiftinterface", linePrefix: "@frozen public struct Int" ) // Test concurrency try testSystemSwiftInterface( - withTaskGroupRef, - ws: ws, - swiftInterfaceFile: "/_Concurrency.swiftinterface", + withTaskGroupRef, + ws: ws, + swiftInterfaceFile: "/_Concurrency.swiftinterface", linePrefix: "@inlinable public func withTaskGroup" ) } - + func testSwiftInterfaceAcrossModules() async throws { guard let ws = try await staticSourceKitSwiftPMWorkspace(name: "SwiftPMPackage") else { return } try ws.buildAndIndex() let importedModule = ws.testLoc("lib:import") try ws.openDocument(importedModule.url, language: .swift) let _resp = try withExtendedLifetime(ws) { - try ws.sk.sendSync(DefinitionRequest( - textDocument: importedModule.docIdentifier, - position: importedModule.position)) + try ws.sk.sendSync( + DefinitionRequest( + textDocument: importedModule.docIdentifier, + position: importedModule.position + ) + ) } let resp = try XCTUnwrap(_resp) guard case .locations(let locations) = resp else { @@ -179,13 +208,17 @@ final class SwiftInterfaceTests: XCTestCase { let location = try XCTUnwrap(locations.first) XCTAssertTrue(location.uri.pseudoPath.hasSuffix("/lib.swiftinterface")) let fileContents = try XCTUnwrap(location.uri.fileURL.flatMap({ try String(contentsOf: $0, encoding: .utf8) })) - XCTAssertTrue(fileContents.contains(""" - public struct Lib { - - public func foo() - - public init() - } - """)) + XCTAssertTrue( + fileContents.contains( + """ + public struct Lib { + + public func foo() + + public init() + } + """ + ) + ) } } diff --git a/Tests/SourceKitLSPTests/SwiftPMIntegration.swift b/Tests/SourceKitLSPTests/SwiftPMIntegration.swift index 2a2529a17..347bfe93d 100644 --- a/Tests/SourceKitLSPTests/SwiftPMIntegration.swift +++ b/Tests/SourceKitLSPTests/SwiftPMIntegration.swift @@ -23,39 +23,57 @@ final class SwiftPMIntegrationTests: XCTestCase { let call = ws.testLoc("Lib.foo:call") let def = ws.testLoc("Lib.foo:def") try ws.openDocument(call.url, language: .swift) - let refs = try ws.sk.sendSync(ReferencesRequest(textDocument: call.docIdentifier, position: call.position, context: ReferencesContext(includeDeclaration: true))) + let refs = try ws.sk.sendSync( + ReferencesRequest( + textDocument: call.docIdentifier, + position: call.position, + context: ReferencesContext(includeDeclaration: true) + ) + ) - XCTAssertEqual(Set(refs), [ - Location(call), - Location(def), - ]) + XCTAssertEqual( + Set(refs), + [ + Location(call), + Location(def), + ] + ) let completions = try withExtendedLifetime(ws) { - try ws.sk.sendSync(CompletionRequest(textDocument: call.docIdentifier, position: call.position)) + try ws.sk.sendSync(CompletionRequest(textDocument: call.docIdentifier, position: call.position)) } - XCTAssertEqual(completions.items, [ - CompletionItem( - label: "foo()", - kind: .method, - detail: "Void", - deprecated: false, - sortText: nil, - filterText: "foo()", - insertText: "foo()", - insertTextFormat: .plain, - textEdit: .textEdit(TextEdit(range: Position(line: 2, utf16index: 24).. TestLocation { ws.testLoc(name) - } + } func loc(_ name: String) -> Location { Location(badUTF16: ws.testLoc(name)) @@ -84,7 +84,12 @@ final class TypeHierarchyTests: XCTestCase { ) } - func item(_ name: String, _ kind: SymbolKind, detail: String = "main", at locName: String) throws -> TypeHierarchyItem { + func item( + _ name: String, + _ kind: SymbolKind, + detail: String = "main", + at locName: String + ) throws -> TypeHierarchyItem { let location = loc(locName) return TypeHierarchyItem( name: name, @@ -99,72 +104,123 @@ final class TypeHierarchyTests: XCTestCase { // Test type hierarchy preparation - assertEqualIgnoringData(try typeHierarchy(at: testLoc("P")), [ - try item("P", .interface, at: "P"), - ]) - assertEqualIgnoringData(try typeHierarchy(at: testLoc("A")), [ - try item("A", .class, at: "A"), - ]) - assertEqualIgnoringData(try typeHierarchy(at: testLoc("S")), [ - try item("S", .struct, at: "S"), - ]) - assertEqualIgnoringData(try typeHierarchy(at: testLoc("E")), [ - try item("E", .enum, at: "E"), - ]) + assertEqualIgnoringData( + try typeHierarchy(at: testLoc("P")), + [ + try item("P", .interface, at: "P") + ] + ) + assertEqualIgnoringData( + try typeHierarchy(at: testLoc("A")), + [ + try item("A", .class, at: "A") + ] + ) + assertEqualIgnoringData( + try typeHierarchy(at: testLoc("S")), + [ + try item("S", .struct, at: "S") + ] + ) + assertEqualIgnoringData( + try typeHierarchy(at: testLoc("E")), + [ + try item("E", .enum, at: "E") + ] + ) // Test supertype hierarchy assertEqualIgnoringData(try supertypes(at: testLoc("A")), []) - assertEqualIgnoringData(try supertypes(at: testLoc("B")), [ - try item("A", .class, at: "A"), - try item("P", .interface, at: "P"), - ]) - assertEqualIgnoringData(try supertypes(at: testLoc("C")), [ - try item("B", .class, at: "B"), - ]) - assertEqualIgnoringData(try supertypes(at: testLoc("D")), [ - try item("A", .class, at: "A"), - ]) - assertEqualIgnoringData(try supertypes(at: testLoc("S")), [ - try item("P", .interface, at: "P"), - try item("X", .interface, at: "X"), // Retroactive conformance - ]) - assertEqualIgnoringData(try supertypes(at: testLoc("E")), [ - try item("P", .interface, at: "P"), - try item("Y", .interface, at: "Y"), // Retroactive conformance - try item("Z", .interface, at: "Z"), // Retroactive conformance - ]) + assertEqualIgnoringData( + try supertypes(at: testLoc("B")), + [ + try item("A", .class, at: "A"), + try item("P", .interface, at: "P"), + ] + ) + assertEqualIgnoringData( + try supertypes(at: testLoc("C")), + [ + try item("B", .class, at: "B") + ] + ) + assertEqualIgnoringData( + try supertypes(at: testLoc("D")), + [ + try item("A", .class, at: "A") + ] + ) + assertEqualIgnoringData( + try supertypes(at: testLoc("S")), + [ + try item("P", .interface, at: "P"), + try item("X", .interface, at: "X"), // Retroactive conformance + ] + ) + assertEqualIgnoringData( + try supertypes(at: testLoc("E")), + [ + try item("P", .interface, at: "P"), + try item("Y", .interface, at: "Y"), // Retroactive conformance + try item("Z", .interface, at: "Z"), // Retroactive conformance + ] + ) // Test subtype hierarchy (includes extensions) - assertEqualIgnoringData(try subtypes(at: testLoc("A")), [ - try item("B", .class, at: "B"), - try item("D", .class, at: "D"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("B")), [ - try item("C", .class, at: "C"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("P")), [ - try item("B", .class, at: "B"), - try item("S", .struct, at: "S"), - try item("E", .enum, at: "E"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("E")), [ - try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("S")), [ - try item("S: X", .null, detail: "Extension at a.swift:15", at: "extS:X"), - try item("S", .null, detail: "Extension at a.swift:16", at: "extS"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("X")), [ - try item("S: X", .null, detail: "Extension at a.swift:15", at: "extS:X"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("Y")), [ - try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z"), - ]) - assertEqualIgnoringData(try subtypes(at: testLoc("Z")), [ - try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z"), - ]) + assertEqualIgnoringData( + try subtypes(at: testLoc("A")), + [ + try item("B", .class, at: "B"), + try item("D", .class, at: "D"), + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("B")), + [ + try item("C", .class, at: "C") + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("P")), + [ + try item("B", .class, at: "B"), + try item("S", .struct, at: "S"), + try item("E", .enum, at: "E"), + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("E")), + [ + try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z") + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("S")), + [ + try item("S: X", .null, detail: "Extension at a.swift:15", at: "extS:X"), + try item("S", .null, detail: "Extension at a.swift:16", at: "extS"), + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("X")), + [ + try item("S: X", .null, detail: "Extension at a.swift:15", at: "extS:X") + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("Y")), + [ + try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z") + ] + ) + assertEqualIgnoringData( + try subtypes(at: testLoc("Z")), + [ + try item("E: Y, Z", .null, detail: "Extension at a.swift:19", at: "extE:Y,Z") + ] + ) // Ensure that type hierarchies can be fetched from uses too diff --git a/Tests/SourceKitLSPTests/WorkspaceTests.swift b/Tests/SourceKitLSPTests/WorkspaceTests.swift index 5ab4055c9..619f9e9c2 100644 --- a/Tests/SourceKitLSPTests/WorkspaceTests.swift +++ b/Tests/SourceKitLSPTests/WorkspaceTests.swift @@ -11,11 +11,11 @@ //===----------------------------------------------------------------------===// import Foundation -import LanguageServerProtocol import LSPTestSupport -import SourceKitLSP +import LanguageServerProtocol import SKCore import SKTestSupport +import SourceKitLSP import TSCBasic import XCTest @@ -25,7 +25,8 @@ final class WorkspaceTests: XCTestCase { guard let ws = try await staticSourceKitSwiftPMWorkspace(name: "SwiftPMPackage") else { return } try ws.buildAndIndex() - guard let otherWs = try await staticSourceKitSwiftPMWorkspace(name: "OtherSwiftPMPackage", server: ws.testServer) else { return } + guard let otherWs = try await staticSourceKitSwiftPMWorkspace(name: "OtherSwiftPMPackage", server: ws.testServer) + else { return } try otherWs.buildAndIndex() assert(ws.testServer === otherWs.testServer, "Sanity check: The two workspaces should be opened in the same server") @@ -36,64 +37,83 @@ final class WorkspaceTests: XCTestCase { try ws.openDocument(call.url, language: .swift) let completions = try withExtendedLifetime(ws) { - try ws.sk.sendSync(CompletionRequest(textDocument: call.docIdentifier, position: call.position)) + try ws.sk.sendSync(CompletionRequest(textDocument: call.docIdentifier, position: call.position)) } - XCTAssertEqual(completions.items, [ - CompletionItem( - label: "foo()", - kind: .method, - detail: "Void", - deprecated: false, - sortText: nil, - filterText: "foo()", - insertText: "foo()", - insertTextFormat: .plain, - textEdit: .textEdit(TextEdit(range: Position(line: 2, utf16index: 24)..