|
;;; outshine-lang.el --- allow outshine mode to fold language elements -*- lexical-binding: t; -*- |
|
|
|
;; Copyright (C) 2017 Ilya Shlyakhter |
|
|
|
;; Author: Ilya Shlyakhter |
|
;; Keywords: lisp |
|
;; Version: 0.0.2 |
|
|
|
;; License is missing |
|
|
|
;;; Commentary: |
|
|
|
;; I made minor changes to the original <https://personal.broadinstitute.org/ilya/outshine-python.el> and put it here so I |
|
;; could load it with use-package via quelpa. |
|
|
|
;; This code extends Thorsten Jolitz's excellent outshine mode ( http://orgmode.org/worg/org-tutorials/org-outside-org.html |
|
;; ) to allow folding of language elements: classes, functions, and other language constructs. Language elements are |
|
;; matched by user-specified regexps (see customization `outshine-lang-headline-regexps'). The outline levels of all |
|
;; language element headlines are made to be greater than the levels of any standard outshine headlines, so that language |
|
;; elements are always subordinate to outshine headlines. The outline level of a language headline is taken to be a |
|
;; large fixed constant (specified by customization `outshine-lang-max-standard-level') plus the amount of whitespace |
|
;; preceding the language headline. Since most code is pretty-printed in a hierarchical way (and Python is required to be), |
|
;; this heuristic usually works well enough. |
|
|
|
;;;; Implementation notes |
|
|
|
;; This code works by redefining, via advice, `outline-regexp' and `outline-level' in outshine buffers. |
|
;; The original values of these buffer-local variables (computed by outshine) are saved. |
|
;; Then, `outline-regexp' is defined to match either standard or language headlines, and to capture the whitespace before |
|
;; language headlines in a subexpression. `outline-level' is defined to call the original `outline-level' implementation |
|
;; from outshine for standard headlines, and for language headlines to use indentation level to determine outline level. |
|
;; For promotion/demotion commands, `outline-regexp' and `outline-level' are restored to outshine's original values; |
|
;; these commands then act only on standard headlines. |
|
|
|
;;; Code: |
|
|
|
(require 'outshine) |
|
(require 'rx) |
|
|
|
;;; Customizations |
|
|
|
(defgroup outshine-lang nil |
|
"Extension allowing outshine mode to fold language elements" |
|
:prefix "outshine-lang-" |
|
:group 'outshine) |
|
|
|
(defcustom outshine-lang-max-standard-level 10 |
|
"Max level of standard (non-language-expression) outline" |
|
:group 'outshine-lang |
|
:type 'integer) |
|
|
|
(defcustom outshine-lang-headline-regexps |
|
`(("python" . ,(rx-to-string '(: |
|
(or "def" "class" |
|
"with" "for" "if" |
|
"##" ; to let the user insert headlines manually |
|
) |
|
space (1+ not-newline) (any "\:,") (0+ space) eol))) |
|
("emacs-lisp" . ,(rx-to-string '(: "(def" (or "un" "var" "const" "group" "custom" "advice"))))) |
|
"Alist of language-specific outline headlines for outshine mode. |
|
|
|
The name of the language (key-string) should be the associated |
|
major-mode name without the '-mode' suffix, eg python for python-mode'. |
|
The value should be a regexp that matches language elements which should |
|
be headlines. The regexp should NOT match the whitespace before the language |
|
element. |
|
" |
|
:group 'outshine-lang |
|
:type '(alist :key-type string |
|
:value-type regexp)) |
|
|
|
;;; Variables for saving outline-regexp and outline-level computed by outshine |
|
|
|
(defvar outshine-lang-orig-outline-regexp nil |
|
"Original value of `outline-regexp', before adding language headlines") |
|
(make-variable-buffer-local 'outshine-lang-orig-outline-regexp) |
|
|
|
(defvar outshine-lang-orig-outline-level nil |
|
"Original value of `outline-level', before adding language headlines") |
|
(make-variable-buffer-local 'outshine-lang-orig-outline-level) |
|
|
|
;;; Utility functions |
|
|
|
(defun outshine-lang-get-language-name () |
|
"Return language name for major-mode of current buffer. |
|
Adapted from `navi-get-language-name'." |
|
(car |
|
(split-string |
|
(symbol-name major-mode) |
|
"-mode" 'OMIT-NULLS))) |
|
|
|
(defun outshine-lang-get-headline-regexp () |
|
"Return the language-element headline regexp for the current buffer's language, |
|
or nil if headline regexps are not defined for that language." |
|
(cdr (assoc (outshine-lang-get-language-name) outshine-lang-headline-regexps))) |
|
|
|
;;; Advice that modifies outshine behavior |
|
|
|
(defadvice outline-static-level-p (around outshine-lang-set-lang-headline-level (level) activate) |
|
"Prevent promotion/demotion commands from touching language headlines" |
|
(setq ad-return-value |
|
(if (outshine-lang-get-headline-regexp) |
|
(>= level outshine-lang-max-standard-level) |
|
ad-do-it))) |
|
|
|
(defadvice outline-change-level (around outshine-lang-change-level-revert-to-orig-outline-defs (delta) activate) |
|
"Revert to original expression" |
|
(let ((outline-regexp outline-regexp) |
|
(outline-level outline-level)) |
|
(when (outshine-lang-get-headline-regexp) |
|
(setq outline-regexp outshine-lang-orig-outline-regexp |
|
outline-level outshine-lang-orig-outline-level)) |
|
ad-do-it)) |
|
|
|
(defun outshine-lang-activate () |
|
"Turn language elems (function defs, class defs etc) into cycleable outshine-org headlines" |
|
(let ((lang-elem-headline-re (outshine-lang-get-headline-regexp))) |
|
(when lang-elem-headline-re |
|
(setq |
|
outshine-lang-orig-outline-regexp outline-regexp |
|
outshine-lang-orig-outline-level outline-level |
|
outline-regexp |
|
;; Define a regexp which matches either language headlines or standard headlines. |
|
;; If a language headline is matched, (match-string 1) gives matched headline and |
|
;; (match-string 2) gives the whitespace preceding it; we take the length of the whitespace |
|
;; to be the outline level. |
|
(rx-to-string |
|
`(or |
|
(group |
|
(group (0+ space)) |
|
(regexp ,lang-elem-headline-re)) |
|
(group (regexp ,outshine-lang-orig-outline-regexp)))) |
|
outline-level |
|
(lambda () |
|
(if (match-string 1) |
|
(+ outshine-lang-max-standard-level (length (match-string 2))) |
|
(funcall outshine-lang-orig-outline-level))))))) |
|
|
|
(defadvice outshine-hook-function (after outshine-lang-do-activate nil activate) |
|
(outshine-lang-activate)) |
|
|
|
(provide 'outshine-lang) |
|
;;; outshine-lang.el ends here |