diff --git a/editor/src/editor/colors.rs b/editor/src/editor/colors.rs index 4ce1d34b97..b62ae561fb 100644 --- a/editor/src/editor/colors.rs +++ b/editor/src/editor/colors.rs @@ -1,6 +1,8 @@ -use crate::ui::colors::ColorTup; - -pub const TXT_COLOR: ColorTup = (1.0, 1.0, 1.0, 1.0); -pub const CODE_COLOR: ColorTup = (0.21, 0.55, 0.83, 1.0); -pub const BG_COLOR: ColorTup = (0.11, 0.11, 0.13, 1.0); +use crate::graphics::colors as gr_colors; +use crate::graphics::colors::ColorTup; +use crate::ui::colors as ui_colors; +pub const BG_COL: ColorTup = (0.17, 0.17, 0.19, 1.0); +pub const EQUALS_SYNTAX_COL: ColorTup = (0.043, 0.0196, 0.102, 1.0); +pub const STRING_SYNTAX_COL: ColorTup = ui_colors::LIGHT_BRAND_COL; +pub const CODE_COLOR: ColorTup = gr_colors::WHITE; diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 597c67da2c..675ca8aacf 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -1,5 +1,5 @@ use colored::*; -use snafu::{Backtrace, ErrorCompat, Snafu, ResultExt, NoneError}; +use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu}; //import errors as follows: // `use crate::error::OutOfBounds;` @@ -35,10 +35,7 @@ pub enum EdError { }, #[snafu(display("UIError: {}", msg))] - UIErrorBacktrace { - msg: String, - backtrace: Backtrace, - }, + UIErrorBacktrace { msg: String, backtrace: Backtrace }, } pub type EdResult = std::result::Result; @@ -113,9 +110,7 @@ impl From for EdError { let msg = format!("{}", ui_err); // hack to handle EdError derive - let dummy_res: Result<(), NoneError> = Err(NoneError{}); - dummy_res.context(UIErrorBacktrace { - msg, - }).unwrap_err() + let dummy_res: Result<(), NoneError> = Err(NoneError {}); + dummy_res.context(UIErrorBacktrace { msg }).unwrap_err() } } diff --git a/editor/src/editor/keyboard_input.rs b/editor/src/editor/keyboard_input.rs index 5d157e7e7e..115cbc600e 100644 --- a/editor/src/editor/keyboard_input.rs +++ b/editor/src/editor/keyboard_input.rs @@ -1,6 +1,8 @@ use crate::editor::ed_error::EdResult; use crate::editor::mvc::app_model::AppModel; -use crate::editor::mvc::app_update::{handle_copy, handle_cut, handle_paste, pass_keydown_to_focused}; +use crate::editor::mvc::app_update::{ + handle_copy, handle_cut, handle_paste, pass_keydown_to_focused, +}; use winit::event::VirtualKeyCode::*; use winit::event::{ElementState, ModifiersState, VirtualKeyCode}; @@ -15,7 +17,9 @@ pub fn handle_keydown( } match virtual_keycode { - Left | Up | Right | Down => pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)?, + Left | Up | Right | Down => { + pass_keydown_to_focused(&modifiers, virtual_keycode, app_model)? + } Copy => handle_copy(app_model)?, Paste => handle_paste(app_model)?, diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 3925409c85..6f4e441ad9 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -1,43 +1,30 @@ - +use super::keyboard_input; +use crate::editor::colors::BG_COL; +use crate::editor::colors::CODE_COLOR; use crate::editor::ed_error::{print_err, print_ui_err}; -use crate::ui::{ - ui_error::UIResult, - text::text_pos::{TextPos}, - text::lines::{Lines}, - colors::{CODE_COLOR, TXT_COLOR}, +use crate::editor::mvc::{app_model::AppModel, app_update, ed_model, ed_model::EdModel, ed_view}; +use crate::graphics::colors::to_wgpu_color; +use crate::graphics::primitives::text::{ + build_glyph_brush, example_code_glyph_rect, queue_code_text_draw, queue_text_draw, Text, }; use crate::graphics::{ - lowlevel::buffer::create_rect_buffers, - lowlevel::ortho::update_ortho_buffer, - lowlevel::pipelines, - style::CODE_FONT_SIZE, - style::CODE_TXT_XY, + lowlevel::buffer::create_rect_buffers, lowlevel::ortho::update_ortho_buffer, + lowlevel::pipelines, style::CODE_FONT_SIZE, style::CODE_TXT_XY, }; -use crate::graphics::primitives::text::{ - build_glyph_brush, example_code_glyph_rect, queue_code_text_draw, queue_text_draw, - Text, +use crate::ui::{ + colors::TXT_COLOR, text::lines::Lines, text::text_pos::TextPos, ui_error::UIResult, }; -use crate::editor::mvc::{ - app_update, ed_model, ed_view, - app_model::AppModel, - ed_model::EdModel, -}; -use super::keyboard_input; //use crate::resources::strings::NOTHING_OPENED; use super::util::slice_get; use crate::lang::{pool::Pool, scope::Scope}; +use bumpalo::Bump; +use cgmath::Vector2; use pipelines::RectResources; use roc_collections::all::MutMap; use roc_module::symbol::{IdentIds, ModuleIds}; use roc_region::all::Region; use roc_types::subs::VarStore; -use bumpalo::Bump; -use cgmath::Vector2; -use std::{ - io, - error::Error, - path::Path, -}; +use std::{error::Error, io, path::Path}; use wgpu::{CommandEncoder, RenderPass, TextureView}; use wgpu_glyph::GlyphBrush; use winit::{ @@ -388,17 +375,14 @@ fn begin_render_pass<'a>( encoder: &'a mut CommandEncoder, texture_view: &'a TextureView, ) -> RenderPass<'a> { + let bg_color = to_wgpu_color(BG_COL); + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: texture_view, resolve_target: None, ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), + load: wgpu::LoadOp::Clear(bg_color), store: true, }, }], diff --git a/editor/src/editor/mod.rs b/editor/src/editor/mod.rs index daa9c05238..5bff69d628 100644 --- a/editor/src/editor/mod.rs +++ b/editor/src/editor/mod.rs @@ -1,8 +1,8 @@ - -pub mod main; -pub mod mvc; +mod colors; mod ed_error; mod keyboard_input; +pub mod main; +mod mvc; mod render_ast; -mod colors; -mod util; \ No newline at end of file +pub mod syntax_highlight; +mod util; // TODO remove pub once we have ast based syntax highlighting diff --git a/editor/src/editor/mvc/app_model.rs b/editor/src/editor/mvc/app_model.rs index d560455ed8..0b5786f3b7 100644 --- a/editor/src/editor/mvc/app_model.rs +++ b/editor/src/editor/mvc/app_model.rs @@ -1,8 +1,8 @@ use super::ed_model::EdModel; use crate::editor::ed_error::{ print_err, - EdResult, EdError::{ClipboardInitFailed, ClipboardReadFailed, ClipboardWriteFailed}, + EdResult, }; use copypasta::{ClipboardContext, ClipboardProvider}; use std::fmt; diff --git a/editor/src/editor/mvc/app_update.rs b/editor/src/editor/mvc/app_update.rs index f3d01a26f9..fcad791ea0 100644 --- a/editor/src/editor/mvc/app_update.rs +++ b/editor/src/editor/mvc/app_update.rs @@ -1,13 +1,12 @@ use super::app_model; use super::app_model::AppModel; -use super::ed_update; -use winit::event::{ModifiersState, VirtualKeyCode}; +use crate::editor::ed_error::EdResult; use crate::ui::text::{ - lines::{SelectableLines, MutSelectableLines}, + lines::{MutSelectableLines, SelectableLines}, text_pos::TextPos, }; use crate::ui::ui_error::UIResult; -use crate::editor::ed_error::EdResult; +use winit::event::{ModifiersState, VirtualKeyCode}; pub fn handle_copy(app_model: &mut AppModel) -> EdResult<()> { if let Some(ref mut ed_model) = app_model.ed_model_opt { @@ -41,48 +40,32 @@ pub fn handle_paste(app_model: &mut AppModel) -> EdResult<()> { let start_caret_pos = selection.start_pos; ed_model.text.del_selection()?; - ed_model - .text - .insert_str(&clipboard_content)?; + ed_model.text.insert_str(&clipboard_content)?; if clipboard_nr_lines > 0 { - ed_model - .text - .set_caret( TextPos { - line: start_caret_pos.line + clipboard_nr_lines, - column: last_line_nr_chars, - }) + ed_model.text.set_caret(TextPos { + line: start_caret_pos.line + clipboard_nr_lines, + column: last_line_nr_chars, + }) } else { - ed_model - .text - .set_caret( TextPos { - line: start_caret_pos.line, - column: start_caret_pos.column + last_line_nr_chars, - }) + ed_model.text.set_caret(TextPos { + line: start_caret_pos.line, + column: start_caret_pos.column + last_line_nr_chars, + }) } } else { - ed_model - .text - .insert_str(&clipboard_content)?; + ed_model.text.insert_str(&clipboard_content)?; if clipboard_nr_lines > 0 { - ed_model - .text - .set_caret( - TextPos { - line: old_caret_pos.line + clipboard_nr_lines, - column: last_line_nr_chars, - } - ) + ed_model.text.set_caret(TextPos { + line: old_caret_pos.line + clipboard_nr_lines, + column: last_line_nr_chars, + }) } else { - ed_model - .text - .set_caret( - TextPos { - line: old_caret_pos.line, - column: old_caret_pos.column + last_line_nr_chars, - } - ) + ed_model.text.set_caret(TextPos { + line: old_caret_pos.line, + column: old_caret_pos.column + last_line_nr_chars, + }) } } } @@ -115,7 +98,7 @@ pub fn pass_keydown_to_focused( ) -> UIResult<()> { if let Some(ref mut ed_model) = app_model.ed_model_opt { if ed_model.has_focus { - ed_update::handle_key_down(modifiers, virtual_keycode, ed_model)?; + ed_model.text.handle_key_down(modifiers, virtual_keycode)?; } } @@ -125,20 +108,20 @@ pub fn pass_keydown_to_focused( pub fn handle_new_char(received_char: &char, app_model: &mut AppModel) -> EdResult<()> { if let Some(ref mut ed_model) = app_model.ed_model_opt { if ed_model.has_focus { - ed_update::handle_new_char(received_char, ed_model)?; + ed_model.text.handle_new_char(received_char)?; } } Ok(()) } +/* #[cfg(test)] pub mod test_app_update { use crate::editor::mvc::app_model; use crate::editor::mvc::app_model::{AppModel, Clipboard}; use crate::editor::mvc::app_update::{handle_copy, handle_cut, handle_paste}; use crate::editor::mvc::ed_model::{EdModel}; - use crate::editor::mvc::ed_update::test_ed_update::gen_big_text; use crate::ui::text::{ big_selectable_text::BigSelectableText, selection::test_selection::{all_lines_vec, convert_selection_to_dsl}, @@ -288,4 +271,4 @@ pub mod test_app_update { Ok(()) } -} +}*/ diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index d6910ccc7e..f3789778d2 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -1,7 +1,5 @@ use crate::graphics::primitives::rect::Rect; -use crate::ui::text::{ - big_selectable_text::{BigSelectableText, from_path}, -}; +use crate::ui::text::big_selectable_text::{from_path, BigSelectableText}; use crate::ui::ui_error::UIResult; use std::path::Path; diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs deleted file mode 100644 index f6ce2c235b..0000000000 --- a/editor/src/editor/mvc/ed_update.rs +++ /dev/null @@ -1,427 +0,0 @@ -use super::ed_model::EdModel; -use crate::ui::text::{ - text_pos::TextPos, - selection::{RawSelection, Selection, validate_selection}, - big_selectable_text::BigSelectableText, - lines::{Lines, SelectableLines, MutSelectableLines}, - caret_w_select::CaretWSelect, -}; -use crate::ui::ui_error::UIResult; -use crate::editor::ed_error::EdResult; -use crate::editor::util::is_newline; -use std::cmp::{max, min}; -use winit::event::VirtualKeyCode::*; -use winit::event::{ModifiersState, VirtualKeyCode}; - -pub type MoveCaretFun = - fn(bool, &BigSelectableText) -> UIResult; - -fn validate_sel_opt(start_pos: TextPos, end_pos: TextPos) -> UIResult> { - Ok( - Some( - validate_selection(start_pos, end_pos)? - ) - ) -} - -fn handle_arrow(move_caret_fun: MoveCaretFun, modifiers: &ModifiersState, ed_model: &mut EdModel) -> UIResult<()> { - let new_caret_w_select = move_caret_fun( - modifiers.shift(), - &ed_model.text, - )?; - - ed_model.text.caret_w_select = new_caret_w_select; - - Ok(()) -} - -// TODO move this to impl EdModel -pub fn handle_select_all(ed_model: &mut EdModel) -> UIResult<()> { - if ed_model.text.nr_of_chars() > 0 { - let last_pos = ed_model.text.last_text_pos(); - - ed_model.text.set_raw_sel( - RawSelection { - start_pos: TextPos { line: 0, column: 0 }, - end_pos: last_pos, - } - )?; - - ed_model.text.set_caret(last_pos); - } - - Ok(()) -} - -pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult<()> { - let old_caret_pos = ed_model.text.caret_w_select.caret_pos; - - // TODO move this to ui folder - - match received_char { - '\u{8}' | '\u{7f}' => { - // On Linux, '\u{8}' is backspace, - // on macOS '\u{7f}'. - - if ed_model.text.is_selection_active() { - ed_model.text.del_selection()?; - } else { - ed_model.text.pop_char(); - } - - ed_model.text.set_sel_none(); - } - ch if is_newline(ch) => { - if ed_model.text.is_selection_active() { - ed_model.text.del_selection()?; - ed_model.text.insert_char(&'\n')?; - } else { - ed_model.text.insert_char(&'\n')?; - - ed_model.text.set_caret(TextPos { - line: old_caret_pos.line + 1, - column: 0, - }); - } - - ed_model.text.set_sel_none(); - } - '\u{1}' // Ctrl + A - | '\u{3}' // Ctrl + C - | '\u{16}' // Ctrl + V - | '\u{18}' // Ctrl + X - | '\u{e000}'..='\u{f8ff}' // http://www.unicode.org/faq/private_use.html - | '\u{f0000}'..='\u{ffffd}' // ^ - | '\u{100000}'..='\u{10fffd}' // ^ - => { - // chars that can be ignored - } - _ => { - if ed_model.text.is_selection_active() { - ed_model.text.del_selection()?; - ed_model - .text - .insert_char(received_char)?; - - ed_model.text.set_caret( - move_caret_right(false, &ed_model.text)?.caret_pos - ); - } else { - ed_model - .text - .insert_char(received_char)?; - - ed_model.text.set_caret( - TextPos { - line: old_caret_pos.line, - column: old_caret_pos.column + 1, - } - ); - } - - ed_model.text.set_sel_none(); - } - } - - Ok(()) -} - -pub fn handle_key_down( - modifiers: &ModifiersState, - virtual_keycode: VirtualKeyCode, - ed_model: &mut EdModel, -) -> UIResult<()> { - match virtual_keycode { - Left => handle_arrow(move_caret_left, modifiers, ed_model), - Up => handle_arrow(move_caret_up, modifiers, ed_model), - Right => handle_arrow(move_caret_right, modifiers, ed_model), - Down => handle_arrow(move_caret_down, modifiers, ed_model), - - A => { - if modifiers.ctrl() { - handle_select_all(ed_model) - } else { - Ok(()) - } - } - Home => { - let curr_line_nr = ed_model.text.caret_w_select.caret_pos.line; - // TODO no unwrap - let curr_line_str = ed_model.text.get_line(curr_line_nr).unwrap(); - let line_char_iter = curr_line_str.chars(); - - let mut first_no_space_char_col = 0; - let mut non_space_found = false; - - for c in line_char_iter { - if !c.is_whitespace() { - non_space_found = true; - break; - } else { - first_no_space_char_col += 1; - } - } - - if !non_space_found { - first_no_space_char_col = 0; - } - - ed_model.text.caret_w_select.move_caret_w_mods( - TextPos { - line: ed_model.text.caret_w_select.caret_pos.line, - column: first_no_space_char_col - }, - modifiers - ) - } - End => { - let curr_line = ed_model.text.caret_w_select.caret_pos.line; - // TODO no unwrap - let new_col = - max( - 0, - ed_model.text.line_len(curr_line).unwrap() - 1 - ); - - let new_pos = - TextPos { - line: curr_line, - column: new_col - }; - - ed_model.text.caret_w_select.move_caret_w_mods(new_pos, modifiers) - } - _ => Ok(()) - } -} - -#[cfg(test)] -pub mod test_ed_update { - use crate::editor::mvc::app_update::test_app_update::mock_app_model; - use crate::editor::mvc::ed_update::{handle_new_char, handle_select_all}; - use crate::ui::text::{ - big_selectable_text::BigSelectableText, - }; - use crate::ui::text::selection::test_selection::{ - all_lines_vec, convert_dsl_to_selection, convert_selection_to_dsl, big_text_from_dsl_str, - }; - use bumpalo::Bump; - - pub fn gen_big_text( - lines: &[&str], - ) -> Result { - let lines_string_slice: Vec = lines.iter().map(|l| l.to_string()).collect(); - let mut big_text = big_text_from_dsl_str(&lines_string_slice); - let caret_w_select = convert_dsl_to_selection(&lines_string_slice).unwrap(); - - big_text.caret_w_select = caret_w_select; - - Ok(big_text) - } - - fn assert_insert( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - new_char: char, - arena: &Bump, - ) -> Result<(), String> { - let pre_big_text = gen_big_text(pre_lines_str)?; - - let app_model = mock_app_model(pre_big_text, None); - let mut ed_model = app_model.ed_model_opt.unwrap(); - - if let Err(e) = handle_new_char(&new_char, &mut ed_model) { - return Err(e.to_string()); - } - - let mut actual_lines = all_lines_vec(&ed_model.text); - let dsl_slice = convert_selection_to_dsl( - ed_model.text.caret_w_select, - &mut actual_lines, - ) - .unwrap(); - assert_eq!(dsl_slice, expected_post_lines_str); - - Ok(()) - } - - #[test] - fn insert_new_char_simple() -> Result<(), String> { - let arena = &Bump::new(); - - assert_insert(&["|"], &["a|"], 'a', arena)?; - assert_insert(&["|"], &[" |"], ' ', arena)?; - assert_insert(&["a|"], &["aa|"], 'a', arena)?; - assert_insert(&["a|"], &["a |"], ' ', arena)?; - assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b', arena)?; - assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b', arena)?; - assert_insert(&["a\n", "|"], &["a\n", "b|"], 'b', arena)?; - assert_insert(&["a\n", "b\n", "c|"], &["a\n", "b\n", "cd|"], 'd', arena)?; - - Ok(()) - } - - #[test] - fn insert_new_char_mid() -> Result<(), String> { - let arena = &Bump::new(); - - assert_insert(&["ab|d"], &["abc|d"], 'c', arena)?; - assert_insert(&["a|cd"], &["ab|cd"], 'b', arena)?; - assert_insert(&["abc\n", "|e"], &["abc\n", "d|e"], 'd', arena)?; - assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", "g| "], 'g', arena)?; - assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", " | "], ' ', arena)?; - - Ok(()) - } - - #[test] - fn simple_backspace() -> Result<(), String> { - let arena = &Bump::new(); - - assert_insert(&["|"], &["|"], '\u{8}', arena)?; - assert_insert(&[" |"], &["|"], '\u{8}', arena)?; - assert_insert(&["a|"], &["|"], '\u{8}', arena)?; - assert_insert(&["ab|"], &["a|"], '\u{8}', arena)?; - assert_insert(&["a|\n", ""], &["|\n", ""], '\u{8}', arena)?; - assert_insert(&["ab|\n", ""], &["a|\n", ""], '\u{8}', arena)?; - assert_insert(&["a\n", "|"], &["a|"], '\u{8}', arena)?; - assert_insert(&["a\n", "b\n", "c|"], &["a\n", "b\n", "|"], '\u{8}', arena)?; - assert_insert(&["a\n", "b\n", "|"], &["a\n", "b|"], '\u{8}', arena)?; - - Ok(()) - } - - #[test] - fn selection_backspace() -> Result<(), String> { - let arena = &Bump::new(); - - assert_insert(&["[a]|"], &["|"], '\u{8}', arena)?; - assert_insert(&["a[a]|"], &["a|"], '\u{8}', arena)?; - assert_insert(&["[aa]|"], &["|"], '\u{8}', arena)?; - assert_insert(&["a[b c]|"], &["a|"], '\u{8}', arena)?; - assert_insert(&["[abc]|\n", ""], &["|\n", ""], '\u{8}', arena)?; - assert_insert(&["a\n", "[abc]|"], &["a\n", "|"], '\u{8}', arena)?; - assert_insert(&["[a\n", "abc]|"], &["|"], '\u{8}', arena)?; - assert_insert(&["a[b\n", "cdef ghij]|"], &["a|"], '\u{8}', arena)?; - assert_insert(&["[a\n", "b\n", "c]|"], &["|"], '\u{8}', arena)?; - assert_insert(&["a\n", "[b\n", "]|"], &["a\n", "|"], '\u{8}', arena)?; - assert_insert( - &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], - &["abc\n", "d|\n", "jkl"], - '\u{8}', - arena, - )?; - assert_insert( - &["abc\n", "[def\n", "ghi]|\n", "jkl"], - &["abc\n", "|\n", "jkl"], - '\u{8}', - arena, - )?; - assert_insert( - &["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"], - &["abc\n", "\n", "|\n", "jkl"], - '\u{8}', - arena, - )?; - assert_insert( - &["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"], - &["|"], - '\u{8}', - arena, - )?; - - Ok(()) - } - - #[test] - fn insert_with_selection() -> Result<(), String> { - let arena = &Bump::new(); - - assert_insert(&["[a]|"], &["z|"], 'z', arena)?; - assert_insert(&["a[a]|"], &["az|"], 'z', arena)?; - assert_insert(&["[aa]|"], &["z|"], 'z', arena)?; - assert_insert(&["a[b c]|"], &["az|"], 'z', arena)?; - assert_insert(&["[abc]|\n", ""], &["z|\n", ""], 'z', arena)?; - assert_insert(&["a\n", "[abc]|"], &["a\n", "z|"], 'z', arena)?; - assert_insert(&["[a\n", "abc]|"], &["z|"], 'z', arena)?; - assert_insert(&["a[b\n", "cdef ghij]|"], &["az|"], 'z', arena)?; - assert_insert(&["[a\n", "b\n", "c]|"], &["z|"], 'z', arena)?; - assert_insert(&["a\n", "[b\n", "]|"], &["a\n", "z|"], 'z', arena)?; - assert_insert( - &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], - &["abc\n", "dz|\n", "jkl"], - 'z', - arena, - )?; - assert_insert( - &["abc\n", "[def\n", "ghi]|\n", "jkl"], - &["abc\n", "z|\n", "jkl"], - 'z', - arena, - )?; - assert_insert( - &["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"], - &["abc\n", "\n", "z|\n", "jkl"], - 'z', - arena, - )?; - assert_insert(&["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"], &["z|"], 'z', arena)?; - - Ok(()) - } - - fn assert_select_all( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - arena: &Bump, - ) -> Result<(), String> { - let pre_big_text = gen_big_text(pre_lines_str)?; - - let app_model = mock_app_model(pre_big_text, None); - let mut ed_model = app_model.ed_model_opt.unwrap(); - - handle_select_all(&mut ed_model).unwrap(); - - let mut big_text_lines = all_lines_vec(&ed_model.text); - let post_lines_str = convert_selection_to_dsl( - ed_model.text.caret_w_select, - &mut big_text_lines, - )?; - - assert_eq!(post_lines_str, expected_post_lines_str); - - Ok(()) - } - - #[test] - fn select_all() -> Result<(), String> { - let arena = &Bump::new(); - - assert_select_all(&["|"], &["|"], arena)?; - assert_select_all(&["|a"], &["[a]|"], arena)?; - assert_select_all(&["a|"], &["[a]|"], arena)?; - assert_select_all(&["abc d|ef ghi"], &["[abc def ghi]|"], arena)?; - assert_select_all(&["[a]|"], &["[a]|"], arena)?; - assert_select_all(&["|[a]"], &["[a]|"], arena)?; - assert_select_all(&["|[abc def ghi]"], &["[abc def ghi]|"], arena)?; - assert_select_all(&["a\n", "[b\n", "]|"], &["[a\n", "b\n", "]|"], arena)?; - assert_select_all(&["a\n", "[b]|\n", ""], &["[a\n", "b\n", "]|"], arena)?; - assert_select_all(&["a\n", "|[b\n", "]"], &["[a\n", "b\n", "]|"], arena)?; - assert_select_all( - &["abc\n", "def\n", "gh|i\n", "jkl"], - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - arena, - )?; - assert_select_all( - &["|[abc\n", "def\n", "ghi\n", "jkl]"], - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - arena, - )?; - - Ok(()) - } - - // TODO hometest - - // TODO endtest -} diff --git a/editor/src/editor/mvc/ed_view.rs b/editor/src/editor/mvc/ed_view.rs index 31e857c7c5..d7b3c2dd9a 100644 --- a/editor/src/editor/mvc/ed_view.rs +++ b/editor/src/editor/mvc/ed_view.rs @@ -1,12 +1,9 @@ -use super::ed_model::{EdModel}; -use crate::ui::ui_error::{MissingGlyphDims}; -use crate::ui::ui_error::UIResult; -use crate::ui::colors::CARET_COLOR; +use super::ed_model::EdModel; use crate::graphics::primitives::rect::Rect; -use crate::ui::text::{ - selection::create_selection_rects, - text_pos::TextPos, -}; +use crate::ui::colors::CARET_COLOR; +use crate::ui::text::{selection::create_selection_rects, text_pos::TextPos}; +use crate::ui::ui_error::MissingGlyphDims; +use crate::ui::ui_error::UIResult; use bumpalo::collections::Vec as BumpVec; use bumpalo::Bump; use snafu::ensure; diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index af950775db..b8b26ab0c2 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -1,5 +1,4 @@ pub mod app_model; pub mod app_update; pub mod ed_model; -pub mod ed_update; pub mod ed_view; diff --git a/editor/src/editor/render_ast.rs b/editor/src/editor/render_ast.rs index 00fb183e5e..24d075f41f 100644 --- a/editor/src/editor/render_ast.rs +++ b/editor/src/editor/render_ast.rs @@ -1,7 +1,4 @@ -use cgmath::Vector2; -use wgpu_glyph::GlyphBrush; -use winit::dpi::PhysicalSize; -use crate::ui::colors::CODE_COLOR; +use crate::editor::colors::CODE_COLOR; use crate::{ graphics::{ primitives::text::{queue_code_text_draw, Text}, @@ -9,6 +6,9 @@ use crate::{ }, lang::{ast::Expr2, expr::Env}, }; +use cgmath::Vector2; +use wgpu_glyph::GlyphBrush; +use winit::dpi::PhysicalSize; pub fn render_expr2<'a>( env: &mut Env<'a>, diff --git a/editor/src/graphics/syntax_highlight.rs b/editor/src/editor/syntax_highlight.rs similarity index 86% rename from editor/src/graphics/syntax_highlight.rs rename to editor/src/editor/syntax_highlight.rs index a675499fe9..1901c38d86 100644 --- a/editor/src/graphics/syntax_highlight.rs +++ b/editor/src/editor/syntax_highlight.rs @@ -1,6 +1,7 @@ -use crate::ui::colors; -use colors::ColorTup; +use crate::editor::colors as ed_colors; +use crate::graphics::colors as gr_colors; use crate::graphics::primitives; +use gr_colors::ColorTup; //TODO optimize memory allocation //TODO this is a demo function, the AST should be used for highlighting, see #904. @@ -10,16 +11,16 @@ pub fn highlight_code( ) { let split_code = split_inclusive(&code_text.text); - let mut active_color = colors::WHITE; + let mut active_color = gr_colors::WHITE; let mut same_type_str = String::new(); for token_seq in split_code { let new_word_color = if token_seq.contains(&'\"'.to_string()) { - colors::CODE_COLOR + ed_colors::STRING_SYNTAX_COL } else if token_seq.contains(&'='.to_string()) { - colors::BLACK + ed_colors::EQUALS_SYNTAX_COL } else { - colors::WHITE + gr_colors::WHITE }; if new_word_color != active_color { diff --git a/editor/src/editor/util.rs b/editor/src/editor/util.rs index c1aa2eec11..2c5facf619 100644 --- a/editor/src/editor/util.rs +++ b/editor/src/editor/util.rs @@ -1,14 +1,7 @@ - use super::ed_error::{EdResult, OutOfBounds}; use snafu::OptionExt; use std::slice::SliceIndex; -pub fn is_newline(char_ref: &char) -> bool { - let newline_codes = vec!['\u{d}', '\n']; - - newline_codes.contains(char_ref) -} - // replace vec methods that return Option with ones that return Result and proper Error pub fn slice_get(index: usize, slice: &[T]) -> EdResult<&>::Output> { let elt_ref = slice.get(index).context(OutOfBounds { diff --git a/editor/src/graphics/colors.rs b/editor/src/graphics/colors.rs new file mode 100644 index 0000000000..6ee6ffee0a --- /dev/null +++ b/editor/src/graphics/colors.rs @@ -0,0 +1,15 @@ +pub type ColorTup = (f32, f32, f32, f32); +pub const WHITE: ColorTup = (1.0, 1.0, 1.0, 1.0); + +pub fn to_wgpu_color((r, g, b, a): ColorTup) -> wgpu::Color { + wgpu::Color { + r: r as f64, + g: g as f64, + b: b as f64, + a: a as f64, + } +} + +pub fn to_slice((r, g, b, a): ColorTup) -> [f32; 4] { + [r, g, b, a] +} diff --git a/editor/src/graphics/lowlevel/buffer.rs b/editor/src/graphics/lowlevel/buffer.rs index 3f39e97c89..a3e8fc0a42 100644 --- a/editor/src/graphics/lowlevel/buffer.rs +++ b/editor/src/graphics/lowlevel/buffer.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/sotrh/learn-wgpu // by Benjamin Hansen, licensed under the MIT license use super::vertex::Vertex; -use crate::ui::colors::to_slice; +use crate::graphics::colors::to_slice; use crate::graphics::primitives::rect::Rect; use bumpalo::collections::Vec as BumpVec; use wgpu::util::{BufferInitDescriptor, DeviceExt}; diff --git a/editor/src/graphics/mod.rs b/editor/src/graphics/mod.rs index 352a538010..0eb7fcd6da 100644 --- a/editor/src/graphics/mod.rs +++ b/editor/src/graphics/mod.rs @@ -1,4 +1,4 @@ +pub mod colors; pub mod lowlevel; pub mod primitives; pub mod style; -mod syntax_highlight; diff --git a/editor/src/graphics/primitives/text.rs b/editor/src/graphics/primitives/text.rs index b52a3c8f97..25672adc29 100644 --- a/editor/src/graphics/primitives/text.rs +++ b/editor/src/graphics/primitives/text.rs @@ -2,12 +2,12 @@ // by Benjamin Hansen, licensed under the MIT license use super::rect::Rect; -use crate::ui::colors; +use crate::editor::syntax_highlight; +use crate::graphics::colors; +use crate::graphics::colors::ColorTup; use crate::graphics::style::{CODE_FONT_SIZE, CODE_TXT_XY}; -use crate::graphics::syntax_highlight; use ab_glyph::{FontArc, Glyph, InvalidFont}; use cgmath::{Vector2, Vector4}; -use colors::{ColorTup, CODE_COLOR, WHITE}; use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, GlyphCruncher, Section}; #[derive(Debug)] @@ -40,7 +40,7 @@ pub fn example_code_glyph_rect(glyph_brush: &mut GlyphBrush<()>) -> Rect { let code_text = Text { position: CODE_TXT_XY.into(), area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(), - color: CODE_COLOR.into(), + color: (1.0, 1.0, 1.0, 1.0).into(), text: "a", size: CODE_FONT_SIZE, ..Default::default() @@ -145,7 +145,7 @@ fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect { top_left_coords: [position.x, top_y].into(), width, height, - color: WHITE, + color: colors::WHITE, } } diff --git a/editor/src/lang/roc_file.rs b/editor/src/lang/roc_file.rs index 2bf7878320..676c74048b 100644 --- a/editor/src/lang/roc_file.rs +++ b/editor/src/lang/roc_file.rs @@ -98,9 +98,9 @@ impl<'a> File<'a> { #[cfg(test)] mod test_file { + use crate::lang::roc_file; use bumpalo::Bump; use std::path::Path; - use crate::lang::roc_file; #[test] fn read_and_fmt_simple_roc_module() { diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 8cc96b6410..93a6f6ed69 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -2,15 +2,15 @@ // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. #![allow(clippy::large_enum_variant)] +#[cfg_attr(test, macro_use)] +extern crate indoc; extern crate pest; #[cfg_attr(test, macro_use)] extern crate pest_derive; -#[cfg_attr(test, macro_use)] -extern crate indoc; -mod graphics; -mod lang; mod editor; +mod graphics; +pub mod lang; //TODO remove pub for unused warnings mod ui; use std::io; @@ -18,4 +18,4 @@ use std::path::Path; pub fn launch(filepaths: &[&Path]) -> io::Result<()> { editor::main::launch(filepaths) -} \ No newline at end of file +} diff --git a/editor/src/ui/colors.rs b/editor/src/ui/colors.rs index 32445c66e9..6c99b07447 100644 --- a/editor/src/ui/colors.rs +++ b/editor/src/ui/colors.rs @@ -1,21 +1,8 @@ -pub type ColorTup = (f32, f32, f32, f32); -pub const WHITE: ColorTup = (1.0, 1.0, 1.0, 1.0); -pub const BLACK: ColorTup = (0.0, 0.0, 0.0, 1.0); +use crate::graphics::colors as gr_colors; +use gr_colors::ColorTup; + +pub const LIGHT_BRAND_COL: ColorTup = (0.506, 0.337, 0.902, 1.0); +//pub const DARK_BRAND_COL: ColorTup = (0.380, 0.169, 0.871, 1.0); pub const TXT_COLOR: ColorTup = (1.0, 1.0, 1.0, 1.0); -pub const CODE_COLOR: ColorTup = (0.21, 0.55, 0.83, 1.0); -pub const CARET_COLOR: ColorTup = WHITE; +pub const CARET_COLOR: ColorTup = gr_colors::WHITE; pub const SELECT_COLOR: ColorTup = (0.45, 0.61, 1.0, 1.0); -pub const BG_COLOR: ColorTup = (0.11, 0.11, 0.13, 1.0); - -pub fn to_wgpu_color((r, g, b, a): ColorTup) -> wgpu::Color { - wgpu::Color { - r: r as f64, - g: g as f64, - b: b as f64, - a: a as f64, - } -} - -pub fn to_slice((r, g, b, a): ColorTup) -> [f32; 4] { - [r, g, b, a] -} diff --git a/editor/src/ui/mod.rs b/editor/src/ui/mod.rs index e07da16887..df364f3ad3 100644 --- a/editor/src/ui/mod.rs +++ b/editor/src/ui/mod.rs @@ -1,4 +1,4 @@ -pub mod ui_error; pub mod colors; pub mod text; -mod util; \ No newline at end of file +pub mod ui_error; +mod util; diff --git a/editor/src/ui/text/big_selectable_text.rs b/editor/src/ui/text/big_selectable_text.rs index 90298eb534..499020e7cf 100644 --- a/editor/src/ui/text/big_selectable_text.rs +++ b/editor/src/ui/text/big_selectable_text.rs @@ -1,26 +1,29 @@ // Adapted from https://github.com/cessen/ropey by Nathan Vegdahl, licensed under the MIT license +use crate::ui::text::{ + caret_w_select::CaretWSelect, + lines::{Lines, MutSelectableLines, SelectableLines}, + selection::{validate_raw_sel, validate_selection, RawSelection, Selection}, + text_pos::TextPos, +}; use crate::ui::ui_error::{ + OutOfBounds, UIError::{FileOpenFailed, TextBufReadFailed}, UIResult, - OutOfBounds, -}; -use crate::ui::text::{ - text_pos::{TextPos}, - selection::{Selection, RawSelection, validate_raw_sel}, - caret_w_select::{CaretWSelect}, - lines::{Lines, SelectableLines, MutSelectableLines}, }; +use crate::ui::util::is_newline; use bumpalo::collections::String as BumpString; use bumpalo::Bump; use ropey::Rope; -use snafu::{ensure}; +use snafu::ensure; use std::{ + cmp::{max, min}, fmt, fs::File, io, - path::Path + path::Path, }; +use winit::event::{ModifiersState, VirtualKeyCode, VirtualKeyCode::*}; pub struct BigSelectableText { pub caret_w_select: CaretWSelect, @@ -30,7 +33,6 @@ pub struct BigSelectableText { } impl BigSelectableText { - fn check_bounds(&self, char_indx: usize) -> UIResult<()> { ensure!( char_indx <= self.text_rope.len_chars(), @@ -66,6 +68,10 @@ impl BigSelectableText { } } +fn validate_sel_opt(start_pos: TextPos, end_pos: TextPos) -> UIResult> { + Ok(Some(validate_selection(start_pos, end_pos)?)) +} + impl Lines for BigSelectableText { fn get_line(&self, line_nr: usize) -> UIResult<&str> { ensure!( @@ -77,7 +83,6 @@ impl Lines for BigSelectableText { } ); - let rope_slice = self.text_rope.line(line_nr); if let Some(line_str_ref) = rope_slice.as_str() { @@ -88,7 +93,6 @@ impl Lines for BigSelectableText { let arena_str_ref = self.arena.alloc(line_str); Ok(arena_str_ref) } - } fn line_len(&self, line_nr: usize) -> UIResult { @@ -125,47 +129,43 @@ impl SelectableLines for BigSelectableText { self.caret_w_select.caret_pos = caret_pos; } - pub fn move_caret_left( - &mut self, - shift_pressed: bool, - ) -> UIResult { - let old_selection_opt = big_text.get_selection(); - let old_caret_pos = big_text.caret_w_select.caret_pos; + fn move_caret_left(&mut self, shift_pressed: bool) -> UIResult<()> { + let old_selection_opt = self.get_selection(); + let old_caret_pos = self.caret_w_select.caret_pos; let old_line_nr = old_caret_pos.line; let old_col_nr = old_caret_pos.column; - + let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { match old_selection_opt { - Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column), + Some(old_selection) => { + (old_selection.start_pos.line, old_selection.start_pos.column) + } None => unreachable!(), } } else if old_col_nr == 0 { if old_line_nr == 0 { (0, 0) } else { - let curr_line_len = big_text.line_len(old_line_nr - 1)?; - + let curr_line_len = self.line_len(old_line_nr - 1)?; + (old_line_nr - 1, curr_line_len - 1) } } else { (old_line_nr, old_col_nr - 1) }; - + let new_caret_pos = TextPos { line: line_nr, column: col_nr, }; - + let new_selection_opt = if shift_pressed { if let Some(old_selection) = old_selection_opt { if old_caret_pos >= old_selection.end_pos { if new_caret_pos == old_selection.start_pos { None } else { - validate_sel_opt( - old_selection.start_pos, - new_caret_pos, - )? + validate_sel_opt(old_selection.start_pos, new_caret_pos)? } } else { validate_sel_opt( @@ -193,27 +193,26 @@ impl SelectableLines for BigSelectableText { } else { None }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) + + self.caret_w_select = CaretWSelect::new(new_caret_pos, new_selection_opt); + + Ok(()) } - - pub fn move_caret_right( - &mut self, - shift_pressed: bool, - ) -> UIResult { - let old_selection_opt = big_text.get_selection(); - let old_caret_pos = big_text.caret_w_select.caret_pos; + + fn move_caret_right(&mut self, shift_pressed: bool) -> UIResult<()> { + let old_selection_opt = self.get_selection(); + let old_caret_pos = self.caret_w_select.caret_pos; let old_line_nr = old_caret_pos.line; let old_col_nr = old_caret_pos.column; - + let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { match old_selection_opt { Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column), None => unreachable!(), } } else { - let curr_line = big_text.get_line(old_line_nr)?; - + let curr_line = self.get_line(old_line_nr)?; + if let Some(last_char) = curr_line.chars().last() { if is_newline(&last_char) { if old_col_nr + 1 > curr_line.len() - 1 { @@ -230,22 +229,19 @@ impl SelectableLines for BigSelectableText { (old_line_nr, old_col_nr) } }; - + let new_caret_pos = TextPos { line: line_nr, column: col_nr, }; - + let new_selection_opt = if shift_pressed { if let Some(old_selection) = old_selection_opt { if old_caret_pos <= old_selection.start_pos { if new_caret_pos == old_selection.end_pos { None } else { - validate_sel_opt( - new_caret_pos, - old_selection.end_pos, - )? + validate_sel_opt(new_caret_pos, old_selection.end_pos)? } } else { validate_sel_opt( @@ -273,41 +269,42 @@ impl SelectableLines for BigSelectableText { } else { None }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) + + self.caret_w_select = CaretWSelect::new(new_caret_pos, new_selection_opt); + + Ok(()) } - - pub fn move_caret_up( - &mut self, - shift_pressed: bool, - ) -> UIResult { - let old_selection_opt = big_text.get_selection(); - let old_caret_pos = big_text.caret_w_select.caret_pos; + + fn move_caret_up(&mut self, shift_pressed: bool) -> UIResult<()> { + let old_selection_opt = self.get_selection(); + let old_caret_pos = self.caret_w_select.caret_pos; let old_line_nr = old_caret_pos.line; let old_col_nr = old_caret_pos.column; - + let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { match old_selection_opt { - Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column), + Some(old_selection) => { + (old_selection.start_pos.line, old_selection.start_pos.column) + } None => unreachable!(), } } else if old_line_nr == 0 { (old_line_nr, 0) } else { - let prev_line_len = big_text.line_len(old_line_nr - 1)?; - + let prev_line_len = self.line_len(old_line_nr - 1)?; + if prev_line_len <= old_col_nr { (old_line_nr - 1, prev_line_len - 1) } else { (old_line_nr - 1, old_col_nr) } }; - + let new_caret_pos = TextPos { line: line_nr, column: col_nr, }; - + let new_selection_opt = if shift_pressed { if let Some(old_selection) = old_selection_opt { if old_selection.end_pos <= old_caret_pos { @@ -320,10 +317,7 @@ impl SelectableLines for BigSelectableText { )? } } else { - validate_sel_opt( - new_caret_pos, - old_selection.end_pos, - )? + validate_sel_opt(new_caret_pos, old_selection.end_pos)? } } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { validate_sel_opt( @@ -336,31 +330,30 @@ impl SelectableLines for BigSelectableText { } else { None }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) + + self.caret_w_select = CaretWSelect::new(new_caret_pos, new_selection_opt); + + Ok(()) } - - pub fn move_caret_down( - &mut self, - shift_pressed: bool, - ) -> UIResult { - let old_selection_opt = big_text.get_selection(); - let old_caret_pos = big_text.caret_w_select.caret_pos; + + fn move_caret_down(&mut self, shift_pressed: bool) -> UIResult<()> { + let old_selection_opt = self.get_selection(); + let old_caret_pos = self.caret_w_select.caret_pos; let old_line_nr = old_caret_pos.line; let old_col_nr = old_caret_pos.column; - + let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed { match old_selection_opt { Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column), None => unreachable!(), } - } else if old_line_nr + 1 >= big_text.nr_of_lines() { - let curr_line_len = big_text.line_len(old_line_nr)?; - + } else if old_line_nr + 1 >= self.nr_of_lines() { + let curr_line_len = self.line_len(old_line_nr)?; + (old_line_nr, curr_line_len) } else { - let next_line = big_text.get_line(old_line_nr + 1)?; - + let next_line = self.get_line(old_line_nr + 1)?; + if next_line.len() <= old_col_nr { if let Some(last_char) = next_line.chars().last() { if is_newline(&last_char) { @@ -375,12 +368,12 @@ impl SelectableLines for BigSelectableText { (old_line_nr + 1, old_col_nr) } }; - + let new_caret_pos = TextPos { line: line_nr, column: col_nr, }; - + let new_selection_opt = if shift_pressed { if let Some(old_selection) = old_selection_opt { if old_caret_pos <= old_selection.start_pos { @@ -393,10 +386,7 @@ impl SelectableLines for BigSelectableText { )? } } else { - validate_sel_opt( - old_selection.start_pos, - new_caret_pos, - )? + validate_sel_opt(old_selection.start_pos, new_caret_pos)? } } else if !(old_line_nr == line_nr && old_col_nr == col_nr) { validate_sel_opt( @@ -409,8 +399,10 @@ impl SelectableLines for BigSelectableText { } else { None }; - - Ok(CaretWSelect::new(new_caret_pos, new_selection_opt)) + + self.caret_w_select = CaretWSelect::new(new_caret_pos, new_selection_opt); + + Ok(()) } fn get_selection(&self) -> Option { @@ -452,6 +444,21 @@ impl SelectableLines for BigSelectableText { self.caret_w_select.selection_opt = None; } + fn select_all(&mut self) -> UIResult<()> { + if self.nr_of_chars() > 0 { + let last_pos = self.last_text_pos(); + + self.set_raw_sel(RawSelection { + start_pos: TextPos { line: 0, column: 0 }, + end_pos: last_pos, + })?; + + self.set_caret(last_pos); + } + + Ok(()) + } + fn last_text_pos(&self) -> TextPos { self.char_indx_to_pos(self.nr_of_chars()) } @@ -459,7 +466,52 @@ impl SelectableLines for BigSelectableText { impl MutSelectableLines for BigSelectableText { fn insert_char(&mut self, new_char: &char) -> UIResult<()> { - self.insert_str(&new_char.to_string()) + if self.is_selection_active() { + self.del_selection()?; + } + + self.insert_str(&new_char.to_string())?; + + if is_newline(new_char) { + self.set_caret(TextPos { + line: self.caret_w_select.caret_pos.line + 1, + column: 0, + }); + } else { + self.move_caret_right(false)?; + } + + self.set_sel_none(); + + Ok(()) + } + + fn handle_new_char(&mut self, received_char: &char) -> UIResult<()> { + match received_char { + '\u{8}' | '\u{7f}' => { + // On Linux, '\u{8}' is backspace, + // on macOS '\u{7f}'. + + self.pop_char()? + } + + '\u{1}' // Ctrl + A + | '\u{3}' // Ctrl + C + | '\u{16}' // Ctrl + V + | '\u{18}' // Ctrl + X + | '\u{e000}'..='\u{f8ff}' // http://www.unicode.org/faq/private_use.html + | '\u{f0000}'..='\u{ffffd}' // ^ + | '\u{100000}'..='\u{10fffd}' // ^ + => { + // chars that can be ignored + } + + _ => { + self.insert_char(received_char)?; + } + } + + Ok(()) } fn insert_str(&mut self, new_str: &str) -> UIResult<()> { @@ -473,15 +525,21 @@ impl MutSelectableLines for BigSelectableText { Ok(()) } - fn pop_char(&mut self) { - let old_caret_pos = self.caret_w_select.caret_pos; - let char_indx = self.pos_to_char_indx(old_caret_pos); + fn pop_char(&mut self) -> UIResult<()> { + if self.is_selection_active() { + self.del_selection()?; + } else { + let old_caret_pos = self.caret_w_select.caret_pos; + let char_indx = self.pos_to_char_indx(old_caret_pos); - if (char_indx > 0) && char_indx <= self.text_rope.len_chars() { - self.text_rope.remove((char_indx - 1)..char_indx); + self.move_caret_left(false)?; + + if (char_indx > 0) && char_indx <= self.text_rope.len_chars() { + self.text_rope.remove((char_indx - 1)..char_indx); + } } - self.move_caret_left(None) + Ok(()) } fn del_selection(&mut self) -> UIResult<()> { @@ -489,14 +547,87 @@ impl MutSelectableLines for BigSelectableText { let (start_char_indx, end_char_indx) = self.sel_to_tup(selection)?; self.check_bounds(end_char_indx)?; - + self.text_rope.remove(start_char_indx..end_char_indx); self.set_caret(selection.start_pos); + + self.set_sel_none(); } Ok(()) } + + fn handle_key_down( + &mut self, + modifiers: &ModifiersState, + virtual_keycode: VirtualKeyCode, + ) -> UIResult<()> { + let shift_pressed = modifiers.shift(); + + match virtual_keycode { + Left => self.move_caret_left(shift_pressed), + Up => self.move_caret_up(shift_pressed), + Right => self.move_caret_right(shift_pressed), + Down => self.move_caret_down(shift_pressed), + + A => { + if modifiers.ctrl() { + self.select_all() + } else { + Ok(()) + } + } + Home => { + let curr_line_nr = self.caret_w_select.caret_pos.line; + // TODO no unwrap + let curr_line_str = self.get_line(curr_line_nr).unwrap(); + let line_char_iter = curr_line_str.chars(); + + let mut first_no_space_char_col = 0; + let mut non_space_found = false; + + for c in line_char_iter { + if !c.is_whitespace() { + non_space_found = true; + break; + } else { + first_no_space_char_col += 1; + } + } + + if !non_space_found { + first_no_space_char_col = 0; + } + + self.caret_w_select.move_caret_w_mods( + TextPos { + line: curr_line_nr, + column: first_no_space_char_col, + }, + modifiers, + ) + } + End => { + let curr_line_nr = self.caret_w_select.caret_pos.line; + let curr_line_len = self.line_len(curr_line_nr)?; + // TODO no unwrap + let new_col = if curr_line_len > 0 { + curr_line_len - 1 + } else { + 0 + }; + + let new_pos = TextPos { + line: curr_line_nr, + column: new_col, + }; + + self.caret_w_select.move_caret_w_mods(new_pos, modifiers) + } + _ => Ok(()), + } + } } pub fn from_path(path: &Path) -> UIResult { @@ -549,3 +680,2308 @@ impl fmt::Debug for BigSelectableText { .finish() } } + +#[cfg(test)] +pub mod test_big_sel_text { + use crate::ui::text::{ + big_selectable_text, + big_selectable_text::BigSelectableText, + caret_w_select::CaretWSelect, + lines::{Lines, MutSelectableLines, SelectableLines}, + selection::validate_selection, + text_pos::TextPos, + }; + use crate::ui::ui_error::{OutOfBounds, UIResult}; + use core::cmp::Ordering; + use pest::Parser; + use snafu::OptionExt; + use std::{ + collections::HashMap, fs, io::Write, path::Path, slice::SliceIndex, time::SystemTime, + }; + + // replace vec methods that return Option with ones that return Result and proper Error + pub fn slice_get( + index: usize, + slice: &[T], + ) -> UIResult<&>::Output> { + let elt_ref = slice.get(index).context(OutOfBounds { + index, + collection_name: "Slice", + len: slice.len(), + })?; + + Ok(elt_ref) + } + + #[derive(Parser)] + #[grammar = "../tests/selection.pest"] + pub struct LineParser; + + // show selection and caret position as symbols in lines for easy testing + pub fn convert_selection_to_dsl( + caret_w_select: CaretWSelect, + lines: &mut [String], + ) -> UIResult<&[String]> { + let selection_opt = caret_w_select.selection_opt; + let caret_pos = caret_w_select.caret_pos; + + if let Some(sel) = selection_opt { + let mut to_insert = vec![(sel.start_pos, '['), (sel.end_pos, ']'), (caret_pos, '|')]; + let symbol_map: HashMap = + [('[', 2), (']', 0), ('|', 1)].iter().cloned().collect(); + + // sort for nice printing + to_insert.sort_by(|a, b| { + let pos_cmp = a.0.cmp(&b.0); + if pos_cmp == Ordering::Equal { + symbol_map.get(&a.1).cmp(&symbol_map.get(&b.1)) + } else { + pos_cmp + } + }); + + // insert symbols into text lines + for i in 0..to_insert.len() { + let (pos, insert_char) = *slice_get(i, &to_insert)?; + + insert_at_pos(lines, pos, insert_char)?; + + // shift position of following symbols now that symbol is inserted + for j in i..to_insert.len() { + let (old_pos, _) = get_mut_res(j, &mut to_insert)?; + + if old_pos.line == pos.line { + old_pos.column += 1; + } + } + } + } else { + insert_at_pos(lines, caret_pos, '|')?; + } + + Ok(lines) + } + + fn insert_at_pos(lines: &mut [String], pos: TextPos, insert_char: char) -> UIResult<()> { + let line = get_mut_res(pos.line, lines)?; + line.insert(pos.column, insert_char); + + Ok(()) + } + + // It's much nicer to have get_mut return a Result with clear error than an Option + fn get_mut_res( + index: usize, + vec: &mut [T], + ) -> UIResult<&mut >::Output> { + let vec_len = vec.len(); + + let elt_ref = vec.get_mut(index).context(OutOfBounds { + index, + collection_name: "Slice", + len: vec_len, + })?; + + Ok(elt_ref) + } + + fn big_text_from_str(lines_str: &str) -> BigSelectableText { + let epoch_time = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_nanos(); + let file_name = format!("temp_{:?}.txt", epoch_time); + let path = Path::new(&file_name); + + let mut temp_file = fs::File::create(path).unwrap(); + temp_file.write_all(lines_str.as_bytes()).unwrap(); + + let big_text = big_selectable_text::from_path(path).unwrap(); + fs::remove_file(path).unwrap(); + + big_text + } + + pub fn big_text_from_dsl_str(lines: &[String]) -> BigSelectableText { + big_text_from_str( + &lines + .iter() + .map(|line| line.replace(&['[', ']', '|'][..], "")) + .collect::>() + .join(""), + ) + } + + pub fn all_lines_vec(big_sel_text: &BigSelectableText) -> Vec { + let mut lines: Vec = Vec::new(); + + for i in 0..big_sel_text.nr_of_lines() { + lines.push(big_sel_text.get_line(i).unwrap().to_string()); + } + + lines + } + + // Retrieve selection and position from formatted string + pub fn convert_dsl_to_selection(lines: &[String]) -> Result { + let lines_str: String = lines.join(""); + + let parsed = LineParser::parse(Rule::linesWithSelect, &lines_str) + .expect("Selection test DSL parsing failed"); + + let mut caret_opt: Option<(usize, usize)> = None; + let mut sel_start_opt: Option<(usize, usize)> = None; + let mut sel_end_opt: Option<(usize, usize)> = None; + let mut line_nr = 0; + let mut col_nr = 0; + + for line in parsed { + for elt in line.into_inner() { + match elt.as_rule() { + Rule::optCaret => { + if elt.as_span().as_str() == "|" { + if caret_opt.is_some() { + return Err( + "Multiple carets found, there should be only one".to_owned() + ); + } else { + caret_opt = Some((line_nr, col_nr)); + } + } + } + Rule::optSelStart => { + if sel_start_opt.is_some() { + if elt.as_span().as_str() == "[" { + return Err("Found start of selection more than once, there should be only one".to_owned()); + } + } else if elt.as_span().as_str() == "[" { + sel_start_opt = Some((line_nr, col_nr)); + } + } + Rule::optSelEnd => { + if sel_end_opt.is_some() { + if elt.as_span().as_str() == "]" { + return Err("Found end of selection more than once, there should be only one".to_owned()); + } + } else if elt.as_span().as_str() == "]" { + sel_end_opt = Some((line_nr, col_nr)); + } + } + Rule::text => { + let split_str = elt + .as_span() + .as_str() + .split('\n') + .into_iter() + .collect::>(); + + if split_str.len() > 1 { + line_nr += split_str.len() - 1; + col_nr = 0 + } + if let Some(last_str) = split_str.last() { + col_nr += last_str.len() + } + } + _ => {} + } + } + } + + // Make sure return makes sense + if let Some((line, column)) = caret_opt { + let caret_pos = TextPos { line, column }; + if sel_start_opt.is_none() && sel_end_opt.is_none() { + Ok(CaretWSelect::new(caret_pos, None)) + } else if let Some((start_line, start_column)) = sel_start_opt { + if let Some((end_line, end_column)) = sel_end_opt { + Ok(CaretWSelect::new( + caret_pos, + Some( + validate_selection( + TextPos { + line: start_line, + column: start_column, + }, + TextPos { + line: end_line, + column: end_column, + }, + ) + .unwrap(), + ), + )) + } else { + Err("Selection end ']' was not found, but selection start '[' was. Bad input string.".to_owned()) + } + } else { + Err("Selection start '[' was not found, but selection end ']' was. Bad input string.".to_owned()) + } + } else { + Err("No caret was found in lines.".to_owned()) + } + } + + pub fn gen_big_text(lines: &[&str]) -> Result { + let lines_string_slice: Vec = lines.iter().map(|l| l.to_string()).collect(); + let mut big_text = big_text_from_dsl_str(&lines_string_slice); + let caret_w_select = convert_dsl_to_selection(&lines_string_slice).unwrap(); + + big_text.caret_w_select = caret_w_select; + + Ok(big_text) + } + + fn assert_insert( + pre_lines_str: &[&str], + expected_post_lines_str: &[&str], + new_char: char, + ) -> Result<(), String> { + let mut big_text = gen_big_text(pre_lines_str)?; + + if let Err(e) = big_text.handle_new_char(&new_char) { + return Err(e.to_string()); + } + + let mut actual_lines = all_lines_vec(&big_text); + let dsl_slice = + convert_selection_to_dsl(big_text.caret_w_select, &mut actual_lines).unwrap(); + assert_eq!(dsl_slice, expected_post_lines_str); + + Ok(()) + } + + #[test] + fn insert_new_char_simple() -> Result<(), String> { + assert_insert(&["|"], &["a|"], 'a')?; + assert_insert(&["|"], &[" |"], ' ')?; + assert_insert(&["a|"], &["aa|"], 'a')?; + assert_insert(&["a|"], &["a |"], ' ')?; + assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b')?; + assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b')?; + assert_insert(&["a\n", "|"], &["a\n", "b|"], 'b')?; + assert_insert(&["a\n", "b\n", "c|"], &["a\n", "b\n", "cd|"], 'd')?; + + Ok(()) + } + + #[test] + fn insert_new_char_mid() -> Result<(), String> { + assert_insert(&["ab|d"], &["abc|d"], 'c')?; + assert_insert(&["a|cd"], &["ab|cd"], 'b')?; + assert_insert(&["abc\n", "|e"], &["abc\n", "d|e"], 'd')?; + assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", "g| "], 'g')?; + assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", " | "], ' ')?; + + Ok(()) + } + + #[test] + fn simple_backspace() -> Result<(), String> { + assert_insert(&["|"], &["|"], '\u{8}')?; + assert_insert(&[" |"], &["|"], '\u{8}')?; + assert_insert(&["a|"], &["|"], '\u{8}')?; + assert_insert(&["ab|"], &["a|"], '\u{8}')?; + assert_insert(&["a|\n", ""], &["|\n", ""], '\u{8}')?; + assert_insert(&["ab|\n", ""], &["a|\n", ""], '\u{8}')?; + assert_insert(&["a\n", "|"], &["a|"], '\u{8}')?; + assert_insert(&["a\n", "b\n", "c|"], &["a\n", "b\n", "|"], '\u{8}')?; + assert_insert(&["a\n", "b\n", "|"], &["a\n", "b|"], '\u{8}')?; + + Ok(()) + } + + #[test] + fn selection_backspace() -> Result<(), String> { + assert_insert(&["[a]|"], &["|"], '\u{8}')?; + assert_insert(&["a[a]|"], &["a|"], '\u{8}')?; + assert_insert(&["[aa]|"], &["|"], '\u{8}')?; + assert_insert(&["a[b c]|"], &["a|"], '\u{8}')?; + assert_insert(&["[abc]|\n", ""], &["|\n", ""], '\u{8}')?; + assert_insert(&["a\n", "[abc]|"], &["a\n", "|"], '\u{8}')?; + assert_insert(&["[a\n", "abc]|"], &["|"], '\u{8}')?; + assert_insert(&["a[b\n", "cdef ghij]|"], &["a|"], '\u{8}')?; + assert_insert(&["[a\n", "b\n", "c]|"], &["|"], '\u{8}')?; + assert_insert(&["a\n", "[b\n", "]|"], &["a\n", "|"], '\u{8}')?; + assert_insert( + &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], + &["abc\n", "d|\n", "jkl"], + '\u{8}', + )?; + assert_insert( + &["abc\n", "[def\n", "ghi]|\n", "jkl"], + &["abc\n", "|\n", "jkl"], + '\u{8}', + )?; + assert_insert( + &["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"], + &["abc\n", "\n", "|\n", "jkl"], + '\u{8}', + )?; + assert_insert( + &["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"], + &["|"], + '\u{8}', + )?; + + Ok(()) + } + + #[test] + fn insert_with_selection() -> Result<(), String> { + assert_insert(&["[a]|"], &["z|"], 'z')?; + assert_insert(&["a[a]|"], &["az|"], 'z')?; + assert_insert(&["[aa]|"], &["z|"], 'z')?; + assert_insert(&["a[b c]|"], &["az|"], 'z')?; + assert_insert(&["[abc]|\n", ""], &["z|\n", ""], 'z')?; + assert_insert(&["a\n", "[abc]|"], &["a\n", "z|"], 'z')?; + assert_insert(&["[a\n", "abc]|"], &["z|"], 'z')?; + assert_insert(&["a[b\n", "cdef ghij]|"], &["az|"], 'z')?; + assert_insert(&["[a\n", "b\n", "c]|"], &["z|"], 'z')?; + assert_insert(&["a\n", "[b\n", "]|"], &["a\n", "z|"], 'z')?; + assert_insert( + &["abc\n", "d[ef\n", "ghi]|\n", "jkl"], + &["abc\n", "dz|\n", "jkl"], + 'z', + )?; + assert_insert( + &["abc\n", "[def\n", "ghi]|\n", "jkl"], + &["abc\n", "z|\n", "jkl"], + 'z', + )?; + assert_insert( + &["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"], + &["abc\n", "\n", "z|\n", "jkl"], + 'z', + )?; + assert_insert(&["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"], &["z|"], 'z')?; + + Ok(()) + } + + fn assert_select_all( + pre_lines_str: &[&str], + expected_post_lines_str: &[&str], + ) -> Result<(), String> { + let mut big_text = gen_big_text(pre_lines_str)?; + + big_text.select_all().unwrap(); + + let mut big_text_lines = all_lines_vec(&big_text); + let post_lines_str = + convert_selection_to_dsl(big_text.caret_w_select, &mut big_text_lines)?; + + assert_eq!(post_lines_str, expected_post_lines_str); + + Ok(()) + } + + #[test] + fn select_all() -> Result<(), String> { + assert_select_all(&["|"], &["|"])?; + assert_select_all(&["|a"], &["[a]|"])?; + assert_select_all(&["a|"], &["[a]|"])?; + assert_select_all(&["abc d|ef ghi"], &["[abc def ghi]|"])?; + assert_select_all(&["[a]|"], &["[a]|"])?; + assert_select_all(&["|[a]"], &["[a]|"])?; + assert_select_all(&["|[abc def ghi]"], &["[abc def ghi]|"])?; + assert_select_all(&["a\n", "[b\n", "]|"], &["[a\n", "b\n", "]|"])?; + assert_select_all(&["a\n", "[b]|\n", ""], &["[a\n", "b\n", "]|"])?; + assert_select_all(&["a\n", "|[b\n", "]"], &["[a\n", "b\n", "]|"])?; + assert_select_all( + &["abc\n", "def\n", "gh|i\n", "jkl"], + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + )?; + assert_select_all( + &["|[abc\n", "def\n", "ghi\n", "jkl]"], + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + )?; + + Ok(()) + } + + // TODO hometest + + // TODO endtest + + type MoveCaretFun = fn(&mut BigSelectableText, bool) -> UIResult<()>; + + // Convert nice string representations and compare results + fn assert_move( + pre_lines_str: &[&str], + expected_post_lines_str: &[&str], + shift_pressed: bool, + move_fun: MoveCaretFun, + ) -> Result<(), String> { + let expected_post_lines: Vec = expected_post_lines_str + .iter() + .map(|l| l.to_string()) + .collect(); + + let mut big_text = gen_big_text(pre_lines_str)?; + + move_fun(&mut big_text, shift_pressed)?; + + let mut lines_vec = all_lines_vec(&big_text); + let post_lines_res = convert_selection_to_dsl(big_text.caret_w_select, &mut lines_vec); + + match post_lines_res { + Ok(post_lines) => { + assert_eq!(expected_post_lines, post_lines); + Ok(()) + } + Err(e) => Err(format!("{:?}", e)), + } + } + + #[test] + fn move_right() -> Result<(), String> { + let move_caret_right = SelectableLines::move_caret_right; + + assert_move(&["|"], &["|"], false, move_caret_right)?; + assert_move(&["a|"], &["a|"], false, move_caret_right)?; + assert_move(&["|A"], &["A|"], false, move_caret_right)?; + assert_move(&["|abc"], &["a|bc"], false, move_caret_right)?; + assert_move(&["a|bc"], &["ab|c"], false, move_caret_right)?; + assert_move(&["abc|"], &["abc|"], false, move_caret_right)?; + assert_move(&["| abc"], &[" |abc"], false, move_caret_right)?; + assert_move(&["abc| "], &["abc |"], false, move_caret_right)?; + assert_move(&["abc|\n", "d"], &["abc\n", "|d"], false, move_caret_right)?; + assert_move(&["abc|\n", ""], &["abc\n", "|"], false, move_caret_right)?; + assert_move( + &["abc\n", "|def"], + &["abc\n", "d|ef"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def| "], + &["abc\n", "def |"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def |\n", "ghi"], + &["abc\n", "def \n", "|ghi"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def|\n", ""], + &["abc\n", "def\n", "|"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "ghi|\n", "jkl"], + &["abc\n", "def\n", "ghi\n", "|jkl"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "|ghi\n", "jkl"], + &["abc\n", "def\n", "g|hi\n", "jkl"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "g|hi\n", "jkl"], + &["abc\n", "def\n", "gh|i\n", "jkl"], + false, + move_caret_right, + )?; + + Ok(()) + } + + #[test] + fn move_left() -> Result<(), String> { + let move_caret_left = SelectableLines::move_caret_left; + + assert_move(&["|"], &["|"], false, move_caret_left)?; + assert_move(&["|a"], &["|a"], false, move_caret_left)?; + assert_move(&["|A"], &["|A"], false, move_caret_left)?; + assert_move(&["a|bc"], &["|abc"], false, move_caret_left)?; + assert_move(&["ab|c"], &["a|bc"], false, move_caret_left)?; + assert_move(&["abc|"], &["ab|c"], false, move_caret_left)?; + assert_move(&[" |abc"], &["| abc"], false, move_caret_left)?; + assert_move(&["abc |"], &["abc| "], false, move_caret_left)?; + assert_move(&["abc\n", "|d"], &["abc|\n", "d"], false, move_caret_left)?; + assert_move(&["abc\n", "|"], &["abc|\n", ""], false, move_caret_left)?; + assert_move( + &["abc\n", "d|ef"], + &["abc\n", "|def"], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def |"], + &["abc\n", "def| "], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def \n", "|ghi"], + &["abc\n", "def |\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "|"], + &["abc\n", "def|\n", ""], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "ghi\n", "|jkl"], + &["abc\n", "def\n", "ghi|\n", "jkl"], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "g|hi\n", "jkl"], + &["abc\n", "def\n", "|ghi\n", "jkl"], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "gh|i\n", "jkl"], + &["abc\n", "def\n", "g|hi\n", "jkl"], + false, + move_caret_left, + )?; + + Ok(()) + } + + #[test] + fn move_up() -> Result<(), String> { + let move_caret_up = SelectableLines::move_caret_up; + + assert_move(&["|"], &["|"], false, move_caret_up)?; + assert_move(&["|a"], &["|a"], false, move_caret_up)?; + assert_move(&["A|"], &["|A"], false, move_caret_up)?; + assert_move(&["a|bc"], &["|abc"], false, move_caret_up)?; + assert_move(&["ab|c"], &["|abc"], false, move_caret_up)?; + assert_move(&["abc|"], &["|abc"], false, move_caret_up)?; + assert_move(&["|abc\n", "def"], &["|abc\n", "def"], false, move_caret_up)?; + assert_move(&["abc\n", "|def"], &["|abc\n", "def"], false, move_caret_up)?; + assert_move(&["abc\n", "d|ef"], &["a|bc\n", "def"], false, move_caret_up)?; + assert_move(&["abc\n", "de|f"], &["ab|c\n", "def"], false, move_caret_up)?; + assert_move(&["abc\n", "def|"], &["abc|\n", "def"], false, move_caret_up)?; + assert_move( + &["abc\n", "def \n", "|ghi"], + &["abc\n", "|def \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "g|hi"], + &["abc\n", "d|ef \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "gh|i"], + &["abc\n", "de|f \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "ghi|"], + &["abc\n", "def| \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "de\n", "ghi|"], + &["abc\n", "de|\n", "ghi"], + false, + move_caret_up, + )?; + assert_move(&["abc\n", "de|"], &["ab|c\n", "de"], false, move_caret_up)?; + assert_move(&["abc\n", "d|e"], &["a|bc\n", "de"], false, move_caret_up)?; + assert_move(&["abc\n", "|de"], &["|abc\n", "de"], false, move_caret_up)?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], + &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cde|f\n", "ghijkl\n", "mnopqrst"], + &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + &["abcdefgh\n", "ijklmn\n", "op|qr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + &["abcdefgh\n", "ijkl|mn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + &["abcdef|gh\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefg|h\n", "ijklmn\n", "opqr\n", "st"], + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["a|bcdefgh\n", "ijklmn\n", "opqr\n", "st"], + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move(&["abc def gh |"], &["|abc def gh "], false, move_caret_up)?; + assert_move(&["abc de|f gh "], &["|abc def gh "], false, move_caret_up)?; + assert_move(&["ab|c def gh "], &["|abc def gh "], false, move_caret_up)?; + assert_move(&["a|bc def gh "], &["|abc def gh "], false, move_caret_up)?; + + Ok(()) + } + + #[test] + fn move_down() -> Result<(), String> { + let move_caret_down = SelectableLines::move_caret_down; + + assert_move(&["|"], &["|"], false, move_caret_down)?; + assert_move(&["|a"], &["a|"], false, move_caret_down)?; + assert_move(&["A|"], &["A|"], false, move_caret_down)?; + assert_move(&["a|bc"], &["abc|"], false, move_caret_down)?; + assert_move(&["ab|c"], &["abc|"], false, move_caret_down)?; + assert_move(&["abc|"], &["abc|"], false, move_caret_down)?; + assert_move(&["abc| "], &["abc |"], false, move_caret_down)?; + assert_move( + &["abc\n", "|def"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "d|ef"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|f"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "def|"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["|abc\n", "def"], + &["abc\n", "|def"], + false, + move_caret_down, + )?; + assert_move( + &["a|bc\n", "def"], + &["abc\n", "d|ef"], + false, + move_caret_down, + )?; + assert_move( + &["ab|c\n", "def"], + &["abc\n", "de|f"], + false, + move_caret_down, + )?; + assert_move( + &["abc|\n", "def"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "|def \n", "ghi"], + &["abc\n", "def \n", "|ghi"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "d|ef \n", "ghi"], + &["abc\n", "def \n", "g|hi"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|f \n", "ghi"], + &["abc\n", "def \n", "gh|i"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "def| \n", "ghi"], + &["abc\n", "def \n", "ghi|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "def |\n", "ghi"], + &["abc\n", "def \n", "ghi|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|\n", "ghi"], + &["abc\n", "de\n", "gh|i"], + false, + move_caret_down, + )?; + assert_move(&["abc|\n", "de"], &["abc\n", "de|"], false, move_caret_down)?; + assert_move(&["ab|c\n", "de"], &["abc\n", "de|"], false, move_caret_down)?; + assert_move(&["a|bc\n", "de"], &["abc\n", "d|e"], false, move_caret_down)?; + assert_move(&["|abc\n", "de"], &["abc\n", "|de"], false, move_caret_down)?; + assert_move( + &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "cd|ef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "cdef\n", "ghij|kl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], + false, + move_caret_down, + )?; + assert_move( + &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], + &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + false, + move_caret_down, + )?; + assert_move(&["abc def gh |"], &["abc def gh |"], false, move_caret_down)?; + assert_move(&["abc de|f gh "], &["abc def gh |"], false, move_caret_down)?; + assert_move(&["ab|c def gh "], &["abc def gh |"], false, move_caret_down)?; + assert_move(&["a|bc def gh "], &["abc def gh |"], false, move_caret_down)?; + assert_move(&["|abc def gh "], &["abc def gh |"], false, move_caret_down)?; + + Ok(()) + } + + #[test] + fn start_selection_right() -> Result<(), String> { + let move_caret_right = SelectableLines::move_caret_right; + + assert_move(&["|"], &["|"], true, move_caret_right)?; + assert_move(&["a|"], &["a|"], true, move_caret_right)?; + assert_move(&["|A"], &["[A]|"], true, move_caret_right)?; + assert_move(&["|abc"], &["[a]|bc"], true, move_caret_right)?; + assert_move(&["a|bc"], &["a[b]|c"], true, move_caret_right)?; + assert_move(&["abc|"], &["abc|"], true, move_caret_right)?; + assert_move(&["| abc"], &["[ ]|abc"], true, move_caret_right)?; + assert_move(&["abc| "], &["abc[ ]|"], true, move_caret_right)?; + assert_move(&["abc|\n", "d"], &["abc[\n", "]|d"], true, move_caret_right)?; + assert_move(&["abc|\n", ""], &["abc[\n", "]|"], true, move_caret_right)?; + assert_move( + &["abc\n", "|def"], + &["abc\n", "[d]|ef"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def| "], + &["abc\n", "def[ ]|"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def |\n", "ghi"], + &["abc\n", "def [\n", "]|ghi"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def|\n", ""], + &["abc\n", "def[\n", "]|"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "ghi|\n", "jkl"], + &["abc\n", "def\n", "ghi[\n", "]|jkl"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "|ghi\n", "jkl"], + &["abc\n", "def\n", "[g]|hi\n", "jkl"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "g|hi\n", "jkl"], + &["abc\n", "def\n", "g[h]|i\n", "jkl"], + true, + move_caret_right, + )?; + + Ok(()) + } + + #[test] + fn start_selection_left() -> Result<(), String> { + let move_caret_left = SelectableLines::move_caret_left; + + assert_move(&["|"], &["|"], true, move_caret_left)?; + assert_move(&["a|"], &["|[a]"], true, move_caret_left)?; + assert_move(&["|A"], &["|A"], true, move_caret_left)?; + assert_move(&["|abc"], &["|abc"], true, move_caret_left)?; + assert_move(&["a|bc"], &["|[a]bc"], true, move_caret_left)?; + assert_move(&["abc|"], &["ab|[c]"], true, move_caret_left)?; + assert_move(&[" |abc"], &["|[ ]abc"], true, move_caret_left)?; + assert_move(&["abc |"], &["abc|[ ]"], true, move_caret_left)?; + assert_move(&["abc|\n", "d"], &["ab|[c]\n", "d"], true, move_caret_left)?; + assert_move(&["abc\n", "|d"], &["abc|[\n", "]d"], true, move_caret_left)?; + assert_move(&["abc\n", "|"], &["abc|[\n", "]"], true, move_caret_left)?; + assert_move( + &["abc\n", " |def"], + &["abc\n", "|[ ]def"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "d|ef"], + &["abc\n", "|[d]ef"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "de|f "], + &["abc\n", "d|[e]f "], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "|"], + &["abc\n", "def|[\n", "]"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "|ghi\n", "jkl"], + &["abc\n", "def|[\n", "]ghi\n", "jkl"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "g|hi\n", "jkl"], + &["abc\n", "def\n", "|[g]hi\n", "jkl"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "gh|i\n", "jkl"], + &["abc\n", "def\n", "g|[h]i\n", "jkl"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "ghi|\n", "jkl"], + &["abc\n", "def\n", "gh|[i]\n", "jkl"], + true, + move_caret_left, + )?; + + Ok(()) + } + + #[test] + fn start_selection_down() -> Result<(), String> { + let move_caret_down = SelectableLines::move_caret_down; + + assert_move(&["|"], &["|"], true, move_caret_down)?; + assert_move(&["|a"], &["[a]|"], true, move_caret_down)?; + assert_move(&["A|"], &["A|"], true, move_caret_down)?; + assert_move(&["a|bc"], &["a[bc]|"], true, move_caret_down)?; + assert_move(&["ab|c"], &["ab[c]|"], true, move_caret_down)?; + assert_move(&["abc|"], &["abc|"], true, move_caret_down)?; + assert_move(&["abc| "], &["abc[ ]|"], true, move_caret_down)?; + assert_move( + &["abc\n", "|def"], + &["abc\n", "[def]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "d|ef"], + &["abc\n", "d[ef]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|f"], + &["abc\n", "de[f]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "def|"], + &["abc\n", "def|"], + true, + move_caret_down, + )?; + assert_move( + &["|abc\n", "def"], + &["[abc\n", "]|def"], + true, + move_caret_down, + )?; + assert_move( + &["a|bc\n", "def"], + &["a[bc\n", "d]|ef"], + true, + move_caret_down, + )?; + assert_move( + &["ab|c\n", "def"], + &["ab[c\n", "de]|f"], + true, + move_caret_down, + )?; + assert_move( + &["abc|\n", "def"], + &["abc[\n", "def]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "|def \n", "ghi"], + &["abc\n", "[def \n", "]|ghi"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "d|ef \n", "ghi"], + &["abc\n", "d[ef \n", "g]|hi"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|f \n", "ghi"], + &["abc\n", "de[f \n", "gh]|i"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "def| \n", "ghi"], + &["abc\n", "def[ \n", "ghi]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "def |\n", "ghi"], + &["abc\n", "def [\n", "ghi]|"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|\n", "ghi"], + &["abc\n", "de[\n", "gh]|i"], + true, + move_caret_down, + )?; + assert_move( + &["abc|\n", "de"], + &["abc[\n", "de]|"], + true, + move_caret_down, + )?; + assert_move( + &["ab|c\n", "de"], + &["ab[c\n", "de]|"], + true, + move_caret_down, + )?; + assert_move( + &["a|bc\n", "de"], + &["a[bc\n", "d]|e"], + true, + move_caret_down, + )?; + assert_move( + &["|abc\n", "de"], + &["[abc\n", "]|de"], + true, + move_caret_down, + )?; + assert_move( + &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], + &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], + true, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "cdef[\n", "ghij]|kl\n", "mnopqrst"], + true, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], + true, + move_caret_down, + )?; + assert_move( + &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], + &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], + true, + move_caret_down, + )?; + assert_move( + &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "[cdef\n", "]|ghijkl\n", "mnopqrst"], + true, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], + true, + move_caret_down, + )?; + assert_move( + &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], + &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], + true, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], + true, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], + true, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], + true, + move_caret_down, + )?; + assert_move(&["abc def gh |"], &["abc def gh |"], true, move_caret_down)?; + assert_move( + &["abc de|f gh "], + &["abc de[f gh ]|"], + true, + move_caret_down, + )?; + assert_move( + &["ab|c def gh "], + &["ab[c def gh ]|"], + true, + move_caret_down, + )?; + assert_move( + &["a|bc def gh "], + &["a[bc def gh ]|"], + true, + move_caret_down, + )?; + assert_move( + &["|abc def gh "], + &["[abc def gh ]|"], + true, + move_caret_down, + )?; + + Ok(()) + } + + #[test] + fn start_selection_up() -> Result<(), String> { + let move_caret_up = SelectableLines::move_caret_up; + + assert_move(&["|"], &["|"], true, move_caret_up)?; + assert_move(&["|a"], &["|a"], true, move_caret_up)?; + assert_move(&["A|"], &["|[A]"], true, move_caret_up)?; + assert_move(&["a|bc"], &["|[a]bc"], true, move_caret_up)?; + assert_move(&["ab|c"], &["|[ab]c"], true, move_caret_up)?; + assert_move(&["abc|"], &["|[abc]"], true, move_caret_up)?; + assert_move(&["|abc\n", "def"], &["|abc\n", "def"], true, move_caret_up)?; + assert_move( + &["abc\n", "|def"], + &["|[abc\n", "]def"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "d|ef"], + &["a|[bc\n", "d]ef"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "de|f"], + &["ab|[c\n", "de]f"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def|"], + &["abc|[\n", "def]"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "|ghi"], + &["abc\n", "|[def \n", "]ghi"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "g|hi"], + &["abc\n", "d|[ef \n", "g]hi"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "gh|i"], + &["abc\n", "de|[f \n", "gh]i"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def \n", "ghi|"], + &["abc\n", "def|[ \n", "ghi]"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "de\n", "ghi|"], + &["abc\n", "de|[\n", "ghi]"], + true, + move_caret_up, + )?; + assert_move(&["abc\n", "de|"], &["ab|[c\n", "de]"], true, move_caret_up)?; + assert_move(&["abc\n", "d|e"], &["a|[bc\n", "d]e"], true, move_caret_up)?; + assert_move(&["abc\n", "|de"], &["|[abc\n", "]de"], true, move_caret_up)?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], + &["ab\n", "cdef\n", "ghijkl|[\n", "mnopqrst]"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + &["ab\n", "cdef|[\n", "ghijkl]\n", "mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], + &["ab\n", "cdef\n", "|[ghijkl\n", "]mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], + &[" |[ab\n", " ]cdef\n", "ghijkl\n", "mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], + &["ab\n", "cdef\n", "ghijkl|[\n", "mnopqr]st"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cde|f\n", "ghijkl\n", "mnopqrst"], + &["ab|[\n", "cde]f\n", "ghijkl\n", "mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + &["abcdefgh\n", "ijklmn\n", "op|[qr\n", "st]"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + &["abcdefgh\n", "ijkl|[mn\n", "opqr]\n", "st"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + &["abcdef|[gh\n", "ijklmn]\n", "opqr\n", "st"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], + &["|[abcdefgh]\n", "ijklmn\n", "opqr\n", "st"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefg|h\n", "ijklmn\n", "opqr\n", "st"], + &["|[abcdefg]h\n", "ijklmn\n", "opqr\n", "st"], + true, + move_caret_up, + )?; + assert_move( + &["a|bcdefgh\n", "ijklmn\n", "opqr\n", "st"], + &["|[a]bcdefgh\n", "ijklmn\n", "opqr\n", "st"], + true, + move_caret_up, + )?; + assert_move( + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], + true, + move_caret_up, + )?; + assert_move(&["abc def gh |"], &["|[abc def gh ]"], true, move_caret_up)?; + assert_move(&["abc de|f gh "], &["|[abc de]f gh "], true, move_caret_up)?; + assert_move(&["ab|c def gh "], &["|[ab]c def gh "], true, move_caret_up)?; + assert_move(&["a|bc def gh "], &["|[a]bc def gh "], true, move_caret_up)?; + + Ok(()) + } + + #[test] + fn end_selection_right() -> Result<(), String> { + let move_caret_right = SelectableLines::move_caret_right; + + assert_move(&["[A]|"], &["A|"], false, move_caret_right)?; + assert_move(&["[a]|bc"], &["a|bc"], false, move_caret_right)?; + assert_move(&["a[b]|c"], &["ab|c"], false, move_caret_right)?; + assert_move(&["ab[c]|"], &["abc|"], false, move_caret_right)?; + assert_move(&["[ ]|abc"], &[" |abc"], false, move_caret_right)?; + assert_move(&["|[ ]abc"], &[" |abc"], false, move_caret_right)?; + assert_move(&["a|[b]c"], &["ab|c"], false, move_caret_right)?; + assert_move( + &["abc[\n", "]|d"], + &["abc\n", "|d"], + false, + move_caret_right, + )?; + assert_move( + &["abc|[\n", "]d"], + &["abc\n", "|d"], + false, + move_caret_right, + )?; + assert_move(&["abc|[\n", "]"], &["abc\n", "|"], false, move_caret_right)?; + assert_move( + &["abc\n", "[d]|ef"], + &["abc\n", "d|ef"], + false, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "ghi[\n", "]|jkl"], + &["abc\n", "def\n", "ghi\n", "|jkl"], + false, + move_caret_right, + )?; + assert_move(&["[ab]|c"], &["ab|c"], false, move_caret_right)?; + assert_move(&["[abc]|"], &["abc|"], false, move_caret_right)?; + assert_move( + &["ab|[c\n", "]def\n", "ghi"], + &["abc\n", "|def\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["ab[c\n", "]|def\n", "ghi"], + &["abc\n", "|def\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["a|[bc\n", "]def\n", "ghi"], + &["abc\n", "|def\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["|[abc\n", "]def\n", "ghi"], + &["abc\n", "|def\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["a|[bc\n", "d]ef\n", "ghi"], + &["abc\n", "d|ef\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["|[abc\n", "def]\n", "ghi"], + &["abc\n", "def|\n", "ghi"], + false, + move_caret_right, + )?; + assert_move( + &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], + &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], + false, + move_caret_right, + )?; + assert_move( + &["|[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]"], + &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], + false, + move_caret_right, + )?; + assert_move( + &["ab\n", "c[def\n", "ghijkl\n", "mno]|pqrst"], + &["ab\n", "cdef\n", "ghijkl\n", "mno|pqrst"], + false, + move_caret_right, + )?; + assert_move( + &["ab\n", "c|[def\n", "ghijkl\n", "mno]pqrst"], + &["ab\n", "cdef\n", "ghijkl\n", "mno|pqrst"], + false, + move_caret_right, + )?; + + Ok(()) + } + + #[test] + fn end_selection_left() -> Result<(), String> { + let move_caret_left = SelectableLines::move_caret_left; + + assert_move(&["[A]|"], &["|A"], false, move_caret_left)?; + assert_move(&["[a]|bc"], &["|abc"], false, move_caret_left)?; + assert_move(&["a[b]|c"], &["a|bc"], false, move_caret_left)?; + assert_move(&["ab[c]|"], &["ab|c"], false, move_caret_left)?; + assert_move(&["[ ]|abc"], &["| abc"], false, move_caret_left)?; + assert_move(&["|[ ]abc"], &["| abc"], false, move_caret_left)?; + assert_move(&["a|[b]c"], &["a|bc"], false, move_caret_left)?; + assert_move(&["abc[\n", "]|d"], &["abc|\n", "d"], false, move_caret_left)?; + assert_move(&["abc|[\n", "]d"], &["abc|\n", "d"], false, move_caret_left)?; + assert_move(&["abc|[\n", "]"], &["abc|\n", ""], false, move_caret_left)?; + assert_move( + &["abc\n", "[d]|ef"], + &["abc\n", "|def"], + false, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "ghi[\n", "]|jkl"], + &["abc\n", "def\n", "ghi|\n", "jkl"], + false, + move_caret_left, + )?; + assert_move(&["[ab]|c"], &["|abc"], false, move_caret_left)?; + assert_move(&["[abc]|"], &["|abc"], false, move_caret_left)?; + assert_move( + &["ab|[c\n", "]def\n", "ghi"], + &["ab|c\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["ab[c\n", "]|def\n", "ghi"], + &["ab|c\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["a|[bc\n", "]def\n", "ghi"], + &["a|bc\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["|[abc\n", "]def\n", "ghi"], + &["|abc\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["a|[bc\n", "d]ef\n", "ghi"], + &["a|bc\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["|[abc\n", "def]\n", "ghi"], + &["|abc\n", "def\n", "ghi"], + false, + move_caret_left, + )?; + assert_move( + &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], + &["|ab\n", "cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_left, + )?; + assert_move( + &["|[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]"], + &["|ab\n", "cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_left, + )?; + assert_move( + &["ab\n", "c[def\n", "ghijkl\n", "mno]|pqrst"], + &["ab\n", "c|def\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_left, + )?; + assert_move( + &["ab\n", "c|[def\n", "ghijkl\n", "mno]pqrst"], + &["ab\n", "c|def\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_left, + )?; + + Ok(()) + } + + #[test] + fn end_selection_down() -> Result<(), String> { + let move_caret_down = SelectableLines::move_caret_down; + + assert_move(&["[a]|"], &["a|"], false, move_caret_down)?; + assert_move(&["|[a]"], &["a|"], false, move_caret_down)?; + assert_move(&["a|[bc]"], &["abc|"], false, move_caret_down)?; + assert_move(&["ab[c]|"], &["abc|"], false, move_caret_down)?; + assert_move(&["abc|[ ]"], &["abc |"], false, move_caret_down)?; + assert_move( + &["abc\n", "|[def]"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "d|[ef]"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|[f]"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["[abc\n", "]|def"], + &["abc\n", "|def"], + false, + move_caret_down, + )?; + assert_move( + &["a[bc\n", "d]|ef"], + &["abc\n", "d|ef"], + false, + move_caret_down, + )?; + assert_move( + &["ab|[c\n", "de]f"], + &["abc\n", "de|f"], + false, + move_caret_down, + )?; + assert_move( + &["abc[\n", "def]|"], + &["abc\n", "def|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "|[def \n", "]ghi"], + &["abc\n", "def \n", "|ghi"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "d[ef \n", "g]|hi"], + &["abc\n", "def \n", "g|hi"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de[f \n", "gh]|i"], + &["abc\n", "def \n", "gh|i"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "def[ \n", "ghi]|"], + &["abc\n", "def \n", "ghi|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "def [\n", "ghi]|"], + &["abc\n", "def \n", "ghi|"], + false, + move_caret_down, + )?; + assert_move( + &["abc\n", "de[\n", "gh]|i"], + &["abc\n", "de\n", "gh|i"], + false, + move_caret_down, + )?; + assert_move( + &["abc|[\n", "de]"], + &["abc\n", "de|"], + false, + move_caret_down, + )?; + assert_move( + &["ab[c\n", "de]|"], + &["abc\n", "de|"], + false, + move_caret_down, + )?; + assert_move( + &["a|[bc\n", "d]e"], + &["abc\n", "d|e"], + false, + move_caret_down, + )?; + assert_move( + &["[abc\n", "]|de"], + &["abc\n", "|de"], + false, + move_caret_down, + )?; + assert_move( + &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], + &["ab\n", "cd|ef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef|[\n", "ghij]kl\n", "mnopqrst"], + &["ab\n", "cdef\n", "ghij|kl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], + &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], + false, + move_caret_down, + )?; + assert_move( + &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], + &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "|[cdef\n", "]ghijkl\n", "mnopqrst"], + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], + &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + false, + move_caret_down, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], + false, + move_caret_down, + )?; + assert_move( + &["abc de[f gh ]|"], + &["abc def gh |"], + false, + move_caret_down, + )?; + assert_move( + &["ab|[c def gh ]"], + &["abc def gh |"], + false, + move_caret_down, + )?; + assert_move( + &["a[bc def gh ]|"], + &["abc def gh |"], + false, + move_caret_down, + )?; + assert_move( + &["[abc def gh ]|"], + &["abc def gh |"], + false, + move_caret_down, + )?; + + Ok(()) + } + + #[test] + fn end_selection_up() -> Result<(), String> { + let move_caret_up = SelectableLines::move_caret_up; + + assert_move(&["[a]|"], &["|a"], false, move_caret_up)?; + assert_move(&["|[a]"], &["|a"], false, move_caret_up)?; + assert_move(&["a|[bc]"], &["a|bc"], false, move_caret_up)?; + assert_move(&["ab[c]|"], &["ab|c"], false, move_caret_up)?; + assert_move(&["abc|[ ]"], &["abc| "], false, move_caret_up)?; + assert_move( + &["abc\n", "|[def]"], + &["abc\n", "|def"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "d|[ef]"], + &["abc\n", "d|ef"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "de|[f]"], + &["abc\n", "de|f"], + false, + move_caret_up, + )?; + assert_move( + &["[abc\n", "]|def"], + &["|abc\n", "def"], + false, + move_caret_up, + )?; + assert_move( + &["a[bc\n", "d]|ef"], + &["a|bc\n", "def"], + false, + move_caret_up, + )?; + assert_move( + &["ab|[c\n", "de]f"], + &["ab|c\n", "def"], + false, + move_caret_up, + )?; + assert_move( + &["abc[\n", "def]|"], + &["abc|\n", "def"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "|[def \n", "]ghi"], + &["abc\n", "|def \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "d[ef \n", "g]|hi"], + &["abc\n", "d|ef \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "de|[f \n", "gh]i"], + &["abc\n", "de|f \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "def[ \n", "ghi]|"], + &["abc\n", "def| \n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "def [\n", "ghi]|"], + &["abc\n", "def |\n", "ghi"], + false, + move_caret_up, + )?; + assert_move( + &["abc\n", "de[\n", "gh]|i"], + &["abc\n", "de|\n", "ghi"], + false, + move_caret_up, + )?; + assert_move(&["abc|[\n", "de]"], &["abc|\n", "de"], false, move_caret_up)?; + assert_move(&["ab[c\n", "de]|"], &["ab|c\n", "de"], false, move_caret_up)?; + assert_move(&["a|[bc\n", "d]e"], &["a|bc\n", "de"], false, move_caret_up)?; + assert_move(&["[abc\n", "]|de"], &["|abc\n", "de"], false, move_caret_up)?; + assert_move( + &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], + &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef|[\n", "ghij]kl\n", "mnopqrst"], + &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], + &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], + &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "|[cdef\n", "]ghijkl\n", "mnopqrst"], + &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], + &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], + &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], + &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], + &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], + false, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], + &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], + false, + move_caret_up, + )?; + assert_move(&["abc de[f gh ]|"], &["abc de|f gh "], false, move_caret_up)?; + assert_move(&["ab|[c def gh ]"], &["ab|c def gh "], false, move_caret_up)?; + assert_move(&["a[bc def gh ]|"], &["a|bc def gh "], false, move_caret_up)?; + assert_move(&["[abc def gh ]|"], &["|abc def gh "], false, move_caret_up)?; + + Ok(()) + } + + #[test] + fn extend_selection_right() -> Result<(), String> { + let move_caret_right = SelectableLines::move_caret_right; + + assert_move(&["[a]|bc"], &["[ab]|c"], true, move_caret_right)?; + assert_move(&["a[b]|c"], &["a[bc]|"], true, move_caret_right)?; + assert_move(&["[ab]|c"], &["[abc]|"], true, move_caret_right)?; + assert_move(&["[ ]|abc"], &["[ a]|bc"], true, move_caret_right)?; + assert_move(&["[abc]|"], &["[abc]|"], true, move_caret_right)?; + assert_move(&["a[bc]|"], &["a[bc]|"], true, move_caret_right)?; + assert_move(&["ab[c]|"], &["ab[c]|"], true, move_caret_right)?; + assert_move( + &["abc[\n", "]|d"], + &["abc[\n", "d]|"], + true, + move_caret_right, + )?; + assert_move(&["ab[c]|\n", ""], &["ab[c\n", "]|"], true, move_caret_right)?; + assert_move( + &["ab[c]|\n", "d"], + &["ab[c\n", "]|d"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "def\n", "ghi[\n", "]|jkl"], + &["abc\n", "def\n", "ghi[\n", "j]|kl"], + true, + move_caret_right, + )?; + assert_move( + &["ab[c\n", "def\n", "ghi\n", "]|jkl"], + &["ab[c\n", "def\n", "ghi\n", "j]|kl"], + true, + move_caret_right, + )?; + assert_move( + &["ab[c\n", "def\n", "]|ghi\n", "jkl"], + &["ab[c\n", "def\n", "g]|hi\n", "jkl"], + true, + move_caret_right, + )?; + assert_move( + &["[abc\n", "def\n", "ghi\n", "jk]|l"], + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + true, + move_caret_right, + )?; + assert_move( + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + true, + move_caret_right, + )?; + + Ok(()) + } + + #[test] + fn extend_selection_left() -> Result<(), String> { + let move_caret_left = SelectableLines::move_caret_left; + + assert_move(&["ab|[c]"], &["a|[bc]"], true, move_caret_left)?; + assert_move(&["a|[bc]"], &["|[abc]"], true, move_caret_left)?; + assert_move(&["|[abc]"], &["|[abc]"], true, move_caret_left)?; + assert_move(&["|[ab]c"], &["|[ab]c"], true, move_caret_left)?; + assert_move(&["|[a]bc"], &["|[a]bc"], true, move_caret_left)?; + assert_move(&[" |[a]bc"], &["|[ a]bc"], true, move_caret_left)?; + assert_move( + &["abc|[\n", "]d"], + &["ab|[c\n", "]d"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "|[d]"], + &["abc|[\n", "d]"], + true, + move_caret_left, + )?; + assert_move(&["ab|[c\n", "]"], &["a|[bc\n", "]"], true, move_caret_left)?; + assert_move( + &["abc\n", "def|[\n", "ghi\n", "j]kl"], + &["abc\n", "de|[f\n", "ghi\n", "j]kl"], + true, + move_caret_left, + )?; + assert_move( + &["a|[bc\n", "def\n", "ghi\n", "jkl]"], + &["|[abc\n", "def\n", "ghi\n", "jkl]"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def\n", "ghi\n", "|[jkl]"], + &["abc\n", "def\n", "ghi|[\n", "jkl]"], + true, + move_caret_left, + )?; + + Ok(()) + } + + #[test] + fn extend_selection_up() -> Result<(), String> { + let move_caret_up = SelectableLines::move_caret_up; + + assert_move(&["ab|[c]"], &["|[abc]"], true, move_caret_up)?; + assert_move(&["a|[bc]"], &["|[abc]"], true, move_caret_up)?; + assert_move(&["|[abc]"], &["|[abc]"], true, move_caret_up)?; + assert_move(&["|[ab]c"], &["|[ab]c"], true, move_caret_up)?; + assert_move(&["|[a]bc"], &["|[a]bc"], true, move_caret_up)?; + assert_move(&[" |[a]bc"], &["|[ a]bc"], true, move_caret_up)?; + assert_move(&["ab[c]|"], &["|[ab]c"], true, move_caret_up)?; + assert_move(&["[a]|"], &["|a"], true, move_caret_up)?; + assert_move(&["[a]|bc"], &["|abc"], true, move_caret_up)?; + assert_move(&["[a]|bc\n", "d"], &["|abc\n", "d"], true, move_caret_up)?; + assert_move( + &["abc\n", "de[f]|"], + &["abc|[\n", "de]f"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "de|[f]"], + &["ab|[c\n", "def]"], + true, + move_caret_up, + )?; + assert_move( + &["ab|[c\n", "def]"], + &["|[abc\n", "def]"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "[mnopqr]|st"], + &["ab\n", "cdef\n", "ghijkl|[\n", "]mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "[mnopqrs]|t"], + &["ab\n", "cdef\n", "ghijkl|[\n", "]mnopqrst"], + true, + move_caret_up, + )?; + assert_move( + &["abcdefgh\n", "ijklmn\n", "|[o]pqr\n", "st"], + &["abcdefgh\n", "|[ijklmn\n", "o]pqr\n", "st"], + true, + move_caret_up, + )?; + + Ok(()) + } + + #[test] + fn extend_selection_down() -> Result<(), String> { + let move_caret_down = SelectableLines::move_caret_down; + + assert_move(&["[ab]|c"], &["[abc]|"], true, move_caret_down)?; + assert_move(&["[a]|bc"], &["[abc]|"], true, move_caret_down)?; + assert_move(&["[abc]|"], &["[abc]|"], true, move_caret_down)?; + assert_move(&["|[ab]c"], &["ab[c]|"], true, move_caret_down)?; + assert_move(&["|[a]bc"], &["a[bc]|"], true, move_caret_down)?; + assert_move( + &["[a]|bc\n", "d"], + &["[abc\n", "d]|"], + true, + move_caret_down, + )?; + assert_move( + &["[a]|bc\n", "de"], + &["[abc\n", "d]|e"], + true, + move_caret_down, + )?; + assert_move( + &["[abc\n", "d]|e"], + &["[abc\n", "de]|"], + true, + move_caret_down, + )?; + assert_move(&["[a]|bc\n", ""], &["[abc\n", "]|"], true, move_caret_down)?; + assert_move( + &["ab\n", "cdef\n", "ghijkl\n", "[mnopqr]|st"], + &["ab\n", "cdef\n", "ghijkl\n", "[mnopqrst]|"], + true, + move_caret_down, + )?; + assert_move( + &["a[b\n", "cdef\n", "ghijkl\n", "mnopqr]|st"], + &["a[b\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], + true, + move_caret_down, + )?; + assert_move( + &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], + &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], + true, + move_caret_down, + )?; + assert_move( + &["abcd[efgh]|\n", "ijklmn\n", "opqr\n", "st"], + &["abcd[efgh\n", "ijklmn]|\n", "opqr\n", "st"], + true, + move_caret_down, + )?; + assert_move( + &["abcd[e]|fgh\n", "ijklmn\n", "opqr\n", "st"], + &["abcd[efgh\n", "ijklm]|n\n", "opqr\n", "st"], + true, + move_caret_down, + )?; + + Ok(()) + } + + #[test] + fn shrink_selection_right() -> Result<(), String> { + let move_caret_right = SelectableLines::move_caret_right; + + assert_move(&["ab|[c]"], &["abc|"], true, move_caret_right)?; + assert_move(&["a|[bc]"], &["ab|[c]"], true, move_caret_right)?; + assert_move(&["|[abc]"], &["a|[bc]"], true, move_caret_right)?; + assert_move( + &["|[abc\n", "def\n", "ghi\n", "jkl]"], + &["a|[bc\n", "def\n", "ghi\n", "jkl]"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "d|[ef\n", "]ghi\n", "jkl"], + &["abc\n", "de|[f\n", "]ghi\n", "jkl"], + true, + move_caret_right, + )?; + assert_move( + &["abc\n", "de|[f]\n", "ghi\n", "jkl"], + &["abc\n", "def|\n", "ghi\n", "jkl"], + true, + move_caret_right, + )?; + + Ok(()) + } + + #[test] + fn shrink_selection_left() -> Result<(), String> { + let move_caret_left = SelectableLines::move_caret_left; + + assert_move(&["ab[c]|"], &["ab|c"], true, move_caret_left)?; + assert_move(&["a[bc]|"], &["a[b]|c"], true, move_caret_left)?; + assert_move(&["[abc]|"], &["[ab]|c"], true, move_caret_left)?; + assert_move( + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + &["[abc\n", "def\n", "ghi\n", "jk]|l"], + true, + move_caret_left, + )?; + assert_move( + &["|[abc\n", "def\n", "ghi\n", "jkl]"], + &["|[abc\n", "def\n", "ghi\n", "jkl]"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "def[\n", "]|ghi\n", "jkl"], + &["abc\n", "def|\n", "ghi\n", "jkl"], + true, + move_caret_left, + )?; + assert_move( + &["abc\n", "d[ef\n", "gh]|i\n", "jkl"], + &["abc\n", "d[ef\n", "g]|hi\n", "jkl"], + true, + move_caret_left, + )?; + + Ok(()) + } + + #[test] + fn shrink_selection_up() -> Result<(), String> { + let move_caret_up = SelectableLines::move_caret_up; + + assert_move(&["[abc]|"], &["|abc"], true, move_caret_up)?; + assert_move(&["[ab]|c"], &["|abc"], true, move_caret_up)?; + assert_move(&["[a]|bc"], &["|abc"], true, move_caret_up)?; + assert_move(&["|abc"], &["|abc"], true, move_caret_up)?; + assert_move( + &["[abc\n", "def]|"], + &["[abc]|\n", "def"], + true, + move_caret_up, + )?; + assert_move( + &["[abc\n", "de]|f"], + &["[ab]|c\n", "def"], + true, + move_caret_up, + )?; + assert_move( + &["[abc\n", "def\n", "ghi\n", "jkl]|"], + &["[abc\n", "def\n", "ghi]|\n", "jkl"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "def\n", "ghi[\n", "jkl]|"], + &["abc\n", "def\n", "ghi|\n", "jkl"], + true, + move_caret_up, + )?; + assert_move( + &["abc\n", "d[ef\n", "ghi\n", "jk]|l"], + &["abc\n", "d[ef\n", "gh]|i\n", "jkl"], + true, + move_caret_up, + )?; + assert_move( + &["[abc\n", "d]|ef\n", "ghi\n", "jkl"], + &["[a]|bc\n", "def\n", "ghi\n", "jkl"], + true, + move_caret_up, + )?; + + Ok(()) + } + + #[test] + fn shrink_selection_down() -> Result<(), String> { + let move_caret_down = SelectableLines::move_caret_down; + + assert_move(&["|[abc]"], &["abc|"], true, move_caret_down)?; + assert_move( + &["|[abc\n", "def]"], + &["abc\n", "|[def]"], + true, + move_caret_down, + )?; + assert_move( + &["a|[bc\n", "def]"], + &["abc\n", "d|[ef]"], + true, + move_caret_down, + )?; + assert_move( + &["|[abc\n", "def\n", "ghi]"], + &["abc\n", "|[def\n", "ghi]"], + true, + move_caret_down, + )?; + assert_move( + &["ab|[c\n", "def\n", "ghi]"], + &["abc\n", "de|[f\n", "ghi]"], + true, + move_caret_down, + )?; + assert_move( + &["abc\n", "de|[f\n", "ghi]"], + &["abc\n", "def\n", "gh|[i]"], + true, + move_caret_down, + )?; + assert_move( + &["abcdef|[\n", "ghij\n", "kl]"], + &["abcdef\n", "ghij|[\n", "kl]"], + true, + move_caret_down, + )?; + assert_move( + &["abcde|[f\n", "ghij\n", "kl]"], + &["abcdef\n", "ghij|[\n", "kl]"], + true, + move_caret_down, + )?; + assert_move( + &["ab|[cdef\n", "ghij\n", "kl]"], + &["abcdef\n", "gh|[ij\n", "kl]"], + true, + move_caret_down, + )?; + + Ok(()) + } +} diff --git a/editor/src/ui/text/caret_w_select.rs b/editor/src/ui/text/caret_w_select.rs index a990edcf36..b03f1098ef 100644 --- a/editor/src/ui/text/caret_w_select.rs +++ b/editor/src/ui/text/caret_w_select.rs @@ -1,10 +1,8 @@ - -use crate::ui::ui_error::UIResult; -use super::text_pos::TextPos; -use super::selection::{Selection}; -use winit::event::{ModifiersState}; use super::selection::validate_selection; - +use super::selection::Selection; +use super::text_pos::TextPos; +use crate::ui::ui_error::UIResult; +use winit::event::ModifiersState; #[derive(Debug, Copy, Clone)] pub struct CaretWSelect { @@ -12,29 +10,20 @@ pub struct CaretWSelect { pub selection_opt: Option, } - fn mk_some_sel(start_pos: TextPos, end_pos: TextPos) -> UIResult> { - Ok( - Some( - validate_selection(start_pos, end_pos)? - ) - ) + Ok(Some(validate_selection(start_pos, end_pos)?)) } impl Default for CaretWSelect { fn default() -> Self { Self { - caret_pos: TextPos { - line: 0, - column: 0 - }, - selection_opt: None + caret_pos: TextPos { line: 0, column: 0 }, + selection_opt: None, } } } impl CaretWSelect { - pub fn new(caret_pos: TextPos, selection_opt: Option) -> Self { Self { caret_pos, @@ -44,72 +33,46 @@ impl CaretWSelect { pub fn move_caret_w_mods(&mut self, new_pos: TextPos, mods: &ModifiersState) -> UIResult<()> { let caret_pos = self.caret_pos; - + // one does not simply move the caret - let valid_sel_opt = - if new_pos != caret_pos { - if mods.shift() { - if let Some(old_sel) = self.selection_opt { - if new_pos < old_sel.start_pos { - if caret_pos > old_sel.start_pos { - mk_some_sel( - new_pos, - old_sel.start_pos - )? - } else { - mk_some_sel( - new_pos, - old_sel.end_pos - )? - } - } else if new_pos > old_sel.end_pos { - if caret_pos < old_sel.end_pos { - mk_some_sel( - old_sel.end_pos, - new_pos - )? - } else { - mk_some_sel( - old_sel.start_pos, - new_pos - )? - } - } else if new_pos > caret_pos { - mk_some_sel( - new_pos, - old_sel.end_pos - )? - } else if new_pos < caret_pos { - mk_some_sel( - old_sel.start_pos, - new_pos - )? + let valid_sel_opt = if new_pos != caret_pos { + if mods.shift() { + if let Some(old_sel) = self.selection_opt { + if new_pos < old_sel.start_pos { + if caret_pos > old_sel.start_pos { + mk_some_sel(new_pos, old_sel.start_pos)? } else { - // TODO should this return none? - None + mk_some_sel(new_pos, old_sel.end_pos)? } - } else if new_pos < self.caret_pos { - mk_some_sel( - new_pos, - caret_pos - )? + } else if new_pos > old_sel.end_pos { + if caret_pos < old_sel.end_pos { + mk_some_sel(old_sel.end_pos, new_pos)? + } else { + mk_some_sel(old_sel.start_pos, new_pos)? + } + } else if new_pos > caret_pos { + mk_some_sel(new_pos, old_sel.end_pos)? + } else if new_pos < caret_pos { + mk_some_sel(old_sel.start_pos, new_pos)? } else { - mk_some_sel( - caret_pos, - new_pos - )? + // TODO should this return none? + None } + } else if new_pos < self.caret_pos { + mk_some_sel(new_pos, caret_pos)? } else { - None + mk_some_sel(caret_pos, new_pos)? } } else { - self.selection_opt - }; - + None + } + } else { + self.selection_opt + }; + self.caret_pos = new_pos; self.selection_opt = valid_sel_opt; Ok(()) } - } diff --git a/editor/src/ui/text/lines.rs b/editor/src/ui/text/lines.rs index ef0f1c386d..2f09388176 100644 --- a/editor/src/ui/text/lines.rs +++ b/editor/src/ui/text/lines.rs @@ -1,15 +1,13 @@ // Adapted from https://github.com/cessen/ropey by Nathan Vegdahl, licensed under the MIT license -use crate::ui::ui_error::{ - UIResult, -}; use crate::ui::text::{ - text_pos::{TextPos}, - selection::{Selection, RawSelection}, - caret_w_select::CaretWSelect, + selection::{RawSelection, Selection}, + text_pos::TextPos, }; +use crate::ui::ui_error::UIResult; use bumpalo::collections::String as BumpString; use bumpalo::Bump; +use winit::event::{ModifiersState, VirtualKeyCode}; pub trait Lines { fn get_line(&self, line_nr: usize) -> UIResult<&str>; @@ -29,13 +27,13 @@ pub trait SelectableLines { fn set_caret(&mut self, caret_pos: TextPos); - fn move_caret_left(&mut self, shift_pressed: bool) -> UIResult; + fn move_caret_left(&mut self, shift_pressed: bool) -> UIResult<()>; - fn move_caret_right(&mut self, shift_pressed: bool) -> UIResult; + fn move_caret_right(&mut self, shift_pressed: bool) -> UIResult<()>; - fn move_caret_up(&mut self, shift_pressed: bool) -> UIResult; + fn move_caret_up(&mut self, shift_pressed: bool) -> UIResult<()>; - fn move_caret_down(&mut self, shift_pressed: bool) -> UIResult; + fn move_caret_down(&mut self, shift_pressed: bool) -> UIResult<()>; fn get_selection(&self) -> Option; @@ -47,15 +45,26 @@ pub trait SelectableLines { fn set_sel_none(&mut self); + fn select_all(&mut self) -> UIResult<()>; + fn last_text_pos(&self) -> TextPos; } pub trait MutSelectableLines { fn insert_char(&mut self, new_char: &char) -> UIResult<()>; + // could be for insertion, backspace, del... + fn handle_new_char(&mut self, received_char: &char) -> UIResult<()>; + fn insert_str(&mut self, new_str: &str) -> UIResult<()>; - fn pop_char(&mut self); + fn pop_char(&mut self) -> UIResult<()>; fn del_selection(&mut self) -> UIResult<()>; + + fn handle_key_down( + &mut self, + modifiers: &ModifiersState, + virtual_keycode: VirtualKeyCode, + ) -> UIResult<()>; } diff --git a/editor/src/ui/text/mod.rs b/editor/src/ui/text/mod.rs index 50f15abdbf..5dad412667 100644 --- a/editor/src/ui/text/mod.rs +++ b/editor/src/ui/text/mod.rs @@ -1,5 +1,5 @@ +pub mod big_selectable_text; pub mod caret_w_select; +pub mod lines; pub mod selection; pub mod text_pos; -pub mod big_selectable_text; -pub mod lines; \ No newline at end of file diff --git a/editor/src/ui/text/selection.rs b/editor/src/ui/text/selection.rs index 815211070c..6c6767b2a9 100644 --- a/editor/src/ui/text/selection.rs +++ b/editor/src/ui/text/selection.rs @@ -1,7 +1,7 @@ -use crate::ui::ui_error::{UIResult, InvalidSelection}; -use crate::ui::colors; -use super::text_pos::TextPos; use super::lines::Lines; +use super::text_pos::TextPos; +use crate::ui::colors; +use crate::ui::ui_error::{InvalidSelection, UIResult}; use bumpalo::collections::Vec as BumpVec; use snafu::ensure; use std::fmt; @@ -33,7 +33,6 @@ pub fn validate_raw_sel(raw_sel: RawSelection) -> UIResult { } pub fn validate_selection(start_pos: TextPos, end_pos: TextPos) -> UIResult { - ensure!( start_pos.line <= end_pos.line, InvalidSelection { @@ -55,14 +54,11 @@ pub fn validate_selection(start_pos: TextPos, end_pos: TextPos) -> UIResult( valid_sel: Selection, @@ -143,2381 +139,3 @@ pub fn create_selection_rects<'a>( Ok(all_rects) } } - -#[cfg(test)] -pub mod test_selection { - use crate::ui::ui_error::{UIResult, OutOfBounds}; - use crate::ui::text::{ - text_pos::TextPos, - selection::{validate_selection}, - big_selectable_text, - big_selectable_text::BigSelectableText, - caret_w_select::CaretWSelect, - lines::Lines, - }; - use crate::editor::mvc::ed_update::{ - move_caret_down, move_caret_left, move_caret_right, move_caret_up, MoveCaretFun, - }; - use crate::ui::util::slice_get; - use core::cmp::Ordering; - use pest::Parser; - use snafu::OptionExt; - use bumpalo::Bump; - use std::collections::HashMap; - use std::{ - slice::SliceIndex, - fs, - io::prelude::*, - path::Path, - time::{SystemTime} - }; - - #[derive(Parser)] - #[grammar = "../tests/selection.pest"] - pub struct LineParser; - - // show selection and caret position as symbols in lines for easy testing - pub fn convert_selection_to_dsl( - caret_w_select: CaretWSelect, - lines: &mut [String], - ) -> UIResult<&[String]> { - let selection_opt = caret_w_select.selection_opt; - let caret_pos = caret_w_select.caret_pos; - - if let Some(sel) = selection_opt { - let mut to_insert = vec![ - (sel.start_pos, '['), - (sel.end_pos, ']'), - (caret_pos, '|'), - ]; - let symbol_map: HashMap = - [('[', 2), (']', 0), ('|', 1)].iter().cloned().collect(); - - // sort for nice printing - to_insert.sort_by(|a, b| { - let pos_cmp = a.0.cmp(&b.0); - if pos_cmp == Ordering::Equal { - symbol_map.get(&a.1).cmp(&symbol_map.get(&b.1)) - } else { - pos_cmp - } - }); - - // insert symbols into text lines - for i in 0..to_insert.len() { - let (pos, insert_char) = *slice_get(i, &to_insert)?; - - insert_at_pos(lines, pos, insert_char)?; - - // shift position of following symbols now that symbol is inserted - for j in i..to_insert.len() { - let (old_pos, _) = get_mut_res(j, &mut to_insert)?; - - if old_pos.line == pos.line { - old_pos.column += 1; - } - } - } - } else { - insert_at_pos(lines, caret_pos, '|')?; - } - - Ok(lines) - } - - fn insert_at_pos(lines: &mut [String], pos: TextPos, insert_char: char) -> UIResult<()> { - let line = get_mut_res(pos.line, lines)?; - line.insert(pos.column, insert_char); - - Ok(()) - } - - // It's much nicer to have get_mut return a Result with clear error than an Option - fn get_mut_res( - index: usize, - vec: &mut [T], - ) -> UIResult<&mut >::Output> { - let vec_len = vec.len(); - - let elt_ref = vec.get_mut(index).context(OutOfBounds { - index, - collection_name: "Slice", - len: vec_len, - })?; - - Ok(elt_ref) - } - - fn big_text_from_str(lines_str: &str) -> BigSelectableText { - let epoch_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_nanos(); - let file_name = format!("temp_{:?}.txt", epoch_time); - let path = Path::new(&file_name); - - let mut temp_file = fs::File::create(path).unwrap(); - temp_file.write_all(lines_str.as_bytes()).unwrap(); - - let big_text = big_selectable_text::from_path(path).unwrap(); - fs::remove_file(path).unwrap(); - - big_text - } - - pub fn big_text_from_dsl_str(lines: &[String]) -> BigSelectableText { - big_text_from_str( - &lines - .iter() - .map(|line| line.replace(&['[', ']', '|'][..], "")) - .collect::>() - .join(""), - ) - } - - pub fn all_lines_vec(big_sel_text: &BigSelectableText) -> Vec { - let mut lines: Vec = Vec::new(); - - for i in 0..big_sel_text.nr_of_lines() { - lines.push( - big_sel_text.get_line(i).unwrap().to_string() - ); - } - - lines - } - - // Retrieve selection and position from formatted string - pub fn convert_dsl_to_selection( - lines: &[String], - ) -> Result { - let lines_str: String = lines.join(""); - - let parsed = LineParser::parse(Rule::linesWithSelect, &lines_str) - .expect("Selection test DSL parsing failed"); - - let mut caret_opt: Option<(usize, usize)> = None; - let mut sel_start_opt: Option<(usize, usize)> = None; - let mut sel_end_opt: Option<(usize, usize)> = None; - let mut line_nr = 0; - let mut col_nr = 0; - - for line in parsed { - for elt in line.into_inner() { - match elt.as_rule() { - Rule::optCaret => { - if elt.as_span().as_str() == "|" { - if caret_opt.is_some() { - return Err( - "Multiple carets found, there should be only one".to_owned() - ); - } else { - caret_opt = Some((line_nr, col_nr)); - } - } - } - Rule::optSelStart => { - if sel_start_opt.is_some() { - if elt.as_span().as_str() == "[" { - return Err("Found start of selection more than once, there should be only one".to_owned()); - } - } else if elt.as_span().as_str() == "[" { - sel_start_opt = Some((line_nr, col_nr)); - } - } - Rule::optSelEnd => { - if sel_end_opt.is_some() { - if elt.as_span().as_str() == "]" { - return Err("Found end of selection more than once, there should be only one".to_owned()); - } - } else if elt.as_span().as_str() == "]" { - sel_end_opt = Some((line_nr, col_nr)); - } - } - Rule::text => { - let split_str = elt - .as_span() - .as_str() - .split('\n') - .into_iter() - .collect::>(); - - if split_str.len() > 1 { - line_nr += split_str.len() - 1; - col_nr = 0 - } - if let Some(last_str) = split_str.last() { - col_nr += last_str.len() - } - } - _ => {} - } - } - } - - // Make sure return makes sense - if let Some((line, column)) = caret_opt { - let caret_pos = TextPos { line, column }; - if sel_start_opt.is_none() && sel_end_opt.is_none() { - Ok(CaretWSelect::new(caret_pos, None)) - } else if let Some((start_line, start_column)) = sel_start_opt { - if let Some((end_line, end_column)) = sel_end_opt { - Ok( - CaretWSelect::new( - caret_pos, - Some( - validate_selection( - TextPos { - line: start_line, - column: start_column, - }, - TextPos { - line: end_line, - column: end_column, - }, - ).unwrap() - ), - ) - ) - } else { - Err("Selection end ']' was not found, but selection start '[' was. Bad input string.".to_owned()) - } - } else { - Err("Selection start '[' was not found, but selection end ']' was. Bad input string.".to_owned()) - } - } else { - Err("No caret was found in lines.".to_owned()) - } - } - - // Convert nice string representations and compare results - fn assert_move( - pre_lines_str: &[&str], - expected_post_lines_str: &[&str], - shift_pressed: bool, - move_fun: MoveCaretFun, - arena: &Bump, - ) -> Result<(), String> { - let pre_lines: Vec = pre_lines_str.iter().map(|l| l.to_string()).collect(); - let expected_post_lines: Vec = expected_post_lines_str - .iter() - .map(|l| l.to_string()) - .collect(); - - let clean_text_buf = big_text_from_dsl_str(&pre_lines); - - let caret_w_select = - move_fun(shift_pressed, &clean_text_buf)?; - - let mut lines_vec = all_lines_vec(&clean_text_buf); - let post_lines_res = convert_selection_to_dsl(caret_w_select, &mut lines_vec); - - match post_lines_res { - Ok(post_lines) => { - assert_eq!(expected_post_lines, post_lines); - Ok(()) - } - Err(e) => Err(format!("{:?}", e)), - } - } - - #[test] - fn move_right() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], false, move_caret_right, arena)?; - assert_move(&["a|"], &["a|"], false, move_caret_right, arena)?; - assert_move(&["|A"], &["A|"], false, move_caret_right, arena)?; - assert_move(&["|abc"], &["a|bc"], false, move_caret_right, arena)?; - assert_move(&["a|bc"], &["ab|c"], false, move_caret_right, arena)?; - assert_move(&["abc|"], &["abc|"], false, move_caret_right, arena)?; - assert_move(&["| abc"], &[" |abc"], false, move_caret_right, arena)?; - assert_move(&["abc| "], &["abc |"], false, move_caret_right, arena)?; - assert_move(&["abc|\n", "d"], &["abc\n", "|d"], false, move_caret_right, arena)?; - assert_move(&["abc|\n", ""], &["abc\n", "|"], false, move_caret_right, arena)?; - assert_move( - &["abc\n", "|def"], - &["abc\n", "d|ef"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def| "], - &["abc\n", "def |"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def |\n", "ghi"], - &["abc\n", "def \n", "|ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def|\n", ""], - &["abc\n", "def\n", "|"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def\n", "ghi|\n", "jkl"], - &["abc\n", "def\n", "ghi\n", "|jkl"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def\n", "|ghi\n", "jkl"], - &["abc\n", "def\n", "g|hi\n", "jkl"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def\n", "g|hi\n", "jkl"], - &["abc\n", "def\n", "gh|i\n", "jkl"], - false, - move_caret_right, - arena, - )?; - - Ok(()) - } - - #[test] - fn move_left() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], false, move_caret_left, arena)?; - assert_move(&["|a"], &["|a"], false, move_caret_left, arena)?; - assert_move(&["|A"], &["|A"], false, move_caret_left, arena)?; - assert_move(&["a|bc"], &["|abc"], false, move_caret_left, arena)?; - assert_move(&["ab|c"], &["a|bc"], false, move_caret_left, arena)?; - assert_move(&["abc|"], &["ab|c"], false, move_caret_left, arena)?; - assert_move(&[" |abc"], &["| abc"], false, move_caret_left, arena)?; - assert_move(&["abc |"], &["abc| "], false, move_caret_left, arena)?; - assert_move(&["abc\n", "|d"], &["abc|\n", "d"], false, move_caret_left, arena)?; - assert_move(&["abc\n", "|"], &["abc|\n", ""], false, move_caret_left, arena)?; - assert_move( - &["abc\n", "d|ef"], - &["abc\n", "|def"], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def |"], - &["abc\n", "def| "], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def \n", "|ghi"], - &["abc\n", "def |\n", "ghi"], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def\n", "|"], - &["abc\n", "def|\n", ""], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def\n", "ghi\n", "|jkl"], - &["abc\n", "def\n", "ghi|\n", "jkl"], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def\n", "g|hi\n", "jkl"], - &["abc\n", "def\n", "|ghi\n", "jkl"], - false, - move_caret_left, - arena, - )?; - assert_move( - &["abc\n", "def\n", "gh|i\n", "jkl"], - &["abc\n", "def\n", "g|hi\n", "jkl"], - false, - move_caret_left, - arena, - )?; - - Ok(()) - } - - #[test] - fn move_up() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], false, move_caret_up, arena)?; - assert_move(&["|a"], &["|a"], false, move_caret_up, arena)?; - assert_move(&["A|"], &["|A"], false, move_caret_up, arena)?; - assert_move(&["a|bc"], &["|abc"], false, move_caret_up, arena)?; - assert_move(&["ab|c"], &["|abc"], false, move_caret_up, arena)?; - assert_move(&["abc|"], &["|abc"], false, move_caret_up, arena)?; - assert_move(&["|abc\n", "def"], &["|abc\n", "def"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "|def"], &["|abc\n", "def"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "d|ef"], &["a|bc\n", "def"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "de|f"], &["ab|c\n", "def"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "def|"], &["abc|\n", "def"], false, move_caret_up, arena)?; - assert_move( - &["abc\n", "def \n", "|ghi"], - &["abc\n", "|def \n", "ghi"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "g|hi"], - &["abc\n", "d|ef \n", "ghi"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "gh|i"], - &["abc\n", "de|f \n", "ghi"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "ghi|"], - &["abc\n", "def| \n", "ghi"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "de\n", "ghi|"], - &["abc\n", "de|\n", "ghi"], - false, - move_caret_up, - arena, - )?; - assert_move(&["abc\n", "de|"], &["ab|c\n", "de"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "d|e"], &["a|bc\n", "de"], false, move_caret_up, arena)?; - assert_move(&["abc\n", "|de"], &["|abc\n", "de"], false, move_caret_up, arena)?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], - &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cde|f\n", "ghijkl\n", "mnopqrst"], - &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - &["abcdefgh\n", "ijklmn\n", "op|qr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - &["abcdefgh\n", "ijkl|mn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - &["abcdef|gh\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefg|h\n", "ijklmn\n", "opqr\n", "st"], - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["a|bcdefgh\n", "ijklmn\n", "opqr\n", "st"], - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move( - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena, - )?; - assert_move(&["abc def gh |"], &["|abc def gh "], false, move_caret_up, arena)?; - assert_move(&["abc de|f gh "], &["|abc def gh "], false, move_caret_up, arena)?; - assert_move(&["ab|c def gh "], &["|abc def gh "], false, move_caret_up, arena)?; - assert_move(&["a|bc def gh "], &["|abc def gh "], false, move_caret_up, arena)?; - - Ok(()) - } - - #[test] - fn move_down() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], false, move_caret_down, arena)?; - assert_move(&["|a"], &["a|"], false, move_caret_down, arena)?; - assert_move(&["A|"], &["A|"], false, move_caret_down, arena)?; - assert_move(&["a|bc"], &["abc|"], false, move_caret_down, arena)?; - assert_move(&["ab|c"], &["abc|"], false, move_caret_down, arena)?; - assert_move(&["abc|"], &["abc|"], false, move_caret_down, arena)?; - assert_move(&["abc| "], &["abc |"], false, move_caret_down, arena)?; - assert_move( - &["abc\n", "|def"], - &["abc\n", "def|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "d|ef"], - &["abc\n", "def|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|f"], - &["abc\n", "def|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def|"], - &["abc\n", "def|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["|abc\n", "def"], - &["abc\n", "|def"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["a|bc\n", "def"], - &["abc\n", "d|ef"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["ab|c\n", "def"], - &["abc\n", "de|f"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc|\n", "def"], - &["abc\n", "def|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "|def \n", "ghi"], - &["abc\n", "def \n", "|ghi"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "d|ef \n", "ghi"], - &["abc\n", "def \n", "g|hi"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|f \n", "ghi"], - &["abc\n", "def \n", "gh|i"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def| \n", "ghi"], - &["abc\n", "def \n", "ghi|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def |\n", "ghi"], - &["abc\n", "def \n", "ghi|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|\n", "ghi"], - &["abc\n", "de\n", "gh|i"], - false, - move_caret_down, - arena, - )?; - assert_move(&["abc|\n", "de"], &["abc\n", "de|"], false, move_caret_down, arena)?; - assert_move(&["ab|c\n", "de"], &["abc\n", "de|"], false, move_caret_down, arena)?; - assert_move(&["a|bc\n", "de"], &["abc\n", "d|e"], false, move_caret_down, arena)?; - assert_move(&["|abc\n", "de"], &["abc\n", "|de"], false, move_caret_down, arena)?; - assert_move( - &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "cd|ef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "cdef\n", "ghij|kl\n", "mnopqrst"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], - false, - move_caret_down, - arena, - )?; - assert_move( - &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], - &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - false, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - false, - move_caret_down, - arena, - )?; - assert_move(&["abc def gh |"], &["abc def gh |"], false, move_caret_down, arena)?; - assert_move(&["abc de|f gh "], &["abc def gh |"], false, move_caret_down, arena)?; - assert_move(&["ab|c def gh "], &["abc def gh |"], false, move_caret_down, arena)?; - assert_move(&["a|bc def gh "], &["abc def gh |"], false, move_caret_down, arena)?; - assert_move(&["|abc def gh "], &["abc def gh |"], false, move_caret_down, arena)?; - - Ok(()) - } - - #[test] - fn start_selection_right() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], true, move_caret_right, arena)?; - assert_move(&["a|"], &["a|"], true, move_caret_right, arena)?; - assert_move(&["|A"], &["[A]|"], true, move_caret_right, arena)?; - assert_move(&["|abc"], &["[a]|bc"], true, move_caret_right, arena)?; - assert_move(&["a|bc"], &["a[b]|c"], true, move_caret_right, arena)?; - assert_move(&["abc|"], &["abc|"], true, move_caret_right, arena)?; - assert_move(&["| abc"], &["[ ]|abc"], true, move_caret_right, arena)?; - assert_move(&["abc| "], &["abc[ ]|"], true, move_caret_right, arena)?; - assert_move(&["abc|\n", "d"], &["abc[\n", "]|d"], true, move_caret_right, arena)?; - assert_move(&["abc|\n", ""], &["abc[\n", "]|"], true, move_caret_right, arena)?; - assert_move( - &["abc\n", "|def"], - &["abc\n", "[d]|ef"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def| "], - &["abc\n", "def[ ]|"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def |\n", "ghi"], - &["abc\n", "def [\n", "]|ghi"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def|\n", ""], - &["abc\n", "def[\n", "]|"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi|\n", "jkl"], - &["abc\n", "def\n", "ghi[\n", "]|jkl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def\n", "|ghi\n", "jkl"], - &["abc\n", "def\n", "[g]|hi\n", "jkl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def\n", "g|hi\n", "jkl"], - &["abc\n", "def\n", "g[h]|i\n", "jkl"], - true, - move_caret_right, - arena - )?; - - Ok(()) - } - - #[test] - fn start_selection_left() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], true, move_caret_left, arena)?; - assert_move(&["a|"], &["|[a]"], true, move_caret_left, arena)?; - assert_move(&["|A"], &["|A"], true, move_caret_left, arena)?; - assert_move(&["|abc"], &["|abc"], true, move_caret_left, arena)?; - assert_move(&["a|bc"], &["|[a]bc"], true, move_caret_left, arena)?; - assert_move(&["abc|"], &["ab|[c]"], true, move_caret_left, arena)?; - assert_move(&[" |abc"], &["|[ ]abc"], true, move_caret_left, arena)?; - assert_move(&["abc |"], &["abc|[ ]"], true, move_caret_left, arena)?; - assert_move(&["abc|\n", "d"], &["ab|[c]\n", "d"], true, move_caret_left, arena)?; - assert_move(&["abc\n", "|d"], &["abc|[\n", "]d"], true, move_caret_left, arena)?; - assert_move(&["abc\n", "|"], &["abc|[\n", "]"], true, move_caret_left, arena)?; - assert_move( - &["abc\n", " |def"], - &["abc\n", "|[ ]def"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "d|ef"], - &["abc\n", "|[d]ef"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "de|f "], - &["abc\n", "d|[e]f "], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "|"], - &["abc\n", "def|[\n", "]"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "|ghi\n", "jkl"], - &["abc\n", "def|[\n", "]ghi\n", "jkl"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "g|hi\n", "jkl"], - &["abc\n", "def\n", "|[g]hi\n", "jkl"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "gh|i\n", "jkl"], - &["abc\n", "def\n", "g|[h]i\n", "jkl"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi|\n", "jkl"], - &["abc\n", "def\n", "gh|[i]\n", "jkl"], - true, - move_caret_left, - arena - )?; - - Ok(()) - } - - #[test] - fn start_selection_down() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], true, move_caret_down, arena)?; - assert_move(&["|a"], &["[a]|"], true, move_caret_down, arena)?; - assert_move(&["A|"], &["A|"], true, move_caret_down, arena)?; - assert_move(&["a|bc"], &["a[bc]|"], true, move_caret_down, arena)?; - assert_move(&["ab|c"], &["ab[c]|"], true, move_caret_down, arena)?; - assert_move(&["abc|"], &["abc|"], true, move_caret_down, arena)?; - assert_move(&["abc| "], &["abc[ ]|"], true, move_caret_down, arena)?; - assert_move( - &["abc\n", "|def"], - &["abc\n", "[def]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "d|ef"], - &["abc\n", "d[ef]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|f"], - &["abc\n", "de[f]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def|"], - &["abc\n", "def|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["|abc\n", "def"], - &["[abc\n", "]|def"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["a|bc\n", "def"], - &["a[bc\n", "d]|ef"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab|c\n", "def"], - &["ab[c\n", "de]|f"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc|\n", "def"], - &["abc[\n", "def]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "|def \n", "ghi"], - &["abc\n", "[def \n", "]|ghi"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "d|ef \n", "ghi"], - &["abc\n", "d[ef \n", "g]|hi"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|f \n", "ghi"], - &["abc\n", "de[f \n", "gh]|i"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def| \n", "ghi"], - &["abc\n", "def[ \n", "ghi]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "def |\n", "ghi"], - &["abc\n", "def [\n", "ghi]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc\n", "de|\n", "ghi"], - &["abc\n", "de[\n", "gh]|i"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abc|\n", "de"], - &["abc[\n", "de]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab|c\n", "de"], - &["ab[c\n", "de]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["a|bc\n", "de"], - &["a[bc\n", "d]|e"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["|abc\n", "de"], - &["[abc\n", "]|de"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], - &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "cdef[\n", "ghij]|kl\n", "mnopqrst"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], - true, - move_caret_down, - arena, - )?; - assert_move( - &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], - &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "[cdef\n", "]|ghijkl\n", "mnopqrst"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], - &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], - true, - move_caret_down, - arena, - )?; - assert_move(&["abc def gh |"], &["abc def gh |"], true, move_caret_down, arena)?; - assert_move( - &["abc de|f gh "], - &["abc de[f gh ]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["ab|c def gh "], - &["ab[c def gh ]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["a|bc def gh "], - &["a[bc def gh ]|"], - true, - move_caret_down, - arena, - )?; - assert_move( - &["|abc def gh "], - &["[abc def gh ]|"], - true, - move_caret_down, - arena, - )?; - - Ok(()) - } - - #[test] - fn start_selection_up() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|"], &["|"], true, move_caret_up, arena)?; - assert_move(&["|a"], &["|a"], true, move_caret_up, arena)?; - assert_move(&["A|"], &["|[A]"], true, move_caret_up, arena)?; - assert_move(&["a|bc"], &["|[a]bc"], true, move_caret_up, arena)?; - assert_move(&["ab|c"], &["|[ab]c"], true, move_caret_up, arena)?; - assert_move(&["abc|"], &["|[abc]"], true, move_caret_up, arena)?; - assert_move(&["|abc\n", "def"], &["|abc\n", "def"], true, move_caret_up, arena)?; - assert_move( - &["abc\n", "|def"], - &["|[abc\n", "]def"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "d|ef"], - &["a|[bc\n", "d]ef"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "de|f"], - &["ab|[c\n", "de]f"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def|"], - &["abc|[\n", "def]"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "|ghi"], - &["abc\n", "|[def \n", "]ghi"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "g|hi"], - &["abc\n", "d|[ef \n", "g]hi"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "gh|i"], - &["abc\n", "de|[f \n", "gh]i"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "def \n", "ghi|"], - &["abc\n", "def|[ \n", "ghi]"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abc\n", "de\n", "ghi|"], - &["abc\n", "de|[\n", "ghi]"], - true, - move_caret_up, - arena, - )?; - assert_move(&["abc\n", "de|"], &["ab|[c\n", "de]"], true, move_caret_up, arena)?; - assert_move(&["abc\n", "d|e"], &["a|[bc\n", "d]e"], true, move_caret_up, arena)?; - assert_move(&["abc\n", "|de"], &["|[abc\n", "]de"], true, move_caret_up, arena)?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], - &["ab\n", "cdef\n", "ghijkl|[\n", "mnopqrst]"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - &["ab\n", "cdef|[\n", "ghijkl]\n", "mnopqrst"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], - &["ab\n", "cdef\n", "|[ghijkl\n", "]mnopqrst"], - true, - move_caret_up, - arena, - )?; - assert_move( - &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], - &[" |[ab\n", " ]cdef\n", "ghijkl\n", "mnopqrst"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], - &["ab\n", "cdef\n", "ghijkl|[\n", "mnopqr]st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["ab\n", "cde|f\n", "ghijkl\n", "mnopqrst"], - &["ab|[\n", "cde]f\n", "ghijkl\n", "mnopqrst"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - &["abcdefgh\n", "ijklmn\n", "op|[qr\n", "st]"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - &["abcdefgh\n", "ijkl|[mn\n", "opqr]\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - &["abcdef|[gh\n", "ijklmn]\n", "opqr\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], - &["|[abcdefgh]\n", "ijklmn\n", "opqr\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["abcdefg|h\n", "ijklmn\n", "opqr\n", "st"], - &["|[abcdefg]h\n", "ijklmn\n", "opqr\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["a|bcdefgh\n", "ijklmn\n", "opqr\n", "st"], - &["|[a]bcdefgh\n", "ijklmn\n", "opqr\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move( - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - &["|abcdefgh\n", "ijklmn\n", "opqr\n", "st"], - true, - move_caret_up, - arena, - )?; - assert_move(&["abc def gh |"], &["|[abc def gh ]"], true, move_caret_up, arena)?; - assert_move(&["abc de|f gh "], &["|[abc de]f gh "], true, move_caret_up, arena)?; - assert_move(&["ab|c def gh "], &["|[ab]c def gh "], true, move_caret_up, arena)?; - assert_move(&["a|bc def gh "], &["|[a]bc def gh "], true, move_caret_up, arena)?; - - Ok(()) - } - - #[test] - fn end_selection_right() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[A]|"], &["A|"], false, move_caret_right, arena)?; - assert_move(&["[a]|bc"], &["a|bc"], false, move_caret_right, arena)?; - assert_move(&["a[b]|c"], &["ab|c"], false, move_caret_right, arena)?; - assert_move(&["ab[c]|"], &["abc|"], false, move_caret_right, arena)?; - assert_move(&["[ ]|abc"], &[" |abc"], false, move_caret_right, arena)?; - assert_move(&["|[ ]abc"], &[" |abc"], false, move_caret_right, arena)?; - assert_move(&["a|[b]c"], &["ab|c"], false, move_caret_right, arena)?; - assert_move( - &["abc[\n", "]|d"], - &["abc\n", "|d"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc|[\n", "]d"], - &["abc\n", "|d"], - false, - move_caret_right, - arena, - )?; - assert_move(&["abc|[\n", "]"], &["abc\n", "|"], false, move_caret_right, arena)?; - assert_move( - &["abc\n", "[d]|ef"], - &["abc\n", "d|ef"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["abc\n", "def\n", "ghi[\n", "]|jkl"], - &["abc\n", "def\n", "ghi\n", "|jkl"], - false, - move_caret_right, - arena, - )?; - assert_move(&["[ab]|c"], &["ab|c"], false, move_caret_right, arena)?; - assert_move(&["[abc]|"], &["abc|"], false, move_caret_right, arena)?; - assert_move( - &["ab|[c\n", "]def\n", "ghi"], - &["abc\n", "|def\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["ab[c\n", "]|def\n", "ghi"], - &["abc\n", "|def\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["a|[bc\n", "]def\n", "ghi"], - &["abc\n", "|def\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["|[abc\n", "]def\n", "ghi"], - &["abc\n", "|def\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["a|[bc\n", "d]ef\n", "ghi"], - &["abc\n", "d|ef\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["|[abc\n", "def]\n", "ghi"], - &["abc\n", "def|\n", "ghi"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], - &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["|[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]"], - &["ab\n", "cdef\n", "ghijkl\n", "mnopqrst|"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["ab\n", "c[def\n", "ghijkl\n", "mno]|pqrst"], - &["ab\n", "cdef\n", "ghijkl\n", "mno|pqrst"], - false, - move_caret_right, - arena, - )?; - assert_move( - &["ab\n", "c|[def\n", "ghijkl\n", "mno]pqrst"], - &["ab\n", "cdef\n", "ghijkl\n", "mno|pqrst"], - false, - move_caret_right, - arena, - )?; - - Ok(()) - } - - #[test] - fn end_selection_left() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[A]|"], &["|A"], false, move_caret_left, arena)?; - assert_move(&["[a]|bc"], &["|abc"], false, move_caret_left, arena)?; - assert_move(&["a[b]|c"], &["a|bc"], false, move_caret_left, arena)?; - assert_move(&["ab[c]|"], &["ab|c"], false, move_caret_left, arena)?; - assert_move(&["[ ]|abc"], &["| abc"], false, move_caret_left, arena)?; - assert_move(&["|[ ]abc"], &["| abc"], false, move_caret_left, arena)?; - assert_move(&["a|[b]c"], &["a|bc"], false, move_caret_left, arena)?; - assert_move(&["abc[\n", "]|d"], &["abc|\n", "d"], false, move_caret_left, arena)?; - assert_move(&["abc|[\n", "]d"], &["abc|\n", "d"], false, move_caret_left, arena)?; - assert_move(&["abc|[\n", "]"], &["abc|\n", ""], false, move_caret_left, arena)?; - assert_move( - &["abc\n", "[d]|ef"], - &["abc\n", "|def"], - false, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi[\n", "]|jkl"], - &["abc\n", "def\n", "ghi|\n", "jkl"], - false, - move_caret_left, - arena - )?; - assert_move(&["[ab]|c"], &["|abc"], false, move_caret_left, arena)?; - assert_move(&["[abc]|"], &["|abc"], false, move_caret_left, arena)?; - assert_move( - &["ab|[c\n", "]def\n", "ghi"], - &["ab|c\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["ab[c\n", "]|def\n", "ghi"], - &["ab|c\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["a|[bc\n", "]def\n", "ghi"], - &["a|bc\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["|[abc\n", "]def\n", "ghi"], - &["|abc\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["a|[bc\n", "d]ef\n", "ghi"], - &["a|bc\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["|[abc\n", "def]\n", "ghi"], - &["|abc\n", "def\n", "ghi"], - false, - move_caret_left, - arena - )?; - assert_move( - &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], - &["|ab\n", "cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_left, - arena - )?; - assert_move( - &["|[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]"], - &["|ab\n", "cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_left, - arena - )?; - assert_move( - &["ab\n", "c[def\n", "ghijkl\n", "mno]|pqrst"], - &["ab\n", "c|def\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_left, - arena - )?; - assert_move( - &["ab\n", "c|[def\n", "ghijkl\n", "mno]pqrst"], - &["ab\n", "c|def\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_left, - arena - )?; - - Ok(()) - } - - #[test] - fn end_selection_down() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[a]|"], &["a|"], false, move_caret_down, arena)?; - assert_move(&["|[a]"], &["a|"], false, move_caret_down, arena)?; - assert_move(&["a|[bc]"], &["abc|"], false, move_caret_down, arena)?; - assert_move(&["ab[c]|"], &["abc|"], false, move_caret_down, arena)?; - assert_move(&["abc|[ ]"], &["abc |"], false, move_caret_down, arena)?; - assert_move( - &["abc\n", "|[def]"], - &["abc\n", "def|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "d|[ef]"], - &["abc\n", "def|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "de|[f]"], - &["abc\n", "def|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["[abc\n", "]|def"], - &["abc\n", "|def"], - false, - move_caret_down, - arena - )?; - assert_move( - &["a[bc\n", "d]|ef"], - &["abc\n", "d|ef"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab|[c\n", "de]f"], - &["abc\n", "de|f"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc[\n", "def]|"], - &["abc\n", "def|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "|[def \n", "]ghi"], - &["abc\n", "def \n", "|ghi"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "d[ef \n", "g]|hi"], - &["abc\n", "def \n", "g|hi"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "de[f \n", "gh]|i"], - &["abc\n", "def \n", "gh|i"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "def[ \n", "ghi]|"], - &["abc\n", "def \n", "ghi|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "def [\n", "ghi]|"], - &["abc\n", "def \n", "ghi|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "de[\n", "gh]|i"], - &["abc\n", "de\n", "gh|i"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc|[\n", "de]"], - &["abc\n", "de|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab[c\n", "de]|"], - &["abc\n", "de|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["a|[bc\n", "d]e"], - &["abc\n", "d|e"], - false, - move_caret_down, - arena - )?; - assert_move( - &["[abc\n", "]|de"], - &["abc\n", "|de"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], - &["ab\n", "cd|ef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab\n", "cdef|[\n", "ghij]kl\n", "mnopqrst"], - &["ab\n", "cdef\n", "ghij|kl\n", "mnopqrst"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], - &["ab\n", "cdef\n", "ghijkl\n", "mnopqr|st"], - false, - move_caret_down, - arena - )?; - assert_move( - &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], - &[" ab\n", " |cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab\n", "|[cdef\n", "]ghijkl\n", "mnopqrst"], - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], - &["ab\n", "cdef\n", "ghijkl\n", "|mnopqrst"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "st|"], - false, - move_caret_down, - arena - )?; - assert_move( - &["abc de[f gh ]|"], - &["abc def gh |"], - false, - move_caret_down, - arena - )?; - assert_move( - &["ab|[c def gh ]"], - &["abc def gh |"], - false, - move_caret_down, - arena - )?; - assert_move( - &["a[bc def gh ]|"], - &["abc def gh |"], - false, - move_caret_down, - arena - )?; - assert_move( - &["[abc def gh ]|"], - &["abc def gh |"], - false, - move_caret_down, - arena - )?; - - Ok(()) - } - - #[test] - fn end_selection_up() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[a]|"], &["|a"], false, move_caret_up, arena)?; - assert_move(&["|[a]"], &["|a"], false, move_caret_up, arena)?; - assert_move(&["a|[bc]"], &["a|bc"], false, move_caret_up, arena)?; - assert_move(&["ab[c]|"], &["ab|c"], false, move_caret_up, arena)?; - assert_move(&["abc|[ ]"], &["abc| "], false, move_caret_up, arena)?; - assert_move( - &["abc\n", "|[def]"], - &["abc\n", "|def"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "d|[ef]"], - &["abc\n", "d|ef"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "de|[f]"], - &["abc\n", "de|f"], - false, - move_caret_up, - arena - )?; - assert_move( - &["[abc\n", "]|def"], - &["|abc\n", "def"], - false, - move_caret_up, - arena - )?; - assert_move( - &["a[bc\n", "d]|ef"], - &["a|bc\n", "def"], - false, - move_caret_up, - arena - )?; - assert_move( - &["ab|[c\n", "de]f"], - &["ab|c\n", "def"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc[\n", "def]|"], - &["abc|\n", "def"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "|[def \n", "]ghi"], - &["abc\n", "|def \n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "d[ef \n", "g]|hi"], - &["abc\n", "d|ef \n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "de|[f \n", "gh]i"], - &["abc\n", "de|f \n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "def[ \n", "ghi]|"], - &["abc\n", "def| \n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "def [\n", "ghi]|"], - &["abc\n", "def |\n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "de[\n", "gh]|i"], - &["abc\n", "de|\n", "ghi"], - false, - move_caret_up, - arena - )?; - assert_move(&["abc|[\n", "de]"], &["abc|\n", "de"], false, move_caret_up, arena)?; - assert_move(&["ab[c\n", "de]|"], &["ab|c\n", "de"], false, move_caret_up, arena)?; - assert_move(&["a|[bc\n", "d]e"], &["a|bc\n", "de"], false, move_caret_up, arena)?; - assert_move(&["[abc\n", "]|de"], &["|abc\n", "de"], false, move_caret_up, arena)?; - assert_move( - &["ab[\n", "cd]|ef\n", "ghijkl\n", "mnopqrst"], - &["ab|\n", "cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "cdef|[\n", "ghij]kl\n", "mnopqrst"], - &["ab\n", "cdef|\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl[\n", "mnopqr]|st"], - &["ab\n", "cdef\n", "ghijkl|\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &[" [ab\n", " ]|cdef\n", "ghijkl\n", "mnopqrst"], - &[" |ab\n", " cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "|[cdef\n", "]ghijkl\n", "mnopqrst"], - &["ab\n", "|cdef\n", "ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "[ghijkl\n", "]|mnopqrst"], - &["ab\n", "cdef\n", "|ghijkl\n", "mnopqrst"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abcdefgh[\n", "ijklmn]|\n", "opqr\n", "st"], - &["abcdefgh|\n", "ijklmn\n", "opqr\n", "st"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn[\n", "opqr]|\n", "st"], - &["abcdefgh\n", "ijklmn|\n", "opqr\n", "st"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr[\n", "st]|"], - &["abcdefgh\n", "ijklmn\n", "opqr|\n", "st"], - false, - move_caret_up, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "opqr\n", "[st]|"], - &["abcdefgh\n", "ijklmn\n", "opqr\n", "|st"], - false, - move_caret_up, - arena - )?; - assert_move(&["abc de[f gh ]|"], &["abc de|f gh "], false, move_caret_up, arena)?; - assert_move(&["ab|[c def gh ]"], &["ab|c def gh "], false, move_caret_up, arena)?; - assert_move(&["a[bc def gh ]|"], &["a|bc def gh "], false, move_caret_up, arena)?; - assert_move(&["[abc def gh ]|"], &["|abc def gh "], false, move_caret_up, arena)?; - - Ok(()) - } - - #[test] - fn extend_selection_right() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[a]|bc"], &["[ab]|c"], true, move_caret_right, arena)?; - assert_move(&["a[b]|c"], &["a[bc]|"], true, move_caret_right, arena)?; - assert_move(&["[ab]|c"], &["[abc]|"], true, move_caret_right, arena)?; - assert_move(&["[ ]|abc"], &["[ a]|bc"], true, move_caret_right, arena)?; - assert_move(&["[abc]|"], &["[abc]|"], true, move_caret_right, arena)?; - assert_move(&["a[bc]|"], &["a[bc]|"], true, move_caret_right, arena)?; - assert_move(&["ab[c]|"], &["ab[c]|"], true, move_caret_right, arena)?; - assert_move( - &["abc[\n", "]|d"], - &["abc[\n", "d]|"], - true, - move_caret_right, - arena - )?; - assert_move(&["ab[c]|\n", ""], &["ab[c\n", "]|"], true, move_caret_right, arena)?; - assert_move( - &["ab[c]|\n", "d"], - &["ab[c\n", "]|d"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi[\n", "]|jkl"], - &["abc\n", "def\n", "ghi[\n", "j]|kl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["ab[c\n", "def\n", "ghi\n", "]|jkl"], - &["ab[c\n", "def\n", "ghi\n", "j]|kl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["ab[c\n", "def\n", "]|ghi\n", "jkl"], - &["ab[c\n", "def\n", "g]|hi\n", "jkl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["[abc\n", "def\n", "ghi\n", "jk]|l"], - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - true, - move_caret_right, - arena - )?; - assert_move( - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - true, - move_caret_right, - arena - )?; - - Ok(()) - } - - #[test] - fn extend_selection_left() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["ab|[c]"], &["a|[bc]"], true, move_caret_left, arena)?; - assert_move(&["a|[bc]"], &["|[abc]"], true, move_caret_left, arena)?; - assert_move(&["|[abc]"], &["|[abc]"], true, move_caret_left, arena)?; - assert_move(&["|[ab]c"], &["|[ab]c"], true, move_caret_left, arena)?; - assert_move(&["|[a]bc"], &["|[a]bc"], true, move_caret_left, arena)?; - assert_move(&[" |[a]bc"], &["|[ a]bc"], true, move_caret_left, arena)?; - assert_move( - &["abc|[\n", "]d"], - &["ab|[c\n", "]d"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "|[d]"], - &["abc|[\n", "d]"], - true, - move_caret_left, - arena - )?; - assert_move(&["ab|[c\n", "]"], &["a|[bc\n", "]"], true, move_caret_left, arena)?; - assert_move( - &["abc\n", "def|[\n", "ghi\n", "j]kl"], - &["abc\n", "de|[f\n", "ghi\n", "j]kl"], - true, - move_caret_left, - arena - )?; - assert_move( - &["a|[bc\n", "def\n", "ghi\n", "jkl]"], - &["|[abc\n", "def\n", "ghi\n", "jkl]"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi\n", "|[jkl]"], - &["abc\n", "def\n", "ghi|[\n", "jkl]"], - true, - move_caret_left, - arena - )?; - - Ok(()) - } - - #[test] - fn extend_selection_up() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["ab|[c]"], &["|[abc]"], true, move_caret_up, arena)?; - assert_move(&["a|[bc]"], &["|[abc]"], true, move_caret_up, arena)?; - assert_move(&["|[abc]"], &["|[abc]"], true, move_caret_up, arena)?; - assert_move(&["|[ab]c"], &["|[ab]c"], true, move_caret_up, arena)?; - assert_move(&["|[a]bc"], &["|[a]bc"], true, move_caret_up, arena)?; - assert_move(&[" |[a]bc"], &["|[ a]bc"], true, move_caret_up, arena)?; - assert_move(&["ab[c]|"], &["|[ab]c"], true, move_caret_up, arena)?; - assert_move(&["[a]|"], &["|a"], true, move_caret_up, arena)?; - assert_move(&["[a]|bc"], &["|abc"], true, move_caret_up, arena)?; - assert_move(&["[a]|bc\n", "d"], &["|abc\n", "d"], true, move_caret_up, arena)?; - assert_move( - &["abc\n", "de[f]|"], - &["abc|[\n", "de]f"], - true, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "de|[f]"], - &["ab|[c\n", "def]"], - true, - move_caret_up, - arena - )?; - assert_move( - &["ab|[c\n", "def]"], - &["|[abc\n", "def]"], - true, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "[mnopqr]|st"], - &["ab\n", "cdef\n", "ghijkl|[\n", "]mnopqrst"], - true, - move_caret_up, - arena - )?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "[mnopqrs]|t"], - &["ab\n", "cdef\n", "ghijkl|[\n", "]mnopqrst"], - true, - move_caret_up, - arena - )?; - assert_move( - &["abcdefgh\n", "ijklmn\n", "|[o]pqr\n", "st"], - &["abcdefgh\n", "|[ijklmn\n", "o]pqr\n", "st"], - true, - move_caret_up, - arena - )?; - - Ok(()) - } - - #[test] - fn extend_selection_down() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[ab]|c"], &["[abc]|"], true, move_caret_down, arena)?; - assert_move(&["[a]|bc"], &["[abc]|"], true, move_caret_down, arena)?; - assert_move(&["[abc]|"], &["[abc]|"], true, move_caret_down, arena)?; - assert_move(&["|[ab]c"], &["ab[c]|"], true, move_caret_down, arena)?; - assert_move(&["|[a]bc"], &["a[bc]|"], true, move_caret_down, arena)?; - assert_move( - &["[a]|bc\n", "d"], - &["[abc\n", "d]|"], - true, - move_caret_down, - arena - )?; - assert_move( - &["[a]|bc\n", "de"], - &["[abc\n", "d]|e"], - true, - move_caret_down, - arena - )?; - assert_move( - &["[abc\n", "d]|e"], - &["[abc\n", "de]|"], - true, - move_caret_down, - arena - )?; - assert_move(&["[a]|bc\n", ""], &["[abc\n", "]|"], true, move_caret_down, arena)?; - assert_move( - &["ab\n", "cdef\n", "ghijkl\n", "[mnopqr]|st"], - &["ab\n", "cdef\n", "ghijkl\n", "[mnopqrst]|"], - true, - move_caret_down, - arena - )?; - assert_move( - &["a[b\n", "cdef\n", "ghijkl\n", "mnopqr]|st"], - &["a[b\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], - true, - move_caret_down, - arena - )?; - assert_move( - &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], - &["[ab\n", "cdef\n", "ghijkl\n", "mnopqrst]|"], - true, - move_caret_down, - arena - )?; - assert_move( - &["abcd[efgh]|\n", "ijklmn\n", "opqr\n", "st"], - &["abcd[efgh\n", "ijklmn]|\n", "opqr\n", "st"], - true, - move_caret_down, - arena - )?; - assert_move( - &["abcd[e]|fgh\n", "ijklmn\n", "opqr\n", "st"], - &["abcd[efgh\n", "ijklm]|n\n", "opqr\n", "st"], - true, - move_caret_down, - arena - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_right() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["ab|[c]"], &["abc|"], true, move_caret_right, arena)?; - assert_move(&["a|[bc]"], &["ab|[c]"], true, move_caret_right, arena)?; - assert_move(&["|[abc]"], &["a|[bc]"], true, move_caret_right, arena)?; - assert_move( - &["|[abc\n", "def\n", "ghi\n", "jkl]"], - &["a|[bc\n", "def\n", "ghi\n", "jkl]"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "d|[ef\n", "]ghi\n", "jkl"], - &["abc\n", "de|[f\n", "]ghi\n", "jkl"], - true, - move_caret_right, - arena - )?; - assert_move( - &["abc\n", "de|[f]\n", "ghi\n", "jkl"], - &["abc\n", "def|\n", "ghi\n", "jkl"], - true, - move_caret_right, - arena - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_left() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["ab[c]|"], &["ab|c"], true, move_caret_left, arena)?; - assert_move(&["a[bc]|"], &["a[b]|c"], true, move_caret_left, arena)?; - assert_move(&["[abc]|"], &["[ab]|c"], true, move_caret_left, arena)?; - assert_move( - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - &["[abc\n", "def\n", "ghi\n", "jk]|l"], - true, - move_caret_left, - arena - )?; - assert_move( - &["|[abc\n", "def\n", "ghi\n", "jkl]"], - &["|[abc\n", "def\n", "ghi\n", "jkl]"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "def[\n", "]|ghi\n", "jkl"], - &["abc\n", "def|\n", "ghi\n", "jkl"], - true, - move_caret_left, - arena - )?; - assert_move( - &["abc\n", "d[ef\n", "gh]|i\n", "jkl"], - &["abc\n", "d[ef\n", "g]|hi\n", "jkl"], - true, - move_caret_left, - arena - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_up() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["[abc]|"], &["|abc"], true, move_caret_up, arena)?; - assert_move(&["[ab]|c"], &["|abc"], true, move_caret_up, arena)?; - assert_move(&["[a]|bc"], &["|abc"], true, move_caret_up, arena)?; - assert_move(&["|abc"], &["|abc"], true, move_caret_up, arena)?; - assert_move( - &["[abc\n", "def]|"], - &["[abc]|\n", "def"], - true, - move_caret_up, - arena - )?; - assert_move( - &["[abc\n", "de]|f"], - &["[ab]|c\n", "def"], - true, - move_caret_up, - arena - )?; - assert_move( - &["[abc\n", "def\n", "ghi\n", "jkl]|"], - &["[abc\n", "def\n", "ghi]|\n", "jkl"], - true, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "def\n", "ghi[\n", "jkl]|"], - &["abc\n", "def\n", "ghi|\n", "jkl"], - true, - move_caret_up, - arena - )?; - assert_move( - &["abc\n", "d[ef\n", "ghi\n", "jk]|l"], - &["abc\n", "d[ef\n", "gh]|i\n", "jkl"], - true, - move_caret_up, - arena - )?; - assert_move( - &["[abc\n", "d]|ef\n", "ghi\n", "jkl"], - &["[a]|bc\n", "def\n", "ghi\n", "jkl"], - true, - move_caret_up, - arena - )?; - - Ok(()) - } - - #[test] - fn shrink_selection_down() -> Result<(), String> { - let arena = &Bump::new(); - - assert_move(&["|[abc]"], &["abc|"], true, move_caret_down, arena)?; - assert_move( - &["|[abc\n", "def]"], - &["abc\n", "|[def]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["a|[bc\n", "def]"], - &["abc\n", "d|[ef]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["|[abc\n", "def\n", "ghi]"], - &["abc\n", "|[def\n", "ghi]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["ab|[c\n", "def\n", "ghi]"], - &["abc\n", "de|[f\n", "ghi]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["abc\n", "de|[f\n", "ghi]"], - &["abc\n", "def\n", "gh|[i]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["abcdef|[\n", "ghij\n", "kl]"], - &["abcdef\n", "ghij|[\n", "kl]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["abcde|[f\n", "ghij\n", "kl]"], - &["abcdef\n", "ghij|[\n", "kl]"], - true, - move_caret_down, - arena - )?; - assert_move( - &["ab|[cdef\n", "ghij\n", "kl]"], - &["abcdef\n", "gh|[ij\n", "kl]"], - true, - move_caret_down, - arena - )?; - - Ok(()) - } -} diff --git a/editor/src/ui/text/text_pos.rs b/editor/src/ui/text/text_pos.rs index f10dc5f66f..3590b74401 100644 --- a/editor/src/ui/text/text_pos.rs +++ b/editor/src/ui/text/text_pos.rs @@ -1,4 +1,3 @@ - use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] @@ -25,4 +24,4 @@ impl PartialEq for TextPos { } } -impl Eq for TextPos {} \ No newline at end of file +impl Eq for TextPos {} diff --git a/editor/src/ui/ui_error.rs b/editor/src/ui/ui_error.rs index 10dfbd964b..ec422d1663 100644 --- a/editor/src/ui/ui_error.rs +++ b/editor/src/ui/ui_error.rs @@ -47,4 +47,4 @@ impl From for String { fn from(ui_error: UIError) -> Self { format!("{}", ui_error) } -} \ No newline at end of file +} diff --git a/editor/src/ui/util.rs b/editor/src/ui/util.rs index 26c1f1867b..8fba081a08 100644 --- a/editor/src/ui/util.rs +++ b/editor/src/ui/util.rs @@ -1,15 +1,5 @@ +pub fn is_newline(char_ref: &char) -> bool { + let newline_codes = vec!['\u{d}', '\n']; -use super::ui_error::{UIResult, OutOfBounds}; -use snafu::OptionExt; -use std::slice::SliceIndex; - -// replace vec methods that return Option with ones that return Result and proper Error -pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&>::Output> { - let elt_ref = slice.get(index).context(OutOfBounds { - index, - collection_name: "Slice", - len: slice.len(), - })?; - - Ok(elt_ref) -} \ No newline at end of file + newline_codes.contains(char_ref) +}