[go: up one dir, main page]

tui-textarea 0.3.1

tui-textarea is a simple yet powerful text editor widget for ratatui and tui-rs. Multi-line text editor can be easily put as part of your TUI application.
Documentation
use tui_textarea::{CursorMove, TextArea};

const BOTTOM_RIGHT: CursorMove = CursorMove::Jump(u16::MAX, u16::MAX);

#[test]
fn empty_textarea() {
    use CursorMove::*;

    let mut t = TextArea::default();
    for m in [
        Forward,
        Back,
        Up,
        Down,
        Head,
        End,
        Top,
        Bottom,
        WordForward,
        WordBack,
        ParagraphForward,
        ParagraphBack,
        Jump(0, 0),
        Jump(u16::MAX, u16::MAX),
    ] {
        t.move_cursor(m);
        assert_eq!(t.cursor(), (0, 0), "{:?}", m);
    }
}

#[test]
fn forward() {
    for (text, positions) in [
        (
            ["abc", "def"],
            [
                (0, 1),
                (0, 2),
                (0, 3),
                (1, 0),
                (1, 1),
                (1, 2),
                (1, 3),
                (1, 3),
            ],
        ),
        (
            ["あいう", "🐢🐱πŸ‘ͺ"],
            [
                (0, 1),
                (0, 2),
                (0, 3),
                (1, 0),
                (1, 1),
                (1, 2),
                (1, 3),
                (1, 3),
            ],
        ),
    ] {
        let mut t = TextArea::from(text);

        for pos in positions {
            t.move_cursor(CursorMove::Forward);
            assert_eq!(t.cursor(), pos, "{:?}", t.lines());
        }
    }
}

#[test]
fn back() {
    for (text, positions) in [
        (
            ["abc", "def"],
            [
                (1, 2),
                (1, 1),
                (1, 0),
                (0, 3),
                (0, 2),
                (0, 1),
                (0, 0),
                (0, 0),
            ],
        ),
        (
            ["あいう", "🐢🐱πŸ‘ͺ"],
            [
                (1, 2),
                (1, 1),
                (1, 0),
                (0, 3),
                (0, 2),
                (0, 1),
                (0, 0),
                (0, 0),
            ],
        ),
    ] {
        let mut t = TextArea::from(text);
        t.move_cursor(BOTTOM_RIGHT);

        for pos in positions {
            t.move_cursor(CursorMove::Back);
            assert_eq!(t.cursor(), pos, "{:?}", t.lines());
        }
    }
}

#[test]
fn up() {
    for text in [
        ["abc", "def", "ghi"],
        ["あいう", "🐢🐱🐰", "πŸ‘ͺπŸ€ŸπŸΏπŸ‘©πŸ»β€β€οΈβ€πŸ’‹β€πŸ‘¨πŸΎ"],
    ] {
        let mut t = TextArea::from(text);

        for col in 0..=3 {
            let mut row = 2;

            t.move_cursor(CursorMove::Jump(2, col as u16));
            assert_eq!(t.cursor(), (row, col), "{:?}", t.lines());

            while row > 0 {
                t.move_cursor(CursorMove::Up);
                row -= 1;
                assert_eq!(t.cursor(), (row, col), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn up_trim() {
    for text in [["", "a", "bcd", "efgh"], ["", "πŸ‘ͺ", "🐢!🐱", "あ?い!"]] {
        let mut t = TextArea::from(text);
        t.move_cursor(CursorMove::Jump(3, 3));

        for expected in [(2, 3), (1, 1), (0, 0)] {
            t.move_cursor(CursorMove::Up);
            assert_eq!(t.cursor(), expected, "{:?}", t.lines());
        }
    }
}

#[test]
fn down() {
    for text in [
        ["abc", "def", "ghi"],
        ["あいう", "🐢🐱🐰", "πŸ‘ͺπŸ€ŸπŸΏπŸ‘©πŸ»β€β€οΈβ€πŸ’‹β€πŸ‘¨πŸΎ"],
    ] {
        let mut t = TextArea::from(text);

        for col in 0..=3 {
            let mut row = 0;

            t.move_cursor(CursorMove::Jump(0, col as u16));
            assert_eq!(t.cursor(), (row, col), "{:?}", t.lines());

            while row < 2 {
                t.move_cursor(CursorMove::Down);
                row += 1;
                assert_eq!(t.cursor(), (row, col), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn down_trim() {
    for text in [["abcd", "efg", "h", ""], ["あ?い!", "🐢!🐱", "πŸ‘ͺ", ""]] {
        let mut t = TextArea::from(text);
        t.move_cursor(CursorMove::Jump(0, 3));

        for expected in [(1, 3), (2, 1), (3, 0)] {
            t.move_cursor(CursorMove::Down);
            assert_eq!(t.cursor(), expected, "{:?}", t.lines());
        }
    }
}

#[test]
fn head() {
    for text in [["efg", "h", ""], ["あいう", "πŸ‘ͺ", ""]] {
        let mut t = TextArea::from(text);
        for row in 0..t.lines().len() {
            let len = t.lines()[row].len();
            for col in [0, len / 2, len] {
                t.move_cursor(CursorMove::Jump(row as u16, col as u16));
                t.move_cursor(CursorMove::Head);
                assert_eq!(t.cursor(), (row, 0), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn end() {
    for text in [["efg", "h", ""], ["あいう", "πŸ‘ͺ", ""]] {
        let mut t = TextArea::from(text);
        for row in 0..t.lines().len() {
            let len = match row {
                0 => 3,
                1 => 1,
                2 => 0,
                _ => unreachable!(),
            };
            for col in [0, len / 2, len] {
                t.move_cursor(CursorMove::Jump(row as u16, col as u16));
                t.move_cursor(CursorMove::End);
                assert_eq!(t.cursor(), (row, len), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn top() {
    for text in [
        ["abc", "def", "ghi"],
        ["あいう", "🐢🐱🐰", "πŸ‘ͺπŸ€ŸπŸΏπŸ‘©πŸ»β€β€οΈβ€πŸ’‹β€πŸ‘¨πŸΎ"],
    ] {
        let mut t = TextArea::from(text);
        for row in 0..=2 {
            for col in 0..=3 {
                t.move_cursor(CursorMove::Jump(row, col));
                t.move_cursor(CursorMove::Top);
                assert_eq!(t.cursor(), (0, col as usize), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn top_trim() {
    for lines in [
        &["a", "bc"][..],
        &["あ", "🐢🐱"][..],
        &["a", "bcd", "ef"][..],
        &["", "犬"][..],
    ] {
        let mut t: TextArea = lines.iter().cloned().collect();
        t.move_cursor(CursorMove::Jump(u16::MAX, u16::MAX));
        t.move_cursor(CursorMove::Top);
        let col = t.lines()[0].chars().count();
        assert_eq!(t.cursor(), (0, col), "{:?}", t.lines());
    }
}

#[test]
fn bottom() {
    for text in [
        ["abc", "def", "ghi"],
        ["あいう", "🐢🐱🐰", "πŸ‘ͺπŸ€ŸπŸΏπŸ‘©πŸ»β€β€οΈβ€πŸ’‹β€πŸ‘¨πŸΎ"],
    ] {
        let mut t = TextArea::from(text);
        for row in 0..=2 {
            for col in 0..=3 {
                t.move_cursor(CursorMove::Jump(row, col));
                t.move_cursor(CursorMove::Bottom);
                assert_eq!(t.cursor(), (2, col as usize), "{:?}", t.lines());
            }
        }
    }
}

#[test]
fn bottom_trim() {
    for lines in [
        &["bc", "a"][..],
        &["🐢🐱", "🐰"][..],
        &["ef", "bcd", "a"][..],
        &["犬", ""][..],
    ] {
        let mut t: TextArea = lines.iter().cloned().collect();
        t.move_cursor(CursorMove::Jump(0, u16::MAX));
        t.move_cursor(CursorMove::Bottom);
        let col = t.lines().last().unwrap().chars().count();
        assert_eq!(t.cursor(), (t.lines().len() - 1, col), "{:?}", t.lines());
    }
}