; .EnTete "Le-Lisp (c) version 15.2" " " "Noyau d'editeur: Edit"
; .EnPied "edit.ll" "%" " "
; .SuperTitre "Edit"
;
; .Centre "*****************************************************************"
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA "
; .Centre "*****************************************************************"
;
; .Centre "$Header: edit.ll,v 4.1 88/01/13 12:35:05 kuczynsk Rel $"
(unless (>= (version) 15.2)
(error 'load 'erricf 'edit))
; .Section "Edit"
; .SSection "Contenu."
; Edit est le noyau pouvant e↑tre partage' par plusieurs type d'e'diteur
; de texte. Ceci permet de gagner de la place symbole pour des editeurs
; de type PEPE. Un exemple d'utilisation se trouve dans le fichier medite.ll
; Reste a` faire:
; - Trouver la sous-expression de niveau N qui contient le curseur.
; - Rajouter une marque auxilliaire non disponible par cle'.
; - Reflechir au undo.
; - Reflechir a la vrai mark qui ne soit pas un positionnement absolu.
; .SSection "De'claration."
(defvar #:sys-package:colon 'edit)
(add-feature 'edit)
(defstruct buffer
(buffer (vector #:edit:null-strg))
(modif 3)
(nolgn 0)
(markx ())
(marky ())
(ldo 0) )
(defstruct #.#:sys-package:colon
(pbuffer (#:buffer:make))
(curx 0)
(cury 0) )
; .SSection "Les variables globales."
(defvar :search-strg ())
(defvar :null-strg (makestring 1 0))
(defvar :kill-buffer ())
(defvar :warning #[
"Argument is not an editor." ; 0
"You're at the beginning of the buffer." ; 1
"You're at the end of the buffer." ; 2
"Null search string." ; 3
"Can't find it." ; 4
"Mark not set." ; 5
"Non-existant kill buffer." ; 6
"New file." ; 7
"File not found." ; 8
"Can't write file" ; 9
"Mark set." ; 10
"Read-only buffer." ; 11
])
; .SSection "Les macros locales."
(defmacro :eolp (e)
`(eq (:curx ,e) (sref (:s ,e) 0)) )
(defmacro :bolp (e)
`(eq (:curx ,e) 0) )
(defmacro :eobp (e)
`(eq (:cury ,e) (:nlgn ,e)) )
(defmacro :bobp (e)
`(eq (:cury ,e) 0) )
(defmacro :buf (e . a)
(if a
`(#:buffer:buffer (:pbuffer ,e) ,(car a))
`(#:buffer:buffer (:pbuffer ,e)) ))
(defmacro :modif (e . a)
(if a
`(#:buffer:modif (:pbuffer ,e) ,(car a))
`(#:buffer:modif (:pbuffer ,e)) ))
(defmacro :nlgn (e . a)
(if a
`(#:buffer:nolgn (:pbuffer ,e) ,(car a))
`(#:buffer:nolgn (:pbuffer ,e)) ))
(defmacro :set-the-mark (e x y)
`(let ( (b (:pbuffer ,e)) )
(#:buffer:markx b ,x)
(#:buffer:marky b ,y) ))
(defmacro :markx (e)
`(#:buffer:markx (:pbuffer ,e)) )
(defmacro :marky (e)
`(#:buffer:marky (:pbuffer ,e)) )
(defmacro :ldo (e . a)
(if a
`(#:buffer:ldo (:pbuffer ,e) ,(car a))
`(#:buffer:ldo (:pbuffer ,e)) ))
(defmacro :s (e)
`(vref (:buf ,e) (:cury ,e)) )
(defmacro fmax (x y)
`(let ((:k ,y))
(or (ge ,x :k) :k) ))
(defmacro fmin (x y)
`(let ((:k ,y))
(or (le ,x :k) :k) ))
(defmacro :ncpname (cn)
`(neq (typecn ,cn) 'cpname) )
(defmacro :msg (n)
`(vref :warning ,n) )
; .SSection "Le moteur d'e'dition."
(defun trap-edit (n e a)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'trap-edit (:msg 0) e)
(prog1
(selectq n
(00 (:buf-new e))
(01 (:buf-link e a))
(02 (:buf-unlink e))
(03 (:cur-top e))
(04 (:cur-buttom e))
(05 (:cur-begline e))
(06 (:cur-endline e))
(07 (:cur-left e))
(08 (:cur-right e))
(09 (:cur-up e))
(10 (:cur-down e))
(11 (:cur-ygoto e a))
(12 (:cur-xgoto e a))
(13 (:char-insert e a))
(14 (:char-delete e))
(15 (:char-delete-back e))
(16 (:word-insert e a))
(17 (:word-delete e))
(18 (:word-delete-back e))
(19 (:word-first-char e))
(20 (:word-last-char e))
(21 (:word-left e))
(22 (:word-search e a))
(23 (:word-reverse-search e a))
(24 (:word-get-symbol e))
(25 (:line-insert e))
(26 (:line-new e a))
(27 (:line-break e))
(28 (:line-return e))
(29 (:line-delete e))
(30 (:line-get e))
(31 (:line-bol e))
(32 (:line-eol e))
(33 (:mark-set e))
(34 (:mark-exchange e))
(35 (:mark-copy e))
(36 (:mark-delete e))
(37 (:file-read e a))
(38 (:file-insert e a))
(39 (:file-print e a))
(40 (:begin-expr e))
(41 (:char-transpose e))
(42 (:buf-readonly e))
(43 (:buf-readwrite e))
(44 (:matchnext e))
(45 (:matchprevious e))
(99 (:undo e)) )
(:ldo e n) )))
; .SSection "Utilisation ge'ne'rale du buffer."
(defun :buf-new (e)
(:buf e (vector :null-strg))
(:nlgn e (:curx e (:cury e 0)))
(:set-the-mark e () ())
(:modif e 3) )
(defun :buf-link (e a)
(ifn (subtypep (type-of a) '#.#:sys-package:colon)
(error 'trap-edit (:msg 0) e)
(:curx e (:curx a))
(:cury e (:cury a))
(:pbuffer e (:pbuffer a))
(:modif e (:modif a)) ))
(defun :buf-unlink (e)
(:pbuffer e (#:buffer:make))
(:curx e (:cury e 0)) )
(defun :buf-readonly (e)
(:modif e (rem (:modif e) 2)) )
(defun :buf-readwrite (e)
(:modif e (add 2 (rem (:modif e) 2))) )
(defun :check-modif (e)
(let ( (m (:modif e)) )
(when (le m 1) (exit eoc (:msg 11)))
(:modif e 5) ))
; .SSection "De'placement du curseur."
(defun :cur-top (e)
(:curx e (:cury e 0)) )
(defun :cur-buttom (e)
(:cury e (:nlgn e))
(:curx e (sref (:s e) 0)) )
(defun :cur-begline (e)
(:curx e 0) )
(defun :cur-endline (e)
(:curx e (sref (:s e) 0)) )
(defun :cur-left (e)
(ifn (:bolp e)
(:curx e (sub1 (:curx e)))
(when (:bobp e) (exit eoc (:msg 1)))
(:cur-up e)
(:cur-endline e) ))
(defun :cur-right (e)
(ifn (:eolp e)
(:curx e (add1 (:curx e)))
(when (:eobp e) (exit eoc (:msg 2)))
(:cur-begline e)
(:cur-down e) ))
(defun :cur-up (e)
(when (:bobp e) (exit eoc (:msg 1)))
(:cury e (sub1 (:cury e)))
(when (gt (:curx e) (sref (:s e) 0)) (:cur-endline e)) )
(defun :cur-down (e)
(when (:eobp e) (exit eoc (:msg 2)))
(:cury e (add1 (:cury e)))
(when (gt (:curx e) (sref (:s e) 0)) (:cur-endline e)) )
(defun :cur-ygoto (e y)
(:cury e (fmax (fmin y (:nlgn e)) 0))
(:curx e (fmax (fmin (:curx e) (sref (:s e) 0)) 0)) )
(defun :cur-xgoto (e x)
(:curx e (fmax (fmin x (sref (:s e) 0)) 0)) )
; .SSection "Ope'ration sur un caracte`re."
(defun :char-new (e n)
(:check-modif e)
(let ( (s (:s e)) )
(when (le (slength s) (add (sref s 0) n))
(setq s
(bltstring
(makestring (add (slength s) (fmax n 11)) #\SP) 0 s 0 ))
(vset (:buf e) (:cury e) s) )
(bltstring s (add1 (add (:curx e) n)) s (add1 (:curx e)))
(sset s 0 (add (sref s 0) n)) ))
(defun :char-insert (e cn)
(:char-new e 1)
(sset (:s e) (add1 (:curx e)) cn)
(:cur-right e) ))
(defun :char-delete (e)
(:check-modif e)
(let ( (s (:s e)) (x (add1 (:curx e))) (y (:cury e)) )
(cond
((eq (sref s 0) 0)
(when (eq y (:nlgn e)) (exit eoc (:msg 2)))
(bltvector (:buf e) y (:buf e) (add1 y))
(:nlgn e (sub1 (:nlgn e))) )
((eq (sub1 x) (sref s 0))
(when (eq y (:nlgn e)) (exit eoc (:msg 2)))
(let ( (ss (vref (:buf e) (add1 y))) )
(bltvector (:buf e) (add1 y) (:buf e) (add y 2))
(:nlgn e (sub1 (:nlgn e)))
(unless (eq (sref ss 0) 0)
(setq s
(catenate
(substring s 0 (add1 (sref s 0)))
(substring ss 1 (sref ss 0)) ))
(vset (:buf e) y s)
(sset s 0 (add (sref s 0) (sref ss 0))) )))
(t (bltstring s x s (add1 x))
(sset s 0 (sub1 (sref s 0))) ))))
(defun :char-delete-back (e)
(when (and (:bobp e) (:bolp e)) (exit eoc (:msg 1)))
(:cur-left e)
(:char-delete e) )
(defun :char-transpose (e)
(:check-modif e)
(let ( (s (:s e)) (x (:curx e)) c1 c2 )
(when (and (ge (sref s 0) 2) (ge x 2))
(setq c1 (sref s x) c2 (sref s (sub1 x)))
(sset s x c2)
(sset s (sub1 x) c1) )))
; .SSection "Ope'ration sur un mot."
(defun :word-insert (e ns)
(:char-new e (slen ns))
(bltstring (:s e) (add1 (:curx e)) ns 0)
(:cur-xgoto e (add (:curx e) (slength ns))) )
(defun :word-delete (e)
(let ( (s (:s e)) (x (:curx e)) )
(cond
((or (eq x (sref s 0)) (:ncpname (sref s (add1 x))))
(:char-delete e)
(:word-delete e) )
(t (until (or (eq x (sref s 0)) (:ncpname (sref s (add1 x))))
(:char-delete e) )))))
(defun :word-delete-back (e)
(let ( (s (:s e)) (x (:curx e)) )
(cond
((or (eq x 0) (:ncpname (sref s x)))
(:char-delete-back e)
(:word-delete-back e) )
(t (until (or (eq x 0) (:ncpname (sref s x)))
(setq x (sub1 x))
(:char-delete-back e) )))))
(defun :word-first-char (e)
(let ( (n (:curx e)) (s (:s e)) )
(cond
((and (:bobp e) (eq n 0)) ())
((or (eq n (sref s 0)) (:ncpname (sref s (add1 n))))
(:cur-left e)
(:word-first-char e) )
(t (until (or (eq n 0) (:ncpname (sref s (add1 n))))
(setq n (sub1 n)) )
(:cur-xgoto e (ifn (:ncpname (sref s (add1 n))) 0 (add1 n))) ))))
(defun :word-last-char (e)
(let ( (n (:curx e)) (s (:s e)) )
(cond
((and (:eobp e) (eq n (sref s 0))) ())
((or (eq n (sref s 0)) (:ncpname (sref s (add1 n))))
(:cur-right e)
(:word-last-char e) )
(t (until (or (eq n (sref s 0)) (:ncpname (sref s (add1 n))))
(setq n (add1 n)) )
(:cur-xgoto e n) ))))
(defun :word-left (e)
(:cur-left e)
(:word-first-char e) )
(defun :word-searchp (s)
(when (or (neq (slength s) 0) :search-strg)
(if (eq (slength s) 0)
:search-strg
(setq :search-strg s) )))
(defun :word-search (e s)
(unless (setq s (:word-searchp s)) (exit eoc (:msg 3)))
(let ( (x (index s (:s e) (add1 (:curx e))))
(ox (:curx e)) (oy (:cury e)) )
(if (and x (le x (sref (:s e) 0)))
(:cur-xgoto e (add (sub1 x) (slength s)))
(:cur-begline e)
(:cur-down e)
(setq x (:word-search2 (index s (:s e) 0) e s))
(when (stringp x)
(:cur-ygoto e oy)
(:cur-xgoto e ox) )
(exit eoc x) )))
(defun :word-search2 (x e s)
(cond
((and x (le x (sref (:s e) 0)))
(:cur-xgoto e (add (sub1 x) (slength s))) )
((:eobp e) (:msg 4))
(t (:cur-down e)
(:word-search2 (index s (:s e) 0) e s) )))
(defun :word-reverse-search (e s)
(unless (setq s (:word-searchp s)) (exit eoc (:msg 3)))
(let ( (x (index s (:s e) 0)) (ox (:curx e)) (oy (:cury e)) (vx ()) )
(untilexit trouve
(while (and x (le (add x (slength s)) (add1 (:curx e))))
(setq vx x x (index s (:s e) (add1 x))) )
(when vx (exit trouve (:cur-xgoto e (sub1 vx))))
(when (:bobp e)
(:cur-ygoto e oy)
(:cur-xgoto e ox)
(exit eoc (:msg 4)) )
(:cur-up e)
(:cur-endline e)
(setq x (index s (:s e) 0) vx ()) )))
(defun :word-get-symbol (e)
(let ( (x (:curx e)) (y (:cury e)) n m)
(with ( (typecn #/: 'cpname) (typecn #/# 'cpname) (typecn #/| 'cpname) )
(:word-first-char e)
(setq n (add1 (:curx e)))
(:word-last-char e)
(setq m (:curx e))
(:cur-ygoto e y)
(:cur-xgoto e x)
(substring (:s e) n (add1 (sub m n))) )))
; .SSection "Ope'ration sur une ligne."
(defun :line-new (e n)
(:line-new2 e n (add1 (:cury e))) )
(defun :line-new2 (e n y)
(:check-modif e)
(let ( (buf (:buf e)) )
(:nlgn e (add (:nlgn e) n))
(when (le (vlength buf) (:nlgn e))
(setq buf
(makevector (add (vlength buf) (or (ge n 10) 10))
:null-strg ))
(bltvector buf 0 (:buf e) 0)
(:buf e buf) )
; C'est le seul bltvector qui etend le vecteur...
(:bltvector buf y n) ))
; Pour savoir si le bltvector marche dans les deux sens.
(defvar :bltvector-flag ())
(defun :bltvector (b1 pos n)
; Toujours 3 arguments.
(if :bltvector-flag
(bltvector b1 (add pos n) b1 pos)
(let* ( (i (sub1 (vlength b1))) (j (sub i n)) )
(repeat (add1 (sub j pos))
(vset b1 i (vref b1 j))
(setq i (sub1 i) j (sub1 j)) ))))
(defun :line-break (e)
(let ( buf (y (add1 (:cury e))) (x (:curx e)) (s (:s e)) )
(:line-new2 e 1 y)
(setq buf (:buf e))
(cond
((eq x 0) (vset buf y s) (vset buf (sub1 y) :null-strg))
((eq x (sref s 0)) (vset buf y :null-strg))
(t (sset (vset buf y (substring s x)) 0 (sub (sref s 0) x))
(sset s 0 x) ))))
(defun :line-return (e)
(:line-break e)
(:cur-right e) )
(defun :line-delete (e)
(when (neq (:ldo e) 29) (setq :kill-buffer ()))
(:line-delete2 e) )
(defun :line-delete2 (e)
(:check-modif e)
(let ( (s (:s e)) (x (:curx e)) (ks 0) (kb :kill-buffer) )
(if (eq x (sref s 0))
(:char-delete e)
(setq ks (substring s x (add1 (sub (sref s 0) x))))
(sset ks 0 (sub1 (slength ks)))
(sset s 0 x) )
(unless kb (setq :kill-buffer (setq kb (:make))))
(if (eq ks 0)
(:line-new2 kb 1 (add1 (:nlgn kb)))
(vset (:buf kb) (:nlgn kb) ks)
() )))
(defun :line-insert (e)
(let ( (kb :kill-buffer) )
(unless kb (exit eoc (:msg 6)))
(let ( (n (add1 (:nlgn kb))) )
(:line-break e)
(:line-new e n)
(bltvector (:buf e) (add1 (:cury e)) (:buf kb) 0 n)
(:copy-kill-buffer (:buf :kill-buffer) (:nlgn :kill-buffer))
(:char-delete e)
(:cur-begline e)
(repeat n (:cur-down e))
(:char-delete-back e) )))
(defun :line-get (e)
(let ( (s (:s e)) )
(substring s 1 (sref s 0)) ))
(defun :line-bol (e)
(let ( (in (inbuf)) (s (:s e)) n )
(if (and (eq (:nlgn e) (:cury e)) (eq (:curx e) (sref s 0)) )
(exit eoc (:msg 2))
(setq n (sref s 0))
(bltstring in 0 s 1 n)
(sset in n #\CR)
(sset in (setq n (add1 n)) #\LF)
(:cur-begline e)
(:cur-down e)
(inmax (add1 n)) )))
(defun :line-eol (e)
(let ( (out (outbuf)) )
(:word-insert e (substring out 0 (outpos)))
(:line-return e)
(fillstring out 0 #\SP (rmargin))
(outpos (lmargin)) ))
; .SSection "Couper - Copier - Coller"
(defun :mark-set (e)
(:set-the-mark e (:curx e) (:cury e))
(:msg 10) )
(defun :mark-exchange (e)
(let ( (x (:markx e)) (y (:marky e)) )
(unless x (exit eoc (:msg 5)))
(:set-the-mark e (:curx e) (:cury e))
(:cur-ygoto e y)
(:cur-xgoto e x) ))
(defun :mark-copy (e)
(let ( (m (:modif e)) )
(:modif e 5)
(:mark-delete e)
(:line-insert e)
(:modif e m)
"Copied" ))
(defun :mark-delete (e)
(when (or (gt (:cury e) (:marky e))
(and (eq (:cury e) (:marky e)) (gt (:curx e) (:markx e))) )
(:mark-exchange e) )
(:mark-exchange e)
(:line-return e)
(:mark-exchange e)
(:line-return e)
(ifn :kill-buffer
(setq :kill-buffer (:make))
(:buf-new :kill-buffer) )
(let ( (y (:cury e)) (d (:marky e)) )
(:line-new2 :kill-buffer (sub d y) 1)
(bltvector (:buf :kill-buffer) 0 (:buf e) y (sub d y))
(bltvector (:buf e) y (:buf e) d)
(:nlgn e (sub (:nlgn e) (sub d y)))
(unless (eq (sref (:s e) 0) 0) (:line-delete2 e))
(:char-delete e)
(:char-delete-back e)
(:mark-set e)
() )))
(defun :copy-kill-buffer (v n)
(when (ge n 0)
(vset v n (copy (vref v n)))
(:copy-kill-buffer v (sub1 n)) ))
; .SSection "Utilisation du buffer comme fichier."
(defun :file-read (e f)
(when (le (:modif e) 1) (exit eoc (:msg 11)))
(:modif e 3)
(let ( buf (rep ()) )
(if f
(setq buf (:file-in-list f) rep f)
(setq buf (vector :null-strg) rep (:msg 7)) )
(:buf-new e)
(:buf e buf)
(:nlgn e (sub1 (vlength buf)))
rep ))
(defun :file-insert (e f)
(let ( y n buf )
(unless f (exit eoc (:msg 8)))
(setq buf (:file-in-list f))
(:line-new e (vlength buf))
(bltvector (:buf e) (add1 (:cury e)) buf 0)
f ))
(defun :file-in-list (f)
(let ( (l ()) )
(with ( (inchan (openi f)) )
(untilexit eof
(bol)
(newl l (catenate " " (substring (inbuf) 0 (sub (inmax) 2))))
(sset (car l) 0 (sub (inmax) 2)) ))
(apply 'vector (nreverse (cons :null-strg l))) ))
(defun :file-print (e f)
(unless (catcherror () (renamefile f (catenate f "SV")))
(exit eoc (:msg 9)) )
(:dofile-print e f)
(catenate "write file " f) )
(defun :dofile-print (e f)
(with ( (outchan (openo f)) (rmargin (slength (outbuf))) )
(let ( (i 0) (buf (:buf e)) s )
(repeat (:nlgn e)
(setq s (vref buf i))
(bltstring (outbuf) 0 s 1 (sref s 0))
(outpos (sref s 0))
(terpri)
(setq i (add1 i)) ))
(:modif e (add 1 (div (:modif e) 2)))
(close (outchan)) ))
; .SSection "Le mode Lisp."
(defun :begin-expr (e)
(:cur-begline e)
(untilexit fin
(cond
((eq (sref (:s e) 0) 0) (:cur-up e))
((eq (typecn (sref (:s e) 1)) 'csep) (:cur-up e))
(t (exit fin)) )))
(defun :matchnext (e)
(let ( (x (:curx e)) (y (:cury e)) (s ()) )
(when (stringp (setq s (tag eoc (:match-next e ()))))
(:cur-ygoto e y)
(:cur-xgoto e x)
(exit eoc s) )))
(defun :match-next (e l)
(:match-next-char e)
(selectq (sref (:s e) (:curx e))
(#/( (:match-next-push e l #/())
(#/[ (:match-next-push e l #/[))
(#/{ (:match-next-push e l #/{))
(#/) (:match-next-pop l #/())
(#/] (:match-next-pop l #/[))
(#/} (:match-next-pop l #/{)) ))
(defun :match-next-push (e l cn)
(:match-next e (cons cn l))
(when l (:match-next e l)) )
(defun :match-next-pop (l cn)
(when (and l (neq (car l) cn))
(exit eoc "Parenthesis mismatch.") ))
(defun :match-next-char (e)
(let ( (nx ()) (cx (:curx e)) (x ()) (s (:s e)) )
(until nx
(setq x (scanstring s "()[]{}""#;|" (add1 cx)))
(if (or (null x) (gt x (sref s 0)))
(setq nx 0)
(selectq (sref s x)
(#/" (setq cx (:skip s """" x)))
(#/| (setq cx (:skip s "|" x)))
(#/; (setq nx 0))
(#/# (if (neq (sref s (add1 x)) #//)
(setq cx (add1 x))
(setq cx (add x 2)) ))
(t (setq nx x)) )))
(cond
((neq nx 0) (:cur-xgoto e nx))
((:eobp e) (exit eoc "Fin du buffer"))
(t (:cur-begline e)
(:cur-down e)
(:match-next-char e) ))))
(defun :skip (s scn x)
(setq x (index scn s (add1 x)))
(if (and x (le x (sref s 0)))
x
(exit eoc "Newline in string") ))
(defun :matchprevious (e)
(let ( (x (:curx e)) (y (:cury e)) (s ()) )
(when (stringp (setq s (tag eoc (:match-prev e ()))))
(:cur-ygoto e y)
(:cur-xgoto e x)
(exit eoc s) )))
(defun :match-prev (e l)
(let ( (x ()) )
(:match-prev-char e)
(selectq (sref (:s e) (add1 (:curx e)))
(#/) (:match-prev-push e l #/())
(#/] (:match-prev-push e l #/[))
(#/} (:match-prev-push e l #/{))
(#/( (:match-next-pop l #/())
(#/[ (:match-next-pop l #/[))
(#/{ (:match-next-pop l #/{))
(t ))))
(defun :match-prev-push (e l cn)
(:match-prev e (cons cn l))
(when l (:match-prev e l)) )
(defun :match-prev-char (e)
(:cur-left e)
(let ( (cx 0) (px 0) (nx ()) (x ()) (s (:s e)) )
(until nx
(setq x (scanstring s "()[]{}""#;|" (add1 cx)))
(if (or (null x) (gt x (add1 (:curx e))))
(setq nx px)
(selectq (sref s x)
(#/" (setq cx (:skip s """" x)))
(#/| (setq cx (:skip s "|" x)))
(#/# (if (neq (sref s (add1 x)) #//)
(setq cx (add1 x))
(setq cx (add x 2)) ))
(#/; (setq nx px))
(t (setq px x cx x)) )))
(cond
((neq nx 0) (:cur-xgoto e (sub1 nx)))
((:bobp e) (exit eoc "Debut du buffer"))
(t (:cur-up e)
(:cur-endline e)
(:match-prev-char e) ))))
; .SSection "Pour compacter un grand coup."
; .SSection "La plus belle de toute."
(defun :undo (e))