Lag with nth depth bullet-fields

Hiya,

I’m resurrecting a prosemirror project I had from a while back (now on Vue3). was getting very close to all the desired behaviour for my implementation (I’ve updated all my prose mirror libraries to the latest on npm) when it comes to the implementation of lists I’m pretty much using what’s there. What I’m finding though is huge penalties when I start holding an arrow key down (up or down arrow key specifically) over nth level lists. I get a compareDeep error. It doesn’t reach overflow, but the overhead is large enough to temporarily freeze my machine!

export default {
  attrs: {
    class: {
      default: 'pm-align--left'
    }
  },
  content: 'listItem+',
  group: 'block',
  parseDOM: [{
    tag: 'ul',
    getAttrs: node => ({
      class: node && node.attributes && node.attributes.class
        ? node.attributes.class.nodeValue
        : 'pm-align--left'
    })
  }],
  toDOM: (node) => ['ul', { class: node.attrs.class }, 0]
}

numbered list

export default {
  attrs: {
    class: {
      default: 'pm-align--left'
    },
    start: {
      default: 1
    },
    style: { default: '' }
  },
  content: 'listItem+',
  group: 'block',
  parseDOM: [{
    tag: 'ol',
    getAttrs: node => {
      return {
        class: node && node.attributes && node.attributes.class
          ? node.attributes.class.nodeValue
          : 'pm-align--left',
        start: node.hasAttribute('start') ? node.attributes.start : 1
      }
    }
  }],
  toDOM: (node) => {
    const start = typeof node.attrs.start !== 'object'
      ? (node.attrs.start || 1)
      : (node.attrs.start.nodeValue
        ? node.attrs.start.nodeValue
        : 1)
    return ['ol',
      {
        class: node.attrs.class,
        start: start,
        style: `counter-increment: pm-ol-counter ${start - 1} !important;`
      }, 0]
  }
}

list item

export default {
  content: 'paragraph block*',
  group: 'block',
  attrs: {
    style: {
      default: ''
    },
    class: {
      default: ''
    }
  },
  defining: true,
  draggable: false,
  parseDOM: [{ tag: 'li' }],
  toDOM: (node) => ['li',
    {
      style: node.attrs.style,
      class: node.attrs.class
    }, 0]
}

here’s an example I just-about-managed to capture with peek:

I copy pasted 3 lots of the bullet list you see, go to the bottom of the page and hold down the up arrow…

I’ve tried stripping out basically all my plugins just to be sure it isn’t one of them. the plugins make the issue more pronounced but the problem persists with a more-or-less vanilla implementation from what I can surmise. Am I missing something obvious or is this a bug?

compareDeep is used to compare attributes. Since it looks like your attributes should be simple values, the kind of recursion you’re seeing the stack trace should definitely not be happening. It might help to modify compareDeep to output its arguments to figure out what kind of value it ends up recursing into, and then find out how that value might have ended up in your node attributes.

node.hasAttribute('start') ? node.attributes.start : 1 looks suspicious—that seems like it’ll put the entire DOM attribute object into the node’s attributes.

1 Like

thanks @marijn (and thank you as always for being so active and invested in this forum!)

I tried some nth level ul lists and I’m not seeing this behaviour so I think you might be right (I wrote a lot of the modelling for this quite a long time ago so it’s very possible there are a few gremlins in there haha!)

I’ll have a dabble after work and see if I can resolve that …

Yeah it’s that… well spotted :smiley:

so I’m trying to allow the user to set the number of their bullet list at run time with offset buttons. I’ll have to keep track of the offset in a different way in that case…

Hmmm, puzzles in puzzles. It looks to me like it’s really sweating over the prose mirror copy paste functionality…

I paste numbered lists 4 times and then hold the up arrow. I removed all that start logic and I still hit a point where the system crunches along after that…

it must not like something about the schema… I tried copy/paste on the tip tap examples and they were behaving just fine…

[edit]

nope, you mean the thing in getAttrs… yep, I comment that out and it’s right as rain! hmmm, ok, yeah that’s gonna need to manage state elsewhere I think :thinking: I can probably work something out with vuex… thanks :smiley:

solution :slight_smile:

getAttrs: node => {
      return {
        class: node && node.attributes && node.attributes.class
          ? node.attributes.class.nodeValue
          : 'pm-align--left',
        start: typeof node.attributes.start === 'object'
          ? node.attributes.start.nodeValue : 1
      }
    }