add auto format link test

This commit is contained in:
appflowy 2021-08-16 17:00:39 +08:00
parent 41eacb7000
commit 0fb808ef4c
10 changed files with 153 additions and 17 deletions

View File

@ -69,6 +69,7 @@ class Document {
length: replaceLength,
);
print('current document delta: $_delta');
print('insert delta: $delta');
compose(delta, ChangeSource.LOCAL);
print('compose insert, current document $_delta');
@ -79,6 +80,7 @@ class Document {
assert(index >= 0 && length > 0);
final delta = _rules.apply(RuleType.DELETE, this, index, length: length);
if (delta.isNotEmpty) {
print('current document delta: $_delta');
compose(delta, ChangeSource.LOCAL);
print('compose delete, current document $_delta');
}
@ -124,6 +126,7 @@ class Document {
attribute: attribute,
);
if (formatDelta.isNotEmpty) {
print('current document delta: $_delta');
compose(formatDelta, ChangeSource.LOCAL);
print('compose format, current document $_delta');
delta = delta.compose(formatDelta);

View File

@ -14,6 +14,7 @@ log = "0.4"
color-eyre = { version = "0.5", default-features = false }
chrono = "0.4.19"
lazy_static = "1.4.0"
url = "2.2"
[dev-dependencies]
criterion = "0.3"

View File

@ -7,19 +7,83 @@ pub struct AutoFormatExt {}
impl InsertExt for AutoFormatExt {
fn ext_name(&self) -> &str { "AutoFormatExt" }
fn apply(&self, delta: &Delta, _replace_len: usize, text: &str, index: usize) -> Option<Delta> {
fn apply(&self, delta: &Delta, replace_len: usize, text: &str, index: usize) -> Option<Delta> {
// enter whitespace to trigger auto format
if !is_whitespace(text) {
return None;
}
let mut iter = DeltaIter::new(delta);
iter.seek::<CharMetric>(index);
let prev = iter.next_op();
if prev.is_none() {
return None;
}
if let Some(prev) = iter.next_op_before(index) {
match AutoFormat::parse(prev.get_data()) {
None => {},
Some(formatter) => {
let mut new_attributes = prev.get_attributes();
let _prev = prev.unwrap();
// format_len should not greater than index. The url crate will add "/" to the
// end of input string that causes the format_len greater than the input string
let format_len = min(index, formatter.format_len());
let format_attributes = formatter.to_attributes();
format_attributes.iter().for_each(|(k, v)| {
if !new_attributes.contains_key(k) {
new_attributes.insert(k.clone(), v.clone());
}
});
let next_attributes = match iter.next_op() {
None => Attributes::empty(),
Some(op) => op.get_attributes(),
};
return Some(
DeltaBuilder::new()
.retain(index + replace_len - min(index, format_len))
.retain_with_attributes(format_len, format_attributes)
.insert_with_attributes(text, next_attributes)
.build(),
);
},
}
}
None
}
}
use crate::{
client::extensions::NEW_LINE,
core::{Attribute, AttributeBuilder, Attributes, DeltaBuilder, Operation},
};
use bytecount::num_chars;
use std::cmp::min;
use url::{ParseError, Url};
pub enum AutoFormatter {
Url(Url),
}
impl AutoFormatter {
pub fn to_attributes(&self) -> Attributes {
match self {
AutoFormatter::Url(url) => AttributeBuilder::new().link(url.as_str(), true).build(),
}
}
pub fn format_len(&self) -> usize {
let s = match self {
AutoFormatter::Url(url) => url.to_string(),
};
num_chars(s.as_bytes())
}
}
pub struct AutoFormat {}
impl AutoFormat {
fn parse(s: &str) -> Option<AutoFormatter> {
if let Ok(url) = Url::parse(s) {
return Some(AutoFormatter::Url(url));
}
None
}

View File

@ -9,6 +9,7 @@ mod format;
mod insert;
pub const NEW_LINE: &'static str = "\n";
pub const WHITESPACE: &'static str = " ";
pub type InsertExtension = Box<dyn InsertExt>;
pub type FormatExtension = Box<dyn FormatExt>;

View File

@ -1,4 +1,7 @@
use crate::{client::extensions::NEW_LINE, core::Operation};
use crate::{
client::extensions::{NEW_LINE, WHITESPACE},
core::Operation,
};
#[inline]
pub fn find_newline(s: &str) -> Option<usize> {
@ -60,7 +63,7 @@ pub fn is_op_contains_newline(op: &Operation) -> bool { contain_newline(op.get_d
pub fn is_newline(s: &str) -> bool { s == NEW_LINE }
#[inline]
pub fn is_whitespace(s: &str) -> bool { s == " " }
pub fn is_whitespace(s: &str) -> bool { s == WHITESPACE }
#[inline]
pub fn contain_newline(s: &str) -> bool { s.contains(NEW_LINE) }

View File

@ -96,7 +96,7 @@ impl std::convert::Into<Attributes> for Attribute {
}
}
pub struct AttrsBuilder {
pub struct AttributeBuilder {
inner: Attributes,
}
@ -112,7 +112,19 @@ macro_rules! impl_bool_attribute {
};
}
impl AttrsBuilder {
macro_rules! impl_str_attribute {
($name: ident,$key: expr) => {
pub fn $name(self, s: &str, value: bool) -> Self {
let value = match value {
true => s,
false => REMOVE_FLAG,
};
self.insert($key, value)
}
};
}
impl AttributeBuilder {
pub fn new() -> Self {
Self {
inner: Attributes::default(),
@ -134,10 +146,12 @@ impl AttrsBuilder {
self
}
// AttributeBuilder::new().bold(true).build()
impl_bool_attribute!(bold, AttributeKey::Bold);
impl_bool_attribute!(italic, AttributeKey::Italic);
impl_bool_attribute!(underline, AttributeKey::Underline);
impl_bool_attribute!(strike_through, AttributeKey::StrikeThrough);
impl_str_attribute!(link, AttributeKey::Link);
pub fn build(self) -> Attributes { self.inner }
}

View File

@ -3,7 +3,7 @@ pub mod helper;
use crate::helper::{TestOp::*, *};
use flowy_ot::core::Interval;
use flowy_ot::client::extensions::NEW_LINE;
use flowy_ot::client::extensions::{NEW_LINE, WHITESPACE};
#[test]
fn attributes_insert_text() {
@ -587,3 +587,51 @@ fn attributes_link_insert_newline_at_middle() {
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn attributes_auto_format_link() {
let site = "https://appflowy.io";
let ops = vec![
Insert(0, site, 0),
AssertOpsJson(0, r#"[{"insert":"https://appflowy.io\n"}]"#),
Insert(0, WHITESPACE, site.len()),
AssertOpsJson(
0,
r#"[{"insert":"https://appflowy.io","attributes":{"link":"https://appflowy.io/"}},{"insert":" \n"}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn attributes_auto_format_exist_link() {
let site = "https://appflowy.io";
let ops = vec![
Insert(0, site, 0),
Link(0, Interval::new(0, site.len()), site, true),
Insert(0, WHITESPACE, site.len()),
AssertOpsJson(
0,
r#"[{"insert":"https://appflowy.io","attributes":{"link":"https://appflowy.io/"}},{"insert":" \n"}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}
#[test]
fn attributes_auto_format_exist_link2() {
let site = "https://appflowy.io";
let ops = vec![
Insert(0, site, 0),
Link(0, Interval::new(0, site.len() / 2), site, true),
Insert(0, WHITESPACE, site.len()),
AssertOpsJson(
0,
r#"[{"insert":"https://a","attributes":{"link":"https://appflowy.io"}},{"insert":"ppflowy.io \n"}]"#,
),
];
OpTester::new().run_script_with_newline(ops);
}

View File

@ -3,6 +3,8 @@ use flowy_ot::{client::Document, core::*};
use rand::{prelude::*, Rng as WrappedRng};
use std::{sync::Once, time::Duration};
const LEVEL: &'static str = "info";
#[derive(Clone, Debug, Display)]
pub enum TestOp {
#[display(fmt = "Insert")]
@ -63,7 +65,7 @@ impl OpTester {
static INIT: Once = Once::new();
INIT.call_once(|| {
color_eyre::install().unwrap();
std::env::set_var("RUST_LOG", "info");
std::env::set_var("RUST_LOG", LEVEL);
env_logger::init();
});

View File

@ -226,7 +226,7 @@ fn delta_seek_4() {
#[test]
fn delta_seek_5() {
let mut delta = Delta::default();
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
let attributes = AttributeBuilder::new().bold(true).italic(true).build();
delta.add(
OpBuilder::insert("1234")
.attributes(attributes.clone())
@ -469,7 +469,7 @@ fn transform2() {
fn delta_transform_test() {
let mut a = Delta::default();
let mut a_s = String::new();
a.insert("123", AttrsBuilder::new().bold(true).build());
a.insert("123", AttributeBuilder::new().bold(true).build());
a_s = a.apply(&a_s).unwrap();
assert_eq!(&a_s, "123");

View File

@ -2,7 +2,7 @@ use flowy_ot::core::*;
#[test]
fn operation_insert_serialize_test() {
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
let attributes = AttributeBuilder::new().bold(true).italic(true).build();
let operation = OpBuilder::insert("123").attributes(attributes).build();
let json = serde_json::to_string(&operation).unwrap();
eprintln!("{}", json);
@ -32,7 +32,7 @@ fn operation_delete_serialize_test() {
fn delta_serialize_test() {
let mut delta = Delta::default();
let attributes = AttrsBuilder::new().bold(true).italic(true).build();
let attributes = AttributeBuilder::new().bold(true).italic(true).build();
let retain = OpBuilder::insert("123").attributes(attributes).build();
delta.add(retain);