// 输出渲染:text / json / html。
///|
fn text_of(value : String?) -> String {
match value {
Some(text) => text
None => ""
}
}
///|
fn number_of(value : Int?) -> String {
match value {
Some(number) => number.to_string()
None => ""
}
}
///|
fn json_number(value : Int?) -> String {
match value {
Some(number) => number.to_string()
None => "null"
}
}
///|
fn json_escape(s : String) -> String {
let builder = StringBuilder()
for c in s {
match c {
'"' => builder.write_string("\\\"")
'\\' => builder.write_string("\\\\")
'\n' => builder.write_string("\\n")
'\r' => builder.write_string("\\r")
'\t' => builder.write_string("\\t")
_ => {
let code = c.to_int()
if code < 32 {
builder.write_string("\\u")
builder.write_string(hex4(code))
} else {
builder.write_char(c)
}
}
}
}
builder.to_string()
}
///|
fn hex4(n : Int) -> String {
let digits = "0123456789abcdef"
let builder = StringBuilder()
let mut shift = 12
while shift >= 0 {
let digit = (n >> shift) & 15
match digits.get_char(digit) {
Some(c) => builder.write_char(c)
None => ()
}
shift = shift - 4
}
builder.to_string()
}
///|
fn html_escape(s : String) -> String {
let builder = StringBuilder()
for c in s {
match c {
'&' => builder.write_string("&")
'<' => builder.write_string("<")
'>' => builder.write_string(">")
'"' => builder.write_string(""")
'\'' => builder.write_string("'")
_ => builder.write_char(c)
}
}
builder.to_string()
}
///|
/// 渲染为纯文本(`-` / `+` 前缀)。
pub fn render_text(result : DiffResult) -> String {
let builder = StringBuilder()
for row in result.rows {
match row.kind {
Equal => {
builder.write_string(" ")
builder.write_string(text_of(row.old_line))
builder.write_string("\n")
}
Delete => {
builder.write_string("- ")
builder.write_string(text_of(row.old_line))
builder.write_string("\n")
}
Insert => {
builder.write_string("+ ")
builder.write_string(text_of(row.new_line))
builder.write_string("\n")
}
Change => {
builder.write_string("- ")
builder.write_string(text_of(row.old_line))
builder.write_string("\n+ ")
builder.write_string(text_of(row.new_line))
builder.write_string("\n")
}
}
}
builder.to_string()
}
///|
/// 渲染为单行 JSON。
pub fn render_json(result : DiffResult) -> String {
let builder = StringBuilder()
builder.write_string("{\"stats\":{\"added\":")
builder.write_string(result.stats.added.to_string())
builder.write_string(",\"removed\":")
builder.write_string(result.stats.removed.to_string())
builder.write_string(",\"changed\":")
builder.write_string(result.stats.changed.to_string())
builder.write_string(",\"unchanged\":")
builder.write_string(result.stats.unchanged.to_string())
builder.write_string("},\"rows\":[")
let mut first = true
for row in result.rows {
if !first {
builder.write_string(",")
}
let kind = match row.kind {
Equal => "equal"
Delete => "delete"
Insert => "insert"
Change => "change"
}
builder.write_string("{\"kind\":\"\{kind}\",\"old\":\"")
builder.write_string(json_escape(text_of(row.old_line)))
builder.write_string("\",\"new\":\"")
builder.write_string(json_escape(text_of(row.new_line)))
builder.write_string("\",\"oldNo\":")
builder.write_string(json_number(row.old_no))
builder.write_string(",\"newNo\":")
builder.write_string(json_number(row.new_no))
builder.write_string("}")
first = false
}
builder.write_string("],\"patch\":\"")
builder.write_string(json_escape(result.patch))
builder.write_string("\"}")
builder.to_string()
}
///|
fn render_spans(spans : Array[WordSpan], fallback : String) -> String {
if spans.length() == 0 {
return html_escape(fallback)
}
let builder = StringBuilder()
for span in spans {
let class_name = match span.kind {
Equal => "eq"
Delete => "del"
Insert => "ins"
}
builder.write_string("")
builder.write_string(html_escape(span.text))
builder.write_string("")
}
builder.to_string()
}
///|
/// 渲染为独立 HTML 页面(左右对照)。
pub fn render_html(
result : DiffResult,
old_name : String,
new_name : String,
) -> String {
let builder = StringBuilder()
builder.write_string(
"\n\n\n\n",
)
builder.write_string("mbt-diffview\n\n\n\n\n\n")
builder.write_string(
"\n",
)
builder.write_string(
" | \{html_escape(old_name)} | | \{html_escape(new_name)} |
\n\n",
)
for row in result.rows {
let class_name = match row.kind {
Equal => "equal"
Delete => "delete"
Insert => "insert"
Change => "change"
}
builder.write_string("")
builder.write_string("| \{number_of(row.old_no)} | ")
builder.write_string("")
builder.write_string(render_spans(row.old_spans, text_of(row.old_line)))
builder.write_string(" | ")
builder.write_string("\{number_of(row.new_no)} | ")
builder.write_string("")
builder.write_string(render_spans(row.new_spans, text_of(row.new_line)))
builder.write_string(" |
\n")
}
builder.write_string("\n
\n\n\n")
builder.to_string()
}