[{"data":1,"prerenderedAt":156},["ShallowReactive",2],{"article-15":3},{"id":4,"title":5,"body":6,"create":145,"description":12,"extension":146,"labels":147,"locked":148,"meta":149,"navigation":150,"path":151,"seo":152,"stem":153,"update":154,"__hash__":155},"articles/article/15.md","Rust 中 Syntect 如何自定义生成的 HTML 代码",{"type":7,"value":8,"toc":142},"minimark",[9,13,29,39,52,58,68,74,85,91,98,106,112,122,128,134],[10,11,12],"p",{},"本文仅讨论自定义最终生成的 HTML 文本，如代码行是否用标签包含，code 标签外添加的 pre 标签怎么自定义样式，不包含自定义主题。",[10,14,15,16,20,21,28],{},"在 ",[17,18,19],"code",{},"syntect"," ",[22,23,27],"a",{"href":24,"rel":25},"https://github.com/trishume/syntect/blob/master/examples/synhtml.rs",[26],"nofollow","代码库提供的案例","中，将代码文件转成行内样式的\nHTML 文本的实现如下：",[30,31,37],"pre",{"className":32,"code":34,"language":35,"meta":36},[33],"language-rust","//! Prints highlighted HTML for a file to stdout.\n//! Basically just wraps a body around `highlighted_html_for_file`\nuse syntect::highlighting::{Color, ThemeSet};\nuse syntect::html::highlighted_html_for_file;\nuse syntect::parsing::SyntaxSet;\n\nfn main() {\n    let ss = SyntaxSet::load_defaults_newlines();\n    let ts = ThemeSet::load_defaults();\n\n    let args: Vec\u003CString> = std::env::args().collect();\n    if args.len() \u003C 2 {\n        println!(\"Please pass in a file to highlight\");\n        return;\n    }\n\n    let style = \"\n        pre {\n            font-size:13px;\n            font-family: Consolas, \\\"Liberation Mono\\\", Menlo, Courier, monospace;\n        }\";\n    println!(\n        \"\u003Chead>\u003Ctitle>{}\u003C/title>\u003Cstyle>{}\u003C/style>\u003C/head>\",\n        &args[1], style\n    );\n    let theme = &ts.themes[\"base16-ocean.dark\"];\n    let c = theme.settings.background.unwrap_or(Color::WHITE);\n    println!(\n        \"\u003Cbody style=\\\"background-color:#{:02x}{:02x}{:02x};\\\">\\n\",\n        c.r, c.g, c.b\n    );\n    let html = highlighted_html_for_file(&args[1], &ss, theme).unwrap();\n    println!(\"{}\", html);\n    println!(\"\u003C/body>\");\n}\n","rust","",[17,38,34],{"__ignoreMap":36},[10,40,41,42,45,46,51],{},"阅读代码发现，大部分的代码是加载一些预设，然后输出模板，最终将代码文件转成 HTML 文本的代码是 ",[17,43,44],{},"highlighted_html_for_file","\n方法，经查看，该方法的",[22,47,50],{"href":48,"rel":49},"https://docs.rs/syntect/latest/src/syntect/html.rs.html#296-318",[26],"源码","如下：",[30,53,56],{"className":54,"code":55,"language":35,"meta":36},[33],"pub fn highlighted_html_for_file\u003CP: AsRef\u003CPath>>(\n    path: P,\n    ss: &SyntaxSet,\n    theme: &Theme,\n) -> Result\u003CString, Error> {\n    let mut highlighter = HighlightFile::new(path, ss, theme)?;\n    let (mut output, bg) = start_highlighted_html_snippet(theme);\n\n    let mut line = String::new();\n    while highlighter.reader.read_line(&mut line)? > 0 {\n        {\n            let regions = highlighter.highlight_lines.highlight_line(&line, ss)?;\n            append_highlighted_html_for_styled_line(\n                &regions[..],\n                IncludeBackground::IfDifferent(bg),\n                &mut output,\n            )?;\n        }\n        line.clear();\n    }\n    output.push_str(\"\u003C/pre>\\n\");\n    Ok(output)\n}\n",[17,57,55],{"__ignoreMap":36},[10,59,60,61,20,64,51],{},"有同类型的方法 ",[17,62,63],{},"highlighted_html_for_string",[22,65,50],{"href":66,"rel":67},"https://docs.rs/syntect/latest/src/syntect/html.rs.html#269-288",[26],[30,69,72],{"className":70,"code":71,"language":35,"meta":36},[33],"pub fn highlighted_html_for_string(\n    s: &str,\n    ss: &SyntaxSet,\n    syntax: &SyntaxReference,\n    theme: &Theme,\n) -> Result\u003CString, Error> {\n    let mut highlighter = HighlightLines::new(syntax, theme);\n    let (mut output, bg) = start_highlighted_html_snippet(theme);\n\n    for line in LinesWithEndings::from(s) {\n        let regions = highlighter.highlight_line(line, ss)?;\n        append_highlighted_html_for_styled_line(\n            &regions[..],\n            IncludeBackground::IfDifferent(bg),\n            &mut output,\n        )?;\n    }\n    output.push_str(\"\u003C/pre>\\n\");\n    Ok(output)\n}\n",[17,73,71],{"__ignoreMap":36},[10,75,76,77,80,81,84],{},"对比可以发现，生成 HTML 文本主要的两个方法有：",[17,78,79],{},"highlighter.highlight_line"," 和 ",[17,82,83],{},"append_highlighted_html_for_styled_line","\n。那么我们就可以使用这两个方法自己来实现最终生成的 HTML 文本：",[30,86,89],{"className":87,"code":88,"language":35,"meta":36},[33],"pub mod code_highlight{\n  use syntect::easy::HighlightLines;\n  use syntect::Error;\n  use syntect::highlighting::ThemeSet;\n  use syntect::html::{append_highlighted_html_for_styled_line, IncludeBackground};\n  use syntect::parsing::SyntaxSet;\n  use syntect::util::LinesWithEndings;\n\n  pub fn highlight(code: &str, lang: &str) -> Result\u003CString, Error>{\n    let ps = SyntaxSet::load_defaults_newlines();\n    let ts = ThemeSet::load_defaults();\n    let theme = &ts.themes[\"InspiredGitHub\"];\n    let syntax = ps.find_syntax_by_extension(lang).unwrap();\n    let mut highlighter = HighlightLines::new(syntax, theme);\n    let mut output = String::from(\"\u003Cpre>\");\n    for line in LinesWithEndings::from(code) {\n      let regions = highlighter.highlight_line(line, &ps)?;\n      append_highlighted_html_for_styled_line(\n        &regions[..],\n        IncludeBackground::No, // 不在代码标签中添加 `style:\"background-color: xxxx\"`\n        &mut output,\n      )?;\n    }\n    output.push_str(\"\u003C/pre>\\n\");\n    Ok(output)\n  }\n}\n\n#[cfg(test)]\nmod test{\n  use syntect::highlighting::ThemeSet;\n  use super::code_highlight;\n  #[test]\n  fn test_my_highlight(){\n    let result = code_highlight::highlight(\"let a = 1 + 2;\\nconsole.log(a)\", \"js\")\n      .unwrap_or_else(|_| String::from(\"error\"));\n    print!(\"{}\", result)\n  }\n\n  #[test]\n  fn traversal_theme(){\n    let ts = ThemeSet::load_defaults();\n    for (name, _the) in ts.themes.iter(){\n      println!(\"{}\", name)\n    }\n  }\n}\n",[17,90,88],{"__ignoreMap":36},[10,92,93,94,97],{},"运行 ",[17,95,96],{},"test_my_highlight"," 方法的结果为：",[30,99,104],{"className":100,"code":102,"language":103,"meta":36},[101],"language-html","\u003Cpre>\u003Cspan style=\"font-weight:bold;color:#a71d5d;\">let \u003C/span>\u003Cspan style=\"color:#323232;\">a \u003C/span>\u003Cspan style=\"font-weight:bold;color:#a71d5d;\">= \u003C/span>\u003Cspan style=\"color:#0086b3;\">1 \u003C/span>\u003Cspan style=\"font-weight:bold;color:#a71d5d;\">+ \u003C/span>\u003Cspan style=\"color:#0086b3;\">2\u003C/span>\u003Cspan style=\"color:#323232;\">;\n\u003C/span>\u003Cspan style=\"color:#795da3;\">console\u003C/span>\u003Cspan style=\"color:#323232;\">.\u003C/span>\u003Cspan style=\"color:#0086b3;\">log\u003C/span>\u003Cspan style=\"color:#323232;\">(a)\u003C/span>\u003C/pre>\n","html",[17,105,102],{"__ignoreMap":36},[107,108,109],"blockquote",{},[10,110,111],{},"这里的代码有多余的换行符，可以自行处理",[10,113,114,115,118,119,121],{},"观察运行结果，不难发现可以通过修改 ",[17,116,117],{},"output"," 变量来实现想要的效果。如果想要在代码行外添加 ",[17,120,10],{}," 标签，只需要在循环中加入文本即可：",[30,123,126],{"className":124,"code":125,"language":35,"meta":36},[33],"for line in LinesWithEndings::from(code) {\n  let regions = highlighter.highlight_line(line, &ps)?;\n  output.push_str(\"\u003Cp>\");\n  append_highlighted_html_for_styled_line(\n    &regions[..],\n    IncludeBackground::No,\n    &mut output,\n  )?;\n  output.push_str(\"\u003C/p>\")\n}\n",[17,127,125],{"__ignoreMap":36},[10,129,130,131,133],{},"对于代码高亮的主题，  ",[17,132,19],{}," 中的默认主题包中包含如下主题：",[30,135,140],{"className":136,"code":138,"language":139,"meta":36},[137],"language-text","InspiredGitHub\nSolarized (dark)\nSolarized (light)\nbase16-eighties.dark\nbase16-mocha.dark\nbase16-ocean.dark\nbase16-ocean.light\n","text",[17,141,138],{"__ignoreMap":36},{"title":36,"searchDepth":143,"depth":143,"links":144},2,[],"2024-09-25T01:19:58.000Z","md",[35],false,{},true,"/article/15",{"title":5,"description":12},"article/15",null,"XUh56_hoDtGs-xkma7cQ-puWwSlEYrHcqV3zVZPGgzw",1755235549196]