From 7f752da89d4fa963d05ee3104ec55bfac8208351 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Mon, 16 Jun 2025 10:36:27 -0700 Subject: [PATCH 1/7] feat: add regex option for username_display --- src/config.rs | 21 +++++++++++++++++++++ src/tests.rs | 1 + 2 files changed, 22 insertions(+) diff --git a/src/config.rs b/src/config.rs index b712c73..26da1e3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -397,6 +397,9 @@ pub enum UserDisplayStyle { // it can wind up being the Matrix username if there are display name collisions in the room, // in order to avoid any confusion. DisplayName, + + // Acts like Username, except when the username matches given regex, then acts like DisplayName + Regex, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -566,6 +569,7 @@ pub struct TunableValues { pub typing_notice_display: bool, pub users: UserOverrides, pub username_display: UserDisplayStyle, + pub username_display_regex: Option, pub message_user_color: bool, pub default_room: Option, pub open_command: Option>, @@ -592,6 +596,7 @@ pub struct Tunables { pub typing_notice_display: Option, pub users: Option, pub username_display: Option, + pub username_display_regex: Option, pub message_user_color: Option, pub default_room: Option, pub open_command: Option>, @@ -622,6 +627,7 @@ impl Tunables { typing_notice_display: self.typing_notice_display.or(other.typing_notice_display), users: merge_maps(self.users, other.users), username_display: self.username_display.or(other.username_display), + username_display_regex: self.username_display_regex.or(other.username_display_regex), message_user_color: self.message_user_color.or(other.message_user_color), default_room: self.default_room.or(other.default_room), open_command: self.open_command.or(other.open_command), @@ -650,6 +656,7 @@ impl Tunables { typing_notice_display: self.typing_notice_display.unwrap_or(true), users: self.users.unwrap_or_default(), username_display: self.username_display.unwrap_or_default(), + username_display_regex: self.username_display_regex, message_user_color: self.message_user_color.unwrap_or(false), default_room: self.default_room, open_command: self.open_command, @@ -1042,6 +1049,20 @@ impl ApplicationSettings { Cow::Borrowed(user_id.as_str()) } }, + (None, UserDisplayStyle::Regex) => { + let re = regex::Regex::new( + &self.tunables.username_display_regex.clone().unwrap_or("*".into()), + ) + .unwrap(); + + if !re.is_match(user_id.as_str()) { + Cow::Borrowed(user_id.as_str()) + } else if let Some(display) = info.display_names.get(user_id) { + Cow::Borrowed(display.as_str()) + } else { + Cow::Borrowed(user_id.as_str()) + } + }, }; Span::styled(name, style) diff --git a/src/tests.rs b/src/tests.rs index 52cb859..ef6f82c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -189,6 +189,7 @@ pub fn mock_tunables() -> TunableValues { open_command: None, external_edit_file_suffix: String::from(".md"), username_display: UserDisplayStyle::Username, + username_display_regex: Some(String::from(".*")), message_user_color: false, mouse: Default::default(), notifications: Notifications { From d2b06d16ee3e042ddcf4f17f20849b67002c2760 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Mon, 16 Jun 2025 10:42:50 -0700 Subject: [PATCH 2/7] feat: add test for new username_display config options --- src/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config.rs b/src/config.rs index 26da1e3..64271e4 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1179,6 +1179,13 @@ mod tests { let res: Tunables = serde_json::from_str("{\"username_display\": \"displayname\"}").unwrap(); assert_eq!(res.username_display, Some(UserDisplayStyle::DisplayName)); + + let res: Tunables = serde_json::from_str( + "{\"username_display\": \"regex\",\n\"username_display_regex\": \"foo\"}", + ) + .unwrap(); + assert_eq!(res.username_display, Some(UserDisplayStyle::Regex)); + assert_eq!(res.username_display_regex.unwrap_or("FAILED".into()), "foo".to_string()); } #[test] From 0ef5c39f7f61dd1e65ae94ba383037be604d78cb Mon Sep 17 00:00:00 2001 From: vaw Date: Wed, 25 Jun 2025 20:14:04 +0000 Subject: [PATCH 3/7] Make merging of configuration options consistent (#471) --- src/config.rs | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1438ed7..f22bd05 100644 --- a/src/config.rs +++ b/src/config.rs @@ -353,29 +353,31 @@ pub struct UserDisplayTunables { pub type UserOverrides = HashMap; -fn merge_sorts(a: SortOverrides, b: SortOverrides) -> SortOverrides { +fn merge_sorts(profile: SortOverrides, global: SortOverrides) -> SortOverrides { SortOverrides { - chats: b.chats.or(a.chats), - dms: b.dms.or(a.dms), - rooms: b.rooms.or(a.rooms), - spaces: b.spaces.or(a.spaces), - members: b.members.or(a.members), + chats: profile.chats.or(global.chats), + dms: profile.dms.or(global.dms), + rooms: profile.rooms.or(global.rooms), + spaces: profile.spaces.or(global.spaces), + members: profile.members.or(global.members), } } -fn merge_maps(a: Option>, b: Option>) -> Option> +fn merge_maps( + profile: Option>, + global: Option>, +) -> Option> where K: Eq + Hash, { - match (a, b) { - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - (Some(mut a), Some(b)) => { - for (k, v) in b { - a.insert(k, v); + match (global, profile) { + (Some(m), None) | (None, Some(m)) => Some(m), + (Some(mut global), Some(profile)) => { + for (k, v) in profile { + global.insert(k, v); } - Some(a) + Some(global) }, (None, None) => None, } @@ -911,7 +913,7 @@ impl ApplicationSettings { } }; - let macros = merge_maps(macros, profile.macros.take()).unwrap_or_default(); + let macros = merge_maps(profile.macros.take(), macros).unwrap_or_default(); let layout = profile.layout.take().or(layout).unwrap_or_default(); let tunables = global.unwrap_or_default(); @@ -1110,10 +1112,10 @@ mod tests { assert_eq!(res, Some(b.clone())); let res = merge_maps(Some(b.clone()), Some(c.clone())); - assert_eq!(res, Some(c.clone())); + assert_eq!(res, Some(b.clone())); let res = merge_maps(Some(c.clone()), Some(b.clone())); - assert_eq!(res, Some(b.clone())); + assert_eq!(res, Some(c.clone())); } #[test] From 52010d44d7a975a74f00848d8a61f279f5fcc501 Mon Sep 17 00:00:00 2001 From: Ulyssa Date: Fri, 4 Jul 2025 17:12:50 -0700 Subject: [PATCH 4/7] Update to modalkit{,-ratatui}@0.0.23 (#473) --- Cargo.lock | 48 ++++++++++++++++++++++---- Cargo.toml | 4 +-- src/base.rs | 62 +++++++++++++++------------------- src/main.rs | 7 ++-- src/windows/room/chat.rs | 8 ++--- src/windows/room/scrollback.rs | 2 +- 6 files changed, 81 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fed98d..630b764 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1442,6 +1442,41 @@ dependencies = [ "which", ] +[[package]] +name = "editor-types" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e99679670f67825fcd24a23cb4eb655a0f92c82bd4d1c1a1357c0cd71e87" +dependencies = [ + "bitflags 2.9.1", + "editor-types-macros", + "keybindings", + "regex", +] + +[[package]] +name = "editor-types-macros" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42680de76cf91f231abd90cc623750d39077f7d2fadb7962325fb082871f4c66" +dependencies = [ + "editor-types-parser", + "nom", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "editor-types-parser" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac4b91fe830fbbe0a60c37ba0264b6e9ffc70e3664c028234dac59e79299ad4" +dependencies = [ + "nom", + "thiserror 1.0.69", +] + [[package]] name = "either" version = "1.15.0" @@ -2706,9 +2741,9 @@ dependencies = [ [[package]] name = "keybindings" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680e4699c91c0622dd70da32c274881aadb1ac86252d738c3641266e90e4ca15" +checksum = "19a726307ed87e05155c31329676130e6a237e62dda80211f7e1ed811e47630f" dependencies = [ "textwrap", "unicode-segmentation", @@ -3292,15 +3327,16 @@ dependencies = [ [[package]] name = "modalkit" -version = "0.0.21" +version = "0.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc7599fc1bcd2f0a0b4598f23433b45613345a46419ab27d3a9adecb57acd648" +checksum = "6ed06f32b03a7504acadcb0d95d06f3d55258934c34b620ed95e3dae24f081a5" dependencies = [ "anymap2", "arboard", "bitflags 2.9.1", "crossterm", "derive_more", + "editor-types", "intervaltree", "keybindings", "nom", @@ -3314,9 +3350,9 @@ dependencies = [ [[package]] name = "modalkit-ratatui" -version = "0.0.21" +version = "0.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd3d88c86435d4b1fb22c7c0f978b09cb888338cafc10336715aa5070e92b6f6" +checksum = "a33bd64f6dd0011ee88f4f12b28108d3e63df0a5c86fe595d24561be9522f6ea" dependencies = [ "crossterm", "intervaltree", diff --git a/Cargo.toml b/Cargo.toml index 1ac3bf1..556fcc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,13 +77,13 @@ features = ["zbus", "serde"] optional = true [dependencies.modalkit] -version = "0.0.21" +version = "0.0.23" default-features = false #git = "https://github.com/ulyssa/modalkit" #rev = "e40dbb0bfeabe4cfd08facd2acb446080a330d75" [dependencies.modalkit-ratatui] -version = "0.0.21" +version = "0.0.23" #git = "https://github.com/ulyssa/modalkit" #rev = "e40dbb0bfeabe4cfd08facd2acb446080a330d75" diff --git a/src/base.rs b/src/base.rs index fdb7875..56d4c2d 100644 --- a/src/base.rs +++ b/src/base.rs @@ -74,7 +74,7 @@ use modalkit::{ ApplicationStore, ApplicationWindowId, }, - completion::{complete_path, CompletionMap}, + completion::{complete_path, Completer, CompletionMap}, context::EditContext, cursor::Cursor, rope::EditRope, @@ -1914,11 +1914,20 @@ impl ApplicationInfo for IambInfo { type WindowId = IambId; type ContentId = IambBufferId; + fn content_of_command(ct: CommandType) -> IambBufferId { + IambBufferId::Command(ct) + } +} + +pub struct IambCompleter; + +impl Completer for IambCompleter { fn complete( + &mut self, text: &EditRope, cursor: &mut Cursor, content: &IambBufferId, - store: &mut ProgramStore, + store: &mut ChatStore, ) -> Vec { match content { IambBufferId::Command(CommandType::Command) => complete_cmdbar(text, cursor, store), @@ -1936,21 +1945,16 @@ impl ApplicationInfo for IambInfo { IambBufferId::UnreadList => vec![], } } - - fn content_of_command(ct: CommandType) -> IambBufferId { - IambBufferId::Command(ct) - } } /// Tab completion for user IDs. -fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); let id = Cow::from(&id); store - .application .presences .complete(id.as_ref()) .into_iter() @@ -1959,7 +1963,7 @@ fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> } /// Tab completion within the message bar. -fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); @@ -1968,13 +1972,12 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - match id.chars().next() { // Complete room aliases. Some('#') => { - return store.application.names.complete(id.as_ref()); + return store.names.complete(id.as_ref()); }, // Complete room identifiers. Some('!') => { return store - .application .rooms .complete(id.as_ref()) .into_iter() @@ -1984,7 +1987,7 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - // Complete Emoji shortcodes. Some(':') => { - let list = store.application.emojis.complete(&id[1..]); + let list = store.emojis.complete(&id[1..]); let iter = list.into_iter().take(200).map(|s| format!(":{}:", s)); return iter.collect(); @@ -1993,7 +1996,6 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - // Complete usernames for @ and empty strings. Some('@') | None => { return store - .application .presences .complete(id.as_ref()) .into_iter() @@ -2007,28 +2009,23 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - } /// Tab completion for Matrix identifiers (usernames, room aliases, etc.) -fn complete_matrix_names( - text: &EditRope, - cursor: &mut Cursor, - store: &ProgramStore, -) -> Vec { +fn complete_matrix_names(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); let id = Cow::from(&id); - let list = store.application.names.complete(id.as_ref()); + let list = store.names.complete(id.as_ref()); if !list.is_empty() { return list; } - let list = store.application.presences.complete(id.as_ref()); + let list = store.presences.complete(id.as_ref()); if !list.is_empty() { return list.into_iter().map(|i| i.to_string()).collect(); } store - .application .rooms .complete(id.as_ref()) .into_iter() @@ -2037,12 +2034,12 @@ fn complete_matrix_names( } /// Tab completion for Emoji shortcode names. -fn complete_emoji(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_emoji(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let sc = text.get_prefix_word_mut(cursor, &WordStyle::Little); let sc = sc.unwrap_or_else(EditRope::empty); let sc = Cow::from(&sc); - store.application.emojis.complete(sc.as_ref()) + store.emojis.complete(sc.as_ref()) } /// Tab completion for command names. @@ -2050,11 +2047,11 @@ fn complete_cmdname( desc: CommandDescription, text: &EditRope, cursor: &mut Cursor, - store: &ProgramStore, + store: &ChatStore, ) -> Vec { // Complete command name and set cursor position. let _ = text.get_prefix_word_mut(cursor, &WordStyle::Little); - store.application.cmds.complete_name(desc.command.as_str()) + store.cmds.complete_name(desc.command.as_str()) } /// Tab completion for command arguments. @@ -2062,9 +2059,9 @@ fn complete_cmdarg( desc: CommandDescription, text: &EditRope, cursor: &mut Cursor, - store: &ProgramStore, + store: &ChatStore, ) -> Vec { - let cmd = match store.application.cmds.get(desc.command.as_str()) { + let cmd = match store.cmds.get(desc.command.as_str()) { Ok(cmd) => cmd, Err(_) => return vec![], }; @@ -2087,12 +2084,7 @@ fn complete_cmdarg( } /// Tab completion for commands. -fn complete_cmd( - cmd: &str, - text: &EditRope, - cursor: &mut Cursor, - store: &ProgramStore, -) -> Vec { +fn complete_cmd(cmd: &str, text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { match CommandDescription::from_str(cmd) { Ok(desc) => { if desc.arg.untrimmed.is_empty() { @@ -2109,7 +2101,7 @@ fn complete_cmd( } /// Tab completion for the command bar. -fn complete_cmdbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_cmdbar(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let eo = text.cursor_to_offset(cursor); let slice = text.slice(..eo); let cow = Cow::from(&slice); @@ -2289,6 +2281,7 @@ pub mod tests { #[tokio::test] async fn test_complete_msgbar() { let store = mock_store().await; + let store = store.application; let text = EditRope::from("going for a walk :walk "); let mut cursor = Cursor::new(0, 22); @@ -2312,6 +2305,7 @@ pub mod tests { #[tokio::test] async fn test_complete_cmdbar() { let store = mock_store().await; + let store = store.application; let users = vec![ "@user1:example.com", "@user2:example.com", diff --git a/src/main.rs b/src/main.rs index 9b8fe46..9060947 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,6 +89,7 @@ use crate::{ ChatStore, HomeserverAction, IambAction, + IambCompleter, IambError, IambId, IambInfo, @@ -529,7 +530,7 @@ impl Application { }, // Unimplemented. - Action::KeywordLookup => { + Action::KeywordLookup(_) => { // XXX: implement None }, @@ -1011,7 +1012,9 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { // Set up the async worker thread and global store. let worker = ClientWorker::spawn(client.clone(), settings.clone()).await; let store = ChatStore::new(worker.clone(), settings.clone()); - let store = Store::new(store); + let mut store = Store::new(store); + store.completer = Box::new(IambCompleter); + let store = Arc::new(AsyncMutex::new(store)); worker.init(store.clone()); diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index cb584f4..426a66c 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -864,16 +864,16 @@ impl PromptActions for ChatState { fn recall( &mut self, + filter: &RecallFilter, dir: &MoveDir1D, count: &Count, - prefixed: bool, ctx: &ProgramContext, _: &mut ProgramStore, ) -> EditResult, IambInfo> { let count = ctx.resolve(count); let rope = self.tbox.get(); - let text = self.sent.recall(&rope, &mut self.sent_scrollback, *dir, prefixed, count); + let text = self.sent.recall(&rope, &mut self.sent_scrollback, filter, *dir, count); if let Some(text) = text { self.tbox.set_text(text); @@ -897,9 +897,7 @@ impl Promptable for ChatState { match act { PromptAction::Submit => self.submit(ctx, store), PromptAction::Abort(empty) => self.abort(*empty, ctx, store), - PromptAction::Recall(dir, count, prefixed) => { - self.recall(dir, count, *prefixed, ctx, store) - }, + PromptAction::Recall(filter, dir, count) => self.recall(filter, dir, count, ctx, store), } } } diff --git a/src/windows/room/scrollback.rs b/src/windows/room/scrollback.rs index 53da0b7..5f32897 100644 --- a/src/windows/room/scrollback.rs +++ b/src/windows/room/scrollback.rs @@ -840,8 +840,8 @@ impl EditorActions for ScrollbackState { fn complete( &mut self, + _: &CompletionStyle, _: &CompletionType, - _: &CompletionSelection, _: &CompletionDisplay, _: &ProgramContext, _: &mut ProgramStore, From 34d3b844af99315a84fbae554e4b20594ecefc66 Mon Sep 17 00:00:00 2001 From: vaw Date: Sat, 5 Jul 2025 00:25:38 +0000 Subject: [PATCH 5/7] Highlight border of focused window (#470) --- src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 9060947..c38b239 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,7 +62,7 @@ use modalkit::crossterm::{ use ratatui::{ backend::CrosstermBackend, layout::Rect, - style::{Color, Style}, + style::{Color, Modifier, Style}, text::Span, widgets::Paragraph, Terminal, @@ -328,6 +328,9 @@ impl Application { .show_dialog(dialogstr) .show_mode(modestr) .borders(true) + .border_style(Style::default().add_modifier(Modifier::DIM)) + .tab_style(Style::default().add_modifier(Modifier::DIM)) + .tab_style_focused(Style::default().remove_modifier(Modifier::DIM)) .focus(focused); f.render_stateful_widget(screen, area, sstate); From e222912acf98c428c53096c84b4eb540126d6a96 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Mon, 16 Jun 2025 10:36:27 -0700 Subject: [PATCH 6/7] feat: add regex option for username_display --- src/config.rs | 21 +++++++++++++++++++++ src/tests.rs | 1 + 2 files changed, 22 insertions(+) diff --git a/src/config.rs b/src/config.rs index f22bd05..3da111f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -399,6 +399,9 @@ pub enum UserDisplayStyle { // it can wind up being the Matrix username if there are display name collisions in the room, // in order to avoid any confusion. DisplayName, + + // Acts like Username, except when the username matches given regex, then acts like DisplayName + Regex, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -568,6 +571,7 @@ pub struct TunableValues { pub typing_notice_display: bool, pub users: UserOverrides, pub username_display: UserDisplayStyle, + pub username_display_regex: Option, pub message_user_color: bool, pub default_room: Option, pub open_command: Option>, @@ -595,6 +599,7 @@ pub struct Tunables { pub typing_notice_display: Option, pub users: Option, pub username_display: Option, + pub username_display_regex: Option, pub message_user_color: Option, pub default_room: Option, pub open_command: Option>, @@ -626,6 +631,7 @@ impl Tunables { typing_notice_display: self.typing_notice_display.or(other.typing_notice_display), users: merge_maps(self.users, other.users), username_display: self.username_display.or(other.username_display), + username_display_regex: self.username_display_regex.or(other.username_display_regex), message_user_color: self.message_user_color.or(other.message_user_color), default_room: self.default_room.or(other.default_room), open_command: self.open_command.or(other.open_command), @@ -655,6 +661,7 @@ impl Tunables { typing_notice_display: self.typing_notice_display.unwrap_or(true), users: self.users.unwrap_or_default(), username_display: self.username_display.unwrap_or_default(), + username_display_regex: self.username_display_regex, message_user_color: self.message_user_color.unwrap_or(false), default_room: self.default_room, open_command: self.open_command, @@ -1048,6 +1055,20 @@ impl ApplicationSettings { Cow::Borrowed(user_id.as_str()) } }, + (None, UserDisplayStyle::Regex) => { + let re = regex::Regex::new( + &self.tunables.username_display_regex.clone().unwrap_or("*".into()), + ) + .unwrap(); + + if !re.is_match(user_id.as_str()) { + Cow::Borrowed(user_id.as_str()) + } else if let Some(display) = info.display_names.get(user_id) { + Cow::Borrowed(display.as_str()) + } else { + Cow::Borrowed(user_id.as_str()) + } + }, }; Span::styled(name, style) diff --git a/src/tests.rs b/src/tests.rs index 3e3f825..7e957e1 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -189,6 +189,7 @@ pub fn mock_tunables() -> TunableValues { open_command: None, external_edit_file_suffix: String::from(".md"), username_display: UserDisplayStyle::Username, + username_display_regex: Some(String::from(".*")), message_user_color: false, mouse: Default::default(), notifications: Notifications { From 1f88ef6e02bc5e5a4b71f9e27b21980bd0eb5ca6 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Mon, 16 Jun 2025 10:42:50 -0700 Subject: [PATCH 7/7] feat: add test for new username_display config options --- src/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config.rs b/src/config.rs index 3da111f..4eac881 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1185,6 +1185,13 @@ mod tests { let res: Tunables = serde_json::from_str("{\"username_display\": \"displayname\"}").unwrap(); assert_eq!(res.username_display, Some(UserDisplayStyle::DisplayName)); + + let res: Tunables = serde_json::from_str( + "{\"username_display\": \"regex\",\n\"username_display_regex\": \"foo\"}", + ) + .unwrap(); + assert_eq!(res.username_display, Some(UserDisplayStyle::Regex)); + assert_eq!(res.username_display_regex.unwrap_or("FAILED".into()), "foo".to_string()); } #[test]