Datenstruktur des Notiz-Baums #7
Reference in New Issue
Block a user
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?
Part of #1
Question
Welche Datenstruktur trägt den Notiz-Baum, und wie entsteht sie?
Entschieden ist: rekursiv einlesen, als aufklappbarer Baum darstellen. Offen ist alles darunter:
Vec<Node>-Kindern, oder eine flache Arena mit Indizes? Der Baum muss in Dioxus-Props leben, alsoClone + PartialEqsein — was das an Kosten bedeutet, gehört zur Entscheidung.expanded: boolpro Knoten) oder daneben (einHashSet<PfadBuf>der offenen Ordner)? Das eine vermischt Daten und UI-Zustand, das andere hält sie getrennt..md, oder alle Dateien mit.mdhervorgehoben? Werden Ordner ohne jede.md-Datei angezeigt?.dionotes/selbst und mit versteckten Ordnern wie.git?Zielgröße: die Typdefinition, plus die Regel, welche Einträge im Baum landen.
Antwort
Der Typ
Verschachtelt statt Arena: Der Baum wird beim Öffnen einmal aufgebaut und danach nur gelesen — die Indirektion einer Arena zahlt sich erst aus, wenn Knoten wandern oder sich gegenseitig referenzieren. Als Enum statt als Struct mit
children: Vec<_>, weil damit unmöglich wird, was ein Struct erlaubt: eine Datei mit Kindern.Eine
File-Variante für alle Dateien, keine Sonderbehandlung für.md. Wer wissen will, ob es eine Notiz ist, fragtpath.extension()— eine Zeile an der Stelle, die es braucht, statt einer Klassifizierung, die der Baum beim Aufbau festschreibt. Damit muss heute auch nicht entschieden werden, was außer.mdals Notiz gilt.Die Einlese-Regeln
.md.Die Punkt-Regel gilt bewusst auch für Verzeichnisse: Sie erledigt
.dionotes/und.git/in einem Aufwasch, ohne eine Ausnahmeliste, die bei.obsidian/oder.vscode/wieder wachsen müsste. Ein.git/im Baum wäre nicht nur unübersichtlich — es sind schnell Tausende Objektdateien, die das Einlesen beim Öffnen spürbar verzögern.Symlinks werden über
symlink_metadata()erkannt (das folgt dem Link nicht,metadata()schon) und übersprungen. Ihnen zu folgen hieße, einen Zyklusschutz über kanonisierte Pfade mitzuführen — realer Aufwand für einen Fall, den in einem Notizordner kaum jemand hat, während ein Hänger beim Öffnen für den Nutzer schwer zu deuten wäre.Case-insensitive sortiert vor allem deshalb, weil
Notizen/undnotizen/sonst je nach Plattform an ganz verschiedenen Stellen stehen — unter Windows sind sie ohnehin derselbe Ordner.Der Aufbau
std::fs::read_dirmit eigener Rekursion.walkdir 2.5.0liegt zwar bereits transitiv im Lockfile, iteriert aber flach — daraus einen Baum zusammenzusetzen ist mehr Code, nicht weniger.Der Aufklapp-Zustand
Liegt neben dem Baum, nicht darin:
Signal<HashSet<PathBuf>>der offenen Ordner.Der Grund ist die Refresh-Entscheidung aus dem Charting: Es gibt keinen Filewatcher, der Nutzer liest bei Bedarf neu ein. Steckte
expanded: boolim Knoten, klappte jeder Refresh sämtliche Ordner zu — der UI-Zustand würde mit den Daten weggeworfen, die er gar nicht beschreibt. So überlebt er den Neuaufbau ohne Übertragungslogik.Nicht persistiert. Beim Öffnen ist die oberste Ebene sichtbar, alle Unterordner sind zu. Andernfalls schriebe die App bei jedem Auf- und Zuklappen auf die Platte, für einen Zustand, den der Nutzer in zwei Klicks wiederherstellt — und es wäre das erste Feld im Marker, das nicht aus einer Entscheidung stammt, sondern aus Bequemlichkeit. Nachrüstbar als ein Feld mit
#[serde(default)].Wo der Baum lebt — abgeleitet, nicht separat entschieden
Der Baum gehört nicht in
struct Workspace, sondern in ein eigenes Signal im Context. Ein Refresh ersetzt den Baum, ohne dass sich am Workspace etwas ändert; läge er im Workspace, zöge jede Baum-Änderung dessen Vergleich nach sich. Der Workspace beschreibt, welcher Ordner offen ist — der Baum ist daraus abgeleiteter Zustand.Widerspruch willkommen; das folgt aus #5, war aber nicht Teil der Fragerunde.
Bekannte Grenzen
Dropeines verschachtelten Baums laufen über den Stack. Bei realistischen Verzeichnistiefen unkritisch, aber es ist die Stelle, die bei einem pathologischen Workspace zuerst aufgibt.PartialEqauf dem Baum. Bekommt eine rekursive Komponente je einenTreeNodeals Prop, vergleicht Dioxus bei jedem Rendern den ganzen Teilbaum. Fällt bei großen Workspaces zusammen mit dem Einlesen ins Gewicht — beides gehört zum Fog-Punkt „Große Workspaces" auf der Map, nicht hierher.