diff --git a/src/components/mod.rs b/src/components/mod.rs index 6e9ede3..4398baf 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,4 +1,4 @@ -pub mod tabs; +pub mod action_bar; pub mod editor; pub mod status_bar; -pub mod action_bar; +pub mod tabs; diff --git a/src/components/tabs.rs b/src/components/tabs.rs index d679b03..d0aef99 100644 --- a/src/components/tabs.rs +++ b/src/components/tabs.rs @@ -11,8 +11,9 @@ pub fn view<'a>(files: &'a [File], active_index: usize) -> Element<'a, Message> let is_focused = index == active_index; let label = text(file.display_name()); - let close_btn = - button(text("x")).padding(1).on_press(Message::FileActionSelected(FileAction::Close(Some(index)))); + let close_btn = button(text("x")) + .padding(1) + .on_press(Message::FileActionSelected(FileAction::Close(Some(index)))); button( row![label, close_btn] @@ -62,7 +63,7 @@ pub fn view<'a>(files: &'a [File], active_index: usize) -> Element<'a, Message> bottom: 0.0, })) .direction(scrollable::Direction::Horizontal( - scrollable::Scrollbar::new().spacing(1) + scrollable::Scrollbar::new().spacing(1), )) .style(|theme: &Theme, status: scrollable::Status| { let mut style = scrollable::default(theme, status); diff --git a/src/file.rs b/src/file.rs index 8f4fa34..a2b818d 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,87 +1,93 @@ -use std::{ffi, path::{Path, PathBuf}}; +use std::{ + ffi, + path::{Path, PathBuf}, +}; use iced::widget::{markdown, text_editor}; pub struct File { - content: text_editor::Content, - path: Option, - markdown: Vec, + content: text_editor::Content, + path: Option, + markdown: Vec, } impl Default for File { - fn default() -> Self { - File { - content: text_editor::Content::new(), - path: None, - markdown: Vec::new(), - } + fn default() -> Self { + File { + content: text_editor::Content::new(), + path: None, + markdown: Vec::new(), } + } } impl File { - pub fn from(content: &str, path: Option) -> Self { - let text_editor_content = text_editor::Content::with_text(content); - let markdown = markdown::parse(content).collect(); + pub fn from(content: &str, path: Option) -> Self { + let text_editor_content = text_editor::Content::with_text(content); + let markdown = markdown::parse(content).collect(); - File { - content: text_editor_content, - path, - markdown, - } + File { + content: text_editor_content, + path, + markdown, } + } - pub fn content(&self) -> &text_editor::Content { - &self.content - } + pub fn content(&self) -> &text_editor::Content { + &self.content + } - pub fn content_mut(&mut self) -> &mut text_editor::Content { - &mut self.content - } + pub fn content_mut(&mut self) -> &mut text_editor::Content { + &mut self.content + } - pub fn set_content(&mut self, content: &str) { - self.content = text_editor::Content::with_text(content); - } + pub fn set_content(&mut self, content: &str) { + self.content = text_editor::Content::with_text(content); + } - pub fn markdown(&self) -> Vec<&markdown::Item> { - self.markdown.iter().collect() - } + pub fn markdown(&self) -> Vec<&markdown::Item> { + self.markdown.iter().collect() + } - pub fn update_markdown(&mut self) { - self.markdown = markdown::parse(&self.content.text()).collect(); - } + pub fn update_markdown(&mut self) { + self.markdown = markdown::parse(&self.content.text()).collect(); + } - pub fn path(&self) -> Option<&PathBuf> { - self.path.as_ref() - } + pub fn path(&self) -> Option<&PathBuf> { + self.path.as_ref() + } - pub fn set_path(&mut self, path: Option) { - self.path = path; - } + pub fn set_path(&mut self, path: Option) { + self.path = path; + } - pub fn extension(&self) -> Option<&str> { - self.path - .as_deref() - .and_then(Path::extension) - .and_then(ffi::OsStr::to_str) - } + pub fn extension(&self) -> Option<&str> { + self + .path + .as_deref() + .and_then(Path::extension) + .and_then(ffi::OsStr::to_str) + } - pub fn display_name(&self) -> &str { - self.path - .as_deref() - .and_then(|p| p.file_name()) - .and_then(|n| n.to_str()) - .unwrap_or("New file") - } + pub fn display_name(&self) -> &str { + self + .path + .as_deref() + .and_then(|p| p.file_name()) + .and_then(|n| n.to_str()) + .unwrap_or("New file") + } - pub fn position_summary(&self) -> String { - let pos = self.content.cursor().position; - format!("Ln {}, Col {}", pos.line, pos.column) - } + pub fn position_summary(&self) -> String { + let pos = self.content.cursor().position; + format!("Ln {}, Col {}", pos.line, pos.column) + } - pub fn path_summary(&self) -> String { - self.path - .as_deref() - .map(|p| p.to_string_lossy().to_string()) - .unwrap_or_default() - } + pub fn path_summary(&self) -> String { + self + .path + .as_deref() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_default() + } } diff --git a/src/handler.rs b/src/handler.rs index 110006d..dcd11fc 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -7,75 +7,75 @@ use iced::Task; use iced::widget::text_editor; pub fn edit(state: &mut State, action: text_editor::Action) -> Task { - state.apply_edit(action); - Task::none() + state.apply_edit(action); + Task::none() } pub fn switch_tab(state: &mut State, index: usize) -> Task { - state.switch_tab(index); - Task::none() + state.switch_tab(index); + Task::none() } pub fn link_clicked(url: String) -> Task { - println!("Opening link: {}", url); - Task::none() + println!("Opening link: {}", url); + Task::none() } pub fn file_action(state: &mut State, action: FileAction) -> Task { - match action { - FileAction::New => { - state.new_file(); - Task::none() - } - FileAction::Close(index) => { - if state.close_file(index) { - iced::exit() - } else { - Task::none() - } - } - FileAction::Open => Task::perform(io::open_file(), Message::FileOpened), - FileAction::Save => { - let (current_path, content) = state.active_file_data(); - let path = current_path.cloned(); - Task::perform(io::save_file(path, content), Message::FileSaved) - } - FileAction::SaveAs => { - let (_, content) = state.active_file_data(); - Task::perform(io::save_file(None, content), Message::FileSaved) - } + match action { + FileAction::New => { + state.new_file(); + Task::none() } + FileAction::Close(index) => { + if state.close_file(index) { + iced::exit() + } else { + Task::none() + } + } + FileAction::Open => Task::perform(io::open_file(), Message::FileOpened), + FileAction::Save => { + let (current_path, content) = state.active_file_data(); + let path = current_path.cloned(); + Task::perform(io::save_file(path, content), Message::FileSaved) + } + FileAction::SaveAs => { + let (_, content) = state.active_file_data(); + Task::perform(io::save_file(None, content), Message::FileSaved) + } + } } pub fn opened_file(state: &mut State, result: Result<(PathBuf, String), String>) -> Task { - match result { - Ok((path, content)) => { - state.open_file(path, content); - } - Err(_error) => {} - }; + match result { + Ok((path, content)) => { + state.open_file(path, content); + } + Err(_error) => {} + }; - Task::none() + Task::none() } pub fn saved_file(state: &mut State, result: Result) -> Task { - match result { - Ok(path) => { - state.set_active_file_path(path); - } - Err(_error) => {} - }; + match result { + Ok(path) => { + state.set_active_file_path(path); + } + Err(_error) => {} + }; - Task::none() + Task::none() } pub fn view_action(state: &mut State, action: ViewAction) -> Task { - match action { - ViewAction::Increase => state.increase_font(), - ViewAction::Decrease => state.decrease_font(), - ViewAction::Reset => state.reset_font(), - ViewAction::TogglePreview => state.toggle_preview(), - } + match action { + ViewAction::Increase => state.increase_font(), + ViewAction::Decrease => state.decrease_font(), + ViewAction::Reset => state.reset_font(), + ViewAction::TogglePreview => state.toggle_preview(), + } - Task::none() + Task::none() } diff --git a/src/io.rs b/src/io.rs index c36cf8d..4466df9 100644 --- a/src/io.rs +++ b/src/io.rs @@ -4,42 +4,42 @@ use rfd::AsyncFileDialog; use tokio::fs; pub async fn open_file() -> Result<(PathBuf, String), String> { - let handle_result = AsyncFileDialog::new() - .set_directory("/") - .pick_file() - .await - .ok_or(String::from("Dialog cancelled")); + let handle_result = AsyncFileDialog::new() + .set_directory("/") + .pick_file() + .await + .ok_or(String::from("Dialog cancelled")); - match handle_result { - Ok(handle) => { - let path = handle.path().to_owned(); - let content_result = fs::read_to_string(&path).await.map_err(|e| e.to_string()); + match handle_result { + Ok(handle) => { + let path = handle.path().to_owned(); + let content_result = fs::read_to_string(&path).await.map_err(|e| e.to_string()); - match content_result { - Ok(content) => Ok((path, content)), - Err(e) => Err(e), - } - } + match content_result { + Ok(content) => Ok((path, content)), Err(e) => Err(e), + } } + Err(e) => Err(e), + } } pub async fn save_file(path: Option, text: String) -> Result { - let save_path = match path { - Some(p) => p, - None => AsyncFileDialog::new() - .set_directory("/") - .save_file() - .await - .ok_or(String::from("Dialog cancelled"))? - .path() - .to_owned(), - }; + let save_path = match path { + Some(p) => p, + None => AsyncFileDialog::new() + .set_directory("/") + .save_file() + .await + .ok_or(String::from("Dialog cancelled"))? + .path() + .to_owned(), + }; - let save_result = fs::write(&save_path, text).await.map_err(|e| e.to_string()); + let save_result = fs::write(&save_path, text).await.map_err(|e| e.to_string()); - match save_result { - Ok(_) => Ok(save_path), - Err(err) => Err(err), - } + match save_result { + Ok(_) => Ok(save_path), + Err(err) => Err(err), + } } diff --git a/src/key_bindings.rs b/src/key_bindings.rs index f1eb0ab..192146f 100644 --- a/src/key_bindings.rs +++ b/src/key_bindings.rs @@ -3,66 +3,66 @@ use iced::keyboard::{self, Key, Modifiers}; use crate::message::{FileAction, Message, ViewAction}; pub struct Keybinding { - key: &'static str, - modifiers: Modifiers, - message: Message, + key: &'static str, + modifiers: Modifiers, + message: Message, } impl Keybinding { - pub fn should_handle(&self, key_pressed: &Key, modifiers: &Modifiers) -> bool { - let key = keyboard::Key::Character(self.key.into()); - modifiers.contains(self.modifiers) && key_pressed == &key - } + pub fn should_handle(&self, key_pressed: &Key, modifiers: &Modifiers) -> bool { + let key = keyboard::Key::Character(self.key.into()); + modifiers.contains(self.modifiers) && key_pressed == &key + } - pub fn message(&self) -> Message { - self.message.clone() - } + pub fn message(&self) -> Message { + self.message.clone() + } } pub const ALL: &[Keybinding] = &[ - Keybinding { - key: "o", - modifiers: Modifiers::CTRL, - message: Message::FileActionSelected(FileAction::Open), - }, - Keybinding { - key: "n", - modifiers: Modifiers::CTRL, - message: Message::FileActionSelected(FileAction::New), - }, - Keybinding { - key: "s", - modifiers: Modifiers::CTRL.union(Modifiers::SHIFT), - message: Message::FileActionSelected(FileAction::SaveAs), - }, - Keybinding { - key: "s", - modifiers: Modifiers::CTRL, - message: Message::FileActionSelected(FileAction::Save), - }, - Keybinding { - key: "w", - modifiers: Modifiers::CTRL, - message: Message::FileActionSelected(FileAction::Close(None)), - }, - Keybinding { - key: "p", - modifiers: Modifiers::CTRL, - message: Message::ViewActionSelected(ViewAction::TogglePreview), - }, - Keybinding { - key: "=", - modifiers: Modifiers::CTRL, - message: Message::ViewActionSelected(ViewAction::Increase), - }, - Keybinding { - key: "-", - modifiers: Modifiers::CTRL, - message: Message::ViewActionSelected(ViewAction::Decrease), - }, - Keybinding { - key: "0", - modifiers: Modifiers::CTRL, - message: Message::ViewActionSelected(ViewAction::Reset), - }, + Keybinding { + key: "o", + modifiers: Modifiers::CTRL, + message: Message::FileActionSelected(FileAction::Open), + }, + Keybinding { + key: "n", + modifiers: Modifiers::CTRL, + message: Message::FileActionSelected(FileAction::New), + }, + Keybinding { + key: "s", + modifiers: Modifiers::CTRL.union(Modifiers::SHIFT), + message: Message::FileActionSelected(FileAction::SaveAs), + }, + Keybinding { + key: "s", + modifiers: Modifiers::CTRL, + message: Message::FileActionSelected(FileAction::Save), + }, + Keybinding { + key: "w", + modifiers: Modifiers::CTRL, + message: Message::FileActionSelected(FileAction::Close(None)), + }, + Keybinding { + key: "p", + modifiers: Modifiers::CTRL, + message: Message::ViewActionSelected(ViewAction::TogglePreview), + }, + Keybinding { + key: "=", + modifiers: Modifiers::CTRL, + message: Message::ViewActionSelected(ViewAction::Increase), + }, + Keybinding { + key: "-", + modifiers: Modifiers::CTRL, + message: Message::ViewActionSelected(ViewAction::Decrease), + }, + Keybinding { + key: "0", + modifiers: Modifiers::CTRL, + message: Message::ViewActionSelected(ViewAction::Reset), + }, ]; diff --git a/src/message.rs b/src/message.rs index e05f1dc..2186d15 100644 --- a/src/message.rs +++ b/src/message.rs @@ -4,70 +4,70 @@ use iced::widget::text_editor; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum FileAction { - New, - Save, - SaveAs, - Open, - Close(Option), + New, + Save, + SaveAs, + Open, + Close(Option), } impl FileAction { - pub const ALL: &'static [FileAction] = &[ - FileAction::New, - FileAction::Save, - FileAction::SaveAs, - FileAction::Open, - FileAction::Close(None), - ]; + pub const ALL: &'static [FileAction] = &[ + FileAction::New, + FileAction::Save, + FileAction::SaveAs, + FileAction::Open, + FileAction::Close(None), + ]; } impl Display for FileAction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - FileAction::New => write!(f, "New file"), - FileAction::Save => write!(f, "Save"), - FileAction::SaveAs => write!(f, "Save as... "), - FileAction::Open => write!(f, "Open"), - FileAction::Close(_) => write!(f, "Close"), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + FileAction::New => write!(f, "New file"), + FileAction::Save => write!(f, "Save"), + FileAction::SaveAs => write!(f, "Save as... "), + FileAction::Open => write!(f, "Open"), + FileAction::Close(_) => write!(f, "Close"), } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ViewAction { - Increase, - Decrease, - Reset, - TogglePreview, + Increase, + Decrease, + Reset, + TogglePreview, } impl ViewAction { - pub const ALL: &'static [ViewAction] = &[ - ViewAction::Increase, - ViewAction::Decrease, - ViewAction::Reset, - ViewAction::TogglePreview, - ]; + pub const ALL: &'static [ViewAction] = &[ + ViewAction::Increase, + ViewAction::Decrease, + ViewAction::Reset, + ViewAction::TogglePreview, + ]; } impl Display for ViewAction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ViewAction::Decrease => write!(f, "Decrease font"), - ViewAction::Increase => write!(f, "Increase font"), - ViewAction::Reset => write!(f, "Reset font"), - ViewAction::TogglePreview => write!(f, "Toggle preview"), - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ViewAction::Decrease => write!(f, "Decrease font"), + ViewAction::Increase => write!(f, "Increase font"), + ViewAction::Reset => write!(f, "Reset font"), + ViewAction::TogglePreview => write!(f, "Toggle preview"), } + } } #[derive(Debug, Clone)] pub enum Message { - Edit(text_editor::Action), - FileActionSelected(FileAction), - ViewActionSelected(ViewAction), - SwitchTab(usize), - LinkClicked(String), - FileOpened(Result<(PathBuf, String), String>), - FileSaved(Result), + Edit(text_editor::Action), + FileActionSelected(FileAction), + ViewActionSelected(ViewAction), + SwitchTab(usize), + LinkClicked(String), + FileOpened(Result<(PathBuf, String), String>), + FileSaved(Result), } diff --git a/src/state.rs b/src/state.rs index 7ca7197..8d9ad96 100644 --- a/src/state.rs +++ b/src/state.rs @@ -2,146 +2,146 @@ use std::path::PathBuf; use iced::widget::text_editor; -use crate::{constants, file}; use crate::message::{FileAction, ViewAction}; +use crate::{constants, file}; #[derive(Default, Copy, Clone)] pub enum Mode { - #[default] - Edit, - Preview, + #[default] + Edit, + Preview, } #[derive(Default)] pub struct State { - mode: Mode, - files: Vec, - current_file: usize, - editor_font_size: u32, - selected_file_action: Option, - selected_view_action: Option, + mode: Mode, + files: Vec, + current_file: usize, + editor_font_size: u32, + selected_file_action: Option, + selected_view_action: Option, } impl State { - pub fn new() -> Self { - let default_file = file::File::default(); + pub fn new() -> Self { + let default_file = file::File::default(); - Self { - files: vec![default_file], - current_file: 0, - editor_font_size: constants::DEFAULT_EDITOR_FONT_SIZE, - ..Default::default() - } + Self { + files: vec![default_file], + current_file: 0, + editor_font_size: constants::DEFAULT_EDITOR_FONT_SIZE, + ..Default::default() + } + } + + pub fn apply_edit(&mut self, action: text_editor::Action) { + self.files[self.current_file].content_mut().perform(action); + } + + pub fn new_file(&mut self) { + self.files.push(file::File::default()); + self.current_file = self.files.len() - 1; + } + + pub fn close_file(&mut self, index: Option) -> bool { + let index = index.unwrap_or(self.current_file); + + if self.files.len() <= 1 { + return true; } - pub fn apply_edit(&mut self, action: text_editor::Action) { - self.files[self.current_file].content_mut().perform(action); + self.files.remove(index); + + if self.current_file >= self.files.len() { + self.current_file = self.files.len().saturating_sub(1); + } else if index < self.current_file { + self.current_file -= 1; } - pub fn new_file(&mut self) { - self.files.push(file::File::default()); - self.current_file = self.files.len() - 1; + false + } + + pub fn open_file(&mut self, path: PathBuf, content: String) { + if let Some(index) = self.files.iter().position(|f| f.path() == Some(&path)) { + self.files[index].set_content(&content); + self.current_file = index; + return; } - pub fn close_file(&mut self, index: Option) -> bool { - let index = index.unwrap_or(self.current_file); + let opened_file = file::File::from(&content, Some(path)); - if self.files.len() <= 1 { - return true; - } + self.files.push(opened_file); + self.current_file = self.files.len() - 1; + } - self.files.remove(index); - - if self.current_file >= self.files.len() { - self.current_file = self.files.len().saturating_sub(1); - } else if index < self.current_file { - self.current_file -= 1; - } - - false + pub fn switch_tab(&mut self, index: usize) { + if index < self.files.len() { + self.current_file = index; } + } - pub fn open_file(&mut self, path: PathBuf, content: String) { - if let Some(index) = self.files.iter().position(|f| f.path() == Some(&path)) { - self.files[index].set_content(&content); - self.current_file = index; - return; - } + pub fn active_file_data(&self) -> (Option<&PathBuf>, String) { + let file = &self.files[self.current_file]; + (file.path(), file.content().text()) + } - let opened_file = file::File::from(&content, Some(path)); - - self.files.push(opened_file); - self.current_file = self.files.len() - 1; + pub fn set_active_file_path(&mut self, path: PathBuf) { + if let Some(file) = self.files.get_mut(self.current_file) { + file.set_path(Some(path)); } + } - pub fn switch_tab(&mut self, index: usize) { - if index < self.files.len() { - self.current_file = index; - } + pub fn increase_font(&mut self) { + if self.editor_font_size < constants::MAX_EDITOR_FONT_SIZE { + self.editor_font_size += 2; } + } - pub fn active_file_data(&self) -> (Option<&PathBuf>, String) { - let file = &self.files[self.current_file]; - (file.path(), file.content().text()) + pub fn decrease_font(&mut self) { + if self.editor_font_size > constants::MIN_EDITOR_FONT_SIZE { + self.editor_font_size -= 2; } + } - pub fn set_active_file_path(&mut self, path: PathBuf) { - if let Some(file) = self.files.get_mut(self.current_file) { - file.set_path(Some(path)); - } - } + pub fn reset_font(&mut self) { + self.editor_font_size = constants::DEFAULT_EDITOR_FONT_SIZE; + } - pub fn increase_font(&mut self) { - if self.editor_font_size < constants::MAX_EDITOR_FONT_SIZE { - self.editor_font_size += 2; - } + pub fn toggle_preview(&mut self) { + match self.mode { + Mode::Edit => { + self.files[self.current_file].update_markdown(); + self.mode = Mode::Preview; + } + Mode::Preview => self.mode = Mode::Edit, } + } - pub fn decrease_font(&mut self) { - if self.editor_font_size > constants::MIN_EDITOR_FONT_SIZE { - self.editor_font_size -= 2; - } - } + pub fn files(&self) -> &[file::File] { + &self.files + } - pub fn reset_font(&mut self) { - self.editor_font_size = constants::DEFAULT_EDITOR_FONT_SIZE; - } + pub fn current_file_index(&self) -> usize { + self.current_file + } - pub fn toggle_preview(&mut self) { - match self.mode { - Mode::Edit => { - self.files[self.current_file].update_markdown(); - self.mode = Mode::Preview; - } - Mode::Preview => self.mode = Mode::Edit, - } - } + pub fn active_file(&self) -> &file::File { + &self.files[self.current_file] + } - pub fn files(&self) -> &[file::File] { - &self.files - } + pub fn mode(&self) -> Mode { + self.mode + } - pub fn current_file_index(&self) -> usize { - self.current_file - } + pub fn font_size(&self) -> u32 { + self.editor_font_size + } - pub fn active_file(&self) -> &file::File { - &self.files[self.current_file] - } + pub fn selected_file_action(&self) -> Option { + self.selected_file_action + } - pub fn mode(&self) -> Mode { - self.mode - } - - pub fn font_size(&self) -> u32 { - self.editor_font_size - } - - pub fn selected_file_action(&self) -> Option { - self.selected_file_action - } - - pub fn selected_view_action(&self) -> Option { - self.selected_view_action - } + pub fn selected_view_action(&self) -> Option { + self.selected_view_action + } }