diff --git a/CHANGELOG.md b/CHANGELOG.md index 3491631de5..761d5ec42d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +**emojified commit message** + +![emojified-commit-message](assets/emojified-commit-message.png) + +## Added +- added support for markdown emoji's in commits [[@andrewpollack](https://github.com/andrewpollack)] ([#768](https://github.com/extrawurst/gitui/issues/768)) + ## Fixed - fix commit msg being broken inside tag list ([#871](https://github.com/extrawurst/gitui/issues/871)) diff --git a/Cargo.lock b/Cargo.lock index cbc0fa7806..7e2e0edc3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,6 +421,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gh-emoji" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6af39cf9a679d7195b3370f5454381ba49c4791bc7ce3ae2a7bf1a2a89c7adf" +dependencies = [ + "phf", + "regex", +] + [[package]] name = "gimli" version = "0.25.0" @@ -481,6 +491,7 @@ dependencies = [ "dirs-next", "easy-cast", "filetreelist", + "gh-emoji", "itertools", "lazy_static", "log", @@ -896,6 +907,24 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "phf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + [[package]] name = "pkg-config" version = "0.3.19" @@ -1242,6 +1271,12 @@ dependencies = [ "log", ] +[[package]] +name = "siphasher" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1" + [[package]] name = "smallvec" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index 74f269fa9a..dbba85bfa6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ easy-cast = "0.4" bugreport = "0.4" lazy_static = "1.4" syntect = { version = "4.5", default-features = false, features = ["metadata", "default-fancy"]} +gh-emoji = "1.0.6" [target.'cfg(all(target_family="unix",not(target_os="macos")))'.dependencies] which = "4.1" diff --git a/assets/emojified-commit-message.png b/assets/emojified-commit-message.png new file mode 100644 index 0000000000..5333c0f2e5 Binary files /dev/null and b/assets/emojified-commit-message.png differ diff --git a/src/components/commitlist.rs b/src/components/commitlist.rs index 78f227da7d..382019a73d 100644 --- a/src/components/commitlist.rs +++ b/src/components/commitlist.rs @@ -301,6 +301,7 @@ impl CommitList { Cow::from(e.msg.as_str()), theme.text(true, selected), )); + Spans::from(txt) } diff --git a/src/components/utils/logitems.rs b/src/components/utils/logitems.rs index 7008319ac1..9f21d3261b 100644 --- a/src/components/utils/logitems.rs +++ b/src/components/utils/logitems.rs @@ -2,6 +2,8 @@ use asyncgit::sync::{CommitId, CommitInfo}; use chrono::{DateTime, Duration, Local, NaiveDateTime, Utc}; use std::slice::Iter; +use crate::components::utils::emojifi_string; + static SLICE_OFFSET_RELOAD_THRESHOLD: usize = 100; pub struct LogEntry { @@ -19,9 +21,15 @@ impl From for LogEntry { NaiveDateTime::from_timestamp(c.time, 0), Utc, )); + + // Replace markdown emojis with Unicode equivalent + let author = c.author; + let mut msg = c.message; + emojifi_string(&mut msg); + Self { - author: c.author, - msg: c.message, + author, + msg, time, hash_short: c.id.get_short_string(), id: c.id, @@ -98,3 +106,41 @@ impl ItemBatch { needs_data_bottom || needs_data_top } } + +#[cfg(test)] +mod tests { + use super::*; + + fn test_conversion(s: &str) -> String { + let mut s = s.to_string(); + emojifi_string(&mut s); + s + } + + #[test] + fn test_emojifi_string_conversion_cases() { + assert_eq!( + &test_conversion("It's :hammer: time!"), + "It's 🔨 time!" + ); + assert_eq!( + &test_conversion(":red_circle::orange_circle::yellow_circle::green_circle::large_blue_circle::purple_circle:"), + "🔴🟠🟡đŸŸĸđŸ”ĩđŸŸŖ" + ); + assert_eq!( + &test_conversion("It's raining :cat:s and :dog:s"), + "It's raining 🐱s and đŸļs" + ); + assert_eq!(&test_conversion(":crab: rules!"), "đŸĻ€ rules!"); + } + + #[test] + fn test_emojifi_string_no_conversion_cases() { + assert_eq!(&test_conversion("123"), "123"); + assert_eq!( + &test_conversion("This :should_not_convert:"), + "This :should_not_convert:" + ); + assert_eq!(&test_conversion(":gopher:"), ":gopher:"); + } +} diff --git a/src/components/utils/mod.rs b/src/components/utils/mod.rs index ab45bbea57..da2208ae2f 100644 --- a/src/components/utils/mod.rs +++ b/src/components/utils/mod.rs @@ -1,4 +1,6 @@ use chrono::{DateTime, Local, NaiveDateTime, Utc}; +use lazy_static::lazy_static; +use std::borrow::Cow; use unicode_width::UnicodeWidthStr; pub mod filetree; @@ -53,6 +55,21 @@ pub fn string_width_align(s: &str, width: usize) -> String { } } +lazy_static! { + static ref EMOJI_REPLACER: gh_emoji::Replacer = + gh_emoji::Replacer::new(); +} + +// Replace markdown emojis with Unicode equivalent +// :hammer: --> 🔨 +#[inline] +pub fn emojifi_string(s: &mut String) { + let resulting_cow = EMOJI_REPLACER.replace_all(s); + if let Cow::Owned(altered_s) = resulting_cow { + *s = altered_s; + } +} + #[inline] fn find_truncate_point(s: &str, chars: usize) -> usize { s.chars().take(chars).map(char::len_utf8).sum()