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))