;;; core/autoload/debug.el -*- lexical-binding: t; -*- ;; ;;; Doom's debug mode ;;;###autoload (defvar doom-debug-variables '(async-debug debug-on-error doom-debug-p garbage-collection-messages gcmh-verbose init-file-debug jka-compr-verbose url-debug use-package-verbose (message-log-max . 16384)) "A list of variable to toggle on `doom-debug-mode'. Each entry can be a variable symbol or a cons cell whose CAR is the variable symbol and CDR is the value to set it to when `doom-debug-mode' is activated.") (defvar doom--debug-vars-old-values nil) (defvar doom--debug-vars-undefined nil) (defun doom--watch-debug-vars-h (&rest _) (when-let (bound-vars (cl-remove-if-not #'boundp doom--debug-vars-undefined)) (doom-log "New variables available: %s" bound-vars) (let ((message-log-max nil)) (doom-debug-mode -1) (doom-debug-mode +1)))) ;;;###autoload (define-minor-mode doom-debug-mode "Toggle `debug-on-error' and `doom-debug-p' for verbose logging." :init-value nil :global t (let ((enabled doom-debug-mode)) (setq doom--debug-vars-undefined nil) (dolist (var doom-debug-variables) (cond ((listp var) (cl-destructuring-bind (var . val) var (if (not (boundp var)) (add-to-list 'doom--debug-vars-undefined var) (set-default var (if (not enabled) (alist-get var doom--debug-vars-old-values) (setf (alist-get var doom--debug-vars-old-values) (symbol-value var)) val))))) ((if (boundp var) (set-default var enabled) (add-to-list 'doom--debug-vars-undefined var))))) (when (called-interactively-p 'any) (when (fboundp 'explain-pause-mode) (explain-pause-mode (if enabled +1 -1)))) ;; Watch for changes in `doom-debug-variables', or when packages load (and ;; potentially define one of `doom-debug-variables'), in case some of them ;; aren't defined when `doom-debug-mode' is first loaded. (cond (enabled (add-variable-watcher 'doom-debug-variables #'doom--watch-debug-vars-h) (add-hook 'after-load-functions #'doom--watch-debug-vars-h)) (t (remove-variable-watcher 'doom-debug-variables #'doom--watch-debug-vars-h) (remove-hook 'after-load-functions #'doom--watch-debug-vars-h))) (message "Debug mode %s" (if enabled "on" "off")))) ;; ;;; Hooks ;;;###autoload (defun doom-run-all-startup-hooks-h () "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-h" (setq after-init-time (current-time)) (let ((inhibit-startup-hooks nil)) (mapc (lambda (hook) (run-hook-wrapped hook #'doom-try-run-hook)) '(after-init-hook delayed-warnings-hook emacs-startup-hook tty-setup-hook window-setup-hook)))) ;; ;;; Helpers (defsubst doom--collect-forms-in (file form) (when (file-readable-p file) (let (forms) (with-temp-buffer (insert-file-contents file) (let (emacs-lisp-mode) (emacs-lisp-mode)) (while (re-search-forward (format "(%s " (regexp-quote form)) nil t) (let ((ppss (syntax-ppss))) (unless (or (nth 4 ppss) (nth 3 ppss)) (save-excursion (goto-char (match-beginning 0)) (push (sexp-at-point) forms))))) (nreverse forms))))) ;;;###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) (require 'core-packages) (let ((default-directory doom-emacs-dir)) (letf! ((defun sh (&rest args) (cdr (apply #'doom-call-process args))) (defun cat (file &optional limit) (with-temp-buffer (insert-file-contents file nil 0 limit) (buffer-string))) (defun abbrev-path (path) (replace-regexp-in-string (regexp-opt (list (user-login-name)) 'words) "$USER" (abbreviate-file-name path))) (defun symlink-path (file) (format "%s%s" (abbrev-path file) (if (file-symlink-p file) "" (concat " -> " (abbrev-path (file-truename file))))))) `((generated . ,(format-time-string "%b %d, %Y %H:%M:%S")) (distro . ,(list (doom-system-distro-version) (sh "uname" "-msr"))) (emacs . ,(delq nil (list emacs-version emacs-repository-branch (and (stringp emacs-repository-version) (substring emacs-repository-version 0 9)) (symlink-path doom-emacs-dir)))) (doom . ,(list doom-version (sh "git" "log" "-1" "--format=%D %h %ci") (symlink-path doom-private-dir))) (shell . ,(abbrev-path shell-file-name)) (features . ,system-configuration-features) (traits . ,(mapcar #'symbol-name (delq nil (list (cond ((not doom-interactive-p) 'batch) ((display-graphic-p) 'gui) ('tty)) (if (daemonp) 'daemon) (if (and (require 'server) (server-running-p)) 'server-running) (if (boundp 'chemacs-profiles-path) 'chemacs) (if (file-exists-p doom-env-file) 'envvar-file) (if (featurep 'exec-path-from-shell) 'exec-path-from-shell) (if (file-symlink-p user-emacs-directory) 'symlinked-emacsdir) (if (file-symlink-p doom-private-dir) 'symlinked-doomdir) (if (and (stringp custom-file) (file-exists-p custom-file)) 'custom-file) (if (doom-files-in `(,@doom-modules-dirs ,doom-core-dir ,doom-private-dir) :type 'files :match "\\.elc$") 'byte-compiled-config))))) (custom ,@(when (and (stringp custom-file) (file-exists-p custom-file)) (cl-loop for (type var _) in (get 'user 'theme-settings) if (eq type 'theme-value) collect var))) (modules ,@(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 collect (let* ((flags (doom-module-get cat (cdr key) :flags)) (path (doom-module-get cat (cdr key) :path)) (module (append (cond ((null path) (list '&nopath)) ((file-in-directory-p path doom-private-dir) (list '&user))) (if flags `(,(cdr key) ,@flags) (list (cdr key)))))) (if (= (length module) 1) (car module) module))) '("n/a"))) (packages ,@(condition-case e (mapcar #'cdr (doom--collect-forms-in (doom-path doom-private-dir "packages.el") "package!")) (error (format "<%S>" e)))) (unpin ,@(condition-case e (mapcan #'identity (mapcar #'cdr (doom--collect-forms-in (doom-path doom-private-dir "packages.el") "unpin!"))) (error (list (format "<%S>" e))))) (elpa ,@(condition-case e (progn (package-initialize) (cl-loop for (name . _) in package-alist collect (format "%s" name))) (error (format "<%S>" e)))))))) ;; ;;; Commands ;;;###autoload (defun doom/version () "Display the current version of Doom & Emacs, including the current Doom branch and commit." (interactive) (let ((default-directory doom-core-dir)) (print! "Doom v%s (%s)" doom-version (or (cdr (doom-call-process "git" "log" "-1" "--format=%D %h %ci")) "n/a")))) ;;;###autoload (defun doom/info (&optional raw) "Collects some debug information about your Emacs session, formats it and copies it to your clipboard, ready to be pasted into bug reports!" (interactive "P") (let ((buffer (get-buffer-create "*doom info*")) (info (doom-info))) (with-current-buffer buffer (erase-buffer) (if raw (progn (save-excursion (pp info (current-buffer))) (dolist (sym '(modules packages)) (when (re-search-forward (format "^ *\\((%s\\)" sym) nil t) (goto-char (match-beginning 1)) (cl-destructuring-bind (beg . end) (bounds-of-thing-at-point 'sexp) (let ((sexp (prin1-to-string (sexp-at-point)))) (delete-region beg end) (insert sexp)))))) (dolist (spec info) (when (cdr spec) (insert! "%-11s %s\n" ((car spec) (if (listp (cdr spec)) (mapconcat (lambda (x) (format "%s" x)) (cdr spec) " ") (cdr spec))))))) (if (not doom-interactive-p) (print! (buffer-string)) (with-current-buffer (pop-to-buffer buffer) (setq buffer-read-only t) (goto-char (point-min)) (kill-new (buffer-string)) (when (y-or-n-p "Your doom-info was copied to the clipboard.\n\nOpen pastebin.com?") (browse-url "https://pastebin.com"))))))) ;;;###autoload (defun doom/am-i-secure () "Test to see if your root certificates are securely configured in emacs. Some items are not supported by the `nsm.el' module." (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://expired.badssl.com/" "https://wrong.host.badssl.com/" "https://self-signed.badssl.com/" "https://untrusted-root.badssl.com/" ;; "https://revoked.badssl.com/" ;; "https://pinning-test.badssl.com/" "https://sha1-intermediate.badssl.com/" "https://rc4-md5.badssl.com/" "https://rc4.badssl.com/" "https://3des.badssl.com/" "https://null.badssl.com/" "https://sha1-intermediate.badssl.com/" ;; "https://client-cert-missing.badssl.com/" "https://dh480.badssl.com/" "https://dh512.badssl.com/" "https://dh-small-subgroup.badssl.com/" "https://dh-composite.badssl.com/" "https://invalid-expected-sct.badssl.com/" ;; "https://no-sct.badssl.com/" ;; "https://mixed-script.badssl.com/" ;; "https://very.badssl.com/" "https://subdomain.preloaded-hsts.badssl.com/" "https://superfish.badssl.com/" "https://edellroot.badssl.com/" "https://dsdtestprovider.badssl.com/" "https://preact-cli.badssl.com/" "https://webpack-dev-server.badssl.com/" "https://captive-portal.badssl.com/" "https://mitm-software.badssl.com/" "https://sha1-2016.badssl.com/" "https://sha1-2017.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))))) ;; ;;; Reporting bugs ;;;###autoload (defun doom/issue-tracker () "Open Doom Emacs' issue tracker on Discourse." (interactive) (browse-url "https://discourse.doomemacs.org/c/support")) ;;;###autoload (defun doom/report-bug () "Open the browser on our Discourse. If called when a backtrace buffer is present, it and the output of `doom-info' will be automatically appended to the result." (interactive) (browse-url "https://discourse.doomemacs.org/how2report")) ;;;###autoload (defun doom/copy-buffer-contents (buffer-name) "Copy the contents of BUFFER-NAME to your clipboard." (interactive (list (if current-prefix-arg (completing-read "Select buffer: " (mapcar #'buffer-name (buffer-list))) (buffer-name (current-buffer))))) (let ((buffer (get-buffer buffer-name))) (unless (buffer-live-p buffer) (user-error "Buffer isn't live")) (kill-new (with-current-buffer buffer (substring-no-properties (buffer-string)))) (message "Contents of %S were copied to the clipboard" buffer-name))) ;; ;;; 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)))