From 544ef9feba3cde30244085f7836a58bf301a987a Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 30 Jul 2020 14:23:18 -0400 Subject: [PATCH] Use `` instead of `` in documentation (#44) * Use `` instead of `` in documentation The `` tag was deprecated in [HTML 4.01]. Let's use the verbose but standardized `` tag with discouraged but supported inline styles instead. (I was wrong when I said it was deprecated before I was born; though [HTML 4]'s spec was published in 1997, about 6 months before I was born, `` was only deprecated in HTML 4.01, published 2 years later when I was about 16 months old.) [HTML 4]: https://www.w3.org/TR/REC-html40-971218/ [HTML 4.01]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/font * Add script to convert `` to `` in docs ``` $ ./scripts/fix_html_examples.py --help usage: fix_html_examples.py [-h] file [file ...] Convert HTML from Gnome terminal's 'Copy as HTML' feature to use modern tags and inline CSS. This script is idempotent, i.e. multiple invocations will not change the output past the first invocation. positional arguments: file Rust file to update
 blocks in.

optional arguments:
  -h, --help  show this help message and exit

$ ./scripts/fix_html_examples.py src/*.rs
Nothing to fix in src/config.rs
Nothing to fix in src/handler.rs
Updated example colored output in src/lib.rs
Nothing to fix in src/private.rs
Nothing to fix in src/writers.rs

$ ./scripts/fix_html_examples.py src/*.rs
Nothing to fix in src/config.rs
Nothing to fix in src/handler.rs
Nothing to fix in src/lib.rs
Nothing to fix in src/private.rs
Nothing to fix in src/writers.rs
```

Co-authored-by: Rebecca Turner 
---
 scripts/default.nix          |  13 +++
 scripts/fix_html_examples.py | 126 +++++++++++++++++++++++++++
 src/lib.rs                   | 162 +++++++++++++++++------------------
 3 files changed, 220 insertions(+), 81 deletions(-)
 create mode 100644 scripts/default.nix
 create mode 100755 scripts/fix_html_examples.py

diff --git a/scripts/default.nix b/scripts/default.nix
new file mode 100644
index 0000000..3db90c4
--- /dev/null
+++ b/scripts/default.nix
@@ -0,0 +1,13 @@
+{ pkgs ? import  { } }:
+let
+  inherit (pkgs) stdenv lib python38;
+
+  py = python38.withPackages (pypkgs: with pypkgs; [ beautifulsoup4 ]);
+
+in stdenv.mkDerivation {
+  pname = "color-eyre-scripts";
+  version = "0.0.0";
+
+  src = ./.;
+  buildInputs = [ py ];
+}
diff --git a/scripts/fix_html_examples.py b/scripts/fix_html_examples.py
new file mode 100755
index 0000000..daf904e
--- /dev/null
+++ b/scripts/fix_html_examples.py
@@ -0,0 +1,126 @@
+#! /usr/bin/env python3.8
+
+from __future__ import annotations
+
+import argparse
+from argparse import FileType, ArgumentParser
+import enum
+import sys
+
+from bs4 import BeautifulSoup, Tag
+
+
+class LineType(enum.Enum):
+    OUTER_DOC = enum.auto()
+    INNER_DOC = enum.auto()
+    SOURCE = enum.auto()
+
+    @classmethod
+    def from_line(cls, line: str) -> (LineType, str):
+        if line.startswith("//!"):
+            return (cls.OUTER_DOC, line[len("//!") :])
+        elif line.startswith("///"):
+            return (cls.INNER_DOC, line[len("///") :])
+        else:
+            return (cls.SOURCE, line)
+
+    def prefix(self) -> str:
+        if self == LineType.OUTER_DOC:
+            return "//!"
+        elif self == LineType.INNER_DOC:
+            return "///"
+        else:
+            return ""
+
+
+def fix_gnome_html(fh: file) -> str:
+    """Tweaks for fixing "Copy as HTML" output from gnome-terminal
+
+    Reads source from a Rust file.
+    """
+
+    anything_changed = False
+    line_type = LineType.SOURCE
+
+    # Lines of current HTML 
 chunk
+    pre_chunk = []
+    # Lines of processed file
+    ret = []
+
+    for (line_type, stripped_line), line in map(
+        lambda line: (LineType.from_line(line), line), fh.readlines()
+    ):
+        if line_type == LineType.SOURCE:
+            ret.append(line)
+        elif stripped_line.lstrip().startswith(""):
+            pre_chunk.append(stripped_line)
+            if any(" str:
+    """Fixes an individual 
 tag from Gnome.
+
+    Optionally prepends a given prefix to each line in the returned output.
+    """
+    soup = BeautifulSoup(html, "html.parser")
+
+    for pre in soup.find_all("pre"):
+        for tag in pre.find_all("font"):
+            #  -> 
+            tag.name = "span"
+            color = tag.attrs.pop("color")
+            tag["style"] = f"color: {color}"
+
+    return "".join(prefix + line for line in str(soup).splitlines(keepends=True))
+
+
+def main():
+    parser = ArgumentParser(
+        description="""Convert HTML from Gnome terminal's 'Copy as HTML' feature
+        to use modern  tags and inline CSS.
+
+        This script is idempotent, i.e. multiple invocations will not change
+        the output past the first invocation."""
+    )
+    parser.add_argument(
+        "file",
+        nargs="+",
+        type=FileType("r+", encoding="utf-8"),
+        help="""Rust file to update 
 blocks in.""",
+    )
+    args = parser.parse_args()
+    for fh in args.file:
+        if not fh.name.endswith(".rs"):
+            print(
+                "This script only fixes Rust source files; you probably didn't mean to include",
+                fh.name,
+                "so I'll skip processing it.",
+            )
+        new_content = fix_gnome_html(fh)
+        if new_content is not None:
+            print("Updated example colored output in", fh.name)
+            fh.seek(0)
+            fh.write(new_content)
+        else:
+            print("Nothing to fix in", fh.name)
+        fh.close()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/src/lib.rs b/src/lib.rs
index bbac17d..cab891c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,27 +5,27 @@
 //!
 //! `color_eyre` helps you build error reports that look like this:
 //!
-//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
-//!  cargo run --example custom_section
-//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
-//!      Running `target/debug/examples/custom_section`
+//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
+//!  cargo run --example custom_section
+//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
+//!      Running `target/debug/examples/custom_section`
 //! Error:
-//!    0: Unable to read config
-//!    1: cmd exited with non-zero status code
+//!    0: Unable to read config
+//!    1: cmd exited with non-zero status code
 //!
 //! Stderr:
 //!    cat: fake_file: No such file or directory
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!    0: custom_section::output2 with self="cat" "fake_file"
-//!       at examples/custom_section.rs:14
-//!    1: custom_section::read_file with path="fake_file"
-//!       at examples/custom_section.rs:58
-//!    2: custom_section::read_config
-//!       at examples/custom_section.rs:63
+//!    0: custom_section::output2 with self="cat" "fake_file"
+//!       at examples/custom_section.rs:14
+//!    1: custom_section::read_file with path="fake_file"
+//!       at examples/custom_section.rs:58
+//!    2: custom_section::read_config
+//!       at examples/custom_section.rs:63
 //!
-//! Suggestion: try using a file that exists next time
+//! Suggestion: try using a file that exists next time
//! //! ## Setup //! @@ -111,56 +111,56 @@ //! Running `cargo run --example usage` without `RUST_LIB_BACKTRACE` set will produce a minimal //! report like this: //! -//!
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0 took 2s
-//!  cargo run --example usage
-//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
-//!      Running `target/debug/examples/usage`
-//! Jul 05 19:15:58.026  INFO read_config:read_file{path="fake_file"}: Reading file
+//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0 took 2s
+//!  cargo run --example usage
+//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
+//!      Running `target/debug/examples/usage`
+//! Jul 05 19:15:58.026  INFO read_config:read_file{path="fake_file"}: Reading file
 //! Error:
-//!    0: Unable to read config
-//!    1: No such file or directory (os error 2)
+//!    0: Unable to read config
+//!    1: No such file or directory (os error 2)
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!    0: usage::read_file with path="fake_file"
-//!       at examples/usage.rs:32
-//!    1: usage::read_config
-//!       at examples/usage.rs:38
+//!    0: usage::read_file with path="fake_file"
+//!       at examples/usage.rs:32
+//!    1: usage::read_config
+//!       at examples/usage.rs:38
 //!
-//! Suggestion: try using a file that exists next time
+//! Suggestion: try using a file that exists next time
//! //!
//! //! Running `RUST_LIB_BACKTRACE=1 cargo run --example usage` tells `color-eyre` to use the short //! format, which additionally capture a [`backtrace::Backtrace`]: //! -//!
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
-//!  RUST_LIB_BACKTRACE=1 cargo run --example usage
-//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
-//!      Running `target/debug/examples/usage`
-//! Jul 05 19:16:02.853  INFO read_config:read_file{path="fake_file"}: Reading file
+//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
+//!  RUST_LIB_BACKTRACE=1 cargo run --example usage
+//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
+//!      Running `target/debug/examples/usage`
+//! Jul 05 19:16:02.853  INFO read_config:read_file{path="fake_file"}: Reading file
 //! Error:
-//!    0: Unable to read config
-//!    1: No such file or directory (os error 2)
+//!    0: Unable to read config
+//!    1: No such file or directory (os error 2)
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!    0: usage::read_file with path="fake_file"
-//!       at examples/usage.rs:32
-//!    1: usage::read_config
-//!       at examples/usage.rs:38
+//!    0: usage::read_file with path="fake_file"
+//!       at examples/usage.rs:32
+//!    1: usage::read_config
+//!       at examples/usage.rs:38
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-//!                                 ⋮ 5 frames hidden ⋮                               
-//!    6: usage::read_file::haee210cb22460af3
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:35
-//!    7: usage::read_config::ha649ef4ec333524d
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:40
-//!    8: usage::main::hbe443b50eac38236
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:11
-//!                                 ⋮ 10 frames hidden ⋮                              
+//!                                 ⋮ 5 frames hidden ⋮                               
+//!    6: usage::read_file::haee210cb22460af3
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:35
+//!    7: usage::read_config::ha649ef4ec333524d
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:40
+//!    8: usage::main::hbe443b50eac38236
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:11
+//!                                 ⋮ 10 frames hidden ⋮                              
 //!
-//! Suggestion: try using a file that exists next time
+//! Suggestion: try using a file that exists next time
//! //!
//! @@ -168,26 +168,26 @@ //! the full format, which in addition to the above will attempt to include source lines where the //! error originated from, assuming it can find them on the disk. //! -//!
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
-//!  RUST_LIB_BACKTRACE=full cargo run --example usage
-//!     Finished dev [unoptimized + debuginfo] target(s) in 0.05s
-//!      Running `target/debug/examples/usage`
-//! Jul 05 19:16:06.335  INFO read_config:read_file{path="fake_file"}: Reading file
+//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0
+//!  RUST_LIB_BACKTRACE=full cargo run --example usage
+//!     Finished dev [unoptimized + debuginfo] target(s) in 0.05s
+//!      Running `target/debug/examples/usage`
+//! Jul 05 19:16:06.335  INFO read_config:read_file{path="fake_file"}: Reading file
 //! Error:
-//!    0: Unable to read config
-//!    1: No such file or directory (os error 2)
+//!    0: Unable to read config
+//!    1: No such file or directory (os error 2)
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!    0: usage::read_file with path="fake_file"
-//!       at examples/usage.rs:32
+//!    0: usage::read_file with path="fake_file"
+//!       at examples/usage.rs:32
 //!         30 │ }
 //!         31 │
 //!         32 > #[instrument]
 //!         33 │ fn read_file(path: &str) -> Result<(), Report> {
 //!         34 │     info!("Reading file");
-//!    1: usage::read_config
-//!       at examples/usage.rs:38
+//!    1: usage::read_config
+//!       at examples/usage.rs:38
 //!         36 │ }
 //!         37 │
 //!         38 > #[instrument]
@@ -195,31 +195,31 @@
 //!         40 │     read_file("fake_file")
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
-//!                                 ⋮ 5 frames hidden ⋮                               
-//!    6: usage::read_file::haee210cb22460af3
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:35
+//!                                 ⋮ 5 frames hidden ⋮                               
+//!    6: usage::read_file::haee210cb22460af3
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:35
 //!         33 │ fn read_file(path: &str) -> Result<(), Report> {
 //!         34 │     info!("Reading file");
-//!         35 >     Ok(std::fs::read_to_string(path).map(drop)?)
+//!         35 >     Ok(std::fs::read_to_string(path).map(drop)?)
 //!         36 │ }
 //!         37 │
-//!    7: usage::read_config::ha649ef4ec333524d
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:40
+//!    7: usage::read_config::ha649ef4ec333524d
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:40
 //!         38 │ #[instrument]
 //!         39 │ fn read_config() -> Result<(), Report> {
-//!         40 >     read_file("fake_file")
+//!         40 >     read_file("fake_file")
 //!         41 │         .wrap_err("Unable to read config")
 //!         42 │         .suggestion("try using a file that exists next time")
-//!    8: usage::main::hbe443b50eac38236
-//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:11
+//!    8: usage::main::hbe443b50eac38236
+//!       at /home/jlusby/git/yaahc/color-eyre/examples/usage.rs:11
 //!          9 │     color_eyre::install()?;
 //!         10 │
-//!         11 >     Ok(read_config()?)
+//!         11 >     Ok(read_config()?)
 //!         12 │ }
 //!         13 │
-//!                                 ⋮ 10 frames hidden ⋮                              
+//!                                 ⋮ 10 frames hidden ⋮                              
 //!
-//! Suggestion: try using a file that exists next time
+//! Suggestion: try using a file that exists next time
//! //! ### Custom `Section`s for error reports via [`Help`] trait //! @@ -266,27 +266,27 @@ //! Running `cargo run --example custom_section` shows us how these sections are //! included in the output: //! -//!
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0 took 2s
-//!  cargo run --example custom_section
-//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
-//!      Running `target/debug/examples/custom_section`
+//! 
color-eyre on  hooked [$!] is 📦 v0.5.0 via 🦀 v1.44.0 took 2s
+//!  cargo run --example custom_section
+//!     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
+//!      Running `target/debug/examples/custom_section`
 //! Error:
-//!    0: Unable to read config
-//!    1: cmd exited with non-zero status code
+//!    0: Unable to read config
+//!    1: cmd exited with non-zero status code
 //!
 //! Stderr:
 //!    cat: fake_file: No such file or directory
 //!
 //!   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SPANTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 //!
-//!    0: custom_section::output2 with self="cat" "fake_file"
-//!       at examples/custom_section.rs:14
-//!    1: custom_section::read_file with path="fake_file"
-//!       at examples/custom_section.rs:58
-//!    2: custom_section::read_config
-//!       at examples/custom_section.rs:63
+//!    0: custom_section::output2 with self="cat" "fake_file"
+//!       at examples/custom_section.rs:14
+//!    1: custom_section::read_file with path="fake_file"
+//!       at examples/custom_section.rs:58
+//!    2: custom_section::read_config
+//!       at examples/custom_section.rs:63
 //!
-//! Suggestion: try using a file that exists next time
+//! Suggestion: try using a file that exists next time
//! //! Only the `Stderr:` section actually gets included. The `cat` command fails, //! so stdout ends up being empty and is skipped in the final report. This gives