What are the rules for replace

Hi,

I’d like to delete everything between two positions:

pm.tr.delete(from, to).apply()

which calls replace with an empty slice. My question is about what constraints on the positions have to be met for a replace to succeed. When I make the call, it doesn’t do anything and it doesn’t report any exception or warning. I have the following document:

{
  "type": "doc",
  "content": [
    {
      "type": "discourseContainer",
      "attrs": {
        "title": "Chapter"
      },
      "content": [
        {
          "type": "paragraph"
        },
        {
          "type": "discourseContainer",
          "attrs": {
            "title": "Section"
          },
          "content": [
            {
              "type": "paragraph"
            }
          ]
        }
      ]
    }
  ]
}

So the document has 1 custom typed node, which in turns has an empty paragraph and another customer typed node. I try:

pm.tr.delete(2,6) with the intent of delete the nested custom node along with its content. Position 2 puts me within the paragraph inside my “Chapter” node and position 6 should be right after the paragraph inside the “Section” node. But it doesn’t work and there are no errors or exceptions. Curiously, both delete(2,5) or delete(2.7) do the job, but that particular position, right after the nested paragraph seems to mess things up. What am I doing wrong here? Even if I’m doing something wrong, clearly I’m trying to replace non-empty content, so I should some error if my position counting is wrong, no?

Thanks! Boris

Replace automatically ensures that a document keeps in a valid shape. In this case, because the first paragraph and the section are only partially cut, they stay. And since a section can’t be empty (since that would make it impossible to put a cursor inside of it), a new empty paragraph is created for it after your delete step removed the old one. Thus the whole thing does indeed end up looking like a no-op.

If you want to remove the empty section, pass positions before and after it. You can’t remove the paragraph and keep the section, except if you give your section a canBeEmpty getter returning true, but that’s probably not a good idea, since it’ll create content that renders weirdly (the section will probably be invisible).

Ok, so for something to be deleted/replace it must fall completely within the [start, end], including the “virtual” closing positions. Ok, I will try to work with that rule, thanks!

But the fact that the section is deleted when I do pm.tr.delete(2,5) where 5 is right after the start of the nested paragraph (within the section) is a bit confusing…is there an explanation? Should be even worse than delete(2,6) where at least I’m after the nested paragraph boundary.

In the case of .delete(2, 5), both sides of the deleted range are texblocks, and thus can be merged, which ProseMirror will do.

If you think this is needlessly complicated, spend a little time thinking about the way deleting/inserting/overwriting works in WYSIWYG-style editors. There is no simple set of rules that will give you the ‘obvious’ behavior that the user expects. I came up with a formalization that’s sort of regular and covers most cases, but it’s still not perfect, and definitely not easy to think about.

Ok, thanks. I appreciate the difficulty with defining a set of rules that lead to a sensible behavior for the end-user. I’m facing the same problem :slight_smile: Re the ‘replace/delete’ operation, as long as I understand what the rules are, I’ll be able to work around them (or with them as the case may be), so thanks for explaining that!

Best, Boris