neco-wrap
Word wrap engine for splitting logical lines into visual lines, with pluggable line-breaking and character-width policies.
How it works
neco-wrap splits a logical line into visual lines according to a WrapPolicy. A policy bundles two function pointers: one that returns the visual width of a character, and one that decides whether a break is allowed, forbidden, or mandatory at a given byte offset.
wrap_line processes one logical line and returns a list of WrapPoint values, each marking the byte offset and cumulative visual width where a visual line ends. WrapMap applies this over a whole document, tracks the results per logical line, and provides coordinate translation between logical (line, byte_offset) and visual line numbers.
Two built-in policies are provided. WrapPolicy::code breaks after ASCII whitespace and common operators. WrapPolicy::japanese_basic applies kinsoku shaping rules for Japanese text.
Usage
Wrapping a single line
use ;
let policy = code;
let wraps = wrap_line;
// Two wrap points: after "ab " and after "cd "
assert_eq!;
assert_eq!;
assert_eq!;
Managing a whole document with WrapMap
use ;
let lines = ;
let policy = code;
let mut map = new;
// Total visual lines across all logical lines
let total = map.total_visual_lines;
// Translate logical (line, byte_offset_in_line) to a visual line number
let vline = map.to_visual_line;
// After an edit, rewrap just the changed line
map.rewrap_line;
API
| Item | Description |
|---|---|
BreakOpportunity |
Allowed, Forbidden, or Mandatory break classification at a byte offset |
WrapPolicy |
Bundles char_width and break_opportunity function pointers |
WrapPolicy::new(char_width, break_opportunity) |
Constructor from two function pointers |
WrapPolicy::char_width() |
Returns the stored character-width function |
WrapPolicy::break_opportunity() |
Returns the stored break-opportunity function |
WrapPolicy::code() |
Built-in policy: breaks after ASCII whitespace and common code operators |
WrapPolicy::japanese_basic() |
Built-in policy: breaks between CJK characters with kinsoku rules |
WrapPoint |
Byte offset and cumulative visual width at a visual line boundary |
WrapPoint::byte_offset() |
Byte offset in the logical line where the visual line ends |
WrapPoint::visual_width() |
Cumulative visual column count up to this wrap point |
VisualLine |
[start, end) byte range of one visual line within a logical line |
VisualLine::start() |
Start byte offset |
VisualLine::end() |
End byte offset |
VisualLine::len() |
Byte length of the visual line |
VisualLine::is_empty() |
True when start equals end |
WrapMap |
Per-document wrap state: stores wrap points for every logical line |
WrapMap::new(lines, max_width, policy) |
Build from a line iterator |
WrapMap::max_width() |
The column limit used for wrapping |
WrapMap::line_count() |
Number of logical lines |
WrapMap::visual_line_count(line) |
Number of visual lines for a logical line |
WrapMap::total_visual_lines() |
Total visual line count across the document |
WrapMap::wrap_points(line) |
Slice of WrapPoint for a logical line |
WrapMap::visual_lines(line, line_len) |
VisualLine segments for a logical line |
WrapMap::to_visual_line(line, byte_offset_in_line) |
Logical position to absolute visual line number |
WrapMap::from_visual_line(visual_line) |
Absolute visual line number to (logical_line, start_byte_offset) |
WrapMap::rewrap_line(line, line_text, policy) |
Recompute wrap points for one logical line after an edit |
WrapMap::set_max_width(max_width, lines, policy) |
Change the column limit and recompute all lines |
WrapMap::splice_lines(start_line, removed_count, new_lines, policy) |
Replace a range of logical lines, matching a document splice |
wrap_line(line_text, max_width, policy) |
Low-level function: wrap one line and return Vec<WrapPoint> |
License
MIT