Getting the to/from positions for a given mark

Hello, How would one go about getting the absolute position of a mark when the mouse is not focused or selected on that tag? Is this possible with Prosemirror?

For more context as to the specific use case of this… I have a custom schema that tacks on a guid as well as some other attributes to the mark. These are then rendered in a component that is separate from the editor. From this rendered component, I would like to be able to delete/update the marks and have that be reflected in the schema presented by the editor, but I would need to know their absolute positions in the editor so I know which parts of the schema need to be updated. I would hope that one of the ways listed below would be a valid way to go about getting the absolute position of a given mark, but I’m not sure if this is something Prosemirror is capable of.

Way 1:

 const mark = document.getElementById(this.guid);
// get the to and from positions off of the constant mark.

Way 2: Within the schema, storing the absolute position on a data attribute. (I could save this whenever a mark is added since I am toggling marks on selected ranges, but that wouldn’t account for copying/pasting that mark around the document or editing text around the mark that would ultimately change it’s absolute position)

get schema(): MarkSpec {
    return {
      attrs: {
        attributes: {}
      },
      toDOM: (node: any) => [
        'mark',
        {
          'data-value': JSON.parse(node.attrs.attributes).value,
          id: JSON.parse(node.attrs.attributes).guid,
          from: // Somehow get the from selection,
          to: // Somehow get the to selection
        }
      ],
      parseDOM: [
        {
         ...
         ...
         ...
        }
      ]
    };
  }

The absolute position from and to are document dependent, and I would caution against saving those in the schema as done in way-2. This adds unnecessary complexity and will be much hard to maintain when the positions become stale.

I am not sure what the most pragmatic approach will be, but here is my attempt:

// uid is a string which uniquely identifies the mark you are looking for
export function findMarkByUid(editorState, from, to, uid) {
  let found;
  editorState.doc.nodesBetween(from, to, (node) => {
   if (found) return false; 
   found = node.marks.find(mark => mark.attrs['data-uid'] === uid)
  });

  return found
}

// somewhere in your mark schema
toDOM: () => ['em', {'data-uid': generateUID()}, 0],

If you want the document wide positions, you can do something like:

function findMarkPosition(mark, doc, from, to) {
  let markPos = { start: -1, end: -1 };
  doc.nodesBetween(from, to, (node, pos) => {
    // stop recursing if result is found
    if (markPos.start > -1) {
      return false;
    }
    if (markPos.start === -1 && mark.isInSet(node.marks)) {
      // expect to see something like `italic('my text')`
      console.log(node.toString())
      markPos = {
        start: pos,
        end: pos + Math.max(node.textContent.length, 1),
      };
    }
  });

  return markPos;
}
2 Likes