Keep marks active on empty lines and line breaks

Hi,

I am trying to achieve something like the “Comments” plugin in the collaboration demo but with Marks instead.

So given a document -

{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "abcd"
        }
      ]
    },
    {
      "type": "paragraph"
    },
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "efg"
        }
      ]
    }
  ]
}
  1. When I select the text from “cd” to “ef” and apply “Bold”, the text parts get the marks applied. If I write new content in middle (the empty paragraph) - How do I apply the bold mark to that as well automatically?

  2. How do I achieve the same when a new line is inserted between 2 mark nodes?

Do I need to scan through the previous and next nodes to figure out what mark needs to be applied or is there a direct way to achieve this? Goal is to keep the mark continuous (Like in Google Docs, if you create a comment - You can edit that comment. add new lines etc and it keeps everything under the comment. You can move out of the comment when you click on the end)

Update - I created a very rough plugin to iterate and find the previous and next text nodes - Check their marks - Match the attributes of the marks - Extract the common marks and then ensureMarks

        appendTransaction (tr, oldState, newState) {
                    let pos = newState.tr.selection.from - 1
                    // eslint-disable-next-line no-unused-vars
                    let previousNode = null
                    let nextNode = null
                    while (pos > 0) {
                        let prevNode = newState.tr.doc.nodeAt(pos)
                        if (prevNode) {
                            if (!prevNode.isText && prevNode.firstChild && prevNode.firstChild.isText) {
                                previousNode = prevNode.firstChild
                                break
                            } else if (prevNode.isText) {
                                previousNode = prevNode
                                break
                            }
                        }
                        pos = pos - 1
                    }

                    pos = newState.tr.selection.to + 1
                    // console.log(newState.tr.doc.nodeSize)
                    while (pos < newState.tr.doc.nodeSize - 2) {
                        let nextnode = newState.tr.doc.nodeAt(pos)
                        if (nextnode) {
                            if (!nextnode.isText && nextnode.firstChild && nextnode.firstChild.isText) {
                                nextNode = nextnode.firstChild
                                break
                            } else if (nextnode.isText) {
                                nextNode = nextnode
                                break
                            }
                        }
                        pos = pos + 1
                    }

                    if (!previousNode || !nextNode) {
                        return
                    }
                    let marks1 = previousNode.marks
                    let marks2 = nextNode.marks
                    let carryOnMarks = []
                    if (newState.schema.marks.highlight.isInSet(marks1) && newState.schema.marks.highlight.isInSet(marks2)) {
                        console.log('Same Mark Found in sibling nodes')
                        marks1.map(mark1 => {
                            if (mark1.attrs['highlight-id']) {
                                let idHighlight = mark1.attrs['highlight-id']

                                let flag = false
                                marks2.map(({ attrs }) => {
                                    if (attrs['highlight-id']) {
                                        if (idHighlight === attrs['highlight-id']) {
                                            flag = true
                                        }
                                        return false
                                    }
                                })
                                if (flag) {
                                    carryOnMarks.push(mark1)
                                }
                            }
                            return false
                        })
                        return newState.tr.ensureMarks(carryOnMarks)
                    }
                }
            })

Not sure if this is really the way to go about it… It seems to be working but am not sure yet…

Well - That didn’t go down so well.

I ended up creating a plugin which -

  1. Finds all the inline nodes with the relevant mark with attr (id)
  2. Build a range for each specific mark
  3. Figure out if the range of the mark is broken (has inline nodes that don’t have the mark)
  4. Fix broken ranges using appendTransaction to dispatch addMark for each of the broken range

The use case was to get the comments done with marks to enable undo/redo and keep them part of the document with collaboration. I read whatever I could find on this quite actively discussed topic of “Comments with Marks” - Too early to say if it’s working properly or not - but any suggestions whether this would be a viable model or not would be awesome! (In case I missed something - still quite new to Prosemirror)

Thanks!