Multilevel Rich Text OTType

152 views
Skip to first unread message

Jason Chen

unread,
Nov 10, 2015, 4:13:22 AM11/10/15
to ShareJS
Hello everyone,

As I'm building out Quill 1.0, I'm running into some challenges that is making me consider an eventual update to rich-text. This is not a proposal--just wanted to share some thoughts and solicit feedback.

The main issue is that rich-text is a 1-dimensional data format. Without any depth, it's always been awkward to represent content presented by a tree structure like the DOM. The current workaround is the use the newline character as a way to format lines. For example:

[
 
{ insert: 'Grocery List\nEggs' },
 
{ insert: '\n', { list: true } },
 
{ insert: 'Milk' },
 
{ insert: '\n', { list: true } },
 
{ insert: 'Note: Get quarters\n' }
]


A couple of side effects from this dependency on newlines:

- Every line needs to end with a newline
- The empty document is a newline
- Lookahead is required when building the DOM representation

This actually isn't as bad as all the problem it solves but there is content that I would eventually like to add to Quill that currently cannot be represented and maintain OT properties out of the box, such as tables.

One thought to solve the newline annoyance is a two dimensional. The type would have a linear array of "sections", and each section's contents is essentially an old rich-text. For example:

[
 
{ section: 'paragraph', insert: [{ insert: 'Grocery List' }] },
 
{ section: 'list', insert: [{ insert: 'Eggs\nMilk' }] },
 
{ section: 'paragraph', insert: [{ insert: 'Note: get quarters' }] }
]


Deleting or retaining (without formatting) an entire section would look similar to the current rich-text delete and retain instructions. Modifying text within an element could look something like this:

[
 
{ retain: 1 },
 
{
    retain
: [   // A standard rich-text delta as the retain value would indicate subsection modification
     
{ retain: 4, { bold: true } }
   
]
 
},
 
{ retain: 1, { section: 'footnote' } }
]


The above instruction would keep the first paragraph, bold "Eggs" and change the last line "Note: Get quarters" into a list. Note this instruction will not cause a merger with the previous list. Also we can simultaneously change subsection content as well as the section type itself.

Most content in a rich text document is either block level or inline level so the two levels would map this nicely. There does not appear to be much added complexity (one area where I'd definitely like some feedback) since the subsections behave the same way as sections themselves. The most problematic conflict I could think of is modifying a section that a remote user deletes, but this the same case as formatting a letter that a remote user deletes, which rich-text already handles (ignores the format for user 1 and deletes for formatted character for user 2).

But this format actually still does not solve the problem of representing tables since tables have 3 levels (tables, rows, cells).

The obvious next step then is the make this "sections" idea recursive. Now if the assumption that going from 1 level to 2 is not much more complexity holds, it should also hold from 2 to N with just a one difference I can currently think of: 

The depth level is now variable--it's now neither always 1 level deep like rich-text is currently nor always 2 levels deep like the non-recursive proposal above. So in theory a user could get an instruction that modifies the 3rd level of a section but he/she currently only sees two levels for that particular section. I'm not sure this is actually possible though given ShareJS's central server architecture (if someone could come up with a counterexample that would put this question to rest). One solution or just to limit the complexity could be that a given section could also declare an immutable integer depth. This would mean we could no longer change section types of unequal depths so no turning a list into a table or a table into a paragraph (which may also mean declaring the section types and depths ahead of time?).

I only briefly looked at the json0 ottype and am not sure it could solve this multilevel rich text format problem (one issue is a json0 document is an object and not an array but I see there was some discussion about representing as an array but not sure where that landed). Also not sure if this use case is cutting across the complexity boundaries that the new ShareJS wants to avoid.

If my optimism on the complexity of adding multiple levels to rich-text holds then this begs the question: can sections be analogous to DOM nodes and use this to OT a DOM tree? Has anyone used the json0 ottype to do this? Is one of the goals of the new json ottype to synchronize DOM trees?

Thanks in advance for any feedback or thoughts.

Devon Govett

unread,
Nov 10, 2015, 12:49:21 PM11/10/15
to sha...@googlegroups.com
I think using a rich text type as a subtype of a JSON type is the right way to go, and this is exactly what we do at Storify. We use an array of blocks, each of which can either be a rich text element, or another type of element (such as an image, tweet, etc.). We even use separate rich text blocks for each paragraph of text. You could do the same thing with tables or other types of elements.

The JSON0 type works fine for this, though if you have more than two levels, it may not work as well. The JSON1 type that Joseph is working on should make dealing with arbitrary JSON trees much better (and faster too). So I’d suggest using that, with the rich text type as a subtype, once it is ready. Much better than reinventing the wheel IMO, and better to keep the rich text type itself as simple as possible.

--
You received this message because you are subscribed to the Google Groups "ShareJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sharejs+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Joe Leaver

unread,
Nov 10, 2015, 1:11:49 PM11/10/15
to ShareJS
I hope Joseph will weigh in, but from what I see coming in JSON1, I think it may help solve a couple of these issues, especially around tree depth. (Which is specifically the reason I'm eagerly eagerly, eagerly) awaiting it. Did I mention I was excited about json1 as much as one can appropriately be excited about such things? (and a little more) 

Jason Chen

unread,
Nov 11, 2015, 3:13:22 PM11/11/15
to ShareJS
Thanks for the insights Devon! One challenge is rich-text's delete does not contain enough information for invert. I'll hold off for now and wait for more json1 details.
Reply all
Reply to author
Forward
0 new messages