Adapter¶
Status: Entwurf
Vorschläge von Claude – nicht verbindlich, bis Daniel sie freigibt.
Ein Adapter weiß, welche Objekte es in seinem Format gibt. Er macht aus dem Text einer Quelldatei einen Baum von Elementen, jedes mit einem strukturellen Selektor, einem Textbereich und dem Text, der für den Fingerprint genutzt wird.
@dataclass(frozen=True)
class Element:
selector: str # "md:heading:Career/Advisor"
kind: str # "heading", "paragraph", "need", ...
label: str # short text for UIs and the CLI
start: int # character offsets in the source
end: int
line: int # 1-based, for humans and editors
text: str # source text of the element (fingerprint input)
children: tuple["Element", ...] = ()
class Adapter(Protocol):
name: str # "md", "rst", "text"
suffixes: tuple[str, ...] # (".md",), (".rst",), (".txt",)
def elements(self, source: str, path: str) -> list[Element]: ...
Adapter sind pur: kein Dateizugriff, kein Zustand. Dadurch lassen sie sich schnell mit Golden Files testen (Quelle rein, Elementliste als JSON raus), und die Sphinx-Erweiterung kann sie später wiederverwenden.
Strukturelle Selektoren¶
Ein Selektor ist ein String <adapter>:<kind>:<path>. Ihn sehen Nutzer in der CLI, und ihn speichert das Dateiformat.
Adapter |
Selektor |
Element |
|---|---|---|
md |
|
der Abschnitt unter diesem Überschriftenpfad, mit seinem Inhalt |
md |
|
der 2. Absatz in diesem Abschnitt |
md |
|
Listenpunkt, Tabellenzeile, Codeblock – pro Abschnitt nummeriert |
md |
|
ein Frontmatter-Feld |
rst |
|
wie in Markdown |
rst |
|
ein Need, gefunden über seine ID – der robusteste Selektor, den es gibt |
rst |
|
eine Option eines Needs |
rst |
|
die erste |
text |
|
reiner Text |
html |
|
eine gerenderte Webseite, im Browser gefunden (Web-Skript) |
py |
|
eine Klasse, Methode oder Funktion, benannt nach ihrem Pfad im Code (Python-Quelltext wird je Kl... (DEC_PYTHON_ADAPTER)) |
py |
|
Anweisungen außerhalb von Funktionen: der Importblock, eine Zuweisung nach Namen, jede andere Anweisung gezählt je Bereich |
json |
|
das Dokument, ein Objekt-Mitglied, ein Array-Eintrag – ein JSON Pointer (JSON- und YAML-Dateien werd... (DEC_DATA_ADAPTERS)) |
yaml |
|
dasselbe für YAML |
Vorschlag: Element-Selektoren sind Pfade aus Überschriften plus ein Zähler pro Abschnitt PROP_SELECTORS
|
Überschriftenpfade überstehen das Verschieben ganzer Abschnitte und Änderungen in anderen Abschnitten. Der Zähler ( |
Markdown¶
Parser: markdown-it-py (CommonMark + Tabellen); Token-Maps liefern Zeilenbereiche, Offsets ergeben sich aus den Zeilenbereichen.
Elemente: Überschriften-Abschnitte, Absätze, Listenpunkte (verschachtelt), Tabellenzeilen, Codeblöcke, Frontmatter-Felder (YAML-Schlüssel auf oberster Ebene).
Als Erstes gebaut, für Host-Apps (In einem Host bekommt das O... (DEC_HOST_ELEMENTS)): deciwa.core.adapters.markdown liefert die Elemente als Dicts in der Form des Sphinx-Elementindex (selector, kind, label, line, end, text, fingerprint, parent) – Abschnitte, Absätze, Listenpunkte, Tabellenzeilen, Codeblöcke und Bilder. Frontmatter-Felder und Zeichen-Offsets fehlen noch. Der Text eines Elements ist das, was Lesende sehen (ohne Inline-Markup), wie in den Sphinx- und Browser-Adaptern; so wird ein in einer dargestellten Ansicht markiertes Zitat darin gefunden, und ein geändertes Linkziel allein macht ein Element nicht veraltet. Zeilen zählen ab 1 und schließen die Frontmatter ein. Die Engine nutzt den Adapter für jede Datei, die kein Build-Index kennt.
rST und Sphinx-Needs¶
docutils merkt sich an jedem Knoten die Quellzeilen. Unbekannte Directives und Rollen (Sphinx, Sphinx-Needs, beliebige Erweiterungen) werden als generische Platzhalter registriert, sodass eine Datei ohne |
Vorschlag: aus |
Python¶
Python-Quelltext wird je Kl... (DEC_PYTHON_ADAPTER) – deciwa.core.adapters.python liest .py- und .pyi-Dateien mit ast aus der Standardbibliothek:
Elemente: Klassen, Funktionen und Methoden, verschachtelt wie im Code (das Elternelement einer Methode ist ihre Klasse, das einer inneren Funktion die Funktion darum); Definitionen in
if-,try- oderwith-Blöcken zählen auf ihrer Ebene.Anweisungen außerhalb von Funktionen – auf Modulebene und im Rumpf einer Klasse – sind Elemente der Art
statement: der Docstring (py:statement:__doc__), ein Block von Importen (py:statement:imports), eine Zuweisung nach ihrem Namen (py:statement:API,py:statement:Engine.size), jede andere Anweisung gezählt je Bereich (py:statement:#1– z. B.if __name__ == "__main__":;py:statement:Engine#2). Anweisungen in einer Funktion gehören zur Funktion.Selektoren:
py:<class|function>:<path>–py:function:Engine.start; eine zweite Definition mit demselben Pfad (ein Property-Setter, eine Neudefinition) bekommt~2.Text: der Quelltext der Definition, Dekoratoren eingeschlossen – jede Änderung an einer freigegebenen Funktion macht sie veraltet. Bezeichnung: ihre erste Zeile (
def start(self) -> Handshake:).Zeilen:
lineist die erste Zeile (der erste Dekorator),enddie letzte.Eine Datei mit Syntaxfehler hat keine Elemente.
Die Elementarten
class,functionundstatementlassen sich in den Review-Regeln nennen.
Noch nicht: Kommentare als Elemente; JS, TS und C (tree-sitter, als Nächstes).
HTML-Dateien¶
HTML-Dateien liest ein Adap... (DEC_HTML_ADAPTER) – deciwa.core.adapters.html liest .html- und .htm-Dateien mit html.parser aus der Standardbibliothek und folgt den Regeln des DOM-Scans im Browser (Das Web-Skript findet die E... (PROP_DOM_ADAPTER)): die Review-Wurzel (main, ein einzelnes article, sonst body), Abschnitte nach der Überschriftengliederung, Absätze, Listenpunkte, Tabellenzeilen, Codeblöcke, Hinweise und Bilder, gezählt je Abschnitt – dieselben html:-Selektoren. Jedes Element trägt seine Anfangszeile, seine Endzeile und sein tag. Ohne Browser gibt es kein Layout, deshalb zählen auch per CSS versteckte Elemente und kleine Icons.
Ein Host markiert jedes Start-Tag der dargestellten Seite mit seiner Quellzeile; das Overlay ordnet jedes Element dem Tag seiner Art auf seiner Zeile zu. Dargestellte Seite und Quelltextansicht teilen sich so ihre Elemente, und Feedback auf der Seite zeigt auf die Quellzeile. Eine unter ihrer Adresse gespeicherte Webseite (aus einem Browser) weicht dem Adapter, sobald ihre Quelldatei bekannt ist.
In einem Host werden die Zeilen jedes Elements zu einem Block der Code-Ansicht zusammengefasst (Host-Apps).
Datendateien (JSON, YAML)¶
JSON- und YAML-Dateien werd... (DEC_DATA_ADAPTERS) – deciwa.core.adapters.json liest .json-Dateien, deciwa.core.adapters.yaml liest .yaml- und .yml-Dateien. Beide liefern dieselben Elemente:
Elemente: das Dokument, jedes Objekt-Mitglied und jeder Array-Eintrag, Blätter eingeschlossen, verschachtelt wie in der Datei. Arten:
object,array,value– sie können in den Review-Regeln genannt werden. Ein YAML-Mapping ist ein Objekt, eine Sequenz ein Array, ein Skalar ein Wert.Selektoren: der Name des Adapters plus der JSON Pointer des Elements (RFC 6901):
json:für das Dokument,json:/color/primary,json:/versions/0;~und/in einem Schlüssel werden zu~0und~1. Jedes Element trägt außerdem seinenpointer, damit ein Host den Knoten in seiner eigenen Baumansicht findet (Host-Apps).Text: der Wert in kanonischer Form – sortierte Schlüssel, keine Leerzeichen (
json.dumps(value, sort_keys=True, ensure_ascii=False, separators=(",", ":"))). Neu formatieren, neu einrücken oder Schlüssel umsortieren behält den Review-Stand; ein geänderter Wert macht sein Element und alle Eltern veraltet.Bezeichnung: der Schlüssel plus eine kurze Vorschau –
primary: "#1f6feb",needs {312},steps [4]; Array-Einträge nehmen ihren Index als Schlüssel.Zeilen:
lineist die Zeile des Schlüssels (Array-Einträge: ihre erste Zeile),enddie letzte Zeile des Werts.JSON: die Standardbibliothek liefert keine Positionen, deshalb liest ein kleiner Scanner die Datei einmal und merkt sich die Zeilen jedes Schlüssels und Werts. Nur Standard-JSON – kein JSON Lines, keine Kommentare, kein
NaN.YAML: der Safe Loader von PyYAML – der Knotenbaum liefert die Positionen, der Safe Constructor die Werte. Nur das erste Dokument wird gelesen. Schlüssel, die keine Strings sind, werden zu ihrer JSON-Form (
1,true). Anker, Aliase und Merge-Schlüssel sind in den Werten aufgelöst; Mitglieder, die ein Mapping über einen Merge-Schlüssel (<<: *base) bekommt, und das Innere eines Alias werden dort reviewt, wo sie stehen, nicht als eigene Elemente.Eine ungültige Datei hat keine Elemente.
Ein Array-Index verschiebt sich, wenn davor ein Eintrag eingefügt wird. Die Engine folgt dann dem reviewten Wert zu seinem neuen Index (State-Engine, Verschobene Elemente), so bleibt das Review beim Wert.
Reiner Text¶
Absätze sind durch Leerzeilen getrennte Blöcke; Zeilen sind eine zweite Ebene. Alles andere ist freie Textauswahl.
HTML im Browser¶
Ein kleiner Adapter in JavaScript liest das DOM der Review-Wurzel und liefert dieselbe Elementliste wie die anderen Adapter:
Der Fingerprint ist SHA-256 über den normalisierten Text, berechnet mit Seiten mit Build-Hinweisen ( |
Freie Textauswahl¶
Vorschlag: Eine freie Textauswahl ist ein Textzitat innerhalb ihres nächsten Elements PROP_FREE_TEXT
|
Wird ein Wort markiert, speichert deciwa den strukturellen Selektor des Elements, das es enthält, plus einen |