git.fiddlerwoaroof.com
lisp_editor.lisp
fe7bbf09
 (load #p"~/quicklisp/setup.lisp")
 (declaim (optimize (speed 0) (safety 3) (debug 3)))
 
 (ql:quickload :cl-charms)
 (ql:quickload :cells)
 (ql:quickload :alexandria)
 (ql:quickload :anaphora)
 
 (defpackage :lisp-edit
   (:use :cl :cl-charms :cells :alexandria :anaphora))
 (in-package :lisp-edit)
 
 (defun apply-cursor-action (action)
   (case action
     ((:up)    (cons  0 -1))
     ((:down)  (cons  0  1))
     ((:left)  (cons -1  0))
     ((:right) (cons  1  0))))
 
 (defun dispatch-char (c)
   (case c
     ((nil) nil)
     ((#\j) :down )
     ((#\h) :left )
     ((#\k) :up   )
     ((#\l) :right)
     ((#\f) :paint)
     ((#\q #\Q) :quit)))
 
 (defmodel input-handler ()
           ((input-key :cell :ephemeral :accessor input-key :initform (c-in nil))
            (action :cell :ephemeral :accessor action :initform (c? (dispatch-char (^input-key))))))
 
 (defmodel cursor ()
           ((action :cell :ephemeral :initarg :action :accessor action :initform (c-in nil))
8b70612f
            (current-char :initarg :current-char :accessor current-char :initform (c-in nil))
fe7bbf09
            (delta :accessor cursor-delta :initform
                   (c? (apply-cursor-action (^action))))
            (x :accessor cursor-x :initarg :x :initform (c... (0) (car (^cursor-delta))))
            (y :accessor cursor-y :initarg :y :initform (c... (0) (cdr (^cursor-delta))))))
 
8b70612f
 (defun map-actions (current-char action)
   (case action
     ((:paint) (case current-char
                 ((#\#) #\Space)
                 ((#\Space) #\*)
                 ((#\*) #\#)))))
 
 (defmodel painter ()
           ((action :cell :ephemeral :initarg :action :accessor action :initform (c-in nil))
            (current-char :initarg :current-char :accessor current-char :initform (c-in nil))
            (output :cell :ephemeral :accessor output :initform (c? (map-actions (^current-char)
                                                                                 (^action))))))
 
fe7bbf09
 (defvar *input-handler*)
 (defvar *painter*)
 (defvar *cursor*)
 
 (defun paint ()
   (with-restored-cursor *standard-window*
     (write-char-at-cursor *standard-window*
                           (if (char/= #\Space (char-at-cursor *standard-window*))
                             #\Space
                             #\*))))
 
 (defun constrain-pos (elem pos delta window-dimensions)
   (let* ((accessor (case elem
                      ((:x) #'car)
                      ((:y) #'cdr)))
          (window-elem (funcall accessor window-dimensions))
          (delta-elem (funcall accessor delta))
          (pos-elem (funcall accessor pos))
          (naive-delta (mod (+ pos-elem delta-elem)
                            window-elem))
          (real-delta (- naive-delta pos-elem)))
     real-delta))
 
 
 (defun main ()
   (cells-reset)
   (handler-case
     (with-curses ()
       (disable-echoing)
       (enable-raw-input :interpret-control-characters t)
       (enable-non-blocking-mode *standard-window*)
 
8b70612f
       (setf *input-handler* (make-instance 'input-handler) 
             *cursor* (make-instance 'cursor :action (c? (action *input-handler*)))
             *painter* (make-instance 'painter
                                      :action (c? (action *input-handler*))
                                      :current-char (c? (current-char *cursor*))))
 
       (let* ((screen-dimensions (multiple-value-bind (x y) (window-dimensions *standard-window*)
                                   (cons x y))))
         (labels
           ((set-current-char (cursor)
              (with-restored-cursor *standard-window*
                (setf (current-char cursor) (char-at-cursor *standard-window*))))
            (mvc (x y)
              (move-cursor *standard-window* x y)
              (set-current-char *cursor*))
            (constrain-coordinate (side coord)
              (mod coord (funcall
                           (ecase side
                             ((:x) #'car)
                             ((:y) #'cdr))
                           screen-dimensions))))
 
           (defobserver output ((self painter))
                        (with-restored-cursor *standard-window*
                          (write-char-at-cursor *standard-window* new-value))
                        (with-integrity (:change)
                          (set-current-char *cursor*)))
 
           (defobserver y ((self cursor))
                        (with-integrity (:change)
                          (let ((adjusted-n-v (constrain-coordinate :y new-value)))
                            (if (/= new-value adjusted-n-v)
                              (setf (^cursor-y) (- adjusted-n-v new-value))
                              (mvc (^cursor-x) adjusted-n-v)))))
 
           (defobserver x ((self cursor))
                        (with-integrity (:change)
                          (let ((adjusted-n-v (constrain-coordinate :x new-value)))
                            (if (/= new-value adjusted-n-v)
                              (setf (^cursor-x) (- adjusted-n-v new-value))
                              (mvc adjusted-n-v (^cursor-y))))))
 
           (mvc 0 0)
           (loop :named driver-loop
                 :for c := (get-char *standard-window* :ignore-error t)
                 :do (progn
                       (refresh-window *standard-window*)
                       (setf (input-key *input-handler*) c))))))
 
fe7bbf09
     (sb-sys:interactive-interrupt (c) (declare (ignore c)))))