-
Notifications
You must be signed in to change notification settings - Fork 104
Add parlance::BidiLevel
#710
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
Changes from all commits
2d1f829
efdf1d6
0679441
02a3651
b128c3e
c2479b2
dd81509
c7923ac
e41dc53
4d40d99
8f551ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,3 +38,76 @@ pub enum BidiOverride { | |
| /// Force right-to-left. | ||
| Rtl, | ||
| } | ||
|
|
||
| /// Bidirectional text embedding level. | ||
| /// | ||
| /// These are numbers indicating how deeply bidirectional embeddings are nested in the text, and the | ||
| /// default direction of text on that level. Even levels are left-to-right, odd levels are | ||
| /// right-to-left. Normally, the minimum level is 0 (left-to-right), and the maximum level, | ||
| /// according to [UAX #9 § 3.1.1 BD2][uax-bd2], is 125. | ||
| /// | ||
| /// See [UAX #9 § 3.1][uax-definitions] for more information. | ||
| /// | ||
| /// [uax-definitions]: https://unicode.org/reports/tr9/#Definitions | ||
| /// [uax-bd2]: https://unicode.org/reports/tr9/#BD2 | ||
| /// | ||
| // NOTICE: If the representation changes, be sure to check the `bytemuck` marker trait | ||
| // implementations. | ||
| // | ||
| // TODO: it would be quite nice for this to implement | ||
| // <https://doc.rust-lang.org/stable/core/iter/trait.Step.html>, once stabilized. | ||
| #[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | ||
| #[repr(transparent)] | ||
| pub struct BidiLevel(u8); | ||
|
|
||
| impl BidiLevel { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense for max level to be stored here, either a u8 or BidiLevel constant?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds sensible. I've added In particular, it's not the greatest value |
||
| /// The maximum bidirectional text embedding level, according to [UAX #9 § 3.1.1 BD2][uax-bd2]. | ||
| /// | ||
| /// It is possible for `BidiLevel` to encode greater values; in particular, `unsafe` code **must | ||
| /// not** rely on `BidiLevel` never being greater than this. | ||
| /// | ||
| /// [uax-bd2]: https://unicode.org/reports/tr9/#BD2 | ||
| pub const MAX: Self = Self(125); | ||
|
|
||
| /// Construct a new bidi level. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd claim that we should debug assert that this is less than or equal to 125, but not blockingly so.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps that makes sense. This ties back into #710 (comment). Deferring this for now. |
||
| #[inline(always)] | ||
| pub const fn new(level: u8) -> Self { | ||
| Self(level) | ||
| } | ||
|
|
||
| /// Get the numeric bidi level. | ||
| #[inline(always)] | ||
| pub const fn to_u8(self) -> u8 { | ||
| self.0 | ||
| } | ||
|
|
||
| /// Whether this level is left-to-right. | ||
| #[inline(always)] | ||
| pub const fn is_ltr(self) -> bool { | ||
| self.0.is_multiple_of(2) | ||
| } | ||
|
|
||
| /// Whether this level is right-to-left. | ||
| #[inline(always)] | ||
| pub const fn is_rtl(self) -> bool { | ||
| !self.is_ltr() | ||
| } | ||
|
|
||
| /// Get the next odd bidi level. | ||
| /// | ||
| /// When the return value overflows (`self.to_u8() >= 255`) this panics when overflow checks are | ||
| /// enabled. Otherwise, the return value wraps. | ||
| #[inline(always)] | ||
| pub const fn next_odd(self) -> Self { | ||
| Self::new((self.to_u8() + 1) | 1) | ||
| } | ||
|
|
||
| /// Get the next even bidi level. | ||
| /// | ||
| /// When the return value overflows (`self.to_u8() >= 254`) this panics when overflow checks are | ||
| /// enabled. Otherwise, the return value wraps. | ||
| #[inline(always)] | ||
| pub const fn next_even(self) -> Self { | ||
| Self::new((self.to_u8() + 2) & !1) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,8 @@ | |
| reason = "The `bytemuck` marker traits are `unsafe` and require `unsafe impl`." | ||
| )] | ||
|
|
||
| use crate::GenericFamily; | ||
| use bytemuck::{Contiguous, NoUninit, Zeroable, checked::CheckedBitPattern}; | ||
| use crate::{BidiLevel, GenericFamily}; | ||
| use bytemuck::{Contiguous, NoUninit, Pod, Zeroable, checked::CheckedBitPattern}; | ||
|
|
||
| // Safety: The enum is `repr(u8)` and has only fieldless variants. | ||
| unsafe impl NoUninit for GenericFamily {} | ||
|
|
@@ -39,12 +39,21 @@ unsafe impl Contiguous for GenericFamily { | |
| const MAX_VALUE: u8 = GenericFamily::MAX_VALUE; | ||
| } | ||
|
|
||
| // Safety: The struct is `repr(transparent)`, wrapping a `u8`. | ||
| // | ||
| // While generally BidiLevels have a maximum of 125, no value is unsound. | ||
| unsafe impl Pod for BidiLevel {} | ||
|
|
||
| // Safety: The struct is `repr(transparent)`, wrapping a `u8`. | ||
| unsafe impl Zeroable for BidiLevel {} | ||
|
Comment on lines
+42
to
+48
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably should have the usual safety tests for these. But also this is so simple it seems hard to imagine it going wrong! I'm not even sure what the tests would look like? Maybe even just that the size is 1 to force this to be revisited?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added the size test. I'm also not sure whether we can do better. |
||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::GenericFamily; | ||
| use bytemuck::{Contiguous, Zeroable, checked::try_from_bytes}; | ||
| use core::ptr; | ||
|
|
||
| use super::{BidiLevel, GenericFamily}; | ||
|
|
||
| #[test] | ||
| fn checked_bit_pattern() { | ||
| let valid = bytemuck::bytes_of(&2_u8); | ||
|
|
@@ -86,6 +95,16 @@ mod tests { | |
| value += 1; | ||
| } | ||
| }; | ||
|
|
||
| /// Tests that [`BidiLevel`] is one byte. | ||
| /// | ||
| /// That may catch its representation changing, in which case the implementations here | ||
| /// definitely need revisiting. | ||
| const _: () = { | ||
| if size_of::<BidiLevel>() != 1 { | ||
| panic!("`BidiLevel` is not one byte"); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| #[cfg(doctest)] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,7 +110,7 @@ impl<'a, B: Brush> Run<'a, B> { | |
|
|
||
| /// Returns `true` if the run has right-to-left directionality. | ||
| pub fn is_rtl(&self) -> bool { | ||
| self.shaped.bidi_level & 1 != 0 | ||
| self.shaped.bidi_level.is_rtl() | ||
| } | ||
|
Comment on lines
111
to
114
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess nominally it might be better to just return the bidi level here, but that's not done here to avoid breaking changes?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I intentionally did not change the That said, we probably should break it. (But not in this PR.) |
||
|
|
||
| /// Returns the cluster range for the run. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incidentally, this being 125 does give us a bit to muck about with. I don't know of any use for that (maybe for whether rule L1 would apply to this?).