iced-code-editor 0.3.10

A custom code editor widget for the Iced GUI framework with syntax highlighting, line numbers, and scrolling support.
Documentation
//! Performance benchmarks for the editor's hot paths.
//!
//! These measure the per-edit / per-scroll work performed on large files:
//! syntax highlighting of a line, line wrapping, fold-region detection, and
//! search. Run them with:
//!
//! ```text
//! cargo bench -p iced-code-editor --features bench
//! ```

use std::collections::HashSet;
use std::hint::black_box;

use criterion::{Criterion, criterion_group, criterion_main};
use iced_code_editor::bench_support::{
    TextBuffer, WrappingCalculator, compute_foldable_regions, find_matches,
    highlight_line_spans,
};
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;

/// Number of lines in the synthetic source file used by the benchmarks.
const SAMPLE_LINES: usize = 10_000;

/// Builds a synthetic Rust-like source file of `lines` lines.
///
/// The content mixes function headers, expressions with comments, macro calls
/// and closing braces so wrapping, folding, search and highlighting all have
/// representative work to do.
fn sample_source(lines: usize) -> String {
    let mut out = String::with_capacity(lines * 48);
    for i in 0..lines {
        match i % 4 {
            0 => {
                out.push_str(&format!(
                    "fn function_{i}(value: usize) -> usize {{\n"
                ));
            }
            1 => {
                out.push_str(&format!(
                    "    let result = value * {i} + 1; // compute result\n"
                ));
            }
            2 => out.push_str("    println!(\"{}\", result);\n"),
            _ => out.push_str("}\n"),
        }
    }
    out
}

/// Benchmarks tokenizing a single line into colored spans.
fn bench_highlight_line(c: &mut Criterion) {
    let syntax_set = SyntaxSet::load_defaults_newlines();
    let syntax = syntax_set
        .find_syntax_by_extension("rs")
        .unwrap_or_else(|| syntax_set.find_syntax_plain_text());
    let theme = ThemeSet::load_defaults()
        .themes
        .get("base16-ocean.dark")
        .cloned()
        .unwrap_or_default();

    let line = "    let result = value * 42 + 1; // compute result here";

    c.bench_function("highlight_line_spans", |b| {
        b.iter(|| {
            highlight_line_spans(black_box(line), syntax, &theme, &syntax_set)
        });
    });
}

/// Benchmarks computing visual (wrapped) lines for a large buffer.
fn bench_wrapping(c: &mut Criterion) {
    let buffer = TextBuffer::new(&sample_source(SAMPLE_LINES));
    let calculator = WrappingCalculator::new(true, None, 16.8, 8.4);
    let hidden = HashSet::new();

    c.bench_function("calculate_visual_lines_10k", |b| {
        b.iter(|| {
            calculator.calculate_visual_lines(
                black_box(&buffer),
                800.0,
                45.0,
                &hidden,
            )
        });
    });
}

/// Benchmarks fold-region detection for a large buffer.
fn bench_folding(c: &mut Criterion) {
    let buffer = TextBuffer::new(&sample_source(SAMPLE_LINES));

    c.bench_function("compute_foldable_regions_10k", |b| {
        b.iter(|| compute_foldable_regions(black_box(&buffer)));
    });
}

/// Benchmarks searching a large buffer for a common substring.
fn bench_search(c: &mut Criterion) {
    let buffer = TextBuffer::new(&sample_source(SAMPLE_LINES));

    c.bench_function("find_matches_10k", |b| {
        b.iter(|| {
            find_matches(black_box(&buffer), "result", false, Some(10_000))
        });
    });
}

criterion_group!(
    benches,
    bench_highlight_line,
    bench_wrapping,
    bench_folding,
    bench_search
);
criterion_main!(benches);