Skip to content
Open
22 changes: 21 additions & 1 deletion Sources/BuildServerIntegration/BuildServerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1557,7 +1557,7 @@ package actor BuildServerManager: QueueBasedMessageHandler {
}
}

package func testFiles() async throws -> [DocumentURI] {
package func projectTestFiles() async throws -> [DocumentURI] {
return try await sourceFiles(includeNonBuildableFiles: false).compactMap { (uri, info) -> DocumentURI? in
guard info.isPartOfRootProject, info.mayContainTests else {
return nil
Expand All @@ -1566,6 +1566,26 @@ package actor BuildServerManager: QueueBasedMessageHandler {
}
}

/// Differs from `sourceFiles(in targets: Set<BuildTargetIdentifier>)` making sure it only includes source files that
/// are part of the root project for cases where we don't care about dependency source files
///
/// - Parameter include: If `nil` will include all targets, otherwise only return files who are part of at least one matching target
/// - Returns: List of filtered source files in root project
package func projectSourceFiles(
in include: Set<BuildTargetIdentifier>? = nil
) async throws -> [DocumentURI: SourceFileInfo] {
return try await sourceFiles(includeNonBuildableFiles: false).filter { (uri, info) -> Bool in
var includeTarget = true
if let include {
includeTarget = info.targets.contains(anyIn: include)
}
guard info.isPartOfRootProject, includeTarget else {
return false
}
return true
}
}

private func watchedFilesReferencing(mainFiles: Set<DocumentURI>) -> Set<DocumentURI> {
return Set(
watchedFiles.compactMap { (watchedFile, mainFileAndLanguage) in
Expand Down
9 changes: 8 additions & 1 deletion Sources/ClangLanguageService/ClangLanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,14 @@ extension ClangLanguageService {
return nil
}

package static func syntacticTestItems(in uri: DocumentURI) async -> [AnnotatedTestItem] {
package func syntacticTestItems(for snapshot: DocumentSnapshot) async -> [AnnotatedTestItem] {
return []
}

package func syntacticPlaygrounds(
for snapshot: DocumentSnapshot,
in workspace: Workspace
) async -> [TextDocumentPlayground] {
return []
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,14 @@ package actor DocumentationLanguageService: LanguageService, Sendable {
// The DocumentationLanguageService does not do anything with document events
}

package static func syntacticTestItems(in uri: DocumentURI) async -> [AnnotatedTestItem] {
package func syntacticTestItems(for snapshot: DocumentSnapshot) async -> [AnnotatedTestItem] {
return []
}

package func syntacticPlaygrounds(
for snapshot: DocumentSnapshot,
in workspace: Workspace
) async -> [TextDocumentPlayground] {
return []
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/SKTestSupport/SwiftPMTestProject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
}
logger.debug(
"""
'swift build' output:
'swift build' output:
\(output)
"""
)
Expand All @@ -288,7 +288,7 @@ package class SwiftPMTestProject: MultiFileTestProject {
}
logger.debug(
"""
'swift package resolve' output:
'swift package resolve' output:
\(output)
"""
)
Expand Down
9 changes: 7 additions & 2 deletions Sources/SKTestSupport/TestSourceKitLSPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
/// The connection via which the server sends requests and notifications to us.
private let serverToClientConnection: LocalConnection

/// The response of the initialize request.
///
/// Must only be set from the initializer and not be accessed before the initializer has finished.
package private(set) nonisolated(unsafe) var initializeResult: InitializeResult?

/// Stream of the notifications that the server has sent to the client.
private let notifications: PendingNotifications

Expand Down Expand Up @@ -200,8 +205,8 @@ package final class TestSourceKitLSPClient: MessageHandler, Sendable {
preInitialization?(self)
if initialize {
let capabilities = capabilities
try await withTimeout(defaultTimeoutDuration) {
_ = try await self.send(
self.initializeResult = try await withTimeout(defaultTimeoutDuration) {
try await self.send(
InitializeRequest(
processId: nil,
rootPath: nil,
Expand Down
3 changes: 2 additions & 1 deletion Sources/SourceKitLSP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_library(SourceKitLSP STATIC
MacroExpansionReferenceDocumentURLData.swift
MessageHandlingDependencyTracker.swift
OnDiskDocumentManager.swift
PlaygroundDiscovery.swift
ReferenceDocumentURL.swift
Rename.swift
SemanticTokensLegend+SourceKitLSPLegend.swift
Expand All @@ -22,7 +23,7 @@ add_library(SourceKitLSP STATIC
SourceKitLSPCommandMetadata.swift
SourceKitLSPServer.swift
SymbolLocation+DocumentURI.swift
SyntacticTestIndex.swift
SyntacticIndex.swift
TestDiscovery.swift
TextEdit+IsNoop.swift
Workspace.swift
Expand Down
10 changes: 9 additions & 1 deletion Sources/SourceKitLSP/LanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,15 @@ package protocol LanguageService: AnyObject, Sendable {
/// Does not write the results to the index.
///
/// The order of the returned tests is not defined. The results should be sorted before being returned to the editor.
static func syntacticTestItems(in uri: DocumentURI) async -> [AnnotatedTestItem]
func syntacticTestItems(for snapshot: DocumentSnapshot) async -> [AnnotatedTestItem]

/// Returns the syntactically scanned playgrounds declared within the workspace.
///
/// The order of the returned playgrounds is not defined. The results should be sorted before being returned to the editor.
func syntacticPlaygrounds(
for snapshot: DocumentSnapshot,
in workspace: Workspace
) async -> [TextDocumentPlayground]

/// A position that is canonical for all positions within a declaration. For example, if we have the following
/// declaration, then all `|` markers should return the same canonical position.
Expand Down
2 changes: 2 additions & 0 deletions Sources/SourceKitLSP/MessageHandlingDependencyTracker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ package enum MessageHandlingDependencyTracker: QueueBasedMessageHandlerDependenc
self = .freestanding
case is WorkspaceTestsRequest:
self = .workspaceRequest
case is WorkspacePlaygroundsRequest:
self = .workspaceRequest
case let request as any TextDocumentRequest:
self = .documentRequest(request.textDocument.uri)
default:
Expand Down
43 changes: 43 additions & 0 deletions Sources/SourceKitLSP/PlaygroundDiscovery.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2025 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 BuildServerIntegration
@_spi(SourceKitLSP) import LanguageServerProtocol
@_spi(SourceKitLSP) import SKLogging
import SemanticIndex
import SwiftExtensions
@_spi(SourceKitLSP) import ToolsProtocolsSwiftExtensions

extension SourceKitLSPServer {

/// Return all the playgrounds in the given workspace.
///
/// The returned list of playgrounds is not sorted. It should be sorted before being returned to the editor.
private func playgrounds(in workspace: Workspace) async -> [Playground] {
// If files have recently been added to the workspace (which is communicated by a `workspace/didChangeWatchedFiles`
// notification, wait these changes to be reflected in the build server so we can include the updated files in the
// playgrounds.
await workspace.buildServerManager.waitForUpToDateBuildGraph()

let playgroundsFromSyntacticIndex = await workspace.syntacticIndex.playgrounds()

// We don't need to sort the playgrounds here because they will get sorted by `workspacePlaygrounds` request handler
return playgroundsFromSyntacticIndex
}

func workspacePlaygrounds(_ req: WorkspacePlaygroundsRequest) async throws -> [Playground] {
return await self.workspaces
.concurrentMap { await self.playgrounds(in: $0) }
.flatMap { $0 }
.sorted { $0.location < $1.location }
}
}
5 changes: 5 additions & 0 deletions Sources/SourceKitLSP/SourceKitLSPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,8 @@ extension SourceKitLSPServer: QueueBasedMessageHandler {
await request.reply { try await workspaceSymbols(request.params) }
case let request as RequestAndReply<WorkspaceTestsRequest>:
await request.reply { try await workspaceTests(request.params) }
case let request as RequestAndReply<WorkspacePlaygroundsRequest>:
await request.reply { try await workspacePlaygrounds(request.params) }
// IMPORTANT: When adding a new entry to this switch, also add it to the `MessageHandlingDependencyTracker` initializer.
default:
await request.reply { throw ResponseError.methodNotFound(Request.method) }
Expand Down Expand Up @@ -1119,6 +1121,9 @@ extension SourceKitLSPServer {
GetReferenceDocumentRequest.method: .dictionary(["version": .int(1)]),
DidChangeActiveDocumentNotification.method: .dictionary(["version": .int(1)]),
]
if let toolchain = await toolchainRegistry.preferredToolchain(containing: [\.swiftc]), toolchain.swiftPlay != nil {
experimentalCapabilities[WorkspacePlaygroundsRequest.method] = .dictionary(["version": .int(1)])
}
for (key, value) in languageServiceRegistry.languageServices.flatMap({ $0.type.experimentalCapabilities }) {
if let existingValue = experimentalCapabilities[key] {
logger.error(
Expand Down
Loading