import Foundation /// The change a review thread's ```suggestion block proposes, as /// `mr threads` carries it: lines `startLine` through `endLine` of `path`, /// `original` replaced by `replacement`. Both texts end every line with /// its terminator, so "" is no lines. nonisolated struct Suggestion: Decodable, Sendable, Hashable { let path: String let startLine: Int64 let endLine: Int64 let original: String let replacement: String /// The lines it replaces have changed at the head, or the file moved; /// it cannot be applied then, and `reason` says which. let outdated: Bool let reason: String? /// "server" where `mr apply-suggestion` commits it; "local" where the /// repository requires signed commits, which only a clone can make. let apply: String enum CodingKeys: String, CodingKey { case path, original, replacement, outdated, reason, apply case startLine = "start_line" case endLine = "end_line" } var appliesOnServer: Bool { apply == "server" } /// The text as lines, without the terminator the last one carries. static func lines(_ text: String) -> [String] { guard !text.isEmpty else { return [] } var lines = text.components(separatedBy: "\n") if lines.last == "" { lines.removeLast() } return lines } /// A comment's text without its ```suggestion block, which the thread /// draws as replaced and proposed lines instead. static func withoutBlock(_ body: String) -> String { guard let start = body.range(of: "```suggestion") else { return body } let afterOpen = body[start.upperBound...] guard let close = afterOpen.range(of: "```") else { return body } let rest = body[..