11import { Fragment , Slice , Node } from "prosemirror-model"
22import { assertSplitBlock , makeDoc } from "./utils.js"
3- import { AddMarkStep , ReplaceStep , Step } from "prosemirror-transform"
3+ import {
4+ AddMarkStep ,
5+ ReplaceAroundStep ,
6+ ReplaceStep ,
7+ Step ,
8+ } from "prosemirror-transform"
49import { default as pmToAm } from "../src/pmToAm.js"
510import { next as am } from "@automerge/automerge"
611import { assert } from "chai"
712import { pmDocFromSpans } from "../src/traversal.js"
813import { EditorState } from "prosemirror-state"
914import { basicSchemaAdapter } from "../src/basicSchema.js"
15+ import { ImmutableString } from "@automerge/automerge-repo"
1016
1117const schema = basicSchemaAdapter . schema
1218
1319function updateDoc (
1420 amDoc : am . Doc < unknown > ,
1521 pmDoc : Node ,
1622 steps : Step [ ] ,
17- ) : am . Patch [ ] {
23+ ) : { diff : am . Patch [ ] ; updatedDoc : am . Doc < unknown > } {
1824 const heads = am . getHeads ( amDoc )
1925 const spans = am . spans ( amDoc , [ "text" ] )
2026 const updatedDoc = am . change ( amDoc , d => {
2127 pmToAm ( basicSchemaAdapter , spans , steps , d , pmDoc , [ "text" ] )
2228 } )
23- return am . diff ( amDoc , heads , am . getHeads ( updatedDoc ) )
29+ return {
30+ diff : am . diff ( amDoc , heads , am . getHeads ( updatedDoc ) ) ,
31+ updatedDoc,
32+ }
2433}
2534
2635describe ( "when converting a ReplaceStep to a change" , ( ) => {
@@ -32,7 +41,7 @@ describe("when converting a ReplaceStep to a change", () => {
3241 // am: 0 1 2 3 4 5 6
3342 // <doc><ol> <li> <p> i t e m ' ' 1 </p></li></ol></doc>
3443 // pm:0 0 1 2 3 4 5 6 7 8 9 10 11 12 13
35- const diff = updateDoc ( doc , editor . doc , [
44+ const { diff } = updateDoc ( doc , editor . doc , [
3645 new ReplaceStep (
3746 9 ,
3847 9 ,
@@ -63,7 +72,7 @@ describe("when converting a ReplaceStep to a change", () => {
6372 { type : "ordered-list-item" , parents : [ ] , attrs : { } } ,
6473 "item 1" ,
6574 ] )
66- const diff = updateDoc ( doc , editor . doc , [
75+ const { diff } = updateDoc ( doc , editor . doc , [
6776 new ReplaceStep (
6877 11 ,
6978 11 ,
@@ -91,7 +100,7 @@ describe("when converting a ReplaceStep to a change", () => {
91100 { type : "paragraph" , parents : [ "ordered-list-item" ] , attrs : { } } ,
92101 "item 1" ,
93102 ] )
94- const diff = updateDoc ( doc , editor . doc , [
103+ const { diff } = updateDoc ( doc , editor . doc , [
95104 new ReplaceStep (
96105 9 ,
97106 9 ,
@@ -121,7 +130,7 @@ describe("when converting a ReplaceStep to a change", () => {
121130 //am: 0 1 2 3 4 5 6
122131 // <doc> <ol> <li> <p> i t e m ' ' 1 </p> </li> </ol> </doc>
123132 //pm: 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13
124- const diff = updateDoc ( doc , editor . doc , [
133+ const { diff } = updateDoc ( doc , editor . doc , [
125134 new ReplaceStep (
126135 11 ,
127136 11 ,
@@ -229,6 +238,204 @@ describe("when converting a ReplaceStep to a change", () => {
229238 strong : [ { start : 5 , end : 6 , name : "strong" , value : true } ] ,
230239 } )
231240 } )
241+
242+ describe ( "expand configuration" , ( ) => {
243+ // These tests all make use of the fact that the basicSchemaAdapter is
244+ // configured with bold spans which are inclusive (in prosemirror terms)
245+ // and link spans which are not inclusive. These tests check that the
246+ // automerge expand configuration is correctly inferred from the schema.
247+
248+ it ( "should use the mark expand configuration from the schema" , ( ) => {
249+ // We use ProseMirror to insert a range which has both a bold and link
250+ // mark on it, then use Automerge to splice a character following this
251+ // range and check that only the bold mark expands
252+ const { editor, doc } = makeDoc ( [ "text" ] )
253+
254+ // First make an insertion which adds marks
255+ let { updatedDoc } = updateDoc ( doc , editor . doc , [
256+ new ReplaceStep (
257+ 1 ,
258+ 1 ,
259+ new Slice (
260+ Fragment . from (
261+ editor . schema . text ( "1" , [
262+ editor . schema . marks . strong . create ( ) ,
263+ editor . schema . marks . link . create ( {
264+ href : "https://example.com" ,
265+ } ) ,
266+ ] ) ,
267+ ) ,
268+ 0 ,
269+ 0 ,
270+ ) ,
271+ ) ,
272+ ] )
273+
274+ // Now, insert into the automerge document after the initial character
275+ updatedDoc = am . change ( updatedDoc , d =>
276+ am . splice ( d as am . Doc < unknown > , [ "text" ] , 1 , 0 , "2" ) ,
277+ )
278+
279+ const spans = am . spans ( updatedDoc , [ "text" ] )
280+ // Here we expect the bold span to expand, because it's configured to do so
281+ // in the schema, and the link span to not expand as it's configured to not
282+ // expand
283+ assert . deepStrictEqual ( spans , [
284+ {
285+ marks : {
286+ link : JSON . stringify ( { href : "https://example.com" , title : null } ) ,
287+ strong : true ,
288+ } ,
289+ type : "text" ,
290+ value : "1" ,
291+ } ,
292+ {
293+ marks : {
294+ strong : true ,
295+ } ,
296+ type : "text" ,
297+ value : "2" ,
298+ } ,
299+ {
300+ type : "text" ,
301+ value : "text" ,
302+ } ,
303+ ] )
304+ } )
305+ } )
306+
307+ it ( "should use the correct mark config when performing more complex ReplaceSteps" , ( ) => {
308+ // In this test we make a slightly more complex change which replaces some existing content
309+ // with new blocks. This means that in the implementation we don't use splice but instead
310+ // use updateSpans.
311+ const { editor, doc } = makeDoc ( [ "item 1" ] )
312+
313+ // Use ProseMirror to replace the entire document with a paragraph
314+ // containing the text "item one" with both strong and link marks
315+ let { updatedDoc } = updateDoc ( doc , editor . doc , [
316+ new ReplaceStep (
317+ 0 ,
318+ 8 ,
319+ new Slice (
320+ Fragment . from (
321+ schema . node ( "ordered_list" , null , [
322+ schema . node ( "list_item" , null , [
323+ schema . node ( "paragraph" , null , [
324+ schema . text ( "item one" , [
325+ editor . schema . marks . strong . create ( ) ,
326+ editor . schema . marks . link . create ( {
327+ href : "https://example.com" ,
328+ } ) ,
329+ ] ) ,
330+ ] ) ,
331+ ] ) ,
332+ ] ) ,
333+ ) ,
334+ 0 ,
335+ 0 ,
336+ ) ,
337+ ) ,
338+ ] )
339+
340+ // Now use Automerge to splice the text 'two' following the marked text we
341+ // just created
342+ updatedDoc = am . change ( updatedDoc , d => {
343+ am . splice ( d as any , [ "text" ] , 9 , 0 , "two" )
344+ } )
345+ const spans = am . spans ( updatedDoc , [ "text" ] )
346+
347+ // Here bold should expand as it is inclusive (in prosemirror terms) whilst
348+ // the link span should not expand as it is not inclusive
349+ assert . deepStrictEqual ( spans , [
350+ {
351+ type : "block" ,
352+ value : {
353+ attrs : { } ,
354+ isEmbed : false ,
355+ parents : [ ] ,
356+ type : new ImmutableString ( "ordered-list-item" ) ,
357+ } ,
358+ } ,
359+ {
360+ marks : {
361+ link : '{"href":"https://example.com","title":null}' ,
362+ strong : true ,
363+ } ,
364+ type : "text" ,
365+ value : "item one" ,
366+ } ,
367+ {
368+ marks : {
369+ strong : true ,
370+ } ,
371+ type : "text" ,
372+ value : "two" ,
373+ } ,
374+ ] )
375+ } )
376+
377+ it ( "should use the correct mark config when performing ReplaceAroundSteps" , ( ) => {
378+ // In this test we create a `ReplaceAroundStep` which again fires the updateSpans
379+ // logic in the plugin
380+ const { editor, doc } = makeDoc ( [ "item one" ] )
381+
382+ // Insert <paragraph>intro</paragraph> into the document and move the
383+ // existing content to occur after this paragraph. The newly inserted
384+ // paragraph has a link and strong mark on it.
385+ let { updatedDoc } = updateDoc ( doc , editor . doc , [
386+ new ReplaceAroundStep (
387+ 0 ,
388+ 10 ,
389+ 1 ,
390+ 9 ,
391+ new Slice (
392+ Fragment . from ( [
393+ schema . node ( "paragraph" , null , [
394+ schema . text ( "intro" , [
395+ editor . schema . marks . strong . create ( ) ,
396+ editor . schema . marks . link . create ( {
397+ href : "https://example.com" ,
398+ } ) ,
399+ ] ) ,
400+ ] ) ,
401+ ] ) ,
402+ 0 ,
403+ 0 ,
404+ ) ,
405+ 6 ,
406+ ) ,
407+ ] )
408+
409+ // Now use automerge to insert the text "middle" in the paragraph we just inserted
410+ updatedDoc = am . change ( updatedDoc , d => {
411+ am . splice ( d as any , [ "text" ] , 5 , 0 , "middle" )
412+ } )
413+ const spans = am . spans ( updatedDoc , [ "text" ] )
414+
415+ // Here bold should expand as it is inclusive (in prosemirror terms) whilst
416+ // the link span should not expand as it is not inclusive
417+ assert . deepStrictEqual ( spans , [
418+ {
419+ marks : {
420+ link : '{"href":"https://example.com","title":null}' ,
421+ strong : true ,
422+ } ,
423+ type : "text" ,
424+ value : "intro" ,
425+ } ,
426+ {
427+ marks : {
428+ strong : true ,
429+ } ,
430+ type : "text" ,
431+ value : "middle" ,
432+ } ,
433+ {
434+ type : "text" ,
435+ value : "item one" ,
436+ } ,
437+ ] )
438+ } )
232439} )
233440
234441describe ( "when converting addMark steps to a change" , ( ) => {
0 commit comments