Skip to content

Commit e187aae

Browse files
authored
Extracted method for getting suggestion marks. (#18)
1 parent 0260c64 commit e187aae

10 files changed

Lines changed: 66 additions & 142 deletions

.yarn/versions/24a8541c.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
releases:
2+
"@handlewithcare/prosemirror-suggest-changes": patch

src/addNodeMarkStep.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
33
import { type AddNodeMarkStep, type Step } from "prosemirror-transform";
44

55
import { rebasePos } from "./rebasePos.js";
6+
import { getSuggestionMarks } from "./utils.js";
67

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

2824
const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
2925
const $pos = trackedTransaction.doc.resolve(rebasedPos);

src/attrStep.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
33
import { type AttrStep, type Step } from "prosemirror-transform";
44

55
import { rebasePos } from "./rebasePos.js";
6+
import { getSuggestionMarks } from "./utils.js";
67

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

2824
const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
2925
const $pos = trackedTransaction.doc.resolve(rebasedPos);

src/commands.ts

Lines changed: 12 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { type EditorView } from "prosemirror-view";
1515

1616
import { findSuggestionMarkEnd } from "./findSuggestionMarkEnd.js";
1717
import { suggestChangesKey } from "./plugin.js";
18+
import { getSuggestionMarks } from "./utils.js";
1819

1920
/**
2021
* Given a node and a transform, add a set of steps to the
@@ -103,9 +104,8 @@ function applySuggestionsToTransform(
103104
}
104105

105106
function revertModifications(node: Node, pos: number, tr: Transform) {
106-
const existingMods = node.marks.filter(
107-
(mark) => mark.type === node.type.schema.marks["modification"],
108-
);
107+
const { modification } = getSuggestionMarks(node.type.schema);
108+
const existingMods = node.marks.filter((mark) => mark.type === modification);
109109
for (const mod of existingMods) {
110110
if (
111111
mod.attrs["type"] === "attr" &&
@@ -146,12 +146,7 @@ function applyModificationsToTransform(
146146
dir: number,
147147
suggestionId?: number,
148148
) {
149-
const { modification } = node.type.schema.marks;
150-
if (!modification) {
151-
throw new Error(
152-
`Failed to apply modifications to node: schema does not contain modification mark. Did you forget to add it?`,
153-
);
154-
}
149+
const { modification } = getSuggestionMarks(node.type.schema);
155150

156151
const modificationIsInSet =
157152
suggestionId === undefined
@@ -194,17 +189,7 @@ function applyModificationsToTransform(
194189
}
195190

196191
function applySuggestionsToNode(node: Node) {
197-
const { deletion, insertion } = node.type.schema.marks;
198-
if (!deletion) {
199-
throw new Error(
200-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
201-
);
202-
}
203-
if (!insertion) {
204-
throw new Error(
205-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
206-
);
207-
}
192+
const { deletion, insertion } = getSuggestionMarks(node.type.schema);
208193

209194
if (deletion.isInSet(node.marks)) {
210195
return null;
@@ -236,17 +221,7 @@ export function applySuggestions(
236221
state: EditorState,
237222
dispatch?: EditorView["dispatch"],
238223
) {
239-
const { deletion, insertion } = state.schema.marks;
240-
if (!deletion) {
241-
throw new Error(
242-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
243-
);
244-
}
245-
if (!insertion) {
246-
throw new Error(
247-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
248-
);
249-
}
224+
const { deletion, insertion } = getSuggestionMarks(state.schema);
250225

251226
const tr = state.tr;
252227
applySuggestionsToTransform(state.doc, tr, insertion, deletion);
@@ -265,17 +240,7 @@ export function applySuggestions(
265240
*/
266241
export function applySuggestion(suggestionId: number): Command {
267242
return (state, dispatch) => {
268-
const { deletion, insertion } = state.schema.marks;
269-
if (!deletion) {
270-
throw new Error(
271-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
272-
);
273-
}
274-
if (!insertion) {
275-
throw new Error(
276-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
277-
);
278-
}
243+
const { deletion, insertion } = getSuggestionMarks(state.schema);
279244

280245
const tr = state.tr;
281246
applySuggestionsToTransform(
@@ -304,17 +269,7 @@ export function revertSuggestions(
304269
state: EditorState,
305270
dispatch?: EditorView["dispatch"],
306271
) {
307-
const { deletion, insertion } = state.schema.marks;
308-
if (!deletion) {
309-
throw new Error(
310-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
311-
);
312-
}
313-
if (!insertion) {
314-
throw new Error(
315-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
316-
);
317-
}
272+
const { deletion, insertion } = getSuggestionMarks(state.schema);
318273
const tr = state.tr;
319274
applySuggestionsToTransform(state.doc, tr, deletion, insertion);
320275
applyModificationsToTransform(tr.doc, tr, -1);
@@ -332,17 +287,7 @@ export function revertSuggestions(
332287
*/
333288
export function revertSuggestion(suggestionId: number): Command {
334289
return (state, dispatch) => {
335-
const { deletion, insertion } = state.schema.marks;
336-
if (!deletion) {
337-
throw new Error(
338-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
339-
);
340-
}
341-
if (!insertion) {
342-
throw new Error(
343-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
344-
);
345-
}
290+
const { deletion, insertion } = getSuggestionMarks(state.schema);
346291

347292
const tr = state.tr;
348293
applySuggestionsToTransform(
@@ -365,22 +310,9 @@ export function revertSuggestion(suggestionId: number): Command {
365310
*/
366311
export function selectSuggestion(suggestionId: number): Command {
367312
return (state, dispatch) => {
368-
const { deletion, insertion, modification } = state.schema.marks;
369-
if (!deletion) {
370-
throw new Error(
371-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
372-
);
373-
}
374-
if (!insertion) {
375-
throw new Error(
376-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
377-
);
378-
}
379-
if (!modification) {
380-
throw new Error(
381-
`Failed to apply tracked changes to node: schema does not contain modification mark. Did you forget to add it?`,
382-
);
383-
}
313+
const { deletion, insertion, modification } = getSuggestionMarks(
314+
state.schema,
315+
);
384316

385317
let changeStart = null as number | null;
386318
let changeEnd = null as number | null;

src/decorations.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DecorationSet,
66
type DecorationSource,
77
} from "prosemirror-view";
8+
import { getSuggestionMarks } from "./utils.js";
89

910
function pilcrow() {
1011
const span = document.createElement("span");
@@ -13,17 +14,7 @@ function pilcrow() {
1314
}
1415

1516
export function getSuggestionDecorations(state: EditorState): DecorationSource {
16-
const { deletion, insertion } = state.schema.marks;
17-
if (!deletion) {
18-
throw new Error(
19-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
20-
);
21-
}
22-
if (!insertion) {
23-
throw new Error(
24-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
25-
);
26-
}
17+
const { deletion, insertion } = getSuggestionMarks(state.schema);
2718

2819
const changeDecorations: Decoration[] = [];
2920
let lastParentNode: Node | null = null;

src/removeNodeMarkStep.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type EditorState, type Transaction } from "prosemirror-state";
33
import { type RemoveNodeMarkStep, type Step } from "prosemirror-transform";
44

55
import { rebasePos } from "./rebasePos.js";
6+
import { getSuggestionMarks } from "./utils.js";
67

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

2824
const rebasedPos = rebasePos(step.pos, prevSteps, trackedTransaction.steps);
2925
const $pos = trackedTransaction.doc.resolve(rebasedPos);

src/replaceAroundStep.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { applySuggestionsToSlice } from "./commands.js";
1616
import { rebasePos } from "./rebasePos.js";
1717
import { suggestRemoveNodeMarkStep } from "./removeNodeMarkStep.js";
1818
import { suggestReplaceStep } from "./replaceStep.js";
19+
import { getSuggestionMarks } from "./utils.js";
1920

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

4743
const newNode = step.slice.content.firstChild;
4844
let from = rebasePos(step.from, prevSteps, trackedTransaction.steps);

src/replaceStep.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { type ReplaceStep, type Step } from "prosemirror-transform";
88

99
import { findSuggestionMarkEnd } from "./findSuggestionMarkEnd.js";
1010
import { rebasePos } from "./rebasePos.js";
11+
import { getSuggestionMarks } from "./utils.js";
1112

1213
/**
1314
* Transform a replace step into its equivalent tracked steps.
@@ -47,17 +48,7 @@ export function suggestReplaceStep(
4748
prevSteps: Step[],
4849
suggestionId: number,
4950
) {
50-
const { deletion, insertion } = state.schema.marks;
51-
if (!deletion) {
52-
throw new Error(
53-
`Failed to apply tracked changes to node: schema does not contain deletion mark. Did you forget to add it?`,
54-
);
55-
}
56-
if (!insertion) {
57-
throw new Error(
58-
`Failed to apply tracked changes to node: schema does not contain insertion mark. Did you forget to add it?`,
59-
);
60-
}
51+
const { deletion, insertion } = getSuggestionMarks(state.schema);
6152

6253
// Check for insertion and deletion marks directly
6354
// adjacent to this step's boundaries. If they exist,

src/utils.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { type MarkType, type Schema } from "prosemirror-model";
2+
3+
export interface SuggestionMarks {
4+
insertion: MarkType;
5+
deletion: MarkType;
6+
modification: MarkType;
7+
}
8+
9+
/**
10+
* Get the suggestion mark types from a schema, with proper error handling.
11+
* Throws an error if any of the required marks are not found.
12+
*/
13+
export function getSuggestionMarks(schema: Schema): SuggestionMarks {
14+
const { insertion, deletion, modification } = schema.marks;
15+
16+
if (!insertion) {
17+
throw new Error(
18+
"Failed to find insertion mark in schema. Did you forget to add it?",
19+
);
20+
}
21+
22+
if (!deletion) {
23+
throw new Error(
24+
"Failed to find deletion mark in schema. Did you forget to add it?",
25+
);
26+
}
27+
28+
if (!modification) {
29+
throw new Error(
30+
"Failed to find modification mark in schema. Did you forget to add it?",
31+
);
32+
}
33+
34+
return { insertion, deletion, modification };
35+
}

src/withSuggestChanges.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { suggestReplaceAroundStep } from "./replaceAroundStep.js";
2020
import { suggestReplaceStep } from "./replaceStep.js";
2121
import { type EditorView } from "prosemirror-view";
2222
import { isSuggestChangesEnabled, suggestChangesKey } from "./plugin.js";
23+
import { getSuggestionMarks } from "./utils.js";
2324

2425
type StepHandler<S extends Step> = (
2526
trackedTransaction: Transaction,
@@ -99,22 +100,10 @@ export function transformToSuggestionTransaction(
99100
originalTransaction: Transaction,
100101
state: EditorState,
101102
) {
102-
const { deletion, insertion, modification } = state.schema.marks;
103-
if (!deletion) {
104-
throw new Error(
105-
`Failed to transform to suggestion: schema does not contain deletion mark. Did you forget to add it?`,
106-
);
107-
}
108-
if (!insertion) {
109-
throw new Error(
110-
`Failed to transform to suggestion: schema does not contain insertion mark. Did you forget to add it?`,
111-
);
112-
}
113-
if (!modification) {
114-
throw new Error(
115-
`Failed to transform to suggestion: schema does not contain modification mark. Did you forget to add it?`,
116-
);
117-
}
103+
// Validate that all required marks exist in the schema
104+
const { deletion, insertion, modification } = getSuggestionMarks(
105+
state.schema,
106+
);
118107

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

0 commit comments

Comments
 (0)