On Thu, Feb 01 2024, Ihor Radchenko <[email protected]> wrote:
> Leo Butler <[email protected]> writes:
>
>> Replying to self: Attached is a patch that adds a property,
>> BEAMER_FRAME, that lets the frame environment name be set on a
>> frame-by-frame basis. In addition, it typesets any fragile frame in the
>> `orgframe' environment. I am not sure if the latter is really needed,
>> given the former.
>
> Thanks!
>
>> Comments?
>
> I am not a big fan of introducing a new BEAMER_FRAME option.
> We already have BEAMER_ENV.
>
> However, BEAMER_ENV is somewhat tricky for frame headings - ox-beamer
> allows special values of frame and fullframe (the latter is not fully
> documented) to allow frames nesting different from
> `org-beamer-frame-level'.
I experimented with BEAMER_ENV, but my impression is that that the code
for extracting a label is quite brittle. I may re-visit this option, but
not at this time.
>
> What about not adding BEAMER_FRAME, but instead adding org-lint checker
> that will detect when frame text contains the problematic \end{orgframe}?
Ok, thanks for your feedback. I have modified the patch along the lines
you suggested. It is attached.
Best,
Leo
From cc3e1572e354d18ce57c6a7b89b7fdd96b7fe1a8 Mon Sep 17 00:00:00 2001
From: Leo Butler <[email protected]>
Date: Thu, 25 Jan 2024 09:48:20 -0600
Subject: [PATCH] lisp/ox-beamer.el: customize the beamer frame environment
name
* lisp/ox-beamer.el (org-beamer-frame-environment): A new customize
variable. It contains the name of an environment that serves as an
alias for the beamer frame environment.
(org-beamer--format-frame): Introduce the new property, :BEAMER_FRAME.
If set in the headline, use that as name of the frame environment.
Otherwise, in frames marked as fragile, the name of the frame
environment is taken from `org-beamer--frame-environment'. In all
other frames, the default name is frame. Unless the frame name is
"frame", the name is added to the list
`org-beamer--frame-environments'.
(org-beamer--frame-environments): New variable and function. The
variable holds a list of names of frame environments found while
formatting frames. The function generates the LaTeX code to define
each new frame environment.
(org-beamer-template): Add a call to `org-beamer--frame-environments'
to insert the environment definitions into the beamer document.
* lisp/org-lint.el (org-lint-beamer-frame): Check the body of each
frame for an occurrence of \begin{orgframe} or \end{orgframe}, or
whatever environment name is in `org-beamer-frame-environment' [4].
Rationale: Code with \begin{frame} or \end{frame} cannot be embedded
in a verbatim environment inside a beamer frame due to a design
decision made by the beamer developers [1]. As suggested in that
report, defining an alias for the beamer frame environment will allow
such verbatim examples to compile correctly [2].
This solution also works with instances of \againframe.
Refs:
[1] https://github.com/josephwright/beamer/issues/360
[2] https://github.com/josephwright/beamer/issues/360#issuecomment-708705250
[3] https://list.orgmode.org/orgmode/87le8eg1hs.fsf@localhost/T/
[4] https://list.orgmode.org/orgmode/87il38i5tb.fsf@localhost/T/
---
lisp/org-lint.el | 15 +++++++++++++++
lisp/ox-beamer.el | 43 ++++++++++++++++++++++++++++++++++++-------
2 files changed, 51 insertions(+), 7 deletions(-)
diff --git a/lisp/org-lint.el b/lisp/org-lint.el
index f68aeab01..7248f687d 100644
--- a/lisp/org-lint.el
+++ b/lisp/org-lint.el
@@ -1446,6 +1446,17 @@ AST is the buffer parse tree."
((memq (org-element-property :type deadline) '(inactive inactive-range))
(list (org-element-begin planning) "Inactive timestamp in DEADLINE will not appear in agenda."))
(t nil))))))
+
+(defun org-lint-beamer-frame (ast)
+ "Check for occurrences of begin or end frame."
+ (org-with-point-at ast
+ (goto-char (point-min))
+ (let (result)
+ (while (re-search-forward
+ (concat "\\\\\\(begin\\|end\\){" org-beamer-frame-environment "}") nil t)
+ (push (list (match-beginning 0) "Beamer frame name may cause error when exporting.") result))
+ result)))
+
;;; Checkers declaration
@@ -1711,6 +1722,10 @@ AST is the buffer parse tree."
"Report $ that might be treated as LaTeX fragment boundary."
#'org-lint-LaTeX-$-ambiguous
:categories '(markup) :trust 'low)
+(org-lint-add-checker 'beamer-frame
+ "Report that frame text contains beamer frame environment."
+ #'org-lint-beamer-frame
+ :categories '(export) :trust 'low)
(org-lint-add-checker 'timestamp-syntax
"Report malformed timestamps."
#'org-lint-timestamp-syntax
diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el
index 82c8841aa..65d8b06ef 100644
--- a/lisp/ox-beamer.el
+++ b/lisp/ox-beamer.el
@@ -149,9 +149,17 @@ which is replaced with the subtitle."
:package-version '(Org . "8.3")
:type '(string :tag "Format string"))
+(defcustom org-beamer-frame-environment "orgframe"
+ "Name of the beamer frame environment."
+ :group 'org-export-beamer
+ :type '(string :tag "Beamer frame"))
+
;;; Internal Variables
+(defvar org-beamer--frame-environments '()
+ "List of beamer frame environments.")
+
(defconst org-beamer-column-widths
"0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 0.0 :ETC"
"The column widths that should be installed as allowed property values.")
@@ -409,12 +417,16 @@ used as a communication channel."
"Format HEADLINE as a frame.
CONTENTS holds the contents of the headline. INFO is a plist
used as a communication channel."
- (let ((fragilep
- ;; FRAGILEP is non-nil when HEADLINE contains an element
- ;; among `org-beamer-verbatim-elements'.
- (org-element-map headline org-beamer-verbatim-elements 'identity
- info 'first-match)))
- (concat "\\begin{frame}"
+ (let* ((fragilep
+ ;; FRAGILEP is non-nil when HEADLINE contains an element
+ ;; among `org-beamer-verbatim-elements'.
+ (org-element-map headline org-beamer-verbatim-elements 'identity
+ info 'first-match))
+ (frame (or (and fragilep org-beamer-frame-environment)
+ "frame")))
+ (unless (string= frame "frame")
+ (add-to-list 'org-beamer--frame-environments frame))
+ (concat "\\begin{" frame "}"
;; Overlay specification, if any. When surrounded by
;; square brackets, consider it as a default
;; specification.
@@ -481,7 +493,7 @@ used as a communication channel."
;; output.
(if (not fragilep) contents
(replace-regexp-in-string "\\`\n*" "\\& " (or contents "")))
- "\\end{frame}")))
+ "\\end{" frame "}")))
(defun org-beamer--format-block (headline contents info)
"Format HEADLINE as a block.
@@ -805,6 +817,21 @@ contextual information."
(org-export-get-reference radio-target info)
text))
+;;;; Frame Environments
+;;
+;; Define any needed frame environments.
+
+(defun org-beamer--frame-environments ()
+ "Define beamer frame environments.
+This function is called once from `org-beamer-template'. As a
+side-effect, it resets `org-beamer--frame-environments' to nil."
+ (prog1
+ (string-join
+ (mapcar
+ #'(lambda(frame)
+ (format "\\newenvironment<>{%s}[1][]{\\begin{frame}[environment=%1$s,#1]}{\\end{frame}}\n" frame))
+ org-beamer--frame-environments))
+ (setq org-beamer--frame-environments '())))
;;;; Template
;;
@@ -825,6 +852,8 @@ holding export options."
(org-latex--insert-compiler info)
;; Document class and packages.
(org-latex-make-preamble info)
+ ;; Define the frame environments, if needed.
+ (org-beamer--frame-environments)
;; Insert themes.
(let ((format-theme
(lambda (prop command)
--
2.43.0