brendanzab / codespan Goto Github PK
View Code? Open in Web Editor NEWBeautiful diagnostic reporting for text-based programming languages.
License: Apache License 2.0
Beautiful diagnostic reporting for text-based programming languages.
License: Apache License 2.0
With the addition of the Files
trait, codespan-reporting
is now decoupled entirely from codespan
. I'm wondering if we should consider renaming it? language-reporting
is already taken, but maybe we could call it something else:
text-reporting
code-reporting
lang-reporting
source-reporting
Curious if anybody has any thoughts!
Sometimes there's no specific place in the source file to point - consider a program in C which does not have main() function.
Presumably line_span
always returns a valid span. However, the API requires you to check the error anyway:
let line = file_db
.line_span(file, start.line)
.expect("line should be in bounds");
println!("{}", file_db.source_slice(file, line).expect("line is still in bounds"));
I would like Files
to get a new line_slice
API which returns a Result<&str, LineIndexOutOfBoundsError>
directly.
There doesn't seem to be any reason for it to consume the writer other than to force the user to lock the output multiple times. I just can't think of any reason for the emit
function to take the writer argument by value and I don't know if I can say much more about this. It should be a trivial change.
At the moment you need to go through the CodeMap
to get a new span. And example of this is here:
This is not very discoverable, and a couple of users have been caught up on this before!
Perhaps we could have a FileMap::span_for
method. Or perhaps a SpanCreator
thingy that is Copy
. That way folks could pass it around without having to worry about ownership.
Label
is a pretty common term in programming languages, and one that I currently conflict with! Instead In need to do:
use codespan_reporting::Label as DiagnosticLabel;
Some ways to fix this could be to:
Note
.type Label = (FileId, Span, String);
Thoughts?
As part of #30 it might be nice to have a simple diagnostic rendering mode to make it easier on screen readers. This would only display a list of diagnostics, without the source snippets.
Here's an example of what it might look like:
error[E0308]: `case` clauses have incompatible types
- FizzBuzz.fun:8:12: expected `String`, found `Nat`
= expected type `String`
found type `Nat`
= FizzBuzz.fun:4:13: `case` clauses have incompatible types
= FizzBuzz.fun:3:15: expected type `String` found here
error[E0308]: `case` clauses have incompatible types
- FizzBuzz.fun:15:16: expected `String`, found `Nat`
= expected type `String`
found type `Nat`
= FizzBuzz.fun:11:5: `case` clauses have incompatible types
= FizzBuzz.fun:12:16: this is found to be of type `String`
= FizzBuzz.fun:13:16: this is found to be of type `String`
= FizzBuzz.fun:14:16: this is found to be of type `String`
Perhaps others have better ideas though?
We added a non-colored smoke test in #76, but we don't currently test the colors in our output. We should figure out how to do that nicely!
Since servo/heapsize#105 the heapsize
crate in unmaintained.
The easiest way to fix this would be to remove support for codespan
's memory_usage
feature.
Having done some work with diagnostic formatting, I'm feeling more that language implementers should be creating intermediate enumerations of for their diagnostic messages, then sending those to various front-ends - be they a CLI, or a language server, etc.
This would help to make it easier to craft messages for those specific front-end, and save us the pain of trying to convert a common diagnostic format between front-ends. At the moment they don't fully match up, and this means we have to do some messy hacks in order to make it work .
maybe a method to_raw_index
where it just unwraps the number inside ByteIndex
I know that can be byte_index.0
, but it would be more expressive
We need a test to check if we handle tab indentation properly when printing diagnostic labels. We may also need to add a 'tab width' display configuration.
See also: wycats/language-reporting#3
Our builder DSL for diagnostic messages doesn't really look the nicest when passed through rustfmt. An example:
Maybe we could switch around how we pass arguments and call methods so that it reads a little nicer?
For language servers the sources will be frequently modified and old sources (FileMap
s) will be thrown out eventually. As CodeMap
works now though, it is not possible to remove a source once it is added which means that memory use will grow for each modification to a file.
If removal of FileMap
s are possible we could maybe run into fragmentation issues for Span
allocation but I think that in practice this can largely be ignored (or we could use some simple memory allocator approach to remedy memory fragmentation).
It would be nice to be able to define custom Files
-style databases depending on the needs of a project.
The language-reporting fork defines ReportingSpan
and ReportingFiles
traits which might be useful for inspiration.
I want to use this crate in my project, but I don't know how to use it ๐ค
Given a file or string of source code, and a start and end location of a token, how do I produce an error message pointing to the location?
If someone helps explain, I can contribute with writing some documentation.
In what cases is it possible for https://docs.rs/codespan-reporting/0.6.0/codespan_reporting/term/fn.emit.html to return an error? I see that the error type is std::io::Error; is it guaranteed to be only if a write to the underlying WriteColor fails? Or are there other validations involving the Config / Files / Diagnostic that might trigger an error from term::emit?
Currently, I'm writing code that looks like this:
for warning in warnings {
let label = Label::new(file, warning.location.span, warning.data.to_string());
Diagnostic::new_warning(warning.data.to_string(), label);
// TODO: emit warning
}
My warnings are fairly compact error enums. If new_warning
and Label::new
took Display
instead, they could
to_string()
so muchThis is blocking gluon-lang/gluon#505 and prevents Piklelet from being published to Crates.io as well. Is there anything blocking us from doing this?
I've been playing with a toy query language that includes UTF-8 string literals. On error reporting, visual alignment looks broken when using non-ASCII characters, even though spans are correct.
I guess this is due to paddings being computed using str::len()
?
codespan/codespan-reporting/src/emitter.rs
Line 106 in a0311f3
In SourceSnippet reporting, it tries to determine the max region of the error. Perhaps in previous versions this made sense, but in the latest version, the diagnostics are a PrimaryLabel + a list of other second labels (with notes and warnings, etc)
So it feels very peculiar to not just point to the PrimaryLabel's location in the span of the error message.
Consider this example from Rust:
error[E0308]: if and else have incompatible types
--> path/to/my_file.rs:221:9
|
218 | / if (true) {
219 | | 5
| | - expected because of this
220 | | } else {
221 | | false
| | ^^^^^ expected integer, found bool
222 | | };
| |_____- if and else have incompatible types
|
SourceSnippet
would put this min/maxed box of path/to/my_file.rs:219:14
as the location, not path/to/my_file.rs:221:9
which is the location of the primary label
Perhaps we should use some tool like ansi-to-svg or ansifilter to automatically generate the screenshot for the README. It's currently out of date!
This would be nice for showing off what we can do!
I'm thinking it might be useful to pass in an optional width for line wrapping in the Config
struct. This would wrap lines and error messages. If we can figure out how to also include types like those from pretty
, then these might be able to take advantage of this information as well.
As mentioned in #77, Rustc does a clever job of grouping together labels:
It would be super neat if we could do something similar!
At the moment our diagnostics are all spread out:
error[E0308]: `case` clauses have incompatible types
โโโ FizzBuzz.fun:8:12 โโโ
โ
8 โ _ _ => num
โ ^^^ expected `String`, found `Nat`
โ
= expected type `String`
found type `Nat`
โโโ FizzBuzz.fun:4:13 โโโ
โ
4 โ fizzโ num = case (mod num 5) (mod num 3) of
โ โญโโโโโโโโโโโโโ'
5 โ โ 0 0 => "FizzBuzz"
6 โ โ 0 _ => "Fizz"
7 โ โ _ 0 => "Buzz"
8 โ โ _ _ => num
โ โฐโโโโโโโโโโโโโโ' `case` clauses have incompatible types
โ
โโโ FizzBuzz.fun:3:15 โโโ
โ
3 โ fizzโ : Nat โ String
โ ------ expected type `String` found here
โ
It would be great if we could collapse things down, for example:
error[E0308]: `case` clauses have incompatible types
โโโ FizzBuzz.fun:8:12 โโโ
โ
3 โ fizzโ : Nat โ String
โ ------ expected type `String` found here
4 โ fizzโ num = case (mod num 5) (mod num 3) of
โ โญโโโโโโโโโโโโโ'
5 โ โ 0 0 => "FizzBuzz"
6 โ โ 0 _ => "Fizz"
7 โ โ _ 0 => "Buzz"
8 โ โ _ _ => num
โ โ ^^^ expected `String`, found `Nat`
โ โฐโโโโโโโโโโโโโโ' `case` clauses have incompatible types
โ
= expected type `String`
found type `Nat`
Or in the case of that rustc error:
error[E0308]: match arms have incompatible types
โโ codespan/src/file.rs:102:34
:
99 โ โญโโโโโโโโ match line_index.compare(self.last_line_index()) {
100 โ โ Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),
โ โ --------------------------------------------- this is found to be of type `std::result::Result<index::ByteIndex, file::LineIndexOutOfBoundsError>`
101 โ โ Ordering::Equal => Ok(self.source_span().end()),
โ โ ---------------------------- this is found to be of type `std::result::Result<index::ByteIndex, file::LineIndexOutOfBoundsError>`
102 โ โ Ordering::Greater => LineIndexOutOfBoundsError {
โ โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ^
103 โ โโ given: line_index,
104 โ โโ max: self.last_line_index(),
105 โ โโ },
โ โฐโโโโโโโโโโโโโโ^ expected enum `std::result::Result`, found struct `file::LineIndexOutOfBoundsError`
106 โ โ }
โ โฐโโโโโโโโโ' `match` arms have incompatible types
:
= note: expected type `std::result::Result<index::ByteIndex, file::LineIndexOutOfBoundsError>`
found type `file::LineIndexOutOfBoundsError`
It might be worth studying how librustc_errors does this. There's a bunch of clever logic in emitter.rs that it might be worth understanding.
It appears that lsp-types
has recently been bumped to version 0.63.1, making codespan-lsp
0.5.0 no longer semver-compatible with this latest version. This new release adds support for LSP 3.15, which includes a number of improvements over the previous release. It would be nice if codespan-lsp
could be bumped on Crates.io to work with this new release.
Our error reporting should be accessible to people with low, to non-sighted users, and also those who are colour blind.
I think at the moment we support colour blind users reasonably well - ie. colour enhnces the experience, but is not required. It would be interesting to know how non-sighted people fare, and how our error reporting works with terminals with large text sizes.
Wondering if we should do some design documentation surrounding this? Is there any existing resources out there with regards to accessibility for error reporting?
It would be nice if we had a good way to set the styling for terminal CLIs in a standard way, without too much drama - either via command line flags or config files. I'm not sure what this would look like though, and I don't want to couple us to a specific command line parser or config format, but am also wary of something too astronautical.
Ultimately we should be able to set up the emitter example have its display configuration be customisable without too much trouble.
Since codespan-lsp
0.4.1 still requires lsp-types
0.60.0, language servers relying on this library are currently unusable on JetBrains IDEs such as IntelliJ or WebStorm because of an issue with the document version schema, currently fixed in lsp-types
0.61.0 (gluon-lang/lsp-types#113). It appears that the lsp-types
version requirement for codespan-lsp
has already been bumped and merged into master
in this commit, but it isn't yet available on Crates.io.
@Marwes pointed out that blue renders quite dark by default on windows. We should special case our usages of blue like rustc does.
Love the library, but there's an issue when using it with long multi-line spans--it shows every line!
I believe the lines that would need to be changed are these:
codespan/codespan-reporting/src/term/views/source_snippet/mod.rs
Lines 259 to 261 in e2d6fc3
rustc, on the other hand, will collapse multiple line spans:
Does codespan support this?
It would be really neat to be easily able to integrate diagnostic reporting with in-browser editors - either when your language is compiled to webasm, or when communicating over HTTP. Perhaps we could focus on one in the short term. There are a number out there, for example:
Would also be nice to have a tutorial and/or example showing how this could be done.
Prior to the release of codespan
0.4.0, the codespan::Span
type used to derive Default
. I personally found this quite handy when hacking on ASTs where I did not care about the span information, and I could conveniently specify Default::default()
everywhere a Span
was expected without having to import it.
In particular, I also happen to find the Default
implementation handy when writing macros that construct ASTs, since they eliminate the need to depend on codespan
and import the type. So if a downstream crate were to import the macro, it would not require the user to depend on codespan
, or for the crate author to re-export codespan::Span
for the macro to use.
Are there any technical reasons to not derive Default
on Span
? If there aren't, it would be a nice touch in the ergonomics department.
Filenames on Unix can be arbitrary bytes; on Windows they can be arbitrary UTF16 (and I think there's some strangeness around surrogate pairs).
error[E0277]: the trait bound `std::string::String: std::convert::From<std::path::PathBuf>` is not satisfied
--> src/main.rs:108:27
|
108 | let file_id = file_db.add(opt.filename, buf);
| ^^^ the trait `std::convert::From<std::path::PathBuf>` is not implemented for `std::string::String`
|
= help: the following implementations were found:
<std::string::String as std::convert::From<&std::string::String>>
<std::string::String as std::convert::From<&str>>
<std::string::String as std::convert::From<rcc::data::LengthError>>
<std::string::String as std::convert::From<std::borrow::Cow<'a, str>>>
<std::string::String as std::convert::From<std::boxed::Box<str>>>
= note: required because of the requirements on the impl of `std::convert::Into<std::string::String>` for `std::path::PathBuf`
As can be seen in the emitter example the error for multiline errors could be improved.
Currently we render:
โโโ FizzBuzz.fun:4:13 โโโ
โ
4 โ fizzโ num = case (mod num 5) (mod num 3) of
5 โ 0 0 => "FizzBuzz"
6 โ 0 _ => "Fizz"
7 โ _ 0 => "Buzz"
8 โ _ _ => num
โ -------------- `case` clauses have incompatible types
โ
Instead, it might be nice if we followed rustc's lead, and tried to mark around the region instead:
โโโ FizzBuzz.fun:4:13 โโโ
โ
4 โ fizzโ num = case (mod num 5) (mod num 3) of
โ โญโโโโโโโโโโโโโโฏ
5 โ โ 0 0 => "FizzBuzz"
6 โ โ 0 _ => "Fizz"
7 โ โ _ 0 => "Buzz"
8 โ โ _ _ => num
โ โฐโโโโโโโโโโโโโโโฏ `case` clauses have incompatible types
โ
Eventually we could try to combine errors like so:
error[E0308]: `case` clauses have incompatible types
โโโ FizzBuzz.fun:8:12 โโโ
โ
3 โ fizzโ : Nat โ String
โ ------ expected type `String` found here
4 โ fizzโ num = case (mod num 5) (mod num 3) of
โ โญโโโโโโโโโโโโโโฏ
5 โ โ 0 0 => "FizzBuzz"
6 โ โ 0 _ => "Fizz"
7 โ โ _ 0 => "Buzz"
8 โ โ _ _ => num
โ โ ^^^ expected `String`, found `Nat`
โ โฐโโโโโโโโโโโโโโโฏ `case` clauses have incompatible types
โ
= expected type `String`
found type `Nat`
A usage guide / book for codespan
would go a long way, I believe.
I am using gluon
and pikelet
as references but am having a difficult time figuring out how to correctly use this library (lifetime/ownership issues, some specifically regarding FileMap
).
The two reference projects I mentioned make use of lalrpop
which may make using codespan
easier but I do not want to use lalrpop
so I feel like I'm stuck out at sea here.
@brendanzab, when you get a chance it would be great to get a patch release for the bugfix in #139 (comment). Thanks!
cc @ebkalderon
There appears to be an issue when rendering diagnostics' line numbers with codespan
0.5. The output looks something like this:
error: unterminated block comment
โโโ stdin:8:5 โโโ
โ
8 โ โญ /*
9 โ โ buildInputs = [
10 โ โ (nixpkgs.rustChannelOf { channel = "stable"; }).rust
11 โ โ # nixpkgs.darwin.apple_sdk.frameworks.Security
12 โ โ # nixpkgs.darwin.apple_sdk.frameworks.CoreServices
13 โ โ nixpkgs.jq
14 โ โ nixpkgs.sqlite
15 โ โ ];
16 โ โ }
17 โ โ
โ โฐ^ unterminated block comment
โ
The line numbers are aligned to the left instead of the right, thereby breaking the ASCII art.
It would be nice to have a clean way of building language servers using codespan. I'm not sure how much of it we will be able to automate though, and how much would be up to the user to build. At least having some nice serializers for the diagnostics would be handy.
The LSP defines some incremental update types for working with text documents:
These are used in the WorkspaceEdit
type. It would be nice to provide analogues to these operations in our Files
type.
Lots of work has been done to improve the API in the time since the last release. What do we need to do to get v0.4.0 out the door?
cc. @Marwes
Currently, calling Files.add(name, file) twice in a row will duplicate the entire file, wasting memory. It would be nice to deduplicate by only storing the file once. This could be done by storing a HashMap<name, FileID> which returns the id if the file already exists.
This would mean that users can no longer add two different files with the same name, but I think that's fine as long as it's documented.
Currently I'm not doing much about this. Here are some options
I'd also like to be able to support domain-specific errors, such as those produced by LALRPOP - at least providing an escape hatch to allow them to be easily created!
Here are some examples of what the output might look like:
calc.lalrpop:6:5: 6:34: Ambiguous grammar detected
The following symbols can be reduced in two ways:
Expr "*" Expr "*" Expr
They could be reduced like so:
Expr "*" Expr "*" Expr
โโExprโโโโโโโ โ
โโExprโโโโโโโโโโโโโโโโ
Alternatively, they could be reduced like so:
Expr "*" Expr "*" Expr
โ โโExprโโโโโโโค
โโExprโโโโโโโโโโโโโโโโ
Hint: This looks like a precedence error related to `Expr`. See the LALRPOP
manual for advice on encoding precedence.
It would be nice to show how codespan can be used in conjunction with other libraries. Some ones we might want to show are:
We get a number of requests (#172, #179) about Salsa, so we could do with a simple example showing how we recommend using it with codespan-reporting!
Many people get started building languages using parser generators, so showing them how to get codespan hooked up to these would be very useful!
It would be nice to have an example of pretty printing types, with source code highlighting, and potentially nifty things like type diffs.
This is a bit of an edge case ... In C there is a #line
macro that allows setting the current line number, it looks like this:
void f();
int main() {
#line 1
return f();
}
<stdin>:1:12: error: returning 'void' from a function with incompatible result type
'int'
Note how the error message says the error occurs on line 1, not line 4.
Currently, it looks like the lines in a file are calculated once and can't be modified. Would it be possible to make the line numbers configurable?
This has the unfortunate complication that there's no longer a 1-1 mapping between the line numbers displayed to the user and the lines in a file: in my example above, there are 2 lines with line number 1. However, this corresponds pretty closely to the current difference between LineIndex
(internal) and LineNumber
(user-facing) so I don't forsee it being a giant deal.
@wycats, @nikomatsakis and @jonathandturner have done some great work in building on top of our work in codespan in their branch here as part of their work on Lark. It builds on top of the work of codespan, making it more configurable through the use of traits, and less confusing by using file ids. I've been using it in https://github.com/brendanzab/rust-nbe-for-mltt and have been quite happy!
The question though is: where to from here? Should we close down this repository and direct people to @wycats' project? Or should we try to take on the changes into this repository? I'm still a bit hesitant to take on the render-tree dependency, as I don't fully understand it yet.
Thoughts?
Useful PRs that might need to be up-streamed:
If I'm not mistaken, ByteIndex
uses 1-based indexing, but that is never mentioned in the docs. This should probably be documented, since I would have expected it to be 0-based.
I'd happily submit a PR with some changes :)
The lsp-types
crate has been recently bumped to 0.70.2, but the latest codespan-lsp
0.7.0 is apparently still stuck on version 0.68. This means that users of codespan-lsp
do not have access to the latest features of the specification and LSP library authors mindful of codespan-lsp
cannot upgrade their dependency on lsp-types
to 0.70.X without breaking downstream code that relies both crates.
Because of this, it would be great if there could be a new release of codespan-lsp
0.8.0 to Crates.io, so the two crate ecosystems do not diverge too far.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.