Expose to JS

This commit is contained in:
Andras Schmelczer 2025-10-26 21:44:43 +00:00
parent 450eaaff05
commit 1b46e5d237
9 changed files with 95 additions and 17 deletions

38
Cargo.lock generated
View file

@ -124,6 +124,12 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "memory_units"
version = "0.4.0"
@ -182,6 +188,7 @@ dependencies = [
"insta",
"pretty_assertions",
"serde",
"serde_json",
"serde_yaml",
"test-case",
"wasm-bindgen",
@ -212,24 +219,47 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"

View file

@ -24,6 +24,7 @@ path = "examples/merge-file.rs"
serde = { version = "1.0.219", optional = true, features = ["derive"] }
wasm-bindgen = { version = "0.2.99", optional = true }
serde_json = { version = "1.0.145", optional = true }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
@ -36,9 +37,9 @@ wee_alloc = { version = "0.4.2", optional = true }
[features]
default = []
serde = [ "dep:serde" ]
wasm = [ "dep:wasm-bindgen", "dep:wee_alloc" ]
wasm = [ "dep:wasm-bindgen", "dep:wee_alloc", "dep:serde_json", "serde" ]
console_error_panic_hook = [ "dep:console_error_panic_hook" ]
all = [ "serde", "wasm", "console_error_panic_hook" ]
all = [ "wasm", "console_error_panic_hook" ]
[dev-dependencies]
insta = "1.42.2"

View file

@ -5,6 +5,7 @@ import {
SpanWithHistory as wasmSpanWithHistory,
reconcileWithHistory as wasmReconcileWithHistory,
isBinary as wasmIsBinary,
getCompactDiff as wasmGetCompactDiff,
initSync,
} from 'reconcile-text';
@ -179,6 +180,26 @@ export function reconcile(
return jsResult;
}
export function getCompactDiff(
original: string,
changed: string | TextWithOptionalCursors,
tokenizer: BuiltinTokenizer = 'Word'
): string {
init();
if (!BUILTIN_TOKENIZERS.includes(tokenizer)) {
throw new Error(UNSUPPORTED_TOKENIZER_ERROR);
}
const changedWasm = toWasmTextWithCursors(changed);
const result = wasmGetCompactDiff(original, changedWasm, tokenizer);
changedWasm.free();
return result;
}
/**
* Merges three versions of text and returns detailed provenance information.
*

View file

@ -170,7 +170,7 @@
//! &changes.into()
//! );
//!
//! let serialized = serde_yaml::to_string(&result.serialise_as_change_set()).unwrap();
//! let serialized = serde_yaml::to_string(&result.to_change_set()).unwrap();
//! assert_eq!(
//! serialized,
//! concat!(

View file

@ -350,7 +350,7 @@ where
/// This is useful for sending changes over the network if there's
/// a clear consensus on the original text.
#[must_use]
pub fn serialise_as_change_set(&self) -> ChangeSet {
pub fn to_change_set(&self) -> ChangeSet {
ChangeSet::new(
SimpleOperation::from_operations(&self.operations),
self.cursors.clone(),
@ -428,7 +428,7 @@ mod tests {
let original = "Merging text is hard!";
let changes = "Merging text is easy with reconcile!";
let result = EditedText::from_strings(original, &changes.into());
let serialized = serde_yaml::to_string(&result.serialise_as_change_set()).unwrap();
let serialized = serde_yaml::to_string(&result.to_change_set()).unwrap();
let expected = concat!(
"operations:\n",
@ -448,7 +448,7 @@ mod tests {
let edited_text = EditedText::from_strings(original, &updated.into());
let change_set = edited_text.serialise_as_change_set();
let change_set = edited_text.to_change_set();
let deserialized_edited_text =
EditedText::from_change_set(original, change_set, &*BuiltinTokenizer::Word);

View file

@ -147,7 +147,7 @@ impl<'de> Deserialize<'de> for SimpleOperation {
type Value = SimpleOperation;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an integer between -2^64 and 2^63 or a string")
formatter.write_str("an integer between -2^63 and 2^64-1 or a string")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>

View file

@ -87,6 +87,25 @@ pub fn generic_reconcile(
}
}
/// WASM wrapper around getting a compact diff representation as a JSON string
///
/// # Panics
///
/// If serialization to JSON fails which should not happen
#[wasm_bindgen(js_name = getCompactDiff)]
#[must_use]
pub fn get_compact_diff(
parent: &str,
changed: &TextWithCursors,
tokenizer: BuiltinTokenizer,
) -> String {
set_panic_hook();
let edited_text = crate::EditedText::from_strings_with_tokenizer(parent, changed, &*tokenizer);
let change_set = edited_text.to_change_set();
serde_json::to_string(&change_set).expect("Failed to serialize change set")
}
/// Heuristically determine if the given data is a binary or a text file's
/// content.
#[wasm_bindgen(js_name = isBinary)]

View file

@ -46,12 +46,11 @@ fn test_document_one_way_with_cursors_and_serialisation() {
&*BuiltinTokenizer::Word,
);
let serialised_left = serde_yaml::from_str(
&serde_yaml::to_string(&left_operations.serialise_as_change_set()).unwrap(),
)
.unwrap();
let serialised_left =
serde_yaml::from_str(&serde_yaml::to_string(&left_operations.to_change_set()).unwrap())
.unwrap();
let serialised_right = serde_yaml::from_str(
&serde_yaml::to_string(&right_operations.serialise_as_change_set()).unwrap(),
&serde_yaml::to_string(&right_operations.to_change_set()).unwrap(),
)
.unwrap();

View file

@ -46,7 +46,7 @@ fn test_merge_text_with_cursors() {
}
#[wasm_bindgen_test(unsupported = test)]
fn merge_binary() {
fn test_merge_binary() {
let left = [0, 1, 2];
let right = [3, 4, 5];
assert_eq!(
@ -62,6 +62,14 @@ fn test_is_binary() {
assert!(!is_binary(b"hello"));
}
#[wasm_bindgen_test(unsupported = test)]
fn test_get_compact_diff() {
let parent = "hello ";
let changed = "world";
let result = get_compact_diff(parent, &changed.into(), BuiltinTokenizer::Word);
assert_eq!(result, "{\"operations\":[-6,\"world\"],\"cursors\":[]}");
}
#[wasm_bindgen_test(unsupported = test)]
fn test_is_binary_empty() {
assert!(!is_binary(b""));