Browse Source

org-element: Allow to escape escaping character before a comma

* lisp/org-element.el (org-element-macro-parser): Allow to escape
  escaping character before a comma.  Also do not trim spaces at
  argument boundaries.
* doc/org.texi (Macro replacement): Update documentation about
  possible locations and escaping mechanism.
* testing/lisp/test-org-element.el: Add tests.

With this patch, macro's arguments are read as the following:

  "a,b"     -> '("a" "b")
  "a\,b"    -> '("a,b")
  "a\\,b"   -> '("a\" "b")
  "a\\\,b"  -> '("a\,b")
  "a\\\\,b" -> '(a"\\" "b")

Note that with the patch, you only need to escape backslashes before
a comma:

  "a\\b\,c" -> '("a\\b,c")
Nicolas Goaziou 12 years ago
parent
commit
42e1a6351d
3 changed files with 30 additions and 18 deletions
  1. 10 7
      doc/org.texi
  2. 10 11
      lisp/org-element.el
  3. 10 0
      testing/lisp/test-org-element.el

+ 10 - 7
doc/org.texi

@@ -9655,18 +9655,21 @@ You can define text snippets with
 #+MACRO: name   replacement text $1, $2 are arguments
 @end example
 
-@noindent which can be referenced anywhere in the document (even in
-code examples) with @code{@{@{@{name(arg1,arg2)@}@}@}}.  In addition to
-defined macros, @code{@{@{@{title@}@}@}}, @code{@{@{@{author@}@}@}}, etc.,
-will reference information set by the @code{#+TITLE:}, @code{#+AUTHOR:}, and
-similar lines.  Also, @code{@{@{@{date(@var{FORMAT})@}@}@}} and
+@noindent which can be referenced in
+paragraphs, verse blocks, table cells and some keywords with
+@code{@{@{@{name(arg1,arg2)@}@}@}}@footnote{Since commas separate arguments,
+commas within arguments have to be escaped with a backslash character.
+Conversely, backslash characters before a comma, and only them, need to be
+escaped with another backslash character.}.  In addition to defined macros,
+@code{@{@{@{title@}@}@}}, @code{@{@{@{author@}@}@}}, etc., will reference
+information set by the @code{#+TITLE:}, @code{#+AUTHOR:}, and similar lines.
+Also, @code{@{@{@{date(@var{FORMAT})@}@}@}} and
 @code{@{@{@{modification-time(@var{FORMAT})@}@}@}} refer to current date time
 and to the modification time of the file being exported, respectively.
 @var{FORMAT} should be a format string understood by
 @code{format-time-string}.
 
-Macro expansion takes place during export, and some people use it to
-construct complex HTML code.
+Macro expansion takes place during export.
 
 
 @node Embedded @LaTeX{},  , Macro replacement, Markup

+ 10 - 11
lisp/org-element.el

@@ -3117,20 +3117,19 @@ Assume point is at the macro."
 	  (post-blank (progn (goto-char (match-end 0))
 			     (skip-chars-forward " \t")))
 	  (end (point))
-	  (args (let ((args (org-match-string-no-properties 3)) args2)
+	  (args (let ((args (org-match-string-no-properties 3)))
 		  (when args
 		    ;; Do not use `org-split-string' since empty
 		    ;; strings are meaningful here.
-		    (setq args (split-string args ","))
-		    (while args
-		      (while (string-match "\\\\\\'" (car args))
-			;; Repair bad splits, when comma is protected,
-                        ;; and thus not a real separator.
-			(setcar (cdr args) (concat (substring (car args) 0 -1)
-						   "," (nth 1 args)))
-			(pop args))
-		      (push (pop args) args2))
-		    (mapcar 'org-trim (nreverse args2))))))
+		    (split-string
+		     (replace-regexp-in-string
+		      "\\(\\\\*\\)\\(,\\)"
+		      (lambda (str)
+			(let ((len (length (match-string 1 str))))
+			  (concat (make-string (/ len 2) ?\\)
+				  (if (zerop (mod len 2)) "\000" ","))))
+		      args nil t)
+		     "\000")))))
       (list 'macro
 	    (list :key key
 		  :value value

+ 10 - 0
testing/lisp/test-org-element.el

@@ -1345,6 +1345,16 @@ e^{i\\pi}+1=0
   (should
    (equal '("C-,")
 	  (org-test-with-temp-text "{{{macro(C-\\,)}}}"
+	    (org-element-property :args (org-element-context)))))
+  ;; Allow to escape escaping character.
+  (should
+   (equal '("C-\\" "")
+	  (org-test-with-temp-text "{{{macro(C-\\\\,)}}}"
+	    (org-element-property :args (org-element-context)))))
+  ;; No need to escape backslashes elsewhere.
+  (should
+   (equal '("\\")
+	  (org-test-with-temp-text "{{{macro(\\)}}}"
 	    (org-element-property :args (org-element-context))))))