From 7ef87546cc7906d5d7108ffdee279d4c85a7ed26 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 11 Feb 2017 00:46:42 -0500 Subject: [PATCH] Refactor package management system --- Makefile | 10 +- core/autoload/packages.el | 185 +++++++++---------------- core/core-editor.el | 41 +++--- core/core-lib.el | 15 -- core/core-packages.el | 283 ++++++++++++++++++++++---------------- core/core-popups.el | 2 +- core/core-projects.el | 2 +- core/core-ui.el | 12 +- core/core.el | 33 ++++- core/packages.el | 40 ++++++ 10 files changed, 335 insertions(+), 288 deletions(-) create mode 100644 core/packages.el diff --git a/Makefile b/Makefile index 5b0131fe4..ace8632da 100644 --- a/Makefile +++ b/Makefile @@ -15,13 +15,13 @@ autoremove: init.el @$(EMACS) -l core/core.el -f 'doom/packages-autoremove' autoloads: init.el - @$(EMACS) -l init.el -f 'doom/refresh-autoloads' + @$(EMACS) -l core/core.el -f 'doom/reload-autoloads' -compile: init.el clean-elc - @$(EMACS) -l init.el -f 'doom/byte-compile' +compile: init.el clean + @$(EMACS) -l core/core.el -f 'doom/recompile' -compile-lite: init.el clean-elc - @$(EMACS) -l core/core.el --eval '(doom/byte-compile t)' +compile-lite: init.el clean + @$(EMACS) -l core/core.el --eval '(doom/recompile t)' clean: @rm -fv init.elc diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 252994b9a..531802879 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -32,24 +32,24 @@ quelpa or nil (if not installed)." list, whose car is NAME, and cdr the current version list and latest version list of the package." (doom-refresh-packages) - (let ((pkg (or (assq name package-alist) - (assq name package--builtins)))) - (when pkg - (let* ((old-version (package-desc-version (cadr pkg))) - (new-version - (pcase (doom-package-backend name) - ('quelpa - (let ((recipe (assq name quelpa-cache)) - (dir (f-expand (symbol-name name) quelpa-build-dir)) - (inhibit-message t)) - (or (and (quelpa-setup-p) (quelpa-checkout recipe dir)) - old-version))) - ('elpa - (package-desc-version (cadr (assq name package-archive-contents))))))) - (when (stringp new-version) - (setq new-version (version-to-list new-version))) - (when (version-list-< old-version new-version) - (list name old-version new-version)))))) + (-when-let (pkg (assq name package-alist)) + (let* ((old-version (package-desc-version (cadr pkg))) + (new-version + (pcase (doom-package-backend name) + ('quelpa + (let ((recipe (assq name quelpa-cache)) + (dir (f-expand (symbol-name name) quelpa-build-dir)) + (inhibit-message t)) + (-if-let (ver (and (quelpa-setup-p) (quelpa-checkout recipe dir))) + (version-to-list ver) + old-version))) + ('elpa + (let ((desc (cadr (assq name package-archive-contents)))) + (when (package-desc-p desc) + (package-desc-version desc))))))) + (when (and (listp old-version) (listp new-version) + (version-list-< old-version new-version)) + (list name old-version new-version))))) ;;;###autoload (defun doom-get-packages (&optional backend) @@ -59,20 +59,19 @@ 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-read-packages t) + (doom-initialize-packages t) (unless (quelpa-setup-p) (error "Could not initialize quelpa")) (-non-nil (--map (or (assq it doom-packages) (list (car (assq it package-alist)))) - (append (mapcar 'car doom-packages) - doom-protected-packages)))) + (append doom-protected-packages + (mapcar 'car doom-packages))))) ;;;###autoload (defun doom-get-outdated-packages () - "Return a list of lists representing packages that are out of date. Each -element is a sublist, containing (PACKAGE-SYMBOL OLD-VERSION-LIST -NEW-VERSION-LIST). + "Return a list of packages that are out of date. Each element is a list, +containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST). Used by `doom/packages-update'." (-non-nil (--map (doom-package-outdated-p (car it)) @@ -84,7 +83,7 @@ Used by `doom/packages-update'." depended on. Used by `doom/packages-autoremove'." - (doom-read-packages t) + (doom-initialize-packages t) (let ((package-selected-packages (append (mapcar 'car doom-packages) doom-protected-packages))) (package--removable-packages))) @@ -97,64 +96,6 @@ that package's `@package' declaration. Used by `doom/packages-install'." (--remove (assq (car it) package-alist) (doom-get-packages))) -(defun doom--scrape-sexps (sym file) - (declare (indent defun)) - (unless (f-exists-p file) - (error "%s does not exist" file)) - (unless (symbolp sym) - (error "%s is not a valid symbol" sym)) - (with-temp-buffer - (buffer-disable-undo) - (emacs-lisp-mode) - (insert-file-contents file nil nil nil t) - (goto-char (point-min)) - (let ((regexp (concat "\\(^\\|\\s-\\)(" (symbol-name sym) " ")) - sexps) - (while (re-search-forward regexp nil t) - (unless (nth 4 (syntax-ppss)) - (save-excursion - (beginning-of-defun) - (push (cdr (sexp-at-point)) sexps)))) - (reverse sexps)))) - -;;;###autoload -(defun doom-read-packages (&optional force-p nopackages) - "Parses your Emacs config to keep track of packages declared with `@package' -in `doom-packages' and enabled modules in `doom-modules'." - (doom-initialize) - (when (or force-p (not doom-modules) (not doom-packages)) - (setq doom-modules - (let (paths mode enabled-modules) - (--each (doom--scrape-sexps '@doom (f-expand "init.el" doom-emacs-dir)) - (dolist (module it) - (cond ((keywordp module) - (setq mode module)) - ((not mode) - (error "Malformed @doom call: no namespace for %s" module)) - (t - (push (cons mode module) enabled-modules))))) - enabled-modules)) - - (unless nopackages - (setq package-pinned-packages nil - doom-packages nil) - (mapc (lambda (pkg) (cl-pushnew pkg doom-packages :key 'car)) - (-map (lambda (mplist) - (@mplist mplist &delete - :preface :ensure :requires :no-require :bind :bind* :bind-keymap - :bind-keymap* :interpreter :mode :commands :defines :functions - :defer :init :after :demand :config :diminish :delight)) - (--sort (string-greaterp (symbol-name (car it)) - (symbol-name (car other))) - (-flatten-n - 1 (mapcar (lambda (file) - (when (f-exists-p file) - (doom--scrape-sexps '@package file))) - (append (f-glob "core*.el" doom-core-dir) - (--map (doom-module-path (car it) (cdr it) "packages.el") - doom-modules))))))) - t))) - ;;;###autoload (defun doom*package-delete (name) "Update `quelpa-cache' upon a successful `package-delete'." @@ -176,24 +117,12 @@ in `doom-packages' and enabled modules in `doom-modules'." "Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an example; the package name can be omitted)." (doom-refresh-packages) - (doom-read-packages) + (doom-initialize-packages) (when (package-installed-p name) (error "%s is already installed, skipping" name)) - (when (plist-get plist :disabled) - (error "%s is disabled, skipping" name)) - (when (plist-get plist :load-path) - (error "%s has a local load-path, skipping" name)) - (let ((needs (plist-get plist :needs))) - (when (and needs - (--any-p (not (rassq it doom-modules)) - (-list needs))) - (error "%s doesn't have necessary dependencies (%s), skipping" needs))) - (let ((inhibit-message (not doom-debug-mode))) - (cond ((plist-get plist :recipe) - (let ((recipe (plist-get plist :recipe))) - (when (and recipe (= 0 (mod (length recipe) 2))) - (setq recipe (cons name recipe))) - (quelpa recipe))) + (let ((inhibit-message (not doom-debug-mode)) + (recipe (plist-get plist :recipe))) + (cond (recipe (quelpa recipe)) (t (package-install name)))) (cl-pushnew (cons name plist) doom-packages :key 'car) (package-installed-p name)) @@ -210,7 +139,7 @@ appropriate." (pcase (doom-package-backend name) ('quelpa (let ((quelpa-upgrade-p t)) - (quelpa it) + (quelpa (assq name quelpa-cache)) (setq quelpa-modified-p t))) ('elpa (let ((desc (cadr (assq name package-alist))) @@ -293,10 +222,10 @@ appropriate." (let ((-max-len (or (-max (--map (length (symbol-name (car it))) packages)) 10))) (mapconcat (lambda (pkg) - (format "+ %s %s\t-> %s" + (format "+ %s %s -> %s" (s-pad-right (+ -max-len 2) " " (symbol-name (car pkg))) - (cadr pkg) - (cadr (cdr pkg)))) + (s-pad-right 14 " " (doom--version-list-str (cadr pkg))) + (doom--version-list-str (caddr pkg)))) (--sort (string-lessp (symbol-name (car it)) (symbol-name (car other))) packages) @@ -345,31 +274,51 @@ appropriate." (doom-message "Finished!"))))) +(defun doom--version-list-str (vlist) + (concat (number-to-string (car vlist)) + "." + (number-to-string (cadr vlist)))) + ;;;###autoload (defalias 'doom/install-package 'package-install) ;;;###autoload -(defun doom/delete-package (&optional package) +(defun doom/delete-package (package) + "Prompts the user with a list of packages and deletes the selected package. +Use this interactively. Use `doom-delete-package' for direct calls." (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) + (if (y-or-n-p (format "%s will be deleted. Confirm?" package)) + (message "%s %s" + (if (doom-delete-package package) + "Deleted" + "Failed to delete") + pkg) + (message "Aborted")) (message "%s isn't installed" package))) ;;;###autoload -(defun doom/update-package (&optional package) - "Use this instead of package.el's update interface." +(defun doom/update-package (package) + "Prompts the user with a list of outdated packages and updates the selected +package. Use this interactively. Use `doom-update-package' for direct +calls." (declare (interactive-only t)) (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) + (let ((packages (doom-get-outdated-packages))) + (list + (if packages + (completing-read "Update package: " (--map (symbol-name (car it)) packages)) + (user-error "All packages are up-to-date"))))) + (-if-let (desc (doom-package-outdated-p (intern package))) + (if (y-or-n-p (format "%s will be updated from %s to %s. Update?" + (car desc) + (doom--version-list-str (cadr desc)) + (doom--version-list-str (caddr desc)))) + (message "%s %s" + (if (doom-update-package package) + "Updated" + "Failed to update") + pkg) + (message "Aborted")) (message "%s is up-to-date" package))) diff --git a/core/core-editor.el b/core/core-editor.el index 075e21529..e7fb9223f 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -93,7 +93,7 @@ ;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; specify their own formatting rules. -(@package editorconfig :demand t +(@def-package editorconfig :demand t :mode ("\\.?editorconfig$" . editorconfig-conf-mode) :config (editorconfig-mode +1) ;; Show whitespace in tabs indentation mode @@ -101,7 +101,7 @@ (if indent-tabs-mode (whitespace-mode +1)))) ;; Auto-close delimiters and blocks as you type -(@package smartparens :demand t +(@def-package smartparens :demand t :init (setq sp-autowrap-region nil ; let evil-surround handle this sp-highlight-pair-overlay nil @@ -137,63 +137,56 @@ ;; Autoloaded Plugins ;; -(@package ace-link :commands (ace-link-help ace-link-org)) +(@def-package ace-link :commands (ace-link-help ace-link-org)) -(@package ace-window +(@def-package ace-window :commands ace-window :config (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) aw-scope 'frame aw-background t)) -(@package avy +(@def-package avy :commands (avy-goto-char-2 avy-goto-line) :config (setq avy-all-windows nil avy-background t)) -(@package command-log-mode +(@def-package command-log-mode :commands (command-log-mode global-command-log-mode) :config (@set :popup "*command-log*" :size 40 :align 'right :noselect t) (setq command-log-mode-auto-show t command-log-mode-open-log-turns-on-mode t)) -(@package emr +(@def-package emr :commands (emr-show-refactor-menu emr-declare-command) :config (emr-initialize) (define-key popup-menu-keymap [escape] 'keyboard-quit)) -(@package expand-region :commands (er/expand-region er/contract-region er/mark-symbol er/mark-word)) +(@def-package expand-region :commands (er/expand-region er/contract-region er/mark-symbol er/mark-word)) -(@package goto-last-change :commands goto-last-change) +(@def-package goto-last-change :commands goto-last-change) -(@package help-fns+ ; Improved help commands +(@def-package help-fns+ ; Improved help commands :commands (describe-buffer describe-command describe-file describe-keymap describe-option describe-option-of-type)) -(@package imenu-anywhere +(@def-package imenu-anywhere :commands (ido-imenu-anywhere ivy-imenu-anywhere helm-imenu-anywhere)) -(@package imenu-list :commands imenu-list-minor-mode) +(@def-package imenu-list :commands imenu-list-minor-mode) -(@package pcre2el :commands rxt-quote-pcre) +(@def-package pcre2el :commands rxt-quote-pcre) -(@package rotate-text - :recipe (:fetcher github :repo "debug-ito/rotate-text.el") +(@def-package rotate-text :commands (rotate-text rotate-text-backward) :config (push '("true" "false") rotate-text-words)) -(@package smart-forward +(@def-package smart-forward :commands (smart-up smart-down smart-backward smart-forward)) -(@package smex - :commands (smex smex-major-mode-commands) - :config - (setq smex-save-file (concat doom-cache-dir "/smex-items")) - (smex-initialize)) +(@def-package swiper :commands (swiper swiper-all)) -(@package swiper :commands (swiper swiper-all)) - -(@package wgrep +(@def-package wgrep :commands (wgrep-setup wgrep-change-to-wgrep-mode) :config (@set :popup "^\\*ivy-occur counsel-ag" :size 25 :select t :regexp t) diff --git a/core/core-lib.el b/core/core-lib.el index 2daa0ff4a..a2aa8b98d 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -26,21 +26,6 @@ byte-compile-current-file) (error "__FILE__ is unset"))) -(@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)) - ;; ;; Library diff --git a/core/core-packages.el b/core/core-packages.el index 9128b548f..fdc7a4151 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -55,6 +55,8 @@ if you have byte-compiled your configuration (as intended).") '(("gnu" . "http://elpa.gnu.org/packages/") ("melpa" . "http://melpa.org/packages/") ("org" . "http://orgmode.org/elpa/")) + ;; I omit Marmalade because its packages are manually submitted rather + ;; than pulled, so packages are often out of date with upstream. use-package-always-defer t use-package-always-ensure nil @@ -74,49 +76,19 @@ if you have byte-compiled your configuration (as intended).") ;; Bootstrap function ;; -(autoload 'use-package "use-package" nil nil 'macro) -(advice-add 'package-delete :after 'doom*package-delete) - -(defmacro @doom (&rest packages) - "DOOM Emacs bootstrap macro. List the modules to load. Benefits from -byte-compilation." - (let (mode) - (dolist (p packages) - (cond ((keywordp p) - (setq mode p)) - ((not mode) - (error "No namespace specified on `@doom' for %s" p)) - ((eq p '*) - (let ((mode-name (substring (symbol-name mode) 1))) - (--map (setq doom-modules (append doom-modules (list (cons mode (f-base it))))) - (f-directories (f-expand mode-name doom-modules-dir))))) - (t - (setq doom-modules (append doom-modules (list (cons mode p)))))))) - (unless noninteractive - `(let (file-name-handler-alist) - ,@(mapcar (lambda (pkg) `(@load ,(car pkg) ,(cdr pkg))) - doom-modules) - - (when (display-graphic-p) - (require 'server) - (unless (server-running-p) - (server-start))) - - ;; Benchmark - (format "Loaded %s packages in %s" - (- (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." + ;; This is called early during Emacs initialization, so we can only use native + ;; emacs functions. (unless (or doom-init-p force-p) (setq load-path doom--base-load-path package-activated-list nil) (package-initialize t) - ;; Sure, package-initialize fills the load-path, but it will error out on - ;; missing packages. UNACCEPTAABBLLLE! + + ;; Sure, `package-initialize' fills the load-path, but when NO-ACTIVATE is + ;; non-nil, it will error out on missing packages. UNACCEPTAABBLLLE! (setq load-path (append load-path (directory-files package-user-dir t "^[a-zA-Z0-9]" t))) ;; Ensure cache folder exists @@ -143,99 +115,168 @@ avoided to speed up startup." ;; 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-initialize-autoloads (&optional force-p) "Ensures that an autoloads file exists and is loaded." - (unless (or (featurep 'autoloads) - (load doom-autoload-file t t)) - (doom/refresh-autoloads) - (unless (file-exists-p doom-autoload-file) - (error "Autoloads file couldn't be generated")))) + (unless (ignore-errors (require 'autoloads doom-autoload-file t)) + (unless noninteractive + (doom/reload-autoloads) + (unless (file-exists-p doom-autoload-file) + (error "Autoloads file couldn't be generated"))))) + +(defun doom-initialize-packages (&optional force-p) + "Parses your Emacs config to keep track of packages declared with `@package' +in `doom-packages' and enabled modules in `doom-modules'." + (doom-initialize force-p) + (when (or force-p (not doom-modules) (not doom-packages)) + (setq doom-modules nil) + (let ((noninteractive t)) + (mapc (lambda (file) (load file nil :nomessage)) + (list (f-expand "packages.el" doom-core-dir) + (f-expand "init.el" doom-emacs-dir))) + ;; Look up packages.el for enabled modules + (mapc (lambda (file) (load file :noerror :nomessage)) + (--map (doom-module-path (car it) (cdr it) "packages.el") + (doom-module-pairs)))))) + +(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/ and will append FILE if non-nil." + (unless (keywordp module) + (error "Expected a keyword, got %s" module)) + (unless (symbolp submodule) + (error "Expected a symbol, got %s" submodule)) + (let ((module-name (substring (symbol-name module) 1)) + (submodule-name (symbol-name submodule))) + (f-expand (concat module-name "/" submodule-name "/" file) + doom-modules-dir))) + +(defun doom-module-pairs () + "TODO" + (let (pairs module) + (dolist (modules doom-modules) + (setq module (car modules)) + (dolist (submodule (cdr modules)) + (push (cons module submodule) pairs))) + pairs)) + +(defun doom-module-loaded-p (module submodule) + "TODO" + (memq submodule (cdr (assq module doom-modules)))) + +(defun doom-enable-module (module submodule &optional force-p) + (unless (or force-p (doom-module-loaded-p module submodule)) + (let ((sublist (assq module doom-modules))) + (if sublist + (setf sublist (cons sublist submodule)) + (push (list module submodule) doom-modules))))) + +(defun doom-enable-modules (modules) + "TODO" + (let (mode) + (dolist (m modules) + (cond ((keywordp m) + (setq mode m)) + ((not mode) + (error "No namespace specified on `@doom' for %s" m)) + ((eq m '*) + (let ((mode-str (substring (symbol-name mode) 1))) + (doom-enable-modules + (cons mode + (--map (intern (f-base it)) + (f-directories + (f-expand mode-str doom-modules-dir))))))) + (t + (doom-enable-module mode m)))) + doom-modules)) ;; ;; Macros ;; -(defvar __PACKAGE__ nil "The name of the current package.") +(autoload 'use-package "use-package" nil nil 'macro) -(defalias '@use-package 'use-package +(defmacro @doom (&rest modules) + "DOOM Emacs bootstrap macro. List the modules to load. Benefits from +byte-compilation." + (doom-enable-modules modules) + (unless noninteractive + `(let (file-name-handler-alist) + ,@(mapcar (lambda (pkg) + `(@require ,(car pkg) ,(cdr pkg) t)) + (doom-module-pairs)) + + (when (display-graphic-p) + (require 'server) + (unless (server-running-p) + (server-start))) + + ;; Benchmark + (format "Loaded %s packages in %s" + (- (length load-path) (length doom--base-load-path)) + (emacs-init-time))))) + +(defalias '@def-package 'use-package "A `use-package' alias. It exists so DOOM configs adhere to the naming -conventions of DOOM emacs. Note that packages are deferred by default. +conventions of DOOM emacs. Note that packages are deferred by default.") -By DOOM conventions, using this instead of `@package' means you are configuring -a package regardless of whether it's installed or not, while `@package' is used -to declare how to install/setup a package.") +(defmacro @load (filesym &optional path noerror) + "TODO" + (let ((path (or (and path (eval path)) __DIR__)) + file) + (unless path + (error "Could not find %s" filesym)) + (setq file (f-expand (concat (symbol-name filesym) ".el") path)) + (if (f-exists-p file) + `(let ((__FILE__ ,file) + (__DIR__ ,path)) + (load ,(f-no-ext file) ,noerror (not doom-debug-mode))) + (unless noerror + (error "Could not @load file %s" file))))) + +(defmacro @require (module submodule &optional reload-p) + "Like `require', but for doom modules." + (unless noninteractive + (let ((loaded-p (doom-module-loaded-p module submodule))) + (when (or reload-p (not loaded-p)) + (unless loaded-p + (doom-enable-module module submodule t)) + `(@load config ,(doom-module-path module submodule) t))))) + + +;; +;; Declarative macros +;; (defmacro @package (name &rest plist) "Declares a package. This does not load nor install them explicitly. -If used in `doom-core-dir', this is a wrapper for `@use-package' (all packages -are deferred by default), and takes the same arguments as `use-package'. +this macro serves a purely declarative purpose, and are run to build +`doom-packages', so that functions like `doom/packages-install' can operate on +them. -If used outside of `doom-core-dir' (i.e. in packages.el files within modules), -this macro serves a purely declarative purpose and doesn't call `@use-package'. -These calls are parsed by `doom-read-packages' to build `doom-packages'. - -Adds a few custom properties in either case: +Accepts the following properties: :recipe RECIPE Takes a MELPA-style recipe (see `quelpa-recipe' for an example); for packages to be installed from external sources. :pin ARCHIVE-NAME Instructs ELPA to only look for this package in - ARCHIVE-NAME. e.g. \"org\". - :needs FEATURE Don't install this package if FEATURE isn't available. Can be a - (:module . submodule) cons pair." + ARCHIVE-NAME. e.g. \"org\"." (declare (indent defun)) - (mapc (lambda (key) (setq plist (use-package-plist-delete plist key))) - '(:recipe :pin :needs)) - `(let ((__PACKAGE__ ',name)) - (@use-package ,name ,@plist))) + (let ((pkg-recipe (plist-get plist :recipe)) + (pkg-pin (plist-get plist :pin))) + (when (= 0 (mod (length pkg-recipe) 2)) + (plist-put plist :recipe (cons name pkg-recipe))) + `(add-to-list 'doom-packages ',(cons name plist) t))) -(defmacro @load (module &optional submodule file) - "Load a module from `doom-modules-dir' when both MODULE and SUBMODULE is -provided (both symbols). If FILE is non-nil, append it to the resulting path. If -SUBMODULE is nil, MODULE is loaded relative to the current file (see `__DIR__'). -When SUBMODULE is nil, FILE isn't used. - -Examples: - (@load :lang emacs-lisp) - - Loads modules/lang/emacs-lisp/FILE.el (defaults to config.el). - - (@load +local-module) - - Loads +local-module.el relative to `__DIR__' or `doom-core-dir'." - (let (path file) - (cond ((null submodule) - (setq path __DIR__ - file (concat (symbol-name module) ".el"))) - (t - (cl-pushnew (cons module submodule) - doom-modules - :test (lambda (x y) (and (eq (car x) (car y)) - (eq (cdr x) (cdr y))))) - (setq path (doom-module-path module submodule) - file (or file "config.el")))) - (setq path (f-slash path) - file (concat path file)) - `(let ((__FILE__ ,file) - (__DIR__ ,path)) - (load ,(f-no-ext file) nil (not doom-debug-mode))))) - -(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/ and will append FILE if non-nil." - (setq module - (cond ((keywordp module) (substring (symbol-name module) 1)) - ((symbolp module) (symbol-name module)) - ((stringp module) module) - (t (error "Not a valid module name: %s" module)))) - (when (symbolp submodule) - (setq submodule (symbol-name submodule))) - (f-expand (concat module "/" submodule "/" file) - doom-modules-dir)) +(defmacro @depends-on (module submodule) + "Declares that this module depends on another. MODULE is a keyword, and +SUBMODULE is a symbol." + (doom-enable-module ,module ',submodule) + `(@load packages ,(doom-module-path module submodule) t)) ;; @@ -243,16 +284,13 @@ Examples: ;; (defun doom/reload () - "Reload `load-path', `doom-modules' and `doom-packages' by -reinitializing doom and parsing config files for `@package' and `@doom' calls. -There are few reasons to use this." + "Reload `load-path' by reinitializing package.el and reloading autoloads." (interactive) (doom-initialize t) - (doom-read-packages t) - (doom-initialize-autoloads) + (doom/reload-autoloads) (message "Reloaded %s packages" (length package-alist))) -(defun doom/refresh-autoloads () +(defun doom/reload-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, e.g. core/autoload/*.el. @@ -262,6 +300,7 @@ In modules, checks modules/*/autoload.el and modules/*/autoload/*.el. Rerun this whenever init.el is modified. You can also use `make autoloads` from the commandline." (interactive) + (doom-initialize-packages noninteractive) (let ((generated-autoload-file doom-autoload-file) autoload-files) (setq autoload-files @@ -272,26 +311,29 @@ the commandline." (and (f-directory-p auto-dir) (f-glob "*.el" auto-dir)))) (--map (doom-module-path (car it) (cdr it)) - doom-modules))) + (doom-module-pairs)))) (f-glob "autoload/*.el" doom-core-dir))) (when (f-exists-p generated-autoload-file) (f-delete generated-autoload-file) (message "Deleted old autoloads.el")) (dolist (file autoload-files) - (update-file-autoloads file) + (@quiet (update-file-autoloads file)) (message "Scanned %s" (f-relative file doom-emacs-dir))) - (with-current-buffer (get-file-buffer generated-autoload-file) - (save-buffer) - (eval-buffer)) - (message "Done!"))) + (condition-case ex + (with-current-buffer (get-file-buffer generated-autoload-file) + (save-buffer) + (eval-buffer) + (message "Done!")) + ('error (error "Couldn't evaluate autoloads.el: %s" (cadr ex)))))) -(defun doom/byte-compile (&optional simple-p) +(defun doom/recompile (&optional simple-p) "Byte (re)compile the important files in your emacs configuration (init.el & core/*.el). DOOM Emacs was designed to benefit from this. If SIMPLE-P is nil, also byte-compile modules/*/*/*.el (except for packages.el). There should be a measurable benefit from this, but it may take a while." (interactive) + (doom-initialize-packages t) (let ((targets (append (list (f-expand "init.el" doom-emacs-dir) (f-expand "core.el" doom-core-dir)) @@ -303,7 +345,7 @@ There should be a measurable benefit from this, but it may take a while." (or (string= (f-base it) "config") (string-prefix-p "+" (f-base it)))) t) - doom-modules))))) + (doom-module-pairs)))))) (n 0) results) (dolist (file targets) @@ -318,5 +360,12 @@ There should be a measurable benefit from this, but it may take a while." (mapconcat (lambda (file) (concat "+ " (if (cdr file) "SUCCESS" "FAIL") ": " (car file))) (reverse results) "\n"))))) + +;; +;; Package.el modifications +;; + +(advice-add 'package-delete :after 'doom*package-delete) + (provide 'core-packages) ;;; core-packages.el ends here diff --git a/core/core-popups.el b/core/core-popups.el index dab998ea7..c762c5a1e 100644 --- a/core/core-popups.el +++ b/core/core-popups.el @@ -57,7 +57,7 @@ ;; Bootstrap ;; -(@package shackle :demand t +(@def-package shackle :demand t :init (setq shackle-default-alignment 'below shackle-select-reused-windows t) diff --git a/core/core-projects.el b/core/core-projects.el index 49f35d848..8c71d1447 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -4,7 +4,7 @@ ;; tools for digging through project files and exposing an API I can use to make ;; other plugins/features project-aware. -(@package projectile :demand t +(@def-package projectile :demand t :init (setq projectile-cache-file (concat doom-cache-dir "/projectile.cache") projectile-completion-system 'ivy diff --git a/core/core-ui.el b/core/core-ui.el index 206d18243..2bf9305ef 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -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. -(@use-package hideshow ; built-in +(@def-package hideshow ; built-in :commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p) :init (defun doom*autoload-hideshow () @@ -110,7 +110,7 @@ disabled.") (advice-add 'evil-toggle-fold :before 'doom*autoload-hideshow)) ;; Show uninterrupted indentation markers with some whitespace voodoo. -(@package highlight-indent-guides +(@def-package highlight-indent-guides :commands highlight-indent-guides-mode :config (setq highlight-indent-guides-method 'character) @@ -161,10 +161,10 @@ file." (delete-trailing-whitespace)))) ;; Some modes don't adequately highlight numbers, therefore... -(@package highlight-numbers :commands highlight-numbers-mode) +(@def-package highlight-numbers :commands highlight-numbers-mode) ;; Line highlighting -(@use-package hl-line ; built-in +(@def-package hl-line ; built-in :init ;; stickiness doesn't play nice with emacs 25+ (setq hl-line-sticky-flag nil @@ -172,7 +172,7 @@ file." ;; Line number column. A faster (or equivalent, in the worst case) line number ;; plugin than the built-in `linum'. -(@package nlinum +(@def-package nlinum :commands nlinum-mode :preface (defvar nlinum-format "%4d ") :init @@ -194,7 +194,7 @@ file." ;; Helps us distinguish stacked delimiter pairs. Especially in parentheses-drunk ;; languages like Lisp. -(@package rainbow-delimiters +(@def-package rainbow-delimiters :commands rainbow-delimiters-mode :config (setq rainbow-delimiters-max-face-count 3) :init diff --git a/core/core.el b/core/core.el index 6120fed19..9fc95e82e 100644 --- a/core/core.el +++ b/core/core.el @@ -50,7 +50,7 @@ line or use --debug-init to enable this.") (defvar doom-autoload-file (concat doom-local-dir "autoloads.el") - "Location of the autoloads.el, which is generated by `doom/refresh-autoloads' + "Location of the autoloads.el, which is generated by `doom/reload-autoloads' and `doom-initialize-autoloads'.") (defconst IS-MAC (eq system-type 'darwin)) @@ -136,6 +136,37 @@ enable multiple minor modes for the same regexp.") (require 'core-lib) (doom-initialize-autoloads) (unless noninteractive + (@def-package anaphora + :commands (awhen aif acond awhile)) + + (@def-package async + :commands (async-start + async-start-process + async-byte-recompile-directory)) + + (@def-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)) + + (@def-package ht + :commands (ht-create ht-merge ht-copy ht-select ht-reject ht-select-keys + ht-get ht-keys ht-values ht-items ht-find ht-size + ht-set! ht-update! ht-remove! ht-clear! ht-reject! + ht-map ht-each + ht? ht-contains? ht-equal? ht-empty? + ht->alist ht->plist + ht<-alist ht<-plist + ht ht-amap ht-aeach)) + + (@def-package smex + :commands (smex smex-major-mode-commands) + :config + (setq smex-save-file (concat doom-cache-dir "/smex-items")) + (smex-initialize)) + (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 diff --git a/core/packages.el b/core/packages.el new file mode 100644 index 000000000..c4aec83cd --- /dev/null +++ b/core/packages.el @@ -0,0 +1,40 @@ +;; -*- no-byte-compile: t; -*- +;;; core/packages.el + +;; core packages +(@package anaphora) +(@package async) +(@package persistent-soft) +(@package ht) +(@package smex) + +;; core-ui.el +(@package highlight-indent-guides) +(@package highlight-numbers) +(@package nlinum) +(@package rainbow-delimiters) + +;; core-popups.el +(@package shackle) + +;; core-editor.el +(@package editorconfig) +(@package smartparens) +(@package ace-link) +(@package ace-window) +(@package avy) +(@package command-log-mode) +(@package emr) +(@package expand-region) +(@package goto-last-change) +(@package help-fns+) +(@package imenu-anywhere) +(@package imenu-list) +(@package pcre2el) +(@package rotate-text :recipe (:fetcher github :repo "debug-ito/rotate-text.el")) +(@package smart-forward) +(@package swiper) +(@package wgrep) + +;; core-projects.el +(@package projectile)