tlc: Sprint 2 B4 — configurable tab width via Alt-T cycle
Adds user-facing control over the editor tab width.
New methods on Editor (editor/mod.rs):
tab_width() -> usize
- Returns current visual tab width in spaces.
set_tab_width(width: usize)
- Sets tab width, clamped to min 1.
cycle_tab_width() -> usize
- Cycles 4 → 8 → 2 → 4 (default 4 → wider → narrower → back).
- Returns the new width.
Alt-T binding (editor/handlers.rs):
try_global_shortcut now handles Alt-T (0x74) and calls
cycle_tab_width(). Status bar shows 'Tab width: N'.
Tab-to-indent (B3) and word-wrap (B1) both consume tab_width
via the field, so changing it via Alt-T takes effect immediately
on next render / insert.
Tests (5 new in editor::tests):
- tab_width_default_is_four
- set_tab_width_updates_field (verifies 0 → 1 clamp)
- cycle_tab_width_advances_2_4_8_2
- cycle_tab_width_from_unusual_value_resets_to_2
- tab_to_indent_uses_current_tab_width
Config wiring note: cfg.editor.tab_width (config.rs) already has
the field with serde defaults and persistence. Wiring it into
Editor::open() requires the constructor to accept &Config and
is deferred to a future sprint.
Total: 1253 passing (was 1248; +5 new).
This commit is contained in:
@@ -296,6 +296,11 @@ impl Editor {
|
||||
});
|
||||
return Some(EditorResult::Running);
|
||||
}
|
||||
0x74 => {
|
||||
let new_width = self.cycle_tab_width();
|
||||
self.message = Some(format!("Tab width: {new_width}"));
|
||||
return Some(EditorResult::Running);
|
||||
}
|
||||
0x72 => {
|
||||
self.redo();
|
||||
return Some(EditorResult::Running);
|
||||
|
||||
@@ -655,6 +655,30 @@ bracket_flash: None,
|
||||
self.word_wrap
|
||||
}
|
||||
|
||||
/// Current visual tab width in spaces.
|
||||
#[must_use]
|
||||
pub fn tab_width(&self) -> usize {
|
||||
self.tab_width
|
||||
}
|
||||
|
||||
/// Set the visual tab width in spaces. Used by Tab-to-indent
|
||||
/// and by the word-wrap renderer.
|
||||
pub fn set_tab_width(&mut self, width: usize) {
|
||||
self.tab_width = width.max(1);
|
||||
}
|
||||
|
||||
/// Cycle through the common tab widths (2 → 4 → 8 → 2). Returns
|
||||
/// the new width. Bound to Alt-T.
|
||||
pub fn cycle_tab_width(&mut self) -> usize {
|
||||
let next = match self.tab_width {
|
||||
w if w <= 2 => 4,
|
||||
w if w <= 4 => 8,
|
||||
_ => 2,
|
||||
};
|
||||
self.tab_width = next;
|
||||
next
|
||||
}
|
||||
|
||||
/// Whether auto-indent is enabled on Enter.
|
||||
#[must_use]
|
||||
pub fn auto_indent(&self) -> bool {
|
||||
@@ -3240,5 +3264,45 @@ mod tests {
|
||||
// contains a non-whitespace byte → not in indent zone → literal tab.
|
||||
assert_eq!(e.buffer().as_string(), "foo\t bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab_width_default_is_four() {
|
||||
let e = make_empty();
|
||||
assert_eq!(e.tab_width(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_tab_width_updates_field() {
|
||||
let mut e = make_empty();
|
||||
e.set_tab_width(8);
|
||||
assert_eq!(e.tab_width(), 8);
|
||||
e.set_tab_width(0);
|
||||
assert_eq!(e.tab_width(), 1); // clamped to min 1
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cycle_tab_width_advances_2_4_8_2() {
|
||||
let mut e = make_empty();
|
||||
assert_eq!(e.tab_width(), 4);
|
||||
assert_eq!(e.cycle_tab_width(), 8);
|
||||
assert_eq!(e.cycle_tab_width(), 2);
|
||||
assert_eq!(e.cycle_tab_width(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cycle_tab_width_from_unusual_value_resets_to_2() {
|
||||
let mut e = make_empty();
|
||||
e.set_tab_width(16);
|
||||
assert_eq!(e.cycle_tab_width(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab_to_indent_uses_current_tab_width() {
|
||||
let mut e = make_empty();
|
||||
e.set_tab_width(8);
|
||||
e.insert_tab_with_indent();
|
||||
assert_eq!(e.buffer().as_string(), " ");
|
||||
assert_eq!(e.cursor.visual_column(), 8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user