diff --git a/init.el b/init.el index f0f565e..94b50fa 100644 --- a/init.el +++ b/init.el @@ -61,6 +61,9 @@ ELPA (or MELPA).") (add-to-list 'load-path prelude-modules-dir) (add-to-list 'load-path prelude-vendor-dir) +(require 'cl) +(require 'dash) + ;; the core stuff (require 'prelude-packages) (require 'prelude-ui) diff --git a/prelude/prelude-core.el b/prelude/prelude-core.el index af153ef..6d54949 100644 --- a/prelude/prelude-core.el +++ b/prelude/prelude-core.el @@ -32,19 +32,8 @@ ;;; Code: -(require 'cl) (require 'thingatpt) -(defun prelude-add-subfolders-to-load-path (parent-dir) - "Adds all first level `parent-dir' subdirs to the -Emacs load path." - (dolist (f (directory-files parent-dir)) - (let ((name (expand-file-name f parent-dir))) - (when (and (file-directory-p name) - (not (equal f "..")) - (not (equal f "."))) - (add-to-list 'load-path name))))) - (defun prelude-open-with () "Simple function that allows us to open the underlying file of a buffer in an external program." diff --git a/prelude/prelude-packages.el b/prelude/prelude-packages.el index 382541a..8ed8253 100644 --- a/prelude/prelude-packages.el +++ b/prelude/prelude-packages.el @@ -32,7 +32,6 @@ ;; Boston, MA 02110-1301, USA. ;;; Code: -(require 'cl) (require 'package) (add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t) @@ -49,9 +48,7 @@ "A list of packages to ensure are installed at launch.") (defun prelude-packages-installed-p () - (loop for p in prelude-packages - when (not (package-installed-p p)) do (return nil) - finally (return t))) + (-all? #'package-installed-p prelude-packages)) (defun prelude-install-packages () (unless (prelude-packages-installed-p) @@ -60,9 +57,9 @@ (package-refresh-contents) (message "%s" " done.") ;; install the missing packages - (dolist (p prelude-packages) - (unless (package-installed-p p) - (package-install p))))) + (-each + (-reject #'package-installed-p prelude-packages) + #'package-install-p))) (prelude-install-packages) diff --git a/vendor/dash.el b/vendor/dash.el new file mode 100644 index 0000000..2eebff8 --- /dev/null +++ b/vendor/dash.el @@ -0,0 +1,692 @@ +;;; dash.el --- A modern list library for Emacs + +;; Copyright (C) 2012 Magnar Sveen + +;; Author: Magnar Sveen +;; Version: 1.0.3 +;; 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 + +;;; Code: + +(defmacro !cons (car cdr) + "Destructive: Sets CDR to the cons of CAR and CDR." + `(setq ,cdr (cons ,car ,cdr))) + +(defmacro !cdr (list) + "Destructive: Sets LIST to the cdr of LIST." + `(setq ,list (cdr ,list))) + +(defmacro --each (list &rest body) + "Anaphoric form of `-each'." + (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))))) + +(put '--each 'lisp-indent-function 1) + +(defun -each (list fn) + "Calls FN with every item in LIST. Returns nil, used for side-effects only." + (--each list (funcall fn it))) + +(defmacro --each-while (list pred &rest body) + "Anaphoric form of `-each-while'." + (let ((l (make-symbol "list")) + (c (make-symbol "continue"))) + `(let ((,l ,list) + (,c t)) + (while (and ,l ,c) + (let ((it (car ,l))) + (if (not ,pred) (setq ,c nil) ,@body)) + (!cdr ,l))))) + +(put '--each-while 'lisp-indent-function 2) + +(defun -each-while (list pred fn) + "Calls FN with every item in LIST while (PRED item) is non-nil. +Returns nil, used for side-effects only." + (--each-while list (funcall pred it) (funcall fn it))) + +(defmacro --dotimes (num &rest body) + "Repeatedly executes BODY (presumably for side-effects) with `it` bound to integers from 0 through n-1." + `(let ((it 0)) + (while (< it ,num) + ,@body + (setq it (1+ it))))) + +(put '--dotimes 'lisp-indent-function 1) + +(defun -dotimes (num fn) + "Repeatedly calls FN (presumably for side-effects) passing in integers from 0 through n-1." + (--dotimes num (funcall fn it))) + +(defun -map (fn list) + "Returns 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'." + `(mapcar (lambda (it) ,form) ,list)) + +(defmacro --reduce-from (form initial-value list) + "Anaphoric form of `-reduce-from'." + `(let ((acc ,initial-value)) + (--each ,list (setq acc ,form)) + acc)) + +(defun -reduce-from (fn initial-value list) + "Returns 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, returns INITIAL-VALUE and +FN is not called. + +In the anaphoric form `--reduce-from', the accumulated value is +exposed as `acc`." + (--reduce-from (funcall fn acc it) initial-value list)) + +(defmacro --reduce (form list) + "Anaphoric form of `-reduce'." + (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) + "Returns 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 returns 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`." + (if list + (-reduce-from fn (car list) (cdr list)) + (funcall fn))) + +(defmacro --filter (form list) + "Anaphoric form of `-filter'." + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (when ,form (!cons it ,r))) + (nreverse ,r)))) + +(defun -filter (pred list) + "Returns 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'." + `(--filter (not ,form) ,list)) + +(defun -remove (pred list) + "Returns 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) + +(defmacro --keep (form list) + "Anaphoric form of `-keep'." + (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) + "Returns a new list of the non-nil results of applying FN to the items in LIST." + (--keep (funcall fn it) list)) + +(defmacro --map-when (pred rep list) + "Anaphoric form of `-map-when'." + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list (!cons (if ,pred ,rep it) ,r)) + (nreverse ,r)))) + +(defmacro --map-indexed (form list) + "Anaphoric form of `-map-indexed'." + (let ((r (make-symbol "result"))) + `(let (,r) + (--each ,list + (!cons ,form ,r)) + (nreverse ,r)))) + +(defun -map-indexed (fn list) + "Returns 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)) + +(defun -map-when (pred rep list) + "Returns 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." + (--map-when (funcall pred it) (funcall rep it) list)) + +(defalias '--replace-where '--map-when) +(defalias '-replace-where '-map-when) + +(defun -flatten (l) + "Takes a nested list L and returns its contents as a single, flat list." + (if (listp l) + (-mapcat '-flatten l) + (list l))) + +(defun -concat (&rest lists) + "Returns a new list with the concatenation of the elements in the supplied LISTS." + (apply 'append lists)) + +(defmacro --mapcat (form list) + "Anaphoric form of `-mapcat'." + `(apply 'append (--map ,form ,list))) + +(defun -mapcat (fn list) + "Returns the result of applying concat to the result of applying map to FN and LIST. +Thus function FN should return a collection." + (--mapcat (funcall fn it) list)) + +(defmacro --first (form list) + "Anaphoric form of `-first'." + (let ((n (make-symbol "needle"))) + `(let (,n) + (--each-while ,list (not ,n) + (when ,form (setq ,n it))) + ,n))) + +(defun -first (pred list) + "Returns 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'." + (--first (funcall pred it) list)) + +(defun ---truthy? (val) + (not (null val))) + +(defmacro --any? (form list) + "Anaphoric form of `-any?'." + `(---truthy? (--first ,form ,list))) + +(defun -any? (pred list) + "Returns t if (PRED x) is non-nil for any x in LIST, else nil. + +Alias: `-some?'" + (--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?'." + (let ((a (make-symbol "all"))) + `(let ((,a t)) + (--each-while ,list ,a (setq ,a ,form)) + (---truthy? ,a)))) + +(defun -all? (pred list) + "Returns t if (PRED x) is non-nil for all x in LIST, else nil. + +Alias: `-every?'" + (--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?'." + `(--all? (not ,form) ,list)) + +(defun -none? (pred list) + "Returns t if (PRED x) is nil for all x in LIST, else nil." + (--none? (funcall pred it) list)) + +(defalias '-none-p '-none?) +(defalias '--none-p '--none?) + +(defmacro --only-some? (form list) + "Anaphoric form of `-only-some?'." + (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) + "Returns `t` if there is a mix of items in LIST that matches and does not match PRED. +Returns `nil` both if all items match the predicate, and if none of the items match the predicate." + (--only-some? (funcall pred it) list)) + +(defalias '-only-some-p '-only-some?) +(defalias '--only-some-p '--only-some?) + +(defun -take (n list) + "Returns 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))) + +(defun -drop (n list) + "Returns the tail of LIST without the first N items." + (--dotimes n (!cdr list)) + list) + +(defmacro --take-while (form list) + "Anaphoric form of `-take-while'." + (let ((r (make-symbol "result"))) + `(let (,r) + (--each-while ,list ,form (!cons it ,r)) + (nreverse ,r)))) + +(defun -take-while (pred list) + "Returns 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'." + (let ((l (make-symbol "list"))) + `(let ((,l ,list)) + (while (and ,l (let ((it (car ,l))) ,form)) + (!cdr ,l)) + ,l))) + +(defun -drop-while (pred list) + "Returns 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) + "Returns a list of ((-take N LIST) (-drop N LIST))" + (list (-take n list) + (-drop n list))) + +(defmacro --split-with (form list) + "Anaphoric form of `-split-with'." + `(list (--take-while ,form ,list) + (--drop-while ,form ,list))) + +(defun -split-with (pred list) + "Returns a list of ((-take-while PRED LIST) (-drop-while PRED LIST))" + (--split-with (funcall pred it) list)) + +(defmacro --separate (form list) + "Anaphoric form of `-separate'." + (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) + "Returns a list of ((-filter PRED LIST) (-remove PRED LIST))." + (--separate (funcall pred it) list)) + +(defun -partition (n list) + "Returns 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." + (let ((result nil) + (sublist nil) + (len 0)) + (while list + (!cons (car list) sublist) + (setq len (1+ len)) + (when (= len n) + (!cons (nreverse sublist) result) + (setq sublist nil) + (setq len 0)) + (!cdr list)) + (nreverse result))) + +(defun -partition-all (n list) + "Returns a new list with the items in LIST grouped into N-sized sublists. +The last group may contain less than N items." + (let (result) + (while list + (!cons (-take n list) result) + (setq list (-drop n list))) + (nreverse result))) + +(defmacro --partition-by (form list) + "Anaphoric form of `-partition-by'." + (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) + "Applies FN to each value in LIST, splitting it each time FN returns a new value." + (--partition-by (funcall fn it) list)) + +(defmacro --group-by (form list) + "Anaphoric form of `-group-by'." + (let ((l (make-symbol "list")) + (v (make-symbol "value")) + (k (make-symbol "key")) + (r (make-symbol "result"))) + `(let ((,l ,list) + ,r) + ;; Convert `list' to an alist and store it in `r'. + (while ,l + (let* ((,v (car ,l)) + (it ,v) + (,k ,form) + (kv (assoc ,k ,r))) + (if kv + (setcdr kv (cons ,v (cdr kv))) + (push (list ,k ,v) ,r)) + (setq ,l (cdr ,l)))) + ;; Reverse lists in each group. + (let ((rest ,r)) + (while rest + (let ((kv (car rest))) + (setcdr kv (nreverse (cdr kv)))) + (setq rest (cdr rest)))) + ;; Reverse order of keys. + (nreverse ,r)))) + +(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) + "Returns 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) + "Returns 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))) + +(defun -partial (fn &rest args) + "Takes a function FN and fewer than the normal arguments to FN, +and returns 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 -rpartial (fn &rest args) + "Takes a function FN and fewer than the normal arguments to FN, +and returns a fn that takes a variable number of additional ARGS. +When called, the returned function calls FN with the additional +args first and then ARGS. + +Requires Emacs 24 or higher." + `(closure (t) (&rest args) + (apply ',fn (append args ',args)))) + +(defun -applify (fn) + "Changes an n-arity function FN to a 1-arity function that +expects a list with n items as arguments" + (apply-partially 'apply fn)) + +(defmacro -> (x &optional form &rest more) + "Threads the expr through the forms. Inserts 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, inserts 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 form &rest more) + "Threads the expr through the forms. Inserts 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, inserts the first form as the +last item in second form, etc." + (if (null more) + (if (listp form) + `(,(car form) ,@(cdr form) ,x) + (list form x)) + `(->> (->> ,x ,form) ,@more))) + +(defmacro --> (x form &rest more) + "Threads the expr through the forms. Inserts X at the position +signified by the token `it' in the first form. If there are more +forms, inserts 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))) + +(put '-> 'lisp-indent-function 1) +(put '->> 'lisp-indent-function 1) +(put '--> 'lisp-indent-function 1) + +(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))) + +(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." + (let (result) + (--each list (!cons it result)) + (--each list2 (unless (-contains? result it) (!cons it result))) + (nreverse result))) + +(defalias '-uniq '-distinct) + +(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)) + +(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 -contains? (list element) + "Return whether LIST contains ELEMENT. +The test for equality is done with `equal', +or with `-compare-fn' if that's non-nil." + (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?) + +(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" + "--filter" + "-filter" + "-select" + "--select" + "--remove" + "-remove" + "-reject" + "--reject" + "--keep" + "-keep" + "-flatten" + "-concat" + "--mapcat" + "-mapcat" + "--first" + "-first" + "--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" + "-take" + "-drop" + "--take-while" + "-take-while" + "--drop-while" + "-drop-while" + "-split-at" + "--split-with" + "-split-with" + "-partition" + "-partition-all" + "-interpose" + "-interleave" + "--map-when" + "-map-when" + "--replace-where" + "-replace-where" + "-partial" + "-rpartial" + "->" + "->>" + "-->" + "-distinct" + "-intersection" + "-difference" + "-contains?" + "-contains-p" + )) + (special-variables '( + "it" + "it-index" + "acc" + ))) + (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