gitbay/Repos/Definition.swift

v1.9.0
gitbay-ios/gitbay/Repos/Definition.swift history · blame · raw

45 lines · 1938 bytes

 1import Foundation
 2import SwiftUI
 3
 4/// Where a name a file uses is defined, from `repo definitions`
 5/// (krz/gitbay#333): the first definition, and how many there are. A name defined
 6/// once links to its line; one defined more often to a search.
 7nonisolated struct Definition: Decodable, Sendable, Hashable {
 8    let name: String
 9    let count: Int
10    let path: String
11    let line: Int
12
13    private static let scheme = "gitbay-definition"
14    /// The index's names, as the server matches them.
15    nonisolated(unsafe) private static let identifier = /[\p{L}_$][\p{L}\p{N}_$]*/
16
17    /// `line` with each defined name marked as a link the file view opens.
18    /// The highlighting stays; names the index does not define stay plain.
19    static func link(_ line: AttributedString, definitions: [String: Definition]) -> AttributedString {
20        guard !definitions.isEmpty else { return line }
21        var out = line
22        let text = String(line.characters)
23        for match in text.matches(of: identifier) {
24            let name = String(match.output)
25            guard definitions[name] != nil,
26                  let lower = AttributedString.Index(match.range.lowerBound, within: out),
27                  let upper = AttributedString.Index(match.range.upperBound, within: out) else { continue }
28            var components = URLComponents()
29            components.scheme = scheme
30            components.host = "name"
31            components.path = "/" + name
32            out[lower..<upper].link = components.url
33            out[lower..<upper].underlineStyle = .init(pattern: .dot)
34        }
35        return out
36    }
37
38    /// The name a link from `link(_:definitions:)` carries; nil for any
39    /// other URL.
40    static func name(in url: URL) -> String? {
41        guard url.scheme == scheme else { return nil }
42        let path = url.path(percentEncoded: false)
43        return path.hasPrefix("/") ? String(path.dropFirst()) : nil
44    }
45}