; .EnTete "Le-Lisp (c) version 15.2" " " "Editor"
; .EnPied "editor.ll" "%" " "
; .SuperTitre "Editor"
;
; .Centre "*****************************************************************"
; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA "
; .Centre "*****************************************************************"
;
; .Centre "$Header: editor.ll,v 4.1 88/01/13 12:35:13 kuczynsk Rel $"
(unless (>= (version) 15.2)
(error 'load 'erricf 'editor))
; .Section "Editor"
; .SSection "Contenu."
; Ge`re le display d'un editeur de type Edit.
; Ge`re une table d'association caracte`re-fonction.
; Ge`re le lancement d'une cle'.
; De'finit les commandes demandant un argument.
; Definit le redisplay.
; Il n'y a donc pas de fontion toplevel.
;
; Un positionnement est valide pour le redisplay dans les conditions
; suivantes :
; xpos <= bx <= xpos + w
; ypos <= by <= ypos + h
; Le comportement par de'faut du redisplay quand les coordonne'es en
; haut a` gauche de l'e'cran sont invalides par rapport aux coordonne'es
; du caracte`re courant est d'ajuster la colonne et/ou la ligne du curseur
; au milieu de l'e'cran.
; Reste a` faire :
; - Connecter le compacteur.
; - Faire qu'une commande soit une suite de cle pour le undo et le ↑U.
; - Faire les macros.
; - Faire les autoloads
; .SSection "De'claration"
(defvar #:sys-package:colon '#:edit:editor)
(add-feature 'editor)
(defstruct #.#:sys-package:colon
(sender ())
(query ':edit-query)
(file "test.ll")
(drawcursor ':edit-drawcursor)
(redisplaystring ':edit-redisplaystring)
(redisplaycleol ':edit-redisplaycleol)
(screen ())
(cmd ())
(ecmd ())
(xcmd ())
(state 0)
(w 80)
(h 20)
(xcursor 0)
(ycursor 0)
(xpos 0)
(ypos 0) )
(de :prin (e) (prin "<" (:w e) "," (:h e) ">"))
; .SSection "Les macros locales."
(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 :nlgn (e)
`(#:buffer:nolgn (:pbuffer ,e)) )
(defmacro :buf (e)
`(#:buffer:buffer (:pbuffer ,e)) )
(defmacro :s (e)
`(vref (:buf ,e) (:cury ,e)) )
(defmacro :modif (e . a)
(if a
`(#:buffer:modif (:pbuffer ,e) ,(car a))
`(#:buffer:modif (:pbuffer ,e)) ))
(defun :add-charp (cn)
(chrpos cn
" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]↑←`abcdefghijklmnopqrstuvwxyz{|}~" ))
; .SSection "Le moteur d'e'dition."
(defun trap-editor (n e a)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'trap-editor "Argument is not an editor." e)
(selectq n
(100 (:win-clear e))
(101 (:win-right e))
(102 (:win-left e))
(103 (:win-down e))
(104 (:win-up e))
(105 (:win-next e))
(106 (:win-previous e))
(107 (:win-cur-top e))
(108 (:win-ygoto e a))
(109 (:win-xgoto e a))
(110 (:cur-win-top e))
(111 (:cur-win-buttom e))
(112 (:cur-win-ygoto e a))
(113 (:cur-win-xgoto e a))
(114 (:state e 1))
(115 (:state e 2))
(116 (:query-readfile e))
(117 (:query-writefile e))
(118 (:query-insertfile e))
(119 (:query-search e))
(120 (:query-rsearch e))
(121 (:savefile e))
(122 (:query-goto-line-number e))
(123 (exit eoc "This key has no global fonction"))
(124 (:exp-eval e))
(125 (:exp-eval-print e)) )))
; .SSection "Initialisation de la fene↑tre de visualisation."
(defun init-editor (e obj w h)
(:screen e (makestring (mul w h) #\SP))
(:modif e (add 1 (mul 2 (div (:modif e) 2))))
(:sender e obj)
(:w e w)
(:h e h) )
(defun :win-clear (e)
(:modif e (add 1 (mul 2 (div (:modif e) 2))))
(fillstring (:screen e) 0 #\SP) )
; .SSection "De'placement de la fene↑tre de visualisation."
(defun :win-right (e)
(unless (eq (:xpos e) 0)
(:xpos e (sub1 (:xpos e)))
(unless (lt (:curx e) (add (:xpos e) (:w e)))
(:curx e (add (:xpos e) (div (:w e) 2))) )))
(defun :win-left (e)
(:xpos e (add1 (:xpos e)))
(unless (le (:xpos e) (:curx e))
(:curx e (add (:xpos e) (div (:w e) 2))) ))
(defun :win-down (e)
(unless (eq (:ypos e) 0)
(:ypos e (sub1 (:ypos e)))
(unless (lt (:cury e) (add (:ypos e) (:h e)))
(:cury e (add (:ypos e) (div (:h e) 2))) )))
(defun :win-up (e)
(unless (and (eq (:cury e) (:ypos e)) (eq (:cury e) (:nlgn e)))
(:ypos e (add1 (:ypos e)))
(unless (le (:ypos e) (:cury e))
(:cury e (fmin (add (:ypos e) (div (:h e) 2)) (:nlgn e))) )))
(defun :win-next (e)
(:ypos e (fmin (add (:ypos e) (div (mul 3 (:h e)) 4)) (:nlgn e)))
(:cury e (:ypos e))
(:curx e (:xpos e)) )
(defun :win-previous (e)
(:ypos e (fmax (sub (:ypos e) (div (mul 3 (:h e)) 4)) 0))
(:cury e (:ypos e))
(:curx e (:xpos e)) )
(defun :win-cur-top (e)
(:ypos e (:cury e)) )
(defun :win-ygoto (e a)
(when (and (ge a 0) (le a (:nlgn e)))
(:ypos e a)
(unless (and (le (:ypos e) (:cury e))
(lt (:cury e) (add (:ypos e) (:h e))) )
(CUR-YGOTO e (add (:ypos e) (div (:h e) 2))) )))
(defun :win-xgoto (e a))
; .SSection "De'placement du curseur dans la fene↑tre de visualisation."
(defun :cur-win-top (e)
(:curx e (:xpos e))
(:cury e (:ypos e)) )
(defun :cur-win-buttom (e)
(:cury e (fmin (add (:ypos e) (sub1 (:h e))) (:nlgn e)))
(:curx e (fmin (add (:xpos e) (sub1 (:w e))) (sref (:s e) 0))) )
(defun :cur-win-ygoto (e y)
(CUR-YGOTO e (add (:ypos e) y)) )
(defun :cur-win-xgoto (e x)
(CUR-XGOTO e (add (:xpos e) x)) )
; .SSection "Les commandes demandant un argument"
(defun :query-readfile (e)
(let ( (f (funcall (:query e) e "Read file: " t)) )
(when f
(ifn (setq f (probepathf f))
"File not found."
(:file e f)
(FILE-READ e f) ))))
(defun :query-writefile (e)
(let ( (f (funcall (:query e) e "Write file: " t)) )
(when f
(if (equal f "") (setq f (:file e)) (:file e f))
(:writefile2 e f) )))
(defun :savefile (e)
(:writefile2 e (:file e)) )
(defun :writefile2 (e f)
(let ( (write? (catcherror () (opena f))) )
(ifn write?
(catenate "Can't write file " f)
(close (car write?))
(FILE-PRINT e f) )))
(defun :query-insertfile (e)
(let ( (f (funcall (:query e) e "Insert file: " t)) )
(when f
(ifn (setq f (probepathf f))
"File not found."
(FILE-INSERT e f) ))))
(defun :query-search (e)
(let ( (s (funcall (:query e) e "Search for : " t)) )
(when s (WORD-SEARCH e s)) ))
(defun :query-rsearch (e)
(let ( (s (funcall (:query e) e "Reverse search for : " t)) )
(when s (WORD-REVERSE-SEARCH e s)) ))
(defun :query-goto-line-number (e)
(let ( (n (funcall (:query e) e "Goto line : " t)) )
(when n
(setq n (implode (explode n)))
(if (fixp n)
(CUR-YGOTO e n)
"Bad line number" ))))
; .SSection "Le redisplay."
(defun edit-redisplay (e)
(:edit-redisplayscreen e (:buf e) (:nlgn e) (:screen e)
(:w e) (:h e) (:ypos e) (:xpos e)
(:redisplaystring e) (:redisplaycleol e) ))
(defun :edit-redisplayscreen (e buf maxln map w h ln bx f1 f2)
(let ( s max1 ibx max2 x0 (y0 0) i (n 0) )
(repeat h
; Init chaine source et marge de comparaison dans chaine but.
(if (or (gt ln maxln) (eq (setq s (vref buf ln)) 0))
(setq max1 n)
(setq max1 (add n (fmin w (fmax 0 (sub (sref s 0) bx))))) )
; Init des marges et compteurs.
(setq max2 (add n w) x0 0 i n ibx (add1 bx))
; On avance au premier caracte`re diffe'rent.
(while (and (gt max1 n) (eq (sref map n) (sref s ibx)))
(setq n (add1 n) ibx (add1 ibx)) )
(setq x0 (add x0 (sub n i)))
; Si diffe'rence affiche TOUT le reste de la chaine source.
(unless (eq max1 n)
(setq i (sub max1 n))
(bltstring map n s ibx i)
(funcall f1 e x0 y0 s ibx i)
(setq x0 (add x0 i) n max1) )
; On avance tant qu'il y a des caracte`es d'espacement.
(setq i n)
(while (and (gt max2 n) (eq (sref map n) #\SP))
(setq n (add1 n)))
(setq x0 (add x0 (sub n i)))
; Si diffe'rent envoie UNE demande d'effacement de ligne.
(unless (eq max2 n)
(setq i (sub max2 n))
(fillstring map n #\SP i)
(funcall f2 e x0 y0 i)
(setq n max2) )
; Ligne suivante.
(setq ln (add1 ln) y0 (add1 y0)) )))
(defun :edit-redisplaystring (e x y s pos n)
(tycursor x y)
(repeat n
(tyo (sref s pos))
(setq pos (add1 pos)) ))
(defun :edit-redisplaycleol (e x y n)
(tycursor x y)
(tycleol) )
(defun :win-check (e)
(let ( (yp (:ypos e)) (xp (:xpos e)) (bx (:curx e)) (by (:cury e))
(w (:w e)) (h (:h e)) )
; (CUR-YGOTO e by)
; (setq by (:cury e))
; (CUR-XGOTO e bx)
; (setq bx (:curx e))
(unless (and (le xp bx) (lt bx (add xp w)))
(:xpos e (fmax (sub bx (div w 2)) 0)) )
(unless (and (le yp by) (lt by (add yp h)))
(:ypos e (fmax (sub by (div h 2)) 0)) )))
; .SSection "Les commandes"
; .SSSection "Les tables d'association cle/fonction"
(defvar editor-simple-command (makevector 128 123))
(defvar editor-escape-command (makevector 128 123))
(defvar editor-extend-command (makevector 128 123))
(defun bind-to-key (cle fnt)
(if (fixp cle)
(vset editor-simple-command cle fnt)
(error 'bind-to-key "Bad key" cle) ))
(defun bind-to-ekey (cle fnt)
(if (fixp cle)
(vset editor-escape-command cle fnt)
(error 'bind-to-ekey "Bad key" cle) ))
(defun bind-to-xkey (cle fnt)
(if (fixp cle)
(vset editor-extend-command cle fnt)
(error 'bind-to-xkey "Bad key" cle) ))
(defun local-bind-to-key (e cle fnt)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'local-bind-to-key "N'est pas un editeur" e)
(:cmd e (cons (cons cle fnt) (:cmd e))) ))
(defun local-bind-to-ekey (e cle fnt)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'local-bind-to-ekey "N'est pas un editeur" e)
(:ecmd e (cons (cons cle fnt) (:ecmd e))) ))
(defun local-bind-to-xkey (e cle fnt)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'local-bind-to-xkey "N'est pas un editeur" e)
(:xcmd e (cons (cons cle fnt) (:xcmd e))) ))
; .SSSection "Le traitement des commandes"
(defun edit-command (e cn)
(ifn (subtypep (type-of e) '#.#:sys-package:colon)
(error 'edit-command "N'est pas un editeur" e)
(tag eoc
(selectq (:state e)
(0 (:edit-simple-command e cn))
(1 (:edit-escape-command e cn))
(2 (:edit-extend-command e cn)) ))))
(defun :edit-simple-command (e cn)
(let ( (cmd (cassq cn (:cmd e))) )
(cond
(cmd (:edit-all-command cmd e))
((not (fixp cn)))
((:add-charp cn) (char-insert e cn))
((lt cn (vlength editor-simple-command))
(:edit-all-command (vref editor-simple-command cn) e) ))))
(defun :edit-escape-command (e cn)
(let ( (cmd (cassq cn (:ecmd e))) )
(:state e 0)
(cond
(cmd (:edit-all-command cmd e))
((not (fixp cn)))
((lt cn (vlength editor-escape-command))
(:edit-all-command (vref editor-escape-command cn) e) ))))
(defun :edit-extend-command (e cn)
(let ( (cmd (cassq cn (:xcmd e))) )
(:state e 0)
(cond
(cmd (:edit-all-command cmd e))
((not (fixp cn)))
((lt cn (vlength editor-extend-command))
(:edit-all-command (vref editor-extend-command cn) e) ))))
(defun :edit-all-command (fnt e)
(cond
((fixp fnt)
(if (lt fnt 100)
(trap-edit fnt e ())
(trap-editor fnt e ()) ))
((symbolp fnt) (funcall fnt e))
((not (consp fnt)))
((eq (car fnt) 'lambda) (funcall fnt e))
((symbolp (car fnt))
(funcall (car fnt) e (cdr fnt)) )))
; .SSection "Commande puis redisplay"
(defun edit-command-redisplay (e cn)
(let ( (rep ()) (ox (:xpos e)) (oy (:ypos e)) ocx ocy ncx ncy )
(when cn (funcall (:query e) e () ()))
(:modif e (mul 2 (div (:modif e) 2)))
(ifn cn
(:modif e (add 1 (:modif e)))
(setq rep (edit-command e cn))
(when (stringp rep) (funcall (:query e) e rep ())) )
(:win-check e)
(setq ocx (:xcursor e) ocy (:ycursor e))
(:xcursor e (setq ncx (sub (:curx e) (:xpos e))))
(:ycursor e (setq ncy (sub (:cury e) (:ypos e))))
(cond
((or (neq oy (:ypos e)) (neq ox (:xpos e)) (eq (rem (:modif e) 2) 1))
(funcall (:drawcursor e) e ocx ocy ())
(edit-redisplay e)
(funcall (:drawcursor e) e ncx ncy t) )
((lt ocx 0)
(funcall (:drawcursor e) e ncx ncy t) )
((or (neq ocx ncx) (neq ocy ncy))
(funcall (:drawcursor e) e ocx ocy ())
(funcall (:drawcursor e) e ncx ncy t) ))))
(defun :edit-drawcursor (e x y f)
(when f (tycursor x y)) )
(defun :edit-query (obj msg fg) ())
; .SSection "Evaluation dans un e'diteur"
(defvar :cure)
(defmacro edit-eval-redisplay (e exp)
`(let ( (#:system:redef-flag t)
(#:sys-package:itsoft
(cons '#.#:sys-package:colon #:sys-package:itsoft) )
(:cure ,e) )
(prog1 (tag eoc ,exp)
(edit-command-redisplay :cure ()) )))
(defun :bol ()
(let ( (e :cure) )
(if (fixp (inchan))
(super-itsoft '#.#:sys-package:colon 'bol ())
(line-bol e) )))
(defun :eol ()
(let ( (e :cure) )
(if (fixp (outchan))
(super-itsoft '#.#:sys-package:colon 'eol ())
(line-eol e) )))
(defun :syserror (:f :m :b)
(exit eoc
(if (debug)
(let ( (#:sys-package:itsoft (cdr #:sys-package:itsoft)) )
(super-itsoft '#.#:sys-package:colon 'syserror (list :f :m :b)))
(edit-print-query :cure
(let ( (#:sys-package:itsoft 'edit-print-query) )
(tag edit-print-query (printerror :f :m :b)) )))))
(defun edit-print-query (e msg)
(let ( (#:sys-package:itsoft 'edit-print-query) )
(setq msg (tag edit-print-query (print msg))) )
(funcall (:query e) e msg ()) )
(defun #:edit-print-query:eol ()
(exit edit-print-query
(prog1
(substring (outbuf) 0 (outpos))
(outpos (lmargin)) )))
(defun :exp-eval (e)
(BEGIN-EXPR e)
(edit-eval-redisplay e (eval (read))) )
(defun :exp-eval-print (e)
(BEGIN-EXPR e)
(edit-print-query e (edit-eval-redisplay e (eval (read)))) )
; .SSection "Les commandes par de'faut"
(bind-to-key #↑A 005)
(bind-to-key #↑B 007)
(bind-to-key #↑D 014)
(bind-to-key #↑E 006)
(bind-to-key #↑F 008)
(bind-to-key #↑H 015)
(bind-to-key #↑K 029)
(bind-to-key #↑M 028)
(bind-to-key #↑N 010)
(bind-to-key #↑O 027)
(bind-to-key #↑P 009)
(bind-to-key #↑R 120)
(bind-to-key #↑S 119)
(bind-to-key #↑T 041)
(bind-to-key #↑V 105)
(bind-to-key #↑W 036)
(bind-to-key #↑X 115)
(bind-to-key #↑Y 025)
(bind-to-key #↑Z 104)
(bind-to-key #\esc 114)
(bind-to-key #\del 015)
(bind-to-ekey #/b 021)
(bind-to-ekey #/d 017)
(bind-to-ekey #/e 124)
(bind-to-ekey #/f 020)
(bind-to-ekey #/h 018)
(bind-to-ekey #/n 122)
(bind-to-ekey #/p 125)
(bind-to-ekey #/s 119)
(bind-to-ekey #/v 106)
(bind-to-ekey #/w 035)
(bind-to-ekey #/z 103)
(bind-to-ekey #/) 44)
(bind-to-ekey #/( 45)
(bind-to-ekey #/> 004)
(bind-to-ekey #/< 003)
(bind-to-ekey #/, 110)
(bind-to-ekey #/. 111)
(bind-to-ekey #/! 107)
(bind-to-ekey #\esc 024)
(bind-to-xkey #/s 121)
(bind-to-xkey #/R 042)
(bind-to-xkey #/W 043)
(bind-to-xkey #↑A 033)
(bind-to-xkey #↑I 118)
(bind-to-xkey #↑R 116)
(bind-to-xkey #↑W 117)
(bind-to-xkey #↑X 034)