Can improve inline decoration rerendering?

When I use remirror(a rich editor base on prosemirror), I found code-block has a poor performance.

I found out that it’s because code block use inline decoration, and inline decoration rerendering has a poor performance.

I found that when I edit a node with many inline decorations(remirror code block), It will re render all span tag after the position where I edit.

I dig into the prosemirror code source, I see that when insert a inline decoration at a node that has many inline decorations, it will update the node’s all inline decorations after the inserting decoration.

reproduce

https://remirror.vercel.app/?path=/story/editors-markdown--basic Screenity video - Apr 19, 2024

My question is:

  • It is necessary to update so many decorations?
  • If the answer is YES,why?
  • If the answer is NO,will prosemirror improve it?
  • Or what I think is wrong?

I cannot reproduce this. If you can show this happening in a minimal setup, rather than a full app, I can take a look. Here’s the code I used.

import {Decoration, DecorationSet, EditorView} from "prosemirror-view"
import {EditorState, Plugin} from "prosemirror-state"
import {schema} from "prosemirror-schema-basic"

let state = EditorState.create({
  doc: schema.node("doc", null, schema.node("paragraph", null, schema.text("x ".repeat(40)))),
  plugins: [new Plugin({
    state: {
      init(conf, state) {
        let deco = []
        for (let i = 1; i < 81; i += 2) {
          deco.push(Decoration.inline(i, i + 1, {style: "background: orange"}))
        }
        return DecorationSet.create(state.doc, deco)
      },
      apply(tr, deco, old, state) { return deco.map(tr.mapping, state.doc) }
    },
    props: {
      decorations(state) { return this.getState(state) }
    }
  })]
})

new EditorView(document.body, {state})

I just use the prosemirror website example code


function lintDeco(doc) {
  let decos = []
  lint(doc).forEach(prob => {
    decos.push(Decoration.inline(prob.from, prob.to, {class: "problem"}))
  })
  return DecorationSet.create(doc, decos)
}

// plugin{
import {Plugin, TextSelection} from "prosemirror-state"

let lintPlugin = new Plugin({
  state: {
    init(_, {doc}) { return lintDeco(doc) },
    apply(tr, old) { return tr.docChanged ? lintDeco(tr.doc) : old }
  },
  props: {
    decorations(state) { return this.getState(state) },

Screenity video - Apr 22, 2024 (1)

I mean inserting a decoration. There are no problems when edit but not insert a new decoration.

The gif show:

When I insert a ! after decoration ., all dom after decoration . has been update.

I see what you mean. The linter just eagerly rendered new icons every time it ran, which obviously requires the library to use the new nodes. This patch shows how to adjust it to be able to preserve the existing icons.

I see, widget decoration has a key property for reusing dom. But my situation is there are lots of inline decorations.

Can inline decorations be optimized like that?

Inline decorations shouldn’t have this problem, since the library can accurately compare them directly.