emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: "Juan Manuel Macías" <maciaschain@posteo.net>
To: orgmode <emacs-orgmode@gnu.org>
Subject: [testing patch] inline-special-block with full implementation for LaTeX backend
Date: Fri, 23 Feb 2024 23:50:52 +0000	[thread overview]
Message-ID: <87ttlyloyr.fsf@posteo.net> (raw)

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

This patch is for testing purposes only, although the implementation for
LaTeX backend is perfectly usable.

The basic syntax of the new element is:

&foo{lorem ipsum dolor}

=> LaTeX: \foo{lorem ipsum dolor}

Nested elements are possible, as well as the inclusion of other elements
such as macros, links or emphasis marks.

The element also supports a list of optional arguments. For the LaTeX
backend there are two specific arguments: :prelatex and :postlatex.
Example:

&foo[:prelatex [bar] :postlatex {baz}]{lorem ipsum dolor}

=> LaTeX: \foo[bar]{lorem ipsum dolor}{baz}

Additionally, there are a number of universal arguments that should be
equivalent between backends where it makes sense to use them (LaTeX,
HTML, odt...). At the moment you can use: :color, :lang and :smallcaps.
Example:

&foo[:color red :smallcaps t :lang french :prelatex [bar] :postlatex {baz}]{lorem ipsum dolor}

=> LaTeX: {\scshape{}\color{red}\foreignlanguage{french}{\foo[bar]{lorem ipsum dolor}{baz}}}

The element also has an anonymous variant that only accepts universal
arguments. If set without arguments it simply returns the content
string. Example:

&_[:color blue :lang italian]{lorem ipsum dolor}

=> LaTeX: {\color{blue}\foreignlanguage{italian}{lorem ipsum dolor}}

We can define in the document a list of aliases that group several arguments:

#+options: inline-special-block-aliases:(("myalias" :color "magenta" :lang "klingon") ("myalias2" :smallcaps t :color "blue" :prelatex "{2345}")) 

&_[:alias myalias :smallcaps t]{lorem ipsum dolor}

=> LaTeX: {\scshape{}\color{magenta}\foreignlanguage{klingon}{lorem ipsum dolor}}

&foo[:alias myalias2]{lorem ipsum dolor}

=> LaTeX: {\scshape{}\color{blue}\foo{2345}{lorem ipsum dolor}}

There can only be one alias per element, but an alias can be combined
with other attributes. It is the closest thing to using styles,
and perhaps the most appropriate term would be ":style". But you can get
confused with the html "style" attribute.

Best regards,

Juan Manuel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-org-element.el-New-element-Inline-Special-Block.patch --]
[-- Type: text/x-patch, Size: 9498 bytes --]

From d211bf601db0dd5c01a3edda74cd1b37f1f9448c Mon Sep 17 00:00:00 2001
From: Juan Manuel Macias <maciaschain@posteo.net>
Date: Wed, 21 Feb 2024 20:44:58 +0100
Subject: [PATCH] org-element.el: New element: Inline Special Block.

* lisp/ox-latex.el (org-latex-inline-special-block): A possible
function for the LaTeX backend.
* lisp/ox.el (org-export-read-inline-special-block-attributes): read
optional attributes.
* lisp/ox.el (org-export-inline-special-block-aliases): aliases for grouped attributes.
---
 lisp/org-element.el | 55 ++++++++++++++++++++++++++++++++++++++++++++-
 lisp/ox-latex.el    | 36 +++++++++++++++++++++++++++++
 lisp/ox.el          | 30 +++++++++++++++++++++++++
 3 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/lisp/org-element.el b/lisp/org-element.el
index 6691ea44e..c430d934b 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -272,6 +272,8 @@ specially in `org-element--object-lex'.")
 			       "\\|"))
 		      ;; Objects starting with "@": export snippets.
 		      "@@"
+                      ;; Objects starting with "&": inline-special-blocks.
+                      "&"
 		      ;; Objects starting with "{": macro.
 		      "{{{"
 		      ;; Objects starting with "<" : timestamp
@@ -313,7 +315,7 @@ specially in `org-element--object-lex'.")
   "List of recursive element types aka Greater Elements.")
 
 (defconst org-element-all-objects
-  '(bold citation citation-reference code entity export-snippet
+  '(bold citation citation-reference code entity export-snippet inline-special-block
 	 footnote-reference inline-babel-call inline-src-block italic line-break
 	 latex-fragment link macro radio-target statistics-cookie strike-through
 	 subscript superscript table-cell target timestamp underline verbatim)
@@ -440,6 +442,7 @@ Don't modify it, set `org-element-affiliated-keywords' instead.")
       ;; Ignore inline babel call and inline source block as formulas
       ;; are possible.  Also ignore line breaks and statistics
       ;; cookies.
+      (inline-special-block ,@standard-set)
       (table-cell citation export-snippet footnote-reference link macro
                   radio-target target timestamp ,@minimal-set)
       (table-row table-cell)
@@ -3535,6 +3538,54 @@ Assume point is at the beginning of the entity."
 	  (org-element-property :name entity)
 	  (when (org-element-property :use-brackets-p entity) "{}")))
 
+;;;; inline special block
+
+(defun org-element-inline-special-block-parser ()
+  "Parse inline special block at point.
+
+When at an inline special block, return a new syntax node of
+`inline-special-block' type containing `:begin', `:end', `:type',
+`:parameters', `:contents-begin', `:contents-end' and
+`:post-blank' as properties.  Otherwise, return nil.
+
+Assume point is at the beginning of the block."
+  (save-excursion
+    (when (looking-at "&\\([_A-Za-z]+\\)[{[]")
+      (goto-char (- (match-end 0) 1))
+      (let* ((begin (match-beginning 0))
+             (parameters
+              (let ((p (org-element--parse-paired-brackets ?\[)))
+                (and (org-string-nw-p p)
+                     (replace-regexp-in-string "\n[ \t]*" " " (org-trim p)))))
+             (contents-begin (when (looking-at-p "{") (+ (point) 1)))
+             (type (org-element--get-cached-string
+                    (match-string-no-properties 1)))
+             (contents-end
+              (progn
+                (goto-char (- contents-begin 1))
+                (org-element--parse-paired-brackets ?\{)
+                (- (point) 1)))
+             (post-blank (skip-chars-forward " \t"))
+             (end (point)))
+        (when contents-end
+          (org-element-create
+           'inline-special-block
+           (list :type type
+                 :parameters parameters
+                 :contents-begin contents-begin
+                 :contents-end contents-end
+                 :begin begin
+                 :end end
+                 :post-blank post-blank)))))))
+
+(defun org-element-inline-special-block-interpreter (inline-special-block contents)
+  "Interpret INLINE SPECIAL BLOCK object as Org syntax."
+  (let ((type (org-element-property :type inline-special-block))
+        (opts (org-element-property :parameters inline-special-block)))
+    (format "&%s%s{%s}"
+            type
+            (if opts (format "[%s]" opts) "")
+            contents)))
 
 ;;;; Export Snippet
 
@@ -5260,6 +5311,8 @@ to an appropriate container (e.g., a paragraph)."
 			          (org-element-strike-through-parser)))
 		         (?@ (and (memq 'export-snippet restriction)
 			          (org-element-export-snippet-parser)))
+                         (?& (and (memq 'inline-special-block restriction)
+                                  (org-element-inline-special-block-parser)))
 		         (?{ (and (memq 'macro restriction)
 			          (org-element-macro-parser)))
 		         (?$ (and (memq 'latex-fragment restriction)
diff --git a/lisp/ox-latex.el b/lisp/ox-latex.el
index cfa2b8178..c0161716b 100644
--- a/lisp/ox-latex.el
+++ b/lisp/ox-latex.el
@@ -101,6 +101,7 @@
     (underline . org-latex-underline)
     (verbatim . org-latex-verbatim)
     (verse-block . org-latex-verse-block)
+    (inline-special-block . org-latex-inline-special-block)
     ;; Pseudo objects and elements.
     (latex-math-block . org-latex-math-block)
     (latex-matrices . org-latex-matrices))
@@ -2095,6 +2096,41 @@ holding contextual information."
    center-block (format "\\begin{center}\n%s\\end{center}" contents) info))
 
 
+;;;; Inline Special Block
+
+(defun org-latex-inline-special-block (inline-special-block contents info)
+  "Transcode an INLINE SPECIAL BLOCK element from Org to LaTeX.
+CONTENTS holds the contents of the block.  INFO is a plist
+holding contextual information."
+  (let* ((type (org-element-property :type inline-special-block))
+         (type-is-anon (string= "_" type))
+	 (parameters (org-element-property :parameters inline-special-block))
+	 (attributes (org-export-read-inline-special-block-attributes parameters))
+         (alias (plist-get attributes :alias))
+         (alias-plist (when alias (cdr (or (assoc alias (plist-get info :inline-special-block-aliases))
+					   (assoc alias org-export-inline-special-block-aliases)))))
+	 (basic-format (if type-is-anon
+                           (format "%s" contents)
+                         (format "\\%s{%s}" type contents))))
+    (if (not attributes)
+        basic-format
+      (let* ((attr-final (if alias-plist (append attributes alias-plist) attributes))
+             (prelatex (plist-get attr-final :prelatex))
+	     (postlatex (plist-get attr-final :postlatex))
+	     (color (plist-get attr-final :color))
+	     (smallcaps (plist-get attr-final :smallcaps))
+	     (lang (plist-get attr-final :lang)))
+        (concat
+         (when (or color smallcaps type-is-anon) "{")
+         (when smallcaps "\\scshape{}")
+         (when color (format "\\color{%s}" color))
+         (when lang (format "\\foreignlanguage{%s}{" lang))
+         (if (not (or prelatex postlatex))
+	     basic-format
+	   (concat "\\" type prelatex "{" contents "}" postlatex))
+         (when lang "}")
+         (when (or color smallcaps type-is-anon) "}"))))))
+
 ;;;; Clock
 
 (defun org-latex-clock (clock _contents info)
diff --git a/lisp/ox.el b/lisp/ox.el
index 5bf55ec3b..cbb6a8dcd 100644
--- a/lisp/ox.el
+++ b/lisp/ox.el
@@ -149,6 +149,7 @@
     (:with-tasks nil "tasks" org-export-with-tasks)
     (:with-timestamps nil "<" org-export-with-timestamps)
     (:with-title nil "title" org-export-with-title)
+    (:inline-special-block-aliases nil "inline-special-block-aliases" org-export-inline-special-block-aliases)
     (:with-todo-keywords nil "todo" org-export-with-todo-keywords)
     ;; Citations processing.
     (:cite-export "CITE_EXPORT" nil org-cite-export-processors))
@@ -528,6 +529,11 @@ This option can also be set with the LANGUAGE keyword."
   :type '(string :tag "Language")
   :safe #'stringp)
 
+(defcustom org-export-inline-special-block-aliases nil
+  "TODO"
+  :group 'org-export-general
+  :type '(alist :value-type (group plist)))
+
 (defcustom org-export-preserve-breaks nil
   "Non-nil means preserve all line breaks when exporting.
 This option can also be set with the OPTIONS keyword,
@@ -3789,6 +3795,30 @@ will become the empty string."
 		(cdr (nreverse (cons (funcall prepare-value s) result))))))))
     (if property (plist-get attributes property) attributes)))
 
+(defun org-export-read-inline-special-block-attributes (attributes)
+  "TODO"
+  (let* ((prepare-value
+	  (lambda (str)
+	    (save-match-data
+	      (cond ((member str '(nil "" "nil")) nil)
+		    ((string-match "^\"\\(\"+\\)?\"$" str)
+		     (or (match-string 1 str) ""))
+		    (t str)))))
+	 (attributes
+	  (let ((value (list attributes)))
+	    (when value
+	      (let ((s (mapconcat #'identity value " ")) result)
+		(while (string-match
+			"\\(?:^\\|[ \t]+\\)\\(:[-a-zA-Z0-9_]+\\)\\([ \t]+\\|$\\)"
+			s)
+		  (let ((value (substring s 0 (match-beginning 0))))
+		    (push (funcall prepare-value value) result))
+		  (push (intern (match-string 1 s)) result)
+		  (setq s (substring s (match-end 0))))
+		;; Ignore any string before first property with `cdr'.
+		(cdr (nreverse (cons (funcall prepare-value s) result))))))))
+    attributes))
+
 (defun org-export-get-caption (element &optional short)
   "Return caption from ELEMENT as a secondary string.
 
-- 
2.43.2


                 reply	other threads:[~2024-02-23 23:52 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=87ttlyloyr.fsf@posteo.net \
    --to=maciaschain@posteo.net \
    --cc=emacs-orgmode@gnu.org \
    /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
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public 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).