Files
sourcekit-lsp/Tests/SourceKitLSPTests/FormattingTests.swift
2025-10-31 14:11:11 -07:00

361 lines
9.4 KiB
Swift
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 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
@_spi(SourceKitLSP) import SKLogging
import SKTestSupport
import SourceKitLSP
import SwiftExtensions
import ToolchainRegistry
import XCTest
import class TSCBasic.Process
final class FormattingTests: SourceKitLSPTestCase {
func testFormatting() async throws {
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
let source = """
struct S {
var foo: Int
var bar: Int
}
"""
testClient.openDocument(source, uri: uri)
let response = try await testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 3, insertSpaces: true)
)
)
let edits = try XCTUnwrap(response)
let formattedSource = apply(edits: edits, to: source)
XCTAssert(edits.allSatisfy { $0.newText.allSatisfy(\.isWhitespace) })
XCTAssertEqual(
formattedSource,
"""
struct S {
var foo: Int
var bar: Int
}
"""
)
}
func testFormattingNoEdits() async throws {
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
testClient.openDocument(
"""
struct S {
var foo: Int
}
""",
uri: uri
)
let response = try await testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
let edits = try XCTUnwrap(response)
XCTAssertEqual(edits.count, 0)
}
func testConfigFileOnDisk() async throws {
// We pick up an invalid swift-format configuration file and thus don't set the user-provided options.
let project = try await MultiFileTestProject(files: [
".swift-format": """
{
"version": 1,
"indentation": {
"spaces": 1
}
}
""",
"test.swift": """
struct Root {
1⃣var bar = 123
}
""",
])
let (uri, positions) = try project.openDocument("test.swift")
let response = try await project.testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
XCTAssertEqual(
response,
[
TextEdit(range: Range(positions["1"]), newText: " ")
]
)
}
func testConfigFileInParentDirectory() async throws {
// We pick up an invalid swift-format configuration file and thus don't set the user-provided options.
let project = try await MultiFileTestProject(files: [
".swift-format": """
{
"version": 1,
"indentation": {
"spaces": 1
}
}
""",
"sub/test.swift": """
struct Root {
1⃣var bar = 123
}
""",
])
let (uri, positions) = try project.openDocument("test.swift")
let response = try await project.testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
XCTAssertEqual(
response,
[
TextEdit(range: Range(positions["1"]), newText: " ")
]
)
}
func testConfigFileInNestedDirectory() async throws {
// We pick up an invalid swift-format configuration file and thus don't set the user-provided options.
let project = try await MultiFileTestProject(files: [
".swift-format": """
{
"version": 1,
"indentation": {
"spaces": 1
}
},
""",
"sub/.swift-format": """
{
"version": 1,
"indentation": {
"spaces": 3
}
}
""",
"sub/test.swift": """
struct Root {
1⃣var bar = 123
}
""",
])
let (uri, positions) = try project.openDocument("test.swift")
let response = try await project.testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
XCTAssertEqual(
response,
[
TextEdit(range: Range(positions["1"]), newText: " ")
]
)
}
func testInvalidConfigurationFile() async throws {
// We pick up an invalid swift-format configuration file and thus don't set the user-provided options.
// The swift-format default is 2 spaces.
let project = try await MultiFileTestProject(files: [
".swift-format": "",
"test.swift": """
struct Root {
1⃣var bar = 123
}
""",
])
let (uri, _) = try project.openDocument("test.swift")
await assertThrowsError(
try await project.testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 3, insertSpaces: true)
)
)
)
}
func testInsertAndRemove() async throws {
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
let source = """
public extension Example {
func function() {}
}
"""
testClient.openDocument(source, uri: uri)
let response = try await testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
let edits = try XCTUnwrap(response)
let formattedSource = apply(edits: edits, to: source)
XCTAssertEqual(
formattedSource,
"""
extension Example {
public func function() {}
}
"""
)
}
func testMultiLineStringInsertion() async throws {
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
let source = #"""
_ = [
Node(
documentation: """
Some great documentation
of an amazing
syntax node
""",
children: [
Child(
documentation: """
The one and only child
\#("")
"""
)
]
)
]
"""#
testClient.openDocument(source, uri: uri)
let response = try await testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
)
let edits = try XCTUnwrap(response)
let formattedSource = apply(edits: edits, to: source)
XCTAssert(edits.allSatisfy { $0.newText.allSatisfy(\.isWhitespace) })
XCTAssertEqual(
formattedSource,
#"""
_ = [
Node(
documentation: """
Some great documentation
of an amazing
syntax node
""",
children: [
Child(
documentation: """
The one and only child
"""
)
]
)
]
"""#
)
}
func testSwiftFormatCrashing() async throws {
try await withTestScratchDir { scratchDir in
let toolchain = try await unwrap(ToolchainRegistry.forTesting.default)
let crashingExecutablePath = scratchDir.appending(component: "crashing-executable")
try await createBinary(
"""
fatalError()
""",
at: crashingExecutablePath
)
let toolchainRegistry = ToolchainRegistry(toolchains: [
Toolchain(
identifier: "\(toolchain.identifier)-crashing-swift-format",
displayName: "\(toolchain.identifier) with crashing swift-format",
path: toolchain.path,
clang: toolchain.clang,
swift: toolchain.swift,
swiftc: toolchain.swiftc,
swiftFormat: crashingExecutablePath,
clangd: toolchain.clangd,
sourcekitd: toolchain.sourcekitd,
sourceKitClientPlugin: toolchain.sourceKitClientPlugin,
sourceKitServicePlugin: toolchain.sourceKitServicePlugin,
libIndexStore: toolchain.libIndexStore
)
])
let testClient = try await TestSourceKitLSPClient(toolchainRegistry: toolchainRegistry)
let uri = DocumentURI(for: .swift)
testClient.openDocument(
// Generate a large source file to increase the chance of the executable we substitute for swift-format
// crashing before the entire input file is sent to it.
String(repeating: "func foo() {}\n", count: 10_000),
uri: uri
)
await assertThrowsError(
try await testClient.send(
DocumentFormattingRequest(
textDocument: TextDocumentIdentifier(uri),
options: FormattingOptions(tabSize: 2, insertSpaces: true)
)
),
expectedMessage: #/Running swift-format failed|Writing to swift-format stdin failed/#
)
}
}
}