Skip to content
This repository was archived by the owner on Sep 27, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions bindings/wysiwyg-ffi/src/ffi_composer_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,31 @@ impl ComposerModel {
))
}

pub fn edit_link_with_text(
self: &Arc<Self>,
url: String,
text: String,
attributes: Vec<Attribute>,
) -> Arc<ComposerUpdate> {
let url = Utf16String::from_str(&url);
let text = Utf16String::from_str(&text);
let attrs = attributes
.iter()
.map(|attr| {
(
Utf16String::from_str(&attr.key),
Utf16String::from_str(&attr.value),
)
})
.collect();
Arc::new(ComposerUpdate::from(
self.inner
.lock()
.unwrap()
.edit_link_with_text(url, text, attrs),
))
}

/// Creates an at-room mention node and inserts it into the composer at the current selection
pub fn insert_at_room_mention(self: &Arc<Self>) -> Arc<ComposerUpdate> {
Arc::new(ComposerUpdate::from(
Expand Down
5 changes: 3 additions & 2 deletions bindings/wysiwyg-ffi/src/ffi_link_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use widestring::Utf16String;
pub enum LinkAction {
CreateWithText,
Create,
Edit { url: String },
Edit { url: String, text: String },
Disabled,
}

Expand All @@ -13,8 +13,9 @@ impl From<wysiwyg::LinkAction<Utf16String>> for LinkAction {
match inner {
wysiwyg::LinkAction::CreateWithText => Self::CreateWithText,
wysiwyg::LinkAction::Create => Self::Create,
wysiwyg::LinkAction::Edit(url) => Self::Edit {
wysiwyg::LinkAction::Edit(url, text) => Self::Edit {
url: url.to_string(),
text: text.to_string(),
},
wysiwyg::LinkAction::Disabled => Self::Disabled,
}
Expand Down
19 changes: 17 additions & 2 deletions bindings/wysiwyg-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ impl ComposerModel {
))
}

pub fn edit_link_with_text(
&mut self,
url: &str,
text: &str,
attributes: js_sys::Map,
) -> ComposerUpdate {
ComposerUpdate::from(self.inner.edit_link_with_text(
Utf16String::from_str(url),
Utf16String::from_str(text),
attributes.into_vec(),
))
}

/// Creates an at-room mention node and inserts it into the composer at the current selection
pub fn insert_at_room_mention(
&mut self,
Expand Down Expand Up @@ -831,6 +844,7 @@ pub struct Create;
#[wasm_bindgen(getter_with_clone)]
pub struct Edit {
pub url: String,
pub text: String,
}

#[derive(Clone)]
Expand Down Expand Up @@ -860,12 +874,13 @@ impl From<wysiwyg::LinkAction<Utf16String>> for LinkAction {
edit_link: None,
disabled: None,
},
wysiwyg::LinkAction::Edit(url) => {
wysiwyg::LinkAction::Edit(url, text) => {
let url = url.to_string();
let text = text.to_string();
Self {
create_with_text: None,
create: None,
edit_link: Some(Edit { url }),
edit_link: Some(Edit { url, text }),
disabled: None,
}
}
Expand Down
29 changes: 28 additions & 1 deletion crates/wysiwyg/src/composer_model/hyperlinks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::dom::nodes::dom_node::DomNodeKind;
use crate::dom::nodes::dom_node::DomNodeKind::{Link, List};
use crate::dom::nodes::ContainerNodeKind;
use crate::dom::nodes::DomNode;
use crate::dom::to_plain_text::ToPlainText;
use crate::dom::unicode_string::UnicodeStrExt;
use crate::dom::Range;
use crate::{
Expand Down Expand Up @@ -53,7 +54,10 @@ where
LinkAction::Disabled
} else {
// Otherwise we edit the first link of the selection.
LinkAction::Edit(first_link.get_link_url().unwrap())
LinkAction::Edit(
first_link.get_link_url().unwrap(),
first_link.to_plain_text(),
)
}
} else if s == e || self.is_blank_selection(range) {
LinkAction::CreateWithText
Expand Down Expand Up @@ -121,6 +125,29 @@ where
self.set_link_in_range(url, range, attributes)
}

pub fn edit_link_with_text(
&mut self,
url: S,
text: S,
attributes: Vec<(S, S)>,
) -> ComposerUpdate<S> {
self.push_state_to_history();
let (s, e) = self.safe_selection();
let range = self.state.dom.find_range(s, e);
let Some(link_loc) = range
.locations
.iter()
.find(|loc| loc.kind == DomNodeKind::Link) else {
panic!("Attempting to edit a link on a range that doesn't contain one")
};
let start = link_loc.position;
let end = start + link_loc.length;
let new_end = start + text.len();
self.do_replace_text_in(text, start, end);
let range = self.state.dom.find_range(start, new_end);
self.set_link_in_range(url, range, attributes)
}

fn set_link_in_range(
&mut self,
mut url: S,
Expand Down
2 changes: 1 addition & 1 deletion crates/wysiwyg/src/link_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ pub enum LinkActionUpdate<S: UnicodeString> {
pub enum LinkAction<S: UnicodeString> {
CreateWithText,
Create,
Edit(S),
Edit(S, S),
Disabled,
}
30 changes: 15 additions & 15 deletions crates/wysiwyg/src/tests/test_get_link_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn get_link_action_from_highlighted_link() {
let model = cm("{<a href=\"https://element.io\">test</a>}|");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test"))
)
}

Expand All @@ -63,7 +63,7 @@ fn get_link_action_from_cursor_at_the_end_of_a_link() {
let model = cm("<a href=\"https://element.io\">test</a>|");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test"))
)
}

Expand All @@ -72,7 +72,7 @@ fn get_link_action_from_cursor_inside_a_link() {
let model = cm("<a href=\"https://element.io\">te|st</a>");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test"))
)
}

Expand All @@ -81,7 +81,7 @@ fn get_link_action_from_cursor_at_the_start_of_a_link() {
let model = cm("|<a href=\"https://element.io\">test</a>");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test"))
)
}

Expand All @@ -90,7 +90,7 @@ fn get_link_action_from_selection_that_contains_a_link_and_non_links() {
let model = cm("<b>{test_bold <a href=\"https://element.io\">test}|_link</a> test_bold</b>");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test_link"))
)
}

Expand All @@ -99,7 +99,7 @@ fn get_link_action_from_selection_that_contains_multiple_links() {
let model = cm("{<a href=\"https://element.io\">test_element</a> <a href=\"https://matrix.org\">test_matrix</a>}|");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test_element"))
)
}

Expand All @@ -108,7 +108,7 @@ fn get_link_action_from_selection_that_contains_multiple_links_partially() {
let model = cm("<a href=\"https://element.io\">test_{element</a> <a href=\"https://matrix.org\">test}|_matrix</a>");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test_element"))
)
}

Expand All @@ -118,7 +118,7 @@ fn get_link_action_from_selection_that_contains_multiple_links_partially_in_diff
let model = cm("<a href=\"https://element.io\"> <b>test_{element</b></a> <i><a href=\"https://matrix.org\">test}|_matrix</a></i>");
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16(" test_element"))
)
}

Expand Down Expand Up @@ -176,7 +176,7 @@ fn get_link_action_on_blank_selection_after_a_link() {
// This is the correct behaviour because the end of a link should be considered part of the link itself
assert_eq!(
model.get_link_action(),
LinkAction::Edit(utf16("https://element.io"))
LinkAction::Edit(utf16("https://element.io"), utf16("test"))
)
}

Expand Down Expand Up @@ -224,7 +224,7 @@ fn get_link_action_on_multiple_link_with_first_immutable() {
model.select(Location::from(20), Location::from(20));
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
}

Expand All @@ -240,7 +240,7 @@ fn get_link_action_on_multiple_link_with_last_immutable() {
model.select(Location::from(0), Location::from(0));
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
}

Expand Down Expand Up @@ -281,13 +281,13 @@ fn get_link_action_on_multiple_link_with_first_is_mention() {
"#});
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
// Selecting the link afterwards works
model.select(Location::from(10), Location::from(10));
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
}

Expand All @@ -300,12 +300,12 @@ fn get_link_action_on_multiple_link_with_last_is_mention() {
"#});
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
// Selecting the mutable link afterwards works
model.select(Location::from(0), Location::from(0));
assert_eq!(
model.get_link_action(),
LinkAction::Edit("https://rust-lang.org".into()),
LinkAction::Edit("https://rust-lang.org".into(), utf16("Rust_mut")),
);
}
94 changes: 94 additions & 0 deletions crates/wysiwyg/src/tests/test_links.rs
Original file line number Diff line number Diff line change
Expand Up @@ -943,3 +943,97 @@ fn set_links_in_list_then_add_list_item() {
"<ul><li><a href=\"https://matrix.org\">test</a></li><li>|</li></ul>"
);
}

#[test]
fn edit_entirely_selected_link() {
let mut model = cm("<a href=\"https://mtrix.org\">{Mtrix}|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(tx(&model), "<a href=\"https://matrix.org\">Matrix|</a>");
}

#[test]
fn edit_partially_selected_link() {
let mut model = cm("<a href=\"https://mtrix.org\">Mtr{ix}|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(tx(&model), "<a href=\"https://matrix.org\">Matrix|</a>");
}

#[test]
fn edit_link_from_cursor_position() {
let mut model = cm("<a href=\"https://mtrix.org\">Mtrix|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(tx(&model), "<a href=\"https://matrix.org\">Matrix|</a>");
}

#[test]
fn edit_link_with_multiple_links_edits_first_occurence() {
// Note: this behaviour might change if we allow replacing
// text with the entire extended plain text from the selection.
let mut model = cm("<a href=\"https://mtrix.org\">{Mtrix</a> <a href=\"https://matrix.org\">Matrix}|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(
tx(&model),
"<a href=\"https://matrix.org\">Matrix|</a> <a href=\"https://matrix.org\">Matrix</a>",
);
}

#[test]
fn edit_link_with_multiple_partially_selected_links_edits_first_occurence() {
// Note: this behaviour might change if we allow replacing
// text with the entire extended plain text from the selection.
let mut model = cm("<a href=\"https://mtrix.org\">Mt{rix</a> <a href=\"https://matrix.org\">Mat}|rix</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(
tx(&model),
"<a href=\"https://matrix.org\">Matrix|</a> <a href=\"https://matrix.org\">Matrix</a>",
);
}

#[test]
fn edit_entirely_formatted_link_keeps_formatting() {
let mut model =
cm("<a href=\"https://mtrix.org\">{<strong>Mtrix</strong>}|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(
tx(&model),
"<a href=\"https://matrix.org\"><strong>Matrix|</strong></a>",
);
}

#[test]
fn edit_partially_formatted_link_removes_formatting() {
// Note: replacing the text of the link makes the formatting position ambiguous
// it is better to remove it than provide unexpected content.
let mut model =
cm("<a href=\"https://mtrix.org\">{M<strong>tri</strong>x}|</a>");
model.edit_link_with_text(
"https://matrix.org".into(),
"Matrix".into(),
vec![],
);
assert_eq!(tx(&model), "<a href=\"https://matrix.org\">Matrix|</a>");
}
Loading