New plugin `prosemirror-codemark` to handle inline code mark creation and manipulation

I wanted to share another library that we just created at Curvenote called prosemirror-codemark, which handles manipulating and navigating inline code marks. This should be simple, but for those of you who have ever edited inline code in Slack, Notion or done this yourself, there are actually quite a few things to do to make this a good experience!

code-mark

Highlights:

  • Create code matching backwards (simple InputRule) and forwards (which is a specialized rule)
  • Select a word and hit `
  • Make sure the next charated makes sense (e.g. removeStoredMarks)
  • Show a visual cursor in the correct location if the next character will or will not be in the inline code
  • Allow arrow keys to enter/exit into the inline code

See a live demo here with a simple alternative implementation in a details box below so you can compare.

codemark is a specialized InputRule and a Plugin to display a fake-cursor as a decoration, which allows you to navigate inside and outside of inline code. The plugin also ensures that the text-input caret indicates what will happen next (both in color and position). The plugin works with undoInputRule to undo code mark creation.

To use the plugins, supply your MarkType and use the plugins in your EditorView.

import codemark from 'prosemirror-codemark';
import 'codemark/dist/codemark.css';

const plugins = codemark({ markType: schema.marks.code });

const view = new EditorView(editor, {
  state: EditorState.create({
    doc,
    plugins: [...plugins, ...otherPlugins],
  }),
});

Background

Browsers don’t distinguish between a cursors position inside and outside inline tags when it comes to where to insert text. This state is held by the application for what to insert next (i.e. storedMarks). This means that visually distinguishing the first two states in the next figure is not possible.

I tried out ​ based on this post but it didn’t work for me as a decoration, and I didn’t want them in the document.

I would be curious on feedback on ways to improve the approach!

MIT License, typescript, contributions welcome! Let me know what you think!

4 Likes

Really great that you’re packaging these up as libraries! The fake cursor trick is neat.

How do you feel about cursor behavior on the edge of other marks (emphasis, link)? Why did you decide code marks should behave differently?

Thanks! My take is that code marks are different due to a combination of:

  1. they are common (e.g. compared to strikethrough, all caps),
  2. there is no standardized shortcut to stop/start editing the mark (cmd-b/i/u/k)
  3. they often have a visual box or background, which by default the cursor is inside (even if that isn’t what happens next)
  4. all of the wysiwyg editors I tried did some combination of this already (albeit inconsistently)

I think that #3 leads us down a path of thinking about them as inline nodes rather than marks - even though they aren’t, and #4 sets expectations that it doesn’t work the same way as bold/italics/underline/links.

This is great, I really like the coloured cursor!

Do you think it might be able to handle pasting non-code into the middle of the content in the code mark, so it adds the pasted content as text inside the existing mark rather than splitting the mark?

In the current demo, if I copy some plain text, put the cursor in the middle of some code, then paste, the code is split into two parts with plain text in the middle.

If the user enters two adjacent backticks, then puts the cursor between them and types some text, should this create a code mark around the new text?

Thanks for the feedback @aeaton!

I think what you describe is possible with a cmd-shift-v (paste without formatting), and also works if the clipboard is actually plain text without any html (e.g. try pasting from your url bar works as you describe). If there is formatting in the paste event, I think that should be kept or be another plugin to override it. Maybe?! Feel free to open an issue to describe/clarify that further!

I have opened an issue here for your other input rule suggestion:

Great idea!

1 Like

Yes, thanks - I think that works well enough :slightly_smiling_face: