emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: Timothy <tecosaur@gmail.com>
To: Ihor Radchenko <yantar92@gmail.com>
Cc: emacs-orgmode@gnu.org, Daniel Fleischer <danflscr@gmail.com>,
	Nicolas Goaziou <mail@nicolasgoaziou.fr>
Subject: Re: [PATCH] New LaTeX code export option: engraved
Date: Fri, 06 May 2022 00:13:56 +0800	[thread overview]
Message-ID: <87sfpoyn0l.fsf@gmail.com> (raw)
In-Reply-To: <87v8ukyo94.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 166 bytes --]

> [5. text/x-patch; 0004-ox-latex-Introduce-engraved-code-highlighting.patch]…

Ooops, I had some ucommited changes. The correct version is attached.

Timothy

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-ox-latex-Introduce-engraved-code-highlighting.patch --]
[-- Type: text/x-patch, Size: 17470 bytes --]

From b66c291b1f0d1419742449bcde42bf0c4d620c23 Mon Sep 17 00:00:00 2001
From: TEC <tec@tecosaur.com>
Date: Sun, 21 Nov 2021 20:04:12 +0800
Subject: [PATCH] ox-latex: Introduce "engraved" code highlighting

* lisp/ox-latex.el (org-latex-src-block, org-latex-src-block--engraved,
org-latex-inline-src-block, org-latex-inline-src-block--engraved,
org-latex-template, org-latex-listings): Make use of the engraved-faces
package (available on ELPA) to provide an alternative LaTeX code
highlighting backend which functions similarly to htmlize.el for HTML
exports.

(org-latex-engraved-preamble, org-latex-engraved-options): Introduce
variables to construct the preamble for engraved code blocks.

* lisp/ox-beamer.el (org-beamer-template): Modify to add engrave-faces
preamble when applicable.
---
 lisp/ox-beamer.el |   9 ++
 lisp/ox-latex.el  | 273 ++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 272 insertions(+), 10 deletions(-)

diff --git a/lisp/ox-beamer.el b/lisp/ox-beamer.el
index 6be73c91e..73bd95539 100644
--- a/lisp/ox-beamer.el
+++ b/lisp/ox-beamer.el
@@ -857,6 +857,15 @@ (defun org-beamer-template (contents info)
      (let ((template (plist-get info :latex-hyperref-template)))
        (and (stringp template)
 	    (format-spec template (org-latex--format-spec info))))
+     ;; engrave-faces-latex preamble
+     (when (eq org-latex-listings 'engraved)
+       (let ((src-p (org-element-map (plist-get info :parse-tree)
+                        '(src-block inline-src-block) #'identity))
+             (fixedw-p
+              (org-element-map (plist-get info :parse-tree)
+                  '(example-block fixed-width) #'identity)))
+         (when (or src-p fixedw-p)
+           (org-latex-generate-engraved-preamble info src-p))))
      ;; Document start.
      "\\begin{document}\n\n"
      ;; Title command.
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index 4181db175..83bb6f078 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -37,6 +37,8 @@ (defvar org-latex-default-packages-alist)
 (defvar org-latex-packages-alist)
 (defvar orgtbl-exp-regexp)
 
+(declare-function engrave-faces-latex-gen-preamble "ext:engrave-faces-latex")
+(declare-function engrave-faces-latex-buffer "ext:engrave-faces-latex")
 
 \f
 ;;; Define Back-End
@@ -125,6 +127,8 @@ (org-export-define-backend 'latex
     (:latex-default-quote-environment nil nil org-latex-default-quote-environment)
     (:latex-default-table-mode nil nil org-latex-default-table-mode)
     (:latex-diary-timestamp-format nil nil org-latex-diary-timestamp-format)
+    (:latex-engraved-options nil nil org-latex-engraved-options)
+    (:latex-engraved-preamble nil nil org-latex-engraved-preamble)
     (:latex-footnote-defined-format nil nil org-latex-footnote-defined-format)
     (:latex-footnote-separator nil nil org-latex-footnote-separator)
     (:latex-format-drawer-function nil nil org-latex-format-drawer-function)
@@ -937,22 +941,48 @@ (defcustom org-latex-listings nil
   "Non-nil means export source code using the listings package.
 
 This package will fontify source code, possibly even with color.
-If you want to use this, you also need to make LaTeX use the
-listings package, and if you want to have color, the color
-package.  Just add these to `org-latex-packages-alist', for
-example using customize, or with something like:
+There are four implementations of this functionality you may
+choose from (ordered from least to most capable):
+1. Verbatim (nil)
+2. Listings (t)
+3. Minted (minted)
+4. Engraved (engraved)
+
+The first two options provide basic syntax
+highlighting (listings), or none at all (verbatim).
+
+When using listings, you also need to make use of the LaTeX
+\"listings\" package. The \"color\" package is also needed if you
+would like color too.  These can simply be added to
+`org-latex-packages-alist', using customise or something like:
 
   (require \\='ox-latex)
   (add-to-list \\='org-latex-packages-alist \\='(\"\" \"listings\"))
   (add-to-list \\='org-latex-packages-alist \\='(\"\" \"color\"))
 
-Alternatively,
+There are two options for more comprehensive fontification. The
+first can be set with,
+
+  (setq org-latex-listings \\='engraved)
+
+which causes source code to be run through
+`engrave-faces-latex-buffer', which generates colorings using
+Emacs' font-lock information.  This requires the engrave-faces
+package (availible from ELPA), and the fvextra LaTeX package be
+installed.
+
+The styling of the engraved result can customised with
+`org-latex-engraved-preamble' and `org-latex-engraved-options'.
+The default preamble also uses the tcolorbox LaTeX package in
+addition to fvextra.
+
+The second more comprehensive option can be used with,
 
   (setq org-latex-listings \\='minted)
 
-causes source code to be exported using the minted package as
-opposed to listings.  If you want to use minted, you need to add
-the minted package to `org-latex-packages-alist', for example
+which causes source code to be exported using the minted package
+as opposed to listings.  If you want to use minted, you need to
+add the minted package to `org-latex-packages-alist', for example
 using customize, or with
 
   (require \\='ox-latex)
@@ -971,8 +1001,9 @@ (defcustom org-latex-listings nil
   :type '(choice
 	  (const :tag "Use listings" t)
 	  (const :tag "Use minted" minted)
+	  (const :tag "Use engrave-faces-latex" engraved)
 	  (const :tag "Export verbatim" nil))
-  :safe (lambda (s) (memq s '(t nil minted))))
+  :safe (lambda (s) (memq s '(t nil minted engraved))))
 
 (defcustom org-latex-listings-langs
   '((emacs-lisp "Lisp") (lisp "Lisp") (clojure "Lisp")
@@ -1142,6 +1173,124 @@ (defcustom org-latex-custom-lang-environments nil
   :version "26.1"
   :package-version '(Org . "9.0"))
 
+(defcustom org-latex-engraved-preamble
+  "\\usepackage{fvextra}
+
+[FVEXTRA-SETUP]
+
+\\renewcommand\\theFancyVerbLine{\\footnotesize\\color{black!40!white}\\arabic{FancyVerbLine}}
+
+\\usepackage{xcolor}
+
+\\providecolor{codebackground}{HTML}{f7f7f7}
+\\providecolor{codeborder}{HTML}{f0f0f0}
+\\providecolor{EFD}{HTML}{28292e}
+
+% TODO have code boxes keep line vertical alignment
+\\usepackage[breakable,xparse]{tcolorbox}
+\\DeclareTColorBox[]{Code}{o}%
+{colback=codebackground, colframe=codeborder,
+  fontupper=\\footnotesize\\setlength{\\fboxsep}{0pt},
+  colupper=EFD,
+  IfNoValueTF={#1}%
+  {boxsep=2pt, arc=2.5pt, outer arc=2.5pt,
+    boxrule=0.5pt, left=2pt}%
+  {boxsep=2.5pt, arc=0pt, outer arc=0pt,
+    boxrule=0pt, leftrule=1.5pt, left=0.5pt},
+  right=2pt, top=1pt, bottom=0.5pt,
+  breakable}"
+  "Preamble content injected when using engrave-faces-latex for source blocks.
+This is relevant when `org-latex-listings' is set to `engraved'.
+
+There is quite a lot of flexibility in what this preamble can be, as long as it:
+- Loads the fvextra package.
+- Loads the package xcolor (if it is not already loader elsewhere).
+- Defines a \"Code\" environment (note the capital C), which can be
+  later used to wrap \"Verbatim\" environments (provided by fvextra).
+
+A macro-like placeholder is used to set fvextra's defaults according to
+`org-latex-engraved-options':
+
+  [FVEXTRA-SETUP]
+
+In the default value the color \"EFD\" is provided as this is the
+foreground colour provided by engrave-faces-latex.  When there
+are example/fixed-width blocks only, the engraved generated
+preamble is not included, and so it is provided so we may use it
+anyway."
+  :group 'org-export-latex
+  :type 'string
+  :package-version '(Org . "9.6"))
+
+(defcustom org-latex-engraved-options
+  '(("commandchars" . "\\\\\\{\\}")
+    ("highlightcolor" . "white!95!black!80!blue")
+    ("breaklines" . "true")
+    ("breaksymbol" . "\\color{white!60!black}\\tiny\\ensuremath{\\hookrightarrow}"))
+  "Association list of options for the latex fvextra package when engraving code.
+
+These options are set using \\fvset{...} in the preamble of the
+LaTeX export.  Each element of the alist should be a list or cons
+cell containing two strings: the name of the option, and the
+value.  For example,
+
+  (setq org-latex-engraved-options
+    \\='((\"highlightcolor\" \"green\") (\"frame\" \"lines\")))
+  ; or
+  (setq org-latex-engraved-options
+    \\='((\"highlightcolor\" . \"green\") (\"frame\" . \"lines\")))
+
+will result in the following LaTeX in the preamble
+
+\\fvset{%
+  bgcolor=bg,
+  frame=lines}
+
+This will affect all fvextra environments.  Note that the same
+options will be applied to all blocks.  If you need
+block-specific options, you may use the following syntax:
+
+  #+ATTR_LATEX: :options key1=value1,key2=value2
+  #+BEGIN_SRC <LANG>
+  ...
+  #+END_SRC"
+  :group 'org-export-latex
+  :type '(alist :key-type (string :tag "option")
+                :value-type (string :tag "value")))
+
+(defun org-latex-generate-engraved-preamble (info syntax-colours-p)
+  "Generate the preamble to setup engraved code.
+The result is constructed from `org-latex-engraved-preamble' and
+`org-latex-engraved-options'."
+  (let* ((engraved-options
+          (plist-get info :latex-engraved-options))
+         (engraved-preamble-template
+          (plist-get info :latex-engraved-preamble))
+         (engraved-preamble
+          (if (string-match "^[ \t]*\\[FVEXTRA-SETUP\\][ \t]*\n?"
+                            engraved-preamble-template)
+              (replace-match
+               (concat
+                "\\fvset{%\n  "
+                (org-latex--make-option-string engraved-options ",\n  ")
+                "}\n")
+               t t
+               engraved-preamble-template)
+            engraved-preamble-template)))
+    (if syntax-colours-p
+        (concat
+         "\n% Setup for code blocks [1/2]\n\n"
+         engraved-preamble
+         "\n\n% Setup for code blocks [2/2]: syntax highlighting colors\n"
+         (if (require 'engrave-faces-latex nil t)
+             (engrave-faces-latex-gen-preamble)
+           (message "Cannot engrave source blocks. Consider installing `engrave-faces'.")
+           "% WARNING syntax highlighting unavailible as engrave-faces-latex was missing.\n")
+         "\n")
+      (concat
+       "\n% Setup for code blocks\n\n"
+       engraved-preamble
+       "\n"))))
 
 ;;;; Compilation
 
@@ -1756,6 +1905,15 @@ (defun org-latex-template (contents info)
      (let ((template (plist-get info :latex-hyperref-template)))
        (and (stringp template)
             (format-spec template spec)))
+     ;; engrave-faces-latex preamble
+     (when (eq org-latex-listings 'engraved)
+       (let ((src-p (org-element-map (plist-get info :parse-tree)
+                        '(src-block inline-src-block) #'identity))
+             (fixedw-p
+              (org-element-map (plist-get info :parse-tree)
+                  '(example-block fixed-width) #'identity)))
+         (when (or src-p fixedw-p)
+           (org-latex-generate-engraved-preamble info src-p))))
      ;; Document start.
      "\\begin{document}\n\n"
      ;; Title command.
@@ -2142,6 +2300,7 @@ (defun org-latex-inline-src-block (inline-src-block _contents info)
     (pcase (plist-get info :latex-listings)
       ('nil (org-latex--text-markup code 'code info))
       ('minted (org-latex-inline-src-block--minted info code lang))
+      ('engraved (org-latex-inline-src-block--engraved info code lang))
       (_ (org-latex-inline-src-block--listings info code lang)))))
 
 (defun org-latex-inline-src-block--minted (info code lang)
@@ -2149,7 +2308,7 @@ (defun org-latex-inline-src-block--minted (info code lang)
 INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
   (let ((mint-lang (or (cadr (assq (intern lang)
                                    (plist-get info :latex-minted-langs)))
-                       (downcase org-lang)))
+                       (downcase lang)))
         (options (org-latex--make-option-string
                   (plist-get info :latex-minted-options))))
     (format "\\mintinline%s{%s}{%s}"
@@ -2157,6 +2316,24 @@ (defun org-latex-inline-src-block--minted (info code lang)
             mint-lang
             code)))
 
+(defun org-latex-inline-src-block--engraved (_info code lang)
+  "Transcode an inline src block's content from Org to LaTeX, using engrave-faces.
+INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
+  (if (require 'engrave-faces-latex nil t)
+      (let (engraved-buffer engraved-code)
+        (setq engraved-buffer
+              (with-temp-buffer
+                (insert code)
+                (funcall (org-src-get-lang-mode lang))
+                (engrave-faces-latex-buffer)))
+        (setq engraved-code
+              (with-current-buffer engraved-buffer
+                (buffer-string)))
+        (kill-buffer engraved-buffer)
+        (format "\\Verb{%s}"
+                engraved-code))
+    (user-error "Cannot engrave inline src block, `engrave-faces-latex' is unavailible.")))
+
 (defun org-latex-inline-src-block--listings (info code lang)
   "Transcode an inline src block's content from Org to LaTeX, using lstlistings.
 INFO, CODE, and LANG are provided by `org-latex-inline-src-block'."
@@ -3017,6 +3194,9 @@ (defun org-latex-src-block (src-block _contents info)
                                      num-start retain-labels attributes float custom-env))
        ((eq listings 'minted)
         (org-latex-src-block--minted src-block info lang caption caption-above-p label
+                                     num-start retain-labels attributes float))
+       ((eq listings 'engraved)
+        (org-latex-src-block--engraved src-block info lang caption caption-above-p label
                                        num-start retain-labels attributes float))
        (t
         (org-latex-src-block--listings src-block info lang caption caption-above-p label
@@ -3133,6 +3313,79 @@ (defun org-latex-src-block--minted
     ;; Return value.
     (format float-env body)))
 
+(defun org-latex-src-block--engraved
+    (src-block info lang caption caption-above-p _label
+               num-start retain-labels attributes float)
+  "Transcode a SRC-BLOCK element from Org to LaTeX, using engrave-faces-latex.
+LANG, CAPTION, CAPTION-ABOVE-P, LABEL, NUM-START, RETAIN-LABELS, ATTRIBUTES
+and FLOAT are extracted from SRC-BLOCK and INFO in `org-latex-src-block'."
+  (if (require 'engrave-faces-latex nil t)
+  (let* ((caption-str (org-latex--caption/label-string src-block info))
+         (placement (or (org-unbracket-string "[" "]" (plist-get attributes :placement))
+                        (plist-get info :latex-default-figure-position)))
+         (float-env
+          (cond
+           ((string= "multicolumn" float)
+            (format "\\begin{listing*}[%s]\n%s%%s\n%s\\end{listing*}"
+                    placement
+                    (if caption-above-p caption-str "")
+                    (if caption-above-p "" caption-str)))
+           (caption
+            (format "\\begin{listing}[%s]\n%s%%s\n%s\\end{listing}"
+                    placement
+                    (if caption-above-p caption-str "")
+                    (if caption-above-p "" caption-str)))
+           ((string= "t" float)
+            (concat (format "\\begin{listing}[%s]\n"
+                            placement)
+                    "%s\n\\end{listing}"))
+           (t "%s")))
+         (options (plist-get info :latex-engraved-options))
+         (content-buffer
+          (with-temp-buffer
+            (insert
+             (let* ((code-info (org-export-unravel-code src-block))
+                    (max-width
+                     (apply 'max
+                            (mapcar 'length
+                                    (org-split-string (car code-info)
+                                                      "\n")))))
+               (org-export-format-code
+                (car code-info)
+                (lambda (loc _num ref)
+                  (concat
+                   loc
+                   (when ref
+                     ;; Ensure references are flushed to the right,
+                     ;; separated with 6 spaces from the widest line
+                     ;; of code.
+                     (concat (make-string (+ (- max-width (length loc)) 6)
+                                          ?\s)
+                             (format "(%s)" ref)))))
+                nil (and retain-labels (cdr code-info)))))
+            (funcall (org-src-get-lang-mode lang))
+            (engrave-faces-latex-buffer)))
+         (content
+          (with-current-buffer content-buffer
+            (buffer-string)))
+         (body
+          (format
+           "\\begin{Code}\n\\begin{Verbatim}[%s]\n%s\\end{Verbatim}\n\\end{Code}"
+           ;; Options.
+           (concat
+            (org-latex--make-option-string
+             (append
+              (when (and num-start (not (assoc "linenos" options)))
+                `(("linenos")
+                  ("firstnumber" ,(number-to-string (1+ num-start)))))
+              (let ((local-options (plist-get attributes :options)))
+                (and local-options (list local-options))))))
+           content)))
+    (kill-buffer content-buffer)
+    ;; Return value.
+    (format float-env body))
+  (user-error "Cannot engrave src block, `engrave-faces-latex' is unavailible.")))
+
 (defun org-latex-src-block--listings
     (src-block info lang caption caption-above-p label
                num-start retain-labels attributes float)
-- 
2.35.3


  reply	other threads:[~2022-05-05 16:26 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-04 15:59 Timothy
2022-05-05  7:52 ` Daniel Fleischer
2022-05-05 16:09   ` Timothy
2022-05-06  2:35     ` Ihor Radchenko
2022-05-06 11:23       ` Timothy
2022-05-05  7:54 ` Daniel Fleischer
2022-05-05  8:48 ` Ihor Radchenko
2022-05-05 15:17   ` Timothy
2022-05-05 16:13     ` Timothy [this message]
2022-05-07  5:16       ` Ihor Radchenko
2022-05-07  6:57         ` Timothy
2022-05-07 10:40           ` Timothy
2022-05-07 11:33             ` Daniel Fleischer
2022-05-08 14:30               ` [PATCH] (v2) " Timothy
2022-05-09  6:20                 ` Ihor Radchenko
2022-05-09 12:57                   ` Timothy
2022-05-10  8:00                     ` Max Nikulin
2022-05-11 11:06                     ` Ihor Radchenko
2022-05-11 16:05                 ` [PATCH] (v3) " Timothy
2022-05-12 16:40                   ` Daniel Fleischer
2022-05-12 16:44                     ` Timothy
2022-05-05 16:48     ` [PATCH] " Max Nikulin
2022-05-07  4:13       ` Ihor Radchenko
2022-05-09 19:19 ` Sébastien Miquel
2022-05-10  1:13   ` Timothy
2022-05-10 16:10     ` Timothy
2022-05-10 17:53       ` Sébastien Miquel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87sfpoyn0l.fsf@gmail.com \
    --to=tecosaur@gmail.com \
    --cc=danflscr@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    --cc=mail@nicolasgoaziou.fr \
    --cc=yantar92@gmail.com \
    --subject='Re: [PATCH] New LaTeX code export option: engraved' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

Code repositories for project(s) associated with this inbox:

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).