diff --git a/src/editor/mod.rs b/src/editor/mod.rs index 714f861..74367e2 100644 --- a/src/editor/mod.rs +++ b/src/editor/mod.rs @@ -4,13 +4,13 @@ use std::{ cmp::{max, min}, collections::HashMap, fs::File, - io::{self, BufReader, BufWriter, Write}, - path::{Path, PathBuf}, + io::{self, BufWriter, Write}, }; -use ropey::Rope; - -use backend::{buffer::Buffer, marks::Mark}; +use backend::{ + buffer::{Buffer, BufferPath}, + marks::Mark, +}; use crate::{ formatter::LineFormatter, @@ -24,7 +24,6 @@ use crate::{ pub struct Editor { pub buffer: Buffer, pub formatter: LineFormatter, - pub file_path: PathBuf, pub line_ending_type: LineEnding, pub soft_tabs: bool, pub soft_tab_width: u8, @@ -43,48 +42,18 @@ pub struct Editor { impl Editor { /// Create a new blank editor - pub fn new(formatter: LineFormatter) -> Editor { - let (buffer, v_msi, c_msi) = { - let mut buffer = Buffer::new("".into()); - let view_idx = buffer.add_mark_set(); - let cursors_idx = buffer.add_mark_set(); + pub fn new(buffer: Buffer, formatter: LineFormatter) -> Editor { + let mut buffer = buffer; - buffer.mark_sets[view_idx].add_mark(Mark::new(0, 0)); - buffer.mark_sets[cursors_idx].add_mark(Mark::new(0, 0)); - - (buffer, view_idx, cursors_idx) - }; - - Editor { - buffer: buffer, - formatter: formatter, - file_path: PathBuf::new(), - line_ending_type: LineEnding::LF, - soft_tabs: false, - soft_tab_width: 4, - editor_dim: (0, 0), - view_dim: (0, 0), - v_msi: v_msi, - c_msi: c_msi, - } - } - - pub fn new_from_file(formatter: LineFormatter, path: &Path) -> io::Result { - let (buffer, v_msi, c_msi) = { - let mut buffer = Buffer::new(Rope::from_reader(BufReader::new(File::open(path)?))?); - let view_idx = buffer.add_mark_set(); - let cursors_idx = buffer.add_mark_set(); - - buffer.mark_sets[view_idx].add_mark(Mark::new(0, 0)); - buffer.mark_sets[cursors_idx].add_mark(Mark::new(0, 0)); - - (buffer, view_idx, cursors_idx) - }; + // Create appropriate mark sets for view positions and cursors. + let v_msi = buffer.add_mark_set(); + let c_msi = buffer.add_mark_set(); + buffer.mark_sets[v_msi].add_mark(Mark::new(0, 0)); + buffer.mark_sets[c_msi].add_mark(Mark::new(0, 0)); let mut ed = Editor { buffer: buffer, formatter: formatter, - file_path: path.to_path_buf(), line_ending_type: LineEnding::LF, soft_tabs: false, soft_tab_width: 4, @@ -97,18 +66,20 @@ impl Editor { ed.auto_detect_line_ending(); ed.auto_detect_indentation_style(); - Ok(ed) + ed } pub fn save_if_dirty(&mut self) -> io::Result<()> { - if self.buffer.is_dirty && self.file_path != PathBuf::new() { - let mut f = BufWriter::new(File::create(&self.file_path)?); + if let BufferPath::File(ref file_path) = self.buffer.path { + if self.buffer.is_dirty { + let mut f = BufWriter::new(File::create(file_path)?); - for c in self.buffer.text.chunks() { - f.write(c.as_bytes())?; + for c in self.buffer.text.chunks() { + f.write(c.as_bytes())?; + } + + self.buffer.is_dirty = false; } - - self.buffer.is_dirty = false; } Ok(()) diff --git a/src/main.rs b/src/main.rs index 389a605..12b770c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,13 @@ -use std::{io::Write, path::Path}; +use std::{ + fs::File, + io::{BufReader, Write}, +}; +use backend::buffer::{Buffer, BufferPath}; use clap::{App, Arg}; use editor::Editor; use formatter::LineFormatter; +use ropey::Rope; use term_ui::TermUI; mod editor; @@ -14,7 +19,7 @@ mod utils; const VERSION: &str = env!("CARGO_PKG_VERSION"); -fn main() { +fn main() -> std::io::Result<()> { // Parse command line arguments. let args = App::new("Led") .version(VERSION) @@ -28,13 +33,17 @@ fn main() { .get_matches(); // Load file, if specified - let editor = if let Some(filepath) = args.value_of("file") { - Editor::new_from_file(LineFormatter::new(4), &Path::new(&filepath[..])) - .expect(&format!("Couldn't open file '{}'.", filepath)) + let buffer = if let Some(filepath) = args.value_of("file") { + Buffer::new( + Rope::from_reader(BufReader::new(File::open(filepath)?))?, + BufferPath::File(filepath.into()), + ) } else { - Editor::new(LineFormatter::new(4)) + Buffer::new("".into(), BufferPath::Temp(0)) }; + let editor = Editor::new(buffer, LineFormatter::new(4)); + // Holds stderr output in an internal buffer, and prints it when dropped. // This keeps stderr from being swallowed by the TUI. let stderr_hold = gag::Hold::stderr().unwrap(); @@ -58,4 +67,6 @@ fn main() { // Resume panic unwind. std::panic::resume_unwind(e); } + + Ok(()) } diff --git a/src/term_ui/mod.rs b/src/term_ui/mod.rs index a518e6a..ece8d0b 100644 --- a/src/term_ui/mod.rs +++ b/src/term_ui/mod.rs @@ -10,9 +10,10 @@ use crossterm::{ style::Color, }; +use backend::buffer::BufferPath; + use crate::{ editor::Editor, - formatter::LineFormatter, string_utils::{char_count, is_line_ending, line_ending_to_str, LineEnding}, utils::{digit_count, Timer}, }; @@ -187,10 +188,6 @@ enum LoopStatus { } impl TermUI { - pub fn new() -> TermUI { - TermUI::new_from_editor(Editor::new(LineFormatter::new(4))) - } - pub fn new_from_editor(ed: Editor) -> TermUI { let (w, h) = crossterm::terminal::size().unwrap(); let mut editor = ed; @@ -463,7 +460,10 @@ impl TermUI { } // Filename and dirty marker - let filename = editor.file_path.display(); + let filename = match editor.buffer.path { + BufferPath::File(ref p) => format!("{}", p.display()), + BufferPath::Temp(i) => format!("Scratch #{}", i + 1), + }; let dirty_char = if editor.buffer.is_dirty { "*" } else { "" }; let name = format!("{}{}", filename, dirty_char); self.screen.draw(c1.1 + 1, c1.0, &name[..], STYLE_INFO); diff --git a/sub_crates/backend/src/buffer.rs b/sub_crates/backend/src/buffer.rs index 2473138..ed08b96 100644 --- a/sub_crates/backend/src/buffer.rs +++ b/sub_crates/backend/src/buffer.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use ropey::Rope; use crate::{ @@ -5,9 +7,20 @@ use crate::{ marks::MarkSet, }; +/// A path for an open text buffer. +/// +/// This indicates where the text data of the buffer came from, and +/// where it should be saved to. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum BufferPath { + File(PathBuf), // A buffer for a normal file on disk. + Temp(usize), // A temporary buffer, with a number ID. +} + /// An open text buffer, currently being edited. #[derive(Debug, Clone)] pub struct Buffer { + pub path: BufferPath, pub is_dirty: bool, // Is this buffer currently out of sync with disk. pub text: Rope, // The actual text content. pub mark_sets: Vec, // MarkSets for cursors, view positions, etc. @@ -15,8 +28,9 @@ pub struct Buffer { } impl Buffer { - pub fn new(text: Rope) -> Buffer { + pub fn new(text: Rope, path: BufferPath) -> Buffer { Buffer { + path: path, is_dirty: false, text: text, mark_sets: Vec::new(), diff --git a/sub_crates/backend/src/editor.rs b/sub_crates/backend/src/editor.rs index 1c315d4..859c9c4 100644 --- a/sub_crates/backend/src/editor.rs +++ b/sub_crates/backend/src/editor.rs @@ -1,5 +1,3 @@ -use std::path::PathBuf; - use crate::buffer::Buffer; /// A struct holding the current editor state. @@ -7,12 +5,5 @@ use crate::buffer::Buffer; /// The Editor represents all currently open buffers available for editing. #[derive(Debug)] pub struct Editor { - open_buffers: Vec<(BufferID, Buffer)>, -} - -/// An ID for an open text buffer. -#[derive(Debug, Clone)] -pub enum BufferID { - File(PathBuf), // A buffer for a normal file on disk, using the full on-disk path as the ID - Temp(usize), // A temporary buffer, with a number ID + open_buffers: Vec, }