branch: master commit d57675fde9e360de3cf17fc0aa2a3f7ddc03d5ec Author: Ian Dunn <du...@gnu.org> Commit: Ian Dunn <du...@gnu.org>
Added new forms for set-property! and fixed up build system * org-edna.el (org-edna--string-is-numeric-p): (org-edna--increment-numeric-property): (org-edna--cycle-property): New helper functions. (org-edna-action/set-property!): Use them for new forms. (org-edna-set-effort): Take entire function from Org for compatibility. (org-edna-action/set-effort!): Use new signature. * Project.ede, test.mk: New files for EDE. * org-edna.org (Property): Document new forms for property action. (Changelog): Start section for 1.0beta5 * .elpaignore: Added ignored files for ELPA to avoid problems with EDE * org-edna-tests.el: Added tests for new property forms --- .bzrignore | 3 +- .elpaignore | 5 +++ Makefile | 68 ---------------------------------- Project.ede | 28 ++++++++++++++ org-edna-tests.el | 50 ++++++++++++++++++++++++- org-edna-tests.org | 3 +- org-edna.el | 107 ++++++++++++++++++++++++++++++++++++++++++++++++----- org-edna.org | 60 +++++++++++++++++++++++++++++- test.mk | 28 ++++++++++++++ 9 files changed, 270 insertions(+), 82 deletions(-) diff --git a/.bzrignore b/.bzrignore index 5228a6a..bbb6def 100644 --- a/.bzrignore +++ b/.bzrignore @@ -2,4 +2,5 @@ local.mk org-edna-autoloads.el org-edna.texi -org-edna.html \ No newline at end of file +org-edna.html +.deps \ No newline at end of file diff --git a/.elpaignore b/.elpaignore new file mode 100644 index 0000000..3ef4d79 --- /dev/null +++ b/.elpaignore @@ -0,0 +1,5 @@ +Project.ede +Makefile +test.mk +org-edna-tests.el +org-edna-tests.org \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 2c123ca..0000000 --- a/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# This is part of org-edna -# -# Copyright (C) 2017-2018 Free Software Foundation, Inc. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -# Load defaults -include defaults.mk - -# Load local definitions -include local.mk - -EMACS=$(emacs) --batch -L $(org_path) -ALLSRC=org-edna.el -SOURCE=$(ALLSRC) -TARGET=$(patsubst %.el,%.elc,$(SOURCE)) - -.PHONY: clean check local.mk help - -all: $(TARGET) - -compile: $(TARGET) - -%.elc: %.el - @$(EMACS) \ - -L "." \ - -f batch-byte-compile $< - -autoloads: org-edna-autoloads.el - -org-edna-autoloads.el: - @$(EMACS) \ - --eval "(require 'package)" \ - --eval "(setq inhibit-message t)" \ - --eval "(package-generate-autoloads \"org-edna\" \"$$(pwd)\")" - -clean: - -rm -f *.elc - -check: compile - @$(EMACS) \ - -L "." \ - --load "ert" \ - --load "org-edna-tests.el" \ - -f ert-run-tests-batch-and-exit - -local.mk: - @cp -n defaults.mk local.mk - -help: - $(info ) - $(info make all - Default) - $(info make compile - Compile Emacs Lisp Files) - $(info make autoloads - Generate Autoloads) - $(info make clean - Remove generated .elc files) - $(info make check - Run Tests) - @echo "" diff --git a/Project.ede b/Project.ede new file mode 100644 index 0000000..decef82 --- /dev/null +++ b/Project.ede @@ -0,0 +1,28 @@ +;; Object ede-proj-project +;; EDE Project Files are auto generated: Do Not Edit +(ede-proj-project "ede-proj-project" + :file "Project.ede" + :name "Org Edna" + :targets + (list + (ede-proj-target-elisp "ede-proj-target-elisp" + :name "compile" + :path "" + :source '("org-edna.el") + :aux-packages '("org")) + (ede-proj-target-makefile-miscelaneous "ede-proj-target-makefile-miscelaneous" + :name "check" + :path "" + :source '("org-edna-tests.el" "org-edna-tests.org") + :partofall nil + :submakefile "test.mk") + (ede-proj-target-aux "ede-proj-target-aux" + :name "extra" + :path "" + :source '("org-edna.org" "COPYING" "dir" "org-edna.info" ".elpaignore")) + (ede-proj-target-elisp-autoloads "ede-proj-target-elisp-autoloads" + :name "autoloads" + :path "" + :source '("org-edna.el") + :autoload-file "org-edna-autoloads.el")) + :web-site-url "https://savannah.nongnu.org/projects/org-edna-el/") diff --git a/org-edna-tests.el b/org-edna-tests.el index 8d6df7c..6ce4f64 100644 --- a/org-edna-tests.el +++ b/org-edna-tests.el @@ -1039,7 +1039,6 @@ (pairs '((cp . rm) (copy . remove) ("cp" . "rm") ("copy" . "remove")))) (org-with-point-at target (dolist (pair pairs) - ;; (message "Pair: %s" pair) (org-edna-action/scheduled! source (car pair)) (should (string-equal (org-entry-get nil "SCHEDULED") "<2000-01-15 Sat 00:00>")) @@ -1179,6 +1178,55 @@ (org-edna-action/delete-property! nil "TEST") (should-not (org-entry-get nil "TEST"))))) +(ert-deftest org-edna-action-property/inc-dec () + (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one))) + (org-with-point-at pom + (org-edna-action/set-property! nil "TEST" "1") + (should (equal (org-entry-get nil "TEST") "1")) + (org-edna-action/set-property! nil "TEST" 'inc) + (should (equal (org-entry-get nil "TEST") "2")) + (org-edna-action/set-property! nil "TEST" 'dec) + (should (equal (org-entry-get nil "TEST") "1")) + (org-edna-action/delete-property! nil "TEST") + (should-not (org-entry-get nil "TEST")) + (should-error (org-edna-action/set-property! nil "TEST" 'inc)) + (should-error (org-edna-action/set-property! nil "TEST" 'dec)) + (org-edna-action/set-property! nil "TEST" "a") + (should (equal (org-entry-get nil "TEST") "a")) + (should-error (org-edna-action/set-property! nil "TEST" 'inc)) + (should-error (org-edna-action/set-property! nil "TEST" 'dec)) + (org-edna-action/delete-property! nil "TEST") + (should-not (org-entry-get nil "TEST"))))) + +(ert-deftest org-edna-action-property/next-prev () + (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one))) + (org-with-point-at pom + (org-edna-action/set-property! nil "TEST" "a") + (should (equal (org-entry-get nil "TEST") "a")) + (should-error (org-edna-action/set-property! nil "TEST" 'next)) + (should-error (org-edna-action/set-property! nil "TEST" 'prev)) + (should-error (org-edna-action/set-property! nil "TEST" 'previous)) + (org-edna-action/delete-property! nil "TEST") + (should-not (org-entry-get nil "TEST")) + ;; Test moving forwards + (org-edna-action/set-property! nil "COUNTER" "a") + (should (equal (org-entry-get nil "COUNTER") "a")) + (org-edna-action/set-property! nil "COUNTER" 'next) + (should (equal (org-entry-get nil "COUNTER") "b")) + ;; Test moving forwards past the last one + (org-edna-action/set-property! nil "COUNTER" "d") + (should (equal (org-entry-get nil "COUNTER") "d")) + (org-edna-action/set-property! nil "COUNTER" 'next) + (should (equal (org-entry-get nil "COUNTER") "a")) + ;; Test moving backwards past the first one + (org-edna-action/set-property! nil "COUNTER" 'prev) + (should (equal (org-entry-get nil "COUNTER") "d")) + ;; Test moving backwards normally + (org-edna-action/set-property! nil "COUNTER" 'previous) + (should (equal (org-entry-get nil "COUNTER") "c")) + (org-edna-action/delete-property! nil "COUNTER") + (should-not (org-entry-get nil "COUNTER"))))) + (ert-deftest org-edna-action-clock () (let ((pom (org-edna-find-test-heading org-edna-test-id-heading-one))) (org-with-point-at pom diff --git a/org-edna-tests.org b/org-edna-tests.org index 9dced13..5e20614 100644 --- a/org-edna-tests.org +++ b/org-edna-tests.org @@ -1,5 +1,6 @@ #+STARTUP: nologdone -#+PROPERTY: EFFORT_ALL 0:01 0:02 0:03 +#+PROPERTY: Effort_ALL 0:01 0:02 0:03 +#+PROPERTY: COUNTER_ALL a b c d * COMMENT Copying Copyright (C) 2017-2018 Free Software Foundation, Inc. diff --git a/org-edna.el b/org-edna.el index 9925af3..cfd6d16 100644 --- a/org-edna.el +++ b/org-edna.el @@ -7,7 +7,7 @@ ;; Keywords: convenience, text, org ;; URL: https://savannah.nongnu.org/projects/org-edna-el/ ;; Package-Requires: ((emacs "25.1") (seq "2.19") (org "9.0.5")) -;; Version: 1.0beta4 +;; Version: 1.0beta5 ;; This file is part of GNU Emacs. @@ -1551,6 +1551,39 @@ Edna Syntax: tag!(\"TAGS\") TAGS is a valid tag specification, such as \":aa:bb:cc:\"." (org-set-tags-to tags)) +(defun org-edna--string-is-numeric-p (string) + "Return non-nil if STRING is a valid numeric string. + +Examples of valid numeric strings are \"1\", \"-3\", or \"123\"." + ;; Can't use string-to-number, because it returns 0 if STRING isn't a + ;; number, which is ambiguous. + (numberp (car (read-from-string string)))) + +(defun org-edna--increment-numeric-property (pom property &optional decrement) + "Return the incremented value of PROPERTY at POM. + +If optional argument DECREMENT is non-nil, decrement the property +value instead." + (let* ((prop-value (org-entry-get pom property))) + (unless prop-value + (error "Attempted to increment/decrement unset property %s" property)) + (unless (org-edna--string-is-numeric-p prop-value) + (error "Property %s doesn't have a numeric value (got %s)" property prop-value)) + (number-to-string (+ (if decrement -1 1) (string-to-number prop-value))))) + +(defun org-edna--cycle-property (pom property &optional previous) + "Cycle the property PROPERTY at POM through its allowed values. + +Change PROPERTY to the next allowed value, unless PREVIOUS is +non-nil, in which case, cycle to the previous allowed value." + (let* ((prop-value (org-entry-get pom property))) + (unless prop-value + (error "Attempted to cycle an unset property %s" property)) + (save-excursion + ;; Jump to the property line, (required for `org-property-next-allowed-value') + (re-search-forward (org-re-property property nil nil prop-value)) + (org-property-next-allowed-value previous)))) + (defun org-edna-action/set-property! (_last-entry property value) "Action to set the property PROPERTY of a target heading to VALUE. @@ -1558,7 +1591,15 @@ Edna Syntax: set-property!(\"PROPERTY\" \"VALUE\") PROPERTY and VALUE are both strings. PROPERTY must be a valid org mode property." - (org-entry-put nil property value)) + (pcase value + ((pred stringp) + (org-entry-put (point) property value)) + ((or `inc `dec) + (let* ((new-value (org-edna--increment-numeric-property (point) property + (eq value 'dec)))) + (org-entry-put (point) property new-value))) + ((or `next `prev `previous) + (org-edna--cycle-property (point) property (memq value '(prev previous)))))) (defun org-edna-action/delete-property! (_last-entry property) "Action to delete a property from a target heading. @@ -1607,12 +1648,58 @@ Form 4: Set the target's priority to the character P." (string-to-char priority-action) priority-action))) -(defun org-edna-set-effort (increment value) - "Compatibility function for `org-set-effort'." - ;; The signature of `org-set-effort' changed in 9.1.6 - (if (version< org-version "9.1.6") - (org-set-effort value increment) - (org-set-effort increment value))) +(defun org-edna-set-effort (value increment) + "Set the effort property of the current entry. +With numerical prefix arg, use the nth allowed value, 0 stands for the +10th allowed value. + +When INCREMENT is non-nil, set the property to the next allowed value." + ;; NOTE: Copied from `org-set-effort', because the signature changed in 9.1.7. + ;; Since the Org repo doesn't change its version string until after a release, + ;; there's no way to tell when to use the old or new signature until after + ;; 9.1.7 is released. Therefore, we cut out the middle man and slap the + ;; entire function here. + (interactive "P") + (when (equal value 0) (setq value 10)) + (let* ((completion-ignore-case t) + (prop org-effort-property) + (cur (org-entry-get nil prop)) + (allowed (org-property-get-allowed-values nil prop 'table)) + (existing (mapcar 'list (org-property-values prop))) + rpl + (val (cond + ((stringp value) value) + ((and allowed (integerp value)) + (or (car (nth (1- value) allowed)) + (car (org-last allowed)))) + ((and allowed increment) + (or (cl-caadr (member (list cur) allowed)) + (user-error "Allowed effort values are not set"))) + (allowed + (message "Select 1-9,0, [RET%s]: %s" + (if cur (concat "=" cur) "") + (mapconcat 'car allowed " ")) + (setq rpl (read-char-exclusive)) + (if (equal rpl ?\r) + cur + (setq rpl (- rpl ?0)) + (when (equal rpl 0) (setq rpl 10)) + (if (and (> rpl 0) (<= rpl (length allowed))) + (car (nth (1- rpl) allowed)) + (org-completing-read "Effort: " allowed nil)))) + (t + (org-completing-read + (concat "Effort" (and cur (string-match "\\S-" cur) + (concat " [" cur "]")) + ": ") + existing nil nil "" nil cur))))) + (unless (equal (org-entry-get nil prop) val) + (org-entry-put nil prop val)) + (org-refresh-property + '((effort . identity) + (effort-minutes . org-duration-to-minutes)) + val) + (message "%s is now %s" prop val))) (defun org-edna-action/set-effort! (_last-entry value) "Action to set the effort of a target heading. @@ -1626,8 +1713,8 @@ the raw value for the effort. For form 2, increment the effort to the next allowed value." (if (eq value 'increment) - (org-edna-set-effort value nil) - (org-edna-set-effort nil value))) + (org-edna-set-effort nil value) + (org-edna-set-effort value nil))) (defun org-edna-action/archive! (_last-entry) "Action to archive a target heading. diff --git a/org-edna.org b/org-edna.org index 2634855..e062701 100644 --- a/org-edna.org +++ b/org-edna.org @@ -761,13 +761,66 @@ clock, if any. :END: - Syntax: set-property!("PROPERTY" "VALUE") +- Syntax: set-property!("PROPERTY" inc) +- Syntax: set-property!("PROPERTY" dec) +- Syntax: set-property!("PROPERTY" next) +- Syntax: set-property!("PROPERTY" prev) +- Syntax: set-property!("PROPERTY" previous) -Sets the property PROPERTY on all targets to VALUE. +The first form sets the property PROPERTY on all targets to VALUE. + +If VALUE is a symbol, it is interpreted as follows: + +- inc :: Increment a numeric property value by one +- dec :: Decrement a numeric property value by one + +If either ~inc~ or ~dec~ attempt to modify a non-numeric property value, Edna will +fail with an error message. + +- next :: Cycle the property through to the next allowed property value +- previous :: Cycle the property through to the previous allowed property value + +The symbol ~prev~ may be used as an abbreviation for ~previous~. Similar to +~inc~ and ~dec~, any of these will fail if there are no defined properties. +When reaching the end of the list of allowed properties, ~next~ will cycle back +to the beginning. + +Example: + +#+begin_src org +,#+PROPERTY: TEST_ALL a b c d + +,* TODO Test Heading + :PROPERTIES: + :TEST: d + :TRIGGER: self set-property!("TEST" next) + :END: +#+end_src + +When "Test Heading" is set to DONE, its TEST property will change to "a". This +also works with ~previous~, but in the opposite direction. + +Additionally, all special forms will fail if the property is not already set: + +#+begin_src org +,* TODO Test + :PROPERTIES: + :TRIGGER: self set-property("TEST" inc) + :END: +#+end_src + +In the above example, if "Test" is set to DONE, Edna will fail to increment the +TEST property, since it doesn't exist. - Syntax: delete-property!("PROPERTY") Deletes the property PROPERTY from all targets. +Examples: + +- set-property!("COUNTER" "1") -> Sets the property COUNTER to 1 on all targets +- set-property!("COUNTER" inc) -> Increments the property COUNTER by 1. Following the previous example, it would be 2. + *** Priority :PROPERTIES: :CUSTOM_ID: priorities @@ -1238,6 +1291,11 @@ making any changes: :PROPERTIES: :DESCRIPTION: List of changes by version :END: +** 1.0beta5 +- Added new forms to set-property! + - Now allows 'inc, 'dec, 'previous, and 'next as values +- Changed build system to EDE to properly handle dependencies +- Fixed compatibility with new Org effort functions ** 1.0beta4 Just some bug fixes from the new form parsing. *** Fixed multiple forms getting incorrect targets diff --git a/test.mk b/test.mk new file mode 100644 index 0000000..87fe6ad --- /dev/null +++ b/test.mk @@ -0,0 +1,28 @@ +# Copyright (C) 2017-2018 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# EDE only allows arbitrary code from an external makefile, so this is how we've +# got to do testing. + +test: + $(EMACS) \ + $(EMACSFLAGS) \ + $(addprefix -L ,$(LOADPATH)) \ + -L "." \ + -l "ert" \ + -l "org-edna-tests.el" \ + -f ert-run-tests-batch-and-exit + +include Makefile