implemented selection test DSL, added caret tests for move right

This commit is contained in:
Anton-4 2021-01-02 19:33:26 +01:00
parent 6e245b20c9
commit 1acbe42962
9 changed files with 244 additions and 5 deletions

2
Cargo.lock generated
View file

@ -2672,6 +2672,8 @@ dependencies = [
"log",
"maplit",
"page_size",
"pest",
"pest_derive",
"pretty_assertions",
"quickcheck",
"quickcheck_macros",

View file

@ -66,6 +66,8 @@ cgmath = "0.17.0"
itertools = "0.9.0"
snafu = { version = "0.6", features = ["backtraces"] }
colored = "2"
pest = "2.1"
pest_derive = "2.1"
[dependencies.bytemuck]

View file

@ -55,8 +55,8 @@ These are potentially inspirational resources for the editor's design.
### Productivity features
* When refactoring;
- cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
- When renaming a function for example, references in comments should be brought to the user's attention.
- Cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
- Ability to link e.g. variable name in comments to actual variable name. Comment is automatically updated when variable name is changed.
* Automatically create all "arms" when pattern matching after entering `when var is` based on the type.
- All `when ... is` should be updated if the type is changed, e.g. adding Indigo to the Color type should add an arm everywhere where `when color is` is used.

View file

@ -8,6 +8,11 @@
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
extern crate pest;
#[cfg(test)]
#[macro_use]
extern crate pest_derive;
use crate::error::print_err;
use crate::graphics::lowlevel::buffer::create_rect_buffers;
use crate::graphics::lowlevel::ortho::{init_ortho, update_ortho_buffer, OrthoResources};

View file

@ -2,7 +2,7 @@ use crate::error::{EdResult, InvalidSelection};
use crate::graphics::colors;
use crate::graphics::primitives::rect::Rect;
use crate::tea::model::RawSelection;
use crate::vec_result::get_res;
use crate::vec_result::{get_res};
use bumpalo::collections::Vec as BumpVec;
use bumpalo::Bump;
use snafu::ensure;
@ -150,3 +150,222 @@ pub fn create_selection_rects<'a>(
Ok(all_rects)
}
}
#[cfg(test)]
mod test_parse {
use crate::tea::update::{move_caret_left, move_caret_right, move_caret_down, move_caret_up};
use crate::tea::model::{RawSelection, Position};
use crate::error::{EdResult, OutOfBounds};
use crate::vec_result::{get_res};
use snafu::OptionExt;
use std::slice::SliceIndex;
use pest::Parser;
#[derive(Parser)]
#[grammar = "../tests/selection.pest"]
pub struct LineParser;
fn convert_selection_to_dsl(raw_sel_opt: Option<RawSelection>, caret_pos: Position, lines: &mut [String]) -> EdResult<&[String]> {
if let Some(raw_sel) = raw_sel_opt {
let mut to_insert = vec![(raw_sel.start_pos, '['), (raw_sel.end_pos, ']'), (caret_pos, '|')];
to_insert.sort();
for i in 0..to_insert.len() {
let (pos, insert_char) = *get_res(i, &to_insert)?;
insert_at_pos(lines, pos, insert_char)?;
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: Position, insert_char: char) -> EdResult<()> {
let line = get_mut_res(pos.line, lines)?;
line.insert(pos.column, insert_char);
Ok(())
}
fn get_mut_res<T>(index: usize, vec: & mut [T]) -> EdResult<& mut <usize as SliceIndex<[T]>>::Output> {
let vec_len = vec.len();
let elt_ref = vec.get_mut(index).context(OutOfBounds {
index,
vec_len,
})?;
Ok(elt_ref)
}
fn convert_dsl_to_selection(lines: &[String]) -> Result<(Option<RawSelection>, Position), String> {
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));
col_nr += elt.as_span().as_str().len();
}
}
},
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));
col_nr += elt.as_span().as_str().len();
}
},
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));
col_nr += elt.as_span().as_str().len();
}
},
Rule::text => {
let split_str = elt.as_span().as_str().split('\n').into_iter().collect::<Vec<&str>>();
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()
}
}
_ => {}
}
}
}
if let Some((line, column)) = caret_opt {
let caret_pos =
Position {
line,
column
};
if sel_start_opt.is_none() && sel_end_opt.is_none() {
Ok ((
None,
caret_pos
))
} else if let Some((start_line, start_column)) = sel_start_opt {
if let Some((end_line, end_column)) = sel_end_opt {
Ok ((
Some (
RawSelection {
start_pos :
Position {
line: start_line,
column: start_column
},
end_pos :
Position {
line: end_line,
column: end_column
}
}
),
caret_pos
))
} 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())
}
}
fn assert_move(
pre_lines_str: &[&str],
expected_post_lines_str: &[&str],
shift_pressed: bool,
move_fun:
fn(Position, Option<RawSelection>, bool, &[String]) -> (Position, Option<RawSelection>)
) -> Result<(), String> {
let pre_lines: Vec<String> = pre_lines_str.iter().map(|l| l.to_string()).collect();
let expected_post_lines: Vec<String> = expected_post_lines_str.iter().map(|l| l.to_string()).collect();
let (sel_opt, caret_pos) = convert_dsl_to_selection(&pre_lines)?;
let mut clean_lines =
pre_lines.into_iter()
.map(|line| {
line.replace(&['[', ']', '|'][..], "")
})
.collect::<Vec<String>>();
let (new_caret_pos, new_sel_opt) = move_fun(caret_pos, sel_opt, shift_pressed, &clean_lines);
let post_lines_res =
convert_selection_to_dsl(new_sel_opt, new_caret_pos, &mut clean_lines);
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> {
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(())
}
}

View file

@ -15,7 +15,6 @@ pub fn init_model() -> Model {
}
}
//Is model.rs the right place for these structs?
#[derive(Debug, Copy, Clone)]
pub struct Position {
pub line: usize,

View file

@ -1,5 +1,5 @@
pub fn is_newline(char_ref: &char) -> bool {
let newline_codes = vec!['\u{d}'];
let newline_codes = vec!['\u{d}', '\n'];
newline_codes.contains(char_ref)
}

View file

@ -13,3 +13,4 @@ pub fn get_res<T>(index: usize, vec: &[T]) -> EdResult<&<usize as SliceIndex<[T]
Ok(elt_ref)
}

View file

@ -0,0 +1,11 @@
text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n")* }
caret = {"|"}
optSelStart = { "["{0,1} }
optSelEnd = { "]"{0,1} }
optCaret = { caret{0,1} }
linesWithSelect = { SOI ~ text ~ optCaret ~ text ~ optSelStart ~ text ~ optCaret ~ text ~ optCaret ~ text ~ optSelEnd ~ text ~ optCaret ~ text ~ EOI}