From eb6f919b5e934887a0c44c6bb530c3a8e53c0416 Mon Sep 17 00:00:00 2001 From: Kai Lau Date: Thu, 27 Jun 2024 22:45:47 -0700 Subject: [PATCH] removed OpenGeneratedInterfaceRequest deleted OpenInterfaceRequest.swift and moved GeneratedInterfaceDetails to LanguageService.swift replaced usages by passing its members as parameters directly, refactored usage of TextDocumentIdentifier to DocumentURI removed from Messages.builtinRequests removed from SwiftInterfaceTests removed from Documentation/`LSP Extensions.md` removed from Sources/LanguageServerProtocol/CMakeLists.txt --- Documentation/LSP Extensions.md | 92 +++++++++++++++---- Sources/LanguageServerProtocol/CMakeLists.txt | 1 - Sources/LanguageServerProtocol/Messages.swift | 1 - .../Requests/OpenInterfaceRequest.swift | 57 ------------ .../Clang/ClangLanguageService.swift | 7 +- Sources/SourceKitLSP/LanguageService.swift | 26 +++++- Sources/SourceKitLSP/SourceKitLSPServer.swift | 29 +++--- .../SourceKitLSP/Swift/OpenInterface.swift | 38 +++++--- .../SwiftInterfaceTests.swift | 49 ---------- 9 files changed, 151 insertions(+), 149 deletions(-) delete mode 100644 Sources/LanguageServerProtocol/Requests/OpenInterfaceRequest.swift diff --git a/Documentation/LSP Extensions.md b/Documentation/LSP Extensions.md index fb6c5a97f..bd777a94b 100644 --- a/Documentation/LSP Extensions.md +++ b/Documentation/LSP Extensions.md @@ -73,39 +73,99 @@ Added case identifier = 'identifier' ``` -## `textDocument/interface` +## `WorkspaceFolder` -New request that request a textual interface of a module to display in the IDE. Used internally within SourceKit-LSP +Added field: + +```ts +/** + * Build settings that should be used for this workspace. + * + * For arguments that have a single value (like the build configuration), this takes precedence over the global + * options set when launching sourcekit-lsp. For all other options, the values specified in the workspace-specific + * build setup are appended to the global options. + */ +var buildSetup?: WorkspaceBuildSetup +``` -- params: `OpenInterfaceParams` -- result: `InterfaceDetails?` +with ```ts -export interface OpenInterfaceRequest: TextDocumentRequest, Hashable { +/** + * The configuration to build a workspace in. + */ +export enum BuildConfiguration { + case debug = 'debug' + case release = 'release' +} + +/** + * The type of workspace; default workspace type selection logic can be overridden. + */ +export enum WorkspaceType { + buildServer = 'buildServer' + compilationDatabase = 'compilationDatabase' + swiftPM = 'swiftPM' +} + +/// Build settings that should be used for a workspace. +interface WorkspaceBuildSetup { /** - * The document whose compiler arguments should be used to generate the interface. + * The configuration that the workspace should be built in. */ - textDocument: TextDocumentIdentifier + buildConfiguration?: BuildConfiguration /** - * The module to generate an index for. + * The default workspace type to use for this workspace. */ - moduleName: string + defaultWorkspaceType?: WorkspaceType /** - * The module group name. + * The build directory for the workspace. */ - groupName?: string + scratchPath?: DocumentURI + + /** + * Arguments to be passed to any C compiler invocations. + */ + cFlags?: string[] + + /** + * Arguments to be passed to any C++ compiler invocations. + */ + cxxFlags?: string[] + + /** + * Arguments to be passed to any linker invocations. + */ + linkerFlags?: string[] /** - * The symbol USR to search for in the generated module interface. + * Arguments to be passed to any Swift compiler invocations. */ - symbolUSR?: string + swiftFlags?: string[] } +``` + +## `textDocument/completion` + +Added field: + +```ts +/** + * Options to control code completion behavior in Swift + */ +sourcekitlspOptions?: SKCompletionOptions +``` + +with -interface InterfaceDetails { - uri: DocumentURI - position?: Position +```ts +interface SKCompletionOptions { + /** + * The maximum number of completion results to return, or `null` for unlimited. + */ + maxResults?: int } ``` diff --git a/Sources/LanguageServerProtocol/CMakeLists.txt b/Sources/LanguageServerProtocol/CMakeLists.txt index e3f16ce7d..2ccbef4d3 100644 --- a/Sources/LanguageServerProtocol/CMakeLists.txt +++ b/Sources/LanguageServerProtocol/CMakeLists.txt @@ -66,7 +66,6 @@ add_library(LanguageServerProtocol STATIC Requests/InlineValueRequest.swift Requests/LinkedEditingRangeRequest.swift Requests/MonikersRequest.swift - Requests/OpenInterfaceRequest.swift Requests/PollIndexRequest.swift Requests/PrepareRenameRequest.swift Requests/ReferencesRequest.swift diff --git a/Sources/LanguageServerProtocol/Messages.swift b/Sources/LanguageServerProtocol/Messages.swift index 6252992da..3440d67d3 100644 --- a/Sources/LanguageServerProtocol/Messages.swift +++ b/Sources/LanguageServerProtocol/Messages.swift @@ -58,7 +58,6 @@ public let builtinRequests: [_RequestType.Type] = [ InlineValueRequest.self, LinkedEditingRangeRequest.self, MonikersRequest.self, - OpenGeneratedInterfaceRequest.self, PollIndexRequest.self, PrepareRenameRequest.self, ReferencesRequest.self, diff --git a/Sources/LanguageServerProtocol/Requests/OpenInterfaceRequest.swift b/Sources/LanguageServerProtocol/Requests/OpenInterfaceRequest.swift deleted file mode 100644 index 2d353cb6b..000000000 --- a/Sources/LanguageServerProtocol/Requests/OpenInterfaceRequest.swift +++ /dev/null @@ -1,57 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -/// Request a generated interface of a module to display in the IDE. -/// **(LSP Extension)** -public struct OpenGeneratedInterfaceRequest: TextDocumentRequest, Hashable { - public static let method: String = "textDocument/openInterface" - public typealias Response = GeneratedInterfaceDetails? - - /// The document whose compiler arguments should be used to generate the interface. - public var textDocument: TextDocumentIdentifier - - /// The module to generate an index for. - public var moduleName: String - - /// The module group name. - public var groupName: String? - - /// The symbol USR to search for in the generated module interface. - public var symbolUSR: String? - - public init(textDocument: TextDocumentIdentifier, name: String, groupName: String?, symbolUSR: String?) { - self.textDocument = textDocument - self.symbolUSR = symbolUSR - self.moduleName = name - self.groupName = groupName - } - - /// Name of interface module name with group names appended - public var name: String { - if let groupName { - return "\(self.moduleName).\(groupName.replacing("/", with: "."))" - } - return self.moduleName - } -} - -/// The textual output of a module interface. -public struct GeneratedInterfaceDetails: ResponseType, Hashable { - - public var uri: DocumentURI - public var position: Position? - - public init(uri: DocumentURI, position: Position?) { - self.uri = uri - self.position = position - } -} diff --git a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift index 0fcd6b614..c4c17a153 100644 --- a/Sources/SourceKitLSP/Clang/ClangLanguageService.swift +++ b/Sources/SourceKitLSP/Clang/ClangLanguageService.swift @@ -623,7 +623,12 @@ extension ClangLanguageService { return try await forwardRequestToClangd(req) } - func openGeneratedInterface(_ request: OpenGeneratedInterfaceRequest) async throws -> GeneratedInterfaceDetails? { + func openGeneratedInterface( + document: DocumentURI, + moduleName: String, + groupName: String?, + symbolUSR symbol: String? + ) async throws -> GeneratedInterfaceDetails? { throw ResponseError.unknown("unsupported method") } diff --git a/Sources/SourceKitLSP/LanguageService.swift b/Sources/SourceKitLSP/LanguageService.swift index 1b2429e37..9580fac17 100644 --- a/Sources/SourceKitLSP/LanguageService.swift +++ b/Sources/SourceKitLSP/LanguageService.swift @@ -76,6 +76,17 @@ public struct RenameLocation: Sendable { let usage: Usage } +/// The textual output of a module interface. +public struct GeneratedInterfaceDetails: ResponseType, Hashable { + public var uri: DocumentURI + public var position: Position? + + public init(uri: DocumentURI, position: Position?) { + self.uri = uri + self.position = position + } +} + /// Provides language specific functionality to sourcekit-lsp from a specific toolchain. /// /// For example, we may have a language service that provides semantic functionality for c-family using a clangd server, @@ -147,7 +158,20 @@ public protocol LanguageService: AnyObject, Sendable { func completion(_ req: CompletionRequest) async throws -> CompletionList func hover(_ req: HoverRequest) async throws -> HoverResponse? func symbolInfo(_ request: SymbolInfoRequest) async throws -> [SymbolDetails] - func openGeneratedInterface(_ request: OpenGeneratedInterfaceRequest) async throws -> GeneratedInterfaceDetails? + + /// Request a generated interface of a module to display in the IDE. + /// + /// - Parameters: + /// - document: The document whose compiler arguments should be used to generate the interface. + /// - moduleName: The module to generate an index for. + /// - groupName: The module group name. + /// - symbol: The symbol USR to search for in the generated module interface. + func openGeneratedInterface( + document: DocumentURI, + moduleName: String, + groupName: String?, + symbolUSR symbol: String? + ) async throws -> GeneratedInterfaceDetails? /// - Note: Only called as a fallback if the definition could not be found in the index. func definition(_ request: DefinitionRequest) async throws -> LocationsOrLocationLinksResponse? diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index 14d81646c..4bc2f78c7 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -743,8 +743,6 @@ extension SourceKitLSPServer: MessageHandler { initialized = true case let request as RequestAndReply: await self.handleRequest(for: request, requestHandler: self.inlayHint) - case let request as RequestAndReply: - await self.handleRequest(for: request, requestHandler: self.openGeneratedInterface) case let request as RequestAndReply: await request.reply { try await pollIndex(request.params) } case let request as RequestAndReply: @@ -1434,11 +1432,19 @@ extension SourceKitLSPServer { } func openGeneratedInterface( - _ req: OpenGeneratedInterfaceRequest, + document: DocumentURI, + moduleName: String, + groupName: String?, + symbolUSR symbol: String?, workspace: Workspace, languageService: LanguageService ) async throws -> GeneratedInterfaceDetails? { - return try await languageService.openGeneratedInterface(req) + return try await languageService.openGeneratedInterface( + document: document, + moduleName: moduleName, + groupName: groupName, + symbolUSR: symbol + ) } /// Find all symbols in the workspace that include a string in their name. @@ -1842,13 +1848,14 @@ extension SourceKitLSPServer { originatorUri: DocumentURI, languageService: LanguageService ) async throws -> Location { - let openInterface = OpenGeneratedInterfaceRequest( - textDocument: TextDocumentIdentifier(originatorUri), - name: moduleName, - groupName: groupName, - symbolUSR: symbolUSR - ) - guard let interfaceDetails = try await languageService.openGeneratedInterface(openInterface) else { + guard + let interfaceDetails = try await languageService.openGeneratedInterface( + document: originatorUri, + moduleName: moduleName, + groupName: groupName, + symbolUSR: symbolUSR + ) + else { throw ResponseError.unknown("Could not generate Swift Interface for \(moduleName)") } let position = interfaceDetails.position ?? Position(line: 0, utf16index: 0) diff --git a/Sources/SourceKitLSP/Swift/OpenInterface.swift b/Sources/SourceKitLSP/Swift/OpenInterface.swift index b81480d52..f3761eee3 100644 --- a/Sources/SourceKitLSP/Swift/OpenInterface.swift +++ b/Sources/SourceKitLSP/Swift/OpenInterface.swift @@ -22,22 +22,34 @@ struct GeneratedInterfaceInfo { extension SwiftLanguageService { public func openGeneratedInterface( - _ request: OpenGeneratedInterfaceRequest + document: DocumentURI, + moduleName: String, + groupName: String?, + symbolUSR symbol: String? ) async throws -> GeneratedInterfaceDetails? { - let name = request.name - let symbol = request.symbolUSR + // Name of interface module name with group names appended + let name = + if let groupName { + "\(moduleName).\(groupName.replacing("/", with: "."))" + } else { + moduleName + } let interfaceFilePath = self.generatedInterfacesPath.appendingPathComponent("\(name).swiftinterface") let interfaceDocURI = DocumentURI(interfaceFilePath) // has interface already been generated if let snapshot = try? self.documentManager.latestSnapshot(interfaceDocURI) { return await self.generatedInterfaceDetails( - request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol ) } else { - let interfaceInfo = try await self.generatedInterfaceInfo(request: request, interfaceURI: interfaceDocURI) + let interfaceInfo = try await self.generatedInterfaceInfo( + document: document, + moduleName: moduleName, + groupName: groupName, + interfaceURI: interfaceDocURI + ) try interfaceInfo.contents.write(to: interfaceFilePath, atomically: true, encoding: String.Encoding.utf8) let snapshot = DocumentSnapshot( uri: interfaceDocURI, @@ -46,7 +58,6 @@ extension SwiftLanguageService { lineTable: LineTable(interfaceInfo.contents) ) let result = await self.generatedInterfaceDetails( - request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol @@ -61,23 +72,27 @@ extension SwiftLanguageService { /// Open the Swift interface for a module. /// /// - Parameters: - /// - request: The OpenGeneratedInterfaceRequest. + /// - document: The document whose compiler arguments should be used to generate the interface. + /// - moduleName: The module to generate an index for. + /// - groupName: The module group name. /// - interfaceURI: The file where the generated interface should be written. /// /// - Important: This opens a document with name `interfaceURI.pseudoPath` in sourcekitd. The caller is responsible /// for ensuring that the document will eventually get closed in sourcekitd again. private func generatedInterfaceInfo( - request: OpenGeneratedInterfaceRequest, + document: DocumentURI, + moduleName: String, + groupName: String?, interfaceURI: DocumentURI ) async throws -> GeneratedInterfaceInfo { let keys = self.keys let skreq = sourcekitd.dictionary([ keys.request: requests.editorOpenInterface, - keys.moduleName: request.moduleName, - keys.groupName: request.groupName, + keys.moduleName: moduleName, + keys.groupName: groupName, keys.name: interfaceURI.pseudoPath, keys.synthesizedExtension: 1, - keys.compilerArgs: await self.buildSettings(for: request.textDocument.uri)?.compilerArgs as [SKDRequestValue]?, + keys.compilerArgs: await self.buildSettings(for: document)?.compilerArgs as [SKDRequestValue]?, ]) let dict = try await self.sourcekitd.send(skreq, fileContents: nil) @@ -85,7 +100,6 @@ extension SwiftLanguageService { } private func generatedInterfaceDetails( - request: OpenGeneratedInterfaceRequest, uri: DocumentURI, snapshot: DocumentSnapshot, symbol: String? diff --git a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift index 5168574e3..d6582a018 100644 --- a/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift +++ b/Tests/SourceKitLSPTests/SwiftInterfaceTests.swift @@ -49,55 +49,6 @@ final class SwiftInterfaceTests: XCTestCase { ) } - func testOpenInterface() async throws { - let project = try await SwiftPMTestProject( - files: [ - "MyLibrary/MyLibrary.swift": """ - public struct Lib { - public func foo() {} - public init() {} - } - """, - "Exec/main.swift": "import MyLibrary", - ], - manifest: """ - let package = Package( - name: "MyLibrary", - targets: [ - .target(name: "MyLibrary"), - .executableTarget(name: "Exec", dependencies: ["MyLibrary"]) - ] - ) - """, - enableBackgroundIndexing: true - ) - - let (mainUri, _) = try project.openDocument("main.swift") - let openInterface = OpenGeneratedInterfaceRequest( - textDocument: TextDocumentIdentifier(mainUri), - name: "MyLibrary", - groupName: nil, - symbolUSR: nil - ) - let interfaceDetails = try unwrap(await project.testClient.send(openInterface)) - XCTAssert(interfaceDetails.uri.pseudoPath.hasSuffix("/MyLibrary.swiftinterface")) - let fileContents = try XCTUnwrap( - interfaceDetails.uri.fileURL.flatMap({ try String(contentsOf: $0, encoding: .utf8) }) - ) - XCTAssertTrue( - fileContents.contains( - """ - public struct Lib { - - public func foo() - - public init() - } - """ - ) - ) - } - func testDefinitionInSystemModuleInterface() async throws { let project = try await IndexedSingleSwiftFileTestProject( """