<commit_before>;;; core-packages.el --- package management system -*- lexical-binding: t; -*-

;; Emacs package management is opinionated, and so am I. I've bound together
;; `use-package', `quelpa' and package.el to create my own, rolling-release,
;; lazily-loaded package management system for Emacs.
;;
;; The three key commands are:
;;
;; + `bin/doom install` or `doom//packages-install': Installs packages that are
;;   wanted, but not installed.
;; + `bin/doom update` or `doom//packages-update': Updates packages that are
;;   out-of-date.
;; + `bin/doom autoremove` or `doom//packages-autoremove': Uninstalls packages
;;   that are no longer needed.
;;
;; This system reads packages.el files located in each activated module (and one
;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what
;; plugins to install and where from.
;;
;; Why all the trouble? Because:
;; 1. Scriptability: I live in the command line. I want a programmable
;;    alternative to `list-packages' for updating and installing packages.
;; 2. Flexibility: I want packages from sources other than ELPA. Primarily
;;    github, because certain plugins are out-of-date through official channels,
;;    have changed hands, have a superior fork, or simply aren't in any ELPA
;;    repo.
;; 3. Stability: I used Cask before this. It would error out with cyrptic errors
;;    depending on the version of Emacs I used and the alignment of the planets.
;;    No more.
;; 4. Performance: A minor point, but this system is lazy-loaded (more so if you
;;    byte-compile). Not having to initialize package.el (or check that your
;;    packages are installed) every time you start up Emacs affords us precious
;;    seconds.
;; 5. Simplicity: No Cask, no external dependencies (unless you count make),
;;    just Emacs. Arguably, my config is still over-complicated, but shhh, it's
;;    fine. Everything is fine.
;;
;; You should be able to use package.el commands without any conflicts.
;;
;; See core/autoload/packages.el for more functions.

(defvar doom-init-p nil
  "Non-nil if `doom-initialize' has run.")

(defvar doom-init-modules-p nil
  "Non-nil if `doom-initialize-modules' has run.")

(defvar doom-init-time nil
  "The time it took, in seconds, for DOOM Emacs to initialize.")

(defvar doom-modules ()
  "A hash table of enabled modules. Set by `doom-initialize-modules'.")

(defvar doom-modules-dirs
  (list (expand-file-name "modules/" doom-private-dir) doom-modules-dir)
  "A list of module root directories. Order determines priority.")

(defvar doom-psuedo-module-dirs (list doom-private-dir)
  "Additional paths for modules that are outside of `doom-modules-dirs'.
`doom//reload-doom-autoloads', `doom//byte-compile' and
`doom-initialize-packages' will include the directories in this list.")

(defvar doom-packages ()
  "A list of enabled packages. Each element is a sublist, whose CAR is the
package's name as a symbol, and whose CDR is the plist supplied to its
`package!' declaration. Set by `doom-initialize-packages'.")

(defvar doom-core-packages '(persistent-soft use-package quelpa async)
  "A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.")

(defvar doom-disabled-packages ()
  "A list of packages that should be ignored by `def-package!'.")

(defvar doom-site-load-path load-path
  "The starting load-path, before it is altered by `doom-initialize'.")

(defvar doom-autoload-file (concat doom-local-dir "autoloads.el")
  "Where `doom//reload-doom-autoloads' will generate its core autoloads file.")

(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
  "Where `doom//reload-package-autoloads' will generate its package.el autoloads
file.")

(defvar doom-reload-hook nil
  "A list of hooks to run when `doom//reload-load-path' is called.")

(defvar doom--current-module nil)
(defvar doom--refreshed-p nil)
(defvar doom--stage 'init)
(defvar doom--inhibit-reload nil)

;;
(setq autoload-compute-prefixes nil
      package--init-file-ensured t
      package-user-dir (expand-file-name "elpa" doom-packages-dir)
      package-enable-at-startup nil
      package-archives
      '(("gnu"   . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ("org"   . "https://orgmode.org/elpa/"))
      ;; I omit Marmalade because its packages are manually submitted rather
      ;; than pulled, so packages are often out of date with upstream.

      ;; security settings
      gnutls-verify-error (not (getenv "INSECURE")) ; you shouldn't use this
      tls-checktrust gnutls-verify-error
      tls-program (list "gnutls-cli --x509cafile %t -p %p %h"
                        ;; compatibility fallbacks
                        "gnutls-cli -p %p %h"
                        "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")

      use-package-verbose doom-debug-mode
      use-package-minimum-reported-time (if doom-debug-mode 0 0.1)

      ;; Don't track MELPA, we'll use package.el for that
      quelpa-checkout-melpa-p nil
      quelpa-update-melpa-p nil
      quelpa-melpa-recipe-stores nil
      quelpa-self-upgrade-p nil
      quelpa-verbose doom-debug-mode
      quelpa-dir (expand-file-name "quelpa" doom-packages-dir)

      byte-compile-dynamic nil
      byte-compile-verbose doom-debug-mode
      byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))

;; accommodate INSECURE setting
(unless gnutls-verify-error
  (dolist (archive package-archives)
    (setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil))))


;;
;; Helpers 'n hooks
;;

(defun doom--assert-stage-p (stage macro)
  (cl-assert (eq stage doom--stage)
             nil
             "Found %s call in non-%s.el file (%s)"
             macro (symbol-name stage)
             (if (file-in-directory-p load-file-name doom-emacs-dir)
                 (file-relative-name load-file-name doom-emacs-dir)
               (abbreviate-file-name load-file-name))))

(defun doom|display-benchmark (&optional return-p)
  "Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.

If RETURN-P, return the message as a string instead of displaying it."
  (funcall (if return-p #'format #'message)
           "Doom loaded %s packages across %d modules in %.03fs"
           ;; Certainly imprecise, especially where custom additions to
           ;; load-path are concerned, but I don't mind a [small] margin of
           ;; error in the plugin count in exchange for faster startup.
           (- (length load-path) (length doom-site-load-path))
           (if doom-modules (hash-table-count doom-modules) 0)
           (or doom-init-time
               (setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))

(defun doom|post-init ()
  "Run `doom-post-init-hook'. That's all."
  (run-hooks 'doom-post-init-hook))

(defun doom|run-all-startup-hooks ()
  "Run all startup Emacs hooks. Meant to be executed after starting Emacs with
-q or -Q, for example:

  emacs -Q -l init.el -f doom|run-all-startup-hooks"
  (run-hooks 'after-init-hook 'delayed-warnings-hook
             'emacs-startup-hook 'term-setup-hook
             'window-setup-hook))


;;
;; Bootstrap helpers
;;

(defun doom-ensure-packages-initialized (&optional force-p)
  "Make sure package.el is initialized."
  (when (or force-p (not package--initialized))
    (require 'package)
    (setq package-activated-list nil
          package--initialized nil)
    (let (byte-compile-warnings)
      (condition-case _
          (quiet! (package-initialize))
        ('error (package-refresh-contents)
                (setq doom--refreshed-p t)
                (package-initialize))))))

(defun doom-ensure-core-packages ()
  "Make sure `doom-core-packages' are installed."
  (when-let* ((core-packages (cl-remove-if #'package-installed-p doom-core-packages)))
    (message "Installing core packages")
    (unless doom--refreshed-p
      (package-refresh-contents))
    (dolist (package core-packages)
      (let ((inhibit-message t))
        (package-install package))
      (if (package-installed-p package)
          (message "✓ Installed %s" package)
        (error "✕ Couldn't install %s" package)))
    (message "Installing core packages...done")))

(defun doom-ensure-core-directories ()
  "Make sure all Doom's essential local directories (in and including
`doom-local-dir') exist."
  (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir))
    (unless (file-directory-p dir)
      (make-directory dir t))))


;;
;; Bootstrap API
;;

(autoload 'doom//reload-doom-autoloads "autoload/modules" nil t)
(autoload 'doom//reload-package-autoloads "autoload/modules" nil t)

(defun doom-initialize (&optional force-p)
  "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).

The bootstrap process involves making sure 1) the essential directories exist,
2) the core packages are installed, 3) `doom-autoload-file' and
`doom-package-autoload-file' exist and have been loaded, and 4) Doom's core
files are loaded.

If the cache exists, much of this function isn't run, which substantially
reduces startup time.

The overall load order of Doom is as follows:

  ~/.emacs.d/init.el
  ~/.emacs.d/core/core.el
  `doom-pre-init-hook'
  ~/.doom.d/init.el
  Module init.el files
  `doom-init-hook'
  Module config.el files
  ~/.doom.d/config.el
  `after-init-hook'
  `emacs-startup-hook'
  `doom-post-init-hook' (at end of `emacs-startup-hook')

Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
  (when (or force-p (not doom-init-p))
    ;; Set this to prevent infinite recursive calls to `doom-initialize'
    (setq doom-init-p t)
    ;; `doom-autoload-file' tells Emacs where to load all its autoloaded
    ;; functions from. This includes everything in core/autoload/*.el and all
    ;; the autoload files in your enabled modules.
    (unless (doom-initialize-autoloads doom-autoload-file force-p)
      (doom-ensure-core-directories)
      (doom-ensure-packages-initialized force-p)
      (doom-ensure-core-packages)
      ;; Regenerate `doom-autoload-file', which tells Doom where to find all its
      ;; module autoloaded functions.
      (unless (or force-p noninteractive)
        (doom//reload-doom-autoloads)))
    ;; Loads `doom-package-autoload-file', which caches `load-path',
    ;; `auto-mode-alist', `Info-directory-list', `doom-disabled-packages' and
    ;; `package-activated-list'. A big reduction in startup time.
    (unless (doom-initialize-autoloads doom-package-autoload-file force-p)
      (unless (or force-p noninteractive)
        (doom//reload-package-autoloads))))
  ;; Initialize Doom core
  (unless noninteractive
    (require 'core-ui)
    (require 'core-editor)
    (require 'core-projects)
    (require 'core-keybinds)))

(defun doom-initialize-modules (&optional force-p)
  "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
  (when (or force-p (not doom-init-modules-p))
    ;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely
    ;; recurse by accident if any of them need `doom-initialize-modules'.
    (setq doom-init-modules-p t)
    (unless noninteractive
      (add-hook! 'emacs-startup-hook
        #'(doom|post-init doom|display-benchmark)))
    (run-hooks 'doom-pre-init-hook)
    (when doom-private-dir
      (let ((load-prefer-newer t))
        (load (expand-file-name "init" doom-private-dir)
              'noerror 'nomessage)))))

(defun doom-initialize-autoloads (file &optional clear-p)
  "Tries to load FILE (an autoloads file). Otherwise tries to regenerate it. If
CLEAR-P is non-nil, regenerate it anyway."
  (unless clear-p
    (load (file-name-sans-extension file) 'noerror 'nomessage)))

(defun doom-initialize-packages (&optional force-p)
  "Ensures that Doom's package management system, package.el and quelpa are
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
populated, if they aren't already.

If FORCE-P is non-nil, do it anyway.
If FORCE-P is 'internal, only (re)populate `doom-packages'.

Use this before any of package.el, quelpa or Doom's package management's API to
ensure all the necessary package metadata is initialized and available for
them."
  (with-temp-buffer ; prevent buffer-local settings from propagating
    (let ((load-prefer-newer t)) ; reduce stale code issues
      ;; package.el and quelpa handle themselves if their state changes during
      ;; the current session, but if you change an packages.el file in a module,
      ;; there's no non-trivial way to detect that, so we give you a way to
      ;; reload only doom-packages (by passing 'internal as FORCE-P).
      ;; `doom-packages'
      (unless (eq force-p 'internal)
        ;; `package-alist'
        (when (or force-p (not (bound-and-true-p package-alist)))
          (setq load-path doom-site-load-path)
          (require 'package)
          (setq package-activated-list nil
                package--initialized nil)
          (let (byte-compile-warnings)
            (condition-case _
                (quiet! (package-initialize))
              ('error (package-refresh-contents)
                      (setq doom--refreshed-p t)
                      (package-initialize)))))

        ;; `quelpa-cache'
        (when (or force-p (not (bound-and-true-p quelpa-cache)))
          (require 'quelpa)
          (setq quelpa-initialized-p nil)
          (or (quelpa-setup-p)
              (error "Could not initialize quelpa"))))

      (when (or force-p (not doom-packages))
        (let ((doom-modules (doom-module-table)))
          (setq doom-packages nil)
          (cl-flet
              ((_load
                (file &optional noerror interactive)
                (condition-case-unless-debug ex
                    (let ((noninteractive (not interactive)))
                      (load file noerror 'nomessage 'nosuffix))
                  ('error
                   (lwarn 'doom-initialize-packages :warning
                          "%s in %s: %s"
                          (car ex)
                          (file-relative-name file doom-emacs-dir)
                          (error-message-string ex))))))
            (let ((doom--stage 'packages))
              (_load (expand-file-name "packages.el" doom-core-dir))
              (cl-loop for key being the hash-keys of doom-modules
                       for path = (doom-module-path (car key) (cdr key) "packages.el")
                       do (let ((doom--current-module key)) (_load path t)))
              (cl-loop for dir in doom-psuedo-module-dirs
                       do (_load (expand-file-name "packages.el" dir) t)))))))))


;;
;; Module API
;;

(defun doom-module-p (category module)
  "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
  (and (hash-table-p doom-modules)
       (gethash (cons category module) doom-modules)
       t))

(defun doom-module-get (category module &optional property)
  "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
  (when-let* ((plist (gethash (cons category module) doom-modules)))
    (if property
        (plist-get plist property)
      plist)))

(defun doom-module-put (category module property value &rest rest)
  "Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs
of PROPERTY and VALUEs."
  (when-let* ((plist (doom-module-get category module)))
    (plist-put plist property value)
    (when rest
      (when (cl-oddp (length rest))
        (signal 'wrong-number-of-arguments (length (length rest))))
      (while rest
        (plist-put rest (pop rest) (pop rest))))
    (puthash (cons category module) plist doom-modules)))

(defun doom-module-set (category module &rest plist)
  "Enables a module by adding it to `doom-modules'.

CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the
following properties:

  :flags [SYMBOL LIST]  list of enabled category flags
  :path  [STRING]       path to category root directory

Example:
  (doom-module-set :lang 'haskell :flags '(+intero))"
  (when plist
    (let ((old-plist (doom-module-get category module)))
      (unless (plist-member plist :flags)
        (plist-put plist :flags (plist-get old-plist :flags)))
      (unless (plist-member plist :path)
        (plist-put plist :path (or (plist-get old-plist :path)
                                   (doom-module-locate-path category module))))))
  (let ((key (cons category module)))
    (puthash key plist doom-modules)))

(defun doom-module-path (category module &optional file)
  "Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and
MODULE (symbol).

If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'."
  (let ((path (doom-module-get category module :path)))
    (if file (expand-file-name file path)
      path)))

(defun doom-module-locate-path (category &optional module file)
  "Searches `doom-modules-dirs' to find the path to a module.

CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE
is a string that will be appended to the resulting path. If no path exists, this
returns nil, otherwise an absolute path.

This doesn't require modules to be enabled. For enabled modules us
`doom-module-path'."
  (when (keywordp category)
    (setq category (substring (symbol-name category) 1)))
  (when (and module (symbolp module))
    (setq module (symbol-name module)))
  (cl-loop for default-directory in doom-modules-dirs
           for path = (concat category "/" module "/" file)
           if (file-exists-p path)
           return (expand-file-name path)))

(defun doom-module-from-path (&optional path)
  "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)."
  (or doom--current-module
      (when path
        (save-match-data
          (setq path (file-truename path))
          (when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)/.*$" path)
            (when-let* ((module (match-string 1 path))
                        (submodule (match-string 2 path)))
              (cons (intern (concat ":" module))
                    (intern submodule))))))))

(defun doom-module-load-path ()
  "Returns a list of absolute file paths to activated modules, with APPEND-FILE
added, if the file exists."
  (append (cl-loop for plist being the hash-values of doom-modules
                   collect (plist-get plist :path))
          (cl-remove-if-not #'file-directory-p doom-psuedo-module-dirs)))

(defun doom-module-table (&optional modules)
  "Converts MODULES (a malformed plist) into a hash table of modules, fit for
`doom-modules'. If MODULES is omitted, it will fetch your module mplist from the
`doom!' block in your private init.el file."
  (let* ((doom-modules (make-hash-table :test #'equal
                                        :size (if modules (length modules) 100)
                                        :rehash-threshold 1.0)))
    (when (null modules)
      (let* ((init-file (expand-file-name "init.el" doom-private-dir))
             (short-init-file (abbreviate-file-name init-file)))
        (if (not (file-exists-p init-file))
            (error "%s doesn't exist" short-init-file)
          (with-temp-buffer
            (insert-file-contents init-file)
            (when (re-search-forward "^\\s-*\\((doom! \\)" nil t)
              (goto-char (match-beginning 1))
              (setq modules (cdr (sexp-at-point))))))
        (unless (or modules noninteractive)
          (warn "Couldn't gather module list from %s" short-init-file))))
    (if (eq modules t) (setq modules nil))
    (let (category)
      (dolist (m modules)
        (cond ((keywordp m) (setq category m))
              ((not category) (error "No module category specified for %s" m))
              ((let ((module (if (listp m) (car m) m))
                     (flags  (if (listp m) (cdr m))))
                 (if-let* ((path (doom-module-locate-path category module)))
                     (doom-module-set category module :flags flags :path path)
                   (when doom-debug-mode
                     (message "Couldn't find the %s %s module" category module))))))))
    doom-modules))


;;
;; Use-package modifications
;;

(autoload 'use-package "use-package-core" nil nil t)

;; Adds the :after-call custom keyword to `use-package' (and consequently,
;; `def-package!'). :after-call takes a symbol ro list of symbols. These symbols<commit_msg>Fix doom-module-table failing to fetch module list<commit_after>;;; core-packages.el --- package management system -*- lexical-binding: t; -*-

;; Emacs package management is opinionated, and so am I. I've bound together
;; `use-package', `quelpa' and package.el to create my own, rolling-release,
;; lazily-loaded package management system for Emacs.
;;
;; The three key commands are:
;;
;; + `bin/doom install` or `doom//packages-install': Installs packages that are
;;   wanted, but not installed.
;; + `bin/doom update` or `doom//packages-update': Updates packages that are
;;   out-of-date.
;; + `bin/doom autoremove` or `doom//packages-autoremove': Uninstalls packages
;;   that are no longer needed.
;;
;; This system reads packages.el files located in each activated module (and one
;; in `doom-core-dir'). These contain `package!' blocks that tell DOOM what
;; plugins to install and where from.
;;
;; Why all the trouble? Because:
;; 1. Scriptability: I live in the command line. I want a programmable
;;    alternative to `list-packages' for updating and installing packages.
;; 2. Flexibility: I want packages from sources other than ELPA. Primarily
;;    github, because certain plugins are out-of-date through official channels,
;;    have changed hands, have a superior fork, or simply aren't in any ELPA
;;    repo.
;; 3. Stability: I used Cask before this. It would error out with cyrptic errors
;;    depending on the version of Emacs I used and the alignment of the planets.
;;    No more.
;; 4. Performance: A minor point, but this system is lazy-loaded (more so if you
;;    byte-compile). Not having to initialize package.el (or check that your
;;    packages are installed) every time you start up Emacs affords us precious
;;    seconds.
;; 5. Simplicity: No Cask, no external dependencies (unless you count make),
;;    just Emacs. Arguably, my config is still over-complicated, but shhh, it's
;;    fine. Everything is fine.
;;
;; You should be able to use package.el commands without any conflicts.
;;
;; See core/autoload/packages.el for more functions.

(defvar doom-init-p nil
  "Non-nil if `doom-initialize' has run.")

(defvar doom-init-modules-p nil
  "Non-nil if `doom-initialize-modules' has run.")

(defvar doom-init-time nil
  "The time it took, in seconds, for DOOM Emacs to initialize.")

(defvar doom-modules ()
  "A hash table of enabled modules. Set by `doom-initialize-modules'.")

(defvar doom-modules-dirs
  (list (expand-file-name "modules/" doom-private-dir) doom-modules-dir)
  "A list of module root directories. Order determines priority.")

(defvar doom-psuedo-module-dirs (list doom-private-dir)
  "Additional paths for modules that are outside of `doom-modules-dirs'.
`doom//reload-doom-autoloads', `doom//byte-compile' and
`doom-initialize-packages' will include the directories in this list.")

(defvar doom-packages ()
  "A list of enabled packages. Each element is a sublist, whose CAR is the
package's name as a symbol, and whose CDR is the plist supplied to its
`package!' declaration. Set by `doom-initialize-packages'.")

(defvar doom-core-packages '(persistent-soft use-package quelpa async)
  "A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.")

(defvar doom-disabled-packages ()
  "A list of packages that should be ignored by `def-package!'.")

(defvar doom-site-load-path load-path
  "The starting load-path, before it is altered by `doom-initialize'.")

(defvar doom-autoload-file (concat doom-local-dir "autoloads.el")
  "Where `doom//reload-doom-autoloads' will generate its core autoloads file.")

(defvar doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
  "Where `doom//reload-package-autoloads' will generate its package.el autoloads
file.")

(defvar doom-reload-hook nil
  "A list of hooks to run when `doom//reload-load-path' is called.")

(defvar doom--current-module nil)
(defvar doom--refreshed-p nil)
(defvar doom--stage 'init)
(defvar doom--inhibit-reload nil)

;;
(setq autoload-compute-prefixes nil
      package--init-file-ensured t
      package-user-dir (expand-file-name "elpa" doom-packages-dir)
      package-enable-at-startup nil
      package-archives
      '(("gnu"   . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ("org"   . "https://orgmode.org/elpa/"))
      ;; I omit Marmalade because its packages are manually submitted rather
      ;; than pulled, so packages are often out of date with upstream.

      ;; security settings
      gnutls-verify-error (not (getenv "INSECURE")) ; you shouldn't use this
      tls-checktrust gnutls-verify-error
      tls-program (list "gnutls-cli --x509cafile %t -p %p %h"
                        ;; compatibility fallbacks
                        "gnutls-cli -p %p %h"
                        "openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof")

      use-package-verbose doom-debug-mode
      use-package-minimum-reported-time (if doom-debug-mode 0 0.1)

      ;; Don't track MELPA, we'll use package.el for that
      quelpa-checkout-melpa-p nil
      quelpa-update-melpa-p nil
      quelpa-melpa-recipe-stores nil
      quelpa-self-upgrade-p nil
      quelpa-verbose doom-debug-mode
      quelpa-dir (expand-file-name "quelpa" doom-packages-dir)

      byte-compile-dynamic nil
      byte-compile-verbose doom-debug-mode
      byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))

;; accommodate INSECURE setting
(unless gnutls-verify-error
  (dolist (archive package-archives)
    (setcdr archive (replace-regexp-in-string "^https://" "http://" (cdr archive) t nil))))


;;
;; Helpers 'n hooks
;;

(defun doom--assert-stage-p (stage macro)
  (cl-assert (eq stage doom--stage)
             nil
             "Found %s call in non-%s.el file (%s)"
             macro (symbol-name stage)
             (if (file-in-directory-p load-file-name doom-emacs-dir)
                 (file-relative-name load-file-name doom-emacs-dir)
               (abbreviate-file-name load-file-name))))

(defun doom|display-benchmark (&optional return-p)
  "Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.

If RETURN-P, return the message as a string instead of displaying it."
  (funcall (if return-p #'format #'message)
           "Doom loaded %s packages across %d modules in %.03fs"
           ;; Certainly imprecise, especially where custom additions to
           ;; load-path are concerned, but I don't mind a [small] margin of
           ;; error in the plugin count in exchange for faster startup.
           (- (length load-path) (length doom-site-load-path))
           (if doom-modules (hash-table-count doom-modules) 0)
           (or doom-init-time
               (setq doom-init-time (float-time (time-subtract (current-time) before-init-time))))))

(defun doom|post-init ()
  "Run `doom-post-init-hook'. That's all."
  (run-hooks 'doom-post-init-hook))

(defun doom|run-all-startup-hooks ()
  "Run all startup Emacs hooks. Meant to be executed after starting Emacs with
-q or -Q, for example:

  emacs -Q -l init.el -f doom|run-all-startup-hooks"
  (run-hooks 'after-init-hook 'delayed-warnings-hook
             'emacs-startup-hook 'term-setup-hook
             'window-setup-hook))


;;
;; Bootstrap helpers
;;

(defun doom-ensure-packages-initialized (&optional force-p)
  "Make sure package.el is initialized."
  (when (or force-p (not package--initialized))
    (require 'package)
    (setq package-activated-list nil
          package--initialized nil)
    (let (byte-compile-warnings)
      (condition-case _
          (quiet! (package-initialize))
        ('error (package-refresh-contents)
                (setq doom--refreshed-p t)
                (package-initialize))))))

(defun doom-ensure-core-packages ()
  "Make sure `doom-core-packages' are installed."
  (when-let* ((core-packages (cl-remove-if #'package-installed-p doom-core-packages)))
    (message "Installing core packages")
    (unless doom--refreshed-p
      (package-refresh-contents))
    (dolist (package core-packages)
      (let ((inhibit-message t))
        (package-install package))
      (if (package-installed-p package)
          (message "✓ Installed %s" package)
        (error "✕ Couldn't install %s" package)))
    (message "Installing core packages...done")))

(defun doom-ensure-core-directories ()
  "Make sure all Doom's essential local directories (in and including
`doom-local-dir') exist."
  (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir))
    (unless (file-directory-p dir)
      (make-directory dir t))))


;;
;; Bootstrap API
;;

(autoload 'doom//reload-doom-autoloads "autoload/modules" nil t)
(autoload 'doom//reload-package-autoloads "autoload/modules" nil t)

(defun doom-initialize (&optional force-p)
  "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).

The bootstrap process involves making sure 1) the essential directories exist,
2) the core packages are installed, 3) `doom-autoload-file' and
`doom-package-autoload-file' exist and have been loaded, and 4) Doom's core
files are loaded.

If the cache exists, much of this function isn't run, which substantially
reduces startup time.

The overall load order of Doom is as follows:

  ~/.emacs.d/init.el
  ~/.emacs.d/core/core.el
  `doom-pre-init-hook'
  ~/.doom.d/init.el
  Module init.el files
  `doom-init-hook'
  Module config.el files
  ~/.doom.d/config.el
  `after-init-hook'
  `emacs-startup-hook'
  `doom-post-init-hook' (at end of `emacs-startup-hook')

Module load order is determined by your `doom!' block. See `doom-modules-dirs'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
  (when (or force-p (not doom-init-p))
    ;; Set this to prevent infinite recursive calls to `doom-initialize'
    (setq doom-init-p t)
    ;; `doom-autoload-file' tells Emacs where to load all its autoloaded
    ;; functions from. This includes everything in core/autoload/*.el and all
    ;; the autoload files in your enabled modules.
    (unless (doom-initialize-autoloads doom-autoload-file force-p)
      (doom-ensure-core-directories)
      (doom-ensure-packages-initialized force-p)
      (doom-ensure-core-packages)
      ;; Regenerate `doom-autoload-file', which tells Doom where to find all its
      ;; module autoloaded functions.
      (unless (or force-p noninteractive)
        (doom//reload-doom-autoloads)))
    ;; Loads `doom-package-autoload-file', which caches `load-path',
    ;; `auto-mode-alist', `Info-directory-list', `doom-disabled-packages' and
    ;; `package-activated-list'. A big reduction in startup time.
    (unless (doom-initialize-autoloads doom-package-autoload-file force-p)
      (unless (or force-p noninteractive)
        (doom//reload-package-autoloads))))
  ;; Initialize Doom core
  (unless noninteractive
    (require 'core-ui)
    (require 'core-editor)
    (require 'core-projects)
    (require 'core-keybinds)))

(defun doom-initialize-modules (&optional force-p)
  "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
  (when (or force-p (not doom-init-modules-p))
    ;; Set `doom-init-modules-p' early, so `doom-pre-init-hook' won't infinitely
    ;; recurse by accident if any of them need `doom-initialize-modules'.
    (setq doom-init-modules-p t)
    (unless noninteractive
      (add-hook! 'emacs-startup-hook
        #'(doom|post-init doom|display-benchmark)))
    (run-hooks 'doom-pre-init-hook)
    (when doom-private-dir
      (let ((load-prefer-newer t))
        (load (expand-file-name "init" doom-private-dir)
              'noerror 'nomessage)))))

(defun doom-initialize-autoloads (file &optional clear-p)
  "Tries to load FILE (an autoloads file). Otherwise tries to regenerate it. If
CLEAR-P is non-nil, regenerate it anyway."
  (unless clear-p
    (load (file-name-sans-extension file) 'noerror 'nomessage)))

(defun doom-initialize-packages (&optional force-p)
  "Ensures that Doom's package management system, package.el and quelpa are
initialized, and `doom-packages', `packages-alist' and `quelpa-cache' are
populated, if they aren't already.

If FORCE-P is non-nil, do it anyway.
If FORCE-P is 'internal, only (re)populate `doom-packages'.

Use this before any of package.el, quelpa or Doom's package management's API to
ensure all the necessary package metadata is initialized and available for
them."
  (with-temp-buffer ; prevent buffer-local settings from propagating
    (let ((load-prefer-newer t)) ; reduce stale code issues
      ;; package.el and quelpa handle themselves if their state changes during
      ;; the current session, but if you change an packages.el file in a module,
      ;; there's no non-trivial way to detect that, so we give you a way to
      ;; reload only doom-packages (by passing 'internal as FORCE-P).
      ;; `doom-packages'
      (unless (eq force-p 'internal)
        ;; `package-alist'
        (when (or force-p (not (bound-and-true-p package-alist)))
          (setq load-path doom-site-load-path)
          (require 'package)
          (setq package-activated-list nil
                package--initialized nil)
          (let (byte-compile-warnings)
            (condition-case _
                (quiet! (package-initialize))
              ('error (package-refresh-contents)
                      (setq doom--refreshed-p t)
                      (package-initialize)))))

        ;; `quelpa-cache'
        (when (or force-p (not (bound-and-true-p quelpa-cache)))
          (require 'quelpa)
          (setq quelpa-initialized-p nil)
          (or (quelpa-setup-p)
              (error "Could not initialize quelpa"))))

      (when (or force-p (not doom-packages))
        (let ((doom-modules (doom-module-table)))
          (setq doom-packages nil)
          (cl-flet
              ((_load
                (file &optional noerror interactive)
                (condition-case-unless-debug ex
                    (let ((noninteractive (not interactive)))
                      (load file noerror 'nomessage 'nosuffix))
                  ('error
                   (lwarn 'doom-initialize-packages :warning
                          "%s in %s: %s"
                          (car ex)
                          (file-relative-name file doom-emacs-dir)
                          (error-message-string ex))))))
            (let ((doom--stage 'packages))
              (_load (expand-file-name "packages.el" doom-core-dir))
              (cl-loop for key being the hash-keys of doom-modules
                       for path = (doom-module-path (car key) (cdr key) "packages.el")
                       do (let ((doom--current-module key)) (_load path t)))
              (cl-loop for dir in doom-psuedo-module-dirs
                       do (_load (expand-file-name "packages.el" dir) t)))))))))


;;
;; Module API
;;

(defun doom-module-p (category module)
  "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
  (and (hash-table-p doom-modules)
       (gethash (cons category module) doom-modules)
       t))

(defun doom-module-get (category module &optional property)
  "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
  (when-let* ((plist (gethash (cons category module) doom-modules)))
    (if property
        (plist-get plist property)
      plist)))

(defun doom-module-put (category module property value &rest rest)
  "Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs
of PROPERTY and VALUEs."
  (when-let* ((plist (doom-module-get category module)))
    (plist-put plist property value)
    (when rest
      (when (cl-oddp (length rest))
        (signal 'wrong-number-of-arguments (length (length rest))))
      (while rest
        (plist-put rest (pop rest) (pop rest))))
    (puthash (cons category module) plist doom-modules)))

(defun doom-module-set (category module &rest plist)
  "Enables a module by adding it to `doom-modules'.

CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the
following properties:

  :flags [SYMBOL LIST]  list of enabled category flags
  :path  [STRING]       path to category root directory

Example:
  (doom-module-set :lang 'haskell :flags '(+intero))"
  (when plist
    (let ((old-plist (doom-module-get category module)))
      (unless (plist-member plist :flags)
        (plist-put plist :flags (plist-get old-plist :flags)))
      (unless (plist-member plist :path)
        (plist-put plist :path (or (plist-get old-plist :path)
                                   (doom-module-locate-path category module))))))
  (let ((key (cons category module)))
    (puthash key plist doom-modules)))

(defun doom-module-path (category module &optional file)
  "Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and
MODULE (symbol).

If the category isn't enabled this will always return nil. For finding disabled
modules use `doom-module-locate-path'."
  (let ((path (doom-module-get category module :path)))
    (if file (expand-file-name file path)
      path)))

(defun doom-module-locate-path (category &optional module file)
  "Searches `doom-modules-dirs' to find the path to a module.

CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE
is a string that will be appended to the resulting path. If no path exists, this
returns nil, otherwise an absolute path.

This doesn't require modules to be enabled. For enabled modules us
`doom-module-path'."
  (when (keywordp category)
    (setq category (substring (symbol-name category) 1)))
  (when (and module (symbolp module))
    (setq module (symbol-name module)))
  (cl-loop for default-directory in doom-modules-dirs
           for path = (concat category "/" module "/" file)
           if (file-exists-p path)
           return (expand-file-name path)))

(defun doom-module-from-path (&optional path)
  "Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path)."
  (or doom--current-module
      (when path
        (save-match-data
          (setq path (file-truename path))
          (when (string-match "/modules/\\([^/]+\\)/\\([^/]+\\)/.*$" path)
            (when-let* ((module (match-string 1 path))
                        (submodule (match-string 2 path)))
              (cons (intern (concat ":" module))
                    (intern submodule))))))))

(defun doom-module-load-path ()
  "Returns a list of absolute file paths to activated modules, with APPEND-FILE
added, if the file exists."
  (append (cl-loop for plist being the hash-values of doom-modules
                   collect (plist-get plist :path))
          (cl-remove-if-not #'file-directory-p doom-psuedo-module-dirs)))

(defun doom-module-table (&optional modules)
  "Converts MODULES (a malformed plist) into a hash table of modules, fit for
`doom-modules'. If MODULES is omitted, it will fetch your module mplist from the
`doom!' block in your private init.el file."
  (let* ((doom-modules (make-hash-table :test #'equal
                                        :size (if modules (length modules) 100)
                                        :rehash-threshold 1.0)))
    (when (null modules)
      (let* ((init-file (expand-file-name "init.el" doom-private-dir))
             (short-init-file (abbreviate-file-name init-file)))
        (if (not (file-exists-p init-file))
            (error "%s doesn't exist" short-init-file)
          (with-temp-buffer
            (delay-mode-hooks (emacs-lisp-mode))
            (insert-file-contents-literally init-file)
            (when (re-search-forward "^\\s-*\\((doom! \\)" nil t)
              (goto-char (match-beginning 1))
              (setq modules (cdr (sexp-at-point))))))
        (unless (or modules noninteractive)
          (warn "Couldn't gather module list from %s" short-init-file))))
    (if (eq modules t) (setq modules nil))
    (let (category)
      (dolist (m modules)
        (cond ((keywordp m) (setq category m))
              ((not category) (error "No module category specified for %s" m))
              ((let ((module (if (listp m) (car m) m))
                     (flags  (if (listp m) (cdr m))))
                 (if-let* ((path (doom-module-locate-path category module)))
                     (doom-module-set category module :flags flags :path path)
                   (when doom-debug-mode
                     (message "Couldn't find the %s %s module" category module))))))))
    doom-modules))


;;
;; Use-package modifications
;;

(autoload 'use-package "use-package-core" nil nil t)

;; Adds the :after-call custom keyword to `use-package' (and consequently,
;; `def-package!'). :after-call takes a symbol ro list of symbols. These symbols
{'fraction_non_alphanumeric': 0.1229279777062413, 'fraction_numerical': 0.0013933599192812185, 'mean_word_length': 2.7685134890458083, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3702902', 'n_tokens_mistral': 13218, 'n_tokens_neox': 12659, 'n_words': 4205}