mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
510 lines
16 KiB
Swift
510 lines
16 KiB
Swift
//===----------------------------------------------------------------------===//
|
||
//
|
||
// This source file is part of the Swift.org open source project
|
||
//
|
||
// Copyright (c) 2014 - 2021 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
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
@_spi(SourceKitLSP) import LanguageServerProtocol
|
||
import SKLogging
|
||
import SKTestSupport
|
||
import ToolchainRegistry
|
||
import XCTest
|
||
|
||
fileprivate extension Toolchain {
|
||
#if compiler(>=6.4)
|
||
#warning(
|
||
"Once we require swift-play in the toolchain that's used to test SourceKit-LSP, we can just use `forTesting`"
|
||
)
|
||
#endif
|
||
static var forTestingWithSwiftPlay: Toolchain {
|
||
get async throws {
|
||
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
|
||
return Toolchain(
|
||
identifier: "\(toolchain.identifier)-swift-swift",
|
||
displayName: "\(toolchain.identifier) with swift-play",
|
||
path: toolchain.path,
|
||
clang: toolchain.clang,
|
||
swift: toolchain.swift,
|
||
swiftc: toolchain.swiftc,
|
||
swiftPlay: URL(fileURLWithPath: "/dummy/usr/bin/swift-play"),
|
||
clangd: toolchain.clangd,
|
||
sourcekitd: toolchain.sourcekitd,
|
||
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
|
||
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
|
||
libIndexStore: toolchain.libIndexStore
|
||
)
|
||
}
|
||
}
|
||
|
||
static var forTestingWithoutSwiftPlay: Toolchain {
|
||
get async throws {
|
||
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
|
||
return Toolchain(
|
||
identifier: "\(toolchain.identifier)-no-swift-swift",
|
||
displayName: "\(toolchain.identifier) without swift-play",
|
||
path: toolchain.path,
|
||
clang: toolchain.clang,
|
||
swift: toolchain.swift,
|
||
swiftc: toolchain.swiftc,
|
||
swiftPlay: nil,
|
||
clangd: toolchain.clangd,
|
||
sourcekitd: toolchain.sourcekitd,
|
||
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
|
||
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
|
||
libIndexStore: toolchain.libIndexStore
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
final class CodeLensTests: SourceKitLSPTestCase {
|
||
|
||
func testNoLenses() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.run: "swift.run",
|
||
SupportedCodeLensCommand.debug: "swift.debug",
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Test.swift": """
|
||
struct MyApp {
|
||
public static func main() {}
|
||
}
|
||
"""
|
||
],
|
||
capabilities: capabilities
|
||
)
|
||
let (uri, _) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(response, [])
|
||
}
|
||
|
||
func testNoClientCodeLenses() async throws {
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Test.swift": """
|
||
import Playgrounds
|
||
@main
|
||
struct MyApp {
|
||
public static func main() {}
|
||
}
|
||
|
||
#Playground {
|
||
print("Hello Playground!")
|
||
}
|
||
|
||
#Playground("named") {
|
||
print("Hello named Playground!")
|
||
}
|
||
"""
|
||
],
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, _) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(response, [])
|
||
}
|
||
|
||
func testSuccessfulCodeLensRequest() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.run: "swift.run",
|
||
SupportedCodeLensCommand.debug: "swift.debug",
|
||
SupportedCodeLensCommand.play: "swift.play",
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyApp/Test.swift": """
|
||
import Playgrounds
|
||
1️⃣@main2️⃣
|
||
struct MyApp {
|
||
public static func main() {}
|
||
}
|
||
|
||
3️⃣#Playground {
|
||
print("Hello Playground!")
|
||
}4️⃣
|
||
|
||
5️⃣#Playground("named") {
|
||
print("Hello named Playground!")
|
||
}6️⃣
|
||
"""
|
||
],
|
||
manifest: """
|
||
// swift-tools-version: 5.7
|
||
|
||
import PackageDescription
|
||
|
||
let package = Package(
|
||
name: "MyApp",
|
||
targets: [.executableTarget(name: "MyApp")]
|
||
)
|
||
""",
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, positions) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(
|
||
response,
|
||
[
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(title: "Run MyApp", command: "swift.run", arguments: [.string("MyApp")])
|
||
),
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(title: "Debug MyApp", command: "swift.debug", arguments: [.string("MyApp")])
|
||
),
|
||
CodeLens(
|
||
range: positions["3️⃣"]..<positions["4️⃣"],
|
||
command: Command(
|
||
title: "Play \"MyApp/Test.swift:7:1\"",
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyApp/Test.swift:7:1",
|
||
label: nil,
|
||
range: positions["3️⃣"]..<positions["4️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
),
|
||
CodeLens(
|
||
range: positions["5️⃣"]..<positions["6️⃣"],
|
||
command: Command(
|
||
title: "Play \"named\"",
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyApp/Test.swift:11:1",
|
||
label: "named",
|
||
range: positions["5️⃣"]..<positions["6️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
),
|
||
]
|
||
)
|
||
}
|
||
|
||
func testMultiplePlaygroundCodeLensOnLine() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.play: "swift.play"
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyLibrary/Test.swift": """
|
||
import Playgrounds
|
||
1️⃣#Playground { print("Hello Playground!") }2️⃣; 3️⃣#Playground { print("Hello Again!") }4️⃣
|
||
"""
|
||
],
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, positions) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(
|
||
response,
|
||
[
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(
|
||
title: #"Play "MyLibrary/Test.swift:2:1""#,
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyLibrary/Test.swift:2:1",
|
||
label: nil,
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
),
|
||
CodeLens(
|
||
range: positions["3️⃣"]..<positions["4️⃣"],
|
||
command: Command(
|
||
title: "Play \"MyLibrary/Test.swift:2:46\"",
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyLibrary/Test.swift:2:46",
|
||
label: nil,
|
||
range: positions["3️⃣"]..<positions["4️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
),
|
||
]
|
||
)
|
||
}
|
||
|
||
func testCodeLensRequestSwiftPlayMissing() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.run: "swift.run",
|
||
SupportedCodeLensCommand.debug: "swift.debug",
|
||
SupportedCodeLensCommand.play: "swift.play",
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithoutSwiftPlay])
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyApp/Test.swift": """
|
||
import Playgrounds
|
||
1️⃣@main2️⃣
|
||
struct MyApp {
|
||
public static func main() {}
|
||
}
|
||
|
||
#Playground {
|
||
print("Hello Playground!")
|
||
}
|
||
|
||
#Playground("named") {
|
||
print("Hello named Playground!")
|
||
}
|
||
"""
|
||
],
|
||
manifest: """
|
||
// swift-tools-version: 5.7
|
||
|
||
import PackageDescription
|
||
|
||
let package = Package(
|
||
name: "MyApp",
|
||
targets: [.executableTarget(name: "MyApp")]
|
||
)
|
||
""",
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, positions) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(
|
||
response,
|
||
[
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(title: "Run MyApp", command: "swift.run", arguments: [.string("MyApp")])
|
||
),
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(title: "Debug MyApp", command: "swift.debug", arguments: [.string("MyApp")])
|
||
),
|
||
]
|
||
)
|
||
}
|
||
|
||
func testNoImportPlaygrounds() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.play: "swift.play"
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyLibrary/Test.swift": """
|
||
public func foo() -> String {
|
||
"bar"
|
||
}
|
||
|
||
#Playground("foo") {
|
||
print(foo())
|
||
}
|
||
|
||
#Playground {
|
||
print(foo())
|
||
}
|
||
|
||
public func bar(_ i: Int, _ j: Int) -> Int {
|
||
i + j
|
||
}
|
||
|
||
#Playground("bar") {
|
||
var i = bar(1, 2)
|
||
i = i + 1
|
||
print(i)
|
||
}
|
||
"""
|
||
],
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, _) = try project.openDocument("Test.swift")
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
XCTAssertEqual(response, [])
|
||
}
|
||
|
||
func testCodeLensRequestNoPlaygrounds() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.play: "swift.play"
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyLibrary/Test.swift": """
|
||
import Playgrounds
|
||
|
||
public func Playground(_ i: Int, _ j: Int) -> Int {
|
||
i + j
|
||
}
|
||
|
||
@Playground
|
||
struct MyPlayground {
|
||
public var playground: String = ""
|
||
}
|
||
"""
|
||
],
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, _) = try project.openDocument("Test.swift")
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
XCTAssertEqual(response, [])
|
||
}
|
||
|
||
func testEmojiPlaygroundName() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.play: "swift.play"
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyLibrary/Test.swift": """
|
||
import Playgrounds
|
||
1️⃣#Playground("🧑🧑🧒🧒") { print("Hello Playground!") }2️⃣
|
||
"""
|
||
],
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, positions) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(
|
||
response,
|
||
[
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(
|
||
title: #"Play "🧑🧑🧒🧒""#,
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyLibrary/Test.swift:2:1",
|
||
label: "🧑🧑🧒🧒",
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
)
|
||
]
|
||
)
|
||
}
|
||
|
||
func testUtf8PlaygroundOffset() async throws {
|
||
var codeLensCapabilities = TextDocumentClientCapabilities.CodeLens()
|
||
codeLensCapabilities.supportedCommands = [
|
||
SupportedCodeLensCommand.play: "swift.play"
|
||
]
|
||
let capabilities = ClientCapabilities(textDocument: TextDocumentClientCapabilities(codeLens: codeLensCapabilities))
|
||
let toolchainRegistry = ToolchainRegistry(toolchains: [try await Toolchain.forTestingWithSwiftPlay])
|
||
|
||
let project = try await SwiftPMTestProject(
|
||
files: [
|
||
"Sources/MyLibrary/Test.swift": """
|
||
import Playgrounds
|
||
/* 🧑🧑🧒🧒 */ 1️⃣#Playground { print("Hello Playground!") }2️⃣
|
||
"""
|
||
],
|
||
capabilities: capabilities,
|
||
toolchainRegistry: toolchainRegistry
|
||
)
|
||
|
||
let (uri, positions) = try project.openDocument("Test.swift")
|
||
|
||
let response = try await project.testClient.send(
|
||
CodeLensRequest(textDocument: TextDocumentIdentifier(uri))
|
||
)
|
||
|
||
XCTAssertEqual(
|
||
response,
|
||
[
|
||
CodeLens(
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
command: Command(
|
||
title: #"Play "MyLibrary/Test.swift:2:33""#,
|
||
command: "swift.play",
|
||
arguments: [
|
||
TextDocumentPlayground(
|
||
id: "MyLibrary/Test.swift:2:33",
|
||
label: nil,
|
||
range: positions["1️⃣"]..<positions["2️⃣"],
|
||
).encodeToLSPAny()
|
||
]
|
||
)
|
||
)
|
||
]
|
||
)
|
||
}
|
||
}
|