Skip to content

Commit 17c0a44

Browse files
authored
Merge pull request swiftlang#1183 from ahoppen/pretty-hover
Make hover popover prettier
2 parents f73df6d + e1b5ffa commit 17c0a44

File tree

4 files changed

+63
-50
lines changed

4 files changed

+63
-50
lines changed

Sources/SourceKitLSP/Swift/CommentXML.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private struct XMLToMarkdown {
7777
newlineIfNeeded(count: 2)
7878
out += "```swift\n"
7979
toMarkdown(node.children)
80-
out += "\n```\n\n---\n"
80+
out += "\n```\n"
8181

8282
case "Name", "USR", "Direction":
8383
break

Sources/SourceKitLSP/Swift/SwiftLanguageService.swift

+29-20
Original file line numberDiff line numberDiff line change
@@ -569,46 +569,55 @@ extension SwiftLanguageService {
569569
let cursorInfoResults = try await cursorInfo(uri, position..<position).cursorInfo
570570

571571
let symbolDocumentations = cursorInfoResults.compactMap { (cursorInfo) -> String? in
572-
guard let name: String = cursorInfo.symbolInfo.name else {
573-
// There is a cursor but we don't know how to deal with it.
574-
return nil
575-
}
576-
577-
/// Prepend backslash to `*` and `_`, to prevent them
578-
/// from being interpreted as markdown.
579-
func escapeNameMarkdown(_ str: String) -> String {
580-
return String(str.flatMap({ ($0 == "*" || $0 == "_") ? ["\\", $0] : [$0] }))
581-
}
582-
583-
var result = escapeNameMarkdown(name)
584572
if let documentation = cursorInfo.documentation {
573+
var result = ""
585574
if let annotatedDeclaration = cursorInfo.annotatedDeclaration {
586575
let markdownDecl =
587576
orLog("Convert XML declaration to Markdown") {
588577
try xmlDocumentationToMarkdown(annotatedDeclaration)
589578
} ?? annotatedDeclaration
590-
result += "\n\(markdownDecl)"
579+
result += "\(markdownDecl)\n"
591580
}
592581
result += documentation
582+
return result
593583
} else if let doc = cursorInfo.documentationXML {
594-
result += """
595-
584+
return """
596585
\(orLog("Convert XML to Markdown") { try xmlDocumentationToMarkdown(doc) } ?? doc)
597586
"""
598587
} else if let annotated: String = cursorInfo.annotatedDeclaration {
599-
result += """
600-
588+
return """
601589
\(orLog("Convert XML to Markdown") { try xmlDocumentationToMarkdown(annotated) } ?? annotated)
602590
"""
591+
} else {
592+
return nil
603593
}
604-
return result
605594
}
606595

607596
if symbolDocumentations.isEmpty {
608597
return nil
609598
}
610599

611-
let joinedDocumentation = symbolDocumentations.joined(separator: "\n# Alternative result\n")
600+
let joinedDocumentation: String
601+
if let only = symbolDocumentations.only {
602+
joinedDocumentation = only
603+
} else {
604+
let documentationsWithSpacing = symbolDocumentations.enumerated().map { index, documentation in
605+
// Work around a bug in VS Code that displays a code block after a horizontal ruler without any spacing
606+
// (the pixels of the code block literally touch the ruler) by adding an empty line into the code block.
607+
// Only do this for subsequent results since only those are preceeded by a ruler.
608+
let prefix = "```swift\n"
609+
if index != 0 && documentation.starts(with: prefix) {
610+
return prefix + "\n" + documentation.dropFirst(prefix.count)
611+
} else {
612+
return documentation
613+
}
614+
}
615+
joinedDocumentation = """
616+
## Multiple results
617+
618+
\(documentationsWithSpacing.joined(separator: "\n\n---\n\n"))
619+
"""
620+
}
612621

613622
return HoverResponse(
614623
contents: .markupContent(MarkupContent(kind: .markdown, value: joinedDocumentation)),
@@ -865,7 +874,7 @@ extension SwiftLanguageService {
865874
// Instead of returning an error, return empty results.
866875
logger.error(
867876
"""
868-
Loading diagnostic failed with the following error. Returning empty diagnostics.
877+
Loading diagnostic failed with the following error. Returning empty diagnostics.
869878
\(error.forLogging)
870879
"""
871880
)

Tests/SourceKitLSPTests/HoverTests.swift

+33-11
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,10 @@ final class HoverTests: XCTestCase {
2626
struct 1️⃣S {}
2727
""",
2828
expectedContent: """
29-
S
3029
```swift
3130
struct S
3231
```
3332
34-
---
3533
This is a doc comment for S.
3634
3735
Details.
@@ -70,21 +68,51 @@ final class HoverTests: XCTestCase {
7068
_ = 1️⃣Foo()
7169
""",
7270
expectedContent: """
73-
Foo
71+
## Multiple results
72+
7473
```swift
7574
struct Foo
7675
```
7776
77+
7878
---
7979
80-
# Alternative result
81-
init()
8280
```swift
81+
8382
init()
8483
```
8584
85+
"""
86+
)
87+
}
88+
89+
func testMultiCursorInfoResultsHoverWithDocumentation() async throws {
90+
try await assertHover(
91+
"""
92+
/// A struct
93+
struct Foo {
94+
/// The initializer
95+
init() {}
96+
}
97+
_ = 1️⃣Foo()
98+
""",
99+
expectedContent: """
100+
## Multiple results
101+
102+
```swift
103+
struct Foo
104+
```
105+
106+
A struct
107+
86108
---
87109
110+
```swift
111+
112+
init()
113+
```
114+
115+
The initializer
88116
"""
89117
)
90118
}
@@ -96,12 +124,10 @@ final class HoverTests: XCTestCase {
96124
func 1️⃣test(_ a: Int, _ b: Int) { }
97125
""",
98126
expectedContent: ##"""
99-
test(\_:\_:)
100127
```swift
101128
func test(_ a: Int, _ b: Int)
102129
```
103130
104-
---
105131
this is **bold** documentation
106132
"""##
107133
)
@@ -114,12 +140,10 @@ final class HoverTests: XCTestCase {
114140
func 1️⃣*%*(lhs: String, rhs: String) { }
115141
""",
116142
expectedContent: ##"""
117-
\*%\*(\_:\_:)
118143
```swift
119144
func *%* (lhs: String, rhs: String)
120145
```
121146
122-
---
123147
this is *italic* documentation
124148
"""##
125149
)
@@ -135,12 +159,10 @@ final class HoverTests: XCTestCase {
135159
func 1️⃣eatApple() {}
136160
""",
137161
expectedContent: """
138-
eatApple()
139162
```swift
140163
func eatApple()
141164
```
142165
143-
---
144166
Eat an apple
145167
146168
- Precondition: Must have an apple

Tests/SourceKitLSPTests/LocalSwiftTests.swift

-18
Original file line numberDiff line numberDiff line change
@@ -755,8 +755,6 @@ final class LocalSwiftTests: XCTestCase {
755755
func foo(_ bar: Baz)
756756
```
757757
758-
---
759-
760758
"""
761759
)
762760
XCTAssertEqual(
@@ -770,8 +768,6 @@ final class LocalSwiftTests: XCTestCase {
770768
func foo() -> Bar
771769
```
772770
773-
---
774-
775771
"""
776772
)
777773
XCTAssertEqual(
@@ -805,8 +801,6 @@ final class LocalSwiftTests: XCTestCase {
805801
func replacingOccurrences<Target, Replacement>(of target: Target, with replacement: Replacement, options: String.CompareOptions = default, range searchRange: Range<String.Index>? = default) -> String where Target : StringProtocol, Replacement : StringProtocol
806802
```
807803
808-
---
809-
810804
"""
811805
)
812806
}
@@ -823,8 +817,6 @@ final class LocalSwiftTests: XCTestCase {
823817
var foo
824818
```
825819
826-
---
827-
828820
"""
829821
)
830822

@@ -839,8 +831,6 @@ final class LocalSwiftTests: XCTestCase {
839831
var foo
840832
```
841833
842-
---
843-
844834
"""
845835
)
846836
XCTAssertEqual(
@@ -854,8 +844,6 @@ final class LocalSwiftTests: XCTestCase {
854844
var foo
855845
```
856846
857-
---
858-
859847
"""
860848
)
861849

@@ -882,8 +870,6 @@ final class LocalSwiftTests: XCTestCase {
882870
var foo
883871
```
884872
885-
---
886-
887873
"""
888874
)
889875

@@ -1064,8 +1050,6 @@ final class LocalSwiftTests: XCTestCase {
10641050
```swift
10651051
struct String
10661052
```
1067-
1068-
---
10691053
A Unicode s
10701054
10711055
### Discussion
@@ -1147,8 +1131,6 @@ final class LocalSwiftTests: XCTestCase {
11471131
```swift
11481132
struct S
11491133
```
1150-
1151-
---
11521134
### Discussion
11531135
11541136
```swift

0 commit comments

Comments
 (0)