Rewrite package management to be less hackish (untested)

This commit is contained in:
Henrik Lissner 2017-02-03 07:58:16 -05:00
parent 74aa0ab6a7
commit f2a31e9d87
7 changed files with 535 additions and 266 deletions

View file

@ -9,7 +9,10 @@ update: init.el
@$(EMACS) --batch -l core/core.el -f 'doom/packages-update'
clean: init.el
@$(EMACS) --batch -l core/core.el -f 'doom/packages-clean'
@$(EMACS) --batch -l core/core.el -f 'doom/packages-autoremove'
autoloads: init.el
@$(EMACS) --batch -l core/core.el -f 'doom/refresh-autoloads'
compile: init.el clean-elc
@$(EMACS) --batch -l core/core.el -f 'doom/byte-compile'
@ -17,9 +20,6 @@ compile: init.el clean-elc
compile-all: init.el clean-elc
@$(EMACS) --batch -l core/core.el --eval '(doom/byte-compile t)'
autoloads: init.el
@$(EMACS) --batch -l core/core.el -f 'doom/refresh-autoloads'
clean-cache:
@$(EMACS) --batch -l core/core.el --eval '(delete-directory doom-cache-dir t)'

View file

@ -1,151 +1,272 @@
;;; packages.el
(defvar doom-packages-last-refresh nil
"A timestamp indicating the last time `package-refresh-contents' was run.")
;;;###autoload
(defun doom-package-outdated-p (package)
"Determine whether PACKAGE (a symbol) is outdated or not. If outdated, returns
a cons cell, whose car is the current version string of PACKAGE (a symbol), and
whose cdr is the latest version of the package. Be sure to run
`package-refresh-contents' beforehand, or the return value could be out of
date."
(unless package-selected-packages
(doom-initialize))
(when (and (memq package package-selected-packages)
(package-installed-p package)
(quelpa-setup-p))
(let* ((pkg-recipe (cdr (assq 'quelpa quelpa-cache)))
(cur-desc (cadr (or (assq package package-alist)
(assq package package--builtins))))
(cur-version (package-desc-version cur-desc))
(inhibit-message t)
new-version)
(setq new-version
(if pkg-recipe
(let ((ver (quelpa-checkout
pkg-recipe
(f-expand (symbol-name package) quelpa-build-dir))))
(or (and ver (version-to-list ver)) cur-version))
(package-desc-version (cadr (assq package package-archive-contents)))))
(unless (version-list-<= new-version cur-version)
(cons cur-version new-version)))))
(defun doom-refresh-packages ()
"Refresh ELPA packages."
(when (or (not doom-packages-last-refresh)
(> (nth 1 (time-since doom-packages-last-refresh)) 3600))
(doom-initialize)
(package-refresh-contents)
(setq doom-packages-last-refresh (current-time))))
;;;###autoload
(defun doom-package-elpa-p (name)
"Returns non-nil if NAME was a package installed with elpa."
(doom-initialize)
(and (assq name package-alist)
(not (doom-package-quelpa-p name))))
;;;###autoload
(defun doom-package-quelpa-p (name)
"Returns non-nil if NAME was a package installed with quelpa."
(unless (quelpa-setup-p)
(error "Could not initialize quelpa"))
(assq name quelpa-cache))
;;;###autoload
(defun doom-package-outdated-p (name)
"Determine whether NAME (a symbol) is outdated or not. If outdated, returns a
list, whose car is NAME, and cdr the current version list and latest version
list of the package."
(doom-refresh-packages)
(package-read-all-archive-contents)
(when (assq name package-alist)
(let* ((old-version
(package-desc-version (cadr (or (assq name package-alist)
(assq name package--builtins)))))
(new-version
(cond ((doom-package-quelpa-p name)
(let ((recipe (assq name quelpa-cache))
(dir (f-expand (symbol-name name) quelpa-build-dir))
(inhibit-message t))
(or (quelpa-checkout recipe dir)
old-version)))
((doom-package-elpa-p name)
(package-desc-version (cadr (assq name package-archive-contents)))))))
(unless (version-list-<= new-version old-version)
(cons name old-version new-version)))))
;;;###autoload
(defun doom-get-packages (&optional backend)
"Retrieves a list of explicitly installed packages (i.e. non-dependencies).
Each element is a cons cell, whose car is the package symbol and whose cdr is
the quelpa recipe (if any).
BACKEND can be 'quelpa or 'elpa, and will instruct this function to return only
the packages relevant to that backend."
(doom-initialize)
(unless (quelpa-setup-p)
(error "Could not initialize quelpa"))
(--map (cons it (assq it quelpa-cache))
(-intersection (package--find-non-dependencies)
(append (mapcar 'car doom-packages) doom-protected-packages))))
;;;###autoload
(defun doom-get-outdated-packages ()
"Return a list of packages that are out of date. Each element is a sublist,
containing (list package-symbol current-version-string new-version-string). Can
be fed to `doom/packages-update'."
(-non-nil (--map (doom-package-outdated-p (car it)) (doom-get-packages))))
;;;###autoload
(defun doom-get-orphaned-packages ()
"Return a list of packages that are no longer needed or depended on. Can be
fed to `doom/packages-delete'."
(doom-initialize)
(-difference (package--removable-packages)
doom-protected-packages))
;;;###autoload
(defun doom-get-packages-to-install ()
"Return a list of packages that aren't installed, but need to be. Used by
`doom/packages-install'."
(doom-refresh-self)
(--remove (assq (car it) package-alist)
(append doom-packages (-map 'list doom-protected-packages))))
;;
;; Main functions
;;
(defun doom-install-package (name &optional recipe)
"Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an
example; the package name can be omitted)."
(doom-refresh-packages)
(when (package-installed-p name)
(error "%s is already installed" name))
(cond (recipe (quelpa (plist-get plist :recipe)))
(t (package-install name)))
(add-to-list 'doom-packages (cons name recipe))
(package-installed-p name))
(defun doom-update-package (name)
"Updates package NAME if it is out of date, using quelpa or package.el as
appropriate."
(doom-refresh-packages)
(unless (package-installed-p name)
(error "%s isn't installed" name))
(when (doom-package-outdated-p name)
(let (quelpa-modified-p)
(cond ((doom-package-quelpa-p name)
(let ((quelpa-upgrade-p t))
(quelpa it)
(setq quelpa-modified-p t)))
(t
(let ((desc (cadr (assq name package-alist)))
(archive (cadr (assq name package-archive-contents))))
(package-install-from-archive archive)
(delete-directory (package-desc-dir desc) t))
(package-install name))))
(when quelpa-modified-p
(quelpa-save-cache))
(version-list-=
(package-desc-version (cadr (assq name package-alist)))
(package-desc-version (cadr (assq name package-archive-contents))))))
(defun doom-delete-package (name)
"Uninstalls package NAME if it exists, and clears it from `quelpa-cache'."
(doom-initialize)
(unless (package-installed-p name)
(error "%s isn't installed" name))
(let ((desc (cadr (assq package package-alist))))
(package-delete desc))
(when (and (quelpa-setup-p)
(assq name quelpa-cache))
(setq quelpa-cache (delq name quelpa-cache))
(let ((path (f-expand (symbol-name name) quelpa-build-dir)))
(when (f-exists-p path)
(delete-directory path t)))
(quelpa-save-cache))
(not (package-installed-p name)))
;;
;; Interactive commands
;;
;;;###autoload
(defun doom/packages-install ()
"Install missing packages."
(interactive)
(let ((pkg-n (doom-reload-packages :install)))
(if (= pkg-n 0)
(message "Nothing to install")
(message "\nInstalled %s packages:\n%s" pkg-n
(mapconcat (lambda (pkg) (concat "+ " (symbol-name pkg)))
doom-installed-packages "\n")))))
(let ((packages (doom-get-packages-to-install)))
(cond ((not packages)
(message "No packages to install!"))
((not (y-or-n-p
(format "%s packages will be installed:\n%s\n\nProceed?"
(length packages)
(mapconcat (lambda (pkg) (format "+ %s (%s)"
(symbol-name (car pkg))
(cond ((cdr pkg) "QUELPA")
(t "ELPA"))))
packages "\n"))))
(message "Aborted!"))
(t
(doom-message "Installing %s packages" (length packages))
(dolist (pkg packages)
(condition-case ex
(doom-message "%s %s (%s)"
(let ((plist (cdr pkg)))
(if (doom-install-package (car pkg) (cdr pkg))
"Installed"
"Failed to install"))
pkg
(cond ((cdr pkg) "QUELPA")
(t "ELPA")))
('error
(doom-message "Error installing %s: %s" (car pkg) ex))))
(doom-message "Finished!")))))
;;;###autoload
(defun doom/packages-update ()
"Update outdated packages. This includes quelpa-installed packages and ELPA
packages. This will delete old versions of packages as well."
(interactive)
(message "Refreshing packages...")
(doom-initialize t)
(if (not package-alist)
(message "No packages are installed")
(require 'quelpa)
(when (quelpa-setup-p)
(setq quelpa-cache (--filter (package-installed-p (car it)) quelpa-cache)))
(let* ((err 0)
(quelpa-packages (-map 'car quelpa-cache))
(elpa-packages (-difference (package--find-non-dependencies) quelpa-packages))
quelpa-modified-p
outdated-packages)
(message "ELPA\n%s\n\nQUELPA\n%s" elpa-packages quelpa-packages)
(dolist (pkg (append quelpa-packages elpa-packages))
(awhen (doom-package-outdated-p pkg)
(push (list pkg it) outdated-packages)))
(message "\nOUTDATED\n%s" outdated-packages)
;; (cond ((not outdated-packages)
;; (message "Everything is up-to-date"))
(let ((packages (doom-get-outdated-packages)))
(cond ((not packages)
(message "Everything is up-to-date"))
;; ((not (y-or-n-p
;; (format "%s packages will be updated:\n%s\n\nProceed?"
;; (length outdated-packages)
;; (mapconcat (lambda (pkg) (format "%s: %s -> %s"
;; (car pkg)
;; (car (cdr pkg))
;; (cdr (cdr pkg))))
;; (--sort (string-lessp (symbol-name (car it))
;; (symbol-name (car other)))
;; outdated-packages) ", "))))
;; (message "Aborted"))
((not (y-or-n-p
(format "%s packages will be updated:\n%s\n\nProceed?"
(length packages)
(mapconcat (lambda (pkg) (format "%s: %s -> %s"
(car pkg)
(car (cdr pkg))
(cdr (cdr pkg))))
(--sort (string-lessp (symbol-name (car it))
(symbol-name (car other)))
outdated-packages) ", "))))
(message "Aborted!"))
;; (t
;; (dolist (pkg outdated-packages)
;; (condition-case ex
;; (cond ((assq pkg quelpa-outdated-packages)
;; (let ((inhibit-message t))
;; (quelpa package)
;; (setq quelpa-modified-p t)))
;; ((memq pkg elpa-outdated-packages)
;; (let ((desc (cadr (assq pkg package-alist)))
;; (archive (cadr (assoc pkg package-archive-contents))))
;; (package-install-from-archive archive)
;; (delete-directory (package-desc-dir desc) t)))
;; (t (error "Not a valid package")))
;; ('error
;; (setq err (1+ err))
;; (message "ERROR (%s): %s" pkg ex))))))
;; (when quelpa-modified-p
;; (quelpa-save-cache))
;; (if (> err 0)
;; (message "Done, but with %s errors" err)
;; (message "Done"))
)))
(t
(dolist (pkg packages)
(condition-case ex
(doom-message "%s %s"
(if (doom-update-package pkg)
"Updated"
"Failed to update")
pkg)
('error
(doom-message "Error installing %s: %s" pkg ex))))
(doom-message "Finished!")))))
;;;###autoload
(defun doom/packages-clean ()
"Delete packages that are no longer used or depended on."
(defun doom/packages-autoremove ()
(interactive)
(doom-reload-packages)
(let* ((package-selected-packages (-intersection (package--find-non-dependencies)
(append doom-packages doom-protected-packages)))
(packages-to-delete (-difference (package--removable-packages) doom-protected-packages))
quelpa-modified-p)
(cond ((not package-selected-packages)
(message "No packages installed!"))
((not packages-to-delete)
(message "No unused packages to remove."))
(let ((packages (doom-get-orphaned-packages)))
(cond ((not packages)
(message "No unused packages to remove"))
((not (y-or-n-p
(format "%s packages will be deleted:\n%s\n\nProceed?"
(length packages-to-delete)
(mapconcat 'symbol-name (-sort 'string-lessp packages-to-delete) ", "))))
(message "Aborted."))
(length packages)
(mapconcat 'symbol-name (-sort 'string-lessp packages) ", "))))
(message "Aborted!"))
(t
(require 'quelpa)
(quelpa-setup-p)
(dolist (p packages-to-delete)
(package-delete (cadr (assq p package-alist)) t)
(when (and quelpa-cache (assq p quelpa-cache))
(setq quelpa-cache (assq-delete-all p quelpa-cache)
quelpa-modified-p t)))
(when quelpa-modified-p
(quelpa-save-cache))))))
(dolist (pkg packages)
(condition-case ex
(doom-message "%s %s"
(if (doom-delete-package pkg)
"Deleted"
"Failed to delete")
pkg)
('error
(doom-message "Error deleting %s: %s" pkg ex))))
(doom-message "Finished!")))))
;;;###autoload
(defun doom/packages-reload ()
"Reload `load-path' by scanning all packages. Run this if you ran make update
or make clean outside of Emacs."
(interactive)
(doom-initialize t)
(message "Reloaded %s packages" (length package-alist)))
(defalias 'doom/package-install 'package-install)
;;;###autoload
(defun doom/packages-delete (&optional package)
"Attempt to delete PACKAGE. Wraps around `package-delete'."
(interactive)
(doom-reload-packages)
(let* ((pkg (or package (completing-read "Delete package: " doom-packages nil t)))
(pkg-desc (cdr (assq pkg package-alist))))
(unless pkg-desc
(error "Couldn't find the package %s" package))
(package-delete pkg-desc)))
(defun doom/package-delete (&optional package)
(interactive
(list (completing-read "Delete package: " (doom-get-packages))))
(if (package-installed-p package)
(message "%s %s"
(if (doom-delete-package package)
"Deleted"
"Failed to delete")
pkg)
(message "%s isn't installed" package)))
;;;###autoload
(defun doom/package-update (&optional package)
(interactive
(list (completing-read "Update package: " (doom-get-packages))))
(if (doom-package-outdated-p package)
(message "%s %s"
(if (doom-update-package package)
"Updated"
"Failed to update")
pkg)
(message "%s is up-to-date" package)))

View file

@ -177,7 +177,7 @@
(package! pcre2el :commands rxt-quote-pcre)
(package! rotate-text
:quelpa (:fetcher github :repo "debug-ito/rotate-text.el")
:recipe (:fetcher github :repo "debug-ito/rotate-text.el")
:commands (rotate-text rotate-text-backward)
:config (push '("true" "false") rotate-text-words))

View file

@ -85,5 +85,158 @@ Examples:
(t (user-error "associate! invalid rules for mode [%s] (in %s) (match %s) (files %s)"
mode in match files)))))
;; Register keywords for proper indentation (see `map!')
(put ':prefix 'lisp-indent-function 'defun)
(put ':map 'lisp-indent-function 'defun)
(put ':map* 'lisp-indent-function 'defun)
(put ':after 'lisp-indent-function 'defun)
(put ':when 'lisp-indent-function 'defun)
(put ':unless 'lisp-indent-function 'defun)
(put ':leader 'lisp-indent-function 'defun)
(put ':localleader 'lisp-indent-function 'defun)
;;;###autoload
(defmacro map! (&rest rest)
"A nightmare of a key-binding macro that will use `evil-define-key*',
`define-key', `local-set-key' and `global-set-key' depending on context and
plist key flags. It was designed to make binding multiple keys more concise,
like in vim.
If evil isn't loaded, it will ignore evil-specific bindings.
Yes, it tries to do too much. Yes, I only did it to make the \"frontend\" config
that little bit more concise. Yes, I could simply have used the above functions.
But it takes a little insanity to custom write your own emacs.d, so what else
were you expecting?
States
:n normal
:v visual
:i insert
:e emacs
:o operator
:m motion
:r replace
:L local
These can be combined (order doesn't matter), e.g. :nvi will apply to
normal, visual and insert mode. The state resets after the following
key=>def pair.
Capitalize the state flag to make it a local binding.
If omitted, the keybind will be defined globally.
Flags
:unset [KEY] ; unset key
(:map [KEYMAP] [...]) ; inner keybinds are applied to KEYMAP
(:prefix [PREFIX] [...]) ; assign prefix to all inner keybindings
(:after [FEATURE] [...]) ; apply keybinds when [FEATURE] loads
Conditional keybinds
(:when [CONDITION] [...])
(:unless [CONDITION] [...])
Example
(map! :map magit-mode-map
:m \"C-r\" 'do-something ; assign C-r in motion state
:nv \"q\" 'magit-mode-quit-window ; assign to 'q' in normal and visual states
\"C-x C-r\" 'a-global-keybind
(:when IS-MAC
:n \"M-s\" 'some-fn
:i \"M-o\" (lambda (interactive) (message \"Hi\"))))"
(let ((keymaps (if (boundp 'keymaps) keymaps))
(prefix (if (boundp 'prefix) prefix))
(state-map '(("n" . normal)
("v" . visual)
("i" . insert)
("e" . emacs)
("o" . operator)
("m" . motion)
("r" . replace)))
local key def states forms)
(while rest
(setq key (pop rest))
(cond
;; it's a sub expr
((listp key)
(push (macroexpand `(map! ,@key)) forms))
;; it's a flag
((keywordp key)
(when (cond ((eq key :leader)
(push (or +evil-leader ",") rest))
((eq key :localleader)
(push (or +evil-localleader "\\") rest)))
(setq key :prefix))
(pcase key
(:prefix (setq prefix (concat prefix (kbd (pop rest)))))
(:map (setq keymaps (-list (pop rest))))
(:unset `(,(macroexpand `(map! ,(kbd (pop rest))))))
(:after (prog1 `((after! ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:when (prog1 `((if ,(pop rest) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(:unless (prog1 `((if (not ,(pop rest)) ,(macroexpand `(map! ,@rest)))) (setq rest '())))
(otherwise ; might be a state prefix
(mapc (lambda (letter)
(cond ((assoc letter state-map)
(push (cdr (assoc letter state-map)) states))
((string= letter "L")
(setq local t))
(t (user-error "Invalid mode prefix %s in key %s" letter key))))
(split-string (substring (symbol-name key) 1) "" t))
(unless states
(user-error "Unrecognized keyword %s" key))
(when (assoc "L" states)
(cond ((= (length states) 1)
(user-error "local keybinding for %s must accompany another state" key))
((> (length keymaps) 0)
(user-error "local keybinding for %s cannot accompany a keymap" key)))))))
;; It's a key-def pair
((or (stringp key)
(characterp key)
(vectorp key))
(unwind-protect
(catch 'skip
(when (stringp key)
(setq key (kbd key)))
(when prefix
(setq key (if (vectorp key) (vconcat prefix key) (concat prefix key))))
(unless (> (length rest) 0)
(user-error "Map has no definition for %s" key))
(setq def (pop rest))
(push
(cond ((and keymaps states)
(throw 'skip 'evil)
(macroexp-progn
(mapcar (lambda (keymap) `(evil-define-key* ',states ,keymap ,key ,def))
keymaps)))
(keymaps
(macroexp-progn
(mapcar (lambda (keymap) `(define-key ,keymap ,key ,def))
keymaps)))
(states
(throw 'skip 'evil)
(macroexp-progn
(mapcar (lambda (state)
`(define-key
(evil-state-property ',state ,(if local :local-keymap :keymap) t)
,key ,def))
states)))
(t `(,(if local 'local-set-key 'global-set-key)
,key ,def)))
forms))
(setq states '()
local nil)))
(t (user-error "Invalid key %s" key))))
(macroexp-progn (reverse forms))))
(when (or noninteractive doom-dont-load-p)
(defmacro add-hook! (&rest _))
(defmacro associate! (&rest _))
(defmacro map! (&rest _)))
(provide 'core-lib)
;;; core-lib.el ends here

View file

@ -25,26 +25,24 @@ submodule symbol, e.g. 'evil.")
(defvar doom-packages nil
"A list of enabled packages.")
(defvar doom-protected-packages '(quelpa-use-package f s dash)
(defvar doom-protected-packages '(quelpa use-package dash f s)
"A list of packages that shouldn't be deleted.")
(defvar doom-installed-packages nil
"A list of packages that were installed during the current session.")
(defvar doom--init nil
(defvar doom-init-p nil
"Non-nil if doom's package system has been initialized or not. It may not be
if you have byte-compiled your configuration (as intended).")
(defvar doom--auto-install-p nil
"If non-nil, install missing packages. Otherwise, strip :ensure and :quelpa
from `package!' calls.")
(defvar doom-dont-load-p nil
"If non-nil, run DOOM emacs in declarative mode, meaning: don't actually load
anything, just track what should be loaded. Useful for scanning packages and
loaded modules.")
(defvar doom--prefer-el-p (or noninteractive doom--auto-install-p)
(defvar doom-prefer-el-p noninteractive
"If non-nil, load uncompiled .el config files.")
(defvar doom--load-path (append (list doom-core-dir
doom-modules-dir)
load-path)
(defvar doom--base-load-path (append (list doom-core-dir
doom-modules-dir)
load-path)
"A backup of `load-path', used as a bare-bones foundation for
`doom/packages-reload' or `doom-initialize'.")
@ -86,7 +84,7 @@ byte-compilation."
(error "No namespace specified on `doom!' for %s" p))
(t
(setq doom-enabled-modules (append doom-enabled-modules (list (cons mode p))))))))
`(unless noninteractive
`(unless doom-dont-load-p
(let (file-name-handler-alist)
,@(mapcar (lambda (pkg) `(load! ,(car pkg) ,(cdr pkg)))
doom-enabled-modules)
@ -99,56 +97,49 @@ byte-compilation."
;; Prevent any auto-displayed text + benchmarking
(advice-add 'display-startup-echo-area-message :override 'ignore)
(message "Loaded %s packages in %s"
(- (length load-path) (length doom--load-path))
(- (length load-path) (length doom--base-load-path))
(emacs-init-time)))))
(defun doom-initialize (&optional force-p)
"Initialize installed packages (using package.el) and ensure the core packages
are installed. If you byte compile core/core.el, calls to `package.el' are
avoided to speed up startup."
(unless (or doom--init force-p)
(setq load-path doom--load-path
(unless (or doom-init-p force-p)
(setq load-path doom--base-load-path
package-activated-list nil)
(package-initialize)
(unless (and (file-exists-p doom-packages-dir)
(require 'quelpa-use-package nil t))
(require 'use-package nil t)
(require 'quelpa nil t))
(package-refresh-contents)
;; Ensure core packages are installed
(mapc 'package-install '(quelpa-use-package dash f s)))
(unless (require 'quelpa-use-package nil t)
(delete-directory doom-packages-dir t)
(error "There was an error initializing DOOM. Try running it again"))
(quelpa-use-package-activate-advice)
;; Move :ensure to after conditional properties
(delq :ensure use-package-keywords)
(push :ensure (cdr (memq :unless use-package-keywords)))
(setq doom--init t)))
(condition-case ex
(mapc (lambda (pkg)
(package-install pkg)
(unless (package-installed-p pkg)
(error "Couldn't install %s" pkg)))
doom-protected-packages)
('error
(delete-directory doom-packages-dir t)
(error "There was an error initializing DOOM. Try running it again"))))
(defun doom-reload-modules ()
"Reload `doom-modules'."
(setq doom-modules nil)
(let ((doom--prefer-el-p t)
(noninteractive t))
(load (concat doom-emacs-dir "init.el") nil :nomessage :nosuffix)))
(require 'quelpa)
(require 'use-package)
(advice-add 'package-delete :around 'doom*package-delete)
;; Remove package management keywords, I'll deal with the myself
(mapc (lambda (keyword) (setq use-package-keywords (delq keyword use-package-keywords)))
'(:ensure :pin))
(setq doom-init-p t)))
(defun doom-reload-packages (&optional install-p)
"Reload `doom-packages'. Returns the difference in packages before and after."
(defun doom-reload ()
"Rereads the Emacs config, reloading `doom-packages' and
`doom-enabled-modules'."
(doom-initialize)
(doom-reload-modules)
(let ((before-packages-n (length package-alist))
(doom--auto-install-p (and install-p t))
(doom--prefer-el-p t)
(after-packages-n 0)
noninteractive)
(when doom--auto-install-p
(package-refresh-contents))
(load (f-expand "core.el" doom-core-dir) nil (not doom-debug-mode) :nosuffix)
(mapc (lambda (pkg)
(let ((path (f-expand "packages.el" (doom-module-path (car pkg) (cdr pkg)))))
(when (f-exists-p path)
(load path nil (not doom-debug-mode) :nosuffix))))
doom-enabled-modules)
(- (length package-alist) before-packages-n)))
(let ((doom-prefer-el-p t)
(doom-dont-load-p t))
(setq doom-modules nil
doom-packages nil)
(load (concat doom-emacs-dir "init.el") :noerror (not doom-debug-mode) :nosuffix)))
;;
@ -157,12 +148,14 @@ avoided to speed up startup."
(defvar __DIR__ nil "The directory of the currently loaded file (set by `load!')")
(defvar __FILE__ nil "The full path of the currently loaded file (set by `load!')")
(defvar __PACKAGE__ nil "The name of the current package.")
(defmacro use-package! (name &rest plist)
"A `use-package' wrapper, to adhere to the naming conventions of DOOM emacs
and let-bind `package-name' for the containing forms. Note that packages are
deferred by default."
`(let ((package-name ',name))
"A `use-package' wrapper. It exists so configs can adhere to the naming
conventions of DOOM emacs, as well as let-bind `__PACKAGE__' for the containing
forms. This is helpful for macros like `set!' and `add-hook!'. Note that
packages are deferred by default."
`(let ((__PACKAGE__ ',name))
(use-package ,name ,@plist)))
(defmacro package! (name &rest plist)
@ -173,25 +166,23 @@ which is the case if you've byte-compiled DOOM Emacs.
Note that packages are deferred by default."
(declare (indent defun))
(let ((use-package-always-ensure doom--auto-install-p)
(recipe (plist-get plist :quelpa)))
;; prepend NAME to quelpa recipe, if none is specified, to avoid local
;; MELPA lookups by quelpa.
(when (and recipe (= 0 (mod (length recipe) 2)))
(push name recipe)
(setq plist (plist-put plist :quelpa recipe)))
(if doom--auto-install-p
(unless (package-installed-p name)
(pushnew name doom-installed-packages))
(setq plist (use-package-plist-delete plist :ensure))
(setq plist (use-package-plist-delete plist :quelpa)))
(pushnew name doom-packages)
(macroexpand `(use-package ,name ,@plist))))
(let ((recipe (cadr (memq :recipe plist)))
(pin (cadr (memq :pin plist))))
(when recipe
(when (= 0 (mod (length recipe) 2))
(push name recipe))
(setq plist (use-package-plist-delete plist :recipe)))
(when pin
(add-to-list 'package-pinned-packages (cons package (plist-get plist :pin)))
(setq plist (use-package-plist-delete plist :pin)))
(pushnew (cons name recipe) doom-packages :key 'car)
(unless doom-dont-load-p
`(use-package! ,name ,@plist))))
(defmacro require! (feature)
"Like `require', but prefers uncompiled files when `doom--prefer-el-p' is
"Like `require', but prefers uncompiled files when `doom-prefer-el-p' is
non-nil or in a noninteractive session."
(let ((prefer-el-p (or doom--prefer-el-p noninteractive)))
(let ((prefer-el-p (or doom-prefer-el-p noninteractive)))
`(require ',feature
,(locate-file (concat (symbol-name feature) (if prefer-el-p ".el"))
load-path))))
@ -199,7 +190,7 @@ non-nil or in a noninteractive session."
(defmacro load! (file-or-module-sym &optional submodule file)
"Load a module from `doom-modules-dir'. Plays the same role as
`load-relative', but is specific to DOOM emacs modules and submodules. If
`doom--prefer-el-p' is non-nil or in an noninteractive session, prefer the
`doom-prefer-el-p' is non-nil or in an noninteractive session, prefer the
un-compiled elisp file.
Examples:
@ -210,30 +201,31 @@ Examples:
(load! +local-module)
Loads +local-module.el relative to `__DIR__' or `doom-core-dir'."
(let ((prefer-el-p (or doom--prefer-el-p noninteractive))
(let ((prefer-el-p (or doom-prefer-el-p noninteractive))
path file)
(cond ((null submodule)
(setq path __DIR__
file (concat (if (symbolp file-or-module-sym)
(symbol-name file-or-module-sym)
file-or-module-sym)
".el")))
file (concat (symbol-name file-or-module-sym) ".el")))
(t
(pushnew (cons file-or-module-sym submodule)
doom-enabled-modules
:test (lambda (x y) (and (eq (car x) (car y))
(eq (cdr x) (cdr y)))))
(setq path (doom-module-path file-or-module-sym submodule)
file (or file "config.el"))))
(setq path (f-slash path)
file (concat path file))
`(let ((__FILE__ ,file)
(__DIR__ ,path))
(load ,(if doom--prefer-el-p file (f-no-ext file))
nil (not doom-debug-mode) ,doom--prefer-el-p))))
(load ,(if doom-prefer-el-p file (f-no-ext file))
nil (not doom-debug-mode) ,doom-prefer-el-p))))
(defun doom-module-path (module submodule &optional file)
"Get the full path to a module: e.g. :lang emacs-lisp maps to
~/.emacs.d/modules/lang/emacs-lisp/. Will append FILE if non-nil."
(setq module
(cond ((keywordp module) (substring (symbol-name module) 1))
((symbolp module) (symbol-name module))
((symbolp module) (symbol-name module))
((stringp module) module)
(t (error "Not a valid module name: %s" module))))
(when (symbolp submodule)
@ -243,9 +235,16 @@ Examples:
;;
;; Defuns
;; Commands
;;
(defun doom/reload ()
"Reload `load-path' by scanning all packages. Run this if you ran make update
or make clean outside of Emacs."
(interactive)
(doom-initialize t)
(message "Reloaded %s packages" (length package-alist)))
(defun doom/refresh-autoloads ()
"Refreshes the autoloads.el file, which tells Emacs where to find all the
autoloaded functions in the modules you use or among the core libraries.
@ -253,8 +252,7 @@ autoloaded functions in the modules you use or among the core libraries.
Rerun this whenever you modify your init.el (or use `make autoloads` from the
command line)."
(interactive)
(unless doom--init
(doom-reload-modules))
(doom-reload)
(let ((generated-autoload-file (concat doom-local-dir "autoloads.el"))
(autoload-files
(append (-flatten (mapcar (lambda (dir)
@ -285,20 +283,21 @@ a lot from this.
If COMPREHENSIVE-P or the envar ALL is non-nil, also compile all autoload files.
The benefit from this is minimal and may take more time."
(interactive)
(doom-initialize)
(doom-reload-modules)
(let* ((map-fn (lambda (file) (and (f-ext-p file "el")
(if comprehensive-p
(not (string= (f-base file) "packages"))
(string= (f-base file) "config")))))
(targets (append
(list (f-expand "init.el" doom-emacs-dir)
(f-expand "core.el" doom-core-dir))
(f-glob "core-*.el" doom-core-dir)
(-flatten (--map (f-entries (doom-module-path (car it) (cdr it)) map-fn t)
doom-enabled-modules))))
(n 0)
results)
(doom-reload)
(let ((targets (append
(list (f-expand "init.el" doom-emacs-dir)
(f-expand "core.el" doom-core-dir))
(f-glob "core-*.el" doom-core-dir)
(-flatten
(--map (f--entries (doom-module-path (car it) (cdr it))
(and (f-ext-p it "el")
(if comprehensive-p
(not (string= (f-base it) "packages"))
(string= (f-base it) "config")))
t)
doom-enabled-modules))))
(n 0)
results)
(mapc (lambda (file)
(push (cons (f-relative file doom-emacs-dir)
(when (byte-recompile-file file nil 0)

View file

@ -101,7 +101,7 @@ disabled.")
;; I modified the built-in `hideshow' package to enable itself when needed. A
;; better, more vim-like code-folding plugin would be the `origami' plugin, but
;; until certain breaking bugs are fixed in it, I won't switch over.
(package! hideshow :ensure nil ; built-in
(use-package! hideshow ; built-in
:commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p)
:init
(defun doom*autoload-hideshow ()
@ -164,7 +164,7 @@ file."
(package! highlight-numbers :commands highlight-numbers-mode)
;; Line highlighting
(package! hl-line :ensure nil ; built-in
(use-package! hl-line ; built-in
:config
;; stickiness doesn't play nice with emacs 25+
(setq hl-line-sticky-flag nil

View file

@ -120,52 +120,48 @@ enable multiple minor modes for the same regexp.")
(setq gc-cons-threshold 339430400
gc-cons-percentage 0.6)
(eval-when-compile
(unless (file-exists-p doom-packages-dir)
(error "No packages are installed, run 'make install'"))
;; Ensure cache folder exist
(unless (file-exists-p doom-cache-dir)
(make-directory doom-cache-dir t)))
(let (file-name-handler-list)
(eval-and-compile
(load (concat doom-core-dir "core-packages") nil :nomessage))
(require 'core-packages (concat doom-core-dir "core-packages")))
(eval-when-compile
;; Ensure cache folder exist
(unless (file-exists-p doom-cache-dir)
(make-directory doom-cache-dir t))
(doom-initialize))
(setq load-path (eval-when-compile load-path))
;;; Essential packages
(eval-and-compile
(require 'dash)
(require 'f)
(require 's))
;;; Let 'er rip
(require 'core-lib)
(package! dash :demand t)
(package! s :demand t)
(package! f :demand t)
;;; Helper packages (autoloaded)
(package! async
:commands (async-start
async-start-process
async-byte-recompile-directory))
(defvar pcache-directory (concat doom-cache-dir "pcache/"))
(package! persistent-soft
:commands (persistent-soft-exists-p
persistent-soft-fetch
persistent-soft-flush
persistent-soft-store))
(package! smex :commands smex)
;;; Let 'er rip! (order matters!)
(require 'core-ui) ; draw me like one of your French editors
(require 'core-states) ; TODO
(require 'core-popups) ; taming sudden yet inevitable windows
(require 'core-editor) ; baseline configuration for text editing
(require 'core-projects) ; getting around your projects
(unless (require 'autoloads (concat doom-local-dir "autoloads.el") t)
(add-hook 'after-init-hook 'doom/refresh-autoloads)))
(doom/refresh-autoloads))
(unless noninteractive
(package! anaphora
:commands (awhen aif acond awhile))
(package! async
:commands (async-start
async-start-process
async-byte-recompile-directory))
(package! persistent-soft
:preface (defvar pcache-directory (concat doom-cache-dir "pcache/"))
:commands (persistent-soft-exists-p
persistent-soft-fetch
persistent-soft-flush
persistent-soft-store))
(require! core-set) ; a centralized config system; provides `set!'
(require! core-states) ; TODO
(require! core-ui) ; draw me like one of your French editors
(require! core-popups) ; taming sudden yet inevitable windows
(require! core-editor) ; baseline configuration for text editing
(require! core-projects))) ; making Emacs project-aware
(provide 'core)
;;; core.el ends here