use iced::advanced::graphics::text::cosmic_text;
use iced::{Color, Rectangle};
#[must_use]
pub fn font_metrics() -> cosmic_text::Metrics {
cosmic_text::Metrics::relative(14.0, 1.3)
}
pub(crate) const MAX_HIGHLIGHT_SIZE: usize = 10 * 1024 * 1024;
pub(crate) const GUTTER_FONT_SIZE: f32 = 11.0;
pub(crate) const MAX_VISUAL_LINES_PER_SOURCE: usize = 10_000;
#[allow(clippy::cast_precision_loss)]
pub(crate) fn compute_total_height(
buffer: &mut cosmic_text::Buffer,
font_sys: &mut cosmic_text::FontSystem,
metrics: cosmic_text::Metrics,
) -> f32 {
let mut total_visual_lines: f32 = 0.0;
for i in 0..buffer.lines.len() {
let visual_count = buffer
.line_layout(font_sys, i)
.map_or(1, |ll| ll.len().min(MAX_VISUAL_LINES_PER_SOURCE));
total_visual_lines += visual_count as f32;
}
total_visual_lines * metrics.line_height
}
pub(crate) fn text_area_rect(bounds: Rectangle, padding: f32, gutter_width: f32) -> Rectangle {
let x = bounds.x + padding + gutter_width + 4.0; let y = bounds.y + padding;
let width = (bounds.width - (x - bounds.x) - padding).max(0.0);
let height = (bounds.height - padding * 2.0).max(0.0);
Rectangle {
x,
y,
width,
height,
}
}
pub(crate) fn gutter_clip_rect(
bounds: Rectangle,
padding: f32,
gutter_width: f32,
text_area_height: f32,
) -> Rectangle {
Rectangle {
x: bounds.x + padding,
y: bounds.y + padding,
width: gutter_width,
height: text_area_height,
}
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(crate) fn iced_color_to_cosmic(c: Color) -> cosmic_text::Color {
let r = (c.r * 255.0).round() as u8;
let g = (c.g * 255.0).round() as u8;
let b = (c.b * 255.0).round() as u8;
cosmic_text::Color::rgb(r, g, b)
}
pub(crate) fn push_or_merge<'a>(
text: &'a str,
result: &mut Vec<(&'a str, cosmic_text::Attrs<'a>)>,
new_text: &'a str,
new_attrs: cosmic_text::Attrs<'a>,
) {
if let Some(last) = result.last_mut() {
if last.1 == new_attrs {
let start = (last.0.as_ptr() as usize) - (text.as_ptr() as usize);
let last_end = start + last.0.len();
let new_start = (new_text.as_ptr() as usize) - (text.as_ptr() as usize);
if last_end == new_start {
let end = new_start + new_text.len();
last.0 = &text[start..end];
return;
}
}
}
result.push((new_text, new_attrs));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn push_or_merge_contiguous_same_attrs_merges() {
let text = "hello world";
let attrs = cosmic_text::Attrs::new();
let mut result = Vec::new();
result.push((&text[0..5], attrs.clone()));
push_or_merge(text, &mut result, &text[5..11], attrs);
assert_eq!(result.len(), 1);
assert_eq!(result[0].0, "hello world");
}
#[test]
fn push_or_merge_non_contiguous_same_attrs_pushes_separately() {
let text = "hello---world";
let attrs = cosmic_text::Attrs::new();
let mut result = Vec::new();
result.push((&text[0..5], attrs.clone()));
push_or_merge(text, &mut result, &text[8..13], attrs);
assert_eq!(result.len(), 2);
assert_eq!(result[0].0, "hello");
assert_eq!(result[1].0, "world");
}
#[test]
fn push_or_merge_contiguous_different_attrs_pushes_separately() {
let text = "hello world";
let attrs1 = cosmic_text::Attrs::new();
let attrs2 = cosmic_text::Attrs::new().color(cosmic_text::Color::rgb(255, 0, 0));
let mut result = Vec::new();
result.push((&text[0..5], attrs1));
push_or_merge(text, &mut result, &text[5..11], attrs2);
assert_eq!(result.len(), 2);
}
#[test]
fn push_or_merge_empty_result_pushes() {
let text = "hello";
let attrs = cosmic_text::Attrs::new();
let mut result = Vec::new();
push_or_merge(text, &mut result, &text[0..5], attrs);
assert_eq!(result.len(), 1);
assert_eq!(result[0].0, "hello");
}
}