Skip to content

Commit 0d15fdf

Browse files
Add markdown escaping for names in hover request
1 parent 20662cd commit 0d15fdf

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

Sources/SourceKit/sourcekitd/SwiftLanguageServer.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,22 @@ extension SwiftLanguageServer {
444444
return
445445
}
446446

447-
var result = "# \(name)"
447+
/// Prepend backslash to all ASCII punctuation, to prevent it
448+
/// from being interpreted as markdown.
449+
///
450+
/// Any ASCII punctuation character may be backslash-escaped.
451+
/// https://spec.commonmark.org/0.29/#backslash-escapes
452+
func escapeMarkdown(_ str: String) -> String {
453+
func isAsciiPunctuation(_ char: Character) -> Bool {
454+
let asciiPunctuation = Set<Character>(##"""
455+
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
456+
"""##)
457+
return asciiPunctuation.contains(char)
458+
}
459+
return String(str.flatMap({ isAsciiPunctuation($0) ? ["\\", $0] : [$0] }))
460+
}
461+
462+
var result = "# \(escapeMarkdown(name))"
448463
if let doc = cursorInfo.documentationXML {
449464
result += """
450465

Tests/SourceKitTests/LocalSwiftTests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,62 @@ final class LocalSwiftTests: XCTestCase {
623623
}
624624
}
625625

626+
func testHoverNameEscaping() {
627+
let url = URL(fileURLWithPath: "/a.swift")
628+
sk.allowUnexpectedNotification = true
629+
630+
sk.send(DidOpenTextDocument(textDocument: TextDocumentItem(
631+
url: url,
632+
language: .swift,
633+
version: 1,
634+
text: """
635+
/// this is **bold** documentation
636+
func test(_ a: Int, _ b: Int) { }
637+
/// this is *italic* documentation
638+
func *%*(lhs: String, rhs: String) { }
639+
""")))
640+
641+
do {
642+
let resp = try! sk.sendSync(HoverRequest(
643+
textDocument: TextDocumentIdentifier(url),
644+
position: Position(line: 1, utf16index: 7)))
645+
646+
XCTAssertNotNil(resp)
647+
if let hover = resp {
648+
XCTAssertNil(hover.range)
649+
XCTAssertEqual(hover.contents.kind, .markdown)
650+
XCTAssertEqual(hover.contents.value, ##"""
651+
# test\(\_\:\_\:\)
652+
```
653+
func test(_ a: Int, _ b: Int)
654+
```
655+
656+
this is **bold** documentation
657+
"""##)
658+
}
659+
}
660+
661+
do {
662+
let resp = try! sk.sendSync(HoverRequest(
663+
textDocument: TextDocumentIdentifier(url),
664+
position: Position(line: 3, utf16index: 7)))
665+
666+
XCTAssertNotNil(resp)
667+
if let hover = resp {
668+
XCTAssertNil(hover.range)
669+
XCTAssertEqual(hover.contents.kind, .markdown)
670+
XCTAssertEqual(hover.contents.value, ##"""
671+
# \*\%\*\(\_\:\_\:\)
672+
```
673+
func *%* (lhs: String, rhs: String)
674+
```
675+
676+
this is *italic* documentation
677+
"""##)
678+
}
679+
}
680+
}
681+
626682
func testDocumentSymbolHighlight() {
627683
let url = URL(fileURLWithPath: "/a.swift")
628684
sk.allowUnexpectedNotification = true

Tests/SourceKitTests/XCTestManifests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extension LocalSwiftTests {
5858
("testDocumentSymbolHighlight", testDocumentSymbolHighlight),
5959
("testEditing", testEditing),
6060
("testHover", testHover),
61+
("testHoverNameEscaping", testHoverNameEscaping),
6162
("testSymbolInfo", testSymbolInfo),
6263
("testXMLToMarkdownComment", testXMLToMarkdownComment),
6364
("testXMLToMarkdownDeclaration", testXMLToMarkdownDeclaration),

0 commit comments

Comments
 (0)