From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms8.migadu.com with LMTPS id CMTgFsgv2WV0ngAA62LTzQ:P1 (envelope-from ) for ; Sat, 24 Feb 2024 00:52:40 +0100 Received: from aspmx1.migadu.com ([2001:41d0:303:e16b::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1.migadu.com with LMTPS id CMTgFsgv2WV0ngAA62LTzQ (envelope-from ) for ; Sat, 24 Feb 2024 00:52:40 +0100 X-Envelope-To: larch@yhetil.org Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=XCTc853C; dmarc=pass (policy=none) header.from=posteo.net; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1708732360; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:mime-version:mime-version: content-type:content-type:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=oNS+s8LBLfQ5S3kfeJuctGxzjFLQp/8kQ+4BCzgLK6w=; b=okwh9thCQifHt/Ec/ulrJBJ4GrLAOyrbmEpTsbV/nRH3BmBVkBPNwCPErlzd9CUpnLsfiN p01V9ANqWsoyPLzwz+iFCYkLfcsbrPY0WdvU1eoz/3Deuy4+Bohzm0Dw8UFh2TQfVOv2I+ uw2A8CMG4oYMoETNrpQNA71XTMTilSsAPXtJ1F3mHEsgA1VgkL8+b0gtAlDgp93jicandr kIwnCgOF4YwYdI52EQe7N0wc0qItdLrJSqifEZwm0TvwL42tfBTiSGqlAg+LHmvADIWkLs HWuTP3gOfe/v4uNCkzz1TZ/eBEhjVbQLrmmr1OVjRKJrjSUpRZpLvXGUmlyt3g== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=posteo.net header.s=2017 header.b=XCTc853C; dmarc=pass (policy=none) header.from=posteo.net; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org" ARC-Seal: i=1; s=key1; d=yhetil.org; t=1708732360; a=rsa-sha256; cv=none; b=Ui27+ySyH5iKnl8A2IqOYWrK4h7IjcjX3W/Eqc5D5P4LMpXwRWccEXx80V5UI4UuAVe0HE 6A79V31OpTSg66y5V01Hh3OuuU2h0Px6oMdFMIDJB5giQ24x5TdTLeEXmY4AUlwF0qSH3z d8029tHb1ckczJeWgp2K+tvteaPKNEgu3nCwHRKPBT/PiAptDn3mU89oZlJmfimTnDJ6+u xMQkWXd5AYY/jecyFLRkLaq8bJt3oSjnIOq1GC1R9tTZU8ktlO02WAqPFsvRk0Gvd603zh accBxAWDgGZnuGEd58xofdD5FpJq7Qi6j9AwktgZosPMkuHKZx4GbhHGYlCxrw== Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id C72066072F for ; Sat, 24 Feb 2024 00:52:39 +0100 (CET) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rdfK2-00037I-2v; Fri, 23 Feb 2024 18:51:18 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rdfJv-0002pw-Eb for emacs-orgmode@gnu.org; Fri, 23 Feb 2024 18:51:13 -0500 Received: from mout01.posteo.de ([185.67.36.65]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rdfJn-0003uD-Ih for emacs-orgmode@gnu.org; Fri, 23 Feb 2024 18:51:10 -0500 Received: from submission (posteo.de [185.67.36.169]) by mout01.posteo.de (Postfix) with ESMTPS id B0639240028 for ; Sat, 24 Feb 2024 00:50:55 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=posteo.net; s=2017; t=1708732255; bh=j4NFL1w22xxD17ERNSNQOUwMysBO5/llGo4FmKPnSYs=; h=From:To:Subject:Date:Message-ID:MIME-Version:Content-Type:From; b=XCTc853CZElx+YPVeLrSlUSDNeVvnsQ8kDnHR6SHCfmLkDGnGYH7uzd5XqULssOaS DtV1mfmN3BaYj3ydVDl8gw9VtiYZKDhuKfKDOahqYUtb16myRf6Ig5q3hL9IGKdtFA URpDTWYgiH8m5VU2L2DIDzI+hvQvpMzOFzcvKrpRv0AaiI0vSXk22J5bM6FCAJ+iKo 2AsLQ1LZfKY1XR+zMN1Bnxa3Fji5o/FP1lQsA7VClBVP8xmEcro256BgCWRZf5OCE/ 7SRhfaKmNdgQDZlJQ881r9/zOqOrMJ7qCBs+V2JbN194IpEYzMgYyHZCnroAoKi2Rb d+ZO0b6jPByiQ== Received: from customer (localhost [127.0.0.1]) by submission (posteo.de) with ESMTPSA id 4ThRbg12wBz6tv5 for ; Sat, 24 Feb 2024 00:50:54 +0100 (CET) From: =?utf-8?Q?Juan_Manuel_Mac=C3=ADas?= To: orgmode Subject: [testing patch] inline-special-block with full implementation for LaTeX backend Date: Fri, 23 Feb 2024 23:50:52 +0000 Message-ID: <87ttlyloyr.fsf@posteo.net> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=185.67.36.65; envelope-from=maciaschain@posteo.net; helo=mout01.posteo.de X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: -9.98 X-Spam-Score: -9.98 X-Migadu-Queue-Id: C72066072F X-Migadu-Scanner: mx13.migadu.com X-TUID: xFLqgiiMufvr --=-=-= Content-Type: text/plain 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 --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=0001-org-element.el-New-element-Inline-Special-Block.patch >From d211bf601db0dd5c01a3edda74cd1b37f1f9448c Mon Sep 17 00:00:00 2001 From: Juan Manuel Macias 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 --=-=-=--