mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
Apply the following changes: - Check for the presence of `#Playgrounds` textually before getting the module name in `SwiftPlaygroundsScanner`. This is important because getting the module name requires us to get build settings for the file, which can be expensive. Do the cheaper check first - Make `syntacticTests` and `syntacticPlaygrounds` closures capture the workspace instead of passing the workspace from the `SwiftSyntacticIndex` back out. I like this better because now we can’t accidentally pass the wrong workspace to a `SwiftSyntacticIndex`, eg. to `buildTargetsChanges`. - Capture the initialize result in `TestSourceKitLSPClient` instead of using `postInitialization` to capture the result - Minor cleanup of unnecessary abstractions, likely artifacts of earlier iterations - Restructure tests so that every test has its own list of source files, allowing for easier local reasoning – turns out some of these tests didn’t even need to open a workspace, just to check the initialize response
109 lines
3.4 KiB
Swift
109 lines
3.4 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
internal import BuildServerIntegration
|
|
import Foundation
|
|
@_spi(SourceKitLSP) import LanguageServerProtocol
|
|
import SKLogging
|
|
import SourceKitLSP
|
|
import SwiftParser
|
|
import SwiftSyntax
|
|
|
|
// MARK: - SwiftPlaygroundsScanner
|
|
|
|
final class SwiftPlaygroundsScanner: SyntaxVisitor {
|
|
/// The base ID to use to generate IDs for any playgrounds found in this file.
|
|
private let baseID: String
|
|
|
|
/// The snapshot of the document for which we are getting playgrounds.
|
|
private let snapshot: DocumentSnapshot
|
|
|
|
/// Accumulating the result in here.
|
|
private var result: [TextDocumentPlayground] = []
|
|
|
|
/// Keep track of if "Playgrounds" has been imported
|
|
private var isPlaygroundImported: Bool = false
|
|
|
|
private init(baseID: String, snapshot: DocumentSnapshot) {
|
|
self.baseID = baseID
|
|
self.snapshot = snapshot
|
|
super.init(viewMode: .sourceAccurate)
|
|
}
|
|
|
|
/// Designated entry point for `SwiftPlaygroundsScanner`.
|
|
static func findDocumentPlaygrounds(
|
|
for snapshot: DocumentSnapshot,
|
|
workspace: Workspace,
|
|
syntaxTreeManager: SyntaxTreeManager,
|
|
) async -> [TextDocumentPlayground] {
|
|
guard snapshot.text.contains("#Playground") else {
|
|
return []
|
|
}
|
|
|
|
guard let canonicalTarget = await workspace.buildServerManager.canonicalTarget(for: snapshot.uri),
|
|
let moduleName = await workspace.buildServerManager.moduleName(for: snapshot.uri, in: canonicalTarget),
|
|
let baseName = snapshot.uri.fileURL?.lastPathComponent
|
|
else {
|
|
return []
|
|
}
|
|
|
|
let syntaxTree = await syntaxTreeManager.syntaxTree(for: snapshot)
|
|
|
|
let visitor = SwiftPlaygroundsScanner(baseID: "\(moduleName)/\(baseName)", snapshot: snapshot)
|
|
visitor.walk(syntaxTree)
|
|
return visitor.isPlaygroundImported ? visitor.result : []
|
|
}
|
|
|
|
/// Add a playground location with the given parameters to the `result` array.
|
|
private func record(
|
|
id: String,
|
|
label: String?,
|
|
range: Range<AbsolutePosition>
|
|
) {
|
|
let positionRange = snapshot.absolutePositionRange(of: range)
|
|
|
|
result.append(
|
|
TextDocumentPlayground(
|
|
id: id,
|
|
label: label,
|
|
range: positionRange,
|
|
)
|
|
)
|
|
}
|
|
|
|
override func visit(_ node: ImportPathComponentSyntax) -> SyntaxVisitorContinueKind {
|
|
if node.name.text == "Playgrounds" {
|
|
isPlaygroundImported = true
|
|
}
|
|
return .skipChildren
|
|
}
|
|
|
|
override func visit(_ node: MacroExpansionExprSyntax) -> SyntaxVisitorContinueKind {
|
|
guard node.macroName.text == "Playground" else {
|
|
return .skipChildren
|
|
}
|
|
|
|
let startPosition = snapshot.sourcekitdPosition(of: snapshot.position(of: node.positionAfterSkippingLeadingTrivia))
|
|
let stringLiteral = node.arguments.first?.expression.as(StringLiteralExprSyntax.self)
|
|
let playgroundLabel = stringLiteral?.representedLiteralValue
|
|
let playgroundID = "\(baseID):\(startPosition.line):\(startPosition.utf8Column)"
|
|
|
|
record(
|
|
id: playgroundID,
|
|
label: playgroundLabel,
|
|
range: node.trimmedRange
|
|
)
|
|
|
|
return .skipChildren
|
|
}
|
|
}
|