Suggestions and autocomplete plugin library, `prosemirror-autocomplete`

I wanted to share a new library that I just released called prosemirror-autocomplete, which adds triggers for #hashtags, @mentions, /menus, and other more complex autocompletions. The prosemirror-autocomplete library can be used to create suggestions similar to Notion, Google Docs, or Confluence; it is created and used by Curvenote. The library does not provide a user interface beyond the demo code.

Live Demo on Github

autocomplete-full

There are a few related projects, prosemirror-suggestions, prosemirror-mentions, prosemirror-suggest being the ones I looked at and tried using before creating this library.

prosemirror-suggestions is similar in that it does not provide a UI, if you want a simple UI out of the box you can look at prosemirror-mentions. All three of the above libraries trigger based on RegExp and leave the decorations in the state. This is similar to how Twitter works, but is undesirable in writing longer documents where you want to dismiss the suggestions with an escape and not see them again in that area.

For example, think of writing an email@address.com – this should not trigger an @mention, or should be dismissed by the user at most one time! This is not how Twitter or the plugins above work, but it is how an IDE like VS Code works and it is what prosemirror-autocomplete is modelled after. I think this library is unique in that there are two plugins returned by autocomplete(opts):

  1. a decoration plugin that wraps the trigger and filter text (e.g. [@][mention]); and
  2. an InputRule plugin that has a series of triggers that are defined in the options.

This allows you to create and dismiss the autocomplete decoration through various input rules.

Example Code

A quick look at how to use prosemirror-autocomplete:

import autocomplete, { Options, Arrow } from 'prosemirror-autocomplete';

const options: Options = {
  triggers: [
    { name: 'hashtag', trigger: '#' },
    { name: 'variable', trigger: /((?:^[a-zA-Z0-9_]+)\s?=)$/, cancelOnFirstSpace: false },
  ],
  onOpen: ({ view, range, trigger, type }) => handleOpen(),
  onClose: ({ view }) => handleClose(),
  onFilter: ({ view, filter }) => handleFilter(),
  onArrow: ({ view, kind }) => handleArrow(kind),
  onSelect: ({ view }) => handleSelect(),
  // use `reducer` to handle these in a single function!
};

const view = new EditorView(editor, {
  state: EditorState.create({
    doc: DOMParser.fromSchema(schema).parse(content),
    plugins: [...autocomplete(options), ...otherPlugins],
  }),
});

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

2 Likes

Looks great! Thanks for sharing.

1 Like

This is very very cool and thank you for releasing this just as I was frustrated with the state of other plugins.

I’m looking to replicate as closely as possible the behaviour of Slack, and this plugin comes the closest I can find. I like the fact that it’s InputRule based as I don’t want to re-open suggestions if the user decides to cancel or overwrite the autocomplete trigger.

I’ve opened a hopefully minor issue about packaging, but I’ve already managed to integrate this.

1 Like

Nice – glad you got up and running, please let me know other ways that the library can improve now that you can actually install it! :slight_smile: Released in 0.2.1!

It is quite a different approach than the social/twitter editors. Would love to see where this is used in your application at some point!

This looks excellent! Very much need this. Thank you.

1 Like