yrby 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +56 -0
- data/Cargo.lock +644 -0
- data/README.md +25 -0
- data/ext/yrby/src/lib.rs +61 -3
- data/ext/yrby/src/prosemirror_html.rs +896 -0
- data/ext/yrby/src/protocol.rs +266 -15
- data/ext/yrby/src/read.rs +84 -8
- data/lib/y/version.rb +1 -1
- metadata +3 -4
- data/lib/y/decoder/version.rb +0 -7
- data/lib/y/decoder.rb +0 -66
- data/lib/yrby-decoder.rb +0 -4
data/ext/yrby/src/lib.rs
CHANGED
|
@@ -6,6 +6,7 @@ use yrs::updates::decoder::Decode;
|
|
|
6
6
|
use yrs::updates::encoder::Encode;
|
|
7
7
|
use yrs::{Doc, GetString, ReadTxn, Transact};
|
|
8
8
|
|
|
9
|
+
mod prosemirror_html;
|
|
9
10
|
mod protocol;
|
|
10
11
|
mod read;
|
|
11
12
|
use protocol::{
|
|
@@ -30,6 +31,7 @@ struct RbDoc(Doc);
|
|
|
30
31
|
fn assert_thread_safe() {
|
|
31
32
|
fn is_send_sync<T: Send + Sync>() {}
|
|
32
33
|
is_send_sync::<Doc>();
|
|
34
|
+
is_send_sync::<RbProseMirror>();
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/// Run `f` with the GVL (Global VM Lock) released, so other Ruby threads,
|
|
@@ -160,9 +162,11 @@ impl RbDoc {
|
|
|
160
162
|
fn read_text(&self, name: String) -> Option<String> {
|
|
161
163
|
let doc = &self.0;
|
|
162
164
|
nogvl(move || {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
// Exactly ONE transaction per call. Opening a second while the
|
|
166
|
+
// first is still held deadlocks against a waiting writer — and
|
|
167
|
+
// inside nogvl that hang can't be interrupted.
|
|
168
|
+
let txn = doc.transact();
|
|
169
|
+
txn.get_text(name.as_str()).map(|t| t.get_string(&txn))
|
|
166
170
|
})
|
|
167
171
|
}
|
|
168
172
|
|
|
@@ -327,6 +331,55 @@ impl RbDoc {
|
|
|
327
331
|
}
|
|
328
332
|
}
|
|
329
333
|
|
|
334
|
+
// ============================================================================
|
|
335
|
+
// Y::ProseMirror — schema-pinned rendering of ProseMirror/Tiptap documents
|
|
336
|
+
// ============================================================================
|
|
337
|
+
|
|
338
|
+
/// A ProseMirror/Tiptap view over a `Y::Doc`. The schema knowledge (node/mark
|
|
339
|
+
/// names, Tiptap's serializer semantics) lives here rather than on the
|
|
340
|
+
/// schema-agnostic `Doc`. Holds a cheap clone of the doc (yrs `Doc` is an Arc
|
|
341
|
+
/// handle), so it reads live state.
|
|
342
|
+
///
|
|
343
|
+
/// Thread safety matches `Y::Doc`: every method opens its own transaction
|
|
344
|
+
/// inside `nogvl` and holds no lock across the GVL boundary.
|
|
345
|
+
#[magnus::wrap(class = "Y::ProseMirror", free_immediately, size)]
|
|
346
|
+
struct RbProseMirror(Doc);
|
|
347
|
+
|
|
348
|
+
impl RbProseMirror {
|
|
349
|
+
/// `Y::ProseMirror.new(doc)`
|
|
350
|
+
fn new(doc: &RbDoc) -> Self {
|
|
351
|
+
RbProseMirror(doc.0.clone())
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/// Render an XML root (default `"default"`, the fragment name Tiptap's
|
|
355
|
+
/// Collaboration extension uses) to HTML. Output follows tiptap-php and
|
|
356
|
+
/// matches Tiptap's `getHTML()` on the reference fixture; see
|
|
357
|
+
/// `prosemirror_html.rs` for coverage and caveats. Returns nil when the
|
|
358
|
+
/// root is missing or not ProseMirror-shaped (e.g. a Lexical document).
|
|
359
|
+
fn to_html(&self, args: &[Value]) -> Result<Option<String>, Error> {
|
|
360
|
+
if args.len() > 1 {
|
|
361
|
+
let ruby = Ruby::get().unwrap();
|
|
362
|
+
return Err(Error::new(
|
|
363
|
+
ruby.exception_arg_error(),
|
|
364
|
+
format!(
|
|
365
|
+
"wrong number of arguments (given {}, expected 0..1)",
|
|
366
|
+
args.len()
|
|
367
|
+
),
|
|
368
|
+
));
|
|
369
|
+
}
|
|
370
|
+
let name: String = match args.first() {
|
|
371
|
+
Some(arg) => TryConvert::try_convert(*arg)?,
|
|
372
|
+
None => "default".to_string(),
|
|
373
|
+
};
|
|
374
|
+
let doc = &self.0;
|
|
375
|
+
Ok(nogvl(move || {
|
|
376
|
+
let txn = doc.transact();
|
|
377
|
+
let fragment = txn.get_xml_fragment(name.as_str())?;
|
|
378
|
+
prosemirror_html::render(&txn, &fragment)
|
|
379
|
+
}))
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
330
383
|
// ============================================================================
|
|
331
384
|
// Protocol codec (stateless), exposed as `Y` module functions
|
|
332
385
|
// ============================================================================
|
|
@@ -407,6 +460,11 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
|
407
460
|
"handle_sync_message",
|
|
408
461
|
method!(RbDoc::handle_sync_message, 1),
|
|
409
462
|
)?;
|
|
463
|
+
// Y::ProseMirror: schema-pinned ProseMirror/Tiptap rendering over a Doc.
|
|
464
|
+
let prosemirror_class = module.define_class("ProseMirror", ruby.class_object())?;
|
|
465
|
+
prosemirror_class.define_singleton_method("new", function!(RbProseMirror::new, 1))?;
|
|
466
|
+
prosemirror_class.define_method("to_html", method!(RbProseMirror::to_html, -1))?;
|
|
467
|
+
|
|
410
468
|
// Stateless protocol codec, as Y module functions.
|
|
411
469
|
module.define_module_function("wrap_update", function!(wrap_update, 1))?;
|
|
412
470
|
module.define_module_function("message_kind", function!(message_kind, 1))?;
|