This commit is contained in:
Shaun Reed 2026-01-28 20:34:00 -05:00
parent 00f9075d0f
commit a431d02a0e
12 changed files with 188 additions and 240 deletions

View File

@ -29,7 +29,8 @@ cd clide
cargo install --path .
```
After installation `clide` can be used directly
After installation `clide` can be used directly.
A path can optionally be provided to open a specific directory with `clide /path/to/project`.
```bash
clide --help

View File

@ -1,3 +1,4 @@
use crate::AppContext;
use anyhow::Result;
use cxx_qt_lib::QString;
use log::trace;
@ -5,8 +6,8 @@ use log::trace;
pub mod colors;
pub mod filesystem;
pub fn run(root_path: std::path::PathBuf) -> Result<()> {
trace!(target:"gui::run()", "Starting the GUI editor at {root_path:?}");
pub fn run(app_context: AppContext) -> Result<()> {
trace!(target:"gui::run()", "Starting the GUI editor at {:?}", app_context.path);
use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl};

View File

@ -1,5 +1,4 @@
use crate::tui::Tui;
use anyhow::{Context, Result};
use anyhow::{Context, Result, anyhow};
use clap::Parser;
use log::{info, trace};
use std::process::{Command, Stdio};
@ -25,41 +24,67 @@ struct Cli {
pub gui: bool,
}
fn main() -> Result<()> {
let args = Cli::parse();
impl Cli {
fn run_mode(&self) -> Result<RunMode> {
let mut modes = Vec::new();
self.tui.then(|| modes.push(RunMode::Tui));
self.gui.then(|| modes.push(RunMode::GuiAttached));
match &modes[..] {
[] => Ok(RunMode::Tui),
[mode] => Ok(*mode),
multiple => Err(anyhow!(
"More than one run mode found {multiple:?} please select one."
)),
}
}
}
let root_path = match args.path {
pub struct AppContext {
pub path: std::path::PathBuf,
pub run_mode: RunMode,
}
impl AppContext {
fn new(cli: Cli) -> Result<Self> {
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().unwrap_or(
// If we can't find the CWD, attempt to open the home directory.
dirs::home_dir().context("Failed to obtain home directory")?,
),
None => std::env::current_dir().context("Failed to obtain current directory")?,
};
info!(target:"main()", "Root path detected: {root_path:?}");
info!(target:"main()", "Root path detected: {path:?}");
match args.gui {
true => {
trace!(target:"main()", "Starting GUI");
gui::run(root_path)
Ok(Self {
path,
run_mode: cli.run_mode()?,
})
}
false => match args.tui {
// Open the TUI editor if requested, otherwise use the QML GUI by default.
true => {
trace!(target:"main()", "Starting TUI");
Ok(Tui::new(root_path)?.start()?)
}
false => {
#[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::Gui => {
trace!(target:"main()", "Starting GUI in a new process");
Command::new(std::env::current_exe()?)
.args(&["--gui", root_path.to_str().unwrap()])
.args(&["--gui", app_context.path.to_str().unwrap()])
.stdout(Stdio::null())
.stderr(Stdio::null())
.stdin(Stdio::null())
.spawn()?;
Ok(())
}
},
.spawn()
.context("Failed to start GUI")
.map(|_| ())
}
}
}

View File

@ -22,22 +22,26 @@ use std::io::{Stdout, stdout};
use tui_logger::{
TuiLoggerFile, TuiLoggerLevelOutput, init_logger, set_default_level, set_log_file,
};
use crate::AppContext;
pub struct Tui {
struct Tui {
terminal: Terminal<CrosstermBackend<Stdout>>,
root_path: std::path::PathBuf,
}
impl Tui {
pub fn id() -> &'static str {
"Tui"
pub fn run(app_context: AppContext) -> Result<()> {
trace!(target:Tui::ID, "Starting TUI");
Tui::new(app_context)?.start()
}
pub fn new(root_path: std::path::PathBuf) -> Result<Self> {
trace!(target:Self::id(), "Building {}", Self::id());
impl Tui {
pub const ID: &str = "Tui";
fn new(app_context: AppContext) -> Result<Self> {
trace!(target:Self::ID, "Building {}", Self::ID);
init_logger(LevelFilter::Trace)?;
set_default_level(LevelFilter::Trace);
debug!(target:Self::id(), "Logging initialized");
debug!(target:Self::ID, "Logging initialized");
let mut dir = env::temp_dir();
dir.push("clide.log");
@ -49,16 +53,16 @@ impl Tui {
.output_file(false)
.output_separator(':');
set_log_file(file_options);
debug!(target:Self::id(), "Logging to file: {dir:?}");
debug!(target:Self::ID, "Logging to file: {dir:?}");
Ok(Self {
terminal: Terminal::new(CrosstermBackend::new(stdout()))?,
root_path,
root_path: app_context.path,
})
}
pub fn start(self) -> Result<()> {
info!(target:Self::id(), "Starting the TUI editor at {:?}", self.root_path);
fn start(self) -> Result<()> {
info!(target:Self::ID, "Starting the TUI editor at {:?}", self.root_path);
ratatui::crossterm::execute!(
stdout(),
EnterAlternateScreen,
@ -75,7 +79,7 @@ impl Tui {
}
fn stop() -> Result<()> {
info!(target:Self::id(), "Stopping the TUI editor");
info!(target:Self::ID, "Stopping the TUI editor");
disable_raw_mode()?;
ratatui::crossterm::execute!(
stdout(),

View File

@ -1,6 +1,6 @@
use log::info;
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Clear, Padding, Paragraph, Widget, Wrap};
@ -8,9 +8,7 @@ pub struct About {}
impl About {
#[allow(unused)]
pub fn id() -> &'static str {
"About"
}
pub const ID: &str = "About";
pub fn new() -> Self {
// trace!(target:Self::id(), "Building {}", Self::id());
@ -23,54 +21,51 @@ impl Widget for About {
where
Self: Sized,
{
Clear::default().render(area, buf);
// Split main area
let kilroy_rect = Rect {
x: area.x,
y: area.y + 8,
width: 85,
height: 35,
};
info!(target: About::ID, "Created rect: {kilroy_rect:?}");
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Fill(2), // image column
Constraint::Fill(1), // image column
Constraint::Fill(2), // text column
Constraint::Fill(1), // Image Layout
Constraint::Fill(2), // Description
])
.split(area);
let top_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1),
Constraint::Fill(3),
Constraint::Fill(1),
])
.split(chunks[1]);
let bottom_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Fill(1),
Constraint::Fill(3),
Constraint::Fill(1),
])
.split(chunks[2]);
// ---------- IMAGE ----------
let kilroy_art = [
let kilroy = [
" * ",
" |.===. ",
" {}o o{} ",
"-----------------------ooO--(_)--Ooo---------------------------",
"# #",
"# CLIDE WAS HERE #",
"# #",
"# https://git.shaunreed.com/shaunred/clide #",
"# https://shaunreed.com/shaunred/clide #",
"# #",
" CLIDE WAS HERE ",
];
let kilroy_lines: Vec<Line> = kilroy_art
let kilroy_lines: Vec<Line> = kilroy.iter().map(|l| Line::from(Span::raw(*l))).collect();
let about_text = [
"Clide",
"",
"Author: Shaun Reed",
"Email: shaunrd0@gmail.com",
"URL: https://git.shaunreed.com/shaunrd0/clide",
"Blog: https://shaunreed.com",
"",
"Description:",
concat!(
"CLIDE is an extendable command-line driven development environment written in Rust",
" using the Qt UI framework that supports both full and headless Linux environments.",
" The GUI is written in QML compiled through Rust using the cxx-qt crate, while the",
" TUI was implemented using the ratatui crate.",
),
];
let about_lines: Vec<Line> = about_text
.iter()
.map(|l| Line::from(Span::raw(*l)))
.collect();
Clear::default().render(area, buf);
Paragraph::new(kilroy_lines)
.block(
Block::default()
@ -79,60 +74,15 @@ impl Widget for About {
)
.wrap(Wrap { trim: false })
.centered()
.render(top_chunks[1], buf);
// ---------- TEXT ----------
let about_text = vec![
Line::from(vec![Span::styled(
"clide\n",
Style::default().add_modifier(Modifier::BOLD),
)])
.centered(),
Line::from(""),
Line::from(vec![
Span::styled("Author: ", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("Shaun Reed"),
])
.left_aligned(),
Line::from(vec![
Span::styled("Email: ", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("shaunrd0@gmail.com"),
])
.left_aligned(),
Line::from(vec![
Span::styled("URL: ", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("https://git.shaunreed.com/shaunrd0/clide"),
])
.left_aligned(),
Line::from(vec![
Span::styled("Blog: ", Style::default().add_modifier(Modifier::BOLD)),
Span::raw("https://shaunreed.com"),
])
.left_aligned(),
Line::from(""),
Line::from(vec![Span::styled(
"Description\n",
Style::default().add_modifier(Modifier::BOLD),
)])
.left_aligned(),
Line::from(concat!(
"CLIDE is an extendable command-line driven development environment written in Rust using the Qt UI framework that supports both full and headless Linux environments. ",
"The GUI is written in QML compiled through Rust using the cxx-qt crate, while the TUI was implemented using the ratatui crate. ",
))
.style(Style::default())
.left_aligned(),
];
Block::bordered().render(area, buf);
let paragraph = Paragraph::new(about_text)
.render(kilroy_rect, buf);
Paragraph::new(about_lines)
.block(
Block::default()
.title("About")
.borders(Borders::ALL)
.padding(Padding::top(0)),
)
.wrap(Wrap { trim: true });
paragraph.render(bottom_chunks[1], buf);
.wrap(Wrap { trim: false })
.render(chunks[1], buf);
}
}

View File

@ -29,7 +29,7 @@ pub enum AppComponent {
}
pub struct App<'a> {
editor_tabs: EditorTab,
editor_tab: EditorTab,
explorer: Explorer<'a>,
logger: Logger,
menu_bar: MenuBar,
@ -38,14 +38,12 @@ pub struct App<'a> {
}
impl<'a> App<'a> {
pub fn id() -> &'static str {
"App"
}
pub const ID: &'static str = "App";
pub fn new(root_path: PathBuf) -> Result<Self> {
trace!(target:Self::id(), "Building {}", Self::id());
trace!(target:Self::ID, "Building {}", Self::ID);
let app = Self {
editor_tabs: EditorTab::new(None),
editor_tab: EditorTab::new(None),
explorer: Explorer::new(&root_path)?,
logger: Logger::new(),
menu_bar: MenuBar::new(),
@ -57,13 +55,13 @@ impl<'a> App<'a> {
/// Logic that should be executed once on application startup.
pub fn start(&mut self) -> Result<()> {
trace!(target:Self::id(), "Starting App");
trace!(target:Self::ID, "Starting App");
Ok(())
}
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
self.start()?;
trace!(target:Self::id(), "Entering App run loop");
trace!(target:Self::ID, "Entering App run loop");
loop {
terminal.draw(|f| {
f.render_widget(&mut self, f.area());
@ -85,11 +83,11 @@ impl<'a> App<'a> {
fn draw_bottom_status(&self, area: Rect, buf: &mut Buffer) {
// Determine help text from the most recently focused component.
let help = match self.last_active {
AppEditor => match self.editor_tabs.current_editor() {
AppEditor => match self.editor_tab.current_editor() {
Some(editor) => editor.component_state.help_text.clone(),
None => {
if !self.editor_tabs.is_empty() {
error!(target:Self::id(), "Failed to get Editor while drawing bottom status bar");
if !self.editor_tab.is_empty() {
error!(target:Self::ID, "Failed to get Editor while drawing bottom status bar");
}
"Failed to get current Editor while getting widget help text".to_string()
}
@ -113,26 +111,26 @@ impl<'a> App<'a> {
}
fn clear_focus(&mut self) {
info!(target:Self::id(), "Clearing all widget focus");
info!(target:Self::ID, "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_tabs.current_editor_mut() {
match self.editor_tab.current_editor_mut() {
None => {
error!(target:Self::id(), "Failed to get current Editor while clearing focus")
error!(target:Self::ID, "Failed to get current Editor while clearing focus")
}
Some(editor) => editor.component_state.set_focus(Focus::Inactive),
}
}
fn change_focus(&mut self, focus: AppComponent) {
info!(target:Self::id(), "Changing widget focus to {:?}", focus);
info!(target:Self::ID, "Changing widget focus to {:?}", focus);
self.clear_focus();
match focus {
AppEditor => match self.editor_tabs.current_editor_mut() {
AppEditor => match self.editor_tab.current_editor_mut() {
None => {
error!(target:Self::id(), "Failed to get current Editor while changing focus")
error!(target:Self::ID, "Failed to get current Editor while changing focus")
}
Some(editor) => editor.component_state.set_focus(Focus::Active),
},
@ -142,32 +140,6 @@ impl<'a> App<'a> {
}
self.last_active = focus;
}
/// Refresh the contents of the editor to match the selected TreeItem in the file Explorer.
/// If the selected item is not a file, this does nothing.
#[allow(unused)]
fn refresh_editor_contents(&mut self) -> Result<()> {
// TODO: This may be useful for a preview mode of the selected file prior to opening a tab.
// Use the currently selected TreeItem or get an absolute path to this source file.
// let selected_pathbuf = match self.explorer.selected() {
// Ok(path) => PathBuf::from(path),
// Err(_) => PathBuf::from(std::path::absolute(file!())?.to_string_lossy().to_string()),
// };
// match self.editor_tabs.current_editor_mut() {
// None => bail!("Failed to get current Editor while refreshing editor contents"),
// Some(editor) => {
// let current_file_path = editor
// .file_path
// .clone()
// .context("Failed to get Editor current file_path")?;
// if selected_pathbuf == current_file_path || !selected_pathbuf.is_file() {
// return Ok(());
// }
// editor.set_contents(&selected_pathbuf)
// }
// }
Ok(())
}
}
impl<'a> Widget for &mut App<'a> {
@ -225,7 +197,7 @@ impl<'a> Widget for &mut App<'a> {
Constraint::Fill(1), // Editor contents.
])
.split(horizontal[1]);
self.editor_tabs
self.editor_tab
.render(editor_layout[0], editor_layout[1], buf);
self.explorer.render(horizontal[0], buf);
}
@ -237,7 +209,7 @@ impl<'a> Widget for &mut App<'a> {
Constraint::Fill(1), // Editor contents.
])
.split(horizontal[0]);
self.editor_tabs
self.editor_tab
.render(editor_layout[0], editor_layout[1], buf);
}
}
@ -258,7 +230,7 @@ impl<'a> Widget for &mut App<'a> {
}
if self.about {
let about_area = area.centered(Constraint::Percentage(50), Constraint::Percentage(45));
let about_area = area.centered(Constraint::Percentage(40), Constraint::Percentage(60));
About::new().render(about_area, buf);
}
}
@ -279,7 +251,7 @@ impl<'a> Component for App<'a> {
}
// Handle events for all components.
let action = match self.last_active {
AppEditor => self.editor_tabs.handle_event(event.clone())?,
AppEditor => self.editor_tab.handle_event(event.clone())?,
AppExplorer => self.explorer.handle_event(event.clone())?,
AppLogger => self.logger.handle_event(event.clone())?,
AppMenuBar => self.menu_bar.handle_event(event.clone())?,
@ -288,7 +260,7 @@ impl<'a> Component for App<'a> {
// Components should always handle mouse events for click interaction.
if let Some(mouse) = event.as_mouse_event() {
if mouse.kind == MouseEventKind::Down(MouseButton::Left) {
if let Some(editor) = self.editor_tabs.current_editor_mut() {
if let Some(editor) = self.editor_tab.current_editor_mut() {
editor.handle_mouse_events(mouse)?;
}
self.explorer.handle_mouse_events(mouse)?;
@ -299,15 +271,15 @@ impl<'a> Component for App<'a> {
// Handle actions returned from widgets that may need context on other widgets or app state.
match action {
Action::Quit | Action::Handled => Ok(action),
Action::Save => match self.editor_tabs.current_editor_mut() {
Action::Save => match self.editor_tab.current_editor_mut() {
None => {
error!(target:Self::id(), "Failed to get current editor while handling App Action::Save");
error!(target:Self::ID, "Failed to get current editor while handling App Action::Save");
Ok(Action::Noop)
}
Some(editor) => match editor.save() {
Ok(_) => Ok(Action::Handled),
Err(e) => {
error!(target:Self::id(), "Failed to save editor contents: {e}");
error!(target:Self::ID, "Failed to save editor contents: {e}");
Ok(Action::Noop)
}
},
@ -315,34 +287,34 @@ impl<'a> Component for App<'a> {
Action::OpenTab => {
if let Ok(path) = self.explorer.selected() {
let path_buf = PathBuf::from(path);
self.editor_tabs.open_tab(&path_buf)?;
self.editor_tab.open_tab(&path_buf)?;
Ok(Action::Handled)
} else {
Ok(Action::Noop)
}
}
Action::CloseTab => match self.editor_tabs.close_current_tab() {
Action::CloseTab => match self.editor_tab.close_current_tab() {
Ok(_) => Ok(Action::Handled),
Err(_) => Ok(Action::Noop),
},
Action::ReloadFile => {
trace!(target:Self::id(), "Reloading file for current editor");
if let Some(editor) = self.editor_tabs.current_editor_mut() {
trace!(target:Self::ID, "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 {
error!(target:Self::id(), "Failed to get current editor while handling App Action::ReloadFile");
error!(target:Self::ID, "Failed to get current editor while handling App Action::ReloadFile");
Ok(Action::Noop)
}
}
Action::ShowHideLogger => {
self.logger.component_state.togget_visible();
self.logger.component_state.toggle_visible();
Ok(Action::Handled)
}
Action::ShowHideExplorer => {
self.explorer.component_state.togget_visible();
self.explorer.component_state.toggle_visible();
Ok(Action::Handled)
}
Action::ShowHideAbout => {

View File

@ -111,7 +111,7 @@ impl FocusState for ComponentState {
fn with_focus(self, focus: Focus) -> Self {
Self {
focus,
vis: Visibility::Visible,
vis: self.vis,
help_text: self.help_text,
}
}
@ -142,7 +142,7 @@ pub enum Visibility {
pub trait VisibleState {
fn with_visible(self, vis: Visibility) -> Self;
fn set_visible(&mut self, vis: Visibility);
fn togget_visible(&mut self);
fn toggle_visible(&mut self);
}
impl VisibleState for ComponentState {
@ -158,7 +158,7 @@ impl VisibleState for ComponentState {
self.vis = vis;
}
fn togget_visible(&mut self) {
fn toggle_visible(&mut self) {
match self.vis {
Visibility::Visible => self.set_visible(Visibility::Hidden),
Visibility::Hidden => self.set_visible(Visibility::Visible),

View File

@ -20,12 +20,10 @@ pub struct Editor {
}
impl Editor {
pub fn id() -> &'static str {
"Editor"
}
pub const ID: &str = "Editor";
pub fn new(path: &std::path::PathBuf) -> Self {
trace!(target:Self::id(), "Building {}", Self::id());
trace!(target:Self::ID, "Building {}", Self::ID);
Editor {
state: EditorState::default(),
event_handler: EditorEventHandler::default(),
@ -39,10 +37,10 @@ impl Editor {
}
pub fn reload_contents(&mut self) -> Result<()> {
trace!(target:Self::id(), "Reloading editor file contents {:?}", self.file_path);
trace!(target:Self::ID, "Reloading editor file contents {:?}", self.file_path);
match self.file_path.clone() {
None => {
error!(target:Self::id(), "Failed to reload editor contents with None file_path");
error!(target:Self::ID, "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),
@ -50,7 +48,7 @@ impl Editor {
}
pub fn set_contents(&mut self, path: &std::path::PathBuf) -> Result<()> {
trace!(target:Self::id(), "Setting Editor contents from path {:?}", path);
trace!(target:Self::ID, "Setting Editor contents from path {:?}", path);
if let Ok(contents) = std::fs::read_to_string(path) {
let lines: Vec<_> = contents
.lines()
@ -66,10 +64,10 @@ impl Editor {
pub fn save(&self) -> Result<()> {
if let Some(path) = &self.file_path {
trace!(target:Self::id(), "Saving Editor contents {:?}", path);
trace!(target:Self::ID, "Saving Editor contents {:?}", path);
return std::fs::write(path, self.state.lines.to_string()).map_err(|e| e.into());
};
error!(target:Self::id(), "Failed saving Editor contents; file_path was None");
error!(target:Self::ID, "Failed saving Editor contents; file_path was None");
bail!("File not saved. No file path set.")
}
}

View File

@ -19,12 +19,10 @@ pub struct EditorTab {
}
impl EditorTab {
fn id() -> &'static str {
"EditorTab"
}
pub const ID: &str = "EditorTab";
pub fn new(path: Option<&std::path::PathBuf>) -> Self {
trace!(target:Self::id(), "Building EditorTab with path {path:?}");
trace!(target:Self::ID, "Building EditorTab with path {path:?}");
match path {
None => Self {
editors: HashMap::new(),
@ -47,7 +45,7 @@ impl EditorTab {
pub fn next_editor(&mut self) {
let next = (self.current_editor + 1) % self.tab_order.len();
trace!(target:Self::id(), "Moving from {} to next editor tab at {}", self.current_editor, next);
trace!(target:Self::ID, "Moving from {} to next editor tab at {}", self.current_editor, next);
self.set_tab_focus(Focus::Active, next);
self.current_editor = next;
}
@ -57,7 +55,7 @@ impl EditorTab {
.current_editor
.checked_sub(1)
.unwrap_or(self.tab_order.len() - 1);
trace!(target:Self::id(), "Moving from {} to previous editor tab at {}", self.current_editor, prev);
trace!(target:Self::ID, "Moving from {} to previous editor tab at {}", self.current_editor, prev);
self.set_tab_focus(Focus::Active, prev);
self.current_editor = prev;
}
@ -66,7 +64,7 @@ impl EditorTab {
match self.tab_order.get(index) {
None => {
if !self.tab_order.is_empty() {
error!(target:Self::id(), "Failed to get editor tab key with invalid index {index}");
error!(target:Self::ID, "Failed to get editor tab key with invalid index {index}");
}
None
}
@ -84,16 +82,16 @@ impl EditorTab {
}
pub fn set_current_tab_focus(&mut self, focus: Focus) {
trace!(target:Self::id(), "Setting current tab {} focus to {:?}", self.current_editor, focus);
trace!(target:Self::ID, "Setting current tab {} focus to {:?}", self.current_editor, focus);
self.set_tab_focus(focus, self.current_editor)
}
pub fn set_tab_focus(&mut self, focus: Focus, index: usize) {
trace!(target:Self::id(), "Setting tab {} focus to {:?}", index, focus);
trace!(target:Self::ID, "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.
trace!(
target:Self::id(),
target:Self::ID,
"New tab {} focus set to Active; Setting current tab {} to Inactive",
index,
self.current_editor
@ -102,12 +100,12 @@ impl EditorTab {
}
match self.get_editor_key(index) {
None => {
error!(target:Self::id(), "Failed setting tab focus for invalid key {index}");
error!(target:Self::ID, "Failed setting tab focus for invalid key {index}");
}
Some(key) => match self.editors.get_mut(&key) {
None => {
error!(
target:Self::id(),
target:Self::ID,
"Failed to update tab focus at index {} with invalid key: {}",
self.current_editor,
self.tab_order[self.current_editor]
@ -119,12 +117,12 @@ impl EditorTab {
}
pub fn open_tab(&mut self, path: &std::path::PathBuf) -> Result<()> {
trace!(target:Self::id(), "Opening new EditorTab with path {:?}", path);
trace!(target:Self::ID, "Opening new EditorTab with path {:?}", path);
if self
.editors
.contains_key(&path.to_string_lossy().to_string())
{
warn!(target:Self::id(), "EditorTab already opened with this file");
warn!(target:Self::ID, "EditorTab already opened with this file");
return Ok(());
}
@ -151,12 +149,12 @@ impl EditorTab {
.to_owned();
match self.editors.remove(&key) {
None => {
error!(target:Self::id(), "Failed to remove editor tab {key} with invalid index {index}")
error!(target:Self::ID, "Failed to remove editor tab {key} with invalid index {index}")
}
Some(_) => {
self.prev_editor();
self.tab_order.remove(index);
info!(target:Self::id(), "Closed editor tab {key} at index {index}")
info!(target:Self::ID, "Closed editor tab {key} at index {index}")
}
}
Ok(())

View File

@ -20,12 +20,10 @@ pub struct Explorer<'a> {
}
impl<'a> Explorer<'a> {
pub fn id() -> &'static str {
"Explorer"
}
pub const ID: &'static str = "Explorer";
pub fn new(path: &PathBuf) -> Result<Self> {
trace!(target:Self::id(), "Building {}", Self::id());
trace!(target:Self::ID, "Building {}", Self::ID);
let explorer = Explorer {
root_path: path.to_owned(),
tree_items: Self::build_tree_from_path(path.to_owned())?,

View File

@ -15,12 +15,10 @@ pub struct Logger {
}
impl Logger {
pub fn id() -> &'static str {
"Logger"
}
pub const ID: &str = "Logger";
pub fn new() -> Self {
trace!(target:Self::id(), "Building {}", Self::id());
trace!(target:Self::ID, "Building {}", Self::ID);
let state = TuiWidgetState::new();
state.transition(TuiWidgetEvent::HideKey);
Self {

View File

@ -2,6 +2,7 @@ use crate::tui::component::{Action, Component, ComponentState, FocusState};
use crate::tui::menu_bar::MenuBarItemOption::{
About, CloseTab, Exit, Reload, Save, ShowHideExplorer, ShowHideLogger,
};
use anyhow::Context;
use log::trace;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{KeyCode, KeyEvent};
@ -83,13 +84,11 @@ pub struct MenuBar {
}
impl MenuBar {
pub fn id() -> &'static str {
"MenuBar"
}
pub const ID: &str = "MenuBar";
const DEFAULT_HELP: &str = "(←/h)/(→/l): Select option | Enter: Choose selection";
pub fn new() -> Self {
trace!(target:Self::id(), "Building {}", Self::id());
trace!(target:Self::ID, "Building {}", Self::ID);
Self {
selected: MenuBarItem::File,
opened: None,
@ -154,7 +153,7 @@ impl MenuBar {
height,
};
// TODO: X offset for item option? It's fine as-is, but it might look nicer.
// trace!(target:Self::id(), "Building Rect under MenuBar popup {}", rect);
// trace!(target:Self::ID, "Building Rect under MenuBar popup {}", rect);
rect
}
}
@ -192,7 +191,11 @@ impl Component for MenuBar {
}
KeyCode::Enter => {
if let Some(selected) = self.list_state.selected() {
let selection = self.selected.options()[selected];
let selection = self
.selected
.options()
.get(selected)
.context("Failed to get selected MenuBar option")?;
return match selection {
Save => Ok(Action::Save),
Exit => Ok(Action::Quit),