Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
eee5442
Start on reshaping
tomcur Sep 14, 2026
01d0356
Implement `shape_like_run`
tomcur Oct 5, 2026
add1931
Edit::new
tomcur Oct 5, 2026
aa94412
Allow `process_shaped_clusters` to process reshaped clusters
tomcur Oct 5, 2026
f945e6d
Implement `ShapedText::reshape`, extract some helpers
tomcur Oct 14, 2026
db3ea5c
Implement `ReshapedText::run_slice`
tomcur Oct 15, 2026
d63b24b
Impl reshaping scans
tomcur Oct 14, 2026
4bc0236
Export
tomcur Oct 15, 2026
956ba5c
Remove unused
tomcur Oct 15, 2026
c134f7c
Docs
tomcur Oct 15, 2026
fb0b841
Rename and document `SpliceMode` variants
tomcur Oct 15, 2026
6b783da
Docs
tomcur Oct 15, 2026
cec245a
Add some accessors
tomcur Oct 15, 2026
22e0d2e
Docs
tomcur Oct 15, 2026
b9530ac
Simplify splicing
tomcur Oct 15, 2026
8cda04e
More splicing simplification
tomcur Oct 15, 2026
f84c478
More consistent wording
tomcur Oct 15, 2026
373c987
Use a single index space for clusters
tomcur Oct 15, 2026
2553221
Document
tomcur Oct 16, 2026
bb050ba
Stop trimming for now
tomcur Oct 16, 2026
ed00f15
Use safe-to-break-before flags to exit early
tomcur Oct 16, 2026
b698e12
Clippy
tomcur Oct 16, 2026
142390c
Typos
tomcur Oct 16, 2026
7d11c25
Improve fast vs exact flow, and sequence such that fragment waste is low
tomcur Oct 16, 2026
086c54d
Document edge case
tomcur Oct 16, 2026
4673593
Improve panics
tomcur Oct 16, 2026
8cf807d
Add test
tomcur Oct 16, 2026
bbd4626
Move the `ShapedText` borrow out of `Reshaper`
tomcur Oct 21, 2026
27eb966
Simplify overlap check
tomcur Oct 21, 2026
71a6412
Add `ReshapedTextMark::default`
tomcur Oct 21, 2026
243097b
Fix `advance` after rebase
tomcur Oct 26, 2026
dcd6ad1
State we don't split graphemes when reshaping
tomcur Oct 26, 2026
b5973ba
Store text in `Layout`
tomcur Oct 21, 2026
adb46bd
Store `ReshapedText` in `LayoutData`
tomcur Oct 21, 2026
db04032
Enable `Run` to take shaped clusters from a `ReshapedText`
tomcur Oct 22, 2026
89b6b58
Add `Reshaper` to breaker state
tomcur Oct 22, 2026
46af138
Take the reshaped start advance delta into account while measuring li…
tomcur Oct 22, 2026
242146c
Reshape lines when not safe to break
tomcur Oct 22, 2026
41d5429
Tune performance
tomcur Oct 23, 2026
64f6102
Simplify handling of overflowing after reshape
tomcur Oct 24, 2026
be15b27
Clean up
tomcur Oct 25, 2026
2cb2e94
Prune test
tomcur Oct 25, 2026
81f6235
Improve naming
tomcur Oct 25, 2026
3512157
Don't account advance delta from reshaping hanging whitespace
tomcur Oct 25, 2026
bb1ac7a
Ensure `append_atom_to_line` is inlined
tomcur Oct 25, 2026
faed8a0
Simplify step back logic given we can ignore hanging whitespace deltas
tomcur Oct 25, 2026
11cba53
Prune comment
tomcur Oct 25, 2026
944f98e
Improve naming
tomcur Oct 25, 2026
947908e
Typo
tomcur Oct 25, 2026
90b9584
Perf
tomcur Oct 25, 2026
6a0c48d
Put main's `clone_from` back (it conflicted in a few commits)
tomcur Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion parley/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ fn build_into_layout<B: Brush>(
layout.data.scale = options.scale;
layout.data.quantize = options.quantize;
layout.data.base_level = lcx.analysis.paragraph_level();
layout.data.text_len = text.len();
layout.data.text.push_str(text);

// Characters not covered by any style run (e.g. the space substituted for empty text during
// analysis) use the root style.
Expand Down
4 changes: 2 additions & 2 deletions parley/src/editing/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Cursor {
}
} else {
Self {
index: layout.data.text_len,
index: layout.data.text.len(),
affinity: Affinity::Upstream,
}
}
Expand All @@ -55,7 +55,7 @@ impl Cursor {
}
}
} else {
(layout.data.text_len, Affinity::Downstream)
(layout.data.text.len(), Affinity::Downstream)
};
Self { index, affinity }
}
Expand Down
2 changes: 1 addition & 1 deletion parley/src/editing/selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl Selection {
h_pos: None,
}
} else {
Cursor::from_byte_index(layout, layout.data.text_len, Affinity::Upstream).into()
Cursor::from_byte_index(layout, layout.data.text.len(), Affinity::Upstream).into()
}
}

Expand Down
6 changes: 3 additions & 3 deletions parley/src/layout/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@ impl<'a, B: Brush> Cluster<'a, B> {
pub(crate) fn first_character(&self) -> &'a Character {
&self
.run
.full_slice()
.line_slice()
.characters_in(self.grapheme.char_range())[0]
}

/// Returns the range of text that defines the cluster.
pub fn text_range(&self) -> Range<usize> {
self.run
.full_slice()
.line_slice()
.text_byte_range(self.grapheme.char_range())
}

Expand Down Expand Up @@ -305,7 +305,7 @@ impl<'a, B: Brush> Cluster<'a, B> {
Some(next)
} else {
let index = self.text_range().end;
if index >= self.run.layout.data.text_len {
if index >= self.run.layout.data.text.len() {
return None;
}
// We have to search for the cluster containing our end index
Expand Down
37 changes: 31 additions & 6 deletions parley/src/layout/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ use crate::style::Brush;
use crate::{IndentOptions, LineHeight, OverflowWrap, TextWrapMode, WhiteSpaceCollapse};
use core::ops::Range;

use alloc::string::String;
use alloc::vec::Vec;
use parlance::BidiLevel;
use parley_engine::shape::Whitespace;
use parley_engine::shape::{ReshapedText, Whitespace};
use parley_engine::{ShapedRun, ShapedSlice, ShapedText};

/// `HarfRust`-based run data
Expand Down Expand Up @@ -157,17 +158,37 @@ pub(crate) struct LineItemData {
// TODO: factor this out?
/// Range of the source text.
pub(crate) text_range: Range<usize>,
/// This run's shaped clusters on this line, as a range into [`ShapedText::shaped_clusters`].
/// This run's shaped clusters on this line, as a range into [`ShapedText::shaped_clusters`], or
/// into [`LayoutData::reshaped`] if [`Self::reshaped_run`] is set.
///
/// The bounds are atom-aligned.
pub(crate) shaped_cluster_range: Range<u32>,
/// If `Some`, this is item is a reshaped run, and this is that run's index into
/// [`LayoutData::reshaped`].
///
/// [`Self::index`] is the index of the run it was reshaped from.
///
/// This will be set for lines whose break falls at a position that is not
/// [`parley_engine::shape::ShapedCluster::is_safe_to_break_before`].
pub(crate) reshaped_run: Option<u32>,
}

impl LineItemData {
#[inline(always)]
pub(crate) fn is_rtl(&self) -> bool {
self.bidi_level.is_rtl()
}

/// This item's shaped clusters.
#[inline(always)]
#[expect(clippy::cast_possible_truncation, reason = "deferred")]
pub(crate) fn slice<'a, B: Brush>(&self, data: &'a LayoutData<B>) -> ShapedSlice<'a> {
match self.reshaped_run {
Some(reshaped_run) => data.reshaped.run_slice(&data.shaped_text, reshaped_run),
None => data.shaped_text.run_slice(self.index as u32),
}
.narrow(self.shaped_cluster_range.clone())
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -195,8 +216,8 @@ pub(crate) struct LayoutData<B: Brush> {
pub(crate) quantize: bool,
/// The `BiDi` base level
pub(crate) base_level: BidiLevel,
/// The length of the text in the layout
pub(crate) text_len: usize,
/// The text of the layout
pub(crate) text: String,

// Output of style resolution (input to line breaking)
pub(crate) styles: Vec<Style<B>>,
Expand All @@ -213,6 +234,8 @@ pub(crate) struct LayoutData<B: Brush> {
pub(crate) lines: Vec<LineData>,
/// Items within each line
pub(crate) line_items: Vec<LineItemData>,
/// Reshaped runs of lines
pub(crate) reshaped: ReshapedText,
/// Position of each aligned subtree rooted at a `vertical-align: top | bottom` style on each line.
/// The root aligned subtree of each line doesn't have an entry as its offset is trivially zero.
///
Expand Down Expand Up @@ -242,7 +265,7 @@ impl<B: Brush> Default for LayoutData<B> {
scale: 1.,
quantize: true,
base_level: BidiLevel::new(0),
text_len: 0,
text: String::new(),
width: 0.,
full_width: 0.,
height: 0.,
Expand All @@ -254,6 +277,7 @@ impl<B: Brush> Default for LayoutData<B> {
items: Vec::new(),
lines: Vec::new(),
line_items: Vec::new(),
reshaped: ReshapedText::new(),
aligned_subtree_offsets: Vec::new(),
alignment: None,
layout_max_advance: 0.0,
Expand All @@ -268,7 +292,7 @@ impl<B: Brush> LayoutData<B> {
self.scale = 1.;
self.quantize = true;
self.base_level = BidiLevel::new(0);
self.text_len = 0;
self.text.clear();
self.width = 0.;
self.full_width = 0.;
self.height = 0.;
Expand All @@ -283,6 +307,7 @@ impl<B: Brush> LayoutData<B> {
self.items.clear();
self.lines.clear();
self.line_items.clear();
self.reshaped.clear();
self.aligned_subtree_offsets.clear();
self.alignment = None;
}
Expand Down
4 changes: 2 additions & 2 deletions parley/src/layout/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl<B: Brush> fmt::Debug for Layout<B> {
f.debug_struct("Layout").field("data", &self.data).finish()
} else {
f.debug_struct("Layout")
.field("text_len", &self.data.text_len)
.field("text_len", &self.data.text.len())
.field("width", &self.data.width)
.field("height", &self.data.height)
.field("lines", &self.data.lines.len())
Expand Down Expand Up @@ -85,7 +85,7 @@ impl<B: Brush> Layout<B> {

/// Returns the length of the source text, in bytes.
pub fn text_len(&self) -> usize {
self.data.text_len
self.data.text.len()
}

/// Returns the alignment that was applied to the layout by [`Layout::align`].
Expand Down
Loading
Loading