Can't get inputrule to apply mark to match only

Hi there,

I use the input rule plugin likes this to allow to add inline code blocks by typing backticks.

  new InputRule(/`(.*)`$/, (state, match, start, end) => state.tr
    .insertText(match[1], start, end)
    .addMark(start, end, schema.marks.code.create())
    .removeStoredMark(schema.marks.code)
  ),

I know that the cursor position in inline elements in browsers can be tricky, however it doesn’t remove the code mark at all if I keep typing. If I type my backticks and keep on typing after the text is wrapped in the code mark the text I type gets appended inside the mark’s wrapping text node and not afterwards.

It seems to be similar (but less generic) than the solution posted here: Input rules for wrapping marks - #11 by ncphillips

What could I be doing wrong?

Thanks for the help!

I’m not sure if that’s the problem you’re running into, but you can’t use end like that in addMark, since it points at the end of the matched range in the document before you ran insertText, and that won’t be a meaningful position anymore after that change.

You should be able to just run replace with an already-marked text node instead of insertText/addMark. That should simplify the whole thing.

Since in my case I have a fixed delimiter (backticks) in a fixed regex, I think just taking end - 1 should have done the trick. I tried variations of that because I wasn’t sure if the implementation of addMark uses the limits exclusively or inclusively. However no dice.

I tried other variations of your suggestion before (e.g. deleting from start to end, then inserting, then adding mark, etc.). I even tried to set the selection explicitly again and inserting (and then deleting) a space after my insertion. Re your suggestion:

  new InputRule(/`(.*)`$/, (state, match, start, end) => state.tr
    .replaceRangeWith(start, end, schema.text(match[1], [schema.marks.code.create()]))
    .removeStoredMark(schema.marks.code) // with or without doesn't change anything
  ),

This doesn’t work either (not at the end of the doc, nor with content coming after it). I checked what exactly happens with the DOM document’s selection to see if I can replicate pressing right arrow after my substitution (which removes the stored mark). I couldn’t even see any difference between a cursor position which ends the mark on typing or which keeps it when typing. Both are inside my text node at the last position.

I am not 100% sure, but I know you mentioned before that browsers handle this case weirdly and I am wondering if this just stopped working in the meantime in Chrome.

Could it be that schema isn’t the same as state.schema?

Thanks for the replies btw.

I checked and they are the exact same object (compared with ===).

Strange. The input rule you showed above works for me.

Could you share how you set that up? I remixed your example editor from the website and there it doesn’t work for me in (close to latest) Chrome.

I tried using replaceRangeWith in the remix as well, however that just removed the whole input and didn’t insert anything.

For the record in the glitch project I shared it applies the code mark correctly, but when I start typing again it removes it from the text again.

I couldn’t figure out the root cause, so in the meantime I solved it by inserting a zero-width space:

  new InputRule(/`(.*)`$/, (state, match, start, end) =>
    state.tr
      .replaceRangeWith(start, end, schema.text(match[1], [schema.marks.code.create()]))
      .removeStoredMark(schema.marks.code)
      .insertText('\u200B', end - 1),
  ),

That works well and in our use case we are fine with these.

I was taking some of this discussion as inspiration for prosemirror-codemark, which solves some of these issues for inline code marks. Might be helpful?! Let me know!

1 Like