mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-10-14 13:49:02 +02:00
310 lines
9.8 KiB
Swift
310 lines
9.8 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_spi(SourceKitLSP) import LanguageServerProtocol
|
|
import SKLogging
|
|
import SKTestSupport
|
|
import SourceKitLSP
|
|
import SwiftExtensions
|
|
import XCTest
|
|
|
|
final class SwiftCompletionSnippetTests: SourceKitLSPTestCase {
|
|
private var snippetCapabilities = ClientCapabilities(
|
|
textDocument: TextDocumentClientCapabilities(
|
|
completion: TextDocumentClientCapabilities.Completion(
|
|
completionItem: TextDocumentClientCapabilities.Completion.CompletionItem(snippetSupport: true)
|
|
)
|
|
)
|
|
)
|
|
|
|
func testKeywordIfProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let ifItem = try XCTUnwrap(completions.items.first(where: { $0.label == "if" }))
|
|
|
|
XCTAssertEqual(ifItem.kind, .keyword)
|
|
XCTAssertEqual(ifItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(ifItem.insertText)
|
|
XCTAssertTrue(insertText.contains("${1:condition}"))
|
|
XCTAssertTrue(insertText.contains("${0:body}"))
|
|
}
|
|
|
|
func testKeywordForProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let forItem = try XCTUnwrap(completions.items.first(where: { $0.label == "for" }))
|
|
|
|
XCTAssertEqual(forItem.kind, .keyword)
|
|
XCTAssertEqual(forItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(forItem.insertText)
|
|
XCTAssertTrue(insertText.contains("${1:item}"))
|
|
XCTAssertTrue(insertText.contains("${2:sequence}"))
|
|
}
|
|
|
|
func testKeywordWhileProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let whileItem = try XCTUnwrap(completions.items.first(where: { $0.label == "while" }))
|
|
|
|
XCTAssertEqual(whileItem.kind, .keyword)
|
|
XCTAssertEqual(whileItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(whileItem.insertText)
|
|
XCTAssertTrue(insertText.contains("${1:condition}"))
|
|
}
|
|
|
|
func testKeywordGuardProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let guardItem = try XCTUnwrap(completions.items.first(where: { $0.label == "guard" }))
|
|
|
|
XCTAssertEqual(guardItem.kind, .keyword)
|
|
XCTAssertEqual(guardItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(guardItem.insertText)
|
|
XCTAssertTrue(insertText.contains("${1:condition}"))
|
|
XCTAssertTrue(insertText.contains("else"))
|
|
}
|
|
|
|
func testKeywordSwitchProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let switchItem = try XCTUnwrap(completions.items.first(where: { $0.label == "switch" }))
|
|
|
|
XCTAssertEqual(switchItem.kind, .keyword)
|
|
XCTAssertEqual(switchItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(switchItem.insertText)
|
|
XCTAssertTrue(insertText.contains("${1:value}"))
|
|
XCTAssertTrue(insertText.contains("case"))
|
|
// Expect `case` to be not indented and the body placeholder to exist (indentation tested elsewhere).
|
|
XCTAssertTrue(insertText.contains("case ${2:pattern}:"))
|
|
XCTAssertTrue(insertText.contains("${0:body}"))
|
|
}
|
|
|
|
func testKeywordRepeatProvidesSnippet() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let repeatItem = try XCTUnwrap(completions.items.first(where: { $0.label == "repeat" }))
|
|
|
|
XCTAssertEqual(repeatItem.kind, .keyword)
|
|
XCTAssertEqual(repeatItem.insertTextFormat, .snippet)
|
|
|
|
let insertText = try XCTUnwrap(repeatItem.insertText)
|
|
XCTAssertTrue(insertText.contains("while"))
|
|
}
|
|
|
|
func testKeywordWithoutSnippetSupport() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
// Client without snippet support should get plain keywords
|
|
let testClient = try await TestSourceKitLSPClient()
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let ifItem = try XCTUnwrap(completions.items.first(where: { $0.label == "if" }))
|
|
|
|
XCTAssertEqual(ifItem.kind, .keyword)
|
|
XCTAssertEqual(ifItem.insertTextFormat, .plain)
|
|
XCTAssertEqual(ifItem.insertText, "if")
|
|
}
|
|
|
|
func testInsertTextAndTextEditAreConsistent() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
for item in completions.items {
|
|
guard let insertText = item.insertText else { continue }
|
|
// Skip SourceKit's implicit method call labels (e.g. "funcName()")
|
|
if item.label.contains("(") && item.label.contains(")") { continue }
|
|
|
|
if case .textEdit(let te) = item.textEdit {
|
|
XCTAssertEqual(insertText, te.newText, "insertText and textEdit.newText differ for item '\(item.label)'")
|
|
}
|
|
}
|
|
}
|
|
|
|
func testKeywordSnippetUsesInferredSpacesIndentation() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
let a = 1
|
|
let b = 2
|
|
1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let ifItem = try XCTUnwrap(completions.items.first(where: { $0.label == "if" }))
|
|
let insertText = try XCTUnwrap(ifItem.insertText)
|
|
|
|
// Expect newline + two spaces before the final placeholder
|
|
XCTAssertTrue(
|
|
insertText.contains("\n ${0:body}"),
|
|
"expected two-space indentation in snippet, got: '\(insertText)'"
|
|
)
|
|
}
|
|
|
|
func testKeywordSnippetUsesInferredTabsIndentation() async throws {
|
|
try await SkipUnless.sourcekitdSupportsPlugin()
|
|
|
|
let testClient = try await TestSourceKitLSPClient(capabilities: snippetCapabilities)
|
|
let uri = DocumentURI(for: .swift)
|
|
// Use raw tabs \t for indentation to ensure server picks up Tab style.
|
|
let positions = testClient.openDocument(
|
|
"""
|
|
func test() {
|
|
\tlet a = 1
|
|
\tlet b = 2
|
|
\t1️⃣
|
|
}
|
|
""",
|
|
uri: uri
|
|
)
|
|
|
|
let completions = try await testClient.send(
|
|
CompletionRequest(textDocument: TextDocumentIdentifier(uri), position: positions["1️⃣"])
|
|
)
|
|
|
|
let ifItem = try XCTUnwrap(completions.items.first(where: { $0.label == "if" }))
|
|
let insertText = try XCTUnwrap(ifItem.insertText)
|
|
// Expect newline + tab then the body placeholder
|
|
XCTAssertTrue(insertText.contains("\n\t${0:body}"), "expected tab indentation in snippet, got: '\(insertText)'")
|
|
}
|
|
}
|