How to represent range-shaped information: marks or decorations?

(related to Discussion: What are marks and Block level marks, and In v0.12 what's the right for "user annotation" use case?)

What is the recommended way of representing range shaped information such as comments?

From what I’m gathered,

  • Decorations: make it easier to model cross-node content and are correctly not part of the document (so you don’t have to filter them out when serializing it), BUT don’t work out of the box with redo/undo operations, nor copy/paste operations (and likely harder to integrate into collab operations, too)
  • Marks: recommend for inline-nodes, but block-nodes are allowed (assuming @marijn hasn’t changed his mind) and if you use them for ranged-information you get redo/undo, copy/paste, and collab all working out of the box, BUT you’ll have to filter them out when serializing the document
  • Both decorations and marks: you have to manually change the element (i.e. div or span depending on whether it’s wrapping block or inline content, if both are a requirement

From previous discussions, it seems like Marjin recommends going the decoration approach (likely due to the ambiguity around block-level marks). The collab example on the site also uses this approach as well, though its use-case is quite simple.

However, unless there are other factors I’m not considering (please weigh in), you seem to get more out of the box if you implemented range related features it via marks. So I’d love to know where people are at with this.

based on above discussions ccing/ @saranrapjs @johanneswilm @bradleyayers

1 Like

A third possibility is to use marks on inline content and add a special attribute (for example called marks) to all block level nodes that you need to cover as well. You still need to add some logic to make the special attribute work similar to regular marks on inline content, but you are free to decide how the attribute makes itself shown when creating the DOM object. If using marks on block level elements, you may end up with incorrect HTML5, such as this construction:

<ul>
    <span class="em">
        <li>...</li>
    </span>
</ul>

Ah that’s a good edge case. I dislike having to split up logic like that but I guess there’s no mess-free way of handling this.

I guess another possibility is representing all ranged information as special node attributes in the nearest parent node. But I’m thinking I’d be difficult to keep that consistent too, i.e. if range gets split into two elements

It’s also worth mentioning that two of the most powerful editors I’ve used - google docs and notion - both use only div/span elements to represent their content, likely because of reasons you mentioned

related to another question I asked: Is there a drawback to using non-semantic HTML for nodes?

So going with this approach you’d just need a seperate serializer for clipboard operations but seems like less work than maintaining three types of mark comments (inline marks, block marks, and node attributes) or making everything work for decorations (undo/redo, collab, etc)

(Posting my continued findings / thought-process here)

Another consideration for ranged information such as comments is: do you need to support multiline information?

If so, then you have to consider what happens when a node gets split.

Marks give you these three options:

// Original
<div class="comment">
  <p>My text with a comment! </p>
</div>

// Split text options:

// 1. Lift text out of comment
<div class="comment">
  <p>My text </p>
</div>
 <p>with a comment! </p>

// 2. Stay in comment, only lift out when entering at end of node
<div class="comment">
  <p>My text </p>
 <p>with a comment! </p>
</div>

// 3. divide comment and keep track of it in multiple places
<div class="comment" id="1">
 <p>My text </p>
</div>
<div class="comment" id="1">
 <p>with a comment! </p>
</div>

Where as with decorations, to support multi-line comments, I think you can just pass an inline-decorations the from and to positions and prosemirror takes care of this for you.

So you might not get undo/redo or copy/paste out of the box, but at least Prosemirror takes care of these sort of edge cases for you in a consistent manner