M0: compilable skeleton — Kigi 0.1.0 fork surgery

Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.

Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
  kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
  ptyctl, ptyctl-cli, third_party/ unchanged; proto package
  xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
  KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
  (templates re-encrypted)

Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
  trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
  module & dc_log, heap-profile uploader, auth-diagnostics uploader,
  session-analytics halves of feedback; local zero-egress observability
  preserved in new kigi-log crate (unified log, --debug firehose,
  subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
  direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
  relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
  ~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
  kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
  session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
  shell util

Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
  https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
  https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
  Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted

Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
  workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
  all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
  exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
  insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean

Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
  (new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
  fast-worktree); RSS measurement tests serialized via serial_test

Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
  notices sustained; kigi-tools ported-code notices extended; README,
  CONTRIBUTING, SECURITY, AGENTS.md rewritten

Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
This commit is contained in:
2026-07-17 05:31:01 -04:00
commit d6c20fc13f
2612 changed files with 1353757 additions and 0 deletions
@@ -0,0 +1,26 @@
#![allow(clippy::new_without_default)]
pub mod render;
pub mod textarea;
pub mod wrapping;
pub use textarea::{
ClipboardProvider, ElementId, ElementKind, InternalClipboard, MouseAction, TextArea,
TextAreaState, TextElement, TextElementEvent, TextElementEventKind, is_undo_input,
};
use crossterm::event::KeyModifiers;
// On Windows, AltGr arrives as Ctrl+Alt; on other platforms it's composed before reaching us.
#[cfg(target_os = "windows")]
#[inline]
pub fn is_altgr(modifiers: KeyModifiers) -> bool {
let without_shift = modifiers & !KeyModifiers::SHIFT;
without_shift == (KeyModifiers::CONTROL | KeyModifiers::ALT)
}
#[cfg(not(target_os = "windows"))]
#[inline]
pub fn is_altgr(_modifiers: KeyModifiers) -> bool {
false
}
@@ -0,0 +1,59 @@
use ratatui::text::Line;
use ratatui::text::Span;
/// Clone a borrowed ratatui `Line` into an owned `'static` line.
pub fn line_to_static(line: &Line<'_>) -> Line<'static> {
Line {
style: line.style,
alignment: line.alignment,
spans: line
.spans
.iter()
.map(|s| Span {
style: s.style,
content: std::borrow::Cow::Owned(s.content.to_string()),
})
.collect(),
}
}
/// Append owned copies of borrowed lines to `out`.
pub fn push_owned_lines<'a>(src: &[Line<'a>], out: &mut Vec<Line<'static>>) {
for l in src {
out.push(line_to_static(l));
}
}
/// Consider a line blank if it has no spans or only spans whose contents are
/// empty or consist solely of spaces (no tabs/newlines).
pub fn is_blank_line_spaces_only(line: &Line<'_>) -> bool {
if line.spans.is_empty() {
return true;
}
line.spans
.iter()
.all(|s| s.content.is_empty() || s.content.chars().all(|c| c == ' '))
}
/// Prefix each line with `initial_prefix` for the first line and
/// `subsequent_prefix` for following lines. Returns a new Vec of owned lines.
pub fn prefix_lines(
lines: Vec<Line<'static>>,
initial_prefix: Span<'static>,
subsequent_prefix: Span<'static>,
) -> Vec<Line<'static>> {
lines
.into_iter()
.enumerate()
.map(|(i, l)| {
let mut spans = Vec::with_capacity(l.spans.len() + 1);
spans.push(if i == 0 {
initial_prefix.clone()
} else {
subsequent_prefix.clone()
});
spans.extend(l.spans);
Line::from(spans).style(l.style)
})
.collect()
}
@@ -0,0 +1 @@
pub mod line_utils;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,605 @@
use ratatui::text::Line;
use ratatui::text::Span;
use std::ops::Range;
use textwrap::Options;
use textwrap::wrap_algorithms::Penalties;
use crate::render::line_utils::push_owned_lines;
pub(crate) fn wrap_ranges<'a, O>(text: &str, width_or_options: O) -> Vec<Range<usize>>
where
O: Into<Options<'a>>,
{
let opts = width_or_options.into();
let mut lines: Vec<Range<usize>> = Vec::new();
let text_start = text.as_ptr() as usize;
let text_end = text_start + text.len();
for line in textwrap::wrap(text, opts).iter() {
match line {
std::borrow::Cow::Borrowed(slice) => {
let slice_addr = slice.as_ptr() as usize;
// Skip slices whose pointers don't lie within `text`.
// This guards against empty Cow::Borrowed("") slices from
// textwrap that reference static memory instead of the
// input buffer (e.g. at zero or degenerate widths).
if slice_addr < text_start || slice_addr > text_end {
continue;
}
let start = slice_addr - text_start;
let end = start + slice.len();
let trailing_spaces = text[end..].chars().take_while(|c| *c == ' ').count();
lines.push(start..end + trailing_spaces);
}
std::borrow::Cow::Owned(_) => panic!("wrap_ranges: unexpected owned string"),
}
}
lines
}
/// Like `wrap_ranges` but returns ranges without trailing whitespace and
/// without the sentinel extra byte. Suitable for general wrapping where
/// trailing spaces should not be preserved.
pub(crate) fn wrap_ranges_trim<'a, O>(text: &str, width_or_options: O) -> Vec<Range<usize>>
where
O: Into<Options<'a>>,
{
let opts = width_or_options.into();
let mut lines: Vec<Range<usize>> = Vec::new();
let text_start = text.as_ptr() as usize;
let text_end = text_start + text.len();
for line in textwrap::wrap(text, opts).iter() {
match line {
std::borrow::Cow::Borrowed(slice) => {
let slice_addr = slice.as_ptr() as usize;
if slice_addr < text_start || slice_addr > text_end {
continue;
}
let start = slice_addr - text_start;
let end = start + slice.len();
lines.push(start..end);
}
std::borrow::Cow::Owned(_) => panic!("wrap_ranges_trim: unexpected owned string"),
}
}
lines
}
#[derive(Debug, Clone)]
pub struct RtOptions<'a> {
/// The width in columns at which the text will be wrapped.
pub width: usize,
/// Line ending used for breaking lines.
pub line_ending: textwrap::LineEnding,
/// Indentation used for the first line of output. See the
/// [`Options::initial_indent`] method.
pub initial_indent: Line<'a>,
/// Indentation used for subsequent lines of output. See the
/// [`Options::subsequent_indent`] method.
pub subsequent_indent: Line<'a>,
/// Allow long words to be broken if they cannot fit on a line.
/// When set to `false`, some lines may be longer than
/// `self.width`. See the [`Options::break_words`] method.
pub break_words: bool,
/// Wrapping algorithm to use, see the implementations of the
/// [`WrapAlgorithm`] trait for details.
pub wrap_algorithm: textwrap::WrapAlgorithm,
/// The line breaking algorithm to use, see the [`WordSeparator`]
/// trait for an overview and possible implementations.
pub word_separator: textwrap::WordSeparator,
/// The method for splitting words. This can be used to prohibit
/// splitting words on hyphens, or it can be used to implement
/// language-aware machine hyphenation.
pub word_splitter: textwrap::WordSplitter,
}
impl From<usize> for RtOptions<'_> {
fn from(width: usize) -> Self {
RtOptions::new(width)
}
}
#[allow(dead_code)]
impl<'a> RtOptions<'a> {
pub fn new(width: usize) -> Self {
RtOptions {
width,
line_ending: textwrap::LineEnding::LF,
initial_indent: Line::default(),
subsequent_indent: Line::default(),
break_words: true,
word_separator: textwrap::WordSeparator::new(),
wrap_algorithm: textwrap::WrapAlgorithm::OptimalFit(Penalties {
// ~infinite overflow penalty, we never want to overflow a line.
overflow_penalty: usize::MAX / 4,
..Default::default()
}),
word_splitter: textwrap::WordSplitter::HyphenSplitter,
}
}
pub fn line_ending(self, line_ending: textwrap::LineEnding) -> Self {
RtOptions {
line_ending,
..self
}
}
pub fn width(self, width: usize) -> Self {
RtOptions { width, ..self }
}
pub fn initial_indent(self, initial_indent: Line<'a>) -> Self {
RtOptions {
initial_indent,
..self
}
}
pub fn subsequent_indent(self, subsequent_indent: Line<'a>) -> Self {
RtOptions {
subsequent_indent,
..self
}
}
pub fn break_words(self, break_words: bool) -> Self {
RtOptions {
break_words,
..self
}
}
pub fn word_separator(self, word_separator: textwrap::WordSeparator) -> RtOptions<'a> {
RtOptions {
word_separator,
..self
}
}
pub fn wrap_algorithm(self, wrap_algorithm: textwrap::WrapAlgorithm) -> RtOptions<'a> {
RtOptions {
wrap_algorithm,
..self
}
}
pub fn word_splitter(self, word_splitter: textwrap::WordSplitter) -> RtOptions<'a> {
RtOptions {
word_splitter,
..self
}
}
}
#[must_use]
pub fn word_wrap_line<'a, O>(line: &'a Line<'a>, width_or_options: O) -> Vec<Line<'a>>
where
O: Into<RtOptions<'a>>,
{
// Flatten the line and record span byte ranges.
let mut flat = String::new();
let mut span_bounds = Vec::new();
let mut acc = 0usize;
for s in &line.spans {
let text = s.content.as_ref();
let start = acc;
flat.push_str(text);
acc += text.len();
span_bounds.push((start..acc, s.style));
}
let rt_opts: RtOptions<'a> = width_or_options.into();
let opts = Options::new(rt_opts.width)
.line_ending(rt_opts.line_ending)
.break_words(rt_opts.break_words)
.wrap_algorithm(rt_opts.wrap_algorithm)
.word_separator(rt_opts.word_separator)
.word_splitter(rt_opts.word_splitter);
let mut out: Vec<Line<'a>> = Vec::new();
// Compute first line range with reduced width due to initial indent.
let initial_width_available = opts
.width
.saturating_sub(rt_opts.initial_indent.width())
.max(1);
let initial_wrapped = wrap_ranges_trim(&flat, opts.clone().width(initial_width_available));
let Some(first_line_range) = initial_wrapped.first() else {
return vec![rt_opts.initial_indent.clone()];
};
// Build first wrapped line with initial indent.
let mut first_line = rt_opts.initial_indent.clone().style(line.style);
{
let sliced = slice_line_spans(line, &span_bounds, first_line_range);
let mut spans = first_line.spans;
spans.append(
&mut sliced
.spans
.into_iter()
.map(|s| s.patch_style(line.style))
.collect(),
);
first_line.spans = spans;
out.push(first_line);
}
// Wrap the remainder using subsequent indent width and map back to original indices.
let base = first_line_range.end;
let skip_leading_spaces = flat[base..].chars().take_while(|c| *c == ' ').count();
let base = base + skip_leading_spaces;
let subsequent_width_available = opts
.width
.saturating_sub(rt_opts.subsequent_indent.width())
.max(1);
let remaining_wrapped = wrap_ranges_trim(&flat[base..], opts.width(subsequent_width_available));
for r in &remaining_wrapped {
if r.is_empty() {
continue;
}
let mut subsequent_line = rt_opts.subsequent_indent.clone().style(line.style);
let offset_range = (r.start + base)..(r.end + base);
let sliced = slice_line_spans(line, &span_bounds, &offset_range);
let mut spans = subsequent_line.spans;
spans.append(
&mut sliced
.spans
.into_iter()
.map(|s| s.patch_style(line.style))
.collect(),
);
subsequent_line.spans = spans;
out.push(subsequent_line);
}
out
}
/// Wrap a sequence of lines, applying the initial indent only to the very first
/// output line, and using the subsequent indent for all later wrapped pieces.
#[allow(dead_code)]
pub(crate) fn word_wrap_lines<'a, I, O>(lines: I, width_or_options: O) -> Vec<Line<'static>>
where
I: IntoIterator<Item = &'a Line<'a>>,
O: Into<RtOptions<'a>>,
{
let base_opts: RtOptions<'a> = width_or_options.into();
let mut out: Vec<Line<'static>> = Vec::new();
for (idx, line) in lines.into_iter().enumerate() {
let opts = if idx == 0 {
base_opts.clone()
} else {
let mut o = base_opts.clone();
let sub = o.subsequent_indent.clone();
o = o.initial_indent(sub);
o
};
let wrapped = word_wrap_line(line, opts);
push_owned_lines(&wrapped, &mut out);
}
out
}
#[allow(dead_code)]
pub(crate) fn word_wrap_lines_borrowed<'a, I, O>(lines: I, width_or_options: O) -> Vec<Line<'a>>
where
I: IntoIterator<Item = &'a Line<'a>>,
O: Into<RtOptions<'a>>,
{
let base_opts: RtOptions<'a> = width_or_options.into();
let mut out: Vec<Line<'a>> = Vec::new();
let mut first = true;
for line in lines.into_iter() {
let opts = if first {
base_opts.clone()
} else {
base_opts
.clone()
.initial_indent(base_opts.subsequent_indent.clone())
};
out.extend(word_wrap_line(line, opts));
first = false;
}
out
}
fn slice_line_spans<'a>(
original: &'a Line<'a>,
span_bounds: &[(Range<usize>, ratatui::style::Style)],
range: &Range<usize>,
) -> Line<'a> {
let start_byte = range.start;
let end_byte = range.end;
let mut acc: Vec<Span<'a>> = Vec::new();
for (i, (range, style)) in span_bounds.iter().enumerate() {
let s = range.start;
let e = range.end;
if e <= start_byte {
continue;
}
if s >= end_byte {
break;
}
let seg_start = start_byte.max(s);
let seg_end = end_byte.min(e);
if seg_end > seg_start {
let local_start = seg_start - s;
let local_end = seg_end - s;
let content = original.spans[i].content.as_ref();
let slice = &content[local_start..local_end];
acc.push(Span {
style: *style,
content: std::borrow::Cow::Borrowed(slice),
});
}
if e >= end_byte {
break;
}
}
Line {
style: original.style,
alignment: original.alignment,
spans: acc,
}
}
#[cfg(test)]
mod tests {
use super::*;
use itertools::Itertools as _;
use pretty_assertions::assert_eq;
use ratatui::style::Color;
use ratatui::style::Stylize;
use std::string::ToString;
fn concat_line(line: &Line) -> String {
line.spans
.iter()
.map(|s| s.content.as_ref())
.collect::<String>()
}
#[test]
fn trivial_unstyled_no_indents_wide_width() {
let line = Line::from("hello");
let out = word_wrap_line(&line, 10);
assert_eq!(out.len(), 1);
assert_eq!(concat_line(&out[0]), "hello");
}
#[test]
fn simple_unstyled_wrap_narrow_width() {
let line = Line::from("hello world");
let out = word_wrap_line(&line, 5);
assert_eq!(out.len(), 2);
assert_eq!(concat_line(&out[0]), "hello");
assert_eq!(concat_line(&out[1]), "world");
}
#[test]
fn simple_styled_wrap_preserves_styles() {
let line = Line::from(vec!["hello ".red(), "world".into()]);
let out = word_wrap_line(&line, 6);
assert_eq!(out.len(), 2);
// First line should carry the red style
assert_eq!(concat_line(&out[0]), "hello");
assert_eq!(out[0].spans.len(), 1);
assert_eq!(out[0].spans[0].style.fg, Some(Color::Red));
// Second line is unstyled
assert_eq!(concat_line(&out[1]), "world");
assert_eq!(out[1].spans.len(), 1);
assert_eq!(out[1].spans[0].style.fg, None);
}
#[test]
fn with_initial_and_subsequent_indents() {
let opts = RtOptions::new(8)
.initial_indent(Line::from("- "))
.subsequent_indent(Line::from(" "));
let line = Line::from("hello world foo");
let out = word_wrap_line(&line, opts);
// Expect three lines with proper prefixes
assert!(concat_line(&out[0]).starts_with("- "));
assert!(concat_line(&out[1]).starts_with(" "));
assert!(concat_line(&out[2]).starts_with(" "));
// And content roughly segmented
assert_eq!(concat_line(&out[0]), "- hello");
assert_eq!(concat_line(&out[1]), " world");
assert_eq!(concat_line(&out[2]), " foo");
}
#[test]
fn empty_initial_indent_subsequent_spaces() {
let opts = RtOptions::new(8)
.initial_indent(Line::from(""))
.subsequent_indent(Line::from(" "));
let line = Line::from("hello world foobar");
let out = word_wrap_line(&line, opts);
assert!(concat_line(&out[0]).starts_with("hello"));
for l in &out[1..] {
assert!(concat_line(l).starts_with(" "));
}
}
#[test]
fn empty_input_yields_single_empty_line() {
let line = Line::from("");
let out = word_wrap_line(&line, 10);
assert_eq!(out.len(), 1);
assert_eq!(concat_line(&out[0]), "");
}
#[test]
fn leading_spaces_preserved_on_first_line() {
let line = Line::from(" hello");
let out = word_wrap_line(&line, 8);
assert_eq!(out.len(), 1);
assert_eq!(concat_line(&out[0]), " hello");
}
#[test]
fn multiple_spaces_between_words_dont_start_next_line_with_spaces() {
let line = Line::from("hello world");
let out = word_wrap_line(&line, 8);
assert_eq!(out.len(), 2);
assert_eq!(concat_line(&out[0]), "hello");
assert_eq!(concat_line(&out[1]), "world");
}
#[test]
fn break_words_false_allows_overflow_for_long_word() {
let opts = RtOptions::new(5).break_words(false);
let line = Line::from("supercalifragilistic");
let out = word_wrap_line(&line, opts);
assert_eq!(out.len(), 1);
assert_eq!(concat_line(&out[0]), "supercalifragilistic");
}
#[test]
fn hyphen_splitter_breaks_at_hyphen() {
let line = Line::from("hello-world");
let out = word_wrap_line(&line, 7);
assert_eq!(out.len(), 2);
assert_eq!(concat_line(&out[0]), "hello-");
assert_eq!(concat_line(&out[1]), "world");
}
#[test]
fn indent_consumes_width_leaving_one_char_space() {
let opts = RtOptions::new(4)
.initial_indent(Line::from(">>>>"))
.subsequent_indent(Line::from("--"));
let line = Line::from("hello");
let out = word_wrap_line(&line, opts);
assert_eq!(out.len(), 3);
assert_eq!(concat_line(&out[0]), ">>>>h");
assert_eq!(concat_line(&out[1]), "--el");
assert_eq!(concat_line(&out[2]), "--lo");
}
#[test]
fn wide_unicode_wraps_by_display_width() {
let line = Line::from("😀😀😀");
let out = word_wrap_line(&line, 4);
assert_eq!(out.len(), 2);
assert_eq!(concat_line(&out[0]), "😀😀");
assert_eq!(concat_line(&out[1]), "😀");
}
#[test]
fn styled_split_within_span_preserves_style() {
use ratatui::style::Stylize;
let line = Line::from(vec!["abcd".red()]);
let out = word_wrap_line(&line, 2);
assert_eq!(out.len(), 2);
assert_eq!(out[0].spans.len(), 1);
assert_eq!(out[1].spans.len(), 1);
assert_eq!(out[0].spans[0].style.fg, Some(Color::Red));
assert_eq!(out[1].spans[0].style.fg, Some(Color::Red));
assert_eq!(concat_line(&out[0]), "ab");
assert_eq!(concat_line(&out[1]), "cd");
}
#[test]
fn wrap_lines_applies_initial_indent_only_once() {
let opts = RtOptions::new(8)
.initial_indent(Line::from("- "))
.subsequent_indent(Line::from(" "));
let lines = vec![Line::from("hello world"), Line::from("foo bar baz")];
let out = word_wrap_lines(&lines, opts);
// Expect: first line prefixed with "- ", subsequent wrapped pieces with " "
// and for the second input line, there should be no "- " prefix on its first piece
let rendered: Vec<String> = out.iter().map(concat_line).collect();
assert!(rendered[0].starts_with("- "));
for r in rendered.iter().skip(1) {
assert!(r.starts_with(" "));
}
}
#[test]
fn wrap_lines_without_indents_is_concat_of_single_wraps() {
let lines = vec![Line::from("hello"), Line::from("world!")];
let out = word_wrap_lines(&lines, 10);
let rendered: Vec<String> = out.iter().map(concat_line).collect();
assert_eq!(rendered, vec!["hello", "world!"]);
}
#[test]
fn wrap_lines_borrowed_applies_initial_indent_only_once() {
let opts = RtOptions::new(8)
.initial_indent(Line::from("- "))
.subsequent_indent(Line::from(" "));
let lines = [Line::from("hello world"), Line::from("foo bar baz")];
let out = word_wrap_lines_borrowed(lines.iter(), opts);
let rendered: Vec<String> = out.iter().map(concat_line).collect();
assert!(rendered.first().unwrap().starts_with("- "));
for r in rendered.iter().skip(1) {
assert!(r.starts_with(" "));
}
}
#[test]
fn wrap_lines_borrowed_without_indents_is_concat_of_single_wraps() {
let lines = [Line::from("hello"), Line::from("world!")];
let out = word_wrap_lines_borrowed(lines.iter(), 10);
let rendered: Vec<String> = out.iter().map(concat_line).collect();
assert_eq!(rendered, vec!["hello", "world!"]);
}
#[test]
fn line_height_counts_double_width_emoji() {
let line = "😀😀😀".into(); // each emoji ~ width 2
assert_eq!(word_wrap_line(&line, 4).len(), 2);
assert_eq!(word_wrap_line(&line, 2).len(), 3);
assert_eq!(word_wrap_line(&line, 6).len(), 1);
}
#[test]
fn wrap_ranges_many_newlines_width_one_does_not_panic() {
let text = "\n".repeat(30);
let ranges = wrap_ranges(
&text,
Options::new(1).wrap_algorithm(textwrap::WrapAlgorithm::FirstFit),
);
for r in &ranges {
assert!(r.end <= text.len(), "range {r:?} out of bounds");
}
}
#[test]
fn wrap_ranges_trim_empty_text_does_not_panic() {
let ranges = wrap_ranges_trim("", 1);
assert!(ranges.is_empty() || ranges == vec![0..0]);
}
#[test]
fn word_wrap_line_width_one_with_newlines_does_not_panic() {
let line = Line::from("\n\n\n\n\n");
let out = word_wrap_line(&line, 1);
assert!(!out.is_empty());
}
#[test]
fn word_wrap_does_not_split_words_simple_english() {
let sample = "Years passed, and Willowmere thrived in peace and friendship. Miras herb garden flourished with both ordinary and enchanted plants, and travelers spoke of the kindness of the woman who tended them.";
let line = Line::from(sample);
let lines = [line];
// Force small width to exercise wrapping at spaces.
let wrapped = word_wrap_lines_borrowed(&lines, 40);
let joined: String = wrapped.iter().map(ToString::to_string).join("\n");
assert_eq!(
joined,
r#"Years passed, and Willowmere thrived
in peace and friendship. Miras herb
garden flourished with both ordinary and
enchanted plants, and travelers spoke
of the kindness of the woman who tended
them."#
);
}
}