[solved] Correct rangeHasMark behaviour?

I do have this simple editable text:

<p class="jsEditor">
   <a href="/">Link with <strong>strong</strong> text</a>
</p>

This simple plugin checks for mark “link” in the range which should be true for every cursor position.

class CLinkView
{
	constructor(oView)
	{
		this.oView = oView;
	}

	update()
	{
		let oState = this.oView.state;
		let oDoc = oState.doc;
		let oSelection = oState.selection;
		let o = { from: oSelection.from, to: oSelection.to };
		let oMarkType = this.oView.state.schema.marks.link
		console.log('range has mark: ', o, oDoc.rangeHasMark(o.from, o.to, oMarkType))
	}
}

But at position 10 and 16 (start / end of strong mark) it returns false. Is this correct? Should I use another command to check which marks are applied at current cursor position?

rangeHasMark assumes that from < to, and is only used by the core commands in situations where that is the case. It seems that it isn’t very reliable when they are the same. You may be looking for something like schema.marks.link.isInSet(state.storedMarks || $cursor.marks()), since for cursor selections you want probably to take the stored marks into account as well.

Hmmmm, I tried now this:

class CLinkView
{
	constructor(oView)
	{
		this.oView = oView;
	}

	update()
	{
		let oState = this.oView.state;
		let oSelection = oState.selection;
		let oMarkType = oState.schema.marks.link;
		console.log('is in set: ', oMarkType.isInSet(oState.storedMarks || oSelection.$cursor.marks()))
	}
}
  1. isInSet returns an object instead a boolean

  2. text selection abborts with TypeError: oSelection.$cursor is null

On which object should I access $cursor?

Yes, it does. I’m not sure how that is a problem.

$cursor only exists on cursor selections, and this code won’t do anything useful on other types of selections, so you’ll probably want to put it in a conditional for selection.empty.

I see, this method exists twice I looked up the wrong one which returns boolean.

Thank you I’ll try.

This code works:

class CLinkView
{
	constructor(oView)
	{
		this.oView = oView;
	}

	update()
	{
		let oState = this.oView.state;
		let oSelection = oState.selection;
		let oMarkType = this.oView.state.schema.marks.link;
		let isLink = false;
		let oDoc = oState.doc;
		let o = { from: oSelection.from, to: oSelection.to };

		// cursor position
		if ( oSelection.empty )
		{
			isLink = oMarkType.isInSet(oState.storedMarks || oSelection.$cursor.marks()) ? true : false;
		}
		// text selection
		else
		{
			isLink = oDoc.rangeHasMark(o.from, o.to, oMarkType)							
		}
		console.log('Link? ', isLink ? 'yes' : 'no');
	}
}

Thank you marjin.

Great. I’ve adjusted rangeHasMark to always return false when called with an empty range in this patch (there are no nodes in an empty range, so, technically, no marks either).