Repository: ueberdosis/tiptap
File: packages/extension-mention/src/mention.ts (TypeScript)

Code before:
import { mergeAttributes, Node } from '@tiptap/core'
import Suggestion, { SuggestionOptions } from '@tiptap/suggestion'
import { Node as ProseMirrorNode } from 'prosemirror-model'
import { PluginKey } from 'prosemirror-state'

export type MentionOptions = {
  HTMLAttributes: Record<string, any>,
  renderLabel: (props: {
    options: MentionOptions,
    node: ProseMirrorNode,
  }) => string,
  deleteOnBackspace: boolean,
  suggestion: Omit<SuggestionOptions, 'editor'>,
}

export const MentionPluginKey = new PluginKey('mention')

export const Mention = Node.create<MentionOptions>({
  name: 'mention',

  addOptions() {
    return {
      HTMLAttributes: {},
      renderLabel({ options, node }) {
        return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
      },
      deleteOnBackspace: false,
      suggestion: {
        char: '@',
        pluginKey: MentionPluginKey,
        command: ({ editor, range, props }) => {
          // increase range.to by one when the next node is of type "text"
          // and starts with a space character
          const nodeAfter = editor.view.state.selection.$to.nodeAfter
          const overrideSpace = nodeAfter?.text?.startsWith(' ')

          if (overrideSpace) {

Review comment (refactor):
I want to add my thoughts to the naming, feels a bit too abstract. The default behaviour is already "delete on backspace", this option doesn't really clarify what it actually does. People reading the documentation might be confused and think this needs to be always `true` otherwise backspace might not work, which is not true.

My suggestion, rename all references to: `deleteTriggerCharWithBackspace` or something along those lines, something more obvious, more explicit...

Code after:
import { mergeAttributes, Node } from '@tiptap/core'
import { DOMOutputSpec, Node as ProseMirrorNode } from '@tiptap/pm/model'
import { PluginKey } from '@tiptap/pm/state'
import Suggestion, { SuggestionOptions } from '@tiptap/suggestion'

export type MentionOptions = {
  HTMLAttributes: Record<string, any>
  /** @deprecated use renderText and renderHTML instead  */
  renderLabel?: (props: { options: MentionOptions; node: ProseMirrorNode }) => string
  renderText: (props: { options: MentionOptions; node: ProseMirrorNode }) => string
  renderHTML: (props: { options: MentionOptions; node: ProseMirrorNode }) => DOMOutputSpec
  deleteTriggerWithBackspace: boolean
  suggestion: Omit<SuggestionOptions, 'editor'>
}

export const MentionPluginKey = new PluginKey('mention')

export const Mention = Node.create<MentionOptions>({
  name: 'mention',

  addOptions() {
    return {
      HTMLAttributes: {},
      renderText({ options, node }) {
        return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`
      },
      deleteTriggerWithBackspace: false,
      renderHTML({ options, node }) {
        return [
          'span',
          this.HTMLAttributes,
          `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`,
        ]
      },
      suggestion: {
        char: '@',
        pluginKey: MentionPluginKey,