Code Monkey home page Code Monkey logo

ariadne's Introduction

pancake

A personal C++ utility library for a variety of uses

What does it do?

pancake includes many pieces of boilerplate code that are common throughout my software including:

Why the name 'pancake'?

I don't know. I searched "random words" online and 'pancake' was the first thing I saw. I'm not very inventive when it comes to names.

ariadne's People

Contributors

01mf02 avatar adampetro avatar calder avatar danipopes avatar firefragment avatar gimbling-away avatar goto-bus-stop avatar legorooj avatar loewenheim avatar lokathor avatar martinohmann avatar mohammadfawaz avatar moxinilian avatar msrd0 avatar not-my-profile avatar override-6 avatar ratmice avatar remimimimimi avatar rumpuslabs avatar slavfox avatar striezel avatar superhawk610 avatar tesujimath avatar trcyx avatar vontum avatar zesterer avatar zheoni avatar zollerboy1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ariadne's Issues

API overhaul

Here's my christmas wishlist for the API overhaul:

  • allow working with non-str data (#8)
  • get rid of initial location in Report::build() (#3)
  • pass Cache by shared reference to allow several threads to access it (#10)
  • allow SourceId (which may be e.g. a reference or an index) to only be interpreted by Cache (don't require it to be Display)
    and maybe this one would be nice:
  • allow somehow to print differently to use the errors in a TUI(#11)

Feel free to edit or comment.

Labels containing multiple lines of text are not handled well

Minimal example:

changing examples/multiline.rs to the following:

use ariadne::{Report, ReportKind, Label, Source, Color, ColorGenerator, Fmt};

fn main() {
    let mut colors = ColorGenerator::new();

    // Generate & choose some colours for each of our elements
    let a = colors.next();
    let b = colors.next();
    let out = Color::Fixed(81);
    let out2= colors.next();

    Report::build(ReportKind::Error, "sample.tao", 12)
        .with_code(3)
        .with_message(format!("Incompatible types"))
        .with_label(Label::new(("sample.tao", 32..33))
            .with_message(format!("This is of type {}", "Nat".fg(a)))
            .with_color(a))
        .with_label(Label::new(("sample.tao", 42..45))
            .with_message(format!("This is of type {}", "Str".fg(b)))
            .with_color(b))
        .with_label(Label::new(("sample.tao", 11..48))
            .with_message(format!(
                "The values are outputs of this {} expression",
                "match".fg(out),
            ))
            .with_color(out))
        .with_label(Label::new(("sample.tao", 0..48))
            .with_message(format!(
                "The {} has a problem",
                "definition".fg(out2),
            ))
            .with_color(out2))
        .with_label(Label::new(("sample.tao", 50..76))
            .with_message(format!(
                "Usage of {} here\nfoo bar\nbaz",
                "definition".fg(out2),
            ))
            .with_color(out2))
        .with_note(format!("Outputs of {} expressions must coerce to the same type", "match".fg(out)))
        .finish()
        .print(("sample.tao", Source::from(include_str!("sample.tao"))))
        .unwrap();
}

Actual result:

Gives this output:

image

$ cargo run --example multiline 
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/examples/multiline`
[03] Error: Incompatible types
   ╭─[sample.tao:1:13]
   │
 1 │ ╭─────▶ def five = match () in {
   · │                  🭯               
   · │ ╭────────────────╯               
 2 │ │ │         () => 5,
   · │ │               ┬  
   · │ │               ╰── This is of type Nat
 3 │ │ │         () => "5",
   · │ │               ─┬─  
   · │ │                ╰─── This is of type Str
 4 │ │ ├───▶ }
   · │ │     🭯   
   · │ ╰───────── The values are outputs of this match expression
   · │       │   
   · ╰───────┴─── The definition has a problem
   · 
 6 │     ╭─▶ def six =
   ⋮     ⋮   
 8 │     ├─▶     + 1
   ·     │             
   ·     ╰───────────── Usage of definition here
foo bar
baz
   ·         
   ·         Note: Outputs of match expressions must coerce to the same type
───╯

Expected result

Newlines are indented to start at the same column as the previous line,
and the arrows/line-drawing characters to the left of it are not disturbed.

Label fails to render when using emojis as source

I noticed that when using an emoji in the source, depending on the range I specify to associate a label to it, I get a label that make sense or not.

Code to reproduce:

use ariadne::{Label, Report, ReportKind, Source};

fn main() {
    for i in 1..=4 {
        println!("Using range {:?} for a 4-bytes emoji", 0..i);
        let mut report = Report::build(ReportKind::Error, (), 0);
        report = report.with_label(Label::new(0..i).with_message("That is very haha 😂"));
        report.finish().print(Source::from("😂")).unwrap();
    }
}

The output:

Using range 0..1 for a 4-bytes emoji
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ 😂
   · ┬
   · ╰── That is very haha 😂
───╯
Using range 0..2 for a 4-bytes emoji
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ 😂
   · ─┬
   ·  ╰── That is very haha 😂
───╯
Using range 0..3 for a 4-bytes emoji
Error:
   ╭─[<unknown>:1:1]
   │
   ·
───╯
Using range 0..4 for a 4-bytes emoji
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ ╭─▶ 😂
───╯

I think the versions with the ranges 0..1, 0..2 and 0..3 don't make sense, but I would expect the last range (0..4) to display the label correctly.

Multiple messages/notes

In codespan-reporting crate, it is possible to attach multiple notes to a diagnostic message. In ariadne, there is only one message and only one note available to provide, limiting the diagnostic messages that I can create with an Ariadne report. It'd be a nice feature if I could associate multiple formatted messages or notes to any given Ariadne report.

Don't consume writer when writing a report

Using Report::write one has to pass in a writer. However, the writer is passed in as mut w: W, consuming the instance in the process.

However, it would be sufficient to just require w: &mut W, as that is all that is needed, not consuming the writer.

Consuming the writer is especially a problem I one wants to write out to a String, which you can never get back, as its being consumed in the process.

Remove redundant setters in the report builder.

The ReportBuilder type has a bunch of duplicate setter functions that call their counterpart (set_message and with_message, set_note and with_note, etc).

It might be a good idea to remove these redundant setters as they needlessly complicate the interface of this type.

Labels without messages still create extra vertical space

Consider this example:

use ariadne::*;

const TEXT: &str = "a b c d";

fn report_n_labels(n: usize) {
    let mut report = Report::build(ReportKind::Error, (), 0);
    for i in 0..n {
        report = report.with_label(Label::new(i * 2..i * 2 + 1));
    }
    report.finish().print(Source::from(TEXT)).unwrap();
}

fn main() {
    for n in 1..=4 {
        report_n_labels(n);
    }
}

This gives output:

Error: 
   ╭─[<unknown>:1:1]
   │
 1 │ a b c d
   · ─
   ·
───╯
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ a b c d
   · ─ ─
   ·
   ·
   ·
───╯
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ a b c d
   · ─ ─ ─
   ·
   ·
   ·
   ·
   ·
───╯
Error:
   ╭─[<unknown>:1:1]
   │
 1 │ a b c d
   · ─ ─ ─ ─
   ·
   ·
   ·
   ·
   ·
   ·
   ·
───╯

It looks like labels add extra vertical space for their messages even if they don't have messages.
This occurs with compact mode too.
I think this is undesirable.

Bug: weird visual bug when a label doesn't have a message

OS: Windows
Version of ariadne: I'm using git version

minimal code:

Report::build(ReportKind::Error, error.span.file_id, error.span.range.0)
.with_code(1)
.with_message(error.error_message)
.with_label(Label::new(error.span).with_color(red))
.finish()
.print(sources(vec![("test.vnl", &self.source)]))
.unwrap();

output:

FileCache doesn't seem to work?

I'm attempting to use ariadne::FileCache with Report::write (or print or eprint), but it doesn't seem to work for me.FileCache implements Cache<Path> but not Cache<PathBuf>, and as std::path::Path isn't sized, I can't pass a Path directly into Report::build or a Label. Using &Path in the following minimal example:

fn example() {
    use ariadne::*;

    let mut fc = FileCache::default();
    let path = std::path::PathBuf::from("example.txt").as_path();

    let mut builder = Report::build(ReportKind::Error, path, 10)
        .with_message("message")
        .with_label(Label::new((path, 10..20)).with_message("something bad"));

    builder.finish().eprint(fc).unwrap();
}

yields the following error:

error[E0277]: the trait bound `ariadne::FileCache: Cache<&Path>` is not satisfied
   --> src/parsing.rs:19:29
    |
19  |     builder.finish().eprint(fc).unwrap();
    |                      ------ ^^ the trait `Cache<&Path>` is not implemented for `ariadne::FileCache`
    |                      |
    |                      required by a bound introduced by this call
    |
    = help: the trait `Cache<Path>` is implemented for `ariadne::FileCache`
note: required by a bound in `ariadne::Report::<S>::eprint`
   --> /home/jon/.cargo/registry/src/github.com-1ecc6299db9ec823/ariadne-0.1.5/src/lib.rs:159:22
    |
159 |     pub fn eprint<C: Cache<S::SourceId>>(&self, cache: C) -> io::Result<()> {
    |                      ^^^^^^^^^^^^^^^^^^ required by this bound in `ariadne::Report::<S>::eprint`

For more information about this error, try `rustc --explain E0277`.

This is using ariadne 0.1.5.

Thanks!

Is possible to only pass a small section of the whole source?

I collect elsewhere the source in a vector of spans:

pub struct Source<'input> {
    pub tokens: &'input [Token<'input>],
    cursor: usize,
}

But for show the error I need to "materialize" all the code into Strings. I wish it were possible to only pass the relevant lines:

    let mut source =  Source::from( get from line 10 -> 20);
    source.line_start = 10;

    err.finish().print(("sample.tao",source))

Feature request: Show the whole content

I'm using ariadne to highlight certain locations in a file.

If a span to highlight contains multiple lines then ariadne will not show the whole content, only the first and last lines of the span.

For my specific use case I would like to still be able to see all the lines contained in the span. I am not sure it is possible right now.

Could an option be added to instruct ariadne to print all span's lines?

Thanks a lot!

Labels fail to print correctly with newlines

Hello, I am trying to use ariadne for error reporting for a crate. I am getting a problem with printing Reports that span multiple lines.

Here is some example code:

#[test]
fn ariadne() {
    let source = "(\r\n\r\n}";

    Report::build(ReportKind::Error, (), 0)
        .with_config(Config::default().with_char_set(CharSet::Ascii))
        .with_label(Label::new(0..1).with_message("opens here"))
        .with_label(Label::new(5..6).with_message("closes here"))
        .finish()
        .eprint(Source::from(source))
        .unwrap();
}

and it produces this:

Error: 
   ,-[<unknown>:1:1]
   |
 1 | (
   : |  
   : `-- opens here
---'

What should happen: The start and end lines are displayed along with both labels

What actually happens: Only the start line is displayed; the second label is completely absent.


I cannot figure out what causes it to go wrong but my hunch is that the span from 5..6 is the problem because 6 is "out of bounds" of the string (even though it's used as the exclusive endpoint).

Add option for a Span to be based on bytes rather than characters

Right now, the error drawer seems to assume that span.start() and span.end() are character indices rather than byte indices. This might be useful for manually choosing the error position, but most lexers use byte positions instead of character positions.

This makes a difference when you use Unicode characters in the source.
This is a language that is lexed using logos, which uses byte indices:

Error: Index 5 is out of range of array (length 3)
   ╭─[test/test.spwn:3:6]
   │
 3 │ ╭─▶ arr[5] = 1
 4 │ ├─▶ // this should not be in the error
   · │
   · ╰──────────────────────────────────────── Index 5 is out of range of array (length 3)
───╯

The error gets offset because of some Unicode characters before the error position:

// øæåææ
let arr = [1, 2, 3]
arr[5] = 1
// this should not be in the error

I am not sure what the most idiomatic API for this is, but one way could be to have an optional function in the Span trait that looks something like fn uses_bytes() -> bool, where the default implementation just returns false. Another way is to add the option to the Config struct.

Support `Caused by`

Now we've handled textual errors pretty well, but there's another non-textual case, like runtime errors, #50, #72.

I think it can be solved by adding Caused by.

For example, IO Error should be

[E001] Error: File Not Found

Caused by:
      path/of/files

Other runtime errors can be:

[E002] Error: Runtime error, reasons...

Caused by:
      path/of/call_site1(line:col)
      path/of/call_site2(line:col)
      path/of/call_site3(line:col)

Are there any other suggestions?

This is not about compatibility, can I submit a PR for this feature?

Initiial source ID and location

In the method to create a report builder, there are two arguments corresponding to a source ID and a location. From reading the source code, these correspond to a location field inside the report builder struct, which is only ever used in

let location = if src_id == self.location.0.borrow() {
and following lines for determining a source location in certain cases. Am I missing something, or am I correct in thinking that this initial location could (and for ease of end usage, should) be made optional?

Support Multi-Line Notes

Currently notes are simply printed, with newlines just starting a new line without indentation. It would be convenient if Ariadne would re-indent when the note starts a new line.

Support CRLF Newlines

Right now the implementation of creating a Source from a string iterates over lines by using str::lines which hides whether a line ended with \n or \r\n. This causes it to put labels in the wrong place, or even lose them if they're near the end of the file, as demonstrated here:

Untitled-1

Up arrow character cannot be rendered in macOS

Repro

Run the example:

cargo run --example multiline

Expected results

All characters are rendered as expected

Actual results

Up arrow character 🭯 (U+1FB6F) is rendered as '?'.

screenshot

Additional info

I tried the following famous terminals but all did not display the character as expected.

  • Terminal.app
  • iTerm2
  • Alacritty

And I also tried several famous fonts but they did not help to solve this issue too.

  • SF Mono
  • Courier
  • Menlo
  • Monaco
  • JetBrains Mono NL

My enviornment

  • macOS 11
  • Rust 1.56.1

Support for above-code labels

I have been thinking about how to make nice errors for using builtins in the lisp for my in-progress build tool, dull. At first i copied the structure of the example output image (in wonderfully shit ascii art lol), but it occurred to me that it would be nice to put the reasons above so that they read in order.

ParameterError

 +-[great.dull:1:16]
  |            +- After this scoop indicator
  |            | +- and this scoop parameter
  |            | | +- There should be no more arguments
  |            _ _ _
1 | (defn foo [& _ _])

How does this fit into your plans for layout?

Allow `Span`s to be `byte` offset based instead of `char` offset based

I use a parser which reports code locations as byte spans instead of char spans. It would be nice if via config we could specify byte offsets instead of char offsets.

One way would be in this line:

let len = line.chars().count();

to switch to:

let len = line.chars().fold(0, |mut acc, i| {acc += i.len_utf8(); acc });

based on the configuration. Of note, this actually fixes some of the spacing issues like in #41 if the location producer also uses bytes.

Feature Request: Suggest an edit for note/help

Would be useful to be able to suggest edits in a note/help message similar to how rust does it.

Need to be able to provide a new version of the line/file, not sure how that would work.

Use line numbers instead of character position

Is it possible to render errors based on line number and then the column? My parser resets the column position on each new line, so rendering errors is difficult since an error on the 4th line at column 4..5 is being rendered as the 4th and 5th characters in the entire source.

Report without source

I have encountered some IO errors that the file cannot be found.

Since the file cannot be found, there is no source.

How to print in this case?

Add a way to get back the `location` from Report

This is quite important since in most cases we don't know at the top of our project from which file originated the error and we don't want to pass all the files imported.
So we definitely need a way to get back the location so we can give the correct file while printing.

if let Err(e) = my_result {
        e.eprint(&std::fs::read_to_string(&e.location().0).unwrap());
}

support byte spans

Currently spans in ariadne reference characters and not bytes. Because converting between char indices and byte indices can be cumbersome it would be nice if both variants would be supported.

Allow annotation with different styles

What I want to achieve:

Currently, there's only a way to underline some text with fancy utf-8 underscores.
I want something similar to this:

error[E0061]: this function takes 1 argument but 3 arguments were supplied
 --> src/main.rs:2:5
  |
2 |     std::mem::transmute(3, 3, 4);
  |     ^^^^^^^^^^^^^^^^^^^    -  - unexpected argument of type `{integer}`
  |                            |
  |                            unexpected argument of type `{integer}`

A possible way to achieve that:

My thought would be adding a with_style option to a Label and maybe restrict those to Underscore, Wiggly and Spiky (just making up names), where Underscore would be the default, Wiggly would use ~ and Spiky ^.

One thing to note would be, that those notation shouldn't be allowed to have a message associated with them?

What do you think?

Weird looking report

Error: Unexpected character `.`
   ╭─[<unknown>:4:14]
   │
 4 │     float: 1.0,
   ·             ─
   ·              ──
───╯

The first _ is labeling the right character, but it's unclear what the floating __ on line 6 signifies.
Produced by the following code:

                t => {
                    return Err(Report::build(ReportKind::Error, (), self.current)
                        .with_message(format!("Unexpected character `{}`", t))
                        .with_label(Label::new(self.current - 1..self.current)));
                }

Option to hide file name and line numbers

I'm using Ariadne to format errors for REPL input, which doesn't originate from a file and typically consists of only one line. Therefore, the file name header and line numbers column are just visual noise for me, and it would be nice to have an option to hide them.

Example in README does not render properly

Hey, I was trying to get started with Ariadne and grabbed an example from the README, but it does not render as expected:

What I got:

image

What README shows:

Ariadne supports arbitrary multi-line spans

Tested with 0.1.5 and 0.1.3.

I was about to open a PR to fix this, the spans are a bit behind for the example in README and examples/sample.rs, however, I just wanted to confirm, can you reproduce this?

EDIT: updated image.

Support HTML output

Is there any way to render output as html?

I want to render error report in jupyter, but if read stderr directly to it will look like this:

image

`Config::default().with_color(false)` not disable colors

Despite setting this to false, the colors are shown when added to labels:

pub fn build_report(
    named: String,
    err: &ErrorParser,
) -> Report<(String, Range<usize>)> {
    let mut colors = ColorGenerator::new();

    // Generate some colours for each of our elements
    let primary = colors.next();
    // let secondary = colors.next();
    let code = err.error_code();
    let config = Config::default().with_color(false);
    let diagnostic = Report::build(ReportKind::Error, named.clone(), 0)
        .with_code(code as usize)
        .with_config(config);

    match err {
        ErrorParser::ScalarParse { span, kind, msg } => diagnostic
            .with_message(msg)
            .with_label(
                Label::new((named, span.range()))
                    .with_message(msg)
                    .with_color(primary), <-- THIS CAUSE IT TO SHOW COLORS
            )
            .with_note(format!("Parsing value of type: {kind:?}"))
            .finish(),
        _ => {
            todo!()
        }
    }
}

Difficulties with implementing Cache

I've got some troubles trying to implement Cache; trying to compile this:

struct CdlFiles<'p> {
    project: &'p spircer::Project,
    cache: Vec<ariadne::Source>,
}

impl<'p> ariadne::Cache<spircer::FileId> for CdlFiles<'p> {
    fn fetch(&mut self, id: &spircer::FileId) -> Result<&ariadne::Source, Box<dyn Debug>> {
        for i in *id..self.project.files.len() {
            self.cache.push(ariadne::Source::from(unsafe {
                std::str::from_utf8_unchecked(self.project.files[i].bytes())
            }))
        }
        Ok(&self.cache[*id])
    }
    fn display<'a>(&self, id: &'a spircer::FileId) -> Option<Box<dyn Display + 'a>> {
        Some(Box::new(self.project.files[*id].path().display()))
    }
}

Errors like this:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/cli.rs:36:23
   |
36 |         Some(Box::new(self.project.files[*id].path().display()))
   |                       ^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'p` as defined on the impl at 26:6...
  --> src/cli.rs:26:6
   |
26 | impl<'p> ariadne::Cache<spircer::FileId> for CdlFiles<'p> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/cli.rs:36:23
   |
36 |         Some(Box::new(self.project.files[*id].path().display()))
   |                       ^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the method body at 35:16...
  --> src/cli.rs:35:16
   |
35 |     fn display<'a>(&self, id: &'a spircer::FileId) -> Option<Box<dyn Display + 'a>> {
   |                ^^
note: ...so that the expression is assignable
  --> src/cli.rs:36:9
   |
36 |         Some(Box::new(self.project.files[*id].path().display()))
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Option<Box<(dyn std::fmt::Display + 'a)>>`
              found `Option<Box<dyn std::fmt::Display>>`

For more information about this error, try `rustc --explain E0495`.

Shouldn't the Cache trait have an explicit lifetime ? Otherwise I don't know how to reconcile 'p and 'a in my example.FileId (what stands for an SourceId) is just an usize indexing into the right File structure, in my Project structure.

Trailing whitespace characters

Firstly thank you for the excellent library! We're heavily using it in PRQL.

In PRQL we have a pre-commit action which removes trailing whitespace. It seems that ariadne's outputs have trailing whitespace, so inline snapshot tests can't pass. Here's an example: PRQL/prql@80ae431, from PRQL/prql#1181.

Would it be reasonable to strip trailing whitespace? Or is that too narrow a goal? It's not that critical for us; we can move to using snapshot tests in .snap files if needed.

Thanks again

Type inference fails with too few Builder method calls

This code compiles:

use ariadne::{Report, ReportKind, Source};
use std::ops::Range;

fn main() {
    Report::<Range<usize>>::build(ReportKind::Error, (), 10)
        .with_message("Unterminated string")
        .finish()
        .print(Source::from(""))
        .unwrap();
}

Removing the turbofish on Report like this:

use ariadne::{Report, ReportKind, Source};
use std::ops::Range;

fn main() {
    Report::build(ReportKind::Error, (), 10)
        .with_message("Unterminated string")
        .finish()
        .print(Source::from(""))
        .unwrap();
}

fails with this message:

error[E0284]: type annotations needed
 --> src/main.rs:5:5
  |
5 |     Report::build(ReportKind::Error, (), 10)
  |     ^^^^^^^^^^^^^ cannot infer type for type parameter `S`
  |
  = note: cannot satisfy `<_ as Span>::SourceId == _`
help: consider specifying the type argument in the method call
  |
6 |         .with_message::<M>("Unterminated string")
  |                      +++++

For more information about this error, try `rustc --explain E0284`.

Tested with ariadne v0.1.3 and rustc 1.58.0-nightly (ff0e14829 2021-10-31).

Implement `clone` on `ReportBuilder`

It would be nice to be able to clone ReportBuilder to make it possible to print it with different configs. E.g. when writing tests for error messages, I would like to compare the report against a simple string without ANSI codes, but print out the report with colors if it doesn't match.

`Source::chars()` iter is lossy but not documented as so

I recently switched my parsing from going over a stream of characters produced by str::chars() to parsing over a stream of characters from ariadne::Source::chars(). This makes it a lot easier to load all of my source files through a cache which can be directly used by ariadne for printing reports.

Unfortunately it isn't well documented that str::char() and ariadne::Source::chars() don't produce the same, or even roughly equivalent, output.

I spent some time debugging why none of my line-ending parsers were succeeding before I realized that ariadne::Source::chars() doesn't produce any line-endings (or even whitespace).

fn main() {
    let raw_src = "a \nb";
    let src = ariadne::Source::from(raw_src);

    let raw_char_vec = raw_src.chars().collect::<Vec<_>>();
    let src_char_vec = src.chars().collect::<Vec<_>>();

    println!("{:#?}", raw_char_vec);
    println!("{:#?}", src_char_vec);

    assert_eq!(raw_src.len(), src.len());
    assert_eq!(raw_src.len(), src.chars().count());
}

It would be nice if ariadne::Source::chars() were documented as not producing any white-space.

It would also be nice if ariadne::Source provided another method which could get a stream of characters including line-endings with appropriate spans.

I mocked up an adapter which can re-engineer a character stream which is mostly equivalent to str::chars() as long as your parser doesn't care about white-space. It returns all the characters in the file with their correct indices, and then also injects newline characters using the index at the end of the line span. This works as a replacement for str::chars().enumerate() in my parsing code.

struct Renumerate<'a> {
    produced: usize,
    line_info: Option<(usize, usize, Vec<char>)>,
    lines: Box<dyn Iterator<Item = &'a ariadne::Line> + 'a>,
}

impl<'a> Renumerate<'a> {
    pub fn new(src: &'a ariadne::Source) -> Renumerate<'a> {
        let mut lines = src.lines();

        Renumerate {
            produced: 0,
            line_info: lines
                .next()
                .map(|line| (line.span().start, line.span().end, line.chars().collect())),
            lines: Box::new(lines),
        }
    }
}

impl<'a> Iterator for Renumerate<'a> {
    type Item = (usize, char);

    fn next(&mut self) -> Option<Self::Item> {
        if let Some((start, end, ref line_chars)) = self.line_info {
            if self.produced < line_chars.len() {
                self.produced += 1;
                Some((start + self.produced - 1, line_chars[self.produced - 1]))
            } else {
                self.line_info = self
                    .lines
                    .next()
                    .map(|line| (line.span().start, line.span().end, line.chars().collect()));
                self.produced = 0;
                Some((end - 1, '\n'))
            }
        } else {
            None
        }
    }
}

Public types missing derivable traits

Line, Source, FileCache, Label, Report, etc. do not implement any of Clone, Debug, PartialEq, etc.

This makes it hard to use them in types that do need to implement those. Implementing them should be a trivial #[derive(…)] macro.

See also C-COMMON-TRAITS in the Rust API Guidelines.

Reports with non-static labels

New to this crate, so not sure if I'm simply doing something wrong but it seems like you can't build reports with non-static values:

Report::build(ReportKind::Error, ...)
    .with_message(...)
    with_label(Label::new((data.file.as_str(), span)))

data.file is a String instance, my main function calls another function passing data which then builds the report. Borrowed data either escapes the function body or doesn't outlive 'static as required

Breaking change in 0.1.6 compared to 0.1.5

With #44 the public type Report got a new generic lifetime parameter, which is breaking in cases where the lifetime cannot be inferred or the elided_lifetimes_in_paths lint is denied.

In my understanding under cargo semver, this should be released as 0.2.0 instead.

Support for line offset

I have a perhaps odd situation where I'm dealing with chunks of text out of a larger file, so to get the correct line number I need to add an offset value. I implemented this here olson-dan@3b66508 but it seems like rustfmt made a mess of this commit. Are you interested in the change at all? Maybe adding a rustfmt config to the main branch could help with this type of situation.

Dotted lines in left margins disappeared in version `0.2.0`

Version 0.2.0 renders the left margin differently than version 0.1.5. See an example from application:

With version 0.1.5:

Error: error: global rule `test_2` depends on non-global rule `test_1`
    ╭─[test.yar:25:5]
    │
  8 │ rule test_1 {
    ·      ───┬──
    ·         ╰──── non-global rule `test_1` declared here
    ·
 23 │ global rule test_2 {
    ·             ───┬──
    ·                ╰──── global rule `test_2` declared here
    ·
 25 │     test_1
    ·     ───┬──
    ·        ╰──── `test_1` is used in the condition of `test_2`
────╯

With version 0.2.0:

error: global rule `test_2` depends on non-global rule `test_1`
    ╭─[test.yar:25:5]
    │
  8 │ rule test_1 {
    │      ───┬──
    │         ╰──── non-global rule `test_1` declared here
    │
 23 │ global rule test_2 {
    │             ───┬──
    │                ╰──── global rule `test_2` declared here
    │
 25 │     test_1
    │     ───┬──
    │        ╰──── `test_1` is used in the condition of `test_2`
────╯ 

Version 0.1.5 uses dotted lines as a visual indication of missing lines, while in version 0.2.0 the dotted lines disappeared and the result is an error report that is less clear than before.

I believe this an unintended change that was introduced here when draw.vbar_break was replaced with draw.vbar.

Or perhaps the intention was replacing the dotted lines with a solid line in a different color? If that's the case I would say that the dotted line (possibly on a different color too) is a better alternative, as it works in monochrome environments as well.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.