mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 23:04:49 +00:00
show instructions if no file was opened, fmt
This commit is contained in:
parent
d6dc6dbf8b
commit
76e2edff97
15 changed files with 229 additions and 282 deletions
|
@ -3,7 +3,7 @@
|
||||||
Run the following from the roc folder:
|
Run the following from the roc folder:
|
||||||
|
|
||||||
```
|
```
|
||||||
cargo run edit
|
cargo run edit examples/hello-world/Hello.roc
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
|
@ -27,19 +27,15 @@ pub enum EdError {
|
||||||
backtrace: Backtrace,
|
backtrace: Backtrace,
|
||||||
},
|
},
|
||||||
#[snafu(display("MissingGlyphDims: glyph_dim_rect_opt was None for model. It needs to be set using the example_code_glyph_rect function."))]
|
#[snafu(display("MissingGlyphDims: glyph_dim_rect_opt was None for model. It needs to be set using the example_code_glyph_rect function."))]
|
||||||
MissingGlyphDims {
|
MissingGlyphDims { backtrace: Backtrace },
|
||||||
backtrace: Backtrace,
|
#[snafu(display(
|
||||||
},
|
"FileOpenFailed: failed to open file with path {} with the following error: {}.",
|
||||||
#[snafu(display("FileOpenFailed: failed to open file with path {} with the following error: {}.", path_str, err_msg))]
|
path_str,
|
||||||
FileOpenFailed {
|
err_msg
|
||||||
path_str: String,
|
))]
|
||||||
err_msg: String,
|
FileOpenFailed { path_str: String, err_msg: String },
|
||||||
},
|
|
||||||
#[snafu(display("TextBufReadFailed: the file {} could be opened but we encountered the following error while trying to read it: {}.", path_str, err_msg))]
|
#[snafu(display("TextBufReadFailed: the file {} could be opened but we encountered the following error while trying to read it: {}.", path_str, err_msg))]
|
||||||
TextBufReadFailed {
|
TextBufReadFailed { path_str: String, err_msg: String },
|
||||||
path_str: String,
|
|
||||||
err_msg: String
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type EdResult<T, E = EdError> = std::result::Result<T, E>;
|
pub type EdResult<T, E = EdError> = std::result::Result<T, E>;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Adapted from https://github.com/sotrh/learn-wgpu
|
// Adapted from https://github.com/sotrh/learn-wgpu
|
||||||
// by Benjamin Hansen, licensed under the MIT license
|
// by Benjamin Hansen, licensed under the MIT license
|
||||||
use super::vertex::Vertex;
|
use super::vertex::Vertex;
|
||||||
use crate::graphics::primitives::rect::Rect;
|
|
||||||
use crate::graphics::colors::to_slice;
|
use crate::graphics::colors::to_slice;
|
||||||
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use bumpalo::collections::Vec as BumpVec;
|
use bumpalo::collections::Vec as BumpVec;
|
||||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::mvc::ed_model::EdModel;
|
use crate::mvc::ed_model::EdModel;
|
||||||
use crate::mvc::update::{move_caret_down, move_caret_left, move_caret_right, move_caret_up, MoveCaretFun};
|
use crate::mvc::update::{
|
||||||
|
move_caret_down, move_caret_left, move_caret_right, move_caret_up, MoveCaretFun,
|
||||||
|
};
|
||||||
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
use winit::event::{ElementState, ModifiersState, VirtualKeyCode};
|
||||||
|
|
||||||
pub fn handle_keydown(
|
pub fn handle_keydown(
|
||||||
|
@ -15,18 +17,10 @@ pub fn handle_keydown(
|
||||||
}
|
}
|
||||||
|
|
||||||
match virtual_keycode {
|
match virtual_keycode {
|
||||||
Left => {
|
Left => handle_arrow(move_caret_left, &modifiers, ed_model),
|
||||||
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),
|
||||||
Up => {
|
Down => handle_arrow(move_caret_down, &modifiers, ed_model),
|
||||||
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)
|
|
||||||
}
|
|
||||||
Copy => {
|
Copy => {
|
||||||
todo!("copy");
|
todo!("copy");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
mod def;
|
mod def;
|
||||||
mod expr;
|
mod expr;
|
||||||
pub mod roc_file;
|
|
||||||
mod module;
|
mod module;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
mod pool;
|
mod pool;
|
||||||
|
pub mod roc_file;
|
||||||
mod scope;
|
mod scope;
|
||||||
mod types;
|
mod types;
|
||||||
|
|
|
@ -23,9 +23,10 @@ use crate::graphics::primitives::text::{
|
||||||
};
|
};
|
||||||
use crate::graphics::style::CODE_FONT_SIZE;
|
use crate::graphics::style::CODE_FONT_SIZE;
|
||||||
use crate::graphics::style::CODE_TXT_XY;
|
use crate::graphics::style::CODE_TXT_XY;
|
||||||
use crate::mvc::ed_model::EdModel;
|
|
||||||
use crate::mvc::{ed_model, update, ed_view};
|
|
||||||
use crate::mvc::app_model::AppModel;
|
use crate::mvc::app_model::AppModel;
|
||||||
|
use crate::mvc::ed_model::EdModel;
|
||||||
|
use crate::mvc::{ed_model, ed_view, update};
|
||||||
|
use crate::resources::strings::NOTHING_OPENED;
|
||||||
use crate::vec_result::get_res;
|
use crate::vec_result::get_res;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use cgmath::Vector2;
|
use cgmath::Vector2;
|
||||||
|
@ -45,11 +46,12 @@ pub mod error;
|
||||||
pub mod graphics;
|
pub mod graphics;
|
||||||
mod keyboard_input;
|
mod keyboard_input;
|
||||||
pub mod lang;
|
pub mod lang;
|
||||||
mod selection;
|
|
||||||
mod mvc;
|
mod mvc;
|
||||||
|
mod resources;
|
||||||
|
mod selection;
|
||||||
|
mod text_buffer;
|
||||||
mod util;
|
mod util;
|
||||||
mod vec_result;
|
mod vec_result;
|
||||||
mod text_buffer;
|
|
||||||
|
|
||||||
/// The editor is actually launched from the CLI if you pass it zero arguments,
|
/// The editor is actually launched from the CLI if you pass it zero arguments,
|
||||||
/// or if you provide it 1 or more files or directories to open on launch.
|
/// or if you provide it 1 or more files or directories to open on launch.
|
||||||
|
@ -135,18 +137,15 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||||
let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
|
let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?;
|
||||||
|
|
||||||
let is_animating = true;
|
let is_animating = true;
|
||||||
let ed_model_opt =
|
let ed_model_opt = if let Some(file_path) = file_path_opt {
|
||||||
if let Some(file_path) = file_path_opt {
|
let ed_model_res = ed_model::init_model(file_path);
|
||||||
let ed_model_res =
|
|
||||||
ed_model::init_model(file_path);
|
|
||||||
|
|
||||||
match ed_model_res {
|
match ed_model_res {
|
||||||
Ok(mut ed_model) => {
|
Ok(mut ed_model) => {
|
||||||
ed_model.glyph_dim_rect_opt =
|
ed_model.glyph_dim_rect_opt = Some(example_code_glyph_rect(&mut glyph_brush));
|
||||||
Some(example_code_glyph_rect(&mut glyph_brush));
|
|
||||||
|
|
||||||
Some(ed_model)
|
Some(ed_model)
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
print_err(&e);
|
print_err(&e);
|
||||||
None
|
None
|
||||||
|
@ -156,9 +155,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut app_model = AppModel {
|
let mut app_model = AppModel { ed_model_opt };
|
||||||
ed_model_opt
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut keyboard_modifiers = ModifiersState::empty();
|
let mut keyboard_modifiers = ModifiersState::empty();
|
||||||
|
|
||||||
|
@ -258,14 +255,15 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
if let Some(ed_model) = &app_model.ed_model_opt {
|
if let Some(ed_model) = &app_model.ed_model_opt {
|
||||||
//TODO don't pass invisible lines
|
//TODO don't pass invisible lines
|
||||||
//TODO show text if no file was opened
|
queue_editor_text(
|
||||||
queue_all_text(
|
|
||||||
&size,
|
&size,
|
||||||
&ed_model.text_buf.all_lines(),
|
&ed_model.text_buf.all_lines(),
|
||||||
ed_model.caret_pos,
|
ed_model.caret_pos,
|
||||||
CODE_TXT_XY.into(),
|
CODE_TXT_XY.into(),
|
||||||
&mut glyph_brush,
|
&mut glyph_brush,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
queue_no_file_text(&size, NOTHING_OPENED, CODE_TXT_XY.into(), &mut glyph_brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
match draw_all_rects(
|
match draw_all_rects(
|
||||||
|
@ -365,7 +363,7 @@ fn begin_render_pass<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns bounding boxes for every glyph
|
// returns bounding boxes for every glyph
|
||||||
fn queue_all_text(
|
fn queue_editor_text(
|
||||||
size: &PhysicalSize<u32>,
|
size: &PhysicalSize<u32>,
|
||||||
editor_lines: &str,
|
editor_lines: &str,
|
||||||
caret_pos: Position,
|
caret_pos: Position,
|
||||||
|
@ -396,3 +394,23 @@ fn queue_all_text(
|
||||||
|
|
||||||
queue_text_draw(&code_text, glyph_brush);
|
queue_text_draw(&code_text, glyph_brush);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn queue_no_file_text(
|
||||||
|
size: &PhysicalSize<u32>,
|
||||||
|
text: &str,
|
||||||
|
text_coords: Vector2<f32>,
|
||||||
|
glyph_brush: &mut GlyphBrush<()>,
|
||||||
|
) {
|
||||||
|
let area_bounds = (size.width as f32, size.height as f32).into();
|
||||||
|
|
||||||
|
let code_text = Text {
|
||||||
|
position: text_coords,
|
||||||
|
area_bounds,
|
||||||
|
color: CODE_COLOR.into(),
|
||||||
|
text: text.to_owned(),
|
||||||
|
size: CODE_FONT_SIZE,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
queue_text_draw(&code_text, glyph_brush);
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
use super::ed_model::EdModel;
|
use super::ed_model::EdModel;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AppModel {
|
pub struct AppModel {
|
||||||
pub ed_model_opt: Option<EdModel>
|
pub ed_model_opt: Option<EdModel>,
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
|
use crate::error::EdResult;
|
||||||
use crate::graphics::primitives::rect::Rect;
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use crate::text_buffer;
|
use crate::text_buffer;
|
||||||
use crate::text_buffer::TextBuffer;
|
use crate::text_buffer::TextBuffer;
|
||||||
use crate::error::EdResult;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EdModel {
|
pub struct EdModel {
|
||||||
|
@ -11,7 +11,7 @@ pub struct EdModel {
|
||||||
pub caret_pos: Position,
|
pub caret_pos: Position,
|
||||||
pub selection_opt: Option<RawSelection>,
|
pub selection_opt: Option<RawSelection>,
|
||||||
pub glyph_dim_rect_opt: Option<Rect>,
|
pub glyph_dim_rect_opt: Option<Rect>,
|
||||||
pub has_focus: bool
|
pub has_focus: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_model(file_path: &Path) -> EdResult<EdModel> {
|
pub fn init_model(file_path: &Path) -> EdResult<EdModel> {
|
||||||
|
@ -20,7 +20,7 @@ pub fn init_model(file_path: &Path) -> EdResult<EdModel> {
|
||||||
caret_pos: Position { line: 0, column: 0 },
|
caret_pos: Position { line: 0, column: 0 },
|
||||||
selection_opt: None,
|
selection_opt: None,
|
||||||
glyph_dim_rect_opt: None,
|
glyph_dim_rect_opt: None,
|
||||||
has_focus: true
|
has_focus: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
|
|
||||||
use super::ed_model::{EdModel, Position};
|
use super::ed_model::{EdModel, Position};
|
||||||
use crate::graphics::primitives::rect::Rect;
|
|
||||||
use crate::error::{EdResult, MissingGlyphDims};
|
use crate::error::{EdResult, MissingGlyphDims};
|
||||||
|
use crate::graphics::colors::CARET_COLOR;
|
||||||
|
use crate::graphics::primitives::rect::Rect;
|
||||||
use crate::selection::create_selection_rects;
|
use crate::selection::create_selection_rects;
|
||||||
use crate::graphics::colors::{CARET_COLOR};
|
|
||||||
use bumpalo::collections::Vec as BumpVec;
|
use bumpalo::collections::Vec as BumpVec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use snafu::{ensure};
|
use snafu::ensure;
|
||||||
|
|
||||||
//TODO add editor text here as well
|
//TODO add editor text here as well
|
||||||
|
|
||||||
pub fn create_ed_rects<'a>(ed_model: &EdModel, arena: &'a Bump) -> EdResult<BumpVec<'a, Rect>> {
|
pub fn create_ed_rects<'a>(ed_model: &EdModel, arena: &'a Bump) -> EdResult<BumpVec<'a, Rect>> {
|
||||||
ensure!(ed_model.glyph_dim_rect_opt.is_some(),
|
ensure!(ed_model.glyph_dim_rect_opt.is_some(), MissingGlyphDims {});
|
||||||
MissingGlyphDims {}
|
|
||||||
);
|
|
||||||
|
|
||||||
let glyph_rect = ed_model.glyph_dim_rect_opt.unwrap();
|
let glyph_rect = ed_model.glyph_dim_rect_opt.unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
|
pub mod app_model;
|
||||||
pub mod ed_model;
|
pub mod ed_model;
|
||||||
pub mod ed_view;
|
pub mod ed_view;
|
||||||
pub mod app_model;
|
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use super::ed_model::{Position, RawSelection};
|
|
||||||
use crate::text_buffer::TextBuffer;
|
|
||||||
use crate::util::is_newline;
|
|
||||||
use super::app_model::AppModel;
|
use super::app_model::AppModel;
|
||||||
use super::ed_model::EdModel;
|
use super::ed_model::EdModel;
|
||||||
|
use super::ed_model::{Position, RawSelection};
|
||||||
use crate::error::EdResult;
|
use crate::error::EdResult;
|
||||||
|
use crate::text_buffer::TextBuffer;
|
||||||
|
use crate::util::is_newline;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
pub type MoveCaretFun =
|
pub type MoveCaretFun =
|
||||||
|
@ -343,12 +343,16 @@ pub fn handle_new_char(app_model: &mut AppModel, received_char: &char) -> EdResu
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(selection) = ed_model.selection_opt {
|
if let Some(selection) = ed_model.selection_opt {
|
||||||
del_selection(selection, ed_model)?;
|
del_selection(selection, ed_model)?;
|
||||||
ed_model.text_buf.insert_char(ed_model.caret_pos, received_char)?;
|
ed_model
|
||||||
|
.text_buf
|
||||||
|
.insert_char(ed_model.caret_pos, received_char)?;
|
||||||
|
|
||||||
ed_model.caret_pos =
|
ed_model.caret_pos =
|
||||||
move_caret_right(ed_model.caret_pos, None, false, &ed_model.text_buf).0;
|
move_caret_right(ed_model.caret_pos, None, false, &ed_model.text_buf).0;
|
||||||
} else {
|
} else {
|
||||||
ed_model.text_buf.insert_char(old_caret_pos, received_char)?;
|
ed_model
|
||||||
|
.text_buf
|
||||||
|
.insert_char(old_caret_pos, received_char)?;
|
||||||
|
|
||||||
ed_model.caret_pos = Position {
|
ed_model.caret_pos = Position {
|
||||||
line: old_caret_pos.line,
|
line: old_caret_pos.line,
|
||||||
|
@ -364,16 +368,19 @@ pub fn handle_new_char(app_model: &mut AppModel, received_char: &char) -> EdResu
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_update {
|
mod test_update {
|
||||||
use crate::selection::test_selection::{convert_dsl_to_selection, convert_selection_to_dsl, text_buffer_from_dsl_str, all_lines_vec};
|
|
||||||
use crate::mvc::ed_model::{Position, EdModel, RawSelection};
|
|
||||||
use crate::mvc::app_model::AppModel;
|
use crate::mvc::app_model::AppModel;
|
||||||
use crate::mvc::update::{handle_new_char};
|
use crate::mvc::ed_model::{EdModel, Position, RawSelection};
|
||||||
|
use crate::mvc::update::handle_new_char;
|
||||||
|
use crate::selection::test_selection::{
|
||||||
|
all_lines_vec, convert_dsl_to_selection, convert_selection_to_dsl, text_buffer_from_dsl_str,
|
||||||
|
};
|
||||||
use crate::text_buffer::TextBuffer;
|
use crate::text_buffer::TextBuffer;
|
||||||
|
|
||||||
fn gen_caret_text_buf(lines: &[&str]) -> Result<(Position, Option<RawSelection>, TextBuffer), String> {
|
fn gen_caret_text_buf(
|
||||||
|
lines: &[&str],
|
||||||
|
) -> Result<(Position, Option<RawSelection>, TextBuffer), String> {
|
||||||
let lines_string_slice: Vec<String> = lines.iter().map(|l| l.to_string()).collect();
|
let lines_string_slice: Vec<String> = lines.iter().map(|l| l.to_string()).collect();
|
||||||
let (selection_opt, caret_pos) = convert_dsl_to_selection(&lines_string_slice)?;
|
let (selection_opt, caret_pos) = convert_dsl_to_selection(&lines_string_slice)?;
|
||||||
let text_buf = text_buffer_from_dsl_str(&lines_string_slice);
|
let text_buf = text_buffer_from_dsl_str(&lines_string_slice);
|
||||||
|
@ -381,17 +388,19 @@ mod test_update {
|
||||||
Ok((caret_pos, selection_opt, text_buf))
|
Ok((caret_pos, selection_opt, text_buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mock_app_model(text_buf: TextBuffer, caret_pos: Position, selection_opt: Option<RawSelection>) -> AppModel {
|
fn mock_app_model(
|
||||||
|
text_buf: TextBuffer,
|
||||||
|
caret_pos: Position,
|
||||||
|
selection_opt: Option<RawSelection>,
|
||||||
|
) -> AppModel {
|
||||||
AppModel {
|
AppModel {
|
||||||
ed_model_opt: Some(
|
ed_model_opt: Some(EdModel {
|
||||||
EdModel{
|
|
||||||
text_buf,
|
text_buf,
|
||||||
caret_pos,
|
caret_pos,
|
||||||
selection_opt,
|
selection_opt,
|
||||||
glyph_dim_rect_opt: None,
|
glyph_dim_rect_opt: None,
|
||||||
has_focus: true
|
has_focus: true,
|
||||||
}
|
}),
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +419,12 @@ mod test_update {
|
||||||
|
|
||||||
if let Some(ed_model) = app_model.ed_model_opt {
|
if let Some(ed_model) = app_model.ed_model_opt {
|
||||||
let mut actual_lines = all_lines_vec(&ed_model.text_buf);
|
let mut actual_lines = all_lines_vec(&ed_model.text_buf);
|
||||||
let dsl_slice = convert_selection_to_dsl(ed_model.selection_opt, ed_model.caret_pos, &mut actual_lines).unwrap();
|
let dsl_slice = convert_selection_to_dsl(
|
||||||
|
ed_model.selection_opt,
|
||||||
|
ed_model.caret_pos,
|
||||||
|
&mut actual_lines,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(dsl_slice, expected_post_lines_str);
|
assert_eq!(dsl_slice, expected_post_lines_str);
|
||||||
} else {
|
} else {
|
||||||
panic!("Mock AppModel did not have an EdModel.");
|
panic!("Mock AppModel did not have an EdModel.");
|
||||||
|
@ -421,132 +435,75 @@ mod test_update {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_new_char_simple() -> Result<(), String> {
|
fn insert_new_char_simple() -> Result<(), String> {
|
||||||
assert_insert(
|
assert_insert(&["|"], &["a|"], 'a')?;
|
||||||
&["|"], &["a|"], 'a'
|
assert_insert(&["|"], &[" |"], ' ')?;
|
||||||
)?;
|
assert_insert(&["a|"], &["aa|"], 'a')?;
|
||||||
assert_insert(
|
assert_insert(&["a|"], &["a |"], ' ')?;
|
||||||
&["|"], &[" |"], ' '
|
assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b')?;
|
||||||
)?;
|
assert_insert(&["a|\n", ""], &["ab|\n", ""], 'b')?;
|
||||||
assert_insert(
|
assert_insert(&["a\n", "|"], &["a\n", "b|"], 'b')?;
|
||||||
&["a|"], &["aa|"], 'a'
|
assert_insert(&["a\n", "b\n", "c|"], &["a\n", "b\n", "cd|"], 'd')?;
|
||||||
)?;
|
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_new_char_mid() -> Result<(), String> {
|
fn insert_new_char_mid() -> Result<(), String> {
|
||||||
assert_insert(
|
assert_insert(&["ab|d"], &["abc|d"], 'c')?;
|
||||||
&["ab|d"], &["abc|d"], 'c'
|
assert_insert(&["a|cd"], &["ab|cd"], 'b')?;
|
||||||
)?;
|
assert_insert(&["abc\n", "|e"], &["abc\n", "d|e"], 'd')?;
|
||||||
assert_insert(
|
assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", "g| "], 'g')?;
|
||||||
&["a|cd"], &["ab|cd"], 'b'
|
assert_insert(&["abc\n", "def\n", "| "], &["abc\n", "def\n", " | "], ' ')?;
|
||||||
)?;
|
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_backspace() -> Result<(), String> {
|
fn simple_backspace() -> Result<(), String> {
|
||||||
assert_insert(
|
assert_insert(&["|"], &["|"], '\u{8}')?;
|
||||||
&["|"], &["|"], '\u{8}'
|
assert_insert(&[" |"], &["|"], '\u{8}')?;
|
||||||
)?;
|
assert_insert(&["a|"], &["|"], '\u{8}')?;
|
||||||
assert_insert(
|
assert_insert(&["ab|"], &["a|"], '\u{8}')?;
|
||||||
&[" |"], &["|"], '\u{8}'
|
assert_insert(&["a|\n", ""], &["|\n", ""], '\u{8}')?;
|
||||||
)?;
|
assert_insert(&["ab|\n", ""], &["a|\n", ""], '\u{8}')?;
|
||||||
assert_insert(
|
assert_insert(&["a\n", "|"], &["a|"], '\u{8}')?;
|
||||||
&["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}')?;
|
||||||
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn selection_backspace() -> Result<(), String> {
|
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(
|
assert_insert(
|
||||||
&["[a]|"], &["|"], '\u{8}'
|
&["abc\n", "d[ef\n", "ghi]|\n", "jkl"],
|
||||||
|
&["abc\n", "d|\n", "jkl"],
|
||||||
|
'\u{8}',
|
||||||
)?;
|
)?;
|
||||||
assert_insert(
|
assert_insert(
|
||||||
&["a[a]|"], &["a|"], '\u{8}'
|
&["abc\n", "[def\n", "ghi]|\n", "jkl"],
|
||||||
|
&["abc\n", "|\n", "jkl"],
|
||||||
|
'\u{8}',
|
||||||
)?;
|
)?;
|
||||||
assert_insert(
|
assert_insert(
|
||||||
&["[aa]|"], &["|"], '\u{8}'
|
&["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"],
|
||||||
|
&["abc\n", "\n", "|\n", "jkl"],
|
||||||
|
'\u{8}',
|
||||||
)?;
|
)?;
|
||||||
assert_insert(
|
assert_insert(
|
||||||
&["a[b c]|"], &["a|"], '\u{8}'
|
&["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"],
|
||||||
)?;
|
&["|"],
|
||||||
assert_insert(
|
'\u{8}',
|
||||||
&["[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(())
|
Ok(())
|
||||||
|
@ -554,48 +511,32 @@ mod test_update {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn insert_with_selection() -> Result<(), String> {
|
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(
|
assert_insert(
|
||||||
&["[a]|"], &["z|"], 'z'
|
&["abc\n", "d[ef\n", "ghi]|\n", "jkl"],
|
||||||
|
&["abc\n", "dz|\n", "jkl"],
|
||||||
|
'z',
|
||||||
)?;
|
)?;
|
||||||
assert_insert(
|
assert_insert(
|
||||||
&["a[a]|"], &["az|"], 'z'
|
&["abc\n", "[def\n", "ghi]|\n", "jkl"],
|
||||||
|
&["abc\n", "z|\n", "jkl"],
|
||||||
|
'z',
|
||||||
)?;
|
)?;
|
||||||
assert_insert(
|
assert_insert(
|
||||||
&["[aa]|"], &["z|"], 'z'
|
&["abc\n", "\n", "[def\n", "ghi]|\n", "jkl"],
|
||||||
)?;
|
&["abc\n", "\n", "z|\n", "jkl"],
|
||||||
assert_insert(
|
'z',
|
||||||
&["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'
|
|
||||||
)?;
|
)?;
|
||||||
|
assert_insert(&["[abc\n", "\n", "def\n", "ghi\n", "jkl]|"], &["z|"], 'z')?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
1
editor/src/resources/mod.rs
Normal file
1
editor/src/resources/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod strings;
|
|
@ -1 +1 @@
|
||||||
pub const NOTHING_OPENED: &str = "Execute `cargo run edit <filename>` to open a file";
|
pub const NOTHING_OPENED: &str = "Execute `cargo run edit <filename>` to open a file.";
|
||||||
|
|
|
@ -126,13 +126,15 @@ pub fn create_selection_rects<'a>(
|
||||||
pub mod test_selection {
|
pub mod test_selection {
|
||||||
use crate::error::{EdResult, OutOfBounds};
|
use crate::error::{EdResult, OutOfBounds};
|
||||||
use crate::mvc::ed_model::{Position, RawSelection};
|
use crate::mvc::ed_model::{Position, RawSelection};
|
||||||
use crate::mvc::update::{move_caret_down, move_caret_left, move_caret_right, move_caret_up, MoveCaretFun};
|
use crate::mvc::update::{
|
||||||
use crate::vec_result::get_res;
|
move_caret_down, move_caret_left, move_caret_right, move_caret_up, MoveCaretFun,
|
||||||
|
};
|
||||||
use crate::text_buffer::TextBuffer;
|
use crate::text_buffer::TextBuffer;
|
||||||
|
use crate::vec_result::get_res;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use snafu::OptionExt;
|
|
||||||
use ropey::Rope;
|
use ropey::Rope;
|
||||||
|
use snafu::OptionExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::slice::SliceIndex;
|
use std::slice::SliceIndex;
|
||||||
|
|
||||||
|
@ -204,7 +206,7 @@ pub mod test_selection {
|
||||||
let elt_ref = vec.get_mut(index).context(OutOfBounds {
|
let elt_ref = vec.get_mut(index).context(OutOfBounds {
|
||||||
index,
|
index,
|
||||||
collection_name: "Slice",
|
collection_name: "Slice",
|
||||||
len: vec_len
|
len: vec_len,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(elt_ref)
|
Ok(elt_ref)
|
||||||
|
@ -223,7 +225,7 @@ pub mod test_selection {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|line| line.replace(&['[', ']', '|'][..], ""))
|
.map(|line| line.replace(&['[', ']', '|'][..], ""))
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("")
|
.join(""),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,8 +360,7 @@ pub mod test_selection {
|
||||||
|
|
||||||
let (sel_opt, caret_pos) = convert_dsl_to_selection(&pre_lines)?;
|
let (sel_opt, caret_pos) = convert_dsl_to_selection(&pre_lines)?;
|
||||||
|
|
||||||
let clean_text_buf =
|
let clean_text_buf = text_buffer_from_dsl_str(&pre_lines);
|
||||||
text_buffer_from_dsl_str(&pre_lines);
|
|
||||||
|
|
||||||
let (new_caret_pos, new_sel_opt) =
|
let (new_caret_pos, new_sel_opt) =
|
||||||
move_fun(caret_pos, sel_opt, shift_pressed, &clean_text_buf);
|
move_fun(caret_pos, sel_opt, shift_pressed, &clean_text_buf);
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
|
|
||||||
// Adapted from https://github.com/cessen/ropey by Nathan Vegdahl, licensed under the MIT license
|
// Adapted from https://github.com/cessen/ropey by Nathan Vegdahl, licensed under the MIT license
|
||||||
|
|
||||||
|
use crate::error::EdError::{FileOpenFailed, TextBufReadFailed};
|
||||||
use crate::error::EdResult;
|
use crate::error::EdResult;
|
||||||
use crate::error::OutOfBounds;
|
use crate::error::OutOfBounds;
|
||||||
use crate::error::EdError::{TextBufReadFailed, FileOpenFailed};
|
|
||||||
use crate::mvc::ed_model::{Position, RawSelection};
|
use crate::mvc::ed_model::{Position, RawSelection};
|
||||||
use crate::selection::{validate_selection};
|
use crate::selection::validate_selection;
|
||||||
|
use ropey::Rope;
|
||||||
|
use snafu::{ensure, OptionExt};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use ropey::{Rope};
|
|
||||||
use snafu::{ensure, OptionExt};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TextBuffer {
|
pub struct TextBuffer {
|
||||||
|
@ -23,11 +21,14 @@ impl TextBuffer {
|
||||||
pub fn insert_char(&mut self, caret_pos: Position, new_char: &char) -> EdResult<()> {
|
pub fn insert_char(&mut self, caret_pos: Position, new_char: &char) -> EdResult<()> {
|
||||||
let char_indx = self.pos_to_char_indx(caret_pos);
|
let char_indx = self.pos_to_char_indx(caret_pos);
|
||||||
|
|
||||||
ensure!(char_indx <= self.text_rope.len_chars(), OutOfBounds{
|
ensure!(
|
||||||
|
char_indx <= self.text_rope.len_chars(),
|
||||||
|
OutOfBounds {
|
||||||
index: char_indx,
|
index: char_indx,
|
||||||
collection_name: "Rope",
|
collection_name: "Rope",
|
||||||
len: self.text_rope.len_chars()
|
len: self.text_rope.len_chars()
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
self.text_rope.insert(char_indx, &new_char.to_string());
|
self.text_rope.insert(char_indx, &new_char.to_string());
|
||||||
|
|
||||||
|
@ -45,11 +46,14 @@ impl TextBuffer {
|
||||||
pub fn del_selection(&mut self, raw_sel: RawSelection) -> EdResult<()> {
|
pub fn del_selection(&mut self, raw_sel: RawSelection) -> EdResult<()> {
|
||||||
let (start_char_indx, end_char_indx) = self.sel_to_tup(raw_sel)?;
|
let (start_char_indx, end_char_indx) = self.sel_to_tup(raw_sel)?;
|
||||||
|
|
||||||
ensure!(end_char_indx <= self.text_rope.len_chars(), OutOfBounds{
|
ensure!(
|
||||||
|
end_char_indx <= self.text_rope.len_chars(),
|
||||||
|
OutOfBounds {
|
||||||
index: end_char_indx,
|
index: end_char_indx,
|
||||||
collection_name: "Rope",
|
collection_name: "Rope",
|
||||||
len: self.text_rope.len_chars()
|
len: self.text_rope.len_chars()
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
self.text_rope.remove(start_char_indx..end_char_indx);
|
self.text_rope.remove(start_char_indx..end_char_indx);
|
||||||
|
|
||||||
|
@ -76,7 +80,7 @@ impl TextBuffer {
|
||||||
self.line_len(line_nr).context(OutOfBounds {
|
self.line_len(line_nr).context(OutOfBounds {
|
||||||
index: line_nr,
|
index: line_nr,
|
||||||
collection_name: "Rope",
|
collection_name: "Rope",
|
||||||
len: self.text_rope.len_lines()
|
len: self.text_rope.len_lines(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,20 +136,16 @@ fn rope_from_path(path: &Path) -> EdResult<Rope> {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
let buf_reader = &mut io::BufReader::new(file);
|
let buf_reader = &mut io::BufReader::new(file);
|
||||||
match Rope::from_reader(buf_reader) {
|
match Rope::from_reader(buf_reader) {
|
||||||
Ok(rope) =>
|
Ok(rope) => Ok(rope),
|
||||||
Ok(rope),
|
Err(e) => Err(TextBufReadFailed {
|
||||||
Err(e) =>
|
|
||||||
Err(TextBufReadFailed {
|
|
||||||
path_str: path_to_string(path),
|
path_str: path_to_string(path),
|
||||||
err_msg: e.to_string()
|
err_msg: e.to_string(),
|
||||||
})
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => Err(FileOpenFailed {
|
||||||
Err(FileOpenFailed {
|
|
||||||
path_str: path_to_string(path),
|
path_str: path_to_string(path),
|
||||||
err_msg: e.to_string()
|
err_msg: e.to_string(),
|
||||||
})
|
}),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue