When no regular expression is present, it's faster and simpler to use search-forward Signed-off-by: Rudi Grinberg <rudi.grinberg@gmail.com>
257 lines
9.6 KiB
Bash
Executable file
257 lines
9.6 KiB
Bash
Executable file
#!/usr/bin/env sh
|
|
":"; command -v emacs >/dev/null || { >&2 echo "Emacs isn't installed"; exit 1; } # -*-emacs-lisp-*-
|
|
":"; VERSION=$(emacs --version | head -n1)
|
|
":"; case $VERSION in *\ 2[0-2].[0-1].[0-9]) echo "You're running $VERSION"; echo "That version is too old to run the doctor (25.3 minimum). Check your PATH"; echo; exit 2 ;; esac
|
|
":"; exec emacs --quick --script "$0"; exit 0
|
|
|
|
;; The Doom doctor is essentially one big, self-contained elisp shell script
|
|
;; that uses a series of simple heuristics to diagnose common issues on your
|
|
;; system. Issues that could intefere with Doom Emacs.
|
|
;;
|
|
;; Doom modules may optionally have a doctor.el file to run their own heuristics
|
|
;; in. Doctor scripts may run in versions of Emacs as old as Emacs 23, so make
|
|
;; no assumptions about what's available in the standard library (e.g. avoid
|
|
;; cl/cl-lib, subr-x, map, seq, etc).
|
|
|
|
|
|
;; Ensure Doom doctor always runs out of the current Emacs directory (optionally
|
|
;; specified by the EMACSDIR envvar)
|
|
(setq user-emacs-directory
|
|
(or (getenv "EMACSDIR")
|
|
(expand-file-name "../" (file-name-directory (file-truename load-file-name))))
|
|
default-directory user-emacs-directory)
|
|
|
|
(unless (file-directory-p user-emacs-directory)
|
|
(error "Couldn't find a Doom config!"))
|
|
(unless noninteractive
|
|
(error "This script must not be run from an interactive session."))
|
|
(when (getenv "DEBUG")
|
|
(setq debug-on-error t))
|
|
|
|
(require 'subr-x)
|
|
(require 'pp)
|
|
(load (expand-file-name "core/autoload/format" user-emacs-directory) nil t)
|
|
|
|
|
|
(defvar doom-init-p nil)
|
|
(defvar doom-warnings 0)
|
|
(defvar doom-errors 0)
|
|
|
|
|
|
;;; Helpers
|
|
|
|
(defun sh (cmd &rest args)
|
|
(ignore-errors
|
|
(string-trim-right
|
|
(shell-command-to-string (if args (apply #'format cmd args) cmd)))))
|
|
|
|
(defun elc-check-dir (dir)
|
|
(dolist (file (directory-files-recursively dir "\\.elc$"))
|
|
(when (file-newer-than-file-p (concat (file-name-sans-extension file) ".el")
|
|
file)
|
|
(warn! "%s is out-of-date" (abbreviate-file-name file)))))
|
|
|
|
(defmacro assert! (condition message &rest args)
|
|
`(unless ,condition
|
|
(error! ,message ,@args)))
|
|
|
|
|
|
;;; Logging
|
|
|
|
(defvar indent 0)
|
|
(defvar prefix "")
|
|
|
|
(defmacro msg! (msg &rest args)
|
|
`(print!
|
|
(indent indent
|
|
(format (concat prefix ,msg)
|
|
,@args))))
|
|
|
|
(defmacro error! (&rest args)
|
|
`(progn (msg! (red ,@args))
|
|
(setq doom-errors (+ doom-errors 1))))
|
|
(defmacro warn! (&rest args)
|
|
`(progn (msg! (yellow ,@args))
|
|
(setq doom-warnings (+ doom-warnings 1))))
|
|
(defmacro success! (&rest args) `(msg! (green ,@args)))
|
|
(defmacro section! (&rest args) `(msg! (bold (blue ,@args))))
|
|
|
|
(defmacro explain! (&rest args)
|
|
`(msg! (indent (+ indent 2) (autofill ,@args))))
|
|
|
|
|
|
;;; Polyfills
|
|
;; early versions of emacs won't have this
|
|
(unless (fboundp 'string-match-p)
|
|
(defun string-match-p (regexp string &optional start)
|
|
(save-match-data
|
|
(string-match regexp string &optional start))))
|
|
|
|
;; subr-x don't exist in older versions of Emacs
|
|
(unless (fboundp 'string-trim-right)
|
|
(defsubst string-trim-right (string &optional regexp)
|
|
(if (string-match (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") string)
|
|
(replace-match "" t t string)
|
|
string)))
|
|
|
|
|
|
;;
|
|
;;; Basic diagnostics
|
|
|
|
(msg! (bold "Doom Doctor"))
|
|
(msg! "Emacs v%s" emacs-version)
|
|
(msg! "Doom v%s (%s)"
|
|
(or (let ((core-file (expand-file-name "core/core.el" user-emacs-directory)))
|
|
(and (file-exists-p core-file)
|
|
(ignore-errors
|
|
(with-temp-buffer
|
|
(insert-file-contents-literally core-file)
|
|
(goto-char (point-min))
|
|
(when (search-forward "doom-version" nil t)
|
|
(forward-char)
|
|
(sexp-at-point))))))
|
|
"???")
|
|
(if (and (executable-find "git")
|
|
(file-directory-p (expand-file-name ".git" user-emacs-directory)))
|
|
(sh "git log -1 --format=\"%D %h %ci\"")
|
|
"n/a"))
|
|
(msg! "shell: %s%s"
|
|
(getenv "SHELL")
|
|
(if (equal (getenv "SHELL") (sh "echo $SHELL"))
|
|
""
|
|
(red " (mismatch)")))
|
|
(when (boundp 'system-configuration-features)
|
|
(msg! "Compiled with:\n%s" (indent 2 system-configuration-features)))
|
|
(msg! "uname -msrv:\n%s\n" (indent 2 (sh "uname -msrv")))
|
|
|
|
|
|
;;
|
|
;;; Check if Emacs is set up correctly
|
|
|
|
(section! "Checking Emacs")
|
|
(let ((indent 2))
|
|
(section! "Checking your Emacs version is 25.3 or newer...")
|
|
(when (version< emacs-version "25.3")
|
|
(error! "Important: Emacs %s detected [%s]" emacs-version (executable-find "emacs"))
|
|
(explain!
|
|
"DOOM only supports >= 25.3. Perhaps your PATH wasn't set up properly."
|
|
(when (eq system-type 'darwin)
|
|
(concat "\nMacOS users should use homebrew (https://brew.sh) to install Emacs\n"
|
|
" brew install emacs --with-modules --with-imagemagick --with-cocoa"))))
|
|
|
|
(section! "Checking for Emacs config conflicts...")
|
|
(when (file-exists-p "~/.emacs")
|
|
(warn! "Detected an ~/.emacs file, which may prevent Doom from loading")
|
|
(explain! "If Emacs finds an ~/.emacs file, it will ignore ~/.emacs.d, where Doom is "
|
|
"typically installed. If you're seeing a vanilla Emacs splash screen, this "
|
|
"may explain why. If you use Chemacs, you may ignore this warning."))
|
|
|
|
(section! "Checking for private config conflicts...")
|
|
(let ((xdg-dir (concat (or (getenv "XDG_CONFIG_HOME")
|
|
"~/.config")
|
|
"/doom/"))
|
|
(doom-dir (or (getenv "DOOMDIR")
|
|
"~/.doom.d/")))
|
|
(when (and (not (file-equal-p xdg-dir doom-dir))
|
|
(file-directory-p xdg-dir)
|
|
(file-directory-p doom-dir))
|
|
(warn! "Detected two private configs, in %s and %s"
|
|
(abbreviate-file-name xdg-dir)
|
|
doom-dir)
|
|
(explain! "The second directory will be ignored, as it has lower precedence.")))
|
|
|
|
(section! "Checking for stale elc files...")
|
|
(elc-check-dir user-emacs-directory))
|
|
|
|
|
|
;;
|
|
;;; Check if system environment is set up correctly
|
|
|
|
(section! "Checking your system...")
|
|
(let ((indent 2))
|
|
;; on windows?
|
|
(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!")))
|
|
|
|
|
|
;;
|
|
;;; Check if Doom Emacs is set up correctly
|
|
|
|
(condition-case-unless-debug ex
|
|
(let ((after-init-time (current-time))
|
|
(doom-format-backend 'ansi)
|
|
noninteractive)
|
|
(section! "Checking DOOM Emacs...")
|
|
(load (concat user-emacs-directory "core/core.el") nil t)
|
|
(unless (file-directory-p doom-private-dir)
|
|
(error "No DOOMDIR was found, did you run `doom install` yet?"))
|
|
|
|
(let ((indent 2))
|
|
;; Make sure Doom is initialized and loaded
|
|
(doom-initialize 'force)
|
|
(doom-initialize-core)
|
|
(success! "Initialized Doom Emacs %s" doom-version)
|
|
|
|
(doom-initialize-modules)
|
|
(if (hash-table-p doom-modules)
|
|
(success! "Initialized %d modules" (hash-table-count doom-modules))
|
|
(warn! "Failed to load any modules. Do you have an private init.el?"))
|
|
|
|
(doom-initialize-packages)
|
|
(success! "Initialized %d packages" (length doom-packages))
|
|
|
|
(section! "Checking Doom core for irregularities...")
|
|
(let ((indent (+ indent 2)))
|
|
(load (expand-file-name "doctor.el" doom-core-dir) nil 'nomessage))
|
|
|
|
(section! "Checking for stale elc files in your DOOMDIR...")
|
|
(when (file-directory-p doom-private-dir)
|
|
(let ((indent (+ indent 2)))
|
|
(elc-check-dir doom-private-dir)))
|
|
|
|
(when doom-modules
|
|
(section! "Checking your enabled modules...")
|
|
(let ((indent (+ indent 2)))
|
|
(advice-add #'require :around #'doom-shut-up-a)
|
|
(maphash
|
|
(lambda (key plist)
|
|
(let ((prefix (format! (bold "(%s %s) " (car key) (cdr key)))))
|
|
(condition-case-unless-debug ex
|
|
(let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
|
|
(packages-file (doom-module-path (car key) (cdr key) "packages.el")))
|
|
(cl-loop for name in (let (doom-packages
|
|
doom-disabled-packages)
|
|
(load packages-file 'noerror 'nomessage)
|
|
(mapcar #'car doom-packages))
|
|
unless (or (doom-package-get name :disable)
|
|
(eval (doom-package-get name :ignore))
|
|
(doom-package-built-in-p name)
|
|
(doom-package-installed-p name))
|
|
do (error! "%s is not installed" name))
|
|
(load doctor-file 'noerror 'nomessage))
|
|
(file-missing (error! "%s" (error-message-string ex)))
|
|
(error (error! "Syntax error: %s" ex)))))
|
|
doom-modules)))))
|
|
(error
|
|
(warn! "Attempt to load DOOM failed\n %s\n"
|
|
(or (cdr-safe ex) (car ex)))
|
|
(setq doom-modules nil)))
|
|
|
|
|
|
;;
|
|
;;; Final report
|
|
|
|
(message "")
|
|
(dolist (msg (list (list doom-errors "error" 'red)
|
|
(list doom-warnings "warning" 'yellow)))
|
|
(when (> (car msg) 0)
|
|
(msg! (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!"))
|