Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .yarn/versions/24a8541c.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
releases:
"@handlewithcare/prosemirror-suggest-changes": patch
8 changes: 2 additions & 6 deletions src/addNodeMarkStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
import { type AddNodeMarkStep, type Step } from "prosemirror-transform";

import { rebasePos } from "./rebasePos.js";
import { getSuggestionMarks } from "./utils.js";

/**
* Transform an add node mark step into its equivalent tracked steps.
Expand All @@ -18,12 +19,7 @@ export function trackAddNodeMarkStep(
prevSteps: Step[],
suggestionId: number,
) {
const { modification } = state.schema.marks;
if (!modification) {
throw new Error(
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { modification } = getSuggestionMarks(state.schema);

const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
const $pos = trackedTransaction.doc.resolve(rebasedPos);
Expand Down
8 changes: 2 additions & 6 deletions src/attrStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
import { type AttrStep, type Step } from "prosemirror-transform";

import { rebasePos } from "./rebasePos.js";
import { getSuggestionMarks } from "./utils.js";

/**
* Transform an attr mark step into its equivalent tracked steps.
Expand All @@ -18,12 +19,7 @@ export function trackAttrStep(
prevSteps: Step[],
suggestionId: number,
) {
const { modification } = state.schema.marks;
if (!modification) {
throw new Error(
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { modification } = getSuggestionMarks(state.schema);

const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
const $pos = trackedTransaction.doc.resolve(rebasedPos);
Expand Down
92 changes: 12 additions & 80 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { type EditorView } from "prosemirror-view";

import { findSuggestionMarkEnd } from "./findSuggestionMarkEnd.js";
import { suggestChangesKey } from "./plugin.js";
import { getSuggestionMarks } from "./utils.js";

/**
* Given a node and a transform, add a set of steps to the
Expand Down Expand Up @@ -103,9 +104,8 @@ function applySuggestionsToTransform(
}

function revertModifications(node: Node, pos: number, tr: Transform) {
const existingMods = node.marks.filter(
(mark) => mark.type === node.type.schema.marks["modification"],
);
const { modification } = getSuggestionMarks(node.type.schema);
const existingMods = node.marks.filter((mark) => mark.type === modification);
for (const mod of existingMods) {
if (
mod.attrs["type"] === "attr" &&
Expand Down Expand Up @@ -146,12 +146,7 @@ function applyModificationsToTransform(
dir: number,
suggestionId?: number,
) {
const { modification } = node.type.schema.marks;
if (!modification) {
throw new Error(
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { modification } = getSuggestionMarks(node.type.schema);

const modificationIsInSet =
suggestionId === undefined
Expand Down Expand Up @@ -194,17 +189,7 @@ function applyModificationsToTransform(
}

function applySuggestionsToNode(node: Node) {
const { deletion, insertion } = node.type.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(node.type.schema);

if (deletion.isInSet(node.marks)) {
return null;
Expand Down Expand Up @@ -236,17 +221,7 @@ export function applySuggestions(
state: EditorState,
dispatch?: EditorView["dispatch"],
) {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);

const tr = state.tr;
applySuggestionsToTransform(state.doc, tr, insertion, deletion);
Expand All @@ -265,17 +240,7 @@ export function applySuggestions(
*/
export function applySuggestion(suggestionId: number): Command {
return (state, dispatch) => {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);

const tr = state.tr;
applySuggestionsToTransform(
Expand Down Expand Up @@ -304,17 +269,7 @@ export function revertSuggestions(
state: EditorState,
dispatch?: EditorView["dispatch"],
) {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);
const tr = state.tr;
applySuggestionsToTransform(state.doc, tr, deletion, insertion);
applyModificationsToTransform(tr.doc, tr, -1);
Expand All @@ -332,17 +287,7 @@ export function revertSuggestions(
*/
export function revertSuggestion(suggestionId: number): Command {
return (state, dispatch) => {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);

const tr = state.tr;
applySuggestionsToTransform(
Expand All @@ -365,22 +310,9 @@ export function revertSuggestion(suggestionId: number): Command {
*/
export function selectSuggestion(suggestionId: number): Command {
return (state, dispatch) => {
const { deletion, insertion, modification } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
if (!modification) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { deletion, insertion, modification } = getSuggestionMarks(
state.schema,
);

let changeStart = null as number | null;
let changeEnd = null as number | null;
Expand Down
13 changes: 2 additions & 11 deletions src/decorations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DecorationSet,
type DecorationSource,
} from "prosemirror-view";
import { getSuggestionMarks } from "./utils.js";

function pilcrow() {
const span = document.createElement("span");
Expand All @@ -13,17 +14,7 @@ function pilcrow() {
}

export function getSuggestionDecorations(state: EditorState): DecorationSource {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);

const changeDecorations: Decoration[] = [];
let lastParentNode: Node | null = null;
Expand Down
8 changes: 2 additions & 6 deletions src/removeNodeMarkStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
import { type RemoveNodeMarkStep, type Step } from "prosemirror-transform";

import { rebasePos } from "./rebasePos.js";
import { getSuggestionMarks } from "./utils.js";

/**
* Transform a remove node mark step into its equivalent tracked steps.
Expand All @@ -18,12 +19,7 @@ export function suggestRemoveNodeMarkStep(
prevSteps: Step[],
suggestionId: number,
) {
const { modification } = state.schema.marks;
if (!modification) {
throw new Error(
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { modification } = getSuggestionMarks(state.schema);

const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
const $pos = trackedTransaction.doc.resolve(rebasedPos);
Expand Down
8 changes: 2 additions & 6 deletions src/replaceAroundStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { applySuggestionsToSlice } from "./commands.js";
import { rebasePos } from "./rebasePos.js";
import { suggestRemoveNodeMarkStep } from "./removeNodeMarkStep.js";
import { suggestReplaceStep } from "./replaceStep.js";
import { getSuggestionMarks } from "./utils.js";

/**
* This detects and handles changes from `setNodeMarkup` so that these are tracked as a modification
Expand All @@ -37,12 +38,7 @@ function suggestSetNodeMarkup(
step.gapFrom === step.from + 1 &&
(step as ReplaceAroundStep & { structure: boolean }).structure
) {
const { modification } = state.schema.marks;
if (!modification) {
throw new Error(
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
);
}
const { modification } = getSuggestionMarks(state.schema);

const newNode = step.slice.content.firstChild;
let from = rebasePos(step.from, prevSteps, trackedTransaction.steps);
Expand Down
13 changes: 2 additions & 11 deletions src/replaceStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { type ReplaceStep, type Step } from "prosemirror-transform";

import { findSuggestionMarkEnd } from "./findSuggestionMarkEnd.js";
import { rebasePos } from "./rebasePos.js";
import { getSuggestionMarks } from "./utils.js";

/**
* Transform a replace step into its equivalent tracked steps.
Expand Down Expand Up @@ -47,17 +48,7 @@ export function suggestReplaceStep(
prevSteps: Step[],
suggestionId: number,
) {
const { deletion, insertion } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
);
}
const { deletion, insertion } = getSuggestionMarks(state.schema);

// Check for insertion and deletion marks directly
// adjacent to this step's boundaries. If they exist,
Expand Down
35 changes: 35 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { type MarkType, type Schema } from "prosemirror-model";

export interface SuggestionMarks {
insertion: MarkType;
deletion: MarkType;
modification: MarkType;
}

/**
* Get the suggestion mark types from a schema, with proper error handling.
* Throws an error if any of the required marks are not found.
*/
export function getSuggestionMarks(schema: Schema): SuggestionMarks {
const { insertion, deletion, modification } = schema.marks;

if (!insertion) {
throw new Error(
"Failed to find insertion mark in schema. Did you forget to add it?",
);
}

if (!deletion) {
throw new Error(
"Failed to find deletion mark in schema. Did you forget to add it?",
);
}

if (!modification) {
throw new Error(
"Failed to find modification mark in schema. Did you forget to add it?",
);
}

return { insertion, deletion, modification };
}
21 changes: 5 additions & 16 deletions src/withSuggestChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { suggestReplaceAroundStep } from "./replaceAroundStep.js";
import { suggestReplaceStep } from "./replaceStep.js";
import { type EditorView } from "prosemirror-view";
import { isSuggestChangesEnabled, suggestChangesKey } from "./plugin.js";
import { getSuggestionMarks } from "./utils.js";

type StepHandler<S extends Step> = (
trackedTransaction: Transaction,
Expand Down Expand Up @@ -99,22 +100,10 @@ export function transformToSuggestionTransaction(
originalTransaction: Transaction,
state: EditorState,
) {
const { deletion, insertion, modification } = state.schema.marks;
if (!deletion) {
throw new Error(
`Failed to transform to suggestion: schema does not contain deletion mark. Did you forget to add it?`,
);
}
if (!insertion) {
throw new Error(
`Failed to transform to suggestion: schema does not contain insertion mark. Did you forget to add it?`,
);
}
if (!modification) {
throw new Error(
`Failed to transform to suggestion: schema does not contain modification mark. Did you forget to add it?`,
);
}
// Validate that all required marks exist in the schema
const { deletion, insertion, modification } = getSuggestionMarks(
state.schema,
);

// Find the highest change id in the document so far,
// and use that as the starting point for new changes
Expand Down