From bfd81c5133dbc08d7b27088edd9c4425b235c301 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Tue, 24 Feb 2026 21:26:59 -0500 Subject: [PATCH] Refactor crates. --- Cargo.lock | 12 ---- Cargo.toml | 7 +- README.md | 2 +- build.rs | 2 +- libclide-macros/src/lib.rs | 2 +- libclide/Cargo.lock | 16 ----- libclide/Cargo.toml | 11 --- {libclide/src => src}/fs.rs | 0 {libclide/src => src}/fs/entry_meta.rs | 2 +- src/ide.rs | 7 ++ src/ide/cli.rs | 43 ++++++++++++ src/ide/cli/app_context.rs | 36 ++++++++++ src/{ => ide}/gui.rs | 11 +-- src/{ => ide}/gui/colors.rs | 2 +- src/{ => ide}/gui/filesystem.rs | 4 +- src/{ => ide}/tui.rs | 32 ++++----- src/{ => ide}/tui/about.rs | 6 +- src/{ => ide}/tui/app.rs | 40 ++++++----- src/{ => ide}/tui/component.rs | 8 +-- src/{ => ide}/tui/editor.rs | 16 ++--- src/{ => ide}/tui/editor_tab.rs | 34 ++++----- src/{ => ide}/tui/explorer.rs | 8 +-- src/{ => ide}/tui/logger.rs | 10 +-- src/{ => ide}/tui/menu_bar.rs | 13 ++-- {libclide/src => src}/lib.rs | 3 +- libclide/src/log.rs => src/logging.rs | 0 {libclide/src/log => src/logging}/macros.rs | 27 ++++---- src/main.rs | 76 ++------------------- {libclide/src => src}/theme.rs | 0 {libclide/src => src}/theme/colors.rs | 0 30 files changed, 209 insertions(+), 221 deletions(-) delete mode 100644 libclide/Cargo.lock delete mode 100644 libclide/Cargo.toml rename {libclide/src => src}/fs.rs (100%) rename {libclide/src => src}/fs/entry_meta.rs (97%) create mode 100644 src/ide.rs create mode 100644 src/ide/cli.rs create mode 100644 src/ide/cli/app_context.rs rename src/{ => ide}/gui.rs (87%) rename src/{ => ide}/gui/colors.rs (99%) rename src/{ => ide}/gui/filesystem.rs (96%) rename src/{ => ide}/tui.rs (81%) rename src/{ => ide}/tui/about.rs (96%) rename src/{ => ide}/tui/app.rs (92%) rename src/{ => ide}/tui/component.rs (95%) rename src/{ => ide}/tui/editor.rs (89%) rename src/{ => ide}/tui/editor_tab.rs (88%) rename src/{ => ide}/tui/explorer.rs (97%) rename src/{ => ide}/tui/logger.rs (92%) rename src/{ => ide}/tui/menu_bar.rs (96%) rename {libclide/src => src}/lib.rs (84%) rename libclide/src/log.rs => src/logging.rs (100%) rename {libclide/src/log => src/logging}/macros.rs (64%) rename {libclide/src => src}/theme.rs (100%) rename {libclide/src => src}/theme/colors.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 2bb2160..00ef795 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,7 +298,6 @@ dependencies = [ "devicons", "dirs", "edtui", - "libclide", "libclide-macros", "log", "ratatui", @@ -1165,17 +1164,6 @@ version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" -[[package]] -name = "libclide" -version = "0.1.0" -dependencies = [ - "anyhow", - "devicons", - "libclide-macros", - "log", - "strum", -] - [[package]] name = "libclide-macros" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 2f18af2..fc8025b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,13 @@ name = "clide" version = "0.1.0" edition = "2024" +[[bin]] +name = "clide" +path = "src/main.rs" + [workspace] resolver = "3" -members = [".", "libclide", "libclide-macros", ] +members = [".", "libclide-macros", ] [workspace.dependencies] anyhow = "1.0.100" @@ -24,7 +28,6 @@ ratatui = "0.30.0" tui-tree-widget = "0.24.0" tui-logger = "0.18.1" edtui = "0.11.1" -libclide = { path = "./libclide" } libclide-macros = { path = "./libclide-macros" } anyhow = { workspace = true } strum = { workspace = true } diff --git a/README.md b/README.md index 8c93c29..d580097 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ And of course, [Rust](https://www.rust-lang.org/tools/install). curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -This project requires at least Qt 6.7.3 To check your Qt version +This project requires at least Qt 6.7.3. To check your Qt version, you can use the following command ```bash qmake6 -query QT_VERSION diff --git a/build.rs b/build.rs index 325c73b..9b37fb0 100644 --- a/build.rs +++ b/build.rs @@ -28,6 +28,6 @@ fn main() { .qt_module("Svg") .qt_module("Xml") .qrc("./resources.qrc") - .files(["src/gui/colors.rs", "src/gui/filesystem.rs"]) + .files(["src/ide/gui/colors.rs", "src/ide/gui/filesystem.rs"]) .build(); } diff --git a/libclide-macros/src/lib.rs b/libclide-macros/src/lib.rs index b92f58b..b133d4f 100644 --- a/libclide-macros/src/lib.rs +++ b/libclide-macros/src/lib.rs @@ -15,7 +15,7 @@ pub fn loggable(item: TokenStream) -> TokenStream { let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); let struct_name_str = struct_name.to_string(); let expanded = quote! { - impl #impl_generics libclide::log::Loggable for #struct_name #type_generics #where_clause { + impl #impl_generics crate::logging::Loggable for #struct_name #type_generics #where_clause { const ID: &'static str = #struct_name_str; } }; diff --git a/libclide/Cargo.lock b/libclide/Cargo.lock deleted file mode 100644 index e5365f2..0000000 --- a/libclide/Cargo.lock +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "libclide" -version = "0.1.0" -dependencies = [ - "anyhow", -] diff --git a/libclide/Cargo.toml b/libclide/Cargo.toml deleted file mode 100644 index f5014f3..0000000 --- a/libclide/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "libclide" -version = "0.1.0" -edition = "2024" - -[dependencies] -anyhow = { workspace = true } -strum = { workspace = true } -log = { workspace = true } -devicons = { workspace = true } -libclide-macros = { path = "../libclide-macros" } diff --git a/libclide/src/fs.rs b/src/fs.rs similarity index 100% rename from libclide/src/fs.rs rename to src/fs.rs diff --git a/libclide/src/fs/entry_meta.rs b/src/fs/entry_meta.rs similarity index 97% rename from libclide/src/fs/entry_meta.rs rename to src/fs/entry_meta.rs index 148df0b..aebcbd2 100644 --- a/libclide/src/fs/entry_meta.rs +++ b/src/fs/entry_meta.rs @@ -43,7 +43,7 @@ impl EntryMeta { .context(format!("Failed to get file name for path: {abs_path:?}"))? .to_string_lossy() .to_string(); - let icon = crate::fs::icon(&abs_path); + let icon = icon(&abs_path); Ok(EntryMeta { abs_path, file_name, diff --git a/src/ide.rs b/src/ide.rs new file mode 100644 index 0000000..881fc2f --- /dev/null +++ b/src/ide.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2026, Shaun Reed +// +// SPDX-License-Identifier: GNU General Public License v3.0 or later + +pub mod cli; +pub mod gui; +pub mod tui; diff --git a/src/ide/cli.rs b/src/ide/cli.rs new file mode 100644 index 0000000..97721a3 --- /dev/null +++ b/src/ide/cli.rs @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2026, Shaun Reed +// +// SPDX-License-Identifier: GNU General Public License v3.0 or later + +pub mod app_context; + +use crate::ide::cli::app_context::RunMode; +use anyhow::{Result, anyhow}; +use clap::Parser; + +/// Extendable command-line driven development environment written in Rust using the Qt UI framework. +/// If no flags are provided, the GUI editor is launched in a separate process. +/// If no path is provided, the current directory is used. +#[derive(Parser, Debug)] +#[structopt(name = "clide", verbatim_doc_comment)] +pub struct Cli { + /// The root directory for the project to open with the clide editor. + #[arg(value_parser = clap::value_parser!(std::path::PathBuf))] + pub path: Option, + + /// Run clide in headless mode. + #[arg(value_name = "tui", short, long)] + pub tui: bool, + + /// Run the clide GUI in the current process, blocking the terminal and showing all output streams. + #[arg(value_name = "gui", short, long)] + pub gui: bool, +} + +impl Cli { + pub fn run_mode(&self) -> Result { + let mut modes = Vec::new(); + self.tui.then(|| modes.push(RunMode::Tui)); + self.gui.then(|| modes.push(RunMode::GuiAttached)); + match &modes[..] { + [] => Ok(RunMode::Gui), + [mode] => Ok(*mode), + multiple => Err(anyhow!( + "More than one run mode found {multiple:?} please select one." + )), + } + } +} diff --git a/src/ide/cli/app_context.rs b/src/ide/cli/app_context.rs new file mode 100644 index 0000000..084d340 --- /dev/null +++ b/src/ide/cli/app_context.rs @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2026, Shaun Reed +// +// SPDX-License-Identifier: GNU General Public License v3.0 or later + +use crate::ide::cli::Cli; +use anyhow::{Context, Result}; + +pub struct AppContext { + pub path: std::path::PathBuf, + pub run_mode: RunMode, +} + +impl AppContext { + pub fn new(cli: Cli) -> Result { + let path = match &cli.path { + // If the CLI was provided a directory, convert it to absolute. + Some(path) => std::path::absolute(path)?, + // If no path was provided, use the current directory. + None => std::env::current_dir().context("Failed to obtain current directory")?, + }; + crate::info!(target:"main()", "Root path detected: {path:?}"); + + Ok(Self { + path, + run_mode: cli.run_mode()?, + }) + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub enum RunMode { + #[default] + Gui, + GuiAttached, + Tui, +} diff --git a/src/gui.rs b/src/ide/gui.rs similarity index 87% rename from src/gui.rs rename to src/ide/gui.rs index 9ca92c7..16ca946 100644 --- a/src/gui.rs +++ b/src/ide/gui.rs @@ -2,15 +2,16 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::AppContext; +pub mod colors; +pub mod filesystem; +pub mod icon_provider; + +use crate::ide::cli::app_context::AppContext; use anyhow::Result; use cxx_qt_lib::{QMapPair, QMapPair_QString_QVariant, QString, QVariant}; -pub mod colors; -pub mod filesystem; - pub fn run(app_context: AppContext) -> Result<()> { - libclide::trace!(target:"gui::run()", "Starting the GUI editor at {:?}", app_context.path); + crate::trace!(target:"gui::run()", "Starting the GUI editor at {:?}", app_context.path); use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl}; diff --git a/src/gui/colors.rs b/src/ide/gui/colors.rs similarity index 99% rename from src/gui/colors.rs rename to src/ide/gui/colors.rs index 8500527..a60f50c 100644 --- a/src/gui/colors.rs +++ b/src/ide/gui/colors.rs @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later +use crate::theme::colors::Colors; use cxx_qt_lib::QColor; -use libclide::theme::colors::Colors; #[cxx_qt::bridge] pub mod qobject { diff --git a/src/gui/filesystem.rs b/src/ide/gui/filesystem.rs similarity index 96% rename from src/gui/filesystem.rs rename to src/ide/gui/filesystem.rs index 1f58fd0..6daed5b 100644 --- a/src/gui/filesystem.rs +++ b/src/ide/gui/filesystem.rs @@ -76,7 +76,7 @@ impl qobject::FileSystem { let meta = fs::metadata(path.to_string()) .unwrap_or_else(|_| panic!("Failed to get file metadata {path:?}")); if !meta.is_file() { - libclide::warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file"); + crate::warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file"); return QString::default(); } let path_str = path.to_string(); @@ -141,6 +141,6 @@ impl qobject::FileSystem { } fn icon(self: std::pin::Pin<&mut Self>, path: &QString) -> QString { - QString::from(libclide::fs::icon(path.to_string().as_str()).to_string()) + QString::from(crate::fs::icon(path.to_string().as_str()).to_string()) } } diff --git a/src/tui.rs b/src/ide/tui.rs similarity index 81% rename from src/tui.rs rename to src/ide/tui.rs index 48a4979..1f16143 100644 --- a/src/tui.rs +++ b/src/ide/tui.rs @@ -2,20 +2,20 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -mod about; -mod app; -mod component; -mod editor; -mod editor_tab; -mod explorer; -mod logger; -mod menu_bar; +pub mod about; +pub mod app; +pub mod component; +pub mod editor; +pub mod editor_tab; +pub mod explorer; +pub mod logger; +pub mod menu_bar; -use crate::AppContext; +use crate::ide::cli::app_context::AppContext; +use crate::logging::Loggable; +use ::log::LevelFilter; use anyhow::{Context, Result}; -use log::LevelFilter; use ratatui::Terminal; -use libclide::log::Loggable; use ratatui::backend::CrosstermBackend; use ratatui::crossterm::event::{ DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, @@ -36,7 +36,7 @@ struct Tui { } pub fn run(app_context: AppContext) -> Result<()> { - libclide::trace!(target: "clide::tui::run", "Starting TUI"); + crate::trace!(target: "clide::tui::run", "Starting TUI"); Tui::new(app_context)?.start() } @@ -44,7 +44,7 @@ impl Tui { fn new(app_context: AppContext) -> Result { init_logger(LevelFilter::Trace)?; set_default_level(LevelFilter::Trace); - libclide::debug!("Logging initialized"); + crate::debug!("Logging initialized"); let mut dir = env::temp_dir(); dir.push("clide.log"); @@ -56,7 +56,7 @@ impl Tui { .output_file(false) .output_separator(':'); set_log_file(file_options); - libclide::debug!("Logging to file: {dir:?}"); + crate::debug!("Logging to file: {dir:?}"); Ok(Self { terminal: Terminal::new(CrosstermBackend::new(stdout()))?, @@ -65,7 +65,7 @@ impl Tui { } fn start(self) -> Result<()> { - libclide::info!("Starting the TUI editor at {:?}", self.root_path); + crate::info!("Starting the TUI editor at {:?}", self.root_path); ratatui::crossterm::execute!( stdout(), EnterAlternateScreen, @@ -82,7 +82,7 @@ impl Tui { } fn stop() -> Result<()> { - libclide::info!("Stopping the TUI editor"); + crate::info!("Stopping the TUI editor"); disable_raw_mode()?; ratatui::crossterm::execute!( stdout(), diff --git a/src/tui/about.rs b/src/ide/tui/about.rs similarity index 96% rename from src/tui/about.rs rename to src/ide/tui/about.rs index 3cbb20a..8ba4768 100644 --- a/src/tui/about.rs +++ b/src/ide/tui/about.rs @@ -2,18 +2,18 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use libclide::log::Loggable; +use crate::logging::Loggable; use ratatui::buffer::Buffer; use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph, Widget, Wrap}; -#[derive(Loggable)] +#[derive(Loggable, Default)] pub struct About {} impl About { pub fn new() -> Self { - // libclide::trace!("Building {}", Self::id()); + // crate::trace!("Building {}", Self::id()); Self {} } } diff --git a/src/tui/app.rs b/src/ide/tui/app.rs similarity index 92% rename from src/tui/app.rs rename to src/ide/tui/app.rs index ac0995d..04a4719 100644 --- a/src/tui/app.rs +++ b/src/ide/tui/app.rs @@ -2,14 +2,14 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::about::About; -use crate::tui::component::{Action, Component, Focus, FocusState, Visibility, VisibleState}; -use crate::tui::editor_tab::EditorTab; -use crate::tui::explorer::Explorer; -use crate::tui::logger::Logger; -use crate::tui::menu_bar::MenuBar; +use crate::ide::tui::about::About; +use crate::ide::tui::component::{Action, Component, Focus, FocusState, Visibility, VisibleState}; +use crate::ide::tui::editor_tab::EditorTab; +use crate::ide::tui::explorer::Explorer; +use crate::ide::tui::logger::Logger; +use crate::ide::tui::menu_bar::MenuBar; +use crate::logging::Loggable; use anyhow::{Context, Result}; -use libclide::log::Loggable; use ratatui::DefaultTerminal; use ratatui::buffer::Buffer; use ratatui::crossterm::event; @@ -42,7 +42,7 @@ pub struct App<'a> { impl<'a> App<'a> { pub fn new(root_path: PathBuf) -> Result { - libclide::trace!("Building {}", Self::ID); + crate::trace!("Building {}", Self::ID); let app = Self { editor_tab: EditorTab::new(), explorer: Explorer::new(&root_path)?, @@ -56,13 +56,13 @@ impl<'a> App<'a> { /// Logic that should be executed once on application startup. pub fn start(&mut self) -> Result<()> { - libclide::trace!("Starting App"); + crate::trace!("Starting App"); Ok(()) } pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> { self.start()?; - libclide::trace!("Entering App run loop"); + crate::trace!("Entering App run loop"); loop { terminal.draw(|f| { f.render_widget(&mut self, f.area()); @@ -88,7 +88,7 @@ impl<'a> App<'a> { Some(editor) => editor.component_state.help_text.clone(), None => { if !self.editor_tab.is_empty() { - libclide::error!("Failed to get Editor while drawing bottom status bar"); + crate::error!("Failed to get Editor while drawing bottom status bar"); } "Failed to get current Editor while getting widget help text".to_string() } @@ -112,26 +112,26 @@ impl<'a> App<'a> { } fn clear_focus(&mut self) { - libclide::info!("Clearing all widget focus"); + crate::info!("Clearing all widget focus"); self.explorer.component_state.set_focus(Focus::Inactive); self.explorer.component_state.set_focus(Focus::Inactive); self.logger.component_state.set_focus(Focus::Inactive); self.menu_bar.component_state.set_focus(Focus::Inactive); match self.editor_tab.current_editor_mut() { None => { - libclide::error!("Failed to get current Editor while clearing focus") + crate::error!("Failed to get current Editor while clearing focus") } Some(editor) => editor.component_state.set_focus(Focus::Inactive), } } fn change_focus(&mut self, focus: AppComponent) { - libclide::info!("Changing widget focus to {:?}", focus); + crate::info!("Changing widget focus to {:?}", focus); self.clear_focus(); match focus { AppComponent::Editor => match self.editor_tab.current_editor_mut() { None => { - libclide::error!("Failed to get current Editor while changing focus") + crate::error!("Failed to get current Editor while changing focus") } Some(editor) => editor.component_state.set_focus(Focus::Active), }, @@ -274,15 +274,13 @@ impl<'a> Component for App<'a> { Action::Quit | Action::Handled => Ok(action), Action::Save => match self.editor_tab.current_editor_mut() { None => { - libclide::error!( - "Failed to get current editor while handling App Action::Save" - ); + crate::error!("Failed to get current editor while handling App Action::Save"); Ok(Action::Noop) } Some(editor) => match editor.save() { Ok(_) => Ok(Action::Handled), Err(e) => { - libclide::error!("Failed to save editor contents: {e}"); + crate::error!("Failed to save editor contents: {e}"); Ok(Action::Noop) } }, @@ -301,14 +299,14 @@ impl<'a> Component for App<'a> { Err(_) => Ok(Action::Noop), }, Action::ReloadFile => { - libclide::trace!("Reloading file for current editor"); + crate::trace!("Reloading file for current editor"); if let Some(editor) = self.editor_tab.current_editor_mut() { editor .reload_contents() .map(|_| Action::Handled) .context("Failed to handle Action::ReloadFile") } else { - libclide::error!( + crate::error!( "Failed to get current editor while handling App Action::ReloadFile" ); Ok(Action::Noop) diff --git a/src/tui/component.rs b/src/ide/tui/component.rs similarity index 95% rename from src/tui/component.rs rename to src/ide/tui/component.rs index f4e8cb0..d875cbc 100644 --- a/src/tui/component.rs +++ b/src/ide/tui/component.rs @@ -4,11 +4,11 @@ #![allow(dead_code, unused_variables)] -use crate::tui::component::Focus::Inactive; +use crate::ide::tui::component::Focus::Inactive; +use crate::logging::Loggable; +use crate::theme::colors::Colors; use Focus::Active; use anyhow::Result; -use libclide::theme::colors::Colors; -use libclide::log::Loggable; use ratatui::crossterm::event::{Event, KeyEvent, MouseEvent}; use ratatui::style::Color; @@ -75,7 +75,7 @@ impl ComponentState { } fn new() -> Self { - libclide::trace!(target:Self::id(), "Building {}", Self::id()); + crate::trace!(target:Self::id(), "Building {}", Self::id()); Self { focus: Active, vis: Visibility::Visible, diff --git a/src/tui/editor.rs b/src/ide/tui/editor.rs similarity index 89% rename from src/tui/editor.rs rename to src/ide/tui/editor.rs index 26c4663..e3caf24 100644 --- a/src/tui/editor.rs +++ b/src/ide/tui/editor.rs @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState}; +use crate::ide::tui::component::{Action, Component, ComponentState, Focus, FocusState}; +use crate::logging::Loggable; use anyhow::{Context, Result, bail}; use edtui::{ EditorEventHandler, EditorState, EditorTheme, EditorView, LineNumbers, Lines, SyntaxHighlighter, }; -use libclide::log::Loggable; use ratatui::buffer::Buffer; use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; use ratatui::layout::{Alignment, Rect}; @@ -27,7 +27,7 @@ pub struct Editor { impl Editor { pub fn new(path: &std::path::Path) -> Self { - libclide::trace!("Building {}", ::ID); + crate::trace!("Building {}", ::ID); Editor { state: EditorState::default(), event_handler: EditorEventHandler::default(), @@ -41,10 +41,10 @@ impl Editor { } pub fn reload_contents(&mut self) -> Result<()> { - libclide::trace!("Reloading editor file contents {:?}", self.file_path); + crate::trace!("Reloading editor file contents {:?}", self.file_path); match self.file_path.clone() { None => { - libclide::error!("Failed to reload editor contents with None file_path"); + crate::error!("Failed to reload editor contents with None file_path"); bail!("Failed to reload editor contents with None file_path") } Some(path) => self.set_contents(&path), @@ -52,7 +52,7 @@ impl Editor { } pub fn set_contents(&mut self, path: &std::path::Path) -> Result<()> { - libclide::trace!("Setting Editor contents from path {:?}", path); + crate::trace!("Setting Editor contents from path {:?}", path); if let Ok(contents) = std::fs::read_to_string(path) { let lines: Vec<_> = contents .lines() @@ -68,10 +68,10 @@ impl Editor { pub fn save(&self) -> Result<()> { if let Some(path) = &self.file_path { - libclide::trace!("Saving Editor contents {:?}", path); + crate::trace!("Saving Editor contents {:?}", path); return std::fs::write(path, self.state.lines.to_string()).map_err(|e| e.into()); }; - libclide::error!("Failed saving Editor contents; file_path was None"); + crate::error!("Failed saving Editor contents; file_path was None"); bail!("File not saved. No file path set.") } } diff --git a/src/tui/editor_tab.rs b/src/ide/tui/editor_tab.rs similarity index 88% rename from src/tui/editor_tab.rs rename to src/ide/tui/editor_tab.rs index 77f2e40..1069fed 100644 --- a/src/tui/editor_tab.rs +++ b/src/ide/tui/editor_tab.rs @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::component::{Action, Component, Focus, FocusState}; -use crate::tui::editor::Editor; +use crate::ide::tui::component::{Action, Component, Focus, FocusState}; +use crate::ide::tui::editor::Editor; +use crate::logging::Loggable; use anyhow::{Context, Result, anyhow}; -use libclide::log::Loggable; use ratatui::buffer::Buffer; use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; use ratatui::layout::Rect; @@ -16,7 +16,7 @@ use std::collections::HashMap; // Render the tabs with keys as titles // Tab keys can be file names. // Render the editor using the key as a reference for lookup -#[derive(Loggable)] +#[derive(Loggable, Default)] pub struct EditorTab { pub(crate) editors: HashMap, tab_order: Vec, @@ -25,7 +25,7 @@ pub struct EditorTab { impl EditorTab { pub fn new() -> Self { - libclide::trace!("Building {}", ::ID); + crate::trace!("Building {}", ::ID); Self { editors: HashMap::new(), tab_order: Vec::new(), @@ -35,7 +35,7 @@ impl EditorTab { pub fn next_editor(&mut self) { let next = (self.current_editor + 1) % self.tab_order.len(); - libclide::trace!( + crate::trace!( "Moving from {} to next editor tab at {}", self.current_editor, next @@ -49,7 +49,7 @@ impl EditorTab { .current_editor .checked_sub(1) .unwrap_or(self.tab_order.len() - 1); - libclide::trace!( + crate::trace!( "Moving from {} to previous editor tab at {}", self.current_editor, prev @@ -62,7 +62,7 @@ impl EditorTab { match self.tab_order.get(index) { None => { if !self.tab_order.is_empty() { - libclide::error!("Failed to get editor tab key with invalid index {index}"); + crate::error!("Failed to get editor tab key with invalid index {index}"); } None } @@ -80,7 +80,7 @@ impl EditorTab { } pub fn set_current_tab_focus(&mut self, focus: Focus) { - libclide::trace!( + crate::trace!( "Setting current tab {} focus to {:?}", self.current_editor, focus @@ -89,10 +89,10 @@ impl EditorTab { } pub fn set_tab_focus(&mut self, focus: Focus, index: usize) { - libclide::trace!("Setting tab {} focus to {:?}", index, focus); + crate::trace!("Setting tab {} focus to {:?}", index, focus); if focus == Focus::Active && index != self.current_editor { // If we are setting another tab to active, disable the current one. - libclide::trace!( + crate::trace!( "New tab {} focus set to Active; Setting current tab {} to Inactive", index, self.current_editor @@ -101,11 +101,11 @@ impl EditorTab { } match self.get_editor_key(index) { None => { - libclide::error!("Failed setting tab focus for invalid key {index}"); + crate::error!("Failed setting tab focus for invalid key {index}"); } Some(key) => match self.editors.get_mut(&key) { None => { - libclide::error!( + crate::error!( "Failed to update tab focus at index {} with invalid key: {}", self.current_editor, self.tab_order[self.current_editor] @@ -117,12 +117,12 @@ impl EditorTab { } pub fn open_tab(&mut self, path: &std::path::Path) -> Result<()> { - libclide::trace!("Opening new EditorTab with path {:?}", path); + crate::trace!("Opening new EditorTab with path {:?}", path); if self .editors .contains_key(&path.to_string_lossy().to_string()) { - libclide::warn!("EditorTab already opened with this file"); + crate::warn!("EditorTab already opened with this file"); return Ok(()); } @@ -147,12 +147,12 @@ impl EditorTab { .to_owned(); match self.editors.remove(&key) { None => { - libclide::error!("Failed to remove editor tab {key} with invalid index {index}") + crate::error!("Failed to remove editor tab {key} with invalid index {index}") } Some(_) => { self.prev_editor(); self.tab_order.remove(index); - libclide::info!("Closed editor tab {key} at index {index}") + crate::info!("Closed editor tab {key} at index {index}") } } Ok(()) diff --git a/src/tui/explorer.rs b/src/ide/tui/explorer.rs similarity index 97% rename from src/tui/explorer.rs rename to src/ide/tui/explorer.rs index 8f79ade..081f6d7 100644 --- a/src/tui/explorer.rs +++ b/src/ide/tui/explorer.rs @@ -2,10 +2,10 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState}; +use crate::fs::entry_meta::EntryMeta; +use crate::ide::tui::component::{Action, Component, ComponentState, Focus, FocusState}; +use crate::logging::Loggable; use anyhow::{Context, Result, bail}; -use libclide::fs::entry_meta::EntryMeta; -use libclide::log::Loggable; use ratatui::buffer::Buffer; use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, MouseEvent, MouseEventKind}; use ratatui::layout::{Alignment, Position, Rect}; @@ -26,7 +26,7 @@ pub struct Explorer<'a> { impl<'a> Explorer<'a> { pub fn new(path: &PathBuf) -> Result { - libclide::trace!("Building {}", ::ID); + crate::trace!("Building {}", ::ID); let explorer = Explorer { root_path: EntryMeta::new(path)?, tree_items: Self::build_tree_from_path(path)?, diff --git a/src/tui/logger.rs b/src/ide/tui/logger.rs similarity index 92% rename from src/tui/logger.rs rename to src/ide/tui/logger.rs index cca2d60..bc4ac16 100644 --- a/src/tui/logger.rs +++ b/src/ide/tui/logger.rs @@ -2,8 +2,8 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState}; -use libclide::log::Loggable; +use crate::ide::tui::component::{Action, Component, ComponentState, Focus, FocusState}; +use crate::logging::Loggable; use log::LevelFilter; use ratatui::buffer::Buffer; use ratatui::crossterm::event::{Event, KeyCode, KeyEvent}; @@ -12,9 +12,9 @@ use ratatui::style::{Color, Style}; use ratatui::widgets::Widget; use tui_logger::{TuiLoggerLevelOutput, TuiLoggerSmartWidget, TuiWidgetEvent, TuiWidgetState}; -/// Any log written as info!(target:self.id(), "message") will work with this logger. +/// Any logging written as info!(target:self.id(), "message") will work with this logger. /// The logger is bound to info!, debug!, error!, trace! macros within Tui::new(). -#[derive(Loggable)] +#[derive(Loggable, Default)] pub struct Logger { state: TuiWidgetState, pub(crate) component_state: ComponentState, @@ -22,7 +22,7 @@ pub struct Logger { impl Logger { pub fn new() -> Self { - libclide::trace!("Building {}", ::ID); + crate::trace!("Building {}", ::ID); let state = TuiWidgetState::new(); state.transition(TuiWidgetEvent::HideKey); Self { diff --git a/src/tui/menu_bar.rs b/src/ide/tui/menu_bar.rs similarity index 96% rename from src/tui/menu_bar.rs rename to src/ide/tui/menu_bar.rs index 6ca1f0e..86c9d78 100644 --- a/src/tui/menu_bar.rs +++ b/src/ide/tui/menu_bar.rs @@ -2,12 +2,12 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use crate::tui::component::{Action, Component, ComponentState, FocusState}; -use crate::tui::menu_bar::MenuBarItemOption::{ +use crate::ide::tui::component::{Action, Component, ComponentState, FocusState}; +use crate::ide::tui::menu_bar::MenuBarItemOption::{ About, CloseTab, Exit, Reload, Save, ShowHideExplorer, ShowHideLogger, }; +use crate::logging::Loggable; use anyhow::Context; -use libclide::log::Loggable; use ratatui::buffer::Buffer; use ratatui::crossterm::event::{KeyCode, KeyEvent}; use ratatui::layout::Rect; @@ -18,8 +18,9 @@ use ratatui::widgets::{ }; use strum::{EnumIter, FromRepr, IntoEnumIterator}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter, Default)] enum MenuBarItem { + #[default] File, View, Help, @@ -80,7 +81,7 @@ impl MenuBarItem { } } -#[derive(Debug, Loggable)] +#[derive(Debug, Loggable, Default)] pub struct MenuBar { selected: MenuBarItem, opened: Option, @@ -91,7 +92,7 @@ pub struct MenuBar { impl MenuBar { const DEFAULT_HELP: &str = "(←/h)/(→/l): Select option | Enter: Choose selection"; pub fn new() -> Self { - libclide::trace!("Building"); + crate::trace!("Building"); Self { selected: MenuBarItem::File, opened: None, diff --git a/libclide/src/lib.rs b/src/lib.rs similarity index 84% rename from libclide/src/lib.rs rename to src/lib.rs index 400e3b7..5d25725 100644 --- a/libclide/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,6 @@ // SPDX-License-Identifier: GNU General Public License v3.0 or later pub mod fs; -pub mod log; +pub mod ide; +pub mod logging; pub mod theme; diff --git a/libclide/src/log.rs b/src/logging.rs similarity index 100% rename from libclide/src/log.rs rename to src/logging.rs diff --git a/libclide/src/log/macros.rs b/src/logging/macros.rs similarity index 64% rename from libclide/src/log/macros.rs rename to src/logging/macros.rs index ca174f8..cc9c8e2 100644 --- a/libclide/src/log/macros.rs +++ b/src/logging/macros.rs @@ -11,13 +11,14 @@ //! //! The Loggable trait can be implemented to automatically associate log messages with a struct. //! ``` -//! use libclide::log::Loggable; +//! use clide::logging; +//! use libclide_macros::Loggable; //! //! #[derive(Loggable)] //! struct MyStruct; //! impl MyStruct { //! fn my_method(&self) { -//! libclide::info!("This log message will use target ::ID, which is 'MyStruct'"); +//! clide::info!("This log message will use target Self::ID, which is 'MyStruct'"); //! } //! } //! ``` @@ -25,51 +26,51 @@ //! If the struct does not derive or implement Loggable, the target variant of the log macros must //! be used instead. //! ``` -//! libclide::info!(target: "CustomTarget", "This log message will have the target 'CustomTarget'"); +//! clide::info!(target: "CustomTarget", "This log message will have the target 'CustomTarget'"); //! ``` //! #[macro_export] macro_rules! info { (target: $target:expr, $($arg:tt)+) => ({ - log::info!(target: $target, $($arg)+) + ::log::info!(target: $target, $($arg)+) }); - ($($arg:tt)+) => (log::info!(target: Self::ID, $($arg)+)) + ($($arg:tt)+) => (::log::info!(target: Self::ID, $($arg)+)) } #[macro_export] macro_rules! debug { (target: $target:expr, $($arg:tt)+) => ({ - log::debug!(target: $target, $($arg)+) + ::log::debug!(target: $target, $($arg)+) }); - ($($arg:tt)+) => (log::debug!(target: Self::ID, $($arg)+)) + ($($arg:tt)+) => (::log::debug!(target: Self::ID, $($arg)+)) } #[macro_export] macro_rules! warn { (target: $target:expr, $($arg:tt)+) => ({ - log::warn!(target: $target, $($arg)+) + ::log::warn!(target: $target, $($arg)+) }); - ($($arg:tt)+) => (log::warn!(target: Self::ID, $($arg)+)) + ($($arg:tt)+) => (::log::warn!(target: Self::ID, $($arg)+)) } #[macro_export] macro_rules! error { (target: $target:expr, $($arg:tt)+) => ({ - log::error!(target: $target, $($arg)+) + ::log::error!(target: $target, $($arg)+) }); - ($($arg:tt)+) => (log::error!(target: Self::ID, $($arg)+)) + ($($arg:tt)+) => (::log::error!(target: Self::ID, $($arg)+)) } #[macro_export] macro_rules! trace { (target: $target:expr, $($arg:tt)+) => ({ - log::trace!(target: $target, $($arg)+) + ::log::trace!(target: $target, $($arg)+) }); - ($($arg:tt)+) => (log::trace!(target: Self::ID, $($arg)+)) + ($($arg:tt)+) => (::log::trace!(target: Self::ID, $($arg)+)) } diff --git a/src/main.rs b/src/main.rs index e8c3851..0711527 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,84 +2,20 @@ // // SPDX-License-Identifier: GNU General Public License v3.0 or later -use anyhow::{Context, Result, anyhow}; +use anyhow::{Context, Result}; use clap::Parser; +use clide::ide::cli::Cli; +use clide::ide::cli::app_context::{AppContext, RunMode}; use std::process::{Command, Stdio}; -pub mod gui; -pub mod tui; -/// Extendable command-line driven development environment written in Rust using the Qt UI framework. -/// If no flags are provided, the GUI editor is launched in a separate process. -/// If no path is provided, the current directory is used. -#[derive(Parser, Debug)] -#[structopt(name = "clide", verbatim_doc_comment)] -struct Cli { - /// The root directory for the project to open with the clide editor. - #[arg(value_parser = clap::value_parser!(std::path::PathBuf))] - pub path: Option, - - /// Run clide in headless mode. - #[arg(value_name = "tui", short, long)] - pub tui: bool, - - /// Run the clide GUI in the current process, blocking the terminal and showing all output streams. - #[arg(value_name = "gui", short, long)] - pub gui: bool, -} - -impl Cli { - fn run_mode(&self) -> Result { - let mut modes = Vec::new(); - self.tui.then(|| modes.push(RunMode::Tui)); - self.gui.then(|| modes.push(RunMode::GuiAttached)); - match &modes[..] { - [] => Ok(RunMode::Gui), - [mode] => Ok(*mode), - multiple => Err(anyhow!( - "More than one run mode found {multiple:?} please select one." - )), - } - } -} - -pub struct AppContext { - pub path: std::path::PathBuf, - pub run_mode: RunMode, -} - -impl AppContext { - fn new(cli: Cli) -> Result { - let path = match &cli.path { - // If the CLI was provided a directory, convert it to absolute. - Some(path) => std::path::absolute(path)?, - // If no path was provided, use the current directory. - None => std::env::current_dir().context("Failed to obtain current directory")?, - }; - libclide::info!(target:"main()", "Root path detected: {path:?}"); - - Ok(Self { - path, - run_mode: cli.run_mode()?, - }) - } -} - -#[derive(Copy, Clone, Debug, Default)] -pub enum RunMode { - #[default] - Gui, - GuiAttached, - Tui, -} - fn main() -> Result<()> { let args = Cli::parse(); let app_context = AppContext::new(args)?; match app_context.run_mode { - RunMode::GuiAttached => gui::run(app_context), - RunMode::Tui => tui::run(app_context), + RunMode::GuiAttached => clide::ide::gui::run(app_context), + RunMode::Tui => clide::ide::tui::run(app_context), RunMode::Gui => { - libclide::trace!(target:"main()", "Starting GUI in a new process"); + clide::trace!(target:"main()", "Starting GUI in a new process"); Command::new(std::env::current_exe()?) .args(["--gui", app_context.path.to_str().unwrap()]) .stdout(Stdio::null()) diff --git a/libclide/src/theme.rs b/src/theme.rs similarity index 100% rename from libclide/src/theme.rs rename to src/theme.rs diff --git a/libclide/src/theme/colors.rs b/src/theme/colors.rs similarity index 100% rename from libclide/src/theme/colors.rs rename to src/theme/colors.rs