[fractal] room-history: Detect mentions with an iterator
- From: Julian Sparber <jsparber src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [fractal] room-history: Detect mentions with an iterator
- Date: Thu, 15 Sep 2022 10:23:17 +0000 (UTC)
commit 31e3cbc24064ec425776f7bd8af7692c2362723e
Author: Kévin Commaille <zecakeh tedomum fr>
Date: Fri Jul 15 10:51:06 2022 +0200
room-history: Detect mentions with an iterator
Allow to reuse the logic in other places
src/session/content/room_history/mod.rs | 148 +++++++++++++++++++++-----------
1 file changed, 98 insertions(+), 50 deletions(-)
---
diff --git a/src/session/content/room_history/mod.rs b/src/session/content/room_history/mod.rs
index b21860d90..39cbeb012 100644
--- a/src/session/content/room_history/mod.rs
+++ b/src/session/content/room_history/mod.rs
@@ -461,6 +461,12 @@ impl RoomHistory {
self.imp().room.borrow().clone()
}
+ /// Get an iterator over chunks of the message entry's text between the
+ /// given start and end, split by mentions.
+ fn split_buffer_mentions(&self, start: gtk::TextIter, end: gtk::TextIter) -> SplitMentions {
+ SplitMentions { iter: start, end }
+ }
+
pub fn send_text_message(&self) {
let priv_ = self.imp();
let buffer = priv_.message_entry.buffer();
@@ -472,56 +478,22 @@ impl RoomHistory {
let mut plain_body = String::with_capacity(body_len);
// formatted_body is Markdown if is_markdown is true, and HTML if false.
let mut formatted_body = String::with_capacity(body_len);
- // uncopied_text_location is the start of the text we haven't copied to
- // plain_body and formatted_body.
- let mut uncopied_text_location = start_iter;
-
- let mut iter = start_iter;
- loop {
- if let Some(anchor) = iter.child_anchor() {
- let widgets = anchor.widgets();
- let pill = widgets.first().unwrap().downcast_ref::<Pill>().unwrap();
- let (url, label) = pill
- .user()
- .map(|user| {
- (
- user.user_id().matrix_to_uri().to_string(),
- user.display_name(),
- )
- })
- .or_else(|| {
- pill.room().map(|room| {
- (
- // No server name needed. matrix.to URIs for mentions aren't
- // routable
- room.room_id().matrix_to_uri().to_string(),
- room.display_name(),
- )
- })
- })
- .unwrap();
-
- // Add more uncopied characters from message
- let some_text = buffer.text(&uncopied_text_location, &iter, false);
- plain_body.push_str(&some_text);
- formatted_body.push_str(&some_text);
- uncopied_text_location = iter;
-
- // Add mention
- has_mentions = true;
- plain_body.push_str(&label);
- formatted_body.push_str(&if is_markdown {
- format!("[{}]({})", label, url)
- } else {
- format!("<a href='{}'>{}</a>", url, label)
- });
- }
- if !iter.forward_char() {
- // Add remaining uncopied characters
- let some_text = buffer.text(&uncopied_text_location, &iter, false);
- plain_body.push_str(&some_text);
- formatted_body.push_str(&some_text);
- break;
+
+ for chunk in self.split_buffer_mentions(start_iter, end_iter) {
+ match chunk {
+ MentionChunk::Text(text) => {
+ plain_body.push_str(&text);
+ formatted_body.push_str(&text);
+ }
+ MentionChunk::Mention { name, uri } => {
+ has_mentions = true;
+ plain_body.push_str(&name);
+ formatted_body.push_str(&if is_markdown {
+ format!("[{name}]({uri})")
+ } else {
+ format!("<a href=\"{uri}\">{name}</a>")
+ });
+ }
}
}
@@ -959,3 +931,79 @@ impl Default for RoomHistory {
Self::new()
}
}
+
+enum MentionChunk {
+ Text(String),
+ Mention { name: String, uri: String },
+}
+
+struct SplitMentions {
+ iter: gtk::TextIter,
+ end: gtk::TextIter,
+}
+
+impl Iterator for SplitMentions {
+ type Item = MentionChunk;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.iter == self.end {
+ // We reached the end.
+ return None;
+ }
+
+ if let Some(pill) = self
+ .iter
+ .child_anchor()
+ .map(|anchor| anchor.widgets())
+ .as_ref()
+ .and_then(|widgets| widgets.first())
+ .and_then(|widget| widget.downcast_ref::<Pill>())
+ {
+ // This chunk is a mention.
+ let (name, uri) = if let Some(user) = pill.user() {
+ (
+ user.display_name(),
+ user.user_id().matrix_to_uri().to_string(),
+ )
+ } else if let Some(room) = pill.room() {
+ (
+ room.display_name(),
+ room.room_id().matrix_to_uri().to_string(),
+ )
+ } else {
+ unreachable!()
+ };
+
+ self.iter.forward_cursor_position();
+
+ return Some(MentionChunk::Mention { name, uri });
+ }
+
+ // This chunk is not a mention. Go forward until the next mention or the
+ // end and return the text in between.
+ let start = self.iter;
+ while self.iter.forward_cursor_position() && self.iter != self.end {
+ if self
+ .iter
+ .child_anchor()
+ .map(|anchor| anchor.widgets())
+ .as_ref()
+ .and_then(|widgets| widgets.first())
+ .and_then(|widget| widget.downcast_ref::<Pill>())
+ .is_some()
+ {
+ break;
+ }
+ }
+
+ let text = self.iter.buffer().text(&start, &self.iter, false);
+ // We might somehow have an empty string before the end, or at the end,
+ // because of hidden `char`s in the buffer, so we must only return
+ // `None` when we have an empty string at the end.
+ if self.iter == self.end && text.is_empty() {
+ None
+ } else {
+ Some(MentionChunk::Text(text.into()))
+ }
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]