#![forbid(unsafe_code)]
pub mod cursor;
pub mod editor;
pub mod rope;
pub mod segment;
pub mod text;
pub mod view;
pub mod width_cache;
pub mod wrap;
pub mod hyphenation;
pub mod incremental_break;
#[cfg(feature = "markup")]
pub mod markup;
#[cfg(feature = "bidi")]
pub mod bidi;
#[cfg(feature = "normalization")]
pub mod normalization;
pub mod cluster_map;
pub mod justification;
pub mod layout_policy;
pub mod script_segmentation;
pub mod search;
pub mod shaped_render;
pub mod shaping;
pub mod shaping_fallback;
pub mod tier_budget;
pub mod vertical_metrics;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct TextMeasurement {
pub minimum: usize,
pub maximum: usize,
}
impl TextMeasurement {
pub const ZERO: Self = Self {
minimum: 0,
maximum: 0,
};
pub fn union(self, other: Self) -> Self {
Self {
minimum: self.minimum.max(other.minimum),
maximum: self.maximum.max(other.maximum),
}
}
pub fn stack(self, other: Self) -> Self {
Self {
minimum: self.minimum.saturating_add(other.minimum),
maximum: self.maximum.saturating_add(other.maximum),
}
}
pub fn clamp(self, min_width: Option<usize>, max_width: Option<usize>) -> Self {
let mut result = self;
if let Some(min_w) = min_width {
result.minimum = result.minimum.max(min_w);
result.maximum = result.maximum.max(min_w);
}
if let Some(max_w) = max_width {
result.minimum = result.minimum.min(max_w);
result.maximum = result.maximum.min(max_w);
}
result
}
}
pub use cluster_map::{ClusterEntry, ClusterMap};
pub use cursor::{CursorNavigator, CursorPosition};
pub use editor::{Editor, Selection};
pub use hyphenation::{
HyphenBreakPoint, HyphenationDict, HyphenationPattern, PatternTrie, break_penalties,
compile_pattern, english_dict_mini,
};
pub use incremental_break::{BreakerSnapshot, EditEvent, IncrementalBreaker, ReflowResult};
pub use justification::{
GlueSpec, JustificationControl, JustifyMode, SUBCELL_SCALE, SpaceCategory, SpacePenalty,
};
pub use layout_policy::{LayoutPolicy, LayoutTier, PolicyError, ResolvedPolicy, RuntimeCapability};
pub use rope::Rope;
pub use script_segmentation::{
RunCacheKey, RunDirection, Script, ScriptRun, TextRun, partition_by_script, partition_text_runs,
};
pub use segment::{ControlCode, Segment, SegmentLine, SegmentLines, join_lines, split_into_lines};
pub use shaped_render::{CellPlacement, RenderHint, ShapedLineLayout, SpacingDelta};
pub use shaping::{
FontFeature, FontFeatures, FontId, NoopShaper, ShapedGlyph, ShapedRun, ShapingCache,
ShapingCacheStats, ShapingKey, TextShaper,
};
pub use shaping_fallback::{FallbackEvent, FallbackStats, LigatureMode, ShapingFallback};
pub use text::{Line, Span, Text};
pub use tier_budget::{
FrameBudget, MemoryBudget, QueueBudget, SafetyInvariant, TierBudget, TierFeatures, TierLadder,
};
pub use vertical_metrics::{
BaselineGrid, LeadingSpec, ParagraphSpacing, VerticalMetrics, VerticalPolicy,
};
pub use view::{TextView, ViewLine, Viewport};
pub use width_cache::{
CacheStats, CountMinSketch, DEFAULT_CACHE_CAPACITY, Doorkeeper, S3FifoWidthCache,
TinyLfuWidthCache, WidthCache,
};
pub use wrap::{
KpBreakResult, WrapMode, WrapOptions, ascii_width, display_width, grapheme_count,
grapheme_width, graphemes, has_wide_chars, is_ascii_only, truncate_to_width,
truncate_to_width_with_info, truncate_with_ellipsis, word_boundaries, word_segments,
wrap_optimal, wrap_text, wrap_text_optimal, wrap_with_options,
};
#[cfg(feature = "markup")]
pub use markup::{MarkupError, MarkupParser, parse_markup};
#[cfg(feature = "normalization")]
pub use normalization::{NormForm, eq_normalized, is_normalized, normalize, normalize_for_search};
#[cfg(feature = "normalization")]
pub use search::{
PolicySearchResult, SearchPolicy, search_case_insensitive, search_normalized,
search_with_policy,
};
pub use search::{
SearchResult, WidthMode, display_col_at, search_ascii_case_insensitive, search_exact,
search_exact_overlapping,
};
#[cfg(test)]
mod measurement_tests {
use super::TextMeasurement;
#[test]
fn union_uses_max_bounds() {
let a = TextMeasurement {
minimum: 2,
maximum: 8,
};
let b = TextMeasurement {
minimum: 4,
maximum: 6,
};
let merged = a.union(b);
assert_eq!(
merged,
TextMeasurement {
minimum: 4,
maximum: 8
}
);
}
#[test]
fn stack_adds_bounds() {
let a = TextMeasurement {
minimum: 1,
maximum: 5,
};
let b = TextMeasurement {
minimum: 2,
maximum: 7,
};
let stacked = a.stack(b);
assert_eq!(
stacked,
TextMeasurement {
minimum: 3,
maximum: 12
}
);
}
#[test]
fn clamp_enforces_min() {
let measurement = TextMeasurement {
minimum: 2,
maximum: 6,
};
let clamped = measurement.clamp(Some(5), None);
assert_eq!(
clamped,
TextMeasurement {
minimum: 5,
maximum: 6
}
);
}
#[test]
fn clamp_enforces_max() {
let measurement = TextMeasurement {
minimum: 4,
maximum: 10,
};
let clamped = measurement.clamp(None, Some(6));
assert_eq!(
clamped,
TextMeasurement {
minimum: 4,
maximum: 6
}
);
}
#[test]
fn clamp_preserves_ordering() {
let measurement = TextMeasurement {
minimum: 3,
maximum: 5,
};
let clamped = measurement.clamp(Some(7), Some(4));
assert!(clamped.minimum <= clamped.maximum);
assert_eq!(clamped.minimum, 4);
assert_eq!(clamped.maximum, 4);
}
#[test]
fn zero_constant() {
assert_eq!(TextMeasurement::ZERO.minimum, 0);
assert_eq!(TextMeasurement::ZERO.maximum, 0);
}
#[test]
fn default_is_zero() {
let m = TextMeasurement::default();
assert_eq!(m, TextMeasurement::ZERO);
}
#[test]
fn union_with_zero_is_identity() {
let m = TextMeasurement {
minimum: 5,
maximum: 10,
};
assert_eq!(m.union(TextMeasurement::ZERO), m);
assert_eq!(TextMeasurement::ZERO.union(m), m);
}
#[test]
fn stack_with_zero_is_identity() {
let m = TextMeasurement {
minimum: 5,
maximum: 10,
};
assert_eq!(m.stack(TextMeasurement::ZERO), m);
assert_eq!(TextMeasurement::ZERO.stack(m), m);
}
#[test]
fn stack_saturates_on_overflow() {
let big = TextMeasurement {
minimum: usize::MAX - 1,
maximum: usize::MAX,
};
let one = TextMeasurement {
minimum: 5,
maximum: 5,
};
let stacked = big.stack(one);
assert_eq!(stacked.maximum, usize::MAX);
}
#[test]
fn clamp_no_constraints() {
let m = TextMeasurement {
minimum: 3,
maximum: 7,
};
let clamped = m.clamp(None, None);
assert_eq!(clamped, m);
}
#[test]
fn clamp_min_raises_both_bounds() {
let m = TextMeasurement {
minimum: 1,
maximum: 2,
};
let clamped = m.clamp(Some(5), None);
assert_eq!(clamped.minimum, 5);
assert_eq!(clamped.maximum, 5);
}
#[test]
fn union_is_commutative() {
let a = TextMeasurement {
minimum: 2,
maximum: 8,
};
let b = TextMeasurement {
minimum: 4,
maximum: 6,
};
assert_eq!(a.union(b), b.union(a));
}
#[test]
fn stack_is_commutative() {
let a = TextMeasurement {
minimum: 2,
maximum: 8,
};
let b = TextMeasurement {
minimum: 4,
maximum: 6,
};
assert_eq!(a.stack(b), b.stack(a));
}
}