From eb304c4759489c523bf5c30ec99a9f641c7a8bef Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Sat, 1 Jun 2024 08:35:31 -0700 Subject: [PATCH] Add a general notion of experimental features to sourcekit-lsp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Background indexing probably won’t be the last experimental feature in sourcekit-lsp that we want to gate behind a feature flag. Instead of adding new parameters ad-hoc, introduce a general notion of experimental features. --- Sources/Diagnose/IndexCommand.swift | 2 +- .../TestSourceKitLSPClient.swift | 4 ++- Sources/SourceKitLSP/CMakeLists.txt | 1 + Sources/SourceKitLSP/CreateBuildSystem.swift | 2 +- .../SourceKitLSP/ExperimentalFeatures.swift | 26 +++++++++++++++++++ .../SourceKitLSP/IndexProgressManager.swift | 2 +- .../SourceKitLSPServer+Options.swift | 5 ++++ Sources/SourceKitLSP/SourceKitLSPServer.swift | 2 +- Sources/SourceKitLSP/Workspace.swift | 19 ++------------ Sources/sourcekit-lsp/SourceKitLSP.swift | 22 ++++++++-------- 10 files changed, 52 insertions(+), 33 deletions(-) create mode 100644 Sources/SourceKitLSP/ExperimentalFeatures.swift diff --git a/Sources/Diagnose/IndexCommand.swift b/Sources/Diagnose/IndexCommand.swift index a00872d29..9923a922d 100644 --- a/Sources/Diagnose/IndexCommand.swift +++ b/Sources/Diagnose/IndexCommand.swift @@ -75,7 +75,7 @@ public struct IndexCommand: AsyncParsableCommand { public func run() async throws { var serverOptions = SourceKitLSPServer.Options() - serverOptions.indexOptions.enableBackgroundIndexing = true + serverOptions.experimentalFeatures.append(.backgroundIndexing) let installPath = if let toolchainOverride, let toolchain = Toolchain(try AbsolutePath(validating: toolchainOverride)) { diff --git a/Sources/SKTestSupport/TestSourceKitLSPClient.swift b/Sources/SKTestSupport/TestSourceKitLSPClient.swift index 83915ad10..71f57c798 100644 --- a/Sources/SKTestSupport/TestSourceKitLSPClient.swift +++ b/Sources/SKTestSupport/TestSourceKitLSPClient.swift @@ -117,7 +117,9 @@ public final class TestSourceKitLSPClient: MessageHandler { if let moduleCache { serverOptions.buildSetup.flags.swiftCompilerFlags += ["-module-cache-path", moduleCache.path] } - serverOptions.indexOptions.enableBackgroundIndexing = enableBackgroundIndexing + if enableBackgroundIndexing { + serverOptions.experimentalFeatures.append(.backgroundIndexing) + } var notificationYielder: AsyncStream.Continuation! self.notifications = AsyncStream { continuation in diff --git a/Sources/SourceKitLSP/CMakeLists.txt b/Sources/SourceKitLSP/CMakeLists.txt index a76fd4faf..875c3d4e5 100644 --- a/Sources/SourceKitLSP/CMakeLists.txt +++ b/Sources/SourceKitLSP/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(SourceKitLSP STATIC CreateBuildSystem.swift DocumentManager.swift DocumentSnapshot+FromFileContents.swift + ExperimentalFeatures.swift IndexProgressManager.swift IndexStoreDB+MainFilesProvider.swift LanguageServerType.swift diff --git a/Sources/SourceKitLSP/CreateBuildSystem.swift b/Sources/SourceKitLSP/CreateBuildSystem.swift index 9eb963419..4b8d0b375 100644 --- a/Sources/SourceKitLSP/CreateBuildSystem.swift +++ b/Sources/SourceKitLSP/CreateBuildSystem.swift @@ -38,7 +38,7 @@ func createBuildSystem( url: rootUrl, toolchainRegistry: toolchainRegistry, buildSetup: options.buildSetup, - isForIndexBuild: options.indexOptions.enableBackgroundIndexing, + isForIndexBuild: options.experimentalFeatures.contains(.backgroundIndexing), reloadPackageStatusCallback: reloadPackageStatusCallback ) } diff --git a/Sources/SourceKitLSP/ExperimentalFeatures.swift b/Sources/SourceKitLSP/ExperimentalFeatures.swift new file mode 100644 index 000000000..ae0a677fd --- /dev/null +++ b/Sources/SourceKitLSP/ExperimentalFeatures.swift @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +/// An experimental feature that can be enabled by passing `--experimental-feature` to `sourcekit-lsp` on the command +/// line. The raw value of this feature is how it is named on the command line. +public enum ExperimentalFeature: String, Codable, Sendable, CaseIterable { + /// Enable background indexing. + case backgroundIndexing = "background-indexing" + + /// Show the files that are currently being indexed / the targets that are currently being prepared in the work done + /// progress. + /// + /// This is an option because VS Code tries to render a multi-line work done progress into a single line text field in + /// the status bar, which looks broken. But at the same time, it is very useful to get a feeling about what's + /// currently happening indexing-wise. + case showActivePreparationTasksInProgress = "show-active-preparation-tasks-in-progress" +} diff --git a/Sources/SourceKitLSP/IndexProgressManager.swift b/Sources/SourceKitLSP/IndexProgressManager.swift index 72d53ffa9..17ae4599d 100644 --- a/Sources/SourceKitLSP/IndexProgressManager.swift +++ b/Sources/SourceKitLSP/IndexProgressManager.swift @@ -99,7 +99,7 @@ actor IndexProgressManager { // Clip the finished tasks to 0 because showing a negative number there looks stupid. let finishedTasks = max(queuedIndexTasks - indexTasks.count, 0) message = "\(finishedTasks) / \(queuedIndexTasks)" - if await sourceKitLSPServer.options.indexOptions.showActivePreparationTasksInProgress { + if await sourceKitLSPServer.options.experimentalFeatures.contains(.showActivePreparationTasksInProgress) { var inProgressTasks: [String] = [] inProgressTasks += preparationTasks.filter { $0.value == .executing } .map { "- Preparing \($0.key.targetID)" } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer+Options.swift b/Sources/SourceKitLSP/SourceKitLSPServer+Options.swift index 402013d20..78dc82d02 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer+Options.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer+Options.swift @@ -49,6 +49,9 @@ extension SourceKitLSPServer { /// notification when running unit tests. public var swiftPublishDiagnosticsDebounceDuration: TimeInterval + /// Experimental features that are enabled. + public var experimentalFeatures: [ExperimentalFeature] + public var indexTestHooks: IndexTestHooks public init( @@ -59,6 +62,7 @@ extension SourceKitLSPServer { completionOptions: SKCompletionOptions = .init(), generatedInterfacesPath: AbsolutePath = defaultDirectoryForGeneratedInterfaces, swiftPublishDiagnosticsDebounceDuration: TimeInterval = 2, /* 2s */ + experimentalFeatures: [ExperimentalFeature] = [], indexTestHooks: IndexTestHooks = IndexTestHooks() ) { self.buildSetup = buildSetup @@ -68,6 +72,7 @@ extension SourceKitLSPServer { self.completionOptions = completionOptions self.generatedInterfacesPath = generatedInterfacesPath self.swiftPublishDiagnosticsDebounceDuration = swiftPublishDiagnosticsDebounceDuration + self.experimentalFeatures = experimentalFeatures self.indexTestHooks = indexTestHooks } } diff --git a/Sources/SourceKitLSP/SourceKitLSPServer.swift b/Sources/SourceKitLSP/SourceKitLSPServer.swift index e883345c7..3d251f7ae 100644 --- a/Sources/SourceKitLSP/SourceKitLSPServer.swift +++ b/Sources/SourceKitLSP/SourceKitLSPServer.swift @@ -941,7 +941,7 @@ extension SourceKitLSPServer { self?.indexProgressManager.indexProgressStatusDidChange() } ) - if let workspace, options.indexOptions.enableBackgroundIndexing, workspace.semanticIndexManager == nil, + if let workspace, options.experimentalFeatures.contains(.backgroundIndexing), workspace.semanticIndexManager == nil, !self.didSendBackgroundIndexingNotSupportedNotification { self.sendNotificationToClient( diff --git a/Sources/SourceKitLSP/Workspace.swift b/Sources/SourceKitLSP/Workspace.swift index 9d7e999d3..43672de55 100644 --- a/Sources/SourceKitLSP/Workspace.swift +++ b/Sources/SourceKitLSP/Workspace.swift @@ -108,7 +108,7 @@ public final class Workspace: Sendable { mainFilesProvider: uncheckedIndex, toolchainRegistry: toolchainRegistry ) - if options.indexOptions.enableBackgroundIndexing, + if options.experimentalFeatures.contains(.backgroundIndexing), let uncheckedIndex, await buildSystemManager.supportsPreparation { @@ -247,37 +247,22 @@ public struct IndexOptions: Sendable { /// explicit calls to pollForUnitChangesAndWait(). public var listenToUnitEvents: Bool - /// Whether background indexing should be enabled. - public var enableBackgroundIndexing: Bool - /// The percentage of the machine's cores that should at most be used for background indexing. /// /// Setting this to a value < 1 ensures that background indexing doesn't use all CPU resources. public var maxCoresPercentageToUseForBackgroundIndexing: Double - /// Whether to show the files that are currently being indexed / the targets that are currently being prepared in the - /// work done progress. - /// - /// This is an option because VS Code tries to render a multi-line work done progress into a single line text field in - /// the status bar, which looks broken. But at the same time, it is very useful to get a feeling about what's - /// currently happening indexing-wise. - public var showActivePreparationTasksInProgress: Bool - public init( indexStorePath: AbsolutePath? = nil, indexDatabasePath: AbsolutePath? = nil, indexPrefixMappings: [PathPrefixMapping]? = nil, listenToUnitEvents: Bool = true, - enableBackgroundIndexing: Bool = false, - maxCoresPercentageToUseForBackgroundIndexing: Double = 1, - showActivePreparationTasksInProgress: Bool = false + maxCoresPercentageToUseForBackgroundIndexing: Double = 1 ) { self.indexStorePath = indexStorePath self.indexDatabasePath = indexDatabasePath self.indexPrefixMappings = indexPrefixMappings self.listenToUnitEvents = listenToUnitEvents - self.enableBackgroundIndexing = enableBackgroundIndexing self.maxCoresPercentageToUseForBackgroundIndexing = maxCoresPercentageToUseForBackgroundIndexing - self.showActivePreparationTasksInProgress = showActivePreparationTasksInProgress } } diff --git a/Sources/sourcekit-lsp/SourceKitLSP.swift b/Sources/sourcekit-lsp/SourceKitLSP.swift index 0cece17c0..467be6b8c 100644 --- a/Sources/sourcekit-lsp/SourceKitLSP.swift +++ b/Sources/sourcekit-lsp/SourceKitLSP.swift @@ -201,18 +201,14 @@ struct SourceKitLSP: AsyncParsableCommand { ) var completionMaxResults = 200 - @Flag( - help: "Enable background indexing. This feature is still under active development and may be incomplete." - ) - var experimentalEnableBackgroundIndexing = false - - @Flag( + @Option( + name: .customLong("experimental-feature"), help: """ - When reporting index progress, show the currently running index tasks in addition to the task's count. \ - This produces a multi-line work done progress, which might render incorrectly, depending on the editor. + Enable an experimental sourcekit-lsp feature. + Available features are: \(ExperimentalFeature.allCases.map(\.rawValue).joined(separator: ", ")) """ ) - var experimentalShowActivePreparationTasksInProgress = false + var experimentalFeatures: [ExperimentalFeature] func mapOptions() -> SourceKitLSPServer.Options { var serverOptions = SourceKitLSPServer.Options() @@ -229,8 +225,6 @@ struct SourceKitLSP: AsyncParsableCommand { serverOptions.indexOptions.indexStorePath = indexStorePath serverOptions.indexOptions.indexDatabasePath = indexDatabasePath serverOptions.indexOptions.indexPrefixMappings = indexPrefixMappings - serverOptions.indexOptions.enableBackgroundIndexing = experimentalEnableBackgroundIndexing - serverOptions.indexOptions.showActivePreparationTasksInProgress = experimentalShowActivePreparationTasksInProgress serverOptions.completionOptions.maxResults = completionMaxResults serverOptions.generatedInterfacesPath = generatedInterfacesPath @@ -285,3 +279,9 @@ struct SourceKitLSP: AsyncParsableCommand { try await Task.sleep(for: .seconds(60 * 60 * 24 * 365 * 10)) } } + +#if compiler(>=6) +extension ExperimentalFeature: @retroactive ExpressibleByArgument {} +#else +extension ExperimentalFeature: ExpressibleByArgument {} +#endif