Skip to content

Commit

Permalink
Show help from keybind
Browse files Browse the repository at this point in the history
  • Loading branch information
yanganto committed Aug 4, 2024
1 parent d89007b commit 53622e4
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 128 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ semver = "1.0.23"
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
sha1 = "0.10.6"
strum = "0.26.3"
toml = "0.8.16"
tui-input = "0.9.0"
tui-tree-widget = "0.21.0"
Expand Down
2 changes: 2 additions & 0 deletions assets/default-keybind.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ CloseOrCancel = ["esc"]
HelpToggle = ["?"]
GoToTop = ["g"]
GoToBottom = ["shift-g"]
GoToNext = ["n"]
GoToPrevious = ["shift-N"]
ScrollDown = ["ctrl-e"]
ScrollUp = ["ctrl-y"]
PageUp = ["ctrl-b"]
Expand Down
7 changes: 6 additions & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,12 @@ impl App<'_> {

fn open_help(&mut self) {
let before_view = std::mem::take(&mut self.view);
self.view = View::of_help(before_view, self.image_protocol, self.tx.clone());
self.view = View::of_help(
before_view,
self.image_protocol,
self.tx.clone(),
self.keybind,
);
}

fn close_help(&mut self) {
Expand Down
29 changes: 28 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::{

use ratatui::crossterm::event::KeyEvent;
use serde::Deserialize;
use strum::{EnumIter, EnumMessage};

pub enum AppEvent {
Key(KeyEvent),
Expand Down Expand Up @@ -83,31 +84,57 @@ pub fn init() -> (Sender, Receiver) {
}

/// The event triggered by user's key input
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, strum::Display, Deserialize, EnumIter, Eq, EnumMessage, Hash, PartialEq)]
pub enum UserEvent {
// NOTE User Event should have document, else the enum item will be hidden in the help page
/// Navigate up
NavigateUp,
/// Navigate down
NavigateDown,
/// Navigate right
NavigateRight,
/// Navigate left
NavigateLeft,
/// Quit serie
Quit,
/// Close widget or cancel current progress
CloseOrCancel,
/// Toggle Help page
HelpToggle,
/// Go to top
GoToTop,
/// Go to bottom
GoToBottom,
/// Go to next item
GoToNext,
/// Go to previous item
GoToPrevious,
/// Scroll one line up
ScrollUp,
/// Scroll one line down
ScrollDown,
/// Scroll one page up
PageUp,
/// Scroll one page down
PageDown,
/// Scroll half page up
HalfPageUp,
/// Scroll half page down
HalfPageDown,
/// Select top part
SelectTop,
/// Select middle part
SelectMiddle,
/// Select bottom part
SelectBottom,
/// Show details
ShowDetails,
/// Search
Search,
/// Copy part of content
ShortCopy,
/// Copy
FullCopy,
/// Toggle for Reference List
RefListToggle,
}
71 changes: 71 additions & 0 deletions src/keybind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ impl KeyBind {
// TODO: let user patch key bind here
Ok(keybind)
}

pub fn keys_for_event(&self, user_event: &UserEvent) -> Vec<String> {
self.0
.iter()
.filter(|(_, ue)| *ue == user_event)
.map(|(ke, _)| key_event_to_string(ke))
.collect()
}
}

impl<'de> Deserialize<'de> for KeyBind {
Expand Down Expand Up @@ -140,3 +148,66 @@ fn parse_key_code_with_modifiers(
};
Ok(KeyEvent::new(c, modifiers))
}

pub fn key_event_to_string(key_event: &KeyEvent) -> String {
let char;
let key_code = match key_event.code {
KeyCode::Backspace => "backspace",
KeyCode::Enter => "enter",
KeyCode::Left => "left",
KeyCode::Right => "right",
KeyCode::Up => "up",
KeyCode::Down => "down",
KeyCode::Home => "home",
KeyCode::End => "end",
KeyCode::PageUp => "pageup",
KeyCode::PageDown => "pagedown",
KeyCode::Tab => "tab",
KeyCode::BackTab => "backtab",
KeyCode::Delete => "delete",
KeyCode::Insert => "insert",
KeyCode::F(c) => {
char = format!("f({c})");
&char
}
KeyCode::Char(' ') => "space",
KeyCode::Char(c) => {
char = c.to_string();
&char
}
KeyCode::Esc => "esc",
KeyCode::Null => "",
KeyCode::CapsLock => "",
KeyCode::Menu => "",
KeyCode::ScrollLock => "",
KeyCode::Media(_) => "",
KeyCode::NumLock => "",
KeyCode::PrintScreen => "",
KeyCode::Pause => "",
KeyCode::KeypadBegin => "",
KeyCode::Modifier(_) => "",
};

let mut modifiers = Vec::with_capacity(3);

if key_event.modifiers.intersects(KeyModifiers::CONTROL) {
modifiers.push("ctrl");
}

if key_event.modifiers.intersects(KeyModifiers::SHIFT) {
modifiers.push("shift");
}

if key_event.modifiers.intersects(KeyModifiers::ALT) {
modifiers.push("alt");
}

let mut key = modifiers.join("-");

if !key.is_empty() {
key.push('-');
}
key.push_str(key_code);

format!("<{key}>")
}
148 changes: 24 additions & 124 deletions src/view/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ use ratatui::{

use crate::{
event::{AppEvent, Sender, UserEvent},
keybind::KeyBind,
protocol::ImageProtocol,
view::View,
};

use strum::{EnumMessage, IntoEnumIterator};

const BLOCK_TITLE_COLOR: Color = Color::Green;
const KEY_COLOR: Color = Color::Yellow;

Expand All @@ -30,8 +33,13 @@ pub struct HelpView<'a> {
}

impl HelpView<'_> {
pub fn new(before: View, image_protocol: ImageProtocol, tx: Sender) -> HelpView {
let (help_key_lines, help_value_lines) = build_lines();
pub fn new<'a>(
before: View<'a>,
image_protocol: ImageProtocol,
tx: Sender,
keybind: &'a KeyBind,
) -> HelpView<'a> {
let (help_key_lines, help_value_lines) = build_lines(keybind);
HelpView {
before,
help_key_lines,
Expand Down Expand Up @@ -124,112 +132,28 @@ impl<'a> HelpView<'a> {
}

#[rustfmt::skip]
fn build_lines() -> (Vec<Line<'static>>, Vec<Line<'static>>) {
let (common_key_lines, common_value_lines) = build_block_lines(
"Common:",
&[
(&["Ctrl-c", "q"], "Quit app"),
(&["?"], "Open help"),
]
);
let (help_key_lines, help_value_lines) = build_block_lines(
"Help:",
&[
(&["Esc", "Backspace", "?"], "Close help"),
(&["Down", "j"], "Scroll down"),
(&["Up", "k"], "Scroll up"),
]
);
let (list_key_lines, list_value_lines) = build_block_lines(
"Commit List:",
&[
(&["Down", "j"], "Move down"),
(&["Up", "k"], "Move up"),
(&["g"], "Go to top"),
(&["G"], "Go to bottom"),
(&["Ctrl-f"], "Scroll page down"),
(&["Ctrl-b"], "Scroll page up"),
(&["Ctrl-d"], "Scroll half page down"),
(&["Ctrl-u"], "Scroll half page up"),
(&["H"], "Select top of the screen"),
(&["M"], "Select middle of the screen"),
(&["L"], "Select bottom of the screen"),
(&["Enter"], "Show commit details"),
(&["Tab"], "Open refs list"),
(&["/"], "Start search"),
(&["Esc"], "Cancel search"),
(&["n"], "Go to next search match"),
(&["N"], "Go to previous search match"),
(&["c"], "Copy commit short hash"),
(&["C"], "Copy commit hash"),
]
);
let (detail_key_lines, detail_value_lines) = build_block_lines(
"Commit Detail:",
&[
(&["Esc", "Backspace"], "Close commit details"),
(&["Down", "j"], "Scroll down"),
(&["Up", "k"], "Scroll up"),
(&["c"], "Copy commit short hash"),
(&["C"], "Copy commit hash"),
]
);
let (refs_key_lines, refs_value_lines) = build_block_lines(
"Refs List:",
&[
(&["Esc", "Backspace", "Tab"], "Close refs list"),
(&["Down", "j"], "Move down"),
(&["Up", "k"], "Move up"),
(&["g"], "Go to top"),
(&["G"], "Go to bottom"),
(&["Right", "l"], "Open node"),
(&["Left", "h"], "Close node"),
(&["c"], "Copy ref name"),
]
);

let key_lines = join_line_groups_with_empty(vec![
common_key_lines,
help_key_lines,
list_key_lines,
detail_key_lines,
refs_key_lines,
]);
let value_lines = join_line_groups_with_empty(vec![
common_value_lines,
help_value_lines,
list_value_lines,
detail_value_lines,
refs_value_lines,
]);

(key_lines, value_lines)
}

fn build_block_lines(
title: &'static str,
keybindings: &[(&[&'static str], &'static str)],
) -> (Vec<Line<'static>>, Vec<Line<'static>>) {
fn build_lines(keybind: &KeyBind) -> (Vec<Line<'static>>, Vec<Line<'static>>) {
let mut event_key_maps = Vec::new();
for user_event in UserEvent::iter() {
let key_events: String = keybind.keys_for_event(&user_event).join(" ");
event_key_maps.push((user_event.get_documentation(), key_events));
}
let mut key_lines = Vec::new();
let mut value_lines = Vec::new();

let key_title_lines = vec![Line::from(title)
let key_title_lines = vec![Line::from("Help")
.fg(BLOCK_TITLE_COLOR)
.add_modifier(Modifier::BOLD)];
let value_title_lines = vec![Line::from("")];
let key_binding_lines: Vec<Line> = keybindings
.iter()
.map(|(keys, _)| {
join_span_groups_with_space(
keys.iter()
.map(|key| vec!["<".into(), key.fg(KEY_COLOR), ">".into()])
.collect(),
)
let key_binding_lines: Vec<Line> = event_key_maps.clone()
.into_iter()
.map(|(_, keys)| {
Line::from(Span::raw(keys)).fg(KEY_COLOR)
})
.collect();
let value_binding_lines: Vec<Line> = keybindings
.iter()
.map(|(_, value)| Line::from(*value))
let value_binding_lines: Vec<Line> = event_key_maps
.into_iter()
.filter_map(|(user_event, _)| user_event.map(Line::from))
.collect();

key_lines.extend(key_title_lines);
Expand All @@ -239,27 +163,3 @@ fn build_block_lines(

(key_lines, value_lines)
}

fn join_line_groups_with_empty(line_groups: Vec<Vec<Line<'static>>>) -> Vec<Line<'static>> {
let mut result = Vec::new();
let n = line_groups.len();
for (i, lines) in line_groups.into_iter().enumerate() {
result.extend(lines);
if i < n - 1 {
result.push(Line::raw(""));
}
}
result
}

fn join_span_groups_with_space(span_groups: Vec<Vec<Span<'static>>>) -> Line<'static> {
let mut spans: Vec<Span> = Vec::new();
let n = span_groups.len();
for (i, ss) in span_groups.into_iter().enumerate() {
spans.extend(ss);
if i < n - 1 {
spans.push(Span::raw(" "));
}
}
Line::from(spans)
}
10 changes: 8 additions & 2 deletions src/view/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
config::Config,
event::{Sender, UserEvent},
git::{Commit, FileChange, Ref},
keybind::KeyBind,
protocol::ImageProtocol,
view::{detail::DetailView, help::HelpView, list::ListView, refs::RefsView},
widget::commit_list::CommitListState,
Expand Down Expand Up @@ -79,7 +80,12 @@ impl<'a> View<'a> {
View::Refs(Box::new(RefsView::new(commit_list_state, refs, config, tx)))
}

pub fn of_help(before: View<'a>, image_protocol: ImageProtocol, tx: Sender) -> Self {
View::Help(Box::new(HelpView::new(before, image_protocol, tx)))
pub fn of_help(
before: View<'a>,
image_protocol: ImageProtocol,
tx: Sender,
keybind: &'a KeyBind,
) -> Self {
View::Help(Box::new(HelpView::new(before, image_protocol, tx, keybind)))
}
}

0 comments on commit 53622e4

Please sign in to comment.