| author | |
| committer | |
| log | 02d45d734921e4a41dddbf7887dd3224bb4c7f4c |
| tree | 1cc8f6cdc96d1022e724af5d0ef93f3ab0c8c3d7 |
| parent | e3fba00fa7baf2a33af4943802256e937ba0573d |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
Preserve inherited character styles, nested child placement, history and unrelated objects while publishing the joined graph and title metadata atomically. Empty left paragraphs adopt the right text identity; left tags survive as verified in independent OneNote controls.
Validate twenty Rust joins through cold native captures. Add preservation, invalid-target, property-reference remapping and interrupted-publication checks, plus stateful split/join fuzzing. Public gate passes 186 Rust tests, 8 doctests and 130 Python tests with fmt, Clippy and builds.
Assisted-by: gpt-6-astra97 files changed, 2054 insertions(+), 178 deletions(-)
corpus/paragraph-edit/README.md+14-2| ... | ... | @@ -34,8 +34,7 @@ directory comes from `ONESTORE_PARAGRAPH_OUTPUT`. Native character/style compari |
| 34 | 34 | use both the keyboard-generated controls and the Rust-written notebooks. Empty |
| 35 | 35 | typing checks extend the preexisting empty run in an independent expected model. |
| 36 | 36 | |
| 37 | `tools/test_paragraph_edit.py` verifies these controls without a VM. The native | |
| 38 | join captures establish behavior for subsequent join implementation. | |
| 37 | `tools/test_paragraph_edit.py` verifies these controls without a VM. | |
| 39 | 38 | Identical captured files link to one canonical copy within this corpus. |
| 40 | 39 | |
| 41 | 40 | `join-edges`, authored with `tools/native/paragraph-joins.ps1`, adds five native |
| ... | ... | @@ -48,3 +47,16 @@ The untouched parent's text gains a timestamp and `0x880034dd`, as in the earlie |
| 48 | 47 | native controls; its content and other properties stay unchanged. The test fixes |
| 49 | 48 | these observed graph, identity and metadata effects alongside independent native |
| 50 | 49 | character-style comparisons. |
| 50 | ||
| 51 | `join-tags`, authored with `tools/native/paragraph-tag-joins.ps1`, confirms the | |
| 52 | left-tag rule for an empty untagged left paragraph, an empty tagged left paragraph | |
| 53 | with a different right tag, and two nonempty paragraphs with different tags. | |
| 54 | Right-side tags disappear in all three cases; only the left tag survives. | |
| 55 | ||
| 56 | `rust-join` retains twenty Rust joins across the original splits, inheritance | |
| 57 | edges and additional tag controls. Each group has a cold OneNote capture and | |
| 58 | saved notebook. Public tests compare the Rust and native-saved images to native | |
| 59 | XML, preserve exact child/content identities across reopening, and compare | |
| 60 | the native-rendered result to the corresponding keyboard-generated control. | |
| 61 | `export_native_paragraph_joins` regenerates candidates in a new directory specified | |
| 62 | by `ONESTORE_PARAGRAPH_JOIN_OUTPUT`. |
corpus/paragraph-edit/join-tags/before/notebook/Open Notebook.onetoc2 created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/before/notebook/Open Notebook.onetoc2 differ |
corpus/paragraph-edit/join-tags/before/notebook/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/before/notebook/synthetic.one differ |
corpus/paragraph-edit/join-tags/before/read/environment.json created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | { | |
| 2 | "powershell": "5.1.14409.1005", | |
| 3 | "schema": "xs2010", | |
| 4 | "hostname": "ONE-M6-57E1FB2A", | |
| 5 | "cold": false, | |
| 6 | "onenote": "14.0.4763.1000" | |
| 7 | } |
corpus/paragraph-edit/join-tags/before/read/hierarchy.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Notebook xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" name="notebook" nickname="notebook" ID="{3E150BBB-447F-4345-8BCC-7B6B8B50CB91}{1}{B0}" path="C:\one-tests\runs\capture\notebook" lastModifiedTime="2026-09-08T09:34:38.000Z" color="#B49EDE"><one:Section name="synthetic" ID="{9E605B7B-B21F-039A-0A67-51514F4CD2FA}{1}{B0}" path="C:\one-tests\runs\capture\notebook\synthetic.one" lastModifiedTime="2026-09-08T09:34:38.000Z" color="#8AA8E4"><one:Page ID="{B229A8FD-3BD4-408B-8D00-F9C0B374CEE5}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1"/><one:Page ID="{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1"/><one:Page ID="{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1"/><one:Page ID="{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1"/></one:Section></one:Notebook> |
corpus/paragraph-edit/join-tags/before/read/page-000.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Other task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left right tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:33:05.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{46}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/before/read/page-001.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:TagDef index="1" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Other task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left two tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:33:05.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{47}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="1" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{56}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/before/read/page-002.xml created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{B229A8FD-3BD4-408B-8D00-F9C0B374CEE5}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Outline author="Rich author" lastModifiedBy="Rich author" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{D5E50041-70A1-433D-82F2-751B4CE82B84}{10}{B0}"><one:Position x="36.0" y="14.40000057220459" z="0"/><one:Size width="127.5136947631836" height="42.90299224853515"/><one:OEChildren><one:OE creationTime="2026-09-08T04:47:24.000Z" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{D280A8B6-83DC-0837-32A6-B61187835972}{1}{B0}" alignment="left"><one:T><![CDATA[<span style='font-weight:bold; | |
| 3 | text-decoration:underline;font-family:Calibri;font-size:11.0pt'>Bold</span><span | |
| 4 | style='font-family:Calibri;font-size:11.0pt'> </span><span style='font-family: | |
| 5 | Arial;font-size:13.5pt'>&#129408;</span><span style='font-family:Calibri; | |
| 6 | font-size:11.0pt'> </span><span style='font-style:italic;text-decoration:line-through; | |
| 7 | font-family:Calibri;font-size:11.0pt'>italic</span><span style='font-family: | |
| 8 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:super;font-family: | |
| 9 | Calibri;font-size:11.0pt'>é</span><span style='font-family:Calibri;font-size: | |
| 10 | 11.0pt'> </span><span style='font-family:Calibri;font-size:11.0pt;color:#0C2238; | |
| 11 | background:yellow;mso-highlight:yellow'>color</span><span style='font-family: | |
| 12 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:sub;font-family: | |
| 13 | Calibri;font-size:11.0pt'>東京</span><span style='font-family:Calibri;font-size: | |
| 14 | 11.0pt'><br> | |
| 15 | End</span>]]></one:T></one:OE><one:OE author="snow" authorInitials="S" lastModifiedBy="snow" lastModifiedByInitials="S" creationTime="2026-09-05T05:06:55.000Z" lastModifiedTime="2026-09-05T05:06:56.000Z" objectID="{D5E50041-70A1-433D-82F2-751B4CE82B84}{11}{B0}" alignment="left" quickStyleIndex="0" style="font-size:11.0pt;color:#1F4E79"><one:T><![CDATA[<span | |
| 16 | style='font-weight:bold;font-family:Calibri' lang=en-US>Fictitious: café, </span><span | |
| 17 | style='font-weight:bold;font-family:SimSun' lang=en-US>東京</span><span | |
| 18 | style='font-weight:bold;font-family:Calibri' lang=en-US>, </span><span | |
| 19 | style='font-weight:bold;font-family:Arial;direction:rtl;unicode-bidi:embed' | |
| 20 | lang=ar-SA>مرحبا</span>]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/before/read/page-003.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:TagDef index="1" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Other task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join different tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:33:05.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[Left]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{46}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="1" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/before/read/payloads.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../before/read/payloads.json | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/cases.json created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "body": "\u003cone:OE\u003e\u003cone:T\u003e\u003c![CDATA[]]\u003e\u003c/one:T\u003e\u003c/one:OE\u003e\u003cone:OE\u003e\u003cone:Tag index=\"1\" completed=\"true\" disabled=\"false\" creationDate=\"2020-01-02T03:04:05.000Z\" completionDate=\"2020-01-02T04:05:06.000Z\"/\u003e\u003cone:T\u003e\u003c![CDATA[\u003cb\u003eBold\u003c/b\u003e \u003ci\u003eitalic\u003c/i\u003e 🦀 é \u003cspan style=\"color:#123456\"\u003etail\u003c/span\u003e]]\u003e\u003c/one:T\u003e\u003c/one:OE\u003e", | |
| 4 | "name": "Join empty left right tag", | |
| 5 | "page": "{110D64A5-F4AB-4699-B620-CF39E7595E3D}{1}{B0}" | |
| 6 | }, | |
| 7 | { | |
| 8 | "body": "\u003cone:OE\u003e\u003cone:Tag index=\"0\" completed=\"true\" disabled=\"false\" creationDate=\"2020-01-02T03:04:05.000Z\" completionDate=\"2020-01-02T04:05:06.000Z\"/\u003e\u003cone:T\u003e\u003c![CDATA[]]\u003e\u003c/one:T\u003e\u003c/one:OE\u003e\u003cone:OE\u003e\u003cone:Tag index=\"1\" completed=\"true\" disabled=\"false\" creationDate=\"2020-01-02T03:04:05.000Z\" completionDate=\"2020-01-02T04:05:06.000Z\"/\u003e\u003cone:T\u003e\u003c![CDATA[\u003cb\u003eBold\u003c/b\u003e \u003ci\u003eitalic\u003c/i\u003e 🦀 é \u003cspan style=\"color:#123456\"\u003etail\u003c/span\u003e]]\u003e\u003c/one:T\u003e\u003c/one:OE\u003e", | |
| 9 | "name": "Join empty left two tags", | |
| 10 | "page": "{4D3539F2-613D-47F1-AD5A-6D8EAD9D9901}{1}{B0}" | |
| 11 | }, | |
| 12 | { | |
| 13 | "body": "\u003cone:OE\u003e\u003cone:Tag index=\"0\" completed=\"true\" disabled=\"false\" creationDate=\"2020-01-02T03:04:05.000Z\" completionDate=\"2020-01-02T04:05:06.000Z\"/\u003e\u003cone:T\u003eLeft\u003c/one:T\u003e\u003c/one:OE\u003e\u003cone:OE\u003e\u003cone:Tag index=\"1\" completed=\"true\" disabled=\"false\" creationDate=\"2020-01-02T03:04:05.000Z\" completionDate=\"2020-01-02T04:05:06.000Z\"/\u003e\u003cone:T\u003e\u003c![CDATA[\u003cb\u003eBold\u003c/b\u003e \u003ci\u003eitalic\u003c/i\u003e 🦀 é \u003cspan style=\"color:#123456\"\u003etail\u003c/span\u003e]]\u003e\u003c/one:T\u003e\u003c/one:OE\u003e", | |
| 14 | "name": "Join different tags", | |
| 15 | "page": "{8266EEF9-BFB3-4061-B62F-67D83F1A49FF}{1}{B0}" | |
| 16 | } | |
| 17 | ] |
corpus/paragraph-edit/join-tags/joined/notebook/Open Notebook.onetoc2	 created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../before/notebook/Open Notebook.onetoc2 | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/notebook/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/joined/notebook/synthetic.one differ |
corpus/paragraph-edit/join-tags/joined/read/environment.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../before/read/environment.json | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/read/hierarchy.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Notebook xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" name="notebook" nickname="notebook" ID="{3E150BBB-447F-4345-8BCC-7B6B8B50CB91}{1}{B0}" path="C:\one-tests\runs\capture\notebook" lastModifiedTime="2026-09-08T09:36:35.000Z" color="#B49EDE" isCurrentlyViewed="true"><one:Section name="synthetic" ID="{9E605B7B-B21F-039A-0A67-51514F4CD2FA}{1}{B0}" path="C:\one-tests\runs\capture\notebook\synthetic.one" lastModifiedTime="2026-09-08T09:36:35.000Z" color="#8AA8E4" isCurrentlyViewed="true"><one:Page ID="{B229A8FD-3BD4-408B-8D00-F9C0B374CEE5}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:34:38.000Z" pageLevel="1"/><one:Page ID="{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:36:31.000Z" pageLevel="1"/><one:Page ID="{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:33.000Z" pageLevel="1"/><one:Page ID="{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:35.000Z" pageLevel="1" isCurrentlyViewed="true"/></one:Section></one:Notebook> |
corpus/paragraph-edit/join-tags/joined/read/page-000.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:36:31.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left right tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:36:31.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:36:31.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/joined/read/page-001.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:33.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:32.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left two tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:36:33.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:33.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:32.000Z" objectID="{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{56}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/joined/read/page-002.xml created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{B229A8FD-3BD4-408B-8D00-F9C0B374CEE5}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:36:30.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Outline author="Rich author" lastModifiedBy="Rich author" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{D5E50041-70A1-433D-82F2-751B4CE82B84}{10}{B0}"><one:Position x="36.0" y="14.40000057220459" z="0"/><one:Size width="523.125" height="42.90299224853515"/><one:OEChildren><one:OE creationTime="2026-09-08T04:47:24.000Z" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{D280A8B6-83DC-0837-32A6-B61187835972}{1}{B0}" alignment="left"><one:T><![CDATA[<span style='font-weight:bold; | |
| 3 | text-decoration:underline;font-family:Calibri;font-size:11.0pt'>Bold</span><span | |
| 4 | style='font-family:Calibri;font-size:11.0pt'> </span><span style='font-family: | |
| 5 | Arial;font-size:13.5pt'>&#129408;</span><span style='font-family:Calibri; | |
| 6 | font-size:11.0pt'> </span><span style='font-style:italic;text-decoration:line-through; | |
| 7 | font-family:Calibri;font-size:11.0pt'>italic</span><span style='font-family: | |
| 8 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:super;font-family: | |
| 9 | Calibri;font-size:11.0pt'>é</span><span style='font-family:Calibri;font-size: | |
| 10 | 11.0pt'> </span><span style='font-family:Calibri;font-size:11.0pt;color:#0C2238; | |
| 11 | background:yellow;mso-highlight:yellow'>color</span><span style='font-family: | |
| 12 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:sub;font-family: | |
| 13 | Calibri;font-size:11.0pt'>東京</span><span style='font-family:Calibri;font-size: | |
| 14 | 11.0pt'><br> | |
| 15 | End</span>]]></one:T></one:OE><one:OE author="snow" authorInitials="S" lastModifiedBy="snow" lastModifiedByInitials="S" creationTime="2026-09-05T05:06:55.000Z" lastModifiedTime="2026-09-05T05:06:56.000Z" objectID="{D5E50041-70A1-433D-82F2-751B4CE82B84}{11}{B0}" alignment="left" quickStyleIndex="0" style="font-size:11.0pt;color:#1F4E79"><one:T><![CDATA[<span | |
| 16 | style='font-weight:bold;font-family:Calibri' lang=en-US>Fictitious: café, </span><span | |
| 17 | style='font-weight:bold;font-family:SimSun' lang=en-US>東京</span><span | |
| 18 | style='font-weight:bold;font-family:Calibri' lang=en-US>, </span><span | |
| 19 | style='font-weight:bold;font-family:Arial;direction:rtl;unicode-bidi:embed' | |
| 20 | lang=ar-SA>مرحبا</span>]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/joined/read/page-003.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:35.000Z" pageLevel="1" isCurrentlyViewed="true" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:34.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join different tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:36:35.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="134.0610046386719" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:36:35.000Z" lastModifiedTime="2026-09-08T09:36:35.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | lang=en-US>Left</span><span style='font-weight:bold' lang=en-US>Bold</span><span | |
| 4 | lang=en-US> </span><span style='font-style:italic' lang=en-US>italic</span><span | |
| 5 | lang=en-US> &#129408; é </span><span style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:36:34.000Z" objectID="{998EEB43-1A80-48BB-B928-2AE6052587A2}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/join-tags/joined/read/payloads.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../before/read/payloads.json | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/ui/join-different-tags.ahk created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | #Requires AutoHotkey v2.0 | |
| 2 | OnError((exception, mode) => (FileAppend(exception.Message, "**"), ExitApp(1))) | |
| 3 | dm := Buffer(220, 0) | |
| 4 | NumPut("UShort", 220, dm, 68) | |
| 5 | if !DllCall("EnumDisplaySettingsW", "Ptr", 0, "UInt", 0xFFFFFFFF, "Ptr", dm) | |
| 6 | throw Error("Cannot inspect display mode") | |
| 7 | NumPut("UInt", NumGet(dm, 72, "UInt") | 0x180000, dm, 72) | |
| 8 | NumPut("UInt", 1280, dm, 172) | |
| 9 | NumPut("UInt", 720, dm, 176) | |
| 10 | if DllCall("ChangeDisplaySettingsW", "Ptr", dm, "UInt", 0, "Int") != 0 | |
| 11 | throw Error("Display mode rejected") | |
| 12 | app := ComObject("OneNote.Application") | |
| 13 | app.NavigateTo("{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}", "{998EEB43-1A80-48BB-B928-2AE6052587A2}{46}{B0}", false) | |
| 14 | hwnd := WinWait("ahk_class Framework::CFrame ahk_exe ONENOTE.EXE",, 10) | |
| 15 | if !hwnd | |
| 16 | throw Error("OneNote window did not appear") | |
| 17 | WinMaximize(hwnd) | |
| 18 | WinActivate(hwnd) | |
| 19 | if !WinWaitActive(hwnd,, 10) | |
| 20 | throw Error("OneNote window did not become active") | |
| 21 | Sleep 300 | |
| 22 | Send "{Left}{Backspace}" |
corpus/paragraph-edit/join-tags/joined/ui/join-different-tags.json created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | { | |
| 2 | "exit": 0, | |
| 3 | "stdout": "", | |
| 4 | "stderr": "", | |
| 5 | "w": 1280, | |
| 6 | "h": 720, | |
| 7 | "error": null, | |
| 8 | "win": { | |
| 9 | "title": "Join different tags - Microsoft OneNote", | |
| 10 | "class": "Framework::CFrame", | |
| 11 | "dialog": false | |
| 12 | } | |
| 13 | } | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/ui/join-different-tags.png created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/joined/ui/join-different-tags.png differ |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-right-tag.ahk created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | #Requires AutoHotkey v2.0 | |
| 2 | OnError((exception, mode) => (FileAppend(exception.Message, "**"), ExitApp(1))) | |
| 3 | dm := Buffer(220, 0) | |
| 4 | NumPut("UShort", 220, dm, 68) | |
| 5 | if !DllCall("EnumDisplaySettingsW", "Ptr", 0, "UInt", 0xFFFFFFFF, "Ptr", dm) | |
| 6 | throw Error("Cannot inspect display mode") | |
| 7 | NumPut("UInt", NumGet(dm, 72, "UInt") | 0x180000, dm, 72) | |
| 8 | NumPut("UInt", 1280, dm, 172) | |
| 9 | NumPut("UInt", 720, dm, 176) | |
| 10 | if DllCall("ChangeDisplaySettingsW", "Ptr", dm, "UInt", 0, "Int") != 0 | |
| 11 | throw Error("Display mode rejected") | |
| 12 | app := ComObject("OneNote.Application") | |
| 13 | app.NavigateTo("{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}", "{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{46}{B0}", false) | |
| 14 | hwnd := WinWait("ahk_class Framework::CFrame ahk_exe ONENOTE.EXE",, 10) | |
| 15 | if !hwnd | |
| 16 | throw Error("OneNote window did not appear") | |
| 17 | WinMaximize(hwnd) | |
| 18 | WinActivate(hwnd) | |
| 19 | if !WinWaitActive(hwnd,, 10) | |
| 20 | throw Error("OneNote window did not become active") | |
| 21 | Sleep 300 | |
| 22 | Send "{Left}{Backspace}" |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-right-tag.json created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | { | |
| 2 | "exit": 0, | |
| 3 | "stdout": "", | |
| 4 | "stderr": "", | |
| 5 | "w": 1280, | |
| 6 | "h": 720, | |
| 7 | "error": null, | |
| 8 | "win": { | |
| 9 | "title": "Join empty left right tag - Microsoft OneNote", | |
| 10 | "class": "Framework::CFrame", | |
| 11 | "dialog": false | |
| 12 | } | |
| 13 | } | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-right-tag.png created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-right-tag.png differ |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-two-tags.ahk created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | #Requires AutoHotkey v2.0 | |
| 2 | OnError((exception, mode) => (FileAppend(exception.Message, "**"), ExitApp(1))) | |
| 3 | dm := Buffer(220, 0) | |
| 4 | NumPut("UShort", 220, dm, 68) | |
| 5 | if !DllCall("EnumDisplaySettingsW", "Ptr", 0, "UInt", 0xFFFFFFFF, "Ptr", dm) | |
| 6 | throw Error("Cannot inspect display mode") | |
| 7 | NumPut("UInt", NumGet(dm, 72, "UInt") | 0x180000, dm, 72) | |
| 8 | NumPut("UInt", 1280, dm, 172) | |
| 9 | NumPut("UInt", 720, dm, 176) | |
| 10 | if DllCall("ChangeDisplaySettingsW", "Ptr", dm, "UInt", 0, "Int") != 0 | |
| 11 | throw Error("Display mode rejected") | |
| 12 | app := ComObject("OneNote.Application") | |
| 13 | app.NavigateTo("{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}", "{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{47}{B0}", false) | |
| 14 | hwnd := WinWait("ahk_class Framework::CFrame ahk_exe ONENOTE.EXE",, 10) | |
| 15 | if !hwnd | |
| 16 | throw Error("OneNote window did not appear") | |
| 17 | WinMaximize(hwnd) | |
| 18 | WinActivate(hwnd) | |
| 19 | if !WinWaitActive(hwnd,, 10) | |
| 20 | throw Error("OneNote window did not become active") | |
| 21 | Sleep 300 | |
| 22 | Send "{Left}{Backspace}" |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-two-tags.json created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | { | |
| 2 | "exit": 0, | |
| 3 | "stdout": "", | |
| 4 | "stderr": "", | |
| 5 | "w": 1280, | |
| 6 | "h": 720, | |
| 7 | "error": null, | |
| 8 | "win": { | |
| 9 | "title": "Join empty left two tags - Microsoft OneNote", | |
| 10 | "class": "Framework::CFrame", | |
| 11 | "dialog": false | |
| 12 | } | |
| 13 | } | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-two-tags.png created| Binary files /dev/null and b/corpus/paragraph-edit/join-tags/joined/ui/join-empty-left-two-tags.png differ |
corpus/paragraph-edit/join-tags/joined/ui/selections.json created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "case": "Join empty left right tag", | |
| 4 | "page": "{2A557903-2906-4962-90DB-1DBBD4B4F368}{1}{B0}", | |
| 5 | "object": "{DB20B227-1FF5-4341-9621-D7C3E0EEE79B}{46}{B0}", | |
| 6 | "keys": "{Left}{Backspace}" | |
| 7 | }, | |
| 8 | { | |
| 9 | "case": "Join empty left two tags", | |
| 10 | "page": "{766D2454-BC90-480A-8BA1-BF0C9E703454}{1}{B0}", | |
| 11 | "object": "{5237862E-022E-4FC4-8E36-D9C8E010D9E1}{47}{B0}", | |
| 12 | "keys": "{Left}{Backspace}" | |
| 13 | }, | |
| 14 | { | |
| 15 | "case": "Join different tags", | |
| 16 | "page": "{B93EF35F-621E-4F9A-90D4-B55A0CF7E4AA}{1}{B0}", | |
| 17 | "object": "{998EEB43-1A80-48BB-B928-2AE6052587A2}{46}{B0}", | |
| 18 | "keys": "{Left}{Backspace}" | |
| 19 | } | |
| 20 | ] | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/run.json created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | { | |
| 2 | "notebook": "/Users/clo/dev/one/corpus/formatted-insertion/native-paragraph/candidate", | |
| 3 | "expected_pages": 4, | |
| 4 | "author": "evidence/m10/native-paragraph-tag-joins.ps1", | |
| 5 | "author_timeout_seconds": 600, | |
| 6 | "inspect": true, | |
| 7 | "collect_notebook": true, | |
| 8 | "base": { | |
| 9 | "file": "win7-office-base.qcow2", | |
| 10 | "format": "qcow2", | |
| 11 | "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", | |
| 12 | "virtual_size": 68719476736 | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "author.ps1": "f975577d6abdb21e6cc8e2a6f605ab239047108e6da2e2f804ab8197991d86e7", | |
| 16 | "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", | |
| 17 | "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" | |
| 18 | } | |
| 19 | } |
corpus/paragraph-edit/join-tags/source.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../join-edges/source.json | |
| \ No newline at end of file |
corpus/paragraph-edit/join-tags/teardown.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../join-edges/teardown.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/inheritance/candidate/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/inheritance/candidate/synthetic.one differ |
corpus/paragraph-edit/rust-join/inheritance/manifest.json created+47| ... | ... | @@ -0,0 +1,47 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "case": "Join inherited styles", | |
| 4 | "intent": { | |
| 5 | "author": "Rust join author", | |
| 6 | "left": "{10952FC5-A849-0DE5-03A7-5D9304627DA7},44", | |
| 7 | "right": "{10952FC5-A849-0DE5-03A7-5D9304627DA7},50" | |
| 8 | }, | |
| 9 | "space": "{D6CEB12D-D1B7-033B-20A2-E9B6FF501602},1" | |
| 10 | }, | |
| 11 | { | |
| 12 | "case": "Join empty left tag", | |
| 13 | "intent": { | |
| 14 | "author": "Rust join author", | |
| 15 | "left": "{8BAFC64A-8457-0253-3906-57D27F8546B7},45", | |
| 16 | "right": "{8BAFC64A-8457-0253-3906-57D27F8546B7},51" | |
| 17 | }, | |
| 18 | "space": "{F6E7A6F0-805F-0858-2B5A-4B1260940CF9},1" | |
| 19 | }, | |
| 20 | { | |
| 21 | "case": "Join right tag", | |
| 22 | "intent": { | |
| 23 | "author": "Rust join author", | |
| 24 | "left": "{2E9A20A9-1E0E-0F04-0FCE-8497CF032068},44", | |
| 25 | "right": "{2E9A20A9-1E0E-0F04-0FCE-8497CF032068},49" | |
| 26 | }, | |
| 27 | "space": "{04EE80E5-584B-04E0-1F05-07D86902DFA1},1" | |
| 28 | }, | |
| 29 | { | |
| 30 | "case": "Join both tags", | |
| 31 | "intent": { | |
| 32 | "author": "Rust join author", | |
| 33 | "left": "{7A5195E3-F792-0C49-1F3C-EBBFCE774FE6},44", | |
| 34 | "right": "{7A5195E3-F792-0C49-1F3C-EBBFCE774FE6},50" | |
| 35 | }, | |
| 36 | "space": "{631A0B54-DBB1-0988-22E1-96E8B1202B59},1" | |
| 37 | }, | |
| 38 | { | |
| 39 | "case": "Join both children", | |
| 40 | "intent": { | |
| 41 | "author": "Rust join author", | |
| 42 | "left": "{0384F5F6-5F0B-0164-32C6-6E97A4F1B661},49", | |
| 43 | "right": "{0384F5F6-5F0B-0164-32C6-6E97A4F1B661},54" | |
| 44 | }, | |
| 45 | "space": "{D7D982D1-A3D3-0690-1181-A9703CF717E5},1" | |
| 46 | } | |
| 47 | ] | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/inheritance/native/notebook/Open Notebook.onetoc2 created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/inheritance/native/notebook/Open Notebook.onetoc2 differ |
corpus/paragraph-edit/rust-join/inheritance/native/notebook/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/inheritance/native/notebook/synthetic.one differ |
corpus/paragraph-edit/rust-join/inheritance/native/read/environment.json created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | { | |
| 2 | "powershell": "5.1.14409.1005", | |
| 3 | "schema": "xs2010", | |
| 4 | "hostname": "ONE-M6-36919857", | |
| 5 | "cold": true, | |
| 6 | "onenote": "14.0.4763.1000" | |
| 7 | } |
corpus/paragraph-edit/rust-join/inheritance/native/read/hierarchy.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Notebook xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" name="notebook" nickname="notebook" ID="{B91F23B4-AB1B-420F-90A5-CE03531BD49B}{1}{B0}" path="C:\one-tests\runs\capture\notebook" lastModifiedTime="2026-09-08T09:41:46.000Z" color="#B49EDE"><one:Section name="synthetic" ID="{7D7D2CEA-26EA-0A71-114D-7381C43452E5}{1}{B0}" path="C:\one-tests\runs\capture\notebook\synthetic.one" lastModifiedTime="2026-09-08T09:41:46.000Z" color="#8AA8E4"><one:Page ID="{5134DF6C-AF21-4960-962A-DB10380C4EFA}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/><one:Page ID="{E3E250B5-308F-46E5-8BB3-BA59D089D3E4}{1}{B0}" name="Join inherited styles" dateTime="2026-09-08T09:07:14.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/><one:Page ID="{C3CB4768-6167-4D86-804B-18FD4F4DC91F}{1}{B0}" name="Join empty left tag" dateTime="2026-09-08T09:07:15.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/><one:Page ID="{31C2617D-B973-413E-B414-543746DB1A47}{1}{B0}" name="Join right tag" dateTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/><one:Page ID="{5636EACC-3A89-4C56-89F0-C5079EF9EEBF}{1}{B0}" name="Join both tags" dateTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/><one:Page ID="{E2F56349-42EB-434E-BA90-FA9F132ED203}{1}{B0}" name="Join both children" dateTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1"/></one:Section></one:Notebook> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-000.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{31C2617D-B973-413E-B414-543746DB1A47}{1}{B0}" name="Join right tag" dateTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:07:16.000Z" objectID="{1BB6C131-FF36-4ADA-A4DF-D778E0DAE58E}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join right tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{1BB6C131-FF36-4ADA-A4DF-D778E0DAE58E}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="134.0610046386719" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{1BB6C131-FF36-4ADA-A4DF-D778E0DAE58E}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | lang=en-US>Left</span><span style='font-weight:bold' lang=en-US>Bold</span><span | |
| 4 | lang=en-US> </span><span style='font-style:italic' lang=en-US>italic</span><span | |
| 5 | lang=en-US> &#129408; é </span><span style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:07:16.000Z" objectID="{1BB6C131-FF36-4ADA-A4DF-D778E0DAE58E}{54}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-001.xml created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{5134DF6C-AF21-4960-962A-DB10380C4EFA}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Outline author="Rich author" lastModifiedBy="Rich author" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{36F877D0-E454-4AD6-99D8-57CBC790AB9B}{10}{B0}"><one:Position x="36.0" y="14.40000057220459" z="0"/><one:Size width="127.5136947631836" height="42.90299224853515"/><one:OEChildren><one:OE creationTime="2026-09-08T04:47:24.000Z" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{319DDF27-1729-01DC-298C-94C10CFBD96D}{1}{B0}" alignment="left"><one:T><![CDATA[<span style='font-weight:bold; | |
| 3 | text-decoration:underline;font-family:Calibri;font-size:11.0pt'>Bold</span><span | |
| 4 | style='font-family:Calibri;font-size:11.0pt'> </span><span style='font-family: | |
| 5 | Arial;font-size:13.5pt'>&#129408;</span><span style='font-family:Calibri; | |
| 6 | font-size:11.0pt'> </span><span style='font-style:italic;text-decoration:line-through; | |
| 7 | font-family:Calibri;font-size:11.0pt'>italic</span><span style='font-family: | |
| 8 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:super;font-family: | |
| 9 | Calibri;font-size:11.0pt'>é</span><span style='font-family:Calibri;font-size: | |
| 10 | 11.0pt'> </span><span style='font-family:Calibri;font-size:11.0pt;color:#0C2238; | |
| 11 | background:yellow;mso-highlight:yellow'>color</span><span style='font-family: | |
| 12 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:sub;font-family: | |
| 13 | Calibri;font-size:11.0pt'>東京</span><span style='font-family:Calibri;font-size: | |
| 14 | 11.0pt'><br> | |
| 15 | End</span>]]></one:T></one:OE><one:OE author="snow" authorInitials="S" lastModifiedBy="snow" lastModifiedByInitials="S" creationTime="2026-09-05T05:06:55.000Z" lastModifiedTime="2026-09-05T05:06:56.000Z" objectID="{36F877D0-E454-4AD6-99D8-57CBC790AB9B}{11}{B0}" alignment="left" quickStyleIndex="0" style="font-size:11.0pt;color:#1F4E79"><one:T><![CDATA[<span | |
| 16 | style='font-weight:bold;font-family:Calibri' lang=en-US>Fictitious: café, </span><span | |
| 17 | style='font-weight:bold;font-family:SimSun' lang=en-US>東京</span><span | |
| 18 | style='font-weight:bold;font-family:Calibri' lang=en-US>, </span><span | |
| 19 | style='font-weight:bold;font-family:Arial;direction:rtl;unicode-bidi:embed' | |
| 20 | lang=ar-SA>مرحبا</span>]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-002.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{5636EACC-3A89-4C56-89F0-C5079EF9EEBF}{1}{B0}" name="Join both tags" dateTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:07:17.000Z" objectID="{4F7D747B-16AA-4997-B42D-B850E1AE8A00}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join both tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{4F7D747B-16AA-4997-B42D-B850E1AE8A00}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="134.0610046386719" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{4F7D747B-16AA-4997-B42D-B850E1AE8A00}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | lang=en-US>Left</span><span style='font-weight:bold' lang=en-US>Bold</span><span | |
| 4 | lang=en-US> </span><span style='font-style:italic' lang=en-US>italic</span><span | |
| 5 | lang=en-US> &#129408; é </span><span style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:07:17.000Z" objectID="{4F7D747B-16AA-4997-B42D-B850E1AE8A00}{54}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-003.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{C3CB4768-6167-4D86-804B-18FD4F4DC91F}{1}{B0}" name="Join empty left tag" dateTime="2026-09-08T09:07:15.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:07:16.000Z" objectID="{BE8327D2-656F-478D-9217-043D505C8351}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{BE8327D2-656F-478D-9217-043D505C8351}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{BE8327D2-656F-478D-9217-043D505C8351}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:16.000Z" lastModifiedTime="2026-09-08T09:07:16.000Z" objectID="{BE8327D2-656F-478D-9217-043D505C8351}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-004.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{E2F56349-42EB-434E-BA90-FA9F132ED203}{1}{B0}" name="Join both children" dateTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:07:17.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join both children]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="184.8066253662109" height="53.71086883544922"/><one:OEChildren><one:OE creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Left]]></one:T><one:OEChildren><one:OE lastModifiedBy="Rust join author" creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{45}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | lang=en-US>Left child</span><span style='font-weight:bold' lang=en-US>Bold</span><span | |
| 4 | lang=en-US> </span><span style='font-style:italic' lang=en-US>italic</span><span | |
| 5 | lang=en-US> &#129408; é </span><span style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:07:17.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{58}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Right child]]></one:T></one:OE></one:OEChildren></one:OE><one:OE creationTime="2026-09-08T09:07:17.000Z" lastModifiedTime="2026-09-08T09:07:17.000Z" objectID="{36A8146E-BE33-44BA-99D7-3D788B287387}{63}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/page-005.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{E3E250B5-308F-46E5-8BB3-BA59D089D3E4}{1}{B0}" name="Join inherited styles" dateTime="2026-09-08T09:07:14.000Z" lastModifiedTime="2026-09-08T09:41:46.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:15.000Z" lastModifiedTime="2026-09-08T09:07:15.000Z" objectID="{25B9CE5D-4971-483B-A8B6-0E7C2BBBB841}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join inherited styles]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{25B9CE5D-4971-483B-A8B6-0E7C2BBBB841}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="160.521011352539" height="33.87984466552734"/><one:OEChildren><one:OE creationTime="2026-09-08T09:07:15.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{25B9CE5D-4971-483B-A8B6-0E7C2BBBB841}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span style='font-family:Calibri; | |
| 3 | font-size:11.0pt'>Left</span><span style='font-weight:bold;font-family:Georgia; | |
| 4 | font-size:18.0pt;color:#123456'>Right </span><span style='font-weight:bold; | |
| 5 | font-style:italic;font-family:Georgia;font-size:18.0pt;color:#123456'>italic</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:07:15.000Z" lastModifiedTime="2026-09-08T09:07:15.000Z" objectID="{25B9CE5D-4971-483B-A8B6-0E7C2BBBB841}{54}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/inheritance/native/read/payloads.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../before/read/payloads.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/inheritance/native/run.json created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "notebook": "/Users/clo/dev/one/evidence/m10/paragraph-rust-join-01/inheritance/candidate", | |
| 3 | "expected_pages": 6, | |
| 4 | "author": null, | |
| 5 | "author_timeout_seconds": 600, | |
| 6 | "inspect": false, | |
| 7 | "collect_notebook": true, | |
| 8 | "base": { | |
| 9 | "file": "win7-office-base.qcow2", | |
| 10 | "format": "qcow2", | |
| 11 | "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", | |
| 12 | "virtual_size": 68719476736 | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", | |
| 16 | "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" | |
| 17 | } | |
| 18 | } |
corpus/paragraph-edit/rust-join/inheritance/native/source.json created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "path": "synthetic.one", | |
| 4 | "bytes": 56344, | |
| 5 | "sha256": "50318b4b3d4f4850dd578518eea4cc7649d29cc0c84b0d6a9b41811f91e6e2e1", | |
| 6 | "mtime_ns": 1788860404399203298 | |
| 7 | } | |
| 8 | ] |
corpus/paragraph-edit/rust-join/inheritance/native/teardown.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../join-edges/teardown.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/split/candidate/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/split/candidate/synthetic.one differ |
corpus/paragraph-edit/rust-join/split/manifest.json created+110| ... | ... | @@ -0,0 +1,110 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "case": "Split middle", | |
| 4 | "intent": { | |
| 5 | "author": "Rust join author", | |
| 6 | "left": "{DFE950EF-6BEB-0F5B-2403-7B74F1AE5002},44", | |
| 7 | "right": "{0BA342F5-0690-087C-24FE-8EE6D81C2041},12" | |
| 8 | }, | |
| 9 | "space": "{FE224563-B517-023E-0D75-9904B1952A26},1" | |
| 10 | }, | |
| 11 | { | |
| 12 | "case": "Split style boundary", | |
| 13 | "intent": { | |
| 14 | "author": "Rust join author", | |
| 15 | "left": "{5FB484E2-1E3E-0A9D-1989-271A3EBBEC42},44", | |
| 16 | "right": "{051BC1DE-5AE6-0D04-0CEF-9D05F53C8EAE},12" | |
| 17 | }, | |
| 18 | "space": "{DED48A05-0DB1-0712-086D-09237C982341},1" | |
| 19 | }, | |
| 20 | { | |
| 21 | "case": "Split start", | |
| 22 | "intent": { | |
| 23 | "author": "Rust join author", | |
| 24 | "left": "{012ED94B-43B3-079A-3050-B1CBE25C1C09},44", | |
| 25 | "right": "{B230A510-9428-0CE7-1B89-7CA970F8F676},12" | |
| 26 | }, | |
| 27 | "space": "{7919E933-0809-00D8-1754-A21684215860},1" | |
| 28 | }, | |
| 29 | { | |
| 30 | "case": "Split end", | |
| 31 | "intent": { | |
| 32 | "author": "Rust join author", | |
| 33 | "left": "{DAF723A3-C2A3-0798-2EAB-34613166A794},44", | |
| 34 | "right": "{601DAE0A-2A53-0C28-3077-1FE8FCB26FC2},12" | |
| 35 | }, | |
| 36 | "space": "{927F2809-173C-036E-37B6-60A6568F7723},1" | |
| 37 | }, | |
| 38 | { | |
| 39 | "case": "Split empty", | |
| 40 | "intent": { | |
| 41 | "author": "Rust join author", | |
| 42 | "left": "{0B467F31-D287-08CD-09F4-A6FBB2F5418F},45", | |
| 43 | "right": "{EACB4A2C-2444-0A1F-03D6-68187F2FB345},12" | |
| 44 | }, | |
| 45 | "space": "{E4D8C4CC-508B-04D2-36CE-C5B8C9D2FEC4},1" | |
| 46 | }, | |
| 47 | { | |
| 48 | "case": "Split parent", | |
| 49 | "intent": { | |
| 50 | "author": "Rust join author", | |
| 51 | "left": "{3860EA13-D444-0598-35D6-CF96769C6A21},44", | |
| 52 | "right": "{C84CA5DB-61A8-0F9B-3F91-D6E0C74536F7},12" | |
| 53 | }, | |
| 54 | "space": "{7A363D4C-73C2-0ABE-0931-E6F62A90B95E},1" | |
| 55 | }, | |
| 56 | { | |
| 57 | "case": "Split child", | |
| 58 | "intent": { | |
| 59 | "author": "Rust join author", | |
| 60 | "left": "{B587CBC6-0BCC-0A23-16F6-E1B0B3C5D061},49", | |
| 61 | "right": "{DC240C63-FC8E-0267-0AFD-A53062C25702},12" | |
| 62 | }, | |
| 63 | "space": "{3CC2C4DC-651A-07C9-2F7B-375C6663F3CA},1" | |
| 64 | }, | |
| 65 | { | |
| 66 | "case": "Split bullet", | |
| 67 | "intent": { | |
| 68 | "author": "Rust join author", | |
| 69 | "left": "{8C65E427-477C-02ED-1807-2F10E277019D},44", | |
| 70 | "right": "{28F3F5EC-E687-02CA-2CFD-E73A184B875F},12" | |
| 71 | }, | |
| 72 | "space": "{942EF5E4-FFD1-0FE4-23D2-7429899577BD},1" | |
| 73 | }, | |
| 74 | { | |
| 75 | "case": "Split number", | |
| 76 | "intent": { | |
| 77 | "author": "Rust join author", | |
| 78 | "left": "{D4ABBE4F-2165-0122-1F9F-ACE3FE92C703},44", | |
| 79 | "right": "{DD3FA0E7-15A6-01C4-1B77-F85F779BCBC8},12" | |
| 80 | }, | |
| 81 | "space": "{A4AE40FF-DD80-0003-2856-17D8295C1B91},1" | |
| 82 | }, | |
| 83 | { | |
| 84 | "case": "Split tag", | |
| 85 | "intent": { | |
| 86 | "author": "Rust join author", | |
| 87 | "left": "{37AA5877-2980-0227-169F-8B1E8EE0795A},44", | |
| 88 | "right": "{1480427A-E9ED-04EC-0409-0653AE11FF78},12" | |
| 89 | }, | |
| 90 | "space": "{739DB38B-4239-0A7C-2652-4E525D219638},1" | |
| 91 | }, | |
| 92 | { | |
| 93 | "case": "Split cell", | |
| 94 | "intent": { | |
| 95 | "author": "Rust join author", | |
| 96 | "left": "{F0832BC9-1578-0A96-08AF-99344644FFB8},50", | |
| 97 | "right": "{1E353497-5C26-0459-3C0C-0E8D29ED8C5A},12" | |
| 98 | }, | |
| 99 | "space": "{4432E3EC-5E5F-0A04-2108-E9A2C17BDBEC},1" | |
| 100 | }, | |
| 101 | { | |
| 102 | "case": "Split soft break", | |
| 103 | "intent": { | |
| 104 | "author": "Rust join author", | |
| 105 | "left": "{88189BA1-2F19-0282-05ED-A0B83153F143},44", | |
| 106 | "right": "{C1568E82-003F-016B-041A-CA304E1A5138},12" | |
| 107 | }, | |
| 108 | "space": "{D8E15673-A45E-0914-291A-7767C3222F19},1" | |
| 109 | } | |
| 110 | ] | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/split/native/notebook/Open Notebook.onetoc2 created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/split/native/notebook/Open Notebook.onetoc2 differ |
corpus/paragraph-edit/rust-join/split/native/notebook/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/split/native/notebook/synthetic.one differ |
corpus/paragraph-edit/rust-join/split/native/read/environment.json created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | { | |
| 2 | "powershell": "5.1.14409.1005", | |
| 3 | "schema": "xs2010", | |
| 4 | "hostname": "ONE-M6-6A0771AD", | |
| 5 | "cold": true, | |
| 6 | "onenote": "14.0.4763.1000" | |
| 7 | } |
corpus/paragraph-edit/rust-join/split/native/read/hierarchy.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Notebook xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" name="notebook" nickname="notebook" ID="{B7838A92-CCAA-444E-9E8A-6B8769364D70}{1}{B0}" path="C:\one-tests\runs\capture\notebook" lastModifiedTime="2026-09-08T09:41:44.000Z" color="#B49EDE"><one:Section name="synthetic" ID="{00520639-1FE0-0064-1EE3-45C1B4C10B45}{1}{B0}" path="C:\one-tests\runs\capture\notebook\synthetic.one" lastModifiedTime="2026-09-08T09:41:44.000Z" color="#8AA8E4"><one:Page ID="{2C1BF5BF-962B-4375-9984-ED5048F9175A}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:44.000Z" pageLevel="1"/><one:Page ID="{B6218E28-6D25-4DF5-A9CA-FCABEEB9B660}{1}{B0}" name="Split middle" dateTime="2026-09-08T08:09:29.000Z" lastModifiedTime="2026-09-08T08:11:45.000Z" pageLevel="1"/><one:Page ID="{96D7414E-D583-48D9-ACD2-6C8C23B4BF07}{1}{B0}" name="Split style boundary" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:47.000Z" pageLevel="1"/><one:Page ID="{311A2278-D03B-4F13-B3EB-C7B9DB0DC426}{1}{B0}" name="Split start" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:49.000Z" pageLevel="1"/><one:Page ID="{DA7CE342-CF0E-4CA5-9309-050909A3EB65}{1}{B0}" name="Split end" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:51.000Z" pageLevel="1"/><one:Page ID="{ACDB0F87-88B9-4B19-9271-A01796FE6282}{1}{B0}" name="Split empty" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:53.000Z" pageLevel="1"/><one:Page ID="{3235F607-ABF0-4575-AD8E-835975BC2518}{1}{B0}" name="Split parent" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:55.000Z" pageLevel="1"/><one:Page ID="{74C10F97-BD28-4802-8BC4-52F3394F6F8C}{1}{B0}" name="Split child" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:57.000Z" pageLevel="1"/><one:Page ID="{DC2D3EAF-27E3-402F-876D-1186D6B9EBFB}{1}{B0}" name="Split bullet" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:59.000Z" pageLevel="1"/><one:Page ID="{ECAD8BB4-05B2-4FC8-8CE9-7277767087D7}{1}{B0}" name="Split number" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:01.000Z" pageLevel="1"/><one:Page ID="{3B9E78C0-9A0B-45B7-82ED-2BFD020D0A7E}{1}{B0}" name="Split tag" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:03.000Z" pageLevel="1"/><one:Page ID="{0C3128A7-866D-45CF-85B7-8C0D9E5747AA}{1}{B0}" name="Split cell" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:05.000Z" pageLevel="1"/><one:Page ID="{90E29D38-7C6C-46DF-8DA5-12C89C0EB35F}{1}{B0}" name="Split soft break" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:07.000Z" pageLevel="1"/><one:Page ID="{3AC29ACF-40BB-4891-82DA-E37311CB08E1}{1}{B0}" name="Split before hyperlink" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:09.000Z" pageLevel="1"/></one:Section></one:Notebook> |
corpus/paragraph-edit/rust-join/split/native/read/page-000.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{0C3128A7-866D-45CF-85B7-8C0D9E5747AA}{1}{B0}" name="Split cell" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:05.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split cell]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="254.9588165283203" height="35.37543487548828"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{40}{B0}" alignment="left"><one:Table bordersVisible="true" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{41}{B0}"><one:Columns><one:Column index="0" width="180.0" isLocked="true"/><one:Column index="1" width="66.82881927490234"/></one:Columns><one:Row objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{42}{B0}" lastModifiedTime="2026-09-08T09:40:04.000Z"><one:Cell lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{43}{B0}"><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{46}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE></one:OEChildren></one:Cell><one:Cell lastModifiedTime="2026-09-08T08:12:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{54}{B0}" lastModifiedBy="virtual" lastModifiedByInitials="V"><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{57}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved cell]]></one:T></one:OE></one:OEChildren></one:Cell></one:Row></one:Table></one:OE><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:04.000Z" objectID="{B880E082-CD4A-455D-AC10-FC9B196863FE}{62}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-001.xml created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{2C1BF5BF-962B-4375-9984-ED5048F9175A}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:44.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Outline author="Rich author" lastModifiedBy="Rich author" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{4BD75D03-DD5E-40C3-9676-618BB765F23B}{10}{B0}"><one:Position x="36.0" y="14.40000057220459" z="0"/><one:Size width="127.5136947631836" height="42.90299224853515"/><one:OEChildren><one:OE creationTime="2026-09-08T04:47:24.000Z" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{4CB2F5F4-2E23-0BC9-2622-A2817C0E80CD}{1}{B0}" alignment="left"><one:T><![CDATA[<span style='font-weight:bold; | |
| 3 | text-decoration:underline;font-family:Calibri;font-size:11.0pt'>Bold</span><span | |
| 4 | style='font-family:Calibri;font-size:11.0pt'> </span><span style='font-family: | |
| 5 | Arial;font-size:13.5pt'>&#129408;</span><span style='font-family:Calibri; | |
| 6 | font-size:11.0pt'> </span><span style='font-style:italic;text-decoration:line-through; | |
| 7 | font-family:Calibri;font-size:11.0pt'>italic</span><span style='font-family: | |
| 8 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:super;font-family: | |
| 9 | Calibri;font-size:11.0pt'>é</span><span style='font-family:Calibri;font-size: | |
| 10 | 11.0pt'> </span><span style='font-family:Calibri;font-size:11.0pt;color:#0C2238; | |
| 11 | background:yellow;mso-highlight:yellow'>color</span><span style='font-family: | |
| 12 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:sub;font-family: | |
| 13 | Calibri;font-size:11.0pt'>東京</span><span style='font-family:Calibri;font-size: | |
| 14 | 11.0pt'><br> | |
| 15 | End</span>]]></one:T></one:OE><one:OE author="snow" authorInitials="S" lastModifiedBy="snow" lastModifiedByInitials="S" creationTime="2026-09-05T05:06:55.000Z" lastModifiedTime="2026-09-05T05:06:56.000Z" objectID="{4BD75D03-DD5E-40C3-9676-618BB765F23B}{11}{B0}" alignment="left" quickStyleIndex="0" style="font-size:11.0pt;color:#1F4E79"><one:T><![CDATA[<span | |
| 16 | style='font-weight:bold;font-family:Calibri' lang=en-US>Fictitious: café, </span><span | |
| 17 | style='font-weight:bold;font-family:SimSun' lang=en-US>東京</span><span | |
| 18 | style='font-weight:bold;font-family:Calibri' lang=en-US>, </span><span | |
| 19 | style='font-weight:bold;font-family:Arial;direction:rtl;unicode-bidi:embed' | |
| 20 | lang=ar-SA>مرحبا</span>]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-002.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{311A2278-D03B-4F13-B3EB-C7B9DB0DC426}{1}{B0}" name="Split start" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:49.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:48.000Z" objectID="{492D1200-9B81-4851-94EF-D464BD70804F}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split start]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{492D1200-9B81-4851-94EF-D464BD70804F}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{492D1200-9B81-4851-94EF-D464BD70804F}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:48.000Z" objectID="{492D1200-9B81-4851-94EF-D464BD70804F}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-003.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{3235F607-ABF0-4575-AD8E-835975BC2518}{1}{B0}" name="Split parent" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:55.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:54.000Z" objectID="{70632158-0C76-4A53-9169-AA3929B0F667}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split parent]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{70632158-0C76-4A53-9169-AA3929B0F667}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="135.3173980712891" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{70632158-0C76-4A53-9169-AA3929B0F667}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T><one:OEChildren><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:54.000Z" objectID="{70632158-0C76-4A53-9169-AA3929B0F667}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved child]]></one:T></one:OE></one:OEChildren></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:54.000Z" objectID="{70632158-0C76-4A53-9169-AA3929B0F667}{53}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-004.xml created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{3AC29ACF-40BB-4891-82DA-E37311CB08E1}{1}{B0}" name="Split before hyperlink" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:09.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:08.000Z" objectID="{1C30D2AC-82F5-45BF-B455-5C48F6664CC0}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split before hyperlink]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T08:12:09.000Z" objectID="{1C30D2AC-82F5-45BF-B455-5C48F6664CC0}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="119.1777801513672" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:09.000Z" objectID="{1C30D2AC-82F5-45BF-B455-5C48F6664CC0}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Be]]></one:T></one:OE><one:OE creationTime="2026-09-08T08:12:09.000Z" lastModifiedTime="2026-09-08T08:12:09.000Z" objectID="{66B1C2E6-94CC-43F9-80CF-15B172F28E66}{11}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[fore <a | |
| 3 | href="https://example.invalid/paragraph">Link label</a> tail]]></one:T></one:OE><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:08.000Z" objectID="{1C30D2AC-82F5-45BF-B455-5C48F6664CC0}{47}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-005.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{3B9E78C0-9A0B-45B7-82ED-2BFD020D0A7E}{1}{B0}" name="Split tag" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:03.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:02.000Z" objectID="{7FA9933C-F1B2-4DEC-B220-EEB1D1CCE51C}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{7FA9933C-F1B2-4DEC-B220-EEB1D1CCE51C}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{7FA9933C-F1B2-4DEC-B220-EEB1D1CCE51C}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:02.000Z" objectID="{7FA9933C-F1B2-4DEC-B220-EEB1D1CCE51C}{49}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-006.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{74C10F97-BD28-4802-8BC4-52F3394F6F8C}{1}{B0}" name="Split child" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:57.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:56.000Z" objectID="{FD84008D-D3FE-45E8-B249-841FECE94C27}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split child]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{FD84008D-D3FE-45E8-B249-841FECE94C27}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="143.921875" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{FD84008D-D3FE-45E8-B249-841FECE94C27}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved parent]]></one:T><one:OEChildren><one:OE lastModifiedBy="Rust join author" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{FD84008D-D3FE-45E8-B249-841FECE94C27}{45}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE></one:OEChildren></one:OE><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:56.000Z" objectID="{FD84008D-D3FE-45E8-B249-841FECE94C27}{53}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-007.xml created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{90E29D38-7C6C-46DF-8DA5-12C89C0EB35F}{1}{B0}" name="Split soft break" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:07.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:06.000Z" objectID="{C01B50EA-F72B-4D49-A152-C5176E7F6D05}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split soft break]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{C01B50EA-F72B-4D49-A152-C5176E7F6D05}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="487.125" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T08:12:07.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{C01B50EA-F72B-4D49-A152-C5176E7F6D05}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Bold<br> | |
| 3 | soft break]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:06.000Z" objectID="{C01B50EA-F72B-4D49-A152-C5176E7F6D05}{45}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-008.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{96D7414E-D583-48D9-ACD2-6C8C23B4BF07}{1}{B0}" name="Split style boundary" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:47.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:46.000Z" objectID="{17B74FA9-C60C-4556-BD36-42B561977004}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split style boundary]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{17B74FA9-C60C-4556-BD36-42B561977004}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{17B74FA9-C60C-4556-BD36-42B561977004}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:46.000Z" objectID="{17B74FA9-C60C-4556-BD36-42B561977004}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-009.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{ACDB0F87-88B9-4B19-9271-A01796FE6282}{1}{B0}" name="Split empty" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:53.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:52.000Z" objectID="{4345B47A-0AB5-4706-AD4B-C354EDD9DDC9}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split empty]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{4345B47A-0AB5-4706-AD4B-C354EDD9DDC9}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="115.6704635620117" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{4345B47A-0AB5-4706-AD4B-C354EDD9DDC9}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:52.000Z" objectID="{4345B47A-0AB5-4706-AD4B-C354EDD9DDC9}{46}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-010.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{B6218E28-6D25-4DF5-A9CA-FCABEEB9B660}{1}{B0}" name="Split middle" dateTime="2026-09-08T08:09:29.000Z" lastModifiedTime="2026-09-08T08:11:45.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:09:32.000Z" objectID="{97EA9BA4-B3D9-4090-80BC-1EDBAE82CC44}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split middle]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{97EA9BA4-B3D9-4090-80BC-1EDBAE82CC44}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:30.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{97EA9BA4-B3D9-4090-80BC-1EDBAE82CC44}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:09:32.000Z" objectID="{97EA9BA4-B3D9-4090-80BC-1EDBAE82CC44}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-011.xml created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{DA7CE342-CF0E-4CA5-9309-050909A3EB65}{1}{B0}" name="Split end" dateTime="2026-09-08T08:09:32.000Z" lastModifiedTime="2026-09-08T08:11:51.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:50.000Z" objectID="{92F4E8E8-1A91-4853-8A14-51CE6E4A3BD2}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split end]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{92F4E8E8-1A91-4853-8A14-51CE6E4A3BD2}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{92F4E8E8-1A91-4853-8A14-51CE6E4A3BD2}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tai</span><span style='color:#123456' | |
| 6 | lang=en-US>l</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:50.000Z" objectID="{92F4E8E8-1A91-4853-8A14-51CE6E4A3BD2}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-012.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{DC2D3EAF-27E3-402F-876D-1186D6B9EBFB}{1}{B0}" name="Split bullet" dateTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:59.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:58.000Z" objectID="{C4662F6C-9F4E-4D26-BCB8-4ABFBD5B9DDB}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split bullet]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{C4662F6C-9F4E-4D26-BCB8-4ABFBD5B9DDB}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="135.3173980712891" height="40.28315734863281"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{C4662F6C-9F4E-4D26-BCB8-4ABFBD5B9DDB}{40}{B0}" alignment="left" quickStyleIndex="0"><one:List><one:Bullet bullet="3" fontSize="11.0"/></one:List><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T><one:OEChildren><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:58.000Z" objectID="{C4662F6C-9F4E-4D26-BCB8-4ABFBD5B9DDB}{48}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved child]]></one:T></one:OE></one:OEChildren></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:33.000Z" lastModifiedTime="2026-09-08T08:11:58.000Z" objectID="{C4662F6C-9F4E-4D26-BCB8-4ABFBD5B9DDB}{54}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/page-013.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{ECAD8BB4-05B2-4FC8-8CE9-7277767087D7}{1}{B0}" name="Split number" dateTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:01.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:00.000Z" objectID="{9CA87504-F957-4EE9-BB20-C94CA1BE5B45}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Split number]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{9CA87504-F957-4EE9-BB20-C94CA1BE5B45}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{9CA87504-F957-4EE9-BB20-C94CA1BE5B45}{40}{B0}" alignment="left" quickStyleIndex="0"><one:List><one:Number numberSequence="1" numberFormat="##." restartNumberingAt="3" fontSize="11.0" font="Calibri" text="III."/></one:List><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T08:09:34.000Z" lastModifiedTime="2026-09-08T08:12:00.000Z" objectID="{9CA87504-F957-4EE9-BB20-C94CA1BE5B45}{49}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/split/native/read/payloads.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../before/read/payloads.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/split/native/run.json created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "notebook": "/Users/clo/dev/one/evidence/m10/paragraph-rust-join-01/split/candidate", | |
| 3 | "expected_pages": 14, | |
| 4 | "author": null, | |
| 5 | "author_timeout_seconds": 600, | |
| 6 | "inspect": false, | |
| 7 | "collect_notebook": true, | |
| 8 | "base": { | |
| 9 | "file": "win7-office-base.qcow2", | |
| 10 | "format": "qcow2", | |
| 11 | "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", | |
| 12 | "virtual_size": 68719476736 | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", | |
| 16 | "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" | |
| 17 | } | |
| 18 | } |
corpus/paragraph-edit/rust-join/split/native/source.json created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "path": "synthetic.one", | |
| 4 | "bytes": 139752, | |
| 5 | "sha256": "6677af274e17de1ac194b2b18bc96d3eb33c04968881c272383f7cc47b10c5de", | |
| 6 | "mtime_ns": 1788860404345967341 | |
| 7 | } | |
| 8 | ] |
corpus/paragraph-edit/rust-join/split/native/teardown.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../join-edges/teardown.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/tags/candidate/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/tags/candidate/synthetic.one differ |
corpus/paragraph-edit/rust-join/tags/manifest.json created+29| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "case": "Join empty left right tag", | |
| 4 | "intent": { | |
| 5 | "author": "Rust join author", | |
| 6 | "left": "{0D11242E-6A38-0F74-261A-A6FC444FA262},45", | |
| 7 | "right": "{0D11242E-6A38-0F74-261A-A6FC444FA262},50" | |
| 8 | }, | |
| 9 | "space": "{FC64EF0A-5CCB-0557-20E0-6C847015B691},1" | |
| 10 | }, | |
| 11 | { | |
| 12 | "case": "Join empty left two tags", | |
| 13 | "intent": { | |
| 14 | "author": "Rust join author", | |
| 15 | "left": "{84061027-77E3-03F1-3E0D-A8F744B19C18},45", | |
| 16 | "right": "{84061027-77E3-03F1-3E0D-A8F744B19C18},51" | |
| 17 | }, | |
| 18 | "space": "{A05CB25D-C95D-043F-3B9A-CE333AD171AD},1" | |
| 19 | }, | |
| 20 | { | |
| 21 | "case": "Join different tags", | |
| 22 | "intent": { | |
| 23 | "author": "Rust join author", | |
| 24 | "left": "{4FBF7D4A-6F4D-048E-0913-5BD9A184C25B},44", | |
| 25 | "right": "{4FBF7D4A-6F4D-048E-0913-5BD9A184C25B},50" | |
| 26 | }, | |
| 27 | "space": "{6F0F6556-17D3-03AF-20EF-C465A856A153},1" | |
| 28 | } | |
| 29 | ] | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/tags/native/notebook/Open Notebook.onetoc2 created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/tags/native/notebook/Open Notebook.onetoc2 differ |
corpus/paragraph-edit/rust-join/tags/native/notebook/synthetic.one created| Binary files /dev/null and b/corpus/paragraph-edit/rust-join/tags/native/notebook/synthetic.one differ |
corpus/paragraph-edit/rust-join/tags/native/read/environment.json created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | { | |
| 2 | "powershell": "5.1.14409.1005", | |
| 3 | "schema": "xs2010", | |
| 4 | "hostname": "ONE-M6-5FECAC90", | |
| 5 | "cold": true, | |
| 6 | "onenote": "14.0.4763.1000" | |
| 7 | } |
corpus/paragraph-edit/rust-join/tags/native/read/hierarchy.xml created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Notebook xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" name="notebook" nickname="notebook" ID="{37CBDE5C-07A2-48D1-98C7-0E523CC21B7B}{1}{B0}" path="C:\one-tests\runs\capture\notebook" lastModifiedTime="2026-09-08T09:41:49.000Z" color="#B49EDE"><one:Section name="synthetic" ID="{7BFF2432-8C29-0E59-15D1-59F0215B7D6A}{1}{B0}" path="C:\one-tests\runs\capture\notebook\synthetic.one" lastModifiedTime="2026-09-08T09:41:49.000Z" color="#8AA8E4"><one:Page ID="{57B6D7B4-05E2-4D48-92B6-F161DD636175}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1"/><one:Page ID="{CFCA064A-1730-44A1-8F6D-151ABAA35CF8}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1"/><one:Page ID="{93F25B1D-82A6-45C9-9417-B7ADF0679BC4}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1"/><one:Page ID="{5CA18C16-5C28-4259-8F62-BDFB62E04B3A}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1"/></one:Section></one:Notebook> |
corpus/paragraph-edit/rust-join/tags/native/read/page-000.xml created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{57B6D7B4-05E2-4D48-92B6-F161DD636175}{1}{B0}" name="Bold 🦀 italic é color 東京" dateTime="2026-09-05T05:06:54.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Outline author="Rich author" lastModifiedBy="Rich author" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{307A7F08-4E97-4EFE-9D44-7DBA22FF8414}{10}{B0}"><one:Position x="36.0" y="14.40000057220459" z="0"/><one:Size width="127.5136947631836" height="42.90299224853515"/><one:OEChildren><one:OE creationTime="2026-09-08T04:47:24.000Z" lastModifiedTime="2026-09-08T04:47:24.000Z" objectID="{371FD7FF-BDEA-05F4-2D10-BEB0E994F6E2}{1}{B0}" alignment="left"><one:T><![CDATA[<span style='font-weight:bold; | |
| 3 | text-decoration:underline;font-family:Calibri;font-size:11.0pt'>Bold</span><span | |
| 4 | style='font-family:Calibri;font-size:11.0pt'> </span><span style='font-family: | |
| 5 | Arial;font-size:13.5pt'>&#129408;</span><span style='font-family:Calibri; | |
| 6 | font-size:11.0pt'> </span><span style='font-style:italic;text-decoration:line-through; | |
| 7 | font-family:Calibri;font-size:11.0pt'>italic</span><span style='font-family: | |
| 8 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:super;font-family: | |
| 9 | Calibri;font-size:11.0pt'>é</span><span style='font-family:Calibri;font-size: | |
| 10 | 11.0pt'> </span><span style='font-family:Calibri;font-size:11.0pt;color:#0C2238; | |
| 11 | background:yellow;mso-highlight:yellow'>color</span><span style='font-family: | |
| 12 | Calibri;font-size:11.0pt'> </span><span style='vertical-align:sub;font-family: | |
| 13 | Calibri;font-size:11.0pt'>東京</span><span style='font-family:Calibri;font-size: | |
| 14 | 11.0pt'><br> | |
| 15 | End</span>]]></one:T></one:OE><one:OE author="snow" authorInitials="S" lastModifiedBy="snow" lastModifiedByInitials="S" creationTime="2026-09-05T05:06:55.000Z" lastModifiedTime="2026-09-05T05:06:56.000Z" objectID="{307A7F08-4E97-4EFE-9D44-7DBA22FF8414}{11}{B0}" alignment="left" quickStyleIndex="0" style="font-size:11.0pt;color:#1F4E79"><one:T><![CDATA[<span | |
| 16 | style='font-weight:bold;font-family:Calibri' lang=en-US>Fictitious: café, </span><span | |
| 17 | style='font-weight:bold;font-family:SimSun' lang=en-US>東京</span><span | |
| 18 | style='font-weight:bold;font-family:Calibri' lang=en-US>, </span><span | |
| 19 | style='font-weight:bold;font-family:Arial;direction:rtl;unicode-bidi:embed' | |
| 20 | lang=ar-SA>مرحبا</span>]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/tags/native/read/page-001.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{5CA18C16-5C28-4259-8F62-BDFB62E04B3A}{1}{B0}" name="Join different tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{7C11940A-24B6-4578-A69E-22476B322832}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join different tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{7C11940A-24B6-4578-A69E-22476B322832}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="134.0610046386719" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{7C11940A-24B6-4578-A69E-22476B322832}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | lang=en-US>Left</span><span style='font-weight:bold' lang=en-US>Bold</span><span | |
| 4 | lang=en-US> </span><span style='font-style:italic' lang=en-US>italic</span><span | |
| 5 | lang=en-US> &#129408; é </span><span style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{7C11940A-24B6-4578-A69E-22476B322832}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/tags/native/read/page-002.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{93F25B1D-82A6-45C9-9417-B7ADF0679BC4}{1}{B0}" name="Join empty left two tags" dateTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1" lang="en-US"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{B7A8F967-3C18-4207-9180-D1698E077671}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left two tags]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B7A8F967-3C18-4207-9180-D1698E077671}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.9218902587891" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{B7A8F967-3C18-4207-9180-D1698E077671}{40}{B0}" alignment="left" quickStyleIndex="0"><one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:04.000Z" lastModifiedTime="2026-09-08T09:33:04.000Z" objectID="{B7A8F967-3C18-4207-9180-D1698E077671}{56}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/tags/native/read/page-003.xml created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | <?xml version="1.0"?> | |
| 2 | <one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="{CFCA064A-1730-44A1-8F6D-151ABAA35CF8}{1}{B0}" name="Join empty left right tag" dateTime="2026-09-08T09:33:01.000Z" lastModifiedTime="2026-09-08T09:41:49.000Z" pageLevel="1" lang="en-US"><one:QuickStyleDef index="0" name="p" fontColor="automatic" highlightColor="automatic" font="Calibri" fontSize="11.0" spaceBefore="0.0" spaceAfter="0.0"/><one:PageSettings RTL="false" color="automatic"><one:PageSize><one:Automatic/></one:PageSize><one:RuleLines visible="false"/></one:PageSettings><one:Title lang="en-US"><one:OE author="virtual" authorInitials="V" lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{3EBFCD6E-21C3-4E82-8997-DF628EF9480B}{33}{B0}" alignment="left" quickStyleIndex="0" style="font-family:Calibri;font-size:17.0pt"><one:T><![CDATA[Join empty left right tag]]></one:T></one:OE></one:Title><one:Outline author="virtual" authorInitials="V" lastModifiedBy="Rust join author" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{3EBFCD6E-21C3-4E82-8997-DF628EF9480B}{39}{B0}"><one:Position x="72.0" y="108.0" z="0"/><one:Size width="116.921875" height="26.85543441772461"/><one:OEChildren><one:OE creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:40:04.000Z" objectID="{3EBFCD6E-21C3-4E82-8997-DF628EF9480B}{40}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[<span | |
| 3 | style='font-weight:bold' lang=en-US>Bold</span><span lang=en-US> </span><span | |
| 4 | style='font-style:italic' lang=en-US>italic</span><span lang=en-US> &#129408; é </span><span | |
| 5 | style='color:#123456' lang=x-none>tail</span>]]></one:T></one:OE><one:OE lastModifiedBy="virtual" lastModifiedByInitials="V" creationTime="2026-09-08T09:33:03.000Z" lastModifiedTime="2026-09-08T09:33:03.000Z" objectID="{3EBFCD6E-21C3-4E82-8997-DF628EF9480B}{55}{B0}" alignment="left" quickStyleIndex="0"><one:T><![CDATA[Preserved sibling]]></one:T></one:OE></one:OEChildren></one:Outline></one:Page> |
corpus/paragraph-edit/rust-join/tags/native/read/payloads.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../../before/read/payloads.json | |
| \ No newline at end of file |
corpus/paragraph-edit/rust-join/tags/native/run.json created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | { | |
| 2 | "notebook": "/Users/clo/dev/one/evidence/m10/paragraph-rust-join-01/tags/candidate", | |
| 3 | "expected_pages": 4, | |
| 4 | "author": null, | |
| 5 | "author_timeout_seconds": 600, | |
| 6 | "inspect": false, | |
| 7 | "collect_notebook": true, | |
| 8 | "base": { | |
| 9 | "file": "win7-office-base.qcow2", | |
| 10 | "format": "qcow2", | |
| 11 | "sha256": "a1a4f8fab782ee14885ff801ca2f6347c208fdcfc3513637096f314315c89346", | |
| 12 | "virtual_size": 68719476736 | |
| 13 | }, | |
| 14 | "scripts": { | |
| 15 | "cold.ps1": "c177fc72ae6c2634d186f5671a880de20b09f9716532c37c69cb13aa15c7e331", | |
| 16 | "read.ps1": "04013bfcccee40a17e2a225f9b9e96f40a3a8daad9e350609654f8eda3ccbb41" | |
| 17 | } | |
| 18 | } |
corpus/paragraph-edit/rust-join/tags/native/source.json created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | [ | |
| 2 | { | |
| 3 | "path": "synthetic.one", | |
| 4 | "bytes": 41808, | |
| 5 | "sha256": "04f5dd05df411fdb228ff7cb80e02ca24744d40b6f0bd485b688aa8c0d6e8ac3", | |
| 6 | "mtime_ns": 1788860404422342545 | |
| 7 | } | |
| 8 | ] |
corpus/paragraph-edit/rust-join/tags/native/teardown.json created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | ../../../join-edges/teardown.json | |
| \ No newline at end of file |
crates/onestore/README.md+9| ... | ... | @@ -50,6 +50,7 @@ harness also accepts `--client-profile release`. |
| 50 | 50 | | `replace_text`, `commit_text`, `commit_file_text` | Replace a UTF-16 range across ordinary text runs; publish text, run boundaries and modification time together | |
| 51 | 51 | | `Insertion`, `PreparedEdit::insert` | Insert paragraphs into editable containers or positioned outlines into a page, retaining intent identities across rebases | |
| 52 | 52 | | `ParagraphSplit`, `PreparedEdit::split` | Split ordinary text at a UTF-16 scalar boundary, retaining the original left identities and moving children to the right | |
| 53 | | `ParagraphJoin`, `PreparedEdit::join` | Join adjacent ordinary text while preserving inherited character styles and native text-identity rules | | |
| 53 | 54 | | `TextAttribute`, `PreparedEdit::format` | Change character formatting over a UTF-16 range while sharing immutable styles; preserve unselected runs | |
| 54 | 55 | | `PreparedEdit::commit`, `PreparedEdit::commit_file` | Publish the exact prepared image under caller-held exclusion or the conservative filesystem adapter | |
| 55 | 56 | | `read_file` | Read a snapshot under whole-file exclusion | |
| ... | ... | @@ -85,6 +86,14 @@ the complete child graph and title metadata; repeating an existing identity requ |
| 85 | 86 | reconciliation. Title containers, generated fields, recording-linked text and |
| 86 | 87 | associated run metadata are rejected before I/O. Native split controls and subsequent |
| 87 | 88 | typing checks reside in [the paragraph corpus](../../corpus/paragraph-edit/README.md). |
| 89 | Joins retain the left paragraph. Nonempty left text keeps its identity; empty left | |
| 90 | text adopts the right text identity. **The left tags win: right-side tags are removed | |
| 91 | from active text even when the left text is empty.** History retains the original | |
| 92 | objects. Select the preceding leaf text; where that leaf is deeper than the right | |
| 93 | paragraph, right children move to its ancestor at the right paragraph's level. | |
| 94 | Ambiguous ancestry, unsupported indentation transitions and unknown implicit | |
| 95 | font/language inheritance reject before I/O. This is a logical join, so keyboard | |
| 96 | actions that only change list or indentation state remain separate operations. | |
| 88 | 97 | Generated fields, protected targets and unsupported run-data boundary changes are |
| 89 | 98 | rejected before publication. Local caches expose text, insertion and formatting edits; |
| 90 | 99 | the [document-writer acceptance](../../evidence/MILESTONE9.md#document-writer-and-offline-acceptance) |
crates/onestore/src/commit.rs+12| ... | ... | @@ -183,6 +183,18 @@ pub struct PreparedEdit<'a> { |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | 185 | impl<'a> PreparedEdit<'a> { |
| 186 | /// Joins adjacent ordinary paragraphs with native left-tag and text-identity semantics. | |
| 187 | pub fn join( | |
| 188 | source: &'a [u8], | |
| 189 | space: ExGuid, | |
| 190 | join: &crate::ParagraphJoin, | |
| 191 | ) -> Result<Self, crate::Error> { | |
| 192 | Ok(Self { | |
| 193 | source, | |
| 194 | written: join.apply(source, space)?, | |
| 195 | }) | |
| 196 | } | |
| 197 | ||
| 186 | 198 | /// Splits a paragraph and updates its children, lists, tags and title metadata atomically. |
| 187 | 199 | /// Fields and associated run metadata are rejected before I/O. |
| 188 | 200 | pub fn split( |
crates/onestore/src/lib.rs+1-1| ... | ... | @@ -32,7 +32,7 @@ pub use files::FileDataReference; |
| 32 | 32 | pub use formatting::TextAttribute; |
| 33 | 33 | pub use insertion::Insertion; |
| 34 | 34 | pub use objects::{Object, ObjectData, ObjectReferences, ResolvedRevision}; |
| 35 | pub use paragraph::ParagraphSplit; | |
| 35 | pub use paragraph::{ParagraphJoin, ParagraphSplit}; | |
| 36 | 36 | pub use properties::{IdStream, Property, PropertySets, Value}; |
| 37 | 37 | pub use revisions::{ExGuid, ObjectSpace, Revision, RevisionIndex}; |
| 38 | 38 | pub use snapshot::{read_snapshot, read_storage_snapshot}; |
crates/onestore/src/paragraph.rs+475-76| ... | ... | @@ -76,7 +76,7 @@ impl ParagraphSplit { |
| 76 | 76 | .into_iter() |
| 77 | 77 | .filter_map(|(sid, page)| (sid == space).then_some(page)) |
| 78 | 78 | .collect(); |
| 79 | let [page] = pages.as_slice() else { | |
| 79 | let [_] = pages.as_slice() else { | |
| 80 | 80 | return Err(invalid("Splitting requires a single active page")); |
| 81 | 81 | }; |
| 82 | 82 | let semantic = document |
| ... | ... | @@ -84,7 +84,7 @@ impl ParagraphSplit { |
| 84 | 84 | .remove(&space) |
| 85 | 85 | .ok_or_else(|| invalid("The active page is unavailable"))?; |
| 86 | 86 | let rid = semantic.contexts[&ExGuid::default()]; |
| 87 | let mut view = semantic | |
| 87 | let view = semantic | |
| 88 | 88 | .revisions |
| 89 | 89 | .into_iter() |
| 90 | 90 | .find_map(|(id, view)| (id == rid).then_some(view)) |
| ... | ... | @@ -134,48 +134,9 @@ impl ParagraphSplit { |
| 134 | 134 | } |
| 135 | 135 | pending.extend(parents.get(&id).into_iter().flatten().copied()); |
| 136 | 136 | } |
| 137 | let node = &view.nodes[&self.text]; | |
| 138 | let Kind::RichText { | |
| 139 | text, | |
| 140 | runs, | |
| 141 | boilerplate, | |
| 142 | .. | |
| 143 | } = &node.kind | |
| 144 | else { | |
| 145 | return Err(invalid("Select ordinary paragraph text")); | |
| 146 | }; | |
| 147 | if *boilerplate || !node.media_ids.is_empty() || node.media_time_ms.is_some() { | |
| 148 | return Err(invalid( | |
| 149 | "Generated or recording-linked text cannot be split", | |
| 150 | )); | |
| 151 | } | |
| 152 | for run in view.text_runs(self.text)? { | |
| 153 | if [ | |
| 154 | run.format.hidden, | |
| 155 | run.format.hyperlink, | |
| 156 | run.format.math, | |
| 157 | run.format.embedded_object, | |
| 158 | ] | |
| 159 | .contains(&Some(true)) | |
| 160 | || run.text.contains(['\u{fffc}', '\u{fddf}']) | |
| 161 | { | |
| 162 | return Err(invalid( | |
| 163 | "This paragraph contains a field or embedded object that cannot be split", | |
| 164 | )); | |
| 165 | } | |
| 166 | } | |
| 167 | 137 | let raw = index.resolve(space, rid)?; |
| 168 | let ObjectData::Properties(blob) = raw.objects[&self.text].data else { | |
| 169 | unreachable!() | |
| 170 | }; | |
| 171 | if PropertySets::parse(blob)?.sets[0] | |
| 172 | .iter() | |
| 173 | .any(|p| matches!(p.id, 0x40003499 | 0x24003458)) | |
| 174 | { | |
| 175 | return Err(invalid( | |
| 176 | "This paragraph contains run metadata that cannot be split", | |
| 177 | )); | |
| 178 | } | |
| 138 | let (text, runs) = ordinary_text(&view, &raw, self.text)?; | |
| 139 | let node = &view.nodes[&self.text]; | |
| 179 | 140 | let length = u32::try_from(text.encode_utf16().count()) |
| 180 | 141 | .map_err(|_| invalid("Paragraph exceeds the UTF-16 offset range"))?; |
| 181 | 142 | if self.offset > length || lists.len() > 251 { |
| ... | ... | @@ -286,39 +247,7 @@ impl ParagraphSplit { |
| 286 | 247 | }; |
| 287 | 248 | object.set(&[(0x14001d7a, &modified)])?; |
| 288 | 249 | } |
| 289 | for (id, object) in &changed { | |
| 290 | view.nodes.insert( | |
| 291 | *id, | |
| 292 | Element::parse( | |
| 293 | &Object { | |
| 294 | jcid: object.jcid, | |
| 295 | reference_count: 0, | |
| 296 | data: ObjectData::Properties(&object.bytes), | |
| 297 | global_ids: Arc::clone(&object.global_ids), | |
| 298 | }, | |
| 299 | &store, | |
| 300 | )?, | |
| 301 | ); | |
| 302 | } | |
| 303 | let title = page_title(&view, &pages, None)?; | |
| 304 | drop(view); | |
| 305 | if let Some((_, automatic, title)) = title { | |
| 306 | let metadata = raw | |
| 307 | .roots | |
| 308 | .get(&2) | |
| 309 | .ok_or_else(|| invalid("Page title metadata is unavailable"))?; | |
| 310 | if raw.objects[metadata].jcid != 0x20030 { | |
| 311 | return Err(invalid("Page title metadata is unavailable")); | |
| 312 | } | |
| 313 | let title = string(&title); | |
| 314 | let mut object = PropertyObject::from_object(&raw.objects[metadata])?; | |
| 315 | object.set(&[(0x1c001cf3, &title)])?; | |
| 316 | changed.insert(*metadata, object); | |
| 317 | changed | |
| 318 | .get_mut(page) | |
| 319 | .unwrap() | |
| 320 | .set(&[(0x1c001d3c, if automatic { &title } else { &[0, 0] })])?; | |
| 321 | } | |
| 250 | update_title(&store, &raw, view, &pages, &mut changed)?; | |
| 322 | 251 | write_revision(source, space, |_| Ok(changed)) |
| 323 | 252 | } |
| 324 | 253 | } |
| ... | ... | @@ -381,3 +310,473 @@ fn fragment( |
| 381 | 310 | ])?; |
| 382 | 311 | Ok(object) |
| 383 | 312 | } |
| 313 | ||
| 314 | fn ordinary_text<'a>( | |
| 315 | view: &'a crate::document::Revision<'_>, | |
| 316 | raw: &crate::ResolvedRevision<'_>, | |
| 317 | id: ExGuid, | |
| 318 | ) -> Result<(&'a str, &'a [crate::document::TextRun]), Error> { | |
| 319 | let node = &view.nodes[&id]; | |
| 320 | let Kind::RichText { | |
| 321 | text, | |
| 322 | runs, | |
| 323 | boilerplate, | |
| 324 | .. | |
| 325 | } = &node.kind | |
| 326 | else { | |
| 327 | return Err(invalid("Select ordinary paragraph text")); | |
| 328 | }; | |
| 329 | if *boilerplate || !node.media_ids.is_empty() || node.media_time_ms.is_some() { | |
| 330 | return Err(invalid( | |
| 331 | "Generated or recording-linked text cannot be split or joined", | |
| 332 | )); | |
| 333 | } | |
| 334 | for run in view.text_runs(id)? { | |
| 335 | if [ | |
| 336 | run.format.hidden, | |
| 337 | run.format.hyperlink, | |
| 338 | run.format.math, | |
| 339 | run.format.embedded_object, | |
| 340 | ] | |
| 341 | .contains(&Some(true)) | |
| 342 | || run.text.contains(['\u{fffc}', '\u{fddf}']) | |
| 343 | { | |
| 344 | return Err(invalid( | |
| 345 | "This paragraph contains a field or embedded object that cannot be split or joined", | |
| 346 | )); | |
| 347 | } | |
| 348 | } | |
| 349 | let ObjectData::Properties(blob) = raw.objects[&id].data else { | |
| 350 | unreachable!() | |
| 351 | }; | |
| 352 | if PropertySets::parse(blob)?.sets[0] | |
| 353 | .iter() | |
| 354 | .any(|p| matches!(p.id, 0x40003499 | 0x24003458)) | |
| 355 | { | |
| 356 | return Err(invalid( | |
| 357 | "This paragraph contains run metadata that cannot be split or joined", | |
| 358 | )); | |
| 359 | } | |
| 360 | Ok((text, runs)) | |
| 361 | } | |
| 362 | ||
| 363 | fn update_title( | |
| 364 | store: &Store<'_>, | |
| 365 | raw: &crate::ResolvedRevision<'_>, | |
| 366 | view: crate::document::Revision<'_>, | |
| 367 | pages: &[ExGuid], | |
| 368 | changed: &mut BTreeMap<ExGuid, PropertyObject>, | |
| 369 | ) -> Result<(), Error> { | |
| 370 | // Shorten the moved view's lifetime to the changed property buffers. | |
| 371 | let mut view = view; | |
| 372 | for (id, object) in changed.iter() { | |
| 373 | view.nodes.insert( | |
| 374 | *id, | |
| 375 | Element::parse( | |
| 376 | &Object { | |
| 377 | jcid: object.jcid, | |
| 378 | reference_count: 0, | |
| 379 | data: ObjectData::Properties(&object.bytes), | |
| 380 | global_ids: Arc::clone(&object.global_ids), | |
| 381 | }, | |
| 382 | store, | |
| 383 | )?, | |
| 384 | ); | |
| 385 | } | |
| 386 | let title = page_title(&view, pages, None)?; | |
| 387 | drop(view); | |
| 388 | if let Some((_, automatic, title)) = title { | |
| 389 | let metadata = raw | |
| 390 | .roots | |
| 391 | .get(&2) | |
| 392 | .ok_or_else(|| invalid("Page title metadata is unavailable"))?; | |
| 393 | if raw.objects[metadata].jcid != 0x20030 { | |
| 394 | return Err(invalid("Page title metadata is unavailable")); | |
| 395 | } | |
| 396 | let title = string(&title); | |
| 397 | let mut object = PropertyObject::from_object(&raw.objects[metadata])?; | |
| 398 | object.set(&[(0x1c001cf3, &title)])?; | |
| 399 | changed.insert(*metadata, object); | |
| 400 | changed | |
| 401 | .get_mut(&pages[0]) | |
| 402 | .unwrap() | |
| 403 | .set(&[(0x1c001d3c, if automatic { &title } else { &[0, 0] })])?; | |
| 404 | } | |
| 405 | Ok(()) | |
| 406 | } | |
| 407 | ||
| 408 | /// Joins adjacent ordinary text in one outline or table cell. | |
| 409 | /// The left paragraph survives. Empty left text adopts the right text identity; | |
| 410 | /// otherwise the left text survives. The left paragraph's tags are retained. | |
| 411 | /// Right-side tags are removed from active text, including when the left text is empty. | |
| 412 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | |
| 413 | #[serde(deny_unknown_fields)] | |
| 414 | pub struct ParagraphJoin { | |
| 415 | left: ExGuid, | |
| 416 | right: ExGuid, | |
| 417 | author: String, | |
| 418 | } | |
| 419 | ||
| 420 | impl ParagraphJoin { | |
| 421 | /// Select the preceding leaf paragraph's text and the following paragraph's text. | |
| 422 | pub fn new(left: ExGuid, right: ExGuid, author: &str) -> Result<Self, Error> { | |
| 423 | if left == right || left.guid == [0; 16] || right.guid == [0; 16] || author.contains('\0') { | |
| 424 | return Err(invalid( | |
| 425 | "Choose two distinct text objects and an author name without NUL", | |
| 426 | )); | |
| 427 | } | |
| 428 | Ok(Self { | |
| 429 | left, | |
| 430 | right, | |
| 431 | author: author.to_owned(), | |
| 432 | }) | |
| 433 | } | |
| 434 | ||
| 435 | pub(crate) fn apply(&self, source: &[u8], space: ExGuid) -> Result<Vec<u8>, Error> { | |
| 436 | if self.left == self.right | |
| 437 | || self.left.guid == [0; 16] | |
| 438 | || self.right.guid == [0; 16] | |
| 439 | || self.author.contains('\0') | |
| 440 | { | |
| 441 | return Err(invalid( | |
| 442 | "Choose two distinct text objects and an author name without NUL", | |
| 443 | )); | |
| 444 | } | |
| 445 | let store = Store::parse(source)?; | |
| 446 | let index = RevisionIndex::parse(&store)?; | |
| 447 | index.validate_current()?; | |
| 448 | let mut document = Document::parse(&index)?; | |
| 449 | let pages: Vec<_> = document | |
| 450 | .pages()? | |
| 451 | .into_iter() | |
| 452 | .filter_map(|(sid, page)| (sid == space).then_some(page)) | |
| 453 | .collect(); | |
| 454 | if pages.len() != 1 { | |
| 455 | return Err(invalid("Joining requires a single active page")); | |
| 456 | } | |
| 457 | let semantic = document | |
| 458 | .spaces | |
| 459 | .remove(&space) | |
| 460 | .ok_or_else(|| invalid("The active page is unavailable"))?; | |
| 461 | let rid = semantic.contexts[&ExGuid::default()]; | |
| 462 | let view = semantic | |
| 463 | .revisions | |
| 464 | .into_iter() | |
| 465 | .find_map(|(id, view)| (id == rid).then_some(view)) | |
| 466 | .unwrap(); | |
| 467 | let parents = editable_parents(&view, &pages, self.left)?; | |
| 468 | editable_parents(&view, &pages, self.right)?; | |
| 469 | let parent = |id| -> Result<ExGuid, Error> { | |
| 470 | let [parent] = parents.get(&id).map(Vec::as_slice).unwrap_or_default() else { | |
| 471 | return Err(invalid("Select text with a unique editable parent")); | |
| 472 | }; | |
| 473 | Ok(*parent) | |
| 474 | }; | |
| 475 | let left = parent(self.left)?; | |
| 476 | let right = parent(self.right)?; | |
| 477 | for (paragraph, text) in [(left, self.left), (right, self.right)] { | |
| 478 | if !matches!(view.nodes[&paragraph].kind, Kind::Paragraph { .. }) | |
| 479 | || view.nodes[&paragraph].content != [text] | |
| 480 | { | |
| 481 | return Err(invalid("Select ordinary paragraph text")); | |
| 482 | } | |
| 483 | let mut at = paragraph; | |
| 484 | while at != pages[0] { | |
| 485 | if matches!(view.nodes[&at].kind, Kind::Title) { | |
| 486 | return Err(invalid( | |
| 487 | "Title containers cannot be joined with ordinary paragraphs", | |
| 488 | )); | |
| 489 | } | |
| 490 | at = parent(at)?; | |
| 491 | } | |
| 492 | } | |
| 493 | if !view.nodes[&left].children.is_empty() { | |
| 494 | return Err(invalid("Select the preceding paragraph's last descendant")); | |
| 495 | } | |
| 496 | let right_parent = parent(right)?; | |
| 497 | if !matches!( | |
| 498 | view.nodes[&right_parent].kind, | |
| 499 | Kind::Outline { .. } | Kind::OutlineGroup | Kind::Paragraph { .. } | Kind::Cell { .. } | |
| 500 | ) { | |
| 501 | return Err(invalid("Select paragraphs in one outline or table cell")); | |
| 502 | } | |
| 503 | let mut stem = left; | |
| 504 | while parent(stem)? != right_parent { | |
| 505 | let previous = stem; | |
| 506 | stem = parent(stem)?; | |
| 507 | if !matches!( | |
| 508 | view.nodes[&stem].kind, | |
| 509 | Kind::Paragraph { .. } | Kind::OutlineGroup | |
| 510 | ) || view.nodes[&stem].children.last() != Some(&previous) | |
| 511 | { | |
| 512 | return Err(invalid( | |
| 513 | "Select adjacent text within one outline or table cell", | |
| 514 | )); | |
| 515 | } | |
| 516 | } | |
| 517 | let siblings = &view.nodes[&right_parent].children; | |
| 518 | if !siblings.windows(2).any(|pair| pair == [stem, right]) { | |
| 519 | return Err(invalid("Select adjacent paragraph text in document order")); | |
| 520 | } | |
| 521 | let children = &view.nodes[&right].children; | |
| 522 | if !children.is_empty() | |
| 523 | && !view.nodes[&stem].children.is_empty() | |
| 524 | && view.nodes[&stem].child_level != view.nodes[&right].child_level | |
| 525 | { | |
| 526 | return Err(invalid( | |
| 527 | "Joining these child indentation levels requires a hierarchy edit", | |
| 528 | )); | |
| 529 | } | |
| 530 | let raw = index.resolve(space, rid)?; | |
| 531 | let (a, a_runs) = ordinary_text(&view, &raw, self.left)?; | |
| 532 | let (b, b_runs) = ordinary_text(&view, &raw, self.right)?; | |
| 533 | let length = u32::try_from(a.encode_utf16().count()) | |
| 534 | .map_err(|_| invalid("Paragraph exceeds the UTF-16 offset range"))?; | |
| 535 | let right_length = u32::try_from(b.encode_utf16().count()) | |
| 536 | .map_err(|_| invalid("Paragraph exceeds the UTF-16 offset range"))?; | |
| 537 | length | |
| 538 | .checked_add(right_length) | |
| 539 | .ok_or_else(|| invalid("Joined text exceeds the UTF-16 offset range"))?; | |
| 540 | let modified = current_timestamps()?.0.to_le_bytes(); | |
| 541 | let mut changed = BTreeMap::new(); | |
| 542 | let (survivor, mut text) = if a.is_empty() { | |
| 543 | let mut target = PropertyObject::from_object(&raw.objects[&self.right])?; | |
| 544 | target.copy_property( | |
| 545 | &PropertyObject::from_object(&raw.objects[&self.left])?, | |
| 546 | 0x40003489, | |
| 547 | )?; | |
| 548 | (self.right, target) | |
| 549 | } else { | |
| 550 | let mut target = PropertyObject::from_object(&raw.objects[&self.left])?; | |
| 551 | let mut styles = BTreeMap::new(); | |
| 552 | let mut ends = Vec::new(); | |
| 553 | let mut references = Vec::new(); | |
| 554 | let mut empty_style = None; | |
| 555 | for run in a_runs.iter().filter(|run| run.start < run.end) { | |
| 556 | let id = if let Some(id) = run.format { | |
| 557 | id | |
| 558 | } else if let Some(id) = empty_style { | |
| 559 | id | |
| 560 | } else { | |
| 561 | let id = ExGuid { | |
| 562 | guid: fresh_guid()?, | |
| 563 | n: 1, | |
| 564 | }; | |
| 565 | changed.insert( | |
| 566 | id, | |
| 567 | PropertyObject { | |
| 568 | jcid: 0x12004d, | |
| 569 | bytes: properties(&[])?, | |
| 570 | global_ids: Arc::new(BTreeMap::from([(0, id.guid)])), | |
| 571 | }, | |
| 572 | ); | |
| 573 | empty_style = Some(id); | |
| 574 | id | |
| 575 | }; | |
| 576 | references.extend_from_slice(&target.reference(id)?); | |
| 577 | ends.extend_from_slice(&run.end.to_le_bytes()); | |
| 578 | } | |
| 579 | let left_base = character_properties(&view, &raw, self.left)?; | |
| 580 | let right_base = character_properties(&view, &raw, self.right)?; | |
| 581 | for (i, run) in b_runs.iter().enumerate() { | |
| 582 | if run.start == run.end && i != b_runs.len() - 1 { | |
| 583 | continue; | |
| 584 | } | |
| 585 | let id = if let Some(id) = styles.get(&run.format) { | |
| 586 | *id | |
| 587 | } else { | |
| 588 | let mut style = if let Some(id) = run.format { | |
| 589 | PropertyObject::from_object(&raw.objects[&id])? | |
| 590 | } else { | |
| 591 | PropertyObject { | |
| 592 | jcid: 0x12004d, | |
| 593 | bytes: properties(&[])?, | |
| 594 | global_ids: Arc::new(BTreeMap::new()), | |
| 595 | } | |
| 596 | }; | |
| 597 | if left_base != right_base { | |
| 598 | let mut values = right_base.clone(); | |
| 599 | for property in &PropertySets::parse(&style.bytes)?.sets[0] { | |
| 600 | let key = property.id & 0x7fffffff; | |
| 601 | if is_character_property(key) { | |
| 602 | let value = match property.value { | |
| 603 | crate::Value::NoData => &[][..], | |
| 604 | crate::Value::Bytes(bytes) => bytes, | |
| 605 | _ => unreachable!(), | |
| 606 | }; | |
| 607 | values.insert(key, (property.id, value.to_vec())); | |
| 608 | } | |
| 609 | } | |
| 610 | for key in left_base.keys() { | |
| 611 | if values.contains_key(key) { | |
| 612 | continue; | |
| 613 | } | |
| 614 | let value = match key >> 26 & 31 { | |
| 615 | 2 => Vec::new(), | |
| 616 | _ if matches!(*key, 0x14001c0c | 0x14001c0d) => { | |
| 617 | 0xff000000_u32.to_le_bytes().to_vec() | |
| 618 | } | |
| 619 | _ => { | |
| 620 | return Err(invalid( | |
| 621 | "The right paragraph's implicit font or language cannot be preserved under the left style", | |
| 622 | )); | |
| 623 | } | |
| 624 | }; | |
| 625 | values.insert(*key, (*key, value)); | |
| 626 | } | |
| 627 | style.set( | |
| 628 | &values | |
| 629 | .values() | |
| 630 | .map(|(id, value)| (*id, value.as_slice())) | |
| 631 | .collect::<Vec<_>>(), | |
| 632 | )?; | |
| 633 | } | |
| 634 | let id = if let Some(id) = run | |
| 635 | .format | |
| 636 | .filter(|id| raw.objects[id].data == ObjectData::Properties(&style.bytes)) | |
| 637 | { | |
| 638 | id | |
| 639 | } else { | |
| 640 | let id = ExGuid { | |
| 641 | guid: fresh_guid()?, | |
| 642 | n: 1, | |
| 643 | }; | |
| 644 | style.reference(id)?; | |
| 645 | changed.insert(id, style); | |
| 646 | id | |
| 647 | }; | |
| 648 | styles.insert(run.format, id); | |
| 649 | id | |
| 650 | }; | |
| 651 | references.extend_from_slice(&target.reference(id)?); | |
| 652 | ends.extend_from_slice(&(length + run.end).to_le_bytes()); | |
| 653 | } | |
| 654 | ends.truncate(ends.len() - 4); | |
| 655 | if ends | |
| 656 | .chunks_exact(4) | |
| 657 | .map(|bytes| u32::from_le_bytes(bytes.try_into().unwrap())) | |
| 658 | .collect::<Vec<_>>() | |
| 659 | .windows(2) | |
| 660 | .any(|pair| pair[0] >= pair[1]) | |
| 661 | { | |
| 662 | return Err(invalid("Text-run boundaries must be strictly increasing")); | |
| 663 | } | |
| 664 | target.set(&[ | |
| 665 | (0x1c001c22, &string(&format!("{a}{b}"))), | |
| 666 | (0x1c001e12, &ends), | |
| 667 | (0x24001e13, &references), | |
| 668 | ])?; | |
| 669 | (self.left, target) | |
| 670 | }; | |
| 671 | text.set(&[(0x14001d7a, &modified)])?; | |
| 672 | changed.insert(survivor, text); | |
| 673 | let author = ExGuid { | |
| 674 | guid: fresh_guid()?, | |
| 675 | n: 1, | |
| 676 | }; | |
| 677 | changed.insert( | |
| 678 | author, | |
| 679 | PropertyObject { | |
| 680 | jcid: 0x120001, | |
| 681 | bytes: properties(&[(0x1c001d75, string(&self.author))])?, | |
| 682 | global_ids: Arc::new(BTreeMap::from([(0, author.guid)])), | |
| 683 | }, | |
| 684 | ); | |
| 685 | let mut left_object = PropertyObject::from_object(&raw.objects[&left])?; | |
| 686 | let content = left_object.reference(survivor)?; | |
| 687 | let author = left_object.reference(author)?; | |
| 688 | left_object.set(&[(0x24001c1f, &content), (0x20001d79, &author)])?; | |
| 689 | changed.insert(left, left_object); | |
| 690 | if !children.is_empty() { | |
| 691 | let target = match changed.entry(stem) { | |
| 692 | std::collections::btree_map::Entry::Occupied(entry) => entry.into_mut(), | |
| 693 | std::collections::btree_map::Entry::Vacant(entry) => { | |
| 694 | entry.insert(PropertyObject::from_object(&raw.objects[&stem])?) | |
| 695 | } | |
| 696 | }; | |
| 697 | let mut references = Vec::new(); | |
| 698 | for id in view.nodes[&stem].children.iter().chain(children) { | |
| 699 | references.extend_from_slice(&target.reference(*id)?); | |
| 700 | } | |
| 701 | target.set(&[(0x24001c20, &references)])?; | |
| 702 | target.copy_property( | |
| 703 | &PropertyObject::from_object(&raw.objects[&right])?, | |
| 704 | 0x0c001c03, | |
| 705 | )?; | |
| 706 | } | |
| 707 | let mut container = PropertyObject::from_object(&raw.objects[&right_parent])?; | |
| 708 | let mut references = Vec::new(); | |
| 709 | for id in siblings.iter().filter(|id| **id != right) { | |
| 710 | references.extend_from_slice(&container.reference(*id)?); | |
| 711 | } | |
| 712 | container.set(&[(0x24001c20, &references)])?; | |
| 713 | changed.insert(right_parent, container); | |
| 714 | let mut pending = vec![left, right_parent]; | |
| 715 | let mut ancestors = BTreeSet::new(); | |
| 716 | while let Some(id) = pending.pop() { | |
| 717 | if !ancestors.insert(id) { | |
| 718 | continue; | |
| 719 | } | |
| 720 | let object = match changed.entry(id) { | |
| 721 | std::collections::btree_map::Entry::Occupied(entry) => entry.into_mut(), | |
| 722 | std::collections::btree_map::Entry::Vacant(entry) => { | |
| 723 | entry.insert(PropertyObject::from_object(&raw.objects[&id])?) | |
| 724 | } | |
| 725 | }; | |
| 726 | object.set(&[(0x14001d7a, &modified)])?; | |
| 727 | pending.extend(parents.get(&id).into_iter().flatten().copied()); | |
| 728 | } | |
| 729 | update_title(&store, &raw, view, &pages, &mut changed)?; | |
| 730 | write_revision(source, space, |_| Ok(changed)) | |
| 731 | } | |
| 732 | } | |
| 733 | ||
| 734 | fn is_character_property(id: u32) -> bool { | |
| 735 | matches!( | |
| 736 | id, | |
| 737 | 0x08001c04 | |
| 738 | ..=0x08001c09 | |
| 739 | | 0x08001e16 | |
| 740 | | 0x08001e14 | |
| 741 | | 0x08001e19 | |
| 742 | | 0x08003401 | |
| 743 | | 0x08001e22 | |
| 744 | | 0x08003476 | |
| 745 | | 0x1c001c0a | |
| 746 | | 0x10001c0b | |
| 747 | | 0x14001c0c | |
| 748 | | 0x14001c0d | |
| 749 | | 0x14001c3b | |
| 750 | ) | |
| 751 | } | |
| 752 | ||
| 753 | fn character_properties( | |
| 754 | view: &crate::document::Revision<'_>, | |
| 755 | raw: &crate::ResolvedRevision<'_>, | |
| 756 | text: ExGuid, | |
| 757 | ) -> Result<BTreeMap<u32, (u32, Vec<u8>)>, Error> { | |
| 758 | let Kind::RichText { | |
| 759 | paragraph_style, .. | |
| 760 | } = view.nodes[&text].kind | |
| 761 | else { | |
| 762 | unreachable!() | |
| 763 | }; | |
| 764 | let mut values = BTreeMap::new(); | |
| 765 | for id in paragraph_style.into_iter().chain([text]) { | |
| 766 | let ObjectData::Properties(bytes) = raw.objects[&id].data else { | |
| 767 | unreachable!() | |
| 768 | }; | |
| 769 | for property in &PropertySets::parse(bytes)?.sets[0] { | |
| 770 | let key = property.id & 0x7fffffff; | |
| 771 | if is_character_property(key) { | |
| 772 | let value = match property.value { | |
| 773 | crate::Value::NoData => &[][..], | |
| 774 | crate::Value::Bytes(bytes) => bytes, | |
| 775 | _ => unreachable!(), | |
| 776 | }; | |
| 777 | values.insert(key, (property.id, value.to_vec())); | |
| 778 | } | |
| 779 | } | |
| 780 | } | |
| 781 | Ok(values) | |
| 782 | } |
crates/onestore/src/write.rs+108| ... | ... | @@ -472,6 +472,114 @@ impl PropertyObject { |
| 472 | 472 | } |
| 473 | 473 | compact(id, &self.global_ids) |
| 474 | 474 | } |
| 475 | ||
| 476 | pub fn copy_property(&mut self, source: &Self, id: u32) -> Result<()> { | |
| 477 | let properties = PropertySets::parse(&source.bytes)?; | |
| 478 | let mut target = Self { | |
| 479 | jcid: self.jcid, | |
| 480 | bytes: self.bytes.clone(), | |
| 481 | global_ids: Arc::clone(&self.global_ids), | |
| 482 | }; | |
| 483 | target.remove(&[id])?; | |
| 484 | let lengths = property_set_lengths(&properties); | |
| 485 | let mut offset = properties.root_ids.as_ptr().addr() - source.bytes.as_ptr().addr() | |
| 486 | + properties.root_ids.len(); | |
| 487 | let mut selected = None; | |
| 488 | for property in &properties.sets[0] { | |
| 489 | let end = offset + field_length(property, &lengths); | |
| 490 | if property.id & 0x7fffffff == id & 0x7fffffff { | |
| 491 | selected = Some((property, &source.bytes[offset..end])); | |
| 492 | break; | |
| 493 | } | |
| 494 | offset = end; | |
| 495 | } | |
| 496 | if let Some((property, field)) = selected { | |
| 497 | let mut added: [Vec<u8>; 3] = std::array::from_fn(|_| Vec::new()); | |
| 498 | let mut pending = vec![property]; | |
| 499 | while let Some(property) = pending.pop() { | |
| 500 | match &property.value { | |
| 501 | Value::References { | |
| 502 | stream, | |
| 503 | compact_ids, | |
| 504 | } => { | |
| 505 | let index = match stream { | |
| 506 | crate::IdStream::Objects => 0, | |
| 507 | crate::IdStream::ObjectSpaces => 1, | |
| 508 | crate::IdStream::Contexts => 2, | |
| 509 | }; | |
| 510 | let mut cursor = crate::bytes::Cursor { | |
| 511 | bytes: compact_ids, | |
| 512 | offset: 0, | |
| 513 | }; | |
| 514 | while cursor.offset < cursor.bytes.len() { | |
| 515 | let id = cursor.compact(&source.global_ids)?; | |
| 516 | added[index].extend_from_slice(&target.reference(id)?); | |
| 517 | } | |
| 518 | } | |
| 519 | Value::Sets(children) => { | |
| 520 | for child in children.clone().rev() { | |
| 521 | pending.extend(properties.sets[child].iter().rev()); | |
| 522 | } | |
| 523 | } | |
| 524 | _ => {} | |
| 525 | } | |
| 526 | } | |
| 527 | let old = PropertySets::parse(&target.bytes)?; | |
| 528 | let count = u16::try_from(old.sets[0].len() + 1).map_err(|_| Error { | |
| 529 | offset: 0, | |
| 530 | message: "Root property count exceeds the format limit", | |
| 531 | })?; | |
| 532 | let streams = crate::properties::reference_streams(&mut crate::bytes::Cursor { | |
| 533 | bytes: &target.bytes, | |
| 534 | offset: 0, | |
| 535 | })?; | |
| 536 | let stream_count = (0..3) | |
| 537 | .rev() | |
| 538 | .find(|i| streams[*i].offset != 0 || !added[*i].is_empty()) | |
| 539 | .unwrap() | |
| 540 | + 1; | |
| 541 | let mut bytes = Vec::new(); | |
| 542 | for i in 0..stream_count { | |
| 543 | let stream = &streams[i]; | |
| 544 | let count = u32::try_from((stream.bytes.len() + added[i].len()) / 4) | |
| 545 | .ok() | |
| 546 | .filter(|n| *n <= 0xffffff) | |
| 547 | .ok_or(Error { | |
| 548 | offset: 0, | |
| 549 | message: "Reference stream exceeds the format limit", | |
| 550 | })?; | |
| 551 | let reserved = if stream.offset == 0 { | |
| 552 | 0 | |
| 553 | } else { | |
| 554 | u32::from_le_bytes( | |
| 555 | target.bytes[stream.offset - 4..stream.offset] | |
| 556 | .try_into() | |
| 557 | .unwrap(), | |
| 558 | ) & 0x3f000000 | |
| 559 | }; | |
| 560 | let flags = match (i, stream_count) { | |
| 561 | (0, 1) => 0x80000000, | |
| 562 | (0 | 1, 3) => 0x40000000, | |
| 563 | _ => 0, | |
| 564 | }; | |
| 565 | bytes.extend_from_slice(&(count | reserved | flags).to_le_bytes()); | |
| 566 | bytes.extend_from_slice(stream.bytes); | |
| 567 | bytes.extend_from_slice(&added[i]); | |
| 568 | } | |
| 569 | bytes.extend_from_slice(&count.to_le_bytes()); | |
| 570 | bytes.extend_from_slice(old.root_ids); | |
| 571 | bytes.extend_from_slice(&property.id.to_le_bytes()); | |
| 572 | let start = | |
| 573 | old.root_ids.as_ptr().addr() - target.bytes.as_ptr().addr() + old.root_ids.len(); | |
| 574 | bytes.extend_from_slice(&target.bytes[start..target.bytes.len() - old.padding.len()]); | |
| 575 | bytes.extend_from_slice(field); | |
| 576 | bytes.resize(bytes.len().next_multiple_of(8), 0); | |
| 577 | PropertySets::parse(&bytes)?; | |
| 578 | target.bytes = bytes; | |
| 579 | } | |
| 580 | *self = target; | |
| 581 | Ok(()) | |
| 582 | } | |
| 475 | 583 | } |
| 476 | 584 | |
| 477 | 585 | pub(crate) fn write_revision( |
crates/onestore/src/write/tests.rs+75-1| ... | ... | @@ -29,8 +29,11 @@ fn document_insertions_and_formatting_respect_readonly_ancestors() { |
| 29 | 29 | )) |
| 30 | 30 | }) |
| 31 | 31 | .unwrap(); |
| 32 | let right = Insertion::paragraph(outline, None, "Right", "Author").unwrap(); | |
| 33 | let expanded = PreparedEdit::insert(&source, sid, &right).unwrap(); | |
| 34 | let source = expanded.as_bytes(); | |
| 32 | 35 | for blocked in [page, outline, paragraph] { |
| 33 | let protected = write_revision(&source, sid, |raw| { | |
| 36 | let protected = write_revision(source, sid, |raw| { | |
| 34 | 37 | let mut object = PropertyObject::from_object(&raw.objects[&blocked])?; |
| 35 | 38 | object.set(&[(0x88001cde, &[])])?; |
| 36 | 39 | Ok(BTreeMap::from([(blocked, object)])) |
| ... | ... | @@ -48,6 +51,8 @@ fn document_insertions_and_formatting_respect_readonly_ancestors() { |
| 48 | 51 | .unwrap(); |
| 49 | 52 | let split = crate::ParagraphSplit::new(text, 1, "Author").unwrap(); |
| 50 | 53 | assert!(PreparedEdit::split(&protected, sid, &split).is_err()); |
| 54 | let join = crate::ParagraphJoin::new(text, right.text_object(), "Author").unwrap(); | |
| 55 | assert!(PreparedEdit::join(&protected, sid, &join).is_err()); | |
| 51 | 56 | assert!( |
| 52 | 57 | PreparedEdit::format( |
| 53 | 58 | &protected, |
| ... | ... | @@ -63,6 +68,18 @@ fn document_insertions_and_formatting_respect_readonly_ancestors() { |
| 63 | 68 | assert!(PreparedEdit::insert(&protected, sid, &outline).is_err()); |
| 64 | 69 | } |
| 65 | 70 | } |
| 71 | let protected = write_revision(source, sid, |raw| { | |
| 72 | let mut object = PropertyObject::from_object(&raw.objects[&right.object()])?; | |
| 73 | object.set(&[(0x88001cde, &[])])?; | |
| 74 | Ok(BTreeMap::from([(right.object(), object)])) | |
| 75 | }) | |
| 76 | .unwrap(); | |
| 77 | let document = crate::document::Document::parse(&index).unwrap(); | |
| 78 | let space = &document.spaces[&sid]; | |
| 79 | let view = &space.revisions[&space.contexts[&ExGuid::default()]]; | |
| 80 | let text = view.nodes[&paragraph].content[0]; | |
| 81 | let join = crate::ParagraphJoin::new(text, right.text_object(), "Author").unwrap(); | |
| 82 | assert!(PreparedEdit::join(&protected, sid, &join).is_err()); | |
| 66 | 83 | } |
| 67 | 84 | |
| 68 | 85 | fn add_paragraph(source: &[u8], number: u32) -> Vec<u8> { |
| ... | ... | @@ -572,6 +589,56 @@ fn nested_fields_and_other_reference_streams_remain_byte_exact() { |
| 572 | 589 | object.remove(&ids).unwrap(); |
| 573 | 590 | assert_eq!(object.bytes, before); |
| 574 | 591 | } |
| 592 | let source = super::PropertyObject { | |
| 593 | jcid: 0x6000e, | |
| 594 | bytes: original.clone(), | |
| 595 | global_ids: std::sync::Arc::new(std::collections::BTreeMap::from([(0, [1; 16])])), | |
| 596 | }; | |
| 597 | let mut target = super::PropertyObject { | |
| 598 | jcid: 0x6000e, | |
| 599 | bytes: properties(&[(0x20000001, vec![6, 0, 0, 0])]).unwrap(), | |
| 600 | global_ids: std::sync::Arc::new(std::collections::BTreeMap::from([(0, [9; 16])])), | |
| 601 | }; | |
| 602 | target.copy_property(&source, 0x40000002).unwrap(); | |
| 603 | let copied = PropertySets::parse(&target.bytes).unwrap(); | |
| 604 | assert_eq!(target.global_ids[&0], [9; 16]); | |
| 605 | assert_eq!(target.global_ids[&1], [1; 16]); | |
| 606 | assert_eq!( | |
| 607 | copied.sets[0][0].value, | |
| 608 | Value::References { | |
| 609 | stream: crate::IdStream::Objects, | |
| 610 | compact_ids: &[6, 0, 0, 0], | |
| 611 | } | |
| 612 | ); | |
| 613 | for (i, n) in [2, 4, 5].into_iter().enumerate() { | |
| 614 | let Value::References { compact_ids, .. } = copied.sets[1][i].value else { | |
| 615 | panic!() | |
| 616 | }; | |
| 617 | assert_eq!(compact_ids, &[n, 1, 0, 0]); | |
| 618 | } | |
| 619 | assert_eq!(copied.sets[1][3], previous.sets[1][3]); | |
| 620 | assert_eq!( | |
| 621 | target | |
| 622 | .bytes | |
| 623 | .windows(nested.len()) | |
| 624 | .filter(|b| *b == nested) | |
| 625 | .count(), | |
| 626 | 1 | |
| 627 | ); | |
| 628 | let before = target.bytes.clone(); | |
| 629 | target.copy_property(&source, 0x40000002).unwrap(); | |
| 630 | assert_eq!(target.bytes, before); | |
| 631 | target.copy_property(&source, 0x20000001).unwrap(); | |
| 632 | assert_eq!(PropertySets::parse(&target.bytes).unwrap().sets[0].len(), 1); | |
| 633 | let before = target.bytes.clone(); | |
| 634 | let ids = std::sync::Arc::clone(&target.global_ids); | |
| 635 | let invalid = super::PropertyObject { | |
| 636 | global_ids: Default::default(), | |
| 637 | ..source | |
| 638 | }; | |
| 639 | assert!(target.copy_property(&invalid, 0x40000002).is_err()); | |
| 640 | assert_eq!(target.bytes, before); | |
| 641 | assert_eq!(target.global_ids, ids); | |
| 575 | 642 | } |
| 576 | 643 | |
| 577 | 644 | #[test] |
| ... | ... | @@ -599,6 +666,13 @@ fn deep_property_splices_do_not_use_the_call_stack() { |
| 599 | 666 | let parsed = PropertySets::parse(&object.bytes).unwrap(); |
| 600 | 667 | assert_eq!(parsed.sets.len(), 100_001); |
| 601 | 668 | assert_eq!(&object.bytes[..bytes.len()], &bytes); |
| 669 | let mut copied = super::PropertyObject { | |
| 670 | jcid: object.jcid, | |
| 671 | bytes: properties(&[]).unwrap(), | |
| 672 | global_ids: Default::default(), | |
| 673 | }; | |
| 674 | copied.copy_property(&object, 0x44000001).unwrap(); | |
| 675 | assert_eq!(copied.bytes, object.bytes); | |
| 602 | 676 | object.remove(&[0x44000001]).unwrap(); |
| 603 | 677 | let parsed = PropertySets::parse(&object.bytes).unwrap(); |
| 604 | 678 | assert_eq!(parsed.sets.len(), 1); |
crates/onestore/tests/paragraph.rs+441-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | use onestore::{ |
| 2 | ExGuid, ParagraphSplit, PreparedEdit, RevisionIndex, Store, | |
| 2 | ExGuid, ParagraphJoin, ParagraphSplit, PreparedEdit, RevisionIndex, Store, | |
| 3 | 3 | document::{Document, Kind, Revision}, |
| 4 | 4 | }; |
| 5 | 5 | use serde_json::Value; |
| ... | ... | @@ -25,6 +25,270 @@ fn characters(view: &Revision<'_>, id: ExGuid) -> Vec<(char, Value)> { |
| 25 | 25 | .collect() |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | const JOIN_FIXTURES: [(&[u8], &[u8], bool); 3] = [ | |
| 29 | ( | |
| 30 | include_bytes!("../../../corpus/paragraph-edit/split/notebook/synthetic.one").as_slice(), | |
| 31 | include_bytes!("../../../corpus/paragraph-edit/joined/notebook/synthetic.one").as_slice(), | |
| 32 | true, | |
| 33 | ), | |
| 34 | ( | |
| 35 | include_bytes!("../../../corpus/paragraph-edit/join-edges/before/notebook/synthetic.one") | |
| 36 | .as_slice(), | |
| 37 | include_bytes!("../../../corpus/paragraph-edit/join-edges/joined/notebook/synthetic.one") | |
| 38 | .as_slice(), | |
| 39 | false, | |
| 40 | ), | |
| 41 | ( | |
| 42 | include_bytes!("../../../corpus/paragraph-edit/join-tags/before/notebook/synthetic.one") | |
| 43 | .as_slice(), | |
| 44 | include_bytes!("../../../corpus/paragraph-edit/join-tags/joined/notebook/synthetic.one") | |
| 45 | .as_slice(), | |
| 46 | false, | |
| 47 | ), | |
| 48 | ]; | |
| 49 | ||
| 50 | fn join_targets(document: &Document<'_>, split_cases: bool) -> Vec<(String, ExGuid, ExGuid)> { | |
| 51 | let mut cases = Vec::new(); | |
| 52 | if split_cases { | |
| 53 | let manifest: Value = | |
| 54 | serde_json::from_str(include_str!("../../../corpus/paragraph-edit/manifest.json")) | |
| 55 | .unwrap(); | |
| 56 | for case in manifest["cases"].as_array().unwrap() { | |
| 57 | cases.push(( | |
| 58 | case["case"].as_str().unwrap().to_owned(), | |
| 59 | serde_json::from_value::<ExGuid>(case["original_text"].clone()).unwrap(), | |
| 60 | serde_json::from_value::<ExGuid>(case["new_text"].clone()).unwrap(), | |
| 61 | )); | |
| 62 | } | |
| 63 | } else { | |
| 64 | for (sid, page) in document.pages().unwrap() { | |
| 65 | let s = &document.spaces[&sid]; | |
| 66 | let view = &s.revisions[&s.contexts[&ExGuid::default()]]; | |
| 67 | let Kind::Metadata { | |
| 68 | title: Some(name), .. | |
| 69 | } = &view.nodes[&view.roots[&2]].kind | |
| 70 | else { | |
| 71 | panic!() | |
| 72 | }; | |
| 73 | if !name.starts_with("Join ") { | |
| 74 | continue; | |
| 75 | } | |
| 76 | let outline = view.nodes[&page] | |
| 77 | .children | |
| 78 | .iter() | |
| 79 | .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) | |
| 80 | .unwrap(); | |
| 81 | let mut left = view.nodes[outline].children[0]; | |
| 82 | while let Some(child) = view.nodes[&left].children.last() { | |
| 83 | left = *child; | |
| 84 | } | |
| 85 | let right = view.nodes[outline].children[1]; | |
| 86 | cases.push(( | |
| 87 | name.clone(), | |
| 88 | view.nodes[&left].content[0], | |
| 89 | view.nodes[&right].content[0], | |
| 90 | )); | |
| 91 | } | |
| 92 | } | |
| 93 | assert_eq!(cases.len(), document.pages().unwrap().len() - 1); | |
| 94 | cases | |
| 95 | } | |
| 96 | ||
| 97 | #[test] | |
| 98 | fn joins_match_native_graphs_tags_and_inherited_character_styles() { | |
| 99 | for (source, native, split_cases) in JOIN_FIXTURES { | |
| 100 | let store = Store::parse(source).unwrap(); | |
| 101 | let index = RevisionIndex::parse(&store).unwrap(); | |
| 102 | let document = Document::parse(&index).unwrap(); | |
| 103 | let native_store = Store::parse(native).unwrap(); | |
| 104 | let native_index = RevisionIndex::parse(&native_store).unwrap(); | |
| 105 | let native_document = Document::parse(&native_index).unwrap(); | |
| 106 | let cases = join_targets(&document, split_cases); | |
| 107 | for (name, left, right) in cases { | |
| 108 | let (sid, space) = document | |
| 109 | .spaces | |
| 110 | .iter() | |
| 111 | .find(|(_, space)| { | |
| 112 | space.revisions[&space.contexts[&ExGuid::default()]] | |
| 113 | .nodes | |
| 114 | .contains_key(&left) | |
| 115 | }) | |
| 116 | .unwrap(); | |
| 117 | let intent = ParagraphJoin::new(left, right, "Join author").unwrap(); | |
| 118 | let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); | |
| 119 | assert_eq!(intent, restored); | |
| 120 | let edited = PreparedEdit::join(source, *sid, &restored); | |
| 121 | if name == "Split before hyperlink" { | |
| 122 | assert!(edited.is_err()); | |
| 123 | continue; | |
| 124 | } | |
| 125 | let edited = edited.unwrap_or_else(|error| panic!("{name}: {error}")); | |
| 126 | let current_store = Store::parse(edited.as_bytes()).unwrap(); | |
| 127 | assert_eq!( | |
| 128 | current_store.header.transaction_count, | |
| 129 | store.header.transaction_count + 1 | |
| 130 | ); | |
| 131 | let current_index = RevisionIndex::parse(&current_store).unwrap(); | |
| 132 | current_index.validate_current().unwrap(); | |
| 133 | let current_document = Document::parse(&current_index).unwrap(); | |
| 134 | let current = &current_document.spaces[sid]; | |
| 135 | let current = &current.revisions[&current.contexts[&ExGuid::default()]]; | |
| 136 | let expected = &native_document.spaces[sid]; | |
| 137 | let expected = &expected.revisions[&expected.contexts[&ExGuid::default()]]; | |
| 138 | let (_, page) = document | |
| 139 | .pages() | |
| 140 | .unwrap() | |
| 141 | .into_iter() | |
| 142 | .find(|(id, _)| id == sid) | |
| 143 | .unwrap(); | |
| 144 | let before = &space.revisions[&space.contexts[&ExGuid::default()]]; | |
| 145 | let left_paragraph = *before | |
| 146 | .nodes | |
| 147 | .iter() | |
| 148 | .find(|(_, node)| node.content == [left]) | |
| 149 | .unwrap() | |
| 150 | .0; | |
| 151 | let right_paragraph = *before | |
| 152 | .nodes | |
| 153 | .iter() | |
| 154 | .find(|(_, node)| node.content == [right]) | |
| 155 | .unwrap() | |
| 156 | .0; | |
| 157 | let right_parent = *before | |
| 158 | .nodes | |
| 159 | .iter() | |
| 160 | .find(|(_, node)| node.children.contains(&right_paragraph)) | |
| 161 | .unwrap() | |
| 162 | .0; | |
| 163 | let survivor = if characters(before, left).is_empty() { | |
| 164 | right | |
| 165 | } else { | |
| 166 | left | |
| 167 | }; | |
| 168 | let mut changed = std::collections::BTreeSet::from([survivor, before.roots[&2]]); | |
| 169 | let mut ancestors = vec![left_paragraph, right_parent]; | |
| 170 | while let Some(id) = ancestors.pop() { | |
| 171 | if !changed.insert(id) || id == page { | |
| 172 | continue; | |
| 173 | } | |
| 174 | ancestors.extend(before.nodes.iter().filter_map(|(parent, node)| { | |
| 175 | node.children | |
| 176 | .iter() | |
| 177 | .chain(&node.content) | |
| 178 | .chain(&node.structure) | |
| 179 | .any(|child| *child == id) | |
| 180 | .then_some(*parent) | |
| 181 | })); | |
| 182 | } | |
| 183 | let old_raw = index | |
| 184 | .resolve(*sid, space.contexts[&ExGuid::default()]) | |
| 185 | .unwrap(); | |
| 186 | let current_rid = current_document.spaces[sid].contexts[&ExGuid::default()]; | |
| 187 | let new_raw = current_index.resolve(*sid, current_rid).unwrap(); | |
| 188 | for (id, object) in old_raw.objects { | |
| 189 | if !changed.contains(&id) { | |
| 190 | assert_eq!( | |
| 191 | object.data, new_raw.objects[&id].data, | |
| 192 | "{name} untouched {id}" | |
| 193 | ); | |
| 194 | } | |
| 195 | } | |
| 196 | let mut pending: Vec<_> = expected.nodes[&page] | |
| 197 | .children | |
| 198 | .iter() | |
| 199 | .filter(|id| matches!(expected.nodes[id].kind, Kind::Outline { .. })) | |
| 200 | .copied() | |
| 201 | .collect(); | |
| 202 | while let Some(id) = pending.pop() { | |
| 203 | let node = &expected.nodes[&id]; | |
| 204 | assert_eq!( | |
| 205 | current.nodes[&id].children, node.children, | |
| 206 | "{name} children {id}" | |
| 207 | ); | |
| 208 | assert_eq!( | |
| 209 | current.nodes[&id].content, node.content, | |
| 210 | "{name} content {id}" | |
| 211 | ); | |
| 212 | assert_eq!( | |
| 213 | current.nodes[&id].child_level, node.child_level, | |
| 214 | "{name} indentation {id}" | |
| 215 | ); | |
| 216 | pending.extend( | |
| 217 | node.children | |
| 218 | .iter() | |
| 219 | .chain(&node.content) | |
| 220 | .chain(&node.structure) | |
| 221 | .copied(), | |
| 222 | ); | |
| 223 | if matches!(node.kind, Kind::RichText { .. }) { | |
| 224 | assert_eq!( | |
| 225 | serde_json::to_value(&current.nodes[&id].tags).unwrap(), | |
| 226 | serde_json::to_value(&node.tags).unwrap(), | |
| 227 | "{name} tags {id}" | |
| 228 | ); | |
| 229 | let normalize = |view, id| { | |
| 230 | characters(view, id) | |
| 231 | .into_iter() | |
| 232 | .map(|(c, mut style)| { | |
| 233 | let fields = style.as_object_mut().unwrap(); | |
| 234 | for key in | |
| 235 | ["alignment", "space_before", "space_after", "line_spacing"] | |
| 236 | { | |
| 237 | fields.remove(key); | |
| 238 | } | |
| 239 | for key in [ | |
| 240 | "bold", | |
| 241 | "italic", | |
| 242 | "underline", | |
| 243 | "strike", | |
| 244 | "superscript", | |
| 245 | "subscript", | |
| 246 | "hidden", | |
| 247 | "hyperlink", | |
| 248 | "hyperlink_label", | |
| 249 | "math", | |
| 250 | "embedded_object", | |
| 251 | "rtl", | |
| 252 | ] { | |
| 253 | if fields[key].is_null() { | |
| 254 | fields.insert(key.into(), false.into()); | |
| 255 | } | |
| 256 | } | |
| 257 | for key in ["color", "highlight"] { | |
| 258 | if fields[key].is_null() { | |
| 259 | fields.insert(key.into(), 0xff000000_u32.into()); | |
| 260 | } | |
| 261 | } | |
| 262 | (c, style) | |
| 263 | }) | |
| 264 | .collect::<Vec<_>>() | |
| 265 | }; | |
| 266 | assert_eq!( | |
| 267 | normalize(current, id), | |
| 268 | normalize(expected, id), | |
| 269 | "{name} styles {id}" | |
| 270 | ); | |
| 271 | } | |
| 272 | } | |
| 273 | for (sid, old) in &index.spaces { | |
| 274 | for revision in old.revisions.keys() { | |
| 275 | let old = index.resolve(*sid, *revision).unwrap(); | |
| 276 | let retained = current_index.resolve(*sid, *revision).unwrap(); | |
| 277 | assert_eq!(old.roots, retained.roots); | |
| 278 | for (id, object) in old.objects { | |
| 279 | assert_eq!(object.data, retained.objects[&id].data); | |
| 280 | } | |
| 281 | } | |
| 282 | } | |
| 283 | assert!(PreparedEdit::join(edited.as_bytes(), *sid, &intent).is_err()); | |
| 284 | assert_eq!( | |
| 285 | space.contexts.len(), | |
| 286 | current_document.spaces[sid].contexts.len() | |
| 287 | ); | |
| 288 | } | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 28 | 292 | #[test] |
| 29 | 293 | fn splits_partition_native_paragraphs_at_every_scalar_boundary() { |
| 30 | 294 | let manifest: Value = |
| ... | ... | @@ -341,3 +605,179 @@ fn interrupted_splits_publish_a_complete_graph_or_retain_the_original() { |
| 341 | 605 | } |
| 342 | 606 | } |
| 343 | 607 | } |
| 608 | ||
| 609 | #[test] | |
| 610 | #[ignore = "exports paragraph joins for independent native validation"] | |
| 611 | fn export_native_paragraph_joins() { | |
| 612 | use std::{fs, path::PathBuf}; | |
| 613 | let output = PathBuf::from(std::env::var_os("ONESTORE_PARAGRAPH_JOIN_OUTPUT").unwrap()); | |
| 614 | assert!(output.is_absolute()); | |
| 615 | fs::create_dir(&output).unwrap(); | |
| 616 | for (number, (source, _, split_cases)) in JOIN_FIXTURES.into_iter().enumerate() { | |
| 617 | let store = Store::parse(source).unwrap(); | |
| 618 | let index = RevisionIndex::parse(&store).unwrap(); | |
| 619 | let document = Document::parse(&index).unwrap(); | |
| 620 | let cases = join_targets(&document, split_cases); | |
| 621 | let mut source = source.to_vec(); | |
| 622 | let mut manifest = Vec::new(); | |
| 623 | for (name, left, right) in cases { | |
| 624 | if name == "Split before hyperlink" { | |
| 625 | continue; | |
| 626 | } | |
| 627 | let sid = *document | |
| 628 | .spaces | |
| 629 | .iter() | |
| 630 | .find(|(_, space)| { | |
| 631 | space.revisions[&space.contexts[&ExGuid::default()]] | |
| 632 | .nodes | |
| 633 | .contains_key(&left) | |
| 634 | }) | |
| 635 | .unwrap() | |
| 636 | .0; | |
| 637 | let intent = ParagraphJoin::new(left, right, "Rust join author").unwrap(); | |
| 638 | let edit = PreparedEdit::join(&source, sid, &intent).unwrap(); | |
| 639 | source = edit.as_bytes().to_vec(); | |
| 640 | manifest.push(serde_json::json!({"case":name,"space":sid,"intent":intent})); | |
| 641 | } | |
| 642 | let folder = output.join(["split", "inheritance", "tags"][number]); | |
| 643 | fs::create_dir_all(folder.join("candidate")).unwrap(); | |
| 644 | fs::write(folder.join("candidate/synthetic.one"), source).unwrap(); | |
| 645 | fs::write( | |
| 646 | folder.join("manifest.json"), | |
| 647 | serde_json::to_vec_pretty(&manifest).unwrap(), | |
| 648 | ) | |
| 649 | .unwrap(); | |
| 650 | } | |
| 651 | } | |
| 652 | ||
| 653 | #[test] | |
| 654 | fn interrupted_joins_preserve_complete_graphs_and_empty_text_adoption() { | |
| 655 | let source = onestore::create_section("join.one", "Left", "Author").unwrap(); | |
| 656 | let store = Store::parse(&source).unwrap(); | |
| 657 | let index = RevisionIndex::parse(&store).unwrap(); | |
| 658 | let document = Document::parse(&index).unwrap(); | |
| 659 | let (sid, page) = document.pages().unwrap()[0]; | |
| 660 | let space = &document.spaces[&sid]; | |
| 661 | let view = &space.revisions[&space.contexts[&ExGuid::default()]]; | |
| 662 | let outline = *view.nodes[&page] | |
| 663 | .children | |
| 664 | .iter() | |
| 665 | .find(|id| matches!(view.nodes[id].kind, Kind::Outline { .. })) | |
| 666 | .unwrap(); | |
| 667 | let left = view.nodes[&view.nodes[&outline].children[0]].content[0]; | |
| 668 | let right = onestore::Insertion::paragraph(outline, None, "Right 🦀", "Author") | |
| 669 | .unwrap() | |
| 670 | .with_formatting(0..5, &[onestore::TextAttribute::Bold(true)]) | |
| 671 | .unwrap(); | |
| 672 | let inserted = PreparedEdit::insert(&source, sid, &right).unwrap(); | |
| 673 | let child = | |
| 674 | onestore::Insertion::paragraph(right.object(), None, "Retained child", "Author").unwrap(); | |
| 675 | let original = PreparedEdit::insert(inserted.as_bytes(), sid, &child).unwrap(); | |
| 676 | let original = original.as_bytes(); | |
| 677 | let empty = onestore::replace_text(original, sid, left, 0..4, "").unwrap(); | |
| 678 | let checkpoint = checkpoint::pending(original, sid, left, 0x14001d7a); | |
| 679 | for source in [original, &empty, &checkpoint] { | |
| 680 | let intent = ParagraphJoin::new(left, right.text_object(), "Join author").unwrap(); | |
| 681 | let edit = PreparedEdit::join(source, sid, &intent).unwrap(); | |
| 682 | let before = current::current(source); | |
| 683 | let after = current::current(edit.as_bytes()); | |
| 684 | for write_limit in [17, 4096] { | |
| 685 | let disk = |fail_at| disk::Disk { | |
| 686 | visible: source.to_vec(), | |
| 687 | durable: source.to_vec(), | |
| 688 | operation: 0, | |
| 689 | fail_at, | |
| 690 | write_limit, | |
| 691 | random: 917, | |
| 692 | }; | |
| 693 | let mut successful = disk(None); | |
| 694 | edit.commit(&mut successful).unwrap(); | |
| 695 | assert_eq!(successful.durable, edit.as_bytes()); | |
| 696 | for at in std::iter::once(1).chain(source.len().div_ceil(193)..=successful.operation) { | |
| 697 | let mut interrupted = disk(Some(at)); | |
| 698 | let error = edit.commit(&mut interrupted).unwrap_err(); | |
| 699 | let recovered = current::current(&interrupted.durable); | |
| 700 | assert!( | |
| 701 | recovered == before || recovered == after, | |
| 702 | "interruption {at}" | |
| 703 | ); | |
| 704 | match error.state { | |
| 705 | onestore::CommitState::NotCommitted => assert_eq!(recovered, before), | |
| 706 | onestore::CommitState::Committed => assert_eq!(recovered, after), | |
| 707 | onestore::CommitState::Unknown => {} | |
| 708 | } | |
| 709 | } | |
| 710 | } | |
| 711 | } | |
| 712 | } | |
| 713 | ||
| 714 | #[test] | |
| 715 | fn joins_reject_invalid_identities_wrong_order_and_unrelated_pages() { | |
| 716 | let (source, _, _) = JOIN_FIXTURES[0]; | |
| 717 | let store = Store::parse(source).unwrap(); | |
| 718 | let index = RevisionIndex::parse(&store).unwrap(); | |
| 719 | let document = Document::parse(&index).unwrap(); | |
| 720 | let cases = join_targets(&document, true); | |
| 721 | let (_, left, right) = &cases[0]; | |
| 722 | let (sid, space) = document | |
| 723 | .spaces | |
| 724 | .iter() | |
| 725 | .find(|(_, space)| { | |
| 726 | space.revisions[&space.contexts[&ExGuid::default()]] | |
| 727 | .nodes | |
| 728 | .contains_key(left) | |
| 729 | }) | |
| 730 | .unwrap(); | |
| 731 | assert!(ParagraphJoin::new(*left, *left, "Author").is_err()); | |
| 732 | assert!(ParagraphJoin::new(ExGuid::default(), *right, "Author").is_err()); | |
| 733 | assert!(ParagraphJoin::new(*left, *right, "a\0b").is_err()); | |
| 734 | let intent = ParagraphJoin::new(*left, *right, "Author").unwrap(); | |
| 735 | for (field, value) in [ | |
| 736 | ("left", serde_json::to_value(ExGuid::default()).unwrap()), | |
| 737 | ("right", serde_json::to_value(left).unwrap()), | |
| 738 | ("author", serde_json::json!("a\0b")), | |
| 739 | ] { | |
| 740 | let mut encoded = serde_json::to_value(&intent).unwrap(); | |
| 741 | encoded[field] = value; | |
| 742 | let invalid = serde_json::from_value(encoded).unwrap(); | |
| 743 | assert!(PreparedEdit::join(source, *sid, &invalid).is_err()); | |
| 744 | } | |
| 745 | for intent in [ | |
| 746 | ParagraphJoin::new(*right, *left, "Author").unwrap(), | |
| 747 | ParagraphJoin::new(*left, cases[1].2, "Author").unwrap(), | |
| 748 | ] { | |
| 749 | assert!(PreparedEdit::join(source, *sid, &intent).is_err()); | |
| 750 | } | |
| 751 | let view = &space.revisions[&space.contexts[&ExGuid::default()]]; | |
| 752 | let paragraph = *view | |
| 753 | .nodes | |
| 754 | .iter() | |
| 755 | .find(|(_, node)| node.content == [*left]) | |
| 756 | .unwrap() | |
| 757 | .0; | |
| 758 | let parent = view | |
| 759 | .nodes | |
| 760 | .values() | |
| 761 | .find(|node| node.children.contains(&paragraph)) | |
| 762 | .unwrap(); | |
| 763 | let last = view.nodes[parent.children.last().unwrap()].content[0]; | |
| 764 | let nonadjacent = ParagraphJoin::new(*left, last, "Author").unwrap(); | |
| 765 | assert!(PreparedEdit::join(source, *sid, &nonadjacent).is_err()); | |
| 766 | let title = view | |
| 767 | .nodes | |
| 768 | .iter() | |
| 769 | .find_map(|(_, node)| matches!(node.kind, Kind::Title).then_some(node)) | |
| 770 | .unwrap(); | |
| 771 | let mut pending = title.children.clone(); | |
| 772 | let mut rejected = 0; | |
| 773 | while let Some(id) = pending.pop() { | |
| 774 | let node = &view.nodes[&id]; | |
| 775 | pending.extend(node.children.iter().chain(&node.content).copied()); | |
| 776 | if matches!(node.kind, Kind::RichText { .. }) { | |
| 777 | let intent = ParagraphJoin::new(id, *right, "Author").unwrap(); | |
| 778 | assert!(PreparedEdit::join(source, *sid, &intent).is_err()); | |
| 779 | rejected += 1; | |
| 780 | } | |
| 781 | } | |
| 782 | assert!(rejected > 0); | |
| 783 | } |
fuzz/fuzz_targets/paragraph.rs+83-44| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | 1 | #![no_main] |
| 2 | 2 | use libfuzzer_sys::fuzz_target; |
| 3 | 3 | use onestore::{ |
| 4 | CommitState, ExGuid, Insertion, ParagraphSplit, PreparedEdit, RevisionIndex, Store, | |
| 5 | TextAttribute as A, | |
| 4 | CommitState, ExGuid, Insertion, ParagraphJoin, ParagraphSplit, PreparedEdit, RevisionIndex, | |
| 5 | Store, TextAttribute as A, | |
| 6 | 6 | document::{Document, Kind}, |
| 7 | 7 | }; |
| 8 | 8 | use std::sync::LazyLock; |
| ... | ... | @@ -43,6 +43,11 @@ fuzz_target!(|input: &[u8]| { |
| 43 | 43 | { |
| 44 | 44 | current::current(edit.as_bytes()); |
| 45 | 45 | } |
| 46 | if let Ok(intent) = serde_json::from_slice::<ParagraphJoin>(input) | |
| 47 | && let Ok(edit) = PreparedEdit::join(source, *sid, &intent) | |
| 48 | { | |
| 49 | current::current(edit.as_bytes()); | |
| 50 | } | |
| 46 | 51 | let mut persisted = source.clone(); |
| 47 | 52 | let mut caches = std::array::from_fn::<_, 12, _>(|_| source.clone()); |
| 48 | 53 | for step in input.chunks_exact(8).take(20) { |
| ... | ... | @@ -65,57 +70,91 @@ fuzz_target!(|input: &[u8]| { |
| 65 | 70 | paragraphs.push(id); |
| 66 | 71 | } |
| 67 | 72 | } |
| 68 | let paragraph = paragraphs[usize::from(step[2]) % paragraphs.len()]; | |
| 69 | let text = view.nodes[&paragraph].content[0]; | |
| 70 | let characters: Vec<_> = view | |
| 71 | .text_runs(text) | |
| 72 | .unwrap() | |
| 73 | .into_iter() | |
| 74 | .flat_map(|run| { | |
| 75 | let style = serde_json::to_value(run.format).unwrap(); | |
| 76 | run.text.chars().map(move |c| (c, style.clone())) | |
| 73 | let characters = |view: &onestore::document::Revision<'_>, id| { | |
| 74 | view.text_runs(id) | |
| 75 | .unwrap() | |
| 76 | .into_iter() | |
| 77 | .flat_map(|run| { | |
| 78 | let style = serde_json::to_value(run.format).unwrap(); | |
| 79 | run.text.chars().map(move |c| (c, style.clone())) | |
| 80 | }) | |
| 81 | .collect::<Vec<_>>() | |
| 82 | }; | |
| 83 | let pairs: Vec<_> = view | |
| 84 | .nodes | |
| 85 | .iter() | |
| 86 | .flat_map(|(parent, node)| { | |
| 87 | node.children.windows(2).filter_map(|pair| { | |
| 88 | (paragraphs.contains(&pair[0]) | |
| 89 | && paragraphs.contains(&pair[1]) | |
| 90 | && view.nodes[&pair[0]].children.is_empty()) | |
| 91 | .then_some((*parent, pair[0], pair[1])) | |
| 92 | }) | |
| 77 | 93 | }) |
| 78 | 94 | .collect(); |
| 79 | let offsets: Vec<u32> = std::iter::once(0) | |
| 80 | .chain(characters.iter().scan(0, |n, (c, _)| { | |
| 81 | *n += u32::try_from(c.len_utf16()).unwrap(); | |
| 82 | Some(*n) | |
| 83 | })) | |
| 84 | .collect(); | |
| 85 | let offset = u32::from(step[3]) % (offsets.last().unwrap() + 2); | |
| 86 | let intent = ParagraphSplit::new(text, offset, "Paragraph fuzz").unwrap(); | |
| 87 | let intent = serde_json::from_value(serde_json::to_value(intent).unwrap()).unwrap(); | |
| 88 | let edit = PreparedEdit::split(source, *sid, &intent); | |
| 89 | let Some(position) = offsets.iter().position(|n| *n == offset) else { | |
| 90 | assert!(edit.is_err()); | |
| 91 | continue; | |
| 95 | let mut expected_text = Vec::new(); | |
| 96 | let mut expected_graph = Vec::new(); | |
| 97 | let edit = if step[1] & 8 != 0 && !pairs.is_empty() { | |
| 98 | let (parent, left, right) = pairs[usize::from(step[2]) % pairs.len()]; | |
| 99 | let a = view.nodes[&left].content[0]; | |
| 100 | let b = view.nodes[&right].content[0]; | |
| 101 | let mut expected = characters(view, a); | |
| 102 | let survivor = if expected.is_empty() { b } else { a }; | |
| 103 | expected.extend(characters(view, b)); | |
| 104 | expected_text.push((survivor, expected)); | |
| 105 | let children: Vec<_> = view.nodes[&parent] | |
| 106 | .children | |
| 107 | .iter() | |
| 108 | .filter(|id| **id != right) | |
| 109 | .copied() | |
| 110 | .collect(); | |
| 111 | expected_graph.push((parent, children, view.nodes[&parent].content.clone())); | |
| 112 | expected_graph.push((left, view.nodes[&right].children.clone(), vec![survivor])); | |
| 113 | let intent = ParagraphJoin::new(a, b, "Paragraph fuzz").unwrap(); | |
| 114 | let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); | |
| 115 | assert_eq!(intent, restored); | |
| 116 | PreparedEdit::join(source, *sid, &restored).unwrap() | |
| 117 | } else { | |
| 118 | let paragraph = paragraphs[usize::from(step[2]) % paragraphs.len()]; | |
| 119 | let text = view.nodes[&paragraph].content[0]; | |
| 120 | let before = characters(view, text); | |
| 121 | let offsets: Vec<u32> = std::iter::once(0) | |
| 122 | .chain(before.iter().scan(0, |n, (c, _)| { | |
| 123 | *n += u32::try_from(c.len_utf16()).unwrap(); | |
| 124 | Some(*n) | |
| 125 | })) | |
| 126 | .collect(); | |
| 127 | let offset = u32::from(step[3]) % (offsets.last().unwrap() + 2); | |
| 128 | let intent = ParagraphSplit::new(text, offset, "Paragraph fuzz").unwrap(); | |
| 129 | let restored = serde_json::from_value(serde_json::to_value(&intent).unwrap()).unwrap(); | |
| 130 | assert_eq!(intent, restored); | |
| 131 | let edit = PreparedEdit::split(source, *sid, &restored); | |
| 132 | let Some(position) = offsets.iter().position(|n| *n == offset) else { | |
| 133 | assert!(edit.is_err()); | |
| 134 | continue; | |
| 135 | }; | |
| 136 | expected_text.push((text, before[..position].to_vec())); | |
| 137 | expected_text.push((intent.text_object(), before[position..].to_vec())); | |
| 138 | expected_graph.push((paragraph, vec![], vec![text])); | |
| 139 | expected_graph.push(( | |
| 140 | intent.object(), | |
| 141 | view.nodes[&paragraph].children.clone(), | |
| 142 | vec![intent.text_object()], | |
| 143 | )); | |
| 144 | edit.unwrap() | |
| 92 | 145 | }; |
| 93 | let edit = edit.unwrap(); | |
| 94 | 146 | let after_store = Store::parse(edit.as_bytes()).unwrap(); |
| 95 | 147 | let after_index = RevisionIndex::parse(&after_store).unwrap(); |
| 96 | 148 | let after_document = Document::parse(&after_index).unwrap(); |
| 97 | 149 | let space = &after_document.spaces[sid]; |
| 98 | 150 | let after_view = &space.revisions[&space.contexts[&ExGuid::default()]]; |
| 99 | for (id, expected) in [ | |
| 100 | (text, &characters[..position]), | |
| 101 | (intent.text_object(), &characters[position..]), | |
| 102 | ] { | |
| 103 | let actual: Vec<_> = after_view | |
| 104 | .text_runs(id) | |
| 105 | .unwrap() | |
| 106 | .into_iter() | |
| 107 | .flat_map(|run| { | |
| 108 | let style = serde_json::to_value(run.format).unwrap(); | |
| 109 | run.text.chars().map(move |c| (c, style.clone())) | |
| 110 | }) | |
| 111 | .collect(); | |
| 112 | assert_eq!(actual, expected); | |
| 151 | for (id, expected) in expected_text { | |
| 152 | assert_eq!(characters(after_view, id), expected); | |
| 153 | } | |
| 154 | for (id, children, content) in expected_graph { | |
| 155 | assert_eq!(after_view.nodes[&id].children, children); | |
| 156 | assert_eq!(after_view.nodes[&id].content, content); | |
| 113 | 157 | } |
| 114 | assert!(after_view.nodes[&paragraph].children.is_empty()); | |
| 115 | assert_eq!( | |
| 116 | after_view.nodes[&intent.object()].children, | |
| 117 | view.nodes[&paragraph].children | |
| 118 | ); | |
| 119 | 158 | let before = current::current(&persisted); |
| 120 | 159 | let after = current::current(edit.as_bytes()); |
| 121 | 160 | let mut disk = disk::Disk { |
tools/native/paragraph-tag-joins.ps1 created+39| ... | ... | @@ -0,0 +1,39 @@ |
| 1 | param([Parameter(Mandatory=$true)][string]$Root, [Parameter(Mandatory=$true)][string]$CloneHost) | |
| 2 | Set-StrictMode -Version Latest | |
| 3 | $ErrorActionPreference = 'Stop' | |
| 4 | & "$PSScriptRoot\cold-current.ps1" -Root $Root -CloneHost $CloneHost | |
| 5 | $app = New-Object -ComObject OneNote.Application | |
| 6 | $notebook = '' | |
| 7 | try { | |
| 8 | $app.OpenHierarchy((Join-Path $Root 'notebook'), '', [ref]$notebook, 0) | |
| 9 | $section = '' | |
| 10 | $app.OpenHierarchy('synthetic.one', $notebook, [ref]$section, 0) | |
| 11 | $mixed = '<b>Bold</b> <i>italic</i> ' + [char]::ConvertFromUtf32(0x1F980) + ' e' + [char]0x0301 + ' <span style="color:#123456">tail</span>' | |
| 12 | $text = '<one:T><![CDATA[' + $mixed + ']]></one:T>' | |
| 13 | $child = '<one:OEChildren><one:OE><one:T>Preserved child</one:T></one:OE></one:OEChildren>' | |
| 14 | $tag = '<one:Tag index="0" completed="true" disabled="false" creationDate="2020-01-02T03:04:05.000Z" completionDate="2020-01-02T04:05:06.000Z"/>' | |
| 15 | $rightTag = $tag.Replace('index="0"', 'index="1"') | |
| 16 | $cases = @( | |
| 17 | @{ name = 'Join empty left right tag'; body = '<one:OE><one:T><![CDATA[]]></one:T></one:OE><one:OE>' + $rightTag + $text + '</one:OE>' }, | |
| 18 | @{ name = 'Join empty left two tags'; body = '<one:OE>' + $tag + '<one:T><![CDATA[]]></one:T></one:OE><one:OE>' + $rightTag + $text + '</one:OE>' }, | |
| 19 | @{ name = 'Join different tags'; body = '<one:OE>' + $tag + '<one:T>Left</one:T></one:OE><one:OE>' + $rightTag + $text + '</one:OE>' } | |
| 20 | ) | |
| 21 | $inputs = Join-Path $Root 'inputs' | |
| 22 | New-Item -ItemType Directory -Path $inputs | Out-Null | |
| 23 | foreach ($case in $cases) { | |
| 24 | $page = '' | |
| 25 | $app.CreateNewPage($section, [ref]$page, 0) | |
| 26 | $xml = '<one:Page xmlns:one="http://schemas.microsoft.com/office/onenote/2010/onenote" ID="' + $page + '"><one:TagDef index="0" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Fixture task"/><one:TagDef index="1" type="0" symbol="3" fontColor="automatic" highlightColor="none" name="Other task"/><one:QuickStyleDef index="0" name="p" font="Calibri" fontSize="11" fontColor="automatic" highlightColor="automatic"/><one:QuickStyleDef index="1" name="h1" font="Georgia" fontSize="18" bold="true" fontColor="#123456" highlightColor="automatic" spaceBefore="8" spaceAfter="4"/><one:Title><one:OE><one:T>' + $case.name + '</one:T></one:OE></one:Title><one:Outline><one:Position x="72" y="108"/><one:OEChildren>' + $case.body + '<one:OE><one:T>Preserved sibling</one:T></one:OE></one:OEChildren></one:Outline></one:Page>' | |
| 27 | [IO.File]::WriteAllText((Join-Path $inputs ($case.name + '.xml')), $xml, [Text.Encoding]::UTF8) | |
| 28 | $app.UpdatePageContent($xml, [DateTime]::MinValue, 1, $true) | |
| 29 | $case.page = $page | |
| 30 | } | |
| 31 | $cases | ConvertTo-Json -Depth 6 | Set-Content (Join-Path $Root 'cases.json') -Encoding UTF8 | |
| 32 | $app.SyncHierarchy($notebook) | |
| 33 | } finally { | |
| 34 | if ($notebook) { $app.CloseNotebook($notebook, $false) } | |
| 35 | [void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($app) | |
| 36 | $app = $null | |
| 37 | [GC]::Collect() | |
| 38 | [GC]::WaitForPendingFinalizers() | |
| 39 | } |
tools/test_paragraph_edit.py+93-53| ... | ... | @@ -17,60 +17,100 @@ compare = runpy.run_path(str(ROOT / 'tools/verify-document.py'))['compare'] |
| 17 | 17 | |
| 18 | 18 | |
| 19 | 19 | class ParagraphEditTest(unittest.TestCase): |
| 20 | def test_rust_joins_match_native_controls_and_retain_cold_graph_identities(self): | |
| 21 | for name, control in [('split', FIXTURE / 'joined/read'), | |
| 22 | ('inheritance', FIXTURE / 'join-edges/joined/read'), | |
| 23 | ('tags', FIXTURE / 'join-tags/joined/read')]: | |
| 24 | fixture = FIXTURE / 'rust-join' / name | |
| 25 | manifest = json.loads((fixture / 'manifest.json').read_text()) | |
| 26 | with self.subTest(fixture=name), TemporaryDirectory() as temporary: | |
| 27 | models = [] | |
| 28 | for source in ('candidate', 'native/notebook'): | |
| 29 | folder = Path(temporary) / source.replace('/', '-') | |
| 30 | shutil.copytree(fixture / 'native/read', folder / 'read') | |
| 31 | compare(fixture / source, folder / 'read') | |
| 32 | subprocess.run([EXPORTER, fixture / source / 'synthetic.one', folder / 'model'], check=True) | |
| 33 | document = json.loads((folder / 'model/document.json').read_text()) | |
| 34 | models.append({r['nodes'][r['roots']['2']]['kind']['title']: (r, page) | |
| 35 | for _, _, r, page in ordered_pages(document)}) | |
| 36 | self.assertEqual(models[0].keys(), models[1].keys()) | |
| 37 | for title, (old, page) in models[0].items(): | |
| 38 | saved, saved_page = models[1][title] | |
| 39 | self.assertEqual(page, saved_page) | |
| 40 | for outline in old['nodes'][page]['children']: | |
| 41 | if old['nodes'][outline]['kind']['type'] != 'Outline': continue | |
| 42 | for oid, node in walk(old, outline): | |
| 43 | self.assertEqual(saved['nodes'][oid]['children'], node['children']) | |
| 44 | self.assertEqual(saved['nodes'][oid]['content'], node['content']) | |
| 45 | captures = [] | |
| 46 | for folder in (control, fixture / 'native/read'): | |
| 47 | captures.append({page.get('name'): native_characters(page, page.findall('one:Outline', ns)) | |
| 48 | for path in folder.glob('page-*.xml') for page in [ET.parse(path).getroot()]}) | |
| 49 | self.assertEqual(len(manifest), {'split': 12, 'inheritance': 5, 'tags': 3}[name]) | |
| 50 | for case in manifest: | |
| 51 | for a, b in zip(captures[0][case['case']], captures[1][case['case']], strict=True): | |
| 52 | for (x, old), (y, new) in zip(a, b, strict=True): | |
| 53 | self.assertEqual(x, y) | |
| 54 | for key in old.keys() | new.keys(): | |
| 55 | default = 'automatic' if key in ('color', 'highlight') else False | |
| 56 | self.assertEqual(old.get(key, default), new.get(key, default)) | |
| 57 | ||
| 20 | 58 | def test_native_joins_preserve_inherited_styles_and_follow_tag_and_child_rules(self): |
| 21 | fixture = FIXTURE / 'join-edges' | |
| 22 | models, captures = {}, {} | |
| 23 | with TemporaryDirectory() as temporary: | |
| 24 | for phase in ('before', 'joined'): | |
| 25 | folder = Path(temporary) / phase | |
| 26 | shutil.copytree(fixture / phase / 'read', folder / 'read') | |
| 27 | compare(fixture / phase / 'notebook', folder / 'read') | |
| 28 | subprocess.run([EXPORTER, fixture / phase / 'notebook/synthetic.one', folder / 'model'], check=True) | |
| 29 | model = json.loads((folder / 'model/document.json').read_text()) | |
| 30 | models[phase] = {r['nodes'][r['roots']['2']]['kind']['title']: (r, page) | |
| 31 | for _, _, r, page in ordered_pages(model)} | |
| 32 | captures[phase] = {} | |
| 33 | for path in (folder / 'read').glob('page-*.xml'): | |
| 34 | page = ET.parse(path).getroot() | |
| 35 | captures[phase][page.get('name')] = native_characters(page, page.findall('one:Outline', ns)) | |
| 36 | self.assertEqual(len(models[phase]), 6) | |
| 37 | cases = json.loads((fixture / 'cases.json').read_text(encoding='utf-8-sig')) | |
| 38 | self.assertEqual(len(cases), 5) | |
| 39 | for case in cases: | |
| 40 | name = case['name'] | |
| 41 | with self.subTest(case=name): | |
| 42 | old, page = models['before'][name] | |
| 43 | new, new_page = models['joined'][name] | |
| 44 | self.assertEqual(page, new_page) | |
| 45 | outline, = [oid for oid in old['nodes'][page]['children'] if old['nodes'][oid]['kind']['type'] == 'Outline'] | |
| 46 | left, right, sibling = old['nodes'][outline]['children'] | |
| 47 | self.assertEqual(new['nodes'][outline]['children'], [left, sibling]) | |
| 48 | target = old['nodes'][left]['children'][-1] if name == 'Join both children' else left | |
| 49 | a, = old['nodes'][target]['content'] | |
| 50 | b, = old['nodes'][right]['content'] | |
| 51 | empty = old['nodes'][a]['kind']['text'] == '' | |
| 52 | survivor = b if empty else a | |
| 53 | self.assertEqual(new['nodes'][target]['content'], [survivor]) | |
| 54 | self.assertEqual(new['nodes'][survivor]['kind']['text'], old['nodes'][a]['kind']['text'] + old['nodes'][b]['kind']['text']) | |
| 55 | self.assertEqual(new['nodes'][survivor]['tags'], old['nodes'][a]['tags']) | |
| 56 | children = old['nodes'][left]['children'] + old['nodes'][right]['children'] | |
| 57 | self.assertEqual(new['nodes'][left]['children'], children) | |
| 58 | self.assertEqual(new['nodes'][sibling], old['nodes'][sibling]) | |
| 59 | if name == 'Join both children': | |
| 60 | parent_text, = old['nodes'][left]['content'] | |
| 61 | original = old['nodes'][parent_text] | |
| 62 | current = new['nodes'][parent_text] | |
| 63 | self.assertGreater(current['modified'], original['modified']) | |
| 64 | expected = {**original, 'modified': current['modified'], | |
| 65 | 'extra': [original['extra'][0] + [{'id': 0x880034dd, 'value': 'NoData'}]]} | |
| 66 | self.assertEqual(current, expected) | |
| 67 | before = [v for paragraph in captures['before'][name] for v in paragraph] | |
| 68 | after = [v for paragraph in captures['joined'][name] for v in paragraph] | |
| 69 | for (a, old_style), (b, new_style) in zip(before, after, strict=True): | |
| 70 | self.assertEqual(a, b) | |
| 71 | for key in old_style.keys() | new_style.keys(): | |
| 72 | default = 'automatic' if key in ('color', 'highlight') else False | |
| 73 | self.assertEqual(old_style.get(key, default), new_style.get(key, default)) | |
| 59 | for fixture_name, expected_cases in [('join-edges', 5), ('join-tags', 3)]: | |
| 60 | fixture = FIXTURE / fixture_name | |
| 61 | models, captures = {}, {} | |
| 62 | with TemporaryDirectory() as temporary: | |
| 63 | for phase in ('before', 'joined'): | |
| 64 | folder = Path(temporary) / phase | |
| 65 | shutil.copytree(fixture / phase / 'read', folder / 'read') | |
| 66 | compare(fixture / phase / 'notebook', folder / 'read') | |
| 67 | subprocess.run([EXPORTER, fixture / phase / 'notebook/synthetic.one', folder / 'model'], check=True) | |
| 68 | model = json.loads((folder / 'model/document.json').read_text()) | |
| 69 | models[phase] = {r['nodes'][r['roots']['2']]['kind']['title']: (r, page) | |
| 70 | for _, _, r, page in ordered_pages(model)} | |
| 71 | captures[phase] = {} | |
| 72 | for path in (folder / 'read').glob('page-*.xml'): | |
| 73 | page = ET.parse(path).getroot() | |
| 74 | captures[phase][page.get('name')] = native_characters(page, page.findall('one:Outline', ns)) | |
| 75 | self.assertEqual(len(models[phase]), expected_cases + 1) | |
| 76 | cases = json.loads((fixture / 'cases.json').read_text(encoding='utf-8-sig')) | |
| 77 | self.assertEqual(len(cases), expected_cases) | |
| 78 | for case in cases: | |
| 79 | name = case['name'] | |
| 80 | with self.subTest(case=name): | |
| 81 | old, page = models['before'][name] | |
| 82 | new, new_page = models['joined'][name] | |
| 83 | self.assertEqual(page, new_page) | |
| 84 | outline, = [oid for oid in old['nodes'][page]['children'] if old['nodes'][oid]['kind']['type'] == 'Outline'] | |
| 85 | left, right, sibling = old['nodes'][outline]['children'] | |
| 86 | self.assertEqual(new['nodes'][outline]['children'], [left, sibling]) | |
| 87 | target = old['nodes'][left]['children'][-1] if name == 'Join both children' else left | |
| 88 | a, = old['nodes'][target]['content'] | |
| 89 | b, = old['nodes'][right]['content'] | |
| 90 | empty = old['nodes'][a]['kind']['text'] == '' | |
| 91 | survivor = b if empty else a | |
| 92 | self.assertEqual(new['nodes'][target]['content'], [survivor]) | |
| 93 | self.assertEqual(new['nodes'][survivor]['kind']['text'], old['nodes'][a]['kind']['text'] + old['nodes'][b]['kind']['text']) | |
| 94 | self.assertEqual(new['nodes'][survivor]['tags'], old['nodes'][a]['tags']) | |
| 95 | children = old['nodes'][left]['children'] + old['nodes'][right]['children'] | |
| 96 | self.assertEqual(new['nodes'][left]['children'], children) | |
| 97 | self.assertEqual(new['nodes'][sibling], old['nodes'][sibling]) | |
| 98 | if name == 'Join both children': | |
| 99 | parent_text, = old['nodes'][left]['content'] | |
| 100 | original = old['nodes'][parent_text] | |
| 101 | current = new['nodes'][parent_text] | |
| 102 | self.assertGreater(current['modified'], original['modified']) | |
| 103 | expected = {**original, 'modified': current['modified'], | |
| 104 | 'extra': [original['extra'][0] + [{'id': 0x880034dd, 'value': 'NoData'}]]} | |
| 105 | self.assertEqual(current, expected) | |
| 106 | before = [v for paragraph in captures['before'][name] for v in paragraph] | |
| 107 | after = [v for paragraph in captures['joined'][name] for v in paragraph] | |
| 108 | for (a, old_style), (b, new_style) in zip(before, after, strict=True): | |
| 109 | self.assertEqual(a, b) | |
| 110 | for key in old_style.keys() | new_style.keys(): | |
| 111 | default = 'automatic' if key in ('color', 'highlight') else False | |
| 112 | self.assertEqual(old_style.get(key, default), new_style.get(key, default)) | |
| 113 | ||
| 74 | 114 | |
| 75 | 115 | def test_rust_splits_match_native_controls_and_preserve_empty_typing_styles(self): |
| 76 | 116 | fixture = FIXTURE / 'rust-split' |