view-modeの私的な設定

最近色々フラストレーションが溜まっているせいか、自分の部屋に居ても愚痴ばかりです。こりゃあかん・・・けど仕事変わるか現場変わるかしないとどうにもならないだろうなぁ。

さて、話は変わってEmacsにはview-modeという閲覧専用のmajor-modeがあります。最近これを頻繁に利用するようになったのですが、使っていると不便なものとか出てくるものです。
色々あったので、使い勝手が変わるかどうかは別として、色々やってみました。

;; view-modeを活用するための設定
(setq view-read-only t)
(defvar pager-keybind
      `( ;; vi-like
        ("a" . ,(lambda () (interactive)
                  (let ((anything-c-moccur-enable-initial-pattern nil))
                    (anything-c-moccur-occur-by-moccur))))
        (";" . anything)
        ("h" . backward-word)
        ("l" . forward-word)
        ("j" . next-window-line)
        ("k" . previous-window-line)
        ("b" . scroll-down)
        (" " . scroll-up)
        ;; w3m-like
        ("m" . gene-word)
        ("i" . win-delete-current-window-and-squeeze)
        ("w" . forward-word)
        ("e" . backward-word)
        ("(" . point-undo)
        (")" . point-redo)
        ("J" . ,(lambda () (interactive) (scroll-up 1)))
        ("K" . ,(lambda () (interactive) (scroll-down 1)))
        ;; bm-easy
        ("." . bm-toggle)
        ("[" . bm-previous)
        ("]" . bm-next)
        ;; langhelp-like
        ("c" . scroll-other-window-down)
        ("v" . scroll-other-window)
        ))

(defun define-many-keys (keymap key-table &optional includes)
  (let (key cmd)
    (dolist (key-cmd key-table)
      (setq key (car key-cmd)
            cmd (cdr key-cmd))
      (if (or (not includes) (member key includes))
        (define-key keymap key cmd))))
  keymap)

(defun view-mode-hook0 ()
  (define-many-keys view-mode-map pager-keybind)
  (hl-line-mode 1)
  (skk-mode 0)                          ; SKKを強制的に切る。
  (view-mode-set-window-controls "s")
  )
(add-hook 'view-mode-hook 'view-mode-hook0)

;; 書き込み不能なファイルはview-modeで開くように
(defadvice find-file
  (around find-file-switch-to-view-file (file &optional wild) activate)
  (if (and (not (file-writable-p file))
           (not (file-directory-p file)))
      (view-file file)
    ad-do-it))

;; 書き込み不能な場合はview-modeを抜けないように
(defvar view-mode-force-exit nil)
(defmacro do-not-exit-view-mode-unless-writable-advice (f)
  `(defadvice ,f (around do-not-exit-view-mode-unless-writable activate)
     (if (and (buffer-file-name)
              (not view-mode-force-exit)
              (not (file-writable-p (buffer-file-name))))
         (message "File is unwritable, so stay in view-mode.")
       (progn
         (hl-line-mode 0)
       ad-do-it))))

(do-not-exit-view-mode-unless-writable-advice view-mode-exit)
(do-not-exit-view-mode-unless-writable-advice view-mode-disable)

;; view-mode時に、手軽にウィンドウ移動、切替を行えるようにする。
(defvar view-mode-window-control-map nil)
(unless view-mode-window-control-map
  (setq view-mode-window-control-map (make-sparse-keymap))

  (define-key view-mode-window-control-map (kbd "l") 'windmove-right)
  (define-key view-mode-window-control-map (kbd "h") 'windmove-left)
  (define-key view-mode-window-control-map (kbd "k") 'windmove-down)
  (define-key view-mode-window-control-map (kbd "j") 'windmove-up)

  (define-key view-mode-window-control-map (kbd "d") 'delete-window)
  (define-key view-mode-window-control-map (kbd "wh") 'split-window-horizontally)
  (define-key view-mode-window-control-map (kbd "wv") 'split-window-vertically)
  (define-key view-mode-window-control-map (kbd "o") 'delete-other-windows)
)

(defvar view-mode-original-keybind nil)
(defun view-mode-set-window-controls (prefix-key)
  (unless view-mode-original-keybind
    (dolist (l (cdr view-mode-map))
      (if (equal ?s (car l))
          (setq view-mode-original-keybind (list prefix-key (cdr l))))))
  (define-key view-mode-map prefix-key view-mode-window-control-map))

(defun view-mode-unset-window-controls()
  (when view-mode-original-keybind
    (define-key view-mode-map (car view-mode-original-keybind)
      (cadr view-mode-original-keybind))
    (setq view-mode-original-keybind nil)))

(defvar view-mode-auto-change-alist '())
(defvar view-mode-auto-change-time 1.0)
(defvar view-mode-auto-change-timer nil)

(add-to-list 'view-mode-auto-change-alist "c++-mode")
(defun view-mode-set-auto-change-timer ()
  (unless view-mode-auto-change-timer
    (setq view-mode-auto-change-timer
          (run-with-idle-timer view-mode-auto-change-time t
                               '(lambda ()
                                  (if (find-if (lambda (x) (string= x major-mode)) view-mode-auto-change-alist)
                                      (view-mode)
                                    nil)))))
  )

(defun view-mode-cancel-auto-change-timer ()
  (when view-mode-auto-change-timer
    (cancel-timer view-mode-auto-change-timer)
    (setq view-mode-auto-change-timer nil))
  )

(defun view-mode-toggle-auto-change-timer ()
  (interactive)
  (if view-mode-auto-change-timer
      (view-mode-cancel-auto-change-timer)
    (view-mode-set-auto-change-timer))
  )

(define-key view-mode-map (kbd "t") 'view-mode-toggle-auto-change-timer)

;; view-modeを活用する設定 -- ここまで

一部rubikitchさんが公開されていた設定も拝借しています。さて、色々設定していますが、

  • ウィンドウの移動とかをctrlとかを押さずにいけるようなキーバインドを設定している
  • 特定のmodeでだけ、一定時間経過したら自動的にview-modeに以降する

というのが、自分で作成した部分です。色々無駄とか無駄とかイケてない部分がありすぎていやんなっちゃいますが、とりあえず利用できています。

この設定だと、同じモード間の移動だとs + h j k lでウィンドウ移動可能となっています。基本的にs + キーでウィンドウ操作をできるようにしています。

こんな設定もさっくりと書けてしまうあたり、やっぱりemacsは「インターフェース」は作りやすいなぁ、と思います。