Skip to content

Commit

Permalink
refactor(tui): update generation mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
orhun committed Dec 28, 2024
1 parent 2abfe36 commit 3d3a2dd
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 67 deletions.
18 changes: 12 additions & 6 deletions git-cliff-tui/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::state::{
Config,
Result,
State,
};
Expand All @@ -15,7 +16,9 @@ use std::sync::mpsc;
#[non_exhaustive]
pub enum Event {
/// Generate changelog.
Generate(bool),
Generate,
/// Update the changelog data.
UpdateChangelog(Vec<Config>),
/// Quit the application.
Quit,
}
Expand All @@ -35,7 +38,12 @@ pub fn handle_key_events(
sender.send(Event::Quit)?;
} else {
if let Some(clipboard) = &mut state.clipboard {
if let Err(e) = clipboard.set_contents(state.contents.clone()) {
let contents = state
.list_state
.selected()
.map(|i| state.configs[i].contents.clone())
.unwrap_or_default();
if let Err(e) = clipboard.set_contents(contents) {
return Err(format!(
"Failed to set clipboard contents: {e}"
)
Expand All @@ -46,11 +54,9 @@ pub fn handle_key_events(
}
KeyCode::Char('k') | KeyCode::Char('K') | KeyCode::Up => {
state.list_state.select_previous();
sender.send(Event::Generate(false))?;
}
KeyCode::Char('j') | KeyCode::Char('J') | KeyCode::Down => {
state.list_state.select_next();
sender.send(Event::Generate(false))?;
}
KeyCode::Char('h') | KeyCode::Char('H') | KeyCode::Left => {
state.scroll_index = state.scroll_index.saturating_sub(1);
Expand All @@ -60,10 +66,10 @@ pub fn handle_key_events(
// state.args.latest = !state.args.latest;
// sender.send(Event::Generate(true))?;
}
KeyCode::Enter => sender.send(Event::Generate(false))?,
KeyCode::Enter => sender.send(Event::Generate)?,
KeyCode::Char('u') | KeyCode::Char('U') => {
state.args.unreleased = !state.args.unreleased;
sender.send(Event::Generate(true))?;
sender.send(Event::Generate)?;
}
_ => {}
}
Expand Down
11 changes: 7 additions & 4 deletions git-cliff-tui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,22 @@ fn main() -> Result<()> {
let args = Args::parse();

// Create an application state.
let mut state = State::new(args.clone())?;
let (sender, receiver) = mpsc::channel::<Event>();
let mut state = State::new(args.clone(), sender.clone())?;

// Initialize the terminal user interface.
let (sender, receiver) = mpsc::channel::<Event>();
let mut terminal = ratatui::init();

// Start the main loop.
loop {
terminal.draw(|frame| ui::render(&mut state, frame))?;
if let Ok(event) = receiver.try_recv() {
match event {
Event::Generate(update_data) => {
state.generate_changelog(update_data)?;
Event::Generate => {
state.generate_changelog()?;
}
Event::UpdateChangelog(contents) => {
state.configs = contents;
}
Event::Quit => break,
}
Expand Down
110 changes: 64 additions & 46 deletions git-cliff-tui/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,67 @@ use git_cliff::args::Args;
use git_cliff::core::changelog::Changelog;
use git_cliff::core::embed::BuiltinConfig;
use ratatui::widgets::ListState;
use std::error;
use std::sync::mpsc;
use std::{
error,
thread,
};
use tachyonfx::Effect;
use throbber_widgets_tui::ThrobberState;

use crate::event::Event;
use crate::logo::Logo;

/// Application result type.
pub type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

#[derive(Debug, Clone, PartialEq)]
pub struct Config {
pub name: String,
pub contents: String,
}

/// Application state.
pub struct State<'a> {
pub struct State {
/// git-cliff arguments.
pub args: Args,
pub args: Args,
/// Built-in configuration files.
pub builtin_configs: Vec<String>,
pub configs: Vec<Config>,
/// The state of the list.
pub list_state: ListState,
/// Changelog object.
changelog: Changelog<'a>,
/// Changelog contents.
pub contents: String,
pub list_state: ListState,
/// Event sender.
pub sender: mpsc::Sender<Event>,
/// Scroll index.
pub scroll_index: usize,
pub scroll_index: usize,
/// Clipboard context.
pub clipboard: Option<ClipboardContext>,
pub clipboard: Option<ClipboardContext>,
/// Throbber state.
pub throbber_state: ThrobberState,
pub throbber_state: ThrobberState,
/// Is generating?
pub is_generating: bool,
pub is_generating: bool,
/// Logo widget.
pub logo: Logo,
pub logo: Logo,
/// Border effect.
pub border_effect: Option<Effect>,
pub border_effect: Option<Effect>,
}

impl State<'_> {
impl State {
/// Constructs a new instance.
pub fn new(args: Args) -> Result<Self> {
let configs = BuiltinConfig::iter().map(|file| file.to_string()).collect();
let mut state = Self {
builtin_configs: configs,
pub fn new(args: Args, sender: mpsc::Sender<Event>) -> Result<Self> {
let configs = BuiltinConfig::iter()
.map(|file| Config {
name: file.to_string(),
contents: String::new(),
})
.collect();
let state = Self {
configs,
list_state: {
let mut list_state = ListState::default();
list_state.select_first();
list_state
},
changelog: git_cliff::generate_changelog(&mut args.clone())?,
contents: String::new(),
sender,
scroll_index: 0,
throbber_state: ThrobberState::default(),
clipboard: match ClipboardContext::new() {
Expand All @@ -65,35 +78,40 @@ impl State<'_> {
args,
border_effect: None,
};
state.generate_changelog(false)?;
state.generate_changelog()?;
Ok(state)
}

/// Returns the changelog contents.
pub fn generate_changelog(&mut self, update_data: bool) -> Result<String> {
let config = BuiltinConfig::parse(
self.builtin_configs[self.list_state.selected().unwrap_or_default()]
.clone(),
)?
.0;

if update_data {
self.changelog = git_cliff::generate_changelog(&mut self.args.clone())?;
}
let mut changelog =
Changelog::new(self.changelog.releases.clone(), config.clone())?;
changelog.add_remote_context()?;
let mut output = Vec::new();
git_cliff::write_changelog(
self.args.clone(),
changelog.clone(),
&mut output,
)?;
let contents = String::from_utf8(output)?;
pub fn generate_changelog(&self) -> Result<()> {
let sender = self.sender.clone();
let args = self.args.clone();
let mut configs = self.configs.clone();
thread::spawn(move || {
let run = || -> Result<()> {
let releases =
git_cliff::generate_changelog(&mut args.clone())?.releases;
for config in configs.iter_mut() {
let builtin_config =
BuiltinConfig::parse(config.name.clone())?.0;
let mut changelog =
Changelog::new(releases.clone(), builtin_config)?;
changelog.add_remote_context()?;
let mut output = Vec::new();
git_cliff::write_changelog(
args.clone(),
changelog.clone(),
&mut output,
)?;
config.contents = String::from_utf8(output)?;
}
sender.send(Event::UpdateChangelog(configs))?;
Ok(())
};
run().expect("failed to generate changelog");
});

self.changelog = changelog;
self.contents = contents.clone();
Ok(contents)
Ok(())
}

/// Handles the tick event of the terminal.
Expand Down
26 changes: 15 additions & 11 deletions git-cliff-tui/src/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ pub fn render(state: &mut State, frame: &mut Frame) {
let rects = Layout::horizontal([
Constraint::Min(
state
.builtin_configs
.configs
.iter()
.map(|c| c.len() as u16)
.map(|c| c.name.len() as u16)
.map(|c| c + 6)
.max()
.unwrap_or_default(),
Expand All @@ -72,11 +72,11 @@ fn render_logo(state: &mut State, frame: &mut Frame) {
}

fn render_list(state: &mut State, frame: &mut Frame, area: Rect) {
if !state.builtin_configs.is_empty() {
if !state.configs.is_empty() {
let items = state
.builtin_configs
.configs
.iter()
.map(|c| ListItem::new(c.to_string()))
.map(|c| ListItem::new(c.name.to_string()))
.collect::<Vec<ListItem>>();
let list = List::new(items)
.block(
Expand All @@ -97,13 +97,18 @@ fn render_list(state: &mut State, frame: &mut Frame, area: Rect) {
vertical: 1,
horizontal: 0,
}),
&mut ScrollbarState::new(state.builtin_configs.len())
&mut ScrollbarState::new(state.configs.len())
.position(state.list_state.selected().unwrap_or_default()),
);
}
}

fn render_changelog(state: &mut State, frame: &mut Frame, area: Rect) {
let contents = state
.list_state
.selected()
.map(|i| state.configs[i].contents.clone())
.unwrap_or_default();
frame.render_widget(
Block::bordered()
.title_top("|Changelog|".yellow().into_left_aligned_line())
Expand All @@ -114,13 +119,13 @@ fn render_changelog(state: &mut State, frame: &mut Frame, area: Rect) {
"> Generating...".white(),
"|".fg(Color::Rgb(100, 100, 100)),
]
} else if !state.contents.is_empty() {
} else if !contents.is_empty() {
vec![
"|".fg(Color::Rgb(100, 100, 100)),
state
.list_state
.selected()
.map(|i| state.builtin_configs[i].clone())
.map(|i| state.configs[i].name.clone())
.unwrap_or_default()
.white()
.italic(),
Expand Down Expand Up @@ -161,8 +166,7 @@ fn render_changelog(state: &mut State, frame: &mut Frame, area: Rect) {
);
frame.render_widget(
tui_markdown::from_str(
&state
.contents
&contents
.lines()
.skip(state.scroll_index)
.collect::<Vec<&str>>()
Expand All @@ -182,7 +186,7 @@ fn render_changelog(state: &mut State, frame: &mut Frame, area: Rect) {
vertical: 1,
horizontal: 0,
}),
&mut ScrollbarState::new(state.contents.len()).position(state.scroll_index),
&mut ScrollbarState::new(contents.len()).position(state.scroll_index),
);

if state.is_generating {
Expand Down

0 comments on commit 3d3a2dd

Please sign in to comment.