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) <noreply@anthropic.com>
This commit is contained in:
Michael Stangl
2026-09-18 16:36:11 +02:00
co-authored by Claude Opus 4.8
parent 40d79f4993
commit 008f497b5d
2 changed files with 100 additions and 56 deletions
+53 -55
View File
@@ -2360,6 +2360,47 @@
;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein ;; sich um einen frischen Bau oder einen Editier-Neuaufbau handelte (kein
;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von ;; Sonderfall mehr noetig). Aufgerufen aus dem *error*-Handler von
;; vf-linienzug-modus. ;; 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 (defun vfl-modus-abbruch-sichern (lastEnt vfl-nummer anzahl-gf anzahl-vf
startpunkt frame as-seite es-seite marker / startpunkt frame as-seite es-seite marker /
e cnt vfl-ins wickel-erg grund fwd n trunc) e cnt vfl-ins wickel-erg grund fwd n trunc)
@@ -6425,32 +6466,11 @@
(setq lastEnt (vf-lastent-ohne-attribute)) (setq lastEnt (vf-lastent-ohne-attribute))
(setq anzahl-gf 0 anzahl-vf 0 frame nil) (setq anzahl-gf 0 anzahl-vf 0 frame nil)
(setq old-error *error*) (setq old-error *error*)
;; Abbruch-Handler: zuerst *error* zuruecksetzen (kein rekursiver ;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam fuer alle drei
;; Wiedereintritt bei einem Fehler waehrend des Wickelns), dann wickeln. ;; Modi, siehe vfl-abbruch-handler-installieren). old-error/lastEnt/vfl-nummer/
;; Laeuft der Aufruf innerhalb einer ssg-start-Sitzung (Editier-Pfad via ;; anzahl-gf/anzahl-vf/startpunkt/frame/as-seite/es-seite/dbg-an sind hier als
;; c:VARIOFOERDERER_EDIT -> vfl-edit-ent), wird diese mit ssg-end sauber ;; Locals gebunden und werden vom Handler zur Abbruchzeit dynamisch gesehen.
;; geschlossen (Undo-Gruppe, gesicherte Systemvariablen, *error* aus dem (vfl-abbruch-handler-installieren "linienzug")
;; 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))))
(if (vfl-wizard-aktiv) (if (vfl-wizard-aktiv)
(vl-catch-all-apply 'vflw-gruppe-punkt-hoehe-impl (vl-catch-all-apply 'vflw-gruppe-punkt-hoehe-impl
@@ -8980,19 +9000,9 @@
;; dbg-old-error) und uebernimmt das dbgclose. *ssg-ils-dim* zuruecksetzen: ;; dbg-old-error) und uebernimmt das dbgclose. *ssg-ils-dim* zuruecksetzen:
;; Begruendung siehe gleichlautende Stelle in vf-linienzug-modus. ;; Begruendung siehe gleichlautende Stelle in vf-linienzug-modus.
(setq old-error dbg-old-error) (setq old-error dbg-old-error)
(setq *error* ;; Scharfen Abbruch-Wickel-Handler installieren (gemeinsam, siehe
(function (lambda (msg) ;; vfl-abbruch-handler-installieren).
(setq *error* old-error) (vfl-abbruch-handler-installieren "linienzug3")
(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))))
(setq n (length segmente)) (setq n (length segmente))
;; erste/letzte "Linie" fuer die Trimmung bestimmen ;; erste/letzte "Linie" fuer die Trimmung bestimmen
(setq first-line -1 last-line -1 i 0) (setq first-line -1 last-line -1 i 0)
@@ -9758,22 +9768,10 @@
;; dbg-Hook selbst) und uebernehmen das dbgclose gleich mit, sonst wuerde ;; dbg-Hook selbst) und uebernehmen das dbgclose gleich mit, sonst wuerde
;; bei einem Abbruch ab hier doppelt geschlossen. ;; bei einem Abbruch ab hier doppelt geschlossen.
(setq old-error dbg-old-error) (setq old-error dbg-old-error)
;; *ssg-ils-dim* hier zuruecksetzen - Begruendung siehe gleichlautende Stelle ;; Scharfen Abbruch-Wickel-Handler installieren (loest den fruehen dbg-Hook ab,
;; im Handler von vf-linienzug-modus (Abbruch kehrt nie zum Aufrufer zurueck, ;; Ruecksprungziel bleibt dbg-old-error via old-error). Gemeinsam fuer alle
;; der den Override abraeumen wuerde). ;; Modi - siehe vfl-abbruch-handler-installieren.
(setq *error* (vfl-abbruch-handler-installieren "linienzug2")
(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))))
;; erste/letzte Gerade fuer AS/ES-Trimmung bestimmen ;; erste/letzte Gerade fuer AS/ES-Trimmung bestimmen
(setq first-line -1 last-line -1 i 0) (setq first-line -1 last-line -1 i 0)
(while (< i n) (while (< i n)
+47 -1
View File
@@ -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 ;; BEFEHL: TEST_UNIT
;; ------------------------------------------------------------ ;; ------------------------------------------------------------
@@ -927,7 +973,7 @@
(foreach fn '("tu-tests-core" "tu-tests-lang" "tu-tests-id" "tu-tests-vfcore" (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-export" "tu-tests-gf" "tu-tests-omni" "tu-tests-kreisel"
"tu-tests-sepliste" "tu-tests-json" "tu-tests-geruest" "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)) (tu-run-block fn))
(princ "\n\n----------------------------------------------------------------") (princ "\n\n----------------------------------------------------------------")