;;; package-cl-inspect-mode.el --- -*- lexical-binding: t -*- ;; TODO: Name? cl-inspect? clinspect? clidget? eclidget? ;; Actually, make it independent of CL. ;; TODO: Better table abstraction? ;; Maybe https://github.com/kiwanami/emacs-ctable? ;; TODO: Add filters. ;; Can we add filters that ask for a column and a range depending on the type of data? (require 'cl-lib) (defvar clinspect-column-max-width 40) (defun clinspect--tabulated-list-sorter (entry index) "Return a sorter that smarter than `tabulated-list' default. If ENTRY element at INDEX is a number, sort by number, otherwise sort by string." (if (numberp (nth index entry)) (lambda (a b) (< (string-to-number (aref (cadr a) index)) (string-to-number (aref (cadr b) index)))) t)) (defun clinspect--tabulated-list-format (header entries) (apply #'vector (mapcar (lambda (index) (let ((column (nth index header))) (list column (min clinspect-column-max-width (max (length column) (apply #'max (mapcar (lambda (entry) (length (prin1-to-string (nth index entry)))) entries)))) (clinspect--tabulated-list-sorter (car entries) index)))) (number-sequence 0 (1- (length header)))))) (define-derived-mode clinspect-mode tabulated-list-mode "Clinspect" "Mode to inspect Common Lisp sequences." (add-hook 'tabulated-list-revert-hook 'tabulated-list-init-header nil t)) (defvar clinspect-buffer-name "clinspector") (defun clinspect (header data &optional name) "Inspect DATA. DATA is a list of things. HEADER is a list of strings, the column names." (switch-to-buffer (generate-new-buffer (format "*%s%s*" clinspect-buffer-name (if name (format "<%s>" name) "")))) (clinspect-mode) (setq tabulated-list-entries (cl-loop for line in data for index from 1 upto (length header) collect (list index (apply #'vector (mapcar #'prin1-to-string line))))) (setq tabulated-list-format (clinspect--tabulated-list-format header data)) (tabulated-list-revert)) (provide 'clinspect)