diff --git a/CHANGELOG.md b/CHANGELOG.md index d5eb2b4488..c74edf3d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## Added +- check branch name validity while typing ([#559](https://github.com/extrawurst/gitui/issues/559)) + ## Fixed - do not allow to ignore .gitignore files ([#825](https://github.com/extrawurst/gitui/issues/825)) - crash in shallow repo ([#836](https://github.com/extrawurst/gitui/issues/836)) diff --git a/asyncgit/src/sync/branch/mod.rs b/asyncgit/src/sync/branch/mod.rs index 5bd364acc1..c1c84fe4d8 100644 --- a/asyncgit/src/sync/branch/mod.rs +++ b/asyncgit/src/sync/branch/mod.rs @@ -12,7 +12,7 @@ use crate::{ error::{Error, Result}, sync::{utils, CommitId}, }; -use git2::{BranchType, Repository}; +use git2::{Branch, BranchType, Repository}; use scopetime::scope_time; use utils::get_head_repo; @@ -90,6 +90,15 @@ impl BranchInfo { } } +/// +pub fn validate_branch_name(name: &str) -> Result { + scope_time!("validate_branch_name"); + + let valid = Branch::name_is_valid(name)?; + + Ok(valid) +} + /// returns a list of `BranchInfo` with a simple summary on each branch /// `local` filters for local branches otherwise remote branches will be returned pub fn get_branches_info( diff --git a/asyncgit/src/sync/mod.rs b/asyncgit/src/sync/mod.rs index 851d45b6ff..06caeecbb9 100644 --- a/asyncgit/src/sync/mod.rs +++ b/asyncgit/src/sync/mod.rs @@ -35,7 +35,7 @@ pub use branch::{ get_branches_info, merge_commit::merge_upstream_commit, merge_ff::branch_merge_upstream_fastforward, merge_rebase::merge_upstream_rebase, rename::rename_branch, - BranchCompare, BranchInfo, + validate_branch_name, BranchCompare, BranchInfo, }; pub use commit::{amend, commit, tag}; pub use commit_details::{ diff --git a/src/components/create_branch.rs b/src/components/create_branch.rs index 434e6abb60..485ec99fd1 100644 --- a/src/components/create_branch.rs +++ b/src/components/create_branch.rs @@ -12,12 +12,16 @@ use crate::{ use anyhow::Result; use asyncgit::{sync, CWD}; use crossterm::event::Event; -use tui::{backend::Backend, layout::Rect, Frame}; +use easy_cast::Cast; +use tui::{ + backend::Backend, layout::Rect, widgets::Paragraph, Frame, +}; pub struct CreateBranchComponent { input: TextInputComponent, queue: Queue, key_config: SharedKeyConfig, + theme: SharedTheme, } impl DrawableComponent for CreateBranchComponent { @@ -26,7 +30,10 @@ impl DrawableComponent for CreateBranchComponent { f: &mut Frame, rect: Rect, ) -> Result<()> { - self.input.draw(f, rect)?; + if self.is_visible() { + self.input.draw(f, rect)?; + self.draw_warnings(f); + } Ok(()) } @@ -95,12 +102,13 @@ impl CreateBranchComponent { Self { queue, input: TextInputComponent::new( - theme, + theme.clone(), key_config.clone(), &strings::create_branch_popup_title(&key_config), &strings::create_branch_popup_msg(&key_config), true, ), + theme, key_config, } } @@ -134,4 +142,35 @@ impl CreateBranchComponent { } } } + + fn draw_warnings(&self, f: &mut Frame) { + let current_text = self.input.get_text(); + + if !current_text.is_empty() { + let valid = sync::validate_branch_name(current_text) + .unwrap_or_default(); + + if !valid { + let msg = strings::branch_name_invalid(); + let msg_length: u16 = msg.len().cast(); + let w = Paragraph::new(msg) + .style(self.theme.text_danger()); + + let rect = { + let mut rect = self.input.get_area(); + rect.y += rect.height.saturating_sub(1); + rect.height = 1; + let offset = + rect.width.saturating_sub(msg_length + 1); + rect.width = + rect.width.saturating_sub(offset + 1); + rect.x += offset; + + rect + }; + + f.render_widget(w, rect); + } + } + } } diff --git a/src/strings.rs b/src/strings.rs index cc1e14ab22..abbf057f99 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -83,6 +83,9 @@ pub fn commit_msg(_key_config: &SharedKeyConfig) -> String { pub fn commit_first_line_warning(count: usize) -> String { format!("[subject length: {}]", count) } +pub const fn branch_name_invalid() -> &'static str { + "[invalid name]" +} pub fn commit_editor_msg(_key_config: &SharedKeyConfig) -> String { r##" # Edit your commit message