Off by one when trying to replace node with text

I have an inline nodeView that is only valid for certain content and should be replaced by text otherwise, so I’m trying to handle the backspace keypress to unwrap the inner text content if it’s no longer valid for the nodeView. An example would be if I were trying to implement Markdown-esque formatting such that typing *text* would result in italicized text (by using a nodeView that italicized its content). Once the ending asterisk is deleted, the nodeView should be removed and replaced with just the text *text.

That part I’m having trouble with is getting the start and end parameters correct for tr.replace. The only combination I’ve found to work is $pos.start() - 1 and $post.end() + 1. Why is it not sufficient when trying to replace a node with a text node to just do state.tr.replace($pos.start(), $pos.end(), schema.text(...)) ?

My best guess is that this is due to the “virtual” (better term?) tokens at the start and end of non-leaf nodes as mentioned in the docs. So iiuc, the start and end params to replace need to include these extra tokens when replacing non-leaf nodes entirely?

Is that close? Is there a more elegant API I’m missing to replace a non-leaf node with a text node?

edit: $pos.before() and $pos.after() are at least convenient ways to avoid the -1 and +1, though it’d be good to know if the need to use them is a consequence of going about this the wrong way or if that is the correct approach.

Nodes with content have an opening and a closing token, which count as 1 in positions. $pos.before()/$pos.after() might be what you’re looking for. Also, if the thing you’re building is anything like italics, marks probably work a lot better than node views.

1 Like