mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
396 lines
15 KiB
Swift
396 lines
15 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Foundation
|
|
import LSPLogging
|
|
import LanguageServerProtocol
|
|
import SKSupport
|
|
|
|
import struct TSCBasic.AbsolutePath
|
|
|
|
/// Options that can be used to modify SourceKit-LSP's behavior.
|
|
///
|
|
/// See `ConfigurationFile.md` for a description of the configuration file's behavior.
|
|
public struct SourceKitLSPOptions: Sendable, Codable {
|
|
public struct SwiftPMOptions: Sendable, Codable {
|
|
/// Build configuration (debug|release).
|
|
///
|
|
/// Equivalent to SwiftPM's `--configuration` option.
|
|
public var configuration: BuildConfiguration?
|
|
|
|
/// Build artifacts directory path. If nil, the build system may choose a default value.
|
|
///
|
|
/// Equivalent to SwiftPM's `--scratch-path` option.
|
|
public var scratchPath: String?
|
|
|
|
/// Equivalent to SwiftPM's `--swift-sdks-path` option
|
|
public var swiftSDKsDirectory: String?
|
|
|
|
/// Equivalent to SwiftPM's `--swift-sdk` option
|
|
public var swiftSDK: String?
|
|
|
|
/// Equivalent to SwiftPM's `--triple` option
|
|
public var triple: String?
|
|
|
|
/// Equivalent to SwiftPM's `-Xcc` option
|
|
public var cCompilerFlags: [String]?
|
|
|
|
/// Equivalent to SwiftPM's `-Xcxx` option
|
|
public var cxxCompilerFlags: [String]?
|
|
|
|
/// Equivalent to SwiftPM's `-Xswiftc` option
|
|
public var swiftCompilerFlags: [String]?
|
|
|
|
/// Equivalent to SwiftPM's `-Xlinker` option
|
|
public var linkerFlags: [String]?
|
|
|
|
public init(
|
|
configuration: BuildConfiguration? = nil,
|
|
scratchPath: String? = nil,
|
|
swiftSDKsDirectory: String? = nil,
|
|
swiftSDK: String? = nil,
|
|
triple: String? = nil,
|
|
cCompilerFlags: [String]? = nil,
|
|
cxxCompilerFlags: [String]? = nil,
|
|
swiftCompilerFlags: [String]? = nil,
|
|
linkerFlags: [String]? = nil
|
|
) {
|
|
self.configuration = configuration
|
|
self.scratchPath = scratchPath
|
|
self.swiftSDKsDirectory = swiftSDKsDirectory
|
|
self.swiftSDK = swiftSDK
|
|
self.triple = triple
|
|
self.cCompilerFlags = cCompilerFlags
|
|
self.cxxCompilerFlags = cxxCompilerFlags
|
|
self.swiftCompilerFlags = swiftCompilerFlags
|
|
self.linkerFlags = linkerFlags
|
|
}
|
|
|
|
static func merging(base: SwiftPMOptions, override: SwiftPMOptions?) -> SwiftPMOptions {
|
|
return SwiftPMOptions(
|
|
configuration: override?.configuration ?? base.configuration,
|
|
scratchPath: override?.scratchPath ?? base.scratchPath,
|
|
swiftSDKsDirectory: override?.swiftSDKsDirectory ?? base.swiftSDKsDirectory,
|
|
swiftSDK: override?.swiftSDK ?? base.swiftSDK,
|
|
triple: override?.triple ?? base.triple,
|
|
cCompilerFlags: override?.cCompilerFlags ?? base.cCompilerFlags,
|
|
cxxCompilerFlags: override?.cxxCompilerFlags ?? base.cxxCompilerFlags,
|
|
swiftCompilerFlags: override?.swiftCompilerFlags ?? base.swiftCompilerFlags,
|
|
linkerFlags: override?.linkerFlags ?? base.linkerFlags
|
|
)
|
|
}
|
|
}
|
|
|
|
public struct CompilationDatabaseOptions: Sendable, Codable {
|
|
/// Additional paths to search for a compilation database, relative to a workspace root.
|
|
public var searchPaths: [String]?
|
|
|
|
public init(searchPaths: [String]? = nil) {
|
|
self.searchPaths = searchPaths
|
|
}
|
|
|
|
static func merging(
|
|
base: CompilationDatabaseOptions,
|
|
override: CompilationDatabaseOptions?
|
|
) -> CompilationDatabaseOptions {
|
|
return CompilationDatabaseOptions(searchPaths: override?.searchPaths ?? base.searchPaths)
|
|
}
|
|
}
|
|
|
|
public struct FallbackBuildSystemOptions: Sendable, Codable {
|
|
public var cCompilerFlags: [String]?
|
|
public var cxxCompilerFlags: [String]?
|
|
public var swiftCompilerFlags: [String]?
|
|
|
|
public init(
|
|
cCompilerFlags: [String]? = nil,
|
|
cxxCompilerFlags: [String]? = nil,
|
|
swiftCompilerFlags: [String]? = nil
|
|
) {
|
|
self.cCompilerFlags = cCompilerFlags
|
|
self.cxxCompilerFlags = cxxCompilerFlags
|
|
self.swiftCompilerFlags = swiftCompilerFlags
|
|
}
|
|
|
|
static func merging(
|
|
base: FallbackBuildSystemOptions,
|
|
override: FallbackBuildSystemOptions?
|
|
) -> FallbackBuildSystemOptions {
|
|
return FallbackBuildSystemOptions(
|
|
cCompilerFlags: override?.cCompilerFlags ?? base.cCompilerFlags,
|
|
cxxCompilerFlags: override?.cxxCompilerFlags ?? base.cxxCompilerFlags,
|
|
swiftCompilerFlags: override?.swiftCompilerFlags ?? base.swiftCompilerFlags
|
|
)
|
|
}
|
|
}
|
|
|
|
public struct IndexOptions: Sendable, Codable {
|
|
public var indexStorePath: String?
|
|
public var indexDatabasePath: String?
|
|
public var indexPrefixMap: [String: String]?
|
|
public var maxCoresPercentageToUseForBackgroundIndexing: Double?
|
|
public var updateIndexStoreTimeout: Int?
|
|
|
|
public var maxCoresPercentageToUseForBackgroundIndexingOrDefault: Double {
|
|
return maxCoresPercentageToUseForBackgroundIndexing ?? 1
|
|
}
|
|
|
|
public var updateIndexStoreTimeoutOrDefault: Duration {
|
|
if let updateIndexStoreTimeout {
|
|
.seconds(updateIndexStoreTimeout)
|
|
} else {
|
|
.seconds(120)
|
|
}
|
|
}
|
|
|
|
public init(
|
|
indexStorePath: String? = nil,
|
|
indexDatabasePath: String? = nil,
|
|
indexPrefixMap: [String: String]? = nil,
|
|
maxCoresPercentageToUseForBackgroundIndexing: Double? = nil,
|
|
updateIndexStoreTimeout: Int? = nil
|
|
) {
|
|
self.indexStorePath = indexStorePath
|
|
self.indexDatabasePath = indexDatabasePath
|
|
self.indexPrefixMap = indexPrefixMap
|
|
self.maxCoresPercentageToUseForBackgroundIndexing = maxCoresPercentageToUseForBackgroundIndexing
|
|
self.updateIndexStoreTimeout = updateIndexStoreTimeout
|
|
}
|
|
|
|
static func merging(base: IndexOptions, override: IndexOptions?) -> IndexOptions {
|
|
return IndexOptions(
|
|
indexStorePath: override?.indexStorePath ?? base.indexStorePath,
|
|
indexDatabasePath: override?.indexDatabasePath ?? base.indexDatabasePath,
|
|
indexPrefixMap: override?.indexPrefixMap ?? base.indexPrefixMap,
|
|
maxCoresPercentageToUseForBackgroundIndexing: override?.maxCoresPercentageToUseForBackgroundIndexing
|
|
?? base.maxCoresPercentageToUseForBackgroundIndexing,
|
|
updateIndexStoreTimeout: override?.updateIndexStoreTimeout ?? base.updateIndexStoreTimeout
|
|
)
|
|
}
|
|
}
|
|
|
|
public enum BackgroundPreparationMode: String {
|
|
/// Build a target to prepare it
|
|
case build
|
|
|
|
/// Prepare a target without generating object files but do not do lazy type checking.
|
|
///
|
|
/// This uses SwiftPM's `--experimental-prepare-for-indexing-no-lazy` flag.
|
|
case noLazy
|
|
|
|
/// Prepare a target without generating object files.
|
|
case enabled
|
|
}
|
|
|
|
public var swiftPM: SwiftPMOptions
|
|
public var compilationDatabase: CompilationDatabaseOptions
|
|
public var fallbackBuildSystem: FallbackBuildSystemOptions
|
|
public var clangdOptions: [String]?
|
|
public var index: IndexOptions
|
|
|
|
/// Default workspace type (buildserver|compdb|swiftpm). Overrides workspace type selection logic.
|
|
public var defaultWorkspaceType: WorkspaceType?
|
|
public var generatedFilesPath: String?
|
|
|
|
/// Whether background indexing is enabled.
|
|
public var backgroundIndexing: Bool?
|
|
|
|
public var backgroundIndexingOrDefault: Bool {
|
|
return backgroundIndexing ?? false
|
|
}
|
|
|
|
public var backgroundPreparationMode: String?
|
|
|
|
public var backgroundPreparationModeOrDefault: BackgroundPreparationMode {
|
|
if let backgroundPreparationMode, let parsed = BackgroundPreparationMode(rawValue: backgroundPreparationMode) {
|
|
return parsed
|
|
}
|
|
return .build
|
|
}
|
|
|
|
/// Experimental features that are enabled.
|
|
public var experimentalFeatures: Set<ExperimentalFeature>? = nil
|
|
|
|
/// The time that `SwiftLanguageService` should wait after an edit before starting to compute diagnostics and
|
|
/// sending a `PublishDiagnosticsNotification`.
|
|
///
|
|
/// This is mostly intended for testing purposes so we don't need to wait the debouncing time to get a diagnostics
|
|
/// notification when running unit tests.
|
|
public var swiftPublishDiagnosticsDebounceDuration: Double? = nil
|
|
|
|
public var swiftPublishDiagnosticsDebounceDurationOrDefault: Duration {
|
|
if let swiftPublishDiagnosticsDebounceDuration {
|
|
return .seconds(swiftPublishDiagnosticsDebounceDuration)
|
|
}
|
|
return .seconds(2)
|
|
}
|
|
|
|
/// When a task is started that should be displayed to the client as a work done progress, how many milliseconds to
|
|
/// wait before actually starting the work done progress. This prevents flickering of the work done progress in the
|
|
/// client for short-lived index tasks which end within this duration.
|
|
public var workDoneProgressDebounceDuration: Double? = nil
|
|
|
|
public var workDoneProgressDebounceDurationOrDefault: Duration {
|
|
if let workDoneProgressDebounceDuration {
|
|
return .seconds(workDoneProgressDebounceDuration)
|
|
}
|
|
return .seconds(1)
|
|
}
|
|
|
|
/// The maximum duration that a sourcekitd request should be allowed to execute before being declared as timed out.
|
|
///
|
|
/// In general, editors should cancel requests that they are no longer interested in, but in case editors don't cancel
|
|
/// requests, this ensures that a long-running non-cancelled request is not blocking sourcekitd and thus most semantic
|
|
/// functionality.
|
|
///
|
|
/// In particular, VS Code does not cancel the semantic tokens request, which can cause a long-running AST build that
|
|
/// blocks sourcekitd.
|
|
public var sourcekitdRequestTimeout: Double? = nil
|
|
|
|
public var sourcekitdRequestTimeoutOrDefault: Duration {
|
|
if let sourcekitdRequestTimeout {
|
|
return .seconds(sourcekitdRequestTimeout)
|
|
}
|
|
return .seconds(120)
|
|
}
|
|
|
|
public init(
|
|
swiftPM: SwiftPMOptions = .init(),
|
|
fallbackBuildSystem: FallbackBuildSystemOptions = .init(),
|
|
compilationDatabase: CompilationDatabaseOptions = .init(),
|
|
clangdOptions: [String]? = nil,
|
|
index: IndexOptions = .init(),
|
|
defaultWorkspaceType: WorkspaceType? = nil,
|
|
generatedFilesPath: String? = nil,
|
|
backgroundIndexing: Bool? = nil,
|
|
backgroundPreparationMode: String? = nil,
|
|
experimentalFeatures: Set<ExperimentalFeature>? = nil,
|
|
swiftPublishDiagnosticsDebounceDuration: Double? = nil,
|
|
workDoneProgressDebounceDuration: Double? = nil,
|
|
sourcekitdRequestTimeout: Double? = nil
|
|
) {
|
|
self.swiftPM = swiftPM
|
|
self.fallbackBuildSystem = fallbackBuildSystem
|
|
self.compilationDatabase = compilationDatabase
|
|
self.clangdOptions = clangdOptions
|
|
self.index = index
|
|
self.generatedFilesPath = generatedFilesPath
|
|
self.defaultWorkspaceType = defaultWorkspaceType
|
|
self.backgroundIndexing = backgroundIndexing
|
|
self.backgroundPreparationMode = backgroundPreparationMode
|
|
self.experimentalFeatures = experimentalFeatures
|
|
self.swiftPublishDiagnosticsDebounceDuration = swiftPublishDiagnosticsDebounceDuration
|
|
self.workDoneProgressDebounceDuration = workDoneProgressDebounceDuration
|
|
self.sourcekitdRequestTimeout = sourcekitdRequestTimeout
|
|
}
|
|
|
|
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
|
|
}
|
|
guard
|
|
let decoded = orLog(
|
|
"Parsing config.json",
|
|
{ try JSONDecoder().decode(Self.self, from: contents) }
|
|
)
|
|
else {
|
|
return nil
|
|
}
|
|
self = decoded
|
|
}
|
|
|
|
public static func merging(base: SourceKitLSPOptions, override: SourceKitLSPOptions?) -> SourceKitLSPOptions {
|
|
return SourceKitLSPOptions(
|
|
swiftPM: SwiftPMOptions.merging(base: base.swiftPM, override: override?.swiftPM),
|
|
fallbackBuildSystem: FallbackBuildSystemOptions.merging(
|
|
base: base.fallbackBuildSystem,
|
|
override: override?.fallbackBuildSystem
|
|
),
|
|
compilationDatabase: CompilationDatabaseOptions.merging(
|
|
base: base.compilationDatabase,
|
|
override: override?.compilationDatabase
|
|
),
|
|
clangdOptions: override?.clangdOptions ?? base.clangdOptions,
|
|
index: IndexOptions.merging(base: base.index, override: override?.index),
|
|
defaultWorkspaceType: override?.defaultWorkspaceType ?? base.defaultWorkspaceType,
|
|
generatedFilesPath: override?.generatedFilesPath ?? base.generatedFilesPath,
|
|
backgroundIndexing: override?.backgroundIndexing ?? base.backgroundIndexing,
|
|
backgroundPreparationMode: override?.backgroundPreparationMode ?? base.backgroundPreparationMode,
|
|
experimentalFeatures: override?.experimentalFeatures ?? base.experimentalFeatures,
|
|
swiftPublishDiagnosticsDebounceDuration: override?.swiftPublishDiagnosticsDebounceDuration
|
|
?? base.swiftPublishDiagnosticsDebounceDuration,
|
|
workDoneProgressDebounceDuration: override?.workDoneProgressDebounceDuration
|
|
?? base.workDoneProgressDebounceDuration,
|
|
sourcekitdRequestTimeout: override?.sourcekitdRequestTimeout ?? base.sourcekitdRequestTimeout
|
|
)
|
|
}
|
|
|
|
public var generatedFilesAbsolutePath: AbsolutePath {
|
|
if let absolutePath = AbsolutePath(validatingOrNil: generatedFilesPath) {
|
|
return absolutePath
|
|
}
|
|
return defaultDirectoryForGeneratedFiles
|
|
}
|
|
|
|
public func hasExperimentalFeature(_ feature: ExperimentalFeature) -> Bool {
|
|
guard let experimentalFeatures else {
|
|
return false
|
|
}
|
|
return experimentalFeatures.contains(feature)
|
|
}
|
|
|
|
public init(from decoder: any Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
|
|
self.swiftPM = try container.decodeIfPresent(SwiftPMOptions.self, forKey: CodingKeys.swiftPM) ?? .init()
|
|
self.compilationDatabase =
|
|
try container.decodeIfPresent(CompilationDatabaseOptions.self, forKey: CodingKeys.compilationDatabase) ?? .init()
|
|
self.fallbackBuildSystem =
|
|
try container.decodeIfPresent(FallbackBuildSystemOptions.self, forKey: CodingKeys.fallbackBuildSystem) ?? .init()
|
|
self.clangdOptions = try container.decodeIfPresent([String].self, forKey: CodingKeys.clangdOptions)
|
|
self.index = try container.decodeIfPresent(IndexOptions.self, forKey: CodingKeys.index) ?? .init()
|
|
self.defaultWorkspaceType = try container.decodeIfPresent(
|
|
WorkspaceType.self,
|
|
forKey: CodingKeys.defaultWorkspaceType
|
|
)
|
|
self.generatedFilesPath = try container.decodeIfPresent(String.self, forKey: CodingKeys.generatedFilesPath)
|
|
self.backgroundIndexing = try container.decodeIfPresent(Bool.self, forKey: CodingKeys.backgroundIndexing)
|
|
self.experimentalFeatures = try container.decodeIfPresent(
|
|
Set<ExperimentalFeature>.self,
|
|
forKey: CodingKeys.experimentalFeatures
|
|
)
|
|
self.swiftPublishDiagnosticsDebounceDuration = try container.decodeIfPresent(
|
|
Double.self,
|
|
forKey: CodingKeys.swiftPublishDiagnosticsDebounceDuration
|
|
)
|
|
self.workDoneProgressDebounceDuration = try container.decodeIfPresent(
|
|
Double.self,
|
|
forKey: CodingKeys.workDoneProgressDebounceDuration
|
|
)
|
|
}
|
|
}
|