Syntax Highlighting With Line Numbering And Classes In Rust

This page is a work in progress. This is an attempt to switch to classes via find/replace since using the classes out of syntect closes spans on differnet lines sometimes. That's fine in general, but I'm tyring to add line numbers and it messes with that.

UPDATE: okay, trying to use just the tokens didn't work either. The issue seems to be when a single type of text breaks across multiple lines. So, the original way farhter below works fine.

rust

```cargo
[dependencies]
syntect = "5.2.0"
```

use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
use syntect::parsing::SyntaxSet;

fn main() {
  let lang = "html";
  let code = r#"<p>
a
b 
c
</p>"#;

  let output = highlight(code, lang);
  println!("{}", output);
}

fn highlight(code: &str, lang: &str) -> String {
    let ps = SyntaxSet::load_defaults_newlines();
    let ts = ThemeSet::load_defaults();
    let syntax = ps.find_syntax_by_token(lang).unwrap_or_else(|| ps.find_syntax_plain_text());
    let mut h = HighlightLines::new(syntax, &ts.themes["Solarized (dark)"]);
    let regions = h.highlight_line(code, &ps).unwrap();
    let mut html = styled_line_to_highlighted_html(&regions[..], IncludeBackground::No).unwrap();
    let replacements = vec![
      ("color:#839496;", "alfa "), 
      ("color:#dc322f;", "bravo "), 
      ("color:#002b36;", "charlie "), 
      ("color:#2aa198;", "delta "), 
      ("color:#b58900;", "echo "), 
      ("color:#657b83;", "foxtrot "), 
      ("color:#268bd2;", "golf "), 
      ("color:#d33682;", "hotel "), 
      ("color:#6c71c4;", "india "), 
      ("color:#6e2e32;", "juliett "), 
      ("color:#586e75;", "kilo "), 
      ("color:#cb4b16;", "lima "), 
      ("color:#93a1a1;", "mike "), 
      ("color:#859900;", "november "), 
      ("background-color:#839496;", "alfa "), 
      ("background-color:#dc322f;", "bravo "), 
      ("background-color:#002b36;", "charlie "), 
      ("background-color:#b58900;", "delta "), 
      ("background-color:#657b83;", "echo "), 
      ("background-color:#6e2e32;", "foxtrot "), 
      ("background-color:#586e75;", "golf "), 
      ("background-color:#cb4b16;", "hotel "), 
    ];
    replacements.iter().for_each(|r| html = html.replace(r.0, r.1));
    html = html.replace("span style", "span class");
    html
}
            
<p>
a
b 
c
</p>
        

NOTE: There may be an issue with with where sometimes it puts closing spans tags on trialign lines. I'm looking at going to a method of doing direct token highlighting and then converting them to classes from there. Based off what I'm reading in the docs that seems like it might be more consistent.

rust

```cargo
[dependencies]
syntect = "5.1.0"
```

use syntect::html::{ClassedHTMLGenerator, ClassStyle};
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;

fn main() {
  let code = r#"fn main() {
  println!("Hello, World");
}"#;

  let lang = "rust";
  let output = highlight_code(code, lang);
  println!("{}", output)
}

fn highlight_code(code: &str, lang: &str) -> String {
  let syntax_set = SyntaxSet::load_defaults_newlines();
  let syntax = syntax_set.find_syntax_by_token(&lang).unwrap_or_else(|| syntax_set.find_syntax_plain_text());
  let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
  for line in LinesWithEndings::from(code.trim()) {
      let _ = html_generator.parse_html_for_line_which_includes_newline(line);
  }
  let initial_html = html_generator.finalize();
  let output_html: Vec<_> = initial_html.lines()
    .map(|line| 
        format!(r#"<span class="numberedLine">{}</span>"#, line))
    .collect();
    format!(
        r#"<pre class="numberedLines"><code>{}</code></pre>"#, 
        output_html.join("\n")
    )
}
            
fn main() {
  println!("Hello, World");
}

rust

```cargo
[dependencies]
syntect = "5.1.0"
```
use syntect::html::{ClassedHTMLGenerator, ClassStyle};
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;

fn main() {
let current_code = r#"
x <- 5
y <- 6
x + y
"#;

let syntax_set = SyntaxSet::load_defaults_newlines();
let syntax = syntax_set.find_syntax_by_name("R").unwrap();
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
for line in LinesWithEndings::from(current_code) {
    html_generator.parse_html_for_line_which_includes_newline(line);
}
let output_html = html_generator.finalize();

println!("{}", output_html)

}
            

x <- 5
y <- 6
x + y


warning: unused `Result` that must be used
  --> /Users/alan/.cargo/target/55/19854259915251/_active_nvim_run:20:5
   |
20 |     html_generator.parse_html_for_line_which_includes_newline(line);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: this `Result` may be an `Err` variant, which should be handled
   = note: `#[warn(unused_must_use)]` on by default
help: use `let _ = ...` to ignore the resulting value
   |
20 |     let _ = html_generator.parse_html_for_line_which_includes_newline(line);
   |     +++++++
        
fn main() {
  println!("Hello, World");
}

css

.numberedLines {
  counter-reset: lineNumber;
}

.numberedLine {
  counter-increment: lineNumber;
}

.numberedLine:before {
    display: inline-block;
    color: goldenrod;
    content: counter(lineNumber);
    padding-right: 0.7rem;
    text-align: right;
    width: 2rem;
}
            

This is how I'm doing syntax highlighting in Rust.

This is the main way I'm doing it where instead of adding the styles inline it adds classes.

References