Files
sourcekit-lsp/Tests/SourceKitLSPTests/DocumentSymbolTests.swift
2024-12-04 16:42:04 -05:00

808 lines
23 KiB
Swift
Raw 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 - 2019 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 LanguageServerProtocol
import SKTestSupport
import SourceKitLSP
import XCTest
final class DocumentSymbolTests: XCTestCase {
// MARK: - Tests
func testEmpty() async throws {
try await assertDocumentSymbols("") { positions in [] }
}
func testUnicode1() async throws {
try await assertDocumentSymbols("1⃣struct 2⃣Żółć3⃣ { }4") { positions in
[
DocumentSymbol(
name: "Żółć",
detail: nil,
kind: .struct,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testUnicode2() async throws {
try await assertDocumentSymbols("1⃣struct 2⃣🍰3⃣ { }4") { positions in
[
DocumentSymbol(
name: "🍰",
detail: nil,
kind: .struct,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testEnumCase() async throws {
try await assertDocumentSymbols(
"""
1⃣enum 2⃣Foo3⃣ {
case 4⃣first5⃣, 6⃣second7
}8
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .enum,
deprecated: nil,
range: positions["1"]..<positions["8"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "first",
detail: nil,
kind: .enumMember,
deprecated: nil,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"],
children: []
),
DocumentSymbol(
name: "second",
detail: nil,
kind: .enumMember,
deprecated: nil,
range: positions["6"]..<positions["7"],
selectionRange: positions["6"]..<positions["7"],
children: []
),
]
)
]
}
}
func testEnumCaseWithAssociatedValue() async throws {
try await assertDocumentSymbols(
"""
1⃣enum 2⃣Foo3⃣ {
case 4⃣first(Int)5
}6
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .enum,
deprecated: nil,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "first(_:)",
detail: nil,
kind: .enumMember,
deprecated: nil,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testEnumCaseWithNamedAssociatedValue() async throws {
try await assertDocumentSymbols(
"""
1⃣enum 2⃣Foo3⃣ {
case 4⃣first(someName: Int)5
}6
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .enum,
deprecated: nil,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "first(someName:)",
detail: nil,
kind: .enumMember,
deprecated: nil,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testExtension() async throws {
try await assertDocumentSymbols(
"""
// struct ThisIsCommentedOut { }
/* struct ThisOneToo { } */
1⃣extension 2⃣Int3⃣ { }4
"""
) { positions in
[
DocumentSymbol(
name: "Int",
detail: nil,
kind: .namespace,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testStruct() async throws {
try await assertDocumentSymbols("1⃣struct 2⃣Struct3⃣ { }4") { positions in
[
DocumentSymbol(
name: "Struct",
detail: nil,
kind: .struct,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testClass() async throws {
try await assertDocumentSymbols("1⃣class 2⃣Class3⃣ { }4") { positions in
[
DocumentSymbol(
name: "Class",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testEnum() async throws {
try await assertDocumentSymbols("1⃣enum 2⃣Enum3⃣ { case 4⃣enumMember5⃣ }6") { positions in
[
DocumentSymbol(
name: "Enum",
detail: nil,
kind: .enum,
deprecated: nil,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "enumMember",
detail: nil,
kind: .enumMember,
deprecated: nil,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testProtocol() async throws {
try await assertDocumentSymbols("1⃣protocol 2⃣Interface3⃣ { 4⃣func 5⃣f()6⃣ }7") { positions in
[
DocumentSymbol(
name: "Interface",
detail: nil,
kind: .interface,
deprecated: nil,
range: positions["1"]..<positions["7"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "f()",
detail: nil,
kind: .method,
deprecated: nil,
range: positions["4"]..<positions["6"],
selectionRange: positions["5"]..<positions["6"],
children: []
)
]
)
]
}
}
func testFunction() async throws {
try await assertDocumentSymbols("1⃣func 2⃣function()3⃣ { }4") { positions in
[
DocumentSymbol(
name: "function()",
detail: nil,
kind: .function,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testVariable() async throws {
try await assertDocumentSymbols("1⃣var 2⃣variable3⃣ = 04") { positions in
[
DocumentSymbol(
name: "variable",
detail: nil,
kind: .variable,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testMultiplePatternsInVariable() async throws {
try await assertDocumentSymbols("var 1⃣varA2⃣: Int,3⃣ 4⃣varB5⃣ = 06") { positions in
[
DocumentSymbol(
name: "varA",
detail: nil,
kind: .variable,
deprecated: nil,
range: positions["1"]..<positions["3"],
selectionRange: positions["1"]..<positions["2"],
children: []
),
DocumentSymbol(
name: "varB",
detail: nil,
kind: .variable,
deprecated: nil,
range: positions["4"]..<positions["6"],
selectionRange: positions["4"]..<positions["5"],
children: []
),
]
}
}
func testConstant() async throws {
try await assertDocumentSymbols("1⃣let 2⃣constant3⃣ = 04") { positions in
[
DocumentSymbol(
name: "constant",
detail: nil,
kind: .variable,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testComputedVariable() async throws {
try await assertDocumentSymbols("1⃣var 2⃣computedVariable3⃣: Int { return 0 }4") { positions in
[
DocumentSymbol(
name: "computedVariable",
detail: nil,
kind: .variable,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testOperatorFunc() async throws {
try await assertDocumentSymbols("1⃣func 2⃣+(lhs: Struct, rhs: Struct)3⃣ { }4") { positions in
[
DocumentSymbol(
name: "+(lhs:rhs:)",
detail: nil,
kind: .operator,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testPrefixOperatorFunc() async throws {
try await assertDocumentSymbols("1⃣prefix func 2⃣-(rhs: Struct)3⃣ { }4") { positions in
[
DocumentSymbol(
name: "-(rhs:)",
detail: nil,
kind: .operator,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testGenericFunc() async throws {
try await assertDocumentSymbols("1⃣func 2⃣f<3⃣TypeParameter4⃣>(type: TypeParameter.Type)5⃣ { }6") { positions in
[
DocumentSymbol(
name: "f(type:)",
detail: nil,
kind: .function,
deprecated: nil,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["5"],
children: [
DocumentSymbol(
name: "TypeParameter",
detail: nil,
kind: .typeParameter,
deprecated: nil,
range: positions["3"]..<positions["4"],
selectionRange: positions["3"]..<positions["4"],
children: []
)
]
)
]
}
}
func testGenericStruct() async throws {
try await assertDocumentSymbols("1⃣struct 2⃣S3⃣<4⃣TypeParameter5⃣> { }6") { positions in
[
DocumentSymbol(
name: "S",
detail: nil,
kind: .struct,
deprecated: nil,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "TypeParameter",
detail: nil,
kind: .typeParameter,
deprecated: nil,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testClassFunction() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣func 5⃣method()6⃣ { }7
}8
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["8"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "method()",
detail: nil,
kind: .method,
deprecated: nil,
range: positions["4"]..<positions["7"],
selectionRange: positions["5"]..<positions["6"],
children: []
)
]
)
]
}
}
func testStaticClassFunction() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣static func 5⃣staticMethod()6⃣ { }7
}8
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["8"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "staticMethod()",
detail: nil,
kind: .method,
deprecated: nil,
range: positions["4"]..<positions["7"],
selectionRange: positions["5"]..<positions["6"],
children: []
)
]
)
]
}
}
func testStaticClassProperty() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣var 5⃣property6⃣ = 07
}8
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["8"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "property",
detail: nil,
kind: .property,
deprecated: nil,
range: positions["4"]..<positions["7"],
selectionRange: positions["5"]..<positions["6"],
children: []
)
]
)
]
}
}
func testInitializer() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣init()5⃣ { }6
}7
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["7"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "init()",
detail: nil,
kind: .constructor,
deprecated: nil,
range: positions["4"]..<positions["6"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testInitializerWithParameters() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣init(_ first: Int, second: Int)5⃣ { }6
}7
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
detail: nil,
kind: .class,
deprecated: nil,
range: positions["1"]..<positions["7"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "init(_:second:)",
detail: nil,
kind: .constructor,
deprecated: nil,
range: positions["4"]..<positions["6"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
func testLocalVariable() async throws {
try await assertDocumentSymbols(
"""
1⃣func 2⃣f()3⃣ {
let localConstant = 0
}4
"""
) { positions in
[
DocumentSymbol(
name: "f()",
detail: nil,
kind: .function,
deprecated: nil,
range: positions["1"]..<positions["4"],
selectionRange: positions["2"]..<positions["3"],
children: []
)
]
}
}
func testLocalFunction() async throws {
try await assertDocumentSymbols(
"""
1⃣func 2⃣f()3⃣ {
4⃣func 5⃣localFunction()6⃣ { }7
}8
"""
) { positions in
[
DocumentSymbol(
name: "f()",
detail: nil,
kind: .function,
deprecated: nil,
range: positions["1"]..<positions["8"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "localFunction()",
detail: nil,
kind: .function,
deprecated: nil,
range: positions["4"]..<positions["7"],
selectionRange: positions["5"]..<positions["6"],
children: []
)
]
)
]
}
}
func testIncludeMarkComments() async throws {
try await assertDocumentSymbols(
"""
1⃣// MARK: Marker2
"""
) { positions in
[
DocumentSymbol(
name: "Marker",
kind: .namespace,
range: positions["1"]..<positions["2"],
selectionRange: positions["1"]..<positions["2"]
)
]
}
try await assertDocumentSymbols(
"""
1⃣// MARK: - Marker2
"""
) { positions in
[
DocumentSymbol(
name: "- Marker",
kind: .namespace,
range: positions["1"]..<positions["2"],
selectionRange: positions["1"]..<positions["2"]
)
]
}
try await assertDocumentSymbols(
"""
1⃣/* MARK: Marker */2
"""
) { positions in
[
DocumentSymbol(
name: "Marker",
kind: .namespace,
range: positions["1"]..<positions["2"],
selectionRange: positions["1"]..<positions["2"]
)
]
}
}
func testNestedMarkComment() async throws {
try await assertDocumentSymbols(
"""
1⃣struct 2⃣Foo3⃣ {
4⃣// MARK: Marker5
}6
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
kind: .struct,
range: positions["1"]..<positions["6"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "Marker",
kind: .namespace,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"]
)
]
)
]
}
}
func testNestedMarkCommentFollowedAttachedToChild() async throws {
try await assertDocumentSymbols(
"""
1⃣struct 2⃣Foo3⃣ {
4⃣// MARK: Marker5
6⃣func 7⃣myFunc()8⃣ { }9
}🔟
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
kind: .struct,
range: positions["1"]..<positions["🔟"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "Marker",
kind: .namespace,
range: positions["4"]..<positions["5"],
selectionRange: positions["4"]..<positions["5"]
),
DocumentSymbol(
name: "myFunc()",
kind: .method,
range: positions["6"]..<positions["9"],
selectionRange: positions["7"]..<positions["8"],
children: []
),
]
)
]
}
}
func testShowDeinit() async throws {
try await assertDocumentSymbols(
"""
1⃣class 2⃣Foo3⃣ {
4⃣deinit5⃣ {
}6
}7
"""
) { positions in
[
DocumentSymbol(
name: "Foo",
kind: .class,
range: positions["1"]..<positions["7"],
selectionRange: positions["2"]..<positions["3"],
children: [
DocumentSymbol(
name: "deinit",
kind: .constructor,
range: positions["4"]..<positions["6"],
selectionRange: positions["4"]..<positions["5"],
children: []
)
]
)
]
}
}
}
fileprivate func assertDocumentSymbols(
_ markedText: String,
_ expectedDocumentSymbols: (DocumentPositions) -> [DocumentSymbol],
file: StaticString = #filePath,
line: UInt = #line
) async throws {
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
let positions = testClient.openDocument(markedText, uri: uri)
let symbols = try unwrap(try await testClient.send(DocumentSymbolRequest(textDocument: TextDocumentIdentifier(uri))))
XCTAssertEqual(symbols, .documentSymbols(expectedDocumentSymbols(positions)), file: file, line: line)
}