Skip to content

Commit

Permalink
Merge pull request #573 from squidowl/fix/user-fragments-incase-sensi…
Browse files Browse the repository at this point in the history
…tive

Update user fragments
  • Loading branch information
tarkah authored Sep 17, 2024
2 parents a9c31b9 + 21969d3 commit 1633c0b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 55 deletions.
113 changes: 60 additions & 53 deletions data/src/message.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::iter;

use chrono::{DateTime, Utc};
use const_format::concatcp;
Expand Down Expand Up @@ -329,21 +330,16 @@ pub fn parse_fragments(text: String, channel_users: &[User]) -> Content {
}
}

Either::Right(std::iter::once(fragment))
Either::Right(iter::once(fragment))
})
.flat_map(|fragment| {
if let Fragment::Text(text) = &fragment {
return Either::Left(parse_user_fragments(text, channel_users).into_iter());
return Either::Left(
parse_user_and_channel_fragments(text, channel_users).into_iter(),
);
}

Either::Right(std::iter::once(fragment))
})
.flat_map(|fragment| {
if let Fragment::Text(text) = &fragment {
return Either::Left(parse_channel_fragments(text).into_iter());
}

Either::Right(std::iter::once(fragment))
Either::Right(iter::once(fragment))
})
.collect::<Vec<_>>();

Expand Down Expand Up @@ -387,54 +383,65 @@ fn parse_url_fragments(text: String) -> Vec<Fragment> {
fragments
}

fn parse_user_fragments(text: &str, channel_users: &[User]) -> Vec<Fragment> {
text.chars()
.group_by(|c| !c.is_whitespace() && !c.is_ascii_punctuation())
.into_iter()
.map(|(is_word, chars)| {
let text = chars.collect::<String>();

if is_word {
if let Some(user) = channel_users
.iter()
.find(|user| user.nickname().as_ref() == text)
{
return Fragment::User(user.clone());
}
}

Fragment::Text(text)
})
.fold(vec![], |mut acc, fragment| {
if let Some(Fragment::Text(text)) = acc.last_mut() {
if let Fragment::Text(next) = &fragment {
text.push_str(next);
return acc;
}
}

acc.push(fragment);
acc
})
}

fn parse_channel_fragments(text: &str) -> Vec<Fragment> {
fn parse_user_and_channel_fragments(text: &str, channel_users: &[User]) -> Vec<Fragment> {
text.chars()
.group_by(|c| c.is_whitespace())
.into_iter()
.map(|(is_whitespace, chars)| {
.flat_map(|(is_whitespace, chars)| {
let text = chars.collect::<String>();

if !is_whitespace
let trimmed = text.trim_matches(|c: char| c.is_ascii_punctuation());
let lower = text.to_ascii_lowercase();
let lower_trimmed = trimmed.to_ascii_lowercase();

if !is_whitespace {
if let Some((is_trimmed, user)) = channel_users.iter().find_map(|user| {
let nickname = user.nickname();
let nick = nickname.as_ref();
// TODO: Consider server case-mapping settings vs just ascii lowercase
let nick_lower = nick.to_ascii_lowercase();

if nick == text || nick_lower == lower {
Some((false, user.clone()))
} else if nick == trimmed || nick_lower == lower_trimmed {
Some((true, user.clone()))
} else {
None
}
}) {
if is_trimmed {
let prefix_end = text.find(|c: char| !c.is_ascii_punctuation());
let suffix_start = text
.rfind(|c: char| !c.is_ascii_punctuation())
.map(|i| i + 1)
.filter(|i| *i < text.len());
let middle = prefix_end.unwrap_or(0)..suffix_start.unwrap_or(text.len());

return Either::Right(
prefix_end
.map(|i| Fragment::Text(text[0..i].to_string()))
.into_iter()
.chain(Some(Fragment::User(user, text[middle].to_string())))
.chain(
suffix_start
.map(|i| Fragment::Text(text[i..text.len()].to_string())),
),
);
} else {
return Either::Left(iter::once(Fragment::User(user.clone(), text)));
}
}
// Only parse on `#` since it's most common and
// using &!+ leads to more false positives than not
&& text.strip_prefix('#').map_or(false, |rest| !rest.is_empty())
&& !text.contains(proto::CHANNEL_BLACKLIST_CHARS)
{
return Fragment::Channel(text);
else if text
.strip_prefix('#')
.map_or(false, |rest| !rest.is_empty())
&& !text.contains(proto::CHANNEL_BLACKLIST_CHARS)
{
return Either::Left(iter::once(Fragment::Channel(text)));
}
}

Fragment::Text(text)
Either::Left(iter::once(Fragment::Text(text)))
})
.fold(vec![], |mut acc, fragment| {
if let Some(Fragment::Text(text)) = acc.last_mut() {
Expand Down Expand Up @@ -468,7 +475,7 @@ impl Content {
pub enum Fragment {
Text(String),
Channel(String),
User(User),
User(User, String),
Url(Url),
Formatted {
text: String,
Expand All @@ -480,8 +487,8 @@ impl Fragment {
pub fn as_str(&self) -> &str {
match self {
Fragment::Text(s) => s,
Fragment::Channel(s) => s,
Fragment::User(u) => u.as_str(),
Fragment::Channel(c) => c,
Fragment::User(_, t) => t,
Fragment::Url(u) => u.as_str(),
Fragment::Formatted { text, .. } => text,
}
Expand Down
4 changes: 2 additions & 2 deletions src/widget/message_content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn message_content_impl<'a, T: Copy + 'a, M: 'a>(
data::message::Fragment::Channel(s) => span(s.as_str())
.color(theme.colors().buffer.url)
.link(message::Link::Channel(s.as_str().to_string())),
data::message::Fragment::User(user) => {
data::message::Fragment::User(user, text) => {
let color_kind = &config.buffer.channel.message.nickname_color;

let NickColor { seed, color } =
Expand All @@ -78,7 +78,7 @@ fn message_content_impl<'a, T: Copy + 'a, M: 'a>(
None => theme.colors().text.primary,
};

span(user.nickname().to_string())
span(text)
.color(color)
.link(message::Link::User(user.clone()))
}
Expand Down

0 comments on commit 1633c0b

Please sign in to comment.