Documentmodus: opslaan vanuit de visuele stand herschrijft de hele bron #1613
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#1613
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Een document opslaan vanuit de visuele stand schrijft niet de bron terug die je hebt bewerkt, maar de bron die uit de heen-en-terugweg naar de rijke-tekstlaag komt. Die weg is niet byte-getrouw, dus het bestand op schijf wordt geherstructureerd op plekken waar je niets hebt aangeraakt.
Wat er gebeurt
Een echte notitie van 24 KB (koppen, GFM-tabellen, genummerde lijsten, een
<!-- timeline -->-marker) geopend in de visuele stand, één woord aan een kop toegevoegd, Cmd+S. Uitkomst: 68 diff-regels tegen het origineel. Wat er veranderde zonder dat het gevraagd was:# 1. Kern van deze notitiebelandt ná de regel die eronder stond;|---|---|wordt| --- | --- |;# <!-- timeline -->wordt<!-- timeline -->;4. Impact) uit de bron in het geheugen helemaal verdwenen.Dat laatste maakt dit meer dan een opmaakkwestie: er kan inhoud sneuvelen.
De drift stapelt bovendien. Elke opslag schrijft de dan-geldende geherstructureerde vorm weg, en de volgende ronde begint dáár weer. In een repository met versiebeheer levert dat een diff op waarin de echte wijziging tussen tientallen spookregels verdwijnt.
Waarom dit tegen de belofte in gaat
saveDocument(lib/services/file/file_service_open.dart) zegt het met zoveel woorden: "precies de bron, geen deck-scaffold, geenmarp:-kop, geen normalisatie", byte-getrouw naar het gekozen pad. DOCUMENT_MODE.md §1.1/§3 belooft hetzelfde: de bron ís de waarheid.De mechaniek eronder weet ook dat de weg niet getrouw is.
_onQuillChanged(lib/widgets/markdown_editor/markdown_editor.dart) noteert het letterlijk: "De heen-en-terugweg naar Markdown levert niet byte-getrouw dezelfde bron op (witregels rond koppen en blokken schuiven)". Daar wordt het gebruikt om een cursorwissel niet als bewerking te tellen — maar zodra er wél iets verandert, gaat de volledige geherstructureerde bron alsnog naar de notifier, en dus naar schijf.Er ís een poort die hierover gaat:
markdownVisualLimitations(lib/utils/markdown_visual_compatibility.dart) heet in zijn eigen doc "Constructs that the current rich-text bridge cannot round-trip without changing the author's Markdown source" en valt voor zulke constructies terug op de brontekst-stand. Die poort kent er drie:rawHtml,escapedPunctuation,looseTableLine. Witregel- en volgordedrift zit er niet in, dus die glipt er langs. En de belofte in datzelfde bestand dat een GFM-tabel "round-trips losslessly" klopt niet op byte-niveau — zie de scheidingsregel hierboven.Reproduceren
.mdmet koppen, een GFM-tabel met een compacte scheidingsregel (|---|---|) en een genummerde lijst.diff origineel opgeslagen.Wat een oplossing moet doen
Twee richtingen, en de keuze is een ontwerpbesluit:
markdownVisualLimitationstoelaat: wat de poort doorlaat moet ongewijzigd terugkomen, en wat dat niet haalt hoort de poort te noemen (en dus terug te vallen op de brontekst-stand).Wat er in elk geval bij hoort: een eigenschapstoets over een corpus van echte documenten —
markdownFromDocument(documentFromMarkdown(x)) == xvoor alles wat de compatibiliteitspoort toelaat, met de bestanden uitdocs/en een handvol notities als invoer. Zonder zo'n toets is elke reparatie hier één regressie van het volgende blok verwijderd. De eerdere reparaties #1507, #1558 en #1561 waren stuk voor stuk één symptoom uit deze klasse.Reikwijdte
Dit raakt het bestandsformaat en wat er in het
.mdterechtkomt — de bewaker hoort mee te kijken vóór de PR, niet erna.Gevonden tijdens het onderzoek naar de niet-opgeslagen-stip (#1611); dat was een andere oorzaak en is los opgelost.