Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/config/keybindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,16 @@ pub struct UniversalKeybinding {
pub prev_screen_mode: String,
#[serde(rename = "createPatchOptionsMenu")]
pub create_patch_options_menu: String,
#[serde(rename = "prevRevertBlock")]
pub prev_revert_block: String,
#[serde(rename = "nextRevertBlock")]
pub next_revert_block: String,
#[serde(rename = "revertBlock")]
pub revert_block: String,
#[serde(rename = "undoRevertBlock")]
pub undo_revert_block: String,
#[serde(rename = "stageBlock")]
pub stage_block: String,
}

impl Default for UniversalKeybinding {
Expand Down Expand Up @@ -161,8 +167,11 @@ impl Default for UniversalKeybinding {
next_screen_mode: "+".into(),
prev_screen_mode: "_".into(),
create_patch_options_menu: "<c-p>".into(),
revert_block: "<enter>".into(),
prev_revert_block: "{".into(),
next_revert_block: "}".into(),
revert_block: "r".into(),
undo_revert_block: "u".into(),
stage_block: "a".into(),
}
}
}
Expand Down Expand Up @@ -393,10 +402,7 @@ pub fn parse_key(s: &str) -> Option<KeyEvent> {
// Ctrl modifier
if let Some(key) = inner.strip_prefix("c-") {
let ch = key.chars().next()?;
return Some(KeyEvent::new(
KeyCode::Char(ch),
KeyModifiers::CONTROL,
));
return Some(KeyEvent::new(KeyCode::Char(ch), KeyModifiers::CONTROL));
}

// Alt modifier
Expand All @@ -411,9 +417,7 @@ pub fn parse_key(s: &str) -> Option<KeyEvent> {
"escape" | "esc" => Some(KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE)),
"tab" => Some(KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE)),
"backtab" | "shift-tab" => Some(KeyEvent::new(KeyCode::BackTab, KeyModifiers::SHIFT)),
"backspace" | "bs" => {
Some(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE))
}
"backspace" | "bs" => Some(KeyEvent::new(KeyCode::Backspace, KeyModifiers::NONE)),
"delete" | "del" => Some(KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE)),
"space" => Some(KeyEvent::new(KeyCode::Char(' '), KeyModifiers::NONE)),
"up" => Some(KeyEvent::new(KeyCode::Up, KeyModifiers::NONE)),
Expand Down
2 changes: 1 addition & 1 deletion src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use anyhow::Result;
pub use app_state::AppState;
pub use keybindings::KeybindingConfig;
pub use theme::{Theme, ColorTheme, COLOR_THEMES};
pub use user_config::UserConfig;
pub use user_config::{HunkMarkerConfig, UserConfig, parse_optional_color};

pub fn config_dir_candidates() -> Vec<PathBuf> {
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
Expand Down
53 changes: 53 additions & 0 deletions src/config/user_config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::Path;

use anyhow::Result;
use ratatui::style::Color;
use serde::{Deserialize, Serialize};

use super::keybindings::KeybindingConfig;
Expand Down Expand Up @@ -91,6 +92,8 @@ pub struct GuiConfig {
pub show_bottom_line: bool,
#[serde(rename = "nerdFontsVersion")]
pub nerd_fonts_version: String,
#[serde(rename = "revertHunkMarker")]
pub hunk_marker: HunkMarkerConfig,
}

impl Default for GuiConfig {
Expand All @@ -106,10 +109,60 @@ impl Default for GuiConfig {
show_command_log: true,
show_bottom_line: true,
nerd_fonts_version: "3".to_string(),
hunk_marker: HunkMarkerConfig::default(),
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct HunkMarkerConfig {
pub icon: String,
pub bold: Option<bool>,
pub color: Option<String>,
#[serde(rename = "selectedColor")]
pub selected_color: Option<String>,
#[serde(rename = "hoverColor")]
pub hover_color: Option<String>,
}

impl Default for HunkMarkerConfig {
fn default() -> Self {
Self {
icon: "│".to_string(),
bold: Some(true),
color: None,
selected_color: None,
hover_color: None,
}
}
}

pub fn parse_optional_color(value: Option<&str>) -> Option<Color> {
let value = value?.trim();
if value.is_empty() {
return None;
}
match value.to_lowercase().as_str() {
"default" => None,
"black" => Some(Color::Black),
"red" => Some(Color::Red),
"green" => Some(Color::Green),
"yellow" => Some(Color::Yellow),
"blue" => Some(Color::Blue),
"magenta" => Some(Color::Magenta),
"cyan" => Some(Color::Cyan),
"white" => Some(Color::White),
s if s.starts_with('#') && s.len() == 7 => {
let r = u8::from_str_radix(&s[1..3], 16).ok()?;
let g = u8::from_str_radix(&s[3..5], 16).ok()?;
let b = u8::from_str_radix(&s[5..7], 16).ok()?;
Some(Color::Rgb(r, g, b))
}
_ => None,
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ThemeConfig {
Expand Down
31 changes: 29 additions & 2 deletions src/git/staging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ impl GitCommands {

/// Stage a specific hunk by applying it as a patch.
pub fn stage_hunk(&self, file_path: &str, hunk: &DiffHunk) -> Result<()> {
let patch = build_patch(file_path, hunk);
let patch = self.build_hunk_patch(file_path, hunk);
self.git()
.args(&["apply", "--cached", "--unidiff-zero", "-"])
.stdin(patch)
Expand All @@ -60,7 +60,7 @@ impl GitCommands {

/// Unstage a specific hunk by reverse-applying it as a patch.
pub fn unstage_hunk(&self, file_path: &str, hunk: &DiffHunk) -> Result<()> {
let patch = build_patch(file_path, hunk);
let patch = self.build_hunk_patch(file_path, hunk);
self.git()
.args(&["apply", "--cached", "--reverse", "--unidiff-zero", "-"])
.stdin(patch)
Expand Down Expand Up @@ -100,6 +100,33 @@ impl GitCommands {
.with_context(|| format!("failed to revert hunk in {}", file_path))?;
Ok(())
}

pub fn build_visual_block_patch_text(
&self,
file_path: &str,
unified_diff: &str,
want_old: Option<(usize, usize)>,
want_new: Option<(usize, usize)>,
) -> Result<String> {
build_visual_block_patch(file_path, unified_diff, want_old, want_new)
}

pub fn build_hunk_patch(&self, file_path: &str, hunk: &DiffHunk) -> String {
build_patch(file_path, hunk)
}

pub fn apply_patch_text(&self, patch: String, cached: bool, reverse: bool) -> Result<()> {
let mut args = vec!["apply"];
if cached {
args.push("--cached");
}
if reverse {
args.push("--reverse");
}
args.extend(["--unidiff-zero", "-"]);
self.git().args(&args).stdin(patch).run_expecting_success()?;
Ok(())
}
}

fn build_visual_block_patch(
Expand Down
10 changes: 3 additions & 7 deletions src/gui/controller/diff_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,12 +515,6 @@ fn handle_diff_exploration_key(gui: &mut Gui, key: KeyEvent) -> Result<()> {
KeyCode::Char('l') | KeyCode::Right => {
gui.diff_view.scroll_right(4);
}
KeyCode::Char('}') => {
gui.diff_view.next_hunk();
}
KeyCode::Char('{') => {
gui.diff_view.prev_hunk();
}
KeyCode::Char(']') => {
use crate::pager::side_by_side::DiffSideView;
gui.diff_view.side_view = match gui.diff_view.side_view {
Expand Down Expand Up @@ -712,7 +706,9 @@ fn show_diff_mode_help(gui: &mut Gui) {
HelpEntry { key: "<enter>".into(), description: "Edit selector / Focus diff".into() },
HelpEntry { key: "`".into(), description: "Toggle file tree view".into() },
HelpEntry { key: "j/k".into(), description: "Navigate files / Scroll diff".into() },
HelpEntry { key: "{/}".into(), description: "Previous / next hunk".into() },
HelpEntry { key: "H".into(), description: "Enter hunk mode".into() },
HelpEntry { key: "j/k".into(), description: "Cycle hunks in hunk mode".into() },
HelpEntry { key: "esc".into(), description: "Exit hunk mode".into() },
HelpEntry { key: "[/]".into(), description: "Toggle old / new only view".into() },
HelpEntry { key: "z".into(), description: "Toggle line wrap".into() },
HelpEntry { key: "g/G".into(), description: "Go to top / bottom".into() },
Expand Down
Loading
Loading