;;; haskell-menu.el --- A Haskell sessions menu -*- lexical-binding: t -*- ;; Copyright (C) 2013 Chris Done ;; Author: Chris Done ;; This file is not part of GNU Emacs. ;; This file is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; This file is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ;; Boston, MA 02110-1301, USA. ;;; Commentary: ;;; Todo: ;;; Code: (require 'cl-lib) (require 'haskell-compat) (require 'haskell-session) (require 'haskell-process) (require 'haskell-interactive-mode) (defcustom haskell-menu-buffer-name "*haskell-menu*" "The name of the Haskell session menu buffer" :group 'haskell-interactive :type 'string) ;;;###autoload (defun haskell-menu () "Launch the Haskell sessions menu." (interactive) (or (get-buffer haskell-menu-buffer-name) (with-current-buffer (get-buffer-create haskell-menu-buffer-name) (haskell-menu-mode))) (switch-to-buffer-other-window (get-buffer haskell-menu-buffer-name)) (haskell-menu-revert-function nil nil)) (defvar haskell-menu-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "n") 'next-line) (define-key map (kbd "p") 'previous-line) (define-key map (kbd "RET") 'haskell-menu-mode-ret) map) "Keymap for `haskell-menu-mode'.") (define-derived-mode haskell-menu-mode special-mode "Haskell Session Menu" "Major mode for managing Haskell sessions. Each line describes one session. Letters do not insert themselves; instead, they are commands." (setq buffer-read-only t) (setq-local revert-buffer-function 'haskell-menu-revert-function) (setq truncate-lines t) (haskell-menu-revert-function nil t)) (suppress-keymap haskell-menu-mode-map t) (defun haskell-menu-revert-function (_arg1 _arg2) "Function to refresh the display." (let ((buffer-read-only nil) (orig-line (line-number-at-pos)) (orig-col (current-column))) (or (eq buffer-undo-list t) (setq buffer-undo-list nil)) (erase-buffer) (haskell-menu-insert-menu) (goto-char (point-min)) (forward-line (1- orig-line)) (forward-char orig-col))) (defun haskell-menu-insert-menu () "Insert the Haskell sessions menu to the current buffer." (if (null haskell-sessions) (insert "No Haskell sessions.") (haskell-menu-tabulate (list "Name" "PID" "Time" "RSS" "Cabal directory" "Working directory" "Command") (mapcar (lambda (session) (let ((process (haskell-process-process (haskell-session-process session)))) (cond (process (let ((id (process-id process))) (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) (if (process-live-p process) (number-to-string id) "-") (if (process-live-p process) (format-time-string "%H:%M:%S" (encode-time (cl-caddr (assoc 'etime (process-attributes id))) 0 0 0 0 0)) "-") (if (process-live-p process) (concat (number-to-string (/ (cdr (assoc 'rss (process-attributes id))) 1024)) "MB") "-") (haskell-session-cabal-dir session) (haskell-session-current-dir session) (mapconcat 'identity (process-command process) " ")))) (t (list (propertize (haskell-session-name session) 'face 'buffer-menu-buffer) "—" "—" "—" (haskell-session-cabal-dir session) (haskell-session-current-dir session)))))) haskell-sessions)))) (defun haskell-menu-tabulate (headings rows) "Prints a list of lists as a formatted table to the current buffer." (let* ((columns (length headings)) (widths (make-list columns 0))) ;; Calculate column widths. This is kind of hideous. (dolist (row rows) (setq widths (let ((list (list))) (dotimes (i columns) (setq list (cons (max (nth i widths) (1+ (length (nth i row))) (1+ (length (nth i headings)))) list))) (reverse list)))) ;; Print headings. (let ((heading (propertize " " 'display '(space :align-to 0)))) (dotimes (i columns) (setq heading (concat heading (format (concat "%-" (number-to-string (nth i widths)) "s") (nth i headings))))) (setq header-line-format heading)) ;; Print tabulated rows. (dolist (row rows) (dotimes (i columns) (insert (format (concat "%-" (number-to-string (nth i widths)) "s") (nth i row)))) (insert "\n")))) (defun haskell-menu-mode-ret () "Handle RET key." (interactive) (let* ((name (save-excursion (goto-char (line-beginning-position)) (buffer-substring-no-properties (point) (progn (search-forward " ") (forward-char -1) (point))))) (session (car (cl-remove-if-not (lambda (session) (string= (haskell-session-name session) name)) haskell-sessions)))) (switch-to-buffer (haskell-session-interactive-buffer session)))) (provide 'haskell-menu) ;;; haskell-menu.el ends here