Conversion of variables and functions 0.10 -> 0.12

I’ll try to update the following list as more things become clear. The functions probably have changed in exactly how they have to be called, but it would be good to know where one needs to start looking.

Feel free to help!

0.10 -> 0.12

pm.setDoc(doc) -> view.updateState(EditorState.create({doc}) pm.scheduleDOMUpdate() -> ? pm.on.setDoc.add() -> ? pm.on.change.add() -> ? pm.on.filterTransform.add() -> ? pm.on.transform.add() -> ? pm.on.transformPastedHTML.add() -> ?

let collab = collabEditing.get(pm) -> ? collab.onSetDoc = function(){} -> ? collab.afterSetDoc = function(){} -> ? collab.versionDoc -> ? collab.mustSend.add() -> ? collab.receivedTransform.add() -> ? collab.hasSendableSteps() -> ? collab.sendableSteps() -> collab.sendableSteps() collab.clientID -> ? collab.version -> -> collab.getVersion() collab.receive() ->

Note that the editor view just works differently now, so you won’t find a direct correspondence for many of the concepts you list (and you’re just going to confuse yourself looking for one).

DOM updates aren’t scheduled anymore. They happen when, and only when, you call .update or .updateState.

The setDoc event was needed for state housekeeping when the document was replaced. You’re now expected to put editor-state-related stuff in the editor state, so that when a new state is created it is automatically reinitialized.

To notice changes or transforms, you put code in your onAction callback that inspects the action.

The collab ‘mustSend’ event is gone. You just, in your action pump, check whether there is something to send on each action. The documentation for the collab module should help you with most of the other collab-related things you list. Why do you need the versionDoc property? That was never part of the public interface, though you could still pluck it out of the plugin’s state, if you don’t mind things breaking on upgrade.

Hmm, we have a lot of code already and we cannot afford to put several developers on redesigning everything from scratch again this year. Would it maybe be a better idea for us to just fork the 0.10 version and just add minor fixes to that ourselves (only if we need them) and then migrate to PM 1.0 the next time we refactor everything in a year or two?

When saving things to the server, I need to send in the last version that I have confirmation for from the server. We previously tried to just save the current version (including steps that hadn’t been confirmed yet), but that just led to issues if one of the steps had been sent from the client but hadn’t reached the server yet, before the server received the full document.

Thanks for making this thread, I was thinking the same thing! Here’s a few of my discoveries so far.

Applying Transforms: transform.apply() -> onAction(transformAction) (E.g onAction(state.tr.setBlockType(from, to, nodeType, attrs));)

Commands: Use to take pm and apply now take state and onAction.

Callback Handlers: The way my editor is setup I have several places that use to setup pm.on handlers. Seems like now we’ve only got the onAction method on the view. As a short term fix, I’m using a global array of extra functions to call with the action. That way all of my components can do globalArray.push(updateFunc).

An odd gotcha: I use to import from the repo’s src directory. This causes weird issues, so it seems you need to import from the dist directory. (I used src previously as I would hack around the src and in some cases make changes).

It’s definitely going to be a fair amount of work to do this upgrade but hopefully it’s the biggest one for the foreseeable future.

More migration info to come!

onAction takes an action, so you should call it with transform.action(), not the transform object itself.

This is interesting. I’m running my local tests and testing demo from the src directories, and dist is just a compiled-to-ES5 version of those, so I’m not sure what could have gone wrong there.

How about the system with a function that starts in a write phase and returns a read phase that returns a write phase, etc.? We use that a lot to coordinate the placement of elements that aren’t part of the editor directly and I cannot find it in the documentation nor in the source code of the 0.12 version.

@marijn In 0.10 I used several transform.apply() calls in a single command. I’ve got a command that lifts lists and blockquotes and THEN calls setBlockType to convert them to headings. Attempting to do this with a single transform object and chaining results in a TransformError.

My current ugly work around is to have onAction return the new state. This way, I can have the lifts return a new state for the setBlockType to work off of.

Thoughts?

Gone. You can still use something like that in a higher layer if you want, but ProseMirror updates only write to the DOM now, so they don’t bother with this kind of scheduling.

Why did you apply the transforms in multiple steps though? A Transform object has its own doc that you can continue inspecting if you want. You can continue calling methods on it to perform all the changes you need to apply.

Ok, I see.

I now wonder if the conversion 0.10 → 0.12 possibly could be done in the form of creating a new class in a single file that takes over a lot of the functionality the old ProseMirror had which then calls the various new prosemirror modules. Our other code could then hopefully stay more or less as it is. Such a class could possible be shared with others who are in the same situation.

Would that seem like a feasible alternative to trying to rewrite all of our code to fit the new way of doing things?

Yes, that should be possible. I considered delivering something like that along with the release, but then realized people would start expecting me to maintain it, so I thought better of that. I think you can emulate ~70% of the old ProseMirror API in a few hundred lines of wrapping code.

(On the other hand, setting yourself up with an extra layer of indirection just to be able to use a less clean, old API might not be a great decision.)

That sounds very promising!

That is a good point. I wonder though. The new APi with the state/view split, etc. and the many new packages may not necessarily be easier to understand for new developers than the 0.10 system. So if we could keep most of that in just one file, we could maybe isolate some of the complexity so that only developers that absolutely need to work on certain internal details of the editing parts can go in there and learn about + mess with those internals.

Another option could be this:

Given that we don’t really gain anything from using the 0.12 release (right?), and switching just means we need to rewrite stuff and add things ourselves that no longer are in prosemirror, maybe it just makes more sense to stick with 0.10 for now.

The main reason I wanted to do the switch now is so that we don’t need to change the file format later on when we have a lot of data. I realize that migrating the data shouldn’t be too difficult, but it makes things more complex if there are different version involved. But maybe we could resolve this by simply modifying the 0.10.1 version slightly so that it uses the 0.12 format for marks? That way we should be able to upgrade some time in the future without having to worry about data migration.

Edit: Tried changing the JSON format of the 0.10 branch, and that was only a handful of line changes, so that’s definitely the easiest way for us to obtain what we need right now.

Had similar discussion with my cofounder. Ultimately we decided ~week of migration work is worth keeping up to date with ProseMirror. If you fork, you take complete ownership of the project and personally I want to keep getting the benefits of Marijn’s hard work. All future bugs, features, etc.

100% agree. No reason to fork at all. We would just be pushing the date for the migration to the latest PM a little into the future. Given that it’s just a tiny change to make the 0.10 version give out the json of the 0.12 version, we can even do so without creating data conversion problems for ourselves in the future.

In 0.10 I used pm.on.domDrop.add. I couldn’t find any editor prop in 0.12 that corresponds to this. Is the recommended way to use the generic handleDOMEvent prop instead? Any particular reason why the drop event isn’t exposed directly anymore?

Yes.

It doesn’t really add anything that handleDOMEvent doesn’t already have.

1 Like

I am giving it another go to try to get to the newest version of PM.

Converting the schemas seemed ok, but some other things seem still difficult to convert. It’s not necessarily harder than in version 0.10, but it’s all different and I don’t seem to be able to find the right places in the code as easily.

Right now I’m trying to find an equivalent of pm.on.change.add(). There is onBlur and onFocus, but I can’t see an onChange. onAction, that was mentioned above doesn’t seem to be present in the sourcecode of any of the repos any more. handleDOMevents seems to be reserved for handling DOM events BEFORE the editor intervenes.

I feel a bit stupid asking about this, and I’m sure that I could eventually find the answer myself by reading more of the source code, but I know if I have issues finding this, students and new comers to the code will have at least as many issues as me with this. Is there maybe some resource I am missing? Or should I just wait until there is more documentation available?

Edit: Looking at the sourcecode of the state, it looks like addApplyListener may help me here. Is that what I should be using?

Edit 2: Or maybe I need to add dispatchTransaction to the view and after applying the transaction in that function call whatever I used to call after the change?

There isn’t – the idea is that, either by using dispatchTransaction or by registering plugins with local state and an apply method, you observe state updates as they come in, and compute your own new state from them. I.e. the architecture has moved from an event-based one to a ‘dataflow-based’ one.

Most certainly not – that’s a really ugly internal hack that should go away once I properly resolve #543. Try to ignore the fact that it exists.

1 Like

Thanks! Ok, I’ll ignore it then.

As for these:

and pm.on.beforeTransform.add()

I assume that I am to create such calls myself inside of dispatchTransaction, right?

I am also wondering about setDoc. Currently I

  1. initiate the editor,

  2. wait for the document to arrive from the server,

  3. set the doc with setDoc

  4. In case of bad communications with the server and collaboration, there are situations in which setDoc is called again with a new version of the doc.

If I understood it right, now initialization and setting of the document are the same step. Combining 1, 2 and 3 should probably work. But I’m not so sure about 4: We have a lot of things going on, and I’m not sure if I could just replace the view with an entirely new view without messing something else up. My question therefore is: Could I do something like this when receiving an entirely new document:

let newState = EditorState.create({
    schema,
    doc: schema.nodeFromJSON(newDoc),
    plugins: [
        history(),
        keymap(baseKeymap),
        keymap(buildKeymap(schema))
    ]
})

oldView.updateState(newState)

This will still reinitiate the plugins, but hopefully it will create fewer issues for other things connected to the editor. If this is OK to do.