Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handling changes to prosemirror stored marks #2

Open
jjallaire opened this issue Jun 19, 2023 · 2 comments
Open

handling changes to prosemirror stored marks #2

jjallaire opened this issue Jun 19, 2023 · 2 comments

Comments

@jjallaire
Copy link

In creating a similar prototype, one additional case I ended up needing to handle was mapping the current Prosemirror stored marks state when intercepting Prosemirror transactions and applying them as Automerge doc changes.

For example, if the user's cursor is located at the end of a bold mark with expand: after and they insert a character, this will normally extend the bold mark in both Prosemirror and Automerge (so far so good). However, if the user toggles the "Bold" command off prior to inserting the character, this updates the Prosemirror "stored marks" state (removing bold from it). This implies that subsequent insertions should not expand the mark (because bold was toggled off).

Similarly, if the user is in the middle of plain text and they execute the "Bold" command (with no selection) it updates the Prosemirror stored marks so that subsequent characters will be bold.

Perhaps this can be modeled in Automerge by marking/unmarking an "empty" selection at the cursor position? Or perhaps the splice logic just needs to look at the current stored marks state (that's what I did to start with).

Note that a Prosemirror transaction that updates the current stored marks will have tr.storedMarksSet === true (and will have the actual stored marks toggled in tr.storedMarks).

@jjallaire jjallaire changed the title handling changes to prosemirror stored mark handling changes to prosemirror stored marks Jun 19, 2023
@alexjg
Copy link
Collaborator

alexjg commented Jun 22, 2023

Yeah I've been trying to think of a nice way to deal with this. What do you mean by "look at the current stored marks state"? You mean do something like immediately after inserting, check the stored marks and if there is no stored mark but automerge expands an existing mark then unmark the newly inserted character?

@jjallaire
Copy link
Author

Yes, there are two cases to handle:

(1) Automerge has extended a mark with 'after' however the corresponding stored mark has been cleared in the editor
(2) There is no mark in Automerge however there is a current stored mark in the editor that should be reflected.

Here's a video showing the desired behavior:

stored-marks

In my current prototype here's how I'm handling both (this code would be right after the Automerge.slice() from a ReplaceStep):

// collect the stored marks at the selection and the inserted marks
const storedMarks = tr.selection.$head.marks();
const insertedMarks = Automerge.marks(doc, kDocContentKey).filter(
  (m) => m.value !== null &&  m.start <= from && m.end >= from
);

// remove any inserted marks that are not in set of stored marks at the selection
for (const mark of insertedMarks) {
  if (!storedMarks.find(storedMark => storedMark.type.name === mark.name)) {
    const expand = schema.marks[mark.name].spec.inclusive === false ? 'none' : 'after';
    Automerge.unmark(
      doc,
      [kDocContentKey],
      { expand, start: from, end: from + insertedContent.length },
      mark.name
    )
  }
}

// apply any stored marks that were not included in the inserted marks
for (const mark of storedMarks) {
  if (!insertedMarks.find(insertedMark => insertedMark.name === mark.type.name)) {
    const expand = mark.type.spec.inclusive === false ? 'none' : 'after';
    Automerge.mark(
      doc,
      [kDocContentKey],
      { expand, start: from, end: from + insertedContent.length },
      mark.type.name,
      JSON.stringify(mark.attrs)
    )
  }
}

Note that this code likely only handles single character typing insertions correctly (a paste at the cursor may need more sophisticated logic). There also might be a much more elegant way to do this that eludes me (or perhaps some additional construct in Automerge that makes this unnecessary).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants