From: Andreas Gerler Date: Sat, 20 Feb 2016 11:56:32 +0000 (+0100) Subject: update dash. X-Git-Url: http://git.bundesbrandschatzamt.de/?a=commitdiff_plain;h=b509b3fc3a3778384921ddf3ab049419d0aab493;p=emacsconfig.git update dash. --- diff --git a/bba.org b/bba.org index ede7315d..c4ff9847 100644 --- a/bba.org +++ b/bba.org @@ -297,7 +297,7 @@ Some key bindings taken from [[https://ebzzry.github.io/emacs-pairs.html][emacs- (if (>= emacs-major-version 24) (progn (add-to-list 'load-path "~/.emacs.d/elpa/smartparens-20150628.1418") - (add-to-list 'load-path "~/.emacs.d/elpa/dash-20150704.253") + (add-to-list 'load-path "~/.emacs.d/elpa/dash-20151216.1315") (require 'smartparens) (smartparens-global-mode 1) (defmacro def-pairs (pairs) diff --git a/elpa/dash-20151021.113/dash-autoloads.el b/elpa/dash-20151021.113/dash-autoloads.el deleted file mode 100644 index 7585dc89..00000000 --- a/elpa/dash-20151021.113/dash-autoloads.el +++ /dev/null @@ -1,15 +0,0 @@ -;;; dash-autoloads.el --- automatically extracted autoloads -;; -;;; Code: -(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) - -;;;### (autoloads nil nil ("dash.el") (22093 56167 390246 0)) - -;;;*** - -;; Local Variables: -;; version-control: never -;; no-byte-compile: t -;; no-update-autoloads: t -;; End: -;;; dash-autoloads.el ends here diff --git a/elpa/dash-20151021.113/dash-pkg.el b/elpa/dash-20151021.113/dash-pkg.el deleted file mode 100644 index e8b125ea..00000000 --- a/elpa/dash-20151021.113/dash-pkg.el +++ /dev/null @@ -1 +0,0 @@ -(define-package "dash" "20151021.113" "A modern list library for Emacs" 'nil :keywords '("lists")) diff --git a/elpa/dash-20151021.113/dash.el b/elpa/dash-20151021.113/dash.el deleted file mode 100644 index a68ac891..00000000 --- a/elpa/dash-20151021.113/dash.el +++ /dev/null @@ -1,2435 +0,0 @@ -;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- - -;; Copyright (C) 2012-2015 Free Software Foundation, Inc. - -;; Author: Magnar Sveen -;; Version: 2.12.1 -;; Package-Version: 20151021.113 -;; Keywords: lists - -;; 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 . - -;;; Commentary: - -;; A modern list api for Emacs. -;; -;; See documentation on https://github.com/magnars/dash.el#functions -;; -;; **Please note** The lexical binding in this file is not utilised at the -;; moment. We will take full advantage of lexical binding in an upcoming 3.0 -;; release of Dash. In the meantime, we've added the pragma to avoid a bug that -;; you can read more about in https://github.com/magnars/dash.el/issues/130. -;; - -;;; Code: - -(defgroup dash () - "Customize group for dash.el" - :group 'lisp - :prefix "dash-") - -(defun dash--enable-fontlock (symbol value) - (when value - (dash-enable-font-lock)) - (set-default symbol value)) - -(defcustom dash-enable-fontlock nil - "If non-nil, enable fontification of dash functions, macros and -special values." - :type 'boolean - :set 'dash--enable-fontlock - :group 'dash) - -(defmacro !cons (car cdr) - "Destructive: Set CDR to the cons of CAR and CDR." - `(setq ,cdr (cons ,car ,cdr))) - -(defmacro !cdr (list) - "Destructive: Set LIST to the cdr of LIST." - `(setq ,list (cdr ,list))) - -(defmacro --each (list &rest body) - "Anaphoric form of `-each'." - (declare (debug (form body)) - (indent 1)) - (let ((l (make-symbol "list"))) - `(let ((,l ,list) - (it-index 0)) - (while ,l - (let ((it (car ,l))) - ,@body) - (setq it-index (1+ it-index)) - (!cdr ,l))))) - -(defun -each (list fn) - "Call FN with every item in LIST. Return nil, used for side-effects only." - (--each list (funcall fn it))) - -(put '-each 'lisp-indent-function 1) - -(defmacro --each-while (list pred &rest body) - "Anaphoric form of `-each-while'." - (declare (debug (form form body)) - (indent 2)) - (let ((l (make-symbol "list")) - (c (make-symbol "continue"))) - `(let ((,l ,list) - (,c t) - (it-index 0)) - (while (and ,l ,c) - (let ((it (car ,l))) - (if (not ,pred) (setq ,c nil) ,@body)) - (setq it-index (1+ it-index)) - (!cdr ,l))))) - -(defun -each-while (list pred fn) - "Call FN with every item in LIST while (PRED item) is non-nil. -Return nil, used for side-effects only." - (--each-while list (funcall pred it) (funcall fn it))) - -(put '-each-while 'lisp-indent-function 2) - -(defmacro --dotimes (num &rest body) - "Repeatedly executes BODY (presumably for side-effects) with `it` bound to integers from 0 through NUM-1." - (declare (debug (form body)) - (indent 1)) - (let ((n (make-symbol "num"))) - `(let ((,n ,num) - (it 0)) - (while (< it ,n) - ,@body - (setq it (1+ it)))))) - -(defun -dotimes (num fn) - "Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through NUM-1." - (--dotimes num (funcall fn it))) - -(put '-dotimes 'lisp-indent-function 1) - -(defun -map (fn list) - "Return a new list consisting of the result of applying FN to the items in LIST." - (mapcar fn list)) - -(defmacro --map (form list) - "Anaphoric form of `-map'." - (declare (debug (form form))) - `(mapcar (lambda (it) ,form) ,list)) - -(defmacro --reduce-from (form initial-value list) - "Anaphoric form of `-reduce-from'." - (declare (debug (form form form))) - `(let ((acc ,initial-value)) - (--each ,list (setq acc ,form)) - acc)) - -(defun -reduce-from (fn initial-value list) - "Return the result of applying FN to INITIAL-VALUE and the -first item in LIST, then applying FN to that result and the 2nd -item, etc. If LIST contains no items, return INITIAL-VALUE and -FN is not called. - -In the anaphoric form `--reduce-from', the accumulated value is -exposed as `acc`. - -See also: `-reduce', `-reduce-r'" - (--reduce-from (funcall fn acc it) initial-value list)) - -(defmacro --reduce (form list) - "Anaphoric form of `-reduce'." - (declare (debug (form form))) - (let ((lv (make-symbol "list-value"))) - `(let ((,lv ,list)) - (if ,lv - (--reduce-from ,form (car ,lv) (cdr ,lv)) - (let (acc it) ,form))))) - -(defun -reduce (fn list) - "Return the result of applying FN to the first 2 items in LIST, -then applying FN to that result and the 3rd item, etc. If LIST -contains no items, FN must accept no arguments as well, and -reduce return the result of calling FN with no arguments. If -LIST has only 1 item, it is returned and FN is not called. - -In the anaphoric form `--reduce', the accumulated value is -exposed as `acc`. - -See also: `-reduce-from', `-reduce-r'" - (if list - (-reduce-from fn (car list) (cdr list)) - (funcall fn))) - -(defun -reduce-r-from (fn initial-value list) - "Replace conses with FN, nil with INITIAL-VALUE and evaluate -the resulting expression. If LIST is empty, INITIAL-VALUE is -returned and FN is not called. - -Note: this function works the same as `-reduce-from' but the -operation associates from right instead of from left. - -See also: `-reduce-r', `-reduce'" - (if (not list) initial-value - (funcall fn (car list) (-reduce-r-from fn initial-value (cdr list))))) - -(defmacro --reduce-r-from (form initial-value list) - "Anaphoric version of `-reduce-r-from'." - (declare (debug (form form form))) - `(-reduce-r-from (lambda (&optional it acc) ,form) ,initial-value ,list)) - -(defun -reduce-r (fn list) - "Replace conses with FN and evaluate the resulting expression. -The final nil is ignored. If LIST contains no items, FN must -accept no arguments as well, and reduce return the result of -calling FN with no arguments. If LIST has only 1 item, it is -returned and FN is not called. - -The first argument of FN is the new item, the second is the -accumulated value. - -Note: this function works the same as `-reduce' but the operation -associates from right instead of from left. - -See also: `-reduce-r-from', `-reduce'" - (cond - ((not list) (funcall fn)) - ((not (cdr list)) (car list)) - (t (funcall fn (car list) (-reduce-r fn (cdr list)))))) - -(defmacro --reduce-r (form list) - "Anaphoric version of `-reduce-r'." - (declare (debug (form form))) - `(-reduce-r (lambda (&optional it acc) ,form) ,list)) - -(defmacro --filter (form list) - "Anaphoric form of `-filter'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list (when ,form (!cons it ,r))) - (nreverse ,r)))) - -(defun -filter (pred list) - "Return a new list of the items in LIST for which PRED returns a non-nil value. - -Alias: `-select'" - (--filter (funcall pred it) list)) - -(defalias '-select '-filter) -(defalias '--select '--filter) - -(defmacro --remove (form list) - "Anaphoric form of `-remove'." - (declare (debug (form form))) - `(--filter (not ,form) ,list)) - -(defun -remove (pred list) - "Return a new list of the items in LIST for which PRED returns nil. - -Alias: `-reject'" - (--remove (funcall pred it) list)) - -(defalias '-reject '-remove) -(defalias '--reject '--remove) - -(defun -remove-first (pred list) - "Return a new list with the first item matching PRED removed. - -Alias: `-reject-first' - -See also: `-remove', `-map-first'" - (let (front) - (while (and list (not (funcall pred (car list)))) - (push (car list) front) - (!cdr list)) - (if list - (-concat (nreverse front) (cdr list)) - (nreverse front)))) - -(defmacro --remove-first (form list) - "Anaphoric form of `-remove-first'." - (declare (debug (form form))) - `(-remove-first (lambda (it) ,form) ,list)) - -(defalias '-reject-first '-remove-first) -(defalias '--reject-first '--remove-first) - -(defun -remove-last (pred list) - "Return a new list with the last item matching PRED removed. - -Alias: `-reject-last' - -See also: `-remove', `-map-last'" - (nreverse (-remove-first pred (nreverse list)))) - -(defmacro --remove-last (form list) - "Anaphoric form of `-remove-last'." - (declare (debug (form form))) - `(-remove-last (lambda (it) ,form) ,list)) - -(defalias '-reject-last '-remove-last) -(defalias '--reject-last '--remove-last) - -(defun -remove-item (item list) - "Remove all occurences of ITEM from LIST. - -Comparison is done with `equal'." - (--remove (equal it item) list)) - -(defmacro --keep (form list) - "Anaphoric form of `-keep'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (m (make-symbol "mapped"))) - `(let (,r) - (--each ,list (let ((,m ,form)) (when ,m (!cons ,m ,r)))) - (nreverse ,r)))) - -(defun -keep (fn list) - "Return a new list of the non-nil results of applying FN to the items in LIST. - -If you want to select the original items satisfying a predicate use `-filter'." - (--keep (funcall fn it) list)) - -(defun -non-nil (list) - "Return all non-nil elements of LIST." - (-remove 'null list)) - -(defmacro --map-indexed (form list) - "Anaphoric form of `-map-indexed'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list - (!cons ,form ,r)) - (nreverse ,r)))) - -(defun -map-indexed (fn list) - "Return a new list consisting of the result of (FN index item) for each item in LIST. - -In the anaphoric form `--map-indexed', the index is exposed as `it-index`." - (--map-indexed (funcall fn it-index it) list)) - -(defmacro --map-when (pred rep list) - "Anaphoric form of `-map-when'." - (declare (debug (form form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each ,list (!cons (if ,pred ,rep it) ,r)) - (nreverse ,r)))) - -(defun -map-when (pred rep list) - "Return a new list where the elements in LIST that does not match the PRED function -are unchanged, and where the elements in LIST that do match the PRED function are mapped -through the REP function. - -Alias: `-replace-where' - -See also: `-update-at'" - (--map-when (funcall pred it) (funcall rep it) list)) - -(defalias '-replace-where '-map-when) -(defalias '--replace-where '--map-when) - -(defun -map-first (pred rep list) - "Replace first item in LIST satisfying PRED with result of REP called on this item. - -See also: `-map-when', `-replace-first'" - (let (front) - (while (and list (not (funcall pred (car list)))) - (push (car list) front) - (!cdr list)) - (if list - (-concat (nreverse front) (cons (funcall rep (car list)) (cdr list))) - (nreverse front)))) - -(defmacro --map-first (pred rep list) - "Anaphoric form of `-map-first'." - `(-map-first (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) - -(defun -map-last (pred rep list) - "Replace first item in LIST satisfying PRED with result of REP called on this item. - -See also: `-map-when', `-replace-last'" - (nreverse (-map-first pred rep (nreverse list)))) - -(defmacro --map-last (pred rep list) - "Anaphoric form of `-map-last'." - `(-map-last (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) - -(defun -replace (old new list) - "Replace all OLD items in LIST with NEW. - -Elements are compared using `equal'. - -See also: `-replace-at'" - (--map-when (equal it old) new list)) - -(defun -replace-first (old new list) - "Replace the first occurence of OLD with NEW in LIST. - -Elements are compared using `equal'. - -See also: `-map-first'" - (--map-first (equal old it) new list)) - -(defun -replace-last (old new list) - "Replace the last occurence of OLD with NEW in LIST. - -Elements are compared using `equal'. - -See also: `-map-last'" - (--map-last (equal old it) new list)) - -(defmacro --mapcat (form list) - "Anaphoric form of `-mapcat'." - (declare (debug (form form))) - `(apply 'append (--map ,form ,list))) - -(defun -mapcat (fn list) - "Return the concatenation of the result of mapping FN over LIST. -Thus function FN should return a list." - (--mapcat (funcall fn it) list)) - -(defun -flatten (l) - "Take a nested list L and return its contents as a single, flat list. - -Note that because `nil' represents a list of zero elements (an -empty list), any mention of nil in L will disappear after -flattening. If you need to preserve nils, consider `-flatten-n' -or map them to some unique symbol and then map them back. - -Conses of two atoms are considered \"terminals\", that is, they -aren't flattened further. - -See also: `-flatten-n'" - (if (and (listp l) (listp (cdr l))) - (-mapcat '-flatten l) - (list l))) - -(defmacro --iterate (form init n) - "Anaphoric version of `-iterate'." - (declare (debug (form form form))) - `(-iterate (lambda (it) ,form) ,init ,n)) - -(defun -flatten-n (num list) - "Flatten NUM levels of a nested LIST. - -See also: `-flatten'" - (-last-item (--iterate (--mapcat (-list it) it) list (1+ num)))) - -(defun -concat (&rest lists) - "Return a new list with the concatenation of the elements in the supplied LISTS." - (apply 'append lists)) - -(defalias '-copy 'copy-sequence - "Create a shallow copy of LIST.") - -(defun -splice (pred fun list) - "Splice lists generated by FUN in place of elements matching PRED in LIST. - -FUN takes the element matching PRED as input. - -This function can be used as replacement for `,@' in case you -need to splice several lists at marked positions (for example -with keywords). - -See also: `-splice-list', `-insert-at'" - (let (r) - (--each list - (if (funcall pred it) - (let ((new (funcall fun it))) - (--each new (!cons it r))) - (!cons it r))) - (nreverse r))) - -(defmacro --splice (pred form list) - "Anaphoric form of `-splice'." - `(-splice (lambda (it) ,pred) (lambda (it) ,form) ,list)) - -(defun -splice-list (pred new-list list) - "Splice NEW-LIST in place of elements matching PRED in LIST. - -See also: `-splice', `-insert-at'" - (-splice pred (lambda (_) new-list) list)) - -(defmacro --splice-list (pred new-list list) - "Anaphoric form of `-splice-list'." - `(-splice-list (lambda (it) ,pred) ,new-list ,list)) - -(defun -cons* (&rest args) - "Make a new list from the elements of ARGS. - -The last 2 members of ARGS are used as the final cons of the -result so if the final member of ARGS is not a list the result is -a dotted list." - (-reduce-r 'cons args)) - -(defun -snoc (list elem &rest elements) - "Append ELEM to the end of the list. - -This is like `cons', but operates on the end of list. - -If ELEMENTS is non nil, append these to the list as well." - (-concat list (list elem) elements)) - -(defmacro --first (form list) - "Anaphoric form of `-first'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each-while ,list (not ,n) - (when ,form (setq ,n it))) - ,n))) - -(defun -first (pred list) - "Return the first x in LIST where (PRED x) is non-nil, else nil. - -To get the first item in the list no questions asked, use `car'. - -Alias: `-find'" - (--first (funcall pred it) list)) - -(defalias '-find '-first) -(defalias '--find '--first) - -(defmacro --some (form list) - "Anaphoric form of `-some'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each-while ,list (not ,n) - (setq ,n ,form)) - ,n))) - -(defun -some (pred list) - "Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. - -Alias: `-any'" - (--some (funcall pred it) list)) - -(defalias '-any '-some) -(defalias '--any '--some) - -(defmacro --last (form list) - "Anaphoric form of `-last'." - (declare (debug (form form))) - (let ((n (make-symbol "needle"))) - `(let (,n) - (--each ,list - (when ,form (setq ,n it))) - ,n))) - -(defun -last (pred list) - "Return the last x in LIST where (PRED x) is non-nil, else nil." - (--last (funcall pred it) list)) - -(defalias '-first-item 'car - "Return the first item of LIST, or nil on an empty list.") - -(defun -last-item (list) - "Return the last item of LIST, or nil on an empty list." - (car (last list))) - -(defun -butlast (list) - "Return a list of all items in list except for the last." - (let (result) - (while (cdr list) - (!cons (car list) result) - (!cdr list)) - (nreverse result))) - -(defmacro --count (pred list) - "Anaphoric form of `-count'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let ((,r 0)) - (--each ,list (when ,pred (setq ,r (1+ ,r)))) - ,r))) - -(defun -count (pred list) - "Counts the number of items in LIST where (PRED item) is non-nil." - (--count (funcall pred it) list)) - -(defun ---truthy? (val) - (not (null val))) - -(defmacro --any? (form list) - "Anaphoric form of `-any?'." - (declare (debug (form form))) - `(---truthy? (--first ,form ,list))) - -(defun -any? (pred list) - "Return t if (PRED x) is non-nil for any x in LIST, else nil. - -Alias: `-any-p', `-some?', `-some-p'" - (--any? (funcall pred it) list)) - -(defalias '-some? '-any?) -(defalias '--some? '--any?) -(defalias '-any-p '-any?) -(defalias '--any-p '--any?) -(defalias '-some-p '-any?) -(defalias '--some-p '--any?) - -(defmacro --all? (form list) - "Anaphoric form of `-all?'." - (declare (debug (form form))) - (let ((a (make-symbol "all"))) - `(let ((,a t)) - (--each-while ,list ,a (setq ,a ,form)) - (---truthy? ,a)))) - -(defun -all? (pred list) - "Return t if (PRED x) is non-nil for all x in LIST, else nil. - -Alias: `-all-p', `-every?', `-every-p'" - (--all? (funcall pred it) list)) - -(defalias '-every? '-all?) -(defalias '--every? '--all?) -(defalias '-all-p '-all?) -(defalias '--all-p '--all?) -(defalias '-every-p '-all?) -(defalias '--every-p '--all?) - -(defmacro --none? (form list) - "Anaphoric form of `-none?'." - (declare (debug (form form))) - `(--all? (not ,form) ,list)) - -(defun -none? (pred list) - "Return t if (PRED x) is nil for all x in LIST, else nil. - -Alias: `-none-p'" - (--none? (funcall pred it) list)) - -(defalias '-none-p '-none?) -(defalias '--none-p '--none?) - -(defmacro --only-some? (form list) - "Anaphoric form of `-only-some?'." - (declare (debug (form form))) - (let ((y (make-symbol "yes")) - (n (make-symbol "no"))) - `(let (,y ,n) - (--each-while ,list (not (and ,y ,n)) - (if ,form (setq ,y t) (setq ,n t))) - (---truthy? (and ,y ,n))))) - -(defun -only-some? (pred list) - "Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. -Return `nil` both if all items match the predicate or if none of the items match the predicate. - -Alias: `-only-some-p'" - (--only-some? (funcall pred it) list)) - -(defalias '-only-some-p '-only-some?) -(defalias '--only-some-p '--only-some?) - -(defun -slice (list from &optional to step) - "Return copy of LIST, starting from index FROM to index TO. - -FROM or TO may be negative. These values are then interpreted -modulo the length of the list. - -If STEP is a number, only each STEPth item in the resulting -section is returned. Defaults to 1." - (let ((length (length list)) - (new-list nil)) - ;; to defaults to the end of the list - (setq to (or to length)) - (setq step (or step 1)) - ;; handle negative indices - (when (< from 0) - (setq from (mod from length))) - (when (< to 0) - (setq to (mod to length))) - - ;; iterate through the list, keeping the elements we want - (--each-while list (< it-index to) - (when (and (>= it-index from) - (= (mod (- from it-index) step) 0)) - (push it new-list))) - (nreverse new-list))) - -(defun -take (n list) - "Return a new list of the first N items in LIST, or all items if there are fewer than N." - (let (result) - (--dotimes n - (when list - (!cons (car list) result) - (!cdr list))) - (nreverse result))) - -(defalias '-drop 'nthcdr "Return the tail of LIST without the first N items.") - -(defmacro --take-while (form list) - "Anaphoric form of `-take-while'." - (declare (debug (form form))) - (let ((r (make-symbol "result"))) - `(let (,r) - (--each-while ,list ,form (!cons it ,r)) - (nreverse ,r)))) - -(defun -take-while (pred list) - "Return a new list of successive items from LIST while (PRED item) returns a non-nil value." - (--take-while (funcall pred it) list)) - -(defmacro --drop-while (form list) - "Anaphoric form of `-drop-while'." - (declare (debug (form form))) - (let ((l (make-symbol "list"))) - `(let ((,l ,list)) - (while (and ,l (let ((it (car ,l))) ,form)) - (!cdr ,l)) - ,l))) - -(defun -drop-while (pred list) - "Return the tail of LIST starting from the first item for which (PRED item) returns nil." - (--drop-while (funcall pred it) list)) - -(defun -split-at (n list) - "Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list." - (let (result) - (--dotimes n - (when list - (!cons (car list) result) - (!cdr list))) - (list (nreverse result) list))) - -(defun -rotate (n list) - "Rotate LIST N places to the right. With N negative, rotate to the left. -The time complexity is O(n)." - (if (> n 0) - (append (last list n) (butlast list n)) - (append (-drop (- n) list) (-take (- n) list)))) - -(defun -insert-at (n x list) - "Return a list with X inserted into LIST at position N. - -See also: `-splice', `-splice-list'" - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons x (cadr split-list))))) - -(defun -replace-at (n x list) - "Return a list with element at Nth position in LIST replaced with X. - -See also: `-replace'" - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons x (cdr (cadr split-list)))))) - -(defun -update-at (n func list) - "Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. - -See also: `-map-when'" - (let ((split-list (-split-at n list))) - (nconc (car split-list) (cons (funcall func (car (cadr split-list))) (cdr (cadr split-list)))))) - -(defmacro --update-at (n form list) - "Anaphoric version of `-update-at'." - (declare (debug (form form form))) - `(-update-at ,n (lambda (it) ,form) ,list)) - -(defun -remove-at (n list) - "Return a list with element at Nth position in LIST removed. - -See also: `-remove-at-indices', `-remove'" - (-remove-at-indices (list n) list)) - -(defun -remove-at-indices (indices list) - "Return a list whose elements are elements from LIST without -elements selected as `(nth i list)` for all i -from INDICES. - -See also: `-remove-at', `-remove'" - (let* ((indices (-sort '< indices)) - (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) - r) - (--each diffs - (let ((split (-split-at it list))) - (!cons (car split) r) - (setq list (cdr (cadr split))))) - (!cons list r) - (apply '-concat (nreverse r)))) - -(defmacro --split-with (pred list) - "Anaphoric form of `-split-with'." - (declare (debug (form form))) - (let ((l (make-symbol "list")) - (r (make-symbol "result")) - (c (make-symbol "continue"))) - `(let ((,l ,list) - (,r nil) - (,c t)) - (while (and ,l ,c) - (let ((it (car ,l))) - (if (not ,pred) - (setq ,c nil) - (!cons it ,r) - (!cdr ,l)))) - (list (nreverse ,r) ,l)))) - -(defun -split-with (pred list) - "Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list." - (--split-with (funcall pred it) list)) - -(defmacro -split-on (item list) - "Split the LIST each time ITEM is found. - -Unlike `-partition-by', the ITEM is discarded from the results. -Empty lists are also removed from the result. - -Comparison is done by `equal'. - -See also `-split-when'" - (declare (debug (form form))) - `(-split-when (lambda (it) (equal it ,item)) ,list)) - -(defmacro --split-when (form list) - "Anaphoric version of `-split-when'." - (declare (debug (form form))) - `(-split-when (lambda (it) ,form) ,list)) - -(defun -split-when (fn list) - "Split the LIST on each element where FN returns non-nil. - -Unlike `-partition-by', the \"matched\" element is discarded from -the results. Empty lists are also removed from the result. - -This function can be thought of as a generalization of -`split-string'." - (let (r s) - (while list - (if (not (funcall fn (car list))) - (push (car list) s) - (when s (push (nreverse s) r)) - (setq s nil)) - (!cdr list)) - (when s (push (nreverse s) r)) - (nreverse r))) - -(defmacro --separate (form list) - "Anaphoric form of `-separate'." - (declare (debug (form form))) - (let ((y (make-symbol "yes")) - (n (make-symbol "no"))) - `(let (,y ,n) - (--each ,list (if ,form (!cons it ,y) (!cons it ,n))) - (list (nreverse ,y) (nreverse ,n))))) - -(defun -separate (pred list) - "Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list." - (--separate (funcall pred it) list)) - -(defun ---partition-all-in-steps-reversed (n step list) - "Private: Used by -partition-all-in-steps and -partition-in-steps." - (when (< step 1) - (error "Step must be a positive number, or you're looking at some juicy infinite loops.")) - (let ((result nil)) - (while list - (!cons (-take n list) result) - (setq list (-drop step list))) - result)) - -(defun -partition-all-in-steps (n step list) - "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. -The last groups may contain less than N items." - (nreverse (---partition-all-in-steps-reversed n step list))) - -(defun -partition-in-steps (n step list) - "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. -If there are not enough items to make the last group N-sized, -those items are discarded." - (let ((result (---partition-all-in-steps-reversed n step list))) - (while (and result (< (length (car result)) n)) - (!cdr result)) - (nreverse result))) - -(defun -partition-all (n list) - "Return a new list with the items in LIST grouped into N-sized sublists. -The last group may contain less than N items." - (-partition-all-in-steps n n list)) - -(defun -partition (n list) - "Return a new list with the items in LIST grouped into N-sized sublists. -If there are not enough items to make the last group N-sized, -those items are discarded." - (-partition-in-steps n n list)) - -(defmacro --partition-by (form list) - "Anaphoric form of `-partition-by'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (s (make-symbol "sublist")) - (v (make-symbol "value")) - (n (make-symbol "new-value")) - (l (make-symbol "list"))) - `(let ((,l ,list)) - (when ,l - (let* ((,r nil) - (it (car ,l)) - (,s (list it)) - (,v ,form) - (,l (cdr ,l))) - (while ,l - (let* ((it (car ,l)) - (,n ,form)) - (unless (equal ,v ,n) - (!cons (nreverse ,s) ,r) - (setq ,s nil) - (setq ,v ,n)) - (!cons it ,s) - (!cdr ,l))) - (!cons (nreverse ,s) ,r) - (nreverse ,r)))))) - -(defun -partition-by (fn list) - "Apply FN to each item in LIST, splitting it each time FN returns a new value." - (--partition-by (funcall fn it) list)) - -(defmacro --partition-by-header (form list) - "Anaphoric form of `-partition-by-header'." - (declare (debug (form form))) - (let ((r (make-symbol "result")) - (s (make-symbol "sublist")) - (h (make-symbol "header-value")) - (b (make-symbol "seen-body?")) - (n (make-symbol "new-value")) - (l (make-symbol "list"))) - `(let ((,l ,list)) - (when ,l - (let* ((,r nil) - (it (car ,l)) - (,s (list it)) - (,h ,form) - (,b nil) - (,l (cdr ,l))) - (while ,l - (let* ((it (car ,l)) - (,n ,form)) - (if (equal ,h ,n) - (when ,b - (!cons (nreverse ,s) ,r) - (setq ,s nil) - (setq ,b nil)) - (setq ,b t)) - (!cons it ,s) - (!cdr ,l))) - (!cons (nreverse ,s) ,r) - (nreverse ,r)))))) - -(defun -partition-by-header (fn list) - "Apply FN to the first item in LIST. That is the header -value. Apply FN to each item in LIST, splitting it each time FN -returns the header value, but only after seeing at least one -other value (the body)." - (--partition-by-header (funcall fn it) list)) - -(defmacro --group-by (form list) - "Anaphoric form of `-group-by'." - (declare (debug t)) - (let ((n (make-symbol "n")) - (k (make-symbol "k")) - (grp (make-symbol "grp"))) - `(nreverse - (-map - (lambda (,n) - (cons (car ,n) - (nreverse (cdr ,n)))) - (--reduce-from - (let* ((,k (,@form)) - (,grp (assoc ,k acc))) - (if ,grp - (setcdr ,grp (cons it (cdr ,grp))) - (push - (list ,k it) - acc)) - acc) - nil ,list))))) - -(defun -group-by (fn list) - "Separate LIST into an alist whose keys are FN applied to the -elements of LIST. Keys are compared by `equal'." - (--group-by (funcall fn it) list)) - -(defun -interpose (sep list) - "Return a new list of all elements in LIST separated by SEP." - (let (result) - (when list - (!cons (car list) result) - (!cdr list)) - (while list - (setq result (cons (car list) (cons sep result))) - (!cdr list)) - (nreverse result))) - -(defun -interleave (&rest lists) - "Return a new list of the first item in each list, then the second etc." - (let (result) - (while (-none? 'null lists) - (--each lists (!cons (car it) result)) - (setq lists (-map 'cdr lists))) - (nreverse result))) - -(defmacro --zip-with (form list1 list2) - "Anaphoric form of `-zip-with'. - -The elements in list1 is bound as `it`, the elements in list2 as `other`." - (declare (debug (form form form))) - (let ((r (make-symbol "result")) - (l1 (make-symbol "list1")) - (l2 (make-symbol "list2"))) - `(let ((,r nil) - (,l1 ,list1) - (,l2 ,list2)) - (while (and ,l1 ,l2) - (let ((it (car ,l1)) - (other (car ,l2))) - (!cons ,form ,r) - (!cdr ,l1) - (!cdr ,l2))) - (nreverse ,r)))) - -(defun -zip-with (fn list1 list2) - "Zip the two lists LIST1 and LIST2 using a function FN. This -function is applied pairwise taking as first argument element of -LIST1 and as second argument element of LIST2 at corresponding -position. - -The anaphoric form `--zip-with' binds the elements from LIST1 as `it`, -and the elements from LIST2 as `other`." - (--zip-with (funcall fn it other) list1 list2)) - -(defun -zip (&rest lists) - "Zip LISTS together. Group the head of each list, followed by the -second elements of each list, and so on. The lengths of the returned -groupings are equal to the length of the shortest input list. - -If two lists are provided as arguments, return the groupings as a list -of cons cells. Otherwise, return the groupings as a list of lists. - -Please note! This distinction is being removed in an upcoming 2.0 -release of Dash. If you rely on this behavior, use -zip-pair instead." - (let (results) - (while (-none? 'null lists) - (setq results (cons (mapcar 'car lists) results)) - (setq lists (mapcar 'cdr lists))) - (setq results (nreverse results)) - (if (= (length lists) 2) - ;; to support backward compatability, return - ;; a cons cell if two lists were provided - (--map (cons (car it) (cadr it)) results) - results))) - -(defalias '-zip-pair '-zip) - -(defun -zip-fill (fill-value &rest lists) - "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The -lengths of the returned groupings are equal to the length of the -longest input list." - (apply '-zip (apply '-pad (cons fill-value lists)))) - -(defun -cycle (list) - "Return an infinite copy of LIST that will cycle through the -elements and repeat from the beginning." - (let ((newlist (-map 'identity list))) - (nconc newlist newlist))) - -(defun -pad (fill-value &rest lists) - "Appends FILL-VALUE to the end of each list in LISTS such that they -will all have the same length." - (let* ((annotations (-annotate 'length lists)) - (n (-max (-map 'car annotations)))) - (--map (append (cdr it) (-repeat (- n (car it)) fill-value)) annotations))) - -(defun -annotate (fn list) - "Return a list of cons cells where each cell is FN applied to each -element of LIST paired with the unmodified element of LIST." - (-zip (-map fn list) list)) - -(defmacro --annotate (form list) - "Anaphoric version of `-annotate'." - (declare (debug (form form))) - `(-annotate (lambda (it) ,form) ,list)) - -(defun dash--table-carry (lists restore-lists &optional re) - "Helper for `-table' and `-table-flat'. - -If a list overflows, carry to the right and reset the list." - (while (not (or (car lists) - (equal lists '(nil)))) - (setcar lists (car restore-lists)) - (pop (cadr lists)) - (!cdr lists) - (!cdr restore-lists) - (when re - (push (nreverse (car re)) (cadr re)) - (setcar re nil) - (!cdr re)))) - -(defun -table (fn &rest lists) - "Compute outer product of LISTS using function FN. - -The function FN should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The dimension of the result is (length lists). - -See also: `-table-flat'" - (let ((restore-lists (copy-sequence lists)) - (last-list (last lists)) - (re (make-list (length lists) nil))) - (while (car last-list) - (let ((item (apply fn (-map 'car lists)))) - (push item (car re)) - (setcar lists (cdar lists)) ;; silence byte compiler - (dash--table-carry lists restore-lists re))) - (nreverse (car (last re))))) - -(defun -table-flat (fn &rest lists) - "Compute flat outer product of LISTS using function FN. - -The function FN should have the same arity as the number of -supplied lists. - -The outer product is computed by applying fn to all possible -combinations created by taking one element from each list in -order. The results are flattened, ignoring the tensor structure -of the result. This is equivalent to calling: - - (-flatten-n (1- (length lists)) (-table fn lists)) - -but the implementation here is much more efficient. - -See also: `-flatten-n', `-table'" - (when lists ;Just in case. - (let* ((list1 (pop lists)) - (restore-lists (copy-sequence lists)) - (last-list (last lists)) - re) - (while (car last-list) - (let ((tail (-map #'car lists))) - (dolist (head list1) - (push (apply fn head tail) re))) - (pop (car lists)) - (dash--table-carry lists restore-lists)) - (nreverse re)))) - -(defun -partial (fn &rest args) - "Take a function FN and fewer than the normal arguments to FN, -and return a fn that takes a variable number of additional ARGS. -When called, the returned function calls FN with ARGS first and -then additional args." - (apply 'apply-partially fn args)) - -(defun -elem-index (elem list) - "Return the index of the first element in the given LIST which -is equal to the query element ELEM, or nil if there is no -such element." - (car (-elem-indices elem list))) - -(defun -elem-indices (elem list) - "Return the indices of all elements in LIST equal to the query -element ELEM, in ascending order." - (-find-indices (-partial 'equal elem) list)) - -(defun -find-indices (pred list) - "Return the indices of all elements in LIST satisfying the -predicate PRED, in ascending order." - (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list))) - -(defmacro --find-indices (form list) - "Anaphoric version of `-find-indices'." - (declare (debug (form form))) - `(-find-indices (lambda (it) ,form) ,list)) - -(defun -find-index (pred list) - "Take a predicate PRED and a LIST and return the index of the -first element in the list satisfying the predicate, or nil if -there is no such element." - (car (-find-indices pred list))) - -(defmacro --find-index (form list) - "Anaphoric version of `-find-index'." - (declare (debug (form form))) - `(-find-index (lambda (it) ,form) ,list)) - -(defun -find-last-index (pred list) - "Take a predicate PRED and a LIST and return the index of the -last element in the list satisfying the predicate, or nil if -there is no such element." - (-last-item (-find-indices pred list))) - -(defmacro --find-last-index (form list) - "Anaphoric version of `-find-last-index'." - `(-find-last-index (lambda (it) ,form) ,list)) - -(defun -select-by-indices (indices list) - "Return a list whose elements are elements from LIST selected -as `(nth i list)` for all i from INDICES." - (let (r) - (--each indices - (!cons (nth it list) r)) - (nreverse r))) - -(defmacro -> (x &optional form &rest more) - "Thread the expr through the forms. Insert X as the second item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -second item in second form, etc." - (cond - ((null form) x) - ((null more) (if (listp form) - `(,(car form) ,x ,@(cdr form)) - (list form x))) - (:else `(-> (-> ,x ,form) ,@more)))) - -(defmacro ->> (x &optional form &rest more) - "Thread the expr through the forms. Insert X as the last item -in the first form, making a list of it if it is not a list -already. If there are more forms, insert the first form as the -last item in second form, etc." - (cond - ((null form) x) - ((null more) (if (listp form) - `(,@form ,x) - (list form x))) - (:else `(->> (->> ,x ,form) ,@more)))) - -(defmacro --> (x form &rest more) - "Thread the expr through the forms. Insert X at the position -signified by the token `it' in the first form. If there are more -forms, insert the first form at the position signified by `it' in -in second form, etc." - (if (null more) - (if (listp form) - (--map-when (eq it 'it) x form) - (list form x)) - `(--> (--> ,x ,form) ,@more))) - -(defmacro -some-> (x &optional form &rest more) - "When expr is non-nil, thread it through the first form (via `->'), -and when that result is non-nil, through the next form, etc." - (if (null form) x - (let ((result (make-symbol "result"))) - `(-some-> (-when-let (,result ,x) - (-> ,result ,form)) - ,@more)))) - -(defmacro -some->> (x &optional form &rest more) - "When expr is non-nil, thread it through the first form (via `->>'), -and when that result is non-nil, through the next form, etc." - (if (null form) x - (let ((result (make-symbol "result"))) - `(-some->> (-when-let (,result ,x) - (->> ,result ,form)) - ,@more)))) - -(defmacro -some--> (x &optional form &rest more) - "When expr in non-nil, thread it through the first form (via `-->'), -and when that result is non-nil, through the next form, etc." - (if (null form) x - (let ((result (make-symbol "result"))) - `(-some--> (-when-let (,result ,x) - (--> ,result ,form)) - ,@more)))) - -(defun -grade-up (comparator list) - "Grade elements of LIST using COMPARATOR relation, yielding a -permutation vector such that applying this permutation to LIST -sorts it in ascending order." - ;; ugly hack to "fix" lack of lexical scope - (let ((comp `(lambda (it other) (funcall ',comparator (car it) (car other))))) - (->> (--map-indexed (cons it it-index) list) - (-sort comp) - (-map 'cdr)))) - -(defun -grade-down (comparator list) - "Grade elements of LIST using COMPARATOR relation, yielding a -permutation vector such that applying this permutation to LIST -sorts it in descending order." - ;; ugly hack to "fix" lack of lexical scope - (let ((comp `(lambda (it other) (funcall ',comparator (car other) (car it))))) - (->> (--map-indexed (cons it it-index) list) - (-sort comp) - (-map 'cdr)))) - -(defvar dash--source-counter 0 - "Monotonic counter for generated symbols.") - -(defun dash--match-make-source-symbol () - "Generate a new dash-source symbol. - -All returned symbols are guaranteed to be unique." - (prog1 (make-symbol (format "--dash-source-%d--" dash--source-counter)) - (setq dash--source-counter (1+ dash--source-counter)))) - -(defun dash--match-ignore-place-p (symbol) - "Return non-nil if SYMBOL is a symbol and starts with _." - (and (symbolp symbol) - (eq (aref (symbol-name symbol) 0) ?_))) - -(defun dash--match-cons-skip-cdr (skip-cdr source) - "Helper function generating idiomatic shifting code." - (cond - ((= skip-cdr 0) - `(pop ,source)) - (t - `(prog1 ,(dash--match-cons-get-car skip-cdr source) - (setq ,source ,(dash--match-cons-get-cdr (1+ skip-cdr) source)))))) - -(defun dash--match-cons-get-car (skip-cdr source) - "Helper function generating idiomatic code to get nth car." - (cond - ((= skip-cdr 0) - `(car ,source)) - ((= skip-cdr 1) - `(cadr ,source)) - (t - `(nth ,skip-cdr ,source)))) - -(defun dash--match-cons-get-cdr (skip-cdr source) - "Helper function generating idiomatic code to get nth cdr." - (cond - ((= skip-cdr 0) - source) - ((= skip-cdr 1) - `(cdr ,source)) - (t - `(nthcdr ,skip-cdr ,source)))) - -(defun dash--match-cons (match-form source) - "Setup a cons matching environment and call the real matcher." - (let ((s (dash--match-make-source-symbol)) - (n 0) - (m match-form)) - (while (and (consp m) - (dash--match-ignore-place-p (car m))) - (setq n (1+ n)) (!cdr m)) - (cond - ;; when we only have one pattern in the list, we don't have to - ;; create a temporary binding (--dash-source--) for the source - ;; and just use the input directly - ((and (consp m) - (not (cdr m))) - (dash--match (car m) (dash--match-cons-get-car n source))) - ;; handle other special types - ((> n 0) - (dash--match m (dash--match-cons-get-cdr n source))) - ;; this is the only entry-point for dash--match-cons-1, that's - ;; why we can't simply use the above branch, it would produce - ;; infinite recursion - (t - (cons (list s source) (dash--match-cons-1 match-form s)))))) - -(defun dash--match-cons-1 (match-form source &optional props) - "Match MATCH-FORM against SOURCE. - -MATCH-FORM is a proper or improper list. Each element of -MATCH-FORM is either a symbol, which gets bound to the respective -value in source or another match form which gets destructured -recursively. - -If the cdr of last cons cell in the list is `nil', matching stops -there. - -SOURCE is a proper or improper list." - (let ((skip-cdr (or (plist-get props :skip-cdr) 0))) - (cond - ((consp match-form) - (cond - ((cdr match-form) - (cond - ((and (symbolp (car match-form)) - (memq (car match-form) '(&keys &plist &alist &hash))) - (dash--match-kv match-form (dash--match-cons-get-cdr skip-cdr source))) - ((dash--match-ignore-place-p (car match-form)) - (dash--match-cons-1 (cdr match-form) source - (plist-put props :skip-cdr (1+ skip-cdr)))) - (t - (-concat (dash--match (car match-form) (dash--match-cons-skip-cdr skip-cdr source)) - (dash--match-cons-1 (cdr match-form) source))))) - (t ;; Last matching place, no need for shift - (dash--match (car match-form) (dash--match-cons-get-car skip-cdr source))))) - ((eq match-form nil) - nil) - (t ;; Handle improper lists. Last matching place, no need for shift - (dash--match match-form (dash--match-cons-get-cdr skip-cdr source)))))) - -(defun dash--vector-tail (seq start) - "Return the tail of SEQ starting at START." - (cond - ((vectorp seq) - (let* ((re-length (- (length seq) start)) - (re (make-vector re-length 0))) - (--dotimes re-length (aset re it (aref seq (+ it start)))) - re)) - ((stringp seq) - (substring seq start)))) - -(defun dash--match-vector (match-form source) - "Setup a vector matching environment and call the real matcher." - (let ((s (dash--match-make-source-symbol))) - (cond - ;; don't bind `s' if we only have one sub-pattern - ((= (length match-form) 1) - (dash--match (aref match-form 0) `(aref ,source 0))) - ;; if the source is a symbol, we don't need to re-bind it - ((symbolp source) - (dash--match-vector-1 match-form source)) - ;; don't bind `s' if we only have one sub-pattern which is not ignored - ((let* ((ignored-places (mapcar 'dash--match-ignore-place-p match-form)) - (ignored-places-n (length (-remove 'null ignored-places)))) - (when (= ignored-places-n (1- (length match-form))) - (let ((n (-find-index 'null ignored-places))) - (dash--match (aref match-form n) `(aref ,source ,n)))))) - (t - (cons (list s source) (dash--match-vector-1 match-form s)))))) - -(defun dash--match-vector-1 (match-form source) - "Match MATCH-FORM against SOURCE. - -MATCH-FORM is a vector. Each element of MATCH-FORM is either a -symbol, which gets bound to the respective value in source or -another match form which gets destructured recursively. - -If second-from-last place in MATCH-FORM is the symbol &rest, the -next element of the MATCH-FORM is matched against the tail of -SOURCE, starting at index of the &rest symbol. This is -conceptually the same as the (head . tail) match for improper -lists, where dot plays the role of &rest. - -SOURCE is a vector. - -If the MATCH-FORM vector is shorter than SOURCE vector, only -the (length MATCH-FORM) places are bound, the rest of the SOURCE -is discarded." - (let ((i 0) - (l (length match-form)) - (re)) - (while (< i l) - (let ((m (aref match-form i))) - (push (cond - ((and (symbolp m) - (eq m '&rest)) - (prog1 (dash--match - (aref match-form (1+ i)) - `(dash--vector-tail ,source ,i)) - (setq i l))) - ((and (symbolp m) - ;; do not match symbols starting with _ - (not (eq (aref (symbol-name m) 0) ?_))) - (list (list m `(aref ,source ,i)))) - ((not (symbolp m)) - (dash--match m `(aref ,source ,i)))) - re) - (setq i (1+ i)))) - (-flatten-n 1 (nreverse re)))) - -(defun dash--match-kv (match-form source) - "Setup a kv matching environment and call the real matcher. - -kv can be any key-value store, such as plist, alist or hash-table." - (let ((s (dash--match-make-source-symbol))) - (cond - ;; don't bind `s' if we only have one sub-pattern (&type key val) - ((= (length match-form) 3) - (dash--match-kv-1 (cdr match-form) source (car match-form))) - ;; if the source is a symbol, we don't need to re-bind it - ((symbolp source) - (dash--match-kv-1 (cdr match-form) source (car match-form))) - (t - (cons (list s source) (dash--match-kv-1 (cdr match-form) s (car match-form))))))) - -(defun dash--match-kv-1 (match-form source type) - "Match MATCH-FORM against SOURCE of type TYPE. - -MATCH-FORM is a proper list of the form (key1 place1 ... keyN -placeN). Each placeK is either a symbol, which gets bound to the -value of keyK retrieved from the key-value store, or another -match form which gets destructured recursively. - -SOURCE is a key-value store of type TYPE, which can be a plist, -an alist or a hash table. - -TYPE is a token specifying the type of the key-value store. -Valid values are &plist, &alist and &hash." - (-flatten-n 1 (-map - (lambda (kv) - (let* ((k (car kv)) - (v (cadr kv)) - (getter (cond - ((or (eq type '&plist) (eq type '&keys)) - `(plist-get ,source ,k)) - ((eq type '&alist) - `(cdr (assoc ,k ,source))) - ((eq type '&hash) - `(gethash ,k ,source))))) - (cond - ((symbolp v) - (list (list v getter))) - (t (dash--match v getter))))) - (-partition 2 match-form)))) - -(defun dash--match-symbol (match-form source) - "Bind a symbol. - -This works just like `let', there is no destructuring." - (list (list match-form source))) - -(defun dash--match (match-form source) - "Match MATCH-FORM against SOURCE. - -This function tests the MATCH-FORM and dispatches to specific -matchers based on the type of the expression. - -Key-value stores are disambiguated by placing a token &plist, -&alist or &hash as a first item in the MATCH-FORM." - (cond - ((symbolp match-form) - (dash--match-symbol match-form source)) - ((consp match-form) - (cond - ;; Handle the "x &as" bindings first. - ((and (consp (cdr match-form)) - (symbolp (car match-form)) - (eq '&as (cadr match-form))) - (let ((s (car match-form))) - (cons (list s source) - (dash--match (cddr match-form) s)))) - ((memq (car match-form) '(&keys &plist &alist &hash)) - (dash--match-kv match-form source)) - (t (dash--match-cons match-form source)))) - ((vectorp match-form) - ;; We support the &as binding in vectors too - (cond - ((and (> (length match-form) 2) - (symbolp (aref match-form 0)) - (eq '&as (aref match-form 1))) - (let ((s (aref match-form 0))) - (cons (list s source) - (dash--match (dash--vector-tail match-form 2) s)))) - (t (dash--match-vector match-form source)))))) - -(defmacro -let* (varlist &rest body) - "Bind variables according to VARLIST then eval BODY. - -VARLIST is a list of lists of the form (PATTERN SOURCE). Each -PATTERN is matched against the SOURCE structurally. SOURCE is -only evaluated once for each PATTERN. - -Each SOURCE can refer to the symbols already bound by this -VARLIST. This is useful if you want to destructure SOURCE -recursively but also want to name the intermediate structures. - -See `-let' for the list of all possible patterns." - (declare (debug ((&rest (sexp form)) body)) - (indent 1)) - (let ((bindings (--mapcat (dash--match (car it) (cadr it)) varlist))) - `(let* ,bindings - ,@body))) - -(defmacro -let (varlist &rest body) - "Bind variables according to VARLIST then eval BODY. - -VARLIST is a list of lists of the form (PATTERN SOURCE). Each -PATTERN is matched against the SOURCE \"structurally\". SOURCE -is only evaluated once for each PATTERN. Each PATTERN is matched -recursively, and can therefore contain sub-patterns which are -matched against corresponding sub-expressions of SOURCE. - -All the SOURCEs are evalled before any symbols are -bound (i.e. \"in parallel\"). - -If VARLIST only contains one (PATTERN SOURCE) element, you can -optionally specify it using a vector and discarding the -outer-most parens. Thus - - (-let ((PATTERN SOURCE)) ..) - -becomes - - (-let [PATTERN SOURCE] ..). - -`-let' uses a convention of not binding places (symbols) starting -with _ whenever it's possible. You can use this to skip over -entries you don't care about. However, this is not *always* -possible (as a result of implementation) and these symbols might -get bound to undefined values. - -Following is the overview of supported patterns. Remember that -patterns can be matched recursively, so every a, b, aK in the -following can be a matching construct and not necessarily a -symbol/variable. - -Symbol: - - a - bind the SOURCE to A. This is just like regular `let'. - -Conses and lists: - - (a) - bind `car' of cons/list to A - - (a . b) - bind car of cons to A and `cdr' to B - - (a b) - bind car of list to A and `cadr' to B - - (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3 ... - - (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. - -Vectors: - - [a] - bind 0th element of a non-list sequence to A (works with - vectors, strings, bit arrays...) - - [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to - A1, 2nd to A2, ... - If the PATTERN is shorter than SOURCE, the values at - places not in PATTERN are ignored. - If the PATTERN is longer than SOURCE, an `error' is - thrown. - - [a1 a2 a3 ... &rest rest] - as above, but bind the rest of - the sequence to REST. This is - conceptually the same as improper list - matching (a1 a2 ... aN . rest) - -Key/value stores: - - (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE plist to aK. If the - value is not found, aK is nil. - - (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE alist to aK. If the - value is not found, aK is nil. - - (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the - SOURCE hash table to aK. If the - value is not found, aK is nil. - -Further, special keyword &keys supports \"inline\" matching of -plist-like key-value pairs, similarly to &keys keyword of -`cl-defun'. - - (a1 a2 ... aN &keys key1 b1 ... keyN bK) - -This binds N values from the list to a1 ... aN, then interprets -the cdr as a plist (see key/value matching above). - -You can name the source using the syntax SYMBOL &as PATTERN. -This syntax works with lists (proper or improper), vectors and -all types of maps. - - (list &as a b c) (list 1 2 3) - -binds A to 1, B to 2, C to 3 and LIST to (1 2 3). - -Similarly: - - (bounds &as beg . end) (cons 1 2) - -binds BEG to 1, END to 2 and BOUNDS to (1 . 2). - - (items &as first . rest) (list 1 2 3) - -binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) - - [vect &as _ b c] [1 2 3] - -binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). - - (plist &as &plist :b b) (list :a 1 :b 2 :c 3) - -binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. - -This is especially useful when we want to capture the result of a -computation and destructure at the same time. Consider the -form (function-returning-complex-structure) returning a list of -two vectors with two items each. We want to capture this entire -result and pass it to another computation, but at the same time -we want to get the second item from each vector. We can achieve -it with pattern - - (result &as [_ a] [_ b]) (function-returning-complex-structure) - -Note: Clojure programmers may know this feature as the \":as -binding\". The difference is that we put the &as at the front -because we need to support improper list binding." - (declare (debug ([&or (&rest (sexp form)) - (vector [&rest [sexp form]])] - body)) - (indent 1)) - (if (vectorp varlist) - `(let* ,(dash--match (aref varlist 0) (aref varlist 1)) - ,@body) - (let* ((inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) - (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) - `(let ,inputs - (-let* ,new-varlist ,@body))))) - -(defmacro -lambda (match-form &rest body) - "Return a lambda which destructures its input as MATCH-FORM and executes BODY. - -Note that you have to enclose the MATCH-FORM in a pair of parens, -such that: - - (-lambda (x) body) - (-lambda (x y ...) body) - -has the usual semantics of `lambda'. Furthermore, these get -translated into normal lambda, so there is no performance -penalty. - -See `-let' for the description of destructuring mechanism." - (declare (doc-string 2) (indent defun) - (debug (&define sexp - [&optional stringp] - [&optional ("interactive" interactive)] - def-body))) - (cond - ((not (consp match-form)) - (signal 'wrong-type-argument "match-form must be a list")) - ;; no destructuring, so just return regular lambda to make things faster - ((-all? 'symbolp match-form) - `(lambda ,match-form ,@body)) - (t - (let* ((inputs (--map-indexed (list it (make-symbol (format "input%d" it-index))) match-form))) - ;; TODO: because inputs to the lambda are evaluated only once, - ;; -let* need not to create the extra bindings to ensure that. - ;; We should find a way to optimize that. Not critical however. - `(lambda ,(--map (cadr it) inputs) - (-let* ,inputs ,@body)))))) - -(defmacro -if-let* (vars-vals then &rest else) - "If all VALS evaluate to true, bind them to their corresponding -VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list -of (VAR VAL) pairs. - -Note: binding is done according to `-let*'. VALS are evaluated -sequentially, and evaluation stops after the first nil VAL is -encountered." - (declare (debug ((&rest (sexp form)) form body)) - (indent 2)) - (->> vars-vals - (--mapcat (dash--match (car it) (cadr it))) - (--reduce-r-from - (let ((var (car it)) - (val (cadr it))) - `(let ((,var ,val)) - (if ,var ,acc ,@else))) - then))) - -(defmacro -if-let (var-val then &rest else) - "If VAL evaluates to non-nil, bind it to VAR and do THEN, -otherwise do ELSE. VAR-VAL should be a (VAR VAL) pair. - -Note: binding is done according to `-let'." - (declare (debug ((sexp form) form body)) - (indent 2)) - `(-if-let* (,var-val) ,then ,@else)) - -(defmacro --if-let (val then &rest else) - "If VAL evaluates to non-nil, bind it to `it' and do THEN, -otherwise do ELSE." - (declare (debug (form form body)) - (indent 2)) - `(-if-let (it ,val) ,then ,@else)) - -(defmacro -when-let* (vars-vals &rest body) - "If all VALS evaluate to true, bind them to their corresponding -VARS and execute body. VARS-VALS should be a list of (VAR VAL) -pairs. - -Note: binding is done according to `-let*'. VALS are evaluated -sequentially, and evaluation stops after the first nil VAL is -encountered." - (declare (debug ((&rest (sexp form)) body)) - (indent 1)) - `(-if-let* ,vars-vals (progn ,@body))) - -(defmacro -when-let (var-val &rest body) - "If VAL evaluates to non-nil, bind it to VAR and execute body. -VAR-VAL should be a (VAR VAL) pair. - -Note: binding is done according to `-let'." - (declare (debug ((sexp form) body)) - (indent 1)) - `(-if-let ,var-val (progn ,@body))) - -(defmacro --when-let (val &rest body) - "If VAL evaluates to non-nil, bind it to `it' and execute -body." - (declare (debug (form body)) - (indent 1)) - `(--if-let ,val (progn ,@body))) - -(defvar -compare-fn nil - "Tests for equality use this function or `equal' if this is nil. -It should only be set using dynamic scope with a let, like: - - (let ((-compare-fn #'=)) (-union numbers1 numbers2 numbers3)") - -(defun -distinct (list) - "Return a new list with all duplicates removed. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil. - -Alias: `-uniq'" - (let (result) - (--each list (unless (-contains? result it) (!cons it result))) - (nreverse result))) - -(defalias '-uniq '-distinct) - -(defun -union (list list2) - "Return a new list containing the elements of LIST1 and elements of LIST2 that are not in LIST1. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - ;; We fall back to iteration implementation if the comparison - ;; function isn't one of `eq', `eql' or `equal'. - (let* ((result (reverse list)) - ;; TODO: get rid of this dynamic variable, pass it as an - ;; argument instead. - (-compare-fn (if (bound-and-true-p -compare-fn) - -compare-fn - 'equal))) - (if (memq -compare-fn '(eq eql equal)) - (let ((ht (make-hash-table :test -compare-fn))) - (--each list (puthash it t ht)) - (--each list2 (unless (gethash it ht) (!cons it result)))) - (--each list2 (unless (-contains? result it) (!cons it result)))) - (nreverse result))) - -(defun -intersection (list list2) - "Return a new list containing only the elements that are members of both LIST and LIST2. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - (--filter (-contains? list2 it) list)) - -(defun -difference (list list2) - "Return a new list with only the members of LIST that are not in LIST2. -The test for equality is done with `equal', -or with `-compare-fn' if that's non-nil." - (--filter (not (-contains? list2 it)) list)) - -(defun -contains? (list element) - "Return non-nil if LIST contains ELEMENT. - -The test for equality is done with `equal', or with `-compare-fn' -if that's non-nil. - -Alias: `-contains-p'" - (not - (null - (cond - ((null -compare-fn) (member element list)) - ((eq -compare-fn 'eq) (memq element list)) - ((eq -compare-fn 'eql) (memql element list)) - (t - (let ((lst list)) - (while (and lst - (not (funcall -compare-fn element (car lst)))) - (setq lst (cdr lst))) - lst)))))) - -(defalias '-contains-p '-contains?) - -(defun -same-items? (list list2) - "Return true if LIST and LIST2 has the same items. - -The order of the elements in the lists does not matter. - -Alias: `-same-items-p'" - (let ((length-a (length list)) - (length-b (length list2))) - (and - (= length-a length-b) - (= length-a (length (-intersection list list2)))))) - -(defalias '-same-items-p '-same-items?) - -(defun -is-prefix? (prefix list) - "Return non-nil if PREFIX is prefix of LIST. - -Alias: `-is-prefix-p'" - (--each-while list (equal (car prefix) it) - (!cdr prefix)) - (not prefix)) - -(defun -is-suffix? (suffix list) - "Return non-nil if SUFFIX is suffix of LIST. - -Alias: `-is-suffix-p'" - (-is-prefix? (reverse suffix) (reverse list))) - -(defun -is-infix? (infix list) - "Return non-nil if INFIX is infix of LIST. - -This operation runs in O(n^2) time - -Alias: `-is-infix-p'" - (let (done) - (while (and (not done) list) - (setq done (-is-prefix? infix list)) - (!cdr list)) - done)) - -(defalias '-is-prefix-p '-is-prefix?) -(defalias '-is-suffix-p '-is-suffix?) -(defalias '-is-infix-p '-is-infix?) - -(defun -sort (comparator list) - "Sort LIST, stably, comparing elements using COMPARATOR. -Return the sorted list. LIST is NOT modified by side effects. -COMPARATOR is called with two elements of LIST, and should return non-nil -if the first element should sort before the second." - (sort (copy-sequence list) comparator)) - -(defmacro --sort (form list) - "Anaphoric form of `-sort'." - (declare (debug (form form))) - `(-sort (lambda (it other) ,form) ,list)) - -(defun -list (&rest args) - "Return a list with ARGS. - -If first item of ARGS is already a list, simply return ARGS. If -not, return a list with ARGS as elements." - (let ((arg (car args))) - (if (listp arg) arg args))) - -(defun -repeat (n x) - "Return a list with X repeated N times. -Return nil if N is less than 1." - (let (ret) - (--dotimes n (!cons x ret)) - ret)) - -(defun -sum (list) - "Return the sum of LIST." - (apply '+ list)) - -(defun -product (list) - "Return the product of LIST." - (apply '* list)) - -(defun -max (list) - "Return the largest value from LIST of numbers or markers." - (apply 'max list)) - -(defun -min (list) - "Return the smallest value from LIST of numbers or markers." - (apply 'min list)) - -(defun -max-by (comparator list) - "Take a comparison function COMPARATOR and a LIST and return -the greatest element of the list by the comparison function. - -See also combinator `-on' which can transform the values before -comparing them." - (--reduce (if (funcall comparator it acc) it acc) list)) - -(defun -min-by (comparator list) - "Take a comparison function COMPARATOR and a LIST and return -the least element of the list by the comparison function. - -See also combinator `-on' which can transform the values before -comparing them." - (--reduce (if (funcall comparator it acc) acc it) list)) - -(defmacro --max-by (form list) - "Anaphoric version of `-max-by'. - -The items for the comparator form are exposed as \"it\" and \"other\"." - (declare (debug (form form))) - `(-max-by (lambda (it other) ,form) ,list)) - -(defmacro --min-by (form list) - "Anaphoric version of `-min-by'. - -The items for the comparator form are exposed as \"it\" and \"other\"." - (declare (debug (form form))) - `(-min-by (lambda (it other) ,form) ,list)) - -(defun -iterate (fun init n) - "Return a list of iterated applications of FUN to INIT. - -This means a list of form: - - (init (fun init) (fun (fun init)) ...) - -N is the length of the returned list." - (if (= n 0) nil - (let ((r (list init))) - (--dotimes (1- n) - (push (funcall fun (car r)) r)) - (nreverse r)))) - -(defun -fix (fn list) - "Compute the (least) fixpoint of FN with initial input LIST. - -FN is called at least once, results are compared with `equal'." - (let ((re (funcall fn list))) - (while (not (equal list re)) - (setq list re) - (setq re (funcall fn re))) - re)) - -(defmacro --fix (form list) - "Anaphoric form of `-fix'." - `(-fix (lambda (it) ,form) ,list)) - -(defun -unfold (fun seed) - "Build a list from SEED using FUN. - -This is \"dual\" operation to `-reduce-r': while -reduce-r -consumes a list to produce a single value, `-unfold' takes a -seed value and builds a (potentially infinite!) list. - -FUN should return `nil' to stop the generating process, or a -cons (A . B), where A will be prepended to the result and B is -the new seed." - (let ((last (funcall fun seed)) r) - (while last - (push (car last) r) - (setq last (funcall fun (cdr last)))) - (nreverse r))) - -(defmacro --unfold (form seed) - "Anaphoric version of `-unfold'." - (declare (debug (form form))) - `(-unfold (lambda (it) ,form) ,seed)) - -(defun -cons-pair? (con) - "Return non-nil if CON is true cons pair. -That is (A . B) where B is not a list." - (and (listp con) - (not (listp (cdr con))))) - -(defun -cons-to-list (con) - "Convert a cons pair to a list with `car' and `cdr' of the pair respectively." - (list (car con) (cdr con))) - -(defun -value-to-list (val) - "Convert a value to a list. - -If the value is a cons pair, make a list with two elements, `car' -and `cdr' of the pair respectively. - -If the value is anything else, wrap it in a list." - (cond - ((-cons-pair? val) (-cons-to-list val)) - (t (list val)))) - -(defun -tree-mapreduce-from (fn folder init-value tree) - "Apply FN to each element of TREE, and make a list of the results. -If elements of TREE are lists themselves, apply FN recursively to -elements of these nested lists. - -Then reduce the resulting lists using FOLDER and initial value -INIT-VALUE. See `-reduce-r-from'. - -This is the same as calling `-tree-reduce-from' after `-tree-map' -but is twice as fast as it only traverse the structure once." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (-reduce-r-from folder init-value (mapcar (lambda (x) (-tree-mapreduce-from fn folder init-value x)) tree))) - (t (funcall fn tree)))) - -(defmacro --tree-mapreduce-from (form folder init-value tree) - "Anaphoric form of `-tree-mapreduce-from'." - (declare (debug (form form form form))) - `(-tree-mapreduce-from (lambda (it) ,form) (lambda (it acc) ,folder) ,init-value ,tree)) - -(defun -tree-mapreduce (fn folder tree) - "Apply FN to each element of TREE, and make a list of the results. -If elements of TREE are lists themselves, apply FN recursively to -elements of these nested lists. - -Then reduce the resulting lists using FOLDER and initial value -INIT-VALUE. See `-reduce-r-from'. - -This is the same as calling `-tree-reduce' after `-tree-map' -but is twice as fast as it only traverse the structure once." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (-reduce-r folder (mapcar (lambda (x) (-tree-mapreduce fn folder x)) tree))) - (t (funcall fn tree)))) - -(defmacro --tree-mapreduce (form folder tree) - "Anaphoric form of `-tree-mapreduce'." - (declare (debug (form form form))) - `(-tree-mapreduce (lambda (it) ,form) (lambda (it acc) ,folder) ,tree)) - -(defun -tree-map (fn tree) - "Apply FN to each element of TREE while preserving the tree structure." - (cond - ((not tree) nil) - ((-cons-pair? tree) (funcall fn tree)) - ((listp tree) - (mapcar (lambda (x) (-tree-map fn x)) tree)) - (t (funcall fn tree)))) - -(defmacro --tree-map (form tree) - "Anaphoric form of `-tree-map'." - (declare (debug (form form))) - `(-tree-map (lambda (it) ,form) ,tree)) - -(defun -tree-reduce-from (fn init-value tree) - "Use FN to reduce elements of list TREE. -If elements of TREE are lists themselves, apply the reduction recursively. - -FN is first applied to INIT-VALUE and first element of the list, -then on this result and second element from the list etc. - -The initial value is ignored on cons pairs as they always contain -two elements." - (cond - ((not tree) nil) - ((-cons-pair? tree) tree) - ((listp tree) - (-reduce-r-from fn init-value (mapcar (lambda (x) (-tree-reduce-from fn init-value x)) tree))) - (t tree))) - -(defmacro --tree-reduce-from (form init-value tree) - "Anaphoric form of `-tree-reduce-from'." - (declare (debug (form form form))) - `(-tree-reduce-from (lambda (it acc) ,form) ,init-value ,tree)) - -(defun -tree-reduce (fn tree) - "Use FN to reduce elements of list TREE. -If elements of TREE are lists themselves, apply the reduction recursively. - -FN is first applied to first element of the list and second -element, then on this result and third element from the list etc. - -See `-reduce-r' for how exactly are lists of zero or one element handled." - (cond - ((not tree) nil) - ((-cons-pair? tree) tree) - ((listp tree) - (-reduce-r fn (mapcar (lambda (x) (-tree-reduce fn x)) tree))) - (t tree))) - -(defmacro --tree-reduce (form tree) - "Anaphoric form of `-tree-reduce'." - (declare (debug (form form))) - `(-tree-reduce (lambda (it acc) ,form) ,tree)) - -(defun -tree-map-nodes (pred fun tree) - "Call FUN on each node of TREE that satisfies PRED. - -If PRED returns nil, continue descending down this node. If PRED -returns non-nil, apply FUN to this node and do not descend -further." - (if (funcall pred tree) - (funcall fun tree) - (if (and (listp tree) - (not (-cons-pair? tree))) - (-map (lambda (x) (-tree-map-nodes pred fun x)) tree) - tree))) - -(defmacro --tree-map-nodes (pred form tree) - "Anaphoric form of `-tree-map-nodes'." - `(-tree-map-nodes (lambda (it) ,pred) (lambda (it) ,form) ,tree)) - -(defun -tree-seq (branch children tree) - "Return a sequence of the nodes in TREE, in depth-first search order. - -BRANCH is a predicate of one argument that returns non-nil if the -passed argument is a branch, that is, a node that can have children. - -CHILDREN is a function of one argument that returns the children -of the passed branch node. - -Non-branch nodes are simply copied." - (cons tree - (when (funcall branch tree) - (-mapcat (lambda (x) (-tree-seq branch children x)) - (funcall children tree))))) - -(defmacro --tree-seq (branch children tree) - "Anaphoric form of `-tree-seq'." - `(-tree-seq (lambda (it) ,branch) (lambda (it) ,children) ,tree)) - -(defun -clone (list) - "Create a deep copy of LIST. -The new list has the same elements and structure but all cons are -replaced with new ones. This is useful when you need to clone a -structure such as plist or alist." - (-tree-map 'identity list)) - -(defun dash-enable-font-lock () - "Add syntax highlighting to dash functions, macros and magic values." - (eval-after-load "lisp-mode" - '(progn - (let ((new-keywords '( - "-each" - "--each" - "-each-while" - "--each-while" - "-dotimes" - "--dotimes" - "-map" - "--map" - "-reduce-from" - "--reduce-from" - "-reduce" - "--reduce" - "-reduce-r-from" - "--reduce-r-from" - "-reduce-r" - "--reduce-r" - "-filter" - "--filter" - "-select" - "--select" - "-remove" - "--remove" - "-reject" - "--reject" - "-remove-first" - "--remove-first" - "-reject-first" - "--reject-first" - "-remove-last" - "--remove-last" - "-reject-last" - "--reject-last" - "-remove-item" - "-non-nil" - "-keep" - "--keep" - "-map-indexed" - "--map-indexed" - "-splice" - "--splice" - "-splice-list" - "--splice-list" - "-map-when" - "--map-when" - "-replace-where" - "--replace-where" - "-map-first" - "--map-first" - "-map-last" - "--map-last" - "-replace" - "-replace-first" - "-replace-last" - "-flatten" - "-flatten-n" - "-concat" - "-mapcat" - "--mapcat" - "-copy" - "-cons*" - "-snoc" - "-first" - "--first" - "-find" - "--find" - "-some" - "--some" - "-any" - "--any" - "-last" - "--last" - "-first-item" - "-last-item" - "-butlast" - "-count" - "--count" - "-any?" - "--any?" - "-some?" - "--some?" - "-any-p" - "--any-p" - "-some-p" - "--some-p" - "-all?" - "--all?" - "-every?" - "--every?" - "-all-p" - "--all-p" - "-every-p" - "--every-p" - "-none?" - "--none?" - "-none-p" - "--none-p" - "-only-some?" - "--only-some?" - "-only-some-p" - "--only-some-p" - "-slice" - "-take" - "-drop" - "-take-while" - "--take-while" - "-drop-while" - "--drop-while" - "-split-at" - "-rotate" - "-insert-at" - "-replace-at" - "-update-at" - "--update-at" - "-remove-at" - "-remove-at-indices" - "-split-with" - "--split-with" - "-split-on" - "-split-when" - "--split-when" - "-separate" - "--separate" - "-partition-all-in-steps" - "-partition-in-steps" - "-partition-all" - "-partition" - "-partition-by" - "--partition-by" - "-partition-by-header" - "--partition-by-header" - "-group-by" - "--group-by" - "-interpose" - "-interleave" - "-zip-with" - "--zip-with" - "-zip" - "-zip-fill" - "-cycle" - "-pad" - "-annotate" - "--annotate" - "-table" - "-table-flat" - "-partial" - "-elem-index" - "-elem-indices" - "-find-indices" - "--find-indices" - "-find-index" - "--find-index" - "-find-last-index" - "--find-last-index" - "-select-by-indices" - "-grade-up" - "-grade-down" - "->" - "->>" - "-->" - "-when-let" - "-when-let*" - "--when-let" - "-if-let" - "-if-let*" - "--if-let" - "-let*" - "-let" - "-lambda" - "-distinct" - "-uniq" - "-union" - "-intersection" - "-difference" - "-contains?" - "-contains-p" - "-same-items?" - "-same-items-p" - "-is-prefix-p" - "-is-prefix?" - "-is-suffix-p" - "-is-suffix?" - "-is-infix-p" - "-is-infix?" - "-sort" - "--sort" - "-list" - "-repeat" - "-sum" - "-product" - "-max" - "-min" - "-max-by" - "--max-by" - "-min-by" - "--min-by" - "-iterate" - "--iterate" - "-fix" - "--fix" - "-unfold" - "--unfold" - "-cons-pair?" - "-cons-to-list" - "-value-to-list" - "-tree-mapreduce-from" - "--tree-mapreduce-from" - "-tree-mapreduce" - "--tree-mapreduce" - "-tree-map" - "--tree-map" - "-tree-reduce-from" - "--tree-reduce-from" - "-tree-reduce" - "--tree-reduce" - "-tree-seq" - "--tree-seq" - "-tree-map-nodes" - "--tree-map-nodes" - "-clone" - "-rpartial" - "-juxt" - "-applify" - "-on" - "-flip" - "-const" - "-cut" - "-orfn" - "-andfn" - "-iteratefn" - "-fixfn" - "-prodfn" - )) - (special-variables '( - "it" - "it-index" - "acc" - "other" - ))) - (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "\\_<" (regexp-opt special-variables 'paren) "\\_>") - 1 font-lock-variable-name-face)) 'append) - (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "(\\s-*" (regexp-opt new-keywords 'paren) "\\_>") - 1 font-lock-keyword-face)) 'append)) - (--each (buffer-list) - (with-current-buffer it - (when (and (eq major-mode 'emacs-lisp-mode) - (boundp 'font-lock-mode) - font-lock-mode) - (font-lock-refresh-defaults))))))) - -(provide 'dash) -;;; dash.el ends here diff --git a/elpa/dash-20151216.1315/dash-autoloads.el b/elpa/dash-20151216.1315/dash-autoloads.el new file mode 100644 index 00000000..1872151c --- /dev/null +++ b/elpa/dash-20151216.1315/dash-autoloads.el @@ -0,0 +1,15 @@ +;;; dash-autoloads.el --- automatically extracted autoloads +;; +;;; Code: +(add-to-list 'load-path (or (file-name-directory #$) (car load-path))) + +;;;### (autoloads nil nil ("dash.el") (22215 9211 926426 0)) + +;;;*** + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; End: +;;; dash-autoloads.el ends here diff --git a/elpa/dash-20151216.1315/dash-pkg.el b/elpa/dash-20151216.1315/dash-pkg.el new file mode 100644 index 00000000..2e03885f --- /dev/null +++ b/elpa/dash-20151216.1315/dash-pkg.el @@ -0,0 +1 @@ +(define-package "dash" "20151216.1315" "A modern list library for Emacs" 'nil :keywords '("lists")) diff --git a/elpa/dash-20151216.1315/dash.el b/elpa/dash-20151216.1315/dash.el new file mode 100644 index 00000000..0b582b23 --- /dev/null +++ b/elpa/dash-20151216.1315/dash.el @@ -0,0 +1,2437 @@ +;;; dash.el --- A modern list library for Emacs -*- lexical-binding: t -*- + +;; Copyright (C) 2012-2015 Free Software Foundation, Inc. + +;; Author: Magnar Sveen +;; Version: 2.12.1 +;; Package-Version: 20151216.1315 +;; Keywords: lists + +;; 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 . + +;;; Commentary: + +;; A modern list api for Emacs. +;; +;; See documentation on https://github.com/magnars/dash.el#functions +;; +;; **Please note** The lexical binding in this file is not utilised at the +;; moment. We will take full advantage of lexical binding in an upcoming 3.0 +;; release of Dash. In the meantime, we've added the pragma to avoid a bug that +;; you can read more about in https://github.com/magnars/dash.el/issues/130. +;; + +;;; Code: + +(defgroup dash () + "Customize group for dash.el" + :group 'lisp + :prefix "dash-") + +(defun dash--enable-fontlock (symbol value) + (when value + (dash-enable-font-lock)) + (set-default symbol value)) + +(defcustom dash-enable-fontlock nil + "If non-nil, enable fontification of dash functions, macros and +special values." + :type 'boolean + :set 'dash--enable-fontlock + :group 'dash) + +(defmacro !cons (car cdr) + "Destructive: Set CDR to the cons of CAR and CDR." + `(setq ,cdr (cons ,car ,cdr))) + +(defmacro !cdr (list) + "Destructive: Set LIST to the cdr of LIST." + `(setq ,list (cdr ,list))) + +(defmacro --each (list &rest body) + "Anaphoric form of `-each'." + (declare (debug (form body)) + (indent 1)) + (let ((l (make-symbol "list"))) + `(let ((,l ,list) + (it-index 0)) + (while ,l + (let ((it (car ,l))) + ,@body) + (setq it-index (1+ it-index)) + (!cdr ,l))))) + +(defun -each (list fn) + "Call FN with every item in LIST. Return nil, used for side-effects only." + (--each list (funcall fn it))) + +(put '-each 'lisp-indent-function 1) + +(defmacro --each-while (list pred &rest body) + "Anaphoric form of `-each-while'." + (declare (debug (form form body)) + (indent 2)) + (let ((l (make-symbol "list")) + (c (make-symbol "continue"))) + `(let ((,l ,list) + (,c t) + (it-index 0)) + (while (and ,l ,c) + (let ((it (car ,l))) + (if (not ,pred) (setq ,c nil) ,@body)) + (setq it-index (1+ it-index)) + (!cdr ,l))))) + +(defun -each-while (list pred fn) + "Call FN with every item in LIST while (PRED item) is non-nil. +Return nil, used for side-effects only." + (--each-while list (funcall pred it) (funcall fn it))) + +(put '-each-while 'lisp-indent-function 2) + +(defmacro --dotimes (num &rest body) + "Repeatedly executes BODY (presumably for side-effects) with `it` bound to integers from 0 through NUM-1." + (declare (debug (form body)) + (indent 1)) + (let ((n (make-symbol "num"))) + `(let ((,n ,num) + (it 0)) + (while (< it ,n) + ,@body + (setq it (1+ it)))))) + +(defun -dotimes (num fn) + "Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through NUM-1." + (--dotimes num (funcall fn it))) + +(put '-dotimes 'lisp-indent-function 1) + +(defun -map (fn list) + "Return a new list consisting of the result of applying FN to the items in LIST." + (mapcar fn list)) + +(defmacro --map (form list) + "Anaphoric form of `-map'." + (declare (debug (form form))) + `(mapcar (lambda (it) ,form) ,list)) + +(defmacro --reduce-from (form initial-value list) + "Anaphoric form of `-reduce-from'." + (declare (debug (form form form))) + `(let ((acc ,initial-value)) + (--each ,list (setq acc ,form)) + acc)) + +(defun -reduce-from (fn initial-value list) + "Return the result of applying FN to INITIAL-VALUE and the +first item in LIST, then applying FN to that result and the 2nd +item, etc. If LIST contains no items, return INITIAL-VALUE and +FN is not called. + +In the anaphoric form `--reduce-from', the accumulated value is +exposed as `acc`. + +See also: `-reduce', `-reduce-r'" + (--reduce-from (funcall fn acc it) initial-value list)) + +(defmacro --reduce (form list) + "Anaphoric form of `-reduce'." + (declare (debug (form form))) + (let ((lv (make-symbol "list-value"))) + `(let ((,lv ,list)) + (if ,lv + (--reduce-from ,form (car ,lv) (cdr ,lv)) + (let (acc it) ,form))))) + +(defun -reduce (fn list) + "Return the result of applying FN to the first 2 items in LIST, +then applying FN to that result and the 3rd item, etc. If LIST +contains no items, FN must accept no arguments as well, and +reduce return the result of calling FN with no arguments. If +LIST has only 1 item, it is returned and FN is not called. + +In the anaphoric form `--reduce', the accumulated value is +exposed as `acc`. + +See also: `-reduce-from', `-reduce-r'" + (if list + (-reduce-from fn (car list) (cdr list)) + (funcall fn))) + +(defun -reduce-r-from (fn initial-value list) + "Replace conses with FN, nil with INITIAL-VALUE and evaluate +the resulting expression. If LIST is empty, INITIAL-VALUE is +returned and FN is not called. + +Note: this function works the same as `-reduce-from' but the +operation associates from right instead of from left. + +See also: `-reduce-r', `-reduce'" + (if (not list) initial-value + (funcall fn (car list) (-reduce-r-from fn initial-value (cdr list))))) + +(defmacro --reduce-r-from (form initial-value list) + "Anaphoric version of `-reduce-r-from'." + (declare (debug (form form form))) + `(-reduce-r-from (lambda (&optional it acc) ,form) ,initial-value ,list)) + +(defun -reduce-r (fn list) + "Replace conses with FN and evaluate the resulting expression. +The final nil is ignored. If LIST contains no items, FN must +accept no arguments as well, and reduce return the result of +calling FN with no arguments. If LIST has only 1 item, it is +returned and FN is not called. + +The first argument of FN is the new item, the second is the +accumulated value. + +Note: this function works the same as `-reduce' but the operation +associates from right instead of from left. + +See also: `-reduce-r-from', `-reduce'" + (cond + ((not list) (funcall fn)) + ((not (cdr list)) (car list)) + (t (funcall fn (car list) (-reduce-r fn (cdr list)))))) + +(defmacro --reduce-r (form list) + "Anaphoric version of `-reduce-r'." + (declare (debug (form form))) + `(-reduce-r (lambda (&optional it acc) ,form) ,list)) + +(defmacro --filter (form list) + "Anaphoric form of `-filter'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (when ,form (!cons it ,r))) + (nreverse ,r)))) + +(defun -filter (pred list) + "Return a new list of the items in LIST for which PRED returns a non-nil value. + +Alias: `-select' + +See also: `-keep'" + (--filter (funcall pred it) list)) + +(defalias '-select '-filter) +(defalias '--select '--filter) + +(defmacro --remove (form list) + "Anaphoric form of `-remove'." + (declare (debug (form form))) + `(--filter (not ,form) ,list)) + +(defun -remove (pred list) + "Return a new list of the items in LIST for which PRED returns nil. + +Alias: `-reject'" + (--remove (funcall pred it) list)) + +(defalias '-reject '-remove) +(defalias '--reject '--remove) + +(defun -remove-first (pred list) + "Return a new list with the first item matching PRED removed. + +Alias: `-reject-first' + +See also: `-remove', `-map-first'" + (let (front) + (while (and list (not (funcall pred (car list)))) + (push (car list) front) + (!cdr list)) + (if list + (-concat (nreverse front) (cdr list)) + (nreverse front)))) + +(defmacro --remove-first (form list) + "Anaphoric form of `-remove-first'." + (declare (debug (form form))) + `(-remove-first (lambda (it) ,form) ,list)) + +(defalias '-reject-first '-remove-first) +(defalias '--reject-first '--remove-first) + +(defun -remove-last (pred list) + "Return a new list with the last item matching PRED removed. + +Alias: `-reject-last' + +See also: `-remove', `-map-last'" + (nreverse (-remove-first pred (nreverse list)))) + +(defmacro --remove-last (form list) + "Anaphoric form of `-remove-last'." + (declare (debug (form form))) + `(-remove-last (lambda (it) ,form) ,list)) + +(defalias '-reject-last '-remove-last) +(defalias '--reject-last '--remove-last) + +(defun -remove-item (item list) + "Remove all occurences of ITEM from LIST. + +Comparison is done with `equal'." + (--remove (equal it item) list)) + +(defmacro --keep (form list) + "Anaphoric form of `-keep'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (m (make-symbol "mapped"))) + `(let (,r) + (--each ,list (let ((,m ,form)) (when ,m (!cons ,m ,r)))) + (nreverse ,r)))) + +(defun -keep (fn list) + "Return a new list of the non-nil results of applying FN to the items in LIST. + +If you want to select the original items satisfying a predicate use `-filter'." + (--keep (funcall fn it) list)) + +(defun -non-nil (list) + "Return all non-nil elements of LIST." + (-remove 'null list)) + +(defmacro --map-indexed (form list) + "Anaphoric form of `-map-indexed'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list + (!cons ,form ,r)) + (nreverse ,r)))) + +(defun -map-indexed (fn list) + "Return a new list consisting of the result of (FN index item) for each item in LIST. + +In the anaphoric form `--map-indexed', the index is exposed as `it-index`." + (--map-indexed (funcall fn it-index it) list)) + +(defmacro --map-when (pred rep list) + "Anaphoric form of `-map-when'." + (declare (debug (form form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (!cons (if ,pred ,rep it) ,r)) + (nreverse ,r)))) + +(defun -map-when (pred rep list) + "Return a new list where the elements in LIST that does not match the PRED function +are unchanged, and where the elements in LIST that do match the PRED function are mapped +through the REP function. + +Alias: `-replace-where' + +See also: `-update-at'" + (--map-when (funcall pred it) (funcall rep it) list)) + +(defalias '-replace-where '-map-when) +(defalias '--replace-where '--map-when) + +(defun -map-first (pred rep list) + "Replace first item in LIST satisfying PRED with result of REP called on this item. + +See also: `-map-when', `-replace-first'" + (let (front) + (while (and list (not (funcall pred (car list)))) + (push (car list) front) + (!cdr list)) + (if list + (-concat (nreverse front) (cons (funcall rep (car list)) (cdr list))) + (nreverse front)))) + +(defmacro --map-first (pred rep list) + "Anaphoric form of `-map-first'." + `(-map-first (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) + +(defun -map-last (pred rep list) + "Replace first item in LIST satisfying PRED with result of REP called on this item. + +See also: `-map-when', `-replace-last'" + (nreverse (-map-first pred rep (nreverse list)))) + +(defmacro --map-last (pred rep list) + "Anaphoric form of `-map-last'." + `(-map-last (lambda (it) ,pred) (lambda (it) (ignore it) ,rep) ,list)) + +(defun -replace (old new list) + "Replace all OLD items in LIST with NEW. + +Elements are compared using `equal'. + +See also: `-replace-at'" + (--map-when (equal it old) new list)) + +(defun -replace-first (old new list) + "Replace the first occurence of OLD with NEW in LIST. + +Elements are compared using `equal'. + +See also: `-map-first'" + (--map-first (equal old it) new list)) + +(defun -replace-last (old new list) + "Replace the last occurence of OLD with NEW in LIST. + +Elements are compared using `equal'. + +See also: `-map-last'" + (--map-last (equal old it) new list)) + +(defmacro --mapcat (form list) + "Anaphoric form of `-mapcat'." + (declare (debug (form form))) + `(apply 'append (--map ,form ,list))) + +(defun -mapcat (fn list) + "Return the concatenation of the result of mapping FN over LIST. +Thus function FN should return a list." + (--mapcat (funcall fn it) list)) + +(defun -flatten (l) + "Take a nested list L and return its contents as a single, flat list. + +Note that because `nil' represents a list of zero elements (an +empty list), any mention of nil in L will disappear after +flattening. If you need to preserve nils, consider `-flatten-n' +or map them to some unique symbol and then map them back. + +Conses of two atoms are considered \"terminals\", that is, they +aren't flattened further. + +See also: `-flatten-n'" + (if (and (listp l) (listp (cdr l))) + (-mapcat '-flatten l) + (list l))) + +(defmacro --iterate (form init n) + "Anaphoric version of `-iterate'." + (declare (debug (form form form))) + `(-iterate (lambda (it) ,form) ,init ,n)) + +(defun -flatten-n (num list) + "Flatten NUM levels of a nested LIST. + +See also: `-flatten'" + (-last-item (--iterate (--mapcat (-list it) it) list (1+ num)))) + +(defun -concat (&rest lists) + "Return a new list with the concatenation of the elements in the supplied LISTS." + (apply 'append lists)) + +(defalias '-copy 'copy-sequence + "Create a shallow copy of LIST.") + +(defun -splice (pred fun list) + "Splice lists generated by FUN in place of elements matching PRED in LIST. + +FUN takes the element matching PRED as input. + +This function can be used as replacement for `,@' in case you +need to splice several lists at marked positions (for example +with keywords). + +See also: `-splice-list', `-insert-at'" + (let (r) + (--each list + (if (funcall pred it) + (let ((new (funcall fun it))) + (--each new (!cons it r))) + (!cons it r))) + (nreverse r))) + +(defmacro --splice (pred form list) + "Anaphoric form of `-splice'." + `(-splice (lambda (it) ,pred) (lambda (it) ,form) ,list)) + +(defun -splice-list (pred new-list list) + "Splice NEW-LIST in place of elements matching PRED in LIST. + +See also: `-splice', `-insert-at'" + (-splice pred (lambda (_) new-list) list)) + +(defmacro --splice-list (pred new-list list) + "Anaphoric form of `-splice-list'." + `(-splice-list (lambda (it) ,pred) ,new-list ,list)) + +(defun -cons* (&rest args) + "Make a new list from the elements of ARGS. + +The last 2 members of ARGS are used as the final cons of the +result so if the final member of ARGS is not a list the result is +a dotted list." + (-reduce-r 'cons args)) + +(defun -snoc (list elem &rest elements) + "Append ELEM to the end of the list. + +This is like `cons', but operates on the end of list. + +If ELEMENTS is non nil, append these to the list as well." + (-concat list (list elem) elements)) + +(defmacro --first (form list) + "Anaphoric form of `-first'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each-while ,list (not ,n) + (when ,form (setq ,n it))) + ,n))) + +(defun -first (pred list) + "Return the first x in LIST where (PRED x) is non-nil, else nil. + +To get the first item in the list no questions asked, use `car'. + +Alias: `-find'" + (--first (funcall pred it) list)) + +(defalias '-find '-first) +(defalias '--find '--first) + +(defmacro --some (form list) + "Anaphoric form of `-some'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each-while ,list (not ,n) + (setq ,n ,form)) + ,n))) + +(defun -some (pred list) + "Return (PRED x) for the first LIST item where (PRED x) is non-nil, else nil. + +Alias: `-any'" + (--some (funcall pred it) list)) + +(defalias '-any '-some) +(defalias '--any '--some) + +(defmacro --last (form list) + "Anaphoric form of `-last'." + (declare (debug (form form))) + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each ,list + (when ,form (setq ,n it))) + ,n))) + +(defun -last (pred list) + "Return the last x in LIST where (PRED x) is non-nil, else nil." + (--last (funcall pred it) list)) + +(defalias '-first-item 'car + "Return the first item of LIST, or nil on an empty list.") + +(defun -last-item (list) + "Return the last item of LIST, or nil on an empty list." + (car (last list))) + +(defun -butlast (list) + "Return a list of all items in list except for the last." + (let (result) + (while (cdr list) + (!cons (car list) result) + (!cdr list)) + (nreverse result))) + +(defmacro --count (pred list) + "Anaphoric form of `-count'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let ((,r 0)) + (--each ,list (when ,pred (setq ,r (1+ ,r)))) + ,r))) + +(defun -count (pred list) + "Counts the number of items in LIST where (PRED item) is non-nil." + (--count (funcall pred it) list)) + +(defun ---truthy? (val) + (not (null val))) + +(defmacro --any? (form list) + "Anaphoric form of `-any?'." + (declare (debug (form form))) + `(---truthy? (--first ,form ,list))) + +(defun -any? (pred list) + "Return t if (PRED x) is non-nil for any x in LIST, else nil. + +Alias: `-any-p', `-some?', `-some-p'" + (--any? (funcall pred it) list)) + +(defalias '-some? '-any?) +(defalias '--some? '--any?) +(defalias '-any-p '-any?) +(defalias '--any-p '--any?) +(defalias '-some-p '-any?) +(defalias '--some-p '--any?) + +(defmacro --all? (form list) + "Anaphoric form of `-all?'." + (declare (debug (form form))) + (let ((a (make-symbol "all"))) + `(let ((,a t)) + (--each-while ,list ,a (setq ,a ,form)) + (---truthy? ,a)))) + +(defun -all? (pred list) + "Return t if (PRED x) is non-nil for all x in LIST, else nil. + +Alias: `-all-p', `-every?', `-every-p'" + (--all? (funcall pred it) list)) + +(defalias '-every? '-all?) +(defalias '--every? '--all?) +(defalias '-all-p '-all?) +(defalias '--all-p '--all?) +(defalias '-every-p '-all?) +(defalias '--every-p '--all?) + +(defmacro --none? (form list) + "Anaphoric form of `-none?'." + (declare (debug (form form))) + `(--all? (not ,form) ,list)) + +(defun -none? (pred list) + "Return t if (PRED x) is nil for all x in LIST, else nil. + +Alias: `-none-p'" + (--none? (funcall pred it) list)) + +(defalias '-none-p '-none?) +(defalias '--none-p '--none?) + +(defmacro --only-some? (form list) + "Anaphoric form of `-only-some?'." + (declare (debug (form form))) + (let ((y (make-symbol "yes")) + (n (make-symbol "no"))) + `(let (,y ,n) + (--each-while ,list (not (and ,y ,n)) + (if ,form (setq ,y t) (setq ,n t))) + (---truthy? (and ,y ,n))))) + +(defun -only-some? (pred list) + "Return `t` if at least one item of LIST matches PRED and at least one item of LIST does not match PRED. +Return `nil` both if all items match the predicate or if none of the items match the predicate. + +Alias: `-only-some-p'" + (--only-some? (funcall pred it) list)) + +(defalias '-only-some-p '-only-some?) +(defalias '--only-some-p '--only-some?) + +(defun -slice (list from &optional to step) + "Return copy of LIST, starting from index FROM to index TO. + +FROM or TO may be negative. These values are then interpreted +modulo the length of the list. + +If STEP is a number, only each STEPth item in the resulting +section is returned. Defaults to 1." + (let ((length (length list)) + (new-list nil)) + ;; to defaults to the end of the list + (setq to (or to length)) + (setq step (or step 1)) + ;; handle negative indices + (when (< from 0) + (setq from (mod from length))) + (when (< to 0) + (setq to (mod to length))) + + ;; iterate through the list, keeping the elements we want + (--each-while list (< it-index to) + (when (and (>= it-index from) + (= (mod (- from it-index) step) 0)) + (push it new-list))) + (nreverse new-list))) + +(defun -take (n list) + "Return a new list of the first N items in LIST, or all items if there are fewer than N." + (let (result) + (--dotimes n + (when list + (!cons (car list) result) + (!cdr list))) + (nreverse result))) + +(defalias '-drop 'nthcdr "Return the tail of LIST without the first N items.") + +(defmacro --take-while (form list) + "Anaphoric form of `-take-while'." + (declare (debug (form form))) + (let ((r (make-symbol "result"))) + `(let (,r) + (--each-while ,list ,form (!cons it ,r)) + (nreverse ,r)))) + +(defun -take-while (pred list) + "Return a new list of successive items from LIST while (PRED item) returns a non-nil value." + (--take-while (funcall pred it) list)) + +(defmacro --drop-while (form list) + "Anaphoric form of `-drop-while'." + (declare (debug (form form))) + (let ((l (make-symbol "list"))) + `(let ((,l ,list)) + (while (and ,l (let ((it (car ,l))) ,form)) + (!cdr ,l)) + ,l))) + +(defun -drop-while (pred list) + "Return the tail of LIST starting from the first item for which (PRED item) returns nil." + (--drop-while (funcall pred it) list)) + +(defun -split-at (n list) + "Return a list of ((-take N LIST) (-drop N LIST)), in no more than one pass through the list." + (let (result) + (--dotimes n + (when list + (!cons (car list) result) + (!cdr list))) + (list (nreverse result) list))) + +(defun -rotate (n list) + "Rotate LIST N places to the right. With N negative, rotate to the left. +The time complexity is O(n)." + (if (> n 0) + (append (last list n) (butlast list n)) + (append (-drop (- n) list) (-take (- n) list)))) + +(defun -insert-at (n x list) + "Return a list with X inserted into LIST at position N. + +See also: `-splice', `-splice-list'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons x (cadr split-list))))) + +(defun -replace-at (n x list) + "Return a list with element at Nth position in LIST replaced with X. + +See also: `-replace'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons x (cdr (cadr split-list)))))) + +(defun -update-at (n func list) + "Return a list with element at Nth position in LIST replaced with `(func (nth n list))`. + +See also: `-map-when'" + (let ((split-list (-split-at n list))) + (nconc (car split-list) (cons (funcall func (car (cadr split-list))) (cdr (cadr split-list)))))) + +(defmacro --update-at (n form list) + "Anaphoric version of `-update-at'." + (declare (debug (form form form))) + `(-update-at ,n (lambda (it) ,form) ,list)) + +(defun -remove-at (n list) + "Return a list with element at Nth position in LIST removed. + +See also: `-remove-at-indices', `-remove'" + (-remove-at-indices (list n) list)) + +(defun -remove-at-indices (indices list) + "Return a list whose elements are elements from LIST without +elements selected as `(nth i list)` for all i +from INDICES. + +See also: `-remove-at', `-remove'" + (let* ((indices (-sort '< indices)) + (diffs (cons (car indices) (-map '1- (-zip-with '- (cdr indices) indices)))) + r) + (--each diffs + (let ((split (-split-at it list))) + (!cons (car split) r) + (setq list (cdr (cadr split))))) + (!cons list r) + (apply '-concat (nreverse r)))) + +(defmacro --split-with (pred list) + "Anaphoric form of `-split-with'." + (declare (debug (form form))) + (let ((l (make-symbol "list")) + (r (make-symbol "result")) + (c (make-symbol "continue"))) + `(let ((,l ,list) + (,r nil) + (,c t)) + (while (and ,l ,c) + (let ((it (car ,l))) + (if (not ,pred) + (setq ,c nil) + (!cons it ,r) + (!cdr ,l)))) + (list (nreverse ,r) ,l)))) + +(defun -split-with (pred list) + "Return a list of ((-take-while PRED LIST) (-drop-while PRED LIST)), in no more than one pass through the list." + (--split-with (funcall pred it) list)) + +(defmacro -split-on (item list) + "Split the LIST each time ITEM is found. + +Unlike `-partition-by', the ITEM is discarded from the results. +Empty lists are also removed from the result. + +Comparison is done by `equal'. + +See also `-split-when'" + (declare (debug (form form))) + `(-split-when (lambda (it) (equal it ,item)) ,list)) + +(defmacro --split-when (form list) + "Anaphoric version of `-split-when'." + (declare (debug (form form))) + `(-split-when (lambda (it) ,form) ,list)) + +(defun -split-when (fn list) + "Split the LIST on each element where FN returns non-nil. + +Unlike `-partition-by', the \"matched\" element is discarded from +the results. Empty lists are also removed from the result. + +This function can be thought of as a generalization of +`split-string'." + (let (r s) + (while list + (if (not (funcall fn (car list))) + (push (car list) s) + (when s (push (nreverse s) r)) + (setq s nil)) + (!cdr list)) + (when s (push (nreverse s) r)) + (nreverse r))) + +(defmacro --separate (form list) + "Anaphoric form of `-separate'." + (declare (debug (form form))) + (let ((y (make-symbol "yes")) + (n (make-symbol "no"))) + `(let (,y ,n) + (--each ,list (if ,form (!cons it ,y) (!cons it ,n))) + (list (nreverse ,y) (nreverse ,n))))) + +(defun -separate (pred list) + "Return a list of ((-filter PRED LIST) (-remove PRED LIST)), in one pass through the list." + (--separate (funcall pred it) list)) + +(defun ---partition-all-in-steps-reversed (n step list) + "Private: Used by -partition-all-in-steps and -partition-in-steps." + (when (< step 1) + (error "Step must be a positive number, or you're looking at some juicy infinite loops.")) + (let ((result nil)) + (while list + (!cons (-take n list) result) + (setq list (-drop step list))) + result)) + +(defun -partition-all-in-steps (n step list) + "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. +The last groups may contain less than N items." + (nreverse (---partition-all-in-steps-reversed n step list))) + +(defun -partition-in-steps (n step list) + "Return a new list with the items in LIST grouped into N-sized sublists at offsets STEP apart. +If there are not enough items to make the last group N-sized, +those items are discarded." + (let ((result (---partition-all-in-steps-reversed n step list))) + (while (and result (< (length (car result)) n)) + (!cdr result)) + (nreverse result))) + +(defun -partition-all (n list) + "Return a new list with the items in LIST grouped into N-sized sublists. +The last group may contain less than N items." + (-partition-all-in-steps n n list)) + +(defun -partition (n list) + "Return a new list with the items in LIST grouped into N-sized sublists. +If there are not enough items to make the last group N-sized, +those items are discarded." + (-partition-in-steps n n list)) + +(defmacro --partition-by (form list) + "Anaphoric form of `-partition-by'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (s (make-symbol "sublist")) + (v (make-symbol "value")) + (n (make-symbol "new-value")) + (l (make-symbol "list"))) + `(let ((,l ,list)) + (when ,l + (let* ((,r nil) + (it (car ,l)) + (,s (list it)) + (,v ,form) + (,l (cdr ,l))) + (while ,l + (let* ((it (car ,l)) + (,n ,form)) + (unless (equal ,v ,n) + (!cons (nreverse ,s) ,r) + (setq ,s nil) + (setq ,v ,n)) + (!cons it ,s) + (!cdr ,l))) + (!cons (nreverse ,s) ,r) + (nreverse ,r)))))) + +(defun -partition-by (fn list) + "Apply FN to each item in LIST, splitting it each time FN returns a new value." + (--partition-by (funcall fn it) list)) + +(defmacro --partition-by-header (form list) + "Anaphoric form of `-partition-by-header'." + (declare (debug (form form))) + (let ((r (make-symbol "result")) + (s (make-symbol "sublist")) + (h (make-symbol "header-value")) + (b (make-symbol "seen-body?")) + (n (make-symbol "new-value")) + (l (make-symbol "list"))) + `(let ((,l ,list)) + (when ,l + (let* ((,r nil) + (it (car ,l)) + (,s (list it)) + (,h ,form) + (,b nil) + (,l (cdr ,l))) + (while ,l + (let* ((it (car ,l)) + (,n ,form)) + (if (equal ,h ,n) + (when ,b + (!cons (nreverse ,s) ,r) + (setq ,s nil) + (setq ,b nil)) + (setq ,b t)) + (!cons it ,s) + (!cdr ,l))) + (!cons (nreverse ,s) ,r) + (nreverse ,r)))))) + +(defun -partition-by-header (fn list) + "Apply FN to the first item in LIST. That is the header +value. Apply FN to each item in LIST, splitting it each time FN +returns the header value, but only after seeing at least one +other value (the body)." + (--partition-by-header (funcall fn it) list)) + +(defmacro --group-by (form list) + "Anaphoric form of `-group-by'." + (declare (debug t)) + (let ((n (make-symbol "n")) + (k (make-symbol "k")) + (grp (make-symbol "grp"))) + `(nreverse + (-map + (lambda (,n) + (cons (car ,n) + (nreverse (cdr ,n)))) + (--reduce-from + (let* ((,k (,@form)) + (,grp (assoc ,k acc))) + (if ,grp + (setcdr ,grp (cons it (cdr ,grp))) + (push + (list ,k it) + acc)) + acc) + nil ,list))))) + +(defun -group-by (fn list) + "Separate LIST into an alist whose keys are FN applied to the +elements of LIST. Keys are compared by `equal'." + (--group-by (funcall fn it) list)) + +(defun -interpose (sep list) + "Return a new list of all elements in LIST separated by SEP." + (let (result) + (when list + (!cons (car list) result) + (!cdr list)) + (while list + (setq result (cons (car list) (cons sep result))) + (!cdr list)) + (nreverse result))) + +(defun -interleave (&rest lists) + "Return a new list of the first item in each list, then the second etc." + (let (result) + (while (-none? 'null lists) + (--each lists (!cons (car it) result)) + (setq lists (-map 'cdr lists))) + (nreverse result))) + +(defmacro --zip-with (form list1 list2) + "Anaphoric form of `-zip-with'. + +The elements in list1 is bound as `it`, the elements in list2 as `other`." + (declare (debug (form form form))) + (let ((r (make-symbol "result")) + (l1 (make-symbol "list1")) + (l2 (make-symbol "list2"))) + `(let ((,r nil) + (,l1 ,list1) + (,l2 ,list2)) + (while (and ,l1 ,l2) + (let ((it (car ,l1)) + (other (car ,l2))) + (!cons ,form ,r) + (!cdr ,l1) + (!cdr ,l2))) + (nreverse ,r)))) + +(defun -zip-with (fn list1 list2) + "Zip the two lists LIST1 and LIST2 using a function FN. This +function is applied pairwise taking as first argument element of +LIST1 and as second argument element of LIST2 at corresponding +position. + +The anaphoric form `--zip-with' binds the elements from LIST1 as `it`, +and the elements from LIST2 as `other`." + (--zip-with (funcall fn it other) list1 list2)) + +(defun -zip (&rest lists) + "Zip LISTS together. Group the head of each list, followed by the +second elements of each list, and so on. The lengths of the returned +groupings are equal to the length of the shortest input list. + +If two lists are provided as arguments, return the groupings as a list +of cons cells. Otherwise, return the groupings as a list of lists. + +Please note! This distinction is being removed in an upcoming 2.0 +release of Dash. If you rely on this behavior, use -zip-pair instead." + (let (results) + (while (-none? 'null lists) + (setq results (cons (mapcar 'car lists) results)) + (setq lists (mapcar 'cdr lists))) + (setq results (nreverse results)) + (if (= (length lists) 2) + ;; to support backward compatability, return + ;; a cons cell if two lists were provided + (--map (cons (car it) (cadr it)) results) + results))) + +(defalias '-zip-pair '-zip) + +(defun -zip-fill (fill-value &rest lists) + "Zip LISTS, with FILL-VALUE padded onto the shorter lists. The +lengths of the returned groupings are equal to the length of the +longest input list." + (apply '-zip (apply '-pad (cons fill-value lists)))) + +(defun -cycle (list) + "Return an infinite copy of LIST that will cycle through the +elements and repeat from the beginning." + (let ((newlist (-map 'identity list))) + (nconc newlist newlist))) + +(defun -pad (fill-value &rest lists) + "Appends FILL-VALUE to the end of each list in LISTS such that they +will all have the same length." + (let* ((annotations (-annotate 'length lists)) + (n (-max (-map 'car annotations)))) + (--map (append (cdr it) (-repeat (- n (car it)) fill-value)) annotations))) + +(defun -annotate (fn list) + "Return a list of cons cells where each cell is FN applied to each +element of LIST paired with the unmodified element of LIST." + (-zip (-map fn list) list)) + +(defmacro --annotate (form list) + "Anaphoric version of `-annotate'." + (declare (debug (form form))) + `(-annotate (lambda (it) ,form) ,list)) + +(defun dash--table-carry (lists restore-lists &optional re) + "Helper for `-table' and `-table-flat'. + +If a list overflows, carry to the right and reset the list." + (while (not (or (car lists) + (equal lists '(nil)))) + (setcar lists (car restore-lists)) + (pop (cadr lists)) + (!cdr lists) + (!cdr restore-lists) + (when re + (push (nreverse (car re)) (cadr re)) + (setcar re nil) + (!cdr re)))) + +(defun -table (fn &rest lists) + "Compute outer product of LISTS using function FN. + +The function FN should have the same arity as the number of +supplied lists. + +The outer product is computed by applying fn to all possible +combinations created by taking one element from each list in +order. The dimension of the result is (length lists). + +See also: `-table-flat'" + (let ((restore-lists (copy-sequence lists)) + (last-list (last lists)) + (re (make-list (length lists) nil))) + (while (car last-list) + (let ((item (apply fn (-map 'car lists)))) + (push item (car re)) + (setcar lists (cdar lists)) ;; silence byte compiler + (dash--table-carry lists restore-lists re))) + (nreverse (car (last re))))) + +(defun -table-flat (fn &rest lists) + "Compute flat outer product of LISTS using function FN. + +The function FN should have the same arity as the number of +supplied lists. + +The outer product is computed by applying fn to all possible +combinations created by taking one element from each list in +order. The results are flattened, ignoring the tensor structure +of the result. This is equivalent to calling: + + (-flatten-n (1- (length lists)) (-table fn lists)) + +but the implementation here is much more efficient. + +See also: `-flatten-n', `-table'" + (when lists ;Just in case. + (let* ((list1 (pop lists)) + (restore-lists (copy-sequence lists)) + (last-list (last lists)) + re) + (while (car last-list) + (let ((tail (-map #'car lists))) + (dolist (head list1) + (push (apply fn head tail) re))) + (pop (car lists)) + (dash--table-carry lists restore-lists)) + (nreverse re)))) + +(defun -partial (fn &rest args) + "Take a function FN and fewer than the normal arguments to FN, +and return a fn that takes a variable number of additional ARGS. +When called, the returned function calls FN with ARGS first and +then additional args." + (apply 'apply-partially fn args)) + +(defun -elem-index (elem list) + "Return the index of the first element in the given LIST which +is equal to the query element ELEM, or nil if there is no +such element." + (car (-elem-indices elem list))) + +(defun -elem-indices (elem list) + "Return the indices of all elements in LIST equal to the query +element ELEM, in ascending order." + (-find-indices (-partial 'equal elem) list)) + +(defun -find-indices (pred list) + "Return the indices of all elements in LIST satisfying the +predicate PRED, in ascending order." + (apply 'append (--map-indexed (when (funcall pred it) (list it-index)) list))) + +(defmacro --find-indices (form list) + "Anaphoric version of `-find-indices'." + (declare (debug (form form))) + `(-find-indices (lambda (it) ,form) ,list)) + +(defun -find-index (pred list) + "Take a predicate PRED and a LIST and return the index of the +first element in the list satisfying the predicate, or nil if +there is no such element." + (car (-find-indices pred list))) + +(defmacro --find-index (form list) + "Anaphoric version of `-find-index'." + (declare (debug (form form))) + `(-find-index (lambda (it) ,form) ,list)) + +(defun -find-last-index (pred list) + "Take a predicate PRED and a LIST and return the index of the +last element in the list satisfying the predicate, or nil if +there is no such element." + (-last-item (-find-indices pred list))) + +(defmacro --find-last-index (form list) + "Anaphoric version of `-find-last-index'." + `(-find-last-index (lambda (it) ,form) ,list)) + +(defun -select-by-indices (indices list) + "Return a list whose elements are elements from LIST selected +as `(nth i list)` for all i from INDICES." + (let (r) + (--each indices + (!cons (nth it list) r)) + (nreverse r))) + +(defmacro -> (x &optional form &rest more) + "Thread the expr through the forms. Insert X as the second item +in the first form, making a list of it if it is not a list +already. If there are more forms, insert the first form as the +second item in second form, etc." + (cond + ((null form) x) + ((null more) (if (listp form) + `(,(car form) ,x ,@(cdr form)) + (list form x))) + (:else `(-> (-> ,x ,form) ,@more)))) + +(defmacro ->> (x &optional form &rest more) + "Thread the expr through the forms. Insert X as the last item +in the first form, making a list of it if it is not a list +already. If there are more forms, insert the first form as the +last item in second form, etc." + (cond + ((null form) x) + ((null more) (if (listp form) + `(,@form ,x) + (list form x))) + (:else `(->> (->> ,x ,form) ,@more)))) + +(defmacro --> (x form &rest more) + "Thread the expr through the forms. Insert X at the position +signified by the token `it' in the first form. If there are more +forms, insert the first form at the position signified by `it' in +in second form, etc." + (if (null more) + (if (listp form) + (--map-when (eq it 'it) x form) + (list form x)) + `(--> (--> ,x ,form) ,@more))) + +(defmacro -some-> (x &optional form &rest more) + "When expr is non-nil, thread it through the first form (via `->'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some-> (-when-let (,result ,x) + (-> ,result ,form)) + ,@more)))) + +(defmacro -some->> (x &optional form &rest more) + "When expr is non-nil, thread it through the first form (via `->>'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some->> (-when-let (,result ,x) + (->> ,result ,form)) + ,@more)))) + +(defmacro -some--> (x &optional form &rest more) + "When expr in non-nil, thread it through the first form (via `-->'), +and when that result is non-nil, through the next form, etc." + (if (null form) x + (let ((result (make-symbol "result"))) + `(-some--> (-when-let (,result ,x) + (--> ,result ,form)) + ,@more)))) + +(defun -grade-up (comparator list) + "Grade elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in ascending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car it) (car other))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + +(defun -grade-down (comparator list) + "Grade elements of LIST using COMPARATOR relation, yielding a +permutation vector such that applying this permutation to LIST +sorts it in descending order." + ;; ugly hack to "fix" lack of lexical scope + (let ((comp `(lambda (it other) (funcall ',comparator (car other) (car it))))) + (->> (--map-indexed (cons it it-index) list) + (-sort comp) + (-map 'cdr)))) + +(defvar dash--source-counter 0 + "Monotonic counter for generated symbols.") + +(defun dash--match-make-source-symbol () + "Generate a new dash-source symbol. + +All returned symbols are guaranteed to be unique." + (prog1 (make-symbol (format "--dash-source-%d--" dash--source-counter)) + (setq dash--source-counter (1+ dash--source-counter)))) + +(defun dash--match-ignore-place-p (symbol) + "Return non-nil if SYMBOL is a symbol and starts with _." + (and (symbolp symbol) + (eq (aref (symbol-name symbol) 0) ?_))) + +(defun dash--match-cons-skip-cdr (skip-cdr source) + "Helper function generating idiomatic shifting code." + (cond + ((= skip-cdr 0) + `(pop ,source)) + (t + `(prog1 ,(dash--match-cons-get-car skip-cdr source) + (setq ,source ,(dash--match-cons-get-cdr (1+ skip-cdr) source)))))) + +(defun dash--match-cons-get-car (skip-cdr source) + "Helper function generating idiomatic code to get nth car." + (cond + ((= skip-cdr 0) + `(car ,source)) + ((= skip-cdr 1) + `(cadr ,source)) + (t + `(nth ,skip-cdr ,source)))) + +(defun dash--match-cons-get-cdr (skip-cdr source) + "Helper function generating idiomatic code to get nth cdr." + (cond + ((= skip-cdr 0) + source) + ((= skip-cdr 1) + `(cdr ,source)) + (t + `(nthcdr ,skip-cdr ,source)))) + +(defun dash--match-cons (match-form source) + "Setup a cons matching environment and call the real matcher." + (let ((s (dash--match-make-source-symbol)) + (n 0) + (m match-form)) + (while (and (consp m) + (dash--match-ignore-place-p (car m))) + (setq n (1+ n)) (!cdr m)) + (cond + ;; when we only have one pattern in the list, we don't have to + ;; create a temporary binding (--dash-source--) for the source + ;; and just use the input directly + ((and (consp m) + (not (cdr m))) + (dash--match (car m) (dash--match-cons-get-car n source))) + ;; handle other special types + ((> n 0) + (dash--match m (dash--match-cons-get-cdr n source))) + ;; this is the only entry-point for dash--match-cons-1, that's + ;; why we can't simply use the above branch, it would produce + ;; infinite recursion + (t + (cons (list s source) (dash--match-cons-1 match-form s)))))) + +(defun dash--match-cons-1 (match-form source &optional props) + "Match MATCH-FORM against SOURCE. + +MATCH-FORM is a proper or improper list. Each element of +MATCH-FORM is either a symbol, which gets bound to the respective +value in source or another match form which gets destructured +recursively. + +If the cdr of last cons cell in the list is `nil', matching stops +there. + +SOURCE is a proper or improper list." + (let ((skip-cdr (or (plist-get props :skip-cdr) 0))) + (cond + ((consp match-form) + (cond + ((cdr match-form) + (cond + ((and (symbolp (car match-form)) + (memq (car match-form) '(&keys &plist &alist &hash))) + (dash--match-kv match-form (dash--match-cons-get-cdr skip-cdr source))) + ((dash--match-ignore-place-p (car match-form)) + (dash--match-cons-1 (cdr match-form) source + (plist-put props :skip-cdr (1+ skip-cdr)))) + (t + (-concat (dash--match (car match-form) (dash--match-cons-skip-cdr skip-cdr source)) + (dash--match-cons-1 (cdr match-form) source))))) + (t ;; Last matching place, no need for shift + (dash--match (car match-form) (dash--match-cons-get-car skip-cdr source))))) + ((eq match-form nil) + nil) + (t ;; Handle improper lists. Last matching place, no need for shift + (dash--match match-form (dash--match-cons-get-cdr skip-cdr source)))))) + +(defun dash--vector-tail (seq start) + "Return the tail of SEQ starting at START." + (cond + ((vectorp seq) + (let* ((re-length (- (length seq) start)) + (re (make-vector re-length 0))) + (--dotimes re-length (aset re it (aref seq (+ it start)))) + re)) + ((stringp seq) + (substring seq start)))) + +(defun dash--match-vector (match-form source) + "Setup a vector matching environment and call the real matcher." + (let ((s (dash--match-make-source-symbol))) + (cond + ;; don't bind `s' if we only have one sub-pattern + ((= (length match-form) 1) + (dash--match (aref match-form 0) `(aref ,source 0))) + ;; if the source is a symbol, we don't need to re-bind it + ((symbolp source) + (dash--match-vector-1 match-form source)) + ;; don't bind `s' if we only have one sub-pattern which is not ignored + ((let* ((ignored-places (mapcar 'dash--match-ignore-place-p match-form)) + (ignored-places-n (length (-remove 'null ignored-places)))) + (when (= ignored-places-n (1- (length match-form))) + (let ((n (-find-index 'null ignored-places))) + (dash--match (aref match-form n) `(aref ,source ,n)))))) + (t + (cons (list s source) (dash--match-vector-1 match-form s)))))) + +(defun dash--match-vector-1 (match-form source) + "Match MATCH-FORM against SOURCE. + +MATCH-FORM is a vector. Each element of MATCH-FORM is either a +symbol, which gets bound to the respective value in source or +another match form which gets destructured recursively. + +If second-from-last place in MATCH-FORM is the symbol &rest, the +next element of the MATCH-FORM is matched against the tail of +SOURCE, starting at index of the &rest symbol. This is +conceptually the same as the (head . tail) match for improper +lists, where dot plays the role of &rest. + +SOURCE is a vector. + +If the MATCH-FORM vector is shorter than SOURCE vector, only +the (length MATCH-FORM) places are bound, the rest of the SOURCE +is discarded." + (let ((i 0) + (l (length match-form)) + (re)) + (while (< i l) + (let ((m (aref match-form i))) + (push (cond + ((and (symbolp m) + (eq m '&rest)) + (prog1 (dash--match + (aref match-form (1+ i)) + `(dash--vector-tail ,source ,i)) + (setq i l))) + ((and (symbolp m) + ;; do not match symbols starting with _ + (not (eq (aref (symbol-name m) 0) ?_))) + (list (list m `(aref ,source ,i)))) + ((not (symbolp m)) + (dash--match m `(aref ,source ,i)))) + re) + (setq i (1+ i)))) + (-flatten-n 1 (nreverse re)))) + +(defun dash--match-kv (match-form source) + "Setup a kv matching environment and call the real matcher. + +kv can be any key-value store, such as plist, alist or hash-table." + (let ((s (dash--match-make-source-symbol))) + (cond + ;; don't bind `s' if we only have one sub-pattern (&type key val) + ((= (length match-form) 3) + (dash--match-kv-1 (cdr match-form) source (car match-form))) + ;; if the source is a symbol, we don't need to re-bind it + ((symbolp source) + (dash--match-kv-1 (cdr match-form) source (car match-form))) + (t + (cons (list s source) (dash--match-kv-1 (cdr match-form) s (car match-form))))))) + +(defun dash--match-kv-1 (match-form source type) + "Match MATCH-FORM against SOURCE of type TYPE. + +MATCH-FORM is a proper list of the form (key1 place1 ... keyN +placeN). Each placeK is either a symbol, which gets bound to the +value of keyK retrieved from the key-value store, or another +match form which gets destructured recursively. + +SOURCE is a key-value store of type TYPE, which can be a plist, +an alist or a hash table. + +TYPE is a token specifying the type of the key-value store. +Valid values are &plist, &alist and &hash." + (-flatten-n 1 (-map + (lambda (kv) + (let* ((k (car kv)) + (v (cadr kv)) + (getter (cond + ((or (eq type '&plist) (eq type '&keys)) + `(plist-get ,source ,k)) + ((eq type '&alist) + `(cdr (assoc ,k ,source))) + ((eq type '&hash) + `(gethash ,k ,source))))) + (cond + ((symbolp v) + (list (list v getter))) + (t (dash--match v getter))))) + (-partition 2 match-form)))) + +(defun dash--match-symbol (match-form source) + "Bind a symbol. + +This works just like `let', there is no destructuring." + (list (list match-form source))) + +(defun dash--match (match-form source) + "Match MATCH-FORM against SOURCE. + +This function tests the MATCH-FORM and dispatches to specific +matchers based on the type of the expression. + +Key-value stores are disambiguated by placing a token &plist, +&alist or &hash as a first item in the MATCH-FORM." + (cond + ((symbolp match-form) + (dash--match-symbol match-form source)) + ((consp match-form) + (cond + ;; Handle the "x &as" bindings first. + ((and (consp (cdr match-form)) + (symbolp (car match-form)) + (eq '&as (cadr match-form))) + (let ((s (car match-form))) + (cons (list s source) + (dash--match (cddr match-form) s)))) + ((memq (car match-form) '(&keys &plist &alist &hash)) + (dash--match-kv match-form source)) + (t (dash--match-cons match-form source)))) + ((vectorp match-form) + ;; We support the &as binding in vectors too + (cond + ((and (> (length match-form) 2) + (symbolp (aref match-form 0)) + (eq '&as (aref match-form 1))) + (let ((s (aref match-form 0))) + (cons (list s source) + (dash--match (dash--vector-tail match-form 2) s)))) + (t (dash--match-vector match-form source)))))) + +(defmacro -let* (varlist &rest body) + "Bind variables according to VARLIST then eval BODY. + +VARLIST is a list of lists of the form (PATTERN SOURCE). Each +PATTERN is matched against the SOURCE structurally. SOURCE is +only evaluated once for each PATTERN. + +Each SOURCE can refer to the symbols already bound by this +VARLIST. This is useful if you want to destructure SOURCE +recursively but also want to name the intermediate structures. + +See `-let' for the list of all possible patterns." + (declare (debug ((&rest (sexp form)) body)) + (indent 1)) + (let ((bindings (--mapcat (dash--match (car it) (cadr it)) varlist))) + `(let* ,bindings + ,@body))) + +(defmacro -let (varlist &rest body) + "Bind variables according to VARLIST then eval BODY. + +VARLIST is a list of lists of the form (PATTERN SOURCE). Each +PATTERN is matched against the SOURCE \"structurally\". SOURCE +is only evaluated once for each PATTERN. Each PATTERN is matched +recursively, and can therefore contain sub-patterns which are +matched against corresponding sub-expressions of SOURCE. + +All the SOURCEs are evalled before any symbols are +bound (i.e. \"in parallel\"). + +If VARLIST only contains one (PATTERN SOURCE) element, you can +optionally specify it using a vector and discarding the +outer-most parens. Thus + + (-let ((PATTERN SOURCE)) ..) + +becomes + + (-let [PATTERN SOURCE] ..). + +`-let' uses a convention of not binding places (symbols) starting +with _ whenever it's possible. You can use this to skip over +entries you don't care about. However, this is not *always* +possible (as a result of implementation) and these symbols might +get bound to undefined values. + +Following is the overview of supported patterns. Remember that +patterns can be matched recursively, so every a, b, aK in the +following can be a matching construct and not necessarily a +symbol/variable. + +Symbol: + + a - bind the SOURCE to A. This is just like regular `let'. + +Conses and lists: + + (a) - bind `car' of cons/list to A + + (a . b) - bind car of cons to A and `cdr' to B + + (a b) - bind car of list to A and `cadr' to B + + (a1 a2 a3 ...) - bind 0th car of list to A1, 1st to A2, 2nd to A3 ... + + (a1 a2 a3 ... aN . rest) - as above, but bind the Nth cdr to REST. + +Vectors: + + [a] - bind 0th element of a non-list sequence to A (works with + vectors, strings, bit arrays...) + + [a1 a2 a3 ...] - bind 0th element of non-list sequence to A0, 1st to + A1, 2nd to A2, ... + If the PATTERN is shorter than SOURCE, the values at + places not in PATTERN are ignored. + If the PATTERN is longer than SOURCE, an `error' is + thrown. + + [a1 a2 a3 ... &rest rest] - as above, but bind the rest of + the sequence to REST. This is + conceptually the same as improper list + matching (a1 a2 ... aN . rest) + +Key/value stores: + + (&plist key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE plist to aK. If the + value is not found, aK is nil. + + (&alist key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE alist to aK. If the + value is not found, aK is nil. + + (&hash key0 a0 ... keyN aN) - bind value mapped by keyK in the + SOURCE hash table to aK. If the + value is not found, aK is nil. + +Further, special keyword &keys supports \"inline\" matching of +plist-like key-value pairs, similarly to &keys keyword of +`cl-defun'. + + (a1 a2 ... aN &keys key1 b1 ... keyN bK) + +This binds N values from the list to a1 ... aN, then interprets +the cdr as a plist (see key/value matching above). + +You can name the source using the syntax SYMBOL &as PATTERN. +This syntax works with lists (proper or improper), vectors and +all types of maps. + + (list &as a b c) (list 1 2 3) + +binds A to 1, B to 2, C to 3 and LIST to (1 2 3). + +Similarly: + + (bounds &as beg . end) (cons 1 2) + +binds BEG to 1, END to 2 and BOUNDS to (1 . 2). + + (items &as first . rest) (list 1 2 3) + +binds FIRST to 1, REST to (2 3) and ITEMS to (1 2 3) + + [vect &as _ b c] [1 2 3] + +binds B to 2, C to 3 and VECT to [1 2 3] (_ avoids binding as usual). + + (plist &as &plist :b b) (list :a 1 :b 2 :c 3) + +binds B to 2 and PLIST to (:a 1 :b 2 :c 3). Same for &alist and &hash. + +This is especially useful when we want to capture the result of a +computation and destructure at the same time. Consider the +form (function-returning-complex-structure) returning a list of +two vectors with two items each. We want to capture this entire +result and pass it to another computation, but at the same time +we want to get the second item from each vector. We can achieve +it with pattern + + (result &as [_ a] [_ b]) (function-returning-complex-structure) + +Note: Clojure programmers may know this feature as the \":as +binding\". The difference is that we put the &as at the front +because we need to support improper list binding." + (declare (debug ([&or (&rest (sexp form)) + (vector [&rest [sexp form]])] + body)) + (indent 1)) + (if (vectorp varlist) + `(let* ,(dash--match (aref varlist 0) (aref varlist 1)) + ,@body) + (let* ((inputs (--map-indexed (list (make-symbol (format "input%d" it-index)) (cadr it)) varlist)) + (new-varlist (--map (list (caar it) (cadr it)) (-zip varlist inputs)))) + `(let ,inputs + (-let* ,new-varlist ,@body))))) + +(defmacro -lambda (match-form &rest body) + "Return a lambda which destructures its input as MATCH-FORM and executes BODY. + +Note that you have to enclose the MATCH-FORM in a pair of parens, +such that: + + (-lambda (x) body) + (-lambda (x y ...) body) + +has the usual semantics of `lambda'. Furthermore, these get +translated into normal lambda, so there is no performance +penalty. + +See `-let' for the description of destructuring mechanism." + (declare (doc-string 2) (indent defun) + (debug (&define sexp + [&optional stringp] + [&optional ("interactive" interactive)] + def-body))) + (cond + ((not (consp match-form)) + (signal 'wrong-type-argument "match-form must be a list")) + ;; no destructuring, so just return regular lambda to make things faster + ((-all? 'symbolp match-form) + `(lambda ,match-form ,@body)) + (t + (let* ((inputs (--map-indexed (list it (make-symbol (format "input%d" it-index))) match-form))) + ;; TODO: because inputs to the lambda are evaluated only once, + ;; -let* need not to create the extra bindings to ensure that. + ;; We should find a way to optimize that. Not critical however. + `(lambda ,(--map (cadr it) inputs) + (-let* ,inputs ,@body)))))) + +(defmacro -if-let* (vars-vals then &rest else) + "If all VALS evaluate to true, bind them to their corresponding +VARS and do THEN, otherwise do ELSE. VARS-VALS should be a list +of (VAR VAL) pairs. + +Note: binding is done according to `-let*'. VALS are evaluated +sequentially, and evaluation stops after the first nil VAL is +encountered." + (declare (debug ((&rest (sexp form)) form body)) + (indent 2)) + (->> vars-vals + (--mapcat (dash--match (car it) (cadr it))) + (--reduce-r-from + (let ((var (car it)) + (val (cadr it))) + `(let ((,var ,val)) + (if ,var ,acc ,@else))) + then))) + +(defmacro -if-let (var-val then &rest else) + "If VAL evaluates to non-nil, bind it to VAR and do THEN, +otherwise do ELSE. VAR-VAL should be a (VAR VAL) pair. + +Note: binding is done according to `-let'." + (declare (debug ((sexp form) form body)) + (indent 2)) + `(-if-let* (,var-val) ,then ,@else)) + +(defmacro --if-let (val then &rest else) + "If VAL evaluates to non-nil, bind it to `it' and do THEN, +otherwise do ELSE." + (declare (debug (form form body)) + (indent 2)) + `(-if-let (it ,val) ,then ,@else)) + +(defmacro -when-let* (vars-vals &rest body) + "If all VALS evaluate to true, bind them to their corresponding +VARS and execute body. VARS-VALS should be a list of (VAR VAL) +pairs. + +Note: binding is done according to `-let*'. VALS are evaluated +sequentially, and evaluation stops after the first nil VAL is +encountered." + (declare (debug ((&rest (sexp form)) body)) + (indent 1)) + `(-if-let* ,vars-vals (progn ,@body))) + +(defmacro -when-let (var-val &rest body) + "If VAL evaluates to non-nil, bind it to VAR and execute body. +VAR-VAL should be a (VAR VAL) pair. + +Note: binding is done according to `-let'." + (declare (debug ((sexp form) body)) + (indent 1)) + `(-if-let ,var-val (progn ,@body))) + +(defmacro --when-let (val &rest body) + "If VAL evaluates to non-nil, bind it to `it' and execute +body." + (declare (debug (form body)) + (indent 1)) + `(--if-let ,val (progn ,@body))) + +(defvar -compare-fn nil + "Tests for equality use this function or `equal' if this is nil. +It should only be set using dynamic scope with a let, like: + + (let ((-compare-fn #'=)) (-union numbers1 numbers2 numbers3)") + +(defun -distinct (list) + "Return a new list with all duplicates removed. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil. + +Alias: `-uniq'" + (let (result) + (--each list (unless (-contains? result it) (!cons it result))) + (nreverse result))) + +(defalias '-uniq '-distinct) + +(defun -union (list list2) + "Return a new list containing the elements of LIST1 and elements of LIST2 that are not in LIST1. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + ;; We fall back to iteration implementation if the comparison + ;; function isn't one of `eq', `eql' or `equal'. + (let* ((result (reverse list)) + ;; TODO: get rid of this dynamic variable, pass it as an + ;; argument instead. + (-compare-fn (if (bound-and-true-p -compare-fn) + -compare-fn + 'equal))) + (if (memq -compare-fn '(eq eql equal)) + (let ((ht (make-hash-table :test -compare-fn))) + (--each list (puthash it t ht)) + (--each list2 (unless (gethash it ht) (!cons it result)))) + (--each list2 (unless (-contains? result it) (!cons it result)))) + (nreverse result))) + +(defun -intersection (list list2) + "Return a new list containing only the elements that are members of both LIST and LIST2. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + (--filter (-contains? list2 it) list)) + +(defun -difference (list list2) + "Return a new list with only the members of LIST that are not in LIST2. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + (--filter (not (-contains? list2 it)) list)) + +(defun -contains? (list element) + "Return non-nil if LIST contains ELEMENT. + +The test for equality is done with `equal', or with `-compare-fn' +if that's non-nil. + +Alias: `-contains-p'" + (not + (null + (cond + ((null -compare-fn) (member element list)) + ((eq -compare-fn 'eq) (memq element list)) + ((eq -compare-fn 'eql) (memql element list)) + (t + (let ((lst list)) + (while (and lst + (not (funcall -compare-fn element (car lst)))) + (setq lst (cdr lst))) + lst)))))) + +(defalias '-contains-p '-contains?) + +(defun -same-items? (list list2) + "Return true if LIST and LIST2 has the same items. + +The order of the elements in the lists does not matter. + +Alias: `-same-items-p'" + (let ((length-a (length list)) + (length-b (length list2))) + (and + (= length-a length-b) + (= length-a (length (-intersection list list2)))))) + +(defalias '-same-items-p '-same-items?) + +(defun -is-prefix? (prefix list) + "Return non-nil if PREFIX is prefix of LIST. + +Alias: `-is-prefix-p'" + (--each-while list (equal (car prefix) it) + (!cdr prefix)) + (not prefix)) + +(defun -is-suffix? (suffix list) + "Return non-nil if SUFFIX is suffix of LIST. + +Alias: `-is-suffix-p'" + (-is-prefix? (reverse suffix) (reverse list))) + +(defun -is-infix? (infix list) + "Return non-nil if INFIX is infix of LIST. + +This operation runs in O(n^2) time + +Alias: `-is-infix-p'" + (let (done) + (while (and (not done) list) + (setq done (-is-prefix? infix list)) + (!cdr list)) + done)) + +(defalias '-is-prefix-p '-is-prefix?) +(defalias '-is-suffix-p '-is-suffix?) +(defalias '-is-infix-p '-is-infix?) + +(defun -sort (comparator list) + "Sort LIST, stably, comparing elements using COMPARATOR. +Return the sorted list. LIST is NOT modified by side effects. +COMPARATOR is called with two elements of LIST, and should return non-nil +if the first element should sort before the second." + (sort (copy-sequence list) comparator)) + +(defmacro --sort (form list) + "Anaphoric form of `-sort'." + (declare (debug (form form))) + `(-sort (lambda (it other) ,form) ,list)) + +(defun -list (&rest args) + "Return a list with ARGS. + +If first item of ARGS is already a list, simply return ARGS. If +not, return a list with ARGS as elements." + (let ((arg (car args))) + (if (listp arg) arg args))) + +(defun -repeat (n x) + "Return a list with X repeated N times. +Return nil if N is less than 1." + (let (ret) + (--dotimes n (!cons x ret)) + ret)) + +(defun -sum (list) + "Return the sum of LIST." + (apply '+ list)) + +(defun -product (list) + "Return the product of LIST." + (apply '* list)) + +(defun -max (list) + "Return the largest value from LIST of numbers or markers." + (apply 'max list)) + +(defun -min (list) + "Return the smallest value from LIST of numbers or markers." + (apply 'min list)) + +(defun -max-by (comparator list) + "Take a comparison function COMPARATOR and a LIST and return +the greatest element of the list by the comparison function. + +See also combinator `-on' which can transform the values before +comparing them." + (--reduce (if (funcall comparator it acc) it acc) list)) + +(defun -min-by (comparator list) + "Take a comparison function COMPARATOR and a LIST and return +the least element of the list by the comparison function. + +See also combinator `-on' which can transform the values before +comparing them." + (--reduce (if (funcall comparator it acc) acc it) list)) + +(defmacro --max-by (form list) + "Anaphoric version of `-max-by'. + +The items for the comparator form are exposed as \"it\" and \"other\"." + (declare (debug (form form))) + `(-max-by (lambda (it other) ,form) ,list)) + +(defmacro --min-by (form list) + "Anaphoric version of `-min-by'. + +The items for the comparator form are exposed as \"it\" and \"other\"." + (declare (debug (form form))) + `(-min-by (lambda (it other) ,form) ,list)) + +(defun -iterate (fun init n) + "Return a list of iterated applications of FUN to INIT. + +This means a list of form: + + (init (fun init) (fun (fun init)) ...) + +N is the length of the returned list." + (if (= n 0) nil + (let ((r (list init))) + (--dotimes (1- n) + (push (funcall fun (car r)) r)) + (nreverse r)))) + +(defun -fix (fn list) + "Compute the (least) fixpoint of FN with initial input LIST. + +FN is called at least once, results are compared with `equal'." + (let ((re (funcall fn list))) + (while (not (equal list re)) + (setq list re) + (setq re (funcall fn re))) + re)) + +(defmacro --fix (form list) + "Anaphoric form of `-fix'." + `(-fix (lambda (it) ,form) ,list)) + +(defun -unfold (fun seed) + "Build a list from SEED using FUN. + +This is \"dual\" operation to `-reduce-r': while -reduce-r +consumes a list to produce a single value, `-unfold' takes a +seed value and builds a (potentially infinite!) list. + +FUN should return `nil' to stop the generating process, or a +cons (A . B), where A will be prepended to the result and B is +the new seed." + (let ((last (funcall fun seed)) r) + (while last + (push (car last) r) + (setq last (funcall fun (cdr last)))) + (nreverse r))) + +(defmacro --unfold (form seed) + "Anaphoric version of `-unfold'." + (declare (debug (form form))) + `(-unfold (lambda (it) ,form) ,seed)) + +(defun -cons-pair? (con) + "Return non-nil if CON is true cons pair. +That is (A . B) where B is not a list." + (and (listp con) + (not (listp (cdr con))))) + +(defun -cons-to-list (con) + "Convert a cons pair to a list with `car' and `cdr' of the pair respectively." + (list (car con) (cdr con))) + +(defun -value-to-list (val) + "Convert a value to a list. + +If the value is a cons pair, make a list with two elements, `car' +and `cdr' of the pair respectively. + +If the value is anything else, wrap it in a list." + (cond + ((-cons-pair? val) (-cons-to-list val)) + (t (list val)))) + +(defun -tree-mapreduce-from (fn folder init-value tree) + "Apply FN to each element of TREE, and make a list of the results. +If elements of TREE are lists themselves, apply FN recursively to +elements of these nested lists. + +Then reduce the resulting lists using FOLDER and initial value +INIT-VALUE. See `-reduce-r-from'. + +This is the same as calling `-tree-reduce-from' after `-tree-map' +but is twice as fast as it only traverse the structure once." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (-reduce-r-from folder init-value (mapcar (lambda (x) (-tree-mapreduce-from fn folder init-value x)) tree))) + (t (funcall fn tree)))) + +(defmacro --tree-mapreduce-from (form folder init-value tree) + "Anaphoric form of `-tree-mapreduce-from'." + (declare (debug (form form form form))) + `(-tree-mapreduce-from (lambda (it) ,form) (lambda (it acc) ,folder) ,init-value ,tree)) + +(defun -tree-mapreduce (fn folder tree) + "Apply FN to each element of TREE, and make a list of the results. +If elements of TREE are lists themselves, apply FN recursively to +elements of these nested lists. + +Then reduce the resulting lists using FOLDER and initial value +INIT-VALUE. See `-reduce-r-from'. + +This is the same as calling `-tree-reduce' after `-tree-map' +but is twice as fast as it only traverse the structure once." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (-reduce-r folder (mapcar (lambda (x) (-tree-mapreduce fn folder x)) tree))) + (t (funcall fn tree)))) + +(defmacro --tree-mapreduce (form folder tree) + "Anaphoric form of `-tree-mapreduce'." + (declare (debug (form form form))) + `(-tree-mapreduce (lambda (it) ,form) (lambda (it acc) ,folder) ,tree)) + +(defun -tree-map (fn tree) + "Apply FN to each element of TREE while preserving the tree structure." + (cond + ((not tree) nil) + ((-cons-pair? tree) (funcall fn tree)) + ((listp tree) + (mapcar (lambda (x) (-tree-map fn x)) tree)) + (t (funcall fn tree)))) + +(defmacro --tree-map (form tree) + "Anaphoric form of `-tree-map'." + (declare (debug (form form))) + `(-tree-map (lambda (it) ,form) ,tree)) + +(defun -tree-reduce-from (fn init-value tree) + "Use FN to reduce elements of list TREE. +If elements of TREE are lists themselves, apply the reduction recursively. + +FN is first applied to INIT-VALUE and first element of the list, +then on this result and second element from the list etc. + +The initial value is ignored on cons pairs as they always contain +two elements." + (cond + ((not tree) nil) + ((-cons-pair? tree) tree) + ((listp tree) + (-reduce-r-from fn init-value (mapcar (lambda (x) (-tree-reduce-from fn init-value x)) tree))) + (t tree))) + +(defmacro --tree-reduce-from (form init-value tree) + "Anaphoric form of `-tree-reduce-from'." + (declare (debug (form form form))) + `(-tree-reduce-from (lambda (it acc) ,form) ,init-value ,tree)) + +(defun -tree-reduce (fn tree) + "Use FN to reduce elements of list TREE. +If elements of TREE are lists themselves, apply the reduction recursively. + +FN is first applied to first element of the list and second +element, then on this result and third element from the list etc. + +See `-reduce-r' for how exactly are lists of zero or one element handled." + (cond + ((not tree) nil) + ((-cons-pair? tree) tree) + ((listp tree) + (-reduce-r fn (mapcar (lambda (x) (-tree-reduce fn x)) tree))) + (t tree))) + +(defmacro --tree-reduce (form tree) + "Anaphoric form of `-tree-reduce'." + (declare (debug (form form))) + `(-tree-reduce (lambda (it acc) ,form) ,tree)) + +(defun -tree-map-nodes (pred fun tree) + "Call FUN on each node of TREE that satisfies PRED. + +If PRED returns nil, continue descending down this node. If PRED +returns non-nil, apply FUN to this node and do not descend +further." + (if (funcall pred tree) + (funcall fun tree) + (if (and (listp tree) + (not (-cons-pair? tree))) + (-map (lambda (x) (-tree-map-nodes pred fun x)) tree) + tree))) + +(defmacro --tree-map-nodes (pred form tree) + "Anaphoric form of `-tree-map-nodes'." + `(-tree-map-nodes (lambda (it) ,pred) (lambda (it) ,form) ,tree)) + +(defun -tree-seq (branch children tree) + "Return a sequence of the nodes in TREE, in depth-first search order. + +BRANCH is a predicate of one argument that returns non-nil if the +passed argument is a branch, that is, a node that can have children. + +CHILDREN is a function of one argument that returns the children +of the passed branch node. + +Non-branch nodes are simply copied." + (cons tree + (when (funcall branch tree) + (-mapcat (lambda (x) (-tree-seq branch children x)) + (funcall children tree))))) + +(defmacro --tree-seq (branch children tree) + "Anaphoric form of `-tree-seq'." + `(-tree-seq (lambda (it) ,branch) (lambda (it) ,children) ,tree)) + +(defun -clone (list) + "Create a deep copy of LIST. +The new list has the same elements and structure but all cons are +replaced with new ones. This is useful when you need to clone a +structure such as plist or alist." + (-tree-map 'identity list)) + +(defun dash-enable-font-lock () + "Add syntax highlighting to dash functions, macros and magic values." + (eval-after-load "lisp-mode" + '(progn + (let ((new-keywords '( + "-each" + "--each" + "-each-while" + "--each-while" + "-dotimes" + "--dotimes" + "-map" + "--map" + "-reduce-from" + "--reduce-from" + "-reduce" + "--reduce" + "-reduce-r-from" + "--reduce-r-from" + "-reduce-r" + "--reduce-r" + "-filter" + "--filter" + "-select" + "--select" + "-remove" + "--remove" + "-reject" + "--reject" + "-remove-first" + "--remove-first" + "-reject-first" + "--reject-first" + "-remove-last" + "--remove-last" + "-reject-last" + "--reject-last" + "-remove-item" + "-non-nil" + "-keep" + "--keep" + "-map-indexed" + "--map-indexed" + "-splice" + "--splice" + "-splice-list" + "--splice-list" + "-map-when" + "--map-when" + "-replace-where" + "--replace-where" + "-map-first" + "--map-first" + "-map-last" + "--map-last" + "-replace" + "-replace-first" + "-replace-last" + "-flatten" + "-flatten-n" + "-concat" + "-mapcat" + "--mapcat" + "-copy" + "-cons*" + "-snoc" + "-first" + "--first" + "-find" + "--find" + "-some" + "--some" + "-any" + "--any" + "-last" + "--last" + "-first-item" + "-last-item" + "-butlast" + "-count" + "--count" + "-any?" + "--any?" + "-some?" + "--some?" + "-any-p" + "--any-p" + "-some-p" + "--some-p" + "-all?" + "--all?" + "-every?" + "--every?" + "-all-p" + "--all-p" + "-every-p" + "--every-p" + "-none?" + "--none?" + "-none-p" + "--none-p" + "-only-some?" + "--only-some?" + "-only-some-p" + "--only-some-p" + "-slice" + "-take" + "-drop" + "-take-while" + "--take-while" + "-drop-while" + "--drop-while" + "-split-at" + "-rotate" + "-insert-at" + "-replace-at" + "-update-at" + "--update-at" + "-remove-at" + "-remove-at-indices" + "-split-with" + "--split-with" + "-split-on" + "-split-when" + "--split-when" + "-separate" + "--separate" + "-partition-all-in-steps" + "-partition-in-steps" + "-partition-all" + "-partition" + "-partition-by" + "--partition-by" + "-partition-by-header" + "--partition-by-header" + "-group-by" + "--group-by" + "-interpose" + "-interleave" + "-zip-with" + "--zip-with" + "-zip" + "-zip-fill" + "-cycle" + "-pad" + "-annotate" + "--annotate" + "-table" + "-table-flat" + "-partial" + "-elem-index" + "-elem-indices" + "-find-indices" + "--find-indices" + "-find-index" + "--find-index" + "-find-last-index" + "--find-last-index" + "-select-by-indices" + "-grade-up" + "-grade-down" + "->" + "->>" + "-->" + "-when-let" + "-when-let*" + "--when-let" + "-if-let" + "-if-let*" + "--if-let" + "-let*" + "-let" + "-lambda" + "-distinct" + "-uniq" + "-union" + "-intersection" + "-difference" + "-contains?" + "-contains-p" + "-same-items?" + "-same-items-p" + "-is-prefix-p" + "-is-prefix?" + "-is-suffix-p" + "-is-suffix?" + "-is-infix-p" + "-is-infix?" + "-sort" + "--sort" + "-list" + "-repeat" + "-sum" + "-product" + "-max" + "-min" + "-max-by" + "--max-by" + "-min-by" + "--min-by" + "-iterate" + "--iterate" + "-fix" + "--fix" + "-unfold" + "--unfold" + "-cons-pair?" + "-cons-to-list" + "-value-to-list" + "-tree-mapreduce-from" + "--tree-mapreduce-from" + "-tree-mapreduce" + "--tree-mapreduce" + "-tree-map" + "--tree-map" + "-tree-reduce-from" + "--tree-reduce-from" + "-tree-reduce" + "--tree-reduce" + "-tree-seq" + "--tree-seq" + "-tree-map-nodes" + "--tree-map-nodes" + "-clone" + "-rpartial" + "-juxt" + "-applify" + "-on" + "-flip" + "-const" + "-cut" + "-orfn" + "-andfn" + "-iteratefn" + "-fixfn" + "-prodfn" + )) + (special-variables '( + "it" + "it-index" + "acc" + "other" + ))) + (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "\\_<" (regexp-opt special-variables 'paren) "\\_>") + 1 font-lock-variable-name-face)) 'append) + (font-lock-add-keywords 'emacs-lisp-mode `((,(concat "(\\s-*" (regexp-opt new-keywords 'paren) "\\_>") + 1 font-lock-keyword-face)) 'append)) + (--each (buffer-list) + (with-current-buffer it + (when (and (eq major-mode 'emacs-lisp-mode) + (boundp 'font-lock-mode) + font-lock-mode) + (font-lock-refresh-defaults))))))) + +(provide 'dash) +;;; dash.el ends here diff --git a/elpa/dash-readme.txt b/elpa/dash-readme.txt new file mode 100644 index 00000000..b6baaa2d --- /dev/null +++ b/elpa/dash-readme.txt @@ -0,0 +1,8 @@ +A modern list api for Emacs. + +See documentation on https://github.com/magnars/dash.el#functions + +**Please note** The lexical binding in this file is not utilised at the +moment. We will take full advantage of lexical binding in an upcoming 3.0 +release of Dash. In the meantime, we've added the pragma to avoid a bug that +you can read more about in https://github.com/magnars/dash.el/issues/130.