Plugins and characters with a diacritic

I’ve got a plugin that appends a transaction to update an attribute on heading nodes when their text is updated, however if the last update is the start of adding a diacritic to a character (typing option-u u on a mac for instance) the transaction breaks the edit and kicks the cursor to the next node. Does anyone know of a reasonable way to sniff out these sorts of changes and hold skip issuing my transaction for now? Is it a matter of inspecting the text of my node and breaking if the last character is in certain unicode ranges, or is there something simpler I’m missing?

Update: or maybe I just need to reset the insertion point… hrm

Yes, changes ‘above’ the selection can disrupt composition (browsers will abort composition when the DOM around the selection is updated). I think the right solution here would be to check for view.composing and hold back the transactions when that is true.

Ahh, that sounds perfect. Apologies, but is there a simple way to get at the view from inside appendTransaction or do I need to sock it away in the plugin state somewhere?

No, there isn’t, since you usually don’t need the view there. You’ll have to arrange for that link yourself somehow.

OK. I’ll see if there’s a reasonable way to close over it somewhere. Thanks for all the pointers.

I think I have that working, but I’m realizing I’m going to need to do it in several plugins that depend on modifying node attributes after edits to those nodes.

I wonder if I’m going about this the wrong way. I have a number of nodes that have attributes that depend on the content of the node which are used by our two front end rendering libraries so they don’t have to replicate a bunch of PM behavior in order to render the body of a story. Is there a sane way to do this from a NodeView for instance? No worries if I’m way outside the normal use cases.

Here’s what’s working for me for now:

let viewReference = null

export function createHeadingsPlugin() {
  return new Plugin({
    view(editorView) { viewReference = editorView; return {} },
    appendTransaction: (transactions, oldState, newState) => {
      if (viewReference.composing) return
      // only operate if there is a change
      if (transactions.find(tr => tr.docChanged )){
        return checkHeadings(newState)
      }
    }
  })
}

Heh, it turns out this ignores the change in the composition event entirely. I’ll have to figure something else out.

For future folks who run into this the trick for us was to use a custom node view with a properly implemented update() method that avoids modifying the inner contentDom. Then the composition event is able to complete at its’ leisure.

Edit: this means we were able to go back to not caring about the view inside our appendTransaction logic.

Rendering nodes differently depending on their content is definitely much better done with a node view than with appendTransaction document changes. But you would have to duplicate the logic in your non-editor rending (but you can use a shared function or something).

(Ah, seeing your reply, I guess you came to the same conclusion.)

That’s actually one of the benefits of the current round of changes. Before we had to do crazy things like this, but now we’ll be able to reduce that whole thing to the props.nodeData.attrs.anchor bit in both our ruby and our react renderers. I’m getting very excited to delete a bunch of duplicative, slightly buggy, code.