From mboxrd@z Thu Jan 1 00:00:00 1970 From: Nicolas Goaziou Subject: Re: [RFC] The "c" Org macro Date: Sun, 21 May 2017 15:37:13 +0200 Message-ID: <8760guib5i.fsf@nicolasgoaziou.fr> References: <2ee94a64a94b46259b0da6e7d34675c9@HE1PR01MB1898.eurprd01.prod.exchangelabs.com> <87y3u7o3dj.fsf@t3610> <87pofjtk4b.fsf@t3610> <2069df8c23bc43f3b04b6e203b96be9d@HE1PR01MB1898.eurprd01.prod.exchangelabs.com> <87r2zvpyst.fsf@delle7240> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:40200) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dCR2x-0006tN-1Q for emacs-orgmode@gnu.org; Sun, 21 May 2017 09:37:24 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dCR2s-000505-W8 for emacs-orgmode@gnu.org; Sun, 21 May 2017 09:37:23 -0400 Received: from relay2-d.mail.gandi.net ([2001:4b98:c:538::194]:57697) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1dCR2s-0004zi-LG for emacs-orgmode@gnu.org; Sun, 21 May 2017 09:37:18 -0400 Received: from mfilter17-d.gandi.net (mfilter17-d.gandi.net [217.70.178.145]) by relay2-d.mail.gandi.net (Postfix) with ESMTP id 57F97C5A4E for ; Sun, 21 May 2017 15:37:16 +0200 (CEST) Received: from relay2-d.mail.gandi.net ([IPv6:::ffff:217.70.183.194]) by mfilter17-d.gandi.net (mfilter17-d.gandi.net [::ffff:10.0.15.180]) (amavisd-new, port 10024) with ESMTP id kPOWswQg6WeS for ; Sun, 21 May 2017 15:37:14 +0200 (CEST) Received: from saiph.selenimh (196.19.124.78.rev.sfr.net [78.124.19.196]) (Authenticated sender: mail@nicolasgoaziou.fr) by relay2-d.mail.gandi.net (Postfix) with ESMTPSA id 174B0C5A60 for ; Sun, 21 May 2017 15:37:13 +0200 (CEST) Received: from ngz by saiph.selenimh with local (Exim 4.89) (envelope-from ) id 1dCR2n-0003Yy-7G for emacs-orgmode@gnu.org; Sun, 21 May 2017 15:37:13 +0200 In-Reply-To: <87r2zvpyst.fsf@delle7240> (Eric S. Fraga's message of "Thu, 11 May 2017 09:45:06 +0100") List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: "Emacs-orgmode" To: emacs-orgmode@gnu.org --=-=-= Content-Type: text/plain Hello, Eric S Fraga writes: > On Monday, 8 May 2017 at 16:52, Nicolas Goaziou wrote: >> Here is an updated patch, in which one can write >> >> {{{c(sub,reset)}}} >> {{{c(sub, 5)}}} >> >> or even, for the default macro >> >> {{{c(,reset)}}} >> {{{c(, 99)}}} > > Finally found some time to try the patch out. All of the above work > very well. Thank you for the feedback. Here is the last update, with tests and an ORG-NEWS entry. Noteworthy change: the "c" macro is now the "n" macro. If there are no objection nor additional suggestions, I will push it to master in a couple of days. Regards, -- Nicolas Goaziou --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=0001-org-macro-Implement-the-n-macro.patch >From 980b713f28596c7f6486dc1ccfa82f76de7c963d Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Mon, 8 May 2017 12:38:38 +0200 Subject: [PATCH] org-macro: Implement the "n" macro * lisp/org-macro.el (org-macro--counter-table): New variable. (org-macro--counter-initialize): (org-macro--counter-increment): New functions. (org-macro-initialize-templates): Use new functions. * doc/org.texi (Macro replacement): Document new macro. * testing/lisp/test-org-macro.el (test-org-macro/n): (test-org-macro/property): New tests. --- etc/ORG-NEWS | 3 ++ lisp/org-macro.el | 38 +++++++++++++-- testing/lisp/test-org-macro.el | 102 +++++++++++++++++++++++++++++++++++------ 3 files changed, 127 insertions(+), 16 deletions(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 3ca5b0553..b6110c412 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -197,6 +197,9 @@ contents, are now supported. This new function is meant to be used in back-ends supporting images as descriptions of links, a.k.a. image links. See its docstring for details. +**** New macro : ~{{{n}}}~ +This macro creates and increment multiple counters in a document. See +manual for details. **** Add global macros through ~org-export-global-macros~ With this variable, one can define macros available for all documents. **** New keyword ~#+EXPORT_FILE_NAME~ diff --git a/lisp/org-macro.el b/lisp/org-macro.el index 71e917b71..f5ddb92e4 100644 --- a/lisp/org-macro.el +++ b/lisp/org-macro.el @@ -36,8 +36,11 @@ ;; Along with macros defined through #+MACRO: keyword, default ;; templates include the following hard-coded macros: -;; {{{time(format-string)}}}, {{{property(node-property)}}}, -;; {{{input-file}}} and {{{modification-time(format-string)}}}. +;; {{{time(format-string)}}}, +;; {{{property(node-property)}}}, +;; {{{input-file}}}, +;; {{{modification-time(format-string)}}}, +;; {{{n(counter,reset}}}. ;; Upon exporting, "ox.el" will also provide {{{author}}}, {{{date}}}, ;; {{{email}}} and {{{title}}} macros. @@ -129,7 +132,7 @@ function installs the following ones: \"property\", (let ((old-template (assoc (car cell) templates))) (if old-template (setcdr old-template (cdr cell)) (push cell templates)))))) - ;; Install hard-coded macros. + ;; Install "property", "time" macros. (mapc update-templates (list (cons "property" "(eval (save-excursion @@ -143,6 +146,7 @@ function installs the following ones: \"property\", l))))) (org-entry-get nil \"$1\" 'selective)))") (cons "time" "(eval (format-time-string \"$1\"))"))) + ;; Install "input-file", "modification-time" macros. (let ((visited-file (buffer-file-name (buffer-base-buffer)))) (when (and visited-file (file-exists-p visited-file)) (mapc update-templates @@ -152,6 +156,10 @@ function installs the following ones: \"property\", (prin1-to-string visited-file) (prin1-to-string (nth 5 (file-attributes visited-file))))))))) + ;; Initialize and install "n" macro. + (org-macro--counter-initialize) + (funcall update-templates + (cons "n" "(eval (org-macro--counter-increment \"$1\" \"$2\"))")) (setq org-macro-templates templates))) (defun org-macro-expand (macro templates) @@ -280,6 +288,9 @@ Return a list of arguments, as strings. This is the opposite of s nil t) "\000")) + +;;; Helper functions and variables for internal macros + (defun org-macro--vc-modified-time (file) (save-window-excursion (when (vc-backend file) @@ -304,6 +315,27 @@ Return a list of arguments, as strings. This is the opposite of (kill-buffer buf)) date)))) +(defvar org-macro--counter-table nil + "Hash table containing counter value per name.") + +(defun org-macro--counter-initialize () + "Initialize `org-macro--counter-table'." + (setq org-macro--counter-table (make-hash-table :test #'equal))) + +(defun org-macro--counter-increment (name &optional reset) + "Increment counter NAME. +NAME is a string identifying the counter. If optional argument +RESET is a non-empty string, reset the counter instead." + (if (org-string-nw-p reset) + (let ((new-value (if (string-match-p "\\`[ \t]*[0-9]+[ \t]*\\'" reset) + (string-to-number reset) + 1))) + (puthash name new-value org-macro--counter-table)) + (let ((value (gethash name org-macro--counter-table))) + (puthash name + (if (null value) 1 (1+ value)) + org-macro--counter-table)))) + (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 26c56745c..64b0a97cc 100644 --- a/testing/lisp/test-org-macro.el +++ b/testing/lisp/test-org-macro.el @@ -75,9 +75,22 @@ (org-macro-initialize-templates) (org-macro-replace-all org-macro-templates) (buffer-string)))) - ;; Test special "property" macro. With only one argument, retrieve - ;; property from current headline. Otherwise, the second argument - ;; is a search option to get the property from another headline. + ;; Macro expansion ignores narrowing. + (should + (string-match + "expansion" + (org-test-with-temp-text + "#+MACRO: macro expansion\n{{{macro}}}\nContents" + (narrow-to-region (point) (point-max)) + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (org-with-wide-buffer (buffer-string)))))) + +(ert-deftest test-org-macro/property () + "Test {{{property}}} macro." + ;; With only one argument, retrieve property from current headline. + ;; Otherwise, the second argument is a search option to get the + ;; property from another headline. (should (equal "1" (org-test-with-temp-text @@ -107,17 +120,80 @@ (org-test-with-temp-text "* H1\n:PROPERTIES:\n:A: 1\n:END:\n* H2\n{{{property(A,*???)}}}" (org-macro-initialize-templates) - (org-macro-replace-all org-macro-templates))) - ;; Macro expansion ignores narrowing. + (org-macro-replace-all org-macro-templates)))) + +(ert-deftest test-org-macro/n () + "Test {{{n}}} macro." + ;; Standard test with default counter. (should - (string-match - "expansion" - (org-test-with-temp-text - "#+MACRO: macro expansion\n{{{macro}}}\nContents" - (narrow-to-region (point) (point-max)) - (org-macro-initialize-templates) - (org-macro-replace-all org-macro-templates) - (org-with-wide-buffer (buffer-string)))))) + (equal "1 2" + (org-test-with-temp-text "{{{n}}} {{{n}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + (should + (equal "1 2" + (org-test-with-temp-text "{{{n()}}} {{{n}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + ;; Test alternative counters. + (should + (equal "1 1 1 2" + (org-test-with-temp-text "{{{n}}} {{{n(c1)}}} {{{n(c2)}}} {{{n(c1)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + ;; Second argument set a counter to a given value. A non-numeric + ;; value resets the counter to 1. + (should + (equal "9 10" + (org-test-with-temp-text "{{{n(c,9)}}} {{{n(c)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + (should + (equal "9 1" + (org-test-with-temp-text "{{{n(c,9)}}} {{{n(c,reset)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + ;; Tolerate spaces in second argument. + (should + (equal "9 10" + (org-test-with-temp-text "{{{n(c, 9)}}} {{{n(c)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + (should + (equal "9 1" + (org-test-with-temp-text "{{{n(c,9)}}} {{{n(c, reset)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + ;; Second argument also applies to default counter. + (should + (equal "9 10 1" + (org-test-with-temp-text "{{{n(,9)}}} {{{n}}} {{{n(,reset)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position))))) + ;; An empty second argument is equivalent to no argument. + (should + (equal "2 3" + (org-test-with-temp-text "{{{n(c,2)}}} {{{n(c,)}}}" + (org-macro-initialize-templates) + (org-macro-replace-all org-macro-templates) + (buffer-substring-no-properties + (line-beginning-position) (line-end-position)))))) (ert-deftest test-org-macro/escape-arguments () "Test `org-macro-escape-arguments' specifications." -- 2.13.0 --=-=-=--