chore: run cargo fmt

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