* bug#49407: Request: Specify default values in `map-let` in Map.el @ 2021-07-04 23:08 Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2021-07-15 8:51 ` Lars Ingebrigtsen 2023-07-27 1:37 ` Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 2 replies; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-07-04 23:08 UTC (permalink / raw) To: 49407 Hello, `map-let` allows one to conveniently bind variables using `map-elt`, but does not provide a way specify a default value if a key is missing. With `map-elt`, one can use the optional third argument to specify this value. It would be good to have this in `map-let` as well. For example, maybe it could look something like ;; As just a third value in the list: (let ((map '(:a 1 :b 2))) (map-let ((:a a) (:b b) (:c c 3)) map (+ a b c))) or ;; More like Common Lisp arg-list for `&key'. (let ((map '(:a 1 :b 2))) (map-let ((:a a) (:b b) (:c (c 3))) map (+ a b c))) Please consider adding this feature. Thank you. ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2021-07-04 23:08 bug#49407: Request: Specify default values in `map-let` in Map.el Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-07-15 8:51 ` Lars Ingebrigtsen 2021-07-16 1:45 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-21 2:56 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-27 1:37 ` Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 2 replies; 18+ messages in thread From: Lars Ingebrigtsen @ 2021-07-15 8:51 UTC (permalink / raw) To: Okam; +Cc: Nicolas Petton, 49407 Okam <okamsn@protonmail.com> writes: > `map-let` allows one to conveniently bind variables using `map-elt`, but > does not provide a way specify a default value if a key is missing. > > With `map-elt`, one can use the optional third argument to specify this > value. It would be good to have this in `map-let` as well. > > For example, maybe it could look something like > > ;; As just a third value in the list: > (let ((map '(:a 1 :b 2))) > (map-let ((:a a) > (:b b) > (:c c 3)) > map > (+ a b c))) Hm... I guess that could be useful. I've added Nicolas to the CCs; perhaps he has an opinion here. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2021-07-15 8:51 ` Lars Ingebrigtsen @ 2021-07-16 1:45 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-21 2:56 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 0 replies; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2021-07-16 1:45 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: Nicolas Petton, 49407 On 7/15/21 4:51 AM, Lars Ingebrigtsen wrote: > Hm... I guess that could be useful. I've added Nicolas to the CCs; > perhaps he has an opinion here. If it helps, below is a version of the idea that I am using, specific to plists: (defmacro plist-bind (bindings plist &rest body) "Bind values in PLIST to variables in BINDINGS, surrounding BODY. - PLIST is a property list. - BINDINGS is of the form (KEY VAR KEY VAR ...). VAR can optionally be a list of two elements: a variable name and a default value, similar to what one would use for expressing keyword parameters in `cl-defun' or `cl-destructuring-bind'. The default value is used /only/ when KEY is not found in PLIST. - BODY is the same as in `let'. This macro works the same as `cl-destructuring-bind', except for the case when keys exist in PLIST that are not listed in BINDINGS. While `cl-destructuring-bind' would signal an error, this macro simply ignores them." (declare (indent 2)) (let ((value-holder (gensym "plist-let-")) (found-key (gensym "plist-prop-found-"))) `(let* ((,value-holder ,plist) ,@(cl-loop for (key var . _) on bindings by #'cddr if (consp var) collect `(,(cl-first var) ;; Use `plist-member' instead of `plist-get' to ;; allow giving `nil' as an argument without ;; using the default value. (if-let ((,found-key (plist-member ,value-holder ,key))) (cl-second ,found-key) ,(cl-second var))) else collect `(,var (plist-get ,value-holder ,key)))) ,@body))) ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2021-07-15 8:51 ` Lars Ingebrigtsen 2021-07-16 1:45 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-21 2:56 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-22 1:48 ` Michael Heerdegen 1 sibling, 1 reply; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-21 2:56 UTC (permalink / raw) To: Lars Ingebrigtsen, Nicolas Petton; +Cc: 49407, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 1659 bytes --] On 2021-07-15 08:51 UTC, Lars Ingebrigtsen wrote: > Okam <okamsn@protonmail.com> writes: > >> `map-let` allows one to conveniently bind variables using `map-elt`, but >> does not provide a way specify a default value if a key is missing. >> >> With `map-elt`, one can use the optional third argument to specify this >> value. It would be good to have this in `map-let` as well. >> >> For example, maybe it could look something like >> >> ;; As just a third value in the list: >> (let ((map '(:a 1 :b 2))) >> (map-let ((:a a) >> (:b b) >> (:c c 3)) >> map >> (+ a b c))) > > Hm... I guess that could be useful. I've added Nicolas to the CCs; > perhaps he has an opinion here. > > -- > (domestic pets only, the antidote for overdose, milk.) > bloggy blog: http://lars.ingebrigtsen.no Hello, I've written a patch and tests that would add an optional third argument for a default value. I also changed the documentation string for the pcase pattern, which said that unfound keys are ignored and won't cause the pattern to fail. That didn't seem true for patterns that didn't match nil. For example, this won't match: (pcase '(:two 2) ((map (:two two 33) (:three `(,a . ,b))) (list two a b)) (_ 'fail)) However, I have a question about avoiding using a lambda. I see that the pattern uses `pcase--flip`, which is "used internally to avoid (funcall (lambda ...) ...)". Why should lambda functions be avoided for this, and I should be using a custom helper function for this one pattern? Thank you. [-- Attachment #2: 0001-Allow-default-values-in-map-let-and-the-pcase-map-fo.patch --] [-- Type: text/x-patch, Size: 6159 bytes --] From 1dbb797a9a736fc81ad9f672da815e2510e7f31a Mon Sep 17 00:00:00 2001 From: Earl Hyatt <okamsn@protonmail.com> Date: Thu, 20 Jul 2023 21:44:41 -0400 Subject: [PATCH] Allow default values in 'map-let' and the pcase 'map' form * lisp/emacs-lisp/map.el (map-let, map) (map--make-pcase-bindings): Add a third argument for specifying a default value, like in 'map-elt'. (Bug#49407) * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys that aren't found aren't ignored, they actually get the value nil (unless the new default value is given). The overall pattern can still fail to match if the sub-pattern for the unfound key doesn't match nil. * test/lisp/emacs-lisp/map-tests.el (test-map-let-default) (test-map-plist-pcase-default, test-map-pcase-matches): Add tests, including for the above item. --- lisp/emacs-lisp/map.el | 31 ++++++++++++------ test/lisp/emacs-lisp/map-tests.el | 52 +++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 10 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7a48ba47434..7f127f312de 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -50,18 +50,23 @@ map ARGS is a list of elements to be matched in the map. -Each element of ARGS can be of the form (KEY PAT), in which case KEY is -evaluated and searched for in the map. The match fails if for any KEY -found in the map, the corresponding PAT doesn't match the value -associated with the KEY. +Each element of ARGS can be of the form (KEY PAT) or (KEY PAT DEFAULT), + +in which case KEY is evaluated and searched for in the map and +DEFAULT is the evaluated value used if KEY is not found. The +match fails if for any KEY found in the map, the corresponding +PAT doesn't match the value associated with the KEY. The match +fails if for any KEY not found in the map, the corresponding PAT +doesn't match the DEFAULT value, if given. Each element can also be a SYMBOL, which is an abbreviation of a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), useful for binding plist values. -Keys in ARGS not found in the map are ignored, and the match doesn't -fail." +When no DEFAULT value is given, the default value for keys in +ARGS not found in the map is nil, and the match doesn't fail so +long at PAT, if given, matches nil." `(and (pred mapp) ,@(map--make-pcase-bindings args))) @@ -71,12 +76,14 @@ map-let KEYS can be a list of symbols, in which case each element will be bound to the looked up value in MAP. -KEYS can also be a list of (KEY VARNAME) pairs, in which case -KEY is an unquoted form. +KEYS can also be a list of (KEY VARNAME) pairs and +\(KEY VARNAME DEFAULT) triples, in which case KEY is an +unquoted form. MAP can be an alist, plist, hash-table, or array." (declare (indent 2) - (debug ((&rest &or symbolp ([form symbolp])) form body))) + (debug ((&rest &or symbolp ([form symbolp &optional form])) + form body))) `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) ,@body)) @@ -599,7 +606,11 @@ map--make-pcase-bindings "Return a list of pcase bindings from ARGS to the elements of a map." (mapcar (lambda (elt) (cond ((consp elt) - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) + (if (cddr elt) + `(app (lambda (arg) + (map-elt arg ,(car elt) ,(caddr elt))) + ,(cadr elt)) + `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt)))) ((keywordp elt) (let ((var (intern (substring (symbol-name elt) 1)))) `(app (pcase--flip map-elt ,elt) ,var))) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 86c0e9e0503..42458fbfeb7 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -577,6 +577,13 @@ test-map-let (should (= b 2)) (should-not c))) +(ert-deftest test-map-let-default () + (map-let (('foo a 3) + ('baz b 4)) + '((foo . 1)) + (should (= a 1)) + (should (= b 4)))) + (ert-deftest test-map-merge () "Test `map-merge'." (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3)) @@ -617,6 +624,51 @@ test-map-plist-pcase (list one two)) '(1 2))))) +(ert-deftest test-map-plist-pcase-default () + (let ((plist '(:two 2))) + (should (equal (pcase-let (((map (:two two 33) + (:three three 44)) + plist)) + (list two three)) + '(2 44))))) + +(ert-deftest test-map-pcase-matches () + (let ((plist '(:two 2))) + (should (equal (pcase plist + ((map (:two two 33) + (:three three)) + (list two three)) + (_ 'fail)) + '(2 nil))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three three 44)) + (list two three)) + (_ 'fail)) + '(2 44))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) '(11 . 22))) + (list two a b)) + (_ 'fail)) + '(2 11 22))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) 44)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) nil)) + (list two a b)) + (_ 'fail)))))) + (ert-deftest test-map-setf-alist-insert-key () (let ((alist)) (should (equal (setf (map-elt alist 'key) 'value) -- 2.34.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-21 2:56 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-22 1:48 ` Michael Heerdegen 2023-07-22 15:45 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-22 18:46 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 2 replies; 18+ messages in thread From: Michael Heerdegen @ 2023-07-22 1:48 UTC (permalink / raw) To: Okam; +Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier Okam <okamsn@protonmail.com> writes: > I've written a patch and tests that would add an optional third > argument for a default value. Thank you. > However, I have a question about avoiding using a lambda. I see that > the pattern uses `pcase--flip`, which is "used internally to avoid > (funcall (lambda ...) ...)". Why should lambda functions be avoided > for this, As far as I understand, to produce more efficient code. > and I should be using a custom helper function for this one pattern? Stefan, would you want to extend `pcase--flip' to more arguments to support this case? | diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el | index 7a48ba47434..7f127f312de 100644 | --- a/lisp/emacs-lisp/map.el | +++ b/lisp/emacs-lisp/map.el | @@ -50,18 +50,23 @@ map | | ARGS is a list of elements to be matched in the map. | | -Each element of ARGS can be of the form (KEY PAT), in which case KEY is | -evaluated and searched for in the map. The match fails if for any KEY | -found in the map, the corresponding PAT doesn't match the value | -associated with the KEY. | +Each element of ARGS can be of the form (KEY PAT) or (KEY PAT DEFAULT), | + | +in which case KEY is evaluated and searched for in the map and That line break in the last sentence is somehow... broken. | +DEFAULT is the evaluated value used if KEY is not found. The | +match fails if for any KEY found in the map, the corresponding | +PAT doesn't match the value associated with the KEY. The match | +fails if for any KEY not found in the map, the corresponding PAT | +doesn't match the DEFAULT value, if given. Here is something grammatically not right in the last sentence. And I think we should say that that last case (PAT doesn't match DEFAULT) only lets matching fail when the KEY is not found. | -Keys in ARGS not found in the map are ignored, and the match doesn't | -fail." | +When no DEFAULT value is given, the default value for keys in | +ARGS not found in the map is nil, and the match doesn't fail so | +long at PAT, if given, matches nil." | `(and (pred mapp) | ,@(map--make-pcase-bindings args))) Typo in doc ("at"). And can't we simply say that omitting DEFAULT is equivalent to a DEFAULT of nil? | @@ -599,7 +606,11 @@ map--make-pcase-bindings | "Return a list of pcase bindings from ARGS to the elements of a map." | (mapcar (lambda (elt) | (cond ((consp elt) | - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) | + (if (cddr elt) | + `(app (lambda (arg) | + (map-elt arg ,(car elt) ,(caddr elt))) | + ,(cadr elt)) | + `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt)))) If we don't extend `pcase--flip' to support this case, I think your lambda would need to use an uninterned symbol as parameter to avoid collisions with the involved expressions. Michael. ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-22 1:48 ` Michael Heerdegen @ 2023-07-22 15:45 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-22 18:46 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 0 replies; 18+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-22 15:45 UTC (permalink / raw) To: Michael Heerdegen; +Cc: Okam, Lars Ingebrigtsen, 49407, Nicolas Petton >> However, I have a question about avoiding using a lambda. I see that >> the pattern uses `pcase--flip`, which is "used internally to avoid >> (funcall (lambda ...) ...)". Why should lambda functions be avoided >> for this, > As far as I understand, to produce more efficient code. Indeed. >> and I should be using a custom helper function for this one pattern? > Stefan, would you want to extend `pcase--flip' to more arguments to > support this case? Probably not worth it, no. Stefan ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-22 1:48 ` Michael Heerdegen 2023-07-22 15:45 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-22 18:46 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-26 3:41 ` Michael Heerdegen 1 sibling, 1 reply; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-22 18:46 UTC (permalink / raw) To: Michael Heerdegen Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 2745 bytes --] On 2023-07-22 01:48 UTC, Michael Heerdegen wrote: > > | diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el > | index 7a48ba47434..7f127f312de 100644 > | --- a/lisp/emacs-lisp/map.el > | +++ b/lisp/emacs-lisp/map.el > | @@ -50,18 +50,23 @@ map > | > | ARGS is a list of elements to be matched in the map. > | > | -Each element of ARGS can be of the form (KEY PAT), in which case KEY is > | -evaluated and searched for in the map. The match fails if for any KEY > | -found in the map, the corresponding PAT doesn't match the value > | -associated with the KEY. > | +Each element of ARGS can be of the form (KEY PAT) or (KEY PAT DEFAULT), > | + > | +in which case KEY is evaluated and searched for in the map and > > That line break in the last sentence is somehow... broken. > > | +DEFAULT is the evaluated value used if KEY is not found. The > | +match fails if for any KEY found in the map, the corresponding > | +PAT doesn't match the value associated with the KEY. The match > | +fails if for any KEY not found in the map, the corresponding PAT > | +doesn't match the DEFAULT value, if given. > > Here is something grammatically not right in the last sentence. And I > think we should say that that last case (PAT doesn't match DEFAULT) only > lets matching fail when the KEY is not found. > > > | -Keys in ARGS not found in the map are ignored, and the match doesn't > | -fail." > | +When no DEFAULT value is given, the default value for keys in > | +ARGS not found in the map is nil, and the match doesn't fail so > | +long at PAT, if given, matches nil." > | `(and (pred mapp) > | ,@(map--make-pcase-bindings args))) > > Typo in doc ("at"). And can't we simply say that omitting DEFAULT is > equivalent to a DEFAULT of nil? > > | @@ -599,7 +606,11 @@ map--make-pcase-bindings > | "Return a list of pcase bindings from ARGS to the elements of a map." > | (mapcar (lambda (elt) > | (cond ((consp elt) > | - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) > | + (if (cddr elt) > | + `(app (lambda (arg) > | + (map-elt arg ,(car elt) ,(caddr elt))) > | + ,(cadr elt)) > | + `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt)))) > > If we don't extend `pcase--flip' to support this case, I think your > lambda would need to use an uninterned symbol as parameter to avoid > collisions with the involved expressions. > > > Michael. Please see the attached file. I have re-worded the doc string and added a wrapper macro to re-order the arguments to `map-elt` for the `app` pattern. [-- Attachment #2: v2-0001-Allow-default-values-in-map-let-and-the-pcase-map.patch --] [-- Type: text/x-patch, Size: 6343 bytes --] From 6cdf145fbc81c75c88c8f5e98450a30688d24901 Mon Sep 17 00:00:00 2001 From: Earl Hyatt <okamsn@protonmail.com> Date: Thu, 20 Jul 2023 21:44:41 -0400 Subject: [PATCH v2] Allow default values in 'map-let' and the pcase 'map' form * lisp/emacs-lisp/map.el (map-let, map) (map--make-pcase-bindings): Add a third argument for specifying a default value, like in 'map-elt'. (Bug#49407) * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys that aren't found aren't ignored, they actually get the value nil (unless the new default value is given). The overall pattern can still fail to match if the sub-pattern for the unfound key doesn't match nil. * test/lisp/emacs-lisp/map-tests.el (test-map-let-default) (test-map-plist-pcase-default, test-map-pcase-matches): Add tests, including for the above item. --- lisp/emacs-lisp/map.el | 40 ++++++++++++++++++------ test/lisp/emacs-lisp/map-tests.el | 52 +++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7a48ba47434..35b8998679a 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -50,18 +50,26 @@ map ARGS is a list of elements to be matched in the map. -Each element of ARGS can be of the form (KEY PAT), in which case KEY is -evaluated and searched for in the map. The match fails if for any KEY -found in the map, the corresponding PAT doesn't match the value -associated with the KEY. +Each element in ARGS can be of the form (KEY PAT) or (KEY PAT +DEFAULT), where KEY is the key sought in the map, PAT is a +`pcase' pattern, and DEFAULT is the default value to use if KEY +isn't found. Both KEY and DEFAULT are evaluated. If DEFAULT +isn't given, then nil is used. Each element can also be a SYMBOL, which is an abbreviation of a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), useful for binding plist values. -Keys in ARGS not found in the map are ignored, and the match doesn't -fail." +The match fails if: + +- KEY is found and PAT does not match the associated value + +- KEY is not found, DEFAULT is given, and PAT does not match + DEFAULT + +- KEY is not found, DEFAULT is not given, and PAT does not match + nil" `(and (pred mapp) ,@(map--make-pcase-bindings args))) @@ -71,12 +79,14 @@ map-let KEYS can be a list of symbols, in which case each element will be bound to the looked up value in MAP. -KEYS can also be a list of (KEY VARNAME) pairs, in which case -KEY is an unquoted form. +KEYS can also be a list of (KEY VARNAME) pairs and +\(KEY VARNAME DEFAULT) triples, in which case KEY is an +unquoted form. MAP can be an alist, plist, hash-table, or array." (declare (indent 2) - (debug ((&rest &or symbolp ([form symbolp])) form body))) + (debug ((&rest &or symbolp ([form symbolp &optional form])) + form body))) `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) ,@body)) @@ -595,11 +605,21 @@ map-into (map-into \\='((1 . 3)) \\='(hash-table :test eql))" (map--into-hash map (cdr type))) +(defmacro map--pcase-map-elt (key default map) + "A macro to make MAP the last argument to `map-elt'. + +This allows using default values for `map-elt', which can't be +done using `pcase--flip'. + +KEY is the key sought in the map. DEFAULT is the default value." + `(map-elt ,map ,key ,default)) + (defun map--make-pcase-bindings (args) "Return a list of pcase bindings from ARGS to the elements of a map." (mapcar (lambda (elt) (cond ((consp elt) - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) + `(app (map--pcase-map-elt ,(car elt) ,(caddr elt)) + ,(cadr elt))) ((keywordp elt) (let ((var (intern (substring (symbol-name elt) 1)))) `(app (pcase--flip map-elt ,elt) ,var))) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 86c0e9e0503..42458fbfeb7 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -577,6 +577,13 @@ test-map-let (should (= b 2)) (should-not c))) +(ert-deftest test-map-let-default () + (map-let (('foo a 3) + ('baz b 4)) + '((foo . 1)) + (should (= a 1)) + (should (= b 4)))) + (ert-deftest test-map-merge () "Test `map-merge'." (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3)) @@ -617,6 +624,51 @@ test-map-plist-pcase (list one two)) '(1 2))))) +(ert-deftest test-map-plist-pcase-default () + (let ((plist '(:two 2))) + (should (equal (pcase-let (((map (:two two 33) + (:three three 44)) + plist)) + (list two three)) + '(2 44))))) + +(ert-deftest test-map-pcase-matches () + (let ((plist '(:two 2))) + (should (equal (pcase plist + ((map (:two two 33) + (:three three)) + (list two three)) + (_ 'fail)) + '(2 nil))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three three 44)) + (list two three)) + (_ 'fail)) + '(2 44))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) '(11 . 22))) + (list two a b)) + (_ 'fail)) + '(2 11 22))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) 44)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) nil)) + (list two a b)) + (_ 'fail)))))) + (ert-deftest test-map-setf-alist-insert-key () (let ((alist)) (should (equal (setf (map-elt alist 'key) 'value) -- 2.34.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-22 18:46 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-26 3:41 ` Michael Heerdegen 2023-07-27 1:39 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 18+ messages in thread From: Michael Heerdegen @ 2023-07-26 3:41 UTC (permalink / raw) To: Okam; +Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier Okam <okamsn@protonmail.com> writes: > Please see the attached file. I have re-worded the doc string and added > a wrapper macro to re-order the arguments to `map-elt` for the `app` > pattern. Perfect! Only one detail: | @@ -71,12 +79,14 @@ map-let | KEYS can be a list of symbols, in which case each element will be | bound to the looked up value in MAP. | | -KEYS can also be a list of (KEY VARNAME) pairs, in which case | -KEY is an unquoted form. | +KEYS can also be a list of (KEY VARNAME) pairs and | +\(KEY VARNAME DEFAULT) triples, in which case KEY is an | +unquoted form. Here you could also say that DEFAULT is an unquoted form. The rest looks good to me. Thanks, Michael. ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-26 3:41 ` Michael Heerdegen @ 2023-07-27 1:39 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-28 18:02 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-27 1:39 UTC (permalink / raw) To: Michael Heerdegen Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 574 bytes --] Michael Heerdegen wrote: > Only one detail: > > | @@ -71,12 +79,14 @@ map-let > | KEYS can be a list of symbols, in which case each element will be > | bound to the looked up value in MAP. > | > | -KEYS can also be a list of (KEY VARNAME) pairs, in which case > | -KEY is an unquoted form. > | +KEYS can also be a list of (KEY VARNAME) pairs and > | +\(KEY VARNAME DEFAULT) triples, in which case KEY is an > | +unquoted form. > > Here you could also say that DEFAULT is an unquoted form. I have made that change. Please see the attached file. [-- Attachment #2: v3-0001-Allow-default-values-in-map-let-and-the-pcase-map.patch --] [-- Type: text/x-patch, Size: 6585 bytes --] From 99208ca640d4944c57082772702cb8534fe91d32 Mon Sep 17 00:00:00 2001 From: Earl Hyatt <okamsn@protonmail.com> Date: Thu, 20 Jul 2023 21:44:41 -0400 Subject: [PATCH v3] Allow default values in 'map-let' and the pcase 'map' form * lisp/emacs-lisp/map.el (map-let, map) (map--make-pcase-bindings): Add a third argument for specifying a default value, like in 'map-elt'. (Bug#49407) * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys that aren't found aren't ignored, they actually get the value nil (unless the new default value is given). The overall pattern can still fail to match if the sub-pattern for the unfound key doesn't match nil. * test/lisp/emacs-lisp/map-tests.el (test-map-let-default) (test-map-plist-pcase-default, test-map-pcase-matches): Add tests, including for the above item. --- lisp/emacs-lisp/map.el | 40 +++++++++++++++------ test/lisp/emacs-lisp/map-tests.el | 59 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7a48ba47434..feb903cf71f 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -50,18 +50,26 @@ map ARGS is a list of elements to be matched in the map. -Each element of ARGS can be of the form (KEY PAT), in which case KEY is -evaluated and searched for in the map. The match fails if for any KEY -found in the map, the corresponding PAT doesn't match the value -associated with the KEY. +Each element in ARGS can be of the form (KEY PAT) or (KEY PAT +DEFAULT), where KEY is the key sought in the map, PAT is a +`pcase' pattern, and DEFAULT is the default value to use if KEY +isn't found. Both KEY and DEFAULT are evaluated. If DEFAULT +isn't given, then nil is used. Each element can also be a SYMBOL, which is an abbreviation of a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), useful for binding plist values. -Keys in ARGS not found in the map are ignored, and the match doesn't -fail." +The match fails if: + +- KEY is found and PAT does not match the associated value + +- KEY is not found, DEFAULT is given, and PAT does not match + DEFAULT + +- KEY is not found, DEFAULT is not given, and PAT does not match + nil" `(and (pred mapp) ,@(map--make-pcase-bindings args))) @@ -71,12 +79,14 @@ map-let KEYS can be a list of symbols, in which case each element will be bound to the looked up value in MAP. -KEYS can also be a list of (KEY VARNAME) pairs, in which case -KEY is an unquoted form. +KEYS can also be a list of (KEY VARNAME) pairs and +\(KEY VARNAME DEFAULT) triples, in which case KEY and +DEFAULT are unquoted forms. MAP can be an alist, plist, hash-table, or array." (declare (indent 2) - (debug ((&rest &or symbolp ([form symbolp])) form body))) + (debug ((&rest &or symbolp ([form symbolp &optional form])) + form body))) `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) ,@body)) @@ -595,11 +605,21 @@ map-into (map-into \\='((1 . 3)) \\='(hash-table :test eql))" (map--into-hash map (cdr type))) +(defmacro map--pcase-map-elt (key default map) + "A macro to make MAP the last argument to `map-elt'. + +This allows using default values for `map-elt', which can't be +done using `pcase--flip'. + +KEY is the key sought in the map. DEFAULT is the default value." + `(map-elt ,map ,key ,default)) + (defun map--make-pcase-bindings (args) "Return a list of pcase bindings from ARGS to the elements of a map." (mapcar (lambda (elt) (cond ((consp elt) - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) + `(app (map--pcase-map-elt ,(car elt) ,(caddr elt)) + ,(cadr elt))) ((keywordp elt) (let ((var (intern (substring (symbol-name elt) 1)))) `(app (pcase--flip map-elt ,elt) ,var))) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 86c0e9e0503..2204743f794 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -577,6 +577,13 @@ test-map-let (should (= b 2)) (should-not c))) +(ert-deftest test-map-let-default () + (map-let (('foo a 3) + ('baz b 4)) + '((foo . 1)) + (should (equal a 1)) + (should (equal b 4)))) + (ert-deftest test-map-merge () "Test `map-merge'." (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3)) @@ -617,6 +624,58 @@ test-map-plist-pcase (list one two)) '(1 2))))) +(ert-deftest test-map-plist-pcase-default () + (let ((plist '(:two 2))) + (should (equal (pcase-let (((map (:two two 33) + (:three three 44)) + plist)) + (list two three)) + '(2 44))))) + +(ert-deftest test-map-pcase-matches () + (let ((plist '(:two 2))) + (should (equal (pcase plist + ((map (:two two 33) + (:three three)) + (list two three)) + (_ 'fail)) + '(2 nil))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three three 44)) + (list two three)) + (_ 'fail)) + '(2 44))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) '(11 . 22))) + (list two a b)) + (_ 'fail)) + '(2 11 22))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) 44)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) nil)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b))) + (list two a b)) + (_ 'fail)))))) + (ert-deftest test-map-setf-alist-insert-key () (let ((alist)) (should (equal (setf (map-elt alist 'key) 'value) -- 2.34.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-27 1:39 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-28 18:02 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-28 19:19 ` Eli Zaretskii 2023-07-29 0:37 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 2 replies; 18+ messages in thread From: Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-28 18:02 UTC (permalink / raw) To: Michael Heerdegen Cc: Okam, Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier Okam via "Bug reports for GNU Emacs, the Swiss army knife of text editors" [2023-07-27 01:39 +0000] wrote: > From 99208ca640d4944c57082772702cb8534fe91d32 Mon Sep 17 00:00:00 2001 > From: Earl Hyatt <okamsn@protonmail.com> I think your past contributions were under "Earl Hyatt <ej32u@protonmail.com>", right? Is okamsn@protonmail.com your current address? Eli, does it make a difference for the CA, do we need to update .mailmap, or anything like that? > Date: Thu, 20 Jul 2023 21:44:41 -0400 > Subject: [PATCH v3] Allow default values in 'map-let' and the pcase 'map' form > > * lisp/emacs-lisp/map.el (map-let, map) > (map--make-pcase-bindings): Add a third argument for specifying a > default value, like in 'map-elt'. (Bug#49407) > > * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys > that aren't found aren't ignored, they actually get the value > nil (unless the new default value is given). The overall pattern can ^^ Two spaces here. [...] > +Each element in ARGS can be of the form (KEY PAT) or (KEY PAT > +DEFAULT), where KEY is the key sought in the map, PAT is a > +`pcase' pattern, and DEFAULT is the default value to use if KEY > +isn't found. Both KEY and DEFAULT are evaluated. If DEFAULT > +isn't given, then nil is used. FWIW, the form/structure can be kept on a single line, and a couple of passive voices avoided, by rewording as e.g. Each element of ARGS can be of the form (KEY PAT [DEFAULT]), which looks up KEY in the map and matches the associated value against `pcase' pattern PAT. DEFAULT specifies the fallback value to use when KEY is not present in the map. If omitted, it defaults to nil. Both KEY and DEFAULT are evaluated. > +The match fails if: > + > +- KEY is found and PAT does not match the associated value > + > +- KEY is not found, DEFAULT is given, and PAT does not match > + DEFAULT > + > +- KEY is not found, DEFAULT is not given, and PAT does not match > + nil" I find this partly redundant/repetitive, since we already know that PAT is meant to match either KEY's value (when found), or DEFAULT (which defaults to nil). What this doesn't make explicit (compared to the previous version) is what happens when there are multiple ARGS; specifically, when one or more of them don't match. > +KEYS can also be a list of (KEY VARNAME) pairs and > +\(KEY VARNAME DEFAULT) triples, in which case KEY and This could also use (KEY VARNAME [DEFAULT]) syntax, if preferred. Otherwise LGTM, thanks, -- Basil ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-28 18:02 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-28 19:19 ` Eli Zaretskii 2023-07-29 0:37 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 0 replies; 18+ messages in thread From: Eli Zaretskii @ 2023-07-28 19:19 UTC (permalink / raw) To: Basil L. Contovounesios Cc: michael_heerdegen, nicolas, okamsn, monnier, larsi, 49407 > Cc: Okam <okamsn@protonmail.com>, Lars Ingebrigtsen <larsi@gnus.org>, > 49407@debbugs.gnu.org, Nicolas Petton <nicolas@petton.fr>, > Stefan Monnier <monnier@iro.umontreal.ca> > Date: Fri, 28 Jul 2023 20:02:08 +0200 > From: "Basil L. Contovounesios" via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org> > > Okam via "Bug reports for GNU Emacs, the Swiss army knife of text editors" [2023-07-27 01:39 +0000] wrote: > > > From 99208ca640d4944c57082772702cb8534fe91d32 Mon Sep 17 00:00:00 2001 > > From: Earl Hyatt <okamsn@protonmail.com> > > I think your past contributions were under "Earl Hyatt > <ej32u@protonmail.com>", right? Is okamsn@protonmail.com your current > address? Eli, does it make a difference for the CA, do we need to > update .mailmap, or anything like that? The CA record is up to date for Earl Hyatt. No action is required. etc/AUTHORS only mentions the name, not the email address, so no action is needed there, either. ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-28 18:02 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-28 19:19 ` Eli Zaretskii @ 2023-07-29 0:37 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-30 13:53 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 1 reply; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-29 0:37 UTC (permalink / raw) To: Basil L. Contovounesios, Michael Heerdegen Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 3187 bytes --] Basil L. Contovounesios wrote: > Okam via "Bug reports for GNU Emacs, the Swiss army knife of text editors" [2023-07-27 01:39 +0000] wrote: > >> From 99208ca640d4944c57082772702cb8534fe91d32 Mon Sep 17 00:00:00 2001 >> From: Earl Hyatt <okamsn@protonmail.com> > > I think your past contributions were under "Earl Hyatt > <ej32u@protonmail.com>", right? Is okamsn@protonmail.com your current > address? Eli, does it make a difference for the CA, do we need to > update .mailmap, or anything like that? Yes, I changed addresses. The mailmap makes it look like I used the old address in `git log`. Would it be OK to make it so that the old address shows as the new one instead? >> Date: Thu, 20 Jul 2023 21:44:41 -0400 >> Subject: [PATCH v3] Allow default values in 'map-let' and the pcase 'map' form >> >> * lisp/emacs-lisp/map.el (map-let, map) >> (map--make-pcase-bindings): Add a third argument for specifying a >> default value, like in 'map-elt'. (Bug#49407) >> >> * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys >> that aren't found aren't ignored, they actually get the value >> nil (unless the new default value is given). The overall pattern can > ^^ > Two spaces here. Fixed. >> +Each element in ARGS can be of the form (KEY PAT) or (KEY PAT >> +DEFAULT), where KEY is the key sought in the map, PAT is a >> +`pcase' pattern, and DEFAULT is the default value to use if KEY >> +isn't found. Both KEY and DEFAULT are evaluated. If DEFAULT >> +isn't given, then nil is used. > > FWIW, the form/structure can be kept on a single line, and a couple of > passive voices avoided, by rewording as e.g. > > Each element of ARGS can be of the form (KEY PAT [DEFAULT]), > which looks up KEY in the map and matches the associated value > against `pcase' pattern PAT. DEFAULT specifies the fallback > value to use when KEY is not present in the map. If omitted, it > defaults to nil. Both KEY and DEFAULT are evaluated. I have changed it to this. >> +The match fails if: >> + >> +- KEY is found and PAT does not match the associated value >> + >> +- KEY is not found, DEFAULT is given, and PAT does not match >> + DEFAULT >> + >> +- KEY is not found, DEFAULT is not given, and PAT does not match >> + nil" > > I find this partly redundant/repetitive, since we already know that PAT > is meant to match either KEY's value (when found), or DEFAULT (which > defaults to nil). > > What this doesn't make explicit (compared to the previous version) is > what happens when there are multiple ARGS; specifically, when one or > more of them don't match. I changed it to "An element of ARGS fails to match if PAT does not match the associated value or the default value. The overall pattern fails to match if any element of ARGS fails to match." Does that work? >> +KEYS can also be a list of (KEY VARNAME) pairs and >> +\(KEY VARNAME DEFAULT) triples, in which case KEY and > > This could also use (KEY VARNAME [DEFAULT]) syntax, if preferred. Changed. Thank you. [-- Attachment #2: v4-0001-Allow-default-values-in-map-let-and-the-pcase-map.patch --] [-- Type: text/x-patch, Size: 6529 bytes --] From 74616d0717c520747328587cf1551399eb11a318 Mon Sep 17 00:00:00 2001 From: Earl Hyatt <okamsn@protonmail.com> Date: Thu, 20 Jul 2023 21:44:41 -0400 Subject: [PATCH v4] Allow default values in 'map-let' and the pcase 'map' form * lisp/emacs-lisp/map.el (map-let, map) (map--make-pcase-bindings): Add a third argument for specifying a default value, like in 'map-elt'. (Bug#49407) * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys that aren't found aren't ignored, they actually get the value nil (unless the new default value is given). The overall pattern can still fail to match if the sub-pattern for the unfound key doesn't match nil. * test/lisp/emacs-lisp/map-tests.el (test-map-let-default) (test-map-plist-pcase-default, test-map-pcase-matches): Add tests, including for the above item. --- lisp/emacs-lisp/map.el | 33 +++++++++++------ test/lisp/emacs-lisp/map-tests.el | 59 +++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 10 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7a48ba47434..b55eb431668 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -50,18 +50,20 @@ map ARGS is a list of elements to be matched in the map. -Each element of ARGS can be of the form (KEY PAT), in which case KEY is -evaluated and searched for in the map. The match fails if for any KEY -found in the map, the corresponding PAT doesn't match the value -associated with the KEY. +Each element of ARGS can be of the form (KEY PAT [DEFAULT]), +which looks up KEY in the map and matches the associated value +against `pcase' pattern PAT. DEFAULT specifies the fallback +value to use when KEY is not present in the map. If omitted, it +defaults to nil. Both KEY and DEFAULT are evaluated. Each element can also be a SYMBOL, which is an abbreviation of a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), useful for binding plist values. -Keys in ARGS not found in the map are ignored, and the match doesn't -fail." +An element of ARGS fails to match if PAT does not match the +associated value or the default value. The overall pattern fails +to match if any element of ARGS fails to match." `(and (pred mapp) ,@(map--make-pcase-bindings args))) @@ -71,12 +73,13 @@ map-let KEYS can be a list of symbols, in which case each element will be bound to the looked up value in MAP. -KEYS can also be a list of (KEY VARNAME) pairs, in which case -KEY is an unquoted form. +KEYS can also be a list of (KEY VARNAME [DEFAULT]) sublists, in +which case KEY and DEFAULT are unquoted forms. MAP can be an alist, plist, hash-table, or array." (declare (indent 2) - (debug ((&rest &or symbolp ([form symbolp])) form body))) + (debug ((&rest &or symbolp ([form symbolp &optional form])) + form body))) `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) ,@body)) @@ -595,11 +598,21 @@ map-into (map-into \\='((1 . 3)) \\='(hash-table :test eql))" (map--into-hash map (cdr type))) +(defmacro map--pcase-map-elt (key default map) + "A macro to make MAP the last argument to `map-elt'. + +This allows using default values for `map-elt', which can't be +done using `pcase--flip'. + +KEY is the key sought in the map. DEFAULT is the default value." + `(map-elt ,map ,key ,default)) + (defun map--make-pcase-bindings (args) "Return a list of pcase bindings from ARGS to the elements of a map." (mapcar (lambda (elt) (cond ((consp elt) - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) + `(app (map--pcase-map-elt ,(car elt) ,(caddr elt)) + ,(cadr elt))) ((keywordp elt) (let ((var (intern (substring (symbol-name elt) 1)))) `(app (pcase--flip map-elt ,elt) ,var))) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 86c0e9e0503..2204743f794 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -577,6 +577,13 @@ test-map-let (should (= b 2)) (should-not c))) +(ert-deftest test-map-let-default () + (map-let (('foo a 3) + ('baz b 4)) + '((foo . 1)) + (should (equal a 1)) + (should (equal b 4)))) + (ert-deftest test-map-merge () "Test `map-merge'." (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3)) @@ -617,6 +624,58 @@ test-map-plist-pcase (list one two)) '(1 2))))) +(ert-deftest test-map-plist-pcase-default () + (let ((plist '(:two 2))) + (should (equal (pcase-let (((map (:two two 33) + (:three three 44)) + plist)) + (list two three)) + '(2 44))))) + +(ert-deftest test-map-pcase-matches () + (let ((plist '(:two 2))) + (should (equal (pcase plist + ((map (:two two 33) + (:three three)) + (list two three)) + (_ 'fail)) + '(2 nil))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three three 44)) + (list two three)) + (_ 'fail)) + '(2 44))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) '(11 . 22))) + (list two a b)) + (_ 'fail)) + '(2 11 22))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) 44)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) nil)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b))) + (list two a b)) + (_ 'fail)))))) + (ert-deftest test-map-setf-alist-insert-key () (let ((alist)) (should (equal (setf (map-elt alist 'key) 'value) -- 2.34.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-29 0:37 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-30 13:53 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-31 4:02 ` Michael Heerdegen 2023-08-06 13:31 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 2 replies; 18+ messages in thread From: Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-30 13:53 UTC (permalink / raw) To: Okam Cc: Michael Heerdegen, Lars Ingebrigtsen, Nicolas Petton, 49407-done, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 689 bytes --] close 49407 30.1 quit Okam [2023-07-29 00:37 +0000] wrote: > Basil L. Contovounesios wrote: >> Okam [2023-07-27 01:39 +0000] wrote: >>> From: Earl Hyatt <okamsn@protonmail.com> >> >> I think your past contributions were under "Earl Hyatt >> <ej32u@protonmail.com>", right? Is okamsn@protonmail.com your current >> address? > > Yes, I changed addresses. The mailmap makes it look like I used the old > address in `git log`. Oh, sorry, I actually didn't notice you had existing mailmap entries in place. > Would it be OK to make it so that the old address > shows as the new one instead? Is okamsn@protonmail.com your canonical address? If so, what we want is the following, right? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: mailmap.diff --] [-- Type: text/x-diff, Size: 599 bytes --] diff --git a/.mailmap b/.mailmap index 8454eb9154c..0531cc0c290 100644 --- a/.mailmap +++ b/.mailmap @@ -51,8 +51,7 @@ David M. Koppelman <koppel@ece.lsu.edu> Deniz Dogan <deniz@dogan.se> <deniz.a.m.dogan@gmail.com> Dick R. Chiang <dick.r.chiang@gmail.com> Dick R. Chiang <dick.r.chiang@gmail.com> dickmao <none> -Earl Hyatt <ej32u@protonmail.com> -Earl Hyatt <ej32u@protonmail.com> <okamsn@protonmail.com> +Earl Hyatt <okamsn@protonmail.com> <ej32u@protonmail.com> Edward M. Reingold <reingold@emr.cs.iit.edu> Eli Zaretskii <eliz@gnu.org> <eliz@is.elta.co.il> Emilio C. Lopes <eclig@gmx.net> [-- Attachment #3: Type: text/plain, Size: 519 bytes --] > I changed it to "An element of ARGS fails to match if PAT does not match > the associated value or the default value. The overall pattern fails > to match if any element of ARGS fails to match." Does that work? WFM, thank you! Installed and closing as done: Allow default values in 'map-let' and the pcase 'map' form 19777b7c864 2023-07-30 15:20:27 +0200 https://git.sv.gnu.org/cgit/emacs.git/commit/?id=19777b7c864 I'll also bump the map.el Version header in a week or so if no-one beats me to it. -- Basil ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-30 13:53 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-31 4:02 ` Michael Heerdegen 2023-08-08 12:41 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-08-06 13:31 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 1 reply; 18+ messages in thread From: Michael Heerdegen @ 2023-07-31 4:02 UTC (permalink / raw) To: 49407 Cc: Basil L. Contovounesios, Nicolas Petton, Okam, Stefan Monnier, Lars Ingebrigtsen, 49407-done [-- Attachment #1: Type: text/plain, Size: 384 bytes --] "Basil L. Contovounesios" via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org> writes: > WFM, thank you! Installed and closing as done: > > Allow default values in 'map-let' and the pcase 'map' form > 19777b7c864 2023-07-30 15:20:27 +0200 > https://git.sv.gnu.org/cgit/emacs.git/commit/?id=19777b7c864 Does this addition look appropriate? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Docstring-tweaks-in-map.el.patch --] [-- Type: text/x-diff, Size: 2159 bytes --] From b2b6f34c08c17a9411c4264ea500b434c833ec04 Mon Sep 17 00:00:00 2001 From: Michael Heerdegen <michael_heerdegen@web.de> Date: Tue, 25 Jul 2023 20:38:46 +0200 Subject: [PATCH] ; Docstring tweaks in map.el * lisp/emacs-lisp/map.el (pcase-defmacro) <map>: (map-let): Mention 'map-elt' where equality predicates are explained. Improve wording. --- lisp/emacs-lisp/map.el | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index b55eb431668..04c00a2e908 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -57,9 +57,10 @@ map defaults to nil. Both KEY and DEFAULT are evaluated. Each element can also be a SYMBOL, which is an abbreviation of -a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL -is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), -useful for binding plist values. +a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). A keyword :FOO +is an abbreviation of the form (:FOO FOO), useful for binding plist values. + +See `map-elt' for an explanation of the used equality tests. An element of ARGS fails to match if PAT does not match the associated value or the default value. The overall pattern fails @@ -70,13 +71,13 @@ map (defmacro map-let (keys map &rest body) "Bind the variables in KEYS to the elements of MAP then evaluate BODY. -KEYS can be a list of symbols, in which case each element will be -bound to the looked up value in MAP. - -KEYS can also be a list of (KEY VARNAME [DEFAULT]) sublists, in -which case KEY and DEFAULT are unquoted forms. +Elements of the list KEYS can be symbols, in which case each +will be bound to the looked up value in MAP, or +(KEY VARNAME [DEFAULT]) sublists where KEY and DEFAULT are +unquoted forms. -MAP can be an alist, plist, hash-table, or array." +MAP can be an alist, plist, hash-table, or array. See `map-elt' +for an explanation of the used equality tests." (declare (indent 2) (debug ((&rest &or symbolp ([form symbolp &optional form])) form body))) -- 2.30.2 [-- Attachment #3: Type: text/plain, Size: 295 bytes --] If it's not clear: I changed :SYMBOL to :FOO because the original text called SYMBOL a keyword and then used SYMBOL and :SYMBOL (although then only :SYMBOL would be the keyword) - I found that confusing. :KEYWORD would also be confusing, so I went with the placeholder "FOO". TIA, Michael. ^ permalink raw reply related [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-31 4:02 ` Michael Heerdegen @ 2023-08-08 12:41 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 0 replies; 18+ messages in thread From: Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-08 12:41 UTC (permalink / raw) To: Michael Heerdegen Cc: Okam, Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier Michael Heerdegen [2023-07-31 06:02 +0200] wrote: > Does this addition look appropriate? Reads fine to me, so AFAIC feel free to install. Just a couple of thoughts inline: > diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el > index b55eb431668..04c00a2e908 100644 > --- a/lisp/emacs-lisp/map.el > +++ b/lisp/emacs-lisp/map.el > @@ -57,9 +57,10 @@ map > defaults to nil. Both KEY and DEFAULT are evaluated. > > Each element can also be a SYMBOL, which is an abbreviation of > -a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL > -is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), > -useful for binding plist values. > +a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). A keyword :FOO > +is an abbreviation of the form (:FOO FOO), useful for binding plist values. If you prefer to keep the stronger connectedness(?) between the sentences, and/or avoid introducing a metaname with no inherent meaning: Specifically when SYMBOL is a keyword :KEY, it is an abbreviation of the form (:KEY KEY), which can be useful for binding plist values. > +See `map-elt' for an explanation of the used equality tests. s/used/relevant/ would avoid the passive voice, if you prefer. Or 'for details on key lookup', or ... [ Trying to get on Eli's non-naughty list. ] > If it's not clear: I changed :SYMBOL to :FOO because the original text > called SYMBOL a keyword and then used SYMBOL and :SYMBOL (although then > only :SYMBOL would be the keyword) - I found that confusing. :KEYWORD > would also be confusing, so I went with the placeholder "FOO". I'm confused, I thought FOO stood For Obfuscation Only ;). Thanks, -- Basil ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-07-30 13:53 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-31 4:02 ` Michael Heerdegen @ 2023-08-06 13:31 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-08-08 12:46 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 1 reply; 18+ messages in thread From: Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-06 13:31 UTC (permalink / raw) To: Basil L. Contovounesios Cc: Michael Heerdegen, Lars Ingebrigtsen, Nicolas Petton, 49407-done, Stefan Monnier Basil L. Contovounesios wrote: >> Would it be OK to make it so that the old address >> shows as the new one instead? > > Is okamsn@protonmail.com your canonical address? > If so, what we want is the following, right? Yes, thank you. > >> I changed it to "An element of ARGS fails to match if PAT does not match >> the associated value or the default value. The overall pattern fails >> to match if any element of ARGS fails to match." Does that work? > > WFM, thank you! Installed and closing as done: > > Allow default values in 'map-let' and the pcase 'map' form > 19777b7c864 2023-07-30 15:20:27 +0200 > https://git.sv.gnu.org/cgit/emacs.git/commit/?id=19777b7c864 > > I'll also bump the map.el Version header in a week or so if no-one beats > me to it. Thanks! ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2023-08-06 13:31 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-08 12:46 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 0 replies; 18+ messages in thread From: Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-08-08 12:46 UTC (permalink / raw) To: Okam Cc: Michael Heerdegen, Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier Okam [2023-08-06 13:31 +0000] wrote: > Basil L. Contovounesios wrote: >>> Would it be OK to make it so that the old address >>> shows as the new one instead? >> Is okamsn@protonmail.com your canonical address? >> If so, what we want is the following, right? > > Yes, thank you. Done: ; .mailmap: Flip entry. 91854ceaee3 2023-08-08 14:43:22 +0200 https://git.sv.gnu.org/cgit/emacs.git/commit/?id=91854ceaee3 Thanks, -- Basil ^ permalink raw reply [flat|nested] 18+ messages in thread
* bug#49407: Request: Specify default values in `map-let` in Map.el 2021-07-04 23:08 bug#49407: Request: Specify default values in `map-let` in Map.el Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2021-07-15 8:51 ` Lars Ingebrigtsen @ 2023-07-27 1:37 ` Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 0 replies; 18+ messages in thread From: Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2023-07-27 1:37 UTC (permalink / raw) To: Michael Heerdegen Cc: Lars Ingebrigtsen, 49407, Nicolas Petton, Stefan Monnier [-- Attachment #1: Type: text/plain, Size: 565 bytes --] Michael Heerdegen wrote: > Only one detail: > > | @@ -71,12 +79,14 @@ map-let > | KEYS can be a list of symbols, in which case each element will be > | bound to the looked up value in MAP. > | > | -KEYS can also be a list of (KEY VARNAME) pairs, in which case > | -KEY is an unquoted form. > | +KEYS can also be a list of (KEY VARNAME) pairs and > | +\(KEY VARNAME DEFAULT) triples, in which case KEY is an > | +unquoted form. > > Here you could also say that DEFAULT is an unquoted form. I have made that change. Please see the attached file. [-- Attachment #2: v3-0001-Allow-default-values-in-map-let-and-the-pcase-map.patch --] [-- Type: text/x-patch, Size: 6585 bytes --] From 99208ca640d4944c57082772702cb8534fe91d32 Mon Sep 17 00:00:00 2001 From: Earl Hyatt <okamsn@protonmail.com> Date: Thu, 20 Jul 2023 21:44:41 -0400 Subject: [PATCH v3] Allow default values in 'map-let' and the pcase 'map' form * lisp/emacs-lisp/map.el (map-let, map) (map--make-pcase-bindings): Add a third argument for specifying a default value, like in 'map-elt'. (Bug#49407) * lisp/emacs-lisp/map.el (map--make-pcase-bindings): Clarify that keys that aren't found aren't ignored, they actually get the value nil (unless the new default value is given). The overall pattern can still fail to match if the sub-pattern for the unfound key doesn't match nil. * test/lisp/emacs-lisp/map-tests.el (test-map-let-default) (test-map-plist-pcase-default, test-map-pcase-matches): Add tests, including for the above item. --- lisp/emacs-lisp/map.el | 40 +++++++++++++++------ test/lisp/emacs-lisp/map-tests.el | 59 +++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el index 7a48ba47434..feb903cf71f 100644 --- a/lisp/emacs-lisp/map.el +++ b/lisp/emacs-lisp/map.el @@ -50,18 +50,26 @@ map ARGS is a list of elements to be matched in the map. -Each element of ARGS can be of the form (KEY PAT), in which case KEY is -evaluated and searched for in the map. The match fails if for any KEY -found in the map, the corresponding PAT doesn't match the value -associated with the KEY. +Each element in ARGS can be of the form (KEY PAT) or (KEY PAT +DEFAULT), where KEY is the key sought in the map, PAT is a +`pcase' pattern, and DEFAULT is the default value to use if KEY +isn't found. Both KEY and DEFAULT are evaluated. If DEFAULT +isn't given, then nil is used. Each element can also be a SYMBOL, which is an abbreviation of a (KEY PAT) tuple of the form (\\='SYMBOL SYMBOL). When SYMBOL is a keyword, it is an abbreviation of the form (:SYMBOL SYMBOL), useful for binding plist values. -Keys in ARGS not found in the map are ignored, and the match doesn't -fail." +The match fails if: + +- KEY is found and PAT does not match the associated value + +- KEY is not found, DEFAULT is given, and PAT does not match + DEFAULT + +- KEY is not found, DEFAULT is not given, and PAT does not match + nil" `(and (pred mapp) ,@(map--make-pcase-bindings args))) @@ -71,12 +79,14 @@ map-let KEYS can be a list of symbols, in which case each element will be bound to the looked up value in MAP. -KEYS can also be a list of (KEY VARNAME) pairs, in which case -KEY is an unquoted form. +KEYS can also be a list of (KEY VARNAME) pairs and +\(KEY VARNAME DEFAULT) triples, in which case KEY and +DEFAULT are unquoted forms. MAP can be an alist, plist, hash-table, or array." (declare (indent 2) - (debug ((&rest &or symbolp ([form symbolp])) form body))) + (debug ((&rest &or symbolp ([form symbolp &optional form])) + form body))) `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) ,@body)) @@ -595,11 +605,21 @@ map-into (map-into \\='((1 . 3)) \\='(hash-table :test eql))" (map--into-hash map (cdr type))) +(defmacro map--pcase-map-elt (key default map) + "A macro to make MAP the last argument to `map-elt'. + +This allows using default values for `map-elt', which can't be +done using `pcase--flip'. + +KEY is the key sought in the map. DEFAULT is the default value." + `(map-elt ,map ,key ,default)) + (defun map--make-pcase-bindings (args) "Return a list of pcase bindings from ARGS to the elements of a map." (mapcar (lambda (elt) (cond ((consp elt) - `(app (pcase--flip map-elt ,(car elt)) ,(cadr elt))) + `(app (map--pcase-map-elt ,(car elt) ,(caddr elt)) + ,(cadr elt))) ((keywordp elt) (let ((var (intern (substring (symbol-name elt) 1)))) `(app (pcase--flip map-elt ,elt) ,var))) diff --git a/test/lisp/emacs-lisp/map-tests.el b/test/lisp/emacs-lisp/map-tests.el index 86c0e9e0503..2204743f794 100644 --- a/test/lisp/emacs-lisp/map-tests.el +++ b/test/lisp/emacs-lisp/map-tests.el @@ -577,6 +577,13 @@ test-map-let (should (= b 2)) (should-not c))) +(ert-deftest test-map-let-default () + (map-let (('foo a 3) + ('baz b 4)) + '((foo . 1)) + (should (equal a 1)) + (should (equal b 4)))) + (ert-deftest test-map-merge () "Test `map-merge'." (should (equal (sort (map-merge 'list '(a 1) '((b . 2) (c . 3)) @@ -617,6 +624,58 @@ test-map-plist-pcase (list one two)) '(1 2))))) +(ert-deftest test-map-plist-pcase-default () + (let ((plist '(:two 2))) + (should (equal (pcase-let (((map (:two two 33) + (:three three 44)) + plist)) + (list two three)) + '(2 44))))) + +(ert-deftest test-map-pcase-matches () + (let ((plist '(:two 2))) + (should (equal (pcase plist + ((map (:two two 33) + (:three three)) + (list two three)) + (_ 'fail)) + '(2 nil))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three three 44)) + (list two three)) + (_ 'fail)) + '(2 44))) + + (should (equal (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) '(11 . 22))) + (list two a b)) + (_ 'fail)) + '(2 11 22))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) 44)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b) nil)) + (list two a b)) + (_ 'fail)))) + + (should (equal 'fail + (pcase plist + ((map (:two two 33) + (:three `(,a . ,b))) + (list two a b)) + (_ 'fail)))))) + (ert-deftest test-map-setf-alist-insert-key () (let ((alist)) (should (equal (setf (map-elt alist 'key) 'value) -- 2.34.1 ^ permalink raw reply related [flat|nested] 18+ messages in thread
end of thread, other threads:[~2023-08-08 12:46 UTC | newest] Thread overview: 18+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2021-07-04 23:08 bug#49407: Request: Specify default values in `map-let` in Map.el Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2021-07-15 8:51 ` Lars Ingebrigtsen 2021-07-16 1:45 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-21 2:56 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-22 1:48 ` Michael Heerdegen 2023-07-22 15:45 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-22 18:46 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-26 3:41 ` Michael Heerdegen 2023-07-27 1:39 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-28 18:02 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-28 19:19 ` Eli Zaretskii 2023-07-29 0:37 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-30 13:53 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-31 4:02 ` Michael Heerdegen 2023-08-08 12:41 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-08-06 13:31 ` Okam via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-08-08 12:46 ` Basil L. Contovounesios via Bug reports for GNU Emacs, the Swiss army knife of text editors 2023-07-27 1:37 ` Earl Hyatt via Bug reports for GNU Emacs, the Swiss army knife of text editors
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.