summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Goaziou <mail@nicolasgoaziou.fr>2015-01-21 00:25:47 +0100
committerNicolas Goaziou <mail@nicolasgoaziou.fr>2015-01-21 00:27:24 +0100
commitec0706ea40efd4d6048fe031b216586f1e32c27b (patch)
tree78d8dd2141aaab995a9add899f5110b93911b545
parenta84c467b8ee7b97218ac789ce11c417217c627d2 (diff)
downloadorg-mode-ec0706ea40efd4d6048fe031b216586f1e32c27b.tar.gz
macro: Implement argument extracting and escaping functions
* lisp/org-macro.el (org-macro-escape-arguments, org-macro-extract-arguments): New functions. * lisp/org-element.el (org-element-macro-parser): Use new function. * testing/lisp/test-org-macro.el (test-org-macro/escape-arguments, test-org-macro/extract-arguments): New tests.
-rw-r--r--lisp/org-element.el13
-rw-r--r--lisp/org-macro.el45
-rw-r--r--testing/lisp/test-org-macro.el24
3 files changed, 70 insertions, 12 deletions
diff --git a/lisp/org-element.el b/lisp/org-element.el
index d5d4bb9..3478421 100644
--- a/lisp/org-element.el
+++ b/lisp/org-element.el
@@ -3091,18 +3091,7 @@ Assume point is at the macro."
(skip-chars-forward " \t")))
(end (point))
(args (let ((args (org-match-string-no-properties 3)))
- (when args
- ;; Do not use `org-split-string' since empty
- ;; strings are meaningful here.
- (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")))))
+ (and args (org-macro-extract-arguments args)))))
(list 'macro
(list :key key
:value value
diff --git a/lisp/org-macro.el b/lisp/org-macro.el
index 923e377..60b2982 100644
--- a/lisp/org-macro.el
+++ b/lisp/org-macro.el
@@ -30,6 +30,10 @@
;; `org-macro-initialize-templates', which recursively calls
;; `org-macro--collect-macros' in order to read setup files.
+;; Argument in macros are separated with commas. Proper escaping rules
+;; are implemented in `org-macro-escape-arguments' and arguments can
+;; be extracted from a string with `org-macro-extract-arguments'.
+
;; Along with macros defined through #+MACRO: keyword, default
;; templates include the following hard-coded macros:
;; {{{time(format-string)}}}, {{{property(node-property)}}},
@@ -195,6 +199,47 @@ found in the buffer with no definition in TEMPLATES."
(error "Undefined Org macro: %s; aborting."
(org-element-property :key object))))))))))))
+(defun org-macro-escape-arguments (&rest args)
+ "Build macro's arguments string from ARGS.
+ARGS are strings. Return value is a string with arguments
+properly escaped and separated with commas. This is the opposite
+of `org-macro-extract-arguments'."
+ (let ((s ""))
+ (dolist (arg (reverse args) (substring s 1))
+ (setq s
+ (concat
+ ","
+ (replace-regexp-in-string
+ "\\(\\\\*\\),"
+ (lambda (m)
+ (concat (make-string (1+ (* 2 (length (match-string 1 m)))) ?\\)
+ ","))
+ ;; If a non-terminal argument ends on backslashes, make
+ ;; sure to also escape them as they will be followed by
+ ;; a comma.
+ (concat arg (and (not (equal s ""))
+ (string-match "\\\\+\\'" arg)
+ (match-string 0 arg)))
+ nil t)
+ s)))))
+
+(defun org-macro-extract-arguments (s)
+ "Extract macro arguments from string S.
+S is a string containing comma separated values properly escaped.
+Return a list of arguments, as strings. This is the opposite of
+`org-macro-escape-arguments'."
+ ;; Do not use `org-split-string' since empty strings are
+ ;; meaningful here.
+ (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" ","))))
+ s nil t)
+ "\000"))
+
(provide 'org-macro)
;;; org-macro.el ends here
diff --git a/testing/lisp/test-org-macro.el b/testing/lisp/test-org-macro.el
index 524ad62..d28cb7f 100644
--- a/testing/lisp/test-org-macro.el
+++ b/testing/lisp/test-org-macro.el
@@ -76,6 +76,30 @@
(org-macro-replace-all org-macro-templates)
(buffer-string)))))
+(ert-deftest test-org-macro/escape-arguments ()
+ "Test `org-macro-escape-arguments' specifications."
+ ;; Regular tests.
+ (should (equal "a" (org-macro-escape-arguments "a")))
+ (should (equal "a,b" (org-macro-escape-arguments "a" "b")))
+ ;; Handle empty arguments.
+ (should (equal "a,,b" (org-macro-escape-arguments "a" "" "b")))
+ ;; Properly escape commas and backslashes preceding them.
+ (should (equal "a\\,b" (org-macro-escape-arguments "a,b")))
+ (should (equal "a\\\\,b" (org-macro-escape-arguments "a\\" "b")))
+ (should (equal "a\\\\\\,b" (org-macro-escape-arguments "a\\,b"))))
+
+(ert-deftest test-org-macro/extract-arguments ()
+ "Test `org-macro-extract-arguments' specifications."
+ ;; Regular tests.
+ (should (equal '("a") (org-macro-extract-arguments "a")))
+ (should (equal '("a" "b") (org-macro-extract-arguments "a,b")))
+ ;; Handle empty arguments.
+ (should (equal '("a" "" "b") (org-macro-extract-arguments "a,,b")))
+ ;; Handle escaped commas and backslashes.
+ (should (equal '("a,b") (org-macro-extract-arguments "a\\,b")))
+ (should (equal '("a\\" "b") (org-macro-extract-arguments "a\\\\,b")))
+ (should (equal '("a\\,b") (org-macro-extract-arguments "a\\\\\\,b"))))
+
(provide 'test-org-macro)
;;; test-org-macro.el ends here