diff --git a/src/app.rs b/src/app.rs index 82688bd..9fe0657 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ backend::Backend, crossterm::event::{KeyCode, KeyEvent}, @@ -22,8 +24,9 @@ use crate::{ widget::commit_list::{CommitInfo, CommitListState}, }; -#[derive(Debug)] +#[derive(Debug, Default)] enum StatusLine { + #[default] None, Input(String, Option, Option), NotificationInfo(String), @@ -37,21 +40,29 @@ pub enum InitialSelection { Head, } +#[derive(Debug)] +pub struct AppContext { + pub keybind: KeyBind, + pub core_config: CoreConfig, + pub ui_config: UiConfig, + pub color_theme: ColorTheme, + pub image_protocol: ImageProtocol, +} + +#[derive(Debug, Default)] +struct AppStatus { + status_line: StatusLine, + numeric_prefix: String, + view_area: Rect, +} + #[derive(Debug)] pub struct App<'a> { repository: &'a Repository, view: View<'a>, - status_line: StatusLine, - - keybind: &'a KeyBind, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + app_status: AppStatus, + ctx: Rc, tx: Sender, - - numeric_prefix: String, - view_area: Rect, } impl<'a> App<'a> { @@ -59,14 +70,10 @@ impl<'a> App<'a> { repository: &'a Repository, graph_image_manager: GraphImageManager<'a>, graph: &'a Graph, - keybind: &'a KeyBind, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, graph_color_set: &'a GraphColorSet, cell_width_type: CellWidthType, - image_protocol: ImageProtocol, initial_selection: InitialSelection, + ctx: Rc, tx: Sender, ) -> Self { let mut ref_name_to_commit_index_map = FxHashMap::default(); @@ -95,8 +102,8 @@ impl<'a> App<'a> { graph_cell_width, head, ref_name_to_commit_index_map, - core_config.search.ignore_case, - core_config.search.fuzzy, + ctx.core_config.search.ignore_case, + ctx.core_config.search.fuzzy, ); if let InitialSelection::Head = initial_selection { match repository.head() { @@ -104,20 +111,14 @@ impl<'a> App<'a> { Head::Detached { target } => commit_list_state.select_commit_hash(target), } } - let view = View::of_list(commit_list_state, ui_config, color_theme, tx.clone()); + let view = View::of_list(commit_list_state, ctx.clone(), tx.clone()); Self { repository, - status_line: StatusLine::None, view, - keybind, - core_config, - ui_config, - color_theme, - image_protocol, + app_status: AppStatus::default(), + ctx, tx, - numeric_prefix: String::new(), - view_area: Rect::default(), } } } @@ -132,7 +133,7 @@ impl App<'_> { terminal.draw(|f| self.render(f))?; match rx.recv() { AppEvent::Key(key) => { - match self.status_line { + match self.app_status.status_line { StatusLine::None | StatusLine::Input(_, _, _) => { // do nothing } @@ -149,12 +150,12 @@ impl App<'_> { } } - let user_event = self.keybind.get(&key); + let user_event = self.ctx.keybind.get(&key); if let Some(UserEvent::Cancel) = user_event { - if !self.numeric_prefix.is_empty() { + if !self.app_status.numeric_prefix.is_empty() { // Clear numeric prefix and cancel the event - self.numeric_prefix.clear(); + self.app_status.numeric_prefix.clear(); continue; } } @@ -165,16 +166,16 @@ impl App<'_> { } Some(ue) => { let event_with_count = - process_numeric_prefix(&self.numeric_prefix, *ue, key); + process_numeric_prefix(&self.app_status.numeric_prefix, *ue, key); self.view.handle_event(event_with_count, key); - self.numeric_prefix.clear(); + self.app_status.numeric_prefix.clear(); } None => { - if let StatusLine::Input(_, _, _) = self.status_line { + if let StatusLine::Input(_, _, _) = self.app_status.status_line { // In input mode, pass all key events to the view // fixme: currently, the only thing that processes key_event is searching the list, // so this probably works, but it's not the right process... - self.numeric_prefix.clear(); + self.app_status.numeric_prefix.clear(); self.view.handle_event( UserEventWithCount::from_event(UserEvent::Unknown), key, @@ -182,9 +183,9 @@ impl App<'_> { } else if let KeyCode::Char(c) = key.code { // Accumulate numeric prefix if c.is_ascii_digit() - && (c != '0' || !self.numeric_prefix.is_empty()) + && (c != '0' || !self.app_status.numeric_prefix.is_empty()) { - self.numeric_prefix.push(c); + self.app_status.numeric_prefix.push(c); } } } @@ -265,8 +266,8 @@ impl App<'_> { fn render(&mut self, f: &mut Frame) { let base = Block::default() - .fg(self.color_theme.fg) - .bg(self.color_theme.bg); + .fg(self.ctx.color_theme.fg) + .bg(self.ctx.color_theme.bg); f.render_widget(base, f.area()); let [view_area, status_line_area] = @@ -281,13 +282,13 @@ impl App<'_> { impl App<'_> { fn render_status_line(&self, f: &mut Frame, area: Rect) { - let text: Line = match &self.status_line { + let text: Line = match &self.app_status.status_line { StatusLine::None => { - if self.numeric_prefix.is_empty() { + if self.app_status.numeric_prefix.is_empty() { Line::raw("") } else { - Line::raw(self.numeric_prefix.as_str()) - .fg(self.color_theme.status_input_transient_fg) + Line::raw(self.app_status.numeric_prefix.as_str()) + .fg(self.ctx.color_theme.status_input_transient_fg) } } StatusLine::Input(msg, _, transient_msg) => { @@ -296,43 +297,45 @@ impl App<'_> { let t_msg_w = console::measure_text_width(t_msg.as_str()); let pad_w = area.width as usize - msg_w - t_msg_w - 2 /* pad */; Line::from(vec![ - msg.as_str().fg(self.color_theme.status_input_fg), + msg.as_str().fg(self.ctx.color_theme.status_input_fg), " ".repeat(pad_w).into(), t_msg .as_str() - .fg(self.color_theme.status_input_transient_fg), + .fg(self.ctx.color_theme.status_input_transient_fg), ]) } else { - Line::raw(msg).fg(self.color_theme.status_input_fg) + Line::raw(msg).fg(self.ctx.color_theme.status_input_fg) } } - StatusLine::NotificationInfo(msg) => Line::raw(msg).fg(self.color_theme.status_info_fg), + StatusLine::NotificationInfo(msg) => { + Line::raw(msg).fg(self.ctx.color_theme.status_info_fg) + } StatusLine::NotificationSuccess(msg) => Line::raw(msg) .add_modifier(Modifier::BOLD) - .fg(self.color_theme.status_success_fg), + .fg(self.ctx.color_theme.status_success_fg), StatusLine::NotificationWarn(msg) => Line::raw(msg) .add_modifier(Modifier::BOLD) - .fg(self.color_theme.status_warn_fg), + .fg(self.ctx.color_theme.status_warn_fg), StatusLine::NotificationError(msg) => Line::raw(format!("ERROR: {msg}")) .add_modifier(Modifier::BOLD) - .fg(self.color_theme.status_error_fg), + .fg(self.ctx.color_theme.status_error_fg), }; let paragraph = Paragraph::new(text).block( Block::default() .borders(Borders::TOP) - .style(Style::default().fg(self.color_theme.divider_fg)) + .style(Style::default().fg(self.ctx.color_theme.divider_fg)) .padding(Padding::horizontal(1)), ); f.render_widget(paragraph, area); - if let StatusLine::Input(_, Some(cursor_pos), _) = &self.status_line { + if let StatusLine::Input(_, Some(cursor_pos), _) = &self.app_status.status_line { let (x, y) = (area.x + cursor_pos + 1, area.y + 1); - match &self.ui_config.common.cursor_type { + match &self.ctx.ui_config.common.cursor_type { CursorType::Native => { f.set_cursor_position((x, y)); } CursorType::Virtual(cursor) => { - let style = Style::default().fg(self.color_theme.virtual_cursor_fg); + let style = Style::default().fg(self.ctx.color_theme.virtual_cursor_fg); f.buffer_mut().set_string(x, y, cursor, style); } } @@ -342,7 +345,7 @@ impl App<'_> { impl App<'_> { fn update_state(&mut self, view_area: Rect) { - self.view_area = view_area; + self.app_status.view_area = view_area; } fn open_detail(&mut self) { @@ -361,9 +364,7 @@ impl App<'_> { commit, changes, refs, - self.ui_config, - self.color_theme, - self.image_protocol, + self.ctx.clone(), self.tx.clone(), ); } @@ -372,12 +373,7 @@ impl App<'_> { fn close_detail(&mut self) { if let View::Detail(ref mut view) = self.view { let commit_list_state = view.take_list_state(); - self.view = View::of_list( - commit_list_state, - self.ui_config, - self.color_theme, - self.tx.clone(), - ); + self.view = View::of_list(commit_list_state, self.ctx.clone(), self.tx.clone()); } } @@ -396,11 +392,8 @@ impl App<'_> { commit_list_state, commit, user_command_number, - self.view_area, - self.core_config, - self.ui_config, - self.color_theme, - self.image_protocol, + self.app_status.view_area, + self.ctx.clone(), self.tx.clone(), ); } else if let View::Detail(ref mut view) = self.view { @@ -411,11 +404,8 @@ impl App<'_> { commit_list_state, commit, user_command_number, - self.view_area, - self.core_config, - self.ui_config, - self.color_theme, - self.image_protocol, + self.app_status.view_area, + self.ctx.clone(), self.tx.clone(), ); } else if let View::UserCommand(ref mut view) = self.view { @@ -427,11 +417,8 @@ impl App<'_> { commit_list_state, commit, user_command_number, - self.view_area, - self.core_config, - self.ui_config, - self.color_theme, - self.image_protocol, + self.app_status.view_area, + self.ctx.clone(), self.tx.clone(), ); } else { @@ -439,11 +426,8 @@ impl App<'_> { commit_list_state, commit, user_command_number, - self.view_area, - self.core_config, - self.ui_config, - self.color_theme, - self.image_protocol, + self.app_status.view_area, + self.ctx.clone(), self.tx.clone(), ); } @@ -462,21 +446,14 @@ impl App<'_> { .cloned() .collect(); if view.before_view_is_list() { - self.view = View::of_list( - commit_list_state, - self.ui_config, - self.color_theme, - self.tx.clone(), - ); + self.view = View::of_list(commit_list_state, self.ctx.clone(), self.tx.clone()); } else { self.view = View::of_detail( commit_list_state, commit, changes, refs, - self.ui_config, - self.color_theme, - self.image_protocol, + self.ctx.clone(), self.tx.clone(), ); } @@ -493,38 +470,20 @@ impl App<'_> { if let View::List(ref mut view) = self.view { let commit_list_state = view.take_list_state(); let refs = self.repository.all_refs().into_iter().cloned().collect(); - self.view = View::of_refs( - commit_list_state, - refs, - self.ui_config, - self.color_theme, - self.tx.clone(), - ); + self.view = View::of_refs(commit_list_state, refs, self.ctx.clone(), self.tx.clone()); } } fn close_refs(&mut self) { if let View::Refs(ref mut view) = self.view { let commit_list_state = view.take_list_state(); - self.view = View::of_list( - commit_list_state, - self.ui_config, - self.color_theme, - self.tx.clone(), - ); + self.view = View::of_list(commit_list_state, self.ctx.clone(), self.tx.clone()); } } fn open_help(&mut self) { let before_view = std::mem::take(&mut self.view); - self.view = View::of_help( - before_view, - self.color_theme, - self.image_protocol, - self.tx.clone(), - self.keybind, - self.core_config, - ); + self.view = View::of_help(before_view, self.ctx.clone(), self.tx.clone()); } fn close_help(&mut self) { @@ -543,7 +502,7 @@ impl App<'_> { if let View::Detail(ref mut view) = self.view { view.select_older_commit(self.repository); } else if let View::UserCommand(ref mut view) = self.view { - view.select_older_commit(self.repository, self.view_area); + view.select_older_commit(self.repository, self.app_status.view_area); } } @@ -551,7 +510,7 @@ impl App<'_> { if let View::Detail(ref mut view) = self.view { view.select_newer_commit(self.repository); } else if let View::UserCommand(ref mut view) = self.view { - view.select_newer_commit(self.repository, self.view_area); + view.select_newer_commit(self.repository, self.app_status.view_area); } } @@ -559,12 +518,12 @@ impl App<'_> { if let View::Detail(ref mut view) = self.view { view.select_parent_commit(self.repository); } else if let View::UserCommand(ref mut view) = self.view { - view.select_parent_commit(self.repository, self.view_area); + view.select_parent_commit(self.repository, self.app_status.view_area); } } fn clear_status_line(&mut self) { - self.status_line = StatusLine::None; + self.app_status.status_line = StatusLine::None; } fn update_status_input( @@ -573,27 +532,27 @@ impl App<'_> { cursor_pos: Option, transient_msg: Option, ) { - self.status_line = StatusLine::Input(msg, cursor_pos, transient_msg); + self.app_status.status_line = StatusLine::Input(msg, cursor_pos, transient_msg); } fn info_notification(&mut self, msg: String) { - self.status_line = StatusLine::NotificationInfo(msg); + self.app_status.status_line = StatusLine::NotificationInfo(msg); } fn success_notification(&mut self, msg: String) { - self.status_line = StatusLine::NotificationSuccess(msg); + self.app_status.status_line = StatusLine::NotificationSuccess(msg); } fn warn_notification(&mut self, msg: String) { - self.status_line = StatusLine::NotificationWarn(msg); + self.app_status.status_line = StatusLine::NotificationWarn(msg); } fn error_notification(&mut self, msg: String) { - self.status_line = StatusLine::NotificationError(msg); + self.app_status.status_line = StatusLine::NotificationError(msg); } fn copy_to_clipboard(&self, name: String, value: String) { - match copy_to_clipboard(value, &self.core_config.external.clipboard) { + match copy_to_clipboard(value, &self.ctx.core_config.external.clipboard) { Ok(_) => { let msg = format!("Copied {name} to clipboard successfully"); self.tx.send(AppEvent::NotifySuccess(msg)); diff --git a/src/lib.rs b/src/lib.rs index 1882b41..2e36a9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ mod keybind; mod view; mod widget; -use std::path::Path; +use std::{path::Path, rc::Rc}; use app::App; use clap::{Parser, ValueEnum}; @@ -134,8 +134,8 @@ pub type Result = std::result::Result>; pub fn run() -> Result<()> { let args = Args::parse(); - let (core_config, ui_config, graph_config, color_theme, key_bind_patch) = config::load()?; - let key_bind = keybind::KeyBind::new(key_bind_patch); + let (core_config, ui_config, graph_config, color_theme, keybind_patch) = config::load()?; + let keybind = keybind::KeyBind::new(keybind_patch); let max_count = args.max_count; let image_protocol = args.protocol.or(core_config.option.protocol).into(); @@ -168,18 +168,21 @@ pub fn run() -> Result<()> { let (tx, rx) = event::init(); + let ctx = Rc::new(app::AppContext { + keybind, + core_config, + ui_config, + color_theme, + image_protocol, + }); let mut app = App::new( &repository, graph_image_manager, &graph, - &key_bind, - &core_config, - &ui_config, - &color_theme, &graph_color_set, cell_width_type, - image_protocol, initial_selection, + ctx, tx, ); let ret = app.run(&mut terminal, rx); diff --git a/src/view/detail.rs b/src/view/detail.rs index 2d8f5cf..40d2c5c 100644 --- a/src/view/detail.rs +++ b/src/view/detail.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ crossterm::event::KeyEvent, layout::{Constraint, Layout, Rect}, @@ -6,11 +8,9 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, - config::UiConfig, + app::AppContext, event::{AppEvent, Sender, UserEvent, UserEventWithCount}, git::{Commit, FileChange, Ref, Repository}, - protocol::ImageProtocol, widget::{ commit_detail::{CommitDetail, CommitDetailState}, commit_list::{CommitList, CommitListState}, @@ -26,9 +26,7 @@ pub struct DetailView<'a> { changes: Vec, refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, clear: bool, } @@ -39,9 +37,7 @@ impl<'a> DetailView<'a> { commit: Commit, changes: Vec, refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, ) -> DetailView<'a> { DetailView { @@ -50,9 +46,7 @@ impl<'a> DetailView<'a> { commit, changes, refs, - ui_config, - color_theme, - image_protocol, + ctx, tx, clear: false, } @@ -129,11 +123,11 @@ impl<'a> DetailView<'a> { } pub fn render(&mut self, f: &mut Frame, area: Rect) { - let detail_height = (area.height - 1).min(self.ui_config.detail.height); + let detail_height = (area.height - 1).min(self.ctx.ui_config.detail.height); let [list_area, detail_area] = Layout::vertical([Constraint::Min(0), Constraint::Length(detail_height)]).areas(area); - let commit_list = CommitList::new(&self.ui_config.list, self.color_theme); + let commit_list = CommitList::new(self.ctx.clone()); f.render_stateful_widget(commit_list, list_area, self.as_mut_list_state()); if self.clear { @@ -141,18 +135,13 @@ impl<'a> DetailView<'a> { return; } - let commit_detail = CommitDetail::new( - &self.commit, - &self.changes, - &self.refs, - &self.ui_config.detail, - self.color_theme, - ); + let commit_detail = + CommitDetail::new(&self.commit, &self.changes, &self.refs, self.ctx.clone()); f.render_stateful_widget(commit_detail, detail_area, &mut self.commit_detail_state); // clear the image area if needed for y in detail_area.top()..detail_area.bottom() { - self.image_protocol.clear_line(y); + self.ctx.image_protocol.clear_line(y); } } } diff --git a/src/view/help.rs b/src/view/help.rs index 067e3a0..4651178 100644 --- a/src/view/help.rs +++ b/src/view/help.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ crossterm::event::KeyEvent, layout::{Constraint, Layout, Rect}, @@ -8,11 +10,11 @@ use ratatui::{ }; use crate::{ + app::AppContext, color::ColorTheme, config::CoreConfig, event::{AppEvent, Sender, UserEvent, UserEventWithCount}, keybind::KeyBind, - protocol::ImageProtocol, view::View, }; @@ -27,21 +29,15 @@ pub struct HelpView<'a> { offset: usize, height: usize, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, clear: bool, } impl HelpView<'_> { - pub fn new<'a>( - before: View<'a>, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, - tx: Sender, - keybind: &'a KeyBind, - core_config: &'a CoreConfig, - ) -> HelpView<'a> { - let (help_key_lines, help_value_lines) = build_lines(color_theme, keybind, core_config); + pub fn new<'a>(before: View<'a>, ctx: Rc, tx: Sender) -> HelpView<'a> { + let (help_key_lines, help_value_lines) = + build_lines(&ctx.color_theme, &ctx.keybind, &ctx.core_config); let help_key_line_max_width = help_key_lines .iter() .map(|line| line.width()) @@ -54,7 +50,7 @@ impl HelpView<'_> { help_key_line_max_width, offset: 0, height: 0, - image_protocol, + ctx, tx, clear: false, } @@ -159,7 +155,7 @@ impl HelpView<'_> { // clear the image area if needed for y in area.top()..area.bottom() { - self.image_protocol.clear_line(y); + self.ctx.image_protocol.clear_line(y); } } } diff --git a/src/view/list.rs b/src/view/list.rs index 7ae225d..9aa150d 100644 --- a/src/view/list.rs +++ b/src/view/list.rs @@ -1,8 +1,9 @@ +use std::rc::Rc; + use ratatui::{crossterm::event::KeyEvent, layout::Rect, Frame}; use crate::{ - color::ColorTheme, - config::UiConfig, + app::AppContext, event::{AppEvent, Sender, UserEvent, UserEventWithCount}, widget::commit_list::{CommitList, CommitListState, SearchState}, }; @@ -11,22 +12,19 @@ use crate::{ pub struct ListView<'a> { commit_list_state: Option>, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, } impl<'a> ListView<'a> { pub fn new( commit_list_state: CommitListState<'a>, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, ) -> ListView<'a> { ListView { commit_list_state: Some(commit_list_state), - ui_config, - color_theme, + ctx, tx, } } @@ -170,7 +168,7 @@ impl<'a> ListView<'a> { } pub fn render(&mut self, f: &mut Frame, area: Rect) { - let commit_list = CommitList::new(&self.ui_config.list, self.color_theme); + let commit_list = CommitList::new(self.ctx.clone()); f.render_stateful_widget(commit_list, area, self.as_mut_list_state()); } } diff --git a/src/view/refs.rs b/src/view/refs.rs index 052fcff..74ea935 100644 --- a/src/view/refs.rs +++ b/src/view/refs.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ crossterm::event::KeyEvent, layout::{Constraint, Layout, Rect}, @@ -5,8 +7,7 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, - config::UiConfig, + app::AppContext, event::{AppEvent, Sender, UserEvent, UserEventWithCount}, git::Ref, widget::{ @@ -22,8 +23,7 @@ pub struct RefsView<'a> { refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, } @@ -31,16 +31,14 @@ impl<'a> RefsView<'a> { pub fn new( commit_list_state: CommitListState<'a>, refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, ) -> RefsView<'a> { RefsView { commit_list_state: Some(commit_list_state), ref_list_state: RefListState::new(), refs, - ui_config, - color_theme, + ctx, tx, } } @@ -96,15 +94,16 @@ impl<'a> RefsView<'a> { pub fn render(&mut self, f: &mut Frame, area: Rect) { let graph_width = self.as_list_state().graph_area_cell_width() + 1; // graph area + marker - let refs_width = (area.width.saturating_sub(graph_width)).min(self.ui_config.refs.width); + let refs_width = + (area.width.saturating_sub(graph_width)).min(self.ctx.ui_config.refs.width); let [list_area, refs_area] = Layout::horizontal([Constraint::Min(0), Constraint::Length(refs_width)]).areas(area); - let commit_list = CommitList::new(&self.ui_config.list, self.color_theme); + let commit_list = CommitList::new(self.ctx.clone()); f.render_stateful_widget(commit_list, list_area, self.as_mut_list_state()); - let ref_list = RefList::new(&self.refs, self.color_theme); + let ref_list = RefList::new(&self.refs, self.ctx.clone()); f.render_stateful_widget(ref_list, refs_area, &mut self.ref_list_state); } } diff --git a/src/view/user_command.rs b/src/view/user_command.rs index 4c3e16c..0e083c6 100644 --- a/src/view/user_command.rs +++ b/src/view/user_command.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ansi_to_tui::IntoText as _; use ratatui::{ crossterm::event::KeyEvent, @@ -8,12 +10,10 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, - config::{CoreConfig, UiConfig}, + app::AppContext, event::{AppEvent, Sender, UserEvent, UserEventWithCount}, external::exec_user_command, git::{Commit, Repository}, - protocol::ImageProtocol, widget::{ commit_list::{CommitList, CommitListState}, commit_user_command::{CommitUserCommand, CommitUserCommandState}, @@ -34,10 +34,7 @@ pub struct UserCommandView<'a> { user_command_number: usize, user_command_output_lines: Vec>, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, before_view: UserCommandViewBeforeView, clear: bool, @@ -49,34 +46,23 @@ impl<'a> UserCommandView<'a> { commit: Commit, user_command_number: usize, view_area: Rect, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, before_view: UserCommandViewBeforeView, ) -> UserCommandView<'a> { - let user_command_output_lines = build_user_command_output_lines( - &commit, - user_command_number, - view_area, - core_config, - ui_config, - ) - .unwrap_or_else(|err| { - tx.send(AppEvent::NotifyError(err)); - vec![] - }); + let user_command_output_lines = + build_user_command_output_lines(&commit, user_command_number, view_area, ctx.clone()) + .unwrap_or_else(|err| { + tx.send(AppEvent::NotifyError(err)); + vec![] + }); UserCommandView { commit_list_state: Some(commit_list_state), commit_user_command_state: CommitUserCommandState::default(), user_command_number, user_command_output_lines, - core_config, - ui_config, - color_theme, - image_protocol, + ctx, tx, before_view, clear: false, @@ -152,16 +138,16 @@ impl<'a> UserCommandView<'a> { } pub fn render(&mut self, f: &mut Frame, area: Rect) { - let user_command_height = (area.height - 1).min(self.ui_config.user_command.height); + let user_command_height = (area.height - 1).min(self.ctx.ui_config.user_command.height); let [list_area, user_command_area] = Layout::vertical([Constraint::Min(0), Constraint::Length(user_command_height)]) .areas(area); - let commit_list = CommitList::new(&self.ui_config.list, self.color_theme); + let commit_list = CommitList::new(self.ctx.clone()); f.render_stateful_widget(commit_list, list_area, self.as_mut_list_state()); let commit_user_command = - CommitUserCommand::new(&self.user_command_output_lines, self.color_theme); + CommitUserCommand::new(&self.user_command_output_lines, self.ctx.clone()); f.render_stateful_widget( commit_user_command, user_command_area, @@ -175,7 +161,7 @@ impl<'a> UserCommandView<'a> { // clear the image area if needed for y in user_command_area.top()..user_command_area.bottom() { - self.image_protocol.clear_line(y); + self.ctx.image_protocol.clear_line(y); } } } @@ -217,8 +203,7 @@ impl<'a> UserCommandView<'a> { &commit, self.user_command_number, view_area, - self.core_config, - self.ui_config, + self.ctx.clone(), ) .unwrap_or_else(|err| { self.tx.send(AppEvent::NotifyError(err)); @@ -246,10 +231,10 @@ fn build_user_command_output_lines<'a>( commit: &Commit, user_command_number: usize, view_area: Rect, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, + ctx: Rc, ) -> Result>, String> { - let command = core_config + let command = ctx + .core_config .user_command .commands .get(&user_command_number.to_string()) @@ -271,9 +256,9 @@ fn build_user_command_output_lines<'a>( .unwrap_or_default(); let area_width = view_area.width - 4; // minus the left and right padding - let area_height = (view_area.height - 1).min(ui_config.user_command.height) - 1; // minus the top border + let area_height = (view_area.height - 1).min(ctx.ui_config.user_command.height) - 1; // minus the top border - let tab_spaces = " ".repeat(core_config.user_command.tab_width as usize); + let tab_spaces = " ".repeat(ctx.core_config.user_command.tab_width as usize); exec_user_command(&command, target_hash, parent_hash, area_width, area_height) .and_then(|output| { output diff --git a/src/view/views.rs b/src/view/views.rs index 7320572..77401ce 100644 --- a/src/view/views.rs +++ b/src/view/views.rs @@ -1,12 +1,11 @@ +use std::rc::Rc; + use ratatui::{crossterm::event::KeyEvent, layout::Rect, Frame}; use crate::{ - color::ColorTheme, - config::{CoreConfig, UiConfig}, + app::AppContext, event::{Sender, UserEventWithCount}, git::{Commit, FileChange, Ref}, - keybind::KeyBind, - protocol::ImageProtocol, view::{ detail::DetailView, help::HelpView, @@ -53,16 +52,10 @@ impl<'a> View<'a> { pub fn of_list( commit_list_state: CommitListState<'a>, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { - View::List(Box::new(ListView::new( - commit_list_state, - ui_config, - color_theme, - tx, - ))) + View::List(Box::new(ListView::new(commit_list_state, ctx, tx))) } pub fn of_detail( @@ -70,9 +63,7 @@ impl<'a> View<'a> { commit: Commit, changes: Vec, refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, ) -> Self { View::Detail(Box::new(DetailView::new( @@ -80,9 +71,7 @@ impl<'a> View<'a> { commit, changes, refs, - ui_config, - color_theme, - image_protocol, + ctx, tx, ))) } @@ -92,10 +81,7 @@ impl<'a> View<'a> { commit: Commit, user_command_number: usize, view_area: Rect, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, ) -> Self { View::UserCommand(Box::new(UserCommandView::new( @@ -103,10 +89,7 @@ impl<'a> View<'a> { commit, user_command_number, view_area, - core_config, - ui_config, - color_theme, - image_protocol, + ctx, tx, UserCommandViewBeforeView::List, ))) @@ -117,10 +100,7 @@ impl<'a> View<'a> { commit: Commit, user_command_number: usize, view_area: Rect, - core_config: &'a CoreConfig, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, + ctx: Rc, tx: Sender, ) -> Self { View::UserCommand(Box::new(UserCommandView::new( @@ -128,10 +108,7 @@ impl<'a> View<'a> { commit, user_command_number, view_area, - core_config, - ui_config, - color_theme, - image_protocol, + ctx, tx, UserCommandViewBeforeView::Detail, ))) @@ -140,34 +117,13 @@ impl<'a> View<'a> { pub fn of_refs( commit_list_state: CommitListState<'a>, refs: Vec, - ui_config: &'a UiConfig, - color_theme: &'a ColorTheme, + ctx: Rc, tx: Sender, ) -> Self { - View::Refs(Box::new(RefsView::new( - commit_list_state, - refs, - ui_config, - color_theme, - tx, - ))) + View::Refs(Box::new(RefsView::new(commit_list_state, refs, ctx, tx))) } - pub fn of_help( - before: View<'a>, - color_theme: &'a ColorTheme, - image_protocol: ImageProtocol, - tx: Sender, - keybind: &'a KeyBind, - core_config: &'a CoreConfig, - ) -> Self { - View::Help(Box::new(HelpView::new( - before, - color_theme, - image_protocol, - tx, - keybind, - core_config, - ))) + pub fn of_help(before: View<'a>, ctx: Rc, tx: Sender) -> Self { + View::Help(Box::new(HelpView::new(before, ctx, tx))) } } diff --git a/src/widget/commit_detail.rs b/src/widget/commit_detail.rs index 5e3d9ae..07739a4 100644 --- a/src/widget/commit_detail.rs +++ b/src/widget/commit_detail.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use chrono::{DateTime, FixedOffset}; use ratatui::{ buffer::Buffer, @@ -8,8 +10,7 @@ use ratatui::{ }; use crate::{ - color::ColorTheme, - config::UiDetailConfig, + app::AppContext, git::{Commit, FileChange, Ref}, }; @@ -57,8 +58,7 @@ pub struct CommitDetail<'a> { commit: &'a Commit, changes: &'a Vec, refs: &'a Vec, - config: &'a UiDetailConfig, - color_theme: &'a ColorTheme, + ctx: Rc, } impl<'a> CommitDetail<'a> { @@ -66,15 +66,13 @@ impl<'a> CommitDetail<'a> { commit: &'a Commit, changes: &'a Vec, refs: &'a Vec, - config: &'a UiDetailConfig, - color_theme: &'a ColorTheme, + ctx: Rc, ) -> Self { Self { commit, changes, refs, - config, - color_theme, + ctx, } } } @@ -102,11 +100,11 @@ impl StatefulWidget for CommitDetail<'_> { impl CommitDetail<'_> { fn render_labels_paragraph(&self, lines: Vec, area: Rect, buf: &mut Buffer) { let paragraph = Paragraph::new(lines) - .style(Style::default().fg(self.color_theme.fg)) + .style(Style::default().fg(self.ctx.color_theme.fg)) .block( Block::default() .borders(Borders::TOP) - .style(Style::default().fg(self.color_theme.divider_fg)) + .style(Style::default().fg(self.ctx.color_theme.divider_fg)) .padding(Padding::left(2)), ); paragraph.render(area, buf); @@ -114,11 +112,11 @@ impl CommitDetail<'_> { fn render_value_paragraph(&self, lines: Vec, area: Rect, buf: &mut Buffer) { let paragraph = Paragraph::new(lines) - .style(Style::default().fg(self.color_theme.fg)) + .style(Style::default().fg(self.ctx.color_theme.fg)) .block( Block::default() .borders(Borders::TOP) - .style(Style::default().fg(self.color_theme.divider_fg)) + .style(Style::default().fg(self.ctx.color_theme.divider_fg)) .padding(Padding::new(1, 2, 0, 0)), ); paragraph.render(area, buf); @@ -128,26 +126,26 @@ impl CommitDetail<'_> { let mut label_lines: Vec = Vec::new(); let mut value_lines: Vec = Vec::new(); - label_lines.push(Line::from(" Author: ").fg(self.color_theme.detail_label_fg)); + label_lines.push(Line::from(" Author: ").fg(self.ctx.color_theme.detail_label_fg)); label_lines.push(self.empty_line()); value_lines.extend(self.author_lines()); if is_author_committer_different(self.commit) { - label_lines.push(Line::from("Committer: ").fg(self.color_theme.detail_label_fg)); + label_lines.push(Line::from("Committer: ").fg(self.ctx.color_theme.detail_label_fg)); label_lines.push(self.empty_line()); value_lines.extend(self.committer_lines()); } - label_lines.push(Line::from(" SHA: ").fg(self.color_theme.detail_label_fg)); + label_lines.push(Line::from(" SHA: ").fg(self.ctx.color_theme.detail_label_fg)); value_lines.push(self.sha_line()); if has_parent(self.commit) { - label_lines.push(Line::from(" Parents: ").fg(self.color_theme.detail_label_fg)); + label_lines.push(Line::from(" Parents: ").fg(self.ctx.color_theme.detail_label_fg)); value_lines.push(self.parents_line()); } if has_refs(self.refs) { - label_lines.push(Line::from(" Refs: ").fg(self.color_theme.detail_label_fg)); + label_lines.push(Line::from(" Refs: ").fg(self.ctx.color_theme.detail_label_fg)); value_lines.push(self.refs_line()); } @@ -182,20 +180,23 @@ impl CommitDetail<'_> { email: &'a str, date: &'a DateTime, ) -> Vec> { - let date_str = if self.config.date_local { + let date_str = if self.ctx.ui_config.detail.date_local { let local = date.with_timezone(&chrono::Local); - local.format(&self.config.date_format).to_string() + local + .format(&self.ctx.ui_config.detail.date_format) + .to_string() } else { - date.format(&self.config.date_format).to_string() + date.format(&self.ctx.ui_config.detail.date_format) + .to_string() }; vec![ Line::from(vec![ - name.fg(self.color_theme.detail_name_fg), + name.fg(self.ctx.color_theme.detail_name_fg), " <".into(), - email.fg(self.color_theme.detail_email_fg), + email.fg(self.ctx.color_theme.detail_email_fg), "> ".into(), ]), - Line::from(date_str.fg(self.color_theme.detail_date_fg)), + Line::from(date_str.fg(self.ctx.color_theme.detail_date_fg)), ] } @@ -204,7 +205,7 @@ impl CommitDetail<'_> { self.commit .commit_hash .as_str() - .fg(self.color_theme.detail_hash_fg), + .fg(self.ctx.color_theme.detail_hash_fg), ) } @@ -213,7 +214,7 @@ impl CommitDetail<'_> { let parents = &self.commit.parent_commit_hashes; for (i, hash) in parents .iter() - .map(|hash| hash.as_short_hash().fg(self.color_theme.detail_hash_fg)) + .map(|hash| hash.as_short_hash().fg(self.ctx.color_theme.detail_hash_fg)) .enumerate() { spans.push(hash); @@ -228,17 +229,17 @@ impl CommitDetail<'_> { let ref_spans = self.refs.iter().filter_map(|r| match r { Ref::Branch { name, .. } => Some( Span::raw(name) - .fg(self.color_theme.detail_ref_branch_fg) + .fg(self.ctx.color_theme.detail_ref_branch_fg) .add_modifier(Modifier::BOLD), ), Ref::RemoteBranch { name, .. } => Some( Span::raw(name) - .fg(self.color_theme.detail_ref_remote_branch_fg) + .fg(self.ctx.color_theme.detail_ref_remote_branch_fg) .add_modifier(Modifier::BOLD), ), Ref::Tag { name, .. } => Some( Span::raw(name) - .fg(self.color_theme.detail_ref_tag_fg) + .fg(self.ctx.color_theme.detail_ref_tag_fg) .add_modifier(Modifier::BOLD), ), Ref::Stash { .. } => None, @@ -276,22 +277,22 @@ impl CommitDetail<'_> { .iter() .map(|c| match c { FileChange::Add { path } => Line::from(vec![ - "A".fg(self.color_theme.detail_file_change_add_fg), + "A".fg(self.ctx.color_theme.detail_file_change_add_fg), " ".into(), path.into(), ]), FileChange::Modify { path } => Line::from(vec![ - "M".fg(self.color_theme.detail_file_change_modify_fg), + "M".fg(self.ctx.color_theme.detail_file_change_modify_fg), " ".into(), path.into(), ]), FileChange::Delete { path } => Line::from(vec![ - "D".fg(self.color_theme.detail_file_change_delete_fg), + "D".fg(self.ctx.color_theme.detail_file_change_delete_fg), " ".into(), path.into(), ]), FileChange::Move { from, to } => Line::from(vec![ - "R".fg(self.color_theme.detail_file_change_move_fg), + "R".fg(self.ctx.color_theme.detail_file_change_move_fg), " ".into(), from.into(), " -> ".into(), @@ -306,7 +307,7 @@ impl CommitDetail<'_> { } fn divider_line(&self, width: usize) -> Line<'_> { - Line::from("─".repeat(width).fg(self.color_theme.divider_fg)) + Line::from("─".repeat(width).fg(self.ctx.color_theme.divider_fg)) } fn update_state(&self, state: &mut CommitDetailState, line_count: usize, area_height: usize) { diff --git a/src/widget/commit_list.rs b/src/widget/commit_list.rs index 8222621..9240798 100644 --- a/src/widget/commit_list.rs +++ b/src/widget/commit_list.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use laurier::highlight::highlight_matched_text; use once_cell::sync::Lazy; @@ -13,8 +15,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use tui_input::{backend::crossterm::EventHandler, Input}; use crate::{ + app::AppContext, color::ColorTheme, - config::UiListConfig, git::{Commit, CommitHash, Head, Ref}, graph::GraphImageManager, }; @@ -653,15 +655,15 @@ impl<'a> CommitListState<'a> { } pub struct CommitList<'a> { - config: &'a UiListConfig, - color_theme: &'a ColorTheme, + ctx: Rc, + _marker: std::marker::PhantomData<&'a ()>, } impl<'a> CommitList<'a> { - pub fn new(config: &'a UiListConfig, color_theme: &'a ColorTheme) -> Self { + pub fn new(ctx: Rc) -> Self { Self { - config, - color_theme, + ctx, + _marker: std::marker::PhantomData, } } } @@ -681,9 +683,9 @@ impl<'a> StatefulWidget for CommitList<'a> { ) = self.calc_cell_widths( state, area.width, - self.config.subject_min_width, - self.config.name_width, - self.config.date_width, + self.ctx.ui_config.list.subject_min_width, + self.ctx.ui_config.list.name_width, + self.ctx.ui_config.list.date_width, ); let chunks = Layout::horizontal([ @@ -808,7 +810,7 @@ impl CommitList<'_> { commit_info, state.head, &state.search_matches[state.offset + i].refs, - self.color_theme, + &self.ctx.color_theme, ); let ref_spans_width: usize = spans.iter().map(|s| s.width()).sum(); let max_width = max_width.saturating_sub(ref_spans_width); @@ -826,13 +828,13 @@ impl CommitList<'_> { highlighted_spans( subject.into(), pos, - self.color_theme.list_subject_fg, + self.ctx.color_theme.list_subject_fg, Modifier::empty(), - self.color_theme, + &self.ctx.color_theme, truncate, ) } else { - vec![subject.fg(self.color_theme.list_subject_fg)] + vec![subject.fg(self.ctx.color_theme.list_subject_fg)] }; spans.extend(sub_spans) @@ -862,13 +864,13 @@ impl CommitList<'_> { highlighted_spans( name.into(), pos, - self.color_theme.list_name_fg, + self.ctx.color_theme.list_name_fg, Modifier::empty(), - self.color_theme, + &self.ctx.color_theme, truncate, ) } else { - vec![name.fg(self.color_theme.list_name_fg)] + vec![name.fg(self.ctx.color_theme.list_name_fg)] }; self.to_commit_list_item(i, spans, state) }) @@ -889,13 +891,13 @@ impl CommitList<'_> { highlighted_spans( hash.into(), pos, - self.color_theme.list_hash_fg, + self.ctx.color_theme.list_hash_fg, Modifier::empty(), - self.color_theme, + &self.ctx.color_theme, false, ) } else { - vec![hash.fg(self.color_theme.list_hash_fg)] + vec![hash.fg(self.ctx.color_theme.list_hash_fg)] }; self.to_commit_list_item(i, spans, state) }) @@ -911,13 +913,20 @@ impl CommitList<'_> { .rendering_commit_iter(state) .map(|(i, commit)| { let date = &commit.author_date; - let date_str = if self.config.date_local { + let date_str = if self.ctx.ui_config.list.date_local { let local = date.with_timezone(&chrono::Local); - local.format(&self.config.date_format).to_string() + local + .format(&self.ctx.ui_config.list.date_format) + .to_string() } else { - date.format(&self.config.date_format).to_string() + date.format(&self.ctx.ui_config.list.date_format) + .to_string() }; - self.to_commit_list_item(i, vec![date_str.fg(self.color_theme.list_date_fg)], state) + self.to_commit_list_item( + i, + vec![date_str.fg(self.ctx.color_theme.list_date_fg)], + state, + ) }) .collect(); Widget::render(List::new(items), area, buf); @@ -955,8 +964,8 @@ impl CommitList<'_> { let mut line = Line::from(spans); if i == state.selected { line = line - .bg(self.color_theme.list_selected_bg) - .fg(self.color_theme.list_selected_fg); + .bg(self.ctx.color_theme.list_selected_bg) + .fg(self.ctx.color_theme.list_selected_fg); } ListItem::new(line) } diff --git a/src/widget/commit_user_command.rs b/src/widget/commit_user_command.rs index 53a3aca..a8d92fb 100644 --- a/src/widget/commit_user_command.rs +++ b/src/widget/commit_user_command.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ buffer::Buffer, layout::Rect, @@ -6,7 +8,7 @@ use ratatui::{ widgets::{Block, Borders, Padding, Paragraph, StatefulWidget, Widget}, }; -use crate::color::ColorTheme; +use crate::app::AppContext; #[derive(Debug, Default)] pub struct CommitUserCommandState { @@ -50,12 +52,12 @@ impl CommitUserCommandState { pub struct CommitUserCommand<'a> { lines: &'a Vec>, - color_theme: &'a ColorTheme, + ctx: Rc, } impl<'a> CommitUserCommand<'a> { - pub fn new(lines: &'a Vec>, color_theme: &'a ColorTheme) -> Self { - Self { lines, color_theme } + pub fn new(lines: &'a Vec>, ctx: Rc) -> Self { + Self { lines, ctx } } } @@ -85,11 +87,11 @@ impl CommitUserCommand<'_> { .cloned() .collect::>(); let paragraph = Paragraph::new(lines) - .style(Style::default().fg(self.color_theme.fg)) + .style(Style::default().fg(self.ctx.color_theme.fg)) .block( Block::default() .borders(Borders::TOP) - .style(Style::default().fg(self.color_theme.divider_fg)) + .style(Style::default().fg(self.ctx.color_theme.divider_fg)) .padding(Padding::horizontal(2)), ); paragraph.render(area, buf); diff --git a/src/widget/ref_list.rs b/src/widget/ref_list.rs index e84a0a4..7598581 100644 --- a/src/widget/ref_list.rs +++ b/src/widget/ref_list.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use ratatui::{ buffer::Buffer, layout::Rect, @@ -7,7 +9,7 @@ use ratatui::{ use semver::Version; use tui_tree_widget::{Tree, TreeItem, TreeState}; -use crate::{color::ColorTheme, git::Ref}; +use crate::{app::AppContext, color::ColorTheme, git::Ref}; const TREE_BRANCH_ROOT_IDENT: &str = "__branches__"; const TREE_REMOTE_ROOT_IDENT: &str = "__remotes__"; @@ -83,19 +85,19 @@ impl RefListState { } } -pub struct RefList<'a> { - items: Vec>, - color_theme: &'a ColorTheme, +pub struct RefList { + items: Vec>, + ctx: Rc, } -impl<'a> RefList<'a> { - pub fn new(refs: &'a [Ref], color_theme: &'a ColorTheme) -> RefList<'a> { - let items = build_ref_tree_items(refs, color_theme); - RefList { items, color_theme } +impl RefList { + pub fn new(refs: &[Ref], ctx: Rc) -> RefList { + let items = build_ref_tree_items(refs, &ctx.color_theme); + RefList { items, ctx } } } -impl StatefulWidget for RefList<'_> { +impl StatefulWidget for RefList { type State = RefListState; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { @@ -106,23 +108,20 @@ impl StatefulWidget for RefList<'_> { .node_no_children_symbol(" ") .highlight_style( Style::default() - .bg(self.color_theme.ref_selected_bg) - .fg(self.color_theme.ref_selected_fg), + .bg(self.ctx.color_theme.ref_selected_bg) + .fg(self.ctx.color_theme.ref_selected_fg), ) .block( Block::default() .borders(Borders::LEFT) - .style(Style::default().fg(self.color_theme.divider_fg)) + .style(Style::default().fg(self.ctx.color_theme.divider_fg)) .padding(Padding::horizontal(1)), ); tree.render(area, buf, &mut state.tree_state); } } -fn build_ref_tree_items<'a>( - refs: &'a [Ref], - color_theme: &'a ColorTheme, -) -> Vec> { +fn build_ref_tree_items(refs: &[Ref], color_theme: &ColorTheme) -> Vec> { let mut branch_refs = Vec::new(); let mut remote_refs = Vec::new(); let mut tag_refs = Vec::new(); @@ -237,7 +236,7 @@ fn refs_to_ref_tree_nodes(ref_names: Vec) -> Vec { fn ref_tree_nodes_to_tree_items( nodes: Vec, color_theme: &ColorTheme, -) -> Vec> { +) -> Vec> { let mut items = Vec::new(); for node in nodes { if node.children.is_empty() { @@ -286,12 +285,12 @@ fn parse_semantic_version_tag(tag: &str) -> Option { Version::parse(tag).ok() } -fn tree_item<'a>( +fn tree_item( identifier: String, name: String, - children: Vec>, - color_theme: &'a ColorTheme, -) -> TreeItem<'a, String> { + children: Vec>, + color_theme: &ColorTheme, +) -> TreeItem<'static, String> { TreeItem::new(identifier, name.fg(color_theme.fg), children).unwrap() } @@ -299,6 +298,6 @@ fn tree_leaf_item( identifier: String, name: String, color_theme: &ColorTheme, -) -> TreeItem<'_, String> { +) -> TreeItem<'static, String> { tree_item(identifier, name, Vec::new(), color_theme) }