Repository: apple/swift-docc (swift)

Issue:
Custom link titles authored with []() syntax for doc links are silently ignored
Custom link titles for doc links are silently ignored.

If I write a doc link with a title like:

```
Check out [this great function](doc:Sloth/eat(_:quantity:)).
```

the rendered output is

```
Check out eat(_:quantity:).
```

We should use the user-provided title, per the original Markdown spec.

### Checklist

- [x] If possible, I've reproduced the issue using the `main` branch of this package.
- [x] This issue hasn't been addressed in an [existing GitHub issue](https://github.com/apple/swift-docc/issues).

### Steps to Reproduce

Author a `doc` link to a symbol or article using a custom title, as described above. Build documentation and notice that in the rendered documentation, the custom title isn't used for the link title.

### Swift-DocC Version Information

**Swift-DocC version:** `5.6`
**Swift Compiler version:** N/A

Radar: rdar://79992417

Pull request description:
<!--
If you're opening a PR to cherry-pick a change for a release branch, use this template instead:
https://github.com/apple/swift-docc/blob/main/.github/PULL_REQUEST_TEMPLATE/CHERRY_PICK.md
-->

Bug/issue #, if applicable: 

Close #271 

## Summary

```
## Overview

Hello

Check out [Hi](https://hi.com)

Check out [this great function](doc:Sloth/eat(_:quantity:))

Check out [this great function ![random image](https://picsum.photos/200)](doc:Sloth/eat(_:quantity:))

- <doc:Sloth/eat(_:quantity:)>
```

Before the PR (current behavior)
<img width="431" alt="image" src="https://user-images.githubusercontent.com/43724855/224548281-8d038865-4980-4815-aafc-09aa77662634.png">

After the PR (expected behavior)
<img width="578" alt="image" src="https://user-images.githubusercontent.com/43724855/224548859-d3c9f5d7-9182-472a-a5e9-6e229fbde51f.png">

## Dependencies

~apple/swift-markdown#82~
apple/swift-markdown#110

## Testing

See RenderContentCompilerTests.swift

https://github.com/apple/swift/pull/61506

## Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

- [x] Added tests
- [x] Ran the `./bin/test` script and it succeeded
- [x] Updated documentation if necessary

Patch:
diff --git a/Package.resolved b/Package.resolved
index aa3320368a..6fc2c43930 100644
--- a/Package.resolved
+++ b/Package.resolved
@@ -60,7 +60,7 @@
         "repositoryURL": "https://github.com/apple/swift-markdown.git",
         "state": {
           "branch": "main",
-          "revision": "4cbd46eab22cbeef307095d17958b6ed23b5f38f",
+          "revision": "36b71b380ca9cb7497fc24416f8b77721eaf7330",
           "version": null
         }
       },
diff --git a/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift b/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift
index 9cb761a71a..ed7e28c023 100644
--- a/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift
+++ b/Sources/SwiftDocC/Model/Rendering/RenderContentCompiler.swift
@@ -165,7 +165,30 @@ struct RenderContentCompiler: MarkupVisitor {
             return [RenderInlineContent.text(link.plainText)]
         }
         
-        return [RenderInlineContent.reference(identifier: .init(resolved.absoluteString), isActive: true, overridingTitle: nil, overridingTitleInlineContent: nil)]
+        let linkTitleInlineContent = link.children.flatMap { visit($0) } as! [RenderInlineContent]
+        let plainTextLinkTitle = linkTitleInlineContent.plainText
+        let overridingTitle = plainTextLinkTitle.isEmpty ? nil : plainTextLinkTitle
+        let overridingTitleInlineContent = linkTitleInlineContent.isEmpty ? nil : linkTitleInlineContent
+        
+        let useOverriding: Bool
+        if link.isAutolink { // If the link is an auto link, we don't use overriding info
+            useOverriding = false
+        } else if let overridingTitle = overridingTitle,
+                  overridingTitle.hasPrefix(ResolvedTopicReference.urlScheme + ":"),
+                  destination.hasPrefix(ResolvedTopicReference.urlScheme + "://"),
+                  destination.hasSuffix(overridingTitle.dropFirst((ResolvedTopicReference.urlScheme + ":").count)) { // If the link is a transformed doc link, we don't use overriding info
+            useOverriding = false
+        } else {
+            useOverriding = true
+        }
+        return [
+            RenderInlineContent.reference(
+                identifier: .init(resolved.absoluteString),
+                isActive: true,
+                overridingTitle: useOverriding ? overridingTitle : nil,
+                overridingTitleInlineContent: useOverriding ? overridingTitleInlineContent : nil
+            )
+        ]
     }
     
     mutating func resolveTopicReference(_ destination: String) -> ResolvedTopicReference? {
@@ -194,7 +217,7 @@ struct RenderContentCompiler: MarkupVisitor {
     func resolveSymbolReference(destination: String) -> ResolvedTopicReference? {
         if let cached = context.documentationCacheBasedLinkResolver.referenceFor(absoluteSymbolPath: destination, parent: identifier) {
             return cached
-        } 
+        }
 
         // The symbol link may be written with a scheme and bundle identifier.
         let url = ValidatedURL(parsingExact: destination)?.requiring(scheme: ResolvedTopicReference.urlScheme) ?? ValidatedURL(symbolPath: destination)


Test patch:
diff --git a/Tests/SwiftDocCTests/Rendering/RenderContentCompilerTests.swift b/Tests/SwiftDocCTests/Rendering/RenderContentCompilerTests.swift
new file mode 100644
index 0000000000..87488caa22
--- /dev/null
+++ b/Tests/SwiftDocCTests/Rendering/RenderContentCompilerTests.swift
@@ -0,0 +1,134 @@
+/*
+ This source file is part of the Swift.org open source project
+
+ Copyright (c) 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 Swift project authors
+*/
+
+import Foundation
+import Markdown
+@testable import SwiftDocC
+import XCTest
+
+class RenderContentCompilerTests: XCTestCase {
+    func testLinkOverrideTitle() throws {
+        let (bundle, context) = try testBundleAndContext(named: "TestBundle")
+        var compiler = RenderContentCompiler(context: context, bundle: bundle, identifier: ResolvedTopicReference(bundleIdentifier: bundle.identifier, path: "/path", fragment: nil, sourceLanguage: .swift))
+
+        let source = """
+        [Example](http://example.com)
+        
+        [Custom Title](doc:UNRESOVLED)
+        
+        [Custom Title](doc:article)
+        
+        [Custom Image Content ![random image](https://example.com/test.png)](doc:article2)
+        
+        <doc:UNRESOVLED>
+        
+        <doc:article3>
+        """
+        let document = Document(parsing: source)
+        let expectedDump = """
+        Document
+        ├─ Paragraph
+        │  └─ Link destination: "http://example.com"
+        │     └─ Text "Example"
+        ├─ Paragraph
+        │  └─ Link destination: "doc:UNRESOVLED"
+        │     └─ Text "Custom Title"
+        ├─ Paragraph
+        │  └─ Link destination: "doc:article"
+        │     └─ Text "Custom Title"
+        ├─ Paragraph
+        │  └─ Link destination: "doc:article2"
+        │     ├─ Text "Custom Image Content "
+        │     └─ Image source: "https://example.com/test.png" title: ""
+        │        └─ Text "random image"
+        ├─ Paragraph
+        │  └─ Link destination: "doc:UNRESOVLED"
+        │     └─ Text "doc:UNRESOVLED"
+        └─ Paragraph
+           └─ Link destination: "doc:article3"
+              └─ Text "doc:article3"
+        """
+        XCTAssertEqual(document.debugDescription(), expectedDump)
+
+        let result = document.children.flatMap { compiler.visit($0) }
+        XCTAssertEqual(result.count, 6)
+        
+        do {
+            guard case let .paragraph(paragraph) = result[0] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let link = RenderInlineContent.reference(
+                identifier: .init(forExternalLink: "http://example.com"),
+                isActive: true,
+                overridingTitle: nil,
+                overridingTitleInlineContent: nil
+            )
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [link]))
+        }
+        do {
+            guard case let .paragraph(paragraph) = result[1] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let text = RenderInlineContent.text("Custom Title")
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [text]))
+        }
+        do {
+            guard case let .paragraph(paragraph) = result[2] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let link = RenderInlineContent.reference(
+                identifier: .init("doc://org.swift.docc.example/documentation/Test-Bundle/article"),
+                isActive: true,
+                overridingTitle: "Custom Title",
+                overridingTitleInlineContent: [.text("Custom Title")])
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [link]))
+        }
+        do {
+            guard case let .paragraph(paragraph) = result[3] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let link = RenderInlineContent.reference(
+                identifier: .init("doc://org.swift.docc.example/documentation/Test-Bundle/article2"),
+                isActive: true,
+                overridingTitle: "Custom Image Content ",
+                overridingTitleInlineContent: [
+                    RenderInlineContent.text("Custom Image Content "),
+                    RenderInlineContent.image(identifier: .init(forExternalLink: "https://example.com/test.png"), metadata: nil)
+                ]
+            )
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [link]))
+        }
+        do {
+            guard case let .paragraph(paragraph) = result[4] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let text = RenderInlineContent.text("doc:UNRESOVLED")
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [text]))
+        }
+        do {
+            guard case let .paragraph(paragraph) = result[5] as? RenderBlockContent else {
+                XCTFail("RenderCotent result is not the expected RenderBlockContent.paragraph(Paragraph)")
+                return
+            }
+            let link = RenderInlineContent.reference(
+                identifier: .init("doc://org.swift.docc.example/documentation/Test-Bundle/article3"),
+                isActive: true,
+                overridingTitle: nil,
+                overridingTitleInlineContent: nil
+            )
+            XCTAssertEqual(paragraph, RenderBlockContent.Paragraph(inlineContent: [link]))
+        }
+    }
+}