A proposed way to visualize a changeset


#1

Is there any proposed way to visualize a changeset? So I would like to visualize a changeset between two versions by having content of both displayed and then additions colored green and removals with red. I am not sure how to arrange this for ProseMirror to render in this way? So both versions at once?

This example does not use changesets.


#2

I’m not aware of there being open-source code to do that. The basic approach would be to insert widget decorations for deletions, containing a rendered form of the deleted slices (with some heuristics to drop leading/trailing open/close tokens to avoid making a mess), and to add range decorations over inserted ranges, coloring them somehow.


#3

Thanks. It makes sense.

You mean inline decorations?


#4

Yeah, indeed (we’re calling them range decorations in the new CodeMirror codebase, and I got confused)


#5

Thanks! I think I am making progress.

But I have trouble converting a transaction to changeset. It is always computing the whole document as inserted. I have a transaction with all steps in it and I would like to compute changeset for the last diffVersionsCount steps:

        const transactionEndIndex = transaction.docs.length - diffVersionsCount;
        if (transactionEndIndex < transaction.docs.length) {
          changeset = ChangeSet.create(transaction.docs[transactionEndIndex]);
          changeset = changeset.addSteps(transaction.doc, transaction.mapping.slice(transactionEndIndex).maps);
        }
        else {
          changeset = ChangeSet.create(transaction.doc);
        }

Would it be better if I call addSteps with every document in the transaction’s diffVersionsCount range instead of just the last one?


#6

This does not work. transaction.mapping.slice(transactionEndIndex).maps is the same as transaction.mapping.maps. This is a surprise. transaction.mapping.maps.slice(transactionEndIndex) seems to work better.


#7

I think I made it (open source). See here. A pretty simple plugin.

I do not get this, or more specifically, I do not seem to be able to imagine what heuristics are necessary. I am also not sure how to even detect invalid open/close tokens. I cannot really imagine how can a diff have partial open/close tokens? Or you removed the whole block, or you removed its content. Can you really remove just one side of the block?

I also use:

const serializer = DOMSerializer.fromSchema(view.state.schema);
return serializer.serializeFragment(span.slice.content);

To render deletion widget content. But it seems there is a slight difference how this is rendered. In editor, <p></p> are always rendered as <p><br/></p> while in the widget it is just <p></p>. This then makes diff content looks differently from the original content. Why is that? How could I make it render it exactly the same? (Both editor and widget have content editable set to false for this “diff view mode”.)