最近閉じたファイル

silog - memo/xyzzy/file #最後に閉じたファイルを開くインスパイヤされました。
今回の改造にあたって加えられた新たなオリジナリティは以下。

  • add-history でヒストリに登録するようにしました。
  • ダイアログを追加しました。

以下の環境で動作確認しました。

recent-closed-file-open-last-one で最後に閉じたファイルを開きます。
recent-closed-file-open-dialog で最近閉じたファイルをダイアログ表示します。ファイルを選択して『OK』を押すと、選択したファイルを開きます。
開いたファイルはヒストリから消されます。
ヒストリの最大個数は *minibuffer-maximum-history-count* です。

コード

;; 最近閉じたファイル
; * silog - memo/xyzzy/file #最後に閉じたファイルを開く
;   <http://white.s151.xrea.com/wiki/index.php?memo%2Fxyzzy%2Ffile#k96261fd>
; から朴りました。
(defvar *recent-closed-file-history* nil)

; ヒストリ変数をファイルに保存する
; 削除するときは
; > (unregister-history-variable '*recent-closed-file-history*)
; するのを忘れるな
;(define-history-variable *recent-closed-file-history* nil)

(defun recent-closed-file-add-history ()
  (let ((file (get-buffer-file-name)))
    (if file
        (if (file-exist-p file)
            (add-history file '*recent-closed-file-history*)
          (or (delete file *recent-closed-file-history* :test #'path-equal)
              (setq *recent-closed-file-history* nil)))))
  t)
#|
; 保存するヒストリの個数を別に設定する
; 負の値を指定すると無制限
(defvar *recent-closed-file-maximum-history-count*
  *minibuffer-maximum-history-count*)

(defun recent-closed-file-add-history ()
  (let ((file (get-buffer-file-name)))
    (when file
      (or (delete file *recent-closed-file-history* :test #'path-equal)
          (setq *recent-closed-file-history* nil))
      (if (file-exist-p file)
          (push file *recent-closed-file-history*))))
  (let (n)
    (and (<= 0 *recent-closed-file-maximum-history-count*)
         (setq n (- (list-length *recent-closed-file-history*)
                    *recent-closed-file-maximum-history-count*))
         (> n 0)
         (or (nbutlast *recent-closed-file-history* n)
             (setq *recent-closed-file-history* nil))))
  t)
|#
(add-hook '*query-kill-buffer-hook* 'recent-closed-file-add-history)

; 最後に閉じたファイルを開く
(defun recent-closed-file-open-last-one ()
  (interactive)
  (unless *recent-closed-file-history*
    (plain-error "ヒストリは空です"))
  (let ((file (pop *recent-closed-file-history*)))
    (unless (file-exist-p file)
      (plain-error "~A: そんなファイルはありません" file))
    (find-file file)))

; ダイアログ
(defvar *recent-closed-file-open-dialog-template*
  `(dialog 0 0 298 117
           (:caption "最近閉じたファイル")
           (:font 9 "MS UI Gothic")
           (:control
            (:listbox IDC_LIST nil #x50b10119 5 6 288 84)
            (:button IDOK "&OK" #x50010001 6 96 50 14)
            (:button IDCANCEL "&Cancel" #x50010000 60 96 50 14))))

(defun recent-closed-file-open-dialog ()
  (interactive)
  (let ((hlist (mapcar #'(lambda (x)
                           (list (abbreviate-display-string x 96 t) x))
                       *recent-closed-file-history*)))
    (multiple-value-bind (result data)
        (let ((omode (get-ime-mode)))
          (toggle-ime nil)
          (unwind-protect
              (dialog-box *recent-closed-file-open-dialog-template*
                          `((IDC_LIST . ,hlist))
                          `((IDC_LIST :column (1)
                                      :must-match t
                                      :enable (IDOK))))
            (toggle-ime omode)))
      (if result
          (let ((lst (cdr (assoc 'IDC_LIST data)))
                dlst)
            (dolist (var lst t)
              (let ((filename (nth 1 var)))
                (or (delete filename *recent-closed-file-history*)
                    (setq *recent-closed-file-history* nil))
                (if (file-exist-p filename)
                    (progn
                      (find-file filename)
                      (add-history filename '*minibuffer-file-name-history*))
                  (push filename dlst))))
            (if dlst
                (let ((lstlen (list-length lst))
                      (dlstlen (list-length dlst)))
                  (if (= lstlen dlstlen 1)
                      (message "~A: そんなファイルはありません" (car dlst))
                    (msgbox (format nil
                                    "~D 個中 ~D 個のファイルが見つかりませんでした~%~{~%~A~}"
                                    lstlen dlstlen dlst)))
                  nil)
              t))))))

; 『ファイル』メニューに追加
(add-hook '*post-startup-hook*
          #'(lambda ()
              (let* ((file (get-menu *app-menu* 'ed::file))
                     (pos (get-menu-position file :above-kill-xyzzy)))
                (insert-menu-item file pos nil
                                  "最近閉じたファイル(&L)..."
                                  'recent-closed-file-open-dialog
                                  #'(lambda ()
                                      (or *recent-closed-file-history*
                                          :disable))))))