branch: externals/ess
commit 1286bf716dca692c9775c1bcb793639b5992bf43
Author: Christophe Gouel <[email protected]>
Commit: Martin Mächler <[email protected]>

    feat(ess-r): Add outline style option
    
    Replace ess-r-outline-regexp with a new user option
    ess-r-outline-style (RStudio or stars). Add
    ess-r-outline-style-alist, regex and level helpers, a dispatcher for
    outline-level, and ess-r-set-outline-style to apply the chosen style
    buffer-locally (hack-local-variables aware). Update ess-r-mode
    initialization, docs, and ChangeLog.
---
 ChangeLog          | 14 ++++++++++++
 doc/ess.texi       | 31 +++++++++++++++++---------
 lisp/ess-custom.el | 11 +++++----
 lisp/ess-r-mode.el | 65 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 102 insertions(+), 19 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6d11a1fa46..dff57a6227 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2025-12-23 Christophe Gouel <[email protected]>
+
+       * lisp/ess-custom.el (ess-r-outline-style) (ess-r-outline-regexp):
+       New user option to pick an outline convention (RStudio rulers vs.
+       stars). Remove option ess-r-outline-regexp.
+       * lisp/ess-r-mode.el (ess-r-outline-style-alist)
+       (ess-r--outline-style-definition)
+       (ess-r--outline-style-value)
+       (ess-r-outline-level)
+       (ess-r-set-outline-style): Drive outline-minor-mode via the selected
+       style and make the choice hack-local-variable aware.
+       * doc/ess.texi (Outline navigation): Document `ess-r-outline-style`
+       and the available styles.
+
 2025-10-27 Christophe Gouel <[email protected]>
 
        * lisp/ess-custom.el: Support for outline-minor-mode
diff --git a/doc/ess.texi b/doc/ess.texi
index 774a685421..74b3b6eb7e 100644
--- a/doc/ess.texi
+++ b/doc/ess.texi
@@ -433,7 +433,7 @@ processes.  This is a good command to consider binding to a 
global key.
 ESS works with processes on remote computers as easily as with processes
 on the local machine.  The recommended way to access a statistical
 program on remote computer is to start it with @xref{Top,,, tramp, TRAMP
-User Manual}. 
+User Manual}.
 
 Start an ssh session using TRAMP with @samp{C-x C-f /ssh:user@@host:
 RET}.  Tramp should open a dired buffer in your remote home directory.
@@ -1328,7 +1328,7 @@ effect of aborting the current command.
 @kindex C-c C-z
 @kbd{C-c C-z} When in process buffer, return to the most recent script
 buffer.  When in a script buffer pop to the associated process buffer.
-Consecutive presses of @kbd{C-z} switch between the script and process 
buffers. 
+Consecutive presses of @kbd{C-z} switch between the script and process buffers.
 
 If @var{toggle-eob} is given, the value of
 @code{ess-switch-to-end-of-proc-buffer} is toggled.
@@ -3089,13 +3089,14 @@ by various ESS variables such as 
@code{ess-imenu-S-generic-expression}.
 @cindex code folding
 @findex outline-minor-mode
 @findex ess-r-outline-level
-@vindex ess-r-outline-regexp
+@vindex ess-r-outline-style
 R editing buffers integrate with @code{outline-minor-mode}.  When you
 toggle the minor mode (for example via @kbd{M-x outline-minor-mode} or
 by adding it to @code{ess-r-mode-hook}), ESS assigns
-@code{ess-r-outline-level} to @code{outline-level} and installs
-@code{ess-r-outline-regexp} so that comment headings drive folding.
-Lines that begin with one or more @samp{#}, followed by text and a
+@code{ess-r-outline-level} to @code{outline-level} and configures
+@code{outline-regexp} according to @code{ess-r-outline-style} so that
+comment headings drive folding.  With the default @code{RStudio} style,
+lines that begin with one or more @samp{#}, followed by text and a
 trailing marker of @samp{----}, @samp{====}, or @samp{####}, are treated
 as outline headings.  This matches the section markers convention adopted by
 RStudio.
@@ -3116,10 +3117,20 @@ mode hook:
 (add-hook 'ess-r-mode-hook #'outline-minor-mode)
 @end example
 
-@defvr {User Option} ess-r-outline-regexp
-Regular expression used by ESS to recognise outline headings in R
-buffers.  The default matches the RStudio-style sections shown above,
-but you can customise it if you prefer a different comment convention.
+@defvr {User Option} ess-r-outline-style
+Select which outline convention @code{ess-r-mode} should use.  The
+default @code{RStudio} style recognises the comment rulers shown above.
+Choose @code{stars} to match the @samp{### * Section} pattern:
+
+@example
+### * Setup
+### ** Data
+### *** Models
+### **** Helpers
+@end example
+
+You can switch styles via
+@code{M-x ess-r-set-outline-style} or by customizing this option.
 @end defvr
 
 @node Toolbar
diff --git a/lisp/ess-custom.el b/lisp/ess-custom.el
index 6610822242..2b051dd376 100644
--- a/lisp/ess-custom.el
+++ b/lisp/ess-custom.el
@@ -1571,11 +1571,14 @@ by `ess-function-template'."
   :group 'ess
   :type 'regexp)
 
-(defcustom ess-r-outline-regexp
-  "^[ \t]*#+ +.*\\(?:----\\|====\\|####\\)\\s-*$"
-  "Regexp used to detect the beginning of R headings."
+(defcustom ess-r-outline-style 'RStudio
+  "Outline convention used by `ess-r-mode'.
+Choose between the comment rulers like RStudio and the \"stars\" headings
+ (\"### ***\")."
   :group 'ess-R
-  :type 'regexp)
+  :type '(choice (const :tag "RStudio comment rulers" RStudio)
+                 (const :tag "Stars (### *** headings)" stars))
+  :safe #'symbolp)
 
 
  ; ess-inf: variables for inferior-ess.
diff --git a/lisp/ess-r-mode.el b/lisp/ess-r-mode.el
index 8cb0d9e0a2..1f542acb05 100644
--- a/lisp/ess-r-mode.el
+++ b/lisp/ess-r-mode.el
@@ -299,14 +299,68 @@ When t, loading a file into a namespaced will output 
information
 about which objects are exported and which stay hidden in the
 namespace.")
 
-(defun ess-r-outline-level ()
-  "R mode `outline-level` function."
+(defconst ess-r--outline-rstudio-regexp
+  "^[ \t]*#+ +.*\\(?:----\\|====\\|####\\)\\s-*$"
+  "R outline Regexp when `ess-r-outline-style' is `RStudio'.")
+
+(defconst ess-r--outline-stars-regexp
+  "^\\(?:> \\)?###\\s-+\\(\\*+\\)\\s-+.*$"
+  "R outline regexp used when `ess-r-outline-style' is `stars'.")
+
+(defun ess-r--outline-style-definition (&optional style)
+  "Return the style definition for STYLE, defaulting to `ess-r-outline-style'."
+  (let ((style (or style ess-r-outline-style)))
+    (or (assq style ess-r-outline-style-alist)
+        (error "Unknown ESS outline style: %s" style))))
+
+(defun ess-r--outline-style-value (key &optional style)
+  "Return value for KEY in STYLE's definition."
+  (cdr (assq key (cdr (ess-r--outline-style-definition style)))))
+
+(defun ess-r--outline-level-rstudio ()
+  "Compute outline level for `RStudio` style headings."
   (save-excursion
     (beginning-of-line)
     (if (looking-at "^[ \t]*\\(#+\\)\\s-")
-       (length (match-string 1))
+        (length (match-string 1))
+      1000)))
+
+(defun ess-r--outline-level-stars ()
+  "Compute outline level for `stars` style headings."
+  (save-excursion
+    (beginning-of-line)
+    (if (looking-at "###\\s-+\\(\\*+\\)\\s-+")
+        (length (match-string 1))
       1000)))
 
+(defconst ess-r-outline-style-alist
+  `((RStudio
+     (outline-regexp . ,ess-r--outline-rstudio-regexp)
+     (outline-level  . ,#'ess-r--outline-level-rstudio))
+    (stars
+     (outline-regexp . ,ess-r--outline-stars-regexp)
+     (outline-level  . ,#'ess-r--outline-level-stars)))
+  "Mapping between outline styles and their regexp/level helpers.")
+
+(defun ess-r-outline-level ()
+  "R mode `outline-level` dispatcher for the current outline style."
+  (funcall (ess-r--outline-style-value 'outline-level)))
+
+(defun ess-r-set-outline-style (&optional style)
+  "Apply STYLE (or `ess-r-outline-style') to the current buffer."
+  (interactive
+   (list (intern (completing-read
+                 "Outline style"
+                 (mapcar (lambda (entry) (symbol-name (car entry)))
+                         ess-r-outline-style-alist)
+                 nil t nil nil
+                 (symbol-name (or ess-r-outline-style 'RStudio))))))
+  (let* ((style (or style ess-r-outline-style))
+         (entry (ess-r--outline-style-definition style)))
+    (setq-local ess-r-outline-style (car entry))
+    (setq-local outline-regexp (ess-r--outline-style-value 'outline-regexp 
style))
+    (setq-local outline-level #'ess-r-outline-level)))
+
 ;; The syntax class for '\' is punctuation character to handle R 4.1
 ;; lambdas. Inside strings it should be treated as an escape
 ;; character which we ensure here.
@@ -843,6 +897,8 @@ top level functions only."
   (setq-local electric-layout-rules '((?{ . after)))
   ;; indentation
   (add-hook 'hack-local-variables-hook #'ess-set-style nil t)
+  ;; outline
+  (add-hook 'hack-local-variables-hook #'ess-r-set-outline-style nil t)
   ;; eldoc
   (ess--setup-eldoc #'ess-r-eldoc-function)
   ;; auto-complete
@@ -864,8 +920,7 @@ top level functions only."
   (when ess-imenu-use-S
     (imenu-add-to-menubar "Imenu-R"))
   ;; outline
-  (setq-local outline-level #'ess-r-outline-level)
-  (setq-local outline-regexp ess-r-outline-regexp)
+  (ess-r-set-outline-style)
   (setq-local beginning-of-defun-function #'ess-r-beginning-of-defun)
   (setq-local end-of-defun-function #'ess-r-end-of-defun)
   (ess-roxy-mode))

Reply via email to