branch: externals/org-modern
commit 8cad2c3d839a720be32b25e12946572b8e569ca2
Merge: 3cc432dc99 fe226e8bc1
Author: Daniel Mendler <[email protected]>
Commit: GitHub <[email protected]>
Merge pull request #281 from minad/org-modern-indent
Org modern indent
---
CHANGELOG.org | 33 ++++-
README.org | 91 ++++++++++++-
org-modern-indent.el | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++
org-modern.el | 17 ++-
4 files changed, 508 insertions(+), 7 deletions(-)
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 59f4d9acf3..c25a84ba62 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -1,9 +1,11 @@
#+title: org-modern.el - Changelog
-#+author: Daniel Mendler
+#+author: Daniel Mendler & J.D. Smith
#+language: en
* Development
+- Merged [[https://github.com/jdtsmith/org-modern-indent][org-modern-indent]]
as a new extension.
+- Automatically provide code block bracket styling when ~org-indent~ is
enabled using ~org-modern-indent-mode~.
- Add ~org-modern-habit~ face to improve habit progress fontification.
* Version 1.7 (2025-03-11)
@@ -67,3 +69,32 @@
* Version 0.8 (2023-02-15)
- Start of changelog.
+
+* org-modern-indent change history
+
+~org-modern-indent~ was incorporated as an extension to ~org-modern~ in v2.0.
The prior change history for ~org-modern-indent~ is as follows, based on its
release versions as a separate package:
+
+- **v0.6**: Merged as an ~org-modern~ extension.
+- **v0.5.1**: Small simplifications for block drawing.
+- **v0.5**: Another complete rewrite which substantially improves
+ performance and accuracy. Now block detection uses ~org-element~
+ and the block styling is implemented in
+ ~before/after-change-functions~. Benefits include:
+ 1. Higher performance and more reliable fontification.
+ 2. Ability to detect and correctly treat _damaged_ blocks
+ (header/footer line altered or removed) as well as _merged_ blocks.
+ 2. Caches all prefix strings for lower memory usage/GC churn.
+ 3. No more "runaway" formatting when partial blocks are created:
+ only _real_ blocks (according to ~org-element~) are
+
+ Note that v0.5 implements indented block styling using display
+ properties on the indentation text, so navigation will "skip over"
+ it.
+- **v0.1**: features a complete re-write to use font-lock directly. This
+ has a few benefits:
+ 1. No longer relies on org-mode face names for recognizing
+ blocks, so ~org-src-block-faces~ can have arbitrary faces
+ applied, e.g. for different ~src~ languages, as in the screenshot.
+ 2. Eliminates the "race" between font-locking and applying the prefix text
properties.
+ 3. Enables in-text bracket decorations for "bulk-indented" blocks, for
example blocks situated
+ in an arbitrarily-nested plain list item.
diff --git a/README.org b/README.org
index 1409c4983c..1c87a05c18 100644
--- a/README.org
+++ b/README.org
@@ -33,6 +33,16 @@ recommend variants of the
[[https://github.com/be5invis/Iosevka][Iosevka]] font,
the Org buffer. Larger values of =line-spacing= are not recommended, since
Emacs
does not center the text vertically (see Emacs
[[https://debbugs.gnu.org/cgi/bugreport.cgi?bug=76390][bug#76390]]).
+* Indent
+
+Since version 2.0, this package also incorporates ~org-modern-indent~, which
provides block bracket styling when ~org-indent~ is enabled, including support
for "bulk-indented" blocks nested within plain lists:
+
+#+html: <img width="716" alt="image"
src="https://github.com/user-attachments/assets/7ca42ce7-dcfb-4c66-b5f4-1798a4fd4df5"
/>
+
+~org-modern-indent~ is enabled by ~org-modern~ by default if you use
~org-indent~; configure ~org-modern-indent-block=nil~ to disable this behavior.
To activate ~org-indent-mode~ in all org files, set ~org-startup-indented=t~.
+
+*Note*: Non-nil ~line-spacing~ is not recommended with ~org-modern-indent~, as
it leads to gaps in the vertical bars drawn to indicate blocks.
+
* Configuration
The package is available on GNU ELPA and MELPA. You can install the package
with
@@ -93,14 +103,89 @@ screenshot above after the installation of =org-modern=.
(global-org-modern-mode)
#+end_src
+An alternative setup, using ~use-package~ and modifying some of the display
entities:
+
+#+begin_src emacs-lisp
+ (use-package org-modern
+ :custom
+ (org-modern-hide-stars nil)
+ (org-modern-todo-faces
+ '(("STARTED" :foreground "yellow")
+ ("CANCELED" org-special-keyword :inverse-video t :weight bold)))
+ (org-modern-list
+ '((?* . "•")
+ (?+ . "‣")))
+ (org-modern-fold-stars
+ '(("▶" . "▼")
+ ("▷" . "▽")
+ ("▸" . "▾")
+ ("▹" . "▿")))
+ (org-modern-checkbox
+ '((?X . "✔")
+ (?- . "┅")
+ (?\s . " ")))
+ (org-modern-label-border 1)
+ :hook
+ (org-mode . org-modern-mode)
+ (org-agenda-finalize . org-modern-agenda))
+#+end_src
+
+
* Incompatibilities
-- =org-indent-mode= is not compatible with the block prettification in the
fringe.
- If =org-indent-mode= is enabled, =org-modern= will disable the block
prettification.
- =org-num-mode= interferes with the =org-modern= prettification of TODO
keywords.
- =visual-wrap-prefix-mode= relies on the =wrap-prefix= text property which is
also
used by =org-modern=.
+* Block Indentation Hints
+
+A few hints for managing indented blocks.
+
+** Bulk-indented blocks (e.g. within plain lists):
+
+Bulk-indented blocks have "real" (space/tab) indentation applied and managed
by org. This extra indentation is applied by org on _top_ of the (fake,
prefix-based) indentation used by org-indent. To nest blocks properly within
such indented content, e.g. in plain list items, you only have to begin the
~#+begin~ at the same level as the list element's text.
+
+As an important principle, ~org-modern-indent~ does not alter the contents of
the text in your org documents, not even indentation. It just styles what is
there. To help achieve proper block bulk-indented alignment, here are a few
ways to alter blocks indentation using org and other commands:
+
+- **Start things right**: Hit return after your last line of text (e.g in a
list item), then immediately hit =C-c C,= to create the desired block. It will
be indented at the right level:
+
+ #+begin_src org
+ - This list item contains a:
+ - sublist, which holds a block:
+ [C-c C-,] here
+ #+end_src
+
+- *Move flush left*: Note: =M-{= will get you to the start of a block quickly.
=M-\= at block start will move the block's first header line to column 0.
Then =M-S-left= (or =right=) will indent the full block.
+- *Indent rigidly*: =M-h= selects the entire block. Then =C-x TAB= enters
"rigid indent" mode, after which left/right moves the entire block.
+- *Re-indent a block*: If you have a block that is partially aligned, perhaps
with a "hanging end", like so:
+
+ #+begin_src org
+
+ - List 1
+ - List 2
+ ,#+begin_src lang
+ foo_lang(x)
+ ,#+end_src
+ #+end_src
+
+ you can simply use =M-S-left/right= at block start (or in fact anywhere on
the block header/footer) to ~org-indent-block~. Note that
~org-src-preserve-indentation=nil~ is an important setting, to allow org to
(re-)indent blocks to respect the local indentation inside list and other
elements. Also note that (from ~org-indent-region~):
+
+ #+begin_quote
+ The function will not indent contents of example blocks, verse blocks and
export blocks as leading white spaces are assumed to be significant there.
+ #+end_quote
+
+** Font spacing and faces
+
+The default ~fixed-pitch~ font (from which ~org-meta-line~ inherits) has line
spacing >1.0 on some systems. This will introduce gaps _even if your default
font is changed_, and ~line-space~ is nil. To correct it, add:
+
+#+begin_src emacs-lisp
+(set-face-attribute 'fixed-pitch nil :family "Hack" :height 1.0) ; or whatever
font family
+#+end_src
+
+*** The bracket style
+
+If you'd like a different face than ~org-meta-line~ for the "bracket",
configure the ~org-modern-indent-bracket-line~ face.
+
* Alternatives
The tag style of =org-modern= is inspired by Nicholas Rougier's
[[https://github.com/rougier/svg-tag-mode][svg-tag-mode]]. In
@@ -116,7 +201,7 @@ table and block styling from =org-modern=. If you are
interested in further
tweaks, Emacs comes with the builtin =prettify-symbols-mode= which can be used
for
individual styling of custom keywords.
-Alternatives are the older =org-superstar= and =org-bullets= packages, which
are
+Alternatives are the older
[[https://github.com/integral-dw/org-superstar-mode][=org-superstar=]] and
[[https://github.com/sabof/org-bullets][=org-bullets=]] packages, which are
more limited and mainly adjust headlines and lists. =org-superstar= relies on
character composition, while =org-modern= uses text properties, which are
considered more future-proof. Note that =org-modern= is a full replacement for
diff --git a/org-modern-indent.el b/org-modern-indent.el
new file mode 100644
index 0000000000..0f70513852
--- /dev/null
+++ b/org-modern-indent.el
@@ -0,0 +1,374 @@
+;;; org-modern-indent.el --- Style indented blocks -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022-2025 J.D. Smith
+
+;; Author: J.D. Smith
+;; Created 2022
+;; Keywords: convenience, text
+
+;; This file is part of GNU Emacs.
+
+;; 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 3 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; 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, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; `org-modern-indent' is an `org-modern' extension which approximates
+;; the block highlighting style of `org-modern'. By default, it is
+;; activated when `org-indent' is is active in the buffer. It was
+;; merged into `org-modern' as of v2.0.
+
+;;; Code:
+(require 'compat)
+(require 'org-indent)
+(require 'org-element)
+(eval-when-compile (require 'cl-lib))
+
+;;;; Face and style
+(defgroup org-modern-indent nil
+ "Org-modern style blocks which works with org-indent."
+ :group 'org
+ :prefix "org-modern-indent-")
+
+;; Face for org-modern-indent line
+(defface omi/bracket-line
+ '((t (:inherit (org-meta-line) :weight light)))
+ "Face for bracket line in org-modern-indent."
+ :group 'faces)
+
+(defconst omi/begin (propertize "╭" 'face 'omi/bracket-line))
+(defconst omi/guide (propertize "│" 'face 'omi/bracket-line))
+(defconst omi/end (propertize "╰" 'face 'omi/bracket-line))
+
+(defvar omi/begin-re
+ "\\([ \t]*\\)\\(#\\+\\)\\(?:begin\\|BEGIN\\)_\\S-")
+
+;;;; Utility
+(defsubst omi/-lbp (p &optional n)
+ "Return the line beginning position N at point P."
+ (save-excursion (goto-char p) (pos-bol n)))
+
+;;;; Block prefix cache
+(defvar omi/-prefixes (make-hash-table :test #'equal)
+ "A hash table of cached line prefixes.
+The hash index is a combination of the (unstyled) `org-indent' length
+and the \"real\" (space) indentation at the block beginning. Each hash
+value is a vector of length 3:
+
+ [BEGIN GUIDE END]
+
+holding the wrap/indent prefix strings for the block begin, end, and
+guide (body).")
+
+(defun omi/-prefix (org-indent extra-indent)
+ "Cache and return prefix strings based on the indent levels.
+There are 2 types of indentation: ORG-INDENT (styled with `org-indent'
+face), and EXTRA-INDENT (unstyled). See `org-modern-indent--prefix' for
+the contents."
+ (unless (and (= org-indent 0) (= extra-indent 0))
+ (let* ((key (+ org-indent (* 1000 extra-indent)))
+ (cached (gethash key omi/-prefixes)))
+ (or cached
+ (puthash key
+ (cl-loop for type in '("begin" "guide" "end")
+ for tstr = (symbol-value
+ (intern (concat "org-modern-indent-"
type)))
+ with olen = (if (> extra-indent 0) org-indent
+ (1- org-indent))
+ with ostr = (and (> olen 0)
+ (propertize (make-string olen ?\s)
+ 'face '(org-indent
default)))
+ with pstr = (and (> extra-indent 0)
+ (make-string (1- extra-indent)
?\s))
+ collect (concat ostr pstr tstr))
+ omi/-prefixes)))))
+
+;;;; Finding and operating on blocks
+(defsubst omi/-block-p (el)
+ "Return non-nil if EL is an org block element."
+ (when-let ((el) (type (car el)))
+ (memq type
+ '(center-block comment-block dynamic-block
+ example-block export-block quote-block
+ special-block src-block verse-block))))
+
+(defun omi/-block-at-point (&optional pos)
+ "Return the org block element at POS, if any.
+Considers both the element at point, and its parent. Does not consider
+affiliated content like title as part of the block."
+ (let ((pos (or pos (point))))
+ (when-let ((element (org-element-at-point pos)))
+ (when (or (omi/-block-p element)
+ (omi/-block-p
+ (setq element (org-element-property :parent element))))
+ (unless (< pos (org-element-property :post-affiliated element))
+ element)))))
+
+(defsubst omi/-block-beg-end (node)
+ "Return a cons of NODE's (BEG . END).
+BEG is at the beginning of the #+BEGIN line, and END is at the end of
+the #+END line."
+ (let* ((bbeg (org-element-property :post-affiliated node)) ; on #+begin
+ (bend (save-excursion
+ (goto-char (org-element-property :end node))
+ (skip-chars-backward "\n\t ")
+ (point))))
+ (cons bbeg bend)))
+
+(defun omi/-walk-blocks (beg end fun)
+ "Walk all org blocks intersecting the region from BEG..END.
+FUN is called on each block with the beginning and end positions of the
+block (at the beginning and end of the lines containing the header and
+footer, respectively). Note that the first block's beginning may lie
+prior to BEG, and the last block's end my fall past END, if either only
+partially overlaps the region."
+ (org-with-wide-buffer
+ (goto-char beg)
+ (let ((node (omi/-block-at-point)) finished)
+ (while (not finished)
+ (when node
+ (cl-destructuring-bind (bbeg . bend) (omi/-block-beg-end node)
+ (funcall fun bbeg bend)
+ (goto-char bend)
+ (when (>= bend end) (setq finished t)))) ; last block
+ (unless finished
+ (if (re-search-forward omi/begin-re end t)
+ (setq node (omi/-block-at-point))
+ (setq finished t)))))))
+
+;;;; Drawing block brackets
+(defun omi/-draw-block (beg end beg0 end0 org-indent real-indent)
+ "Insert brackets for block between BEG and END.
+BEG0 and END0 represents the block's full range, corresponding to the
+line beginning/end positions of the block's #+BEGIN/END header/footer
+lines. The `org-indent' prefix length at block header is ORG-INDENT,
+and REAL-INDENT is the amount of \"real\" (hard space) block
+indentation. REAL-INDENT may be zero."
+ (with-silent-modifications
+ (pcase-let* (((seq beg-prefix guide-prefix end-prefix)
+ (omi/-prefix org-indent real-indent))
+ ((seq beg-display guide-display end-display)
+ (and (> real-indent 0) (omi/-prefix real-indent 0)))
+ (beg-bol (omi/-lbp beg))
+ (body-start (max (omi/-lbp beg0 2) beg-bol)))
+ (cl-macrolet ((add-prefixes (pbeg pend line-prefix wrap-prefix)
+ `(add-text-properties ,pbeg ,pend
+ `(,@(and (= real-indent 0) `(line-prefix ,,line-prefix))
+ wrap-prefix ,,wrap-prefix)))
+ (add-guides (pbeg pend display-str)
+ `(add-text-properties ,pbeg ,pend
+ `( omi/display ,,display-str rear-nonsticky t))))
+ (when (> body-start beg) ;; BEGIN
+ (add-prefixes beg-bol body-start beg-prefix guide-prefix)
+ (when (> real-indent 0)
+ (add-guides beg-bol (+ beg-bol real-indent) beg-display)))
+ (when (> end body-start) ;; GUIDE BODY
+ (let* ((end-bol (omi/-lbp end))
+ (end0-bol (omi/-lbp end0))
+ (after-end (omi/-lbp end 2))
+ (body-final (if (= end-bol end0-bol) end-bol after-end)))
+ (add-prefixes body-start body-final guide-prefix guide-prefix)
+ (when (> real-indent 0)
+ (goto-char body-start)
+ (while (< (point) body-final)
+ (when (>= (current-indentation) real-indent)
+ (add-guides (point) (+ (point) real-indent) guide-display))
+ (forward-line)))
+ (when (= end-bol end0-bol) ;; END
+ (if (= real-indent 0)
+ (add-prefixes end-bol after-end end-prefix end-prefix)
+ (goto-char end-bol)
+ (when (>= (current-indentation) real-indent)
+ (add-guides end-bol (+ end-bol real-indent)
end-display))))))))))
+
+;;;; org-indent interfacing
+(defvar-local omi/-level-change-end nil)
+(defun omi/-refresh-maybe-watch (fun beg end &rest r)
+ "Watch for org-indent heading refreshes and record the new end position.
+FUN is the wrapped function `org-indent-refresh-maybe', and BEG,
+END, and R are its arguments."
+ ;; sadly org-indent-refresh-maybe modifies beg/end but does not return
+ (let ((oiap-orig (symbol-function 'org-indent-add-properties)))
+ (cl-letf (((symbol-function 'org-indent-add-properties)
+ (lambda (b e &optional delay)
+ (setq omi/-level-change-end (and (> e end) e))
+ (funcall oiap-orig b e delay))))
+ (apply fun beg end r))))
+
+(defun omi/-strip-display (beg end)
+ (with-silent-modifications
+ (remove-text-properties beg end '(omi/display nil))))
+
+(defvar-local omi/-init nil)
+(defun omi/-wait-and-refresh (buf)
+ "Wait for org-indent to finish initializing BUF, then refresh."
+ (if (or (not (bound-and-true-p org-indent-agentized-buffers))
+ (not (memq buf org-indent-agentized-buffers)))
+ (omi/init buf)
+ ;; still waiting
+ (when (buffer-live-p buf)
+ (with-current-buffer buf
+ (if omi/-init
+ (let ((cnt (cl-incf (cadr omi/-init))))
+ (if (> cnt 5)
+ (user-error
+ "org-modern-indent: Gave up waiting for %s to initialize"
+ buf)
+ (timer-activate
+ (timer-set-time (car omi/-init)
+ (time-add (current-time) 0.2)))))
+ (setq omi/-init
+ (list (run-at-time 0.1 nil #'omi/-wait-and-refresh buf)
+ 1)))))))
+
+(defun omi/-refresh ()
+ "Strip custom display property and refresh line prefix in entire buffer."
+ (with-silent-modifications
+ (org-with-wide-buffer
+ (when org-indent-mode
+ (omi/-strip-display (point-min) (point-max))
+ (org-indent-refresh-maybe (point-min) (point-max) nil)))))
+
+;;;; Before/after change
+(defun omi/-intersects-leader-at (target beg end)
+ "Return non-nil if BEG..END intersects initial non-blank text at TARGET."
+ (save-excursion
+ (goto-char target)
+ (back-to-indentation)
+ (let ((beg-targ (point))
+ (end-targ (progn (skip-chars-forward "^[:blank:]\n") (point))))
+ (and (<= beg end-targ) (<= beg-targ end)))))
+
+(defun omi/-before-change (beg end)
+ "Check the BEG..END range for modified block header/footer lines.
+Adds an `:org-modern-indent-block' text property to the block, with
+value `damaged' for damaged block."
+ (cl-labels
+ ((detect-block-damage (bl-beg bl-end)
+ "Detect and mark damaged blocks."
+ (unless (and (<= beg bl-beg) (>= end bl-end)) ; entire block
replaced/deleted
+ (with-silent-modifications
+ (if (or (omi/-intersects-leader-at bl-beg beg end)
+ (omi/-intersects-leader-at bl-end beg end))
+ (progn
+ (put-text-property bl-beg (1+ bl-beg) :omi/indent nil) ; now
unknown!
+ (put-text-property bl-beg (omi/-lbp bl-end 2) :omi/block
'damaged))
+ (put-text-property bl-beg (omi/-lbp bl-end 2) :omi/block t))))))
+ (omi/-walk-blocks beg end #'detect-block-damage)))
+
+(defun omi/-pos-in-leader-p (pos)
+ "Return non-nil if a block's header/footer leader text encompasses POS."
+ (when-let ((node (omi/-block-at-point pos)))
+ (org-with-wide-buffer
+ (cl-destructuring-bind (bbeg . bend) (omi/-block-beg-end node)
+ (or (omi/-intersects-leader-at bbeg pos pos)
+ (omi/-intersects-leader-at bend pos pos))))))
+
+(defun omi/-after-change (beg end _len)
+ "Perform after-change operations on changed text from BEG..END.
+To be added after `org-indent-refresh-maybe' on
+`after-change-functions'."
+ (with-silent-modifications
+ (let ((beg-block (get-text-property beg :omi/block))
+ (end-block (get-text-property end :omi/block))
+ extend)
+ (when (and beg-block (or (eq beg-block 'damaged) (omi/-pos-in-leader-p
beg)))
+ (setq extend t
+ beg (or (previous-single-property-change beg :omi/block) beg)))
+ (when (and end-block (or (eq end-block 'damaged) (omi/-pos-in-leader-p
end)))
+ (setq extend t
+ end (or (next-single-property-change end :omi/block) end)))
+ (when extend
+ (remove-text-properties beg end '(:omi/block nil :omi/indent nil))
+ (org-indent-add-properties beg end)))
+ ;; If we had a level change, extend down to the next heading
+ (when omi/-level-change-end (setq end (max end omi/-level-change-end)))
+ (setq beg (omi/-lbp beg) end (omi/-lbp end 2))
+ (remove-text-properties beg end '(omi/display nil))
+ (omi/-walk-blocks beg end
+ (lambda (bl-beg bl-end)
+ (let ((beg0 bl-beg) (end0 bl-end)
+ (pf (get-text-property bl-beg 'line-prefix))
+ oi-pos oi ci full-block)
+ (save-excursion
+ (goto-char bl-beg)
+ (back-to-indentation)
+ (setq oi-pos (point)
+ oi (get-text-property oi-pos :omi/indent)
+ ci (current-indentation)))
+ (unless (eq oi ci)
+ (put-text-property oi-pos (1+ oi-pos) :omi/indent ci))
+ (when (and oi (= oi 0) (/= oi ci)) ; was flush, is now indented:
+ (org-indent-add-properties bl-beg bl-end)) ; repair block's prefixes
+ (when (< bl-beg beg) ; 1st block straddles BEG
+ (if (eq oi ci) (setq bl-beg beg) (setq full-block t)))
+ (when (> bl-end end) ; last block straddles END
+ (if (eq oi ci) (setq bl-end end) (setq full-block t)))
+ (when full-block
+ (with-silent-modifications
+ (remove-text-properties bl-beg bl-end '(omi/display nil))))
+ (omi/-draw-block bl-beg bl-end beg0 end0 (length pf) ci))))))
+
+;;;; Mode/setup
+(defun omi/init (&optional buf)
+ "Register buffer BUF and refresh.
+To be added to `org-indent-post-buffer-init-functions'."
+ (interactive)
+ (let ((buf (or buf (current-buffer))))
+ (when (buffer-live-p buf) ; org-capture buffers vanish fast
+ (with-current-buffer buf
+ (add-hook 'before-change-functions #'omi/-before-change nil t)
+ (or (memq #'omi/-after-change after-change-functions)
+ (cl-loop for func on after-change-functions
+ if (eq (car func) 'org-indent-refresh-maybe) do
+ (setcdr func (cons #'omi/-after-change (cdr func))) and
return t)
+ (add-hook 'after-change-functions #'omi/-after-change 98 t))
+ (org-with-wide-buffer (omi/-after-change (point-min) (point-max) nil))
+ (setq omi/-init t)))))
+
+;;;###autoload
+(define-minor-mode omi/mode
+ "Org-modern-like block brackets within org-indent."
+ :global nil
+ :group 'org-modern-indent
+ (if omi/mode
+ (progn
+ (advice-add 'org-indent-refresh-maybe :around
+ #'omi/-refresh-maybe-watch)
+ (cond
+ ;; already registered before, just toggle
+ ((or (called-interactively-p 'any) omi/-init) (omi/init))
+ ;; Register with buffer init
+ ((boundp 'org-indent-post-buffer-init-functions)
+ (add-hook 'org-indent-post-buffer-init-functions #'omi/init nil t))
+ ;; No hook available, use the less reliable method
+ (t (omi/-wait-and-refresh (current-buffer))))
+ (cl-pushnew 'omi/display
+ (alist-get 'display char-property-alias-alist)))
+ ;; Disabling
+ (advice-remove 'org-indent-refresh-maybe #'omi/-refresh-maybe-watch)
+ (remove-hook 'before-change-functions #'omi/-before-change t)
+ (remove-hook 'after-change-functions #'omi/-after-change t)
+ (if (boundp 'org-indent-post-buffer-init-functions)
+ (remove-hook 'org-indent-post-buffer-init-functions #'omi/init t)
+ (cancel-timer (car omi/-init))
+ (setq omi/-init nil))
+ (omi/-refresh)))
+
+(provide 'org-modern-indent)
+;;; org-modern-indent.el ends here
+;; Local Variables:
+;; read-symbol-shorthands: (("omi/" . "org-modern-indent-") (":omi/" .
":org-modern-indent-"))
+;; package-lint--sane-prefixes: "^omi/"
+;; End:
+
diff --git a/org-modern.el b/org-modern.el
index 22b3cba928..152e96f3ac 100644
--- a/org-modern.el
+++ b/org-modern.el
@@ -225,6 +225,11 @@ which specifies the offset of the block border from the
edge of
the window."
:type '(choice boolean natnum))
+(defcustom org-modern-indent-block t
+ "Whether to style indented blocks when using `org-indent'.
+See `org-modern-indent-mode'."
+ :type 'boolean)
+
(defcustom org-modern-keyword t
"Prettify keywords like #+title.
If set to t, the prefix #+ will be hidden. If set to a string,
@@ -377,6 +382,9 @@ the font.")
(defconst org-modern--table-overline '(:overline t))
(defconst org-modern--table-sp '((space :width (org-modern--table-sp-width))
(space :width (org-modern--table-sp-width))))
+(defvar org-indent-mode)
+(defvar org-modern-indent-mode)
+(declare-function org-modern-indent-mode "org-modern-indent")
(defun org-modern--checkbox ()
"Prettify checkboxes according to `org-modern-checkbox'."
@@ -796,7 +804,7 @@ whole buffer; otherwise, for the line at point."
(2 '(face org-modern-label display #(" " 0 1 (face (:strike-through
t)))) t))))
;; Do not add source block fringe markers if org-indent-mode is
;; enabled. org-indent-mode uses line prefixes for indentation.
- ;; Therefore we cannot have both.
+ ;; org-modern-indent handles this.
(when (and org-modern-block-fringe (not (bound-and-true-p org-indent-mode)))
'(("^[ \t]*#\\+\\(?:begin\\|BEGIN\\)_\\S-"
(0 (org-modern--block-fringe)))))
@@ -862,7 +870,9 @@ whole buffer; otherwise, for the line at point."
(when (eq org-modern-star 'fold)
(add-hook 'org-cycle-hook #'org-modern--cycle nil 'local))
(org-modern--update-faces)
- (org-modern--update-bitmaps))
+ (org-modern--update-bitmaps)
+ (when (and org-indent-mode org-modern-indent-block)
+ (org-modern-indent-mode 1)))
(t
(remove-from-invisibility-spec 'org-modern)
(font-lock-remove-keywords nil org-modern--font-lock-keywords)
@@ -872,7 +882,8 @@ whole buffer; otherwise, for the line at point."
(remove-hook 'org-after-promote-entry-hook #'org-modern--unfontify-line
'local)
(remove-hook 'org-after-demote-entry-hook #'org-modern--unfontify-line
'local)
(when (eq org-modern-star 'fold)
- (remove-hook 'org-cycle-hook #'org-modern--cycle 'local))))
+ (remove-hook 'org-cycle-hook #'org-modern--cycle 'local))
+ (when org-modern-indent-mode (org-modern-indent-mode 0))))
(without-restriction
(with-silent-modifications
(org-modern--unfontify (point-min) (point-max)))