When using hideshow (specifically hs-hide-initial-comment-block), I often run into the problem where the very first comment is a license block, followed by a more detailed comment on the purpose of the file.

For example:

 * This program 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 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

 * This file contains functions that solve problem W, part of API X.
 * Take care not to do Y because it can cause issues with Z.

As you might guess, I would like to fold only the first comment, but keep all other comments expanded since it's not part of the boiler plate license header.

Is there a way to tread blank lines as delimiters when hiding comments to avoid hiding multiple, unrelated comments at once?

1 Answer 1


This can be done using advice, although it's not trivial, the following code only collapses the first comment block.

(defmacro my-with-advice (fn-orig where fn-advice &rest body)
  "Execute BODY with WHERE advice on FN-ORIG temporarily enabled."
  (let ((fn-advice-var ,fn-advice))
        (advice-add ,fn-orig ,where fn-advice-var)
      (advice-remove ,fn-orig fn-advice-var))))

(defun my-c-comment-end-skip-backward (pos)
  (cond ;; Check for "/*".
    ((and (eq (char-before pos) ?/) (eq (char-before (- pos 1)) 42))
      (- pos 2))

(defun my-comment-bounds ()
  (let ((state (syntax-ppss))
        (point-init (point)))

    ;; We may be at the comment start, which isn't considered a comment.
    (unless (nth 4 state)
        (skip-syntax-forward "^-")
        (let ((state-test (syntax-ppss)))
          (when (and (nth 4 state-test) (<= point-init (nth 8 state-test)))
            (setq state state-test)))))

    (when (nth 4 state)
      (let ((start (nth 8 state)))
          (goto-char start)
          (forward-comment 1)
          (skip-syntax-backward ">")
          (cons start (point)))))))

(defun my-hs-hide-initial-comment-block (orig-fun &rest args)
  (my-with-advice 'hs-inside-comment-p
    (lambda (fn)
          (goto-char (point-min))
          (skip-chars-forward " \t\n\f")
          (let ((bounds (my-comment-bounds)))
            (when bounds
              (narrow-to-region (car bounds) (cdr bounds))))
          (funcall fn))))
    (apply orig-fun args)))

(with-eval-after-load 'hideshow
  (advice-add 'hs-hide-initial-comment-block
              :around #'my-hs-hide-initial-comment-block))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.