hecto/src/document.rs

159 lines
3.9 KiB
Rust
Raw Normal View History

2021-03-10 13:48:21 -05:00
use crate::Position;
2021-03-08 10:21:06 -05:00
use crate::Row;
use crate::SearchDirection;
2021-03-08 13:34:25 -05:00
use std::fs;
2021-03-10 14:51:11 -05:00
use std::io::{Error, Write};
2021-03-08 10:21:06 -05:00
#[derive(Default)]
pub struct Document {
rows: Vec<Row>,
2021-03-08 14:41:40 -05:00
pub file_name: Option<String>,
2021-03-10 15:26:12 -05:00
dirty: bool,
2021-03-08 10:43:40 -05:00
}
impl Document {
2021-03-08 13:34:25 -05:00
pub fn open(filename: &str) -> Result<Self, std::io::Error> {
let contents = fs::read_to_string(filename)?;
2021-03-08 10:43:40 -05:00
let mut rows = Vec::new();
2021-03-08 13:34:25 -05:00
for value in contents.lines() {
rows.push(Row::from(value));
}
Ok(Self {
2021-03-08 14:41:40 -05:00
rows,
file_name: Some(filename.to_string()),
2021-03-10 15:26:12 -05:00
dirty: false,
2021-03-08 13:34:25 -05:00
})
2021-03-08 10:43:40 -05:00
}
pub fn row(&self, index: usize) -> Option<&Row> {
self.rows.get(index)
}
pub fn is_empty(&self) -> bool {
self.rows.is_empty()
}
2021-03-08 14:21:24 -05:00
pub fn len(&self) -> usize {
self.rows.len()
}
2021-03-10 13:48:21 -05:00
pub fn insert(&mut self, at: &Position, c: char) {
if at.y > self.rows.len() {
2021-03-10 15:26:12 -05:00
return;
}
// File has been modified
self.dirty = true;
2021-03-10 14:37:58 -05:00
if c == '\n' {
self.insert_newline(at);
return;
}
if at.y == self.rows.len() {
2021-03-10 13:48:21 -05:00
let mut row = Row::default();
row.insert(0, c);
self.rows.push(row);
2021-03-10 15:26:12 -05:00
} else {
#[allow(clippy::indexing_slicing)]
let row = &mut self.rows[at.y];
2021-03-10 13:48:21 -05:00
row.insert(at.x, c);
}
}
#[allow(clippy::integer_arithmetic, clippy::indexing_slicing)]
2021-03-10 13:48:21 -05:00
pub fn delete(&mut self, at: &Position) {
let len = self.rows.len();
2021-03-10 14:37:58 -05:00
if at.y >= len {
return;
2021-03-10 13:48:21 -05:00
}
2021-03-10 15:26:12 -05:00
// File has been modified
self.dirty = true;
if at.x == self.rows[at.y].len() && at.y + 1 < len {
2021-03-10 14:37:58 -05:00
let next_row = self.rows.remove(at.y + 1);
let row = &mut self.rows[at.y];
2021-03-10 14:37:58 -05:00
row.append(&next_row);
} else {
let row = &mut self.rows[at.y];
2021-03-10 14:37:58 -05:00
row.delete(at.x);
}
}
2021-03-10 15:26:12 -05:00
pub fn save(&mut self) -> Result<(), Error> {
2021-03-10 14:51:11 -05:00
if let Some(file_name) = &self.file_name {
let mut file = fs::File::create(file_name)?;
for row in &self.rows {
file.write_all(row.as_bytes())?;
file.write_all(b"\n")?;
}
2021-03-10 15:26:12 -05:00
// File has been cleaned! (Saved)
self.dirty = false;
2021-03-10 14:51:11 -05:00
}
Ok(())
}
2021-03-10 15:26:12 -05:00
pub fn is_dirty(&self) -> bool {
self.dirty
}
2021-03-10 14:37:58 -05:00
#[allow(clippy::indexing_slicing)]
pub fn find(&self, query: &str, at: &Position, direction: SearchDirection) -> Option<Position> {
if at.y >= self.rows.len() {
return None;
}
2021-03-12 12:17:32 -05:00
let mut position = Position { x: at.x, y: at.y };
let (start, end) = match direction {
SearchDirection::Forward => (at.y, self.rows.len()),
SearchDirection::Backward => (0, at.y.saturating_add(1)),
};
for _ in start..end {
if let Some(row) = self.rows.get(position.y) {
if let Some(x) = row.find(&query, position.x, direction) {
position.x = x;
return Some(position);
}
if direction == SearchDirection::Forward {
position.y = position.y.saturating_add(1);
position.x = 0;
} else {
position.y = position.y.saturating_sub(1);
position.x = self.rows[position.y].len();
}
} else {
return None;
2021-03-12 11:50:28 -05:00
}
}
None
}
2021-03-10 15:26:12 -05:00
fn insert_newline(&mut self, at: &Position) {
if at.y > self.rows.len() {
return;
}
if at.y == self.rows.len() {
2021-03-10 14:37:58 -05:00
self.rows.push(Row::default());
return;
}
#[allow(clippy::indexing_slicing)]
let new_row = self.rows[at.y].split(at.x);
#[allow(clippy::integer_arithmetic)]
2021-03-10 14:37:58 -05:00
self.rows.insert(at.y + 1, new_row);
2021-03-10 13:48:21 -05:00
}
}