diff --git a/Makefile b/Makefile index 47758ab40..a233df3cf 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,8 @@ -# Ensure emacs always runs from this makefile's PWD -EMACS = emacs -q --eval "(setq user-emacs-directory default-directory load-prefer-newer t)" -DOOM = $(EMACS) --batch -l init.el -DOOMI = $(subst --batch,,$(DOOM)) - +DOOM = bin/doom MODULES = $(patsubst modules/%/, %, $(sort $(dir $(wildcard modules/*/ modules/*/*/)))) all: - @$(DOOM) -f doom//reload-packages + @$(DOOM) refresh ## Shortcuts a: autoloads @@ -16,88 +12,66 @@ U: upgrade r: autoremove c: compile cc: compile-core -ce: compile-elpa +cp: compile-plugins re: recompile d: doctor -quickstart: | ~/.doom.d/init.el all recompile -~/.doom.d/init.el: - @echo "Creating ~/.doom.d directory" - @mkdir ~/.doom.d && cp init.example.el ~/.doom.d/init.el - @touch ~/.doom.d/config.el +quickstart: + @$(DOOM) quickstart + ## Package management -install: | .local/autoloads.el - @$(DOOM) -f doom//packages-install - -update: | .local/autoloads.el - @$(DOOM) -f doom//packages-update - -autoremove: | .local/autoloads.el - @$(DOOM) -f doom//packages-autoremove - +install: + @$(DOOM) install +update: + @$(DOOM) update +autoremove: + @$(DOOM) autoremove autoloads: - @$(DOOM) -f doom//reload-autoloads - -upgrade: | _upgrade recompile all -_upgrade: - @git pull origin $(shell git rev-parse --abbrev-ref HEAD) + @$(DOOM) autoloads +upgrade: + @$(DOOM) upgrade ## Byte compilation -# compile -# compile-core +compile: + @$(DOOM) compile +compile-core: + @$(DOOM) compile :core +compile-private: + @$(DOOM) compile :private +compile-plugins: + @$(DOOM) compile :plugins +recompile: + @$(DOOM) recompile +clean: + @$(DOOM) clean # compile-module # compile-module/submodule -compile: | clean - @$(DOOM) -f doom//byte-compile - -compile-core: | clean - @$(DOOM) -f doom//byte-compile-core - -compile-elpa: - @$(DOOM) -f doom//byte-recompile-plugins - $(patsubst %, compile-%, $(MODULES)): | .local/autoloads.el - @$(DOOM) -f doom//byte-compile -- $(patsubst compile-%, %, $@) - -recompile: - @$(DOOM) -f doom//byte-compile -- -r - -clean: - @$(DOOM) -f doom//clean-byte-compiled-files + @$(DOOM) $@ $(subst compile-, , $@) ## Unit tests -# test -# test-core +test: + @$(DOOM) test +test-core: + @$(DOOM) test :core # test-module # test-module/submodule -test: | .local/autoloads.el - @$(DOOM) -f doom//run-tests - -test-core $(patsubst %, test-%, $(MODULES)): | .local/autoloads.el - @$(DOOM) -f doom//run-tests -- $(subst test-, , $@) - -# run tests interactively -testi: | .local/autoloads.el - @$(DOOMI) -f doom//run-tests +$(patsubst %, test-%, $(MODULES)): + @$(DOOM) test $(subst test-, , $@) ## Utility tasks # Runs Emacs from a different folder than ~/.emacs.d; only use this for testing! run: - @$(DOOMI) $(ARGS) --eval "(run-hooks 'after-init-hook 'emacs-startup-hook 'window-setup-hook)" + @$(DOOM) run $(ARGS) +# Prints debug info about your current setup +info: + @$(DOOM) info # Diagnoses potential OS/environment issues doctor: - @$(EMACS) --script bin/doom-doctor - -# Prints debug info about your current setup -info: - @$(EMACS) --batch -l core/core.el -l core/autoload/util.el -f doom/info - -## Internal tasks -.local/autoloads.el: - @$(DOOM) -f doom-initialize-autoloads + @$(DOOM) doctor .PHONY: all compile test testi clean diff --git a/bin/doom b/bin/doom new file mode 100755 index 000000000..53e55de43 --- /dev/null +++ b/bin/doom @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +":"; command -v emacs >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; } # -*-emacs-lisp-*- +":"; VERSION=$(emacs --version | head -n1) +":"; [[ $VERSION == *\ 2[0-2].[0-1].[0-9] ]] && { echo "You're running $VERSION"; echo "That version is too old to run Doom. Check your PATH"; echo; exit 2; } +":"; DOOMDIR=$(dirname "${BASH_SOURCE:-${(%):-%x}}")/.. +":"; [[ $1 == doc || $1 == doctor ]] && { cd "$DOOMDIR"; exec emacs --script bin/doom-doctor; exit 0; } +":"; [[ $1 == run ]] && { cd "$DOOMDIR"; shift; exec emacs -Q -l bin/doom "$@"; exit 0; } +":"; exec emacs --quick --script "$0" -- $@ +":"; exit 0 + +(defun usage () + (with-temp-buffer + (insert (format! "%s %s [COMMAND] [ARGS...]\n" + (bold "Usage:") + (file-name-nondirectory load-file-name)) + "\n" + "A command line interfacing for managing Doom Emacs; including\n" + "package management, diagnostics, unit tests, and byte-compilation.\n" + "\n" + "This tool also makes it trivial to launch Emacs out of a different\n" + "folder or with a different private module.\n" + "\n" + (format! (bold "Example:\n")) + " doom install\n" + " doom help update\n" + " doom compile :core lang/php lang/python\n" + " doom run\n" + " doom run -nw file.txt file2.el\n" + " doom run -p ~/.other.doom.d -e ~/.other.emacs.d -nw file.txt\n" + "\n" + (format! (bold "Options:\n")) + " -d --debug\t\tTurns on doom-debug-mode (and debug-on-error)\n" + " -e --emacsd DIR\tUse the emacs config at DIR (e.g. ~/.emacs.d)\n" + " -p --private DIR\tUse the private module at DIR (e.g. ~/.doom.d)\n" + " -y --yes\t\tAuto-accept all confirmation prompts\n\n") + (princ (buffer-string))) + (doom--dispatch-help)) + +;; +(let ((args (cdr (cdr (cdr command-line-args)))) + (emacs-dir (expand-file-name "../" (file-name-directory load-file-name)))) + ;; Parse options + (while (ignore-errors (string-prefix-p "-" (car args))) + (pcase (pop args) + ((or "-d" "--debug") + (setq doom-debug-mode t)) + ((or "-p" "--private") + (setq doom-private-dir (expand-file-name (pop args))) + (or (file-directory-p doom-private-dir) + (error "%s does not exist" doom-private-dir))) + ((or "-e" "--emacsd") + (setq emacs-dir (expand-file-name (pop args))) + (or (file-directory-p emacs-dir) + (error "%s does not exist" emacs-dir))) + ((or "-y" "--yes") + (setq doom-auto-accept t)))) + + ;; Bootstrap Doom + (load (expand-file-name "init" emacs-dir) + nil 'nomessage) + + (cond ((not noninteractive) + (doom|run-all-startup-hooks)) + ((and (not (cdr args)) + (member (car args) '("help" "h"))) + (usage)) + ((not args) + (error "Expecting a command")) + ((let ((default-directory user-emacs-directory)) + (setq argv nil + noninteractive 'doom) + (doom-dispatch args))))) + diff --git a/bin/doom-doctor b/bin/doom-doctor index 08899c919..0ad1e003a 100755 --- a/bin/doom-doctor +++ b/bin/doom-doctor @@ -1,6 +1,8 @@ #!/usr/bin/env bash ":"; command -v emacs >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; } # -*-emacs-lisp-*- -":"; [[ $(emacs --version | head -n1) == *\ 2[0-2].[0-1].[0-9] ]] && { echo "You're running $(emacs --version | head -n1)"; echo "That version is too old to run the doctor. Check your PATH"; echo; exit 2; } || exec emacs --quick --script "$0" +":"; VERSION=$(emacs --version | head -n1) +":"; [[ $VERSION == *\ 2[0-2].[0-1].[0-9] ]] && { echo "You're running $VERSION"; echo "That version is too old to run the doctor. Check your PATH"; echo; exit 2; } +":"; exec emacs --quick --script "$0"; exit 0 ;; Uses a couple simple heuristics to locate issues with your environment that ;; could interfere with running or setting up DOOM Emacs. @@ -18,6 +20,7 @@ ;; (defvar doom-init-p nil) +(defvar doom-warnings 0) (defvar doom-errors 0) (defmacro when! (cond &rest body) (declare (indent defun)) @@ -62,7 +65,7 @@ (format (concat prefix ,msg) ,@args)))) (defmacro error! (&rest args) `(progn (msg! (color 31 ,@args)) (setq doom-errors (+ doom-errors 1)))) -(defmacro warn! (&rest args) `(progn (msg! (color 33 ,@args)) (setq doom-errors (+ doom-errors 1)))) +(defmacro warn! (&rest args) `(progn (msg! (color 33 ,@args)) (setq doom-warnings (+ doom-warnings 1)))) (defmacro success! (&rest args) `(msg! (color 32 ,@args))) (defmacro section! (&rest args) `(msg! (color 1 (color 34 ,@args)))) @@ -112,11 +115,8 @@ (message "Compiled with:\n%s" (indented 2 system-configuration-features))) (message "uname -a:\n%s\n" (indented 2 (sh "uname -a"))) -(msg! "----\n") - ;; --- is emacs set up properly? ------------------------------ -(section! "test-emacs") (when (version< emacs-version "25.1") (error! "Important: Emacs %s detected [%s]" emacs-version (executable-find "emacs")) (explain! @@ -125,7 +125,6 @@ (concat "\nMacOS users should use homebrew (https://brew.sh) to install Emacs\n" " brew install emacs --with-modules --with-imagemagick --with-cocoa")))) -(section! "test-private-config") (let ((xdg-dir (concat (or (getenv "XDG_CONFIG_HOME") "~/.config") "/doom/")) @@ -140,14 +139,14 @@ ;; --- is the environment set up properly? -------------------- -;; windows? windows -(section! "test-windows") +;; on windows? +(section! "Checking your OS...") (when (memq system-type '(windows-nt ms-dos cygwin)) (warn! "Warning: Windows detected") (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")) -;; are all default fonts present -(section! "test-fonts") +;; are all default fonts present? +(section! "Checking your fonts...") (if (not (fboundp 'find-font)) (progn (warn! "Warning: unable to detect font") @@ -170,7 +169,7 @@ "case, ignore this warning.")))))) ;; gnutls-cli & openssl -(section! "test-gnutls") +(section! "Checking gnutls/openssl...") (cond ((executable-find "gnutls-cli")) ((executable-find "openssl") (let* ((output (sh "openssl ciphers -v")) @@ -205,7 +204,8 @@ "network, provider, government, neckbearded mother-in-laws, geeky roommates, " "or just about anyone who knows more about computers than you do!"))) -(section! "test-tls") +;; are certificates validated properly? +(section! "Testing your root certificates...") (cond ((not (string-match-p "\\_" system-configuration-features)) (warn! "Warning: You didn't install Emacs with gnutls support") (explain! @@ -227,8 +227,7 @@ (gnutls-verify-error t)) (dolist (url '("https://elpa.gnu.org" "https://melpa.org")) (when! (condition-case-unless-debug e - (if (let ((inhibit-message t)) (url-retrieve-synchronously url)) - (ignore (success! "Validated %s" url)) + (unless (let ((inhibit-message t)) (url-retrieve-synchronously url)) 'empty) ('timed-out 'timeout) ('error e)) @@ -245,7 +244,7 @@ t 'empty) ('timed-out 'timeout) - ('error (ignore (success! "Successfully rejected %s" url)))) + ('error)) (pcase it (`empty (error! "Couldn't reach %s" url)) (`timeout (error! "Timed out trying to contact %s" ex)) @@ -254,8 +253,8 @@ ((error! "Nope!"))) -;; bsd vs gnu tar -(section! "test-tar") +;; which variant of tar is on your system? bsd or gnu tar? +(section! "Checking for GNU/BSD tar...") (let ((tar-bin (or (executable-find "gtar") (executable-find "tar")))) (if tar-bin @@ -276,14 +275,11 @@ ;; --- are your modules set up properly? ---------------------- -(message "\n----") (let (doom-core-packages doom-debug-mode) (condition-case ex (let ((inhibit-message t)) - (load "~/.emacs.d/core/core.el" nil t) - (let (noninteractive) - (doom-initialize t) - (doom|finalize)) + (load (concat user-emacs-directory "init.el") nil t) + (doom-initialize-modules) (success! "Attempt to load DOOM: success! Loaded v%s" doom-version)) ('error (warn! "Attempt to load DOOM: failed\n %s\n" @@ -291,14 +287,14 @@ (setq doom-modules nil)))) (when (bound-and-true-p doom-modules) - (section! "test-modules") + (section! "Checking your enabled modules...") (let ((indent 4)) (advice-add #'require :around #'doom*shut-up) (maphash (lambda (key plist) (condition-case ex - (let ((doctor-file (doom-module-expand-file (car key) (cdr key) "doctor.el")) - (packages-file (doom-module-expand-file (car key) (cdr key) "packages.el")) + (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el")) + (packages-file (doom-module-path (car key) (cdr key) "packages.el")) doom-packages) (when (or (file-exists-p doctor-file) (file-exists-p packages-file)) @@ -306,7 +302,8 @@ (doom--stage 'packages)) (when (load packages-file t t) (dolist (package (cl-remove-if #'package-installed-p doom-packages :key #'car)) - (error! "%s is not installed" (car package)))) + (unless (package-built-in-p (car package)) + (error! "%s is not installed" (car package))))) (let ((doom--stage 'doctor)) (load doctor-file t t))))) ('error @@ -314,7 +311,13 @@ doom-modules))) ;; -(message "\n----") -(if (> doom-errors 0) - (warn! "There were %s issues!" doom-errors) +(message "\n") +(dolist (msg (list (list doom-errors "error" 31) + (list doom-warnings "warning" 33))) + (when (> (car msg) 0) + (message (color (nth 2 msg) (if (= (car msg) 1) "There is %d %s!" "There are %d %ss!") + (car msg) (nth 1 msg))))) + +(when (and (zerop doom-errors) + (zerop doom-warnings)) (success! "Everything seems fine, happy Emacs'ing!")) diff --git a/bin/doom.cmd b/bin/doom.cmd new file mode 100644 index 000000000..2eeb0f2fe --- /dev/null +++ b/bin/doom.cmd @@ -0,0 +1,14 @@ +:: Forward the ./doom script to Emacs + +@ECHO OFF +PUSHD "%~dp0" >NUL + +IF %1=="run" ( + SHIFT + emacs -Q $* -l init.el -f "doom|run-all-startup-hooks" +) ELSE ( + emacs --quick --script ./doom -- $* +) + +POPD >NUL +ECHO ON diff --git a/core/autoload/cache.el b/core/autoload/cache.el index c4392be0a..1093bf9ce 100644 --- a/core/autoload/cache.el +++ b/core/autoload/cache.el @@ -12,8 +12,6 @@ ;; Like persistent-soft, caches assume a 2-tier structure, where all caches are ;; namespaced by location. -(require 'persistent-soft) - (defvar doom-cache-alists () "An alist of alists, containing lists of variables for the doom cache library to persist across Emacs sessions.") diff --git a/core/autoload/debug.el b/core/autoload/debug.el new file mode 100644 index 000000000..2662128e5 --- /dev/null +++ b/core/autoload/debug.el @@ -0,0 +1,303 @@ +;;; core/autoload/debug.el -*- lexical-binding: t; -*- + +(defun doom-template-insert (template) + "TODO" + (let ((file (expand-file-name (format "templates/%s" template) doom-core-dir))) + (when (file-exists-p file) + (insert-file-contents file)))) + +;;;###autoload +(defun doom-info () + "Returns diagnostic information about the current Emacs session in markdown, +ready to be pasted in a bug report on github." + (doom-initialize) + (require 'vc-git) + (let ((default-directory doom-emacs-dir) + (doom-modules (doom-module-table))) + (format + (concat "- OS: %s (%s)\n" + "- Emacs: %s (%s)\n" + "- Doom: %s (%s %s)\n" + "- Graphic display: %s (daemon: %s)\n" + "- System features: %s\n" + "- Details:\n" + " ```elisp\n" + " uname -a: %s\n" + " modules: %s\n" + " packages: %s\n" + " elc dirs: %s\n" + " exec-path: %s\n" + " ```") + system-type system-configuration + emacs-version (format-time-string "%b %d, %Y" emacs-build-time) + doom-version + (if-let* ((branch (vc-git--symbolic-ref "core/core.el"))) + branch + "n/a") + (if-let* ((rev (vc-git-working-revision "core/core.el"))) + rev + "n/a") + (display-graphic-p) (daemonp) + (bound-and-true-p system-configuration-features) + ;; details + (with-temp-buffer + (unless (zerop (call-process "uname" nil t nil "-a")) + (insert (format "%s" system-type))) + (string-trim (buffer-string))) + (or (cl-loop with cat = nil + for key being the hash-keys of doom-modules + if (or (not cat) (not (eq cat (car key)))) + do (setq cat (car key)) and collect cat + else collect + (let ((flags (doom-module-get cat (cdr key) :flags))) + (if flags + `(,(cdr key) ,@flags) + (cdr key)))) + "n/a") + (or (let (packages) + (ignore-errors + (require 'async) + ;; collect these in another session to protect this + ;; session's state + (async-get + (async-start + `(lambda () + (let ((noninteractive t) + (load-path ',load-path) + (package-alist ',package-alist)) + (load ,(expand-file-name "init.el" doom-emacs-dir)) + (doom-get-packages))) + (lambda (p) (setq packages p)))) + (cl-loop for pkg in (cl-sort packages #'string-lessp + :key (lambda (x) (symbol-name (car x)))) + collect (if (cdr pkg) + (format "%s" pkg) + (symbol-name (car pkg)))))) + "n/a") + (or (ignore-errors + (cl-delete-duplicates + (cl-loop for file in (append (reverse (directory-files-recursively doom-core-dir "\\.elc$")) + (cl-loop for dir in doom-modules-dirs + nconc (directory-files-recursively dir "\\.elc$"))) + collect (file-relative-name (file-name-directory file) doom-emacs-dir)) + :test #'equal)) + "n/a") + ;; abbreviate $HOME to hide username + (mapcar #'abbreviate-file-name exec-path)))) + + +;; +;; Commands +;; + +;;;###autoload +(defun doom/info () + "Collects some debug information about your Emacs session, formats it into +markdown and copies it to your clipboard, ready to be pasted into bug reports!" + (declare (interactive-only t)) + (interactive) + (message "Generating Doom info...") + (if noninteractive + (print! (doom-info)) + (kill-new (doom-info)) + (message "Done! Copied to clipboard."))) + +;;;###autoload +(defun doom/am-i-secure () + "Test to see if your root certificates are securely configured in emacs." + (declare (interactive-only t)) + (interactive) + (unless (string-match-p "\\_" system-configuration-features) + (warn "gnutls support isn't built into Emacs, there may be problems")) + (if-let* ((bad-hosts + (cl-loop for bad + in '("https://wrong.host.badssl.com/" + "https://self-signed.badssl.com/") + if (condition-case _e + (url-retrieve-synchronously bad) + (error nil)) + collect bad))) + (error "tls seems to be misconfigured (it got %s)." + bad-hosts) + (url-retrieve "https://badssl.com" + (lambda (status) + (if (or (not status) (plist-member status :error)) + (warn "Something went wrong.\n\n%s" (pp-to-string status)) + (message "Your trust roots are set up properly.\n\n%s" (pp-to-string status)) + t))))) + +;;;###autoload +(defun doom/version () + "Display the current version of Doom & Emacs, including the current Doom +branch and commit." + (interactive) + (require 'vc-git) + (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s." + doom-version + emacs-version + (or (vc-git--symbolic-ref doom-core-dir) + "n/a") + (or (vc-git-working-revision doom-core-dir) + "n/a"))) + + +;; +;; Vanilla sandbox +;; + +(defun doom--run-vanilla-sandbox () + "TODO" + (interactive) + (when (equal (buffer-name) "*doom:vanilla-sandbox*") + (let ((file (make-temp-file "doom-eval-"))) + (write-file file) + (require 'pp) + (require 'restart-emacs) + (restart-emacs--launch-other-emacs + (list "-Q" + "--eval" + (prin1-to-string + `(setq user-emacs-directory ,doom-emacs-dir + package--init-file-ensured t + package-user-dir ,package-user-dir + package-archives ',package-archives + debug-on-error t)) + "-f" "package-initialize" + "-l" file + "--eval" (prin1-to-string `(delete-file ,file))))))) + +;;;###autoload +(defun doom/open-vanilla-sandbox () + "Open an Emacs Lisp buffer destinated to run in a blank Emacs session. + +This vanilla sandbox is started with emacs -Q, and provides a testbed for +debugging code without Doom standing in the way, and without sacrificing +access to the installed packages." + (interactive) + (let ((buf (get-buffer-create "*doom:vanilla-sandbox*"))) + (with-current-buffer buf + (emacs-lisp-mode) + (local-set-key (kbd "C-c C-c") #'doom--run-vanilla-sandbox) + (local-set-key (kbd "C-c C-k") #'kill-this-buffer) + (setq header-line-format "C-c C-c to run the session / C-c C-k to abort it") + (setq-local default-directory doom-emacs-dir) + (doom-template-insert "VANILLA_SANDBOX") + (goto-char (point-max))) + (pop-to-buffer buf))) + + +;; +;; Reporting bugs +;; + +(defun doom--open-bug-report () + "TODO" + (interactive) + (let ((url "https://github.com/hlissner/doom-emacs/issues/new?body=")) + ;; TODO Refactor me + (save-restriction + (widen) + (goto-char (point-min)) + (re-search-forward "^-------------------------------------------------------------------\n" nil t) + (skip-chars-forward " \n\t") + (condition-case e + (progn + (save-excursion + (when (and (re-search-backward "\\+ [ ] " nil t) + (not (y-or-n-p "You haven't checked all the boxes. Continue anyway?"))) + (error "Aborted submit"))) + (narrow-to-region (point) (point-max)) + (let ((text (buffer-string))) + ;; `url-encode-url' doesn't encode ampersands + (setq text (replace-regexp-in-string "&" "%26" text)) + (setq url (url-encode-url (concat url text))) + ;; HACK: encode some characters according to HTML URL Encoding Reference + ;; http://www.w3schools.com/tags/ref_urlencode.asp + (setq url (replace-regexp-in-string "#" "%23" url)) + (setq url (replace-regexp-in-string ";" "%3B" url)) + (browse-url url))) + (error (signal (car e) (car e))))))) + +;;;###autoload +(defun doom/open-bug-report () + "Open a markdown buffer destinated to populate the New Issue page on Doom +Emacs' issue tracker. + +If called when a backtrace buffer is present, it and the output of `doom-info' +will be automatically appended to the result." + (interactive) + ;; TODO Refactor me + (let ((backtrace + (when (get-buffer "*Backtrace*") + (with-current-buffer "*Backtrace*" + (string-trim + (buffer-substring-no-properties + (point-min) + (min (point-max) 1000)))))) + (buf (get-buffer-create "*doom:vanilla-sandbox*"))) + (with-current-buffer buf + (erase-buffer) + (condition-case _ (gfm-mode) + (error (text-mode))) + (doom-template-insert "SUBMIT_BUG_REPORT") + (goto-char (point-max)) + (let ((pos (point))) + (save-excursion + (insert + "\n" (doom-info) "\n" + (if (and backtrace (not (string-empty-p backtrace))) + (format "\n
\nBacktrace\n\n```\n%s\n```\n
\n" + backtrace) + ""))) + (local-set-key (kbd "C-c C-c") #'doom--open-bug-report) + (local-set-key (kbd "C-c C-k") #'kill-this-buffer) + (setq header-line-format "C-c C-c to submit / C-c C-k to close") + ;; + (narrow-to-region (point-min) pos) + (goto-char (point-min))) + (pop-to-buffer buf)))) + + +;; +;; Profiling +;; + +(defvar doom--profiler nil) +;;;###autoload +(defun doom/toggle-profiler () + "Toggle the Emacs profiler. Run it again to see the profiling report." + (interactive) + (if (not doom--profiler) + (profiler-start 'cpu+mem) + (profiler-report) + (profiler-stop)) + (setq doom--profiler (not doom--profiler))) + +;;;###autoload +(defun doom/profile-emacs () + "Profile the startup time of Emacs in the background with ESUP. +If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE." + (interactive) + (require 'esup) + (let ((init-file esup-user-init-file)) + (message "Starting esup...") + (esup-reset) + (setq esup-server-process (esup-server-create (esup-select-port))) + (setq esup-server-port (process-contact esup-server-process :service)) + (message "esup process started on port %s" esup-server-port) + (let ((process-args `("*esup-child*" + "*esup-child*" + ,esup-emacs-path + "-q" + "-L" ,esup-load-path + "-l" "esup-child" + ,(format "--eval=(esup-child-run \"%s\" \"%s\" %d)" + init-file + esup-server-port + esup-depth) + "--eval=(run-hooks 'after-init-hook 'emacs-startup-hook 'window-setup-hook)"))) + (when esup-run-as-batch-p + (setq process-args (append process-args '("--batch")))) + (setq esup-child-process (apply #'start-process process-args))) + (set-process-sentinel esup-child-process 'esup-child-process-sentinel))) + diff --git a/core/autoload/editor.el b/core/autoload/editor.el index b3b4ce058..4baa3fc93 100644 --- a/core/autoload/editor.el +++ b/core/autoload/editor.el @@ -215,17 +215,21 @@ possible, or just one char if that's not possible." (t (delete-char (- n) killflag)))) ;;;###autoload -(defun doom/retab (&optional beg end) +(defun doom/retab (arg &optional beg end) "Converts tabs-to-spaces or spaces-to-tabs within BEG and END (defaults to buffer start and end, to make indentation consistent. Which it does depends on -the value of `indent-tab-mode'." - (interactive "r") +the value of `indent-tab-mode'. + +If ARG (universal argument) is non-nil, retab the current buffer using the +opposite indentation style." + (interactive "Pr") (unless (and beg end) (setq beg (point-min) end (point-max))) - (if indent-tabs-mode - (tabify beg end) - (untabify beg end))) + (let ((indent-tabs-mode (if arg (not indent-tabs-mode) indent-tabs-mode))) + (if indent-tabs-mode + (tabify beg end) + (untabify beg end)))) (defvar-local doom--buffer-narrowed-origin nil) ;;;###autoload diff --git a/core/autoload/files.el b/core/autoload/files.el index c2bf906b3..687ee2d91 100644 --- a/core/autoload/files.el +++ b/core/autoload/files.el @@ -56,9 +56,10 @@ (not force-p) (not (y-or-n-p (format "File already exists at %s, overwrite?" short-new-name)))) (throw 'status 'aborted)) - (t + ((file-exists-p old-path) (copy-file old-path new-path t) - short-new-name)))) + short-new-name) + (short-new-name)))) ;;;###autoload (defun doom/delete-this-file (&optional path force-p) @@ -74,8 +75,7 @@ kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)." ((not (or force-p (y-or-n-p (format "Really delete %s?" fbase)))) (message "Aborted") nil) - (t - (unwind-protect + ((unwind-protect (progn (delete-file path) t) (let ((short-path (file-relative-name path (doom-project-root)))) (if (file-exists-p path) @@ -107,7 +107,8 @@ file if it exists, without confirmation." (let ((old-path (buffer-file-name)) (new-path (expand-file-name new-path))) (when-let* ((dest (doom--copy-file old-path new-path force-p))) - (delete-file old-path) + (when (file-exists-p old-path) + (delete-file old-path)) (kill-this-buffer) (find-file new-path) (doom--forget-file old-path new-path) diff --git a/core/autoload/help.el b/core/autoload/help.el index 202b49980..783492d12 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -1,22 +1,5 @@ ;;; core/autoload/help.el -*- lexical-binding: t; -*- -;;;###autoload -(defun doom/describe-setting (setting) - "Open the documentation of SETTING (a keyword defined with `def-setting!'). - -Defaults to the " - (interactive - (let ((sym (symbol-at-point))) - (list (completing-read "Describe setting: " - (sort (mapcar #'car doom-settings) #'string-lessp) - nil t (if (keywordp sym) (symbol-name sym)))))) - (let ((fn (cdr (assq (intern setting) doom-settings)))) - (unless fn - (error "'%s' is not a valid DOOM setting" setting)) - (describe-function fn))) - - -;; (defvar doom--module-mode-alist '((c-mode :lang cc) (c++-mode :lang cc) @@ -58,6 +41,42 @@ Defaults to the " (stylus-mode :lang web)) "TODO") +(defvar doom-docs-dir (concat doom-emacs-dir "docs/") + "TODO") + + +;; +;; Helpers +;; + +;;;###autoload +(defun doom-active-minor-modes () + "Return a list of active minor-mode symbols." + (cl-loop for mode in minor-mode-list + if (and (boundp mode) (symbol-value mode)) + collect mode)) + + + +;; +;; Commands +;; + +;;;###autoload +(defun doom/describe-setting (setting) + "Open the documentation of SETTING (a keyword defined with `def-setting!'). + +Defaults to the " + (interactive + (let ((sym (symbol-at-point))) + (list (completing-read "Describe setting: " + (sort (mapcar #'car doom-settings) #'string-lessp) + nil t (if (keywordp sym) (symbol-name sym)))))) + (let ((fn (cdr (assq (intern setting) doom-settings)))) + (unless fn + (error "'%s' is not a valid DOOM setting" setting)) + (describe-function fn))) + ;;;###autoload (defun doom/describe-module (module) "Open the documentation of MODULE (a string that represents the category and @@ -75,6 +94,7 @@ in, or d) the module associated with the current major mode (see "init.el") (thing-at-point 'sexp t))) ((save-excursion + (require 'smartparens) (ignore-errors (sp-beginning-of-sexp) (unless (eq (char-after) ?\() @@ -97,22 +117,58 @@ in, or d) the module associated with the current major mode (see (mapcar #'intern (split-string module " ")) (unless (doom-module-p category submodule) (error "'%s' isn't a valid module" module)) - (let ((doc-path (doom-module-expand-file category submodule "README.org"))) + (let ((doc-path (doom-module-path category submodule "README.org"))) (unless (file-exists-p doc-path) (error "There is no documentation for this module")) (find-file doc-path)))) ;;;###autoload -(defun doom/version () - "Display the current version of Doom & Emacs, including the current Doom -branch and commit." +(defun doom/describe-active-minor-mode (mode) + "Get information on an active minor mode. Use `describe-minor-mode' for a +selection of all minor-modes, active or not." + (interactive + (list (completing-read "Minor mode: " + (doom-active-minor-modes)))) + (describe-minor-mode-from-symbol + (cl-typecase mode + (string (intern mode)) + (symbol mode) + (t (error "Expected a symbol/string, got a %s" (type-of mode)))))) + +;;;###autoload +(defun doom/what-face (&optional pos) + "Shows all faces and overlay faces at point. + +Interactively prints the list to the echo area. Noninteractively, returns a list +whose car is the list of faces and cadr is the list of overlay faces." (interactive) - (message "Doom v%s (Emacs v%s). Branch: %s. Commit: %s." - doom-version - emacs-version - (if-let* ((branch (vc-git--symbolic-ref "core/core.el"))) - branch - "n/a") - (if-let* ((rev (vc-git-working-revision "core/core.el"))) - rev - "n/a"))) + (let* ((pos (or pos (point))) + (faces (let ((face (get-text-property pos 'face))) + (if (keywordp (car-safe face)) + (list face) + (cl-loop for f in (doom-enlist face) collect f)))) + (overlays (cl-loop for ov in (overlays-at pos (1+ pos)) + nconc (doom-enlist (overlay-get ov 'face))))) + (cond ((called-interactively-p 'any) + (message "%s %s\n%s %s" + (propertize "Faces:" 'face 'font-lock-comment-face) + (if faces + (cl-loop for face in faces + if (listp face) + concat (format "'%s " face) + else + concat (concat (propertize (symbol-name face) 'face face) " ")) + "n/a ") + (propertize "Overlays:" 'face 'font-lock-comment-face) + (if overlays + (cl-loop for ov in overlays + concat (concat (propertize (symbol-name ov) 'face ov) " ")) + "n/a"))) + (t + (and (or faces overlays) + (list faces overlays)))))) + +;;;###autoload +(defun doom//open-manual () + (interactive) + (find-file (expand-file-name "index.org" doom-docs-dir))) diff --git a/core/autoload/message.el b/core/autoload/message.el index 33197690a..3fcdc86a9 100644 --- a/core/autoload/message.el +++ b/core/autoload/message.el @@ -1,55 +1,68 @@ ;;; core/autoload/message.el -*- lexical-binding: t; -*- (defconst doom-message-fg - '((reset . 0) - (black . 30) - (red . 31) - (green . 32) - (yellow . 33) - (blue . 34) - (magenta . 35) - (cyan . 36) - (white . 37)) + '((black 30 term-color-black) + (red 31 term-color-red) + (green 32 term-color-green) + (yellow 33 term-color-yellow) + (blue 34 term-color-blue) + (magenta 35 term-color-magenta) + (cyan 36 term-color-cyan) + (white 37 term-color-white)) "List of text colors.") (defconst doom-message-bg - '((on-black . 40) - (on-red . 41) - (on-green . 42) - (on-yellow . 43) - (on-blue . 44) - (on-magenta . 45) - (on-cyan . 46) - (on-white . 47)) + '((on-black 40 term-color-black) + (on-red 41 term-color-red) + (on-green 42 term-color-green) + (on-yellow 43 term-color-yellow) + (on-blue 44 term-color-blue) + (on-magenta 45 term-color-magenta) + (on-cyan 46 term-color-cyan) + (on-white 47 term-color-white)) "List of colors to draw text on.") (defconst doom-message-fx - '((bold . 1) - (dark . 2) - (italic . 3) - (underscore . 4) - (blink . 5) - (rapid . 6) - (contrary . 7) - (concealed . 8) - (strike . 9)) + '((bold 1 :weight bold) + (dark 2) + (italic 3 :slant italic) + (underscore 4 :underline t) + (blink 5) + (rapid 6) + (contrary 7) + (concealed 8) + (strike 9 :strike-through t)) "List of styles.") ;;;###autoload (defun doom-ansi-apply (code message &rest args) - "Apply the ansi CODE to formatted MESSAGE with ARGS." - (let ((rule (or (assq code doom-message-fg) - (assq code doom-message-bg) - (assq code doom-message-fx)))) - (format "\e[%dm%s\e[%dm" - (cdr rule) - (apply #'format message args) - 0))) + "Apply CODE to formatted MESSAGE with ARGS. CODE is derived from any of +`doom-message-fg', `doom-message-bg' or `doom-message-fx'. + +In a noninteractive session, this wraps the result in ansi color codes. +Otherwise, it maps colors to a term-color-* face." + (let ((text (apply #'format message args))) + (if noninteractive + (format "\e[%dm%s\e[%dm" + (cadr + (or (assq code doom-message-fg) + (assq code doom-message-bg) + (assq code doom-message-fx))) + text 0) + (require 'term) ; piggyback on term's color faces + (propertize + text 'face + (let (spec) + (cond ((setq spec (caddr (assq code doom-message-fg))) + `(:foreground ,(face-foreground spec))) + ((setq spec (caddr (assq code doom-message-bg))) + `(:background ,(face-background spec))) + ((cddr (assq code doom-message-fx))))))))) ;;;###autoload (defmacro format! (message &rest args) - "An alternative to `format' that strips out ANSI codes if used in an -interactive session." + "An alternative to `format' that understands (color ...) and converts them +into faces or ANSI codes depending on the type of sesssion we're in." `(cl-flet* (,@(cl-loop for rule in (append doom-message-fg doom-message-bg doom-message-fx) @@ -63,19 +76,19 @@ interactive session." (format ,message ,@args))) ;;;###autoload -(defmacro message! (message &rest args) - "An alternative to `message' that strips out ANSI codes if used in an -interactive session." - `(if noninteractive +(defmacro print! (message &rest args) + "Uses `message' in interactive sessions and `princ' otherwise (prints to +standard out). + +Can be colored using (color ...) blocks: + + (print! \"Hello %s %s\" (bold (blue \"How are you?\"))) + (print! \"Hello %s %s\" (red \"World\")) + (print! (green \"Great %s!\" \"success\")) + +Uses faces in interactive sessions and ANSI codes otherwise." + `(if (not noninteractive) (message (format! ,message ,@args)) - (let ((buf (get-buffer-create " *doom messages*"))) - (with-current-buffer buf - (goto-char (point-max)) - (let ((beg (point)) - end) - (insert (format! ,message ,@args)) - (insert "\n") - (setq end (point)) - (ansi-color-apply-on-region beg end))) - (pop-to-buffer buf) - (goto-char (point-max))))) + ;; princ prints to stdout, message to stderr + (princ (format! ,message ,@args)) + (terpri))) diff --git a/core/autoload/modules.el b/core/autoload/modules.el new file mode 100644 index 000000000..f3b189a64 --- /dev/null +++ b/core/autoload/modules.el @@ -0,0 +1,378 @@ +;;; core/autoload/modules.el -*- lexical-binding: t; -*- + +(autoload 'print! "autoload/message" nil 'macro) + +(defun doom--server-eval (body) + (require 'server) + (when (server-running-p) + (server-eval-at server-name body))) + +;;;###autoload +(defun doom//reload (&optional force-p) + "Reloads your config. This is experimental! + +If called from a noninteractive session, this will try to communicate with a +live server (if one is found) to tell it to run this function. + +If called from an interactive session, tries to reload autoloads files (if +necessary), reinistalize doom (via `doom-initialize') and reloads your private +init.el and config.el. Then runs `doom-reload-hook'." + (interactive) + (unless doom--inhibit-reload + (cond ((and noninteractive (not (daemonp))) + (require 'server) + (if (not (server-running-p)) + (doom//reload-autoloads force-p) + (print! "Reloading active Emacs session...") + (print! + (bold "%%s") + (if (server-eval-at server-name '(doom//reload)) + (green "Done!") + (red "There were issues!"))))) + ((let ((load-prefer-newer t)) + (doom//reload-autoloads force-p) + (doom-initialize t) + (doom-initialize-modules t) + (print! (green "%d packages reloaded" (length package-alist))) + (run-hooks 'doom-reload-hook) + t))))) + + +;; +;; Autoload file generation +;; + +(defvar doom-autoload-excluded-packages '(marshal gh) + "Packages that have silly or destructive autoload files that try to load +everyone in the universe and their dog, causing errors that make babies cry. No +one wants that.") + +(defun doom--byte-compile (file) + (let ((short-name (file-name-nondirectory file))) + (condition-case-unless-debug ex + (when (byte-compile-file file) + (load (byte-compile-dest-file file) nil t) + (unless noninteractive + (message "Finished compiling %s" short-name))) + ('error + (doom-delete-autoloads-file file) + (error "Error in %s: %s -- %s" + short-name + (car ex) (error-message-string ex)))))) + +;;;###autoload +(defun doom-delete-autoloads-file (file) + "Delete FILE (an autoloads file), and delete the accompanying *.elc file, if +it exists." + (or (stringp file) + (signal 'wrong-type-argument (list 'stringp file))) + (when (file-exists-p file) + (delete-file file) + (ignore-errors (delete-file (byte-compile-dest-file file))) + (print! "Deleted old %s" (file-name-nondirectory file)))) + +;;;###autoload +(defun doom//reload-autoloads (&optional file force-p) + "Reloads FILE (an autoload file), if it needs reloading. + +FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If +it is nil, it will try to reload both. If FORCE-P (universal argument) do it +even if it doesn't need reloading!" + (interactive + (list nil current-prefix-arg)) + (or (null file) + (stringp file) + (signal 'wrong-type-argument (list 'stringp file))) + (cond ((equal file doom-autoload-file) + (doom//reload-doom-autoloads force-p)) + ((equal file doom-package-autoload-file) + (doom//reload-package-autoloads force-p)) + ((progn + (doom//reload-doom-autoloads force-p) + (doom//reload-package-autoloads force-p))))) + +(defvar generated-autoload-load-name) +;;;###autoload +(defun doom//reload-doom-autoloads (&optional force-p) + "Refreshes the autoloads.el file, specified by `doom-autoload-file', if +necessary (or if FORCE-P is non-nil). + +It scans and reads core/autoload/*.el, modules/*/*/autoload.el and +modules/*/*/autoload/*.el, and generates `doom-autoload-file'. This file tells +Emacs where to find lazy-loaded functions. + +This should be run whenever your `doom!' block, or a module autoload file, is +modified." + (interactive) + (let ((doom-modules (doom-module-table)) + (default-directory doom-emacs-dir) + (targets + (file-expand-wildcards + (expand-file-name "autoload/*.el" doom-core-dir)))) + (dolist (path (doom-module-load-path)) + (let ((auto-dir (expand-file-name "autoload" path)) + (auto-file (expand-file-name "autoload.el" path))) + (when (file-exists-p auto-file) + (push auto-file targets)) + (when (file-directory-p auto-dir) + (dolist (file (doom-files-in auto-dir :match "\\.el$" :full t)) + (push file targets))))) + (if (and (not force-p) + (file-exists-p doom-autoload-file) + (not (cl-loop for file in targets + if (file-newer-than-file-p file doom-autoload-file) + return t))) + (ignore (print! (green "Doom core autoloads is up-to-date")) + (doom-initialize-autoloads doom-autoload-file)) + (doom-delete-autoloads-file doom-autoload-file) + ;; in case the buffer is open somewhere and modified + (when-let* ((buf (find-buffer-visiting doom-autoload-file))) + (with-current-buffer buf + (set-buffer-modified-p nil)) + (kill-buffer buf)) + (message "Generating new autoloads.el") + (dolist (file (nreverse targets)) + (let* ((file (file-truename file)) + (generated-autoload-load-name (file-name-sans-extension file)) + (noninteractive (not doom-debug-mode))) + (print! + (cond ((not (doom-file-cookie-p file)) + "⚠ Ignoring %s") + ((update-file-autoloads file nil doom-autoload-file) + (yellow "✕ Nothing in %%s")) + ((green "✓ Scanned %%s"))) + (if (file-in-directory-p file default-directory) + (file-relative-name file) + (abbreviate-file-name file))))) + (make-directory (file-name-directory doom-autoload-file) t) + (let ((buf (find-file-noselect doom-autoload-file t)) + case-fold-search) + (unwind-protect + (with-current-buffer buf + (goto-char (point-min)) + (insert ";;; -*- lexical-binding:t -*-\n" + ";; This file is autogenerated by `doom//reload-doom-autoloads', DO NOT EDIT !!\n\n") + (save-excursion + ;; Replace autoload paths (only for module autoloads) with + ;; absolute paths for faster resolution during load and + ;; simpler `load-path' + (let ((load-path (append doom-psuedo-module-dirs + doom-modules-dirs + load-path)) + cache) + (save-excursion + (while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) + (let ((path (match-string 1))) + (replace-match + (or (cdr (assoc path cache)) + (when-let* ((libpath (locate-library path)) + (libpath (file-name-sans-extension libpath))) + (push (cons path (abbreviate-file-name libpath)) cache) + libpath) + path) + t t nil 1))) + (print! (green "✓ Autoload paths expanded"))))) + ;; Remove byte-compile inhibiting file variables so we can + ;; byte-compile the file. + (when (re-search-forward "^;; no-byte-compile: t\n" nil t) + (replace-match "" t t)) + ;; Byte compile it to give the file a chance to reveal errors. + (save-buffer) + (doom--byte-compile doom-autoload-file) + (when (and noninteractive (not (daemonp))) + (doom--server-eval `(load-file ,doom-autoload-file))) + t) + (kill-buffer buf)))))) + +;;;###autoload +(defun doom//reload-package-autoloads (&optional force-p) + "Compiles `doom-package-autoload-file' from the autoloads files of all +installed packages. It also caches `load-path', `Info-directory-list', +`doom-disabled-packages', `package-activated-list' and `auto-mode-alist'. + +Will do nothing if none of your installed packages have been modified. If +FORCE-P (universal argument) is non-nil, regenerate it anyway. + +This should be run whenever your `doom!' block or update your packages." + (interactive) + (if (and (not force-p) + (file-exists-p doom-package-autoload-file) + (not (cl-loop initially do (doom-ensure-packages-initialized t) + for (_pkg desc) in package-alist + for autoload-file = (concat (package--autoloads-file-name desc) ".el") + if (file-newer-than-file-p autoload-file doom-package-autoload-file) + return t))) + (ignore (print! (green "Doom package autoloads is up-to-date")) + (doom-initialize-autoloads doom-package-autoload-file)) + (doom-delete-autoloads-file doom-package-autoload-file) + (with-temp-file doom-package-autoload-file + (insert ";;; -*- lexical-binding:t -*-\n" + ";; This file is autogenerated by `doom//reload-package-autoloads', DO NOT EDIT !!\n\n") + (save-excursion + ;; Cache the important and expensive-to-initialize state here. + (doom-initialize-packages 'internal) + (prin1 `(setq load-path ',load-path + auto-mode-alist ',auto-mode-alist + Info-directory-list ',Info-directory-list + doom-disabled-packages ',doom-disabled-packages + package-activated-list ',package-activated-list) + (current-buffer)) + (print! (green "✓ Cached package state")) + + ;; insert package autoloads + (dolist (spec package-alist) + (cl-destructuring-bind (pkg desc) spec + (unless (memq pkg doom-autoload-excluded-packages) + (let ((file + (abbreviate-file-name + (concat (package--autoloads-file-name desc) ".el")))) + (when (file-exists-p file) + (insert "(let ((load-file-name " (prin1-to-string file) "))\n") + (insert-file-contents file) + (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\)" nil t) + (unless (nth 8 (syntax-ppss)) + (replace-match "" t t))) + (unless (bolp) (insert "\n")) + (insert ")\n"))))))) + (print! (green "✓ Package autoloads included")) + ;; Remove `load-path' and `auto-mode-alist' modifications (most of them, + ;; at least); they are cached later, so all those membership checks are + ;; unnecessary overhead. + (while (re-search-forward "^\\s-*\\((\\(?:add-to-list\\|when (boundp \\)\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\)" nil t) + (goto-char (match-beginning 1)) + (kill-sexp)) + (print! (green "✓ Removed load-path/auto-mode-alist entries"))) + (doom--byte-compile doom-package-autoload-file) + (when (and noninteractive (not (daemonp))) + (doom--server-eval `(load-file ,doom-package-autoload-file))) + t)) + + +;; +;; Byte compilation +;; + +;;;###autoload +(defun doom//byte-compile (&optional modules recompile-p) + "Byte compiles your emacs configuration. + +init.el is always byte-compiled by this. + +If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are +byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom +core. It always ignores unit tests and files with `no-byte-compile' enabled. + +Doom was designed to benefit from byte-compilation, but the process may take a +while. Also, while your config files are byte-compiled, changes to them will not +take effect! Use `doom//clean-byte-compiled-files' or `make clean' to remove +these files. + +If RECOMPILE-P is non-nil, only recompile out-of-date files." + (interactive + (list nil current-prefix-arg)) + (let ((default-directory doom-emacs-dir)) + (unless recompile-p + (doom//clean-byte-compiled-files)) + (let ((total-ok 0) + (total-fail 0) + (total-noop 0) + compile-plugins-p + targets) + (dolist (module (delete-dups modules) (nreverse targets)) + (pcase module + (":core" (push doom-core-dir targets)) + (":private" (push doom-private-dir targets)) + (":plugins" + (byte-recompile-directory package-user-dir 0 t) + (setq compile-plugins-p t)) + ((pred file-directory-p) + (push module targets)) + ((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$")) + (push (doom-module-locate-path + (intern (format ":%s" (match-string 1 module))) + (intern (match-string 2 module))) + targets)))) + (unless (equal modules (list ":plugins")) + (let ((inhibit-message t) + noninteractive) + ;; But first we must be sure that Doom and your private config have been + ;; fully loaded. Which usually aren't so in an noninteractive session. + (doom//reload-autoloads) + (doom-initialize t) + (unless (equal modules (list ":core")) + (doom-initialize-modules t)))) + ;; If no targets were supplied, then we use your module list. + (unless targets + (setq targets (append (list doom-core-dir) + (doom-module-load-path)))) + ;; Assemble el files we want to compile; taking into account that MODULES + ;; may be a list of MODULE/SUBMODULE strings from the command line. + (let ((target-files (doom-files-in targets :depth 2 :match "\\.el$"))) + (if (not target-files) + (unless compile-plugins-p + (message "No targets to %scompile" (if recompile-p "re" ""))) + (condition-case ex + (let ((use-package-expand-minimally t)) + ;; Always compile private init file + (cl-pushnew (expand-file-name "init.el" doom-private-dir) + target-files :test #'equal) + (cl-pushnew (expand-file-name "init.el" doom-emacs-dir) + target-files :test #'equal) + (dolist (target (cl-delete-duplicates (mapcar #'file-truename target-files) :test #'equal)) + (if (or (not recompile-p) + (let ((elc-file (byte-compile-dest-file target))) + (and (file-exists-p elc-file) + (file-newer-than-file-p target elc-file)))) + (let ((result (if (or (string-match-p "/\\(?:packages\\|doctor\\)\\.el$" target) + (not (doom-file-cookie-p target))) + 'no-byte-compile + (byte-compile-file target))) + (short-name (if (file-in-directory-p target doom-emacs-dir) + (file-relative-name target doom-emacs-dir) + (abbreviate-file-name target)))) + (cl-incf + (cond ((eq result 'no-byte-compile) + (print! (dark (white "⚠ Ignored %s" short-name))) + total-noop) + ((null result) + (print! (red "✕ Failed to compile %s" short-name)) + total-fail) + (t + (print! (green "✓ Compiled %s" short-name)) + (quiet! (load target t t)) + total-ok)))) + (cl-incf total-noop))) + (print! + (bold + (color (if (= total-fail 0) 'green 'red) + "%s %d/%d file(s) (%d ignored)" + (if recompile-p "Recompiled" "Compiled") + total-ok (- (length target-files) total-noop) + total-noop)))) + (error + (print! (red "\n%%s\n\n%%s\n\n%%s") + "There were breaking errors." + (error-message-string ex) + "Reverting changes...") + (quiet! (doom//clean-byte-compiled-files)) + (print! (yellow "Finished (nothing was byte-compiled)"))))))))) + +;;;###autoload +(defun doom//clean-byte-compiled-files () + "Delete all the compiled elc files in your Emacs configuration and private +module. This does not include your byte-compiled, third party packages.'" + (interactive) + (cl-loop with default-directory = doom-emacs-dir + for path in (append (doom-files-in doom-emacs-dir :match "\\.elc$" :depth 1) + (doom-files-in doom-psuedo-module-dirs :match "\\.elc$" :depth 1) + (doom-files-in doom-core-dir :match "\\.elc$") + (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4)) + for truepath = (file-truename path) + if (file-exists-p path) + do (delete-file path) + and do + (print! (green "✓ Deleted %%s") + (if (file-in-directory-p truepath default-directory) + (file-relative-name truepath) + (abbreviate-file-name truepath))) + finally do (print! (bold (green "Everything is clean"))))) diff --git a/core/autoload/packages.el b/core/autoload/packages.el index f5f6f7efa..8b3be26c2 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,15 +1,8 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- (load! cache) -(require 'use-package) -(require 'quelpa) -(require 'package) -(require 'async) ;;; Private functions -(defsubst doom--sort-alpha (it other) - (string-lessp (symbol-name (car it)) - (symbol-name (car other)))) (defun doom--packages-choose (prompt) (let ((table (cl-loop for pkg in package-alist @@ -26,15 +19,15 @@ (condition-case ex2 (progn ,@body) ('file-error - (message! (bold (red " FILE ERROR: %s" (error-message-string ex2)))) - (message! " Trying again...") + (print! (bold (red " FILE ERROR: %s" (error-message-string ex2)))) + (print! " Trying again...") (quiet! (doom-refresh-packages-maybe t)) ,@body)) ('user-error - (message! (bold (red " ERROR: %s" ex)))) + (print! (bold (red " ERROR: %s" ex)))) ('error (doom--refresh-pkg-cache) - (message! (bold (red " FATAL ERROR: %s" ex)))))) + (print! (bold (red " FATAL ERROR: %s" ex)))))) (defun doom--refresh-pkg-cache () "Clear the cache for `doom-refresh-packages-maybe'." @@ -57,7 +50,7 @@ (progn (message "Refreshing package archives") (package-refresh-contents) - (doom-cache-set 'last-pkg-refresh t 900)) + (doom-cache-set 'last-pkg-refresh t 1200)) ('error (doom--refresh-pkg-cache) (message "Failed to refresh packages: (%s) %s" @@ -147,7 +140,7 @@ Warning: this function is expensive; it re-evaluates all of doom's config files. Be careful not to use it in a loop. If INSTALLED-ONLY-P, only return packages that are installed." - (doom-initialize-packages (not noninteractive)) + (doom-initialize-packages t) (cl-loop with packages = (append doom-core-packages (mapcar #'car doom-packages)) for sym in (cl-delete-duplicates packages) if (and (or (not installed-only-p) @@ -180,7 +173,8 @@ containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST). If INCLUDE-FROZEN-P is non-nil, check frozen packages as well. Used by `doom//packages-update'." - (doom-initialize-packages (not noninteractive)) + (doom-initialize-packages t) + (doom-refresh-packages-maybe doom-debug-mode) (require 'async) (let (quelpa-pkgs elpa-pkgs) ;; Separate quelpa from elpa packages @@ -201,9 +195,12 @@ Used by `doom//packages-update'." (message "New thread for: %s" pkg)) (push (async-start `(lambda () - (let ((doom-init-p 'internal) + (let ((doom-init-p t) (noninteractive t) (load-path ',load-path) + (package-alist ',package-alist) + (package-archive-contents ',package-archive-contents) + (package-selected-packages ',package-selected-packages) (doom-packages ',doom-packages) (doom-modules ',doom-modules) (quelpa-cache ',quelpa-cache) @@ -211,6 +208,8 @@ Used by `doom//packages-update'." doom-private-dir) (load ,(expand-file-name "core.el" doom-core-dir)) (load ,(expand-file-name "autoload/packages.el" doom-core-dir)) + (require 'package) + (require 'quelpa) (doom-package-outdated-p ',pkg)))) futures)) (delq nil @@ -223,7 +222,7 @@ Used by `doom//packages-update'." depended on. Used by `doom//packages-autoremove'." - (doom-initialize-packages (not noninteractive)) + (doom-initialize-packages t) (let ((package-selected-packages (append (mapcar #'car doom-packages) doom-core-packages))) (append (package--removable-packages) @@ -243,7 +242,6 @@ If INCLUDE-IGNORED-P is non-nil, includes missing packages that are ignored, i.e. they have an :ignore property. Used by `doom//packages-install'." - (doom-initialize-packages (not noninteractive)) (cl-loop for desc in (doom-get-packages) for (name . plist) = desc if (and (or include-ignored-p @@ -260,6 +258,7 @@ Used by `doom//packages-install'." ;; Main functions ;; +;;;###autoload (defun doom-install-package (name &optional plist) "Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an example; the package name can be omitted)." @@ -283,6 +282,7 @@ example; the package name can be omitted)." (map-put doom-packages name plist) name))) +;;;###autoload (defun doom-update-package (name &optional force-p) "Updates package NAME (a symbol) if it is out of date, using quelpa or package.el as appropriate." @@ -310,6 +310,7 @@ package.el as appropriate." (delete-directory old-dir t))) t)))) +;;;###autoload (defun doom-delete-package (name &optional force-p) "Uninstalls package NAME if it exists, and clears it from `quelpa-cache'." (unless (package-installed-p name) @@ -328,162 +329,6 @@ package.el as appropriate." t))) -;; -;; Batch/interactive commands -;; - -;;;###autoload -(defun doom//packages-install () - "Interactive command for installing missing packages." - (interactive) - (if (not noninteractive) - (doom-packages--async-run 'doom//packages-install) - (message! "Looking for packages to install...") - (let ((packages (reverse (doom-get-missing-packages)))) - (cond ((not packages) - (message! (green "No packages to install!")) - nil) - - ((not (or (getenv "YES") - (y-or-n-p - (format "%s packages will be installed:\n\n%s\n\nProceed?" - (length packages) - (mapconcat - (lambda (pkg) - (format "+ %s (%s)" - (car pkg) - (cond ((doom-package-different-recipe-p (car pkg)) - "new recipe") - ((doom-package-different-backend-p (car pkg)) - (if (plist-get (cdr pkg) :recipe) - "ELPA -> QUELPA" - "QUELPA -> ELPA")) - ((plist-get (cdr pkg) :recipe) - "QUELPA") - (t - "ELPA")))) - (sort (cl-copy-list packages) #'doom--sort-alpha) - "\n"))))) - (message! (yellow "Aborted!")) - nil) - - (t - (doom-refresh-packages-maybe doom-debug-mode) - (dolist (pkg packages) - (message! "Installing %s" (car pkg)) - (doom--condition-case! - (message! "%s%s" - (if (and (package-installed-p (car pkg)) - (not (doom-package-different-backend-p (car pkg))) - (not (doom-package-different-recipe-p (car pkg)))) - (dark (white "⚠ ALREADY INSTALLED")) - (condition-case e - (if (doom-install-package (car pkg) (cdr pkg)) - (green "✓ DONE") - (red "✕ FAILED")) - (error - (red "✕ ERROR (%s)" e)))) - (if (plist-member (cdr pkg) :pin) - (format " [pinned: %s]" (plist-get (cdr pkg) :pin)) - "")))) - - (message! (bold (green "Finished!"))) - (doom//reload-load-path) - t))))) - -;;;###autoload -(defun doom//packages-update () - "Interactive command for updating packages." - (interactive) - (if (not noninteractive) - (doom-packages--async-run 'doom//packages-update) - (message! "Looking for outdated packages...") - (doom-refresh-packages-maybe doom-debug-mode) - (let ((packages (sort (doom-get-outdated-packages) #'doom--sort-alpha))) - (cond ((not packages) - (message! (green "Everything is up-to-date")) - nil) - - ((not (or (getenv "YES") - (y-or-n-p - (format "%s packages will be updated:\n\n%s\n\nProceed?" - (length packages) - (let ((max-len - (or (car (sort (mapcar (lambda (it) (length (symbol-name (car it)))) packages) - (lambda (it other) (> it other)))) - 10))) - (mapconcat - (lambda (pkg) - (format (format "+ %%-%ds %%-%ds -> %%s" (+ max-len 2) 14) - (symbol-name (car pkg)) - (package-version-join (cadr pkg)) - (package-version-join (cl-caddr pkg)))) - packages - "\n")))))) - (message! (yellow "Aborted!")) - nil) - - (t - (dolist (pkg packages) - (message! "Updating %s" (car pkg)) - (doom--condition-case! - (message! - (let ((result (doom-update-package (car pkg) t))) - (color (if result 'green 'red) - (if result "✓ DONE" "✕ FAILED")))))) - - (message! (bold (green "Finished!"))) - (doom//reload-load-path) - t))))) - -;;;###autoload -(defun doom//packages-autoremove () - "Interactive command for auto-removing orphaned packages." - (interactive) - (if (not noninteractive) - (doom-packages--async-run 'doom//packages-autoremove) - (message! "Looking for orphaned packages...") - (let ((packages (doom-get-orphaned-packages))) - (cond ((not packages) - (message! (green "No unused packages to remove")) - nil) - - ((not - (or (getenv "YES") - (y-or-n-p - (format - "%s packages will be deleted:\n\n%s\n\nProceed?" - (length packages) - (mapconcat - (lambda (sym) - (format "+ %s (%s)" sym - (let ((backend (doom-package-backend sym))) - (if (doom-package-different-backend-p sym) - (pcase backend - (`quelpa "QUELPA->ELPA") - (`elpa "ELPA->QUELPA") - (_ "removed")) - (upcase (symbol-name backend)))))) - (sort (cl-copy-list packages) #'string-lessp) - "\n"))))) - (message! (yellow "Aborted!")) - nil) - - (t - (dolist (pkg packages) - (doom--condition-case! - (message! - (let ((result (doom-delete-package pkg t))) - (color (if result 'green 'red) - "%s %s" - (if result "✓ Removed" "✕ Failed to remove") - pkg))))) - - (message! (bold (green "Finished!"))) - (doom//reload-load-path) - t))))) - - ;; ;; Interactive commands ;; @@ -533,3 +378,148 @@ calls." (when (file-exists-p path) (delete-directory path t)))))) + +;; +;; Package Management +;; + +;;;###autoload +(defun doom//packages-install (&optional auto-accept-p) + "Interactive command for installing missing packages." + (interactive "P") + (print! "Looking for packages to install...") + (let ((packages (reverse (doom-get-missing-packages)))) + (cond ((not packages) + (print! (green "No packages to install!")) + nil) + + ((not (or auto-accept-p + (y-or-n-p + (format "%s packages will be installed:\n\n%s\n\nProceed?" + (length packages) + (mapconcat + (lambda (pkg) + (format "+ %s (%s)" + (car pkg) + (cond ((doom-package-different-recipe-p (car pkg)) + "new recipe") + ((doom-package-different-backend-p (car pkg)) + (if (plist-get (cdr pkg) :recipe) + "ELPA -> QUELPA" + "QUELPA -> ELPA")) + ((plist-get (cdr pkg) :recipe) + "QUELPA") + (t + "ELPA")))) + (cl-sort (cl-copy-list packages) #'string-lessp + :key #'car) + "\n"))))) + (error "Aborted!")) + + ((let (success) + (doom-refresh-packages-maybe doom-debug-mode) + (dolist (pkg packages) + (print! "Installing %s" (car pkg)) + (doom--condition-case! + (print! "%s%s" + (cond ((and (package-installed-p (car pkg)) + (not (doom-package-different-backend-p (car pkg))) + (not (doom-package-different-recipe-p (car pkg)))) + (dark (white "⚠ ALREADY INSTALLED"))) + ((doom-install-package (car pkg) (cdr pkg)) + (setq success t) + (green "✓ DONE")) + ((red "✕ FAILED"))) + (if (plist-member (cdr pkg) :pin) + (format " [pinned: %s]" (plist-get (cdr pkg) :pin)) + "")))) + (print! (bold (green "Finished!"))) + (if success (doom-delete-autoloads-file doom-package-autoload-file)) + success))))) + +;;;###autoload +(defun doom//packages-update (&optional auto-accept-p) + "Interactive command for updating packages." + (interactive "P") + (print! "Looking for outdated packages...") + (let ((packages (cl-sort (cl-copy-list (doom-get-outdated-packages)) #'string-lessp + :key #'car))) + (cond ((not packages) + (print! (green "Everything is up-to-date")) + nil) + + ((not (or auto-accept-p + (y-or-n-p + (format "%s packages will be updated:\n\n%s\n\nProceed?" + (length packages) + (let ((max-len + (or (car (sort (mapcar (lambda (it) (length (symbol-name (car it)))) packages) + #'>)) + 10))) + (mapconcat + (lambda (pkg) + (format (format "+ %%-%ds %%-%ds -> %%s" (+ max-len 2) 14) + (symbol-name (car pkg)) + (package-version-join (cadr pkg)) + (package-version-join (cl-caddr pkg)))) + packages + "\n")))))) + (error "Aborted!")) + + ((let (success) + (dolist (pkg packages) + (print! "Updating %s" (car pkg)) + (doom--condition-case! + (print! + (let ((result (doom-update-package (car pkg) t))) + (when result (setq success t)) + (color (if result 'green 'red) + (if result "✓ DONE" "✕ FAILED")))))) + (print! (bold (green "Finished!"))) + (if success (doom-delete-autoloads-file doom-package-autoload-file)) + success))))) + +;;;###autoload +(defun doom//packages-autoremove (&optional auto-accept-p) + "Interactive command for auto-removing orphaned packages." + (interactive "P") + (print! "Looking for orphaned packages...") + (let ((packages (doom-get-orphaned-packages))) + (cond ((not packages) + (print! (green "No unused packages to remove")) + nil) + + ((not + (or auto-accept-p + (y-or-n-p + (format + "%s packages will be deleted:\n\n%s\n\nProceed?" + (length packages) + (mapconcat + (lambda (sym) + (format "+ %s (%s)" sym + (let ((backend (doom-package-backend sym))) + (if (doom-package-different-backend-p sym) + (pcase backend + (`quelpa "QUELPA->ELPA") + (`elpa "ELPA->QUELPA") + (_ "removed")) + (upcase (symbol-name backend)))))) + (sort (cl-copy-list packages) #'string-lessp) + "\n"))))) + (error "Aborted!")) + + ((let (success) + (dolist (pkg packages) + (doom--condition-case! + (print! + (let ((result (doom-delete-package pkg t))) + (when result (setq success t)) + (color (if result 'green 'red) + "%s %s" + (if result "✓ Removed" "✕ Failed to remove") + pkg))))) + + (print! (bold (green "Finished!"))) + (if success (doom-delete-autoloads-file doom-package-autoload-file)) + success))))) diff --git a/core/autoload/test.el b/core/autoload/test.el index 9017e9288..0a65a6c33 100644 --- a/core/autoload/test.el +++ b/core/autoload/test.el @@ -1,6 +1,5 @@ ;;; core/autoload/test.el -*- lexical-binding: t; no-byte-compile: t; -*- -;;;###autoload (defun doom//run-tests (&optional modules) "Run all loaded tests, specified by MODULES (a list of module cons cells) or command line args following a double dash (each arg should be in the @@ -8,56 +7,56 @@ command line args following a double dash (each arg should be in the If neither is available, run all tests in all enabled modules." (interactive) - (let ((doom-modules (make-hash-table :test #'equal))) - ;; ensure DOOM is initialized - (let (noninteractive) - ;; Core libraries aren't fully loaded in a noninteractive session, so we - ;; reload it with `noninteractive' set to nil to force them to. - (doom-initialize t) - (run-hooks 'doom-init-hook 'pre-command-hook 'doom-after-switch-buffer-hook)) - (condition-case-unless-debug ex - (let ((target-paths - ;; Convert targets (either from MODULES or `argv') into a list of - ;; string paths, pointing to the root directory of modules - (cond ((string= (car argv) "--") ; command line - (save-match-data - (cl-loop for arg in (cdr argv) - if (string= arg "core") collect doom-core-dir - else if (string-match-p "/" arg) - nconc (cl-loop for dir in doom-modules-dirs - collect (expand-file-name arg dir)) - else - nconc (cl-loop for dir in doom-modules-dirs - for path = (expand-file-name arg dir) - if (file-directory-p path) - nconc - (cl-remove-if-not - #'file-directory-p - (directory-files path t "^[^.]" t))) - finally do (setq argv nil)))) + (doom//reload-autoloads) + (let (noninteractive) + ;; Core libraries aren't fully loaded in a noninteractive session, so we + ;; reload it with `noninteractive' set to nil to force them to. + (doom-initialize t) + (doom-initialize-modules t)) + (condition-case-unless-debug ex + (let* ((doom-modules (doom-module-table)) + (target-paths + ;; Convert targets (either from MODULES or `argv') into a list of + ;; string paths, pointing to the root directory of modules + (cond ((stringp (car modules)) ; command line + (save-match-data + (cl-loop for arg in modules + if (string= arg ":core") collect doom-core-dir + else if (string-match-p "/" arg) + nconc (cl-loop for dir in doom-modules-dirs + collect (expand-file-name arg dir)) + else + nconc (cl-loop for dir in doom-modules-dirs + for path = (expand-file-name arg dir) + if (file-directory-p path) + nconc + (cl-remove-if-not + #'file-directory-p + (directory-files path t "^[^.]" t))) + finally do (setq argv nil)))) - (modules ; cons-cells given to MODULES - (cl-loop for (module . submodule) in modules - if (doom-module-find-path module submodule) - collect it)) + (modules ; cons-cells given to MODULES + (cl-loop for (module . submodule) in modules + if (doom-module-locate-path module submodule) + collect it)) - ((let (noninteractive) - (load (expand-file-name "init.test.el" user-emacs-directory) nil t) - (append (list doom-core-dir) (doom-module-load-path))))))) - ;; Load all the unit test files... - (dolist (path target-paths) - (let ((test-path (expand-file-name "test/" path))) - (when (file-directory-p test-path) - (dolist (test-file (reverse (doom-packages--files test-path "\\.el$"))) - (load test-file nil :noerror))))) - ;; ... then run them - (if noninteractive - (ert-run-tests-batch-and-exit) - (call-interactively #'ert-run-tests-interactively))) - ('error - (lwarn 'doom-test :error - "%s -> %s" - (car ex) (error-message-string ex)))))) + ((append (list doom-core-dir) + (doom-module-load-path)))))) + ;; Load all the unit test files... + (dolist (path target-paths) + (let ((test-path (expand-file-name "test/" path))) + (when (file-directory-p test-path) + (dolist (test-file (reverse (doom-files-in test-path :match "\\.el$" :full t))) + (load test-file nil :noerror))))) + ;; ... then run them + (switch-to-buffer (get-buffer-create "*blank*")) + (if noninteractive + (ert-run-tests-batch-and-exit) + (call-interactively #'ert-run-tests-interactively))) + ('error + (lwarn 'doom-test :error + "%s -> %s" + (car ex) (error-message-string ex))))) ;; --- Test helpers ----------------------- diff --git a/core/autoload/util.el b/core/autoload/util.el deleted file mode 100644 index 2500df78b..000000000 --- a/core/autoload/util.el +++ /dev/null @@ -1,212 +0,0 @@ -;;; core/autoload/util.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun doom/what-face (&optional pos) - "Shows all faces and overlay faces at point. - -Interactively prints the list to the echo area. Noninteractively, returns a list -whose car is the list of faces and cadr is the list of overlay faces." - (interactive) - (let* ((pos (or pos (point))) - (faces (let ((face (get-text-property pos 'face))) - (if (keywordp (car-safe face)) - (list face) - (cl-loop for f in (doom-enlist face) collect f)))) - (overlays (cl-loop for ov in (overlays-at pos (1+ pos)) - nconc (doom-enlist (overlay-get ov 'face))))) - (cond ((called-interactively-p 'any) - (message "%s %s\n%s %s" - (propertize "Faces:" 'face 'font-lock-comment-face) - (if faces - (cl-loop for face in faces - if (listp face) - concat (format "'%s " face) - else - concat (concat (propertize (symbol-name face) 'face face) " ")) - "n/a ") - (propertize "Overlays:" 'face 'font-lock-comment-face) - (if overlays - (cl-loop for ov in overlays - concat (concat (propertize (symbol-name ov) 'face ov) " ")) - "n/a"))) - (t - (and (or faces overlays) - (list faces overlays)))))) - -;;;###autoload -(defun doom-active-minor-modes () - "Get a list of active minor-mode symbols." - (cl-loop for mode in minor-mode-list - if (and (boundp mode) (symbol-value mode)) - collect mode)) - -;;;###autoload -(defun doom/what-minor-mode (mode) - "Get information on an active minor mode. Use `describe-minor-mode' for a -selection of all minor-modes, active or not." - (interactive - (list (completing-read "Minor mode: " - (doom-active-minor-modes)))) - (describe-minor-mode-from-symbol - (cl-typecase mode - (string (intern mode)) - (symbol mode) - (t (error "Expected a symbol/string, got a %s" (type-of mode)))))) - -;;;###autoload -(defun doom/am-i-secure () - "Test to see if your root certificates are securely configured in emacs." - (declare (interactive-only t)) - (interactive) - (unless (string-match-p "\\_" system-configuration-features) - (warn "gnutls support isn't built into Emacs, there may be problems")) - (if-let* ((bad-hosts - (cl-loop for bad - in '("https://wrong.host.badssl.com/" - "https://self-signed.badssl.com/") - if (condition-case _e - (url-retrieve-synchronously bad) - (error nil)) - collect bad))) - (error (format "tls seems to be misconfigured (it got %s)." - bad-hosts)) - (url-retrieve "https://badssl.com" - (lambda (status) - (if (or (not status) (plist-member status :error)) - (warn "Something went wrong.\n\n%s" (pp-to-string status)) - (message "Your trust roots are set up properly.\n\n%s" (pp-to-string status)) - t))))) - -(defvar doom--profiler nil) -;;;###autoload -(defun doom/toggle-profiler () - "Toggle the Emacs profiler. Starts it if isn't running. Stops it and pops up -the profiling report otherwise." - (interactive) - (if (not doom--profiler) - (profiler-start 'cpu+mem) - (profiler-report) - (profiler-stop)) - (setq doom--profiler (not doom--profiler))) - -;;;###autoload -(defun doom/profile-emacs () - "Profile the startup time of Emacs in the background. -If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE." - (interactive) - (require 'esup) - (let ((init-file esup-user-init-file)) - (message "Starting esup...") - (esup-reset) - (setq esup-server-process (esup-server-create (esup-select-port))) - (setq esup-server-port (process-contact esup-server-process :service)) - (message "esup process started on port %s" esup-server-port) - (let ((process-args `("*esup-child*" - "*esup-child*" - ,esup-emacs-path - "-q" - "-L" ,esup-load-path - "-l" "esup-child" - ,(format "--eval=(esup-child-run \"%s\" \"%s\" %d)" - init-file - esup-server-port - esup-depth) - "--eval=(run-hooks 'after-init-hook 'emacs-startup-hook 'window-setup-hook)"))) - (when esup-run-as-batch-p - (setq process-args (append process-args '("--batch")))) - (setq esup-child-process (apply #'start-process process-args))) - (set-process-sentinel esup-child-process 'esup-child-process-sentinel))) - -;;;###autoload -(defun doom-info () - "Returns diagnostic information about the current Emacs session in markdown, -ready to be pasted in a bug report on github." - (require 'vc-git) - (let ((default-directory doom-emacs-dir)) - (format - (concat "- OS: %s (%s)\n" - "- Emacs: %s (%s)\n" - "- Doom: %s (%s %s)\n" - "- Graphic display: %s (daemon: %s)\n" - "- System features: %s\n" - "- Details:\n" - " ```elisp\n" - " uname -a: %s\n" - " modules: %s\n" - " packages: %s\n" - " elc dirs: %s\n" - " exec-path: %s\n" - " ```\n") - system-type system-configuration - emacs-version (format-time-string "%b %d, %Y" emacs-build-time) - doom-version - (if-let* ((branch (vc-git--symbolic-ref "core/core.el"))) - branch - "n/a") - (if-let* ((rev (vc-git-working-revision "core/core.el"))) - (format "https://github.com/hlissner/doom-emacs/commit/%s" rev) - "n/a") - (display-graphic-p) (daemonp) - (bound-and-true-p system-configuration-features) - ;; details - (with-temp-buffer - (unless (zerop (call-process "uname" nil t nil "-a")) - (insert (format "%s" system-type))) - (string-trim (buffer-string))) - (or (cl-loop with cat = nil - for key being the hash-keys of doom-modules - if (or (not cat) (not (eq cat (car key)))) - do (setq cat (car key)) and collect cat - else collect - (let ((flags (doom-module-get cat (cdr key) :flags))) - (if flags - `(,(cdr key) ,@flags) - (cdr key)))) - "n/a") - (or (let (packages) - (ignore-errors - (require 'async) - ;; collect these in another session to protect this - ;; session's state - (async-get - (async-start - `(lambda () - (setq load-path ',load-path) - (load ,(expand-file-name "core/core.el" doom-emacs-dir)) - (load ,(expand-file-name "init.el" doom-emacs-dir)) - (load ,(expand-file-name "core/autoload/packages.el" doom-emacs-dir)) - (doom-get-packages)) - (lambda (p) (setq packages p)))) - (mapcar (lambda (x) - (if (cdr x) - (format "%s" x) - (symbol-name (car x)))) - (cl-sort packages #'string-lessp :key (lambda (x) (symbol-name (car x))))))) - "n/a") - (or (ignore-errors - (cl-delete-duplicates - (cl-loop for file in (append (reverse (directory-files-recursively doom-core-dir "\\.elc$")) - (cl-loop for dir in doom-modules-dirs - nconc (directory-files-recursively dir "\\.elc$"))) - collect (file-relative-name (file-name-directory file) doom-emacs-dir)) - :test #'equal)) - "n/a") - exec-path))) - -;;;###autoload -(defun doom/info () - "Collects some debug information about your Emacs session, formats it into -markdown and copies it to your clipboard, ready to be pasted into bug reports!" - (declare (interactive-only t)) - (interactive) - (if noninteractive - (message "%s" (doom-info)) - (message "Generating Doom info...") - (kill-new (doom-info)) - (message "Done! Copied to clipboard."))) - -;;;###autoload -(defun doom/toggle-debug-mode () - (interactive) - (setq doom-debug-mode (not doom-debug-mode)) - (toggle-debug-on-error)) diff --git a/core/core-dispatcher.el b/core/core-dispatcher.el new file mode 100644 index 000000000..d52f9a13b --- /dev/null +++ b/core/core-dispatcher.el @@ -0,0 +1,286 @@ +;;; -*- lexical-binding: t; no-byte-compile: t; -*- + +;; Eagerly load these libraries because this module may be loaded in a session +;; that hasn't been fully initialized (where autoloads files haven't been +;; generated or `load-path' populated). +(load! autoload/packages) +(load! autoload/modules) +(load! autoload/debug) +(load! autoload/message) + + +;; +;; Dispatcher API +;; + +(defvar doom-auto-accept (getenv "YES") + "If non-nil, Doom will auto-accept any confirmation prompts during batch +commands like `doom//packages-install', `doom//packages-update' and +`doom//packages-autoremove'.") + +(defconst doom--dispatch-command-alist ()) +(defconst doom--dispatch-alias-alist ()) + +(defun doom--dispatch-format (desc &optional short) + (if (equal desc "TODO") + (format! (yellow "TODO")) + (with-temp-buffer + (let ((fill-column 72)) + (insert desc) + (goto-char (point-min)) + (while (re-search-forward "\n\n[^ \n]" nil t) + (fill-paragraph))) + (if (not short) + (buffer-string) + (goto-char (point-min)) + (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)))))) + +(defun doom--dispatch-help (&optional command desc &rest args) + "Display help documentation for a dispatcher command. If COMMAND and DESC are +omitted, show all available commands, their aliases and brief descriptions." + (if command + (princ (doom--dispatch-format desc)) + (print! (bold "%-10s\t%s\t%s" "Command:" "Alias" "Description")) + (dolist (spec (sort doom--dispatch-command-alist + (lambda (x y) (string-lessp (car x) (car y))))) + (cl-destructuring-bind (command &key desc _body) spec + (let ((aliases (cl-loop for (alias . cmd) in doom--dispatch-alias-alist + if (eq cmd command) + collect (symbol-name alias)))) + (print! " %-10s\t%s\t%s" + command (if aliases (string-join aliases ",") "") + (doom--dispatch-format desc t))))))) + +(defun doom-dispatch (args) + "Invoke a dispatcher command and pass ARGS to it." + (let ((help (equal (car args) "help"))) + (if help (pop args)) + (cl-destructuring-bind (command &key desc body) + (let ((sym (intern (car args)))) + (or (assq sym doom--dispatch-command-alist) + (assq (cdr (assq sym doom--dispatch-alias-alist)) doom--dispatch-command-alist) + (error "Invalid command: %s" (car args)))) + (if help + (apply #'doom--dispatch-help command desc (cdr args)) + (funcall body (cdr args)))))) + +;; FIXME Clumsy way of registering commands, refactor! +(defmacro def-dispatcher! (command desc &rest body) + "Define a dispatcher command. COMMAND is a symbol or a list of symbols +representing the aliases for this command. DESC is a string description. The +first line should be short (under 60 letters), as it will be displayed for +bin/doom help. + +BODY will be run when this dispatcher is called." + (declare (doc-string 2)) + (let* ((command (doom-enlist command)) + (cmd (car command)) + (aliases (cdr command))) + `(progn + ,(when aliases + `(dolist (alias ',aliases) + (map-put doom--dispatch-alias-alist alias ',cmd))) + (map-put doom--dispatch-command-alist + ',cmd (list :desc ,desc + ;; FIXME Implicit args var; ew + :body (lambda (args) ,@body)))))) + + +;; +;; Dispatch commands +;; + +;; Dummy dispatchers (no-op because they're handled especially) +(def-dispatcher! run + "Run Doom Emacs from bin/doom's parent directory. + +All arguments are passed on to Emacs (except for -p and -e). + + doom run + doom run -nw init.el + +WARNING: this command exists for convenience and testing. Doom will suffer +additional overhead for be started this way. For the best performance, it +is best to run Doom out of ~/.emacs.d and ~/.doom.d.") + +(def-dispatcher! (doctor doc) + "Checks for issues with your current Doom config.") + +(def-dispatcher! (help h) + "Look up additional information about a command.") + +;; Real dispatchers +(def-dispatcher! (quickstart qs) + "Quickly deploy a private module and Doom. + +This deploys a barebones config to ~/.doom.d. The destination can be changed +with the -p option, e.g. + + doom -p ~/.config/doom quickstart + +This command will refuse to overwrite the private directory if it already +exists." + (doom//quickstart)) + +(def-dispatcher! (install i) + "Installs requested plugins that aren't installed." + (doom//reload-doom-autoloads) + (when (doom//packages-install doom-auto-accept) + (doom//reload-package-autoloads))) + +(def-dispatcher! (update u) + "Checks for and updates outdated plugins." + (doom//reload-doom-autoloads) + (when (doom//packages-update doom-auto-accept) + (doom//reload-package-autoloads))) + +(def-dispatcher! (autoremove r) + "Removes orphaned plugins." + (doom//reload-doom-autoloads) + (when (doom//packages-autoremove doom-auto-accept) + (doom//reload-package-autoloads))) + +(def-dispatcher! (autoloads a) + "Regenerates Doom's autoloads file. + +This file tells Emacs where to find your module's autoloaded functions and +plugins." + (doom//reload-autoloads nil 'force)) + +(def-dispatcher! (upgrade up) + "Checks out the latest Doom on this branch." + (doom//upgrade)) + +(def-dispatcher! (compile c) + "Byte-compiles your config or selected modules. + + compile [TARGETS...] + compile :core :private lang/python + compile feature lang + +Accepts :core, :private and :plugins as special arguments, indicating you want +to byte-compile Doom's core files, your private config or your ELPA plugins, +respectively." + (doom//byte-compile args)) + +(def-dispatcher! (recompile rc) + "Re-byte-compiles outdated *.elc files." + (doom//byte-compile args 'recompile)) + +(def-dispatcher! clean + "Delete all *.elc files." + (doom//clean-byte-compiled-files)) + +(def-dispatcher! test + "Run Doom unit tests." + (load! autoload/test) + (doom//run-tests args)) + +(def-dispatcher! info + "Output system info in markdown for bug reports." + (doom/info)) + +(def-dispatcher! (version v) + "Reports the version of Doom and Emacs." + (doom/version)) + +(def-dispatcher! (refresh re) + "Refresh Doom. Same as autoremove+install+autoloads. + +This is the equivalent of running autoremove, install, autoloads, then +recompile. Run this whenever you: + + 1. Modify your `doom!' block, + 2. Add or remove `package!' blocks to your config, + 3. Add or remove autoloaded functions in module autoloaded files. + 4. Update Doom outside of Doom (e.g. with git)" + (let ((doom--inhibit-reload t)) + (with-demoted-errors "%s" (doom//packages-autoremove)) + (with-demoted-errors "%s" (doom//packages-install))) + (doom//reload-autoloads) + (doom//byte-compile nil 'recompile)) + + +;; +;; Quality of Life Commands +;; + +;; FIXME Detect & enforce remote +(defvar doom-remote "origin" + "TODO") + +(defun doom//upgrade () + "Upgrade Doom to the latest version." + (interactive) + (require 'vc-git) + (let ((core-file (expand-file-name "init.el" doom-core-dir)) + (branch (vc-git--symbolic-ref core-file)) + (default-directory doom-emacs-dir)) + (unless (file-exists-p core-file) + (error "Couldn't find %s, was Doom cloned properly?" + (abbreviate-file-name core-file))) + (unless branch + (error "Couldn't detect what branch you're using. Is %s a repo?" + (abbreviate-file-name doom-emacs-dir))) + (unless (eq (vc-state core-file 'Git) 'up-to-date) + (user-error "Doom has been modified; refusing to upgrade. Stash or undo your changes")) + (with-temp-buffer + (let ((buf (current-buffer))) + (when (zerop (process-file "git" nil buf nil + "fetch" "--tags" doom-remote branch)) + (let ((current-rev (vc-git-working-revision core-file)) + (rev (shell-command-to-string (format "git rev-parse %s/%s" doom-remote branch)))) + (unless rev + (error "Couldn't detect Doom's version. Is %s a repo?" + (abbreviate-file-name doom-emacs-dir))) + (if (equal current-rev rev) + (message "Doom is up to date!") + (when (or doom-auto-accept + (y-or-n-p "Doom is out of date, update?")) + (unless (zerop (process-file "git" nil buf nil + "checkout" (format "%s/%s" doom-remote branch))) + (error "An error occurred while checking out the latest commit")) + (when (file-exists-p (byte-compile-dest-file core-file)) + (message "Your config is byte-compiled, removing byte-compiled files") + (doom//clean-byte-compiled-files)) + (doom//reload) + (message "Done! Please restart Emacs for changes to take effect"))))))))) + +(defun doom//quickstart () + "Quickly deploy a private module and Doom. + +This deploys a barebones config to `doom-private-dir', installs all missing +packages and regenerates the autoloads file." + (interactive) + (let ((short-private-dir (abbreviate-file-name doom-private-dir))) + (if (file-directory-p doom-private-dir) + (print! (yellow "%s directory already exists. Skipping." short-private-dir)) + (print! "Creating %s" short-private-dir) + (make-directory doom-private-dir t) + (print! (green "Done!"))) + (let ((init-file (expand-file-name "init.el" doom-private-dir))) + (if (file-exists-p init-file) + (print! (yellow "%sinit.el already exists. Skipping." short-private-dir)) + (print! "Copying init.example.el to %s" short-private-dir) + (copy-file (expand-file-name "init.example.el" doom-emacs-dir) + init-file) + (print! (green "Done!")))) + (let ((config-file (expand-file-name "config.el" doom-private-dir))) + (if (file-exists-p config-file) + (print! "%sconfig.el already exists. Skipping." short-private-dir) + (print! "Deploying empty config.el file in %s" short-private-dir) + (with-temp-file config-file (insert "")) + (print! (green "Done!"))))) + (print! "Installing plugins") + (doom//packages-install) + (print! "Regenerating autoloads files") + (doom//reload-autoloads nil 'force-p) + (print! (bold (green "\nFinished! Doom is ready to go!\n"))) + (with-temp-buffer + (doom-template-insert "QUICKSTART_INTRO") + (print! (buffer-string)))) + +(provide 'core-dispatcher) +;;; core-dispatcher.el ends here diff --git a/core/core-editor.el b/core/core-editor.el index da456d948..491c50001 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -70,7 +70,7 @@ fundamental-mode) for performance sake." ;; Built-in plugins ;; -(push '("/[A-Z]+$" . text-mode) auto-mode-alist) +(push '("/LICENSE\\'" . text-mode) auto-mode-alist) (electric-indent-mode -1) ; enabled by default in Emacs 25+. No thanks. @@ -83,14 +83,15 @@ fundamental-mode) for performance sake." ;; revert buffers for changed files (def-package! autorevert - :defer doom-before-switch-buffer-hook + :after-call doom-before-switch-buffer-hook :config (setq auto-revert-verbose nil) (global-auto-revert-mode +1)) ;; persist variables across sessions (def-package! savehist - :defer (pre-command-hook . 1) + :defer 1 + :after-call post-command-hook :config (setq savehist-file (concat doom-cache-dir "savehist") savehist-save-minibuffer-history t @@ -100,7 +101,7 @@ fundamental-mode) for performance sake." ;; persistent point location in buffers (def-package! saveplace - :defer doom-before-switch-buffer-hook + :after-call doom-before-switch-buffer-hook :config (setq save-place-file (concat doom-cache-dir "saveplace")) (defun doom*recenter-on-load-saveplace (&rest _) @@ -112,20 +113,21 @@ fundamental-mode) for performance sake." ;; Keep track of recently opened files (def-package! recentf - :defer (pre-command-hook . 1) + :defer 1 + :after-call find-file-hook :commands recentf-open-files :config (setq recentf-save-file (concat doom-cache-dir "recentf") - recentf-auto-cleanup 60 + recentf-auto-cleanup 120 recentf-max-menu-items 0 recentf-max-saved-items 300 recentf-filename-handlers '(file-truename) recentf-exclude - (list #'file-remote-p "\\.\\(gz\\|gif\\|svg\\|png\\|jpe?g\\)$" + (list #'file-remote-p "\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\)$" "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$" "^/var/folders/.+$" ;; ignore private DOOM temp files (but not all of them) - (concat "^" (file-truename doom-local-dir)))) + (lambda (file) (file-in-directory-p file doom-local-dir)))) (recentf-mode +1)) @@ -135,7 +137,7 @@ fundamental-mode) for performance sake." ;; Auto-close delimiters and blocks as you type (def-package! smartparens - :defer doom-before-switch-buffer-hook + :after-call doom-before-switch-buffer-hook :commands (sp-pair sp-local-pair sp-with-modes) :config (require 'smartparens-config) @@ -155,7 +157,7 @@ fundamental-mode) for performance sake." ;; Branching undo (def-package! undo-tree - :defer doom-before-switch-buffer-hook + :after-call doom-before-switch-buffer-hook :config ;; persistent undo history is known to cause undo history corruption, which ;; can be very destructive! So disable it! @@ -176,13 +178,15 @@ fundamental-mode) for performance sake." command-log-mode-open-log-turns-on-mode t)) (def-package! dtrt-indent + :after-call doom-before-switch-buffer-hook :config (setq dtrt-indent-verbosity (if doom-debug-mode 2 0)) (defun doom|detect-indentation () (unless (or doom-inhibit-indent-detection (eq major-mode 'fundamental-mode)) (dtrt-indent-mode +1))) - (add-hook 'after-change-major-mode-hook #'doom|detect-indentation)) + (unless noninteractive + (add-hook 'after-change-major-mode-hook #'doom|detect-indentation))) (def-package! expand-region :commands (er/expand-region er/contract-region er/mark-symbol er/mark-word) diff --git a/core/core-keybinds.el b/core/core-keybinds.el index 0fca8ca65..3d9e1ed57 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -56,19 +56,15 @@ If any hook returns non-nil, all hooks after it are ignored.") ;; embolden local bindings (set-face-attribute 'which-key-local-map-description-face nil :weight 'bold) (which-key-setup-side-window-bottom) - (add-hook 'doom-init-hook #'which-key-mode)) + (add-hook 'doom-post-init-hook #'which-key-mode)) (def-package! hydra - :commands (defhydra defhydradio) - :init - ;; In case I later need to wrap defhydra in any special functionality. - (defalias 'def-hydra! 'defhydra) - (defalias 'def-hydra-radio! 'defhydradio) + :defer t :config (setq lv-use-seperator t) - (def-hydra! doom@text-zoom (:hint t :color red) + (defhydra doom@text-zoom (:hint t :color red) " Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset " @@ -76,7 +72,7 @@ If any hook returns non-nil, all hooks after it are ignored.") ("k" text-scale-decrease "out") ("0" (text-scale-set 0) "reset")) - (def-hydra! doom@window-nav (:hint nil) + (defhydra doom@window-nav (:hint nil) " Split: _v_ert _s_:horz Delete: _c_lose _o_nly diff --git a/core/core-lib.el b/core/core-lib.el index 89008a283..9e76dc0dc 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,8 +1,15 @@ ;;; core-lib.el -*- lexical-binding: t; -*- +;; Built in packages we use a lot of +(require 'subr-x) +(require 'cl-lib) +(require 'map) + (eval-and-compile (unless EMACS26+ (with-no-warnings + ;; if-let and when-let are deprecated in Emacs 26+ in favor of their + ;; if-let* variants, so we alias them for 25 users. (defalias 'if-let* #'if-let) (defalias 'when-let* #'when-let)))) @@ -11,18 +18,40 @@ ;; Helpers ;; -(defun doom--resolve-path-forms (paths &optional root) - (cond ((stringp paths) +(defun doom--resolve-path-forms (spec &optional directory) + "Converts a simple nested series of or/and forms into a series of +`file-exists-p' checks. + +For example + + (doom--resolve-path-forms + '(or \"some-file\" (and path-var \"/an/absolute/path\")) + \"~\") + +Returns + + '(or (file-exists-p (expand-file-name \"some-file\" \"~\")) + (and (file-exists-p (expand-file-name path-var \"~\")) + (file-exists-p \"/an/absolute/path\"))) + +This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." + (cond ((stringp spec) `(file-exists-p - (expand-file-name - ,paths ,(if (or (string-prefix-p "./" paths) - (string-prefix-p "../" paths)) - 'default-directory - (or root `(doom-project-root)))))) - ((listp paths) - (cl-loop for i in paths - collect (doom--resolve-path-forms i root))) - (t paths))) + ,(if (file-name-absolute-p spec) + spec + `(expand-file-name ,spec ,directory)))) + ((symbolp spec) + `(file-exists-p ,(if directory + `(expand-file-name ,spec ,directory) + path))) + ((and (listp spec) + (memq (car spec) '(or and))) + `(,(car spec) + ,@(cl-loop for i in (cdr spec) + collect (doom--resolve-path-forms i directory)))) + ((listp spec) + (doom--resolve-path-forms (eval spec t) directory)) + (t spec))) (defun doom--resolve-hook-forms (hooks) (cl-loop with quoted-p = (eq (car-safe hooks) 'quote) @@ -33,6 +62,11 @@ collect hook else collect (intern (format "%s-hook" (symbol-name hook))))) + +;; +;; Functions +;; + (defun doom-unquote (exp) "Return EXP unquoted." (while (memq (car-safe exp) '(quote function)) @@ -43,6 +77,94 @@ "Return EXP wrapped in a list, or as-is if already a list." (if (listp exp) exp (list exp))) +(defun doom-file-cookie-p (file) + "Returns the return value of the ;;;###if predicate form in FILE." + (with-temp-buffer + (insert-file-contents-literally file nil 0 256) + (if (and (re-search-forward "^;;;###if " nil t) + (<= (line-number-at-pos) 3)) + (let ((load-file-name file)) + (eval (sexp-at-point))) + t))) + +(defun doom-keyword-intern (str) + "Converts STR (a string) into a keyword (`keywordp')." + (or (stringp str) + (signal 'wrong-type-argument (list 'stringp str))) + (intern (concat ":" str))) + +(defun doom-keyword-name (keyword) + "Returns the string name of KEYWORD (`keywordp') minus the leading colon." + (or (keywordp keyword) + (signal 'wrong-type-argument (list 'keywordp keyword))) + (substring (symbol-name keyword) 1)) + +(cl-defun doom-files-in + (path-or-paths &rest rest + &key + filter + map + full + (follow-symlinks t) + (type 'files) + (relative-to (unless full default-directory)) + (depth 99999) + (match "^[^.]")) + "Returns a list of files/directories in PATH-OR-PATHS (one string path or a +list of them). + +FILTER is a function or symbol that takes one argument (the path). If it returns +non-nil, the entry will be excluded. + +MAP is a function or symbol which will be used to transform each entry in the +results. + +TYPE determines what kind of path will be included in the results. This can be t +(files and folders), 'files or 'dirs. + +By default, this function returns paths relative to PATH-OR-PATHS if it is a +single path. If it a list of paths, this function returns absolute paths. +Otherwise, by setting RELATIVE-TO to a path, the results will be transformed to +be relative to it. + +The search recurses up to DEPTH and no further. DEPTH is an integer. + +MATCH is a string regexp. Only entries that match it will be included." + (cond + ((listp path-or-paths) + (cl-loop for path in path-or-paths + if (file-directory-p path) + nconc (apply #'doom-files-in path (plist-put rest :relative-to relative-to)))) + ((let ((path path-or-paths) + result) + (dolist (file (file-name-all-completions "" path)) + (unless (member file '("./" "../")) + (let ((fullpath (expand-file-name file path))) + (cond ((directory-name-p fullpath) + (when (and (memq type '(t dirs)) + (string-match-p match file) + (not (and filter (funcall filter fullpath))) + (not (and (file-symlink-p fullpath) + (not follow-symlinks)))) + (setq result + (nconc result + (list (cond (map (funcall map fullpath)) + (relative-to (file-relative-name fullpath relative-to)) + (fullpath)))))) + (unless (<= depth 1) + (setq result + (nconc result (apply #'doom-files-in fullpath + (append `(:depth ,(1- depth) :relative-to ,relative-to) + rest)))))) + ((and (memq type '(t files)) + (string-match-p match file) + (not (and filter (funcall filter fullpath)))) + (push (if relative-to + (file-relative-name fullpath relative-to) + fullpath) + result)))))) + result)))) + (defun doom*shut-up (orig-fn &rest args) "Generic advisor for silencing noisy functions." (quiet! (apply orig-fn args))) @@ -61,29 +183,31 @@ (defmacro after! (targets &rest body) "A smart wrapper around `with-eval-after-load'. Supresses warnings during -compilation." +compilation. This will no-op on features that have been disabled by the user." (declare (indent defun) (debug t)) - (list (if (or (not (bound-and-true-p byte-compile-current-file)) - (dolist (next (doom-enlist targets)) - (if (symbolp next) - (require next nil :no-error) - (load next :no-message :no-error)))) - #'progn - #'with-no-warnings) - (cond ((symbolp targets) - `(eval-after-load ',targets '(progn ,@body))) - ((and (consp targets) - (memq (car targets) '(:or :any))) - `(progn - ,@(cl-loop for next in (cdr targets) - collect `(after! ,next ,@body)))) - ((and (consp targets) - (memq (car targets) '(:and :all))) - (dolist (next (cdr targets)) - (setq body `(after! ,next ,@body))) - body) - ((listp targets) - `(after! (:all ,@targets) ,@body))))) + (unless (and (symbolp targets) + (memq targets doom-disabled-packages)) + (list (if (or (not (bound-and-true-p byte-compile-current-file)) + (dolist (next (doom-enlist targets)) + (if (symbolp next) + (require next nil :no-error) + (load next :no-message :no-error)))) + #'progn + #'with-no-warnings) + (cond ((symbolp targets) + `(eval-after-load ',targets '(progn ,@body))) + ((and (consp targets) + (memq (car targets) '(:or :any))) + `(progn + ,@(cl-loop for next in (cdr targets) + collect `(after! ,next ,@body)))) + ((and (consp targets) + (memq (car targets) '(:and :all))) + (dolist (next (cdr targets)) + (setq body `(after! ,next ,@body))) + body) + ((listp targets) + `(after! (:all ,@targets) ,@body)))))) (defmacro quiet! (&rest forms) "Run FORMS without making any output." @@ -121,10 +245,11 @@ HOOK can be a quoted hook or a sharp-quoted function (which will be advised)." ,@forms (cond ((functionp ,hook) (advice-remove ,hook #',fn)) ((symbolp ,hook) (remove-hook ,hook #',fn))) - (unintern ',fn nil))) + (fmakunbound ',fn))) (cond ((functionp ,hook) (advice-add ,hook ,(if append :after :before) #',fn)) ((symbolp ,hook) + (put ',fn 'permanent-local-hook t) (add-hook ,hook #',fn ,append)))))) (defmacro add-hook! (&rest args) @@ -138,7 +263,7 @@ HOOK can be a quoted hook or a sharp-quoted function (which will be advised)." 3. A function, list of functions, or body forms to be wrapped in a lambda. Examples: - (add-hook! 'some-mode-hook 'enable-something) + (add-hook! 'some-mode-hook 'enable-something) (same as `add-hook') (add-hook! some-mode '(enable-something and-another)) (add-hook! '(one-mode-hook second-mode-hook) 'enable-something) (add-hook! (one-mode second-mode) 'enable-something) @@ -201,7 +326,19 @@ Body forms can access the hook's arguments through the let-bound variable (nreverse forms)))) (defmacro associate! (mode &rest plist) - "Associate a minor mode to certain patterns and project files." + "Enables a minor mode if certain conditions are met. + +The available conditions are: + + :modes SYMBOL_LIST + A list of major/minor modes in which this minor mode may apply. + :match REGEXP + A regexp to be tested against the current file path. + :files SPEC + Accepts what `project-file-exists-p!' accepts. Checks if certain files exist + relative to the project root. + :when FORM + Whenever FORM returns non-nil." (declare (indent 1)) (unless noninteractive (let ((modes (plist-get plist :modes)) @@ -220,7 +357,7 @@ Body forms can access the hook's arguments through the let-bound variable (not ,mode) (and buffer-file-name (not (file-remote-p buffer-file-name))) ,(if match `(if buffer-file-name (string-match-p ,match buffer-file-name)) t) - ,(if files (doom--resolve-path-forms files) t) + ,(if files (doom--resolve-path-forms files '(doom-project-root)) t) ,(or pred-form t)) (,mode 1))) ,@(if (and modes (listp modes)) @@ -232,5 +369,19 @@ Body forms can access the hook's arguments through the let-bound variable (t (user-error "associate! invalid rules for mode [%s] (modes %s) (match %s) (files %s)" mode modes match files)))))) +(defmacro file-exists-p! (spec &optional directory) + "Returns t if the files in SPEC all exist. + +SPEC can be a single file or a list of forms/files. It understands nested (and +...) and (or ...), as well. + +DIRECTORY is where to look for the files in SPEC if they aren't absolute. This +doesn't apply to variables, however. + +For example: + + (file-exists-p (or doom-core-dir \"~/.config\" \"some-file\") \"~\")" + (doom--resolve-path-forms spec directory)) + (provide 'core-lib) ;;; core-lib.el ends here diff --git a/core/core-os.el b/core/core-os.el index 6e2d9002d..b270aa375 100644 --- a/core/core-os.el +++ b/core/core-os.el @@ -30,20 +30,21 @@ ;; Don't open files from the workspace in a new frame ns-pop-up-frames nil) - (cond ((display-graphic-p) - ;; A known problem with GUI Emacs on MacOS: it runs in an isolated - ;; environment, so envvars will be wrong. That includes the PATH - ;; Emacs picks up. `exec-path-from-shell' fixes this. - (when (require 'exec-path-from-shell nil t) - (def-setting! :env (&rest vars) - "Inject VARS from your shell environment into Emacs." - `(exec-path-from-shell-copy-envs (list ,@vars))) - (setq exec-path-from-shell-check-startup-files nil - exec-path-from-shell-arguments (delete "-i" exec-path-from-shell-arguments)) - (defvaralias 'exec-path-from-shell-debug 'doom-debug-mode) - (exec-path-from-shell-initialize))) - ((require 'osx-clipboard nil t) - (osx-clipboard-mode +1)))) + (if (not (display-graphic-p)) + (add-hook 'doom-post-init-hook #'osx-clipboard-mode) + ;; A known problem with GUI Emacs on MacOS: it runs in an isolated + ;; environment, so envvars will be wrong. That includes the PATH Emacs + ;; picks up. `exec-path-from-shell' fixes this. + (defun doom|init-exec-path () + (when (require 'exec-path-from-shell nil t) + (def-setting! :env (&rest vars) + "Inject VARS from your shell environment into Emacs." + `(exec-path-from-shell-copy-envs (list ,@vars))) + (setq exec-path-from-shell-check-startup-files nil + exec-path-from-shell-arguments (delete "-i" exec-path-from-shell-arguments)) + (defvaralias 'exec-path-from-shell-debug 'doom-debug-mode) + (exec-path-from-shell-initialize))) + (add-hook 'doom-pre-init-hook #'doom|init-exec-path))) (IS-LINUX (setq x-gtk-use-system-tooltips nil ; native tooltips are ugly! diff --git a/core/core-packages.el b/core/core-packages.el index f06e50e2b..faca27409 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -1,19 +1,17 @@ ;;; core-packages.el --- package management system -*- lexical-binding: t; -*- -(require 'core-lib (concat doom-core-dir "core-lib")) - -;; Emacs package management is opinionated. Unfortunately, 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. +;; 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: ;; -;; + `make install` or `doom//packages-install': Installs packages that are +;; + `bin/doom install` or `doom//packages-install': Installs packages that are ;; wanted, but not installed. -;; + `make update` or `doom//packages-update': Updates packages that are +;; + `bin/doom update` or `doom//packages-update': Updates packages that are ;; out-of-date. -;; + `make autoremove` or `doom//packages-autoremove': Uninstalls packages that -;; are no longer needed. +;; + `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 @@ -42,56 +40,50 @@ ;; See core/autoload/packages.el for more functions. (defvar doom-init-p nil - "Non-nil if doom is done initializing (once `doom-post-init-hook' is done). If -this is nil after Emacs has started something is wrong.") + "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 - (make-hash-table :test #'equal :size 100 :rehash-threshold 1.0) +(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) +(defvar doom-psuedo-module-dirs (list doom-private-dir) "Additional paths for modules that are outside of `doom-modules-dirs'. -`doom//reload-autoloads', `doom//byte-compile' and `doom-initialize-packages' -will include the directories in this list.") +`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) +(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-autoload-excluded-packages '(marshal gh) - "Packages that have silly or destructive autoload files that try to load -everyone in the universe and their dog, causing errors that make babies cry. No -one wants that.") - -(defvar doom-reload-hook nil - "A list of hooks to run when `doom/reload-load-path' is called.") - (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-autoloads' will generate its autoloads file.") + "Where `doom//reload-doom-autoloads' will generate its core autoloads file.") -(defvar doom-packages-file (concat doom-cache-dir "packages.el") - "Where to cache `load-path', `Info-directory-list', `doom-disabled-packages' -and `auto-mode-alist'.") +(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) @@ -152,22 +144,6 @@ and `auto-mode-alist'.") (file-relative-name load-file-name doom-emacs-dir) (abbreviate-file-name load-file-name)))) -(defun doom|refresh-cache () - "Refresh `doom-packages-file', which caches `load-path', -`Info-directory-list', `doom-disabled-packages', `auto-mode-alist' and -`package-activated-list'." - (doom-initialize-packages 'internal) - (let ((coding-system-for-write 'emacs-internal)) - (with-temp-file doom-packages-file - (insert ";;; -*- lexical-binding:t -*-\n" - ";; This file was autogenerated by `doom|refresh-cache', DO NOT EDIT!\n") - (prin1 `(setq load-path ',load-path - auto-mode-alist ',auto-mode-alist - Info-directory-list ',Info-directory-list - doom-disabled-packages ',doom-disabled-packages - package-activated-list ',package-activated-list) - (current-buffer))))) - (defun doom|display-benchmark (&optional return-p) "Display a benchmark, showing number of packages and modules, and how quickly they were loaded at startup. @@ -179,236 +155,300 @@ If RETURN-P, return the message as a string instead of displaying it." ;; 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)) - (hash-table-count doom-modules) + (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)))))) -(add-hook 'emacs-startup-hook #'doom|display-benchmark) -(add-hook 'doom-reload-hook #'doom|display-benchmark) +(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 ;; -(defun doom-initialize (&optional force-p) - "Bootstrap the bare essentials to get Doom running, if it hasn't already. If -FORCE-P is non-nil, do it anyway. +(autoload 'doom//reload-doom-autoloads "autoload/modules" nil t) +(autoload 'doom//reload-package-autoloads "autoload/modules" nil t) -1. Ensures all the essential directories exist, -2. Ensures core packages are installed, -3. Loads your autoloads file in `doom-autoload-file', -4. Builds and caches `load-path', `Info-directory-list' and - `doom-disabled-packages' in `doom-packages-file'" - ;; Called early during initialization; only use native (and cl-lib) functions! - (let ((load-path doom-site-load-path)) - (require 'subr-x) - (require 'cl-lib) - (require 'map)) - (when (eq doom-init-p 'internal) - (setq force-p nil)) +(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)) - ;; packages.el cache - (when (and force-p (file-exists-p doom-packages-file)) - (message "Deleting packages.el cache") - (delete-file doom-packages-file)) - (unless (load doom-packages-file 'noerror 'nomessage 'nosuffix) - ;; Ensure core folders exist, otherwise we get errors - (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir)) - (unless (file-directory-p dir) - (make-directory dir t))) - ;; Ensure packages have been initialized - (require 'package) - (setq package-activated-list nil - package--initialized nil) - (let (byte-compile-warnings) - (condition-case _ - (package-initialize) - ('error (package-refresh-contents) - (setq doom--refreshed-p t) - (package-initialize)))) - ;; Ensure core packages are installed. - (let ((core-packages (cl-remove-if #'package-installed-p doom-core-packages))) - (when 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")))) - ;; autoloads file - (condition-case-unless-debug e - (unless - (let (byte-compile-warnings) - (load (substring doom-autoload-file 0 -3) 'noerror 'nomessage)) - (error "No autoloads file! Run make autoloads")) - (error - (funcall (if noninteractive #'warn #'error) - "Autoload file error: %s -> %s" (car e) (error-message-string e)))) - (add-to-list 'load-path doom-core-dir)) - ;; initialize Doom core - (require 'core-os) + ;; 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 - (unless doom-init-p - ;; Cache important packages.el state - (doom|refresh-cache)) (require 'core-ui) (require 'core-editor) (require 'core-projects) - (require 'core-keybinds)) - (setq doom-init-p t)) + (require 'core-keybinds))) -(defun doom-initialize-autoloads () - "Ensures that `doom-autoload-file' exists and is loaded. Otherwise run -`doom//reload-autoloads' to generate it. Used from Doom's Makefile." - (unless (file-exists-p doom-autoload-file) - (quiet! (doom//reload-autoloads)))) +(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-packages', `packages-alist' and `quelpa-cache' are -populated. + "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. -This reads modules' packages.el files, runs `package-initialize', and -initializes quelpa, if they haven't already. If FORCE-P is non-nil, do it -anyway. If FORCE-P is 'internal, only (re)populate `doom-packages'. +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)) + (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)) - (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 _ - (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")))) - - (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-expand-file (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)))))))) + (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 (module submodule) - "Returns t if MODULE SUBMODULE is enabled (ie. present in `doom-modules')." +(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 module submodule) doom-modules) + (gethash (cons category module) doom-modules) t)) -(defun doom-module-get (module submodule &optional property) - "Returns the plist for MODULE/SUBMODULE. If PROPERTY is set, get its property." - (when-let* ((plist (gethash (cons module submodule) doom-modules))) +(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 (module submodule property value) - "Set a PROPERTY for MODULE SUBMODULE to VALUE." - (when-let* ((plist (doom-module-get module submodule))) - (puthash (cons module submodule) - (plist-put plist property value) - doom-modules))) +(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 (module submodule &rest plist) - "Adds MODULE and SUBMODULE to `doom-modules' and sets its plist to PLIST, -which should contain a minimum of :flags and :path. +(defun doom-module-set (category module &rest plist) + "Enables a module by adding it to `doom-modules'. -MODULE is a keyword, SUBMODULE is a symbol, PLIST is a plist that accepts the +CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the following properties: - :flags [SYMBOL LIST] list of enabled module flags - :path [STRING] path to module root directory + :flags [SYMBOL LIST] list of enabled category flags + :path [STRING] path to category root directory Example: - - (doom-module-set :lang 'haskell :flags '(+intero)) - -Used by `require!'." + (doom-module-set :lang 'haskell :flags '(+intero))" (when plist - (let ((old-plist (doom-module-get module submodule))) + (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-find-path module submodule)))))) - (let ((key (cons module submodule))) + (doom-module-locate-path category module)))))) + (let ((key (cons category module))) (puthash key plist doom-modules))) -(defun doom-module-find-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." - (when (keywordp module) - (setq module (substring (symbol-name module) 1))) - (when (symbolp submodule) - (setq submodule (symbol-name submodule))) +(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 module "/" submodule "/" file) + for path = (concat category "/" module "/" file) if (file-exists-p path) return (expand-file-name path))) (defun doom-module-from-path (&optional path) - "Get module cons cell (MODULE . SUBMODULE) for PATH, if possible." - (or doom--current-module + "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-expand-file (module submodule &optional file) - "Like `expand-file-name', but expands FILE relative to MODULE (keywordp) and -SUBMODULE (symbol)" - (let ((path (doom-module-get module submodule :path))) - (if file - (expand-file-name file path) - path))) + (intern submodule)))))))) (defun doom-module-load-path () "Returns a list of absolute file paths to activated modules, with APPEND-FILE @@ -417,13 +457,88 @@ added, if the file exists." 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" nil nil 'macro) +(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 +;; can be functions to hook variables. +;; +;; (use-package X :after-call find-file-hook) +;; +;; This will load X on the first invokation of `find-file-hook' (then it will +;; remove itself from the hook). +(defvar doom--deferred-packages-alist ()) +(after! use-package-core + (add-to-list 'use-package-deferring-keywords :after-call nil #'eq) + + (setq use-package-keywords + (use-package-list-insert :after-call use-package-keywords :after)) + + (defalias 'use-package-normalize/:after-call + 'use-package-normalize-symlist) + + (defun use-package-handler/:after-call (name-symbol _keyword hooks rest state) + (let ((fn (intern (format "doom|transient-hook--load-%s" name-symbol))) + (hooks (delete-dups hooks))) + (if (plist-get state :demand) + (use-package-process-keywords name rest state) + (use-package-concat + `((fset ',fn + (lambda (&rest _) + (require ',name-symbol) + (dolist (hook (cdr (assq ',name-symbol doom--deferred-packages-alist))) + (if (functionp hook) + (advice-remove hook #',fn) + (remove-hook hook #',fn))) + (map-delete doom--deferred-packages-alist ',name-symbol) + (fmakunbound ',fn)))) + (cl-mapcan (lambda (hook) + (if (functionp hook) + `((advice-add #',hook :before #',fn)) + `((add-hook ',hook #',fn)))) + hooks) + `((map-put doom--deferred-packages-alist + ',name-symbol + '(,@hooks ,@(cdr (assq name-symbol doom--deferred-packages-alist))))) + (use-package-process-keywords name rest state)))))) ;; @@ -433,33 +548,51 @@ added, if the file exists." (defmacro doom! (&rest modules) "Bootstraps DOOM Emacs and its modules. -MODULES is an malformed plist of modules to load." - (let (init-forms config-forms file-name-handler-alist) - (let (module) - (dolist (m modules) - (cond ((keywordp m) (setq module m)) - ((not module) (error "No namespace specified in `doom!' for %s" m)) - ((let ((submodule (if (listp m) (car m) m)) - (flags (if (listp m) (cdr m)))) - (let ((path (doom-module-find-path module submodule))) - (if (not path) - (when doom-debug-mode - (message "Couldn't find the %s %s module" module submodule)) - (doom-module-set module submodule :flags flags :path path) - (push `(let ((doom--current-module ',(cons module submodule))) - (load! init ,path t)) - init-forms) - (push `(let ((doom--current-module ',(cons module submodule))) - (load! config ,path t)) - config-forms)))))))) +The bootstrap process involves making sure the essential directories exist, core +packages are installed, `doom-autoload-file' is loaded, `doom-packages-file' +cache exists (and is loaded) and, finally, loads your private init.el (which +should contain your `doom!' block). + +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)." + (let ((doom-modules (doom-module-table (or modules t))) + init-forms config-forms file-name-handler-alist) + (maphash (lambda (key value) + (let ((path (plist-get value :path))) + (push `(let ((doom--current-module ',key)) (load! init ,path t)) + init-forms) + (push `(let ((doom--current-module ',key)) (load! config ,path t)) + config-forms))) + doom-modules) `(let (file-name-handler-alist) (setq doom-modules ',doom-modules) ,@(nreverse init-forms) + (run-hooks 'doom-init-hook) (unless noninteractive (let ((doom--stage 'config)) ,@(nreverse config-forms) (when doom-private-dir - (load ,(concat doom-private-dir "config") t t))))))) + (let ((load-prefer-newer t)) + (load ,(expand-file-name "config" doom-private-dir) + t (not doom-debug-mode))))))))) (defmacro def-package! (name &rest plist) "A thin wrapper around `use-package'." @@ -473,16 +606,7 @@ MODULES is an malformed plist of modules to load." (or (and (plist-member plist :if) (not (eval (plist-get plist :if) t))) (and (plist-member plist :when) (not (eval (plist-get plist :when) t))) (and (plist-member plist :unless) (eval (plist-get plist :unless) t)))) - `(progn - ,(when-let* ((defer (plist-get plist :defer)) - (value (or (car-safe defer) defer))) - (setq plist (plist-put plist :defer (or (cdr-safe defer) t))) - (unless (or (memq value '(t nil)) - (number-or-marker-p value)) - `(add-transient-hook! ',value - ,(intern (format "load-%s" name)) - (require ',name)))) - (use-package ,name ,@plist)))) + `(use-package ,name ,@plist))) (defmacro def-package-hook! (package when &rest body) "Reconfigures a package's `def-package!' block. @@ -511,8 +635,7 @@ to have them return non-nil (or exploit that to overwrite Doom's config)." package (substring (symbol-name when) 1))) ,@body))) - (t - (error "'%s' isn't a valid hook for def-package-hook!" when)))) + ((error "'%s' isn't a valid hook for def-package-hook!" when)))) (defmacro load! (filesym &optional path noerror) "Load a file relative to the current executing file (`load-file-name'). @@ -525,10 +648,13 @@ the lookup is relative to `load-file-name', `byte-compile-current-file' or If NOERROR is non-nil, don't throw an error if the file doesn't exist." (or (symbolp filesym) (signal 'wrong-type-argument (list 'symbolp filesym))) - (let ((path (or path - (and load-file-name (file-name-directory load-file-name)) + (let ((path (or (when path + (cond ((stringp path) path) + ((symbolp path) (symbol-value path)) + ((listp path) (eval path t)))) (and (bound-and-true-p byte-compile-current-file) (file-name-directory byte-compile-current-file)) + (and load-file-name (file-name-directory load-file-name)) (and buffer-file-name (file-name-directory buffer-file-name)) (error "Could not detect path to look for '%s' in" filesym))) @@ -549,7 +675,7 @@ The module is only loaded once. If RELOAD-P is non-nil, load it again." (apply #'doom-module-set module submodule (mapcar #'eval plist))) (when (or reload-p (not enabled-p)) - (let ((module-path (doom-module-find-path module submodule))) + (let ((module-path (doom-module-locate-path module submodule))) (if (file-directory-p module-path) `(condition-case-unless-debug ex (let ((doom--current-module ',(cons module submodule))) @@ -578,7 +704,8 @@ Module FLAGs are set in your config's `doom!' block, typically in When this macro is used from inside a module, MODULE and SUBMODULE can be omitted. eg. (featurep! +flag1)" (unless submodule - (let* ((path (or load-file-name byte-compile-current-file)) + (let* ((path (or (bound-and-true-p byte-compile-current-file) + load-file-name)) (module-pair (doom-module-from-path path))) (unless module-pair (error "featurep! couldn't detect what module its in! (in %s)" path)) @@ -677,359 +804,16 @@ loads MODULE SUBMODULE's packages.el file." (flags ,flags)) (when flags (doom-module-put ,module ',submodule :flags flags)) - (load! packages ,(doom-module-find-path module submodule) t))) - - -;; -;; Commands -;; - -(defun doom//reload () - "Reload your private Doom config. Experimental!" - (interactive) - (message "Reloading your private config...") - (load (concat doom-private-dir "init.el") nil nil 'nosuffix) - (doom-packages--async-run #'doom//reload-packages) - (let ((doom--stage 'config)) - (load (concat doom-private-dir "config.el") nil nil 'nosuffix)) - (message "✓ Done!")) - -(defun doom-packages--read-if-cookies (file) - "Returns the value of the ;;;###if predicate form in FILE." - (with-temp-buffer - (insert-file-contents-literally file nil 0 256) - (if (and (re-search-forward "^;;;###if " nil t) - (<= (line-number-at-pos) 3)) - (let ((load-file-name file)) - (eval (sexp-at-point))) - t))) - -(defun doom-packages--async-run (fn) - (let* ((default-directory doom-emacs-dir)) - (compile (format "%s --quick --batch -l core/core.el -f %s" - (executable-find "emacs") - (symbol-name fn))) - (while compilation-in-progress - (sit-for 1)))) - -(defun doom-packages--files (dir pattern) - "Like `directory-files-recursively', but traverses symlinks." - (cl-letf (((symbol-function #'file-symlink-p) #'ignore)) - (directory-files-recursively dir pattern))) - -(defun doom//reload-load-path () - "Reload `load-path' and recompile files (if necessary). - -Use this when `load-path' is out of sync with your plugins. This should only -happen if you manually modify/update/install packages from outside Emacs, while -an Emacs session is running. - -This isn't necessary if you use Doom's package management commands because they -call `doom//reload-load-path' remotely (through emacsclient)." - (interactive) - (unless doom--inhibit-reload - (when (file-exists-p doom-packages-file) - (delete-file doom-packages-file)) - (cond ((and noninteractive (not (daemonp))) - (require 'server) - (when (server-running-p) - (message "Reloading active Emacs session...") - (server-eval-at server-name '(doom//reload-load-path)))) - (t - (doom-initialize t) - (message "%d packages reloaded" (length package-alist)) - (run-hooks 'doom-reload-hook))))) - -(defvar generated-autoload-load-name) -(defun doom//reload-autoloads () - "Refreshes the autoloads.el file, specified by `doom-autoload-file'. - -It scans and reads core/autoload/*.el, modules/*/*/autoload.el and -modules/*/*/autoload/*.el, and generates an autoloads file at the path specified -by `doom-autoload-file'. This file tells Emacs where to find lazy-loaded -functions. - -This should be run whenever init.el or an autoload file is modified. Running -'make autoloads' from the commandline executes this command." - (interactive) - ;; This function must not use autoloaded functions or external dependencies. - ;; It must assume nothing is set up! - (if (not noninteractive) - ;; This is done in another instance to protect the current session's state - ;; in case this function has side effects. - (progn - (doom-packages--async-run 'doom//reload-autoloads) - (load doom-autoload-file 'noerror nil 'nosuffix)) - (let ((default-directory doom-emacs-dir) - (targets - (file-expand-wildcards - (expand-file-name "autoload/*.el" doom-core-dir)))) - (dolist (path (doom-module-load-path)) - (let ((auto-dir (expand-file-name "autoload" path)) - (auto-file (expand-file-name "autoload.el" path))) - (when (file-exists-p auto-file) - (push auto-file targets)) - (when (file-directory-p auto-dir) - (dolist (file (doom-packages--files auto-dir "\\.el$")) - (push file targets))))) - (when (file-exists-p doom-autoload-file) - (delete-file doom-autoload-file) - (ignore-errors (delete-file (byte-compile-dest-file doom-autoload-file))) - (message "Deleted old autoloads.el")) - (message "Generating new autoloads.el") - (dolist (file (mapcar #'file-truename (reverse targets))) - (let ((generated-autoload-load-name (file-name-sans-extension file))) - (message - (cond ((not (doom-packages--read-if-cookies file)) - "⚠ Ignoring %s") - ((update-file-autoloads file nil doom-autoload-file) - "✕ Nothing in %s") - ("✓ Scanned %s")) - (if (file-in-directory-p file default-directory) - (file-relative-name file) - (abbreviate-file-name file))))) - (make-directory (file-name-directory doom-autoload-file) t) - (let ((buf (find-file-noselect doom-autoload-file t)) - (load-path (append doom-psuedo-module-dirs - doom-modules-dirs - load-path)) - case-fold-search) - ;; FIXME Make me faster - (unwind-protect - (with-current-buffer buf - (goto-char (point-min)) - (insert ";;; -*- lexical-binding:t -*-\n" - ";; This file is autogenerated by `doom//reload-autoloads', DO NOT EDIT !!\n\n") - - ;; Replace autoload paths (only for module autoloads) with - ;; absolute paths for faster resolution during load and simpler - ;; `load-path' - (save-excursion - (let (cache) - (while (re-search-forward "^\\s-*(autoload\\s-+'[^ ]+\\s-+\"\\([^/][^\"]*\\)\"" nil t) - (let ((path (match-string 1))) - (replace-match - (or (cdr (assoc path cache)) - (when-let* ((libpath (locate-library path)) - (libpath (file-name-sans-extension libpath))) - (push (cons path libpath) cache) - libpath) - (progn - (warn "Couldn't find absolute path for: %s" path) - path)) - t t nil 1)))) - (message "✓ Autoload paths expanded")) - - ;; insert package autoloads - (save-excursion - (dolist (spec package-alist) - (let ((pkg (car spec))) - (unless (memq pkg doom-autoload-excluded-packages) - (let ((file (concat (package--autoloads-file-name (cadr spec)) ".el"))) - (insert "(let ((load-file-name " (prin1-to-string file) "))") - (insert-file-contents file) - (while (re-search-forward "^;;\\(.*\n\\)" nil t) - (replace-match "" t t)) - (unless (bolp) (insert "\n")) - (insert ")\n"))))) - (message "✓ Package autoloads included")) - - ;; Remove `load-path' and `auto-mode-alist' modifications (most - ;; of them, at least); they are cached elsewhere, so these are - ;; unnecessary overhead. - (while (re-search-forward (concat "^\\s-*(\\(" - "add-to-list\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\|" - "if (fboundp 'register-definition-prefixes)" - "\\)") - nil t) - (beginning-of-line) - (skip-chars-forward " \t") - (kill-sexp)) - (message "✓ load-path/auto-mode-alist entries removed") - - ;; Remove byte-compile inhibiting file variables so we can - ;; byte-compile the file. - (when (re-search-forward "^;; no-byte-compile: t\n$" nil t) - (replace-match "" t t)) - (save-buffer) - - ;; Byte compile it to give the file a chance to reveal errors. - (condition-case-unless-debug ex - (quiet! (byte-compile-file doom-autoload-file 'load)) - ('error - (delete-file doom-autoload-file) - (message "Deleting autoloads file!") - (error "Error in autoloads.el: %s -- %s" - (car ex) (error-message-string ex)))) - (message "Done!")) - (kill-buffer buf)))))) - -(defun doom//byte-compile (&optional modules recompile-p) - "Byte compiles your emacs configuration. - -init.el is always byte-compiled by this. - -If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are -byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom -core. It always ignores unit tests and files with `no-byte-compile' enabled. - -Doom was designed to benefit from byte-compilation, but the process may take a -while. Also, while your config files are byte-compiled, changes to them will not -take effect! Use `doom//clean-byte-compiled-files' or `make clean' to remove -these files. - -If RECOMPILE-P is non-nil, only recompile out-of-date files." - (interactive - (list nil current-prefix-arg)) - (let ((default-directory doom-emacs-dir) - (recompile-p (or recompile-p (and (member "-r" (cdr argv)) t))) - (argv (delete "-r" argv))) - (if (not noninteractive) - ;; This is done in another instance to protect the current session's - ;; state, because this function has side effects. - (doom-packages--async-run 'doom//byte-compile) - (let ((total-ok 0) - (total-fail 0) - (total-noop 0) - (modules (or modules (cdr argv))) - compile-targets) - ;; Ensure that Doom has been fully loaded, some of its state may be - ;; pertinent to files compiled later. - (let (noninteractive) - ;; Core libraries aren't fully loaded in a noninteractive session, so - ;; we pretend to be interactive and reinitialize - (doom-initialize) - ;; In case autoloads.el hasn't been properly generated at this point. - (unless (file-exists-p doom-autoload-file) - (mapc #'load (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))))) - ;; Assemble el files we want to compile; taking into account that - ;; MODULES may be a list of MODULE/SUBMODULE strings from the command - ;; line. - (setq compile-targets - (cl-loop for target - in (or modules (append (list doom-core-dir) (doom-module-load-path))) - if (equal target "core") - nconc (nreverse (doom-packages--files doom-core-dir "\\.el$")) - and collect (expand-file-name "init.el" doom-private-dir) - else if (file-directory-p target) - nconc (nreverse (doom-packages--files target "\\.el$")) - else if (cl-member target doom-psuedo-module-dirs :test #'file-in-directory-p) - nconc (nreverse (doom-packages--files it "\\.el$")) - else if (string-match "^\\([^/]+\\)/\\([^/]+\\)$" target) - nconc (nreverse (doom-packages--files - (doom-module-find-path - (intern (format ":%s" (match-string 1 target))) - (intern (match-string 2 target))) - "\\.el$")) - else if (file-exists-p target) - collect target - finally do (setq argv nil))) - (if (not compile-targets) - (message "No targets to compile") - (condition-case ex - (let ((use-package-expand-minimally t)) - (push (expand-file-name "init.el" doom-emacs-dir) compile-targets) - (dolist (target (cl-delete-duplicates (mapcar #'file-truename compile-targets) :test #'equal)) - (when (and (not (string-match-p "/\\(packages\\|doctor\\)\\.el$" target)) - (or (not recompile-p) - (let ((elc-file (byte-compile-dest-file target))) - (and (file-exists-p elc-file) - (file-newer-than-file-p target elc-file))))) - (let ((result (if (doom-packages--read-if-cookies target) - (byte-compile-file target) - 'no-byte-compile)) - (short-name (if (file-in-directory-p target doom-emacs-dir) - (file-relative-name target doom-emacs-dir) - (abbreviate-file-name target)))) - (cl-incf - (cond ((eq result 'no-byte-compile) - (message! (dark (white "⚠ Ignored %s" short-name))) - total-noop) - ((null result) - (message! (red "✕ Failed to compile %s" short-name)) - total-fail) - (t - (message! (green "✓ Compiled %s" short-name)) - (quiet! (load target t t)) - total-ok)))))) - (message! - (bold - (color (if (= total-fail 0) 'green 'red) - "%s %s file(s) %s" - (if recompile-p "Recompiled" "Compiled") - (format "%d/%d" total-ok (- (length compile-targets) total-noop)) - (format "(%s ignored)" total-noop))))) - (error - (message! (red "\n%%s\n\n%%s\n\n%%s") - "There were breaking errors." - (error-message-string ex) - "Reverting changes...") - (quiet! (doom//clean-byte-compiled-files)) - (message! (green "Finished (nothing was byte-compiled)"))))))))) - -(defun doom//byte-compile-core (&optional recompile-p) - "Byte compile the core Doom files. - -This is faster than `doom//byte-compile', still yields considerable performance -benefits, and is more reliable in an ever-changing Emacs config (since you won't -likely change core files directly). - -If RECOMPILE-P is non-nil, only recompile out-of-date core files." - (interactive "P") - (if (not noninteractive) - ;; This is done in another instance to protect the current session's - ;; state. `doom-initialize-packages' will have side effects otherwise. - (doom-packages--async-run 'doom//byte-compile-core) - (doom//byte-compile (list "core") recompile-p))) - -(defun doom//byte-recompile-plugins () - "Recompile all installed plugins. If you're getting odd errors after upgrading -(or downgrading) Emacs, this may fix it." - (interactive) - (if (not noninteractive) - ;; This is done in another instance to protect the current session's - ;; state. `doom-initialize-packages' will have side effects otherwise. - (doom-packages--async-run 'doom//byte-recompile-plugins) - (byte-recompile-directory package-user-dir 0 t))) - -(defun doom//clean-byte-compiled-files () - "Delete all the compiled elc files in your Emacs configuration. This excludes -compiled packages.'" - (interactive) - (unless - (cl-loop with default-directory = doom-emacs-dir - for path - in (append (file-expand-wildcards "*.elc" t) - (doom-packages--files doom-core-dir "\\.elc$") - (cl-loop for dir in (doom-module-load-path) - nconc (doom-packages--files dir "\\.elc$"))) - for truepath = (file-truename path) - if (file-exists-p truepath) - collect path - and do (delete-file truepath) - and do - (message "✓ Deleted %s" - (if (file-in-directory-p truepath default-directory) - (file-relative-name truepath) - (abbreviate-file-name path)))) - (message "Everything is clean"))) - -(defun doom//reload-packages () - "Runs `doom//reload-autoloads', `doom//packages-autoremove' and -`doom//packages-install' before reloading your Emacs session." - (interactive) - (let ((doom--inhibit-reload t)) - (doom//packages-autoremove) - (doom//packages-install) - (doom//reload-autoloads)) - (doom//reload-load-path)) + (load! packages ,(doom-module-locate-path module submodule) t))) ;; ;; Make package.el cooperate with Doom ;; -(defun doom*initialize-packages (&rest _) (package-initialize)) +(defun doom*initialize-packages (&rest _) + (unless package--initialized + (package-initialize))) (advice-add #'package-delete :before #'doom*initialize-packages) (advice-add #'package-install :before #'doom*initialize-packages) @@ -1048,11 +832,13 @@ compiled packages.'" ;; Cross-module configuration ;; -;; I needed a way to reliably cross-configure modules without worrying about -;; whether they were enabled or not, so I wrote `set!'. If a setting doesn't -;; exist at runtime, the `set!' call is ignored and its arguments are left -;; unevaluated (and entirely omitted when byte-compiled). -(defvar doom-settings nil) +;; I needed a way to reliably cross-configure modules without littering my +;; modules with `after!' blocks or testing whether they were enabled, so I wrote +;; `set!'. If a setting doesn't exist at runtime, the `set!' call is ignored and +;; its arguments are left unevaluated (and entirely omitted when byte-compiled). + +(defvar doom-settings nil + "An alist mapping setting keywords to functions.") (defmacro def-setting! (keyword arglist &optional docstring &rest forms) "Define a setting. Like `defmacro', this should return a form to be executed diff --git a/core/core-projects.el b/core/core-projects.el index 497b6c5b6..5ad28773b 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -13,7 +13,7 @@ :config (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) - (add-hook 'find-file-hook #'doom|autoload-project-mode) + (add-hook 'find-file-hook #'doom|init-project-mode) (projectile-mode +1) ;; a more generic project root file @@ -94,12 +94,12 @@ If NOCACHE, don't fetch a cached answer." (defalias 'doom-project-expand #'projectile-expand-root) -(defmacro doom-project-has! (files) +(defmacro project-file-exists-p! (files) "Checks if the project has the specified FILES. Paths are relative to the project root, unless they start with ./ or ../ (in which case they're relative to `default-directory'). If they start with a slash, they are absolute." - (doom--resolve-path-forms files (doom-project-root))) + (doom--resolve-path-forms files '(doom-project-root))) (defun doom-project-find-file (dir) "Fuzzy-find a file under DIR." @@ -131,7 +131,7 @@ for .dir-locals.el.") "Hook run when a project is enabled. The name of the project's mode and its state are passed in.") -(defun doom|autoload-project-mode () +(defun doom|init-project-mode () "Auto-enable the project(s) listed in `doom-project'." (when doom-project (if (symbolp doom-project) diff --git a/core/core-ui.el b/core/core-ui.el index bf0bb9439..7a4256f5c 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -24,6 +24,7 @@ Expects a `font-spec'.") return a string). This changes the 'long' name of a major-mode, allowing for shorter major mode name in the mode-line. See `doom|set-mode-name'.") +;; (defvar doom-init-ui-hook nil "List of hooks to run when the UI has been initialized.") @@ -57,7 +58,7 @@ with `doom//reload-theme').") compilation-scroll-output 'first-error confirm-nonexistent-file-or-buffer t cursor-in-non-selected-windows nil ; hide cursors in other windows - custom-theme-directory (concat doom-private-dir "themes/") + custom-theme-directory (expand-file-name "themes/" doom-private-dir) display-line-numbers-width 3 enable-recursive-minibuffers nil frame-inhibit-implied-resize t @@ -177,15 +178,11 @@ DEFAULT is non-nil, set the default mode-line for all buffers." ;; Plugins ;; -(def-package! ace-link - :commands (ace-link-help ace-link-org ace-link-addr ace-link-mu4e)) - -(def-package! avy - :commands (avy-goto-char-2 avy-goto-line) - :config - (setq avy-all-windows nil - avy-background t)) +;; `avy' +(setq avy-all-windows nil + avy-background t) +;; `all-the-icons' (def-package! all-the-icons :commands (all-the-icons-octicon all-the-icons-faicon all-the-icons-fileicon all-the-icons-wicon all-the-icons-material all-the-icons-alltheicon @@ -200,19 +197,41 @@ DEFAULT is non-nil, set the default mode-line for all buffers." all-the-icons-wicon all-the-icons-alltheicon)) (advice-add fn :around #'doom*disable-all-the-icons-in-tty))) -(def-package! hideshow ; built-in - :commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p) - :config (setq hs-hide-comments-when-hiding-all nil)) +;; `hide-mode-line-mode' +(add-hook 'completion-list-mode-hook #'hide-mode-line-mode) -(def-package! hide-mode-line - :commands hide-mode-line-mode - :init (add-hook 'completion-list-mode-hook #'hide-mode-line-mode)) +;; `rainbow-delimiters' Helps us distinguish stacked delimiter pairs. Especially +;; in parentheses-drunk languages like Lisp. +(def-package! rainbow-delimiters + :hook (lisp-mode . rainbow-delimiters-mode) + :config (setq rainbow-delimiters-max-face-count 3)) -(def-package! highlight-indentation - :commands (highlight-indentation-mode highlight-indentation-current-column-mode)) +;; `restart-emacs' +(setq restart-emacs--args (list "--restore")) -;; For modes with sub-par number fontification -(def-package! highlight-numbers :commands highlight-numbers-mode) +;; `visual-fill-column' For a distractions-free-like UI, that dynamically +;; resizes margins and can center a buffer. +(setq visual-fill-column-center-text t + visual-fill-column-width + ;; take Emacs 26 line numbers into account + (+ (if (boundp 'display-line-numbers) 6 0) + fill-column)) + + +;; +;; Built-in packages +;; + +;; `hideshow' +(setq hs-hide-comments-when-hiding-all nil) + +;; show typed keystrokes in minibuffer +(defun doom|enable-ui-keystrokes () (setq echo-keystrokes 0.02)) +(defun doom|disable-ui-keystrokes () (setq echo-keystrokes 0)) +(doom|enable-ui-keystrokes) +;; ...but hide them while isearch is active +(add-hook 'isearch-mode-hook #'doom|disable-ui-keystrokes) +(add-hook 'isearch-mode-end-hook #'doom|enable-ui-keystrokes) ;; Highlights the current line (def-package! hl-line ; built-in @@ -253,50 +272,15 @@ DEFAULT is non-nil, set the default mode-line for all buffers." (add-hook 'evil-visual-state-entry-hook #'doom|disable-hl-line) (add-hook 'evil-visual-state-exit-hook #'doom|enable-hl-line-maybe))) -;; Helps us distinguish stacked delimiter pairs. Especially in parentheses-drunk -;; languages like Lisp. -(def-package! rainbow-delimiters - :hook (lisp-mode . rainbow-delimiters-mode) - :config (setq rainbow-delimiters-max-face-count 3)) - -(def-package! restart-emacs - :commands restart-emacs - :config (setq restart-emacs--args (list "--restore"))) - -;; For a distractions-free-like UI, that dynamically resizes margins and can -;; center a buffer. -(def-package! visual-fill-column - :commands visual-fill-column-mode - :config - (setq-default - visual-fill-column-center-text t - visual-fill-column-width - ;; take Emacs 26 line numbers into account - (+ (if (boundp 'display-line-numbers) 6 0) - fill-column))) - - -;; -;; Built-in packages -;; - -;; show typed keystrokes in minibuffer -(defun doom|enable-ui-keystrokes () (setq echo-keystrokes 0.02)) -(defun doom|disable-ui-keystrokes () (setq echo-keystrokes 0)) -(doom|enable-ui-keystrokes) -;; ...but hide them while isearch is active -(add-hook 'isearch-mode-hook #'doom|disable-ui-keystrokes) -(add-hook 'isearch-mode-end-hook #'doom|enable-ui-keystrokes) - ;; undo/redo changes to Emacs' window layout (def-package! winner - :defer doom-before-switch-window-hook + :after-call doom-before-switch-window-hook :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself :config (winner-mode +1)) ;; highlight matching delimiters (def-package! paren - :defer doom-before-switch-buffer-hook + :after-call doom-before-switch-buffer-hook :config (setq show-paren-delay 0.1 show-paren-highlight-openparen t @@ -316,7 +300,7 @@ DEFAULT is non-nil, set the default mode-line for all buffers." (remove-hook 'kill-buffer-query-functions #'server-kill-buffer-query-function)) (add-hook 'server-visit-hook #'server-remove-kill-buffer-hook) -;; whitespace-mode settings +;; `whitespace-mode' (setq whitespace-line-column nil whitespace-style '(face indentation tabs tab-mark spaces space-mark newline newline-mark @@ -371,7 +355,7 @@ from the default." (advice-add #'switch-to-buffer :around #'doom*switch-buffer-hooks) (advice-add #'display-buffer :around #'doom*switch-buffer-hooks) (advice-add #'pop-to-buffer :around #'doom*switch-buffer-hooks)) -(add-hook 'doom-init-hook #'doom|init-custom-hooks) +(add-hook 'doom-post-init-hook #'doom|init-custom-hooks) (defun doom*load-theme-hooks (theme &rest _) (setq doom-theme theme) @@ -704,7 +688,7 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original ;; (run-hooks 'doom-init-ui-hook)) -(add-hook 'doom-init-hook #'doom|init-ui) +(add-hook 'doom-post-init-hook #'doom|init-ui) (provide 'core-ui) ;;; core-ui.el ends here diff --git a/core/core.el b/core/core.el index 20cb1c018..389004d16 100644 --- a/core/core.el +++ b/core/core.el @@ -1,5 +1,10 @@ ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- +(eval-when-compile + (when (version< emacs-version "25") + (error "Doom only supports Emacs 25.1 and higher!"))) + +;; (defvar doom-version "2.0.9" "Current version of DOOM emacs.") @@ -15,7 +20,7 @@ line or use --debug-init to enable this.") ;; (defvar doom-emacs-dir (eval-when-compile (file-truename user-emacs-directory)) - "The path to this emacs.d directory.") + "The path to this emacs.d directory. Must end in a slash.") (defvar doom-core-dir (concat doom-emacs-dir "core/") "Where essential files are stored.") @@ -44,14 +49,29 @@ Use this for files that change often, like cache files.") (defvar doom-private-dir (eval-when-compile - (or (let ((xdg-path (concat (or (getenv "XDG_CONFIG_HOME") - "~/.config") - "/doom/"))) + (or (let ((xdg-path + (expand-file-name "doom/" + (or (getenv "XDG_CONFIG_HOME") + "~/.config")))) (if (file-directory-p xdg-path) xdg-path)) "~/.doom.d/")) "Where your private customizations are placed. Must end in a slash. Respects XDG directory conventions if ~/.config/doom exists.") +;; Doom hooks +(defvar doom-pre-init-hook nil + "Hooks run after Doom is first initialized; after Doom's core files are +loaded, but before your private init.el file or anything else is loaded.") + +(defvar doom-init-hook nil + "Hooks run after all init.el files are loaded, including your private and all +module init.el files, but before their config.el files are loaded.") + +(defvar doom-post-init-hook nil + "A list of hooks run when Doom is fully initialized. Fires at the end of +`emacs-startup-hook', as late as possible. Guaranteed to run after everything +else (except for `window-setup-hook').") + ;;; ;; UTF-8 as the default coding system @@ -70,6 +90,13 @@ XDG directory conventions if ~/.config/doom exists.") debug-on-error doom-debug-mode ffap-machine-p-known 'reject ; don't ping things that look like domain names idle-update-delay 2 ; update ui less often + auto-mode-case-fold nil +;; be quiet at startup; don't load or display anything unnecessary + inhibit-startup-message t + inhibit-startup-echo-area-message user-login-name + inhibit-default-init t + initial-major-mode 'fundamental-mode + initial-scratch-message nil ;; keep the point out of the minibuffer minibuffer-prompt-properties '(read-only t point-entered minibuffer-avoid-prompt face minibuffer-prompt) ;; History & backup settings (save nothing, that's what git is for) @@ -93,20 +120,6 @@ XDG directory conventions if ~/.config/doom exists.") url-cache-directory (concat doom-cache-dir "url/") url-configuration-directory (concat doom-etc-dir "url/")) -;; be quiet at startup; don't load or display anything unnecessary -(unless noninteractive - (advice-add #'display-startup-echo-area-message :override #'ignore) - (setq inhibit-startup-message t - inhibit-startup-echo-area-message user-login-name - inhibit-default-init t - initial-major-mode 'fundamental-mode - initial-scratch-message nil)) - -;; Custom init hooks; clearer than `after-init-hook', `emacs-startup-hook', and -;; `window-setup-hook'. -(defvar doom-init-hook nil - "A list of hooks run when DOOM is initialized.") - ;; ;; Emacs fixes/hacks @@ -148,9 +161,12 @@ with functions that require it (like modeline segments)." buffer)) (advice-add #'make-indirect-buffer :around #'doom*set-indirect-buffer-filename) +;; Truly silence startup message +(fset #'display-startup-echo-area-message #'ignore) + ;; -;; Bootstrap +;; Optimize startup ;; (defvar doom--file-name-handler-alist file-name-handler-alist) @@ -167,20 +183,28 @@ with functions that require it (like modeline segments)." "Resets garbage collection settings to reasonable defaults (if you don't do this, you'll get stuttering and random freezes) and resets `file-name-handler-alist'." - (unless noninteractive - (run-hooks 'doom-init-hook)) (setq file-name-handler-alist doom--file-name-handler-alist - gc-cons-threshold 16777216 - gc-cons-percentage 0.15)) + gc-cons-threshold 8388608 + gc-cons-percentage 0.1)) + +(add-hook 'emacs-startup-hook #'doom|finalize) +(add-hook 'doom-reload-hook #'doom|finalize) + ;; -(require 'core-packages (concat doom-core-dir "core-packages")) -(doom-initialize noninteractive) +;; Bootstrap Doom +;; -(add-hook! '(emacs-startup-hook doom-reload-hook) - #'doom|finalize) -(when doom-private-dir - (load (concat doom-private-dir "init") t t)) +(add-to-list 'load-path doom-core-dir) + +(require 'core-lib) +(require 'core-packages) +(require 'core-os) + +(doom-initialize noninteractive) +(if noninteractive + (require 'core-dispatcher) + (doom-initialize-modules)) (provide 'core) ;;; core.el ends here diff --git a/core/packages.el b/core/packages.el index 7edaaa5ba..a6f962fde 100644 --- a/core/packages.el +++ b/core/packages.el @@ -36,3 +36,6 @@ ;; core-keybinds.el (package! which-key) (package! hydra) + +;; autoload/debug.el +(package! esup) diff --git a/core/templates/BUG_REPORT b/core/templates/BUG_REPORT new file mode 100644 index 000000000..a46909cce --- /dev/null +++ b/core/templates/BUG_REPORT @@ -0,0 +1,29 @@ +Please read through the following before you submit your issue. + ++ [ ] Running `make` (then restarting Emacs) did not fix my issue ++ [ ] If I have byte-compiled, I've tried recompiling with `make compile` ++ [ ] If I changed the version of Emacs installed, I've recompiled by plugins + with `make compile-elpa` ++ [ ] I ran `make doctor` and it produced no leads ++ [ ] My issue cannot be found [on the wiki](/docs/troubleshoot.org) ++ [ ] I filled out the four fields in the template below + +------------------------------------------------------------------- + +### Observed behavior + + + +### Expected behavior + + + +### Steps to reproduce + + + +### Extra details + + + +------------------------------------------------------------------- diff --git a/core/templates/QUICKSTART_INTRO b/core/templates/QUICKSTART_INTRO new file mode 100644 index 000000000..61a8af0ec --- /dev/null +++ b/core/templates/QUICKSTART_INTRO @@ -0,0 +1,30 @@ +Before you doom yourself, there are a few things you should know: + +1. If you use GUI Emacs, run `M-x all-the-icons-install-fonts` so you don't get + weird symbols all over the place. + +2. When you edit ~/.doom.d/init.el or modify modules, run: + + bin/doom refresh + + This will ensure all needed packages are installed, all orphaned packages are + removed, and your autoloads files are up to date. This is important! If you + forget to do this you will get errors! + +3. If something inexplicably goes wrong, it's a good idea to try: + + bin/doom doctor + + It will diagnose common issues with your environment and setup, and may give + you clues about what is wrong. + +4. To update doom, run + + bin/doom upgrade + + Doing it any other way will require you run `bin/doom refresh` otherwise, + +5. Check out `bin/doom help` to see what else it can do (it is also safe to add + ~/.emacs.d/bin to your PATH). + +Have fun! diff --git a/core/templates/VANILLA_SANDBOX b/core/templates/VANILLA_SANDBOX new file mode 100644 index 000000000..201ad51d1 --- /dev/null +++ b/core/templates/VANILLA_SANDBOX @@ -0,0 +1,7 @@ +;; Welcome to the vanilla sanbox! +;; +;; This is a test bed for Emacs Lisp to be run in a blank instance of Emacs +;; (free of Doom's clutches). This is equivalent to using emacs -Q with +;; package.el initialized and nothing else (so you have access to installed +;; plugins). + diff --git a/core/test/autoload-buffers.el b/core/test/autoload-buffers.el index 49f27c113..98242988a 100644 --- a/core/test/autoload-buffers.el +++ b/core/test/autoload-buffers.el @@ -7,17 +7,18 @@ (dolist (bsym buffer-args) (push `(,bsym (get-buffer-create ,(symbol-name bsym))) buffers)) - `(cl-flet ((buffer-list - (lambda () - (cl-remove-if-not #'buffer-live-p (list ,@(reverse (mapcar #'car buffers))))))) - (let* ((split-width-threshold 0) - (window-min-width 0) - persp-mode - ,@buffers) - (delete-other-windows) - ,@body - (let (kill-buffer-query-functions kill-buffer-hook) - (mapc #'kill-buffer (buffer-list))))))) + `(save-window-excursion + (cl-flet ((buffer-list + (lambda () + (cl-remove-if-not #'buffer-live-p (list ,@(reverse (mapcar #'car buffers))))))) + (let* ((split-width-threshold 0) + (window-min-width 0) + persp-mode + ,@buffers) + (delete-other-windows) + ,@body + (let (kill-buffer-query-functions kill-buffer-hook) + (mapc #'kill-buffer (buffer-list)))))))) ;; (def-test! get-buffers @@ -93,7 +94,7 @@ (with-temp-buffers!! (a b c d e) (dolist (buf (list a b)) (with-current-buffer buf - (emacs-lisp-mode))) + (delay-mode-hooks (emacs-lisp-mode)))) (dolist (buf (list c d e)) (with-current-buffer buf (text-mode))) diff --git a/core/test/autoload-debug.el b/core/test/autoload-help.el similarity index 94% rename from core/test/autoload-debug.el rename to core/test/autoload-help.el index 062a16de5..262f8fecb 100644 --- a/core/test/autoload-debug.el +++ b/core/test/autoload-help.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- -;;; core/test/autoload-debug.el +;;; core/test/autoload-help.el (def-test! what-face (insert (propertize "Hello " 'face 'font-lock-keyword-face)) diff --git a/core/test/autoload-message.el b/core/test/autoload-message.el index c3e456a2f..0cdae9f09 100644 --- a/core/test/autoload-message.el +++ b/core/test/autoload-message.el @@ -10,15 +10,15 @@ "Hello World")) (should (equal (format! (green "Hello %s" "World")) (format "\e[%dm%s\e[0m" - (cdr (assq 'green doom-message-fg)) + (cadr (assq 'green doom-message-fg)) "Hello World"))) (should (equal (format! (on-red "Hello %s" "World")) (format "\e[%dm%s\e[0m" - (cdr (assq 'on-red doom-message-bg)) + (cadr (assq 'on-red doom-message-bg)) "Hello World"))) (should (equal (format! (bold "Hello %s" "World")) (format "\e[%dm%s\e[0m" - (cdr (assq 'bold doom-message-fx)) + (cadr (assq 'bold doom-message-fx)) "Hello World"))))) (def-test! ansi-format-nested diff --git a/core/test/autoload-package.el b/core/test/autoload-package.el index 22103070f..cb5f8595e 100644 --- a/core/test/autoload-package.el +++ b/core/test/autoload-package.el @@ -1,6 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/autoload-package.el +(require 'package) +(require 'quelpa) + (defun -pkg (name version &optional reqs) (package-desc-create :name name :version version :reqs reqs)) diff --git a/core/test/core-lib.el b/core/test/core-lib.el index 2fff1fd6e..10a9d6ee3 100644 --- a/core/test/core-lib.el +++ b/core/test/core-lib.el @@ -7,8 +7,8 @@ (def-test! resolve-path-forms (should (equal (doom--resolve-path-forms '(and "fileA" "fileB")) - '(and (file-exists-p (expand-file-name "fileA" (doom-project-root))) - (file-exists-p (expand-file-name "fileB" (doom-project-root))))))) + '(and (file-exists-p (expand-file-name "fileA" nil)) + (file-exists-p (expand-file-name "fileB" nil)))))) ;; `doom--resolve-hook-forms' (def-test! resolve-hook-forms diff --git a/core/test/core-projects.el b/core/test/core-projects.el index d580b921e..47435061b 100644 --- a/core/test/core-projects.el +++ b/core/test/core-projects.el @@ -33,15 +33,15 @@ (should (equal (doom-project-expand "init.el") (expand-file-name "init.el" (doom-project-root)))))) -;; `doom-project-has!' +;; `project-file-exists-p!' (def-test! project-has! :minor-mode projectile-mode (let ((default-directory doom-core-dir)) ;; Resolve from project root - (should (doom-project-has! "init.el")) + (should (project-file-exists-p! "init.el")) ;; Chained file checks - (should (doom-project-has! (and "init.el" "LICENSE"))) - (should (doom-project-has! (or "init.el" "does-not-exist"))) - (should (doom-project-has! (and "init.el" (or "LICENSE" "does-not-exist")))) + (should (project-file-exists-p! (and "init.el" "LICENSE"))) + (should (project-file-exists-p! (or "init.el" "does-not-exist"))) + (should (project-file-exists-p! (and "init.el" (or "LICENSE" "does-not-exist")))) ;; Should resolve relative paths from `default-directory' - (should (doom-project-has! (and "./core.el" "../init.el"))))) + (should (project-file-exists-p! (and "core/core.el" "./init.el"))))) diff --git a/debug.el b/debug.el deleted file mode 100644 index 0aa3970a3..000000000 --- a/debug.el +++ /dev/null @@ -1,16 +0,0 @@ -;;; debug.el -*- lexical-binding: t; -*- - -;; To test something in a blank, vanilla Emacs session (Emacs -Q) load me: -;; -;; emacs -Q -l debug.el - -(setq user-emacs-directory (file-name-directory load-file-name) - package--init-file-ensured t - package-user-dir (expand-file-name ".local/packages/elpa" user-emacs-directory) - package-archives - '(("gnu" . "https://elpa.gnu.org/packages/") - ("melpa" . "https://melpa.org/packages/") - ("org" . "https://orgmode.org/elpa/"))) -(package-initialize) - -;; Then you can test packages in isolation here... diff --git a/early-init.el b/early-init.el index e86bca68b..e47330734 100644 --- a/early-init.el +++ b/early-init.el @@ -11,7 +11,8 @@ ;; Faster to disable these here (before they've been initialized) (setq tool-bar-mode nil - menu-bar-mode nil) -(set-scroll-bar-mode nil) + menu-bar-mode nil + scroll-bar-mode nil) +(modify-all-frames-parameters '((vertical-scroll-bars))) ;; TODO Once Emacs 27 hits stable, perhaps replace init.el with early-init.el diff --git a/init.el b/init.el index 50621daa5..eda0e8894 100644 --- a/init.el +++ b/init.el @@ -27,4 +27,7 @@ ;; ;;; License: MIT +(setq user-emacs-directory (file-name-directory load-file-name) + load-prefer-newer noninteractive) + (require 'core (concat user-emacs-directory "core/core")) diff --git a/init.example.el b/init.example.el index 64b6d2b27..55a4953fc 100644 --- a/init.example.el +++ b/init.example.el @@ -26,9 +26,6 @@ +childframe) ; uses childframes for popups (Emacs 26+ only) :ui - (popup ; tame sudden yet inevitable temporary windows - +all ; catch all popups that start with an asterix - +defaults) ; default popup rules doom ; what makes DOOM look the way it does doom-dashboard ; a nifty splash screen for Emacs doom-modeline ; a snazzy Atom-inspired mode-line @@ -37,6 +34,9 @@ hl-todo ; highlight TODO/FIXME/NOTE tags nav-flash ; blink the current line after jumping neotree ; a project drawer, like NERDTree for vim + (popup ; tame sudden yet inevitable temporary windows + +all ; catch all popups that start with an asterix + +defaults) ; default popup rules ;tabbar ; FIXME an (incomplete) tab bar for Emacs ;unicode ; extended unicode support for various languages vi-tilde-fringe ; fringe tildes to mark beyond EOB @@ -45,69 +45,67 @@ :tools dired ; making dired pretty [functional] editorconfig ; let someone else argue about tabs vs spaces - ein ; tame Jupyter notebooks with emacs electric-indent ; smarter, keyword-based electric-indent + ;ein ; tame Jupyter notebooks with emacs eshell ; a consistent, cross-platform shell (WIP) - gist ; interacting with github gists + ;gist ; interacting with github gists imenu ; an imenu sidebar and searchable code index ;macos ; MacOS-specific commands - make ; run make tasks from Emacs + ;make ; run make tasks from Emacs ;magit ; - password-store ; password manager for nerds + ;password-store ; password manager for nerds pdf ; pdf enhancements - prodigy ; Managing external services + ;prodigy ; FIXME managing external services & code builders ;rgb ; creating color strings rotate-text ; cycle region at point between text candidates - term ; terminals in Emacs + ;term ; terminals in Emacs tmux ; an API for interacting with tmux upload ; map local to remote projects via ssh/ftp :lang - assembly ; assembly for fun or debugging - cc ; C/C++/Obj-C madness - crystal ; ruby at the speed of c - clojure ; java with a lisp - csharp ; unity, .NET, and mono shenanigans + ;assembly ; assembly for fun or debugging + ;cc ; C/C++/Obj-C madness + ;crystal ; ruby at the speed of c + ;clojure ; java with a lisp + ;csharp ; unity, .NET, and mono shenanigans data ; config/data formats ;erlang ; an elegant language for a more civilized age - elixir ; erlang done right - elm ; care for a cup of TEA? + ;elixir ; erlang done right + ;elm ; care for a cup of TEA? emacs-lisp ; drown in parentheses - ess ; emacs speaks statistics - go ; the hipster dialect - (haskell +intero) ; a language that's lazier than I am - hy ; readability of scheme w/ speed of python - (java +meghanada) ; the poster child for carpal tunnel syndrome - javascript ; all(hope(abandon(ye(who(enter(here)))))) - julia ; a better, faster MATLAB - latex ; writing papers in Emacs has never been so fun - ledger ; an accounting system in Emacs - lua ; one-based indices? one-based indices + ;ess ; emacs speaks statistics + ;go ; the hipster dialect + ;(haskell +intero) ; a language that's lazier than I am + ;hy ; readability of scheme w/ speed of python + ;(java +meghanada) ; the poster child for carpal tunnel syndrome + ;javascript ; all(hope(abandon(ye(who(enter(here)))))) + ;julia ; a better, faster MATLAB + ;latex ; writing papers in Emacs has never been so fun + ;ledger ; an accounting system in Emacs + ;lua ; one-based indices? one-based indices markdown ; writing docs for people to ignore - nim ; python + lisp at the speed of c - nix ; I hereby declare "nix geht mehr!" - ocaml ; an objective camel + ;nim ; python + lisp at the speed of c + ;nix ; I hereby declare "nix geht mehr!" + ;ocaml ; an objective camel (org ; organize your plain life in plain text +attach ; custom attachment system +babel ; running code in org +capture ; org-capture in and outside of Emacs +export ; Exporting org to whatever you want - +present ; Emacs for presentations - +publish) ; Emacs+Org as a static site generator - perl ; write code no one else can comprehend - php ; perl's insecure younger brother - plantuml ; diagrams for confusing people more - purescript ; javascript, but functional - python ; beautiful is better than ugly - rest ; Emacs as a REST client - ruby ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} - rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() - scala ; java, but good + +present) ; Emacs for presentations + ;perl ; write code no one else can comprehend + ;php ; perl's insecure younger brother + ;plantuml ; diagrams for confusing people more + ;purescript ; javascript, but functional + ;python ; beautiful is better than ugly + ;rest ; Emacs as a REST client + ;ruby ; 1.step do {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} + ;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() + ;scala ; java, but good sh ; she sells (ba|z)sh shells on the C xor - solidity ; do you need a blockchain? No. - swift ; who asked for emoji variables? - typescript ; javascript, but better - web ; the tubes + ;solidity ; do you need a blockchain? No. + ;swift ; who asked for emoji variables? + ;web ; the tubes ;; Applications are complex and opinionated modules that transform Emacs ;; toward a specific purpose. They may have additional dependencies and diff --git a/modules/collab/impatient-mode/autoload.el b/modules/collab/impatient-mode/autoload.el index 3222b5477..41047d359 100644 --- a/modules/collab/impatient-mode/autoload.el +++ b/modules/collab/impatient-mode/autoload.el @@ -2,9 +2,8 @@ ;;;###autoload (defun +impatient-mode/toggle () - "TODO" + "Toggle `impatient-mode' in the current buffer." (interactive) - (require 'simple-httpd) (unless (process-status "httpd") (httpd-start)) (impatient-mode) diff --git a/modules/collab/impatient-mode/config.el b/modules/collab/impatient-mode/config.el deleted file mode 100644 index 0087bee52..000000000 --- a/modules/collab/impatient-mode/config.el +++ /dev/null @@ -1,6 +0,0 @@ -;;; collab/impatient-mode/config.el -*- lexical-binding: t; -*- - -;; Show off code as you write it - -(def-package! impatient-mode - :commands impatient-mode) diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el index 12a1f352b..ec6113713 100644 --- a/modules/completion/company/config.el +++ b/modules/completion/company/config.el @@ -24,8 +24,7 @@ MODES should be one major-mode symbol or a list of them." ;; (def-package! company - :commands (company-mode global-company-mode company-complete - company-complete-common company-manual-begin company-grab-line) + :commands (company-complete-common company-manual-begin company-grab-line) :init (setq company-idle-delay nil company-tooltip-limit 14 @@ -48,7 +47,8 @@ MODES should be one major-mode symbol or a list of them." (def-package! company :when (featurep! +auto) - :defer pre-command-hook + :defer 2 + :after-call pre-command-hook :config (setq company-idle-delay 0.2)) @@ -73,27 +73,12 @@ MODES should be one major-mode symbol or a list of them." (def-package! company-dict - :commands company-dict + :defer t :config (defun +company|enable-project-dicts (mode &rest _) "Enable per-project dictionaries." (if (symbol-value mode) - (cl-pushnew mode company-dict-minor-mode-list :test #'eq) + (add-to-list 'company-dict-minor-mode-list mode #'eq) (setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list)))) (add-hook 'doom-project-hook #'+company|enable-project-dicts)) - -;; -;; Included with company.el -;; - -(autoload 'company-capf "company-capf") -(autoload 'company-dabbrev "company-dabbrev") -(autoload 'company-dabbrev-code "company-dabbrev-code") -(autoload 'company-elisp "company-elisp") -(autoload 'company-etags "company-etags") -(autoload 'company-files "company-files") -(autoload 'company-gtags "company-gtags") -(autoload 'company-ispell "company-ispell") -(autoload 'company-yasnippet "company-yasnippet") - diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index b4e6c5282..7caa3e8a4 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -11,7 +11,24 @@ ;; (def-package! helm-mode - :defer (pre-command-hook . 1) + :defer 1 + :after-call pre-command-hook + :init + (map! :map global-map + [remap apropos] #'helm-apropos + [remap bookmark-jump] #'helm-bookmarks + [remap bookmark-jump] #'helm-bookmarks + [remap execute-extended-command] #'helm-M-x + [remap find-file] #'helm-find-files + [remap imenu-anywhere] #'helm-imenu-anywhere + [remap imenu-anywhere] #'helm-imenu-anywhere + [remap imenu] #'helm-semantic-or-imenu + [remap noop-show-kill-ring] #'helm-show-kill-ring + [remap projectile-find-file] #'helm-projectile-find-file + [remap projectile-recentf] #'helm-projectile-recentf + [remap projectile-switch-project] #'helm-projectile-switch-project + [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer + [remap recentf-open-files] #'helm-recentf) :config (helm-mode +1) ;; helm is too heavy for find-file-at-point @@ -40,7 +57,6 @@ helm-move-to-line-cycle-in-source t) :config - (load "helm-autoloads" nil t) (setq projectile-completion-system 'helm) (defvar helm-projectile-find-file-map (make-sparse-keymap)) @@ -72,20 +88,7 @@ (setq-local cursor-type nil)))) (add-hook 'helm-minibuffer-set-up-hook #'+helm*hide-minibuffer-maybe) - (map! :map global-map - [remap apropos] #'helm-apropos - [remap find-file] #'helm-find-files - [remap recentf-open-files] #'helm-recentf - [remap projectile-switch-to-buffer] #'helm-projectile-switch-to-buffer - [remap projectile-recentf] #'helm-projectile-recentf - [remap projectile-find-file] #'helm-projectile-find-file - [remap imenu] #'helm-semantic-or-imenu - [remap bookmark-jump] #'helm-bookmarks - [remap noop-show-kill-ring] #'helm-show-kill-ring - [remap projectile-switch-project] #'helm-projectile-switch-project - [remap projectile-find-file] #'helm-projectile-find-file - [remap imenu-anywhere] #'helm-imenu-anywhere - [remap execute-extended-command] #'helm-M-x)) + ) (def-package! helm-locate @@ -94,40 +97,28 @@ :config (set-keymap-parent helm-generic-files-map helm-map)) -(def-package! helm-bookmark - :commands helm-bookmark - :config (setq-default helm-bookmark-show-location t)) +(after! helm-bookmark + (setq-default helm-bookmark-show-location t)) -(def-package! helm-files - :defer t - :config +(after! helm-files (setq helm-boring-file-regexp-list (append (list "\\.projects$" "\\.DS_Store$") helm-boring-file-regexp-list))) -(def-package! helm-ag - :defer t - :config - (map! :map helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort)) +;; `helm-ag' +(map! :after helm-ag + :map helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) -(def-package! helm-css-scss ; https://github.com/ShingoFukuyama/helm-css-scss - :commands (helm-css-scss - helm-css-scss-multi - helm-css-scss-insert-close-comment) - :config +(after! helm-css-scss ; https://github.com/ShingoFukuyama/helm-css-scss (setq helm-css-scss-split-direction #'split-window-vertically helm-css-scss-split-with-multiple-windows t)) -(def-package! helm-for-files - :commands (helm-for-files helm-recentf helm-multi-files)) - - (def-package! helm-swoop ; https://github.com/ShingoFukuyama/helm-swoop - :commands (helm-swoop helm-multi-swoop helm-multi-swoop-all) + :commands helm-multi-swoop-all :config (setq helm-swoop-use-line-number-face t helm-swoop-candidate-number-limit 200 @@ -135,9 +126,6 @@ helm-swoop-pre-input-function (lambda () ""))) -(def-package! helm-describe-modes :commands helm-describe-modes) - - (def-package! wgrep - :commands (wgrep-setup wgrep-change-to-wgrep-mode) + :commands wgrep-change-to-wgrep-mode :config (setq wgrep-auto-save-buffer t)) diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index eef638f21..1ef711277 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -116,9 +116,9 @@ If ARG (universal argument), open selection in other-window." "\\):?\\s-*\\(.+\\)") x) (error - (message! (red "Error matching task in file: (%s) %s" - (error-message-string ex) - (car (split-string x ":")))) + (print! (red "Error matching task in file: (%s) %s" + (error-message-string ex) + (car (split-string x ":")))) nil)) collect `((type . ,(match-string 3 x)) (desc . ,(match-string 4 x)) @@ -202,7 +202,6 @@ search current file. See `+ivy-task-tags' to customize what this searches for." ;; File searching ;; -(defvar +ivy--file-last-search nil) (defvar +ivy--file-search-recursion-p t) (defvar +ivy--file-search-all-files-p nil) @@ -220,8 +219,7 @@ search current file. See `+ivy-task-tags' to customize what this searches for." (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (end (or (bound-and-true-p evil-visual-end) (region-end)))) (when (> (abs (- end beg)) 1) - (rxt-quote-pcre (buffer-substring-no-properties beg end))))) - +ivy--file-last-search)) + (rxt-quote-pcre (buffer-substring-no-properties beg end))))))) (prompt (format "%s%%s %s" (symbol-name engine) @@ -232,7 +230,6 @@ search current file. See `+ivy-task-tags' to customize what this searches for." (t (file-relative-name directory project-root))))) (default-directory directory)) - (setq +ivy--file-last-search query) (require 'counsel) (cl-letf (((symbol-function 'counsel-ag-function) (symbol-function '+ivy*counsel-ag-function)) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 60a201d32..a37ea4baf 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -24,9 +24,10 @@ immediately runs it on the current candidate (ending the ivy session)." ;; (def-package! ivy - :defer (pre-command-hook . 1) + :defer 1 + :after-call pre-command-hook :config - (setq ivy-height 12 + (setq ivy-height 15 ivy-do-completion-in-region nil ivy-wrap t ivy-fixed-height-minibuffer t @@ -141,7 +142,7 @@ immediately runs it on the current candidate (ending the ivy session)." "C-o" #'+ivy@coo/body "M-o" #'ivy-dispatching-done-hydra) :config - (def-hydra! +ivy@coo (:hint nil :color pink) + (defhydra +ivy@coo (:hint nil :color pink) " Move ^^^^^^^^^^ | Call ^^^^ | Cancel^^ | Options^^ | Action _w_/_s_/_a_: %s(ivy-action-name) ----------^^^^^^^^^^-+--------------^^^^-+-------^^-+--------^^-+--------------------------------- @@ -184,7 +185,7 @@ immediately runs it on the current candidate (ending the ivy session)." (def-package! wgrep - :commands (wgrep-setup wgrep-change-to-wgrep-mode) + :commands wgrep-change-to-wgrep-mode :config (setq wgrep-auto-save-buffer t)) diff --git a/modules/config/default/+bindings.el b/modules/config/default/+bindings.el index a72aa6e2c..b4033b14a 100644 --- a/modules/config/default/+bindings.el +++ b/modules/config/default/+bindings.el @@ -197,8 +197,10 @@ :desc "Magit status" :n "g" #'magit-status :desc "List gists" :n "G" #'+gist:list :desc "Initialize repo" :n "i" #'magit-init + :desc "Browse issues tracker" :n "I" #'+vcs/git-browse-issues :desc "Magit buffer log" :n "l" #'magit-log-buffer-file :desc "List repositories" :n "L" #'magit-list-repositories + :desc "Browse remote" :n "o" #'+vcs/git-browse :desc "Magit push popup" :n "p" #'magit-push-popup :desc "Magit pull popup" :n "P" #'magit-pull-popup :desc "Git revert hunk" :n "r" #'git-gutter:revert-hunk @@ -213,9 +215,11 @@ (:desc "help" :prefix "h" :n "h" help-map :desc "Apropos" :n "a" #'apropos + :desc "Open Bug Report" :n "b" #'doom/open-bug-report :desc "Describe char" :n "c" #'describe-char :desc "Describe DOOM module" :n "d" #'doom/describe-module - :desc "Open Doom manual" :n "D" #'doom/help + :desc "Open Doom manual" :n "D" #'doom//open-manual + :desc "Open vanilla sandbox" :n "E" #'doom/open-vanilla-sandbox :desc "Describe function" :n "f" #'describe-function :desc "Describe face" :n "F" #'describe-face :desc "Info" :n "i" #'info-lookup-symbol @@ -233,7 +237,7 @@ :desc "Print Doom version" :n "V" #'doom/version :desc "Describe at point" :n "." #'helpful-at-point :desc "What face" :n "'" #'doom/what-face - :desc "What minor modes" :n ";" #'doom/what-minor-mode) + :desc "What minor modes" :n ";" #'doom/describe-active-minor-mode) (:desc "insert" :prefix "i" :desc "From kill-ring" :nv "y" #'counsel-yank-pop @@ -251,7 +255,7 @@ :desc "REPL" :n "r" #'+eval/open-repl :v "r" #'+eval:repl :desc "Neotree" :n "n" #'+neotree/open - :desc "Neotree: on this file" :n "N" #'+neotree/find-this-file + :desc "Neotree: find file" :n "N" #'+neotree/find-this-file :desc "Imenu sidebar" :nv "i" #'imenu-list-smart-toggle :desc "Terminal" :n "t" #'+term/open-popup-in-project @@ -295,11 +299,13 @@ :desc "Browse remote files" :n "." #'ssh-deploy-browse-remote-handler :desc "Detect remote changes" :n ">" #'ssh-deploy-remote-changes-handler)) - (:desc "snippets" :prefix "s" - :desc "New snippet" :n "n" #'yas-new-snippet - :desc "Insert snippet" :nv "i" #'yas-insert-snippet - :desc "Find snippet for mode" :n "s" #'yas-visit-snippet-file - :desc "Find snippet" :n "S" #'+default/find-in-snippets) + (:when (featurep! :feature snippets) + (:desc "snippets" :prefix "s" + :desc "New snippet" :n "n" #'yas-new-snippet + :desc "Insert snippet" :nv "i" #'yas-insert-snippet + :desc "Find snippet" :n "s" #'+default/find-in-snippets + :desc "Find snippet for mode" :n "S" #'+default/browse-snippets + :desc "Find global snippet" :n "/" #'yas-visit-snippet-file)) (:desc "toggle" :prefix "t" :desc "Flyspell" :n "s" #'flyspell-mode @@ -387,11 +393,12 @@ [escape] #'company-search-abort)) ;; counsel - (:after counsel - (:map counsel-ag-map - [backtab] #'+ivy/wgrep-occur ; search/replace on results - "C-SPC" #'ivy-call-and-recenter ; preview - "M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action))) + (:when (featurep! :completion ivy) + (:after counsel + (:map counsel-ag-map + [backtab] #'+ivy/wgrep-occur ; search/replace on results + "C-SPC" #'ivy-call-and-recenter ; preview + "M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action)))) ;; easymotion :m "gs" #'+default/easymotion ; lazy-load `evil-easymotion' @@ -766,14 +773,16 @@ ;; Evil-collection fixes ;; -(when (featurep 'evil-collection) - (defun +config|deal-with-evil-collections-bs (_feature keymaps) - "Unmap keys that conflict with Doom's defaults." - (dolist (map keymaps) - (evil-define-key '(normal visual motion) map - doom-leader-key nil - "C-j" nil "C-k" nil - "gd" nil "gf" nil - "K" nil - "]" nil "[" nil))) - (add-hook 'evil-collection-setup-hook #'+config|deal-with-evil-collections-bs)) +(defun +config|deal-with-evil-collections-bs (_feature keymaps) + "Unmap keys that conflict with Doom's defaults." + (dolist (map keymaps) + (evil-delay `(and (boundp ',map) (keymapp ,map)) + `(evil-define-key* '(normal visual motion) ,map + (kbd doom-leader-key) nil + (kbd "C-j") nil (kbd "C-k") nil + "gd" nil "gf" nil "K" nil + "]" nil "[" nil) + 'after-load-functions t nil + (format "+default-redefine-key-in-%s" map)))) + +(add-hook 'evil-collection-setup-hook #'+config|deal-with-evil-collections-bs) diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el index 6526c029e..738ea0a7b 100644 --- a/modules/config/default/autoload/default.el +++ b/modules/config/default/autoload/default.el @@ -36,8 +36,10 @@ ;;;###autoload (defun +default/browse-snippets () - (interactive) (doom-project-browse emacs-snippets-dir)) -;; NOTE No need for a browse-snippets variant, use `yas-visit-snippet-file' + (interactive) (doom-project-browse +snippets-dir)) +;;;###autoload +(defun +default/find-in-snippets () + (interactive) (doom-project-find-file +snippets-dir)) ;;;###autoload (defun +default/find-in-config () diff --git a/modules/config/default/config.el b/modules/config/default/config.el index 6d5ba386b..12bd8c5ec 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -35,6 +35,15 @@ (sp-pair "'" nil :unless unless-list) (sp-pair "\"" nil :unless unless-list)) + ;; Major-mode specific fixes + (sp-local-pair 'ruby-mode "{" "}" + :pre-handlers '(:rem sp-ruby-prehandler) + :post-handlers '(:rem sp-ruby-posthandler)) + ;; sp's default rules for these modes are obnoxious, so disable them + (provide 'smartparens-latex) + (provide 'smartparens-elixir) + (provide 'smartparens-lua) + ;; Expand {|} => { | } ;; Expand {|} => { ;; | diff --git a/modules/feature/debugger/config.el b/modules/feature/debugger/config.el index 1b7ef587d..78660ffa8 100644 --- a/modules/feature/debugger/config.el +++ b/modules/feature/debugger/config.el @@ -3,7 +3,8 @@ (def-package! realgud :commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb) :config - (set! :popup "^\\*\\(?trepanjs:\\(?:g\\|zsh\\|bash\\)db\\)" '((size . 20))) + (set! :popup "^\\*\\(?trepanjs:\\(?:g\\|zsh\\|bash\\)db\\)" + '((size . 20))) ;; TODO Temporary Ex commands for the debugger ;; (def-tmp-excmd! doom:def-debug-on doom:def-debug-off diff --git a/modules/feature/eval/config.el b/modules/feature/eval/config.el index 72b086d15..1549eacb8 100644 --- a/modules/feature/eval/config.el +++ b/modules/feature/eval/config.el @@ -10,12 +10,7 @@ ;; (def-package! quickrun - :commands (quickrun - quickrun-region - quickrun-with-arg - quickrun-shell - quickrun-compile-only - quickrun-replace-region) + :defer t :init (unless (boundp 'display-line-numbers) (add-hook 'quickrun--mode-hook #'nlinum-mode)) diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index f1c41106d..a04891154 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -1,6 +1,21 @@ ;; feature/evil/autoload/evil.el -*- lexical-binding: t; -*- ;;;###if (featurep! :feature evil) +;;;###autoload +(def-setting! :evil-state (modes state) + "Set the initialize STATE of MODE using `evil-set-initial-state'." + (let ((unquoted-modes (doom-unquote modes))) + (if (listp unquoted-modes) + `(progn + ,@(cl-loop for mode in unquoted-modes + collect `(evil-set-initial-state ',mode ,state))) + `(evil-set-initial-state ,modes ,state)))) + + +;; +;; Commands +;; + ;;;###autoload (defun +evil/visual-indent () "vnoremap < " evil-args-closers))) -(def-package! evil-indent-plus - :commands (evil-indent-plus-i-indent - evil-indent-plus-a-indent - evil-indent-plus-i-indent-up - evil-indent-plus-a-indent-up - evil-indent-plus-i-indent-up-down - evil-indent-plus-a-indent-up-down)) - - -(def-package! evil-textobj-anyblock - :commands (evil-textobj-anyblock-inner-block evil-textobj-anyblock-a-block)) - - ;; ;; Multiple cursors compatibility (for the plugins that use it) ;; diff --git a/modules/feature/file-templates/autoload.el b/modules/feature/file-templates/autoload.el index 2566434c0..348c14915 100644 --- a/modules/feature/file-templates/autoload.el +++ b/modules/feature/file-templates/autoload.el @@ -66,8 +66,12 @@ evil is loaded and enabled)." ;;;###autoload (defun +file-templates-get-short-path () "Fetches a short file path for the header in Doom module templates." - (when (string-match "/modules/\\(.+\\)$" buffer-file-truename) - (match-string 1 buffer-file-truename))) + (let ((path (file-truename (or buffer-file-name default-directory)))) + (cond ((string-match "/modules/\\(.+\\)$" path) + (match-string 1 path)) + ((file-in-directory-p path doom-emacs-dir) + (file-relative-name path doom-emacs-dir)) + ((abbreviate-file-name path))))) ;; diff --git a/modules/feature/file-templates/config.el b/modules/feature/file-templates/config.el index 511bca956..8a9963c3d 100644 --- a/modules/feature/file-templates/config.el +++ b/modules/feature/file-templates/config.el @@ -6,142 +6,137 @@ (expand-file-name "templates/" (file-name-directory load-file-name)) "The path to a directory of yasnippet folders to use for file templates.") -(defvar +file-templates-alist () - "An alist of file template rules. The CAR of each rule is either a major mode -symbol or regexp string. The CDR is a plist. See `doom--set:file-template' for -more information.") - (defvar +file-templates-default-trigger "__" "The default yasnippet trigger key (a string) for file template rules that don't have a :trigger property in `+file-templates-alist'.") +(defvar +file-templates-alist + `(;; General + (gitignore-mode) + (dockerfile-mode) + ("/docker-compose\\.yml$" :mode yaml-mode) + ("/Makefile$" :mode makefile-gmake-mode) + ;; elisp + ("/.dir-locals.el$") + ("/packages\\.el$" :when +file-templates-in-emacs-dirs-p + :trigger "__doom-packages" + :mode emacs-lisp-mode) + ("/doctor\\.el$" :when +file-templates-in-emacs-dirs-p + :trigger "__doom-doctor" + :mode emacs-lisp-mode) + ("/test/.+\\.el$" :when +file-templates-in-emacs-dirs-p + :trigger "__doom-test" + :mode emacs-lisp-mode) + ("\\.el$" :when +file-templates-in-emacs-dirs-p + :trigger "__doom-module" + :mode emacs-lisp-mode) + ("-test\\.el$" :mode emacs-ert-mode) + (emacs-lisp-mode :trigger "__initfile") + (snippet-mode) + ;; C/C++ + ("/main\\.c\\(?:c\\|pp\\)$" :trigger "__main.cpp" :mode c++-mode) + ("/win32_\\.c\\(?:c\\|pp\\)$" :trigger "__winmain.cpp" :mode c++-mode) + ("\\.c\\(?:c\\|pp\\)$" :trigger "__cpp" :mode c++-mode) + ("\\.h\\(?:h\\|pp\\|xx\\)$" :trigger "__hpp" :mode c++-mode) + ("\\.h$" :trigger "__h" :mode c-mode) + (c-mode :trigger "__c" :mode c-mode) + ;; go + ("/main\\.go$" :trigger "__main.go" :mode go-mode :project t) + (go-mode :trigger "__.go") + ;; web-mode + ("/normalize\\.scss$" :trigger "__normalize.scss" :mode scss-mode) + ("/master\\.scss$" :trigger "__master.scss" :mode scss-mode) + ("\\.html$" :trigger "__.html" :mode web-mode) + (scss-mode) + ;; java + ("/main\\.java$" :trigger "__main" :mode java-mode) + ("/build\\.gradle$" :trigger "__build.gradle" :mode android-mode) + ("/src/.+\\.java$" :mode java-mode) + ;; javascript + ("/package\\.json$" :trigger "__package.json" :mode json-mode) + ("/bower\\.json$" :trigger "__bower.json" :mode json-mode) + ("/gulpfile\\.js$" :trigger "__gulpfile.js" :mode js-mode) + ("/webpack\\.config\\.js$" :trigger "__webpack.config.js" :mode js-mode) + ("\\.js\\(?:on\\|hintrc\\)$" :mode json-mode) + ;; Lua + ("/main\\.lua$" :trigger "__main.lua" :mode love-mode) + ("/conf\\.lua$" :trigger "__conf.lua" :mode love-mode) + ;; Markdown + (markdown-mode) + ;; Org + ("\\.org$" :trigger "__" :mode org-mode) + ("/README\\.org$" + :when +file-templates-in-emacs-dirs-p + :trigger "__doom-readme" + :mode org-mode) + ;; PHP + ("\\.class\\.php$" :trigger "__.class.php" :mode php-mode) + (php-mode) + ;; Python + ;; TODO ("tests?/test_.+\\.py$" :trigger "__" :mode nose-mode) + ;; TODO ("/setup\\.py$" :trigger "__setup.py" :mode python-mode) + (python-mode) + ;; Ruby + ("/lib/.+\\.rb$" :trigger "__module" :mode ruby-mode :project t) + ("/spec_helper\\.rb$" :trigger "__helper" :mode rspec-mode :project t) + ("_spec\\.rb$" :mode rspec-mode :project t) + ("/\\.rspec$" :trigger "__.rspec" :mode rspec-mode :project t) + ("\\.gemspec$" :trigger "__.gemspec" :mode ruby-mode :project t) + ("/Gemfile$" :trigger "__Gemfile" :mode ruby-mode :project t) + ("/Rakefile$" :trigger "__Rakefile" :mode ruby-mode :project t) + (ruby-mode) + ;; Rust + ("/Cargo.toml$" :trigger "__Cargo.toml" :mode rust-mode) + ("/main\\.rs$" :trigger "__main.rs" :mode rust-mode) + ;; Slim + ("/\\(?:index\\|main\\)\\.slim$" :mode slim-mode) + ;; Shell scripts + ("\\.zunit$" :trigger "__zunit" :mode sh-mode) + (fish-mode) + (sh-mode)) + "An alist of file template rules. The CAR of each rule is either a major mode +symbol or regexp string. The CDR is a plist. See `doom--set:file-template' for +more information.") + ;; -;; Bootstrap +;; Library ;; -(after! yasnippet - (add-to-list 'yas-snippet-dirs '+file-templates-dir 'append #'eq)) - (defun +file-template-p (rule) "Return t if RULE applies to the current buffer." (let ((pred (car rule)) (plist (cdr rule))) - (and (cond ((stringp pred) (string-match-p pred)) + (and (cond ((and (stringp pred) buffer-file-name) (string-match-p pred buffer-file-name)) ((symbolp pred) (eq major-mode pred))) (or (not (plist-member plist :when)) (funcall (plist-get plist :when) buffer-file-name)) rule))) -(defun +file-templates|init () - "Check if the current buffer is a candidate for file template expansion. It -must be non-read-only, empty, and there must be a rule in -`+file-templates-alist' that applies to it." - (when (and (not buffer-read-only) - (bobp) (eobp)) - (when-let* ((rule (cl-find-if #'+file-template-p +file-templates-alist))) - (apply #'+file-templates--expand rule)))) - -(add-hook 'find-file-hook #'+file-templates|init) - - -;; -;; File templates -;; - (defun +file-templates-in-emacs-dirs-p (file) "Returns t if FILE is in Doom or your private directory." (or (file-in-directory-p file doom-private-dir) (file-in-directory-p file doom-emacs-dir))) -(setq +file-templates-alist - `(;; General - (gitignore-mode) - (dockerfile-mode) - ("/docker-compose\\.yml$" :mode yaml-mode) - ("/Makefile$" :mode makefile-gmake-mode) - ;; elisp - ("/.dir-locals.el$") - ("/packages\\.el$" :when +file-templates-in-emacs-dirs-p - :trigger "__doom-packages" - :mode emacs-lisp-mode) - ("/doctor\\.el$" :when +file-templates-in-emacs-dirs-p - :trigger "__doom-doctor" - :mode emacs-lisp-mode) - ("/test/.+\\.el$" :when +file-templates-in-emacs-dirs-p - :trigger "__doom-test" - :mode emacs-lisp-mode) - ("\\.el$" :when +file-templates-in-emacs-dirs-p - :trigger "__doom-module" - :mode emacs-lisp-mode) - ("-test\\.el$" :mode emacs-ert-mode) - (emacs-lisp-mode :trigger "__initfile") - (snippet-mode) - ;; C/C++ - ("/main\\.c\\(?:c\\|pp\\)$" :trigger "__main.cpp" :mode c++-mode) - ("/win32_\\.c\\(?:c\\|pp\\)$" :trigger "__winmain.cpp" :mode c++-mode) - ("\\.c\\(?:c\\|pp\\)$" :trigger "__cpp" :mode c++-mode) - ("\\.h\\(?:h\\|pp\\|xx\\)$" :trigger "__hpp" :mode c++-mode) - ("\\.h$" :trigger "__h" :mode c-mode) - (c-mode :trigger "__c" :mode c-mode) - ;; go - ("/main\\.go$" :trigger "__main.go" :mode go-mode :project t) - (go-mode :trigger "__.go") - ;; web-mode - ("/normalize\\.scss$" :trigger "__normalize.scss" :mode scss-mode) - ("/master\\.scss$" :trigger "__master.scss" :mode scss-mode) - ("\\.html$" :trigger "__.html" :mode web-mode) - (scss-mode) - ;; java - ("/main\\.java$" :trigger "__main" :mode java-mode) - ("/build\\.gradle$" :trigger "__build.gradle" :mode android-mode) - ("/src/.+\\.java$" :mode java-mode) - ;; javascript - ("/package\\.json$" :trigger "__package.json" :mode json-mode) - ("/bower\\.json$" :trigger "__bower.json" :mode json-mode) - ("/gulpfile\\.js$" :trigger "__gulpfile.js" :mode js-mode) - ("/webpack\\.config\\.js$" :trigger "__webpack.config.js" :mode js-mode) - ("\\.js\\(?:on\\|hintrc\\)$" :mode json-mode) - ;; Lua - ("/main\\.lua$" :trigger "__main.lua" :mode love-mode) - ("/conf\\.lua$" :trigger "__conf.lua" :mode love-mode) - ;; Markdown - (markdown-mode) - ;; Org - ("\\.org$" :trigger "__" :mode org-mode) - ("/README\\.org$" - :when +file-templates-in-emacs-dirs-p - :trigger "__doom-readme" - :mode org-mode) - ;; PHP - ("\\.class\\.php$" :trigger "__.class.php" :mode php-mode) - (php-mode) - ;; Python - ;; TODO ("tests?/test_.+\\.py$" :trigger "__" :mode nose-mode) - ;; TODO ("/setup\\.py$" :trigger "__setup.py" :mode python-mode) - (python-mode) - ;; Ruby - ("/lib/.+\\.rb$" :trigger "__module" :mode ruby-mode :project t) - ("/spec_helper\\.rb$" :trigger "__helper" :mode rspec-mode :project t) - ("_spec\\.rb$" :mode rspec-mode :project t) - ("/\\.rspec$" :trigger "__.rspec" :mode rspec-mode :project t) - ("\\.gemspec$" :trigger "__.gemspec" :mode ruby-mode :project t) - ("/Gemfile$" :trigger "__Gemfile" :mode ruby-mode :project t) - ("/Rakefile$" :trigger "__Rakefile" :mode ruby-mode :project t) - (ruby-mode) - ;; Rust - ("/Cargo.toml$" :trigger "__Cargo.toml" :mode rust-mode) - ("/main\\.rs$" :trigger "__main.rs" :mode rust-mode) - ;; Slim - ("/\\(?:index\\|main\\)\\.slim$" :mode slim-mode) - ;; Shell scripts - ("\\.zunit$" :trigger "__zunit" :mode sh-mode) - (fish-mode) - (sh-mode) - )) +(defun +file-templates|check () + "Check if the current buffer is a candidate for file template expansion. It +must be non-read-only, empty, and there must be a rule in +`+file-templates-alist' that applies to it." + (when (and (not buffer-read-only) + (bobp) (eobp) + (not (string-match-p "^ *\\*" (buffer-name)))) + (when-let* ((rule (cl-find-if #'+file-template-p +file-templates-alist))) + (apply #'+file-templates--expand rule)))) ;; -;; Plugins +;; Bootstrap ;; +(defun +file-templates|init () + (after! yasnippet + (add-to-list 'yas-snippet-dirs '+file-templates-dir 'append #'eq)) + (add-hook 'find-file-hook #'+file-templates|check)) + +(add-hook 'doom-post-init-hook #'+file-templates|init) + diff --git a/modules/feature/file-templates/templates/org-mode/__doom-readme b/modules/feature/file-templates/templates/org-mode/__doom-readme index 901c25faf..8a6f23a5d 100644 --- a/modules/feature/file-templates/templates/org-mode/__doom-readme +++ b/modules/feature/file-templates/templates/org-mode/__doom-readme @@ -11,10 +11,9 @@ ${2:A short summary about what this module does.} ${3:If necessary, include a longer description below it that goes into more detail. This may be as long as you like. -+ If possible, include a list of features -+ Include links to major plugins that the module uses, if applicable -+ Use links whenever you can -+ Mention dependencies on other modules here} ++ If possible, include a brief list of feature highlights here ++ Like code completion, syntax checking or available snippets ++ Include links to packages & external things where possible * Table of Contents :TOC: @@ -25,11 +24,15 @@ This module provides no flags. This module has no prereqisites. * Features -A list of features, how to use them, and their dependencies. +An in-depth list of features, how to use them, and their dependencies. * Configuration +How to configure this module, including common problems and how to address them. * Appendix ** Commands ++ A list or table of public commands (and their keybinds) and functions that this module exposes. ++ A brief description of how to use them ** Hacks ++ Include a list of ways this module changes default behavior $0 \ No newline at end of file diff --git a/modules/feature/lookup/autoload/devdocs.el b/modules/feature/lookup/autoload/devdocs.el new file mode 100644 index 000000000..2b2c4a514 --- /dev/null +++ b/modules/feature/lookup/autoload/devdocs.el @@ -0,0 +1,11 @@ +;;; feature/lookup/autoload/devdocs.el -*- lexical-binding: t; -*- +;;;###if (featurep! +devdocs) + +;;;###autoload +(def-setting! :devdocs (modes docset) + "Map major MODES (one major-mode symbol or a list of them) to a devdocs +DOCSET (a string). + +See `devdocs-alist' for the defaults. " + `(dolist (mode ',modes) + (push (cons mode ,docset) devdocs-alist))) diff --git a/modules/feature/lookup/autoload/docsets.el b/modules/feature/lookup/autoload/docsets.el new file mode 100644 index 000000000..89fef163c --- /dev/null +++ b/modules/feature/lookup/autoload/docsets.el @@ -0,0 +1,40 @@ +;;; feature/lookup/autoload/docsets.el -*- lexical-binding: t; -*- +;;;###if (featurep! +docsets) + +;;;###autoload +(def-setting! :docset (modes &rest docsets) + "Registers a list of DOCSETS (strings) for MODES (either one major mode +symbol or a list of them). + +If MODES is a minor mode, you can use :add or :remove as the first element of +DOCSETS, to instruct it to append (or remove) those from the docsets already set +by a major-mode, if any. + +Used by `+lookup/in-docsets' and `+lookup/documentation'." + (let* ((modes (doom-unquote modes)) + (ivy-p (featurep! :completion ivy)) + (hook-sym (intern (format "+lookup|%s-docsets--%s" + (cond ((eq ',(car docsets) :add) 'add) + ((eq ',(car docsets) :remove) 'remove) + ('set)) + (string-join docsets "-")))) + (var-sym (if ivy-p 'counsel-dash-docsets 'helm-dash-docsets))) + `(progn + (defun ,hook-sym () + (make-variable-buffer-local ',var-sym) + ,(cond ((eq ',(car docsets) :add) + `(setq ,var-sym (append ,var-sym (list ,@(cdr docsets))))) + ((eq ',(car docsets) :remove) + `(setq ,var-sym + (cl-loop with to-delete = (list ,@(cdr docsets)) + for docset in ,var-sym + unless (member docset to-delete) + collect docset))) + (`(setq ,var-sym (list ,@docsets))))) + (add-hook! ,modes #',hook-sym)))) + +;;;###autoload +(autoload 'helm-dash-installed-docsets "helm-dash") + +;;;###autoload +(autoload 'helm-dash-docset-installed-p "helm-dash") diff --git a/modules/feature/lookup/autoload/lookup.el b/modules/feature/lookup/autoload/lookup.el index 56203adf5..167e12811 100644 --- a/modules/feature/lookup/autoload/lookup.el +++ b/modules/feature/lookup/autoload/lookup.el @@ -160,6 +160,7 @@ Goes down a list of possible backends: identifier (+lookup--online-provider (not current-prefix-arg)))))) +(defvar ffap-file-finder) ;;;###autoload (defun +lookup/file (path) "Figure out PATH from whatever is at point and open it. diff --git a/modules/feature/lookup/config.el b/modules/feature/lookup/config.el index b693f44bc..bbabb689a 100644 --- a/modules/feature/lookup/config.el +++ b/modules/feature/lookup/config.el @@ -122,33 +122,29 @@ ones." ;; (def-package! dumb-jump - :commands (dumb-jump-go dumb-jump-quick-look - dumb-jump-back dumb-jump-result-follow) + :commands dumb-jump-result-follow :config (setq dumb-jump-default-project doom-emacs-dir dumb-jump-aggressive nil dumb-jump-selector (cond ((featurep! :completion ivy) 'ivy) ((featurep! :completion helm) 'helm) - (t 'popup)))) + ('popup)))) ;; ;; xref ;; -(def-package! xref - :commands (xref-backend-identifier-at-point xref-find-definitions xref-find-references) - :config - ;; By default, `etags--xref-backend' is the default xref backend. No need. - ;; We'll set these up ourselves in other modules. - (setq-default xref-backend-functions '(t)) +;; By default, `etags--xref-backend' is the default xref backend. No need. We'll +;; set these up ourselves in other modules. +(setq-default xref-backend-functions '(t)) - ;; ...however, it breaks `projectile-find-tag', unless we put it back. - (defun +lookup*projectile-find-tag (orig-fn) - (let ((xref-backend-functions '(etags--xref-backend t))) - (funcall orig-fn))) - (advice-add #'projectile-find-tag :around #'+lookup*projectile-find-tag)) +;; ...however, it breaks `projectile-find-tag', unless we put it back. +(defun +lookup*projectile-find-tag (orig-fn) + (let ((xref-backend-functions '(etags--xref-backend t))) + (funcall orig-fn))) +(advice-add #'projectile-find-tag :around #'+lookup*projectile-find-tag) (def-package! ivy-xref @@ -168,41 +164,9 @@ ones." ;; (when (featurep! +docsets) - (def-setting! :docset (modes &rest docsets) - "Registers a list of DOCSETS (strings) for MODES (either one major mode -symbol or a list of them). - -If MODES is a minor mode, you can use :add or :remove as the first element of -DOCSETS, to instruct it to append (or remove) those from the docsets already set -by a major-mode, if any. - -Used by `+lookup/in-docsets' and `+lookup/documentation'." - (let* ((modes (doom-unquote modes)) - (ivy-p (featurep! :completion ivy)) - (hook-sym (intern (format "+lookup|%s-docsets--%s" - (cond ((eq ',(car docsets) :add) 'add) - ((eq ',(car docsets) :remove) 'remove) - ('set)) - (string-join docsets "-")))) - (var-sym (if ivy-p 'counsel-dash-docsets 'helm-dash-docsets))) - `(progn - (defun ,hook-sym () - (make-variable-buffer-local ',var-sym) - ,(cond ((eq ',(car docsets) :add) - `(setq ,var-sym (append ,var-sym (list ,@(cdr docsets))))) - ((eq ',(car docsets) :remove) - `(setq ,var-sym - (cl-loop with to-delete = (list ,@(cdr docsets)) - for docset in ,var-sym - unless (member docset to-delete) - collect docset))) - (`(setq ,var-sym (list ,@docsets))))) - (add-hook! ,modes #',hook-sym)))) - ;; Both packages depend on helm-dash (def-package! helm-dash - :commands (helm-dash helm-dash-install-docset helm-dash-at-point - helm-dash-docset-installed-p helm-dash-installed-docsets) + :defer t :init (setq helm-dash-enable-debugging doom-debug-mode helm-dash-browser-func #'eww) @@ -214,7 +178,7 @@ Used by `+lookup/in-docsets' and `+lookup/documentation'." (def-package! counsel-dash :when (featurep! :completion ivy) - :commands (counsel-dash counsel-dash-install-docset) + :commands counsel-dash-install-docset :config (setq counsel-dash-min-length 2))) @@ -223,20 +187,11 @@ Used by `+lookup/in-docsets' and `+lookup/documentation'." ;; (when (featurep! +devdocs) - (def-setting! :devdocs (modes docset) - "Map major MODES (one major-mode symbol or a list of them) to a devdocs -DOCSET (a string). - -See `devdocs-alist' for the defaults. " - `(dolist (mode ',modes) - (push (cons mode ,docset) devdocs-alist))) - - (def-package! devdocs-lookup - :commands (devdocs-setup devdocs-lookup) - :config - (setq devdocs-subjects - (append '(("SCSS" "scss") - ("GFM" "markdown") - ("Typescript" "typescript")) - devdocs-subjects)))) + (after! devdocs-lookup + (unless (assoc "SCSS" devdocs-subjects) + (setq devdocs-subjects + (append '(("SCSS" "scss") + ("GFM" "markdown") + ("Typescript" "typescript")) + devdocs-subjects))))) diff --git a/modules/feature/snippets/config.el b/modules/feature/snippets/config.el index a9acfadd6..33757e31e 100644 --- a/modules/feature/snippets/config.el +++ b/modules/feature/snippets/config.el @@ -1,9 +1,16 @@ ;;; feature/snippets/config.el -*- lexical-binding: t; -*- +(defvar +snippets-dir (expand-file-name "snippets/" doom-private-dir) + "Directory where `yasnippet' will search for your private snippets.") + + +;; +;; Plugins +;; + (def-package! yasnippet - :commands (yas-minor-mode yas-minor-mode-on yas-expand yas-expand-snippet - yas-lookup-snippet yas-insert-snippet yas-new-snippet - yas-visit-snippet-file snippet-mode) + :commands (yas-minor-mode-on yas-expand yas-expand-snippet yas-lookup-snippet + yas-insert-snippet yas-new-snippet yas-visit-snippet-file) :preface (defvar yas-minor-mode-map (let ((map (make-sparse-keymap))) @@ -23,28 +30,25 @@ (setq yas-verbosity (if doom-debug-mode 3 0) yas-also-auto-indent-first-line t yas-prompt-functions (delq #'yas-dropdown-prompt yas-prompt-functions) - ;; Allow nested snippets - yas-triggers-in-field t) + yas-triggers-in-field t) ; Allow nested snippets - (cl-pushnew (expand-file-name "snippets/" doom-private-dir) yas-snippet-dirs - :test #'string=) + (add-to-list 'yas-snippet-dirs '+snippets-dir nil #'eq) (defun +snippets|enable-project-modes (mode &rest _) - "Enable snippets for project modes." + "Automatically enable snippet libraries for project minor modes defined with +`def-project-mode!'." (if (symbol-value mode) (yas-activate-extra-mode mode) (yas-deactivate-extra-mode mode))) (add-hook 'doom-project-hook #'+snippets|enable-project-modes) - ;; fix an error caused by smartparens interfering with yasnippet bindings - (advice-add #'yas-expand :before #'sp-remove-active-pair-overlay) - ;; Exit snippets on ESC from normal mode - (add-hook 'doom-escape-hook #'yas-abort-snippet)) + (add-hook 'doom-escape-hook #'yas-abort-snippet) + + (after! smartparens + ;; fix an error caused by smartparens interfering with yasnippet bindings + (advice-add #'yas-expand :before #'sp-remove-active-pair-overlay))) -(def-package! auto-yasnippet - :commands (aya-create aya-expand aya-open-line aya-persist-snippet) - :config +(after! auto-yasnippet (setq aya-persist-snippets-dir (concat doom-local-dir "auto-snippets/"))) - diff --git a/modules/feature/snippets/doctor.el b/modules/feature/snippets/doctor.el deleted file mode 100644 index 8ba004a36..000000000 --- a/modules/feature/snippets/doctor.el +++ /dev/null @@ -1,7 +0,0 @@ -;; -*- lexical-binding: t; no-byte-compile: t; -*- -;;; feature/snippets/doctor.el - -(require 'yasnippet) -(unless (ignore-errors (yas-reload-all) - (yas--get-snippet-tables)) - (warn! "Couldn't find any snippets in any of these directories: %s" yas-snippet-dirs)) diff --git a/modules/feature/spellcheck/config.el b/modules/feature/spellcheck/config.el index c1f8b3bb6..2cc8dadb6 100644 --- a/modules/feature/spellcheck/config.el +++ b/modules/feature/spellcheck/config.el @@ -8,7 +8,7 @@ Since spellchecking can be slow in some buffers, this can be disabled with: (setq-hook! 'LaTeX-mode-hook +spellcheck-immediately nil)") (def-package! flyspell ; built-in - :commands flyspell-mode + :defer t :init (add-hook 'flyspell-mode-hook #'+spellcheck|immediately) :config diff --git a/modules/feature/spellcheck/packages.el b/modules/feature/spellcheck/packages.el index bb01a00ba..97172953f 100644 --- a/modules/feature/spellcheck/packages.el +++ b/modules/feature/spellcheck/packages.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; feature/spellcheck/packages.el -(package! flyspell-correct) -(cond ((featurep! :completion ivy) - (package! flyspell-correct-ivy)) - ((featurep! :completion helm) - (package! flyspell-correct-helm)) - (t - (package! flyspell-correct-popup))) +(when (package! flyspell-correct) + (cond ((featurep! :completion ivy) + (package! flyspell-correct-ivy)) + ((featurep! :completion helm) + (package! flyspell-correct-helm)) + (t + (package! flyspell-correct-popup)))) diff --git a/modules/feature/syntax-checker/config.el b/modules/feature/syntax-checker/config.el index 29048618e..9bdc0b5fa 100644 --- a/modules/feature/syntax-checker/config.el +++ b/modules/feature/syntax-checker/config.el @@ -1,18 +1,11 @@ ;;; feature/syntax-checker/config.el -*- lexical-binding: t; -*- -;; Since Doom doesn't use `package-initialize', pkg-info won't get autoloaded -;; when `flycheck-version' needs it, so we need this: -(autoload 'pkg-info-version-info "pkg-info") - (def-package! flycheck - :commands (flycheck-mode flycheck-list-errors flycheck-buffer) + :commands (flycheck-list-errors flycheck-buffer) :config ;; Emacs feels snappier without checks on newline (setq flycheck-check-syntax-automatically '(save idle-change mode-enabled)) - ;; Popup - (add-hook 'flycheck-mode-hook #'+syntax-checker-popup-mode) - (after! evil (defun +syntax-checkers|flycheck-buffer () "Flycheck buffer on ESC in normal mode." @@ -20,21 +13,16 @@ (ignore-errors (flycheck-buffer)) nil)) (add-hook 'doom-escape-hook #'+syntax-checkers|flycheck-buffer t) - (add-hook 'evil-insert-state-exit-hook #'+syntax-checkers|flycheck-buffer) - - ;; With the option of flychecking the buffer on escape or leaving insert - ;; mode, we don't need auto-flychecking on idle-change (which can feel slow, - ;; esp on computers without SSDs). - (delq 'idle-change flycheck-check-syntax-automatically))) + (add-hook 'evil-insert-state-exit-hook #'+syntax-checkers|flycheck-buffer))) (def-package! flycheck-popup-tip - :commands (flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup)) + :commands (flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup) + :init (add-hook 'flycheck-mode-hook #'+syntax-checker-popup-mode)) (def-package! flycheck-posframe - :when EMACS26+ - :when (featurep! +childframe) + :when (and EMACS26+ (featurep! +childframe)) :commands flycheck-posframe-show-posframe :config (setq flycheck-posframe-warning-prefix "⚠ " diff --git a/modules/feature/version-control/+git.el b/modules/feature/version-control/+git.el index cbb110f5f..8488f9076 100644 --- a/modules/feature/version-control/+git.el +++ b/modules/feature/version-control/+git.el @@ -1,10 +1,5 @@ ;;; feature/version-control/+git.el -*- lexical-binding: t; -*- -;; These don't need `def-package!' blocks because they've already been set up by -;; `doom-initialize'. -(autoload 'gitconfig-mode "gitconfig-mode" nil t) -(autoload 'gitignore-mode "gitignore-mode" nil t) - (when (featurep! :feature evil) (add-hook 'git-commit-mode-hook #'evil-insert-state)) @@ -33,7 +28,7 @@ (ignore (git-gutter)))) (add-hook 'doom-escape-hook #'+version-control|update-git-gutter t)) - (def-hydra! +version-control@git-gutter + (defhydra +version-control@git-gutter (:body-pre (git-gutter-mode 1) :hint nil) " ╭─────────────────┐ @@ -59,7 +54,7 @@ (def-package! git-timemachine - :commands (git-timemachine git-timemachine-toggle) + :defer t :config ;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of ;; showing revision details in the minibuffer, show them in @@ -67,10 +62,6 @@ (setq git-timemachine-show-minibuffer-details t) (advice-add #'git-timemachine--show-minibuffer-details :override #'+vcs*update-header-line) - ;; Force evil to rehash keybindings for the current state - (add-hook 'git-timemachine-mode-hook #'evil-force-normal-state)) - - -(def-package! git-link - :commands (git-link git-link-commit git-link-homepage)) - + (after! evil + ;; Force evil to rehash keybindings for the current state + (add-hook 'git-timemachine-mode-hook #'evil-force-normal-state))) diff --git a/modules/feature/version-control/config.el b/modules/feature/version-control/config.el index 4a2a9791d..d49a806b1 100644 --- a/modules/feature/version-control/config.el +++ b/modules/feature/version-control/config.el @@ -28,11 +28,11 @@ (defalias #'smerge-diff-upper-lower #'smerge-diff-mine-other) (defalias #'smerge-diff-base-lower #'smerge-diff-base-other))) - (def-hydra! +hydra-smerge (:hint nil - :pre (smerge-mode 1) - ;; Disable `smerge-mode' when quitting hydra if - ;; no merge conflicts remain. - :post (smerge-auto-leave)) + (defhydra +hydra-smerge (:hint nil + :pre (smerge-mode 1) + ;; Disable `smerge-mode' when quitting hydra if + ;; no merge conflicts remain. + :post (smerge-auto-leave)) " ╭────────┐ Movement Keep Diff Other │ smerge │ diff --git a/modules/feature/workspaces/autoload/workspaces.el b/modules/feature/workspaces/autoload/workspaces.el index b014d5588..90b2d5b3c 100644 --- a/modules/feature/workspaces/autoload/workspaces.el +++ b/modules/feature/workspaces/autoload/workspaces.el @@ -211,7 +211,7 @@ current workspace (by name) from session files." (completing-read "Workspace to load: " (persp-list-persp-names-in-file - (expand-file-name +workspace-data-file persp-save-dir)))))) + (expand-file-name +workspaces-data-file persp-save-dir)))))) (if (not (+workspace-load name)) (+workspace-error (format "Couldn't load workspace %s" name)) (+workspace/switch-to name) diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index fd8df7a29..bc1467bcf 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -27,7 +27,7 @@ new project directory.") stored in `persp-save-dir'.") (defun +workspaces-restore-last-session (&rest _) - (add-hook 'emacs-startup-hook #'+workspace/load-session 'append)) + (add-hook 'doom-post-init-hook #'+workspace/load-session 'append)) (map-put command-switch-alist '"--restore" #'+workspaces-restore-last-session) @@ -77,7 +77,7 @@ Uses `+workspaces-main' to determine the name of the main workspace." (display-buffer-in-side-window warnings '((window-height . shrink-window-if-larger-than-buffer)))))))))) - (add-hook 'doom-init-hook #'+workspaces|init t) + (add-hook 'doom-post-init-hook #'+workspaces|init t) :config (setq persp-autokill-buffer-on-remove 'kill-weak persp-nil-hidden t diff --git a/modules/feature/workspaces/test/autoload-workspaces.el b/modules/feature/workspaces/test/autoload-workspaces.el index e7cc2e9bd..acd647ba9 100644 --- a/modules/feature/workspaces/test/autoload-workspaces.el +++ b/modules/feature/workspaces/test/autoload-workspaces.el @@ -2,6 +2,7 @@ ;;; feature/workspaces/test/autoload-workspaces.el (require! :feature workspaces) +(doom|init-custom-hooks) (defmacro with-workspace!! (buffer-args &rest body) (declare (indent defun)) @@ -13,7 +14,8 @@ (require 'persp-mode) (let (noninteractive) (persp-mode +1)) - (+workspace-switch +workspaces-main t) + (let (persp-before-switch-functions persp-activated-functions) + (+workspace-switch +workspaces-main t)) (let* (,@buffers) (cl-loop with persp = (get-current-persp) for buf in (list ,@(mapcar #'car buffers)) diff --git a/modules/lang/assembly/autoload.el b/modules/lang/assembly/autoload.el new file mode 100644 index 000000000..38b582a66 --- /dev/null +++ b/modules/lang/assembly/autoload.el @@ -0,0 +1,4 @@ +;;; lang/assembly/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(map-put auto-mode-alist "\\.hax\\'" 'haxor-mode) diff --git a/modules/lang/assembly/config.el b/modules/lang/assembly/config.el deleted file mode 100644 index 98b879b24..000000000 --- a/modules/lang/assembly/config.el +++ /dev/null @@ -1,8 +0,0 @@ -;;; lang/assembly/config.el -*- lexical-binding: t; -*- - -(def-package! mips-mode :mode "\\.mips$") - -(def-package! haxor-mode :mode "\\.hax$") - -(def-package! nasm-mode :commands nasm-mode) - diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index d14f7c28e..f063cf11c 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -6,7 +6,7 @@ (interactive) (unless (memq major-mode '(c-mode c++-mode objc-mode)) (user-error "Not a C/C++/ObjC buffer")) - (unless (doom-project-has! "compile_commands.json") + (unless (project-file-exists-p! "compile_commands.json") (user-error "No compile_commands.json file")) ;; first rtag (when (and (featurep 'rtags) @@ -89,13 +89,6 @@ compilation dbs." nconc (list "-I" path))) (doom-project-root))))) -;;;###autoload -(defun +cc|init-rtags () - "Start an rtags server in c-mode and c++-mode buffers." - (when (and (memq major-mode '(c-mode c++-mode)) - (rtags-executable-find "rtags")) - (rtags-start-process-unless-running))) - ;;;###autoload (defun +cc|cleanup-rtags () "Kill rtags server(s) if there are no C/C++ buffers open." diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index 0077083c1..cb02a6558 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -55,10 +55,10 @@ compilation database is present in the project.") :config (set! :electric '(c-mode c++-mode objc-mode java-mode) - :chars '(?\n ?\})) + :chars '(?\n ?\})) (set! :company-backend - '(c-mode c++-mode objc-mode) - '(company-irony-c-headers company-irony)) + '(c-mode c++-mode objc-mode) + '(company-irony-c-headers company-irony)) ;;; Style/formatting ;; C/C++ style settings @@ -101,8 +101,8 @@ compilation database is present in the project.") (label . 0)))) ;;; Keybindings - ;; Completely disable electric keys because it interferes with smartparens and - ;; custom bindings. We'll do this ourselves. + ;; Disable electric keys because it interferes with smartparens and custom + ;; bindings. We'll do it ourselves (mostly). (setq c-tab-always-indent nil c-electric-flag nil) (dolist (key '("#" "}" "/" "*" ";" "," ":" "(" ")" "\177")) @@ -129,6 +129,7 @@ compilation database is present in the project.") (def-package! irony + :when (featurep! +irony) :commands (irony-install-server irony-mode) :preface (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) @@ -172,23 +173,13 @@ compilation database is present in the project.") ;; (def-package! cmake-mode - :mode "/CMakeLists\\.txt$" - :mode "\\.cmake\\$" + :defer t :config (set! :company-backend 'cmake-mode '(company-cmake company-yasnippet))) -(def-package! cuda-mode :mode "\\.cuh?$") +(def-package! opencl-mode :mode "\\.cl\\'") -(def-package! opencl-mode :mode "\\.cl$") - -(def-package! demangle-mode - :hook llvm-mode) - -(def-package! glsl-mode - :mode "\\.glsl$" - :mode "\\.vert$" - :mode "\\.frag$" - :mode "\\.geom$") +(def-package! demangle-mode :hook llvm-mode) ;; @@ -213,7 +204,12 @@ compilation database is present in the project.") (def-package! rtags :commands rtags-executable-find :init - (add-hook! (c-mode c++-mode) #'+cc|init-rtags) + (defun +cc|init-rtags () + "Start an rtags server in c-mode and c++-mode buffers." + (when (and (memq major-mode '(c-mode c++-mode)) + (rtags-executable-find "rtags")) + (rtags-start-process-unless-running))) + (add-hook 'c-mode-common-hook #'+cc|init-rtags) :config (setq rtags-autostart-diagnostics t rtags-use-bookmarks nil diff --git a/modules/lang/cc/doctor.el b/modules/lang/cc/doctor.el index 442798ce4..5f9799ecf 100644 --- a/modules/lang/cc/doctor.el +++ b/modules/lang/cc/doctor.el @@ -7,9 +7,9 @@ (warn! "Couldn't find the rtag client and/or server programs %s. Disabling rtags support" bins))) ;; irony server -(require 'irony) -(unless (file-directory-p irony-server-install-prefix) - (warn! "Irony server isn't installed. Run M-x irony-install-server")) +(when (require 'irony nil t) + (unless (file-directory-p irony-server-install-prefix) + (warn! "Irony server isn't installed. Run M-x irony-install-server"))) (when (featurep! :completion company) ;; glslangValidator diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index 53a18eded..e78813ef5 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -1,13 +1,7 @@ ;;; lang/clojure/config.el -*- lexical-binding: t; -*- -(def-package! clojure-mode - :mode "\\.clj$" - :mode "\\.edn$" - :mode "\\(?:build\\|profile\\)\\.boot$" - :mode ("\\.cljs$" . clojurescript-mode) - :mode ("\\.cljc$" . clojurec-mode) - :config - (add-hook 'clojure-mode #'rainbow-delimiters-mode)) +;; `clojure-mode' +(add-hook 'clojure-mode #'rainbow-delimiters-mode) (def-package! clj-refactor diff --git a/modules/lang/crystal/config.el b/modules/lang/crystal/config.el index f1b765e72..717a4137c 100644 --- a/modules/lang/crystal/config.el +++ b/modules/lang/crystal/config.el @@ -1,8 +1,7 @@ ;;; lang/crystal/config.el -*- lexical-binding: t; -*- (def-package! crystal-mode - :mode "\\.cr$" - :interpreter "crystal" + :defer t :config (set! :lookup 'crystal-mode :definition #'crystal-def-jump @@ -19,5 +18,4 @@ :config (add-hook 'crystal-mode-hook #'flycheck-mode)) -(def-package! inf-crystal - :commands (inf-crystal crystal-switch-to-inf)) +(def-package! inf-crystal :commands crystal-switch-to-inf) diff --git a/modules/lang/csharp/config.el b/modules/lang/csharp/config.el index 306f93b12..c7ecabe28 100644 --- a/modules/lang/csharp/config.el +++ b/modules/lang/csharp/config.el @@ -1,17 +1,16 @@ ;;; lang/csharp/config.el -*- lexical-binding: t; -*- -(def-package! csharp-mode :mode "\\.cs$") - -(def-package! shader-mode :mode "\\.shader$") ; unity shaders +(add-to-list 'auto-mode-alist '("\\.shader$" . shader-mode)) ; unity shaders (def-package! omnisharp - :after csharp-mode + :hook (csharp-mode . omnisharp-mode) + :commands omnisharp-install-server :preface (setq omnisharp-auto-complete-want-documentation nil omnisharp-cache-directory (concat doom-cache-dir "omnisharp")) :config - (add-hook! csharp-mode #'(flycheck-mode omnisharp-mode)) + (add-hook 'csharp-mode-hook #'flycheck-mode) (defun +csharp|cleanup-omnisharp-server () "Clean up the omnisharp server once you kill the last csharp-mode buffer." diff --git a/modules/lang/data/config.el b/modules/lang/data/config.el index 60b1af9b2..80134d84d 100644 --- a/modules/lang/data/config.el +++ b/modules/lang/data/config.el @@ -1,47 +1,41 @@ ;;; lang/data/config.el -*- lexical-binding: t; -*- -(push '("/sxhkdrc" . conf-mode) auto-mode-alist) +;; Built in plugins +(dolist (spec '(("/sxhkdrc\\'" . conf-mode) + ("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode) + ("\\.plist\\'" . nxml-mode))) + (map-put auto-mode-alist (car spec) (cdr spec))) + +(set! :company-backend 'nxml-mode '(company-nxml company-yasnippet)) -(def-package! dockerfile-mode - :mode "/Dockerfile$") +;; +;; Third-party plugins +;; +;; `csv-mode' +(map! :after csv-mode + :map csv-mode-map + (:localleader + :desc "Align fields" :nvm "a" #'csv-align-fields + :desc "Unalign fields" :nvm "u" #'csv-unalign-fields + :desc "Sort fields" :nvm "s" #'csv-sort-fields + :desc "Sort fields (n)" :nvm "S" #'csv-sort-numeric-fields + :desc "Kill fields" :nvm "k" #'csv-kill-fields + :desc "Transpose fields" :nvm "t" #'csv-transpose)) (def-package! graphql-mode - :mode "\\.g\\(?:raph\\)?ql$") - - -(def-package! hexl ; For ROM hacking or debugging - :mode ("\\.hex$" . hexl-mode) - :mode ("\\.nes$" . hexl-mode)) - + :mode "\\.gql\\'") (def-package! json-mode - :mode "\\.js\\(?:on\\|[hl]int\\(rc\\)?\\)$" + :mode "\\.js\\(?:on\\|[hl]int\\(rc\\)?\\)\\'" :config (when (featurep! :feature syntax-checker) (add-hook 'json-mode-hook #'flycheck-mode)) (set! :electric 'json-mode :chars '(?\n ?: ?{ ?}))) - -(def-package! nxml-mode - :mode "\\.plist$" - :config - (set! :company-backend 'nxml-mode '(company-nxml company-yasnippet))) - - -(def-package! toml-mode - :mode "\\.toml$") - - (def-package! vimrc-mode - :mode "/\\.?g?vimrc$" - :mode "\\.vimp?$" - :mode "\\.?vimperatorrc$") - - -(def-package! yaml-mode - :mode "\\.ya?ml$") + :mode "\\.?vimperatorrc\\'") ;; diff --git a/modules/lang/data/packages.el b/modules/lang/data/packages.el index fb60ce156..a34a56266 100644 --- a/modules/lang/data/packages.el +++ b/modules/lang/data/packages.el @@ -7,4 +7,4 @@ (package! toml-mode) (package! vimrc-mode) (package! yaml-mode) - +(package! csv-mode) diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index 590bb545a..6afd25471 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -1,21 +1,18 @@ ;;; lang/elixir/config.el -*- lexical-binding: t; -*- (def-package! elixir-mode - :mode "\\.exs?\\'" - :mode "\\.elixir2\\'" - :init - ;; sp's default elixir rules are obnoxious, so disable them - (provide 'smartparens-elixir) + :defer t :config ;; ...and only complete the basics - (sp-with-modes 'elixir-mode - (sp-local-pair "do" "end" - :when '(("RET" "")) - :unless '(sp-in-comment-p sp-in-string-p) - :skip-match 'sp-elixir-skip-def-p - :post-handlers '("||\n[i]")) - (sp-local-pair "do " " end" :unless '(sp-in-comment-p sp-in-string-p)) - (sp-local-pair "fn " " end" :unless '(sp-in-comment-p sp-in-string-p)))) + (after! smartparens + (sp-with-modes 'elixir-mode + (sp-local-pair "do" "end" + :when '(("RET" "")) + :unless '(sp-in-comment-p sp-in-string-p) + :skip-match 'sp-elixir-skip-def-p + :post-handlers '("||\n[i]")) + (sp-local-pair "do " " end" :unless '(sp-in-comment-p sp-in-string-p)) + (sp-local-pair "fn " " end" :unless '(sp-in-comment-p sp-in-string-p))))) (def-package! alchemist @@ -30,13 +27,13 @@ (def-package! alchemist-company :when (featurep! :completion company) - :after elixir-mode + :commands alchemist-company + :init + (set! :company-backend 'elixir-mode '(alchemist-company company-yasnippet)) :config ;; Alchemist doesn't use hook symbols to add these backends, so we have to use ;; the entire closure to get rid of it. (let ((fn (byte-compile (lambda () (add-to-list (make-local-variable 'company-backends) 'alchemist-company))))) (remove-hook 'alchemist-mode-hook fn) - (remove-hook 'alchemist-iex-mode-hook fn)) - - (set! :company-backend 'elixir-mode '(alchemist-company company-yasnippet))) + (remove-hook 'alchemist-iex-mode-hook fn))) diff --git a/modules/lang/elm/config.el b/modules/lang/elm/config.el index 6bc5f9c00..6cf23914d 100644 --- a/modules/lang/elm/config.el +++ b/modules/lang/elm/config.el @@ -1,13 +1,12 @@ ;;; lang/elm/config.el -*- lexical-binding: t; -*- -(def-package! elm-mode - :mode "\\.elm$" - :config - (load "elm-mode-autoloads" nil t) - (add-hook! 'elm-mode-hook #'(flycheck-mode rainbow-delimiters-mode)) - (set! :company-backend 'elm-mode '(company-elm)) - (set! :repl 'elm-mode #'run-elm-interactive) - (setq elm-format-on-save t)) +;; `elm-mode' +(setq elm-format-on-save t) + +(add-hook! 'elm-mode-hook #'(flycheck-mode rainbow-delimiters-mode)) + +(set! :company-backend 'elm-mode 'company-elm) +(set! :repl 'elm-mode #'run-elm-interactive) (def-package! flycheck-elm diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 05e3b6e96..4071df3f1 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -1,5 +1,13 @@ ;;; lang/emacs-lisp/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(autoload 'overseer-test "overseer" nil t) + + +;; +;; Library +;; + ;;;###autoload (defun +emacs-lisp/repl () "Open the Emacs Lisp REPL (`ielm')." diff --git a/modules/lang/emacs-lisp/config.el b/modules/lang/emacs-lisp/config.el index 20033e171..97172e8b6 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -67,27 +67,17 @@ ;; Plugins ;; -(def-package! auto-compile - :commands auto-compile-on-save-mode - :config - (setq auto-compile-display-buffer nil - auto-compile-use-mode-line nil)) +;; `auto-compile' +(setq auto-compile-display-buffer nil + auto-compile-use-mode-line nil) -(def-package! highlight-quoted - :commands highlight-quoted-mode) +;; `slime' +(setq inferior-lisp-program "clisp") +(after! slime (require 'slime-fuzzy)) -(def-package! slime - :defer t - :config - (setq inferior-lisp-program "clisp") - (require 'slime-fuzzy)) - - -(def-package! macrostep - :commands macrostep-expand - :config +(after! macrostep (map! :map macrostep-keymap :n "RET" #'macrostep-expand :n "e" #'macrostep-expand @@ -104,6 +94,7 @@ :n "q" #'macrostep-collapse-all :n "C" #'macrostep-collapse-all) + ;; `evil-normalize-keymaps' seems to be required for macrostep or it won't ;; apply for the very first invocation (add-hook 'macrostep-mode-hook #'evil-normalize-keymaps)) @@ -111,18 +102,14 @@ (def-package! flycheck-cask :when (featurep! :feature syntax-checker) - :commands flycheck-cask-setup + :defer t :init (add-hook! 'emacs-lisp-mode-hook (add-hook 'flycheck-mode-hook #'flycheck-cask-setup nil t))) -(def-package! overseer - :commands overseer-test) - - -;; ;; +;; Project modes ;; (def-project-mode! +emacs-lisp-ert-mode diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index 8a180d37a..933c455f4 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -1,20 +1,19 @@ ;;; private/erlang/config.el -*- lexical-binding: t; -*- -(def-package! erlang - ;; customizations - :mode "\\.erlang$" - ;; rebar files - :mode "/rebar\\.config\\(?:\\.script\\)?$" - ;; erlang configs - :mode "/\\(?:app\\|sys\\)\\.config$") +(dolist (regexp '("\\.erlang$" + ;; rebar files + "/rebar\\.config\\(?:\\.script\\)?$" + ;; erlang configs + "/\\(?:app\\|sys\\)\\.config$")) + (map-put auto-mode-alist regexp 'erlang-mode)) + (def-package! flycheck-rebar3 :when (featurep! :feature syntax-checker) - :after erlang - :config - (flycheck-rebar3-setup)) + :after flycheck + :config (flycheck-rebar3-setup)) + -;; Completion via Ivy (def-package! ivy-erlang-complete :when (featurep! :completion ivy) :hook (erlang-mode . ivy-erlang-complete-init) @@ -23,7 +22,6 @@ (add-hook 'after-save-hook #'ivy-erlang-complete-reparse nil t))) -;; Completion via Company (def-package! company-erlang :when (featurep! :completion company) :hook (erlang-mode . company-erlang-init)) diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index e08657a08..a1fc837b2 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -79,6 +79,6 @@ :n "cn" #'ess-noweb-next-chunk)))) -(def-package! ess-smart-equals - :hook ((ess-mode . ess-smart-equals-mode) - (inferior-ess-mode . ess-smart-equals-mode))) +;; `ess-smart-equals-mode' +(add-hook! (ess-mode inferior-ess) + #'ess-smart-equals-mode) diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el index 34e4d57d5..f8e8c6050 100644 --- a/modules/lang/go/config.el +++ b/modules/lang/go/config.el @@ -4,10 +4,7 @@ ;; Plugins ;; -(def-package! go-mode - :mode "\\.go$" - :interpreter "go" - :config +(after! go-mode (set! :env "GOPATH" "GOROOT") (set! :repl 'go-mode #'gorepl-run) (set! :lookup 'go-mode @@ -15,11 +12,12 @@ :references #'go-guru-referrers :documentation #'godoc-at-point) - (when (executable-find "goimports") - (setq gofmt-command "goimports")) + (when-let* ((goimports (executable-find "goimports"))) + (setq gofmt-command goimports)) (setq gofmt-show-errors nil) ; Leave it to flycheck - (add-hook 'go-mode-hook #'flycheck-mode) + + (add-hook! 'go-mode-hook #'(flycheck-mode go-eldoc-setup)) (add-hook! go-mode (add-hook 'before-save-hook #'gofmt-before-save nil t)) @@ -70,25 +68,13 @@ :v "r" #'go-play-region)) -(def-package! go-eldoc - :hook (go-mode . go-eldoc-setup)) - - -(def-package! go-guru - :commands (go-guru-describe go-guru-freevars go-guru-implements go-guru-peers - go-guru-referrers go-guru-definition go-guru-pointsto - go-guru-callstack go-guru-whicherrs go-guru-callers go-guru-callees - go-guru-expand-region)) - - (def-package! gorepl-mode - :commands (gorepl-run gorepl-run-load-current-file)) + :commands gorepl-run-load-current-file) (def-package! company-go :when (featurep! :completion company) - :init (setq command-go-gocode-command "gocode") :after go-mode :config - (setq company-go-show-annotation t) - (set! :company-backend 'go-mode '(company-go))) + (set! :company-backend 'go-mode 'company-go) + (setq company-go-show-annotation t)) diff --git a/modules/lang/haskell/config.el b/modules/lang/haskell/config.el index a97fa9bff..559b244aa 100644 --- a/modules/lang/haskell/config.el +++ b/modules/lang/haskell/config.el @@ -8,18 +8,7 @@ ;; Common plugins ;; -(def-package! haskell-mode - :mode "\\.hs$" - :mode ("\\.ghci$" . ghci-script-mode) - :mode ("\\.cabal$" . haskell-cabal-mode) - :interpreter (("runghc" . haskell-mode) - ("runhaskell" . haskell-mode)) - :config - (load "haskell-mode-autoloads" nil t) +(after! haskell-mode (set! :repl 'haskell-mode #'switch-to-haskell) - (push ".hi" completion-ignored-extensions) - - (autoload 'switch-to-haskell "inf-haskell" nil t) - (after! inf-haskell - (map! :map inferior-haskell-mode-map "ESC ESC" #'+popup/close))) + (add-to-list 'completion-ignored-extensions ".hi")) diff --git a/modules/lang/hy/config.el b/modules/lang/hy/config.el index bc106c784..bd24be0da 100644 --- a/modules/lang/hy/config.el +++ b/modules/lang/hy/config.el @@ -1,8 +1,8 @@ ;;; lang/hy/config.el -*- lexical-binding: t; -*- (def-package! hy-mode - :mode "\\.hy$" + :mode "\\.hy\\'" :interpreter "hy" :config (set! :repl 'hy-mode #'hy-shell-start-or-switch-to-shell) - (set! :company-backend 'hy-mode '(company-hy))) + (set! :company-backend 'hy-mode 'company-hy)) diff --git a/modules/lang/java/autoload.el b/modules/lang/java/autoload.el index 3e7a73c78..fa1689503 100644 --- a/modules/lang/java/autoload.el +++ b/modules/lang/java/autoload.el @@ -24,9 +24,9 @@ ;;;###autoload (defun +java|android-mode-maybe () - (when (doom-project-has! (or "local.properties" - "AndroidManifest.xml" - "src/main/AndroidManifest.xml")) + (when (project-file-exists-p! (or "local.properties" + "AndroidManifest.xml" + "src/main/AndroidManifest.xml")) (android-mode +1) (doom/set-build-command "./gradlew %s" "build.gradle"))) diff --git a/modules/lang/java/config.el b/modules/lang/java/config.el index 8be32f44c..5ace2680a 100644 --- a/modules/lang/java/config.el +++ b/modules/lang/java/config.el @@ -3,8 +3,8 @@ (add-hook 'java-mode-hook #'rainbow-delimiters-mode) (cond ((featurep! +meghanada) (load! +meghanada)) - ;; TODO lang/java +eclim - ;; ((featurep! +eclim) (load! +eclim)) + ;; TODO lang/java +lsp (lsp-java?) + ;; ((featurep! +lsp) (load! +lsp)) ) diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index 8d7c4d202..c5721ff22 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -22,6 +22,7 @@ (add-hook! 'js2-mode-hook #'(flycheck-mode rainbow-delimiters-mode)) (set! :electric 'js2-mode :chars '(?\} ?\) ?. ?:)) + (set! :repl 'js2-mode #'+javascript/repl) ;; Conform switch-case indentation to js2 normal indent (defvaralias 'js-switch-indent-offset 'js2-basic-offset) @@ -31,19 +32,10 @@ (map! :map js2-mode-map :localleader - :n "S" #'+javascript/skewer-this-buffer)) - - -(def-package! typescript-mode - :commands typescript-mode - :config - (add-hook! 'typescript-mode-hook #'(flycheck-mode rainbow-delimiters-mode)) - (set! :electric 'typescript-mode - :chars '(?\} ?\)) :words '("||" "&&"))) + :n "S" #'+javascript/skewer-this-buffer)) (def-package! rjsx-mode - :commands rjsx-mode :mode "components/.+\\.js$" :init (defun +javascript-jsx-file-p () @@ -54,8 +46,7 @@ magic-mode-regexp-match-limit t) (progn (goto-char (match-beginning 1)) (not (sp-point-in-string-or-comment))))) - - (push '(+javascript-jsx-file-p . rjsx-mode) magic-mode-alist) + (map-put magic-mode-alist #'+javascript-jsx-file-p 'rjsx-mode) :config (set! :electric 'rjsx-mode :chars '(?\} ?\) ?. ?>)) (add-hook! 'rjsx-mode-hook @@ -67,14 +58,19 @@ ;; However, the parser doesn't run immediately, so a fast typist can outrun ;; it, causing issues, so force it to parse. (defun +javascript|reparse (n) - ;; if n != 0, then rjsx-maybe-reparse will be run elsewhere - (if (= n 0) (rjsx-maybe-reparse))) + ;; if n != 1, rjsx-electric-gt calls rjsx-maybe-reparse itself + (if (= n 1) (rjsx-maybe-reparse))) (advice-add #'rjsx-electric-gt :before #'+javascript|reparse)) -(def-package! coffee-mode - :defer t ; file extensions registered by autoloads file - :init (setq coffee-indent-like-python-mode t)) +(after! typescript-mode + (add-hook! 'typescript-mode-hook #'(flycheck-mode rainbow-delimiters-mode)) + (set! :electric 'typescript-mode + :chars '(?\} ?\)) :words '("||" "&&"))) + + +;; `coffee-mode' +(setq coffee-indent-like-python-mode t) ;; @@ -86,6 +82,7 @@ :hook (typescript-mode . tide-setup) :init (defun +javascript|init-tide-in-web-mode () + "Enable `tide-mode' if in a *.tsx file." (when (string= (file-name-extension (or buffer-file-name "")) "tsx") (tide-setup))) (add-hook 'web-mode-hook #'+javascript|init-tide-in-web-mode) @@ -156,12 +153,6 @@ :init (set! :lookup 'js2-mode :xref-backend #'xref-js2-xref-backend)) -(def-package! nodejs-repl - :commands nodejs-repl - :init - (set! :repl 'js2-mode #'+javascript/repl)) - - (def-package! js2-refactor :commands (js2r-extract-function js2r-extract-method js2r-introduce-parameter @@ -174,51 +165,38 @@ js2r-debug-this js2r-forward-slurp js2r-forward-barf)) -(def-package! web-beautify - :commands web-beautify-js - :init - (map! :map* (json-mode js2-mode-map) :n "gQ" #'web-beautify-js)) - - (def-package! eslintd-fix - :commands (eslintd-fix-mode eslintd-fix) + :commands eslintd-fix :config (defun +javascript|set-flycheck-executable-to-eslint () (setq flycheck-javascript-eslint-executable eslintd-fix-executable)) (add-hook 'eslintd-fix-mode-hook #'+javascript|set-flycheck-executable-to-eslint)) -(def-package! skewer-mode - :commands (skewer-mode run-skewer) - :config - (map! :map skewer-mode-map +;; `skewer-mode' +(map! (:after skewer-mode + :map skewer-mode-map :localleader :n "sE" #'skewer-eval-last-expression :n "se" #'skewer-eval-defun - :n "sf" #'skewer-load-buffer)) + :n "sf" #'skewer-load-buffer) - -(def-package! skewer-css ; in skewer-mode - :commands skewer-css-mode - :config - (map! :map skewer-css-mode-map + (:after skewer-css + :map skewer-css-mode-map :localleader :n "se" #'skewer-css-eval-current-declaration :n "sr" #'skewer-css-eval-current-rule :n "sb" #'skewer-css-eval-buffer - :n "sc" #'skewer-css-clear-all)) + :n "sc" #'skewer-css-clear-all) - -(def-package! skewer-html ; in skewer-mode - :commands skewer-html-mode - :config - (map! :map skewer-html-mode-map + (:after skewer-html + :map skewer-html-mode-map :localleader :n "se" #'skewer-html-eval-tag)) -(def-package! skewer-repl - :commands skewer-repl) +;; `web-beautify' +(map! :map* (json-mode-map js2-mode-map) :n "gQ" #'web-beautify-js) ;; @@ -226,7 +204,7 @@ ;; (def-project-mode! +javascript-screeps-mode - :match "/screeps\\(-ai\\)?/.+$" + :match "/screeps\\(?:-ai\\)?/.+$" :modes (+javascript-npm-mode) :add-hooks (+javascript|init-screeps-mode) :on-load (load! +screeps)) diff --git a/modules/lang/julia/config.el b/modules/lang/julia/config.el index 2656fd4c8..c7be5a650 100644 --- a/modules/lang/julia/config.el +++ b/modules/lang/julia/config.el @@ -1,7 +1,6 @@ ;;; lang/julia/config.el -*- lexical-binding: t; -*- (use-package julia-mode - :mode "\\.jl$" :interpreter "julia" :config (set! :repl 'julia-mode #'+julia/repl) diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 29ecefa08..758f703a7 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -18,21 +18,12 @@ "Sets the directory where AUCTeX will search for PDFs associated to BibTeX references." `(setq +latex-bibtex-dir ,dir)) -;; sp's default latex rules are obnoxious, so disable them -(provide 'smartparens-latex) - ;; ;; Plugins ;; -(def-package! tex-site - :init - ;; Manually load the AUCTEX autoloads. This is normally done by - ;; package-initialize, ... which we do not use. - (load "auctex.el" nil t t) - (load "auctex-autoloads.el" nil t t) - :config +(after! tex-site ;; Set some varibles to fontify common LaTeX commands. (load! +fontification) (setq ;; Enable parse on load. @@ -182,7 +173,6 @@ (def-package! bibtex :defer t - :mode ("\\.bib\\'" . bibtex-mode) :config (setq bibtex-dialect 'biblatex bibtex-align-at-equal-sign t diff --git a/modules/lang/ledger/config.el b/modules/lang/ledger/config.el index 5db4e7c71..0df0dbc5f 100644 --- a/modules/lang/ledger/config.el +++ b/modules/lang/ledger/config.el @@ -1,8 +1,7 @@ ;;; lang/ledger/config.el -*- lexical-binding: t; -*- -(def-package! ledger-mode - :mode "\\.ledger$" - :config (setq ledger-clear-whole-transactions 1)) +;; `ledger-mode' +(setq ledger-clear-whole-transactions 1) (def-package! evil-ledger @@ -12,4 +11,5 @@ (def-package! flycheck-ledger :when (featurep! :feature syntax-checker) - :init (add-hook 'ledger-mode-hook #'flycheck-mode)) + :after ledger-mode + :config (add-hook 'ledger-mode-hook #'flycheck-mode)) diff --git a/modules/lang/lua/config.el b/modules/lang/lua/config.el index 9a2dece5a..d8557f3b3 100644 --- a/modules/lang/lua/config.el +++ b/modules/lang/lua/config.el @@ -1,18 +1,12 @@ ;;; lang/lua/config.el -*- lexical-binding: t; -*- -(def-package! lua-mode - :mode "\\.lua$" - :interpreter "lua" - :init - ;; sp's default lua rules are obnoxious, so disable them. Use snippets - ;; instead! - (provide 'smartparens-lua) - :config +(after! lua-mode (add-hook 'lua-mode-hook #'flycheck-mode) (set! :lookup 'lua-mode :documentation 'lua-search-documentation) (set! :electric 'lua-mode :words '("else" "end")) (set! :repl 'lua-mode #'+lua/repl) + (set! :company-backend 'lua-mode '(company-lua company-yasnippet)) (def-menu! +lua/build-menu "Build/compilation commands for `lua-mode' buffers." @@ -24,15 +18,8 @@ :n "b" #'+lua/build-menu)) -(def-package! company-lua - :after (:all company lua-mode) - :config - (set! :company-backend 'lua-mode '(company-lua company-yasnippet))) - - -(def-package! moonscript - :mode ("\\.moon$" . moonscript-mode) - :config (defvaralias 'moonscript-indent-offset 'tab-width)) +(after! moonscript + (defvaralias 'moonscript-indent-offset 'tab-width)) ;; diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index b45cbe084..0438171e0 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -1,9 +1,7 @@ ;;; lang/markdown/config.el -*- lexical-binding: t; -*- (def-package! markdown-mode - :mode "/README$" - :mode ("/README\\.md$" . gfm-mode) - :mode "\\.m\\(?:d\\|arkdown\\)$" + :mode ("/README\\(?:\\.\\(?:markdown\\|md\\)\\)?\\'" . gfm-mode) :init (setq markdown-enable-wiki-links t markdown-enable-math t diff --git a/modules/lang/nim/config.el b/modules/lang/nim/config.el index 4beffb199..c43aab0aa 100644 --- a/modules/lang/nim/config.el +++ b/modules/lang/nim/config.el @@ -1,32 +1,20 @@ ;;; lang/nim/config.el -*- lexical-binding: t; -*- -(def-package! nim-mode - :mode "\\.nim\\'" - :mode ("\\.nim\\(ble\\|s\\)\\'" . nimscript-mode) - :config - (load "nim-mode-autoloads" nil t) - ;; NOTE nim-mode autoloads sets up xref - +(after! nim-mode (defun +nim|init-nimsuggest-mode () "Conditionally load `nimsuggest-mode', instead of clumsily erroring out if nimsuggest isn't installed." - (when (executable-find "nimsuggest") + (when (file-executable-p nimsuggest-path) (nimsuggest-mode))) (add-hook 'nim-mode-hook #'+nim|init-nimsuggest-mode) (map! :map nim-mode-map :localleader - :n "b" #'+nim/build-menu) - - (def-menu! +nim/build-menu - "Building commands for `nim-mode' buffers." - '(("Build & run" :exec nim-compile)) - :prompt "Build")) + :n "b" #'nim-compile)) (def-package! flycheck-nim :when (featurep! :feature syntax-checker) :after nim-mode - :config - (add-hook 'nimsuggest-mode-hook #'flycheck-mode)) + :config (add-hook 'nimsuggest-mode-hook #'flycheck-mode)) diff --git a/modules/lang/nix/config.el b/modules/lang/nix/config.el deleted file mode 100644 index e9d090fc3..000000000 --- a/modules/lang/nix/config.el +++ /dev/null @@ -1,4 +0,0 @@ -;;; lang/nix/config.el -*- lexical-binding: t; -*- - -(def-package! nix-mode - :mode "\\.nix$") diff --git a/modules/lang/ocaml/config.el b/modules/lang/ocaml/config.el index 22067cffb..bdd4974d2 100644 --- a/modules/lang/ocaml/config.el +++ b/modules/lang/ocaml/config.el @@ -1,9 +1,13 @@ ;;; lang/ocaml/config.el -*- lexical-binding: t; -*- (def-package! tuareg - :mode ("\\.ml[4ilpy]?$" . tuareg-mode)) + :mode ("\\.ml[4ilpy]?\\'" . tuareg-mode)) (def-package! merlin :after tuareg - :hook (tuareg-mode . merlin-mode)) + :hook (tuareg-mode . merlin-mode) + :config + (set! :company-backend 'tuareg-mode 'merlin-compand-backend) + (after! company + (remove-hook 'company-backends 'merlin-compand-backend))) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index a2c17bb28..d1ff4d5b8 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -16,12 +16,8 @@ ;; Plugins ;; -(def-package! toc-org - :commands toc-org-enable - :config (setq toc-org-hrefify-default "org")) - -(def-package! org-bullets - :commands org-bullets-mode) +;; `toc-org' +(setq toc-org-hrefify-default "org") (def-package! evil-org :when (featurep! :feature evil) @@ -126,7 +122,13 @@ unfold to point on startup." org-agenda-dim-blocked-tasks nil org-agenda-files (ignore-errors (directory-files +org-dir t "\\.org$" t)) org-agenda-inhibit-startup t - org-agenda-skip-unavailable-files t)) + org-agenda-skip-unavailable-files t) + ;; Move the agenda to show the previous 3 days and the next 7 days for a bit + ;; better context instead of just the current week which is a bit confusing + ;; on, for example, a sunday + (setq org-agenda-span 10 + org-agenda-start-on-weekday nil + org-agenda-start-day "-3d")) (defun +org|setup-ui () "Configures the UI for `org-mode'." @@ -278,12 +280,14 @@ between the two." :ni [M-return] (λ! (+org/insert-item 'below)) :ni [S-M-return] (λ! (+org/insert-item 'above)) ;; more org-ish vim motion keys - :n "]]" (λ! (org-forward-heading-same-level nil) (org-beginning-of-line)) - :n "[[" (λ! (org-backward-heading-same-level nil) (org-beginning-of-line)) - :n "]l" #'org-next-link - :n "[l" #'org-previous-link - :n "]s" #'org-babel-next-src-block - :n "[s" #'org-babel-previous-src-block + :m "]]" (λ! (org-forward-heading-same-level nil) (org-beginning-of-line)) + :m "[[" (λ! (org-backward-heading-same-level nil) (org-beginning-of-line)) + :m "]h" #'org-next-visible-heading + :m "[h" #'org-previous-visible-heading + :m "]l" #'org-next-link + :m "[l" #'org-previous-link + :m "]s" #'org-babel-next-src-block + :m "[s" #'org-babel-previous-src-block :m "^" #'evil-org-beginning-of-line :m "0" (λ! (let ((visual-line-mode)) (org-beginning-of-line))) :n "gQ" #'org-fill-paragraph @@ -348,10 +352,11 @@ between the two." (def-package! org-clock :commands org-clock-save :hook (org-mode . org-clock-load) - :config + :init (setq org-clock-persist 'history org-clock-persist-file (concat doom-etc-dir "org-clock-save.el")) - (add-hook 'kill-emacs-hook 'org-clock-save)) + :config + (add-hook 'kill-emacs-hook #'org-clock-save)) ;; (when (featurep 'org) diff --git a/modules/lang/plantuml/config.el b/modules/lang/plantuml/config.el index cb66d1495..a60b92e92 100644 --- a/modules/lang/plantuml/config.el +++ b/modules/lang/plantuml/config.el @@ -1,7 +1,7 @@ ;;; lang/plantuml/config.el -*- lexical-binding: t; -*- (def-package! plantuml-mode - :mode "\\.p\\(?:lant\\)?uml$" + :defer t :init (setq plantuml-jar-path (concat doom-etc-dir "plantuml.jar") org-plantuml-jar-path plantuml-jar-path) diff --git a/modules/lang/purescript/config.el b/modules/lang/purescript/config.el index 39eafd64c..579391e8e 100644 --- a/modules/lang/purescript/config.el +++ b/modules/lang/purescript/config.el @@ -1,16 +1,18 @@ ;;; lang/purescript/config.el -*- lexical-binding: t; -*- -(def-package! purescript-mode - :mode "\\.purs$" - :config +(after! purescript-mode (add-hook! 'purescript-mode-hook - #'(flycheck-mode purescript-indentation-mode rainbow-delimiters-mode))) + #'(flycheck-mode + purescript-indentation-mode + rainbow-delimiters-mode))) + ;; (def-package! flycheck-purescript ;; :after purescript-mode ;; :config ;; (add-hook 'flycheck-mode-hook #'flycheck-purescript-setup)) + (def-package! psc-ide :hook (purescript-mode . psc-ide-mode)) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 4c6fe44f1..4e129b9d3 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -16,7 +16,7 @@ is loaded.") ;; (def-package! python - :commands python-mode + :defer t :init (setq python-environment-directory doom-cache-dir python-indent-guess-indent-offset-verbose nil @@ -73,7 +73,6 @@ environment variables." (def-package! anaconda-mode - :after python :hook python-mode :init (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") diff --git a/modules/lang/rest/config.el b/modules/lang/rest/config.el index c3781d2b1..117908050 100644 --- a/modules/lang/rest/config.el +++ b/modules/lang/rest/config.el @@ -1,8 +1,7 @@ ;;; lang/rest/config.el -*- lexical-binding: t; -*- (def-package! restclient - :commands restclient-mode - :mode ("\\.http$" . restclient-mode) + :mode ("\\.http\\'" . restclient-mode) :config (set! :popup "^\\*HTTP Response" '((size . 0.4)) '((quit . other))) (map! :mode restclient-mode @@ -16,4 +15,4 @@ (def-package! company-restclient :when (featurep! :completion company) :after restclient - :config (set! :company-backend 'restclient-mode '(company-restclient))) + :config (set! :company-backend 'restclient-mode 'company-restclient)) diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index 3d4fd3ae3..5eb32b4fc 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -12,15 +12,12 @@ ;; (def-package! ruby-mode - :mode "\\.rb$" - :mode "\\.rake$" - :mode "\\.gemspec$" - :mode "\\.\\(?:pry\\|irb\\)rc$" - :mode "/\\(?:Gem\\|Cap\\|Vagrant\\|Rake\\|Pod\\|Puppet\\|Berks\\)file$" - :interpreter "ruby" + :mode "\\.\\(?:pry\\|irb\\)rc\\'" :config - (set! :company-backend 'ruby-mode '(company-dabbrev-code)) + (set! :company-backend 'ruby-mode 'company-dabbrev-code) (set! :electric 'ruby-mode :words '("else" "end" "elseif")) + (set! :env "RBENV_ROOT") + (set! :repl 'ruby-mode #'inf-ruby) ; `inf-ruby' (setq ruby-deep-indent-paren t) ;; Don't interfere with my custom RET behavior (define-key ruby-mode-map [?\n] nil) @@ -74,10 +71,8 @@ environment variables." (def-package! rspec-mode - :mode ("/\\.rspec$" . text-mode) + :mode ("/\\.rspec\\'" . text-mode) :init - (associate! rspec-mode :match "/\\.rspec$") - (associate! rspec-mode :modes (ruby-mode yaml-mode) :files ("/spec/")) (defvar rspec-mode-verifiable-map (make-sparse-keymap)) (defvar evilmi-ruby-match-tags '((("unless" "if") ("elsif" "else") "end") @@ -96,18 +91,12 @@ environment variables." :n "v" #'rspec-verify)) -(def-package! inf-ruby - :commands (inf-ruby inf-ruby-console-auto) - :init (set! :repl 'ruby-mode 'inf-ruby)) - - (def-package! company-inf-ruby :when (featurep! :completion company) :after inf-ruby - :config (set! :company-backend 'inf-ruby-mode '(company-inf-ruby))) + :config (set! :company-backend 'inf-ruby-mode 'company-inf-ruby)) -(def-package! rake - :commands (rake rake-find-task rake-rerun) - :config (setq rake-completion-system 'default)) +;; `rake' +(setq rake-completion-system 'default) diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el index 9a45ae71e..cd216273f 100644 --- a/modules/lang/rust/config.el +++ b/modules/lang/rust/config.el @@ -1,12 +1,6 @@ ;;; lang/rust/config.el -*- lexical-binding: t; -*- -;; -;; Plugins -;; - -(def-package! rust-mode - :mode "\\.rs$" - :config +(after! rust-mode (set! :env "RUST_SRC_PATH") (set! :docset 'rust-mode "Rust") (setq rust-indent-method-chain t) @@ -14,6 +8,7 @@ (map! :map rust-mode-map :localleader :n "b" #'+rust/build-menu) + (def-menu! +rust/build-menu "TODO" '(("cargo run" :exec "cargo run --color always") @@ -40,6 +35,5 @@ (def-package! flycheck-rust :when (featurep! :feature syntax-checker) :after rust-mode - :hook (flycheck-mode . flycheck-rust-setup) - :init (add-hook 'rust-mode-hook #'flycheck-mode)) + :config (add-hook! 'rust-mode-hook #'(flycheck-mode flycheck-rust-setup))) diff --git a/modules/lang/scala/config.el b/modules/lang/scala/config.el index b5a2a1bf0..44a39ced9 100644 --- a/modules/lang/scala/config.el +++ b/modules/lang/scala/config.el @@ -1,26 +1,24 @@ ;;; lang/scala/config.el -*- lexical-binding: t; -*- -(def-package! scala-mode - :mode "\\.s\\(?:cala\\|bt\\)$" - :config (setq scala-indent:align-parameters t)) +(after! scala-mode + (setq scala-indent:align-parameters t) + (add-to-list 'dtrt-indent-hook-mapping-list '(scala-mode c/c++/java scala-indent:step))) -(def-package! sbt-mode :after scala-mode) - - -(def-package! ensime - :commands (ensime ensime-scala-mode-hook) - :hook (scala-mode . ensime-mode) - :config - (set! :company-backend 'scala-mode '(ensime-company company-yasnippet)) - +(after! ensime (setq ensime-startup-snapshot-notification nil ensime-startup-notification nil ensime-eldoc-hints 'all ;; let DOOM handle company setup ensime-completion-style nil) + (set! :company-backend 'scala-mode '(ensime-company company-yasnippet)) + ;; Fix void-variable imenu-auto-rescan error caused by `ensime--setup-imenu' ;; trying to make imenu variables buffer local before imenu is loaded. (require 'imenu)) + +(def-package! sbt-mode + :after scala-mode + :config (set! :repl 'scala-mode #'run-scala)) diff --git a/modules/lang/sh/config.el b/modules/lang/sh/config.el index d100a8c86..d705509da 100644 --- a/modules/lang/sh/config.el +++ b/modules/lang/sh/config.el @@ -12,21 +12,19 @@ ;; (def-package! sh-script ; built-in - :mode ("\\.zsh$" . sh-mode) - :mode ("\\.zunit$" . sh-mode) - :mode ("/bspwmrc$" . sh-mode) - :init - (add-hook! sh-mode #'(flycheck-mode highlight-numbers-mode)) + :mode ("\\.zunit\\'" . sh-mode) + :mode ("/bspwmrc\\'" . sh-mode) :config + (add-hook! sh-mode #'(flycheck-mode highlight-numbers-mode)) (set! :electric 'sh-mode :words '("else" "elif" "fi" "done" "then" "do" "esac" ";;")) (set! :repl 'sh-mode #'+sh/repl) (setq sh-indent-after-continuation 'always) ;; recognize function names with dashes in them - (push '((sh . ((nil "^\\s-*function\\s-+\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*\\(?:()\\)?" 1) - (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1)))) - sh-imenu-generic-expression) + (map-put sh-imenu-generic-expression + 'sh '((nil "^\\s-*function\\s-+\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*\\(?:()\\)?" 1) + (nil "^\\s-*\\([[:alpha:]_-][[:alnum:]_-]*\\)\\s-*()" 1))) ;; `sh-set-shell' is chatty about setting up indentation rules (advice-add #'sh-set-shell :around #'doom*shut-up) @@ -52,7 +50,7 @@ (string-match-p "\\.zsh\\'" buffer-file-name)) (save-excursion (goto-char (point-min)) - (looking-at-p "^#!.+zsh[$\\s-]"))) + (looking-at-p "^#!.+/zsh[$ ]"))) (sh-set-shell "zsh"))) (add-hook 'sh-mode-hook #'+sh|detect-zsh)) diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el index 9daae8f3a..6ba19cf0e 100644 --- a/modules/lang/swift/config.el +++ b/modules/lang/swift/config.el @@ -1,13 +1,14 @@ ;;; lang/swift/config.el -*- lexical-binding: t; -*- -;; TODO Set up emacs task runners for fruitstrap +;; `swift-mode' +(set! :repl 'swift-mode #'run-swift) -(def-package! swift-mode - :mode "\\.swift$" - :config - (add-hook 'swift-mode-hook #'flycheck-mode) - (set! :repl 'swift-mode #'swift-mode-run-repl) ; TODO test this - (push 'swift flycheck-checkers)) + +(def-package! flycheck-swift + :when (featurep! :feature syntax-checker) + :after swift-mode + :init (add-hook 'swift-mode-hook #'flycheck-mode) + :config (flycheck-swift-setup)) (def-package! company-sourcekit diff --git a/modules/lang/swift/packages.el b/modules/lang/swift/packages.el index a9cc3a778..73e74da91 100644 --- a/modules/lang/swift/packages.el +++ b/modules/lang/swift/packages.el @@ -5,3 +5,6 @@ (when (featurep! :completion company) (package! company-sourcekit)) + +(when (featurep! :feature syntax-checker) + (package! flycheck-swift)) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index d49d19292..834a2ae6e 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -1,10 +1,5 @@ ;;; lang/web/+css.el -*- lexical-binding: t; -*- -;; css-mode hooks apply to scss and less-css modes -(add-hook 'css-mode-hook #'rainbow-delimiters-mode) -(add-hook! (css-mode sass-mode stylus-mode) - #'(yas-minor-mode-on flycheck-mode highlight-numbers-mode)) - ;; An improved newline+continue comment function (setq-hook! css-mode comment-indent-function #'+css/comment-indent-new-line) @@ -13,29 +8,36 @@ (sp-local-pair "/*" "*/" :post-handlers '(("||\n[i]" "RET") ("| " "SPC"))))) (map! :map* (css-mode-map scss-mode-map less-css-mode-map) - :n "M-R" #'+css/web-refresh-browser - (:localleader - :n "rb" #'+css/toggle-inline-or-block)) + :localleader + :n "rb" #'+css/toggle-inline-or-block) ;; ;; Packages ;; +;; css-mode hooks apply to scss and less-css modes +(add-hook 'css-mode-hook #'rainbow-delimiters-mode) +(add-hook! (css-mode sass-mode stylus-mode) + #'(yas-minor-mode-on + flycheck-mode + highlight-numbers-mode + rainbow-mode)) + + (def-package! counsel-css :when (featurep! :completion ivy) - :commands (counsel-css counsel-css-imenu-setup) + :commands counsel-css :hook (css-mode . counsel-css-imenu-setup) :init (map! :map* (css-mode-map scss-mode-map less-css-mode-map) :localleader :n ";" #'counsel-css)) -(def-package! rainbow-mode - :hook (css-mode sass-mode)) - - -(after! css-mode ; contains both css-mode & scss-mode +(def-package! css-mode ; built-in + :defer t + :config + ;; contains both css-mode & scss-mode (set! :docset 'css-mode "CSS") (set! :docset 'scss-mode "Sass") (unless EMACS26+ @@ -45,7 +47,7 @@ (def-package! sass-mode - :commands sass-mode + :defer t :config (set! :docset 'sass-mode "Sass") (set! :company-backend 'sass-mode 'company-css) diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index a01979605..4a27bd0ed 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -14,7 +14,6 @@ :mode "wp-content/themes/.+/.+\\.php$" :mode "templates/.+\\.php$" :config - (set! :company-backend 'web-mode '(company-web-html company-yasnippet)) (setq web-mode-enable-html-entities-fontification t web-mode-enable-auto-quoting nil) @@ -91,10 +90,7 @@ :nv "[T" #'web-mode-element-parent)) -(def-package! company-web - :when (featurep! :completion company) - :after web-mode) - - -;; `pug-mode' -(set! :company-backend 'pug-mode '(company-yasnippet)) +;; +(set! :company-backend 'pug-mode 'company-web-jade) +(set! :company-backend 'web-mode 'company-web-html) +(set! :company-backend 'slim-mode 'company-web-slim) diff --git a/modules/lang/web/doctor.el b/modules/lang/web/doctor.el new file mode 100644 index 000000000..c351f0c73 --- /dev/null +++ b/modules/lang/web/doctor.el @@ -0,0 +1,8 @@ +;;; lang/web/doctor.el -*- lexical-binding: t; -*- + +(unless (executable-find "js-beautify") + (warn! "Couldn't find js-beautify. Code formatting in JS/CSS/HTML modes will not work.")) + +(unless (executable-find "stylelint") + (warn! "Couldn't find stylelint. Linting for CSS modes will not work.")) + diff --git a/modules/lang/web/packages.el b/modules/lang/web/packages.el index f89e975d7..9a864d1bc 100644 --- a/modules/lang/web/packages.el +++ b/modules/lang/web/packages.el @@ -13,9 +13,9 @@ (package! haml-mode) (package! pug-mode) (package! slim-mode) -(package! web-mode) -(when (featurep! :completion company) - (package! company-web)) +(when (package! web-mode) + (when (featurep! :completion company) + (package! company-web))) ;; +css.el (package! less-css-mode) diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index 2e5d98795..c458b8a2e 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -3,7 +3,8 @@ ;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; specify their own formatting rules. (def-package! editorconfig - :hook (doom-init . editorconfig-mode) + :defer 2 + :after-call doom-before-switch-buffer :config ;; Register missing indent variables (setq editorconfig-indentation-alist @@ -49,9 +50,7 @@ extension, try to guess one." ;; editorconfig to ignore indentation there. I prefer dynamic indentation ;; support built into Emacs. (dolist (mode '(emacs-lisp-mode lisp-mode)) - (map-delete editorconfig-indentation-alist mode))) - - -(def-package! editorconfig-conf-mode - :mode "\\.?editorconfig$") + (map-delete editorconfig-indentation-alist mode)) + ;; + (editorconfig-mode +1)) diff --git a/modules/tools/ein/autoload.el b/modules/tools/ein/autoload.el index d30f5119e..303deb947 100644 --- a/modules/tools/ein/autoload.el +++ b/modules/tools/ein/autoload.el @@ -1,9 +1,29 @@ ;;; tools/ein/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(def-setting! :ein-notebook-dir (dir) + "Set the default directory from where to open Jupyter notebooks." + `(setq ein:jupyter-default-notebook-directory ,dir)) + + +;; +;; Library +;; + +(defun +ein--collect-ein-buffer-links () + (let ((end (window-end)) + points) + (save-excursion + (goto-char (window-start)) + (while (re-search-forward "~?/.+\\|\s\\[" end t) + (push (+ (match-beginning 0) 1) points)) + (nreverse points)))) + ;;;###autoload (defun +ein/ace-link-ein () "Ace jump to links in ein notebooklist." (interactive) + (require 'avy) (let ((res (avy-with +ein/ace-link-ein (avy--process (+ein--collect-ein-buffer-links) @@ -13,14 +33,4 @@ (goto-char (1+ res)) (widget-button-press (point))))) -;;;###autoload -(defun +ein--collect-ein-buffer-links () - (interactive) - (let ((end (window-end)) - points) - (save-excursion - (goto-char (window-start)) - (while (re-search-forward "~?/.+\\|\s\\[" end t) - (push (+ (match-beginning 0) 1) points)) - (nreverse points)))) diff --git a/modules/tools/ein/config.el b/modules/tools/ein/config.el index 5633bf690..e3b469254 100644 --- a/modules/tools/ein/config.el +++ b/modules/tools/ein/config.el @@ -3,49 +3,51 @@ (defvar +ein-notebook-dir "~/" "Default directory from where Jupyter notebooks are to be opened.") -(def-setting! :ein-notebook-dir (dir) - "Set the default directory from where to open Jupyter notebooks." - `(setq +ein-notebook-dir ,dir)) +;; +;; Plugins +;; (def-package! ein - :commands (ein:notebooklist-open ein:notebooklist-login ein:jupyter-server-start) + :defer t :init - (push (lambda (buf) (string-match-p "^\\*ein: .*" (buffer-name buf))) - doom-real-buffer-functions) (set! :popup "\\*ein: .*" :ignore) - (set! :popup "\\*ein:tb .*" '((side . bottom) (size . 0.3)) '((quit . t) (transient) (select))) - (set! :popup "\\*ein:notebooklist *" '((side . left) (size . 50)) '((select))) - ;; Ace-link on notebook list buffers - (add-hook! 'ein:notebooklist-mode-hook - (map! :map ein:notebooklist-mode-map - "o" #'+ein/ace-link-ein)) - ;; Ein uses request to store http cookies. Store them in the cache dir. - (setq request-storage-directory (concat doom-cache-dir "/request")) + (set! :popup "\\*ein:tb .*" + '((side . bottom) (size . 0.3)) + '((quit . t) (transient) (select))) + (set! :popup "\\*ein:notebooklist *" + '((side . left) (size . 50)) + '((select))) + ;; Auto complete with company (when (featurep! :completion company) (setq ein:completion-backend 'ein:use-company-backend) - (set! :company-backend - '(ein:notebook-multilang-mode - ein:notebook-python-mode - ein:notebook-plain-mode) + (set! :company-backend '(ein:notebook-multilang-mode + ein:notebook-python-mode + ein:notebook-plain-mode) 'ein:company-backend)) :config - ;; Manually load the autoloads of EIN. This takes time... - (load "ein-loaddefs.el" nil t t) - (setq - ;; Slice images into rows so that we can navigate buffers with images more easily - ein:slice-image t - ein:jupyter-default-notebook-directory +ein-notebook-dir - ein:jupyter-default-server-command "jupyter" - ein:jupyter-server-args '("--no-browser") - ein:notebook-modes - '(ein:notebook-multilang-mode ein:notebook-python-mode ein:notebook-plain-mode)) - ;; Avy is required for showing links in the notebook list with ace-link. - (require 'avy) + (setq ein:jupyter-server-args '("--no-browser") + ein:notebook-modes + '(ein:notebook-multilang-mode ein:notebook-python-mode ein:notebook-plain-mode) + ;; Slice images into rows; easier to navigate around images + ein:slice-image t) + + (unless ein:jupyter-default-notebook-directory + (setq ein:jupyter-default-notebook-directory "~/")) + + (defun +ein-buffer-p (buf) + (string-match-p "^\\*ein: .*" (buffer-name buf))) + (add-to-list 'doom-real-buffer-functions #'+ein-buffer-p nil #'eq) + + ;; Ace-link on notebook list buffers + (map! :after ein-notebooklist + :map ein:notebooklist-mode-map + "o" #'+ein/ace-link-ein) + ;; add hydra - (def-hydra! +ein/hydra (:hint t :color red) + (defhydra +ein/hydra (:hint t :color red) " Operations on Cells^^^^^^ Other ----------------------------^^^^^^ ----------------------------------^^^^ diff --git a/modules/tools/electric-indent/config.el b/modules/tools/electric-indent/config.el index 809b07800..01a1f3f86 100644 --- a/modules/tools/electric-indent/config.el +++ b/modules/tools/electric-indent/config.el @@ -8,16 +8,6 @@ (defvar-local doom-electric-indent-words '() "TODO") -(setq-default electric-indent-chars '(?\n ?\^?)) - -(defun +electric-indent|char (_c) - (when (and (eolp) doom-electric-indent-words) - (save-excursion - (backward-word) - (looking-at-p - (concat "\\<" (regexp-opt doom-electric-indent-words)))))) -(push #'+electric-indent|char electric-indent-functions) - (def-setting! :electric (modes &rest plist) "Declare :words (list of strings) or :chars (lists of chars) in MODES that trigger electric indentation." @@ -34,3 +24,15 @@ trigger electric indentation." ,@(if words `((setq doom-electric-indent-words ',words)))) (add-hook! ,modes #',fn-name)))))) +;; +(after! electric + (setq-default electric-indent-chars '(?\n ?\^?)) + + (defun +electric-indent|char (_c) + (when (and (eolp) doom-electric-indent-words) + (save-excursion + (backward-word) + (looking-at-p + (concat "\\<" (regexp-opt doom-electric-indent-words)))))) + (add-to-list 'electric-indent-functions #'+electric-indent|char)) + diff --git a/modules/tools/eshell/autoload/eshell.el b/modules/tools/eshell/autoload/eshell.el index 57da503c8..059d0fba4 100644 --- a/modules/tools/eshell/autoload/eshell.el +++ b/modules/tools/eshell/autoload/eshell.el @@ -175,14 +175,14 @@ delete." "Create a new eshell window below the current one." (interactive) (select-window (split-window-vertically)) - (+eshell/open)) + (bury-buffer)) ;;;###autoload (defun +eshell/split-right () "Create a new eshell window to the right of the current one." (interactive) (select-window (split-window-horizontally)) - (+eshell/open)) + (bury-buffer)) ;; `make-ring' ;; `ring-ref' diff --git a/modules/tools/eshell/config.el b/modules/tools/eshell/config.el index 26f919bd9..75870835a 100644 --- a/modules/tools/eshell/config.el +++ b/modules/tools/eshell/config.el @@ -1,7 +1,5 @@ ;;; tools/eshell/config.el -*- lexical-binding: t; -*- -;; This is highly experimental. I don't use eshell often, so this may need work. - ;; see: ;; + `+eshell/open': open in current buffer ;; + `+eshell/open-popup': open in a popup @@ -9,10 +7,10 @@ ;; workspaces) (def-package! eshell ; built-in - :commands eshell-mode + :defer t :init (setq eshell-directory-name - (let ((dir (concat doom-private-dir "eshell"))) + (let ((dir (expand-file-name "eshell" doom-private-dir))) (if (file-directory-p dir) dir "~/.eshell")) diff --git a/modules/tools/gist/config.el b/modules/tools/gist/config.el index 46473990a..3e563de26 100644 --- a/modules/tools/gist/config.el +++ b/modules/tools/gist/config.el @@ -4,9 +4,7 @@ ;; errors. If that happens, try `+gist/kill-cache'. You may have to restart ;; Emacs. -(def-package! gist - :commands (gist-list gist-region-or-buffer-private gist-region-or-buffer) - :config +(after! gist (set! :evil-state 'gist-list-mode 'normal) (defun +gist*list-render (orig-fn &rest args) diff --git a/modules/tools/imenu/config.el b/modules/tools/imenu/config.el index 58628197d..577476ab9 100644 --- a/modules/tools/imenu/config.el +++ b/modules/tools/imenu/config.el @@ -1,13 +1,10 @@ ;;; tools/imenu/config.el -*- lexical-binding: t; -*- -(def-package! imenu-anywhere - :commands (ido-imenu-anywhere ivy-imenu-anywhere helm-imenu-anywhere) - :config (setq imenu-anywhere-delimiter ": ")) +;; `imenu-anywhere' +(setq imenu-anywhere-delimiter ": ") -(def-package! imenu-list - :commands (imenu-list-minor-mode imenu-list-smart-toggle) - :config +(after! imenu-list (setq imenu-list-idle-update-delay 0.5) (set! :popup "^\\*Ilist" diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index db32ca8d1..2d1d810f8 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -2,8 +2,6 @@ (def-package! magit :defer t - :init - (load "magit-autoloads" nil t) :config (setq magit-completing-read-function (if (featurep! :completion ivy) @@ -18,13 +16,11 @@ (map! :map magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit)) -(def-package! magit-blame - :commands magit-blame - :after git-timemachine) +(def-package! magit-blame :after git-timemachine) (def-package! magithub - :commands (magithub-clone magithub-feature-autoinject) + :commands magithub-feature-autoinject :after magit :preface (setq magithub-dir (concat doom-etc-dir "magithub/")) @@ -32,10 +28,13 @@ (setq magithub-clone-default-directory "~/" magithub-preferred-remote-method 'clone_url) :config - (load "magithub-autoloads" nil t) (magithub-feature-autoinject t)) +(def-package! magit-gitflow + :hook (magit-mode . turn-on-magit-gitflow)) + + (def-package! evil-magit :when (featurep! :feature evil) :after magit diff --git a/modules/tools/magit/packages.el b/modules/tools/magit/packages.el index 132e1fc30..d510a8977 100644 --- a/modules/tools/magit/packages.el +++ b/modules/tools/magit/packages.el @@ -1,7 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/magit/packages.el -(package! magit) -(package! magithub) -(when (featurep! :feature evil) - (package! evil-magit)) +(when (package! magit) + (package! magithub) + (package! magit-gitflow) + (when (featurep! :feature evil) + (package! evil-magit))) diff --git a/modules/tools/password-store/config.el b/modules/tools/password-store/config.el index f57b69c8d..4fac1bb32 100644 --- a/modules/tools/password-store/config.el +++ b/modules/tools/password-store/config.el @@ -11,17 +11,18 @@ ;; Plugins ;; -(def-package! password-store +;; `password-store' +(setq password-store-password-length 12) + + +;; `pass' +(def-package! pass :defer t :config - (setq password-store-password-length 12)) - - -(def-package! pass - :commands pass - :config (set! :evil-state 'pass-mode 'emacs) - (set! :popup "^\\*Password-Store" '((side . left) (size . 0.25)) '((quit))) + (set! :popup "^\\*Password-Store" + '((side . left) (size . 0.25)) + '((quit))) (map! :map pass-mode-map "j" #'pass-next-entry "k" #'pass-prev-entry @@ -30,12 +31,6 @@ "C-k" #'pass-next-directory)) -(def-package! helm-pass - :when (featurep! :completion helm) - :commands helm-pass) - - ;; Is built into Emacs 26+ -(def-package! auth-source-pass - :when (featurep! +auth) - :config (auth-source-pass-enable)) +(when (and EMACS26+ (featurep! +auth)) + (auth-source-pass-enable)) diff --git a/modules/tools/password-store/packages.el b/modules/tools/password-store/packages.el index 6827fc23e..dbc5f2afd 100644 --- a/modules/tools/password-store/packages.el +++ b/modules/tools/password-store/packages.el @@ -4,7 +4,7 @@ (package! pass) (package! password-store) -(when (featurep! +auth) +(when (and EMACS26+ (featurep! +auth)) (package! auth-source-pass)) (when (featurep! :completion helm) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index 0dd7c0fc9..100246093 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -1,8 +1,7 @@ ;;; tools/pdf/config.el -*- lexical-binding: t; -*- (def-package! pdf-tools - :mode ("\\.pdf$" . pdf-view-mode) - :init (load "pdf-tools-autoloads" nil t) + :mode ("\\.pdf\\'" . pdf-view-mode) :config (unless noninteractive (pdf-tools-install)) diff --git a/modules/tools/prodigy/autoload.el b/modules/tools/prodigy/autoload.el index 88781dd3d..6f27d40df 100644 --- a/modules/tools/prodigy/autoload.el +++ b/modules/tools/prodigy/autoload.el @@ -1,5 +1,11 @@ ;;; tools/prodigy/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(def-setting! :service (&rest plist) + "TODO" + `(after! prodigy + (prodigy-define-service ,@plist))) + ;;;###autoload (defun +prodigy/create () "Interactively create a new prodigy service." @@ -32,3 +38,17 @@ (file-directory-p (plist-get service :project))) collect service into services finally do (setq prodigy-service services))) + +;;;###autoload +(defun +prodigy*services (orig-fn &rest args) + "Adds a new :project property to prodigy services, which hides the service +unless invoked from the relevant project." + (let ((project-root (downcase (doom-project-root))) + (services (apply orig-fn args))) + (if current-prefix-arg + services + (cl-remove-if-not (lambda (service) + (let ((project (plist-get service :project))) + (or (not project) + (file-in-directory-p project-root project)))) + services)))) diff --git a/modules/tools/prodigy/config.el b/modules/tools/prodigy/config.el index 8763db6a3..e9ba7ca32 100644 --- a/modules/tools/prodigy/config.el +++ b/modules/tools/prodigy/config.el @@ -1,38 +1,13 @@ ;;; tools/prodigy/config.el -*- lexical-binding: t; -*- -(def-setting! :service (&rest plist) - "TODO" - `(after! prodigy - (prodigy-define-service ,@plist))) - - -;; -;; Plugins -;; - -(def-package! prodigy - :commands (prodigy prodigy-view-mode prodigy-add-filter) - :config +(after! prodigy (set! :evil-state 'prodigy-mode 'emacs) ;; Make services, etc persistent between Emacs sessions (doom-cache-persist :prodigy '(prodigy-services prodigy-tags prodigy-filters)) - (defun +prodigy*services (orig-fn &rest args) - "Adds a new :project property to prodigy services, which hides the service -unless invoked from the relevant project." - (let ((project-root (downcase (doom-project-root))) - (services (apply orig-fn args))) - (if current-prefix-arg - services - (cl-remove-if-not (lambda (service) - (let ((project (plist-get service :project))) - (or (not project) - (file-in-directory-p project-root project)))) - services)))) (advice-add #'prodigy-services :around #'+prodigy*services) - ;; Keybindings (map! :map prodigy-mode-map "d" #'+prodigy/delete)) diff --git a/modules/tools/rgb/config.el b/modules/tools/rgb/config.el index b69768ce7..08a93d06a 100644 --- a/modules/tools/rgb/config.el +++ b/modules/tools/rgb/config.el @@ -4,13 +4,10 @@ ;; Plugins ;; -(def-package! rainbow-mode) - - (def-package! kurecolor :after rainbow-mode :config - (def-hydra! +rgb@kurecolor (:color pink :hint nil) + (defhydra +rgb@kurecolor (:color pink :hint nil) " Inc/Dec _w_/_W_ brightness _d_/_D_ saturation _e_/_E_ hue " ("w" kurecolor-decrease-brightness-by-step) diff --git a/modules/tools/rotate-text/autoload.el b/modules/tools/rotate-text/autoload.el new file mode 100644 index 000000000..1cae4ba58 --- /dev/null +++ b/modules/tools/rotate-text/autoload.el @@ -0,0 +1,17 @@ +;;; tools/rotate-text/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(def-setting! :rotate (modes &rest plist) + "Declare :symbols, :words or :patterns (all lists of strings) that +`rotate-text' will cycle through." + (declare (indent 1)) + (let* ((modes (doom-enlist (doom-unquote modes))) + (fn-name (intern (format "doom--rotate-%s" (mapconcat #'symbol-name modes "-"))))) + `(progn + (defun ,fn-name () + (require 'rotate-text) + (let ((plist (list ,@plist))) + (setq rotate-text-local-symbols (plist-get plist :symbols) + rotate-text-local-words (plist-get plist :words) + rotate-text-local-patterns (plist-get plist :patterns)))) + (add-hook! ,modes #',fn-name)))) diff --git a/modules/tools/rotate-text/config.el b/modules/tools/rotate-text/config.el index 5fb81df5a..df310e3a5 100644 --- a/modules/tools/rotate-text/config.el +++ b/modules/tools/rotate-text/config.el @@ -1,22 +1,4 @@ ;;; tools/rotate-text/config.el -*- lexical-binding: t; -*- -(def-package! rotate-text - :commands (rotate-text rotate-text-backward) - :config - (push '("true" "false") rotate-text-words)) - - -(def-setting! :rotate (modes &rest plist) - "Declare :symbols, :words or :patterns (all lists of strings) that -`rotate-text' will cycle through." - (declare (indent 1)) - (let* ((modes (doom-enlist (doom-unquote modes))) - (fn-name (intern (format "doom--rotate-%s" (mapconcat #'symbol-name modes "-"))))) - `(progn - (defun ,fn-name () - (let ((plist (list ,@plist))) - (setq rotate-text-local-symbols (plist-get plist :symbols) - rotate-text-local-words (plist-get plist :words) - rotate-text-local-patterns (plist-get plist :patterns)))) - (add-hook! ,modes #',fn-name)))) - +(after! rotate-text + (add-to-list 'rotate-text-words '("true" "false"))) diff --git a/modules/tools/term/config.el b/modules/tools/term/config.el index adb1d470b..3aaad2453 100644 --- a/modules/tools/term/config.el +++ b/modules/tools/term/config.el @@ -1,8 +1,14 @@ ;;; tools/term/config.el -*- lexical-binding: t; -*- -(def-package! multi-term - :commands (multi-term multi-term-next multi-term-prev) - :config - (setq multi-term-program (getenv "SHELL") - multi-term-dedicated-window-height 20 - multi-term-switch-after-close 'PREVIOUS)) +;; `multi-term' +(setq multi-term-dedicated-window-height 20 + multi-term-switch-after-close 'PREVIOUS) + +;; `term' (built-in) +(after! term + (set! :env "SHELL") + + ;; Consider term buffers real + (defun +term-p (buf) + (eq (buffer-local-value 'major-mode buf) 'term-mode)) + (add-to-list 'doom-real-buffer-functions #'+term-p #'eq)) diff --git a/modules/tools/tmux/autoload/tmux.el b/modules/tools/tmux/autoload/tmux.el index fdfaa6c5e..9508172a2 100644 --- a/modules/tools/tmux/autoload/tmux.el +++ b/modules/tools/tmux/autoload/tmux.el @@ -63,7 +63,7 @@ but do not execute them." (interactive "P") (unless +tmux-last-command (user-error "No last command to run")) - (apply #'+tmux (car +tmux-last-command) (cdr +tmux-last-command))) + (apply #'+tmux +tmux-last-command)) ;;;###autoload (defun +tmux/cd (&optional arg directory) diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index 841235faf..a446f2d8a 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -114,16 +114,17 @@ Possible values: (defun +doom-dashboard|init () "Initializes Doom's dashboard." - (add-hook 'window-configuration-change-hook #'+doom-dashboard|resize) - (add-hook 'window-size-change-functions #'+doom-dashboard|resize) - (add-hook 'kill-buffer-query-functions #'+doom-dashboard|reload-on-kill) - (add-hook 'doom-after-switch-buffer-hook #'+doom-dashboard|reload-on-kill) - (unless (daemonp) - (add-hook 'after-make-frame-functions #'+doom-dashboard|make-frame)) - ;; `persp-mode' integration: update `default-directory' when switching - (add-hook 'persp-created-functions #'+doom-dashboard|record-project) - (add-hook 'persp-activated-functions #'+doom-dashboard|detect-project) - (add-hook 'persp-before-switch-functions #'+doom-dashboard|record-project) + (unless noninteractive + (add-hook 'window-configuration-change-hook #'+doom-dashboard|resize) + (add-hook 'window-size-change-functions #'+doom-dashboard|resize) + (add-hook 'kill-buffer-query-functions #'+doom-dashboard|reload-on-kill) + (add-hook 'doom-after-switch-buffer-hook #'+doom-dashboard|reload-on-kill) + (unless (daemonp) + (add-hook 'after-make-frame-functions #'+doom-dashboard|make-frame)) + ;; `persp-mode' integration: update `default-directory' when switching + (add-hook 'persp-created-functions #'+doom-dashboard|record-project) + (add-hook 'persp-activated-functions #'+doom-dashboard|detect-project) + (add-hook 'persp-before-switch-functions #'+doom-dashboard|record-project)) (+doom-dashboard-reload t)) (defun +doom-dashboard|reload-on-kill () @@ -329,11 +330,11 @@ controlled by `+doom-dashboard-pwd-policy'." "\n\n")))) `(("Homepage" "mark-github" (browse-url "https://github.com/hlissner/doom-emacs")) - ,(when (and (featurep! :feature workspaces) + ,(when (and (bound-and-true-p persp-mode) (file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir))) '("Reload last session" "history" (+workspace/load-session))) - ,(when (featurep! :lang org) + ,(when (fboundp 'org-agenda-list) '("See agenda for this week" "calendar" (call-interactively #'org-agenda-list))) ("Recently opened files" "file-text" diff --git a/modules/ui/doom-modeline/config.el b/modules/ui/doom-modeline/config.el index f35a3a478..759132f89 100644 --- a/modules/ui/doom-modeline/config.el +++ b/modules/ui/doom-modeline/config.el @@ -4,11 +4,7 @@ ;; mode-line. (def-package! anzu - :commands (anzu-mode global-anzu-mode - anzu-query-replace anzu-query-replace-regexp - anzu-query-replace-at-cursor anzu-replace-at-cursor-thing) - :init - (add-transient-hook! #'isearch-mode (require 'anzu)) + :after-call isearch-mode :config (setq anzu-cons-mode-line-p nil anzu-minimum-input-length 1 @@ -35,10 +31,7 @@ (def-package! evil-anzu - :defer t - :init - (add-transient-hook! #'evil-ex-start-search (require 'evil-anzu)) - (add-transient-hook! #'evil-ex-start-word-search (require 'evil-anzu))) + :after-call (evil-ex-start-search evil-ex-start-word-search)) ;; fish-style modeline diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index 3f8561302..98edef3da 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -22,10 +22,11 @@ ;; (def-package! doom-themes - :config + :defer t + :init (unless doom-theme (setq doom-theme 'doom-one)) - + :config ;; Reload common faces when reloading doom-themes live (defun +doom*reload-common (&rest _) (load "doom-themes-common.el" nil t)) (advice-add #'doom//reload-theme :before #'+doom*reload-common) @@ -41,7 +42,7 @@ (def-package! solaire-mode - :commands (solaire-mode turn-on-solaire-mode solaire-mode-swap-bg) + :defer t :init (defun +doom|solaire-mode-swap-bg-maybe () (when-let* ((rule (assq doom-theme +doom-solaire-themes))) @@ -49,8 +50,7 @@ (if (cdr rule) (solaire-mode-swap-bg)))) (add-hook 'doom-load-theme-hook #'+doom|solaire-mode-swap-bg-maybe t) :config - (add-hook 'after-change-major-mode-hook #'turn-on-solaire-mode) - (setq solaire-mode-real-buffer-fn #'doom-real-buffer-p) + (add-hook 'change-major-mode-after-body-hook #'turn-on-solaire-mode) ;; fringe can become unstyled when deleting or focusing frames (add-hook 'focus-in-hook #'solaire-mode-reset) ;; Prevent color glitches when reloading either DOOM or loading a new theme diff --git a/modules/ui/evil-goggles/config.el b/modules/ui/evil-goggles/config.el index 1a17d0ebe..3d1f65fe2 100644 --- a/modules/ui/evil-goggles/config.el +++ b/modules/ui/evil-goggles/config.el @@ -2,11 +2,13 @@ (def-package! evil-goggles :when (featurep! :feature evil) - :defer pre-command-hook + :after-call pre-command-hook :init (setq evil-goggles-duration 0.05 evil-goggles-pulse nil ; too slow - evil-goggles-enable-delete t + ;; evil-goggles provides a good indicator of what has been affected. + ;; delete/change is obvious, so I'd rather disable it for these. + evil-goggles-enable-delete nil evil-goggles-enable-change nil) :config (evil-goggles-mode +1)) diff --git a/modules/ui/nav-flash/config.el b/modules/ui/nav-flash/config.el index a1519f993..3ab42ec28 100644 --- a/modules/ui/nav-flash/config.el +++ b/modules/ui/nav-flash/config.el @@ -1,7 +1,7 @@ ;;; ui/nav-flash/config.el -*- lexical-binding: t; -*- (def-package! nav-flash - :commands nav-flash-show + :defer t :init ;; NOTE In :feature lookup `recenter' is hooked to a bunch of jumping ;; commands, which will trigger nav-flash. diff --git a/modules/ui/neotree/config.el b/modules/ui/neotree/config.el index 2723ab273..f7c8847db 100644 --- a/modules/ui/neotree/config.el +++ b/modules/ui/neotree/config.el @@ -24,13 +24,14 @@ neo-show-hidden-files t neo-hidden-regexp-list '(;; vcs folders - "^\\.\\(git\\|hg\\|svn\\)$" + "^\\.\\(?:git\\|hg\\|svn\\)$" ;; compiled files - "\\.\\(pyc\\|o\\|elc\\|lock\\|css.map\\|class\\)$" + "\\.\\(?:pyc\\|o\\|elc\\|lock\\|css.map\\|class\\)$" ;; generated files, caches or local pkgs - "^\\(node_modules\\|vendor\\|.\\(project\\|cask\\|yardoc\\|sass-cache\\)\\)$" + "^\\(?:node_modules\\|vendor\\|.\\(project\\|cask\\|yardoc\\|sass-cache\\)\\)$" ;; org-mode folders - "^\\.\\(sync\\|export\\|attach\\)$" + "^\\.\\(?:sync\\|export\\|attach\\)$" + ;; temp files "~$" "^#.*#$")) diff --git a/modules/ui/popup/autoload.el b/modules/ui/popup/autoload.el index 8b33d7d91..c1e318514 100644 --- a/modules/ui/popup/autoload.el +++ b/modules/ui/popup/autoload.el @@ -1,5 +1,73 @@ ;;; ui/popup/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(defvar +popup--display-buffer-alist nil) + +;;;###autoload +(def-setting! :popup (condition &optional alist parameters) + "Register a popup rule. + +CONDITION can be a regexp string or a function. See `display-buffer' for a list +of possible entries for ALIST, which tells the display system how to initialize +the popup window. PARAMETERS is an alist of window parameters. See +`+popup-window-parameters' for a list of custom parameters provided by the popup +module. + +ALIST supports one custom parameter: `size', which will resolve to +`window-height' or `window-width' depending on `side'." + `(progn + (+popup-define ,condition ,alist ,parameters) + (when (bound-and-true-p +popup-mode) + (setq display-buffer-alist +popup--display-buffer-alist)) + +popup--display-buffer-alist)) + +;;;###autoload +(def-setting! :popups (&rest rules) + "Register multiple popup rules with :popup setting (`doom--set:popup'). For +example: + + (set! :popups + (\"^ \\*\" '((slot . 1) (vslot . -1) (size . +popup-shrink-to-fit))) + (\"^\\*\" '((slot . 1) (vslot . -1)) '((select . t))))" + `(progn + ,@(cl-loop for rule in rules collect `(+popup-define ,@rule)) + (when (bound-and-true-p +popup-mode) + (setq display-buffer-alist +popup--display-buffer-alist)) + +popup--display-buffer-alist)) + +;;;###autoload +(defsubst +popup-define (condition &optional alist parameters) + "Define a popup rule. + +The buffers of new windows displayed by `pop-to-buffer' and `display-buffer' +will be tested against CONDITION, which is either a) a regexp string (which is +matched against the buffer's name) or b) a function that takes no arguments and +returns a boolean. + +If CONDITION is met, the buffer will be displayed in a popup window with ALIST +and window PARAMETERS. See `display-buffer-alist' for details on what ALIST may +contain and `+popup-window-parameters' for what window parameters that the popup +module supports. + +ALIST also supports the `size' parameter, which will be translated to +`window-width' or `window-height' depending on `side'. + +If certain attributes/parameters are omitted, the ones from +`+popup-default-alist' and `+popup-default-parameters' will be used." + (declare (indent 1)) + (push (if (eq alist :ignore) + (list condition nil) + `(,condition + (+popup-buffer) + ,@alist + (window-parameters ,@parameters))) + +popup--display-buffer-alist)) + + +;; +;; Library +;; + (defvar +popup--populate-wparams (not EMACS26+)) (defvar +popup--inhibit-transient nil) (defvar +popup--inhibit-select nil) @@ -68,7 +136,8 @@ and enables `+popup-buffer-mode'." ttl) (when (and (buffer-file-name buffer) (buffer-modified-p buffer) - (y-or-n-p "Popup buffer is modified. Save it?")) + (or (+popup-parameter-fn 'autosave window buffer) + (y-or-n-p "Popup buffer is modified. Save it?"))) (with-current-buffer buffer (save-buffer))) (set-buffer-modified-p nil) (let ((ignore-window-parameters t)) diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index 62c201748..d83c8d708 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -45,6 +45,18 @@ Modifying this has no effect, unless done before ui/popup loads. modeline defined with `def-modeline!', nil (show no modeline) or a function that returns one of these. The function takes one argument: the popup buffer. +(autosave . CDR) + This parameter determines what to do with modified buffers in closing popup + windows. CDR can be a t, 'ignore, a function or nil. + + If t, no prompts. Just save them automatically (if they're file-visiting + buffers). + If 'ignore, no prompts, no saving. Just silently kill it. + If nil (the default), prompt the user what to do if the buffer is + file-visiting and modified. + If a function, the return value must return one of the other values. It takes + two arguments: the popup window and buffer. + (popup . t) This is for internal use, do not change this. It simply marks a window as a popup window. @@ -103,13 +115,13 @@ deleted.") '((transient . 0) (quit . t))) (+popup-define "^\\*\\(?:scratch\\|Messages\\)" nil - '((transient))) + '((autosave . t) (transient))) (+popup-define "^\\*doom \\(?:term\\|eshell\\)" '((size . 0.25) (vslot . -10)) '((select . t) (quit) (transient . 0))) (+popup-define "^\\*doom:" '((size . 0.35) (side . bottom)) - '((select . t) (modeline . t) (quit) (transient . t))) + '((autosave . t) (select . t) (modeline . t) (quit) (transient . t))) (+popup-define "^\\*\\(?:\\(?:Pp E\\|doom e\\)val\\)" '((size . +popup-shrink-to-fit)) '((transient . 0) (select . ignore))) diff --git a/modules/ui/popup/init.el b/modules/ui/popup/init.el deleted file mode 100644 index 8b6b99f16..000000000 --- a/modules/ui/popup/init.el +++ /dev/null @@ -1,61 +0,0 @@ -;;; ui/popup/init.el -*- lexical-binding: t; -*- - -(defvar +popup--display-buffer-alist nil) - -(defun +popup-define (condition &optional alist parameters) - "Define a popup rule. - -The buffers of new windows displayed by `pop-to-buffer' and `display-buffer' -will be tested against CONDITION, which is either a) a regexp string (which is -matched against the buffer's name) or b) a function that takes no arguments and -returns a boolean. - -If CONDITION is met, the buffer will be displayed in a popup window with ALIST -and window PARAMETERS. See `display-buffer-alist' for details on what ALIST may -contain and `+popup-window-parameters' for what window parameters that the popup -module supports. - -ALIST also supports the `size' parameter, which will be translated to -`window-width' or `window-height' depending on `side'. - -If certain attributes/parameters are omitted, the ones from -`+popup-default-alist' and `+popup-default-parameters' will be used." - (declare (indent 1)) - (push (if (eq alist :ignore) - (list condition nil) - `(,condition - (+popup-buffer) - ,@alist - (window-parameters ,@parameters))) - +popup--display-buffer-alist)) - -;; -(def-setting! :popup (condition &optional alist parameters) - "Register a popup rule. - -CONDITION can be a regexp string or a function. See `display-buffer' for a list -of possible entries for ALIST, which tells the display system how to initialize -the popup window. PARAMETERS is an alist of window parameters. See -`+popup-window-parameters' for a list of custom parameters provided by the popup -module. - -ALIST supports one custom parameter: `size', which will resolve to -`window-height' or `window-width' depending on `side'." - `(progn - (+popup-define ,condition ,alist ,parameters) - (when (bound-and-true-p +popup-mode) - (setq display-buffer-alist +popup--display-buffer-alist)) - +popup--display-buffer-alist)) - -(def-setting! :popups (&rest rules) - "Register multiple popup rules with :popup setting (`doom--set:popup'). For -example: - - (set! :popups - (\"^ \\*\" '((slot . 1) (vslot . -1) (size . +popup-shrink-to-fit))) - (\"^\\*\" '((slot . 1) (vslot . -1)) '((select . t))))" - `(progn - ,@(cl-loop for rule in rules collect `(+popup-define ,@rule)) - (when (bound-and-true-p +popup-mode) - (setq display-buffer-alist +popup--display-buffer-alist)) - +popup--display-buffer-alist)) diff --git a/modules/ui/unicode/autoload.el b/modules/ui/unicode/autoload.el new file mode 100644 index 000000000..1af675c04 --- /dev/null +++ b/modules/ui/unicode/autoload.el @@ -0,0 +1,23 @@ +;;; ui/unicode/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(add-hook 'doom-post-init-hook #'+unicode|init-fonts) + +;;;###autoload +(defun +unicode|init-fonts () + "Set up `unicode-fonts' to eventually run; accomodating the daemon, if +necessary." + (setq-default bidi-display-reordering t + doom-unicode-font nil) + (if initial-window-system + (+unicode|setup-fonts (selected-frame)) + (add-hook 'after-make-frame-functions #'+unicode|setup-fonts))) + +;;;###autoload +(defun +unicode|setup-fonts (&optional frame) + "Initialize `unicode-fonts', if in a GUI session." + (when (and frame (display-graphic-p frame)) + (with-selected-frame frame + (require 'unicode-fonts) + ;; NOTE will impact startup time on first run + (unicode-fonts-setup)))) diff --git a/modules/ui/unicode/config.el b/modules/ui/unicode/config.el deleted file mode 100644 index a41ae6554..000000000 --- a/modules/ui/unicode/config.el +++ /dev/null @@ -1,19 +0,0 @@ -;;; ui/unicode/config.el -*- lexical-binding: t; -*- - -(def-package! unicode-fonts - :init - (setq-default bidi-display-reordering t - doom-unicode-font nil) - - (defun +unicode|init-fonts (&optional frame) - "Initialize `unicode-fonts', if in a GUI session." - (when (and frame (display-graphic-p frame)) - (with-selected-frame frame - (require 'unicode-fonts) - ;; NOTE will impact startup time on first run - (unicode-fonts-setup)))) - - (add-hook! 'after-init-hook - (if initial-window-system - (+unicode|init-fonts (selected-frame)) - (add-hook 'after-make-frame-functions #'+unicode|init-fonts)))) diff --git a/modules/ui/vi-tilde-fringe/autoload.el b/modules/ui/vi-tilde-fringe/autoload.el new file mode 100644 index 000000000..ab9029cfc --- /dev/null +++ b/modules/ui/vi-tilde-fringe/autoload.el @@ -0,0 +1,4 @@ +;;; ui/vi-tilde-fringe/autoload.el -*- lexical-binding: t; -*- + +;;;###autoload +(add-hook! (prog-mode text-mode conf-mode) #'vi-tilde-fringe-mode) diff --git a/modules/ui/vi-tilde-fringe/config.el b/modules/ui/vi-tilde-fringe/config.el deleted file mode 100644 index 1f099b7aa..000000000 --- a/modules/ui/vi-tilde-fringe/config.el +++ /dev/null @@ -1,6 +0,0 @@ -;;; ui/vi-tilde-fringe/config.el -*- lexical-binding: t; -*- - -;; indicators for empty lines past EOF -(def-package! vi-tilde-fringe - :hook ((prog-mode text-mode conf-mode) . vi-tilde-fringe-mode)) - diff --git a/modules/ui/window-select/config.el b/modules/ui/window-select/config.el index b055bb779..3989310e3 100644 --- a/modules/ui/window-select/config.el +++ b/modules/ui/window-select/config.el @@ -2,9 +2,7 @@ (def-package! switch-window :when (featurep! +switch-window) - :commands (switch-window switch-window-then-maximize switch-window-then-split-below - switch-window-then-split-right switch-window-then-delete - switch-window-then-swap-buffer) + :defer t :init (define-key global-map [remap other-window] #'switch-window) :config @@ -14,8 +12,7 @@ (def-package! ace-window :unless (featurep! +switch-window) - :commands (ace-window ace-swap-window ace-delete-window - ace-select-window ace-delete-other-windows) + :defer t :init (define-key global-map [remap other-window] #'ace-window) :config