mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
add auto format link test
This commit is contained in:
parent
41eacb7000
commit
0fb808ef4c
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>;
|
||||
|
@ -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) }
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user