mirror of
https://github.com/youwen5/iamb.git
synced 2025-06-19 05:09:51 -07:00
Do proper Unicode collation on room names (#440)
This commit is contained in:
parent
9ed9400b67
commit
ba7d0392d8
6 changed files with 85 additions and 21 deletions
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -579,6 +579,15 @@ version = "1.7.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
|
@ -674,6 +683,17 @@ dependencies = [
|
|||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.7.7"
|
||||
|
@ -1619,6 +1639,19 @@ dependencies = [
|
|||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "feruca"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06eccaab9dc53ad4bffb4ed748baf5c1f9475d5e9cac35e1b8eac69dac56899e"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"bstr",
|
||||
"once_cell",
|
||||
"rustc-hash",
|
||||
"unicode-canonical-combining-class",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
|
@ -2198,6 +2231,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"edit",
|
||||
"emojis",
|
||||
"feruca",
|
||||
"futures",
|
||||
"gethostname",
|
||||
"html5ever",
|
||||
|
@ -5713,6 +5747,12 @@ version = "2.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-canonical-combining-class"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41c99d5174052d02ce765418e826597a1be18f32c114e35d9e22f92390239561"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
|
|
|
@ -34,6 +34,7 @@ clap = {version = "~4.3", features = ["derive"]}
|
|||
css-color-parser = "0.1.2"
|
||||
dirs = "4.0.0"
|
||||
emojis = "0.5"
|
||||
feruca = "0.10.1"
|
||||
futures = "0.3"
|
||||
gethostname = "0.4.1"
|
||||
html5ever = "0.26.0"
|
||||
|
|
|
@ -1486,6 +1486,9 @@ pub struct ChatStore {
|
|||
|
||||
/// Whether the application is currently focused
|
||||
pub focused: bool,
|
||||
|
||||
/// Collator for locale-aware text sorting.
|
||||
pub collator: feruca::Collator,
|
||||
}
|
||||
|
||||
impl ChatStore {
|
||||
|
@ -1500,6 +1503,7 @@ impl ChatStore {
|
|||
cmds: crate::commands::setup_commands(),
|
||||
emojis: emoji_map(),
|
||||
|
||||
collator: Default::default(),
|
||||
names: Default::default(),
|
||||
rooms: Default::default(),
|
||||
presences: Default::default(),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! # Logic for loading and validating application configuration
|
||||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt;
|
||||
use std::fs::File;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
@ -105,7 +105,7 @@ fn validate_profile_name(name: &str) -> bool {
|
|||
name.chars().all(is_profile_char)
|
||||
}
|
||||
|
||||
fn validate_profile_names(names: &HashMap<String, ProfileConfig>) {
|
||||
fn validate_profile_names(names: &BTreeMap<String, ProfileConfig>) {
|
||||
for name in names.keys() {
|
||||
if validate_profile_name(name.as_str()) {
|
||||
continue;
|
||||
|
@ -787,7 +787,7 @@ pub struct ProfileConfig {
|
|||
|
||||
#[derive(Clone, Deserialize)]
|
||||
pub struct IambConfig {
|
||||
pub profiles: HashMap<String, ProfileConfig>,
|
||||
pub profiles: BTreeMap<String, ProfileConfig>,
|
||||
pub default_profile: Option<String>,
|
||||
pub settings: Option<Tunables>,
|
||||
pub dirs: Option<Directories>,
|
||||
|
|
|
@ -82,6 +82,7 @@ use crate::base::{
|
|||
|
||||
use self::{room::RoomState, welcome::WelcomeState};
|
||||
use crate::message::MessageTimeStamp;
|
||||
use feruca::Collator;
|
||||
|
||||
pub mod room;
|
||||
pub mod welcome;
|
||||
|
@ -170,7 +171,12 @@ fn user_cmp(a: &MemberItem, b: &MemberItem, field: &SortFieldUser) -> Ordering {
|
|||
}
|
||||
}
|
||||
|
||||
fn room_cmp<T: RoomLikeItem>(a: &T, b: &T, field: &SortFieldRoom) -> Ordering {
|
||||
fn room_cmp<T: RoomLikeItem>(
|
||||
a: &T,
|
||||
b: &T,
|
||||
field: &SortFieldRoom,
|
||||
collator: &mut Collator,
|
||||
) -> Ordering {
|
||||
match field {
|
||||
SortFieldRoom::Favorite => {
|
||||
let fava = a.has_tag(TagName::Favorite);
|
||||
|
@ -186,7 +192,7 @@ fn room_cmp<T: RoomLikeItem>(a: &T, b: &T, field: &SortFieldRoom) -> Ordering {
|
|||
// If a has LowPriority and b doesn't, it should sort later in room list.
|
||||
lowa.cmp(&lowb)
|
||||
},
|
||||
SortFieldRoom::Name => a.name().cmp(b.name()),
|
||||
SortFieldRoom::Name => collator.collate(a.name(), b.name()),
|
||||
SortFieldRoom::Alias => some_cmp(a.alias(), b.alias(), Ord::cmp),
|
||||
SortFieldRoom::RoomId => a.room_id().cmp(b.room_id()),
|
||||
SortFieldRoom::Unread => {
|
||||
|
@ -209,9 +215,10 @@ fn room_fields_cmp<T: RoomLikeItem>(
|
|||
a: &T,
|
||||
b: &T,
|
||||
fields: &[SortColumn<SortFieldRoom>],
|
||||
collator: &mut Collator,
|
||||
) -> Ordering {
|
||||
for SortColumn(field, order) in fields {
|
||||
match (room_cmp(a, b, field), order) {
|
||||
match (room_cmp(a, b, field, collator), order) {
|
||||
(Ordering::Equal, _) => continue,
|
||||
(o, SortOrder::Ascending) => return o,
|
||||
(o, SortOrder::Descending) => return o.reverse(),
|
||||
|
@ -219,7 +226,7 @@ fn room_fields_cmp<T: RoomLikeItem>(
|
|||
}
|
||||
|
||||
// Break ties on ascending room id.
|
||||
room_cmp(a, b, &SortFieldRoom::RoomId)
|
||||
room_cmp(a, b, &SortFieldRoom::RoomId, collator)
|
||||
}
|
||||
|
||||
fn user_fields_cmp(
|
||||
|
@ -516,7 +523,8 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.map(|room_info| DirectItem::new(room_info, store))
|
||||
.collect::<Vec<_>>();
|
||||
let fields = &store.application.settings.tunables.sort.dms;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.set(items);
|
||||
|
||||
|
@ -561,7 +569,8 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.map(|room_info| RoomItem::new(room_info, store))
|
||||
.collect::<Vec<_>>();
|
||||
let fields = &store.application.settings.tunables.sort.rooms;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.set(items);
|
||||
|
||||
|
@ -592,7 +601,8 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
items.extend(dms);
|
||||
|
||||
let fields = &store.application.settings.tunables.sort.chats;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.set(items);
|
||||
|
||||
|
@ -625,7 +635,8 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
items.extend(dms);
|
||||
|
||||
let fields = &store.application.settings.tunables.sort.chats;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.set(items);
|
||||
|
||||
|
@ -645,7 +656,8 @@ impl WindowOps<IambInfo> for IambWindow {
|
|||
.map(|room| SpaceItem::new(room, store))
|
||||
.collect::<Vec<_>>();
|
||||
let fields = &store.application.settings.tunables.sort.spaces;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.set(items);
|
||||
|
||||
|
@ -1627,6 +1639,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sort_rooms() {
|
||||
let mut collator = Collator::default();
|
||||
let collator = &mut collator;
|
||||
let server = server_name!("example.com");
|
||||
|
||||
let room1 = TestRoomItem {
|
||||
|
@ -1659,13 +1673,13 @@ mod tests {
|
|||
// Sort by Name ascending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Name, SortOrder::Ascending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room3, &room2, &room1]);
|
||||
|
||||
// Sort by Name descending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Name, SortOrder::Descending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room1, &room2, &room3]);
|
||||
|
||||
// Sort by Favorite and Alias before Name to show order matters.
|
||||
|
@ -1675,7 +1689,7 @@ mod tests {
|
|||
SortColumn(SortFieldRoom::Alias, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::Name, SortOrder::Ascending),
|
||||
];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room1, &room2, &room3]);
|
||||
|
||||
// Now flip order of Favorite with Descending
|
||||
|
@ -1685,12 +1699,14 @@ mod tests {
|
|||
SortColumn(SortFieldRoom::Alias, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::Name, SortOrder::Ascending),
|
||||
];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room2, &room3, &room1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_room_recents() {
|
||||
let mut collator = Collator::default();
|
||||
let collator = &mut collator;
|
||||
let server = server_name!("example.com");
|
||||
|
||||
let room1 = TestRoomItem {
|
||||
|
@ -1729,18 +1745,20 @@ mod tests {
|
|||
// Sort by Recent ascending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Ascending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room2, &room3, &room1]);
|
||||
|
||||
// Sort by Recent descending.
|
||||
let mut rooms = vec![&room1, &room2, &room3];
|
||||
let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Descending)];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room1, &room3, &room2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sort_room_invites() {
|
||||
let mut collator = Collator::default();
|
||||
let collator = &mut collator;
|
||||
let server = server_name!("example.com");
|
||||
|
||||
let room1 = TestRoomItem {
|
||||
|
@ -1776,7 +1794,7 @@ mod tests {
|
|||
SortColumn(SortFieldRoom::Invite, SortOrder::Ascending),
|
||||
SortColumn(SortFieldRoom::Name, SortOrder::Ascending),
|
||||
];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room3, &room1, &room2]);
|
||||
|
||||
// Sort invites after
|
||||
|
@ -1785,7 +1803,7 @@ mod tests {
|
|||
SortColumn(SortFieldRoom::Invite, SortOrder::Descending),
|
||||
SortColumn(SortFieldRoom::Name, SortOrder::Ascending),
|
||||
];
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
assert_eq!(rooms, vec![&room1, &room2, &room3]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,8 @@ impl StatefulWidget for Space<'_> {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
let fields = &self.store.application.settings.tunables.sort.rooms;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields));
|
||||
let collator = &mut self.store.application.collator;
|
||||
items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator));
|
||||
|
||||
state.list.set(items);
|
||||
state.last_fetch = Some(Instant::now());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue