use typ_buffer::{Position, Selection};
use typ_core::{Action, Panel};
use typ_panel_editor::EditorPanel;
fn pos(line: usize, col: usize) -> Position {
Position { line, col }
}
fn sel(from: (usize, usize), to: (usize, usize)) -> Selection {
Selection {
anchor: pos(from.0, from.1),
head: pos(to.0, to.1),
}
}
fn text(p: &EditorPanel) -> String {
(0..p.line_count())
.map(|i| p.line_text(i))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn tab_at_a_caret_goes_to_the_next_tab_stop() {
let mut p = EditorPanel::from_str("ab\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 2))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
"ab \n",
"column 2 with a width of 4 needs two spaces, not four"
);
assert_eq!(p.cursor(), pos(0, 4));
}
#[test]
fn tab_at_the_start_of_a_line_inserts_a_full_level() {
let mut p = EditorPanel::from_str("ab\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 0))]);
p.apply_action(Action::Indent);
assert_eq!(text(&p), " ab\n");
}
#[test]
fn tab_with_a_selection_indents_every_line_it_touches() {
let mut p = EditorPanel::from_str("one\ntwo\nthree\n");
p.set_selections_for_test(vec![sel((0, 1), (1, 2))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
" one\n two\nthree\n",
"the selection spans two lines, so two lines move"
);
}
#[test]
fn indenting_a_selection_does_not_replace_it() {
let mut p = EditorPanel::from_str("one\ntwo\n");
p.set_selections_for_test(vec![sel((0, 0), (1, 3))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
" one\n two\n",
"a selection is the target of an indent, not text to be overwritten"
);
}
#[test]
fn the_selection_survives_an_indent_and_follows_the_text() {
let mut p = EditorPanel::from_str("one\ntwo\n");
p.set_selections_for_test(vec![sel((0, 1), (1, 2))]);
p.apply_action(Action::Indent);
assert_eq!(
p.selections().primary().range(),
(pos(0, 5), pos(1, 6)),
"both ends shift by the four spaces added to their own line"
);
}
#[test]
fn a_selection_ending_at_column_zero_leaves_that_line_alone() {
let mut p = EditorPanel::from_str("one\ntwo\n");
p.set_selections_for_test(vec![sel((0, 0), (1, 0))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
" one\ntwo\n",
"nothing on the second line is selected, so it is not part of the block"
);
}
#[test]
fn indenting_skips_blank_lines() {
let mut p = EditorPanel::from_str("one\n\ntwo\n");
p.set_selections_for_test(vec![sel((0, 0), (2, 3))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
" one\n\n two\n",
"indenting a blank line leaves trailing whitespace and nothing else"
);
}
#[test]
fn outdent_removes_one_level() {
let mut p = EditorPanel::from_str(" deep\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 8))]);
p.apply_action(Action::Outdent);
assert_eq!(text(&p), " deep\n");
}
#[test]
fn outdent_takes_a_partial_level_to_zero() {
let mut p = EditorPanel::from_str(" three\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 3))]);
p.apply_action(Action::Outdent);
assert_eq!(
text(&p),
"three\n",
"three spaces is less than a level, so it goes to zero rather than minus one"
);
}
#[test]
fn outdent_on_an_unindented_line_does_nothing() {
let mut p = EditorPanel::from_str("flush\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 2))]);
p.apply_action(Action::Outdent);
assert_eq!(
text(&p),
"flush\n",
"there is no indentation to remove, so no character may be eaten"
);
assert_eq!(p.cursor(), pos(0, 2));
}
#[test]
fn outdent_removes_a_leading_tab_whole() {
let mut p = EditorPanel::from_str("\ttabbed\n");
p.set_selections_for_test(vec![Selection::caret(pos(0, 1))]);
p.apply_action(Action::Outdent);
assert_eq!(text(&p), "tabbed\n");
}
#[test]
fn indent_across_several_cursors_is_one_undo_step() {
let mut p = EditorPanel::from_str("a\nb\nc\n");
p.set_selections_for_test(vec![
Selection::caret(pos(0, 0)),
Selection::caret(pos(1, 0)),
Selection::caret(pos(2, 0)),
]);
p.apply_action(Action::Indent);
assert_eq!(text(&p), " a\n b\n c\n");
p.apply_action(Action::Undo);
assert_eq!(
text(&p),
"a\nb\nc\n",
"one indent is one undo, not one per cursor"
);
}
#[test]
fn two_cursors_on_one_line_indent_it_once() {
let mut p = EditorPanel::from_str("shared\n");
p.set_selections_for_test(vec![sel((0, 0), (0, 2)), sel((0, 3), (0, 5))]);
p.apply_action(Action::Indent);
assert_eq!(
text(&p),
" shared\n",
"the line is indented once however many cursors are sitting on it"
);
}