From 66509f2ead423b814378a44a55c9f63dcb1e149b Mon Sep 17 00:00:00 2001 From: Naoya Yamashita Date: Wed, 9 Sep 2020 09:52:39 +0900 Subject: [PATCH] Add gv-define-expander for plist-get It is necessary to make plist-get as a generalized variable, and this definition allows user to use setf and other useful functions on plist-get. * lisp/emacs-lisp/gv.el: Add gv-define-expander for plist-get * lisp/emacs-lisp/gv-tests.el: Add new tests for plist-get --- lisp/emacs-lisp/gv.el | 11 +++++++++ test/lisp/emacs-lisp/gv-tests.el | 40 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el index 78d86b9fc31..5470b8532fc 100644 --- a/lisp/emacs-lisp/gv.el +++ b/lisp/emacs-lisp/gv.el @@ -417,6 +417,17 @@ The return value is the last VAL in the list. `(delq ,p ,getter)))))) ,v)))))))))) +(gv-define-expander plist-get + (lambda (do plist prop) + (macroexp-let2 macroexp-copyable-p key prop + (gv-letplace (getter setter) plist + (macroexp-let2 nil p `(cdr (plist-member ,getter ,key)) + (funcall do + `(car ,p) + (lambda (val) + `(if ,p + (setcar ,p ,val) + ,(funcall setter `(cons ,key (cons ,val ,getter))))))))))) ;;; Some occasionally handy extensions. diff --git a/test/lisp/emacs-lisp/gv-tests.el b/test/lisp/emacs-lisp/gv-tests.el index 7a8402be074..10e3b531f36 100644 --- a/test/lisp/emacs-lisp/gv-tests.el +++ b/test/lisp/emacs-lisp/gv-tests.el @@ -156,6 +156,46 @@ its getter (Bug#41853)." (eval-buffer))) (should (equal (get 'gv-setter-edebug 'gv-setter-edebug-prop) '(123)))) +(ert-deftest gv-plist-get () + (require 'cl-lib) + + ;; Simple setf usage for plist-get. + (should (equal (let ((target '(:a "a" :b "b" :c "c"))) + (setf (plist-get target :b) "modify") + target) + '(:a "a" :b "modify" :c "c"))) + + ;; Other function (cl-rotatef) usage for plist-get. + (should (equal (let ((target '(:a "a" :b "b" :c "c"))) + (cl-rotatef (plist-get target :b) (plist-get target :c)) + target) + '(:a "a" :b "c" :c "b"))) + + ;; Add new key value pair at top of list if setf for missing key. + (should (equal (let ((target '(:a "a" :b "b" :c "c"))) + (setf (plist-get target :d) "modify") + target) + '(:d "modify" :a "a" :b "b" :c "c"))) + + ;; Rotate with missing value. + ;; The value corresponding to the missing key is assumed to be nil. + (should (equal (let ((target '(:a "a" :b "b" :c "c"))) + (cl-rotatef (plist-get target :b) (plist-get target :d)) + target) + '(:d "b" :a "a" :b nil :c "c"))) + + ;; Simple setf usage for plist-get. (symbol plist) + (should (equal (let ((target '(a "a" b "b" c "c"))) + (setf (plist-get target 'b) "modify") + target) + '(a "a" b "modify" c "c"))) + + ;; Other function (cl-rotatef) usage for plist-get. (symbol plist) + (should (equal (let ((target '(a "a" b "b" c "c"))) + (cl-rotatef (plist-get target 'b) (plist-get target 'c)) + target) + '(a "a" b "c" c "b")))) + ;; `ert-deftest' messes up macroexpansion when the test file itself is ;; compiled (see Bug #24402). -- 2.39.2