Vendor latest annotate-snippets (#27033)
## Summary This is a first step towards #20411. After this, we would work to either upstream changes or adjust things within Ruff. Performance improvements have already been upstreamed (https://github.com/rust-lang/annotate-snippets-rs/pull/437, https://github.com/rust-lang/annotate-snippets-rs/pull/440) Re-implemented the following from the fork: - Hidden snippets (https://github.com/rust-lang/annotate-snippets-rs/issues/448) - Renderer-level disabling of hyperlinks - Jupyter cell indices (https://github.com/rust-lang/annotate-snippets-rs/issues/449) - Fixable mark (https://github.com/rust-lang/annotate-snippets-rs/issues/450) - `lineno_offset` (maybe not needed if #20648 is taken care of) - anonymized lines affects origin (https://github.com/rust-lang/annotate-snippets-rs/issues/451) - cut indicator (maybe not needed if we can switch to unicode renderer) Additional hacks to minimize behavior change include - Adjust upstream's hidden severity behavior to match Ruff's (https://github.com/rust-lang/annotate-snippets-rs/issues/452) - How newlines are annotated (https://github.com/rust-lang/annotate-snippets-rs/issues/454) - To not indent for line numbers when none will be present (https://github.com/rust-lang/annotate-snippets-rs/issues/453) - Enabling `simd` by default All of the above represents work that needs to be resolved to address #20411. `lib.name` hack is used to simplify maintenance with the assumption that we'll continue working towards #20411. Behavior changes that were included - Tweak when implicit padding lines are present - Fix Origin's line/column is now derived from the primary annotation - Make consistent the rendering of primary/context annotations - Fix snippets to show up for empty source (but may degrade #24528) - Fix handling of term width - Fix rendering of annotations between newline and first character (granted, maybe the caller is wrong) - Tweak multiline annotation markers New or upcoming features annotate-snippets features that we will be able to leverage by moving this forward - `AnnotationKind::Visible` to improve the quality of built-in code-folding if Ruff were to switch to it - `DecorStyle::Unicode` - *(upcoming)* No-graphiscs mode for screen readers (may also be useful for AIs) Deferred: - Moving related `Group`s into the same `Report` - Using `secondary_title` for any `Group` after the first in a `Report` - Using a `Snippet`, instead of titles, for "virtual code" like https://github.com/astral-sh/ruff/pull/27033#discussion_r3622727236 - Switching hidden snippets to `Origin` ## Test Plan Within cpython, I ran ```console $ hyperfine -i "../../ruff/target-baseline/release/ruff check" "../../ruff/target-update/release/ruff check" -N -m20 --output=null Benchmark 1: ../../ruff/target-baseline/release/ruff check Time (mean ± σ): 27.5 ms ± 1.1 ms [User: 33.0 ms, System: 108.4 ms] Range (min … max): 25.0 ms … 31.7 ms 94 runs Warning: Ignoring non-zero exit code. Benchmark 2: ../../ruff/target-update/release/ruff check Time (mean ± σ): 28.1 ms ± 2.0 ms [User: 33.4 ms, System: 105.7 ms] Range (min … max): 24.8 ms … 38.9 ms 102 runs Warning: Ignoring non-zero exit code. Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use t he '--warmup' or '--prepare' options. Summary ../../ruff/target-baseline/release/ruff check ran 1.03 ± 0.08 times faster than ../../ruff/target-update/release/ruff check ``` Before: <img width="486" height="583" alt="image" src="https://github.com/user-attachments/assets/2d94dabf-b28d-48c1-b17c-52aac59fd57e" /> After: <img width="486" height="583" alt="image" src="https://github.com/user-attachments/assets/b388ada3-e3d7-4ba0-b90b-1330eb81f4be" />
No files matched your search
@@ -948,7 +948,7 @@ checksum = "a867d7322eb69cf3a68a5426387a25b45cb3b9c5ee41023ee6cea92e2afadd82"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"fancy-regex",
|
||||
"libtest-mimic 0.8.1",
|
||||
"libtest-mimic",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@@ -1968,18 +1968,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtest-mimic"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc0bda45ed5b3a2904262c1bb91e526127aa70e7ef3758aba2ef93cf896b9b58"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"escape8259",
|
||||
"termcolor",
|
||||
"threadpool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libtest-mimic"
|
||||
version = "0.8.1"
|
||||
@@ -2286,16 +2274,6 @@ dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.3"
|
||||
@@ -3146,10 +3124,7 @@ dependencies = [
|
||||
"anstyle",
|
||||
"memchr",
|
||||
"ruff_annotate_snippets",
|
||||
"serde",
|
||||
"snapbox",
|
||||
"toml 1.1.3+spec-1.1.0",
|
||||
"tryfn",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
@@ -4255,15 +4230,6 @@ dependencies = [
|
||||
"windows-sys 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.4.3"
|
||||
@@ -4383,15 +4349,6 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpool"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
|
||||
dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tikv-jemalloc-sys"
|
||||
version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
|
||||
@@ -4608,17 +4565,6 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tryfn"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f68b00518dd6c69ee2289900b140e55dad068cb925678603bfa8d539f61ef6c1"
|
||||
dependencies = [
|
||||
"ignore",
|
||||
"libtest-mimic 0.7.3",
|
||||
"snapbox",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ty"
|
||||
version = "0.0.0"
|
||||
|
||||
@@ -202,7 +202,6 @@ tracing-subscriber = { version = "0.3.18", default-features = false, features =
|
||||
"ansi",
|
||||
"smallvec",
|
||||
] }
|
||||
tryfn = { version = "1.0.0" }
|
||||
typed-arena = { version = "2.0.2" }
|
||||
unicode-ident = { version = "1.0.12" }
|
||||
unicode-normalization = { version = "0.1.23" }
|
||||
|
||||
@@ -1354,7 +1354,6 @@ mod tests {
|
||||
|
|
||||
1 | 1
|
||||
| ^
|
||||
|
|
||||
|
||||
panic: Panicked at <location> when checking `test.py`: `Test panic for FormatCommandError`
|
||||
--> test.py:1:1
|
||||
|
||||
@@ -56,14 +56,14 @@ fn default_files() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> bar.py:1:1
|
||||
--> bar.py:1:7
|
||||
|
|
||||
- bar = "needs formatting"
|
||||
1 + bar = "needs formatting"
|
||||
|
|
||||
|
||||
unformatted: File would be reformatted
|
||||
--> foo.py:1:1
|
||||
--> foo.py:1:7
|
||||
|
|
||||
- foo = "needs formatting"
|
||||
1 + foo = "needs formatting"
|
||||
@@ -520,7 +520,7 @@ exclude = ["format_excluded.py"]
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> main.py:1:1
|
||||
--> main.py:1:3
|
||||
|
|
||||
- x = 1
|
||||
1 + x = 1
|
||||
@@ -548,7 +548,7 @@ fn deduplicate_directory_and_explicit_file() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> main.py:1:1
|
||||
--> main.py:1:3
|
||||
|
|
||||
- x = 1
|
||||
1 + x = 1
|
||||
@@ -584,7 +584,6 @@ from module import =
|
||||
|
|
||||
2 | from module import =
|
||||
| ^
|
||||
|
|
||||
|
||||
|
||||
----- stderr -----
|
||||
@@ -700,7 +699,7 @@ fn output_format_notebook() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> CRATE_ROOT/resources/test/fixtures/unformatted.ipynb:cell 1:1:1
|
||||
--> CRATE_ROOT/resources/test/fixtures/unformatted.ipynb:cell 1:2:1
|
||||
::: cell 1
|
||||
|
|
||||
1 | import numpy
|
||||
@@ -835,7 +834,7 @@ fn check_quiet_mode_shows_diagnostics_only() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> main.py:1:1
|
||||
--> main.py:1:5
|
||||
|
|
||||
- def foo():
|
||||
- pass
|
||||
@@ -859,7 +858,7 @@ fn check_default_mode_shows_diagnostics_and_summary() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> main.py:1:1
|
||||
--> main.py:1:5
|
||||
|
|
||||
- def foo():
|
||||
- pass
|
||||
@@ -2052,7 +2051,6 @@ fn syntax_error_in_notebooks_check() -> Result<()> {
|
||||
2 | # выберите случайный индекс в диапазон от 0 до len(X)-1 включительно при помощи функции random.randint
|
||||
3 | j = # ваш код здесь
|
||||
| ^
|
||||
|
|
||||
|
||||
|
||||
----- stderr -----
|
||||
@@ -2557,7 +2555,7 @@ fn markdown_formatting() -> Result<()> {
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> CRATE_ROOT/resources/test/fixtures/unformatted.md:1:1
|
||||
--> CRATE_ROOT/resources/test/fixtures/unformatted.md:4:7
|
||||
|
|
||||
3 | ```py
|
||||
- print( "hello" )
|
||||
@@ -2694,7 +2692,7 @@ print( 'hello' )
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
unformatted: File would be reformatted
|
||||
--> test.bar:1:1
|
||||
--> test.bar:5:7
|
||||
|
|
||||
4 | ```py
|
||||
- print( 'hello' )
|
||||
|
||||
@@ -4475,7 +4475,6 @@ fn show_fixes_in_full_output_with_preview_enabled() {
|
||||
|
|
||||
1 | import math
|
||||
| ^^^^
|
||||
|
|
||||
help: Remove unused import: `math`
|
||||
|
|
||||
- import math
|
||||
@@ -5195,7 +5194,6 @@ fn ruff_toml_is_linted() -> Result<()> {
|
||||
|
|
||||
1 | lint.select = ["F401"]
|
||||
| ^^^^
|
||||
|
|
||||
help: Replace rule code with `unused-import`
|
||||
|
|
||||
- lint.select = ["F401"]
|
||||
|
||||
@@ -120,7 +120,6 @@ fn stdin_error() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -148,7 +147,6 @@ fn stdin_filename() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -187,7 +185,6 @@ import bar # unused import
|
||||
|
|
||||
2 | import bar # unused import
|
||||
| ^^^
|
||||
|
|
||||
help: Remove unused import: `bar`
|
||||
|
|
||||
1 |
|
||||
@@ -199,7 +196,6 @@ import bar # unused import
|
||||
|
|
||||
2 | import foo # unused import
|
||||
| ^^^
|
||||
|
|
||||
help: Remove unused import: `foo`
|
||||
|
|
||||
1 |
|
||||
@@ -231,7 +227,6 @@ fn check_warn_stdin_filename_with_files() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -261,7 +256,6 @@ fn stdin_source_type_py() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -502,7 +496,6 @@ fn stdin_fix_jupyter() {
|
||||
|
|
||||
1 | print(x)
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 3 errors (2 fixed, 1 remaining).
|
||||
"#);
|
||||
@@ -601,7 +594,6 @@ fn stdin_override_parser_ipynb() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
::: cell 1
|
||||
|
|
||||
@@ -613,7 +605,6 @@ fn stdin_override_parser_ipynb() {
|
||||
|
|
||||
1 | import sys
|
||||
| ^^^
|
||||
|
|
||||
help: Remove unused import: `sys`
|
||||
::: cell 3
|
||||
|
|
||||
@@ -647,7 +638,6 @@ fn stdin_override_parser_py() {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -685,7 +675,6 @@ extension = {ipynb="python"}
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -884,7 +873,6 @@ fn stdin_parse_error() {
|
||||
|
|
||||
1 | from foo import
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -914,7 +902,6 @@ fn stdin_multiple_parse_error() {
|
||||
1 | from foo import
|
||||
2 | bar =
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 2 errors.
|
||||
|
||||
@@ -936,7 +923,6 @@ fn parse_error_not_included() {
|
||||
|
|
||||
1 | foo =
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -959,7 +945,6 @@ fn full_output_preview() {
|
||||
|
|
||||
1 | l = 1
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -988,7 +973,6 @@ preview = true
|
||||
|
|
||||
1 | l = 1
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -1013,7 +997,6 @@ fn full_output_format() {
|
||||
|
|
||||
1 | l = 1
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -1870,7 +1853,6 @@ fn check_input_from_argfile() -> Result<()> {
|
||||
|
|
||||
1 | import os
|
||||
| ^^
|
||||
|
|
||||
help: Remove unused import: `os`
|
||||
|
|
||||
- import os
|
||||
@@ -2502,7 +2484,6 @@ select = ["RUF017"]
|
||||
2 | y = [4, 5, 6]
|
||||
3 | sum([x, y], [])
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Replace with `functools.reduce`
|
||||
|
||||
Found 1 error.
|
||||
@@ -2543,7 +2524,6 @@ unfixable = ["RUF"]
|
||||
2 | y = [4, 5, 6]
|
||||
3 | sum([x, y], [])
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Replace with `functools.reduce`
|
||||
|
||||
Found 1 error.
|
||||
@@ -2571,7 +2551,6 @@ fn pyproject_toml_stdin_syntax_error() {
|
||||
|
|
||||
1 | [project
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -2598,7 +2577,6 @@ fn pyproject_toml_stdin_schema_error() {
|
||||
1 | [project]
|
||||
2 | name = 1
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
|
||||
@@ -2691,7 +2669,6 @@ fn pyproject_toml_stdin_schema_error_fix() {
|
||||
1 | [project]
|
||||
2 | name = 1
|
||||
| ^
|
||||
|
|
||||
|
||||
Found 1 error.
|
||||
"
|
||||
|
||||
@@ -12,28 +12,91 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
anstyle = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
memchr = { workspace = true, optional = true }
|
||||
unicode-width = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ruff_annotate_snippets = { path = ".", features = ["testing-colors"] }
|
||||
|
||||
anstream = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
snapbox = { workspace = true, features = ["diff", "term-svg", "cmd", "examples"] }
|
||||
toml = { workspace = true }
|
||||
tryfn = { workspace = true }
|
||||
snapbox = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["std", "simd"]
|
||||
std = ["anstyle/std", "memchr?/std"]
|
||||
simd = ["dep:memchr"]
|
||||
testing-colors = []
|
||||
|
||||
[[test]]
|
||||
name = "fixtures"
|
||||
harness = false
|
||||
# Using upstream lints
|
||||
[lints.rust]
|
||||
rust_2018_idioms = { level = "warn", priority = -1 }
|
||||
unnameable_types = "warn"
|
||||
unreachable_pub = "warn"
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unused_macro_rules = "warn"
|
||||
unused_qualifications = "warn"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
[lints.clippy]
|
||||
disallowed_methods = "allow" # HACK: minimize changes from upstream
|
||||
bool_assert_comparison = "allow"
|
||||
branches_sharing_code = "allow"
|
||||
checked_conversions = "warn"
|
||||
collapsible_else_if = "allow"
|
||||
create_dir = "warn"
|
||||
dbg_macro = "warn"
|
||||
debug_assert_with_mut_call = "warn"
|
||||
doc_markdown = "warn"
|
||||
empty_enums = "warn"
|
||||
enum_glob_use = "warn"
|
||||
expl_impl_clone_on_copy = "warn"
|
||||
explicit_deref_methods = "warn"
|
||||
explicit_into_iter_loop = "warn"
|
||||
fallible_impl_from = "warn"
|
||||
filter_map_next = "warn"
|
||||
flat_map_option = "warn"
|
||||
float_cmp_const = "warn"
|
||||
fn_params_excessive_bools = "warn"
|
||||
from_iter_instead_of_collect = "warn"
|
||||
if_same_then_else = "allow"
|
||||
implicit_clone = "warn"
|
||||
imprecise_flops = "warn"
|
||||
inconsistent_struct_constructor = "warn"
|
||||
inefficient_to_string = "warn"
|
||||
infinite_loop = "warn"
|
||||
invalid_upcast_comparisons = "warn"
|
||||
large_digit_groups = "warn"
|
||||
large_stack_arrays = "warn"
|
||||
large_types_passed_by_value = "warn"
|
||||
let_and_return = "allow" # sometimes good to name what you are returning
|
||||
linkedlist = "warn"
|
||||
lossy_float_literal = "warn"
|
||||
macro_use_imports = "warn"
|
||||
mem_forget = "warn"
|
||||
mutex_integer = "warn"
|
||||
needless_continue = "allow"
|
||||
needless_for_each = "warn"
|
||||
negative_feature_names = "warn"
|
||||
path_buf_push_overwrite = "warn"
|
||||
ptr_as_ptr = "warn"
|
||||
rc_mutex = "warn"
|
||||
redundant_feature_names = "warn"
|
||||
ref_option_ref = "warn"
|
||||
rest_pat_in_fully_bound_structs = "warn"
|
||||
result_large_err = "allow"
|
||||
same_functions_in_if_condition = "warn"
|
||||
self_named_module_files = "warn"
|
||||
semicolon_if_nothing_returned = "warn"
|
||||
str_to_string = "warn"
|
||||
string_add = "warn"
|
||||
string_add_assign = "warn"
|
||||
string_lit_as_bytes = "warn"
|
||||
todo = "warn"
|
||||
trait_duplication_in_bounds = "warn"
|
||||
uninlined_format_args = "warn"
|
||||
verbose_file_reads = "warn"
|
||||
wildcard_imports = "warn"
|
||||
zero_sized_map_values = "warn"
|
||||
|
||||
[lib]
|
||||
name = "annotate_snippets"
|
||||
test = false
|
||||
@@ -0,0 +1,32 @@
|
||||
use annotate_snippets::renderer::DecorStyle;
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet};
|
||||
|
||||
fn main() {
|
||||
let source = r#"//@ compile-flags: -Ztreat-err-as-bug
|
||||
//@ failure-status: 101
|
||||
//@ error-pattern: aborting due to `-Z treat-err-as-bug=1`
|
||||
//@ error-pattern: [eval_static_initializer] evaluating initializer of static `C`
|
||||
//@ normalize-stderr: "note: .*\n\n" -> ""
|
||||
//@ normalize-stderr: "thread 'rustc' panicked.*:\n.*\n" -> ""
|
||||
//@ rustc-env:RUST_BACKTRACE=0
|
||||
|
||||
#![crate_type = "rlib"]
|
||||
|
||||
pub static C: u32 = 0 - 1;
|
||||
//~^ ERROR could not evaluate static initializer
|
||||
"#;
|
||||
let report = &[Level::ERROR
|
||||
.with_name(Some("error: internal compiler error"))
|
||||
.primary_title("could not evaluate static initializer")
|
||||
.id("E0080")
|
||||
.element(
|
||||
Snippet::source(source).path("$DIR/err.rs").annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(386..391)
|
||||
.label("attempt to compute `0_u32 - 1_u32`, which would overflow"),
|
||||
),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="751px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,17 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error: internal compiler error[E0080]</tspan><tspan class="bold">: could not evaluate static initializer</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/whitespace-trimming.rs:LL:193</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/err.rs:11:21</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL |</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan> let _: () = 42;</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> pub static C: u32 = 0 - 1;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected (), found integer</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">attempt to compute `0_u32 - 1_u32`, which would overflow</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="118px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
use annotate_snippets::renderer::DecorStyle;
|
||||
use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet};
|
||||
|
||||
fn main() {
|
||||
let source = r#"// Regression test for issue #114529
|
||||
// Tests that we do not ICE during const eval for a
|
||||
// break-with-value in contexts where it is illegal
|
||||
|
||||
#[allow(while_true)]
|
||||
fn main() {
|
||||
[(); {
|
||||
while true {
|
||||
break 9; //~ ERROR `break` with value from a `while` loop
|
||||
};
|
||||
51
|
||||
}];
|
||||
|
||||
[(); {
|
||||
while let Some(v) = Some(9) {
|
||||
break v; //~ ERROR `break` with value from a `while` loop
|
||||
};
|
||||
51
|
||||
}];
|
||||
|
||||
while true {
|
||||
break (|| { //~ ERROR `break` with value from a `while` loop
|
||||
let local = 9;
|
||||
});
|
||||
}
|
||||
}
|
||||
"#;
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title("`break` with value from a `while` loop")
|
||||
.id("E0571")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.line_start(1)
|
||||
.path("$DIR/issue-114529-illegal-break-with-value.rs")
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(483..581)
|
||||
.label("can only break with a value inside `loop` or breakable block"),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(462..472)
|
||||
.label("you can't `break` with a value in a `while` loop"),
|
||||
),
|
||||
),
|
||||
Level::HELP
|
||||
.with_name(Some("suggestion"))
|
||||
.secondary_title("use `break` on its own without a value inside this `while` loop")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.line_start(1)
|
||||
.path("$DIR/issue-114529-illegal-break-with-value.rs")
|
||||
.patch(Patch::new(483..581, "break")),
|
||||
),
|
||||
];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<svg width="740px" height="344px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0571]</tspan><tspan class="bold">: `break` with value from a `while` loop</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/issue-114529-illegal-break-with-value.rs:22:9</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">21</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> while true {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">──────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">you can't `break` with a value in a `while` loop</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┏</tspan><tspan> break (|| { //~ ERROR `break` with value from a `while` loop</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> let local = 9;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> });</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┗━━━━━━━━━━┛</tspan><tspan> </tspan><tspan class="fg-bright-red bold">can only break with a value inside `loop` or breakable block</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-cyan bold">suggestion</tspan><tspan>: use `break` on its own without a value inside this `while` loop</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> </tspan><tspan class="fg-bright-red">break (|| { //~ ERROR `break` with value from a `while` loop</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-red">- let local = 9;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-red">- })</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">22</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> </tspan><tspan class="fg-bright-green">break</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let source = r#"# Docstring followed by a newline
|
||||
|
||||
def foobar(door, bar={}):
|
||||
"""
|
||||
"""
|
||||
"#;
|
||||
|
||||
let report = &[Group::with_level(Level::NOTE)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.fold(false)
|
||||
.annotation(AnnotationKind::Primary.span(56..58).label("B006")),
|
||||
)
|
||||
.element(Level::HELP.message("Replace with `None`; initialize within function"))];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<svg width="740px" height="200px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> # Docstring followed by a newline</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> def foobar(door, bar={}):</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-green bold">━━</tspan><tspan> </tspan><tspan class="fg-bright-green bold">B006</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> """</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">5</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> """</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">help</tspan><tspan>: Replace with `None`; initialize within function</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -1,23 +1,27 @@
|
||||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let source = r#" annotations: vec![SourceAnnotation {
|
||||
label: "expected struct `annotate_snippets::snippet::Slice`, found reference"
|
||||
,
|
||||
range: <22, 25>,"#;
|
||||
let message = Level::Error.title("expected type, found `22`").snippet(
|
||||
Snippet::source(source)
|
||||
.line_start(26)
|
||||
.origin("examples/footer.rs")
|
||||
.fold(true)
|
||||
.annotation(
|
||||
Level::Error
|
||||
.span(193..195)
|
||||
.label("expected struct `annotate_snippets::snippet::Slice`, found reference"),
|
||||
)
|
||||
.annotation(Level::Info.span(34..50).label("while parsing this struct")),
|
||||
);
|
||||
let report =
|
||||
&[Level::ERROR
|
||||
.primary_title("expected type, found `22`")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.line_start(26)
|
||||
.path("examples/footer.rs")
|
||||
.annotation(AnnotationKind::Primary.span(193..195).label(
|
||||
"expected struct `annotate_snippets::snippet::Slice`, found reference",
|
||||
))
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(34..50)
|
||||
.label("while parsing this struct"),
|
||||
),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
anstream::println!("{}", renderer.render(message));
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<svg width="860px" height="218px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="860px" height="182px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
@@ -19,27 +19,23 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">expected type, found `22`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected type, found `22`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> examples/footer.rs:29:25</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>examples/footer.rs:29:25</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26 |</tspan><tspan> annotations: vec![SourceAnnotation {</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> annotations: vec![SourceAnnotation {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">----------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">info: while parsing this struct</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">────────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">while parsing this struct</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">27 |</tspan><tspan> label: "expected struct `annotate_snippets::snippet::Slice`, found reference"</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">28 |</tspan><tspan> ,</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">29</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> range: <22, 25>,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">29 |</tspan><tspan> range: <22, 25>,</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px">
|
||||
<tspan x="10px" y="172px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -1,22 +1,23 @@
|
||||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||||
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let message =
|
||||
Level::Error
|
||||
.title("mismatched types")
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.snippet(
|
||||
.element(
|
||||
Snippet::source(" slices: vec![\"A\",")
|
||||
.line_start(13)
|
||||
.origin("src/multislice.rs")
|
||||
.annotation(Level::Error.span(21..24).label(
|
||||
.path("src/multislice.rs")
|
||||
.annotation(AnnotationKind::Primary.span(21..24).label(
|
||||
"expected struct `annotate_snippets::snippet::Slice`, found reference",
|
||||
)),
|
||||
)
|
||||
.footer(Level::Note.title(
|
||||
"expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`",
|
||||
));
|
||||
),
|
||||
Group::with_title(Level::NOTE.secondary_title(
|
||||
"expected type: `snippet::Annotation`\n found type: `__&__snippet::Annotation`",
|
||||
)),
|
||||
];
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
anstream::println!("{}", renderer.render(message));
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<svg width="844px" height="182px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
@@ -20,21 +20,21 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> src/multislice.rs:13:22</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>src/multislice.rs:13:22</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">13 |</tspan><tspan> slices: vec!["A",</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">13</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> slices: vec!["A",</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected struct `annotate_snippets::snippet::Slice`, found reference</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">= </tspan><tspan class="fg-bright-green bold">note</tspan><tspan>: expected type: `snippet::Annotation`</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-green bold">note</tspan><tspan>: expected type: `snippet::Annotation`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> found type: `__&__snippet::Annotation`</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> found type: `__&__snippet::Annotation`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px">
|
||||
</tspan>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
@@ -1,4 +1,4 @@
|
||||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let source = r#") -> Option<String> {
|
||||
@@ -23,22 +23,26 @@ fn main() {
|
||||
_ => continue,
|
||||
}
|
||||
}"#;
|
||||
let message = Level::Error.title("mismatched types").id("E0308").snippet(
|
||||
Snippet::source(source)
|
||||
.line_start(51)
|
||||
.origin("src/format.rs")
|
||||
.annotation(
|
||||
Level::Warning
|
||||
.span(5..19)
|
||||
.label("expected `Option<String>` because of return type"),
|
||||
)
|
||||
.annotation(
|
||||
Level::Error
|
||||
.span(26..724)
|
||||
.label("expected enum `std::option::Option`"),
|
||||
),
|
||||
);
|
||||
let report = &[Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.line_start(51)
|
||||
.path("src/format.rs")
|
||||
.fold(false)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(5..19)
|
||||
.label("expected `Option<String>` because of return type"),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(26..724)
|
||||
.label("expected enum `std::option::Option`"),
|
||||
),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
anstream::println!("{}", renderer.render(message));
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
<svg width="740px" height="542px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="524px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
@@ -20,63 +19,61 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> src/format.rs:51:6</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>src/format.rs:52:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51 |</tspan><tspan> ) -> Option<String> {</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> ) -> Option<String> {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">--------------</tspan><tspan> </tspan><tspan class="fg-yellow bold">expected `Option<String>` because of return type</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">──────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected `Option<String>` because of return type</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">/</tspan><tspan> for ann in annotations {</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┏</tspan><tspan> for ann in annotations {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> match (ann.range.0, ann.range.1) {</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> match (ann.range.0, ann.range.1) {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> (None, None) => continue,</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> (None, None) => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> (Some(start), Some(end)) if start > end_index => continue,</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> (Some(start), Some(end)) if start > end_index => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">56 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> (Some(start), Some(end)) if start >= start_index => {</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">56</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> (Some(start), Some(end)) if start >= start_index => {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">57 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> let label = if let Some(ref label) = ann.label {</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">57</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> let label = if let Some(ref label) = ann.label {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">58 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> format!(" {}", label)</tspan>
|
||||
<tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">58</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> format!(" {}", label)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">59 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> } else {</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">59</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> } else {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">60 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> String::from("")</tspan>
|
||||
<tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">60</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> String::from("")</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan class="fg-bright-blue bold">61 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> };</tspan>
|
||||
<tspan x="10px" y="280px"><tspan class="fg-bright-blue bold">61</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> };</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">62 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">62</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">63 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> return Some(format!(</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">63</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> return Some(format!(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan class="fg-bright-blue bold">64 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> "{}{}{}",</tspan>
|
||||
<tspan x="10px" y="334px"><tspan class="fg-bright-blue bold">64</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> "{}{}{}",</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="352px"><tspan class="fg-bright-blue bold">65 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> " ".repeat(start - start_index),</tspan>
|
||||
<tspan x="10px" y="352px"><tspan class="fg-bright-blue bold">65</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> " ".repeat(start - start_index),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="370px"><tspan class="fg-bright-blue bold">66 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> "^".repeat(end - start),</tspan>
|
||||
<tspan x="10px" y="370px"><tspan class="fg-bright-blue bold">66</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> "^".repeat(end - start),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="388px"><tspan class="fg-bright-blue bold">67 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> label</tspan>
|
||||
<tspan x="10px" y="388px"><tspan class="fg-bright-blue bold">67</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> label</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="406px"><tspan class="fg-bright-blue bold">68 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> ));</tspan>
|
||||
<tspan x="10px" y="406px"><tspan class="fg-bright-blue bold">68</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> ));</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="424px"><tspan class="fg-bright-blue bold">69 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
<tspan x="10px" y="424px"><tspan class="fg-bright-blue bold">69</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="442px"><tspan class="fg-bright-blue bold">70 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> _ => continue,</tspan>
|
||||
<tspan x="10px" y="442px"><tspan class="fg-bright-blue bold">70</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> _ => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="460px"><tspan class="fg-bright-blue bold">71 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
<tspan x="10px" y="460px"><tspan class="fg-bright-blue bold">71</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="478px"><tspan class="fg-bright-blue bold">72 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
<tspan x="10px" y="478px"><tspan class="fg-bright-blue bold">72</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="496px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|____^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`</tspan>
|
||||
<tspan x="10px" y="496px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">┗━━━━┛</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="514px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="532px">
|
||||
<tspan x="10px" y="514px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 7.0 KiB |
@@ -0,0 +1,67 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
use anstyle::AnsiColor;
|
||||
use anstyle::Effects;
|
||||
use anstyle::Style;
|
||||
|
||||
fn main() {
|
||||
let source = r#"// Make sure "highlighted" code is colored purple
|
||||
|
||||
//@ compile-flags: --error-format=human --color=always
|
||||
//@ edition:2018
|
||||
|
||||
use core::pin::Pin;
|
||||
use core::future::Future;
|
||||
use core::any::Any;
|
||||
|
||||
fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<(
|
||||
dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
|
||||
)>>) {}
|
||||
|
||||
fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin<Box<(
|
||||
dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static
|
||||
)>> {
|
||||
Box::pin(async { Err("nope".into()) })
|
||||
}
|
||||
|
||||
fn main() {
|
||||
query(wrapped_fn);
|
||||
}"#;
|
||||
|
||||
const MAGENTA: Style = AnsiColor::Magenta.on_default().effects(Effects::BOLD);
|
||||
let message = format!(
|
||||
"expected fn pointer `{MAGENTA}for<'a>{MAGENTA:#} fn(Box<{MAGENTA}(dyn Any + Send + 'a){MAGENTA:#}>) -> Pin<_>`
|
||||
found fn item `fn(Box<{MAGENTA}(dyn Any + Send + 'static){MAGENTA:#}>) -> Pin<_> {MAGENTA}{{wrapped_fn}}{MAGENTA:#}`",
|
||||
);
|
||||
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/highlighting.rs")
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(553..563)
|
||||
.label("one type is more general than the other"),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(547..552)
|
||||
.label("arguments to this function are incorrect"),
|
||||
),
|
||||
)
|
||||
.element(Level::NOTE.message(&message)),
|
||||
Level::NOTE
|
||||
.secondary_title("function defined here")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/highlighting.rs")
|
||||
.annotation(AnnotationKind::Context.span(200..333).label(""))
|
||||
.annotation(AnnotationKind::Primary.span(194..199)),
|
||||
),
|
||||
];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<svg width="768px" height="362px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.fg-magenta { fill: #AA00AA }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/highlighting.rs:21:11</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">21</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> query(wrapped_fn);</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">┬────</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">one type is more general than the other</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">arguments to this function are incorrect</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">note</tspan><tspan>: expected fn pointer `</tspan><tspan class="fg-magenta bold">for<'a></tspan><tspan> fn(Box<</tspan><tspan class="fg-magenta bold">(dyn Any + Send + 'a)</tspan><tspan>>) -> Pin<_>`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> found fn item `fn(Box<</tspan><tspan class="fg-magenta bold">(dyn Any + Send + 'static)</tspan><tspan>>) -> Pin<_> </tspan><tspan class="fg-magenta bold">{wrapped_fn}</tspan><tspan>`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-green bold">note</tspan><tspan>: function defined here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/highlighting.rs:10:4</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">10</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">┌────</tspan><tspan class="fg-bright-green bold">━━━━━</tspan><tspan class="fg-bright-blue bold">─┘</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">12</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> )>>) {}</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴└───┘</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="352px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,31 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let source = r#"//@ compile-flags: -Z teach
|
||||
|
||||
#![allow(warnings)]
|
||||
|
||||
const CON: Vec<i32> = vec![1, 2, 3]; //~ ERROR E0010
|
||||
//~| ERROR cannot call non-const method
|
||||
fn main() {}
|
||||
"#;
|
||||
let report = &[Level::ERROR.primary_title("allocations are not allowed in constants")
|
||||
.id("E0010")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/E0010-teach.rs")
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(72..85)
|
||||
.label("allocation not allowed in constants")
|
||||
.highlight_source(true),
|
||||
),
|
||||
)
|
||||
.element(
|
||||
Level::NOTE.message("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."),
|
||||
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<svg width="953px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0010]</tspan><tspan class="bold">: allocations are not allowed in constants</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/E0010-teach.rs:5:23</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">5</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> const CON: Vec<i32> = </tspan><tspan class="fg-bright-red bold">vec![1, 2, 3]</tspan><tspan>; //~ ERROR E0010</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">allocation not allowed in constants</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">note</tspan><tspan>: The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,32 @@
|
||||
use annotate_snippets::renderer::DecorStyle;
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet};
|
||||
|
||||
fn main() {
|
||||
let source = r#"//@ compile-flags: -Zterminal-urls=yes
|
||||
fn main() {
|
||||
let () = 4; //~ ERROR
|
||||
}
|
||||
"#;
|
||||
let report = &[Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.id_url("https://doc.rust-lang.org/error_codes/E0308.html")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.line_start(1)
|
||||
.path("$DIR/terminal_urls.rs")
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(59..61)
|
||||
.label("expected integer, found `()`"),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(64..65)
|
||||
.label("this expression has type `{integer}`"),
|
||||
),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[</tspan><tspan class="fg-bright-red bold"><a href="https://doc.rust-lang.org/error_codes/E0308.html">E0308</a></tspan><tspan class="fg-bright-red bold">]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/terminal_urls.rs:3:9</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let () = 4; //~ ERROR</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┯━</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">─</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">this expression has type `{integer}`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected integer, found `()`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,75 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let source = r#"
|
||||
#![allow(dead_code)]
|
||||
struct U <T> {
|
||||
wtf: Option<Box<U<T>>>,
|
||||
x: T,
|
||||
}
|
||||
fn main() {
|
||||
U {
|
||||
wtf: Some(Box(U {
|
||||
wtf: None,
|
||||
x: (),
|
||||
})),
|
||||
x: ()
|
||||
};
|
||||
let _ = std::collections::HashMap();
|
||||
let _ = std::collections::HashMap {};
|
||||
let _ = Box {};
|
||||
}
|
||||
"#;
|
||||
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title(
|
||||
"cannot construct `Box<_, _>` with struct literal syntax due to private fields",
|
||||
)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.annotation(AnnotationKind::Primary.span(295..298)),
|
||||
)
|
||||
.element(Level::NOTE.message("private fields `0` and `1` that were not provided")),
|
||||
Level::HELP
|
||||
.secondary_title(
|
||||
"you might have meant to use an associated function to build this type",
|
||||
)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.patch(Patch::new(298..301, "::new(_)")),
|
||||
)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.patch(Patch::new(298..301, "::new_uninit()")),
|
||||
)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.patch(Patch::new(298..301, "::new_zeroed()")),
|
||||
)
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.patch(Patch::new(298..301, "::new_in(_, _)")),
|
||||
)
|
||||
.element(Level::NOTE.no_name().message("and 12 other candidates")),
|
||||
Level::HELP
|
||||
.secondary_title("consider using the `Default` trait")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/multi-suggestion.rs")
|
||||
.patch(Patch::new(295..295, "<"))
|
||||
.patch(Patch::new(
|
||||
298..301,
|
||||
" as std::default::Default>::default()",
|
||||
)),
|
||||
),
|
||||
];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<svg width="740px" height="524px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: cannot construct `Box<_, _>` with struct literal syntax due to private fields</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/multi-suggestion.rs:17:13</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let _ = Box {};</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">note</tspan><tspan>: private fields `0` and `1` that were not provided</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: you might have meant to use an associated function to build this type</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-red"> {}</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-green">::new(_)</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">├╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-red"> {}</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-green">::new_uninit()</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan> </tspan><tspan class="fg-bright-blue bold">├╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-red"> {}</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-green">::new_zeroed()</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-bright-blue bold">├╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="352px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-red"> {}</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="370px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-green">::new_in(_, _)</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="388px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="406px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan>and 12 other candidates</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="424px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: consider using the `Default` trait</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="442px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="460px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> let _ = Box</tspan><tspan class="fg-bright-red"> {}</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="478px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> let _ = </tspan><tspan class="fg-bright-green"><</tspan><tspan>Box</tspan><tspan class="fg-bright-green"> as std::default::Default>::default()</tspan><tspan>;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="496px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="514px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
@@ -1,19 +1,21 @@
|
||||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||||
use annotate_snippets::{Annotation, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
fn main() {
|
||||
let message = Level::Error
|
||||
.title("mismatched types")
|
||||
.snippet(
|
||||
Snippet::source("Foo")
|
||||
let report = &[Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.element(
|
||||
Snippet::<Annotation<'_>>::source("Foo")
|
||||
.line_start(51)
|
||||
.origin("src/format.rs"),
|
||||
.fold(false)
|
||||
.path("src/format.rs"),
|
||||
)
|
||||
.snippet(
|
||||
Snippet::source("Faa")
|
||||
.element(
|
||||
Snippet::<Annotation<'_>>::source("Faa")
|
||||
.line_start(129)
|
||||
.origin("src/display.rs"),
|
||||
);
|
||||
.fold(false)
|
||||
.path("src/display.rs"),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
anstream::println!("{}", renderer.render(message));
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<svg width="740px" height="200px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { background: #000000 }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
@@ -19,23 +19,23 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> src/format.rs</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>src/format.rs</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold"> 51 |</tspan><tspan> Foo</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold"> 51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> Foo</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">:::</tspan><tspan> src/display.rs</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ⸬ </tspan><tspan>src/display.rs:129</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">129 |</tspan><tspan> Faa</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">129</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> Faa</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px">
|
||||
</tspan>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,27 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
fn main() {
|
||||
let source = r#"struct S {
|
||||
field1: usize,
|
||||
field2: usize,
|
||||
field3: usize,
|
||||
field4: usize,
|
||||
fn foo() {},
|
||||
field6: usize,
|
||||
}
|
||||
"#;
|
||||
let report = &[Level::ERROR
|
||||
.primary_title("functions are not allowed in struct definitions")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("$DIR/struct_name_as_context.rs")
|
||||
.annotation(AnnotationKind::Primary.span(91..102))
|
||||
.annotation(AnnotationKind::Visible.span(0..8)),
|
||||
)
|
||||
.element(
|
||||
Level::HELP
|
||||
.message("unlike in C++, Java, and C#, functions are declared in `impl` blocks"),
|
||||
)];
|
||||
|
||||
let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
anstream::println!("{}", renderer.render(report));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<svg width="740px" height="200px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: functions are not allowed in struct definitions</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/struct_name_as_context.rs:6:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> struct S {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">6</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> fn foo() {},</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">help</tspan><tspan>: unlike in C++, Java, and C#, functions are declared in `impl` blocks</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px">
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,235 @@
|
||||
//! [`Level`] constants for easy importing
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
|
||||
use anstyle::Style;
|
||||
|
||||
use crate::renderer::stylesheet::Stylesheet;
|
||||
use crate::snippet::{ERROR_TXT, HELP_TXT, INFO_TXT, NOTE_TXT, WARNING_TXT};
|
||||
use crate::{Message, OptionCow, Title};
|
||||
|
||||
/// Default `error:` [`Level`]
|
||||
pub const ERROR: Level<'_> = Level {
|
||||
name: None,
|
||||
level: LevelInner::Error,
|
||||
};
|
||||
|
||||
/// Default `warning:` [`Level`]
|
||||
pub const WARNING: Level<'_> = Level {
|
||||
name: None,
|
||||
level: LevelInner::Warning,
|
||||
};
|
||||
|
||||
/// Default `info:` [`Level`]
|
||||
pub const INFO: Level<'_> = Level {
|
||||
name: None,
|
||||
level: LevelInner::Info,
|
||||
};
|
||||
|
||||
/// Default `note:` [`Level`]
|
||||
pub const NOTE: Level<'_> = Level {
|
||||
name: None,
|
||||
level: LevelInner::Note,
|
||||
};
|
||||
|
||||
/// Default `help:` [`Level`]
|
||||
pub const HELP: Level<'_> = Level {
|
||||
name: None,
|
||||
level: LevelInner::Help,
|
||||
};
|
||||
|
||||
/// Severity level for [`Title`]s and [`Message`]s
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use annotate_snippets::*;
|
||||
/// let report = &[
|
||||
/// Level::ERROR.primary_title("mismatched types").id("E0308")
|
||||
/// .element(Level::NOTE.message("expected reference")),
|
||||
/// Group::with_title(
|
||||
/// Level::HELP.secondary_title("function defined here")
|
||||
/// ),
|
||||
/// ];
|
||||
/// ```
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Level<'a> {
|
||||
pub(crate) name: Option<Option<Cow<'a, str>>>,
|
||||
pub(crate) level: LevelInner,
|
||||
}
|
||||
|
||||
/// # Constructors
|
||||
impl<'a> Level<'a> {
|
||||
pub const ERROR: Level<'a> = ERROR;
|
||||
pub const WARNING: Level<'a> = WARNING;
|
||||
pub const INFO: Level<'a> = INFO;
|
||||
pub const NOTE: Level<'a> = NOTE;
|
||||
pub const HELP: Level<'a> = HELP;
|
||||
}
|
||||
|
||||
impl<'a> Level<'a> {
|
||||
/// For the primary, or root cause, [`Group`][crate::Group] (the first) in a [`Report`][crate::Report]
|
||||
///
|
||||
/// See [`Group::with_title`][crate::Group::with_title]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn primary_title(self, text: impl Into<Cow<'a, str>>) -> Title<'a> {
|
||||
Title {
|
||||
level: self,
|
||||
id: None,
|
||||
text: text.into(),
|
||||
allows_styling: false,
|
||||
is_fixable: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// For any secondary, or context, [`Group`][crate::Group]s (subsequent) in a [`Report`][crate::Report]
|
||||
///
|
||||
/// See [`Group::with_title`][crate::Group::with_title]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is allowed to be styled, as such all
|
||||
/// text is considered "trusted input" and has no normalizations applied to
|
||||
/// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be
|
||||
/// used to normalize untrusted text before it is passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn secondary_title(self, text: impl Into<Cow<'a, str>>) -> Title<'a> {
|
||||
Title {
|
||||
level: self,
|
||||
id: None,
|
||||
text: text.into(),
|
||||
allows_styling: true,
|
||||
is_fixable: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// A text [`Element`][crate::Element] in a [`Group`][crate::Group]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is allowed to be styled, as such all
|
||||
/// text is considered "trusted input" and has no normalizations applied to
|
||||
/// it. [`normalize_untrusted_str`](crate::normalize_untrusted_str) can be
|
||||
/// used to normalize untrusted text before it is passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn message(self, text: impl Into<Cow<'a, str>>) -> Message<'a> {
|
||||
Message {
|
||||
level: self,
|
||||
text: text.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_str(&'a self) -> &'a str {
|
||||
match (&self.name, self.level) {
|
||||
(Some(Some(name)), _) => name.as_ref(),
|
||||
(Some(None), _) => "",
|
||||
(None, LevelInner::Error) => ERROR_TXT,
|
||||
(None, LevelInner::Warning) => WARNING_TXT,
|
||||
(None, LevelInner::Info) => INFO_TXT,
|
||||
(None, LevelInner::Note) => NOTE_TXT,
|
||||
(None, LevelInner::Help) => HELP_TXT,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn style(&self, stylesheet: &Stylesheet) -> Style {
|
||||
self.level.style(stylesheet)
|
||||
}
|
||||
}
|
||||
|
||||
/// # Customize the `Level`
|
||||
impl<'a> Level<'a> {
|
||||
/// Replace the name describing this [`Level`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/custom_level.rs")]
|
||||
/// ```
|
||||
#[doc = include_str!("../examples/custom_level.svg")]
|
||||
pub fn with_name(self, name: impl Into<OptionCow<'a>>) -> Level<'a> {
|
||||
Level {
|
||||
name: Some(name.into().0),
|
||||
level: self.level,
|
||||
}
|
||||
}
|
||||
|
||||
/// Do not show the [`Level`]s name
|
||||
///
|
||||
/// Useful for:
|
||||
/// - Another layer of the application will include the level (e.g. when rendering errors)
|
||||
/// - [`Message`]s that are part of a previous [`Group`][crate::Group] [`Element`][crate::Element]s
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level};
|
||||
///let source = r#"fn main() {
|
||||
/// let b: &[u8] = include_str!("file.txt"); //~ ERROR mismatched types
|
||||
/// let s: &str = include_bytes!("file.txt"); //~ ERROR mismatched types
|
||||
/// }"#;
|
||||
/// let report = &[
|
||||
/// Level::ERROR.primary_title("mismatched types").id("E0308")
|
||||
/// .element(
|
||||
/// Snippet::source(source)
|
||||
/// .path("$DIR/mismatched-types.rs")
|
||||
/// .annotation(
|
||||
/// AnnotationKind::Primary
|
||||
/// .span(105..131)
|
||||
/// .label("expected `&str`, found `&[u8; 0]`"),
|
||||
/// )
|
||||
/// .annotation(
|
||||
/// AnnotationKind::Context
|
||||
/// .span(98..102)
|
||||
/// .label("expected due to this"),
|
||||
/// ),
|
||||
/// )
|
||||
/// .element(
|
||||
/// Level::NOTE
|
||||
/// .no_name()
|
||||
/// .message("expected reference `&str`\nfound reference `&'static [u8; 0]`"),
|
||||
/// ),
|
||||
/// ];
|
||||
/// ```
|
||||
pub fn no_name(self) -> Level<'a> {
|
||||
self.with_name(None::<&str>)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) enum LevelInner {
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Note,
|
||||
Help,
|
||||
}
|
||||
|
||||
impl LevelInner {
|
||||
pub(crate) fn style(self, stylesheet: &Stylesheet) -> Style {
|
||||
match self {
|
||||
LevelInner::Error => stylesheet.error,
|
||||
LevelInner::Warning => stylesheet.warning,
|
||||
LevelInner::Info => stylesheet.info,
|
||||
LevelInner::Note => stylesheet.note,
|
||||
LevelInner::Help => stylesheet.help,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,93 @@
|
||||
//! A library for formatting of text or programming code snippets.
|
||||
//!
|
||||
//! It's primary purpose is to build an ASCII-graphical representation of the snippet
|
||||
//! with annotations.
|
||||
//! Format [diagnostic reports][Report], including highlighting snippets of text
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[allow(clippy::needless_doctest_main)]
|
||||
#![doc = include_str!("../examples/expected_type.rs")]
|
||||
//! ```
|
||||
//!
|
||||
#![doc = include_str!("../examples/expected_type.svg")]
|
||||
//!
|
||||
//! The crate uses a three stage process with two conversions between states:
|
||||
//! # Visual overview
|
||||
//!
|
||||
//! [`Report`]
|
||||
//!
|
||||
#![doc = include_str!("../examples/multi_suggestion.svg")]
|
||||
//!
|
||||
//! ### Primary group
|
||||
//!
|
||||
//! [`Title`]
|
||||
//! ```text
|
||||
//! Message --> Renderer --> impl Display
|
||||
//! error: cannot construct `Box<_, _>` with struct literal syntax due to private fields
|
||||
//! ```
|
||||
//!
|
||||
//! The input type - [Message] is a structure designed
|
||||
//! to align with likely output from any parser whose code snippet is to be
|
||||
//! annotated.
|
||||
//!
|
||||
//! The middle structure - [Renderer] is a structure designed
|
||||
//! to convert a snippet into an internal structure that is designed to store
|
||||
//! the snippet data in a way that is easy to format.
|
||||
//! [Renderer] also handles the user-configurable formatting
|
||||
//! options, such as color, or margins.
|
||||
//! [`Annotation`] on a [`Snippet`]
|
||||
//! ```text
|
||||
//! ╭▸ $DIR/multi-suggestion.rs:17:13
|
||||
//! │
|
||||
//! 17 │ let _ = Box {};
|
||||
//! │ ━━━
|
||||
//! │
|
||||
//! ```
|
||||
//!
|
||||
//! Finally, `impl Display` into a final `String` output.
|
||||
//! [`Message`]
|
||||
//! ```text
|
||||
//! ╰ note: private fields `0` and `1` that were not provided
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! ### Secondary group: suggested fix
|
||||
//!
|
||||
//! [`Title`] (proposed solution)
|
||||
//! ```text
|
||||
//! help: you might have meant to use an associated function to build this type
|
||||
//! ```
|
||||
//!
|
||||
//! [`Patch`] Option 1 on a [`Snippet`]
|
||||
//! ```text
|
||||
//! ╭╴
|
||||
//! 21 - let _ = Box {};
|
||||
//! 21 + let _ = Box::new(_);
|
||||
//! ├╴
|
||||
//! ```
|
||||
//!
|
||||
//! [`Patch`] Option 2 on a [`Snippet`]
|
||||
//! ```text
|
||||
//! ├╴
|
||||
//! 17 - let _ = Box {};
|
||||
//! 17 + let _ = Box::new_uninit();
|
||||
//! ├╴
|
||||
//! ```
|
||||
//!
|
||||
//! *etc for Options 3 and 4*
|
||||
//!
|
||||
//! [`Message`]
|
||||
//! ```text
|
||||
//! ╰ and 12 other candidates
|
||||
//! ```
|
||||
//!
|
||||
//! ### Secondary group: alternative suggested fix
|
||||
//!
|
||||
//! [`Title`] (proposed solution)
|
||||
//! ```text
|
||||
//! help: consider using the `Default` trait
|
||||
//! ```
|
||||
//!
|
||||
//! Only [`Patch`] on a [`Snippet`]
|
||||
//! ```text
|
||||
//! ╭╴
|
||||
//! 17 - let _ = Box {};
|
||||
//! 17 + let _ = <Box as std::default::Default>::default();
|
||||
//! ╰╴
|
||||
//! ```
|
||||
//!
|
||||
//! # Cargo `features`
|
||||
//!
|
||||
//! - `simd` - Speeds up folding
|
||||
//!
|
||||
//! # features
|
||||
//! - `testing-colors` - Makes [Renderer::styled] colors OS independent, which
|
||||
//! allows for easier testing when testing colored output. It should be added as
|
||||
//! a feature in `[dev-dependencies]`, which can be done with the following command:
|
||||
@@ -37,33 +95,36 @@
|
||||
//! cargo add annotate-snippets --dev --feature testing-colors
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![warn(clippy::print_stderr)]
|
||||
#![warn(clippy::print_stdout)]
|
||||
#![warn(clippy::std_instead_of_alloc)]
|
||||
#![warn(clippy::std_instead_of_core)]
|
||||
#![warn(missing_debug_implementations)]
|
||||
// Since this is a vendored copy of `annotate-snippets`, we squash Clippy
|
||||
// warnings from upstream in order to the reduce the diff. If our copy drifts
|
||||
// far from upstream such that patches become impractical to apply in both
|
||||
// places, then we can get rid of these suppressions and fix the lints.
|
||||
#![allow(
|
||||
clippy::return_self_not_must_use,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::explicit_iter_loop,
|
||||
clippy::unused_self,
|
||||
clippy::unnecessary_wraps,
|
||||
clippy::range_plus_one,
|
||||
clippy::redundant_closure_for_method_calls,
|
||||
clippy::struct_field_names,
|
||||
clippy::cloned_instead_of_copied,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::needless_as_bytes,
|
||||
clippy::unnecessary_map_or
|
||||
)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
pub mod level;
|
||||
pub mod renderer;
|
||||
mod snippet;
|
||||
|
||||
/// Normalize the string to avoid any unicode control characters.
|
||||
///
|
||||
/// This is important for untrusted input, as it can contain
|
||||
/// invalid unicode sequences.
|
||||
pub fn normalize_untrusted_str(s: &str) -> String {
|
||||
renderer::normalize_whitespace(s).into_owned()
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use level::Level;
|
||||
#[doc(inline)]
|
||||
pub use renderer::Renderer;
|
||||
pub use snippet::*;
|
||||
|
||||
#[doc = include_str!("../README.md")]
|
||||
#[cfg(doctest)]
|
||||
pub struct ReadmeDoctests;
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::cmp::{max, min};
|
||||
use core::cmp::{max, min};
|
||||
|
||||
const ELLIPSIS_PASSING: usize = 6;
|
||||
const LONG_WHITESPACE: usize = 20;
|
||||
@@ -17,7 +17,7 @@ pub(crate) struct Margin {
|
||||
/// The end of the line to be displayed.
|
||||
computed_right: usize,
|
||||
/// The current width of the terminal. 140 by default and in tests.
|
||||
term_width: usize,
|
||||
pub(crate) term_width: usize,
|
||||
/// The end column of a span label, including the span. Doesn't account for labels not in the
|
||||
/// same line as the span.
|
||||
label_right: usize,
|
||||
@@ -41,15 +41,9 @@ impl Margin {
|
||||
// | ^^^^^^^^^
|
||||
// ```
|
||||
|
||||
let whitespace_left = whitespace_left.saturating_sub(ELLIPSIS_PASSING);
|
||||
let span_left = span_left.saturating_sub(ELLIPSIS_PASSING);
|
||||
|
||||
let mut m = Margin {
|
||||
// When an annotation points at leading whitespace (e.g. an indentation error),
|
||||
// `whitespace_left` can exceed `span_left`. Clamp it so that trimming whitespace
|
||||
// never hides the leftmost annotation.
|
||||
whitespace_left: min(whitespace_left, span_left),
|
||||
span_left,
|
||||
whitespace_left: whitespace_left.saturating_sub(ELLIPSIS_PASSING),
|
||||
span_left: span_left.saturating_sub(ELLIPSIS_PASSING),
|
||||
span_right: span_right + ELLIPSIS_PASSING,
|
||||
computed_left: 0,
|
||||
computed_right: 0,
|
||||
@@ -77,7 +71,12 @@ impl Margin {
|
||||
|
||||
if self.computed_right - self.computed_left > self.term_width {
|
||||
// Trimming only whitespace isn't enough, let's get craftier.
|
||||
if self.label_right - self.whitespace_left <= self.term_width {
|
||||
if self.label_right.saturating_sub(self.whitespace_left) <= self.term_width
|
||||
// Trimming whitespace when the right-most label is somewhrere
|
||||
// within it would result in the label pointing to the wrong
|
||||
// place
|
||||
&& self.label_right >= self.whitespace_left
|
||||
{
|
||||
// Attempt to fit the code window only trimming whitespace.
|
||||
self.computed_left = self.whitespace_left;
|
||||
self.computed_right = self.computed_left + self.term_width;
|
||||
|
||||
@@ -1,37 +1,118 @@
|
||||
//! The renderer for [`Message`]s
|
||||
//! The [Renderer] and its settings
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! use ruff_annotate_snippets::{Renderer, Snippet, Level};
|
||||
//! let snippet = Level::Error.title("mismatched types")
|
||||
//! .snippet(Snippet::source("Foo").line_start(51).origin("src/format.rs"))
|
||||
//! .snippet(Snippet::source("Faa").line_start(129).origin("src/display.rs"));
|
||||
//!
|
||||
//! let renderer = Renderer::styled();
|
||||
//! println!("{}", renderer.render(snippet));
|
||||
//! ```
|
||||
//! # use annotate_snippets::*;
|
||||
//! # use annotate_snippets::renderer::*;
|
||||
//! # use annotate_snippets::Level;
|
||||
//! let report = // ...
|
||||
//! # &[Group::with_title(
|
||||
//! # Level::ERROR
|
||||
//! # .primary_title("unresolved import `baz::zed`")
|
||||
//! # .id("E0432")
|
||||
//! # )];
|
||||
//!
|
||||
//! let renderer = Renderer::styled().decor_style(DecorStyle::Unicode);
|
||||
//! let output = renderer.render(report);
|
||||
//! anstream::println!("{output}");
|
||||
//! ```
|
||||
|
||||
mod display_list;
|
||||
mod margin;
|
||||
mod styled_buffer;
|
||||
pub(crate) mod render;
|
||||
pub(crate) mod source_map;
|
||||
pub(crate) mod stylesheet;
|
||||
|
||||
use crate::snippet::Message;
|
||||
pub use anstyle::*;
|
||||
use display_list::DisplayList;
|
||||
use margin::Margin;
|
||||
use std::fmt::Display;
|
||||
use stylesheet::Stylesheet;
|
||||
mod margin;
|
||||
mod styled_buffer;
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::Report;
|
||||
|
||||
pub(crate) use render::ElementStyle;
|
||||
pub(crate) use render::UnderlineParts;
|
||||
pub(crate) use render::normalize_whitespace;
|
||||
pub(crate) use render::{LineAnnotation, LineAnnotationType, char_width, num_overlap};
|
||||
pub(crate) use stylesheet::Stylesheet;
|
||||
|
||||
pub use anstyle::*;
|
||||
|
||||
/// See [`Renderer::term_width`]
|
||||
pub const DEFAULT_TERM_WIDTH: usize = 140;
|
||||
|
||||
/// A renderer for [`Message`]s
|
||||
const USE_WINDOWS_COLORS: bool = cfg!(windows) && !cfg!(feature = "testing-colors");
|
||||
const BRIGHT_BLUE: Style = if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightCyan.on_default()
|
||||
} else {
|
||||
AnsiColor::BrightBlue.on_default()
|
||||
};
|
||||
/// [`Renderer::error`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_ERROR_STYLE: Style = AnsiColor::BrightRed.on_default().effects(Effects::BOLD);
|
||||
/// [`Renderer::warning`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_WARNING_STYLE: Style = if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightYellow.on_default()
|
||||
} else {
|
||||
AnsiColor::Yellow.on_default()
|
||||
}
|
||||
.effects(Effects::BOLD);
|
||||
/// [`Renderer::info`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_INFO_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
|
||||
/// [`Renderer::note`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_NOTE_STYLE: Style = AnsiColor::BrightGreen.on_default().effects(Effects::BOLD);
|
||||
/// [`Renderer::help`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_HELP_STYLE: Style = AnsiColor::BrightCyan.on_default().effects(Effects::BOLD);
|
||||
/// [`Renderer::line_num`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_LINE_NUM_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
|
||||
/// [`Renderer::emphasis`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_EMPHASIS_STYLE: Style = if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightWhite.on_default()
|
||||
} else {
|
||||
Style::new()
|
||||
}
|
||||
.effects(Effects::BOLD);
|
||||
/// [`Renderer::none`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_NONE_STYLE: Style = Style::new();
|
||||
/// [`Renderer::context`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_CONTEXT_STYLE: Style = BRIGHT_BLUE.effects(Effects::BOLD);
|
||||
/// [`Renderer::addition`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_ADDITION_STYLE: Style = AnsiColor::BrightGreen.on_default();
|
||||
/// [`Renderer::removal`] applied by [`Renderer::styled`]
|
||||
pub const DEFAULT_REMOVAL_STYLE: Style = AnsiColor::BrightRed.on_default();
|
||||
|
||||
/// The [Renderer] for a [`Report`]
|
||||
///
|
||||
/// The caller is expected to detect any relevant terminal features and configure the renderer,
|
||||
/// including
|
||||
/// - ANSI Escape code support (always outputted with [`Renderer::styled`])
|
||||
/// - Terminal width ([`Renderer::term_width`])
|
||||
/// - Unicode support ([`Renderer::decor_style`])
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use annotate_snippets::*;
|
||||
/// # use annotate_snippets::renderer::*;
|
||||
/// # use annotate_snippets::Level;
|
||||
/// let report = // ...
|
||||
/// # &[Group::with_title(
|
||||
/// # Level::ERROR
|
||||
/// # .primary_title("unresolved import `baz::zed`")
|
||||
/// # .id("E0432")
|
||||
/// # )];
|
||||
///
|
||||
/// let renderer = Renderer::styled();
|
||||
/// let output = renderer.render(report);
|
||||
/// anstream::println!("{output}");
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Renderer {
|
||||
anonymized_line_numbers: bool,
|
||||
term_width: usize,
|
||||
decor_style: DecorStyle,
|
||||
stylesheet: Stylesheet,
|
||||
cut_indicator: &'static str,
|
||||
hyperlink: bool,
|
||||
short_message: bool,
|
||||
cut_indicator: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
@@ -40,57 +121,71 @@ impl Renderer {
|
||||
Self {
|
||||
anonymized_line_numbers: false,
|
||||
term_width: DEFAULT_TERM_WIDTH,
|
||||
decor_style: DecorStyle::Ascii,
|
||||
stylesheet: Stylesheet::plain(),
|
||||
cut_indicator: "...",
|
||||
hyperlink: false,
|
||||
short_message: false,
|
||||
cut_indicator: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Default terminal styling
|
||||
///
|
||||
/// If ANSI escape codes are not supported, either
|
||||
/// - Call [`Renderer::plain`] instead
|
||||
/// - Strip them after the fact, like with [`anstream`](https://docs.rs/anstream/latest/anstream/)
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// When testing styled terminal output, see the [`testing-colors` feature](crate#features)
|
||||
pub const fn styled() -> Self {
|
||||
const USE_WINDOWS_COLORS: bool = cfg!(windows) && !cfg!(feature = "testing-colors");
|
||||
const BRIGHT_BLUE: Style = if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightCyan.on_default()
|
||||
} else {
|
||||
AnsiColor::BrightBlue.on_default()
|
||||
};
|
||||
Self {
|
||||
stylesheet: Stylesheet {
|
||||
error: AnsiColor::BrightRed.on_default().effects(Effects::BOLD),
|
||||
warning: if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightYellow.on_default()
|
||||
} else {
|
||||
AnsiColor::Yellow.on_default()
|
||||
}
|
||||
.effects(Effects::BOLD),
|
||||
info: BRIGHT_BLUE.effects(Effects::BOLD),
|
||||
note: AnsiColor::BrightGreen.on_default().effects(Effects::BOLD),
|
||||
help: AnsiColor::BrightCyan.on_default().effects(Effects::BOLD),
|
||||
line_no: BRIGHT_BLUE.effects(Effects::BOLD),
|
||||
emphasis: if USE_WINDOWS_COLORS {
|
||||
AnsiColor::BrightWhite.on_default()
|
||||
} else {
|
||||
Style::new()
|
||||
}
|
||||
.effects(Effects::BOLD),
|
||||
none: Style::new(),
|
||||
hyperlink: true,
|
||||
error: DEFAULT_ERROR_STYLE,
|
||||
warning: DEFAULT_WARNING_STYLE,
|
||||
info: DEFAULT_INFO_STYLE,
|
||||
note: DEFAULT_NOTE_STYLE,
|
||||
help: DEFAULT_HELP_STYLE,
|
||||
line_num: DEFAULT_LINE_NUM_STYLE,
|
||||
emphasis: DEFAULT_EMPHASIS_STYLE,
|
||||
none: DEFAULT_NONE_STYLE,
|
||||
context: DEFAULT_CONTEXT_STYLE,
|
||||
addition: DEFAULT_ADDITION_STYLE,
|
||||
removal: DEFAULT_REMOVAL_STYLE,
|
||||
},
|
||||
hyperlink: true,
|
||||
..Self::plain()
|
||||
}
|
||||
}
|
||||
|
||||
/// Abbreviate the message
|
||||
pub const fn short_message(mut self, short_message: bool) -> Self {
|
||||
self.short_message = short_message;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the width to render within
|
||||
///
|
||||
/// Affects the rendering of [`Snippet`][crate::Snippet]s
|
||||
pub const fn term_width(mut self, term_width: usize) -> Self {
|
||||
self.term_width = term_width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the character set used for rendering decor
|
||||
pub const fn decor_style(mut self, decor_style: DecorStyle) -> Self {
|
||||
self.decor_style = decor_style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Anonymize line numbers
|
||||
///
|
||||
/// This enables (or disables) line number anonymization. When enabled, line numbers are replaced
|
||||
/// with `LL`.
|
||||
/// When enabled, line numbers are replaced with `LL` which is useful for tests.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```text
|
||||
/// --> $DIR/whitespace-trimming.rs:LL:193
|
||||
/// --> $DIR/whitespace-trimming.rs:4:193
|
||||
/// |
|
||||
/// LL | ... let _: () = 42;
|
||||
/// | ^^ expected (), found integer
|
||||
@@ -100,82 +195,252 @@ impl Renderer {
|
||||
self.anonymized_line_numbers = anonymized_line_numbers;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the terminal width
|
||||
pub const fn term_width(mut self, term_width: usize) -> Self {
|
||||
self.term_width = term_width;
|
||||
self
|
||||
impl Renderer {
|
||||
/// Render a diagnostic [`Report`]
|
||||
pub fn render(&self, groups: Report<'_>) -> String {
|
||||
render::render(self, groups)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the output style for `error`
|
||||
/// Customize [`Renderer::styled`]
|
||||
impl Renderer {
|
||||
/// Override the output style for [error][crate::Level::ERROR]
|
||||
pub const fn error(mut self, style: Style) -> Self {
|
||||
self.stylesheet.error = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for `warning`
|
||||
/// Override the output style for [warnings][crate::Level::WARNING]
|
||||
pub const fn warning(mut self, style: Style) -> Self {
|
||||
self.stylesheet.warning = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for `info`
|
||||
/// Override the output style for [info][crate::Level::INFO]
|
||||
pub const fn info(mut self, style: Style) -> Self {
|
||||
self.stylesheet.info = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for `note`
|
||||
/// Override the output style for [notes][crate::Level::NOTE]
|
||||
pub const fn note(mut self, style: Style) -> Self {
|
||||
self.stylesheet.note = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for `help`
|
||||
/// Override the output style for [help][crate::Level::HELP]
|
||||
pub const fn help(mut self, style: Style) -> Self {
|
||||
self.stylesheet.help = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for line numbers
|
||||
pub const fn line_no(mut self, style: Style) -> Self {
|
||||
self.stylesheet.line_no = style;
|
||||
/// Override the output style for line numbers in the [`Snippet`][crate::Snippet] gutter
|
||||
pub const fn line_num(mut self, style: Style) -> Self {
|
||||
self.stylesheet.line_num = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for emphasis
|
||||
/// Override the output style for emphasis for the
|
||||
/// [`primary_title`][crate::Level::primary_title]
|
||||
pub const fn emphasis(mut self, style: Style) -> Self {
|
||||
self.stylesheet.emphasis = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the output style for none
|
||||
/// Override the output style for [`AnnotationKind::Context`][crate::AnnotationKind::Context]
|
||||
pub const fn context(mut self, style: Style) -> Self {
|
||||
self.stylesheet.context = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the output style for [`Patch`][crate::Patch] additions
|
||||
pub const fn addition(mut self, style: Style) -> Self {
|
||||
self.stylesheet.addition = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the output style for [`Patch`][crate::Patch] removals
|
||||
pub const fn removal(mut self, style: Style) -> Self {
|
||||
self.stylesheet.removal = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the output style for all other text
|
||||
pub const fn none(mut self, style: Style) -> Self {
|
||||
self.stylesheet.none = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub const fn hyperlink(mut self, hyperlink: bool) -> Self {
|
||||
self.stylesheet.hyperlink = hyperlink;
|
||||
self.hyperlink = hyperlink;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the string used for when a long line is cut.
|
||||
///
|
||||
/// The default is `...` (three `U+002E` characters).
|
||||
pub const fn cut_indicator(mut self, string: &'static str) -> Self {
|
||||
self.cut_indicator = string;
|
||||
/// The default for [`DecorStyle::Ascii`] is `...` (three `U+002E` characters).
|
||||
pub const fn cut_indicator(mut self, cut: &'static str) -> Self {
|
||||
self.cut_indicator = Some(cut);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Render a snippet into a `Display`able object
|
||||
pub fn render<'a>(&'a self, msg: Message<'a>) -> impl Display + 'a {
|
||||
DisplayList::new(
|
||||
msg,
|
||||
&self.stylesheet,
|
||||
self.anonymized_line_numbers,
|
||||
self.term_width,
|
||||
self.cut_indicator,
|
||||
)
|
||||
/// The character set for rendering for decor
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DecorStyle {
|
||||
Ascii,
|
||||
Unicode,
|
||||
}
|
||||
|
||||
impl DecorStyle {
|
||||
fn col_separator(&self) -> char {
|
||||
match self {
|
||||
DecorStyle::Ascii => '|',
|
||||
DecorStyle::Unicode => '│',
|
||||
}
|
||||
}
|
||||
|
||||
fn note_separator(&self, is_cont: bool) -> &str {
|
||||
match self {
|
||||
DecorStyle::Ascii => "= ",
|
||||
DecorStyle::Unicode if is_cont => "├ ",
|
||||
DecorStyle::Unicode => "╰ ",
|
||||
}
|
||||
}
|
||||
|
||||
fn multi_suggestion_separator(&self) -> &'static str {
|
||||
match self {
|
||||
DecorStyle::Ascii => "|",
|
||||
DecorStyle::Unicode => "├╴",
|
||||
}
|
||||
}
|
||||
|
||||
fn file_start(&self, is_first: bool, alone: bool) -> &'static str {
|
||||
match self {
|
||||
DecorStyle::Ascii => "--> ",
|
||||
DecorStyle::Unicode if is_first && alone => " ─▸ ",
|
||||
DecorStyle::Unicode if is_first => " ╭▸ ",
|
||||
DecorStyle::Unicode => " ├▸ ",
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary_file_start(&self) -> &'static str {
|
||||
match self {
|
||||
DecorStyle::Ascii => "::: ",
|
||||
DecorStyle::Unicode => " ⸬ ",
|
||||
}
|
||||
}
|
||||
|
||||
fn diff(&self) -> char {
|
||||
match self {
|
||||
DecorStyle::Ascii => '~',
|
||||
DecorStyle::Unicode => '±',
|
||||
}
|
||||
}
|
||||
|
||||
fn margin(&self) -> &'static str {
|
||||
match self {
|
||||
DecorStyle::Ascii => "...",
|
||||
DecorStyle::Unicode => "…",
|
||||
}
|
||||
}
|
||||
|
||||
fn underline(&self, is_primary: bool) -> UnderlineParts {
|
||||
// X0 Y0
|
||||
// label_start > ┯━━━━ < underline
|
||||
// │ < vertical_text_line
|
||||
// text
|
||||
|
||||
// multiline_start_down ⤷ X0 Y0
|
||||
// top_left > ┌───╿──┘ < top_right_flat
|
||||
// top_left > ┏│━━━┙ < top_right
|
||||
// multiline_vertical > ┃│
|
||||
// ┃│ X1 Y1
|
||||
// ┃│ X2 Y2
|
||||
// ┃└────╿──┘ < multiline_end_same_line
|
||||
// bottom_left > ┗━━━━━┥ < bottom_right_with_text
|
||||
// multiline_horizontal ^ `X` is a good letter
|
||||
|
||||
// multiline_whole_line > ┏ X0 Y0
|
||||
// ┃ X1 Y1
|
||||
// ┗━━━━┛ < multiline_end_same_line
|
||||
|
||||
// multiline_whole_line > ┏ X0 Y0
|
||||
// ┃ X1 Y1
|
||||
// ┃ ╿ < multiline_end_up
|
||||
// ┗━━┛ < bottom_right
|
||||
|
||||
match (self, is_primary) {
|
||||
(DecorStyle::Ascii, true) => UnderlineParts {
|
||||
style: ElementStyle::UnderlinePrimary,
|
||||
underline: '^',
|
||||
label_start: '^',
|
||||
vertical_text_line: '|',
|
||||
multiline_vertical: '|',
|
||||
multiline_horizontal: '_',
|
||||
multiline_whole_line: '/',
|
||||
multiline_start_down: '^',
|
||||
bottom_right: '|',
|
||||
top_left: ' ',
|
||||
top_right_flat: '^',
|
||||
bottom_left: '|',
|
||||
multiline_end_up: '^',
|
||||
multiline_end_same_line: '^',
|
||||
multiline_bottom_right_with_text: '|',
|
||||
},
|
||||
(DecorStyle::Ascii, false) => UnderlineParts {
|
||||
style: ElementStyle::UnderlineSecondary,
|
||||
underline: '-',
|
||||
label_start: '-',
|
||||
vertical_text_line: '|',
|
||||
multiline_vertical: '|',
|
||||
multiline_horizontal: '_',
|
||||
multiline_whole_line: '/',
|
||||
multiline_start_down: '-',
|
||||
bottom_right: '|',
|
||||
top_left: ' ',
|
||||
top_right_flat: '-',
|
||||
bottom_left: '|',
|
||||
multiline_end_up: '-',
|
||||
multiline_end_same_line: '-',
|
||||
multiline_bottom_right_with_text: '|',
|
||||
},
|
||||
(DecorStyle::Unicode, true) => UnderlineParts {
|
||||
style: ElementStyle::UnderlinePrimary,
|
||||
underline: '━',
|
||||
label_start: '┯',
|
||||
vertical_text_line: '│',
|
||||
multiline_vertical: '┃',
|
||||
multiline_horizontal: '━',
|
||||
multiline_whole_line: '┏',
|
||||
multiline_start_down: '╿',
|
||||
bottom_right: '┙',
|
||||
top_left: '┏',
|
||||
top_right_flat: '┛',
|
||||
bottom_left: '┗',
|
||||
multiline_end_up: '╿',
|
||||
multiline_end_same_line: '┛',
|
||||
multiline_bottom_right_with_text: '┥',
|
||||
},
|
||||
(DecorStyle::Unicode, false) => UnderlineParts {
|
||||
style: ElementStyle::UnderlineSecondary,
|
||||
underline: '─',
|
||||
label_start: '┬',
|
||||
vertical_text_line: '│',
|
||||
multiline_vertical: '│',
|
||||
multiline_horizontal: '─',
|
||||
multiline_whole_line: '┌',
|
||||
multiline_start_down: '│',
|
||||
bottom_right: '┘',
|
||||
top_left: '┌',
|
||||
top_right_flat: '┘',
|
||||
bottom_left: '└',
|
||||
multiline_end_up: '│',
|
||||
multiline_end_same_line: '┘',
|
||||
multiline_bottom_right_with_text: '┤',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,828 @@
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::string::String;
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::cmp::{max, min};
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::renderer::{LineAnnotation, LineAnnotationType, char_width, num_overlap};
|
||||
use crate::{Annotation, AnnotationKind, Patch};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SourceMap<'a> {
|
||||
lines: Vec<LineInfo<'a>>,
|
||||
pub(crate) source: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> SourceMap<'a> {
|
||||
pub(crate) fn new(source: &'a str, line_start: usize) -> Self {
|
||||
// Empty sources do have a "line", but it is empty, so we need to add
|
||||
// a line with an empty string to the source map.
|
||||
if source.is_empty() {
|
||||
return Self {
|
||||
lines: vec![LineInfo {
|
||||
line: "",
|
||||
line_index: line_start,
|
||||
start_byte: 0,
|
||||
end_byte: 0,
|
||||
end_line_size: 0,
|
||||
}],
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
let mut current_index = 0;
|
||||
|
||||
let mut mapping = vec![];
|
||||
for (idx, (line, end_line)) in CursorLines::new(source).enumerate() {
|
||||
let line_length = line.len();
|
||||
let line_range = current_index..current_index + line_length;
|
||||
let end_line_size = end_line.len();
|
||||
|
||||
mapping.push(LineInfo {
|
||||
line,
|
||||
line_index: line_start + idx,
|
||||
start_byte: line_range.start,
|
||||
end_byte: line_range.end + end_line_size,
|
||||
end_line_size,
|
||||
});
|
||||
|
||||
current_index += line_length + end_line_size;
|
||||
}
|
||||
Self {
|
||||
lines: mapping,
|
||||
source,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_line(&self, idx: usize) -> Option<&'a str> {
|
||||
self.lines
|
||||
.iter()
|
||||
.find(|l| l.line_index == idx)
|
||||
.map(|info| info.line)
|
||||
}
|
||||
|
||||
pub(crate) fn span_to_locations(&self, span: Range<usize>) -> (Loc, Loc) {
|
||||
let start_info = self
|
||||
.lines
|
||||
.iter()
|
||||
.find(|info| span.start >= info.start_byte && span.start < info.end_byte)
|
||||
.unwrap_or(self.lines.last().unwrap());
|
||||
let (mut start_char_pos, start_display_pos) = start_info.line
|
||||
[0..(span.start - start_info.start_byte).min(start_info.line.len())]
|
||||
.chars()
|
||||
.fold((0, 0), |(char_pos, byte_pos), c| {
|
||||
let display = char_width(c);
|
||||
(char_pos + 1, byte_pos + display)
|
||||
});
|
||||
// correct the char pos if we are highlighting the end of a line
|
||||
if (span.start - start_info.start_byte).saturating_sub(start_info.line.len()) > 0 {
|
||||
start_char_pos += 1;
|
||||
}
|
||||
let start = Loc {
|
||||
line: start_info.line_index,
|
||||
char: start_char_pos,
|
||||
display: start_display_pos,
|
||||
byte: span.start,
|
||||
};
|
||||
|
||||
if span.start == span.end {
|
||||
return (start, start);
|
||||
}
|
||||
|
||||
let (end_idx, end_info, eof) = self
|
||||
.lines
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, info)| span.end >= info.start_byte && span.end < info.end_byte)
|
||||
.map(|(idx, info)| (idx, info, false))
|
||||
.unwrap_or((self.lines.len() - 1, self.lines.last().unwrap(), true));
|
||||
let (end_char_pos, end_display_pos) = end_info.line
|
||||
[0..(span.end - end_info.start_byte).min(end_info.line.len())]
|
||||
.chars()
|
||||
.fold((0, 0), |(char_pos, byte_pos), c| {
|
||||
let display = char_width(c);
|
||||
(char_pos + 1, byte_pos + display)
|
||||
});
|
||||
|
||||
let mut end = Loc {
|
||||
line: end_info.line_index,
|
||||
char: end_char_pos,
|
||||
display: end_display_pos,
|
||||
byte: span.end,
|
||||
};
|
||||
if start.line < end.line && end.char == 0 && !eof {
|
||||
let prev_line_info = &self.lines[end_idx - 1];
|
||||
let (end_char_pos, end_display_pos) = prev_line_info.line
|
||||
[0..(span.end - prev_line_info.start_byte).min(prev_line_info.line.len())]
|
||||
.chars()
|
||||
.fold((0, 0), |(char_pos, byte_pos), c| {
|
||||
let display = char_width(c);
|
||||
(char_pos + 1, byte_pos + display)
|
||||
});
|
||||
if prev_line_info.end_byte == start.byte {
|
||||
end = Loc {
|
||||
line: prev_line_info.line_index,
|
||||
char: end_char_pos + 1,
|
||||
display: end_display_pos + 1,
|
||||
byte: span.end,
|
||||
};
|
||||
} else {
|
||||
end = Loc {
|
||||
line: prev_line_info.line_index,
|
||||
char: end_char_pos,
|
||||
display: end_display_pos,
|
||||
byte: span.end,
|
||||
};
|
||||
}
|
||||
}
|
||||
if start.line != end.line && end.byte > end_info.end_byte - end_info.end_line_size {
|
||||
end.char += 1;
|
||||
end.display += 1;
|
||||
}
|
||||
|
||||
(start, end)
|
||||
}
|
||||
|
||||
pub(crate) fn span_to_snippet(&self, span: Range<usize>) -> Option<&str> {
|
||||
self.source.get(span)
|
||||
}
|
||||
|
||||
pub(crate) fn span_to_lines(&self, span: Range<usize>) -> Vec<&LineInfo<'a>> {
|
||||
let mut lines = vec![];
|
||||
let start = span.start;
|
||||
let end = span.end;
|
||||
for line_info in &self.lines {
|
||||
if start >= line_info.end_byte {
|
||||
continue;
|
||||
}
|
||||
if end < line_info.start_byte {
|
||||
break;
|
||||
}
|
||||
lines.push(line_info);
|
||||
}
|
||||
|
||||
if lines.is_empty() && !self.lines.is_empty() {
|
||||
lines.push(self.lines.last().unwrap());
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
pub(crate) fn annotated_lines(
|
||||
&self,
|
||||
annotations: Vec<Annotation<'a>>,
|
||||
fold: bool,
|
||||
) -> (usize, Vec<AnnotatedLineInfo<'a>>) {
|
||||
let source_len = self.source.len();
|
||||
if let Some(bigger) = annotations.iter().find_map(|x| {
|
||||
// Allow highlighting one past the last character in the source.
|
||||
if source_len + 1 < x.span.end {
|
||||
Some(&x.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
panic!("Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`")
|
||||
}
|
||||
|
||||
let mut annotated_line_infos = self
|
||||
.lines
|
||||
.iter()
|
||||
.map(|info| AnnotatedLineInfo {
|
||||
line: info.line,
|
||||
line_index: info.line_index,
|
||||
annotations: vec![],
|
||||
keep: false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let mut multiline_annotations = vec![];
|
||||
|
||||
for Annotation {
|
||||
span,
|
||||
label,
|
||||
kind,
|
||||
highlight_source,
|
||||
is_file_level: _,
|
||||
} in annotations
|
||||
{
|
||||
let (lo, mut hi) = self.span_to_locations(span.clone());
|
||||
if kind == AnnotationKind::Visible {
|
||||
for line_idx in lo.line..=hi.line {
|
||||
self.keep_line(&mut annotated_line_infos, line_idx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Watch out for "empty spans". If we get a span like 6..6, we
|
||||
// want to just display a `^` at 6, so convert that to
|
||||
// 6..7. This is degenerate input, but it's best to degrade
|
||||
// gracefully -- and the parser likes to supply a span like
|
||||
// that for EOF, in particular.
|
||||
|
||||
if lo.display == hi.display && lo.line == hi.line {
|
||||
hi.display += 1;
|
||||
}
|
||||
|
||||
if lo.line == hi.line {
|
||||
let line_ann = LineAnnotation {
|
||||
start: lo,
|
||||
end: hi,
|
||||
kind,
|
||||
label,
|
||||
annotation_type: LineAnnotationType::Singleline,
|
||||
highlight_source,
|
||||
};
|
||||
self.add_annotation_to_file(&mut annotated_line_infos, lo.line, line_ann);
|
||||
} else {
|
||||
multiline_annotations.push(MultilineAnnotation {
|
||||
depth: 1,
|
||||
start: lo,
|
||||
end: hi,
|
||||
kind,
|
||||
label,
|
||||
overlaps_exactly: false,
|
||||
highlight_source,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut primary_spans = vec![];
|
||||
|
||||
// Find overlapping multiline annotations, put them at different depths
|
||||
multiline_annotations.sort_by_key(|ml| (ml.start.line, usize::MAX - ml.end.line));
|
||||
for (outer_i, ann) in multiline_annotations.clone().into_iter().enumerate() {
|
||||
if ann.kind.is_primary() {
|
||||
primary_spans.push((ann.start, ann.end));
|
||||
}
|
||||
for (inner_i, a) in &mut multiline_annotations.iter_mut().enumerate() {
|
||||
// Move all other multiline annotations overlapping with this one
|
||||
// one level to the right.
|
||||
if !ann.same_span(a)
|
||||
&& num_overlap(ann.start.line, ann.end.line, a.start.line, a.end.line, true)
|
||||
{
|
||||
a.increase_depth();
|
||||
} else if ann.same_span(a) && outer_i != inner_i {
|
||||
a.overlaps_exactly = true;
|
||||
} else {
|
||||
if primary_spans
|
||||
.iter()
|
||||
.any(|(s, e)| a.start == *s && a.end == *e)
|
||||
{
|
||||
a.kind = AnnotationKind::Primary;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut max_depth = 0; // max overlapping multiline spans
|
||||
for ann in &multiline_annotations {
|
||||
max_depth = max(max_depth, ann.depth);
|
||||
}
|
||||
// Change order of multispan depth to minimize the number of overlaps in the ASCII art.
|
||||
for a in &mut multiline_annotations {
|
||||
a.depth = max_depth - a.depth + 1;
|
||||
}
|
||||
for ann in multiline_annotations {
|
||||
let mut end_ann = ann.as_end();
|
||||
if ann.overlaps_exactly {
|
||||
end_ann.annotation_type = LineAnnotationType::Singleline;
|
||||
} else {
|
||||
// avoid output like
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | |_____|
|
||||
// | || bar,
|
||||
// | || );
|
||||
// | || ^
|
||||
// | ||______|
|
||||
// | |______foo
|
||||
// | baz
|
||||
//
|
||||
// and instead get
|
||||
//
|
||||
// | foo(
|
||||
// | _____^
|
||||
// | | bar,
|
||||
// | | );
|
||||
// | | ^
|
||||
// | | |
|
||||
// | |______foo
|
||||
// | baz
|
||||
self.add_annotation_to_file(
|
||||
&mut annotated_line_infos,
|
||||
ann.start.line,
|
||||
ann.as_start(),
|
||||
);
|
||||
// 4 is the minimum vertical length of a multiline span when presented: two lines
|
||||
// of code and two lines of underline. This is not true for the special case where
|
||||
// the beginning doesn't have an underline, but the current logic seems to be
|
||||
// working correctly.
|
||||
let middle = min(ann.start.line + 4, ann.end.line);
|
||||
// We'll show up to 4 lines past the beginning of the multispan start.
|
||||
// We will *not* include the tail of lines that are only whitespace, a comment or
|
||||
// a bare delimiter.
|
||||
let filter = |s: &str| {
|
||||
let s = s.trim();
|
||||
// Consider comments as empty, but don't consider docstrings to be empty.
|
||||
!(s.starts_with("//") && !(s.starts_with("///") || s.starts_with("//!")))
|
||||
// Consider lines with nothing but whitespace, a single delimiter as empty.
|
||||
&& !["", "{", "}", "(", ")", "[", "]"].contains(&s)
|
||||
};
|
||||
let until = (ann.start.line..middle)
|
||||
.rev()
|
||||
.filter_map(|line| self.get_line(line).map(|s| (line + 1, s)))
|
||||
.find(|(_, s)| filter(s))
|
||||
.map_or(ann.start.line, |(line, _)| line);
|
||||
for line in ann.start.line + 1..until {
|
||||
// Every `|` that joins the beginning of the span (`___^`) to the end (`|__^`).
|
||||
self.add_annotation_to_file(&mut annotated_line_infos, line, ann.as_line());
|
||||
}
|
||||
let line_end = ann.end.line - 1;
|
||||
let end_is_empty = self.get_line(line_end).is_some_and(|s| !filter(s));
|
||||
if middle < line_end && !end_is_empty {
|
||||
self.add_annotation_to_file(&mut annotated_line_infos, line_end, ann.as_line());
|
||||
}
|
||||
}
|
||||
self.add_annotation_to_file(&mut annotated_line_infos, end_ann.end.line, end_ann);
|
||||
}
|
||||
|
||||
if fold {
|
||||
annotated_line_infos.retain(|l| !l.annotations.is_empty() || l.keep);
|
||||
}
|
||||
|
||||
(max_depth, annotated_line_infos)
|
||||
}
|
||||
|
||||
fn add_annotation_to_file(
|
||||
&self,
|
||||
annotated_line_infos: &mut Vec<AnnotatedLineInfo<'a>>,
|
||||
line_index: usize,
|
||||
line_ann: LineAnnotation<'a>,
|
||||
) {
|
||||
if let Some(line_info) = annotated_line_infos
|
||||
.iter_mut()
|
||||
.find(|line_info| line_info.line_index == line_index)
|
||||
{
|
||||
line_info.annotations.push(line_ann);
|
||||
} else {
|
||||
let info = self
|
||||
.lines
|
||||
.iter()
|
||||
.find(|l| l.line_index == line_index)
|
||||
.unwrap();
|
||||
annotated_line_infos.push(AnnotatedLineInfo {
|
||||
line: info.line,
|
||||
line_index,
|
||||
annotations: vec![line_ann],
|
||||
keep: false,
|
||||
});
|
||||
annotated_line_infos.sort_by_key(|l| l.line_index);
|
||||
}
|
||||
}
|
||||
|
||||
fn keep_line(&self, annotated_line_infos: &mut Vec<AnnotatedLineInfo<'a>>, line_index: usize) {
|
||||
if let Some(line_info) = annotated_line_infos
|
||||
.iter_mut()
|
||||
.find(|line_info| line_info.line_index == line_index)
|
||||
{
|
||||
line_info.keep = true;
|
||||
} else {
|
||||
let info = self
|
||||
.lines
|
||||
.iter()
|
||||
.find(|l| l.line_index == line_index)
|
||||
.unwrap();
|
||||
annotated_line_infos.push(AnnotatedLineInfo {
|
||||
line: info.line,
|
||||
line_index,
|
||||
annotations: vec![],
|
||||
keep: true,
|
||||
});
|
||||
annotated_line_infos.sort_by_key(|l| l.line_index);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn splice_lines<'b>(
|
||||
&'a self,
|
||||
mut patches: Vec<Patch<'b>>,
|
||||
fold: bool,
|
||||
) -> Option<SplicedLines<'b>> {
|
||||
fn push_trailing(buf: &mut String, line_opt: Option<&str>, lo: &Loc, hi_opt: Option<&Loc>) {
|
||||
// Convert CharPos to Usize, as CharPose is character offset
|
||||
// Extract low index and high index
|
||||
let (lo, hi_opt) = (lo.char, hi_opt.map(|hi| hi.char));
|
||||
if let Some(line) = line_opt {
|
||||
if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
|
||||
// Get high index while account for rare unicode and emoji with char_indices
|
||||
let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
|
||||
match hi_opt {
|
||||
// If high index exist, take string from low to high index
|
||||
Some(hi) if hi > lo => buf.push_str(&line[lo..hi]),
|
||||
Some(_) => (),
|
||||
// If high index absence, take string from low index till end string.len
|
||||
None => buf.push_str(&line[lo..]),
|
||||
}
|
||||
}
|
||||
// If high index is None
|
||||
if hi_opt.is_none() {
|
||||
buf.push('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let source_len = self.source.len();
|
||||
if let Some(bigger) = patches.iter().find_map(|x| {
|
||||
// Allow patching one past the last character in the source.
|
||||
if source_len + 1 < x.span.end {
|
||||
Some(&x.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
panic!("Patch span `{bigger:?}` is beyond the end of buffer `{source_len}`")
|
||||
}
|
||||
|
||||
// Assumption: all spans are in the same file, and all spans
|
||||
// are disjoint. Sort in ascending order.
|
||||
patches.sort_by_key(|p| p.span.start);
|
||||
|
||||
// Find the bounding span.
|
||||
let (lo, hi) = if fold {
|
||||
let lo = patches.iter().map(|p| p.span.start).min()?;
|
||||
let hi = patches.iter().map(|p| p.span.end).max()?;
|
||||
(lo, hi)
|
||||
} else {
|
||||
(0, source_len)
|
||||
};
|
||||
|
||||
let lines = self.span_to_lines(lo..hi);
|
||||
|
||||
let mut highlights = vec![];
|
||||
// To build up the result, we do this for each span:
|
||||
// - push the line segment trailing the previous span
|
||||
// (at the beginning a "phantom" span pointing at the start of the line)
|
||||
// - push lines between the previous and current span (if any)
|
||||
// - if the previous and current span are not on the same line
|
||||
// push the line segment leading up to the current span
|
||||
// - splice in the span substitution
|
||||
//
|
||||
// Finally push the trailing line segment of the last span
|
||||
let (mut prev_hi, _) = self.span_to_locations(lo..hi);
|
||||
prev_hi.char = 0;
|
||||
let mut prev_line = lines.first().map(|line| line.line);
|
||||
let mut buf = String::new();
|
||||
|
||||
let trimmed_patches = patches
|
||||
.into_iter()
|
||||
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
|
||||
// suggestion and snippet to look as if we just suggested to add
|
||||
// `"b"`, which is typically much easier for the user to understand.
|
||||
.map(|part| part.trim_trivial_replacements(self.source))
|
||||
.collect::<Vec<_>>();
|
||||
let mut line_highlight = vec![];
|
||||
// We need to keep track of the difference between the existing code and the added
|
||||
// or deleted code in order to point at the correct column *after* substitution.
|
||||
let mut acc = 0;
|
||||
for part in &trimmed_patches {
|
||||
let (cur_lo, cur_hi) = self.span_to_locations(part.span.clone());
|
||||
if prev_hi.line == cur_lo.line {
|
||||
push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
|
||||
} else {
|
||||
acc = 0;
|
||||
highlights.push(core::mem::take(&mut line_highlight));
|
||||
push_trailing(&mut buf, prev_line, &prev_hi, None);
|
||||
// push lines between the previous and current span (if any)
|
||||
for idx in prev_hi.line + 1..(cur_lo.line) {
|
||||
if let Some(line) = self.get_line(idx) {
|
||||
buf.push_str(line.as_ref());
|
||||
buf.push('\n');
|
||||
highlights.push(core::mem::take(&mut line_highlight));
|
||||
}
|
||||
}
|
||||
if let Some(cur_line) = self.get_line(cur_lo.line) {
|
||||
let end = match cur_line.char_indices().nth(cur_lo.char) {
|
||||
Some((i, _)) => i,
|
||||
None => cur_line.len(),
|
||||
};
|
||||
buf.push_str(&cur_line[..end]);
|
||||
}
|
||||
}
|
||||
// Add a whole line highlight per line in the snippet.
|
||||
let len: isize = part
|
||||
.replacement
|
||||
.split('\n')
|
||||
.next()
|
||||
.unwrap_or(&part.replacement)
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'\t' => 4,
|
||||
_ => 1,
|
||||
})
|
||||
.sum();
|
||||
line_highlight.push(SubstitutionHighlight {
|
||||
start: (cur_lo.char as isize + acc) as usize,
|
||||
end: (cur_lo.char as isize + acc + len) as usize,
|
||||
});
|
||||
buf.push_str(&part.replacement);
|
||||
// Account for the difference between the width of the current code and the
|
||||
// snippet being suggested, so that the *later* suggestions are correctly
|
||||
// aligned on the screen. Note that cur_hi and cur_lo can be on different
|
||||
// lines, so cur_hi.col can be smaller than cur_lo.col
|
||||
acc += len - (cur_hi.char as isize - cur_lo.char as isize);
|
||||
prev_hi = cur_hi;
|
||||
prev_line = self.get_line(prev_hi.line);
|
||||
for line in part.replacement.split('\n').skip(1) {
|
||||
acc = 0;
|
||||
highlights.push(core::mem::take(&mut line_highlight));
|
||||
let end: usize = line
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'\t' => 4,
|
||||
_ => 1,
|
||||
})
|
||||
.sum();
|
||||
line_highlight.push(SubstitutionHighlight { start: 0, end });
|
||||
}
|
||||
}
|
||||
highlights.push(core::mem::take(&mut line_highlight));
|
||||
if fold {
|
||||
// if the replacement already ends with a newline, don't print the next line
|
||||
if !buf.ends_with('\n') {
|
||||
push_trailing(&mut buf, prev_line, &prev_hi, None);
|
||||
}
|
||||
} else {
|
||||
// Add the trailing part of the source after the last patch
|
||||
if let Some(snippet) = self.span_to_snippet(prev_hi.byte..source_len) {
|
||||
buf.push_str(snippet);
|
||||
for _ in snippet.matches('\n') {
|
||||
highlights.push(core::mem::take(&mut line_highlight));
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove trailing newlines
|
||||
while buf.ends_with('\n') {
|
||||
buf.pop();
|
||||
}
|
||||
|
||||
let (bounding_lo, bounding_hi) = self.span_to_locations(lo..hi);
|
||||
let line_count = bounding_hi.line.saturating_sub(bounding_lo.line) + 1;
|
||||
let mut replaced_highlights: Vec<Vec<SubstitutionHighlight>> = vec![Vec::new(); line_count];
|
||||
for part in &trimmed_patches {
|
||||
let (cur_lo, cur_hi) = self.span_to_locations(part.span.clone());
|
||||
for line in cur_lo.line..=cur_hi.line {
|
||||
let start = if line == cur_lo.line { cur_lo.char } else { 0 };
|
||||
let end = if line == cur_hi.line {
|
||||
cur_hi.char
|
||||
} else {
|
||||
self.get_line(line).unwrap_or_default().chars().count()
|
||||
};
|
||||
replaced_highlights[line - bounding_lo.line]
|
||||
.push(SubstitutionHighlight { start, end });
|
||||
}
|
||||
}
|
||||
|
||||
if highlights.iter().all(|parts| parts.is_empty()) {
|
||||
None
|
||||
} else {
|
||||
Some((buf, trimmed_patches, highlights, replaced_highlights))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub(crate) struct MultilineAnnotation<'a> {
|
||||
pub depth: usize,
|
||||
pub start: Loc,
|
||||
pub end: Loc,
|
||||
pub kind: AnnotationKind,
|
||||
pub label: Option<Cow<'a, str>>,
|
||||
pub overlaps_exactly: bool,
|
||||
pub highlight_source: bool,
|
||||
}
|
||||
|
||||
impl<'a> MultilineAnnotation<'a> {
|
||||
pub(crate) fn increase_depth(&mut self) {
|
||||
self.depth += 1;
|
||||
}
|
||||
|
||||
/// Compare two `MultilineAnnotation`s considering only the `Span` they cover.
|
||||
pub(crate) fn same_span(&self, other: &MultilineAnnotation<'_>) -> bool {
|
||||
self.start == other.start && self.end == other.end
|
||||
}
|
||||
|
||||
pub(crate) fn as_start(&self) -> LineAnnotation<'a> {
|
||||
LineAnnotation {
|
||||
start: self.start,
|
||||
end: Loc {
|
||||
line: self.start.line,
|
||||
char: self.start.char + 1,
|
||||
display: self.start.display + 1,
|
||||
byte: self.start.byte + 1,
|
||||
},
|
||||
kind: self.kind,
|
||||
label: None,
|
||||
annotation_type: LineAnnotationType::MultilineStart(self.depth),
|
||||
highlight_source: self.highlight_source,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_end(&self) -> LineAnnotation<'a> {
|
||||
LineAnnotation {
|
||||
start: Loc {
|
||||
line: self.end.line,
|
||||
char: self.end.char.saturating_sub(1),
|
||||
display: self.end.display.saturating_sub(1),
|
||||
byte: self.end.byte.saturating_sub(1),
|
||||
},
|
||||
end: self.end,
|
||||
kind: self.kind,
|
||||
label: self.label.clone(),
|
||||
annotation_type: LineAnnotationType::MultilineEnd(self.depth),
|
||||
highlight_source: self.highlight_source,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_line(&self) -> LineAnnotation<'a> {
|
||||
LineAnnotation {
|
||||
start: Loc::default(),
|
||||
end: Loc::default(),
|
||||
kind: self.kind,
|
||||
label: None,
|
||||
annotation_type: LineAnnotationType::MultilineLine(self.depth),
|
||||
highlight_source: self.highlight_source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LineInfo<'a> {
|
||||
pub(crate) line: &'a str,
|
||||
pub(crate) line_index: usize,
|
||||
pub(crate) start_byte: usize,
|
||||
pub(crate) end_byte: usize,
|
||||
end_line_size: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AnnotatedLineInfo<'a> {
|
||||
pub(crate) line: &'a str,
|
||||
pub(crate) line_index: usize,
|
||||
pub(crate) annotations: Vec<LineAnnotation<'a>>,
|
||||
pub(crate) keep: bool,
|
||||
}
|
||||
|
||||
/// A source code location used for error reporting.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub(crate) struct Loc {
|
||||
/// The (1-based) line number.
|
||||
pub(crate) line: usize,
|
||||
/// The (0-based) column offset.
|
||||
pub(crate) char: usize,
|
||||
/// The (0-based) column offset when displayed.
|
||||
pub(crate) display: usize,
|
||||
/// The (0-based) byte offset.
|
||||
pub(crate) byte: usize,
|
||||
}
|
||||
|
||||
struct CursorLines<'a>(&'a str);
|
||||
|
||||
impl CursorLines<'_> {
|
||||
fn new(src: &str) -> CursorLines<'_> {
|
||||
CursorLines(src)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
enum EndLine {
|
||||
Eof,
|
||||
Lf,
|
||||
Crlf,
|
||||
}
|
||||
|
||||
impl EndLine {
|
||||
/// The number of characters this line ending occupies in bytes.
|
||||
pub(crate) fn len(self) -> usize {
|
||||
match self {
|
||||
EndLine::Eof => 0,
|
||||
EndLine::Lf => 1,
|
||||
EndLine::Crlf => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for CursorLines<'a> {
|
||||
type Item = (&'a str, EndLine);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.0.is_empty() {
|
||||
None
|
||||
} else {
|
||||
self.0
|
||||
.find('\n')
|
||||
.map(|x| {
|
||||
let ret = if 0 < x {
|
||||
if self.0.as_bytes()[x - 1] == b'\r' {
|
||||
(&self.0[..x - 1], EndLine::Crlf)
|
||||
} else {
|
||||
(&self.0[..x], EndLine::Lf)
|
||||
}
|
||||
} else {
|
||||
("", EndLine::Lf)
|
||||
};
|
||||
self.0 = &self.0[x + 1..];
|
||||
ret
|
||||
})
|
||||
.or_else(|| {
|
||||
let ret = Some((self.0, EndLine::Eof));
|
||||
self.0 = "";
|
||||
ret
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type SplicedLines<'a> = (
|
||||
String,
|
||||
Vec<TrimmedPatch<'a>>,
|
||||
// Char spans to highlight per line of the post-substitution output.
|
||||
Vec<Vec<SubstitutionHighlight>>,
|
||||
// Char spans of the replaced (original) code, per original line in the
|
||||
// bounding range covered by the splice.
|
||||
Vec<Vec<SubstitutionHighlight>>,
|
||||
);
|
||||
|
||||
/// Used to translate between `Span`s and byte positions within a single output line in highlighted
|
||||
/// code of structured suggestions.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct SubstitutionHighlight {
|
||||
pub(crate) start: usize,
|
||||
pub(crate) end: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TrimmedPatch<'a> {
|
||||
pub(crate) original_span: Range<usize>,
|
||||
pub(crate) span: Range<usize>,
|
||||
pub(crate) replacement: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> TrimmedPatch<'a> {
|
||||
pub(crate) fn is_addition(&self, sm: &SourceMap<'_>) -> bool {
|
||||
!self.replacement.is_empty() && !self.replaces_meaningful_content(sm)
|
||||
}
|
||||
|
||||
pub(crate) fn is_deletion(&self, sm: &SourceMap<'_>) -> bool {
|
||||
self.replacement.trim().is_empty() && self.replaces_meaningful_content(sm)
|
||||
}
|
||||
|
||||
pub(crate) fn is_replacement(&self, sm: &SourceMap<'_>) -> bool {
|
||||
!self.replacement.is_empty() && self.replaces_meaningful_content(sm)
|
||||
}
|
||||
|
||||
/// Whether this is a replacement that overwrites source with a snippet
|
||||
/// in a way that isn't a superset of the original string. For example,
|
||||
/// replacing "abc" with "abcde" is not destructive, but replacing it
|
||||
/// it with "abx" is, since the "c" character is lost.
|
||||
pub(crate) fn is_destructive_replacement(&self, sm: &SourceMap<'_>) -> bool {
|
||||
self.is_replacement(sm)
|
||||
&& sm
|
||||
.span_to_snippet(self.span.clone())
|
||||
.is_none_or(|s| as_substr(s.trim(), self.replacement.trim()).is_none())
|
||||
}
|
||||
|
||||
fn replaces_meaningful_content(&self, sm: &SourceMap<'_>) -> bool {
|
||||
sm.span_to_snippet(self.span.clone())
|
||||
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
|
||||
/// the case where a substring of the suggestion is "sandwiched" in the original, like
|
||||
/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
|
||||
/// of the suffix.
|
||||
pub(crate) fn as_substr<'a>(
|
||||
original: &'a str,
|
||||
suggestion: &'a str,
|
||||
) -> Option<(usize, &'a str, usize)> {
|
||||
if let Some(stripped) = suggestion.strip_prefix(original) {
|
||||
Some((original.len(), stripped, 0))
|
||||
} else if let Some(stripped) = suggestion.strip_suffix(original) {
|
||||
Some((0, stripped, original.len()))
|
||||
} else {
|
||||
let common_prefix = original
|
||||
.chars()
|
||||
.zip(suggestion.chars())
|
||||
.take_while(|(c1, c2)| c1 == c2)
|
||||
.map(|(c, _)| c.len_utf8())
|
||||
.sum();
|
||||
let original = &original[common_prefix..];
|
||||
let suggestion = &suggestion[common_prefix..];
|
||||
if let Some(stripped) = suggestion.strip_suffix(original) {
|
||||
let common_suffix = original.len();
|
||||
Some((common_prefix, stripped, common_suffix))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,13 @@
|
||||
//!
|
||||
//! [styled_buffer]: https://github.com/rust-lang/rust/blob/894f7a4ba6554d3797404bbf550d9919df060b97/compiler/rustc_errors/src/styled_buffer.rs
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
use crate::Level;
|
||||
use crate::renderer::ElementStyle;
|
||||
use crate::renderer::stylesheet::Stylesheet;
|
||||
use anstyle::Style;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StyledBuffer {
|
||||
@@ -15,13 +18,13 @@ pub(crate) struct StyledBuffer {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub(crate) struct StyledChar {
|
||||
ch: char,
|
||||
style: Style,
|
||||
style: ElementStyle,
|
||||
}
|
||||
|
||||
impl StyledChar {
|
||||
pub(crate) const SPACE: Self = StyledChar::new(' ', Style::new());
|
||||
pub(crate) const SPACE: Self = StyledChar::new(' ', ElementStyle::NoStyle);
|
||||
|
||||
pub(crate) const fn new(ch: char, style: Style) -> StyledChar {
|
||||
pub(crate) const fn new(ch: char, style: ElementStyle) -> StyledChar {
|
||||
StyledChar { ch, style }
|
||||
}
|
||||
}
|
||||
@@ -37,33 +40,40 @@ impl StyledBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn render(&self, stylesheet: &Stylesheet) -> Result<String, fmt::Error> {
|
||||
pub(crate) fn render(
|
||||
&self,
|
||||
level: &Level<'_>,
|
||||
stylesheet: &Stylesheet,
|
||||
str: &mut String,
|
||||
) -> Result<(), fmt::Error> {
|
||||
let capacity = self.lines.iter().map(|line| line.len()).sum();
|
||||
let mut str = String::with_capacity(capacity);
|
||||
str.reserve(capacity);
|
||||
|
||||
for (i, line) in self.lines.iter().enumerate() {
|
||||
let mut current_style = stylesheet.none;
|
||||
for ch in line {
|
||||
if ch.style != current_style {
|
||||
for StyledChar { ch, style } in line {
|
||||
let ch_style = style.color_spec(level, stylesheet);
|
||||
if ch_style != current_style {
|
||||
if !line.is_empty() {
|
||||
write!(str, "{}", current_style.render_reset())?;
|
||||
write!(str, "{current_style:#}")?;
|
||||
}
|
||||
current_style = ch.style;
|
||||
write!(str, "{}", current_style.render())?;
|
||||
current_style = ch_style;
|
||||
write!(str, "{current_style}")?;
|
||||
}
|
||||
str.push(ch.ch);
|
||||
str.push(*ch);
|
||||
}
|
||||
write!(str, "{}", current_style.render_reset())?;
|
||||
write!(str, "{current_style:#}")?;
|
||||
if i != self.lines.len() - 1 {
|
||||
str.push('\n');
|
||||
}
|
||||
}
|
||||
Ok(str)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets `chr` with `style` for given `line`, `col`.
|
||||
/// If `line` does not exist in our buffer, adds empty lines up to the given
|
||||
/// and fills the last line with unstyled whitespace.
|
||||
pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
|
||||
pub(crate) fn putc(&mut self, line: usize, col: usize, chr: char, style: ElementStyle) {
|
||||
self.ensure_lines(line);
|
||||
if col >= self.lines[line].len() {
|
||||
self.lines[line].resize(col + 1, StyledChar::SPACE);
|
||||
@@ -74,24 +84,29 @@ impl StyledBuffer {
|
||||
/// Sets `string` with `style` for given `line`, starting from `col`.
|
||||
/// If `line` does not exist in our buffer, adds empty lines up to the given
|
||||
/// and fills the last line with unstyled whitespace.
|
||||
pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
|
||||
pub(crate) fn puts(&mut self, line: usize, col: usize, string: &str, style: ElementStyle) {
|
||||
if string.is_empty() {
|
||||
// don't add trailing whitespace (from column offset) for blank strings
|
||||
return;
|
||||
}
|
||||
|
||||
self.ensure_lines(line);
|
||||
let char_count = string.chars().count();
|
||||
let needed = col + char_count;
|
||||
if needed > self.lines[line].len() {
|
||||
self.lines[line].resize(needed, StyledChar::SPACE);
|
||||
}
|
||||
let line = &mut self.lines[line];
|
||||
for (i, c) in string.chars().enumerate() {
|
||||
line[col + i] = StyledChar::new(c, style);
|
||||
|
||||
let new_len = col + string.chars().count();
|
||||
if new_len > line.len() {
|
||||
line.resize(new_len, StyledChar::SPACE);
|
||||
}
|
||||
|
||||
for (offset, chr) in string.chars().enumerate() {
|
||||
let col = col + offset;
|
||||
line[col] = StyledChar::new(chr, style);
|
||||
}
|
||||
}
|
||||
|
||||
/// For given `line` inserts `string` with `style` after old content of that line,
|
||||
/// adding lines if needed
|
||||
pub(crate) fn append(&mut self, line: usize, string: &str, style: Style) {
|
||||
pub(crate) fn append(&mut self, line: usize, string: &str, style: ElementStyle) {
|
||||
if line >= self.lines.len() {
|
||||
self.puts(line, 0, string, style);
|
||||
} else {
|
||||
@@ -100,7 +115,58 @@ impl StyledBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn replace(&mut self, line: usize, start: usize, end: usize, string: &str) {
|
||||
if start == end {
|
||||
return;
|
||||
}
|
||||
// If the replacement range would be out of bounds, do nothing, as we
|
||||
// can't replace things that don't exist.
|
||||
if start > self.lines[line].len() || end > self.lines[line].len() {
|
||||
return;
|
||||
};
|
||||
self.lines[line].splice(
|
||||
start..end,
|
||||
string
|
||||
.chars()
|
||||
.map(|c| StyledChar::new(c, ElementStyle::LineNumber)),
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn num_lines(&self) -> usize {
|
||||
self.lines.len()
|
||||
}
|
||||
|
||||
/// Set `style` for `line`, `col_start..col_end` range if:
|
||||
/// 1. That line and column range exist in `StyledBuffer`
|
||||
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
|
||||
pub(crate) fn set_style_range(
|
||||
&mut self,
|
||||
line: usize,
|
||||
col_start: usize,
|
||||
col_end: usize,
|
||||
style: ElementStyle,
|
||||
overwrite: bool,
|
||||
) {
|
||||
for col in col_start..col_end {
|
||||
self.set_style(line, col, style, overwrite);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set `style` for `line`, `col` if:
|
||||
/// 1. That line and column exist in `StyledBuffer`
|
||||
/// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
|
||||
pub(crate) fn set_style(
|
||||
&mut self,
|
||||
line: usize,
|
||||
col: usize,
|
||||
style: ElementStyle,
|
||||
overwrite: bool,
|
||||
) {
|
||||
if let Some(ref mut line) = self.lines.get_mut(line)
|
||||
&& let Some(StyledChar { style: s, .. }) = line.get_mut(col)
|
||||
&& (overwrite || matches!(s, ElementStyle::NoStyle | ElementStyle::Quotation))
|
||||
{
|
||||
*s = style;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,12 @@ pub(crate) struct Stylesheet {
|
||||
pub(crate) info: Style,
|
||||
pub(crate) note: Style,
|
||||
pub(crate) help: Style,
|
||||
pub(crate) line_no: Style,
|
||||
pub(crate) line_num: Style,
|
||||
pub(crate) emphasis: Style,
|
||||
pub(crate) none: Style,
|
||||
pub(crate) hyperlink: bool,
|
||||
pub(crate) context: Style,
|
||||
pub(crate) addition: Style,
|
||||
pub(crate) removal: Style,
|
||||
}
|
||||
|
||||
impl Default for Stylesheet {
|
||||
@@ -27,44 +29,12 @@ impl Stylesheet {
|
||||
info: Style::new(),
|
||||
note: Style::new(),
|
||||
help: Style::new(),
|
||||
line_no: Style::new(),
|
||||
line_num: Style::new(),
|
||||
emphasis: Style::new(),
|
||||
none: Style::new(),
|
||||
hyperlink: false,
|
||||
context: Style::new(),
|
||||
addition: Style::new(),
|
||||
removal: Style::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Stylesheet {
|
||||
pub(crate) fn error(&self) -> &Style {
|
||||
&self.error
|
||||
}
|
||||
|
||||
pub(crate) fn warning(&self) -> &Style {
|
||||
&self.warning
|
||||
}
|
||||
|
||||
pub(crate) fn info(&self) -> &Style {
|
||||
&self.info
|
||||
}
|
||||
|
||||
pub(crate) fn note(&self) -> &Style {
|
||||
&self.note
|
||||
}
|
||||
|
||||
pub(crate) fn help(&self) -> &Style {
|
||||
&self.help
|
||||
}
|
||||
|
||||
pub(crate) fn line_no(&self) -> &Style {
|
||||
&self.line_no
|
||||
}
|
||||
|
||||
pub(crate) fn emphasis(&self) -> &Style {
|
||||
&self.emphasis
|
||||
}
|
||||
|
||||
pub(crate) fn none(&self) -> &Style {
|
||||
&self.none
|
||||
}
|
||||
}
|
||||
@@ -1,77 +1,101 @@
|
||||
//! Structures used as an input for the library.
|
||||
//!
|
||||
//! Example:
|
||||
//!
|
||||
//! ```
|
||||
//! use ruff_annotate_snippets::*;
|
||||
//!
|
||||
//! Level::Error.title("mismatched types")
|
||||
//! .snippet(Snippet::source("Foo").line_start(51).origin("src/format.rs"))
|
||||
//! .snippet(Snippet::source("Faa").line_start(129).origin("src/display.rs"));
|
||||
//! ```
|
||||
|
||||
use std::ops::Range;
|
||||
use alloc::borrow::{Cow, ToOwned};
|
||||
use alloc::string::String;
|
||||
use alloc::{vec, vec::Vec};
|
||||
use core::ops::Range;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
use crate::Level;
|
||||
use crate::renderer::source_map::{TrimmedPatch, as_substr};
|
||||
|
||||
pub(crate) const ERROR_TXT: &str = "error";
|
||||
pub(crate) const HELP_TXT: &str = "help";
|
||||
pub(crate) const INFO_TXT: &str = "info";
|
||||
pub(crate) const NOTE_TXT: &str = "note";
|
||||
pub(crate) const WARNING_TXT: &str = "warning";
|
||||
|
||||
/// A [diagnostic message][Title] and any associated [context][Element] to help users
|
||||
/// understand it
|
||||
///
|
||||
/// The first [`Group`] is the ["primary" group][Level::primary_title], ie it contains the diagnostic
|
||||
/// message.
|
||||
///
|
||||
/// All subsequent [`Group`]s are for distinct pieces of [context][Level::secondary_title].
|
||||
/// The primary group will be visually distinguished to help tell them apart.
|
||||
pub type Report<'a> = &'a [Group<'a>];
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct Id<'a> {
|
||||
pub(crate) id: &'a str,
|
||||
pub(crate) url: Option<&'a str>,
|
||||
pub(crate) id: Option<Cow<'a, str>>,
|
||||
pub(crate) url: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
/// Primary structure provided for formatting
|
||||
/// A [`Title`] with supporting [context][Element] within a [`Report`]
|
||||
///
|
||||
/// See [`Level::title`] to create a [`Message`]
|
||||
#[derive(Debug)]
|
||||
pub struct Message<'a> {
|
||||
pub(crate) level: Level,
|
||||
pub(crate) id: Option<Id<'a>>,
|
||||
pub(crate) title: &'a str,
|
||||
pub(crate) snippets: Vec<Snippet<'a>>,
|
||||
pub(crate) footer: Vec<Message<'a>>,
|
||||
pub(crate) is_fixable: bool,
|
||||
/// [Decor][crate::renderer::DecorStyle] is used to visually connect [`Element`]s of a `Group`.
|
||||
///
|
||||
/// Generally, you will create separate group's for:
|
||||
/// - New [`Snippet`]s, especially if they need their own [`AnnotationKind::Primary`]
|
||||
/// - Each logically distinct set of [suggestions][Patch`]
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/highlight_message.rs")]
|
||||
/// ```
|
||||
#[doc = include_str!("../examples/highlight_message.svg")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Group<'a> {
|
||||
pub(crate) primary_level: Level<'a>,
|
||||
pub(crate) title: Option<Title<'a>>,
|
||||
pub(crate) elements: Vec<Element<'a>>,
|
||||
pub(crate) lineno_offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Message<'a> {
|
||||
pub fn id(mut self, id: &'a str) -> Self {
|
||||
self.id = Some(Id { id, url: None });
|
||||
self
|
||||
impl<'a> Group<'a> {
|
||||
/// Create group with a [`Title`], deriving [`AnnotationKind::Primary`] from its [`Level`]
|
||||
pub fn with_title(title: Title<'a>) -> Self {
|
||||
let level = title.level.clone();
|
||||
let mut x = Self::with_level(level);
|
||||
x.title = Some(title);
|
||||
x
|
||||
}
|
||||
|
||||
pub fn id_with_url(mut self, id: &'a str, url: Option<&'a str>) -> Self {
|
||||
self.id = Some(Id { id, url });
|
||||
self
|
||||
}
|
||||
|
||||
pub fn snippet(mut self, slice: Snippet<'a>) -> Self {
|
||||
self.snippets.push(slice);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn snippets(mut self, slice: impl IntoIterator<Item = Snippet<'a>>) -> Self {
|
||||
self.snippets.extend(slice);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footer(mut self, footer: Message<'a>) -> Self {
|
||||
self.footer.push(footer);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn footers(mut self, footer: impl IntoIterator<Item = Message<'a>>) -> Self {
|
||||
self.footer.extend(footer);
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not the diagnostic for this message is fixable.
|
||||
/// Create a title-less group with a primary [`Level`] for [`AnnotationKind::Primary`]
|
||||
///
|
||||
/// This is rendered as a `[*]` indicator after the `id` in an annotation header, if the
|
||||
/// annotation also has `Level::None`.
|
||||
pub fn is_fixable(mut self, yes: bool) -> Self {
|
||||
self.is_fixable = yes;
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/elide_header.rs")]
|
||||
/// ```
|
||||
#[doc = include_str!("../examples/elide_header.svg")]
|
||||
pub fn with_level(level: Level<'a>) -> Self {
|
||||
Self {
|
||||
primary_level: level,
|
||||
title: None,
|
||||
elements: vec![],
|
||||
lineno_offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Append an [`Element`] that adds context to the [`Title`]
|
||||
pub fn element(mut self, section: impl Into<Element<'a>>) -> Self {
|
||||
self.elements.push(section.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Append [`Element`]s that adds context to the [`Title`]
|
||||
pub fn elements(mut self, sections: impl IntoIterator<Item = impl Into<Element<'a>>>) -> Self {
|
||||
self.elements.extend(sections.into_iter().map(Into::into));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elements.is_empty() && self.title.is_none()
|
||||
}
|
||||
|
||||
/// Add an offset used for aligning the header sigil (`-->`) with the line number separators.
|
||||
///
|
||||
/// For normal diagnostics this is computed automatically based on the lines to be rendered.
|
||||
@@ -83,61 +107,193 @@ impl<'a> Message<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure containing the slice of text to be annotated and
|
||||
/// basic information about the location of the slice.
|
||||
/// A section of content within a [`Group`]
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Element<'a> {
|
||||
Message(Message<'a>),
|
||||
Cause(Snippet<'a, Annotation<'a>>),
|
||||
Suggestion(Snippet<'a, Patch<'a>>),
|
||||
Origin(Origin<'a>),
|
||||
Padding(Padding),
|
||||
}
|
||||
|
||||
impl<'a> From<Message<'a>> for Element<'a> {
|
||||
fn from(value: Message<'a>) -> Self {
|
||||
Element::Message(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Snippet<'a, Annotation<'a>>> for Element<'a> {
|
||||
fn from(value: Snippet<'a, Annotation<'a>>) -> Self {
|
||||
Element::Cause(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Snippet<'a, Patch<'a>>> for Element<'a> {
|
||||
fn from(value: Snippet<'a, Patch<'a>>) -> Self {
|
||||
Element::Suggestion(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Origin<'a>> for Element<'a> {
|
||||
fn from(value: Origin<'a>) -> Self {
|
||||
Element::Origin(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Padding> for Element<'_> {
|
||||
fn from(value: Padding) -> Self {
|
||||
Self::Padding(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// A whitespace [`Element`] in a [`Group`]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Padding;
|
||||
|
||||
/// A title that introduces a [`Group`], describing the main point
|
||||
///
|
||||
/// One `Snippet` is meant to represent a single, continuous,
|
||||
/// slice of source code that you want to annotate.
|
||||
#[derive(Debug)]
|
||||
pub struct Snippet<'a> {
|
||||
pub(crate) origin: Option<&'a str>,
|
||||
pub(crate) line_start: usize,
|
||||
/// To create a `Title`, see [`Level::primary_title`] or [`Level::secondary_title`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use annotate_snippets::*;
|
||||
/// let report = &[
|
||||
/// Group::with_title(
|
||||
/// Level::ERROR.primary_title("mismatched types").id("E0308")
|
||||
/// ),
|
||||
/// Group::with_title(
|
||||
/// Level::HELP.secondary_title("function defined here")
|
||||
/// ),
|
||||
/// ];
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Title<'a> {
|
||||
pub(crate) level: Level<'a>,
|
||||
pub(crate) id: Option<Id<'a>>,
|
||||
pub(crate) text: Cow<'a, str>,
|
||||
pub(crate) allows_styling: bool,
|
||||
pub(crate) is_fixable: bool,
|
||||
}
|
||||
|
||||
pub(crate) source: &'a str,
|
||||
pub(crate) annotations: Vec<Annotation<'a>>,
|
||||
impl<'a> Title<'a> {
|
||||
/// The category for this [`Report`]
|
||||
///
|
||||
/// Useful for looking searching for more information to resolve the diagnostic.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn id(mut self, id: impl Into<Cow<'a, str>>) -> Self {
|
||||
self.id.get_or_insert(Id::default()).id = Some(id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fold: bool,
|
||||
/// Provide a URL for [`Title::id`] for more information on this diagnostic
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// This is only relevant if `id` is present
|
||||
///
|
||||
/// </div>
|
||||
pub fn id_url(mut self, url: impl Into<Cow<'a, str>>) -> Self {
|
||||
self.id.get_or_insert(Id::default()).url = Some(url.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Append an [`Element`] that adds context to the [`Title`]
|
||||
pub fn element(self, section: impl Into<Element<'a>>) -> Group<'a> {
|
||||
Group::with_title(self).element(section)
|
||||
}
|
||||
|
||||
/// Append [`Element`]s that adds context to the [`Title`]
|
||||
pub fn elements(self, sections: impl IntoIterator<Item = impl Into<Element<'a>>>) -> Group<'a> {
|
||||
Group::with_title(self).elements(sections)
|
||||
}
|
||||
|
||||
/// Whether or not the diagnostic for this message is fixable.
|
||||
///
|
||||
/// This is rendered as a `[*]` indicator after the `id` in an annotation header, if the
|
||||
/// annotation also has `Level::None`.
|
||||
pub fn is_fixable(mut self, yes: bool) -> Self {
|
||||
self.is_fixable = yes;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A text [`Element`] in a [`Group`]
|
||||
///
|
||||
/// See [`Level::message`] to create this.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Message<'a> {
|
||||
pub(crate) level: Level<'a>,
|
||||
pub(crate) text: Cow<'a, str>,
|
||||
}
|
||||
|
||||
/// A source view [`Element`] in a [`Group`]
|
||||
///
|
||||
/// If you do not have [source][Snippet::source] available, see instead [`Origin`]
|
||||
///
|
||||
/// `Snippet`s come in the following styles (`T`):
|
||||
/// - With [`Annotation`]s, see [`Snippet::annotation`]
|
||||
/// - With [`Patch`]s, see [`Snippet::patch`]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Snippet<'a, T> {
|
||||
pub(crate) path: Option<Cow<'a, str>>,
|
||||
/// The optional cell index in a Jupyter notebook, used for reporting source locations along
|
||||
/// with the ranges on `annotations`.
|
||||
pub(crate) cell_index: Option<usize>,
|
||||
pub(crate) line_start: usize,
|
||||
pub(crate) source: Cow<'a, str>,
|
||||
pub(crate) markers: Vec<T>,
|
||||
pub(crate) fold: bool,
|
||||
}
|
||||
|
||||
impl<'a> Snippet<'a> {
|
||||
pub fn source(source: &'a str) -> Self {
|
||||
impl<'a, T: Clone> Snippet<'a, T> {
|
||||
/// The source code to be rendered
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn source(source: impl Into<Cow<'a, str>>) -> Self {
|
||||
Self {
|
||||
origin: None,
|
||||
path: None,
|
||||
line_start: 1,
|
||||
source,
|
||||
annotations: vec![],
|
||||
fold: false,
|
||||
cell_index: None,
|
||||
source: source.into(),
|
||||
markers: vec![],
|
||||
fold: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// When manually [`fold`][Self::fold]ing,
|
||||
/// the [`source`][Self::source]s line offset from the original start
|
||||
pub fn line_start(mut self, line_start: usize) -> Self {
|
||||
self.line_start = line_start;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn origin(mut self, origin: &'a str) -> Self {
|
||||
self.origin = Some(origin);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn annotation(mut self, annotation: Annotation<'a>) -> Self {
|
||||
self.annotations.push(annotation);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn annotations(mut self, annotation: impl IntoIterator<Item = Annotation<'a>>) -> Self {
|
||||
self.annotations.extend(annotation);
|
||||
self
|
||||
}
|
||||
|
||||
/// Hide lines without [`Annotation`]s
|
||||
pub fn fold(mut self, fold: bool) -> Self {
|
||||
self.fold = fold;
|
||||
/// The location of the [`source`][Self::source] (e.g. a path)
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn path(mut self, path: impl Into<OptionCow<'a>>) -> Self {
|
||||
self.path = path.into().0;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -146,23 +302,89 @@ impl<'a> Snippet<'a> {
|
||||
self.cell_index = index;
|
||||
self
|
||||
}
|
||||
|
||||
/// Control whether lines without [`Annotation`]s are shown
|
||||
///
|
||||
/// The default is `fold(true)`, collapsing uninteresting lines.
|
||||
///
|
||||
/// See [`AnnotationKind::Visible`] to force specific spans to be shown.
|
||||
pub fn fold(mut self, fold: bool) -> Self {
|
||||
self.fold = fold;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// An annotation for a [`Snippet`].
|
||||
impl<'a> Snippet<'a, Annotation<'a>> {
|
||||
/// Highlight and describe a span of text within the [`source`][Self::source]
|
||||
pub fn annotation(mut self, annotation: Annotation<'a>) -> Snippet<'a, Annotation<'a>> {
|
||||
self.markers.push(annotation);
|
||||
self
|
||||
}
|
||||
|
||||
/// Highlight and describe spans of text within the [`source`][Self::source]
|
||||
pub fn annotations(mut self, annotation: impl IntoIterator<Item = Annotation<'a>>) -> Self {
|
||||
self.markers.extend(annotation);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Snippet<'a, Patch<'a>> {
|
||||
/// Suggest to the user an edit to the [`source`][Self::source]
|
||||
pub fn patch(mut self, patch: Patch<'a>) -> Snippet<'a, Patch<'a>> {
|
||||
self.markers.push(patch);
|
||||
self
|
||||
}
|
||||
|
||||
/// Suggest to the user edits to the [`source`][Self::source]
|
||||
pub fn patches(mut self, patches: impl IntoIterator<Item = Patch<'a>>) -> Self {
|
||||
self.markers.extend(patches);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Highlight and describe a span of text within a [`Snippet`]
|
||||
///
|
||||
/// See [`Level::span`] to create a [`Annotation`]
|
||||
#[derive(Debug)]
|
||||
/// See [`AnnotationKind`] to create an annotation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/expected_type.rs")]
|
||||
/// ```
|
||||
///
|
||||
#[doc = include_str!("../examples/expected_type.svg")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Annotation<'a> {
|
||||
/// The byte range of the annotation in the `source` string
|
||||
pub(crate) range: Range<usize>,
|
||||
pub(crate) label: Option<&'a str>,
|
||||
pub(crate) level: Level,
|
||||
pub(crate) span: Range<usize>,
|
||||
pub(crate) label: Option<Cow<'a, str>>,
|
||||
pub(crate) kind: AnnotationKind,
|
||||
pub(crate) highlight_source: bool,
|
||||
pub(crate) is_file_level: bool,
|
||||
}
|
||||
|
||||
impl<'a> Annotation<'a> {
|
||||
pub fn label(mut self, label: &'a str) -> Self {
|
||||
self.label = Some(label);
|
||||
/// Describe the reason the span is highlighted
|
||||
///
|
||||
/// This will be styled according to the [`AnnotationKind`]
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn label(mut self, label: impl Into<OptionCow<'a>>) -> Self {
|
||||
self.label = label.into().0;
|
||||
self
|
||||
}
|
||||
|
||||
/// Style the source according to the [`AnnotationKind`]
|
||||
///
|
||||
/// This gives extra emphasis to this annotation
|
||||
pub fn highlight_source(mut self, highlight_source: bool) -> Self {
|
||||
self.highlight_source = highlight_source;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -172,40 +394,225 @@ impl<'a> Annotation<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Types of annotations.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Level {
|
||||
/// Do not attach any annotation.
|
||||
None,
|
||||
/// Error annotations are displayed using red color and "^" character.
|
||||
Error,
|
||||
/// Warning annotations are displayed using blue color and "-" character.
|
||||
Warning,
|
||||
Info,
|
||||
Note,
|
||||
Help,
|
||||
/// The type of [`Annotation`] being applied to a [`Snippet`]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[non_exhaustive]
|
||||
pub enum AnnotationKind {
|
||||
/// For showing the source that the [Group's Title][Group::with_title] references
|
||||
///
|
||||
/// For [`Title`]-less groups, see [`Group::with_level`]
|
||||
Primary,
|
||||
/// Additional context to better understand the [`Primary`][Self::Primary]
|
||||
/// [`Annotation`]
|
||||
///
|
||||
/// See also [`Renderer::context`].
|
||||
///
|
||||
/// [`Renderer::context`]: crate::renderer::Renderer
|
||||
Context,
|
||||
/// Prevents the annotated text from getting [folded][Snippet::fold]
|
||||
///
|
||||
/// By default, [`Snippet`]s will [fold][`Snippet::fold`] (remove) lines
|
||||
/// that do not contain any annotations. [`Visible`][Self::Visible] makes
|
||||
/// it possible to selectively prevent this behavior for specific text,
|
||||
/// allowing context to be preserved without adding any annotation
|
||||
/// characters.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/struct_name_as_context.rs")]
|
||||
/// ```
|
||||
///
|
||||
#[doc = include_str!("../examples/struct_name_as_context.svg")]
|
||||
///
|
||||
Visible,
|
||||
}
|
||||
|
||||
impl Level {
|
||||
pub fn title(self, title: &str) -> Message<'_> {
|
||||
Message {
|
||||
level: self,
|
||||
id: None,
|
||||
title,
|
||||
snippets: vec![],
|
||||
footer: vec![],
|
||||
is_fixable: false,
|
||||
lineno_offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`Annotation`] with the given span for a [`Snippet`]
|
||||
impl AnnotationKind {
|
||||
/// Annotate a byte span within [`Snippet`]
|
||||
pub fn span<'a>(self, span: Range<usize>) -> Annotation<'a> {
|
||||
Annotation {
|
||||
range: span,
|
||||
span,
|
||||
label: None,
|
||||
level: self,
|
||||
kind: self,
|
||||
highlight_source: false,
|
||||
is_file_level: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_primary(&self) -> bool {
|
||||
matches!(self, AnnotationKind::Primary)
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggested edit to the [`Snippet`]
|
||||
///
|
||||
/// See [`Snippet::patch`]
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[allow(clippy::needless_doctest_main)]
|
||||
#[doc = include_str!("../examples/multi_suggestion.rs")]
|
||||
/// ```
|
||||
///
|
||||
#[doc = include_str!("../examples/multi_suggestion.svg")]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Patch<'a> {
|
||||
pub(crate) span: Range<usize>,
|
||||
pub(crate) replacement: Cow<'a, str>,
|
||||
}
|
||||
|
||||
impl<'a> Patch<'a> {
|
||||
/// Splice `replacement` into the [`Snippet`] at the specified byte span
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn new(span: Range<usize>, replacement: impl Into<Cow<'a, str>>) -> Self {
|
||||
Self {
|
||||
span,
|
||||
replacement: replacement.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to turn a replacement into an addition when the span that is being
|
||||
/// overwritten matches either the prefix or suffix of the replacement.
|
||||
pub(crate) fn trim_trivial_replacements(self, source: &str) -> TrimmedPatch<'a> {
|
||||
let mut trimmed = TrimmedPatch {
|
||||
original_span: self.span.clone(),
|
||||
span: self.span,
|
||||
replacement: self.replacement,
|
||||
};
|
||||
|
||||
if trimmed.replacement.is_empty() {
|
||||
return trimmed;
|
||||
}
|
||||
let Some(snippet) = source.get(trimmed.original_span.clone()) else {
|
||||
return trimmed;
|
||||
};
|
||||
|
||||
if let Some((prefix, substr, suffix)) = as_substr(snippet, &trimmed.replacement) {
|
||||
trimmed.span = trimmed.original_span.start + prefix
|
||||
..trimmed.original_span.end.saturating_sub(suffix);
|
||||
trimmed.replacement = Cow::Owned(substr.to_owned());
|
||||
}
|
||||
trimmed
|
||||
}
|
||||
}
|
||||
|
||||
/// A source location [`Element`] in a [`Group`]
|
||||
///
|
||||
/// If you have source available, see instead [`Snippet`]
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use annotate_snippets::{Group, Snippet, AnnotationKind, Level, Origin};
|
||||
/// let report = &[
|
||||
/// Level::ERROR.primary_title("mismatched types").id("E0308")
|
||||
/// .element(
|
||||
/// Origin::path("$DIR/mismatched-types.rs")
|
||||
/// )
|
||||
/// ];
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Origin<'a> {
|
||||
pub(crate) path: Cow<'a, str>,
|
||||
/// The optional cell index in a Jupyter notebook, used for reporting source locations along
|
||||
/// with the ranges on `annotations`.
|
||||
pub(crate) cell_index: Option<usize>,
|
||||
pub(crate) line: Option<usize>,
|
||||
pub(crate) char_column: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'a> Origin<'a> {
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Text passed to this function is considered "untrusted input", as such
|
||||
/// all text is passed through a normalization function. Pre-styled text is
|
||||
/// not allowed to be passed to this function.
|
||||
///
|
||||
/// </div>
|
||||
pub fn path(path: impl Into<Cow<'a, str>>) -> Self {
|
||||
Self {
|
||||
path: path.into(),
|
||||
cell_index: None,
|
||||
line: None,
|
||||
char_column: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attach a Jupyter notebook cell index.
|
||||
pub fn cell_index(mut self, index: Option<usize>) -> Self {
|
||||
self.cell_index = index;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the default line number to display
|
||||
pub fn line(mut self, line: usize) -> Self {
|
||||
self.line = Some(line);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the default column to display
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// `char_column` is only be respected if [`Origin::line`] is also set.
|
||||
///
|
||||
/// </div>
|
||||
pub fn char_column(mut self, char_column: usize) -> Self {
|
||||
self.char_column = Some(char_column);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Cow<'a, str>> for Origin<'a> {
|
||||
fn from(origin: Cow<'a, str>) -> Self {
|
||||
Self::path(origin)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OptionCow<'a>(pub(crate) Option<Cow<'a, str>>);
|
||||
|
||||
impl<'a, T: Into<Cow<'a, str>>> From<Option<T>> for OptionCow<'a> {
|
||||
fn from(value: Option<T>) -> Self {
|
||||
Self(value.map(Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Cow<'a, str>> for OptionCow<'a> {
|
||||
fn from(value: &'a Cow<'a, str>) -> Self {
|
||||
Self(Some(Cow::Borrowed(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Cow<'a, str>> for OptionCow<'a> {
|
||||
fn from(value: Cow<'a, str>) -> Self {
|
||||
Self(Some(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for OptionCow<'a> {
|
||||
fn from(value: &'a str) -> Self {
|
||||
Self(Some(Cow::Borrowed(value)))
|
||||
}
|
||||
}
|
||||
impl<'a> From<String> for OptionCow<'a> {
|
||||
fn from(value: String) -> Self {
|
||||
Self(Some(Cow::Owned(value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a String> for OptionCow<'a> {
|
||||
fn from(value: &'a String) -> Self {
|
||||
Self(Some(Cow::Borrowed(value.as_str())))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">expected `.`, `=`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> Cargo.toml:1:5</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:1:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1 |</tspan><tspan> asdf</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asdf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let input = &[Level::ERROR.primary_title("expected `.`, `=`").element(
|
||||
Snippet::source("asdf")
|
||||
.path("Cargo.toml")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Primary.span(4..4).label("")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ann_eof.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ann_eof.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:1:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> asdf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">expected `.`, `=`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> Cargo.toml:1:3</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:1:3</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1 |</tspan><tspan> asf</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">'d' belongs here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let input = &[Level::ERROR.primary_title("expected `.`, `=`").element(
|
||||
Snippet::source("asf")
|
||||
.path("Cargo.toml")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Primary.span(2..2).label("'d' belongs here")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ann_insertion.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ann_insertion.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:1:3</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> asf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">'d' belongs here</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="182px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,23 +19,21 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0027]</tspan><tspan>: </tspan><tspan class="bold">pattern does not mention fields `lineno`, `content`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0027]</tspan><tspan class="bold">: pattern does not mention fields `lineno`, `content`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> src/display_list.rs:139:32</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>src/display_list.rs:139:32</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">139 |</tspan><tspan> if let DisplayLine::Source {</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">139</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> if let DisplayLine::Source {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">________________________________^</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold"> ________________________________^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">140 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> ref mut inline_marks,</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">140</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> ref mut inline_marks,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">141 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> } = body[body_idx]</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">141</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> } = body[body_idx]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|_________________________^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">missing fields `lineno`, `content`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#" if let DisplayLine::Source {
|
||||
ref mut inline_marks,
|
||||
} = body[body_idx]
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("pattern does not mention fields `lineno`, `content`")
|
||||
.id("E0027")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("src/display_list.rs")
|
||||
.line_start(139)
|
||||
.fold(false)
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(31..128)
|
||||
.label("missing fields `lineno`, `content`"),
|
||||
),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ann_multiline.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ann_multiline.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0027]</tspan><tspan class="bold">: pattern does not mention fields `lineno`, `content`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>src/display_list.rs:139:32</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">139</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> if let DisplayLine::Source {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">140</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> ref mut inline_marks,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">141</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> } = body[body_idx]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">┗━━━━━━━━━━━━━━━━━━━━━━━━━┛</tspan><tspan> </tspan><tspan class="fg-bright-red bold">missing fields `lineno`, `content`</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -19,19 +19,19 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E####]</tspan><tspan>: </tspan><tspan class="bold">spacing error found</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E####]</tspan><tspan class="bold">: spacing error found</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> foo.txt:26:12</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>foo.txt:26:12</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26 |</tspan><tspan> This is an example</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> This is an example</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">this should not be on separate lines</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">27 |</tspan><tspan> of an edge case of an annotation overflowing</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">27</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> of an edge case of an annotation overflowing</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">28 |</tspan><tspan> to exactly one character on next line.</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">28</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> to exactly one character on next line.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
@@ -0,0 +1,34 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"This is an example
|
||||
of an edge case of an annotation overflowing
|
||||
to exactly one character on next line.
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("spacing error found")
|
||||
.id("E####")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("foo.txt")
|
||||
.line_start(26)
|
||||
.fold(false)
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(11..19)
|
||||
.label("this should not be on separate lines"),
|
||||
),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ann_multiline2.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ann_multiline2.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E####]</tspan><tspan class="bold">: spacing error found</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>foo.txt:26:12</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">26</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> This is an example</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">this should not be on separate lines</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">27</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> of an edge case of an annotation overflowing</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">28</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> to exactly one character on next line.</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">expected `.`, `=`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> Cargo.toml:1:5</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:1:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1 |</tspan><tspan> asdf</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> asdf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let input = &[Level::ERROR.primary_title("expected `.`, `=`").element(
|
||||
Snippet::source("asdf")
|
||||
.path("Cargo.toml")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Primary.span(4..5).label("")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ann_removed_nl.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ann_removed_nl.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected `.`, `=`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:1:5</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> asdf</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="1356px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="1356px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> <file>:7:1</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan><file>:7:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">7 |</tspan><tspan> "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#""haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR.primary_title("invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("<file>")
|
||||
.line_start(7)
|
||||
.annotation(AnnotationKind::Primary.span(0..35).label(""))
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["ensure_emoji_highlight_width.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["ensure_emoji_highlight_width.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="1356px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan><file>:7:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> "haha this isn't a valid name 🐛" = { package = "libc", version = "0.1" }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg width="740px" height="290px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>$DIR/file.txt:3:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> this is from a txt file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">-----------------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">the macro expands to this string</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">::: </tspan><tspan>$DIR/mismatched-types.rs:2:12</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let b: &[u8] = include_str!("file.txt");</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">-----</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^^^^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected `&[u8]`, found `&str`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected due to this</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">= </tspan><tspan class="bold">note</tspan><tspan>: expected reference `&[u8]`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan> found reference `&'static str`</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,52 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let file_txt_source = r#"this is from a txt file"#;
|
||||
|
||||
let rust_source = r#"fn main() {
|
||||
let b: &[u8] = include_str!("file.txt");
|
||||
let s: &str = include_bytes!("file.txt");
|
||||
}"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(file_txt_source)
|
||||
.line_start(3)
|
||||
.path("$DIR/file.txt")
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(0..23)
|
||||
.label("the macro expands to this string"),
|
||||
),
|
||||
)
|
||||
.element(
|
||||
Snippet::source(rust_source)
|
||||
.path("$DIR/mismatched-types.rs")
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(23..28)
|
||||
.label("expected due to this"),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(31..55)
|
||||
.label("expected `&[u8]`, found `&str`"),
|
||||
),
|
||||
)
|
||||
.element(
|
||||
Level::NOTE.message("expected reference `&[u8]`\n found reference `&'static str`"),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["first_snippet_is_primary.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["first_snippet_is_primary.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<svg width="740px" height="290px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/file.txt:3:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> this is from a txt file</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">───────────────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">the macro expands to this string</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ⸬ </tspan><tspan>$DIR/mismatched-types.rs:2:12</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let b: &[u8] = include_str!("file.txt");</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">┬────</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━━━━━━━━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected `&[u8]`, found `&str`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected due to this</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰ </tspan><tspan class="bold">note</tspan><tspan>: expected reference `&[u8]`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan> found reference `&'static str`</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -4,7 +4,6 @@
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
@@ -20,29 +19,29 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> src/format.rs:51:6</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>src/format.rs:52:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51 |</tspan><tspan> ) -> Option<String> {</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> ) -> Option<String> {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">--------------</tspan><tspan> </tspan><tspan class="fg-yellow bold">expected `std::option::Option<std::string::String>` because of return type</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">--------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected `std::option::Option<std::string::String>` because of return type</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">/</tspan><tspan> for ann in annotations {</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">/</tspan><tspan> for ann in annotations {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> match (ann.range.0, ann.range.1) {</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> match (ann.range.0, ann.range.1) {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">...</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> (None, None) => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">71 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> (Some(start), Some(end)) if start > end_index || end < start_index => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">72 |</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">...</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|_____^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`, found ()</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">72</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|</tspan><tspan> }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">|_____^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`, found ()</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#") -> Option<String> {
|
||||
for ann in annotations {
|
||||
match (ann.range.0, ann.range.1) {
|
||||
(None, None) => continue,
|
||||
(Some(start), Some(end)) if start > end_index || end < start_index => continue,
|
||||
(Some(start), Some(end)) if start >= start_index && end <= end_index => {
|
||||
let label = if let Some(ref label) = ann.label {
|
||||
format!(" {}", label)
|
||||
} else {
|
||||
String::from("")
|
||||
};
|
||||
|
||||
return Some(format!(
|
||||
"{}{}{}",
|
||||
" ".repeat(start - start_index),
|
||||
"^".repeat(end - start),
|
||||
label
|
||||
));
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("mismatched types")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("src/format.rs")
|
||||
.line_start(51)
|
||||
.annotation(AnnotationKind::Context.span(5..19).label(
|
||||
"expected `std::option::Option<std::string::String>` because of return type",
|
||||
))
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(22..766)
|
||||
.label("expected enum `std::option::Option`, found ()"),
|
||||
),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["fold_ann_multiline.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["fold_ann_multiline.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<svg width="869px" height="236px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: mismatched types</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>src/format.rs:52:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">51</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> ) -> Option<String> {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">──────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">expected `std::option::Option<std::string::String>` because of return type</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">52</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┏</tspan><tspan> for ann in annotations {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">53</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> match (ann.range.0, ann.range.1) {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan class="fg-bright-blue bold">54</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> (None, None) => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">55</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> (Some(start), Some(end)) if start > end_index || end < start_index => continue,</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┇</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan class="fg-bright-blue bold">72</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">┃</tspan><tspan> }</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">┗━━━━━┛</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected enum `std::option::Option`, found ()</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -1,10 +1,9 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
@@ -20,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: </tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> path/to/error.rs:3:1</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>path/to/error.rs:3:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3 |</tspan><tspan> invalid syntax</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> invalid syntax</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">--------------</tspan><tspan> </tspan><tspan class="fg-yellow bold">error here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">--------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">error here</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"
|
||||
|
||||
invalid syntax
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR.primary_title("").element(
|
||||
Snippet::source(source)
|
||||
.path("path/to/error.rs")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Context.span(2..16).label("error here")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["fold_bad_origin_line.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["fold_bad_origin_line.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: </tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>path/to/error.rs:3:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> invalid syntax</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴──────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">error here</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">invalid type: integer `20`, expected a bool</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a bool</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> Cargo.toml:11:13</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:11:13</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11 |</tspan><tspan> workspace = 20</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> workspace = 20</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"[workspace]
|
||||
|
||||
[package]
|
||||
name = "hello"
|
||||
version = "1.0.0"
|
||||
license = "MIT"
|
||||
rust-version = "1.70"
|
||||
edition = "2021"
|
||||
|
||||
[lints]
|
||||
workspace = 20
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("invalid type: integer `20`, expected a bool")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("Cargo.toml")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Primary.span(132..134).label("")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["fold_leading.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["fold_leading.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">mismatched types</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a bool</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/whitespace-trimming.rs:LL:193</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:11:13</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">LL |</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">...</tspan><tspan> let _: () = 42ñ</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">11</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> workspace = 20</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">expected (), found integer</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
@@ -19,17 +19,15 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">invalid type: integer `20`, expected a lints table</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a lints table</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> Cargo.toml:1:9</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:1:9</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1 |</tspan><tspan> lints = 20</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> lints = 20</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"lints = 20
|
||||
|
||||
[workspace]
|
||||
|
||||
[package]
|
||||
name = "hello"
|
||||
version = "1.0.0"
|
||||
license = "MIT"
|
||||
rust-version = "1.70"
|
||||
edition = "2021"
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("invalid type: integer `20`, expected a lints table")
|
||||
.id("E0308")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("Cargo.toml")
|
||||
.line_start(1)
|
||||
.annotation(AnnotationKind::Primary.span(8..10).label("")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["fold_trailing.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["fold_trailing.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan class="bold">: invalid type: integer `20`, expected a lints table</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:1:9</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> lints = 20</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,54 @@
|
||||
<svg width="740px" height="272px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0422]</tspan><tspan class="bold">: cannot find struct, variant or union type `哈哈哈哈` in this scope</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>$DIR/highlight_diff_line_with_wide_characters.rs:3:18</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> struct 啊啊啊啊 {}</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">------------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">similarly named struct `啊啊啊啊` defined here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> const 哦哦: 啊啊啊啊 = 哈哈哈哈 {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: a struct with a similar name exists: `啊啊啊啊`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>const 哦哦: 啊啊啊啊 = </tspan><tspan class="fg-bright-red">哈哈哈哈</tspan><tspan> {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan>const 哦哦: 啊啊啊啊 = </tspan><tspan class="fg-bright-green">啊啊啊啊</tspan><tspan> {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,44 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"struct 啊啊啊啊 {}
|
||||
|
||||
const 哦哦: 啊啊啊啊 = 哈哈哈哈 {}; // some comment"#;
|
||||
|
||||
let path = "$DIR/highlight_diff_line_with_wide_characters.rs";
|
||||
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title("cannot find struct, variant or union type `哈哈哈哈` in this scope")
|
||||
.id("E0422")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path(path)
|
||||
.annotation(AnnotationKind::Primary.span(53..65).label("here"))
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(0..22)
|
||||
.label("similarly named struct `啊啊啊啊` defined here"),
|
||||
),
|
||||
),
|
||||
Level::HELP
|
||||
.secondary_title("a struct with a similar name exists: `啊啊啊啊`")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path(path)
|
||||
.patch(Patch::new(53..65, "啊啊啊啊")),
|
||||
),
|
||||
];
|
||||
|
||||
let expected_ascii = file!["highlight_diff_line_with_wide_characters.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode =
|
||||
file!["highlight_diff_line_with_wide_characters.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<svg width="740px" height="272px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0422]</tspan><tspan class="bold">: cannot find struct, variant or union type `哈哈哈哈` in this scope</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/highlight_diff_line_with_wide_characters.rs:3:18</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> struct 啊啊啊啊 {}</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">──────────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">similarly named struct `啊啊啊啊` defined here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> const 哦哦: 啊啊啊啊 = 哈哈哈哈 {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: a struct with a similar name exists: `啊啊啊啊`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>const 哦哦: 啊啊啊啊 = </tspan><tspan class="fg-bright-red">哈哈哈哈</tspan><tspan> {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan>const 哦哦: 啊啊啊啊 = </tspan><tspan class="fg-bright-green">啊啊啊啊</tspan><tspan> {}; // some comment</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
@@ -0,0 +1,62 @@
|
||||
<svg width="740px" height="344px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0061]</tspan><tspan class="bold">: this function takes 6 arguments but 7 arguments were supplied</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>$DIR/wrong-highlight-span-extra-arguments-147070.rs:17:15</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let foo = Thingy::new(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">...</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> String::from(""),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">----------------</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">unexpected argument #7 of type `String`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-green bold">note</tspan><tspan>: associated function defined here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>$DIR/wrong-highlight-span-extra-arguments-147070.rs:4:19</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold"> 4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> pub(crate) fn new(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-green bold">^^^</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: remove the extra argument</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-red">- String::from(""),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,75 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
// https://github.com/rust-lang/rust/blob/4b94758d2ba7d0ef71ccf5fde29ce4bc5d6fe2a4/tests/ui/argument-suggestions/wrong-highlight-span-extra-arguments-147070.rs
|
||||
|
||||
let source = r#"struct Thingy;
|
||||
|
||||
impl Thingy {
|
||||
pub(crate) fn new(
|
||||
_a: String,
|
||||
_b: String,
|
||||
_c: String,
|
||||
_d: String,
|
||||
_e: String,
|
||||
_f: String,
|
||||
) -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let foo = Thingy::new(
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
String::from(""),
|
||||
);
|
||||
}"#;
|
||||
|
||||
let path = "$DIR/wrong-highlight-span-extra-arguments-147070.rs";
|
||||
|
||||
let report = &[
|
||||
Level::ERROR
|
||||
.primary_title("this function takes 6 arguments but 7 arguments were supplied")
|
||||
.id("E0061")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path(path)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(426..442)
|
||||
.label("unexpected argument #7 of type `String`"),
|
||||
)
|
||||
.annotation(AnnotationKind::Primary.span(249..260)),
|
||||
),
|
||||
Level::NOTE
|
||||
.secondary_title("associated function defined here")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path(path)
|
||||
.annotation(AnnotationKind::Primary.span(48..51)),
|
||||
),
|
||||
Level::HELP
|
||||
.secondary_title("remove the extra argument")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path(path)
|
||||
.patch(Patch::new(413..439, "")),
|
||||
),
|
||||
];
|
||||
|
||||
let expected_ascii = file!["highlight_duplicated_diff_lines.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["highlight_duplicated_diff_lines.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<svg width="740px" height="344px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-cyan { fill: #55FFFF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0061]</tspan><tspan class="bold">: this function takes 6 arguments but 7 arguments were supplied</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/wrong-highlight-span-extra-arguments-147070.rs:17:15</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">17</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let foo = Thingy::new(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">24</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> String::from(""),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">────────────────</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">unexpected argument #7 of type `String`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-green bold">note</tspan><tspan>: associated function defined here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>$DIR/wrong-highlight-span-extra-arguments-147070.rs:4:19</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="244px"><tspan class="fg-bright-blue bold"> 4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> pub(crate) fn new(</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="262px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-bright-green bold">━━━</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="280px"><tspan class="fg-bright-cyan bold">help</tspan><tspan>: remove the extra argument</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="298px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="316px"><tspan class="fg-bright-blue bold">23</tspan><tspan> </tspan><tspan class="fg-bright-red">- String::from(""),</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="334px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1,41 @@
|
||||
<svg width="740px" height="164px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: <sample error message></tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan><sample path>:LL:18</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">6</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> ts.into_iter()</tspan><tspan class="fg-bright-red">.map(|t| {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-red">- (is_true, t)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">8</tspan><tspan> </tspan><tspan class="fg-bright-red">- })</tspan><tspan>.flatten()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">6</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan> ts.into_iter().flatten()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{Level, Patch, Renderer, Snippet};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let report = &[Level::ERROR
|
||||
.primary_title("<sample error message>")
|
||||
.element(
|
||||
Snippet::source(
|
||||
"\t\t\tts.into_iter().map(|t| {\n\t\t\t\t(is_true, t)\n\t\t\t}).flatten()\n",
|
||||
)
|
||||
.line_start(6)
|
||||
.path("<sample path>")
|
||||
.patch(Patch::new(17..50, "")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["highlight_first_line_tab_371.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
}
|
||||
@@ -19,19 +19,19 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error[E0308]</tspan><tspan>: </tspan><tspan class="bold">call-non-callable</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: all annotation kinds have a highlighted source</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> $DIR/main.py:5:9</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>Cargo.toml:9:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4 |</tspan><tspan> def f():</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> [workspace.lints.rust]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">5 |</tspan><tspan> return (1 == '2')() # Tab indented</tspan>
|
||||
<tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">...</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">9</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">unused_qualifications</tspan><tspan> = </tspan><tspan class="fg-bright-blue bold">"warn"</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^^^^^^^^^^^^^^^^^^^^^</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">------</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"
|
||||
[workspace.lints.rust]
|
||||
rust_2018_idioms = { level = "warn", priority = -1 }
|
||||
unnameable_types = "warn"
|
||||
unreachable_pub = "warn"
|
||||
unsafe_op_in_unsafe_fn = "warn"
|
||||
unused_lifetimes = "warn"
|
||||
unused_macro_rules = "warn"
|
||||
unused_qualifications = "warn"
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR
|
||||
.primary_title("all annotation kinds have a highlighted source")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("Cargo.toml")
|
||||
.annotation(
|
||||
AnnotationKind::Primary
|
||||
.span(214..235)
|
||||
.highlight_source(true),
|
||||
)
|
||||
.annotation(
|
||||
AnnotationKind::Context
|
||||
.span(238..244)
|
||||
.highlight_source(true),
|
||||
)
|
||||
.annotation(AnnotationKind::Visible.span(2..22).highlight_source(true)),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["highlight_source.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["highlight_source.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<svg width="740px" height="146px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: all annotation kinds have a highlighted source</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>Cargo.toml:9:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> [workspace.lints.rust]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">9</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-red bold">unused_qualifications</tspan><tspan> = </tspan><tspan class="fg-bright-blue bold">"warn"</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">━━━━━━━━━━━━━━━━━━━━━</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">──────</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> [lorem ipsum](</tspan><tspan class="fg-yellow bold">表意文字</tspan><tspan>)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">^^^^^^^^</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,20 @@
|
||||
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"[lorem ipsum](表意文字)"#;
|
||||
let report = &[Group::with_level(Level::WARNING).element(
|
||||
Snippet::source(source)
|
||||
.annotation(AnnotationKind::Primary.span(14..26).highlight_source(true)),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["highlight_source_multi_width_chars.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["highlight_source_multi_width_chars.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> [lorem ipsum](</tspan><tspan class="fg-yellow bold">表意文字</tspan><tspan>)</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-yellow bold">━━━━━━━━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,30 @@
|
||||
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> [</tspan><tspan class="fg-yellow bold">résumé</tspan><tspan>] [</tspan><tspan class="fg-yellow bold">naïve</tspan><tspan>]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">^^^^^^</tspan><tspan> </tspan><tspan class="fg-yellow bold">^^^^^</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,21 @@
|
||||
use annotate_snippets::{AnnotationKind, Group, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"[résumé] [naïve]"#;
|
||||
let report = &[Group::with_level(Level::WARNING).element(
|
||||
Snippet::source(source)
|
||||
.annotation(AnnotationKind::Primary.span(1..11).highlight_source(true))
|
||||
.annotation(AnnotationKind::Primary.span(14..21).highlight_source(true)),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["highlight_source_zero_width_chars.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["highlight_source_zero_width_chars.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<svg width="740px" height="74px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> [</tspan><tspan class="fg-yellow bold">résumé</tspan><tspan>] [</tspan><tspan class="fg-yellow bold">naïve</tspan><tspan>]</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan> </tspan><tspan class="fg-yellow bold">━━━━━━</tspan><tspan> </tspan><tspan class="fg-yellow bold">━━━━━</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,10 +1,9 @@
|
||||
<svg width="911px" height="236px" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="911px" height="218px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.fg-yellow { fill: #AA5500 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
@@ -20,29 +19,27 @@
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan>: </tspan><tspan class="bold">expected one of `.`, `;`, `?`, or an operator, found `for`</tspan>
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected one of `.`, `;`, `?`, or an operator, found `for`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--></tspan><tspan> /code/rust/src/test/ui/annotate-snippet/suggestion.rs:4:5</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold">--> </tspan><tspan>/code/rust/src/test/ui/annotate-snippet/suggestion.rs:9:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4 |</tspan><tspan> let x = vec![1];</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let x = vec![1];</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">-</tspan><tspan> </tspan><tspan class="fg-yellow bold">move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">-</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="118px"><tspan class="fg-bright-blue bold">...</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">7 |</tspan><tspan> let y = x;</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> let y = x;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-yellow bold">-</tspan><tspan> </tspan><tspan class="fg-yellow bold">value moved here</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">-</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">value moved here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">8</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">9 |</tspan><tspan> x;</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">9</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> x;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan><tspan> </tspan><tspan class="fg-bright-red bold">^</tspan><tspan> </tspan><tspan class="fg-bright-red bold">value used here after move</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="226px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
use annotate_snippets::{AnnotationKind, Level, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn case() {
|
||||
let source = r#"let x = vec![1];
|
||||
|
||||
|
||||
let y = x;
|
||||
|
||||
x;
|
||||
"#;
|
||||
|
||||
let input = &[Level::ERROR.primary_title("expected one of `.`, `;`, `?`, or an operator, found `for`")
|
||||
.element(
|
||||
Snippet::source(source)
|
||||
.path("/code/rust/src/test/ui/annotate-snippet/suggestion.rs")
|
||||
.line_start(4)
|
||||
.annotation(AnnotationKind::Context.span(4..5).label("move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait"))
|
||||
.annotation(AnnotationKind::Context.span(27..28).label("value moved here"))
|
||||
.annotation(AnnotationKind::Primary.span(31..32).label("value used here after move"))
|
||||
)
|
||||
];
|
||||
|
||||
let expected_ascii = file!["issue_9.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(input), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["issue_9.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(input), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<svg width="911px" height="218px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan class="fg-bright-red bold">error</tspan><tspan class="bold">: expected one of `.`, `;`, `?`, or an operator, found `for`</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan> </tspan><tspan class="fg-bright-blue bold"> ╭▸ </tspan><tspan>/code/rust/src/test/ui/annotate-snippet/suggestion.rs:9:1</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let x = vec![1];</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">─</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">┆</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="136px"><tspan class="fg-bright-blue bold">7</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> let y = x;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="154px"><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">─</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">value moved here</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="172px"><tspan class="fg-bright-blue bold">8</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="190px"><tspan class="fg-bright-blue bold">9</tspan><tspan> </tspan><tspan class="fg-bright-blue bold">│</tspan><tspan> x;</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="208px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan><tspan class="fg-bright-red bold">━</tspan><tspan> </tspan><tspan class="fg-bright-red bold">value used here after move</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,32 @@
|
||||
mod ann_eof;
|
||||
mod ann_insertion;
|
||||
mod ann_multiline;
|
||||
mod ann_multiline2;
|
||||
mod ann_removed_nl;
|
||||
mod ensure_emoji_highlight_width;
|
||||
mod first_snippet_is_primary;
|
||||
mod fold_ann_multiline;
|
||||
mod fold_bad_origin_line;
|
||||
mod fold_leading;
|
||||
mod fold_trailing;
|
||||
mod highlight_diff_line_with_wide_characters;
|
||||
mod highlight_duplicated_diff_lines;
|
||||
mod highlight_first_line_tab_371;
|
||||
mod highlight_source;
|
||||
mod highlight_source_multi_width_chars;
|
||||
mod highlight_source_zero_width_chars;
|
||||
mod issue_9;
|
||||
mod multiline_removal_indent;
|
||||
mod multiline_removal_last_line_tabs;
|
||||
mod multiline_removal_suggestion;
|
||||
mod multiple_annotations;
|
||||
mod multiple_highlight_duplicated;
|
||||
mod multiple_multiline_removal;
|
||||
mod primary_title_second_group;
|
||||
mod regression_leading_tab_label_alignment;
|
||||
mod regression_leading_tab_long_line;
|
||||
mod simple;
|
||||
mod strip_line;
|
||||
mod strip_line_char;
|
||||
mod strip_line_non_ws;
|
||||
mod styled_title;
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> </tspan><tspan class="fg-bright-red">local function f()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-red">- print()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-red">- end</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,19 @@
|
||||
use annotate_snippets::{Group, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let report = &[Group::with_level(Level::ERROR).element(
|
||||
Snippet::source("do\n local function f()\n print()\n end\nend\n")
|
||||
.patch(Patch::new(5..41, "")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["multiline_removal_indent.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["multiline_removal_indent.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<svg width="740px" height="110px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold">╭╴</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan> </tspan><tspan class="fg-bright-red">local function f()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-red">- print()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">4</tspan><tspan> </tspan><tspan class="fg-bright-red">- end</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan> </tspan><tspan class="fg-bright-blue bold">╰╴</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,37 @@
|
||||
<svg width="740px" height="128px" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.fg { fill: #AAAAAA }
|
||||
.bg { fill: #000000 }
|
||||
.fg-bright-blue { fill: #5555FF }
|
||||
.fg-bright-green { fill: #55FF55 }
|
||||
.fg-bright-red { fill: #FF5555 }
|
||||
.container {
|
||||
padding: 0 10px;
|
||||
line-height: 18px;
|
||||
}
|
||||
.bold { font-weight: bold; }
|
||||
tspan {
|
||||
font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
|
||||
white-space: pre;
|
||||
line-height: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
|
||||
|
||||
<text xml:space="preserve" class="container fg">
|
||||
<tspan x="10px" y="28px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="46px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-red">- </tspan><tspan>a</tspan><tspan class="fg-bright-red">.foo(|t| {</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="64px"><tspan class="fg-bright-blue bold">2</tspan><tspan> </tspan><tspan class="fg-bright-red">- b</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="82px"><tspan class="fg-bright-blue bold">3</tspan><tspan> </tspan><tspan class="fg-bright-red">- })</tspan><tspan>.bar()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="100px"><tspan class="fg-bright-blue bold">1</tspan><tspan> </tspan><tspan class="fg-bright-green">+ </tspan><tspan>a.bar()</tspan>
|
||||
</tspan>
|
||||
<tspan x="10px" y="118px"><tspan> </tspan><tspan class="fg-bright-blue bold">|</tspan>
|
||||
</tspan>
|
||||
</text>
|
||||
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,18 @@
|
||||
use annotate_snippets::{Group, Level, Patch, Renderer, Snippet, renderer::DecorStyle};
|
||||
|
||||
use snapbox::{assert_data_eq, file};
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let report = &[Group::with_level(Level::ERROR).element(
|
||||
Snippet::source("a.foo(|t| {\n\t\t\tb\n\t\t\t}).bar()\n").patch(Patch::new(1..22, "")),
|
||||
)];
|
||||
|
||||
let expected_ascii = file!["multiline_removal_last_line_tabs.ascii.term.svg": TermSvg];
|
||||
let renderer = Renderer::styled();
|
||||
assert_data_eq!(renderer.render(report), expected_ascii);
|
||||
|
||||
let expected_unicode = file!["multiline_removal_last_line_tabs.unicode.term.svg": TermSvg];
|
||||
let renderer = renderer.decor_style(DecorStyle::Unicode);
|
||||
assert_data_eq!(renderer.render(report), expected_unicode);
|
||||
}
|
||||