From 008f497b5d76b0367ea75e490f279cc3d8eb63c2 Mon Sep 17 00:00:00 2001 From: Michael Stangl Date: Fri, 18 Sep 2026 16:35:40 +0200 Subject: [PATCH] VF-Linienzug: Abbruch-Wickel-Handler der 3 Modi zentralisieren Der scharfe *error*-Abbruch-Handler (wickelt Teil-Geometrie bei ESC/(exit) zu einem VF_n-Block mit vollem Journal) stand in Modus 1/2/3 dreimal WORTGLEICH, nur der Marker-String unterschied sich. Jetzt eine gemeinsame Funktion vfl-abbruch-handler-installieren (marker), die *error* als Lambda-Liste mit eingebautem Marker-Literal setzt (list-Konstruktion, kein globaler Zwischen- zustand). Der fragile Wickel-Rumpf existiert damit nur noch EINMAL - kuenftige Fixes an einer Stelle statt drei. Die je-Modus unterschiedliche old-error-Quelle bleibt an der Aufrufstelle (M1: *error*, M2/M3: dbg-old-error via fruehem dbg-Hook) - nur der wortgleiche Handler-Rumpf wird geteilt. Alle vom Handler zur Abbruchzeit dynamisch aufgeloesten Locals (old-error/lastEnt/vfl-nummer/... /dbg-an) sind unveraendert im Modus-Frame gebunden (VERTRAG in der Funktion dokumentiert). Tests (tests/test_unit.lsp, tu-tests-vfl-abbruch): der Konstruktor erzeugt eine gueltige (lambda (msg) ...)-Struktur und baut GENAU den uebergebenen Marker als letztes Argument des vfl-modus-abbruch-sichern-Aufrufs ein (linienzug2 vs linienzug3 geprueft). 240/240 LISP-Unit-Tests gruen in BricsCAD; keine neuen Python-Failures (11 = HEAD-Baseline). Modus-1-ESC-Abbruch interaktiv bestaetigt (Teilkette bleibt als editierbarer VF_n-Block stehen). Co-Authored-By: Claude Opus 4.8 (1M context) --- Lisp/vf_linienzug.lsp | 108 +++++++++++++++++++++--------------------- tests/test_unit.lsp | 48 ++++++++++++++++++- 2 files changed, 100 insertions(+), 56 deletions(-) diff --git a/Lisp/vf_linienzug.lsp b/Lisp/vf_linienzug.lsp index 41acffb..6db3ff6 100644 --- a/Lisp/vf_linienzug.lsp +++ b/Lisp/vf_linienzug.lsp @@ -2360,6 +2360,47 @@ ;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein ;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von ;; vf-linienzug-modus. +;; Den scharfen Abbruch-Wickel-Handler als *error* installieren - gemeinsam +;; fuer alle drei Linienzug-Modi (frueher 3x wortgleich ausgeschrieben, nur der +;; Marker-String "linienzug"/"linienzug2"/"linienzug3" unterschied sich). +;; +;; Der Handler faengt jeden Abbruch (ESC / (exit) / Laufzeitfehler) ab, wickelt +;; die bis dahin gebaute Teil-Geometrie per vfl-modus-abbruch-sichern zu einem +;; VF_n-Block mit dem vollen Journal (Marker als XDATA) und stellt *error* +;; wieder her. Details/Begruendung (dynamic scoping, *ssg-ils-dim*-Reset, +;; ssg-end) siehe die ausfuehrlichen Kommentare an der frueheren Aufrufstelle +;; in vf-linienzug-modus. +;; +;; VERTRAG (dynamic scoping): Der Aufrufer MUSS als Locals gebunden haben und +;; VOR diesem Aufruf gesetzt haben: old-error (Ruecksprung-*error*), lastEnt, +;; vfl-nummer, anzahl-gf, anzahl-vf, startpunkt, frame, as-seite, es-seite, +;; dbg-an. Sie werden vom Handler-Lambda zur ABBRUCHZEIT dynamisch aufgeloest - +;; also gegen den dann noch aktiven Modus-Frame, nicht gegen diesen Konstruktor +;; (der ist beim Abbruch laengst zurueckgekehrt). marker wird dagegen ZUR +;; INSTALL-ZEIT als String-Literal in den Lambda eingebacken (list-Konstruktion), +;; damit der Handler ihn ohne globalen Zwischenzustand selbst traegt. +(defun vfl-abbruch-handler-installieren (marker) + ;; *error* auf eine LAMBDA-LISTE setzen, in die der marker als String-Literal + ;; eingebaut ist (list-Konstruktion). AutoLISP ruft *error* als Funktion auf, + ;; egal ob (lambda ...) oder (function (lambda ...)) - die reine Lambda-Liste + ;; genuegt und vermeidet eval/function-Feinheiten. Alle uebrigen Symbole + ;; (old-error, lastEnt, ...) bleiben ungequotet-frei und werden zur Aufrufzeit + ;; dynamisch aufgeloest. + (setq *error* + (list 'lambda '(msg) + '(setq *error* old-error) + '(setq *ssg-ils-dim* nil) + (list 'vfl-modus-abbruch-sichern 'lastEnt 'vfl-nummer 'anzahl-gf 'anzahl-vf + 'startpunkt 'frame 'as-seite 'es-seite marker) + '(if dbg-an + (progn + (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) + (dbgreturn nil) + (dbgclose))) + '(if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end)) + '(princ))) + *error*) + (defun vfl-modus-abbruch-sichern (lastEnt vfl-nummer anzahl-gf anzahl-vf startpunkt frame as-seite es-seite marker / e cnt vfl-ins wickel-erg grund fwd n trunc) @@ -6425,32 +6466,11 @@ (setq lastEnt (vf-lastent-ohne-attribute)) (setq anzahl-gf 0 anzahl-vf 0 frame nil) (setq old-error *error*) - ;; Abbruch-Handler: zuerst *error* zuruecksetzen (kein rekursiver - ;; Wiedereintritt bei einem Fehler waehrend des Wickelns), dann wickeln. - ;; Laeuft der Aufruf innerhalb einer ssg-start-Sitzung (Editier-Pfad via - ;; c:VARIOFOERDERER_EDIT -> vfl-edit-ent), wird diese mit ssg-end sauber - ;; geschlossen (Undo-Gruppe, gesicherte Systemvariablen, *error* aus dem - ;; Sitzungs-Frame) - sonst bliebe die von ssg-start geoeffnete Undo-Gruppe - ;; offen. - ;; *ssg-ils-dim*: der Editier-/Konverter-Pfad setzt vor dem Replay einen - ;; Dimensions-Override und raeumt ihn NACH der Rueckkehr wieder ab. Bei einem - ;; Abbruch kehrt der Aufruf nie dorthin zurueck (der Handler beendet den - ;; Befehl), daher MUSS der Override hier zurueckgesetzt werden - sonst bliebe - ;; er fuer den Rest der Sitzung stehen und alle folgenden Einfuegungen liefen - ;; in der falschen Dimension. - (setq *error* - (function (lambda (msg) - (setq *error* old-error) - (setq *ssg-ils-dim* nil) - (vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf - startpunkt frame as-seite es-seite "linienzug") - (if dbg-an - (progn - (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) - (dbgreturn nil) - (dbgclose))) - (if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end)) - (princ)))) + ;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam fuer alle drei + ;; Modi, siehe vfl-abbruch-handler-installieren). old-error/lastEnt/vfl-nummer/ + ;; anzahl-gf/anzahl-vf/startpunkt/frame/as-seite/es-seite/dbg-an sind hier als + ;; Locals gebunden und werden vom Handler zur Abbruchzeit dynamisch gesehen. + (vfl-abbruch-handler-installieren "linienzug") (if (vfl-wizard-aktiv) (vl-catch-all-apply 'vflw-gruppe-punkt-hoehe-impl @@ -8980,19 +9000,9 @@ ;; dbg-old-error) und uebernimmt das dbgclose. *ssg-ils-dim* zuruecksetzen: ;; Begruendung siehe gleichlautende Stelle in vf-linienzug-modus. (setq old-error dbg-old-error) - (setq *error* - (function (lambda (msg) - (setq *error* old-error) - (setq *ssg-ils-dim* nil) - (vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf - startpunkt frame as-seite es-seite "linienzug3") - (if dbg-an - (progn - (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) - (dbgreturn nil) - (dbgclose))) - (if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end)) - (princ)))) + ;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam, siehe + ;; vfl-abbruch-handler-installieren). + (vfl-abbruch-handler-installieren "linienzug3") (setq n (length segmente)) ;; erste/letzte "Linie" fuer die Trimmung bestimmen (setq first-line -1 last-line -1 i 0) @@ -9758,22 +9768,10 @@ ;; dbg-Hook selbst) und uebernehmen das dbgclose gleich mit, sonst wuerde ;; bei einem Abbruch ab hier doppelt geschlossen. (setq old-error dbg-old-error) - ;; *ssg-ils-dim* hier zuruecksetzen - Begruendung siehe gleichlautende Stelle - ;; im Handler von vf-linienzug-modus (Abbruch kehrt nie zum Aufrufer zurueck, - ;; der den Override abraeumen wuerde). - (setq *error* - (function (lambda (msg) - (setq *error* old-error) - (setq *ssg-ils-dim* nil) - (vfl-modus-abbruch-sichern lastEnt vfl-nummer anzahl-gf anzahl-vf - startpunkt frame as-seite es-seite "linienzug2") - (if dbg-an - (progn - (dbgmsg (strcat "=== ABBRUCH: " (if msg msg "(exit)") " ===")) - (dbgreturn nil) - (dbgclose))) - (if (and (boundp '*ssg-start-stack*) *ssg-start-stack*) (ssg-end)) - (princ)))) + ;; Scharfen Abbruch-Wickel-Handler installieren (loest den fruehen dbg-Hook ab, + ;; Ruecksprungziel bleibt dbg-old-error via old-error). Gemeinsam fuer alle + ;; Modi - siehe vfl-abbruch-handler-installieren. + (vfl-abbruch-handler-installieren "linienzug2") ;; erste/letzte Gerade fuer AS/ES-Trimmung bestimmen (setq first-line -1 last-line -1 i 0) (while (< i n) diff --git a/tests/test_unit.lsp b/tests/test_unit.lsp index 1e8b8d8..7877ad1 100644 --- a/tests/test_unit.lsp +++ b/tests/test_unit.lsp @@ -905,6 +905,52 @@ ) +;; ------------------------------------------------------------ +;; TESTS: vf_linienzug.lsp - gemeinsamer Abbruch-Handler-Konstruktor +;; ------------------------------------------------------------ +;; vfl-abbruch-handler-installieren baut *error* als Lambda-Liste, in die der +;; Marker ("linienzug"/"linienzug2"/"linienzug3") als String-Literal eingebaut +;; ist. Der wortgleiche Wickel-Handler steht damit nur noch EINMAL statt 3x. +;; Getestet wird die KONSTRUKTION (Marker an der richtigen Stelle, Struktur +;; aufrufbar) - NICHT ein echter Abbruch (der braeuchte Zeichnung/Geometrie). +;; *error* wird gesichert und wiederhergestellt, damit der Testlauf unberuehrt +;; bleibt. +(defun tu-tests-vfl-abbruch ( / _save _h _call) + (princ "\n[vf_linienzug/Abbruch-Handler]") + (if (null (atoms-family 1 '("VFL-ABBRUCH-HANDLER-INSTALLIEREN"))) + (tu-record nil "vfl-abbruch/modul" + "vf_linienzug.lsp nicht geladen (VFL-ABBRUCH-HANDLER-INSTALLIEREN fehlt)") + (progn + (setq _save *error*) + (setq _h (vfl-abbruch-handler-installieren "linienzug2")) + ;; Rueckgabe = das gesetzte *error* (identisch) + (tu-true "vfl-abbruch/setzt-error" (equal _h *error*)) + ;; Struktur: (lambda (msg) ...) - erstes Element lambda, zweites (msg) + (tu-eq "vfl-abbruch/ist-lambda" 'lambda (car _h)) + (tu-eq "vfl-abbruch/arg-msg" '(msg) (cadr _h)) + ;; Der vfl-modus-abbruch-sichern-Aufruf im Rumpf traegt GENAU den + ;; uebergebenen Marker als letztes Argument (nicht einen anderen Modus). + (setq _call (car (vl-member-if + (function (lambda (f) + (and (listp f) (eq (car f) 'vfl-modus-abbruch-sichern)))) + (cddr _h)))) + (tu-true "vfl-abbruch/ruft-sichern" (not (null _call))) + (tu-eq "vfl-abbruch/marker-linienzug2" "linienzug2" (last _call)) + ;; Anderer Marker -> anderes Literal an derselben Position + (vfl-abbruch-handler-installieren "linienzug3") + (setq _call (car (vl-member-if + (function (lambda (f) + (and (listp f) (eq (car f) 'vfl-modus-abbruch-sichern)))) + (cddr *error*)))) + (tu-eq "vfl-abbruch/marker-linienzug3" "linienzug3" (last _call)) + ;; *error* wiederherstellen - Testlauf darf nicht mit einem Wickel-Handler + ;; ohne gebundene Modus-Locals zurueckbleiben. + (setq *error* _save) + ) + ) +) + + ;; ------------------------------------------------------------ ;; BEFEHL: TEST_UNIT ;; ------------------------------------------------------------ @@ -927,7 +973,7 @@ (foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore" "tu-tests-export" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel" "tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" - "tu-tests-sepscan" "tu-tests-vfl-segs") + "tu-tests-sepscan" "tu-tests-vfl-segs" "tu-tests-vfl-abbruch") (tu-run-block fn)) (princ "\n\n----------------------------------------------------------------")