git.fiddlerwoaroof.com
Raw Blame History
(in-package :cl-user)

(define-condition repl-error (error)
  ())
(define-condition no-such-directory (repl-error)
  ((%attempted-directory :reader attempted-directory :initarg :ad))
  (:report (lambda (condition stream)
             (format stream "No such directory: ~a" (attempted-directory condition)))))

(defun read-evaluated-form (&optional (prompt-control nil promptp)
                            &rest prompt-args)
  (apply #'format *query-io*
         (if promptp prompt-control "~&Enter a form to be evaluated: ")
         prompt-args)
  (finish-output *query-io*)
  (list (eval (read *query-io*))))

(defun cd (new)
  (flet ((%cd (new-directory)
           (if (probe-file new-directory)
               (return-from cd
                 (setf *default-pathname-defaults* (truename new-directory)))
               (cerror "Try again" 'no-such-directory :ad new-directory))))
    (fw.lu:retry-once (is-retry)
      (let ((new-directory (make-pathname :directory (append (pathname-directory *default-pathname-defaults*)
                                                             (list new))
                                          :defaults *default-pathname-defaults*)))
        (restart-case (%cd new-directory)
          (use-value (value)
            :test (lambda (_) _ is-retry)
            :report (lambda (stream)
                      (format stream "Use specified value."))
            :interactive read-evaluated-form
            (setf new value)))))))

(defun ls (&optional pattern)
  (if pattern
      (directory pattern)
      (directory "*.*")))

(defun rm (file)
  (unless (listp file)
    (setf file (list file)))
  (mapcar #'delete-file file))

(defun --parse-path (it)
  (let ((parts (fwoar.string-utils:split #\/ it)))
    (values (elt parts (1- (length parts)))
            (coerce (subseq parts 0 (1- (length parts)))
                    'list))))

(defun load-project-asds (name)
  (multiple-value-bind (proj-name proj-sub) (--parse-path name)
    (mapcar 'asdf:load-asd
            (directory (make-pathname :host "PROJECTS"
                                      :directory (list :absolute (string-upcase proj-name))
                                      :name :wild
                                      :type "ASD")))))

#+(or)
(mapcar 'asdf:load-asd
        (remove #\. (directory "*.asd")
                :test #'eql
                :key (data-lens:• (data-lens:element 0)
                                  #'pathname-name)))

(defun gh-repo-root (coordinate)
  (let ((git-url (format nil "git@github.com:~a.git" coordinate)))
    (merge-pathnames (uiop:parse-unix-namestring coordinate
                                                   :ensure-directory t)
                      (merge-pathnames (make-pathname :directory
                                                      (list :relative
                                                            "git_repos"
                                                            "github.com"))
                                       (user-homedir-pathname)))))

(defun gh-coordinate (coordinate)
  (format nil "git@github.com:~a.git" coordinate))

(defun gh-dir (coordinate)
  (uiop:nest (merge-pathnames (uiop:parse-unix-namestring coordinate :ensure-directory t))
             (merge-pathnames (make-pathname :directory (list :relative "git_repos" "github.com")))
             (user-homedir-pathname)))

(defun gh (coordinate)
  (let ((git-url (gh-coordinate coordinate))
        (target (:printv (gh-dir coordinate))))
    (unless (probe-file target)
      (legit:clone git-url
                   (ensure-directories-exist target)))
    (directory (merge-pathnames (make-pathname :directory (list :relative :wild-inferiors)
                                               :name :wild
                                               :type "asd")
                                target))))

(defun gl (coordinate)
  (let ((git-url (format nil "git@gitlab.com:~a.git" coordinate))
        (target (:printv
                 (merge-pathnames (:printv
                                   (uiop:parse-unix-namestring coordinate
                                                               :ensure-directory t))
                                  (merge-pathnames (make-pathname :directory
                                                                  (list :relative
                                                                        "git_repos"
                                                                        "gitlab.com"))
                                                   (user-homedir-pathname))))))
    (unless (probe-file target)
      (legit:clone git-url
                   (ensure-directories-exist target)))
    (directory (merge-pathnames (make-pathname :directory (list :relative :wild-inferiors)
                                               :name :wild
                                               :type "asd")
                                target))))

(defvar *fwoar/reset-cache* nil)
(defmacro $caching (&body body)
  (alexandria:with-gensyms (cache)
    `(let ((,cache (load-time-value (vector nil nil))))
       (cond ((and (elt ,cache 1)
                   (not *fwoar/reset-cache*))
              (elt ,cache 0))
             (t
              (prog1 (setf (elt ,cache 0)
                           (progn ,@body))
                (setf (elt ,cache 1) t
                      *fwoar/reset-cache* nil)))))))
(defun gf-repos/raw ()
  ($caching
    (trivial-ssh:with-connection
        (c "git.fiddlerwoaroof.com"
         (trivial-ssh:key "git" (namestring (merge-pathnames ".ssh/id_ed25519"
                                                             (user-homedir-pathname)))))
        (libssh2:with-execute (s c "info")
          (let ((libssh2:*channel-read-zero-as-eof* t))
            (loop for line = (read-line s nil)
                  while line
                  when (serapeum:string-prefix-p " " line)
                    collect line))))))

(defun gf-repos (&optional (pattern nil pattern-p))
  (funcall (if pattern-p
               (data-lens:include (data-lens:on (data-lens:regex-match pattern)
                                                'car))
               #'identity)
           (mapcar (data-lens:• (lambda (it)
                                  (coerce it 'list))
                                #'reverse
                                (lambda (it)
                                  (data-lens.lenses:over (data-lens.lenses:make-list-lens 0)
                                                         (lambda (it) (fwoar.string-utils:split #\space it))
                                                         it))
                                (lambda (it) (fwoar.string-utils:split #\tab it)))
                   (gf-repos/raw))))

(defun gf-url (coordinate)
  (format nil "git@git.fiddlerwoaroof.com:~a.git" coordinate))

(defun gf-target-dir (coordinate)
  (merge-pathnames (uiop:parse-unix-namestring coordinate
                                               :ensure-directory t)
                   (merge-pathnames (make-pathname :directory
                                                   (list :relative
                                                         "git_repos"
                                                         "git.fiddlerwoaroof.com"))
                                    (user-homedir-pathname))))

(defun gf (coordinate)
  (let ((git-url (gf-url coordinate))
        (target (:printv (gf-target-dir coordinate))))
    (unless (probe-file target)
      (legit:clone git-url
                   (ensure-directories-exist target)))
    (directory (merge-pathnames (make-pathname :directory (list :relative :wild-inferiors)
                                               :name :wild
                                               :type "asd")
                                target))))

(defun lp (git-url)
  (let* ((uri (if (and (not (net.uri:uri-p git-url))
                       (find #\@ git-url))
                  (puri:parse-uri git-url)
                  git-url))
         (target (merge-pathnames (make-pathname :directory
                                                 (list :relative
                                                       "local-projects"
                                                       (etypecase uri
                                                         (puri:uri (car (last (puri:uri-parsed-path uri))))
                                                         (string (subseq uri
                                                                         (or (1+
                                                                              (position #\/ (string-right-trim "/" uri)
                                                                                        :from-end t))
                                                                             0))))))
                                  ql:*quicklisp-home*)))
    (legit:clone (etypecase git-url
                   (puri:uri (puri:render-uri git-url nil))
                   (string git-url))
                 (namestring target))))


(defclass fw-define-op (asdf:define-op)
  ((%systems-before :reader systems-before :initform (asdf:registered-systems))
   (%new-systems :initarg :new-systems :accessor new-systems)))
(defmethod asdf:operate :after ((o fw-define-op) (c asdf:system) &key)
  (setf (new-systems o) (set-difference (asdf:registered-systems)
                                        (slot-value o '%systems-before)
                                        :test 'equal)))
(defun load-asd (pathname &key name)
  "Load system definitions from PATHNAME.
NAME if supplied is the name of a system expected to be defined in that file.

Do NOT try to load a .asd file directly with CL:LOAD. Always use ASDF:LOAD-ASD."
  (asdf/session:with-asdf-session ()
    ;; TODO: use OPERATE, so we consult the cache and only load once per session.
    (flet ((do-it (o c) (asdf:operate o c)))
      (let ((primary-name (asdf:primary-system-name (or name (pathname-name pathname))))
            (operation (asdf:make-operation 'fw-define-op)))
        (uiop:if-let (system (asdf:registered-system primary-name))
          (progn
            ;; We already determine this to be obsolete ---
            ;; or should we move some tests from find-system to check for up-to-date-ness here?
            (setf (asdf/action:component-operation-time operation system) t
                  (asdf/system:definition-dependency-list system) nil
                  (asdf/system:definition-dependency-set system)
                  (uiop:list-to-hash-set nil))
            (do-it operation system))
          (let ((system (make-instance 'asdf/system:undefined-system
                                       :name primary-name :source-file pathname)))
            (asdf/system-registry:register-system system)
            (unwind-protect (do-it operation system)
              (when (typep system 'asdf/system:undefined-system)
                (asdf:clear-system system)))))))))

(export
 (defmacro vj ((op &rest args))
   `(values * (,op * ,@args))))

(export
 (defmacro wl ((op &rest args))
   `(values * (,op ,@args))))

(rename-package #1=:cl-user (package-name #1#) (adjoin :• (package-nicknames #1#)))

#+swank
(defun plot-stream (s &key
                        (xrange nil xrange-p)
                        (yrange nil yrange-p)
                        (background "#2A2B2E")
                        (frame-color "#7fdf7f")
                        (line-color "#DCDCCC")
                        (lines nil))
  (let ((fn (format nil "/tmp/~a.svg" (gensym))))
    (uiop:run-program (with-output-to-string (s)
                        (format (make-broadcast-stream s *error-output*)
                                "gnuplot -e \"~:[~*~;set xrange [~{~f~^:~}];~]~:[~*~;set yrange [~{~f~^:~}];~]set terminal svg font 'Alegreya,14' enhanced  background '~a'; set border lw 3 lc rgb '~a'; plot '< cat' lt rgb '~a' notitle ~:[~;with linespoint~]\""
                                xrange-p xrange
                                yrange-p yrange
                                background
                                line-color
                                frame-color
                                lines))

                      :input s
                      :error-output *error-output*
                      :output (parse-namestring fn))
    (swank::send-to-emacs (list :write-image fn " ")))
  (values))

#+swank
(defun wi (fn)
  (swank::send-to-emacs (list :write-image fn " ")))

(export
 (defun dl (&optional (*default-pathname-defaults* *default-pathname-defaults*))
   (let ((files (funcall (data-lens:exclude (data-lens:on (data-lens:regex-match "^[.]")
                                                          'pathname-name))
                         (directory "*.asd"))))
     (values (mapcar 'load-asd files)
             files))))