Skip to content

Add break_line_with_next#575

Merged
taj-p merged 1 commit intolinebender:mainfrom
taj-p:tajp/breakNextWithLength
Mar 17, 2026
Merged

Add break_line_with_next#575
taj-p merged 1 commit intolinebender:mainfrom
taj-p:tajp/breakNextWithLength

Conversation

@taj-p
Copy link
Contributor

@taj-p taj-p commented Mar 17, 2026

We have been using this break_line_with_next breaking algorithm in production for a couple months now. The use case is that sometimes the consumer knows how many characters they want to break each line at (rather than specifying a width by some pixel dimension). (It's also significantly faster than the current implementation for obvious reasons - no need to calculate and bookkeep intermediary line length state).

Happy to change the name to something like break_line_with_max_chars or similar.

See https://xi.zulipchat.com/#narrow/channel/205635-parley/topic/Floated.20boxes/near/564353468

@taj-p taj-p requested a review from conor-93 March 17, 2026 22:35
Copy link
Contributor

@conor-93 conor-93 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this!

@taj-p taj-p added this pull request to the merge queue Mar 17, 2026
Copy link
Collaborator

@nicoburns nicoburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether it would make more sense for this to use a separate struct rather than BreakLines. And it generally seems like it ought to be possible to simplify the code for this mode. But this seems basically fine.

}

#[inline]
fn resolve_indent(&mut self) -> f32 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be &mut?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! Oops!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised the linter didn't pick this one up

env.check_layout_snapshot(&layout);
}

/// This test verifies that breaking in the middle of a ligature produces valid glyphs.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test verifies that breaking in the middle of a ligature produces valid glyphs.

It sounds like it actually verifies that breaking in the middle of a ligature doesn't produce valid glyphs? In the other line breaker we just break early in leiu of reshaping. I guess you have reasons for not wanting to do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! My bad on this phrasing.

In the other line breaker we just break early in leiu of reshaping.

👀 ! Maybe not for long! There is work being planned to make this more correct - to reshape after breaking if it is required (for Arabic / ligatures / and so on)!

I guess you have reasons for not wanting to do that?

We actually don't like this behaviour - we want to reshape after breaking, but that isn't currently supported

use crate::util::TestEnv;
use parley::{Alignment, AlignmentOptions, InlineBox, PositionedLayoutItem, StyleProperty};

#[test]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to see a test that includes some emoji. They seems like a case that might easily break if not handled correctly.

if self.done {
return None;
}
self.prev_state = Some(self.state.clone());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? I would expect this line breaker to never need to revert?

let item = &self.layout.data.items[self.state.item_idx];

match item.kind {
LayoutItemKind::InlineBox => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does supporting inline boxes in this mode make sense? (are you using them?) I guess if you carefully control the size of the boxes it would work decently well..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not using them. Happy to change this behaviour to something else?

Copy link
Collaborator

@nicoburns nicoburns Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be tempted to ignore them entirely in this mode. But I guess it's also harmless.


// Compute the x position.
// Newlines don't contribute to line width (matching break_next behavior).
let next_x = if is_newline {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to strip out newlines before passing the string into Parley? That's what Blitz is doing. I guess there's a general conversation to be had about whitespace collapsing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I'm not sure TBH! We might need to talk about it

Merged via the queue into linebender:main with commit 2a343dc Mar 17, 2026
24 checks passed
@taj-p taj-p deleted the tajp/breakNextWithLength branch March 17, 2026 23:53
github-merge-queue bot pushed a commit that referenced this pull request Mar 18, 2026
Please see Nico's great review here
#575 (review)
🙏
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants