diff --git a/lisp/outline.el b/lisp/outline.el index 25ef1616b9..3b8e68bfe1 100644 --- a/lisp/outline.el +++ b/lisp/outline.el @@ -292,6 +292,16 @@ outline-minor-mode-use-buttons :safe #'booleanp :version "29.1") +(defcustom outline-minor-mode-use-margins '(derived-mode . special-mode) + "Whether to display clickable buttons on the margins. +The value should be a `buffer-match-p' condition. + +These buttons can be used to hide and show the body under the heading. +Note that this feature is meant to be used in editing buffers." + :type 'buffer-predicate + :safe #'booleanp + :version "29.1") + (define-icon outline-open nil '((image "outline-open.svg" "outline-open.pbm" :height 15 :ascent center) @@ -437,8 +447,10 @@ outline-minor-mode-highlight-buffer (and (eq outline-minor-mode-highlight t) (not (get-text-property (match-beginning 0) 'face)))) (overlay-put overlay 'face (outline-font-lock-face))) - (when (outline--use-buttons-p) - (outline--insert-open-button))) + (cond ((outline--use-margins-p) + (outline--insert-open-button t)) + ((outline--use-buttons-p) + (outline--insert-open-button)))) (goto-char (match-end 0)))))) ;;;###autoload @@ -453,6 +465,9 @@ outline-minor-mode (key-description outline-minor-mode-prefix) outline-mode-prefix-map) (if outline-minor-mode (progn + (when (outline--use-margins-p) + (setq-local left-margin-width (1+ left-margin-width)) + (setq-local fringes-outside-margins t)) (when outline-minor-mode-highlight (if (and global-font-lock-mode (font-lock-specified-p major-mode)) (progn @@ -473,6 +488,9 @@ outline-minor-mode (font-lock-remove-keywords nil outline-font-lock-keywords)) (remove-overlays nil nil 'outline-overlay t) (font-lock-flush)) + (when (outline--use-margins-p) + (setq-local left-margin-width (1- left-margin-width)) + (setq-local fringes-outside-margins nil)) (setq line-move-ignore-invisible nil) ;; Cause use of ellipses for invisible text. (remove-from-invisibility-spec '(outline . t)) @@ -483,6 +501,10 @@ outline--use-buttons-p (and outline-minor-mode (buffer-match-p outline-minor-mode-use-buttons (current-buffer)))) +(defun outline--use-margins-p () + (and outline-minor-mode + (buffer-match-p outline-minor-mode-use-margins (current-buffer)))) + (defvar-local outline-heading-alist () "Alist associating a heading for every possible level. Each entry is of the form (HEADING . LEVEL). @@ -1012,10 +1034,34 @@ outline--make-button-overlay (overlay-put o 'face (plist-get icon 'face)))) o)) -(defun outline--insert-open-button () +(defun outline--make-margin-overlay (type) + (let ((o (seq-find (lambda (o) + (overlay-get o 'outline-margin)) + (overlays-at (point))))) + (unless o + (setq o (make-overlay (point) (1+ (point)))) + (overlay-put o 'follow-link 'mouse-face) + (overlay-put o 'mouse-face 'highlight) + (overlay-put o 'outline-margin t)) + (let ((icon + (icon-elements (if (eq type 'close) 'outline-close 'outline-open))) + (inhibit-read-only t)) + (overlay-put o 'before-string + (propertize " " 'display `((margin left-margin) + ,(or (plist-get icon 'image) + (plist-get icon 'string)))))) + o)) + +(defun outline--insert-open-button (&optional margins-p) (with-silent-modifications (save-excursion - (beginning-of-line) + (beginning-of-line) + (if margins-p + (let ((o (outline--make-margin-overlay 'open))) + (overlay-put o 'help-echo "Click to hide") + (overlay-put o 'keymap + (define-keymap + "" #'outline-hide-subtree))) (when (derived-mode-p 'special-mode) (let ((inhibit-read-only t)) (insert " ") @@ -1025,12 +1071,18 @@ outline--insert-open-button (overlay-put o 'keymap (define-keymap "RET" #'outline-hide-subtree - "" #'outline-hide-subtree)))))) + "" #'outline-hide-subtree))))))) -(defun outline--insert-close-button () +(defun outline--insert-close-button (&optional margins-p) (with-silent-modifications (save-excursion - (beginning-of-line) + (beginning-of-line) + (if margins-p + (let ((o (outline--make-margin-overlay 'close))) + (overlay-put o 'help-echo "Click to show") + (overlay-put o 'keymap + (define-keymap + "" #'outline-show-subtree))) (when (derived-mode-p 'special-mode) (let ((inhibit-read-only t)) (insert " ") @@ -1040,23 +1092,25 @@ outline--insert-close-button (overlay-put o 'keymap (define-keymap "RET" #'outline-show-subtree - "" #'outline-show-subtree)))))) + "" #'outline-show-subtree))))))) (defun outline--fix-up-all-buttons (&optional from to) - (when (outline--use-buttons-p) - (when from - (save-excursion - (goto-char from) - (setq from (line-beginning-position)))) - (outline-map-region - (lambda () - (if (save-excursion - (outline-end-of-heading) - (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline)) - (overlays-at (point)))) - (outline--insert-close-button) - (outline--insert-open-button))) - (or from (point-min)) (or to (point-max))))) + (let ((buttons-p (outline--use-buttons-p)) + (margins-p (outline--use-margins-p))) + (when (or buttons-p margins-p) + (when from + (save-excursion + (goto-char from) + (setq from (line-beginning-position)))) + (outline-map-region + (lambda () + (if (save-excursion + (outline-end-of-heading) + (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline)) + (overlays-at (point)))) + (outline--insert-close-button margins-p) + (outline--insert-open-button margins-p))) + (or from (point-min)) (or to (point-max)))))) (define-obsolete-function-alias 'hide-subtree #'outline-hide-subtree "25.1")