Allow specification of SourceKitLSPOptions in the initialize request

This allows editors to provide UI elements to toggle SourceKit-LSP options.

# Conflicts:
#	Sources/SourceKitLSP/Swift/SwiftLanguageService.swift
This commit is contained in:
Alex Hoppen
2024-06-28 07:21:13 +02:00
parent 68bff7f216
commit 0fa7ffcee3
6 changed files with 92 additions and 3 deletions

View File

@@ -4,6 +4,7 @@
- `~/.sourcekit-lsp/config.json`
- On macOS: `~/Library/Application Support/org.swift.sourcekit-lsp/config.json` from the various `Library` folders on the system
- If the `XDG_CONFIG_HOME` environment variable is set: `$XDG_CONFIG_HOME/org.swift.sourcekit-lsp/config.json`
- Initialization options passed in the initialize request
- A `.sourcekit-lsp/config.json` file in a workspaces root
The structure of the file is currently not guaranteed to be stable. Options may be removed or renamed.

View File

@@ -12,7 +12,7 @@
import Foundation
import LSPLogging
import SKCore
import LanguageServerProtocol
import SKSupport
import struct TSCBasic.AbsolutePath
@@ -250,6 +250,21 @@ public struct SourceKitLSPOptions: Sendable, Codable {
self.workDoneProgressDebounceDuration = workDoneProgressDebounceDuration
}
public init?(fromLSPAny lspAny: LSPAny?) throws {
guard let lspAny else {
return nil
}
let jsonEncoded = try JSONEncoder().encode(lspAny)
self = try JSONDecoder().decode(Self.self, from: jsonEncoded)
}
public var asLSPAny: LSPAny {
get throws {
let jsonEncoded = try JSONEncoder().encode(self)
return try JSONDecoder().decode(LSPAny.self, from: jsonEncoded)
}
}
public init?(path: URL?) {
guard let path, let contents = try? String(contentsOf: path, encoding: .utf8) else {
return nil

View File

@@ -80,6 +80,7 @@ public class MultiFileTestProject {
public init(
files: [RelativeFileLocation: String],
workspaces: (URL) async throws -> [WorkspaceFolder] = { [WorkspaceFolder(uri: DocumentURI($0))] },
initializationOptions: LSPAny? = nil,
capabilities: ClientCapabilities = ClientCapabilities(),
options: SourceKitLSPOptions = .testDefault(),
testHooks: TestHooks = TestHooks(),
@@ -118,6 +119,7 @@ public class MultiFileTestProject {
self.testClient = try await TestSourceKitLSPClient(
options: options,
testHooks: testHooks,
initializationOptions: initializationOptions,
capabilities: capabilities,
usePullDiagnostics: usePullDiagnostics,
enableBackgroundIndexing: enableBackgroundIndexing,

View File

@@ -150,6 +150,7 @@ public class SwiftPMTestProject: MultiFileTestProject {
files: [RelativeFileLocation: String],
manifest: String = SwiftPMTestProject.defaultPackageManifest,
workspaces: (URL) async throws -> [WorkspaceFolder] = { [WorkspaceFolder(uri: DocumentURI($0))] },
initializationOptions: LSPAny? = nil,
capabilities: ClientCapabilities = ClientCapabilities(),
options: SourceKitLSPOptions = .testDefault(),
testHooks: TestHooks = TestHooks(),
@@ -190,6 +191,7 @@ public class SwiftPMTestProject: MultiFileTestProject {
try await super.init(
files: filesByPath,
workspaces: workspaces,
initializationOptions: initializationOptions,
capabilities: capabilities,
options: options,
testHooks: testHooks,

View File

@@ -104,7 +104,7 @@ public actor SourceKitLSPServer {
/// This ensures that we only inform the user about background indexing not being supported for these projects once.
private var didSendBackgroundIndexingNotSupportedNotification = false
let options: SourceKitLSPOptions
var options: SourceKitLSPOptions
let testHooks: TestHooks
@@ -959,6 +959,10 @@ extension SourceKitLSPServer {
func initialize(_ req: InitializeRequest) async throws -> InitializeResult {
capabilityRegistry = CapabilityRegistry(clientCapabilities: req.capabilities)
self.options = SourceKitLSPOptions.merging(
base: self.options,
override: orLog("Parsing SourceKitLSPOptions", { try SourceKitLSPOptions(fromLSPAny: req.initializationOptions) })
)
await workspaceQueue.async { [testHooks] in
if let workspaceFolders = req.workspaceFolders {
@@ -983,12 +987,13 @@ extension SourceKitLSPServer {
if self.workspaces.isEmpty {
logger.error("No workspace found")
let options = self.options
let workspace = await Workspace(
documentManager: self.documentManager,
rootUri: req.rootURI,
capabilityRegistry: self.capabilityRegistry!,
toolchainRegistry: self.toolchainRegistry,
options: self.options,
options: options,
testHooks: testHooks,
underlyingBuildSystem: nil,
index: nil,

View File

@@ -861,4 +861,68 @@ final class WorkspaceTests: XCTestCase {
}
XCTAssertEqual(diagnostics.items.map(\.message), ["Cannot convert value of type 'Int' to specified type 'String'"])
}
func testOptionsInInitializeRequest() async throws {
let project = try await SwiftPMTestProject(
files: [
"Test.swift": """
func test() {
#if TEST
let x: String = 1
#endif
}
"""
],
initializationOptions: SourceKitLSPOptions(
swiftPM: SourceKitLSPOptions.SwiftPMOptions(swiftCompilerFlags: ["-D", "TEST"])
).asLSPAny
)
let (uri, _) = try project.openDocument("Test.swift")
let diagnostics = try await project.testClient.send(
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
)
guard case .full(let diagnostics) = diagnostics else {
XCTFail("Expected full diagnostics")
return
}
XCTAssertEqual(diagnostics.items.map(\.message), ["Cannot convert value of type 'Int' to specified type 'String'"])
}
func testWorkspaceOptionsOverrideGlobalOptions() async throws {
let project = try await SwiftPMTestProject(
files: [
"/.sourcekit-lsp/config.json": """
{
"swiftPM": {
"swiftCompilerFlags": ["-D", "TEST"]
}
}
""",
"Test.swift": """
func test() {
#if TEST
let x: String = 1
#endif
#if OTHER
let x: String = 1.0
#endif
}
""",
],
initializationOptions: SourceKitLSPOptions(
swiftPM: SourceKitLSPOptions.SwiftPMOptions(swiftCompilerFlags: ["-D", "OTHER"])
).asLSPAny
)
let (uri, _) = try project.openDocument("Test.swift")
let diagnostics = try await project.testClient.send(
DocumentDiagnosticsRequest(textDocument: TextDocumentIdentifier(uri))
)
guard case .full(let diagnostics) = diagnostics else {
XCTFail("Expected full diagnostics")
return
}
XCTAssertEqual(diagnostics.items.map(\.message), ["Cannot convert value of type 'Int' to specified type 'String'"])
}
}