diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 2da04e621..7b9a4e372 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -539,6 +539,22 @@ The change is breaking when ~org-use-property-inheritance~ is set to ~t~. The =TEST= parameter is better served by Emacs debugging tools. ** New and changed options +*** New option ~org-beamer-frame-environment~ + +The new option defines name of an alternative environment to be used +for fragile beamer frames. This option is needed to work around +beamer bug with frame contents containing literal =\end{frame}= string +(for example, inside example blocks). See +https://github.com/josephwright/beamer/issues/360 + +The default value is =orgframe=. + +The option should normally not be changed, except when you need to put +=\end{orgframe}= string inside beamer frames. + +A checker has been added to =M-x org-lint= to detect instances of +~org-beamer-frame-environment~ in Org documents. + *** New option ~org-export-process-citations~ The new option controls whether to use citation processors to process diff --git a/lisp/org-lint.el b/lisp/org-lint.el index 4d2a55d15..e65f9a7eb 100644 --- a/lisp/org-lint.el +++ b/lisp/org-lint.el @@ -1507,6 +1507,19 @@ 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)))))) + +(defvar org-beamer-frame-environment) ; defined in ox-beamer.el +(defun org-lint-beamer-frame (ast) + "Check for occurrences of begin or end frame." + (require 'ox-beamer) + (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. Consider customizing `org-beamer-frame-environment'.") result)) + result))) + ;;; Checkers declaration @@ -1787,6 +1800,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 571b9c239..998810a28 100644 --- a/lisp/ox-beamer.el +++ b/lisp/ox-beamer.el @@ -148,6 +148,22 @@ 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 alternative beamer frame environment. +In frames marked as fragile, this environment is used in place of +the usual frame environment. + +This permits insertion of a beamer frame inside example blocks, +working around beamer limitations. See +https://list.orgmode.org/87a5nux3zr.fsf@t14.reltub.ca/T/#mc7221e93f138bdd56c916b194b9230d3a6c3de09 + +This option may need to be changed when \"\\end{orgframe}\" string is +used inside beamer slides." + :group 'org-export-beamer + :package-version '(Org . "9.7") + :type '(string :tag "Beamer frame") + :safe (lambda (str) (string-match-p "^[A-Za-z]+$" str))) + ;;; Internal Variables @@ -408,12 +424,14 @@ 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"))) + (concat "\\begin{" frame "}" ;; Overlay specification, if any. When surrounded by ;; square brackets, consider it as a default ;; specification. @@ -480,7 +498,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. @@ -814,7 +832,6 @@ contextual information." (org-export-get-reference radio-target info) text)) - ;;;; Template ;; ;; Template used is similar to the one used in `latex' backend, @@ -834,6 +851,9 @@ holding export options." (org-latex--insert-compiler info) ;; Document class and packages. (org-latex-make-preamble info) + ;; Define the alternative frame environment. + (format "\\newenvironment<>{%s}[1][]{\\begin{frame}[environment=%1$s,#1]}{\\end{frame}}\n" + org-beamer-frame-environment) ;; Insert themes. (let ((format-theme (lambda (prop command)