Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
Spu7Nix opened this issue Sep 10, 2021 · 14 comments
Closed

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

Spu7Nix opened this issue Sep 10, 2021 · 14 comments
Labels
enhancement New feature or request

Comments

@Spu7Nix
Copy link

Spu7Nix commented Sep 10, 2021

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.

@zesterer zesterer added the enhancement New feature or request label Sep 23, 2021
@bestouff bestouff mentioned this issue Oct 12, 2021
5 tasks
@danaugrs
Copy link

danaugrs commented Aug 19, 2022

I was using spans with byte-based indexing and came across this issue so I changed to character-based indexing. It actually turned out to be a bit more efficient as well.

@brockelmore
Copy link

fwiw I created a fork that does this (but removes char based version entirely) here

All it does is switch this line:

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

to:

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

I am happy to open a PR that adds a function from_with_byte_offsets(s: S) -> Self where S: AsRef<str> to Source if that is desirable

@Johan-Mi
Copy link

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

Isn't that simply line.len()?

@brockelmore
Copy link

haha TIL String::len gives the length in bytes! yes you are correct

@goto-bus-stop
Copy link
Contributor

I'm interested in working on this. @zesterer would you be open to switching entirely to byte indices? Or should both ways be supported?

@zesterer
Copy link
Owner

I'm interested in working on this. @zesterer would you be open to switching entirely to byte indices? Or should both ways be supported?

Definitely interested! In my view, spans should use byte indices by default, with some built-in way to look up character indices. I'm happy to accept a temporary solution for now: long-term, I think the crate needs more substantial changes anyway.

@VonTum
Copy link
Contributor

VonTum commented Feb 20, 2024

Is there a branch which already solves this?

@zesterer
Copy link
Owner

Not yet, no. I don't currently have the time to work on it, although I wish I did.

@VonTum
Copy link
Contributor

VonTum commented Feb 20, 2024

I see in the code it's possible to provide your own impl Cache<>, perhaps turning Source into a trait, (or of course for backward compatibility, have Source implement a new SourceTrait), which can have differing implementations such that one can also avoid the String allocations for each line, and allow byte indexing.

@zesterer
Copy link
Owner

This has been suggested elsewhere, yes. That's probably something to add to the list for an eventual refactor.

@VonTum
Copy link
Contributor

VonTum commented Feb 22, 2024

I'm getting started in a PR to implement this, which approach would you prefer @zesterer ?

  • Extend the Span trait with a fn is_byte_span(&self) -> bool, returning false by default not to break existing code, then add a ByteSpan impl Span, or
  • Add a field or template parameter to Report/ReportBuilder that sais if the given spans are byte spans.

The first is of course more general, even allowing users to combine Char and Byte spans, but requires two new structs for Span with and without SourceId.

The second is less general, requiring the user to specify it for the whole report. Which is more ergonomic, but limits freedom

@VonTum
Copy link
Contributor

VonTum commented Feb 27, 2024

Well, I've finally come to the point where I need to switch it over. I'll implement it as a boolean flag to ReportBuilder.

VonTum added a commit to VonTum/ariadne that referenced this issue Feb 29, 2024
Closes zesterer#8
Closes Duplicate issues zesterer#71 and zesterer#57
@Zollerboy1
Copy link
Contributor

Zollerboy1 commented Apr 25, 2024

Any chance that this feature could get into a patch release soon? I'm fine with putting

ariadne = { git = "https://github.com/zesterer/ariadne.git", rev = "a061071" }

in my Cargo.toml for now, but it would be much nicer if this could be version 0.4.1 instead.


EDIT:

Actually, while playing around with this feature, I discovered that the column number of the offset of a report is printed incorrectly when using byte indices. I created a PR to fix this: #113.

@zesterer
Copy link
Owner

I've just released 0.4.1, which includes these changes. Thanks @VonTum and @Zollerboy1 for the contributions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants