summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/library/text/par.rs54
-rw-r--r--src/library/text/shaping.rs11
-rw-r--r--src/util/mod.rs30
3 files changed, 50 insertions, 45 deletions
diff --git a/src/library/text/par.rs b/src/library/text/par.rs
index 57ced189..6eb3da66 100644
--- a/src/library/text/par.rs
+++ b/src/library/text/par.rs
@@ -1,13 +1,14 @@
use std::sync::Arc;
use unicode_bidi::{BidiInfo, Level};
+use unicode_script::{Script, UnicodeScript};
use xi_unicode::LineBreakIterator;
use super::{shape, Lang, ShapedText, TextNode};
use crate::font::FontStore;
use crate::library::layout::Spacing;
use crate::library::prelude::*;
-use crate::util::{ArcExt, EcoString, SliceExt};
+use crate::util::{ArcExt, EcoString};
/// Arrange text, spacing and inline-level nodes into a paragraph.
#[derive(Hash)]
@@ -437,23 +438,46 @@ fn prepare<'a>(
_ => None,
});
- let mut items = vec![];
let mut cursor = 0;
+ let mut items = vec![];
// Layout the children and collect them into items.
for (segment, styles) in segments {
+ let end = cursor + segment.len();
match segment {
- Segment::Text(len) => {
- // TODO: Also split by script.
- let mut start = cursor;
- for (level, count) in bidi.levels[cursor .. cursor + len].group() {
- let end = start + count;
- let text = &bidi.text[start .. end];
+ Segment::Text(_) => {
+ let mut process = |text, level: Level| {
let dir = if level.is_ltr() { Dir::LTR } else { Dir::RTL };
let shaped = shape(&mut ctx.fonts, text, styles, dir);
items.push(Item::Text(shaped));
- start = end;
+ };
+
+ let mut prev_level = Level::ltr();
+ let mut prev_script = Script::Unknown;
+
+ // Group by embedding level and script.
+ for i in cursor .. end {
+ if !text.is_char_boundary(i) {
+ continue;
+ }
+
+ let level = bidi.levels[i];
+ let script =
+ text[i ..].chars().next().map_or(Script::Unknown, |c| c.script());
+
+ if level != prev_level || !is_compatible(script, prev_script) {
+ if cursor < i {
+ process(&text[cursor .. i], prev_level);
+ }
+ cursor = i;
+ prev_level = level;
+ prev_script = script;
+ } else if is_generic_script(prev_script) {
+ prev_script = script;
+ }
}
+
+ process(&text[cursor .. end], prev_level);
}
Segment::Spacing(spacing) => match spacing {
Spacing::Relative(v) => {
@@ -482,12 +506,22 @@ fn prepare<'a>(
}
}
- cursor += segment.len();
+ cursor = end;
}
Ok(Preparation { bidi, items, styles, children: &par.0 })
}
+/// Whether this is not a specific script.
+fn is_generic_script(script: Script) -> bool {
+ matches!(script, Script::Unknown | Script::Common | Script::Inherited)
+}
+
+/// Whether these script can be part of the same shape run.
+fn is_compatible(a: Script, b: Script) -> bool {
+ is_generic_script(a) || is_generic_script(b) || a == b
+}
+
/// Find suitable linebreaks.
fn linebreak<'a>(
p: &'a Preparation<'a>,
diff --git a/src/library/text/shaping.rs b/src/library/text/shaping.rs
index 0a480c83..72f86a38 100644
--- a/src/library/text/shaping.rs
+++ b/src/library/text/shaping.rs
@@ -12,7 +12,6 @@ use crate::util::SliceExt;
/// This type contains owned or borrowed shaped text runs, which can be
/// measured, used to reshape substrings more quickly and converted into a
/// frame.
-#[derive(Debug, Clone)]
pub struct ShapedText<'a> {
/// The text that was shaped.
pub text: &'a str,
@@ -269,11 +268,13 @@ impl<'a> ShapedText<'a> {
// RTL needs offset one because the left side of the range should be
// exclusive and the right side inclusive, contrary to the normal
// behaviour of ranges.
- if !ltr {
- idx += 1;
- }
+ self.glyphs[idx].safe_to_break.then(|| idx + (!ltr) as usize)
+ }
+}
- self.glyphs[idx].safe_to_break.then(|| idx)
+impl Debug for ShapedText<'_> {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.text.fmt(f)
}
}
diff --git a/src/util/mod.rs b/src/util/mod.rs
index e42d0664..d898f545 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -103,12 +103,6 @@ where
/// Additional methods for slices.
pub trait SliceExt<T> {
- /// Find consecutive runs of the same elements in a slice and yield for
- /// each such run the element and number of times it appears.
- fn group(&self) -> Group<'_, T>
- where
- T: PartialEq;
-
/// Split a slice into consecutive runs with the same key and yield for
/// each such run the key and the slice of elements with that key.
fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F>
@@ -118,35 +112,11 @@ pub trait SliceExt<T> {
}
impl<T> SliceExt<T> for [T] {
- fn group(&self) -> Group<'_, T> {
- Group { slice: self }
- }
-
fn group_by_key<K, F>(&self, f: F) -> GroupByKey<'_, T, F> {
GroupByKey { slice: self, f }
}
}
-/// This struct is created by [`SliceExt::group`].
-pub struct Group<'a, T> {
- slice: &'a [T],
-}
-
-impl<'a, T> Iterator for Group<'a, T>
-where
- T: PartialEq,
-{
- type Item = (&'a T, usize);
-
- fn next(&mut self) -> Option<Self::Item> {
- let mut iter = self.slice.iter();
- let first = iter.next()?;
- let count = 1 + iter.take_while(|&t| t == first).count();
- self.slice = &self.slice[count ..];
- Some((first, count))
- }
-}
-
/// This struct is created by [`SliceExt::group_by_key`].
pub struct GroupByKey<'a, T, F> {
slice: &'a [T],