バッファ内のコメントを非表示にする

TeXで文章を書いているときにコメント部分を一括して非表示にしたくなった。似たような機能としてはoutline-modeやhide-ifdef-modeがあるが、そのものずばりの機能は見つからなかったので、これらを参考にしながらちょろっと書いてみた。

(2006.3.6 11:21 ちょっと修正)

(defun beginning-of-comment ()
  (interactive)
  (let ((pos (comment-beginning)))
    (and pos (goto-char pos)))
  (while (and (save-excursion
                (let ((pos (point)))
                  (and (forward-comment -1)
                       (forward-comment 1)
                       (= pos (point)))))
              (forward-comment -1))))

(defun end-of-comment ()
  (interactive)
  (let ((pos (comment-beginning)))
    (if pos
        (goto-char pos)
      (setq pos (point))))
  (let ((n 0))
    (while (and (save-excursion
                  (let ((pos (point)))
                    (and (forward-comment 1)
                         (forward-comment -1)
                         (= pos (point)))))
                (forward-comment 1))
      (setq n (1+ n)))
    (if (> n 0)
        (backward-char))))

(defun enable-hide-comment ()
  (let ((elem '(hide-comment . t)))
    (if (or (atom buffer-invisibility-spec)
            (member elem buffer-invisibility-spec))
        (add-to-invisibility-spec elem))))

(defun disable-hide-comment ()
  (remove-overlays (point-min) (point-max) 'invisible 'hide-comment)
  (remove-from-invisibility-spec '(hide-comment . t)))

(defun hide-comment ()
  (interactive)
  (enable-hide-comment)
  (let* ((beg (progn (beginning-of-comment) (point)))
         (end (progn (end-of-comment) (point))))
    (if (< beg end)
        (overlay-put (make-overlay beg end) 'invisible 'hide-comment))))

(defun hide-comment-buffer ()
  (interactive)
  (enable-hide-comment)
  (save-excursion
    (goto-char (point-min))
    (while (comment-search-forward nil t)
      (hide-comment))))

(defun toggle-hide-comment ()
  (interactive)
  (let ((ovls (remove-if-not
               (lambda (o)
                 (overlay-get o 'invisible))
               (let ((beg (max (1- (point)) (point-min)))
                     (end (min (1+ (point)) (point-max))))
                 (overlays-in beg end)))))
    (if ovls
        (mapc (lambda (o)
                (and (eq (overlay-get o 'invisible) 'hide-comment)
                     (delete-overlay o)))
              ovls)
      (hide-comment))))

M-x hide-comment-bufferでバッファ中の全コメントが非表示になる。M-x toggle-hide-commentで個別にコメントの表示/非表示を切り替えることもできる。

おまけ。非表示になったときの省略記号("...")を変更する方法を調べているうちに、こんな風にすると省略記号をカスタマイズできることが分かった。

(defvar highlight-selective-display nil)
(make-variable-buffer-local 'highlight-selective-display)

(defun highlight-selective-display (flag &optional ellipsis)
  (interactive "P")
  (or ellipsis (setq ellipsis "..."))
  (setq highlight-selective-display
        (cond ((null flag) (not highlight-selective-display))
              ((numberp flag) (positivep flag))
              (t t)))
  (let ((v (if highlight-selective-display
               (apply 'vector
                      (mapcar (lambda (c)
                                (logior c (ash (face-id 'highlight) 19)))
                              (string-to-list ellipsis)))
             (string-to-vector ellipsis))))
    (or buffer-display-table
        (setq buffer-display-table (make-display-table)))
    (set-display-table-slot buffer-display-table 'selective-display v)))

こんなことしてる場合じゃないのに。時間がないときに限って深追いしたくなるんだよな。