1

I have the following configuration for outlines in my init.el file:

(defvar my-outline-regexp-alist
  '((emacs-lisp-mode . "\\s-*;;\\*+")
    (js2-mode        . "\\s-*//\\*+")
    (web-mode        . "\\s-*//\\*+")
    (java-mode       . "\\s-*//\\*+")
    (c-mode          . "\\s-*//\\*+")
    (python-mode     . "\\s-*##\\*+")
    (sh-mode         . "\\s-*##\\*+")))

(add-hook 'emacs-lisp-mode-hook #'my-setup-outline-mode)
(add-hook 'js2-mode-hook #'my-setup-outline-mode)
(add-hook 'python-mode-hook #'my-setup-outline-mode)
(add-hook 'sh-mode-hook #'my-setup-outline-mode)
(add-hook 'java-mode-hook #'my-setup-outline-mode)
(add-hook 'c-mode-hook #'my-setup-outline-mode)
(add-hook 'web-mode-hook #'my-setup-outline-mode)

(defun my-toggle-outline ()
  "Toggle outline visibility using `outline-toggle-children'."
  (interactive)
  (if (outline-on-heading-p t)
      (outline-toggle-children)
    (indent-for-tab-command)))

(defun my-setup-outline-mode ()
  (let ((regexp (cdr (assoc major-mode my-outline-regexp-alist))))
    (when regexp
      (setq-local outline-regexp regexp)
      (outline-minor-mode 1)
      (evil-define-key 'normal outline-minor-mode-map (kbd "TAB") 'my-toggle-outline))))

It works perfectly fine for all programming languages except for Python (I can toggle the outlines with the TAB key in evil mode). The idea is to use the outline with a comment and * after it, this way I have something similar to org-mode in my programming language files. So, it works as the following:

  1. In emacs-lisp-mode I can use ;;* to create the first layer of the outline, ;;** to create the second layer of it and so on... It also accepts any space before it.
  2. In js2-mode I can use //* for the first layer and so on.
  3. In sh-mode I can use ##* for the first layer and so on.

Well, even though it works perfectly fine in any programming language that I've tried, it doesn't work at all in python-mode. I know that if I use the expression:

(python-mode . "\\s-*\\(def\\|class\\)\\b")

it will fold my outlines fine in class and def keywords... But I'd like to fold Python code using comments like ##* or #*.

What am I getting wrong? It doesn't make sense that it works fine in sh-mode and it doesn't work for python-mode. Is there any extra detail I should know while creating outlines in Python?

1 Answer 1

2

python-mode sets outline-heading-end-regexp to ":[^\n]*\n" which makes the heading stretch to the next colon.

(setq-local outline-heading-end-regexp "\n") should make the heading you are looking for.

python-mode also sets outline-level to

(lambda ()
                "`outline-level' function for Python mode."
                (1+ (/ (current-indentation) python-indent-offset)))

I believe you want the outline-level to be based on the length of the regexp match.

Modifying the outline-level found in outline.el yields:

(defun my-python-outline-level ()
  (- (match-end 0) (match-beginning 0)))

and then

(setq-local outline-level #'my-python-outline-level)

A complete functional solution that allows you to outline Python and other languages would be:

(defvar my-outline-regexp-alist
  '((emacs-lisp-mode . "\\s-*;;\\*+")
    (js2-mode        . "\\s-*//\\*+")
    (web-mode        . "\\s-*//\\*+")
    (java-mode       . "\\s-*//\\*+")
    (c-mode          . "\\s-*//\\*+")
    (python-mode     . "\\s-*##\\*+")
    (sh-mode         . "\\s-*##\\*+")))

(add-hook 'emacs-lisp-mode-hook #'my-setup-outline-mode)
(add-hook 'js2-mode-hook #'my-setup-outline-mode)
(add-hook 'python-mode-hook #'my-setup-outline-mode)
(add-hook 'sh-mode-hook #'my-setup-outline-mode)
(add-hook 'java-mode-hook #'my-setup-outline-mode)
(add-hook 'c-mode-hook #'my-setup-outline-mode)
(add-hook 'web-mode-hook #'my-setup-outline-mode)

(defun my-toggle-outline ()
  "Toggle outline visibility using `outline-toggle-children'."
  (interactive)
  (if (outline-on-heading-p t)
      (outline-toggle-children)
    (indent-for-tab-command)))

(defun my-python-outline-level ()
  (- (match-end 0) (match-beginning 0)))

(defun my-setup-outline-mode ()
  (let ((regexp (cdr (assoc major-mode my-outline-regexp-alist))))
    (when regexp
      (setq-local outline-regexp regexp)
      (outline-minor-mode 1)
      (evil-define-key 'normal outline-minor-mode-map (kbd "TAB") 'my-toggle-outline)
      (when (eq major-mode 'python-mode)
        (setq-local outline-heading-end-regexp "\n")
        (setq-local outline-level #'my-python-outline-level)))))

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.