Getting mark when cursor or selection is between the bounds of a given mark type

Hi there!

I am trying to find the Mark of a given type (if one exists) that is at the current cursor position or between the current selection if the selection is within the mark “bounds” (included).

Here is the function I wrote:

getMarkForCursorOrSelectionBetweenMarkOfType(state: EditorState, markType: MarkType): Mark {
        const selection = state.selection;

        if (selection.empty) {
            return markType.isInSet(state.storedMarks || selection.$from.marks());
        }

        const fromMark = markType.isInSet(selection.$from.marks());
        const toMark = markType.isInSet(selection.$to.marks());

        if (fromMark === toMark) {
            return fromMark;
        }

        return null;
}

The logic to find the mark of a given type at the cursor position works well. The issue I have is when a range selection is performed. The condition above returns the mark of a given type such that the mark is between the range selection but bounds excluded. Here is a visual example of the code above running:

Peek 2020-05-27 14-48

  • Cursor between m and p -> link mark returned -> OK

  • Selection is “is a sa” -> no mark returned -> OK

  • Selection is “e, nothi” -> no mark returned -> OK

  • Selection is “ample” -> link mark returned -> OK

  • Selection is “mp” -> link mark returned -> OK

  • Selection is “sam” -> link mark not returned -> NOT OK

  • Selection is “ple” -> link mark not returned -> NOT OK

  • Selection is “sample” -> link mark not returned -> NOT OK

What I would like to do is to consider the mark bounds (bounds included) so that when I select sam, ple or sample, the mark is returned. The problem seems to be with selection.$from.marks() and selection.$to.marks() that respectively ignore the leading and trailing character. How could I make them consider the leading and trailing character?

The link mark node spec used for testing in the example mentioned had attribute inclusive set to false. Setting this attribute to true solves the following case:

  • Selection is “ple” -> link mark not returned -> NOT OK

but the following are still present:

  • Selection is “sam” -> link mark not returned -> NOT OK
  • Selection is “sample” -> link mark not returned -> NOT OK

The problem seems to be with selection.$from.marks().

Here is the full link mark spec that is used:

attrs: {
        href: {},
        rel: {default: 'noopener nofollow'},
        target: {default: '_blank'},
        title: {default: null}
    },
    inclusive: true,
    parseDOM: [{
        tag: 'a[href]', getAttrs(dom: HTMLElement) {
            return {
                href: dom.getAttribute('href'),
                rel: dom.getAttribute('rel'),
                target: dom.getAttribute('target'),
                title: dom.getAttribute('title')
            }
        }
    }],
    toDOM(mark: ProsemirrorMark): DOMOutputSpec {
        const {href, rel, target, title} = mark.attrs;
        return ['a', {href, rel, target, title}, 0];
    }
}

Any idea how to make the $from ResolvedPos consider the mark?

Nothing is wrong—ResolvedPos.marks works as intended (indicating which marks you’d get when you typed at that position, which, at the start of a marked range, doesn’t include that mark). You could write something based on ResolvedPos.nodeAfter if you need something else.