Plaster

common-lisp
(defun traverse-files (function directory &key (test #'identity)) (dolist (file (directory (merge-pathnames "*.*" directory))) (if (or (pathname-type file) (pathname-name file)) (funcall function file) (when (funcall test file) (traverse-files function file :test test))))) (defun ignored-file-p (file) (char= #\. (char (car (last (pathname-directory file))) 0))) (defun file-line-lengths (file) (with-open-file (stream file :direction :input) (loop for line = (read-line stream NIL) while line collect (length line)))) (defun line-stats (lengths) (let ((empty 0) (lines 0) (maximum 0) (characters 0)) (dolist (length lengths) (incf lines) (incf characters length) (when (< maximum length) (setf maximum length)) (when (= 0 length) (incf empty))) (values lines characters empty maximum))) (defun compute-project-stats (directory &key file-types) (let ((files 0) (lines 0) (characters 0) (maxline 0) (maxfile 0) (empty 0)) (traverse-files (lambda (file) (when (or (null file-types) (find (pathname-type file) file-types :test #'equalp)) (multiple-value-bind (l c e m) (line-stats (file-line-lengths file)) (incf files) (incf lines l) (incf characters c) (incf empty e) (when (< maxline m) (setf maxline m)) (when (< maxfile c) (setf maxfile c))))) directory :test (complement #'ignored-file-p)) (values files lines characters empty maxline maxfile))) (defun report-project-stats (directory &key (file-types '("lisp" "lsp" "cl" "asd"))) (multiple-value-bind (files lines characters empty maxline maxfile) (compute-project-stats directory :file-types file-types) (format T "~ Files: ~10,,'':d Lines: ~10,,'':d (~2,1f% empty) Characters: ~10,,'':d Max Line: ~10,,'':dc Max File: ~10,,'':dc Avg Line: ~10,1fc (~,1fc w/empty)" files lines (/ empty lines 0.01) characters maxline maxfile (/ characters lines) (/ characters (- lines empty)))))

Annotations