Work on Editor

Split editor and Via structs
This commit is contained in:
Andrew Dinh 2021-01-15 23:30:18 -08:00
parent cbeabc55bb
commit 57113c6b9d
Signed by: andrewkdinh
GPG Key ID: 2B557D93C6C08B86
8 changed files with 425 additions and 247 deletions

7
Cargo.lock generated
View File

@ -87,10 +87,17 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "unicode-segmentation"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "via"
version = "0.1.0"
dependencies = [
"regex",
"termion",
"unicode-segmentation",
]

View File

@ -16,3 +16,4 @@ categories = ["command-line-utilities", "text-editors"]
[dependencies]
termion = "^1.5"
regex = "^1.3"
unicode-segmentation = "^1.7.1"

View File

@ -1,69 +0,0 @@
// mod piece_table;
use std::fs::File;
use std::io::{BufReader};
#[derive(Debug)]
/// The overarching editor class
pub(crate) struct Editor {
/// The piece table
pub(crate) piece_table: super::piece_table::PieceTable,
/// Reader of the file
reader: BufReader<File>,
/// Whether we have read all of `self.reader`
eof_reached: bool,
/// Configured options
options: EditorOptions,
/// Represents each line of the editor, and how many characters are in that line
lines: Vec<usize>,
}
impl Editor {
/// Initialize a new editor with a piece table and reader
pub(crate) fn new(piece_table: super::piece_table::PieceTable, reader: BufReader<File>, eof_reached: bool, options: EditorOptions) -> Editor {
Editor {piece_table: piece_table,
reader: reader,
eof_reached:
eof_reached,
options: options,
lines: Vec::new(),
}
}
/// Read to end of the file and add it to `piece_table.original_buffer`
fn read_to_eof(&mut self) {
let mut final_str = String::new();
let mut temp_str = String::new();
loop {
// match self.reader.read_line(&mut temp_str) {
match std::io::BufRead::read_line(&mut self.reader, &mut temp_str) {
Ok(0) => break,
Ok(len) => len,
Err(e) => panic!("Error reading file: {:?}", e),
};
final_str.push_str(&temp_str);
temp_str.clear();
}
self.piece_table.original_buffer.push_str(&final_str);
}
/// Returns the file name
fn file_name(&self) -> &String {
return &self.options.file_name;
}
}
#[derive(Debug)]
pub(crate) struct EditorOptions {
/// Name of the file being editing
pub(crate) file_name: String,
/// Level of verboseness
pub(crate) verboseness: usize,
}
impl EditorOptions {
/// Return default options
pub(crate) fn new(file_name: String) -> EditorOptions {
EditorOptions {file_name: file_name, verboseness: 1}
}
}

View File

@ -2,34 +2,16 @@
// #![warn(unused_variables)]
// #![warn(unused_mut)]
extern crate termion;
extern crate regex;
use std::env;
use std::fs::File;
use std::io::{BufReader};
// use std::io::prelude::*;
use std::path::Path;
use regex::Regex;
use std::convert::TryInto;
mod modules;
mod editor;
mod piece_table;
// use termion::{color, cursor, clear};
// use termion::event::*;
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
use std::io::{Write, stdout, stdin};
use modules::via::Via;
fn main() {
let args: Vec<String> = env::args().collect();
// TODO: Match command line options, different order, etc.
let editor = initialize(process_args(&args));
let mut piece_table = editor.piece_table;
// let mut via = Via::new(env::args().collect());
/*
let mut editor = initialize(process_args(&args));
// Print the current file text onto screen
let stdin = stdin();
let mut stdout = stdout().into_raw_mode().unwrap();
@ -41,7 +23,7 @@ fn main() {
termion::clear::All,
termion::cursor::Goto(1, 1)).unwrap();
for line in piece_table.text().lines() {
for line in editor.piece_table.text().lines() {
editor_y += 1;
write!(stdout,
"{}{}",
@ -50,9 +32,9 @@ fn main() {
}
stdout.flush().unwrap();
let mut buffer_index = piece_table.text_len();
let mut buffer_index = editor.piece_table.text_len();
let mut cmd_index = 0;
let mut cmd_piece_table = piece_table::PieceTable::new("".to_string());
let mut cmd_piece_table = piece_table::PieceTable::new();
let mut mode = 1; // [0: insert, 1: visual, 2: command]
for c in stdin.keys() {
write!(stdout,
@ -61,7 +43,9 @@ fn main() {
termion::clear::CurrentLine)
.unwrap();
let i = c.unwrap();
editor.process(c.unwrap());
*/
/*
if i == Key::Esc {
mode = 1
} else if mode == 0 {
@ -132,87 +116,6 @@ fn main() {
}
stdout.flush().unwrap();
}
write!(stdout, "{}", termion::cursor::Show).unwrap();
}
/// Process command line options and return EditorOptions
fn process_args(args: &Vec<String>) -> editor::EditorOptions {
let mut flags = Vec::new();
let mut file_names = Vec::new();
let file_name: String;
let flags_regex = Regex::new(r"--?\w+").unwrap();
let default_files = vec!["target/debug/via", "via"];
for arg in args {
if flags_regex.is_match(&arg) {
flags.push(arg);
} else if !default_files.contains(&arg.as_str()) {
file_names.push(arg);
}
}
if file_names.len() == 1 {
file_name = file_names.first().unwrap().to_string();
} else {
println!("{:?}", file_names);
panic!("Must specify a single file to edit"); // Maybe change this to edit multiple files later on
}
let mut editor_options = editor::EditorOptions::new(file_name);
for option in flags {
if option == "-v" {
editor_options.verboseness += 1;
}
}
editor_options
}
fn initialize(editor_options: editor::EditorOptions) -> editor::Editor {
// TODO: Might not want to create file, but instead write to memory then at the end then write to file
let file_name = &editor_options.file_name;
let file_path = Path::new(&file_name);
let file: File;
if !file_path.is_file() {
panic!("{} is not a file", file_name);
} else if file_path.exists() {
file = File::open(file_name).expect("Failed to open file");
} else {
File::create(file_path).expect("Unable to create file");
file = File::open(file_name).expect("Failed to open file");
}
let mut reader = BufReader::new(file);
// Read until viewport is filled
// For now, only read 2 lines
let mut initial_text =String::new();
let eof_reached = read_lines(&mut reader, 1000000000000000, &mut initial_text); // TODO: FIX ME
// TODO: Add this to initialization of piece table
editor::Editor::new(piece_table::PieceTable::new(initial_text), reader, eof_reached, editor_options)
}
/// Read `num_lines` from `reader`, append to str, and returns whether EOF reached
fn read_lines(reader: &mut BufReader<File>, num_lines: usize, str: &mut String) -> bool {
let mut temp_str = String::new();
for _ in 0..num_lines {
// match reader.read_line(&mut temp_str) {
match std::io::BufRead::read_line(reader, &mut temp_str) {
Ok(0) => return true,
Ok(len) => len,
Err(e) => panic!("Error reading file: {}", e),
};
str.push_str(&temp_str);
temp_str.clear();
}
false
}
#[cfg(test)]
mod tests {
/*
use super::*;
#[test]
fn init() {
let args: Vec<String> = vec!["unittests/1.in".to_string()];
let editor = initialize(process_args(&args));
}
*/
}
}

209
src/modules/editor.rs Normal file
View File

@ -0,0 +1,209 @@
use std::fs::File;
use std::io::{BufRead, BufReader};
// use std::env;
use std::cmp::min;
use std::path::Path;
use super::piece_table::PieceTable;
#[derive(Debug)]
/// An editor window
pub(crate) struct Editor {
/// The piece table
piece_table: PieceTable,
/// Index we are currently at in `self.piece_table` (0-indexed)
pt_index: usize,
/// Path of file being editing (may not yet exist).
/// Empty string if no file specified
file_path: String,
/// Reader of the file (Error if is an nonexistent file)
reader: Result<BufReader<File>, String>,
/// Whether we have read all of `self.reader`
eof_reached: bool,
/// Represents each line of the editor, and how many characters are in that line
lines: Vec<usize>,
/// Row cursor is at (1-indexed)
row: usize,
/// Column cursor is at (1-indexed)
col: usize,
/// Column cursor we want (1-indexed). When we move vertically, from a long
/// line to short one, we want to try to get to a specific column
col_want: usize,
}
impl Editor {
/// Initialize a new editor from a file path
pub(crate) fn new(file_path: String) -> Editor {
// let (reader, eof_reached) = create_reader(file_path);
let reader;
let eof_reached;
if file_path == "" {
reader = Err("No file specified".to_string());
eof_reached = true;
} else if Path::new(&file_path).is_file() {
reader = Ok(BufReader::new(File::open(file_path.clone()).unwrap()));
eof_reached = false;
} else if Path::new(&file_path).is_dir() || file_path.ends_with("/") {
panic!("No support (yet) for writing to directories");
} else {
reader = Err("File doesn't exist".to_string());
eof_reached = true;
}
Editor {piece_table: PieceTable::new(),
pt_index: 0,
file_path: file_path,
reader: reader,
eof_reached: eof_reached,
lines: Vec::new(),
row: 1,
col: 1,
col_want: 1,
}
}
/// Adds `text` at the current cursor position
fn add_text(&mut self, text: String) {
}
/// Read `num_lines` from `reader`, updating `self.piece_table` & `self.lines`
/// Returns number of lines actually read
fn read_lines(&mut self, num_lines: usize) -> usize {
if self.eof_reached {
// panic!("EOF already reached");
return 0;
}
let mut lines_read = 0;
let reader = self.reader.as_mut().unwrap();
for _ in 0..num_lines {
let mut temp_str = String::new();
match reader.read_line(&mut temp_str) {
Ok(0) => {
self.eof_reached = true;
break
},
Ok(len) => {
lines_read += 1;
self.lines.push(len);
self.piece_table.update_original_buffer(temp_str);
},
Err(e) => panic!("Error reading file: {:?}", e),
}
}
lines_read
}
/// Read to EOF, updating `self.piece_table` & `self.lines`
fn read_to_eof(&mut self) {
if self.eof_reached {
return;
}
let reader = self.reader.as_mut().unwrap();
loop {
let mut temp_str = String::new();
match reader.read_line(&mut temp_str) {
Ok(0) => {
self.eof_reached = true;
break
},
Ok(len) => {
self.lines.push(len);
self.piece_table.update_original_buffer(temp_str);
},
Err(e) => panic!("Error reading file: {:?}", e),
}
}
}
/// Move the cursor up `num` places
pub(crate) fn up(&mut self, num: usize) -> Result<(), String> {
if self.row == 1 || num >= self.row {
return Err("Can't go up".to_string());
}
// Update self.pt_index
// self.pt_index -= self.col;
for i in 1..num {
self.pt_index -= self.lines.get(self.row - i).unwrap();
}
self.row -= num;
let line_cols = self.lines.get(self.row).unwrap();
self.col = min(self.col_want, line_cols + 1);
// self.pt_index -= line_cols - self.col;
Ok(())
}
/// Move the cursor down `num` places
pub(crate) fn down(&mut self, num: usize) -> Result<(), String> {
if self.row + num > self.lines.len() {
let from_bottom = self.row + num - self.lines.len();
let lines_read = self.read_lines(from_bottom);
for _ in lines_read..from_bottom {
self.lines.push(0);
}
}
self.row += num;
self.col = min(self.col_want, self.lines.get(self.row).unwrap() + 1);
Ok(())
}
/// Move the cursor right `num` places
pub(crate) fn right(&mut self, num: usize) -> Result<(), String> {
let line_cols = self.lines.get(self.row).unwrap() + 1;
if self.col + num > line_cols {
return Err("Can't go right".to_string());
}
self.col += num;
self.col_want = self.col;
Ok(())
}
/// Move the cursor left `num` places
pub(crate) fn left(&mut self, num: usize) -> Result<(), String> {
if num >= self.col {
return Err("Can't go left".to_string());
}
self.col -= num;
self.col_want = self.col;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_file() {
// let mut editor = Editor::new("".to_string());
}
}
/*
/// Returns a reader for a file and if a temp file had to be created
// fn create_reader(file_path: String) -> Result<(BufReader<File>, bool), String> {
fn create_reader(file_path: String) -> (Result<BufReader<File>, String>, bool) {
// Only will work in Linux/MacOS systems
let tmp_path;
if file_path.starts_with("/") {
tmp_path = "/tmp/via/".to_string() + &file_path.replace("/", "%");
} else {
let curr_dir = env::current_dir().unwrap().to_str().unwrap();
tmp_path = "/tmp/via/".to_string() + &curr_dir.replace("/", "%") + &file_path.replace("/", "%");
}
if Path::new(&file_path).is_file() {
// return Ok((BufReader::new(File::open(file_path).unwrap()), false))
return (Ok(BufReader::new(File::open(file_path).unwrap())), false);
} else if !Path::new(&tmp_path).is_file() {
// return Ok((BufReader::new(File::open(tmp_path).unwrap()), true))
return (Ok(BufReader::new(File::open(tmp_path).unwrap())), true);
}
/*
let error_message = "File is already being edited and/or last session of Via didn't exit cleanly\n
To remove this message, delete ".to_string() + tmp_path.as_str();
Err(error_message.to_string())
*/
panic!("File is already being edited and/or last session of Via didn't exit cleanly\n
To remove this message, delete {}", tmp_path);
}
*/

3
src/modules/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod via;
pub(crate) mod editor;
pub(crate) mod piece_table;

View File

@ -1,4 +1,4 @@
// use unicode_segmentation::UnicodeSegmentation;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Debug)]
/// The main structure for storing text
@ -24,77 +24,79 @@ pub(crate) struct PieceTable {
}
impl PieceTable {
/// Initializes a piece table with the original buffer set
pub(crate) fn new(initial_text: String) -> PieceTable {
let text_len = initial_text.len();
let text = initial_text.clone();
let table = if text_len == 0 {Vec::new()} else {vec![TableEntry::new(false, 0, text_len)]};
/// Initializes a piece table
pub(crate) fn new() -> PieceTable {
PieceTable {
table: table,
original_buffer: initial_text,
table: Vec::new(),
original_buffer: String::new(),
add_buffer: String::new(),
text: text,
text_len: text_len,
text: String::new(),
text_len: 0,
text_up_to_date: true,
actions: Vec::new(),
actions_index: 0,
}
}
/// Append text to the original buffer and add a table entry
pub(crate) fn update_original_buffer(&mut self, text: String) {
let org_buffer_len = self.original_buffer.len();
self.original_buffer.push_str(&text);
self.table.push(TableEntry::new(false, org_buffer_len, org_buffer_len + text.len()));
self.text_len += text.len();
self.text_up_to_date = false;
}
/// Add text at a certain index
pub(crate) fn add_text(&mut self, text: String, cursor: usize) {
pub(crate) fn add_text(&mut self, text: String, index: usize) {
let text_len = text.len();
let add_buffer_len = self.add_buffer.len();
if cursor > self.text_len {
panic!("cursor ({}) is a greater value than text len ({})", cursor, self.text_len);
if index > self.text_len {
panic!("index ({}) is a greater value than text len ({})", index, self.text_len);
}
let mut curr_pos = 0;
let mut table_entry_index = 0;
let mut action: Vec<usize> = Vec::new();
let mut add_table: Vec<TableEntry> = Vec::with_capacity(3);
let mut add_table_indices: Vec<usize> = Vec::with_capacity(3);
if cursor == 0 {
if index == 0 {
self.table.insert(0, TableEntry::new(true, add_buffer_len, add_buffer_len + text.len()));
action.push(0);
} else if cursor == self.text_len {
} else if index == self.text_len {
self.table.push(TableEntry::new(true, add_buffer_len, add_buffer_len + text.len()));
action.push(self.table.len());
} else {
for table_entry in self.table.iter_mut() {
for (i, table_entry) in self.table.iter_mut().enumerate() {
if table_entry.active {
let len = table_entry.end_index - table_entry.start_index;
if curr_pos == cursor {
if curr_pos == index {
add_table.push(TableEntry::new(true, add_buffer_len, add_buffer_len + text.len()));
add_table_indices.push(table_entry_index);
add_table_indices.push(i);
break
} else if curr_pos + len > cursor {
} else if curr_pos + len > index {
// Split into 2 parts and disable original [ab] + [c] -> [a][c][b]
let split_point = cursor - curr_pos;
let split_point = index - curr_pos;
table_entry.active = false;
action.push(table_entry_index);
action.push(i);
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index, table_entry.start_index + split_point));
action.push(table_entry_index + 1);
action.push(i + 1);
add_table.push(TableEntry::new(true, add_buffer_len, add_buffer_len + text.len()));
action.push(table_entry_index + 2);
action.push(i + 2);
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index + split_point, table_entry.end_index));
action.push(table_entry_index + 3);
action.push(i + 3);
add_table_indices.push(table_entry_index);
add_table_indices.push(table_entry_index + 1);
add_table_indices.push(table_entry_index + 2);
add_table_indices.push(i);
add_table_indices.push(i + 1);
add_table_indices.push(i + 2);
break
}
curr_pos += len;
}
table_entry_index += 1;
}
table_entry_index = 0;
for table_entry in add_table {
self.table.insert(*add_table_indices.get(table_entry_index).unwrap(), table_entry);
table_entry_index += 1;
for (i, table_entry) in add_table_indices.iter().zip(add_table) {
self.table.insert(*i, table_entry);
}
}
@ -110,12 +112,11 @@ impl PieceTable {
panic!("Can't delete from start ({}) to end ({}) of text size {}", start, end, self.text_len);
}
let mut curr_pos = 0;
let mut table_entry_index = 0;
let mut action: Vec<usize> = Vec::new();
let mut add_table: Vec<TableEntry> = Vec::with_capacity(4);
let mut add_table_indices: Vec<usize> = Vec::with_capacity(4);
for table_entry in self.table.iter_mut() {
for (i, table_entry) in self.table.iter_mut().enumerate() {
if curr_pos == end {
break
}
@ -125,28 +126,28 @@ impl PieceTable {
// At table entry to continue/end at
// OR start & end is this exact table entry
table_entry.active = false;
action.push(table_entry_index + add_table.len());
action.push(i + add_table.len());
} else if start > curr_pos && start < curr_pos + len && end >= curr_pos + len {
// At table entry to start at (split table entry in two)
// Only occurs once, and will be the first
let split_point = start - curr_pos;
table_entry.active = false;
action.push(table_entry_index);
action.push(i);
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index, table_entry.start_index + split_point));
add_table_indices.push(table_entry_index + 1);
action.push(table_entry_index + 1);
add_table_indices.push(i + 1);
action.push(i + 1);
} else if start <= curr_pos && end > curr_pos && end < curr_pos + len {
// At table entry to end at (split table entry in two)
let split_point = end - curr_pos;
table_entry.active = false;
action.push(table_entry_index + add_table.len());
action.push(i + add_table.len());
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index + split_point, table_entry.end_index));
add_table_indices.push(table_entry_index + add_table.len());
action.push(table_entry_index + add_table.len());
add_table_indices.push(i + add_table.len());
action.push(i + add_table.len());
break
} else if start > curr_pos && end < curr_pos + len {
// At table entry to split into 3 [abc] -> [a](b)[c]
@ -155,26 +156,23 @@ impl PieceTable {
let second_split = end - curr_pos;
table_entry.active = false;
action.push(table_entry_index);
action.push(i);
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index, table_entry.start_index + first_split));
add_table_indices.push(table_entry_index + 1);
action.push(table_entry_index + 1);
add_table_indices.push(i + 1);
action.push(i + 1);
add_table.push(TableEntry::new(table_entry.is_add_buffer, table_entry.start_index + second_split, table_entry.end_index));
add_table_indices.push(table_entry_index + 2);
action.push(table_entry_index + 2);
add_table_indices.push(i + 2);
action.push(i + 2);
break
}
curr_pos += len;
}
table_entry_index += 1;
}
table_entry_index = 0;
for table_entry in add_table {
self.table.insert(*add_table_indices.get(table_entry_index).unwrap(), table_entry);
table_entry_index += 1;
for (i, table_entry) in add_table_indices.iter().zip(add_table) {
self.table.insert(*i, table_entry);
}
self.add_action(action);
@ -232,7 +230,7 @@ impl PieceTable {
/// Returns the text represented by a table entry
fn table_entry_text(&self, table_entry: &TableEntry) -> &str {
let buffer = if table_entry.is_add_buffer {&self.add_buffer} else {&self.original_buffer};
buffer.get(table_entry.start_index..table_entry.end_index).unwrap()
buffer.get(table_entry.start_index..table_entry.end_index).unwrap() // TODO: Get this working with Unicode
}
/// Returns length of text
@ -261,7 +259,6 @@ impl PieceTable {
}
}
// assert_eq!(text.len(), self.text_len); // TODO: REMOVE ME LATER
self.text = text;
self.text_up_to_date = true;
}
@ -281,12 +278,10 @@ impl PieceTable {
/// Return the index-th line
/// TODO: Remove this and do something better regarding lines
pub(crate) fn line(&mut self, index: usize) -> Result<&str, &str> {
let mut count: usize = 0;
for line in self.text().lines() {
if count == index {
for (i, line) in self.text().lines().enumerate() {
if i == index {
return Ok(line)
}
count += 1
}
Err("Invalid line index")
}
@ -329,8 +324,9 @@ mod tests {
#[test]
fn add_text() {
let mut piece_table = PieceTable::new("a".to_string());
let mut piece_table = PieceTable::new();
let mut want_str = "a";
piece_table.add_text("a".to_string(), 0);
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
@ -352,39 +348,44 @@ mod tests {
#[test]
fn delete_text() {
let mut piece_table = PieceTable::new("abc".to_string());
let mut piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.delete_text(0, 1);
let mut want_str = "bc";
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("".to_string());
piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.delete_text(1, 2);
want_str = "ac";
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("abc".to_string());
piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.delete_text(2, 3);
want_str = "ab";
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("abc".to_string());
piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.delete_text(2, 3);
want_str = "ab";
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("ab".to_string());
piece_table = PieceTable::new();
piece_table.add_text("ab".to_string(), 0);
piece_table.add_text("cd".to_string(), 2);
piece_table.delete_text(2, 3);
want_str = "abd";
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("ab".to_string());
piece_table = PieceTable::new();
piece_table.add_text("ab".to_string(), 0);
piece_table.add_text("cd".to_string(), 2);
piece_table.add_text("ef".to_string(), 4);
piece_table.delete_text(1, 5);
@ -392,7 +393,8 @@ mod tests {
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("ab".to_string());
piece_table = PieceTable::new();
piece_table.add_text("ab".to_string(), 0);
piece_table.add_text("cd".to_string(), 2);
piece_table.add_text("ef".to_string(), 4);
piece_table.delete_text(1, 6);
@ -400,7 +402,8 @@ mod tests {
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("ab".to_string());
piece_table = PieceTable::new();
piece_table.add_text("ab".to_string(), 0);
piece_table.add_text("cd".to_string(), 2);
piece_table.add_text("ef".to_string(), 4);
piece_table.delete_text(0, 6);
@ -411,7 +414,8 @@ mod tests {
#[test]
fn undo_redo() {
let mut piece_table = PieceTable::new("abc".to_string());
let mut piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.delete_text(0, 1);
let mut want_str = "abc";
piece_table.undo();
@ -423,7 +427,8 @@ mod tests {
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new("abc".to_string());
piece_table = PieceTable::new();
piece_table.add_text("abc".to_string(), 0);
piece_table.add_text("d".to_string(), 3); // "abcd"
piece_table.delete_text(0, 2); // "cd"
piece_table.undo();
@ -443,4 +448,22 @@ mod tests {
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
}
#[test]
fn edge_cases() {
let mut piece_table = PieceTable::new();
piece_table.add_text("\n".to_string(), 0);
let mut want_str = "\n";
assert_eq!(want_str.len(), 1);
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
piece_table = PieceTable::new();
piece_table.add_text("😀".to_string(), 0);
want_str = "😀";
assert_eq!(want_str.len(), 4);
// assert_eq!(want_str.graphemes(true).count(), 1);
assert_eq!(piece_table.text_len, want_str.len());
assert_eq!(piece_table.text(), want_str);
}
}

101
src/modules/via.rs Normal file
View File

@ -0,0 +1,101 @@
extern crate termion;
extern crate regex;
// use termion::event::Key;
use regex::Regex;
use super::editor::Editor;
use super::piece_table::PieceTable;
#[derive(Debug)]
/// Via main class, comprised of `Editor`'s
pub struct Via {
/// List of current editors
editors: Vec<Editor>,
/// List of files to edit
file_paths: Vec<String>,
/// Configured options
options: ViaOptions,
/// Piece table of the command line
cmd_piece_table: PieceTable,
}
impl Via {
/// Initialize a new instance of Via from arguments
pub fn new(args: Vec<String>) -> Via {
let (mut file_paths, options) = process_args(args);
if file_paths.is_empty() {
file_paths.push("".to_string());
}
Via {
editors: vec![Editor::new((*file_paths.first().unwrap()).as_str().to_string())],
file_paths: file_paths,
options: options,
cmd_piece_table: PieceTable::new(),
}
}
/*
fn initialize(editor_options: editor::ViaOptions) -> editor::Editor {
// TODO: Might not want to create file, but instead write to memory then at the end then write to file
let file_name = &editor_options.file_name;
let file_path = Path::new(&file_name);
let file: File;
if !file_path.is_file() {
panic!("{} is not a file", file_name);
} else if file_path.exists() {
file = File::open(file_name).expect("Failed to open file");
} else {
File::create(file_path).expect("Unable to create file");
file = File::open(file_name).expect("Failed to open file");
}
let mut reader = BufReader::new(file);
// Read until viewport is filled
// For now, only read 2 lines
let mut initial_text =String::new();
let eof_reached = read_lines(&mut reader, 1000000000000000, &mut initial_text); // TODO: FIX ME
// TODO: Add this to initialization of piece table
editor::Editor::new(piece_table::PieceTable::new(initial_text), reader, eof_reached, editor_options)
}
*/
}
/// Process command line options and returns the files to edit and ViaOptions
// fn process_args(&mut self, args: &Vec<String>) -> Result<(Vec<String>, ViaOptions), &str> {
fn process_args(args: Vec<String>) -> (Vec<String>, ViaOptions) {
let mut flags = Vec::new();
let mut file_paths = Vec::new();
let flags_regex = Regex::new(r"--?\w+").unwrap();
// let default_files = vec!["target/debug/via", "via"];
for arg in &args[1..] {
if flags_regex.is_match(&arg) {
flags.push(arg);
} else {
file_paths.push((*arg).as_str().to_string());
}
}
let mut via_options = ViaOptions::new();
for option in flags {
if option == "-v" {
via_options.verboseness += 1;
} else {
panic!("Unknown flag {}", option);
}
}
(file_paths, via_options)
}
#[derive(Debug)]
struct ViaOptions {
/// Level of verboseness
verboseness: usize,
}
impl ViaOptions {
/// Return default options
pub(crate) fn new() -> ViaOptions {
ViaOptions {verboseness: 1}
}
}