135e94b5e4
Replaces the character-counting wrap in build_wrap_map with a
word-boundary algorithm that matches MC's intent: lines break at
the last whitespace before the wrap limit, mid-word break as
fallback for overlong words, tabs counted as their visual width
(next tab_width boundary), control chars as 2 cols.
New helpers (editor/render.rs):
visual_width(ch, col, tab_width) -> usize
- Tabs: tab_width - (col % tab_width), min 1
- Control chars (0x00..=0x1F, 0x7F): 2 cols (renders as ^X)
- Other chars: 1 col
count_wrapped_rows(line_bytes, body_width, tab_width) -> usize
- Walks line left-to-right, tracks last whitespace position
- On overflow: wrap at last_ws_col (preserves word boundary),
else mid-word break
- Stops at \n (line_bytes excludes \n in caller)
Editor (mod.rs):
- New tab_width: usize field (default 4)
- Matches the renderer's ' ' tab expansion in push_rendered_text
- Future B4 will make this user-configurable
Tests:
+12 wrap_tests in editor::render::wrap_tests:
- visual_width_tab_expands_to_next_boundary
- visual_width_ascii_and_control
- count_wrapped_rows_empty_line
- count_wrapped_rows_short_line_fits_in_one_row
- count_wrapped_rows_exact_fit
- count_wrapped_rows_one_char_overflow_no_whitespace
- count_wrapped_rows_word_boundary_break
- count_wrapped_rows_three_words_at_width_six
- count_wrapped_rows_long_word_falls_back_to_mid_word
- count_wrapped_rows_tab_visual_width
- count_wrapped_rows_zero_width_returns_one
- count_wrapped_rows_stops_at_newline
Total: 1242 passing (was 1230; +12 new).