diff --git a/LICENSE b/LICENSE index 93b8e4bb1..02b93bee4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2018 Henrik Lissner. +Copyright (c) 2016-2019 Henrik Lissner. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/bin/doom b/bin/doom index 7b8780f7a..92b66bd26 100755 --- a/bin/doom +++ b/bin/doom @@ -30,6 +30,7 @@ " doom run -p ~/.other.doom.d -e ~/.other.emacs.d -nw file.txt\n" "\n" (format! (bold "Options:\n")) + " -h --help\t\tSame as help command\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" " -i --insecure\t\tDisable TLS/SSL validation (not recommended)\n" @@ -46,7 +47,7 @@ (while (ignore-errors (string-prefix-p "-" (car args))) (pcase (pop args) ((or "-h" "--help") - (error "Did you mean 'doom help'?")) + (push "help" args)) ((or "-d" "--debug") (setenv "DEBUG" "1") (message "Debug mode on")) diff --git a/bin/doom-doctor b/bin/doom-doctor index ac162f863..2fe9c2241 100755 --- a/bin/doom-doctor +++ b/bin/doom-doctor @@ -4,17 +4,24 @@ ":"; [[ $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. +;; 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 module 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 you +;; are limited to very basic standard library calls (e.g. avoid cl, subr-x, and +;; any Doom dependencies). -;; In case it isn't defined (in really old versions of Emacs, like the one that -;; ships with MacOS). +;; In really old versions of Emacs `user-emacs-directory' isn't defined (defvar user-emacs-directory (expand-file-name "../" (file-name-directory load-file-name))) (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 'pp) @@ -117,10 +124,10 @@ ;; --- is emacs set up properly? ------------------------------ -(when (version< emacs-version "25.1") +(when (version< emacs-version "25.3") (error! "Important: Emacs %s detected [%s]" emacs-version (executable-find "emacs")) (explain! - "DOOM only supports >= 25.1. Perhaps your PATH wasn't set up properly." + "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")))) @@ -166,7 +173,7 @@ font font-dest) (explain! "You can install it by running `M-x all-the-icons-install-fonts' within Emacs.\n\n" "This could also mean you've installed them in non-standard locations, in which " - "case, ignore this warning.")))))) + "case feel free to ignore this warning.")))))) ;; gnutls-cli & openssl (section! "Checking gnutls/openssl...") @@ -289,6 +296,9 @@ (or (cdr-safe ex) (car ex))) (setq doom-modules nil))) +(section! "Checking Doom core for irregularities...") +(load (expand-file-name "doctor.el" doom-core-dir) nil 'nomessage) + (when (bound-and-true-p doom-modules) (section! "Checking your enabled modules...") (let ((indent 4)) @@ -296,22 +306,21 @@ (maphash (lambda (key plist) (let ((prefix (format "%s" (color 1 "(%s %s) " (car key) (cdr key))))) - (condition-case ex + (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")) - doom-packages) - (when (or (file-exists-p doctor-file) - (file-exists-p packages-file)) - (let ((doom--stage 'packages)) - (when (load packages-file t t) - (cl-loop for (name . plist) in doom-packages - unless (or (doom-package-prop name :disable) - (doom-package-prop name :ignore t) - (package-built-in-p name) - (package-installed-p name)) - do (error! "%s is not installed" name))) - (let ((doom--stage 'doctor)) - (load doctor-file t t))))) + (packages-file (doom-module-path (car key) (cdr key) "packages.el"))) + (cl-loop with doom--stage = 'packages + for name in (let (doom-packages + doom-disabled-packages) + (load packages-file 'noerror 'nomessage) + (mapcar #'car doom-packages)) + unless (or (doom-package-prop name :disable) + (doom-package-prop name :ignore t) + (package-built-in-p name) + (package-installed-p name)) + do (error! "%s is not installed" name)) + (let ((doom--stage 'doctor)) + (load doctor-file 'noerror 'nomessage))) (file-missing (error! "%s" (error-message-string ex))) (error (error! "Syntax error: %s" ex))))) doom-modules))) diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el index 46241d4ff..08bcc4889 100644 --- a/core/autoload/buffers.el +++ b/core/autoload/buffers.el @@ -34,11 +34,6 @@ See `doom-real-buffer-p' for more information.") "The name of the buffer to fall back to if no other buffers exist (will create it if it doesn't exist).") -;;;###autoload -(defvar doom-cleanup-hook () - "A list of hooks run when `doom/cleanup-session' is run, meant to clean up -leftover buffers and processes.") - ;; ;; Functions @@ -54,7 +49,8 @@ BUF should be skipped over by functions like `next-buffer' and `other-buffer'." (defun doom-fallback-buffer () "Returns the fallback buffer, creating it if necessary. By default this is the scratch buffer. See `doom-fallback-buffer-name' to change this." - (get-buffer-create doom-fallback-buffer-name)) + (let (buffer-list-update-hook) + (get-buffer-create doom-fallback-buffer-name))) ;;;###autoload (defalias 'doom-buffer-list #'buffer-list) @@ -224,15 +220,17 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (format "Buffer %s is modified; kill anyway?" buf)))) (message "Aborted") (set-buffer-modified-p nil) - (when (or ;; if there aren't more real buffers than visible buffers, - ;; then there are no real, non-visible buffers left. - (not (cl-set-difference (doom-real-buffer-list) - (doom-visible-buffers))) - ;; if we end up back where we start (or previous-buffer - ;; returns nil), we have nowhere left to go - (memq (previous-buffer) (list buf 'nil))) - (switch-to-buffer (doom-fallback-buffer))) - (kill-buffer buf))) + (let (buffer-list-update-hook) + (when (or ;; if there aren't more real buffers than visible buffers, + ;; then there are no real, non-visible buffers left. + (not (cl-set-difference (doom-real-buffer-list) + (doom-visible-buffers))) + ;; if we end up back where we start (or previous-buffer + ;; returns nil), we have nowhere left to go + (memq (switch-to-prev-buffer nil t) (list buf 'nil))) + (switch-to-buffer (doom-fallback-buffer))) + (unless (delq (selected-window) (get-buffer-window-list buf nil t)) + (kill-buffer buf))))) ((funcall orig-fn))))) @@ -254,7 +252,8 @@ If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)." (set-buffer-modified-p nil))) (kill-buffer buffer) (cl-loop for win in windows - if (doom-real-buffer-p (window-buffer win)) + if (and (window-live-p win) + (doom-unreal-buffer-p (window-buffer win))) do (with-selected-window win (previous-buffer))))) ;;;###autoload @@ -264,10 +263,16 @@ If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)." If PROJECT-P (universal argument), don't close windows and only kill buffers that belong to the current project." (interactive "P") + (save-some-buffers) (unless project-p (delete-other-windows)) (switch-to-buffer (doom-fallback-buffer)) - (doom/cleanup-session nil (if project-p (doom-project-buffer-list)))) + (let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list)))) + (mapc #'kill-buffer buffers) + (when (called-interactively-p 'interactive) + (message "Killed %s buffers" + (- (length buffers) + (length (cl-remove-if-not #'buffer-live-p buffers))))))) ;;;###autoload (defun doom/kill-other-buffers (&optional project-p) @@ -276,13 +281,14 @@ that belong to the current project." If PROJECT-P (universal argument), kill only buffers that belong to the current project." (interactive "P") - (let ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list))) - (current-buffer (current-buffer))) - (dolist (buf buffers) - (unless (eq buf current-buffer) - (doom-kill-buffer-and-windows buf))) + (let ((buffers + (delq (current-buffer) + (if project-p (doom-project-buffer-list) (doom-buffer-list))))) + (mapc #'doom-kill-buffer-and-windows buffers) (when (called-interactively-p 'interactive) - (message "Killed %s buffers" (length buffers))))) + (message "Killed %s buffers" + (- (length buffers) + (length (cl-remove-if-not #'buffer-live-p buffers))))))) ;;;###autoload (defun doom/kill-matching-buffers (pattern &optional project-p) @@ -293,44 +299,23 @@ project." (interactive (list (read-regexp "Buffer pattern: ") current-prefix-arg)) - (let* ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list))) - (n (doom-kill-matching-buffers pattern buffers))) + (let* ((buffers (if project-p (doom-project-buffer-list) (doom-buffer-list)))) + (doom-kill-matching-buffers pattern buffers) (when (called-interactively-p 'interactive) - (message "Killed %s buffers" n)))) + (message "Killed %d buffer(s)" + (- (length buffers) + (length (cl-remove-if-not #'buffer-live-p buffers))))))) ;;;###autoload -(defun doom/cleanup-session (arg &optional buffer-list) - "Clean up buried buries and orphaned processes in the current workspace. If -ALL-P (universal argument), clean them up globally." +(defun doom/kill-buried-buffers (&optional project-p) + "Kill buffers that are buried. + +If PROJECT-P (universal argument), only kill buried buffers belonging to the +current project." (interactive "P") - (let ((buffers (doom-buried-buffers buffer-list)) - (n 0)) - (dolist (buf buffers) - (unless (buffer-modified-p buf) - (kill-buffer buf) - (cl-incf n))) - (when arg - (setq n (+ n (doom/cleanup-buffer-processes)))) - (dolist (hook doom-cleanup-hook) - (let ((m (funcall hook))) - (when (integerp m) - (setq n (+ n m))))) - (message "Cleaned up %s buffers" n) - n)) - -;;;###autoload -(defun doom/cleanup-buffer-processes () - "Kill all processes that have no visible associated buffers. Return number of -processes killed." - (interactive) - (let ((n 0)) - (dolist (p (process-list)) - (let ((process-buffer (process-buffer p))) - (when (and (process-live-p p) - (not (string= (process-name p) "server")) - (or (not process-buffer) - (and (bufferp process-buffer) - (not (buffer-live-p process-buffer))))) - (delete-process p) - (cl-incf n)))) - n)) + (let ((buffers (doom-buried-buffers (if project-p (doom-project-buffer-list))))) + (mapc #'kill-buffer buffers) + (when (called-interactively-p 'interactive) + (message "Killed %d buffer(s)" + (- (length buffers) + (length (cl-remove-if-not #'buffer-live-p buffers))))))) diff --git a/core/autoload/cli.el b/core/autoload/cli.el index 5ab0f66d0..ec9481937 100644 --- a/core/autoload/cli.el +++ b/core/autoload/cli.el @@ -3,17 +3,38 @@ (require 'core-cli) (defun doom--run (command &optional yes) - (let ((default-directory doom-emacs-dir) - (doom-auto-accept yes)) - (let ((compilation-buffer-name-function (lambda (_) "*bin/doom*"))) - (compile (format "bin/doom %s" command) t)) - (while compilation-in-progress - (sit-for 1)) - (when (y-or-n-p "Reload Doom config?") - (doom/reload)) - (message "Done"))) + (let* ((default-directory doom-emacs-dir) + (doom-auto-accept yes) + (buf (get-buffer-create " *bin/doom*")) + (wconf (current-window-configuration)) + (ignore-window-parameters t) + (noninteractive t) + (standard-output + (lambda (char) + (with-current-buffer buf + (insert char) + (when (memq char '(?\n ?\r)) + (ansi-color-apply-on-region (line-beginning-position -1) (line-end-position)) + (redisplay)))))) + (doom-initialize t) + (setq doom-modules (doom-modules)) + (doom-initialize-modules t) + (doom-initialize-packages t) + (with-current-buffer (switch-to-buffer buf) + (erase-buffer) + (require 'package) + (redisplay) + (doom-dispatch command nil) + (print! (green "\nDone!")))) + (message (format! (green "Done!")))) +;;;###autoload +(defun doom//autoloads (&optional yes) + "TODO" + (interactive "P") + (doom--run "autoloads" yes)) + ;;;###autoload (defun doom//update (&optional yes) "TODO" @@ -24,7 +45,9 @@ (defun doom//upgrade (&optional yes) "TODO" (interactive "P") - (doom--run "upgrade" yes)) + (doom--run "upgrade" yes) + (when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?") + (doom/restart-and-restore))) ;;;###autoload (defun doom//install (&optional yes) @@ -43,24 +66,3 @@ "TODO" (interactive "P") (doom--run "refresh" yes)) - -;;;###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 "P") - (require 'core-cli) - (doom-reload-autoloads force-p) - (setq load-path doom-site-load-path) - (let (doom-init-p) - (doom-initialize)) - (with-demoted-errors "PRIVATE CONFIG ERROR: %s" - (doom-initialize-modules 'force)) - (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) - (message "Finished!")) diff --git a/core/autoload/config.el b/core/autoload/config.el new file mode 100644 index 000000000..011904461 --- /dev/null +++ b/core/autoload/config.el @@ -0,0 +1,38 @@ +;;; core/autoload/config.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun doom/open-private-config () + "TODO" + (interactive) + (unless (file-directory-p doom-private-dir) + (make-directory doom-private-dir t)) + (doom-project-browse doom-private-dir)) + +;;;###autoload +(defun doom/find-file-in-private-config () + "TODO" + (interactive) + (doom-project-find-file doom-private-dir)) + +;;;###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 "P") + (require 'core-cli) + (doom-reload-autoloads force-p) + (setq load-path doom-site-load-path) + (let (doom-init-p) + (doom-initialize)) + (with-demoted-errors "PRIVATE CONFIG ERROR: %s" + (doom-initialize-modules 'force)) + (when (bound-and-true-p doom-packages) + (doom/reload-packages)) + (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) + (message "Finished!")) diff --git a/core/autoload/debug.el b/core/autoload/debug.el index 1d8c5d885..cc7f177c9 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -61,8 +61,8 @@ ready to be pasted in a bug report on github." "n/a") (or (ignore-errors (require 'use-package) - (cl-loop for (name . plist) in (doom-get-packages :private t) - if (use-package-plist-delete (copy-sequence plist) :private) + (cl-loop for (name . plist) in (doom-find-packages :private t) + if (use-package-plist-delete (copy-sequence plist) :modules) collect (format "%s" (cons name it)) else collect (symbol-name name))) @@ -115,7 +115,7 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" branch and commit." (interactive) (require 'vc-git) - (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s." + (print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s" doom-version emacs-version (or (vc-git--symbolic-ref doom-core-dir) @@ -139,25 +139,38 @@ pasting into a bug report or discord." (defvar doom--sandbox-init-doom-p nil) -(defun doom--run-vanilla-sandbox (&optional load-doom-p) +(defun doom--run-vanilla-sandbox (&optional mode) (interactive) (let ((contents (buffer-string)) (file (make-temp-file "doom-sandbox-"))) + (require 'package) (with-temp-file file (insert (prin1-to-string - `(cond (,load-doom-p - (setq doom-private-dir "/tmp/does/not/exist" - doom-modules ,doom-modules) - (load ,user-init-file)) - ((setq package--init-file-ensured t - package-user-dir ,package-user-dir - package-archives ',package-archives - user-emacs-directory ,doom-emacs-dir) - (package-initialize)))) + (macroexp-progn + (append `((setq debug-on-error t + package--init-file-ensured t + package-user-dir ,package-user-dir + package-archives ',package-archives + user-emacs-directory ,doom-emacs-dir + doom-modules ,doom-modules)) + (pcase mode + (`vanilla-doom+ ; Doom core + modules - private config + `((setq doom-private-dir "/tmp/does/not/exist") + (load-file ,user-init-file) + (doom|run-all-startup-hooks))) + (`vanilla-doom ; only Doom core + `((setq doom-private-dir "/tmp/does/not/exist" + doom-init-modules-p t) + (load-file ,user-init-file) + (doom|run-all-startup-hooks))) + (`vanilla ; nothing loaded + `((package-initialize))))))) "\n(unwind-protect (progn\n" contents "\n)\n" (format "(delete-file %S))" file))) - (let ((args (list "-Q" "-l" file))) + (let ((args (if (eq mode 'doom) + (list "-l" file) + (list "-Q" "-l" file)))) (require 'restart-emacs) (condition-case e (cond ((display-graphic-p) @@ -174,30 +187,58 @@ pasting into a bug report or discord." (delete-file file) (signal (car e) (cdr e))))))) -(defun doom--run-vanilla-doom-sandbox () - (interactive) - (doom--run-vanilla-sandbox t)) +(fset 'doom--run-vanilla-emacs (lambda! (doom--run-vanilla-sandbox 'vanilla))) +(fset 'doom--run-vanilla-doom (lambda! (doom--run-vanilla-sandbox 'vanilla-doom))) +(fset 'doom--run-vanilla-doom+ (lambda! (doom--run-vanilla-sandbox 'vanilla-doom+))) +(fset 'doom--run-full-doom (lambda! (doom--run-vanilla-sandbox 'doom))) + +(defvar doom-sandbox-emacs-lisp-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") #'doom--run-vanilla-emacs) + (define-key map (kbd "C-c C-d") #'doom--run-vanilla-doom) + (define-key map (kbd "C-c C-p") #'doom--run-vanilla-doom+) + (define-key map (kbd "C-c C-f") #'doom--run-full-doom) + (define-key map (kbd "C-c C-k") #'kill-this-buffer) + map)) + +(define-derived-mode doom-sandbox-emacs-lisp-mode emacs-lisp-mode "Sandbox Elisp" + "TODO") ;;;###autoload (defun doom/open-vanilla-sandbox () - "Open an Emacs Lisp buffer destinated to run in a blank Emacs session (and -optionally load only Doom and its modules, without your private config). + "Open the Emacs Lisp sandbox. -This provides a testbed for debugging code without Doom (or your private config) -standing in the way, and without sacrificing access to installed packages." +This is a test bed for running Emacs Lisp in an instance of Emacs with varying +amounts of Doom loaded, including: + + a) vanilla Emacs (nothing loaded), + b) vanilla Doom (only Doom core) and + c) Doom + modules - your private config. + +This is done without sacrificing access to installed packages. Use the sandbox +to reproduce bugs and determine if Doom is to blame." (interactive) (let* ((buffer-name "*doom:vanilla-sandbox*") (exists (get-buffer buffer-name)) (buf (get-buffer-create buffer-name))) (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-d") #'doom--run-vanilla-doom-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-d to run it with vanilla Doom loaded / C-c C-k to abort it") + (doom-sandbox-emacs-lisp-mode) + (setq header-line-format + (concat "C-c C-c = vanilla Emacs" + " / " + "C-c C-d = Doom core" + " / " + "C-c C-p = Doom core + modules - private config" + " / " + "C-c C-f = Full Doom" + " / " + "C-c C-k to abort")) (setq-local default-directory doom-emacs-dir) (unless (buffer-live-p exists) - (doom-template-insert "VANILLA_SANDBOX")) + (doom-template-insert "VANILLA_SANDBOX") + (let ((contents (substitute-command-keys (buffer-string)))) + (erase-buffer) + (insert contents "\n"))) (goto-char (point-max))) (pop-to-buffer buf))) @@ -320,3 +361,14 @@ If INIT-FILE is non-nil, profile that instead of USER-INIT-FILE." ;;;###autoload (advice-add #'esup :override #'doom/profile-emacs) + +;;;###autoload +(defun doom/toggle-debug-mode (&optional arg) + "Toggle `debug-on-error' and `doom-debug-mode' for verbose logging." + (interactive (list (or current-prefix-arg 'toggle))) + (let ((value + (cond ((eq arg 'toggle) (not doom-debug-mode)) + ((> (prefix-numeric-value arg) 0))))) + (setq doom-debug-mode value + debug-on-error value) + (message "Debug mode %s" (if value "on" "off")))) diff --git a/core/autoload/files.el b/core/autoload/files.el index 571863987..89a6d1128 100644 --- a/core/autoload/files.el +++ b/core/autoload/files.el @@ -184,9 +184,9 @@ file if it exists, without confirmation." (when (file-exists-p old-path) (delete-file old-path)) (kill-this-buffer) - (find-file new-path) (doom--forget-file old-path new-path) (doom--update-file new-path) + (find-file new-path) (message "File successfully moved to %s" dest)))) (`overwrite-self (error "Cannot overwrite self")) (`aborted (message "Aborted")) diff --git a/core/autoload/help.el b/core/autoload/help.el index 75d4d1a69..8b04e3356 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -1,45 +1,67 @@ ;;; core/autoload/help.el -*- lexical-binding: t; -*- (defvar doom--module-mode-alist - '((c-mode :lang cc) - (c++-mode :lang cc) - (objc++-mode :lang cc) - (java-mode :lang java) - (csharp-mode :lang csharp) - (clojure-mode :lang clojure) + '((dockerfile-mode :tools docker) + (haxor-mode :lang assembly) + (mips-mode :lang assembly) + (nasm-mode :lang assembly) + (c-mode :lang cc) + (c++-mode :lang cc) + (objc++-mode :lang cc) + (crystal-mode :lang crystal) + (lisp-mode :lang common-lisp) + (csharp-mode :lang csharp) + (clojure-mode :lang clojure) + (graphql-mode :lang data) + (toml-mode :lang data) + (json-mode :lang data) + (yaml-mode :lang data) + (csv-mode :lang data) + (dhall-mode :lang data) + (erlang-mode :lang erlang) + (elixir-mode :lang elixir) + (elm-mode :lang elm) (emacs-lisp-mode :lang emacs-lisp) - (go-mode :lang go) - (haskell-mode :lang haskell) - (js2-mode :lang javascript) - (julia-mode :lang julia) - (latex-mode :lang latex) - (LaTeX-mode :lang latex) - (ledger-mode :lang ledger) - (lua-mode :lang lua) - (markdown-mode :lang markdown) - (gfm-mode :lang markdown) - (ocaml-mode :lang ocaml) - (org-mode :lang org) - (perl-mode :lang perl) - (php-mode :lang php) - (hack-mode :lang php) - (plantuml-mode :lang plantuml) + (ess-r-mode :lang ess) + (ess-julia-mode :lang ess) + (go-mode :lang go) + (haskell-mode :lang haskell) + (hy-mode :lang hy) + (java-mode :lang java) + (js2-mode :lang javascript) + (rjsx-mode :lang javascript) + (typescript-mode :lang javascript) + (coffee-mode :lang javascript) + (julia-mode :lang julia) + (latex-mode :lang latex) + (LaTeX-mode :lang latex) + (ledger-mode :lang ledger) + (lua-mode :lang lua) + (markdown-mode :lang markdown) + (gfm-mode :lang markdown) + (nim-mode :lang nim) + (nix-mode :lang nix) + (taureg-mode :lang ocaml) + (org-mode :lang org) + (perl-mode :lang perl) + (php-mode :lang php) + (hack-mode :lang php) + (plantuml-mode :lang plantuml) (purescript-mode :lang purescript) - (python-mode :lang python) + (python-mode :lang python) (restclient-mode :lang rest) - (ruby-mode :lang ruby) - (enh-ruby-mode :lang ruby) - (rust-mode :lang rust) - (scala-mode :lang scala) - (sh-mode :lang sh) - (swift-mode :lang swift) - (typescript-mode :lang typescript) - (web-mode :lang web) - (css-mode :lang web) - (scss-mode :lang web) - (sass-mode :lang web) - (less-css-mode :lang web) - (stylus-mode :lang web)) + (ruby-mode :lang ruby) + (enh-ruby-mode :lang ruby) + (rust-mode :lang rust) + (scala-mode :lang scala) + (sh-mode :lang sh) + (swift-mode :lang swift) + (web-mode :lang web) + (css-mode :lang web) + (scss-mode :lang web) + (sass-mode :lang web) + (less-css-mode :lang web) + (stylus-mode :lang web)) "TODO") @@ -54,16 +76,17 @@ collect mode)) - ;; ;; Commands ;;;###autoload -(define-obsolete-function-alias 'doom/describe-setting 'doom/describe-setters "2.1.0") +(defun doom/describe-autodefs (autodef) + "Open the documentation of Doom autodefs. -;;;###autoload -(defun doom/describe-setters (setting) - "Open the documentation of Doom functions and configuration macros." +What is an autodef? It's a function or macro that is always defined, even if its +containing module is disabled (in which case it will safely no-op). This +syntactic sugar lets you use them without needing to check if they are +available." (interactive (let* ((settings (cl-loop with case-fold-search = nil @@ -74,42 +97,52 @@ (string-match-p "[a-z]!$" sym-name)) collect sym)) (sym (symbol-at-point)) - (setting + (autodef (completing-read "Describe setter: " ;; TODO Could be cleaner (refactor me!) (cl-loop with maxwidth = (apply #'max (mapcar #'length (mapcar #'symbol-name settings))) for def in (sort settings #'string-lessp) - if (or (get def 'doom-module) - (doom-module-from-path (symbol-file def))) + if (get def 'doom-module) collect (format (format "%%-%ds%%s" (+ maxwidth 4)) def (propertize (format "%s %s" (car it) (cdr it)) 'face 'font-lock-comment-face)) - else if (file-in-directory-p (symbol-file def) doom-core-dir) + else if (and (string-match-p "^set-.+!$" (symbol-name def)) + (symbol-file def) + (file-in-directory-p (symbol-file def) doom-core-dir)) collect (format (format "%%-%ds%%s" (+ maxwidth 4)) - def (propertize (format "%s %s" :core (file-name-sans-extension (file-relative-name (symbol-file def) doom-core-dir))) - 'face 'font-lock-comment-face)) - else - collect (symbol-name def)) + def (propertize (format "core/%s.el" (file-name-sans-extension (file-relative-name (symbol-file def) doom-core-dir))) + 'face 'font-lock-comment-face))) nil t (when (and (symbolp sym) (string-match-p "!$" (symbol-name sym))) (symbol-name sym))))) - (list (and setting (car (split-string setting " ")))))) - (or (stringp setting) - (functionp setting) - (signal 'wrong-type-argument (list '(stringp functionp) setting))) - (let ((fn (if (functionp setting) - setting - (intern-soft setting)))) + (list (and autodef (car (split-string autodef " ")))))) + (or (stringp autodef) + (functionp autodef) + (signal 'wrong-type-argument (list '(stringp functionp) autodef))) + (let ((fn (if (functionp autodef) + autodef + (intern-soft autodef)))) (or (fboundp fn) - (error "'%s' is not a valid DOOM setting" setting)) + (error "'%s' is not a valid DOOM autodef" autodef)) (if (fboundp 'helpful-callable) (helpful-callable fn) (describe-function fn)))) +;;;###autoload +(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 + (cond ((stringp mode) (intern mode)) + ((symbolp mode) mode) + ((error "Expected a symbol/string, got a %s" (type-of mode)))))) + ;;;###autoload (defun doom/describe-module (category module) "Open the documentation of CATEGORY MODULE. @@ -162,55 +195,121 @@ current file is in, or d) the module associated with the current major mode (see (cl-check-type module symbol) (or (doom-module-p category module) (error "'%s %s' isn't a valid module" category module)) - (let ((doc-path (doom-module-path category module "README.org"))) - (unless (file-exists-p doc-path) - (error "There is no documentation for this module (%s)" doc-path)) - (find-file doc-path))) + (doom-project-browse (doom-module-path category module))) + +(defun doom--describe-package-insert-button (label path &optional regexp) + (declare (indent defun)) + (insert-text-button + (string-trim label) + 'face 'link + 'follow-link t + 'action + `(lambda (_) + (unless (file-exists-p ,path) + (user-error "Module doesn't exist")) + (when (window-dedicated-p) + (other-window 1)) + (let ((buffer (find-file ,path))) + (when ,(stringp regexp) + (with-current-buffer buffer + (goto-char (point-min)) + (if (re-search-forward ,regexp nil t) + (recenter) + (message "Couldn't find the config block")))))))) ;;;###autoload -(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." +(global-set-key [remap describe-package] #'doom/describe-package) + +(defvar doom--describe-package-list-cache nil) +;;;###autoload +(defun doom/describe-package (package) + "Like `describe-packages', but is Doom aware. + +Only shows installed packages. Includes information about where packages are +defined and configured. + +If prefix arg is prsent, refresh the cache." (interactive - (list (completing-read "Minor mode: " (doom-active-minor-modes)))) - (describe-minor-mode-from-symbol - (cond ((stringp mode) (intern mode)) - ((symbolp mode) mode) - ((error "Expected a symbol/string, got a %s" (type-of mode)))))) + (list + (let* ((guess (or (function-called-at-point) + (symbol-at-point)))) + (require 'finder-inf nil t) + (require 'core-packages) + (doom-initialize-packages) + (let ((packages + (or (unless current-prefix-arg doom--describe-package-list-cache) + (cl-loop for pkg + in (cl-delete-duplicates + (sort (append (mapcar #'car package-alist) + (mapcar #'car package-archive-contents) + (mapcar #'car package--builtins)) + #'string-greaterp)) + if (assq pkg package-alist) + collect (symbol-name pkg) + else + collect (propertize (symbol-name pkg) 'face 'font-lock-comment-face))))) + (unless (memq guess packages) + (setq guess nil)) + (setq doom--describe-package-list-cache packages) + (intern + (completing-read + (if guess + (format "Describe package (default %s): " + guess) + "Describe package: ") + packages nil t nil nil + (if guess (symbol-name guess)))))))) + (describe-package package) + (save-excursion + (with-current-buffer (help-buffer) + (let ((inhibit-read-only t)) + (goto-char (point-min)) + (when (and (doom-package-installed-p package) + (re-search-forward "^ *Status: " nil t)) + (end-of-line) + (let ((indent (make-string (length (match-string 0)) ? ))) + (insert "\n" indent "Installed by the following Doom modules:\n") + (dolist (m (get package 'doom-module)) + (insert indent) + (doom--describe-package-insert-button + (format " %s %s" (car m) (or (cdr m) "")) + (pcase (car m) + (:core doom-core-dir) + (:private doom-private-dir) + (category (doom-module-path category (cdr m))))) + (insert "\n")) + + (package--print-help-section "Source") + (pcase (doom-package-backend package) + (`elpa (insert "[M]ELPA")) + (`quelpa (insert (format "QUELPA %s" (prin1-to-string (doom-package-prop package :recipe))))) + (`emacs (insert "Built-in"))) + (insert "\n") + + (package--print-help-section "Configs") + (dolist (file (get package 'doom-files)) + (doom--describe-package-insert-button + (abbreviate-file-name file) + file + (format "\\((\\(:?after!\\|def-package!\\)[ \t\n]*%s\\|^[ \t]*;; `%s'$\\)" + package package)) + (insert "\n" indent)) + (delete-char -1))))))) ;;;###autoload -(defun doom/what-face (arg &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 "P") - (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 (or (listp face) arg) - 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 - if arg concat (concat (symbol-name ov) " ") - else concat (concat (propertize (symbol-name ov) 'face ov) " ")) - "n/a"))) - (t - (and (or faces overlays) - (list faces overlays)))))) +(defun doom/describe-symbol (symbol) + "Show help for SYMBOL, a variable, function or macro." + (interactive + (list (helpful--read-symbol "Symbol: " #'helpful--bound-p))) + (let* ((sym (intern-soft symbol)) + (bound (boundp sym)) + (fbound (fboundp sym))) + (cond ((and sym bound (not fbound)) + (helpful-variable sym)) + ((and sym fbound (not bound)) + (helpful-callable sym)) + ((apropos (format "^%s\$" symbol))) + ((apropos (format "%s" symbol)))))) ;;;###autoload (defalias 'doom/help 'doom/open-manual) @@ -219,4 +318,14 @@ whose car is the list of faces and cadr is the list of overlay faces." (defun doom/open-manual () "TODO" (interactive) - (find-file (expand-file-name "index.org" doom-docs-dir))) + (user-error "This command isn't implemented yet") + ;; (find-file (expand-file-name "index.org" doom-docs-dir)) + ) + +;;;###autoload +(defun doom/open-news () + "TODO" + (interactive) + (user-error "This command isn't implemented yet") + ;; (find-file (expand-file-name (concat "news/" doom-version) doom-docs-dir)) + ) diff --git a/core/autoload/line-numbers.el b/core/autoload/line-numbers.el index 10862f1fd..a926c0a2b 100644 --- a/core/autoload/line-numbers.el +++ b/core/autoload/line-numbers.el @@ -1,5 +1,5 @@ ;;; core/autoload/line-numbers.el -*- lexical-binding: t; -*- -;;;###if (not EMACS26+) +;;;###if (version< emacs-version "26.1") ;; This was lifted out of the display-line-numbers library in Emacs 26.1 and ;; modified to use nlinum for Emacs 25.x users. It should be removed should diff --git a/core/autoload/message.el b/core/autoload/message.el index e00eb0236..340e98954 100644 --- a/core/autoload/message.el +++ b/core/autoload/message.el @@ -32,7 +32,7 @@ "TODO") ;;;###autoload -(defun doom-ansi-apply (style text) +(defun doom-color-apply (style text) "Apply CODE to formatted MESSAGE with ARGS. CODE is derived from any of `doom-message-fg', `doom-message-bg' or `doom-message-fx'. @@ -53,19 +53,27 @@ Otherwise, it maps colors to a term-color-* face." `(:foreground ,(face-foreground (caddr (assq style doom-ansi-alist))))) ((cddr (assq style doom-ansi-alist))))))))) +(defun doom--short-color-replace (forms) + "Replace color-name functions with calls to `doom-color-apply'." + (cond ((null forms) nil) + ((listp forms) + (append (cond ((not (symbolp (car forms))) + (list (doom--short-color-replace (car forms)))) + ((assq (car forms) doom-ansi-alist) + `(doom-color-apply ',(car forms))) + ((eq (car forms) 'color) + (pop forms) + `(doom-color-apply ,(car forms))) + ((list (car forms)))) + (doom--short-color-replace (cdr forms)) + nil)) + (forms))) + ;;;###autoload (defmacro format! (message &rest args) "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 - (,@(mapcar (lambda (rule) `(,(car rule) - (lambda (message) - (doom-ansi-apply ',(car rule) message)))) - doom-ansi-alist) - (color - (lambda (code format) - (doom-ansi-apply code format)))) - (format ,message ,@args))) + `(format ,@(doom--short-color-replace `(,message ,@args)))) ;;;###autoload (defmacro print! (message &rest args) @@ -79,8 +87,5 @@ Can be colored using (color ...) blocks: (print! (green \"Great %s!\") \"success\") Uses faces in interactive sessions and ANSI codes otherwise." - `(if (not noninteractive) - (message (format! ,message ,@args)) - ;; princ prints to stdout, message to stderr - (princ (format! ,message ,@args)) - (terpri))) + `(progn (princ (format! ,message ,@args)) + (terpri))) diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 1e0e12756..77fee86d9 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -40,8 +40,9 @@ ;;;###autoload (defun doom-package-backend (name &optional noerror) - "Get which backend the package NAME was installed with. Can either be elpa or -quelpa. Throws an error if NOERROR is nil and the package isn't installed." + "Get which backend the package NAME was installed with. Can either be elpa, +quelpa or emacs (built-in). Throws an error if NOERROR is nil and the package +isn't installed." (cl-check-type name symbol) (cond ((assq name quelpa-cache) 'quelpa) ((assq name package-alist) 'elpa) @@ -96,7 +97,6 @@ list of the package." "Return t if a package named NAME (a symbol) has a new backend than what it was installed with. Returns nil otherwise, or if package isn't installed." (cl-check-type name symbol) - (doom-initialize-packages) (and (package-installed-p name) (let* ((plist (cdr (assq name doom-packages))) (old-backend (doom-package-backend name 'noerror)) @@ -108,7 +108,6 @@ was installed with. Returns nil otherwise, or if package isn't installed." "Return t if a package named NAME (a symbol) has a different recipe than it was installed with." (cl-check-type name symbol) - (doom-initialize-packages) (and (package-installed-p name) (when-let* ((quelpa-recipe (assq name quelpa-cache)) (doom-recipe (assq name doom-packages))) @@ -116,15 +115,16 @@ was installed with." (cdr (plist-get (cdr doom-recipe) :recipe))))))) ;;;###autoload -(cl-defun doom-get-packages (&key (installed 'any) - (private 'any) - (disabled 'any) - (pinned 'any) - (ignored 'any) - (sort t) - changed - backend - deps) +(cl-defun doom-find-packages (&key (installed 'any) + (private 'any) + (disabled 'any) + (pinned 'any) + (ignored 'any) + (core 'any) + sort + changed + backend + deps) "Retrieves a list of primary packages (i.e. non-dependencies). Each element is a cons cell, whose car is the package symbol and whose cdr is the quelpa recipe (if any). @@ -132,76 +132,146 @@ a cons cell, whose car is the package symbol and whose cdr is the quelpa recipe You can build a filtering criteria using one or more of the following properties: - :backend BACKEND - Can be 'quelpa, 'elpa or 'emacs - :installed BOOL - Only return installed packages (t) or uninstalled packages (nil) - :private BOOL - Only return private packages (t) or non-private packages (nil) - :disabled BOOL - Only return packages that are disabled (t) or otherwise (nil) - :ignored BOOL - Only return packages that are ignored (t) or otherwise (nil) + :backend 'quelpa|'elpa|'emacs|'any + Include packages installed through 'quelpa, 'elpa or 'emacs. 'any is the + wildcard. + :installed BOOL|'any + t = only include installed packages + nil = exclude installed packages + :private BOOL|'any + t = only include user-installed packages + nil = exclude user-installed packages + :core BOOL|'any + t = only include Doom core packages + nil = exclude Doom core packages + :disabled BOOL|'any + t = only include disabled packages + nil = exclude disabled packages + :ignored BOOL|'any + t = only include ignored packages + nil = exclude ignored packages :pinned BOOL|ARCHIVE Only return packages that are pinned (t), not pinned (nil) or pinned to a specific archive (stringp) :deps BOOL - Includes the package's dependencies (t). + Includes the package's dependencies (t) or not (nil). The resulting list is sorted unless :sort nil is passed to this function. Warning: this function is expensive, as it re-evaluates your all packages.el files." - (doom-initialize-packages) - (cl-remove-duplicates - (cl-loop with packages = (append (mapcar #'list doom-core-packages) - doom-packages) - for (sym . plist) - in (if sort - (cl-sort (copy-sequence packages) #'string-lessp :key #'car) - packages) - if (and (or (not backend) - (eq (doom-package-backend sym t) backend)) - (or (eq ignored 'any) - (let* ((form (plist-get plist :ignore)) - (value (eval form))) - (if ignored value (not value)))) - (or (eq disabled 'any) - (if disabled - (plist-get plist :disable) - (not (plist-get plist :disable)))) - (or (eq installed 'any) - (if installed - (doom-package-installed-p sym) - (not (doom-package-installed-p sym)))) - (or (eq private 'any) - (if private - (plist-get plist :private) - (not (plist-get plist :private)))) - (or (eq pinned 'any) - (cond ((eq pinned 't) - (plist-get plist :pin)) - ((null pinned) - (not (plist-get plist :pin))) - ((equal (plist-get plist :pin) pinned))))) - collect (cons sym plist) - and if (and deps (not (package-built-in-p sym))) - nconc - (cl-loop for pkg in (doom-get-dependencies-for sym 'recursive 'noerror) - if (or (eq installed 'any) - (if installed - (doom-package-installed-p pkg) - (not (doom-package-installed-p pkg)))) - collect (cons pkg (cdr (assq pkg doom-packages))))) - :key #'car)) + (cl-loop with packages = doom-packages + for (sym . plist) + in (if sort + (cl-sort (copy-sequence doom-packages) #'string-lessp :key #'car) + packages) + if (and (or (not backend) + (eq (doom-package-backend sym t) backend)) + (or (eq ignored 'any) + (let* ((form (plist-get plist :ignore)) + (value (eval form))) + (if ignored value (not value)))) + (or (eq disabled 'any) + (if disabled + (plist-get plist :disable) + (not (plist-get plist :disable)))) + (or (eq installed 'any) + (if installed + (doom-package-installed-p sym) + (not (doom-package-installed-p sym)))) + (or (eq private 'any) + (let ((modules (plist-get plist :modules))) + (if private + (assq :private modules) + (not (assq :private modules))))) + (or (eq core 'any) + (let ((modules (plist-get plist :modules))) + (if core + (assq :core modules) + (not (assq :core modules))))) + (or (eq pinned 'any) + (cond ((eq pinned 't) + (plist-get plist :pin)) + ((null pinned) + (not (plist-get plist :pin))) + ((equal (plist-get plist :pin) pinned))))) + collect (cons sym plist) + and if (and deps (not (package-built-in-p sym))) + nconc + (cl-loop for pkg in (doom-get-dependencies-for sym 'recursive 'noerror) + if (or (eq installed 'any) + (if installed + (doom-package-installed-p pkg) + (not (doom-package-installed-p pkg)))) + collect (cons pkg (cdr (assq pkg doom-packages)))))) + +(defun doom--read-module-packages-file (file &optional raw noerror) + (with-temp-buffer ; prevent buffer-local settings from propagating + (condition-case e + (if (not raw) + (load file noerror t t) + (when (file-readable-p file) + (insert-file-contents file) + (while (re-search-forward "(package! " nil t) + (save-excursion + (goto-char (match-beginning 0)) + (cl-destructuring-bind (name . plist) (cdr (sexp-at-point)) + (push (cons name + (plist-put plist :modules + (cond ((file-in-directory-p file doom-private-dir) + (list :private)) + ((file-in-directory-p file doom-core-dir) + (list :core)) + ((doom-module-from-path file))))) + doom-packages)))))) + ((debug error) + (signal 'doom-package-error + (list (or (doom-module-from-path file) + '(:private . packages)) + e)))))) + +;;;###autoload +(defun doom-package-list (&optional all-p) + "Retrieve a list of explicitly declared packages from enabled modules. + +This excludes core packages listed in `doom-core-packages'. + +If ALL-P, gather packages unconditionally across all modules, including disabled +ones." + (let ((noninteractive t) + (doom--stage 'packages) + (doom-modules (doom-modules)) + doom-packages + doom-disabled-packages + package-pinned-packages) + (doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p) + (let ((private-packages (expand-file-name "packages.el" doom-private-dir))) + (unless all-p + ;; We load the private packages file twice to ensure disabled packages + ;; are seen ASAP, and a second time to ensure privately overridden + ;; packages are properly overwritten. + (doom--read-module-packages-file private-packages nil t)) + (if all-p + (mapc #'doom--read-module-packages-file + (doom-files-in doom-modules-dir + :depth 2 + :full t + :match "/packages\\.el$")) + (cl-loop for key being the hash-keys of doom-modules + for path = (doom-module-path (car key) (cdr key) "packages.el") + for doom--current-module = key + do (doom--read-module-packages-file path nil t))) + (doom--read-module-packages-file private-packages all-p t)) + (append (cl-loop for package in doom-core-packages + collect (list package :modules '((:core internal)))) + (nreverse doom-packages)))) ;;;###autoload (defun doom-get-package-alist () "Returns a list of all desired packages, their dependencies and their desc objects, in the order of their `package! blocks.'" - (doom-initialize-packages) (cl-remove-duplicates - (cl-loop for name in (append doom-core-packages (mapcar #'car doom-packages)) + (cl-loop for name in (mapcar #'car doom-packages) if (assq name package-alist) nconc (cl-loop for dep in (package--get-deps name) if (assq dep package-alist) @@ -244,49 +314,46 @@ 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 t) (doom-refresh-packages-maybe doom-debug-mode) - (require 'async) - (let (quelpa-pkgs elpa-pkgs) - ;; Separate quelpa from elpa packages - (dolist (pkg (mapcar #'car package-alist)) - (when (and (or (not (doom-package-prop pkg :freeze 'eval)) - include-frozen-p) - (not (doom-package-prop pkg :ignore 'eval)) - (not (doom-package-different-backend-p pkg))) - (push pkg - (if (eq (doom-package-backend pkg) 'quelpa) - quelpa-pkgs - elpa-pkgs)))) + (let-alist + (seq-group-by + #'doom-package-backend + (cl-loop for package in (mapcar #'car package-alist) + when (and (or (not (doom-package-prop package :freeze 'eval)) + include-frozen-p) + (not (doom-package-prop package :ignore 'eval)) + (not (doom-package-different-backend-p package))) + collect package)) ;; The bottleneck in this process is quelpa's version checks, so check them ;; asynchronously. - (let (futures) - (dolist (pkg quelpa-pkgs) - (when doom-debug-mode - (message "New thread for: %s" pkg)) - (push (async-start - `(lambda () - (let ((gc-cons-threshold ,doom-gc-cons-upper-limit) - (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) - (user-emacs-directory ,user-emacs-directory) - 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 - (append (mapcar #'doom-package-outdated-p elpa-pkgs) - (mapcar #'async-get (reverse futures))))))) + (cl-loop with partitions = (min 2 (/ (length .quelpa) 4)) + for package-list in (seq-partition .quelpa partitions) + do (doom-log "New thread for: %s" package-list) + collect + (async-start + `(lambda () + (let ((gc-cons-threshold ,doom-gc-cons-upper-limit) + (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) + (user-emacs-directory ,user-emacs-directory) + 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) + (delq nil (mapcar #'doom-package-outdated-p ',package-list))))) + into futures + finally return + (append (delq nil (mapcar #'doom-package-outdated-p .elpa)) + (mapcan #'async-get futures) + nil)))) ;;;###autoload (defun doom-get-orphaned-packages () @@ -295,7 +362,7 @@ depended on. Used by `doom-packages-autoremove'." (let ((package-selected-packages - (mapcar #'car (doom-get-packages :ignored nil :disabled nil)))) + (mapcar #'car (doom-find-packages :ignored nil :disabled nil)))) (append (package--removable-packages) (cl-loop for pkg in package-selected-packages if (and (doom-package-different-backend-p pkg) @@ -303,22 +370,17 @@ Used by `doom-packages-autoremove'." collect pkg)))) ;;;###autoload -(defun doom-get-missing-packages (&optional include-ignored-p) +(defun doom-get-missing-packages () "Return a list of requested packages that aren't installed or built-in, but are enabled (with a `package!' directive). Each element is a list whose CAR is the package symbol, and whose CDR is a plist taken from that package's `package!' declaration. -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) (cl-loop for (name . plist) - in (doom-get-packages :ignored (if include-ignored-p 'any) - :disabled nil - :deps t - :sort nil) + in (doom-find-packages :ignored nil + :disabled nil + :deps t) if (and (or (plist-get plist :pin) (not (package-built-in-p name))) (or (not (doom-package-installed-p name)) @@ -343,7 +405,6 @@ Used by `doom-packages-install'." "Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an example; the package name can be omitted)." (cl-check-type name symbol) - (doom-initialize-packages) (when (and (package-installed-p name) (not (package-built-in-p name))) (if (or (doom-package-different-backend-p name) @@ -362,6 +423,7 @@ example; the package name can be omitted)." (package-install name)) (if (not (package-installed-p name)) (doom--delete-package-files name) + (add-to-list 'package-selected-packages name nil 'eq) (setf (alist-get name doom-packages) plist) name))) @@ -370,7 +432,6 @@ example; the package name can be omitted)." "Updates package NAME (a symbol) if it is out of date, using quelpa or package.el as appropriate." (cl-check-type name symbol) - (doom-initialize-packages) (unless (package-installed-p name) (error "%s isn't installed" name)) (when (doom-package-different-backend-p name) @@ -401,7 +462,6 @@ package.el as appropriate." (defun doom-delete-package (name &optional force-p) "Uninstalls package NAME if it exists, and clears it from `quelpa-cache'." (cl-check-type name symbol) - (doom-initialize-packages) (unless (package-installed-p name) (user-error "%s isn't installed" name)) (let ((inhibit-message (not doom-debug-mode)) @@ -419,6 +479,14 @@ package.el as appropriate." ;; ;; Interactive commands +;;;###autoload +(defun doom/reload-packages () + "Reload `doom-packages', `package' and `quelpa'." + (interactive) + (message "Reloading packages") + (doom-initialize-packages t) + (message "Reloading packages...DONE")) + ;;;###autoload (defun doom/update-package (pkg) "Prompts the user with a list of outdated packages and updates the selected @@ -436,7 +504,6 @@ calls." (unless name (user-error "'%s' is already up-to-date" selection)) (list (assq name packages)))) - (doom-initialize-packages) (cl-destructuring-bind (package old-version new-version) pkg (if-let* ((desc (doom-package-outdated-p package))) (let ((old-v-str (package-version-join old-version)) @@ -456,7 +523,6 @@ calls." ;;;###autoload (defun doom*package-delete (desc &rest _) "Update `quelpa-cache' upon a successful `package-delete'." - (doom-initialize-packages) (let ((name (package-desc-name desc))) (unless (package-installed-p name) (when-let* ((spec (assq name quelpa-cache))) @@ -474,8 +540,7 @@ calls." ;; Replace with Doom variants ;;;###autoload -(advice-add #'package-autoremove :override (λ! (doom-packages-autoremove current-prefix-arg))) +(advice-add #'package-autoremove :override #'doom//autoremove) ;;;###autoload -(advice-add #'package-install-selected-packages :override (λ! (doom-packages-install current-prefix-arg))) - +(advice-add #'package-install-selected-packages :override #'doom//install) diff --git a/core/autoload/projects.el b/core/autoload/projects.el index bc1c911b0..157bd17b7 100644 --- a/core/autoload/projects.el +++ b/core/autoload/projects.el @@ -1,5 +1,9 @@ ;;; core/autoload/projects.el -*- lexical-binding: t; -*- +;;;###autoload +(autoload 'projectile-relevant-known-projects "projectile") + + ;; ;; Macros @@ -33,6 +37,28 @@ they are absolute." (dolist (fn projectile-project-root-files-functions) (remhash (format "%s-%s" fn default-directory) projectile-project-root-cache))) +;;;###autoload +(defun doom/find-file-in-other-project (project-root) + "Preforms `projectile-find-file' in a known project of your choosing." + (interactive + (list + (completing-read "Find file in project: " (projectile-relevant-known-projects) + nil nil nil nil (doom-project-root)))) + (unless (file-directory-p project-root) + (error "Project directory '%s' doesn't exist" project-root)) + (doom-project-find-file project-root)) + +;;;###autoload +(defun doom/browse-in-other-project (project-root) + "Preforms `find-file' in a known project of your choosing." + (interactive + (list + (completing-read "Browse in project: " (projectile-relevant-known-projects) + nil nil nil nil (doom-project-root)))) + (unless (file-directory-p project-root) + (error "Project directory '%s' doesn't exist" project-root)) + (doom-project-browse project-root)) + ;; ;; Library diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el new file mode 100644 index 000000000..1ba92fd85 --- /dev/null +++ b/core/autoload/sessions.el @@ -0,0 +1,119 @@ +;;; core/autoload/sessions.el -*- lexical-binding: t; -*- + +;; +;;; Helpers + +;;;###autoload +(defun doom-save-session (&optional file) + "TODO" + (setq file (expand-file-name (or file (doom-session-file)))) + (cond ((require 'persp-mode nil t) + (unless persp-mode (persp-mode +1)) + (setq persp-auto-save-opt 0) + (persp-save-state-to-file file)) + ((and (require 'frameset nil t) + (require 'restart-emacs nil t)) + (let ((frameset-filter-alist (append '((client . restart-emacs--record-tty-file)) + frameset-filter-alist)) + (desktop-base-file-name (file-name-nondirectory file)) + (desktop-dirname (file-name-directory file)) + (desktop-restore-eager t) + desktop-file-modtime) + (make-directory desktop-dirname t) + (desktop-save desktop-dirname t))) + ((error "No session backend to save session with")))) + +;;;###autoload +(defun doom-load-session (&optional file) + "TODO" + (setq file (expand-file-name (or file (doom-session-file)))) + (message "Attempting to load %s" file) + (cond ((require 'persp-mode nil t) + (unless persp-mode (persp-mode +1)) + (persp-load-state-from-file file)) + ((and (require 'frameset nil t) + (require 'restart-emacs nil t)) + (restart-emacs--restore-frames-using-desktop file)) + ((error "No session backend to load session with")))) + +;;;###autoload +(defun doom-session-file () + "TODO" + (cond ((require 'persp-mode nil t) + (expand-file-name persp-auto-save-fname persp-save-dir)) + ((require 'desktop nil t) + (desktop-full-file-name)) + ((error "No session backend available")))) + + +;; +;;; Command line switch + +;;;###autoload +(defun doom-restore-session-handler (&rest _) + "TODO" + (add-hook 'window-setup-hook #'doom-load-session 'append)) + +;;;###autoload +(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)) + + +;; +;;; Commands + +;;;###autoload +(defun doom/quickload-session () + "TODO" + (interactive) + (message "Restoring session...") + (doom-load-session) + (message "Session restored. Welcome back.")) + +;;;###autoload +(defun doom/quicksave-session () + "TODO" + (interactive) + (message "Saving session") + (doom-save-session) + (message "Saving session...DONE")) + +;;;###autoload +(defun doom/load-session (file) + "TODO" + (interactive + (let ((session-file (doom-session-file))) + (list (or (read-file-name "Session to restore: " + (file-name-directory session-file) + nil t + (file-name-nondirectory session-file)) + (user-error "No session selected. Aborting"))))) + (unless file + (error "No session file selected")) + (message "Loading '%s' session" file) + (doom-load-session file)) + +;;;###autoload +(defun doom/save-session (file) + "TODO" + (interactive + (let ((session-file (doom-session-file))) + (list (or (read-file-name "Save session to: " + (file-name-directory session-file) + nil nil + (file-name-nondirectory session-file)) + (user-error "No session selected. Aborting"))))) + (unless file + (error "No session file selected")) + (message "Saving '%s' session" file) + (doom-save-session file)) + +;;;###autoload +(defalias 'doom/restart #'restart-emacs) + +;;;###autoload +(defun doom/restart-and-restore (&optional debug) + "TODO" + (interactive "P") + (doom/quicksave-session) + (restart-emacs + (delq nil (list (if debug "--debug-init") "--restore")))) diff --git a/core/autoload/editor.el b/core/autoload/text.el similarity index 85% rename from core/autoload/editor.el rename to core/autoload/text.el index 380a6e4a1..51486d559 100644 --- a/core/autoload/editor.el +++ b/core/autoload/text.el @@ -1,4 +1,4 @@ -;;; core/autoload/editor.el -*- lexical-binding: t; -*- +;;; core/autoload/text.el -*- lexical-binding: t; -*- ;;;###autoload (defun doom-surrounded-p (pair &optional inline balanced) @@ -108,7 +108,7 @@ afterwards, kill line to beginning of line." (interactive) (let ((empty-line-p (save-excursion (beginning-of-line) (looking-at-p "[ \t]*$")))) - (funcall (if (featurep 'evil) + (funcall (if (fboundp 'evil-delete) #'evil-delete #'delete-region) (point-at-bol) (point)) @@ -132,30 +132,6 @@ opposite indentation style." (tabify beg end) (untabify beg end)))) -(defvar-local doom--buffer-narrowed-origin nil) -;;;###autoload -(defun doom/clone-and-narrow-buffer (beg end &optional clone-p) - "Restrict editing in this buffer to the current region, indirectly. With CLONE-P, -clone the buffer and hard-narrow the selection. If mark isn't active, then widen -the buffer (if narrowed). - -Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/" - (interactive "rP") - (cond ((or (region-active-p) - (and beg end)) - (deactivate-mark) - (when clone-p - (let ((old-buf (current-buffer))) - (switch-to-buffer (clone-indirect-buffer nil nil)) - (setq doom--buffer-narrowed-origin old-buf))) - (narrow-to-region beg end)) - (doom--buffer-narrowed-origin - (kill-this-buffer) - (switch-to-buffer doom--buffer-narrowed-origin) - (setq doom--buffer-narrowed-origin nil)) - (t - (widen)))) - ;;;###autoload (defun doom/delete-trailing-newlines () "Trim trailing newlines. diff --git a/core/autoload/ui.el b/core/autoload/ui.el index 28ac10180..f163e8019 100644 --- a/core/autoload/ui.el +++ b/core/autoload/ui.el @@ -163,6 +163,30 @@ OPACITY is an integer between 0 to 100, inclusive." 100)))) (set-frame-parameter nil 'alpha opacity)) +(defvar-local doom--buffer-narrowed-origin nil) +;;;###autoload +(defun doom/clone-and-narrow-buffer (beg end &optional clone-p) + "Restrict editing in this buffer to the current region, indirectly. With CLONE-P, +clone the buffer and hard-narrow the selection. If mark isn't active, then widen +the buffer (if narrowed). + +Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/" + (interactive "rP") + (cond ((or (region-active-p) + (and beg end)) + (deactivate-mark) + (when clone-p + (let ((old-buf (current-buffer))) + (switch-to-buffer (clone-indirect-buffer nil nil)) + (setq doom--buffer-narrowed-origin old-buf))) + (narrow-to-region beg end)) + (doom--buffer-narrowed-origin + (kill-this-buffer) + (switch-to-buffer doom--buffer-narrowed-origin) + (setq doom--buffer-narrowed-origin nil)) + (t + (widen)))) + ;; ;; Modes diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index 6ab127f2e..84678d50e 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -37,9 +37,8 @@ it exists." (print! (bold (green "\nFinished!"))) (message "If you have a running Emacs Session, you will need to restart it or") (message "reload Doom for changes to take effect:\n") - (when (fboundp '+workspace/restart-emacs-then-restore) - (message " M-x +workspace/restart-emacs-then-restore")) - (message " M-x restart-emacs") + (message " M-x doom/restart-and-restore") + (message " M-x doom/restart") (message " M-x doom/reload")) (defun doom--do-load (&rest files) @@ -156,7 +155,7 @@ even if it doesn't need reloading!" forms) (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) (let* ((sexp (sexp-at-point)) - (pred (match-string 1)) + (alt-sexp (match-string 1)) (type (car sexp)) (name (doom-unquote (cadr sexp))) (origin (cond ((doom-module-from-path path)) @@ -166,37 +165,39 @@ even if it doesn't need reloading!" `(:core . ,(intern (file-name-base path)))))) (doom-file-form `(put ',name 'doom-file ,(abbreviate-file-name path)))) - (cond ((memq type '(defun defmacro cl-defun cl-defmacro)) + (cond ((and (not member-p) alt-sexp) + (push (read alt-sexp) forms)) + + ((memq type '(defun defmacro cl-defun cl-defmacro)) (cl-destructuring-bind (_ name arglist &rest body) sexp (let ((docstring (if (stringp (car body)) (pop body) "No documentation."))) - (push (cond ((not (and member-p - (or (null pred) - (let ((load-file-name path)) - (eval (read pred) t))))) - (push doom-file-form forms) - (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" - origin docstring)) - (condition-case-unless-debug e - (append (list (pcase type - (`defun 'defmacro) - (`cl-defun `cl-defmacro) - (_ type)) - name arglist docstring) - (cl-loop for arg in arglist - if (and (symbolp arg) - (not (keywordp arg)) - (not (memq arg cl--lambda-list-keywords))) - collect arg into syms - else if (listp arg) - collect (car arg) into syms - finally return (if syms `((ignore ,@syms))))) - ('error - (message "Ignoring autodef %s (%s)" - name e) - nil))) - ((make-autoload sexp (abbreviate-file-name (file-name-sans-extension path))))) + (push (if member-p + (make-autoload sexp (abbreviate-file-name (file-name-sans-extension path))) + (push doom-file-form forms) + (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + origin docstring)) + (condition-case-unless-debug e + (if alt-sexp + (read alt-sexp) + (append (list (pcase type + (`defun 'defmacro) + (`cl-defun `cl-defmacro) + (_ type)) + name arglist docstring) + (cl-loop for arg in arglist + if (and (symbolp arg) + (not (keywordp arg)) + (not (memq arg cl--lambda-list-keywords))) + collect arg into syms + else if (listp arg) + collect (car arg) into syms + finally return (if syms `((ignore ,@syms)))))) + ('error + (message "Ignoring autodef %s (%s)" + name e) + nil))) forms) (push `(put ',name 'doom-module ',origin) forms)))) @@ -204,19 +205,16 @@ even if it doesn't need reloading!" (cl-destructuring-bind (_type name target &optional docstring) sexp (let ((name (doom-unquote name)) (target (doom-unquote target))) - (unless (and member-p - (or (null pred) - (let ((load-file-name path)) - (eval (read pred) t)))) + (unless member-p + (setq docstring (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + origin docstring)) (setq target #'ignore)) (push doom-file-form forms) (push `(put ',name 'doom-module ',origin) forms) (push `(defalias ',name #',target ,docstring) forms)))) - ((and member-p - (or (null pred) - (eval (read pred) t))) + (member-p (push sexp forms))))) (if forms (concat (string-join (mapcar #'prin1-to-string (reverse forms)) "\n") @@ -320,7 +318,7 @@ modified." (prin1 `(setq load-path ',load-path auto-mode-alist ',auto-mode-alist Info-directory-list ',Info-directory-list - doom-disabled-packages ',doom-disabled-packages + doom-disabled-packages ',(mapcar #'car (doom-find-packages :disabled t)) package-activated-list ',package-activated-list) (current-buffer))) @@ -354,10 +352,10 @@ This should be run whenever your `doom!' block or update your packages." (with-temp-file doom-package-autoload-file (doom--generate-header 'doom-reload-package-autoloads) (save-excursion - ;; Cache the important and expensive-to-initialize state here. + ;; Cache important and expensive-to-initialize state here. (doom--generate-var-cache) (print! (green "✓ Cached package state")) - ;; Loop through packages and concatenate all their autoloads files. + ;; Concatenate the autoloads of all installed packages. (doom--generate-package-autoloads) (print! (green "✓ Package autoloads included"))) ;; Remove `load-path' and `auto-mode-alist' modifications (most of them, diff --git a/core/cli/quickstart.el b/core/cli/quickstart.el index 8c89b10ec..dac3d13dd 100644 --- a/core/cli/quickstart.el +++ b/core/cli/quickstart.el @@ -60,6 +60,9 @@ packages and regenerates the autoloads file." (doom-packages-install doom-auto-accept) (print! "Regenerating autoloads files") (doom-reload-autoloads nil 'force-p) + (when (y-or-n-p "Download and install all-the-icon's fonts?") + (require 'all-the-icons) + (all-the-icons-install-fonts 'yes)) (print! (bold (green "\nFinished! Doom is ready to go!\n"))) (with-temp-buffer (doom-template-insert "QUICKSTART_INTRO") diff --git a/core/cli/test.el b/core/cli/test.el index 6a6a36c25..3d44c34dd 100644 --- a/core/cli/test.el +++ b/core/cli/test.el @@ -16,46 +16,46 @@ If neither is available, run all tests in all enabled modules." ;; Core libraries aren't fully loaded in a noninteractive session, so we ;; reload it with `noninteractive' set to nil to force them to. (quiet! (doom-reload-autoloads)) - (doom-initialize 'force t) - (doom-initialize-modules 'force) - (let ((target-paths - ;; Convert targets 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 (mapcar (apply-partially #'expand-file-name arg) - doom-modules-dirs) - else - nconc (cl-loop for dir in doom-modules-dirs - for path = (expand-file-name arg dir) - if (file-directory-p path) - nconc (doom-files-in path :type 'dirs :depth 1 :full t)) - finally do (setq argv nil)))) + (let ((doom-modules (doom-modules)) + noninteractive) + (let ((target-paths + ;; Convert targets 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 (mapcar (apply-partially #'expand-file-name arg) + doom-modules-dirs) + else + nconc (cl-loop for dir in doom-modules-dirs + for path = (expand-file-name arg dir) + if (file-directory-p path) + nconc (doom-files-in path :type 'dirs :depth 1 :full t)) + finally do (setq argv nil)))) - (modules ; cons-cells given to MODULES - (cl-loop for (module . submodule) in modules - if (doom-module-locate-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)) - ((append (list doom-core-dir) - (doom-module-load-path)))))) - ;; Load all the unit test files... - (require 'buttercup) - (mapc (lambda (file) (load file :noerror (not doom-debug-mode))) - (doom-files-in (mapcar (apply-partially #'expand-file-name "test/") - target-paths) - :match "\\.el$" :full t)) - ;; ... then run them - (when doom-debug-mode - (setq buttercup-stack-frame-style 'pretty)) - (let ((split-width-threshold 0) - (split-height-threshold 0) - (window-min-width 0) - (window-min-height 0)) - (buttercup-run)))) + ((append (list doom-core-dir) + (doom-module-load-path)))))) + ;; Load all the unit test files... + (require 'buttercup) + (mapc (lambda (file) (load file :noerror (not doom-debug-mode))) + (doom-files-in (mapcar (apply-partially #'expand-file-name "test/") + target-paths) + :match "\\.el$" :full t)) + ;; ... then run them + (when doom-debug-mode + (setq buttercup-stack-frame-style 'pretty)) + (let ((split-width-threshold 0) + (split-height-threshold 0) + (window-min-width 0) + (window-min-height 0)) + (buttercup-run))))) ;; diff --git a/core/core-editor.el b/core/core-editor.el index 189ca880b..35eac8e18 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -21,7 +21,7 @@ successfully sets indent_style/indent_size.") detected.") (setq-default - large-file-warning-threshold 30000000 + large-file-warning-threshold 15000000 vc-follow-symlinks t ;; Save clipboard contents into kill-ring before replacing them save-interprogram-paste-before-kill t @@ -37,7 +37,7 @@ detected.") hscroll-margin 2 hscroll-step 1 scroll-conservatively 1001 - scroll-margin 0 + scroll-margin 2 scroll-preserve-screen-position t ;; Whitespace (see `editorconfig') indent-tabs-mode nil @@ -52,23 +52,14 @@ detected.") ;; Remove hscroll-margin in shells, otherwise it causes jumpiness (setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0) -(defun doom|check-large-file () - "Check if the buffer's file is large (see `doom-large-file-size'). If so, ask -for confirmation to open it literally (read-only, disabled undo and in -fundamental-mode) for performance sake." - (when (and (not (memq major-mode doom-large-file-modes-list)) - auto-mode-alist - (get-buffer-window)) - (when-let* ((size (nth 7 (file-attributes buffer-file-name)))) - (when (and (> size (* 1024 1024 doom-large-file-size)) - (y-or-n-p - (format (concat "%s is a large file, open literally to " - "avoid performance issues?") - (file-relative-name buffer-file-name)))) - (setq buffer-read-only t) - (buffer-disable-undo) - (fundamental-mode))))) -(add-hook 'find-file-hook #'doom|check-large-file) +(defun doom*optimize-literal-mode-for-large-files (buffer) + "TODO" + (with-current-buffer buffer + (when find-file-literally + (setq buffer-read-only t) + (buffer-disable-undo)) + buffer)) +(advice-add #'find-file-noselect-1 :filter-return #'doom*optimize-literal-mode-for-large-files) ;; @@ -156,7 +147,7 @@ savehist file." (def-package! smartparens ;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; but that is all Doom uses it for. - :after-call (doom-exit-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :commands (sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string) :config (require 'smartparens-config) @@ -229,9 +220,9 @@ savehist file." (advice-add #'dtrt-indent-mode :around #'doom*fix-broken-smie-modes)) -(def-package! undo-tree +(def-package! undo-tree ;; Branching & persistent undo - :after-call (doom-exit-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config (setq undo-tree-auto-save-history t ;; undo-in-region is known to cause undo history corruption, which can @@ -278,23 +269,29 @@ savehist file." command-log-mode-is-global t)) -(def-package! expand-region - :commands (er/contract-region er/mark-symbol er/mark-word) - :config - (defun doom*quit-expand-region () - "Properly abort an expand-region region." - (when (memq last-command '(er/expand-region er/contract-region)) - (er/contract-region 0))) - (advice-add #'evil-escape :before #'doom*quit-expand-region) - (advice-add #'doom/escape :before #'doom*quit-expand-region)) - - ;; `helpful' --- a better *help* buffer -(let ((map (current-global-map))) - (define-key map [remap describe-function] #'helpful-callable) - (define-key map [remap describe-command] #'helpful-command) - (define-key map [remap describe-variable] #'helpful-variable) - (define-key map [remap describe-key] #'helpful-key)) +(def-package! helpful + :commands helpful--read-symbol + :init + (define-key! + [remap describe-function] #'helpful-callable + [remap describe-command] #'helpful-command + [remap describe-variable] #'helpful-variable + [remap describe-key] #'helpful-key + [remap describe-symbol] #'doom/describe-symbol) + + (after! apropos + ;; patch apropos buttons to call helpful instead of help + (dolist (fun-bt '(apropos-function apropos-macro apropos-command)) + (button-type-put + fun-bt 'action + (lambda (button) + (helpful-callable (button-get button 'apropos-symbol))))) + (dolist (var-bt '(apropos-variable apropos-user-option)) + (button-type-put + var-bt 'action + (lambda (button) + (helpful-variable (button-get button 'apropos-symbol))))))) (def-package! ws-butler diff --git a/core/core-keybinds.el b/core/core-keybinds.el index acbfe37eb..86fb7f6c0 100644 --- a/core/core-keybinds.el +++ b/core/core-keybinds.el @@ -2,26 +2,54 @@ ;; A centralized keybinds system, integrated with `which-key' to preview ;; available keybindings. All built into one powerful macro: `map!'. If evil is -;; never loaded, then evil bindings set with `map!' will be ignored. +;; never loaded, then evil bindings set with `map!' are ignored (i.e. omitted +;; entirely for performance reasons). (defvar doom-leader-key "SPC" - "The leader prefix key for Evil users.") + "The leader prefix key for Evil users. + +This needs to be changed from $DOOMDIR/init.el.") (defvar doom-leader-alt-key "M-SPC" "An alternative leader prefix key, used for Insert and Emacs states, and for -non-evil users.") +non-evil users. + +This needs to be changed from $DOOMDIR/init.el.") (defvar doom-localleader-key "SPC m" - "The localleader prefix key, for major-mode specific commands.") + "The localleader prefix key, for major-mode specific commands. + +This needs to be changed from $DOOMDIR/init.el.") (defvar doom-localleader-alt-key "M-SPC m" - "The localleader prefix key, for major-mode specific commands.") + "The localleader prefix key, for major-mode specific commands. Used for Insert +and Emacs states, and for non-evil users. + +This needs to be changed from $DOOMDIR/init.el.") (defvar doom-leader-map (make-sparse-keymap) "An overriding keymap for keys.") +(defvar doom-which-key-leader-prefix-regexp nil) + ;; +;;; Universal, non-nuclear escape + +;; `keyboard-quit' is too much of a nuclear option. I wanted an ESC/C-g to +;; do-what-I-mean. It serves four purposes (in order): +;; +;; 1. Quit active states; e.g. highlights, searches, snippets, iedit, +;; multiple-cursors, recording macros, etc. +;; 2. Close popup windows remotely (if it is allowed to) +;; 3. Refresh buffer indicators, like git-gutter and flycheck +;; 4. Or fall back to `keyboard-quit' +;; +;; And it should do these things incrementally, rather than all at once. And it +;; shouldn't interfere with recording macros or the minibuffer. This may require +;; you press ESC/C-g two or three times on some occasions to reach +;; `keyboard-quit', but this is much more intuitive. + (defvar doom-escape-hook nil "A hook run after C-g is pressed (or ESC in normal mode, for evil users). Both trigger `doom/escape'. @@ -29,13 +57,13 @@ trigger `doom/escape'. If any hook returns non-nil, all hooks after it are ignored.") (defun doom/escape () - "Run the `doom-escape-hook'." + "Run `doom-escape-hook'." (interactive) (cond ((minibuffer-window-active-p (minibuffer-window)) ;; quit the minibuffer if open. (abort-recursive-edit)) ;; Run all escape hooks. If any returns non-nil, then stop there. - ((cl-find-if #'funcall doom-escape-hook)) + ((run-hook-with-args-until-success 'doom-escape-hook)) ;; don't abort macros ((or defining-kbd-macro executing-kbd-macro) nil) ;; Back to the default @@ -45,53 +73,122 @@ If any hook returns non-nil, all hooks after it are ignored.") ;; -;; General +;;; General + leader/localleader keys (require 'general) - ;; Convenience aliases (defalias 'define-key! #'general-def) (defalias 'unmap! #'general-unbind) -;; leader/localleader keys -(defvar doom-leader-alist `((t . ,doom-leader-map))) -(add-to-list 'emulation-mode-map-alists 'doom-leader-alist) - -;; We avoid `general-create-definer' to ensure that :states, :prefix and +;; We avoid `general-create-definer' to ensure that :states, :wk-full-keys and ;; :keymaps cannot be overwritten. (defmacro define-leader-key! (&rest args) `(general-define-key :states nil + :wk-full-keys nil :keymaps 'doom-leader-map - :prefix doom-leader-alt-key ,@args)) (general-create-definer define-localleader-key! :major-modes t - :wk-full-keys nil :prefix doom-localleader-alt-key) -;; Because :non-normal-prefix doesn't work for non-evil sessions (only evil's -;; emacs state), we must redefine `define-localleader-key!' once evil is loaded +;; :non-normal-prefix doesn't apply to non-evil sessions (only evil's emacs +;; state), so we must redefine `define-localleader-key!' to behave differently +;; where evil is present. (after! evil - (defmacro define-leader-key! (&rest args) - `(general-define-key - :states '(normal visual motion emacs) - :keymaps 'doom-leader-map - :prefix doom-leader-key - :non-normal-prefix doom-leader-alt-key - ,@args)) - (general-create-definer define-localleader-key! :states '(normal visual motion emacs) :major-modes t - :wk-full-keys nil :prefix doom-localleader-key :non-normal-prefix doom-localleader-alt-key)) +;; We use a prefix commands instead of general's :prefix/:non-normal-prefix +;; properties because general is incredibly slow binding keys en mass with them +;; in conjunction with :states -- an effective doubling of Doom's startup time! +(define-prefix-command 'doom/leader 'doom-leader-map) +(define-key doom-leader-map [override-state] 'all) + +;; Bind `doom-leader-key' and `doom-leader-alt-key' as late as possible to give +;; the user a chance to modify them. +(defun doom|init-leader-keys () + "Bind `doom-leader-key' and `doom-leader-alt-key'." + (let ((map general-override-mode-map)) + (if (not (featurep 'evil)) + (define-key map (kbd doom-leader-alt-key) 'doom/leader) + (evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader) + (evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader)) + (general-override-mode +1)) + (unless (stringp doom-which-key-leader-prefix-regexp) + (setq doom-which-key-leader-prefix-regexp + (concat "\\(?:" + (cl-loop for key in (append (list doom-leader-key doom-leader-alt-key) + (where-is-internal 'doom/leader)) + if (stringp key) collect key into keys + else collect (key-description key) into keys + finally return (string-join keys "\\|")) + "\\)")))) +(add-hook 'doom-after-init-modules-hook #'doom|init-leader-keys) + +;; However, the prefix command approach (along with :wk-full-keys in +;; `define-leader-key!') means that which-key is only informed of the key +;; sequence minus `doom-leader-key'/`doom-leader-alt-key'. e.g. binding to `SPC +;; f s' creates a wildcard label for any key that ends in 'f s'. +;; +;; So we forcibly inject `doom-leader-key' and `doom-leader-alt-key' into the +;; which-key key replacement regexp for keybinds created on `doom-leader-map'. +;; This is a dirty hack, but I'd rather this than general being responsible for +;; 50% of Doom's startup time. +(defun doom*general-extended-def-:which-key (_state keymap key edef kargs) + (with-eval-after-load 'which-key + (let* ((wk (general--getf2 edef :which-key :wk)) + (major-modes (general--getf edef kargs :major-modes)) + (keymaps (plist-get kargs :keymaps)) + ;; index of keymap in :keymaps + (keymap-index (cl-dotimes (ind (length keymaps)) + (when (eq (nth ind keymaps) keymap) + (cl-return ind)))) + (mode (let ((mode (if (and major-modes (listp major-modes)) + (nth keymap-index major-modes) + major-modes))) + (if (eq mode t) + (general--remove-map keymap) + mode))) + (key (key-description key)) + (key-regexp (concat (if (general--getf edef kargs :wk-full-keys) + "\\`" + ;; Modification begin + (if (memq 'doom-leader-map keymaps) + (concat "\\`" doom-which-key-leader-prefix-regexp " "))) + ;; Modification end + (regexp-quote key) + "\\'")) + (prefix (plist-get kargs :prefix)) + (binding (or (when (and (plist-get edef :def) + (not (plist-get edef :keymp))) + (plist-get edef :def)) + (when (and prefix (string= key prefix)) + (plist-get kargs :prefix-command)))) + (replacement (cond ((stringp wk) + (cons nil wk)) + (wk))) + (match/replacement + (cons + (cons (when (general--getf edef kargs :wk-match-keys) + key-regexp) + (when (and (general--getf edef kargs :wk-match-binding) + binding + (symbolp binding)) + (symbol-name binding))) + replacement))) + (general--add-which-key-replacement mode match/replacement) + (when (and (consp replacement) (not (functionp replacement))) + (general--add-which-key-title-prefix mode key (cdr replacement)))))) +(advice-add #'general-extended-def-:which-key :override #'doom*general-extended-def-:which-key) + ;; -;; Packages +;;; Packages (def-package! which-key :defer 1 @@ -111,11 +208,13 @@ If any hook returns non-nil, all hooks after it are ignored.") (which-key-mode +1)) -;; `hydra' +;;;###package hydra (setq lv-use-seperator t) ;; +;;; `map!' macro + (defvar doom-evil-state-alist '((?n . normal) (?v . visual) diff --git a/core/core-lib.el b/core/core-lib.el index 86bf7cf60..7f71cad74 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,20 +1,7 @@ ;;; core-lib.el -*- lexical-binding: t; -*- -;; Built-in packages we use a lot of -(require 'subr-x) -(require 'cl-lib) - -(eval-and-compile - (unless EMACS26+ - (with-no-warnings - ;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we - ;; alias them for 25 users. - (defalias 'if-let* #'if-let) - (defalias 'when-let* #'when-let)))) - - ;; -;; Helpers +;;; Helpers (defun doom--resolve-path-forms (spec &optional directory) "Converts a simple nested series of or/and forms into a series of @@ -23,23 +10,27 @@ For example (doom--resolve-path-forms - '(or \"some-file\" (and path-var \"/an/absolute/path\")) + '(or A (and B C)) \"~\") -Returns +Returns (approximately): - '(let ((_directory \"~\")) - (or (file-exists-p (expand-file-name \"some-file\" _directory)) - (and (file-exists-p (expand-file-name path-var _directory)) - (file-exists-p \"/an/absolute/path\")))) + '(let* ((_directory \"~\") + (A (expand-file-name A _directory)) + (B (expand-file-name B _directory)) + (C (expand-file-name C _directory))) + (or (and (file-exists-p A) A) + (and (if (file-exists-p B) B) + (if (file-exists-p C) C)))) This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." (declare (pure t) (side-effect-free t)) (cond ((stringp spec) - `(file-exists-p - ,(if (file-name-absolute-p spec) - spec - `(expand-file-name ,spec ,directory)))) + `(let ((--file-- ,(if (file-name-absolute-p spec) + spec + `(expand-file-name ,spec ,directory)))) + (and (file-exists-p --file--) + --file--))) ((and (listp spec) (memq (car spec) '(or and))) `(,(car spec) @@ -47,12 +38,14 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." collect (doom--resolve-path-forms i directory)))) ((or (symbolp spec) (listp spec)) - `(file-exists-p ,(if (and directory - (or (not (stringp directory)) - (file-name-absolute-p directory))) - `(expand-file-name ,spec ,directory) - spec))) - (t spec))) + `(let ((--file-- ,(if (and directory + (or (not (stringp directory)) + (file-name-absolute-p directory))) + `(expand-file-name ,spec ,directory) + spec))) + (and (file-exists-p --file--) + --file--))) + (spec))) (defun doom--resolve-hook-forms (hooks) (declare (pure t) (side-effect-free t)) @@ -76,7 +69,7 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." ;; -;; Public library +;;; Public library (defun doom-unquote (exp) "Return EXP unquoted." @@ -102,6 +95,23 @@ This is used by `associate!', `file-exists-p!' and `project-file-exists-p!'." (cl-check-type :test keyword) (substring (symbol-name keyword) 1)) +(defmacro doom-log (format-string &rest args) + "Log to *Messages* if `doom-debug-mode' is on. +Does not interrupt the minibuffer if it is in use, but still logs to *Messages*. +Accepts the same arguments as `message'." + `(when doom-debug-mode + (let ((inhibit-message (active-minibuffer-window))) + (message + ,(concat (propertize "DOOM " 'face 'font-lock-comment-face) + format-string + (when doom--current-module + (propertize + (format " [%s/%s]" + (doom-keyword-name (car doom--current-module)) + (cdr doom--current-module)) + 'face 'warning))) + ,@args)))) + (defun FILE! () "Return the emacs lisp file this macro is called from." (cond ((bound-and-true-p byte-compile-current-file)) @@ -143,20 +153,25 @@ serve as a predicated alternative to `after!'." (add-hook 'after-load-functions #',fun))))) (defmacro defer-feature! (feature &optional mode) - "TODO" + "Pretend FEATURE hasn't been loaded yet, until FEATURE-hook is triggered. + +Some packages (like `elisp-mode' and `lisp-mode') are loaded immediately at +startup, which will prematurely trigger `after!' (and `with-eval-after-load') +blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet, +then wait until FEATURE-hook (or MODE-hook, if MODE is provided) is triggered to +reverse this and trigger `after!' blocks at a more reasonable time." (let ((advice-fn (intern (format "doom|defer-feature-%s" feature))) (mode (or mode feature))) `(progn - (delq ',feature features) + (setq features (delq ',feature features)) (advice-add #',mode :before #',advice-fn) (defun ,advice-fn (&rest _) - ;; Some plugins (like yasnippet) run `lisp-mode' early, to parse some - ;; elisp. This would prematurely trigger this function. In these cases, - ;; `lisp-mode-hook' is let-bound to nil or its hooks are delayed, so if - ;; we see either, keep pretending elisp-mode isn't loaded. + ;; Some plugins (like yasnippet) will invoke a mode early, e.g. to + ;; parse some code. This would prematurely trigger this function. This + ;; checks for that: (when (and ,(intern (format "%s-hook" mode)) (not delay-mode-hooks)) - ;; Otherwise, announce to the world elisp-mode has been loaded, so + ;; Otherwise, announce to the world this package has been loaded, so ;; `after!' handlers can respond and configure elisp-mode as ;; expected. (provide ',feature) @@ -279,7 +294,9 @@ Examples: (add-hook! :append :local (one-mode second-mode) (setq v 5) (setq a 2)) Body forms can access the hook's arguments through the let-bound variable -`args'." +`args'. + +\(fn [:append :local] HOOKS FUNCTIONS)" (declare (indent defun) (debug t)) (let ((hook-fn 'add-hook) append-p local-p) @@ -310,7 +327,9 @@ Body forms can access the hook's arguments through the let-bound variable (defmacro remove-hook! (&rest args) "Convenience macro for `remove-hook'. Takes the same arguments as -`add-hook!'." +`add-hook!'. + +\(fn [:append :local] HOOKS FUNCTIONS)" (declare (indent defun) (debug t)) `(add-hook! :remove ,@args)) @@ -331,6 +350,33 @@ Body forms can access the hook's arguments through the let-bound variable (push `(setq-local ,var ,val) forms))) (nreverse forms)))) +(defun advice-add! (symbols where functions) + "Variadic version of `advice-add'. + +SYMBOLS and FUNCTIONS can be lists of functions." + (let ((functions (if (functionp functions) + (list functions) + functions))) + (dolist (s (doom-enlist symbols)) + (dolist (f (doom-enlist functions)) + (advice-add s where f))))) + +(defun advice-remove! (symbols where-or-fns &optional functions) + "Variadic version of `advice-remove'. + +WHERE-OR-FNS is ignored if FUNCTIONS is provided. This lets you substitute +advice-add with advice-remove and evaluate them without having to modify every +statement." + (unless functions + (setq functions where-or-fns + where-or-fns nil)) + (let ((functions (if (functionp functions) + (list functions) + functions))) + (dolist (s (doom-enlist symbols)) + (dolist (f (doom-enlist functions)) + (advice-remove s f))))) + (cl-defmacro associate! (mode &key modes match files when) "Enables a minor mode if certain conditions are met. @@ -377,16 +423,15 @@ The available conditions are: mode modes match files when))))) (defmacro file-exists-p! (spec &optional directory) - "Returns t if the files in SPEC all exist. + "Returns non-nil 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. +Returns the last file found to meet the rules set by SPEC. 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. +DIRECTORY is where to look for the files in SPEC if they aren't absolute. For example: - (file-exists-p! (or doom-core-dir \"~/.config\" \"some-file\") \"~\")" (if directory `(let ((--directory-- ,directory)) diff --git a/core/core-modules.el b/core/core-modules.el index 3007460cc..f91778076 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -17,7 +17,8 @@ (syntax-checker (:tools flycheck))) (:tools (rotate-text (:editor rotate-text))) (:emacs (electric-indent (:emacs electric)) - (hideshow (:editor fold)))) + (hideshow (:editor fold))) + (:ui (doom-modeline (:ui modeline)))) "An alist of deprecated modules, mapping deprecated modules to an optional new location (which will create an alias). Each CAR and CDR is a (CATEGORY . MODULES). E.g. @@ -32,7 +33,24 @@ A warning will be put out if these deprecated modules are used.") ;; -;; Bootstrap API +;;; Custom hooks + +(defvar doom-before-init-modules-hook nil + "A list of hooks to run before Doom's modules' config.el files are loaded, but +after their init.el files are loaded.") + +(defvar doom-init-modules-hook nil + "A list of hooks to run after Doom's modules' config.el files have loaded, but +before the user's private module.") + +(defvaralias 'doom-after-init-modules-hook 'after-init-hook) + +(define-obsolete-variable-alias 'doom-post-init-hook 'doom-init-modules-hook "2.1.0") +(define-obsolete-variable-alias 'doom-init-hook 'doom-before-init-modules-hook "2.1.0") + + +;; +;;; Bootstrap API (defun doom-initialize-modules (&optional force-p) "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy @@ -50,23 +68,23 @@ non-nil." (doom--current-flags (plist-get plist :flags))) (load! "init" (plist-get plist :path) t))) doom-modules) - (run-hook-wrapped 'doom-init-hook #'doom-try-run-hook) + (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (unless noninteractive (maphash (lambda (key plist) (let ((doom--current-module key) (doom--current-flags (plist-get plist :flags))) (load! "config" (plist-get plist :path) t))) doom-modules) + (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (load! "config" doom-private-dir t) (unless custom-file (setq custom-file (concat doom-local-dir "custom.el"))) (when (stringp custom-file) - (load custom-file t t t)) - (run-hook-wrapped 'doom-post-init-hook #'doom-try-run-hook)))) + (load custom-file t t t))))) ;; -;; Module API +;;; Module API (defun doom-module-p (category module) "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." @@ -176,7 +194,6 @@ non-nil, return paths of possible modules, activated or otherwise." (let ((noninteractive t) doom-modules doom-init-modules-p) - (message "Initializing modules") (load! "init" doom-private-dir t) (or doom-modules (make-hash-table :test 'equal @@ -185,7 +202,7 @@ non-nil, return paths of possible modules, activated or otherwise." ;; -;; Use-package modifications +;;; Use-package modifications (autoload 'use-package "use-package-core" nil nil t) @@ -194,43 +211,24 @@ non-nil, return paths of possible modules, activated or otherwise." use-package-minimum-reported-time (if doom-debug-mode 0 0.1) use-package-expand-minimally (not noninteractive)) -;; Adds two new keywords to `use-package' (and consequently, `def-package!'), -;; they are: +;; Adds two new keywords to `use-package' (and consequently, `def-package!') to +;; expand its lazy-loading capabilities. They are: ;; ;; :after-call SYMBOL|LIST -;; Takes a symbol or list of symbols representing functions or hook variables. -;; The first time any of these functions or hooks are executed, the package is -;; loaded. e.g. -;; -;; (def-package! projectile -;; :after-call (pre-command-hook after-find-file dired-before-readin-hook) -;; ...) -;; ;; :defer-incrementally SYMBOL|LIST|t -;; Takes a symbol or list of symbols representing packages that will be loaded -;; incrementally at startup before this one. This is helpful for large -;; packages like magit or org, which load a lot of dependencies on first load. -;; This lets you load them piece-meal, one at a time, during idle periods, so -;; that when you finally do need the package, it'll loads much quicker. e.g. ;; -;; (def-package! magit -;; ;; You do not need to include magit in this list! -;; :defer-incrementally (dash f s with-editor git-commit package) -;; ...) -;; -;; (def-package! x -;; ;; This is equivalent to :defer-incrementally (x) -;; :defer-incrementally t -;; ...) +;; Check out `def-package!'s documentation for more about these two. (defvar doom--deferred-packages-alist '(t)) (after! use-package-core - (add-to-list 'use-package-deferring-keywords :defer-incrementally nil #'eq) - (add-to-list 'use-package-deferring-keywords :after-call nil #'eq) + ;; :ensure and :pin don't work well with Doom, so we forcibly remove them. + (dolist (keyword '(:ensure :pin)) + (setq use-package-keywords (delq keyword use-package-keywords))) - (setq use-package-keywords - (use-package-list-insert :defer-incrementally use-package-keywords :after)) - (setq use-package-keywords - (use-package-list-insert :after-call use-package-keywords :after)) + ;; Insert new deferring keywords + (dolist (keyword '(:defer-incrementally :after-call)) + (add-to-list 'use-package-deferring-keywords keyword nil #'eq) + (setq use-package-keywords + (use-package-list-insert keyword use-package-keywords :after))) (defalias 'use-package-normalize/:defer-incrementally 'use-package-normalize-symlist) (defun use-package-handler/:defer-incrementally (name _keyword targets rest state) @@ -249,18 +247,18 @@ non-nil, return paths of possible modules, activated or otherwise." (use-package-concat `((fset ',fn (lambda (&rest _) - (when doom-debug-mode - (message "Loading deferred package %s from %s" ',name ',fn)) - (condition-case e (require ',name) + (doom-log "Loading deferred package %s from %s" ',name ',fn) + (condition-case e + (require ',name) ((debug error) (message "Failed to load deferred package %s: %s" ',name e))) - (dolist (hook (cdr (assq ',name doom--deferred-packages-alist))) - (if (functionp hook) - (advice-remove hook #',fn) - (remove-hook hook #',fn))) - (delq (assq ',name doom--deferred-packages-alist) - doom--deferred-packages-alist) - (fmakunbound ',fn)))) + (when-let* ((deferral-list (assq ',name doom--deferred-packages-alist))) + (dolist (hook (cdr deferral-list)) + (if (functionp hook) + (advice-remove hook #',fn) + (remove-hook hook #',fn))) + (setq doom--deferred-packages-alist + (delq deferral-list doom--deferred-packages-alist)))))) (let (forms) (dolist (hook hooks forms) (push (if (functionp hook) @@ -275,7 +273,7 @@ non-nil, return paths of possible modules, activated or otherwise." ;; -;; Module config macros +;;; Module config macros (defmacro doom! (&rest modules) "Bootstraps DOOM Emacs and its modules. @@ -294,12 +292,14 @@ The overall load order of Doom is as follows: ~/.emacs.d/core/core.el $DOOMDIR/init.el {$DOOMDIR,~/.emacs.d}/modules/*/*/init.el - `doom-init-hook' + `doom-before-init-modules-hook' {$DOOMDIR,~/.emacs.d}/modules/*/*/config.el + `doom-init-modules-hook' $DOOMDIR/config.el + `doom-after-init-modules-hook' `after-init-hook' `emacs-startup-hook' - `doom-post-init-hook' (at end of `emacs-startup-hook') + `window-setup-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 @@ -335,10 +335,43 @@ to least)." (defvar doom-disabled-packages) (defmacro def-package! (name &rest plist) - "This is a thin wrapper around `use-package'." - `(use-package ,name - ,@(if (memq name doom-disabled-packages) `(:disabled t)) - ,@plist)) + "This is a thin wrapper around `use-package'. + +It is ignored if the NAME package is disabled. + +Supports two special properties over `use-package': + +:after-call SYMBOL|LIST + Takes a symbol or list of symbols representing functions or hook variables. + The first time any of these functions or hooks are executed, the package is + loaded. e.g. + + (def-package! projectile + :after-call (pre-command-hook after-find-file dired-before-readin-hook) + ...) + +:defer-incrementally SYMBOL|LIST|t + Takes a symbol or list of symbols representing packages that will be loaded + incrementally at startup before this one. This is helpful for large packages + like magit or org, which load a lot of dependencies on first load. This lets + you load them piece-meal during idle periods, so that when you finally do need + the package, it'll load quicker. e.g. + + NAME is implicitly added if this property is present and non-nil. No need to + specify it. A value of `t' implies NAME, e.g. + + (def-package! x + ;; This is equivalent to :defer-incrementally (x) + :defer-incrementally t + ...)" + (unless (or (memq name doom-disabled-packages) + ;; At compile-time, use-package will forcibly load its package to + ;; prevent compile-time errors. However, Doom users can + ;; intentionally disable packages, resulting if file-missing + ;; package errors, so we preform this check at compile time: + (and (bound-and-true-p byte-compile-current-file) + (not (locate-library (symbol-name name))))) + `(use-package ,name ,@plist))) (defmacro def-package-hook! (package when &rest body) "Reconfigures a package's `def-package!' block. diff --git a/core/core-os.el b/core/core-os.el index a7fd04298..2f5046bbd 100644 --- a/core/core-os.el +++ b/core/core-os.el @@ -1,10 +1,18 @@ ;;; core-os.el -*- lexical-binding: t; -*- ;; clipboard -(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING) - ;; Use a shared clipboard - select-enable-clipboard t - select-enable-primary t) +(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) + +;; fewer opts to process for systems that don't need them +(unless IS-MAC (setq command-line-ns-option-alist nil)) +(unless IS-LINUX (setq command-line-x-option-alist nil)) + +;; Fix the clipboard in terminal or daemon Emacs (non-GUI) +(defun doom|init-clipboard-in-tty-emacs () + (if IS-MAC + (if (require 'osx-clipboard nil t) (osx-clipboard-mode)) + (if (require 'xclip nil t) (xclip-mode)))) +(add-hook 'tty-setup-hook #'doom|init-clipboard-in-tty-emacs) ;; stop copying each visual state move to the clipboard: ;; https://bitbucket.org/lyro/evil/issue/336/osx-visual-state-copies-the-region-on @@ -29,14 +37,11 @@ ;; than a new one ns-pop-up-frames nil) - ;; Fix the clipboard in terminal or daemon Emacs (non-GUI) - (when (or (daemonp) (not (display-graphic-p))) - (add-hook 'doom-post-init-hook #'osx-clipboard-mode)) - (when (or (daemonp) (display-graphic-p)) ;; Syncs ns frame parameters with theme (and fixes mismatching text ;; colr in the frame title) - (require 'ns-auto-titlebar nil t) + (when (require 'ns-auto-titlebar nil t) + (add-hook 'doom-load-theme-hook #'ns-auto-titlebar-mode)) ;; A known problem with GUI Emacs on MacOS (or daemons started via ;; launchctl or brew services): it runs in an isolated diff --git a/core/core-packages.el b/core/core-packages.el index 94908abc4..eac7f293a 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -87,52 +87,27 @@ If FORCE-P is 'internal, only (re)populate `doom-packages'. Use this before any of package.el, quelpa or Doom's package management's API to ensure all the necessary package metadata is initialized and available for them." - (with-temp-buffer ; prevent buffer-local settings from propagating - (let ((load-prefer-newer t)) ; reduce stale code issues - ;; package.el and quelpa handle themselves if their state changes during - ;; the current session, but if you change an packages.el file in a module, - ;; there's no non-trivial way to detect that, so we give you a way to - ;; reload only doom-packages (by passing 'internal as FORCE-P). - (unless (eq force-p 'internal) - ;; `package-alist' - (when (or force-p (not (bound-and-true-p package-alist))) - (doom-ensure-packages-initialized 'force) - (setq load-path (cl-remove-if-not #'file-directory-p load-path))) - ;; `quelpa-cache' - (when (or force-p (not (bound-and-true-p quelpa-cache))) - ;; ensure un-byte-compiled version of quelpa is loaded - (unless (featurep 'quelpa) - (load (locate-library "quelpa.el") nil t t)) - (setq quelpa-initialized-p nil) - (or (quelpa-setup-p) - (error "Could not initialize quelpa")))) - ;; `doom-packages' - (when (or force-p (not doom-packages)) - (cl-flet - ((_load - (lambda (file &optional noerror) - (condition-case e - (load file noerror t t) - ((debug error) - (signal 'doom-package-error - (list (or (doom-module-from-path file) - '(:private . packages)) - e))))))) - (let ((doom-modules (doom-modules)) - (doom--stage 'packages) - (noninteractive t)) - (setq doom-packages nil) - (_load (expand-file-name "packages.el" doom-core-dir)) - ;; We load the private packages file twice to ensure disabled - ;; packages are seen ASAP, and a second time to ensure privately - ;; overridden packages are properly overwritten. - (let ((private-packages (expand-file-name "packages.el" doom-private-dir))) - (_load private-packages t) - (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))) - (_load private-packages t) - (setq doom-packages (reverse doom-packages))))))))) + (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 to reload only + ;; doom-packages pass 'internal as FORCE-P or use `doom/reload-packages'. + (unless (eq force-p 'internal) + ;; `package-alist' + (when (or force-p (not (bound-and-true-p package-alist))) + (doom-ensure-packages-initialized 'force) + (setq load-path (cl-delete-if-not #'file-directory-p load-path))) + ;; `quelpa-cache' + (when (or force-p (not (bound-and-true-p quelpa-cache))) + ;; ensure un-byte-compiled version of quelpa is loaded + (unless (featurep 'quelpa) + (load (locate-library "quelpa.el") nil t t)) + (setq quelpa-initialized-p nil) + (or (quelpa-setup-p) + (error "Could not initialize quelpa")))) + ;; `doom-packages' + (when (or force-p (not doom-packages)) + (setq doom-packages (doom-package-list))))) ;; @@ -169,7 +144,7 @@ them." ;; ;; Module package macros -(cl-defmacro package! (name &rest plist &key recipe pin disable _ignore _freeze) +(cl-defmacro package! (name &rest plist &key built-in recipe pin disable _ignore _freeze) "Declares a package and how to install it (if applicable). This macro is declarative and does not load nor install packages. It is used to @@ -193,30 +168,48 @@ Accepts the following properties: Do not install this package. :freeze FORM Do not update this package if FORM is non-nil. + :built-in BOOL + Same as :ignore if the package is a built-in Emacs package. Returns t if package is successfully registered, and nil if it was disabled elsewhere." (declare (indent defun)) (doom--assert-stage-p 'packages #'package!) - (let ((plist (append plist (cdr (assq name doom-packages))))) + (let ((old-plist (cdr (assq name doom-packages)))) (when recipe (when (cl-evenp (length recipe)) (setq plist (plist-put plist :recipe (cons name recipe)))) (setq pin nil plist (plist-put plist :pin nil))) - (when (file-in-directory-p (FILE!) doom-private-dir) - (setq plist (plist-put plist :private t))) - (let (newplist) - (while plist - (unless (null (cadr plist)) - (push (cadr plist) newplist) - (push (car plist) newplist)) - (pop plist) - (pop plist)) - (setq plist newplist)) + (let ((module-list (plist-get old-plist :modules)) + (module (or doom--current-module + (let ((file (FILE!))) + (cond ((file-in-directory-p file doom-private-dir) + (list :private)) + ((file-in-directory-p file doom-core-dir) + (list :core)) + ((doom-module-from-path file))))))) + (doom-log "Registered package '%s'%s" + name (if recipe (format " with recipe %s" recipe) "")) + (unless (member module module-list) + (setq module-list (append module-list (list module) nil) + plist (plist-put plist :modules module-list)))) + (when (and built-in (locate-library (symbol-name name) nil doom-site-load-path)) + (doom-log "Ignoring built-in package '%s'" name) + (setq plist (plist-put plist :ignore t))) + (while plist + (unless (null (cadr plist)) + (setq old-plist (plist-put old-plist (car plist) (cadr plist)))) + (pop plist) + (pop plist)) + (setq plist old-plist) (macroexp-progn - (append (if disable `((add-to-list 'doom-disabled-packages ',name nil #'eq))) - (if pin `((setf (alist-get ',name package-pinned-packages) ,pin))) + (append (when disable + (doom-log "Disabling package '%s'" name) + `((add-to-list 'doom-disabled-packages ',name nil 'eq))) + (when pin + (doom-log "Pinning package '%s' to '%s'" name pin) + `((setf (alist-get ',name package-pinned-packages) ,pin))) `((setf (alist-get ',name doom-packages) ',plist) (not (memq ',name doom-disabled-packages))))))) diff --git a/core/core-projects.el b/core/core-projects.el index 3e2059379..d6c0b0bc1 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -1,7 +1,7 @@ ;;; core-projects.el -*- lexical-binding: t; -*- (def-package! projectile - :after-call (pre-command-hook after-find-file dired-before-readin-hook) + :after-call (after-find-file dired-before-readin-hook minibuffer-setup-hook) :commands (projectile-project-root projectile-project-name projectile-project-p) :init (setq projectile-cache-file (concat doom-cache-dir "projectile.cache") @@ -15,9 +15,11 @@ :config (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) - (add-hook 'find-file-hook #'doom|init-project-mode) (projectile-mode +1) + (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag) + (global-set-key [remap find-tag] #'projectile-find-tag) + ;; a more generic project root file (push ".project" projectile-project-root-files-bottom-up) @@ -66,23 +68,10 @@ ;; ;; Project-based minor modes -(defvar-local doom-project nil - "Either the symbol or a list of project modes you want to enable. Available -for .dir-locals.el.") - (defvar doom-project-hook nil "Hook run when a project is enabled. The name of the project's mode and its state are passed in.") -(defun doom|init-project-mode () - "Auto-enable the project(s) listed in `doom-project'." - (when doom-project - (if (symbolp doom-project) - (funcall doom-project) - (cl-loop for mode in doom-project - unless (symbol-value mode) - do (funcall mode))))) - (cl-defmacro def-project-mode! (name &key modes files @@ -94,9 +83,9 @@ state are passed in.") on-exit) "Define a project minor-mode named NAME (a symbol) and declare where and how it is activated. Project modes allow you to configure 'sub-modes' for -major-modes that are specific to a specific folder, certain project structure, -framework or arbitrary context you define. These project modes can have their -own settings, keymaps, hooks, snippets, etc. +major-modes that are specific to a folder, project structure, framework or +whatever arbitrary context you define. These project modes can have their own +settings, keymaps, hooks, snippets, etc. This creates NAME-hook and NAME-map as well. diff --git a/core/core-ui.el b/core/core-ui.el index a7c745a93..300bc26d9 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -1,9 +1,12 @@ ;;; core-ui.el -*- lexical-binding: t; -*- +;; +;;; Variables + (defvar doom-theme nil "A symbol representing the Emacs theme to load at startup. -This is changed when `load-theme' is used as well.") +This is changed by `load-theme'.") (defvar doom-font nil "The default font to use. @@ -48,15 +51,106 @@ Expects either a `font-spec', font object, a XFT font string or XLFD string. See It is recommended you don't set specify a font-size, as to inherit `doom-font's size.") - -;; -(defvar doom-init-ui-hook nil - "List of hooks to run when the UI has been initialized.") - (defvar doom--prefer-theme-elc nil "If non-nil, `load-theme' will prefer the compiled theme (unlike its default behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") + +;; +;;; Custom hooks + +(defvar doom-init-ui-hook nil + "List of hooks to run when the UI has been initialized.") + +(defvar doom-load-theme-hook nil + "Hook run after the theme is loaded with `load-theme' or reloaded with +`doom/reload-theme'.") + +(defvar doom-switch-buffer-hook nil + "TODO") + +(defvar doom-switch-window-hook nil + "TODO") + +(defvar doom-switch-frame-hook nil + "TODO") + +(defvar doom-inhibit-switch-buffer-hooks nil + "Letvar for inhibiting `doom-switch-buffer-hook'. Do not set this directly.") +(defvar doom-inhibit-switch-window-hooks nil + "Letvar for inhibiting `doom-switch-window-hook'. Do not set this directly.") +(defvar doom-inhibit-switch-frame-hooks nil + "Letvar for inhibiting `doom-switch-frame-hook'. Do not set this directly.") + +(defvar doom--last-window nil) +(defvar doom--last-frame nil) + +(defun doom|run-switch-window-hooks () + (unless (or doom-inhibit-switch-window-hooks + (eq doom--last-window (selected-window)) + (minibufferp)) + (let ((doom-inhibit-switch-window-hooks t)) + (run-hooks 'doom-switch-window-hook) + (doom-log "Window switched to %s" (selected-window)) + (setq doom--last-window (selected-window))))) + +(defun doom|run-switch-frame-hooks (&rest _) + (let ((selected-frame (selected-frame))) + (unless (or doom-inhibit-switch-frame-hooks + (eq doom--last-frame (selected-frame)) + (frame-parameter nil 'parent-frame)) + (let ((doom-inhibit-switch-frame-hooks t)) + (run-hooks 'doom-switch-frame-hook) + (doom-log "Frame switched to %s" (selected-frame)) + (setq doom--last-frame (selected-frame)))))) + +(defun doom*run-switch-buffer-hooks (orig-fn buffer-or-name &rest args) + (if (or doom-inhibit-switch-buffer-hooks + (if (eq orig-fn 'switch-to-buffer) + (car args) ; norecord + (eq (get-buffer buffer-or-name) (current-buffer)))) + (apply orig-fn buffer-or-name args) + (let ((doom-inhibit-switch-buffer-hooks t)) + (doom-log "Buffer switched in %s" (selected-window)) + (prog1 (apply orig-fn buffer-or-name args) + (run-hooks 'doom-switch-buffer-hook))))) + +(defun doom*run-load-theme-hooks (theme &optional _no-confirm no-enable) + "Set up `doom-load-theme-hook' to run after `load-theme' is called." + (unless no-enable + (setq doom-theme theme) + (run-hooks 'doom-load-theme-hook))) + +(defun doom|protect-fallback-buffer () + "Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'." + (not (eq (current-buffer) (doom-fallback-buffer)))) + +(defun doom|highlight-non-default-indentation () + "Highlight whitespace that doesn't match your `indent-tabs-mode' setting. + +e.g. If you indent with spaces by default, tabs will be highlighted. If you +indent with tabs, spaces at BOL are highlighted. + +Does nothing if `whitespace-mode' is already active or the current buffer is +read-only or not file-visiting." + (unless (or (bound-and-true-p global-whitespace-mode) + (bound-and-true-p whitespace-mode) + (eq major-mode 'fundamental-mode) + buffer-read-only + (null buffer-file-name)) + (require 'whitespace) + (set (make-local-variable 'whitespace-style) + (if (bound-and-true-p whitespace-newline-mode) + (cl-union (if indent-tabs-mode '(indentation) '(tabs tab-mark)) + whitespace-style) + `(face ,@(if indent-tabs-mode '(indentation) '(tabs tab-mark)) + trailing-lines tail))) + (whitespace-mode +1))) + + +;; +;;; General configuration + (setq-default ansi-color-for-comint-mode t bidi-display-reordering nil ; disable bidirectional text for tiny performance boost @@ -71,6 +165,7 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") display-line-numbers-width 3 enable-recursive-minibuffers nil frame-inhibit-implied-resize t + frame-title-format '("%b – Doom Emacs") ; simple name in frame title ;; remove continuation arrow on right fringe fringe-indicator-alist (delq (assq 'continuation fringe-indicator-alist) @@ -86,7 +181,7 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") resize-mini-windows 'grow-only ; Minibuffer resizing show-help-function nil ; hide :help-echo text split-width-threshold 160 ; favor horizontal splits - uniquify-buffer-name-style 'forward + uniquify-buffer-name-style nil ; custom modeline will show file paths anyway use-dialog-box nil ; always avoid GUI visible-cursor nil x-stretch-cursor nil @@ -99,68 +194,41 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") ;; don't resize emacs in steps, it looks weird window-resize-pixelwise t frame-resize-pixelwise t) - ;; y/n instead of yes/no (fset #'yes-or-no-p #'y-or-n-p) - ;; Truly silence startup message (fset #'display-startup-echo-area-message #'ignore) +;; relegate tooltips to echo area only +(if (bound-and-true-p tooltip-mode) (tooltip-mode -1)) +;; enabled by default; no thanks, too distracting +(blink-cursor-mode -1) +;; Handle ansi codes in compilation buffer +(add-hook 'compilation-filter-hook #'doom|apply-ansi-color-to-compilation-buffer) +;; 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) +;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. +(add-to-list 'default-frame-alist '(buffer-predicate . doom-buffer-frame-predicate)) +;; Prevent the glimpse of un-styled Emacs by setting these early. +(add-to-list 'default-frame-alist '(tool-bar-lines . 0)) +(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) +(add-to-list 'default-frame-alist '(vertical-scroll-bars)) +;; prompts the user for confirmation when deleting a non-empty frame +(global-set-key [remap delete-frame] #'doom/delete-frame) ;; -;; Third party packages +;;; Built-in packages -;; `avy' -(setq avy-all-windows nil - avy-background t) +;; Disable these because whitespace should be customized programmatically +;; (through `whitespace-style'), and not through these commands. +(put 'whitespace-toggle-options 'disabled t) +(put 'global-whitespace-toggle-options 'disabled 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) - :init - (defun doom*disable-all-the-icons-in-tty (orig-fn &rest args) - (if (display-graphic-p) - (apply orig-fn args) - "")) - :config - ;; all-the-icons doesn't work in the terminal, so we "disable" it. - (dolist (fn '(all-the-icons-octicon all-the-icons-material - all-the-icons-faicon all-the-icons-fileicon - all-the-icons-wicon all-the-icons-alltheicon)) - (advice-add fn :around #'doom*disable-all-the-icons-in-tty))) - -;; `hide-mode-line-mode' -(add-hook 'completion-list-mode-hook #'hide-mode-line-mode) -(add-hook 'Man-mode-hook #'hide-mode-line-mode) - -;; `highlight-numbers' --- better number literal fontification in code -(def-package! highlight-numbers - :hook ((prog-mode conf-mode) . highlight-numbers-mode) - :config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>")) - -;; `highlight-escape-sequences' -(def-package! highlight-escape-sequences - :hook ((prog-mode conf-mode) . highlight-escape-sequences-mode)) - -;; `rainbow-delimiters' --- helps us distinguish stacked delimiter pairs. -;; Especially in parentheses-drunk languages like Lisp. -(setq rainbow-delimiters-max-face-count 3) - -;; `restart-emacs' --- provides a simple mechanism for restarting Emacs and -;; daemons interactively. -(setq restart-emacs--args (list "--restore")) - -;; `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 EMACS26+ 6 0) fill-column)) - - -;; -;; Built-in packages (def-package! ediff :defer t @@ -169,13 +237,15 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") ediff-split-window-function #'split-window-horizontally ediff-window-setup-function #'ediff-setup-windows-plain) :config + (defvar doom--ediff-saved-wconf nil) ;; Restore window config after quitting ediff (defun doom|ediff-save-wconf () - (setq +ediff--saved-wconf (current-window-configuration))) + (setq doom--ediff-saved-wconf (current-window-configuration))) (add-hook 'ediff-before-setup-hook #'doom|ediff-save-wconf) (defun doom|ediff-restore-wconf () - (set-window-configuration +ediff--saved-wconf)) + (when (window-configuration-p doom--ediff-saved-wconf) + (set-window-configuration doom--ediff-saved-wconf))) (add-hook 'ediff-quit-hook #'doom|ediff-restore-wconf 'append) (add-hook 'ediff-suspend-hook #'doom|ediff-restore-wconf 'append)) @@ -189,21 +259,6 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (setq hl-line-sticky-flag nil global-hl-line-sticky-flag nil) - ;; On Emacs 26+, when point is on the last line, hl-line highlights bleed into - ;; the rest of the window after eob. This is the fix. - (when EMACS26+ - (defun doom--line-range () - (cons (line-beginning-position) - (cond ((let ((eol (line-end-position))) - (and (= eol (point-max)) - (/= eol (line-beginning-position)))) - (1- (line-end-position))) - ((or (eobp) - (= (line-end-position 2) (point-max))) - (line-end-position)) - ((line-beginning-position 2))))) - (setq hl-line-range-function #'doom--line-range)) - ;; Disable `hl-line' in evil-visual mode (temporarily). `hl-line' can make the ;; selection region harder to see while in evil visual mode. (after! evil @@ -220,14 +275,14 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (def-package! winner ;; undo/redo changes to Emacs' window layout - :after-call doom-exit-window-hook - :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself - :config (winner-mode +1)) + :after-call (after-find-file doom-switch-window-hook) + :preface (defvar winner-dont-bind-my-keys t) + :config (winner-mode +1)) ; I'll bind keys myself (def-package! paren ;; highlight matching delimiters - :after-call (after-find-file doom-exit-buffer-hook) + :after-call (after-find-file doom-switch-buffer-hook) :init (defun doom|disable-show-paren-mode () "Turn off `show-paren-mode' buffer-locally." @@ -259,7 +314,56 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") ;; -;; Line numbers +;;; Third party packages + +;;;###package avy +(setq avy-all-windows nil + avy-background t) + +(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) + :init + (defun doom*disable-all-the-icons-in-tty (orig-fn &rest args) + (if (display-graphic-p) + (apply orig-fn args) + "")) + :config + ;; all-the-icons doesn't work in the terminal, so we "disable" it. + (dolist (fn '(all-the-icons-octicon all-the-icons-material + all-the-icons-faicon all-the-icons-fileicon + all-the-icons-wicon all-the-icons-alltheicon)) + (advice-add fn :around #'doom*disable-all-the-icons-in-tty))) + +;;;###package hide-mode-line-mode +(add-hook 'completion-list-mode-hook #'hide-mode-line-mode) +(add-hook 'Man-mode-hook #'hide-mode-line-mode) + +;; Better fontification of number literals in code +(def-package! highlight-numbers + :hook ((prog-mode conf-mode) . highlight-numbers-mode) + :config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>")) + +;;;###package highlight-escape-sequences +(def-package! highlight-escape-sequences + :hook ((prog-mode conf-mode) . highlight-escape-sequences-mode)) + +;;;###package rainbow-delimiters +;; Helps us distinguish stacked delimiter pairs, especially in parentheses-drunk +;; languages like Lisp. +(setq rainbow-delimiters-max-face-count 3) + +;;;###package 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 EMACS26+ 6 0) fill-column)) + + +;; +;;; Line numbers ;; line numbers in most modes (add-hook! (prog-mode text-mode conf-mode) #'display-line-numbers-mode) @@ -267,8 +371,7 @@ behavior). Do not set this directly, this is let-bound in `doom|init-theme'.") (defun doom|enable-line-numbers () (display-line-numbers-mode +1)) (defun doom|disable-line-numbers () (display-line-numbers-mode -1)) -;; Emacs 26+ has native line number support, and will ignore nlinum. This is for -;; Emacs 25 users: +;; `nlinum' is used for Emacs 25 users, as Emacs 26+ has native line numbers. (def-package! nlinum ;; Line number column. A faster (or equivalent, in the worst case) line number ;; plugin than `linum-mode'. @@ -332,18 +435,18 @@ character that looks like a space that `whitespace-mode' won't affect.") (advice-add #'web-mode-fold-or-unfold :after #'nlinum-hl-do-generic-flush) ;; Changing fonts can leave nlinum line numbers in their original size; this ;; forces them to resize. - (advice-add #'set-frame-font :after #'nlinum-hl-flush-all-windows)) + (add-hook 'after-setting-font-hook #'nlinum-hl-flush-all-windows)) (def-package! nlinum-relative :unless EMACS26+ :defer t :config (setq nlinum-format " %d ") - (add-hook 'evil-mode #'nlinum-relative-setup-evil)) + (add-hook 'evil-mode-hook #'nlinum-relative-setup-evil)) ;; -;; Theme & font +;;; Theme & font (defvar doom-last-window-system (if (daemonp) 'daemon initial-window-system) @@ -351,12 +454,13 @@ character that looks like a space that `whitespace-mode' won't affect.") frame's window-system, the theme will be reloaded.") (defun doom|init-fonts () - "Initialize fonts." + "Loads fonts. + +Fonts are specified by `doom-font', `doom-variable-pitch-font', +`doom-serif-font' and `doom-unicode-font'." (condition-case e (progn (cond (doom-font - ;; We avoid `set-frame-font' for performance reasons. - ;; Manipulating `default-frame-alist' is effective enough. (add-to-list 'default-frame-alist (cons 'font @@ -380,20 +484,27 @@ frame's window-system, the theme will be reloaded.") (signal 'doom-error e))))) (defun doom|init-theme () - "Set the theme and load the font, in that order." + "Load the theme specified by `doom-theme'." (when (and doom-theme (not (memq doom-theme custom-enabled-themes))) (let ((doom--prefer-theme-elc t)) (load-theme doom-theme t)))) -;; Getting themes to remain consistent across GUI Emacs, terminal Emacs and -;; daemon Emacs is hairy. `doom|init-theme' sorts out the initial GUI frame. -;; Attaching `doom|init-theme-in-frame' to `after-make-frame-functions' sorts -;; out daemon and emacsclient frames. -;; -;; There will still be issues with simultaneous gui and terminal (emacsclient) -;; frames, however. There's always `doom/reload-theme' if you need it! +(defun doom|reload-theme-maybe (_frame) + "Reloads the theme if the display device has changed." + (unless (cl-find doom-last-window-system (frame-list) :key #'framep-on-display) + (setq doom-last-window-system nil) + (doom|reload-theme-in-frame-maybe (selected-frame)))) + (defun doom|reload-theme-in-frame-maybe (frame) - "Reloads the theme in new daemon or tty frames." + "Reloads the theme if the display device has changed. + +Getting themes to remain consistent across GUI Emacs, terminal Emacs and daemon +Emacs is hairy. `doom|init-theme' sorts out the initial GUI frame. Attaching +`doom|reload-theme-in-frame-maybe' to `after-make-frame-functions' sorts out +daemon and emacsclient frames. + +There will still be issues with simultaneous gui and terminal (emacsclient) +frames, however. There's always `doom/reload-theme' if you need it!" (when (and doom-theme (framep frame) (not (eq doom-last-window-system (framep-on-display frame)))) @@ -401,109 +512,47 @@ frame's window-system, the theme will be reloaded.") (load-theme doom-theme t)) (setq doom-last-window-system (framep-on-display frame)))) -(defun doom|reload-theme-maybe (_frame) - "Reloads the theme after closing the last frame of a type." - (unless (cl-find doom-last-window-system (frame-list) :key #'framep-on-display) - (setq doom-last-window-system nil) - (doom|reload-theme-in-frame (selected-frame)))) - -;; fonts -(add-hook 'doom-init-ui-hook #'doom|init-fonts) -;; themes -(unless (daemonp) - (add-hook 'doom-init-ui-hook #'doom|init-theme)) -(add-hook 'after-make-frame-functions #'doom|reload-theme-in-frame-maybe) -(add-hook 'after-delete-frame-functions #'doom|reload-theme-maybe) - ;; -;; Bootstrap - -;; simple name in frame title -(setq frame-title-format '("%b – Doom Emacs")) - -;; relegate tooltips to echo area only -(if (boundp 'tooltip-mode) (tooltip-mode -1)) - -;; enabled by default; no thanks, too distracting -(blink-cursor-mode -1) - -;; Handle ansi codes in compilation buffer -(add-hook 'compilation-filter-hook #'doom|apply-ansi-color-to-compilation-buffer) - -;; 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) - -;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. -(add-to-list 'default-frame-alist '(buffer-predicate . doom-buffer-frame-predicate)) -;; Prevent the glimpse of un-styled Emacs by setting these early. -(add-to-list 'default-frame-alist '(tool-bar-lines . 0)) -(add-to-list 'default-frame-alist '(menu-bar-lines . 0)) -(add-to-list 'default-frame-alist '(vertical-scroll-bars)) -;; prompts the user for confirmation when deleting a non-empty frame -(global-set-key [remap delete-frame] #'doom/delete-frame) - -(defun doom|protect-visible-buffer () - "Don't kill the current buffer if it is visible in another window (bury it -instead). Meant for `kill-buffer-query-functions'." - (not (and (delq (selected-window) (get-buffer-window-list nil nil t)) - (not (member (substring (buffer-name) 0 1) '(" " "*")))))) - -(defun doom|protect-fallback-buffer () - "Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'." - (not (eq (current-buffer) (doom-fallback-buffer)))) - -(defun doom|highlight-non-default-indentation () - "Highlight whitespace that doesn't match your `indent-tabs-mode' setting. - -e.g. If you indent with spaces by default, tabs will be highlighted. If you -indent with tabs, spaces at BOL are highlighted. - -Does nothing if `whitespace-mode' is already active or the current buffer is -read-only or not file-visiting." - (unless (or (bound-and-true-p global-whitespace-mode) - (bound-and-true-p whitespace-mode) - (eq major-mode 'fundamental-mode) - buffer-read-only - (null buffer-file-name)) - (require 'whitespace) - (set (make-local-variable 'whitespace-style) - (if (bound-and-true-p whitespace-newline-mode) - (cl-union (if indent-tabs-mode '(indentation) '(tabs tab-mark)) - whitespace-style) - `(face ,@(if indent-tabs-mode '(indentation) '(tabs tab-mark)) - trailing-lines tail))) - (whitespace-mode +1))) +;;; Bootstrap (defun doom|init-ui () "Initialize Doom's user interface by applying all its advice and hooks." - (add-to-list 'kill-buffer-query-functions #'doom|protect-fallback-buffer nil #'eq) - (add-to-list 'kill-buffer-query-functions #'doom|protect-visible-buffer nil #'eq) - (add-hook 'after-change-major-mode-hook #'doom|highlight-non-default-indentation) - (run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook)) + (run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook) -(add-hook 'emacs-startup-hook #'doom|init-ui) + (add-to-list 'kill-buffer-query-functions #'doom|protect-fallback-buffer nil 'eq) + (add-hook 'after-change-major-mode-hook #'doom|highlight-non-default-indentation) + + ;; Reload theme if the display device has changed + (add-hook 'after-make-frame-functions #'doom|reload-theme-in-frame-maybe) + (add-hook 'after-delete-frame-functions #'doom|reload-theme-maybe) + + ;; Initialize custom switch-{buffer,window,frame} hooks: + ;; + `doom-switch-buffer-hook' + ;; + `doom-switch-window-hook' + ;; + `doom-switch-frame-hook' + (add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-add! '(switch-to-buffer display-buffer) :around #'doom*run-switch-buffer-hooks)) + +;; Apply `doom-theme' +(unless (daemonp) + (add-hook 'doom-init-ui-hook #'doom|init-theme)) +;; Apply `doom-font' et co +(add-hook 'doom-after-init-modules-hook #'doom|init-fonts) +;; Setup `doom-load-theme-hook' +(advice-add #'load-theme :after #'doom*run-load-theme-hooks) + +(add-hook 'window-setup-hook #'doom|init-ui) ;; -;; Fixes/hacks +;;; Fixes/hacks ;; doesn't exist in terminal Emacs; we define it to prevent errors (unless (fboundp 'define-fringe-bitmap) (defun define-fringe-bitmap (&rest _))) -(defun doom*disable-old-themes-first (orig-fn &rest args) - (mapc #'disable-theme custom-enabled-themes) - (apply orig-fn args) - (when (fboundp 'powerline-reset) - (powerline-reset))) -(advice-add #'load-theme :around #'doom*disable-old-themes-first) - (defun doom*prefer-compiled-theme (orig-fn &rest args) "Make `load-theme' prioritize the byte-compiled theme for a moderate boost in startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." @@ -517,27 +566,37 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." (apply orig-fn args))) (advice-add #'load-theme :around #'doom*prefer-compiled-theme) -(defun doom|disable-whitespace-mode-in-childframes (frame) - "`whitespace-mode' inundates child frames with whitspace markers, so disable +(after! whitespace + (defun doom*disable-whitespace-mode-in-childframes (orig-fn) + "`whitespace-mode' inundates child frames with whitspace markers, so disable it to fix all that visual noise." - (when (frame-parameter frame 'parent-frame) - (with-selected-frame frame - (setq-local whitespace-style nil) - frame))) -(add-hook 'after-make-frame-functions #'doom|disable-whitespace-mode-in-childframes) + (unless (frame-parameter nil 'parent-frame) + (funcall orig-fn))) + (add-function :around whitespace-enable-predicate #'doom*disable-whitespace-mode-in-childframes) -(defun doom*silence-motion-errors (orig-fn &rest args) - "Prevent disruptive motion errors taking over the minibuffer while we're in -it." - (if (not (minibufferp)) - (apply orig-fn args) - (ignore-errors (apply orig-fn args)) - (when (<= (point) (minibuffer-prompt-end)) - (goto-char (minibuffer-prompt-end))))) -(advice-add #'left-char :around #'doom*silence-motion-errors) -(advice-add #'right-char :around #'doom*silence-motion-errors) -(advice-add #'delete-backward-char :around #'doom*silence-motion-errors) -(advice-add #'backward-kill-sentence :around #'doom*silence-motion-errors) + (defun doom|disable-whitespace-mode-in-childframes (frame) + "`whitespace-mode' inundates child frames with whitspace markers, so disable +it to fix all that visual noise." + (when (frame-parameter frame 'parent-frame) + (with-selected-frame frame + (setq-local whitespace-style nil) + frame))) + (add-hook 'after-make-frame-functions #'doom|disable-whitespace-mode-in-childframes)) + +;; Don't allow cursor to enter the prompt +(setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt)) +(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) + +;; Don't display messages in the minibuffer when using the minibuffer +(defmacro doom-silence-motion-key (command key) + (let ((key-command (intern (format "doom/silent-%s" command)))) + `(progn + (defun ,key-command () + (interactive) + (ignore-errors (call-interactively ',command))) + (define-key minibuffer-local-map (kbd ,key) #',key-command)))) +(doom-silence-motion-key backward-delete-char "") +(doom-silence-motion-key delete-char "") ;; Switch to `doom-fallback-buffer' if on last real buffer (advice-add #'kill-this-buffer :around #'doom*switch-to-fallback-buffer-maybe) diff --git a/core/core.el b/core/core.el index 81a39d93b..1ab53ce60 100644 --- a/core/core.el +++ b/core/core.el @@ -113,94 +113,9 @@ Doom was setup, which can cause problems.") ;; ;; Custom hooks -(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 near the end of -`emacs-startup-hook', as late as possible. Guaranteed to run after everything -else (except for `window-setup-hook').") - (defvar doom-reload-hook nil "A list of hooks to run when `doom/reload' is called.") -(defvar doom-load-theme-hook nil - "Hook run after the theme is loaded with `load-theme' or reloaded with -`doom/reload-theme'.") - -(defvar doom-exit-window-hook nil - "Hook run before `switch-window' or `switch-frame' are called. - -Also see `doom-enter-window-hook'.") - -(defvar doom-enter-window-hook nil - "Hook run after `switch-window' or `switch-frame' are called. - -Also see `doom-exit-window-hook'.") - -(defvar doom-exit-buffer-hook nil - "Hook run after `switch-to-buffer', `pop-to-buffer' or `display-buffer' are -called. The buffer to be switched to is current when these hooks run. - -Also see `doom-enter-buffer-hook'.") - -(defvar doom-enter-buffer-hook nil - "Hook run before `switch-to-buffer', `pop-to-buffer' or `display-buffer' are -called. The buffer to be switched to is current when these hooks run. - -Also see `doom-exit-buffer-hook'.") - -(defvar doom-inhibit-switch-buffer-hooks nil - "Letvar for inhibiting `doom-enter-buffer-hook' and `doom-exit-buffer-hook'. -Do not set this directly.") -(defvar doom-inhibit-switch-window-hooks nil - "Letvar for inhibiting `doom-enter-window-hook' and `doom-exit-window-hook'. -Do not set this directly.") - -(defun doom*switch-window-hooks (orig-fn window &optional norecord) - (if (or doom-inhibit-switch-window-hooks - (null window) - (eq window (selected-window)) - (window-minibuffer-p) - (window-minibuffer-p window)) - (funcall orig-fn window norecord) - (let ((doom-inhibit-switch-window-hooks t)) - (run-hooks 'doom-exit-window-hook) - (prog1 (funcall orig-fn window norecord) - (with-selected-window window - (run-hooks 'doom-enter-window-hook)))))) - -(defun doom*switch-buffer-hooks (orig-fn buffer-or-name &rest args) - (if (or doom-inhibit-switch-buffer-hooks - (eq (get-buffer buffer-or-name) (current-buffer))) - (apply orig-fn buffer-or-name args) - (let ((doom-inhibit-switch-buffer-hooks t)) - (run-hooks 'doom-exit-buffer-hook) - (prog1 (apply orig-fn buffer-or-name args) - (when (buffer-live-p (get-buffer buffer-or-name)) - (with-current-buffer buffer-or-name - (run-hooks 'doom-enter-buffer-hook))))))) - -(defun doom|init-switch-hooks (&optional disable) - "Set up enter/exit hooks for windows and buffers. - -See `doom-enter-buffer-hook', `doom-enter-window-hook', `doom-exit-buffer-hook' -and `doom-exit-window-hook'." - (dolist (spec '((select-window . doom*switch-window-hooks) - (switch-to-buffer . doom*switch-buffer-hooks) - (display-buffer . doom*switch-buffer-hooks) - (pop-to-buffer . doom*switch-buffer-hooks))) - (if disable - (advice-remove (car spec) (cdr spec)) - (advice-add (car spec) :around (cdr spec))))) - -(defun doom*load-theme-hooks (theme &rest _) - "Set up `doom-load-theme-hook' to run after `load-theme' is called." - (setq doom-theme theme) - (run-hooks 'doom-load-theme-hook)) -(advice-add #'load-theme :after #'doom*load-theme-hooks) - ;; ;; Emacs core configuration @@ -208,12 +123,10 @@ and `doom-exit-window-hook'." ;; UTF-8 as the default coding system (when (fboundp 'set-charset-priority) (set-charset-priority 'unicode)) ; pretty -(prefer-coding-system 'utf-8) ; pretty -(set-terminal-coding-system 'utf-8) ; pretty -(set-keyboard-coding-system 'utf-8) ; pretty -(set-selection-coding-system 'utf-8) ; perdy -(setq locale-coding-system 'utf-8) ; please -(setq-default buffer-file-coding-system 'utf-8) ; with sugar on top +(prefer-coding-system 'utf-8) ; pretty +(setq selection-coding-system 'utf-8) ; pretty +(setq locale-coding-system 'utf-8) ; please +(if IS-WINDOWS (set-w32-system-coding-system 'utf-8)) ; with sugar on top (setq-default ad-redefinition-action 'accept ; silence advised function warnings @@ -230,8 +143,6 @@ and `doom-exit-window-hook'." 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) auto-save-default nil create-lockfiles nil @@ -255,6 +166,9 @@ and `doom-exit-window-hook'." async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") auto-save-list-file-name (concat doom-cache-dir "autosave") backup-directory-alist (list (cons "." (concat doom-cache-dir "backup/"))) + desktop-dirname (concat doom-etc-dir "desktop") + desktop-base-file-name "autosave" + desktop-base-lock-name "autosave-lock" pcache-directory (concat doom-cache-dir "pcache/") request-storage-directory (concat doom-cache-dir "request") server-auth-dir (concat doom-cache-dir "server/") @@ -315,6 +229,13 @@ original value of `symbol-file'." #'doom-try-run-hook)) (add-hook 'hack-local-variables-hook #'doom|run-local-var-hooks) +(defun doom|run-local-var-hooks-if-necessary () + "If `enable-local-variables' is disabled, then `hack-local-variables-hook' is +never triggered." + (unless enable-local-variables + (doom|run-local-var-hooks))) +(add-hook 'after-change-major-mode-hook #'doom|run-local-var-hooks-if-necessary) + ;; ;; Incremental lazy-loading @@ -357,17 +278,17 @@ intervals." (let* ((reqs (cl-delete-if #'featurep packages)) (req (ignore-errors (pop reqs)))) (when req - (when doom-debug-mode - (message "Incrementally loading %s" req)) + (doom-log "Incrementally loading %s" req) (condition-case e (require req nil t) - (error + ((error debug) (message "Failed to load '%s' package incrementally, because: %s" req e))) - (when reqs - (run-with-idle-timer doom-incremental-idle-timer - nil #'doom-load-packages-incrementally - reqs t)))))))) + (if reqs + (run-with-idle-timer doom-incremental-idle-timer + nil #'doom-load-packages-incrementally + reqs t) + (doom-log "Finished incremental loading")))))))) (defun doom|load-packages-incrementally () "Begin incrementally loading packages in `doom-incremental-packages'. @@ -380,7 +301,7 @@ If this is a daemon session, load them all immediately instead." nil #'doom-load-packages-incrementally (cdr doom-incremental-packages) t)))) -(add-hook 'emacs-startup-hook #'doom|load-packages-incrementally) +(add-hook 'window-setup-hook #'doom|load-packages-incrementally) ;; @@ -391,8 +312,7 @@ If this is a daemon session, load them all immediately instead." issues easier. Meant to be used with `run-hook-wrapped'." - (when doom-debug-mode - (message "Running doom hook: %s" hook)) + (doom-log "Running doom hook: %s" hook) (condition-case e (funcall hook) ((debug error) @@ -457,11 +377,18 @@ If RETURN-P, return the message as a string instead of displaying it." ;; ;; Bootstrap functions -(defun doom-initialize (&optional force-p force-load-core-p) - "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). +(defun doom-initialize-autoloads (file) + "Tries to load FILE (an autoloads file). Return t on success, throws an error +in interactive sessions, nil otherwise (but logs a warning)." + (condition-case e + (load (file-name-sans-extension file) 'noerror 'nomessage) + ((debug error) + (if noninteractive + (message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) + (signal 'doom-autoload-error (list (file-name-nondirectory file) e)))))) -Loads Doom core files if in an interactive session or FORCE-LOAD-CORE-P is -non-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 @@ -477,12 +404,14 @@ The overall load order of Doom is as follows: ~/.emacs.d/core/core.el ~/.doom.d/init.el Module init.el files - `doom-init-hook' + `doom-before-init-modules-hook' Module config.el files ~/.doom.d/config.el - `doom-post-init-hook' + `doom-init-modules-hook' `after-init-hook' `emacs-startup-hook' + `doom-init-ui-hook' + `window-setup-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 @@ -508,47 +437,45 @@ to least)." ;; autoloads file and caches `load-path', `auto-mode-alist', ;; `Info-directory-list', `doom-disabled-packages' and ;; `package-activated-list'. A big reduction in startup time. - (unless (or force-p - (doom-initialize-autoloads doom-package-autoload-file) - noninteractive) - (user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them"))) + (let (command-switch-alist) + (unless (or force-p + (doom-initialize-autoloads doom-package-autoload-file) + noninteractive) + (user-error "Your package autoloads are missing! Run `bin/doom refresh' to regenerate them")))) + (require 'core-lib) + (require 'core-modules) (require 'core-os) - (when (or force-load-core-p (not noninteractive)) - (add-hook! 'emacs-startup-hook - #'(doom|init-switch-hooks doom|display-benchmark)) - + (if noninteractive + (require 'core-cli) + (add-hook 'window-setup-hook #'doom|display-benchmark) + (require 'core-keybinds) (require 'core-ui) - (require 'core-editor) (require 'core-projects) - (require 'core-keybinds))) - -(defun doom-initialize-autoloads (file) - "Tries to load FILE (an autoloads file). Return t on success, throws an error -in interactive sessions, nil otherwise (but logs a warning)." - (condition-case e - (load (file-name-sans-extension file) 'noerror 'nomessage) - ((debug error) - (if noninteractive - (message "Autoload file warning: %s -> %s" (car e) (error-message-string e)) - (signal 'doom-autoload-error (list (file-name-nondirectory file) e)))))) + (require 'core-editor))) ;; ;; Bootstrap Doom -(add-to-list 'load-path doom-core-dir) +(eval-and-compile + (require 'subr-x) + (require 'cl-lib) + (unless EMACS26+ + (with-no-warnings + ;; if-let and when-let were moved to (if|when)-let* in Emacs 26+ so we + ;; alias them for 25 users. + (defalias 'if-let* #'if-let) + (defalias 'when-let* #'when-let)))) -(require 'core-lib) -(require 'core-modules) -(when noninteractive - (require 'core-cli)) -(after! package - (require 'core-packages)) +(add-to-list 'load-path doom-core-dir) (doom-initialize noninteractive) (unless noninteractive (doom-initialize-modules)) +(after! package + (require 'core-packages) + (doom-initialize-packages)) (provide 'core) ;;; core.el ends here diff --git a/core/doctor.el b/core/doctor.el new file mode 100644 index 000000000..8487cf324 --- /dev/null +++ b/core/doctor.el @@ -0,0 +1,29 @@ +;;; core/doctor.el -*- lexical-binding: t; -*- + +(defun file-size (file &optional dir) + (setq file (expand-file-name file dir)) + (when (file-exists-p file) + (/ (nth 7 (file-attributes file)) + 1024.0))) + +;; Check for oversized problem files in cache that may cause unusual/tremendous +;; delays or freezing. This shouldn't happen often. +(dolist (file (list "savehist" + "projectile.cache")) + (let* ((path (expand-file-name file doom-core-dir)) + (size (file-size path))) + (when (and (numberp size) (> size 2000)) + (warn! "%s is too large (%.02fmb). This may cause freezes or odd startup delays" + (file-relative-name path doom-core-dir) + (/ size 1024)) + (explain! "Consider deleting it from your system (manually)")))) + +(when! (not (executable-find "fd")) + (warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")) + +(let ((default-directory "~")) + (require 'projectile) + (when! (cl-find-if #'projectile-file-exists-p projectile-project-root-files-bottom-up) + (warn! "Your $HOME is recognized as a project root") + (explain! "Doom will disable bottom-up root search, which may reduce the accuracy of project\n" + "detection."))) diff --git a/core/packages.el b/core/packages.el index 0e16d3e3f..e13d240d2 100644 --- a/core/packages.el +++ b/core/packages.el @@ -2,6 +2,7 @@ ;;; core/packages.el ;; core-os.el +(package! xclip) (when IS-MAC (package! exec-path-from-shell) (package! osx-clipboard) @@ -10,7 +11,6 @@ ;; core-ui.el (package! all-the-icons) (package! hide-mode-line) -(package! highlight-indentation) (package! highlight-numbers) (package! highlight-escape-sequences :recipe (:fetcher github :repo "hlissner/highlight-escape-sequences")) @@ -28,7 +28,6 @@ (package! avy) (package! command-log-mode) (package! dtrt-indent) -(package! expand-region) (package! helpful) (package! pcre2el) (package! smartparens) diff --git a/core/templates/VANILLA_SANDBOX b/core/templates/VANILLA_SANDBOX index 520d8c49c..f9fe77dcc 100644 --- a/core/templates/VANILLA_SANDBOX +++ b/core/templates/VANILLA_SANDBOX @@ -1,6 +1,12 @@ ;; Welcome to the vanilla sandbox! ;; -;; This is a test bed for running Emacs Lisp in either a vanilla Emacs session -;; free of Doom's clutches (C-c C-c), or in a vanilla Doom session free of your -;; private config (C-c C-d). - +;; This is a test bed for running Emacs Lisp in an instance of Emacs with varying +;; amounts of Doom loaded: +;; +;; a) vanilla Emacs (nothing loaded) \\[doom--run-vanilla-emacs] +;; b) vanilla Doom (only Doom core) \\[doom--run-vanilla-doom] +;; c) Doom + modules - your private config \\[doom--run-vanilla-doom+] +;; d) Doom (normal) \\[doom--run-full-doom] +;; +;; This is done without sacrificing access to installed packages. Use the sandbox +;; to reproduce bugs and determine if Doom is to blame. diff --git a/core/test/test-autoload-help.el b/core/test/test-autoload-help.el index 1f80a8e73..8df3a9a3a 100644 --- a/core/test/test-autoload-help.el +++ b/core/test/test-autoload-help.el @@ -1,31 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-autoload-help.el -(load! "autoload/help" doom-core-dir) +;; (load! "autoload/help" doom-core-dir) ;; -(describe "core/autoload/help" - :var (a) - (before-each (setq a (switch-to-buffer (get-buffer-create "a")))) - (after-each (kill-buffer a)) - - (describe "what-face" - (before-each - (insert (propertize "Hello " 'face 'font-lock-keyword-face)) - (insert "world")) - (it "returns list of faces at point" - (expect (doom/what-face nil (point-min)) :to-equal '((font-lock-keyword-face) ()))) - (it "returns nil if no faces at point" - (expect (doom/what-face nil (point-max)) :to-be nil))) - - (describe "what-face overlays" - (before-each - (insert "Hello world") - (let ((ov (make-overlay 1 6))) - (overlay-put ov 'face 'font-lock-keyword-face))) - - (it "returns list of overlays at point" - (expect (doom/what-face nil (point-min)) :to-equal '(() (font-lock-keyword-face)))) - (it "returns nil if no overlays at point" - (expect (doom/what-face nil (point-max)) :to-be nil)))) - +;; (describe "core/autoload/help" +;; :var (a) +;; (before-each (setq a (switch-to-buffer (get-buffer-create "a")))) +;; (after-each (kill-buffer a))) diff --git a/core/test/test-autoload-package.el b/core/test/test-autoload-package.el index 32b4bd4d5..ac5c81534 100644 --- a/core/test/test-autoload-package.el +++ b/core/test/test-autoload-package.el @@ -55,8 +55,8 @@ (doom-missing-dummy) (doom-noquelpa-dummy) (doom-disabled-dummy :disable t) - (doom-private-dummy :private t) - (doom-disabled-private-dummy :private t :disable t) + (doom-private-dummy :modules ((:private))) + (doom-disabled-private-dummy :modules ((:private)) :disable t) (doom-quelpa-dummy :recipe (doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist"))) quelpa-cache '((doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist") @@ -95,31 +95,31 @@ (spy-on #'doom-package-installed-p :and-call-fake #'package-installed-p)) (it "returns all packages" - (expect (mapcar #'car (doom-get-packages)) + (expect (mapcar #'car (doom-find-packages)) :to-have-same-items-as (mapcar #'car doom-packages))) (it "returns only disabled packages" - (expect (mapcar #'car (doom-get-packages :disabled t)) + (expect (mapcar #'car (doom-find-packages :disabled t)) :to-have-same-items-as '(doom-disabled-dummy doom-disabled-private-dummy))) (it "returns only non-disabled packages" - (expect (mapcar #'car (doom-get-packages :disabled nil)) + (expect (mapcar #'car (doom-find-packages :disabled nil)) :to-have-same-items-as '(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-missing-dummy doom-noquelpa-dummy doom-private-dummy))) (it "returns only installed packages" - (expect (mapcar #'car (doom-get-packages :disabled nil :installed t)) + (expect (mapcar #'car (doom-find-packages :disabled nil :installed t)) :to-have-same-items-as '(doom-dummy doom-uptodate-dummy doom-quelpa-dummy doom-noquelpa-dummy))) (it "returns only non-installed packages" - (expect (mapcar #'car (doom-get-packages :disabled nil :installed nil)) + (expect (mapcar #'car (doom-find-packages :disabled nil :installed nil)) :to-have-same-items-as '(doom-missing-dummy doom-private-dummy))) (it "returns only private packages" - (expect (mapcar #'car (doom-get-packages :private t)) + (expect (mapcar #'car (doom-find-packages :private t)) :to-have-same-items-as '(doom-private-dummy doom-disabled-private-dummy))) (it "returns only disabled and private packages" - (expect (mapcar #'car (doom-get-packages :disabled t :private t)) + (expect (mapcar #'car (doom-find-packages :disabled t :private t)) :to-have-same-items-as '(doom-disabled-private-dummy)))) diff --git a/core/test/test-core-keybinds.el b/core/test/test-core-keybinds.el index 294cc1ea7..046f33ead 100644 --- a/core/test/test-core-keybinds.el +++ b/core/test/test-core-keybinds.el @@ -1,6 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-keybinds.el +(require 'core-keybinds) + (buttercup-define-matcher :to-expand-into (src result) (let ((src (funcall src)) (result (funcall result))) diff --git a/core/test/test-core-lib.el b/core/test/test-core-lib.el index 761cd49a2..608fdb66e 100644 --- a/core/test/test-core-lib.el +++ b/core/test/test-core-lib.el @@ -1,6 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-lib.el +(require 'core-lib) + (describe "core/lib" ;; --- Helpers ---------------------------- (describe "doom-unquote" diff --git a/core/test/test-core-modules.el b/core/test/test-core-modules.el index bd9aea708..a786ad2ea 100644 --- a/core/test/test-core-modules.el +++ b/core/test/test-core-modules.el @@ -1,4 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/test-core-modules.el +;; (require 'core-modules) + (describe "core-modules") diff --git a/core/test/test-core-ui.el b/core/test/test-core-ui.el index d3d3a1ecf..0ace0bc73 100644 --- a/core/test/test-core-ui.el +++ b/core/test/test-core-ui.el @@ -1,33 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; ../core/test/test-core-ui.el +(require 'core-ui) + (describe "core/ui" - (describe "doom|protect-visible-buffer" - :var (kill-buffer-query-functions wconf a b) - (before-each - (setq a (switch-to-buffer (get-buffer-create "a")) - b (get-buffer-create "b") - kill-buffer-query-functions '(doom|protect-visible-buffer) - wconf (current-window-configuration)) - (delete-other-windows)) - - (after-each - (let (kill-buffer-query-functions kill-buffer-hook) - (kill-buffer a) - (kill-buffer b)) - (set-window-configuration wconf)) - - (it "shouldn't kill buffers that are visible in more than one window" - (with-temp-buffer-window - (switch-to-buffer a) (split-window) - (switch-to-buffer b) (split-window) - (switch-to-buffer a) - (expect (kill-buffer) :to-be nil) - - (select-window (get-buffer-window b)) - (expect (kill-buffer))))) - - (describe "doom|protect-fallback-buffer" :var (kill-buffer-query-functions a b) (before-all diff --git a/core/test/test-core.el b/core/test/test-core.el index 068d9ff7f..1a3fc80fc 100644 --- a/core/test/test-core.el +++ b/core/test/test-core.el @@ -61,43 +61,39 @@ (before-each (setq a (switch-to-buffer (get-buffer-create "a")) b (get-buffer-create "b")) - (spy-on 'before-hook) - (spy-on 'after-hook) - (doom|init-switch-hooks)) + (spy-on 'hook) + (add-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (add-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-add! '(switch-to-buffer display-buffer) :around #'doom*run-switch-buffer-hooks)) (after-each - (doom|init-switch-hooks 'disable) + (remove-hook 'buffer-list-update-hook #'doom|run-switch-window-hooks) + (remove-hook 'focus-in-hook #'doom|run-switch-frame-hooks) + (advice-remove! '(switch-to-buffer display-buffer) #'doom*run-switch-buffer-hooks) (kill-buffer a) (kill-buffer b)) (describe "switch-buffer" - :var (doom-exit-buffer-hook - doom-enter-buffer-hook) + :var (doom-switch-buffer-hook) (before-each - (setq doom-exit-buffer-hook '(before-hook) - doom-enter-buffer-hook '(after-hook))) + (setq doom-switch-buffer-hook '(hook))) (after-each - (setq doom-exit-buffer-hook nil - doom-enter-buffer-hook nil)) + (setq doom-switch-buffer-hook nil)) (it "should trigger when switching buffers" (switch-to-buffer b) (switch-to-buffer a) (switch-to-buffer b) - (expect 'before-hook :to-have-been-called-times 3) - (expect 'after-hook :to-have-been-called-times 3)) + (expect 'hook :to-have-been-called-times 3)) (it "should trigger only once on the same buffer" (switch-to-buffer b) (switch-to-buffer b) (switch-to-buffer a) - (expect 'before-hook :to-have-been-called-times 2) - (expect 'after-hook :to-have-been-called-times 2))) + (expect 'hook :to-have-been-called-times 2))) (describe "switch-window" - :var (doom-exit-window-hook - doom-enter-window-hook - x y) + :var (doom-switch-window-hook x y) (before-each (delete-other-windows) (setq x (get-buffer-window a) @@ -105,21 +101,17 @@ (with-selected-window y (switch-to-buffer b)) (select-window x) - (spy-calls-reset 'before-hook) - (spy-calls-reset 'after-hook) - (setq doom-exit-window-hook '(before-hook) - doom-enter-window-hook '(after-hook))) + (spy-calls-reset 'hook) + (setq doom-switch-window-hook '(hook))) (it "should trigger when switching windows" (select-window y) (select-window x) (select-window y) - (expect 'before-hook :to-have-been-called-times 3) - (expect 'after-hook :to-have-been-called-times 3)) + (expect 'hook :to-have-been-called-times 3)) (it "should trigger only once on the same window" (select-window y) (select-window y) (select-window x) - (expect 'before-hook :to-have-been-called-times 2) - (expect 'after-hook :to-have-been-called-times 2)))))) + (expect 'hook :to-have-been-called-times 2)))))) diff --git a/early-init.el b/early-init.el index d2eadd3c2..8365ba3a8 100644 --- a/early-init.el +++ b/early-init.el @@ -15,3 +15,6 @@ (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) (add-to-list 'default-frame-alist '(menu-bar-lines . 0)) (add-to-list 'default-frame-alist '(vertical-scroll-bars)) + +;; One less file to load at startup +(setq site-run-file nil) diff --git a/init.example.el b/init.example.el index 08180c997..beeddd51d 100644 --- a/init.example.el +++ b/init.example.el @@ -13,20 +13,20 @@ :completion company ; the ultimate code completion backend - helm ; the *other* search engine for love and life + ;;helm ; the *other* search engine for love and life ;;ido ; the other *other* search engine... - ;;ivy ; a search engine for love and life + ivy ; a search engine for love and life :ui ;;deft ; notational velocity for Emacs 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 doom-quit ; DOOM quit-message prompts when you quit Emacs evil-goggles ; display visual hints when editing in evil ;;fci ; a `fill-column' indicator hl-todo ; highlight TODO/FIXME/NOTE tags - ;;modeline ; snazzy, Atom-inspired modeline, plus API + ;;indent-guides ; highlighted indent columns + modeline ; snazzy, Atom-inspired modeline, plus API nav-flash ; blink the current line after jumping ;;neotree ; a project drawer, like NERDTree for vim treemacs ; a project drawer, like neotree but cooler @@ -69,7 +69,7 @@ ;;gist ; interacting with github gists ;;lsp ;;macos ; MacOS-specific commands - ;;magit ; a git porcelain for Emacs + magit ; a git porcelain for Emacs ;;make ; run make tasks from Emacs ;;password-store ; password manager for nerds ;;pdf ; pdf enhancements diff --git a/modules/README.org b/modules/README.org new file mode 100644 index 000000000..a660b4c6a --- /dev/null +++ b/modules/README.org @@ -0,0 +1,171 @@ +#+TITLE: Doom Modules + +* Table of Contents :TOC:noexport: +- [[#feature][:feature]] +- [[#completion][:completion]] +- [[#ui][:ui]] +- [[#editor][:editor]] +- [[#emacs][:emacs]] +- [[#tools][:tools]] +- [[#lang][:lang]] +- [[#app][:app]] +- [[#collab][:collab]] +- [[#config][:config]] + +* :feature +Broad modules that bring essential IDE functionality to Emacs. + ++ debugger: A (nigh-)universal debugger in Emacs ++ [[file:feature/eval/README.org][eval]]: REPL & code evaluation support for a variety of languages ++ [[file:feature/evil/README.org][evil]] =+everywhere=: Vim in Emacs ++ [[file:feature/file-templates/README.org][file-templates]]: Auto-inserted templates in blank new files ++ [[file:feature/lookup/README.org][lookup]] =+docsets=: Universal jump-to & documentation lookup backend ++ [[file:feature/snippets/README.org][snippets]]: A templating system for Emacs for lazy typers (aka programmers) ++ [[file:feature/workspaces/README.org][workspaces]]: Isolated workspaces + +* :completion +Swappable completion modules for quickly narrowing down lists of candidates. + ++ [[file:completion/company/README.org][company]] =+auto +childframe=: The ultimate code completion backend ++ helm =+fuzzy +childframe=: *Another* search engine for love and life ++ ido: The /other/ *other* search engine for love and life ++ [[file:completion/ivy/README.org][ivy]] =+fuzzy +childframe=: /The/ search engine for love and life + +* :ui +Aesthetic modules that affect the Emacs interface or user experience. + ++ [[file:ui/deft/README.org][deft]]: ++ [[file:ui/doom/README.org][doom]]: ++ [[file:ui/doom-dashboard/README.org][doom-dashboard]]: ++ [[file:ui/doom-quit/README.org][doom-quit]]: ++ [[file:ui/evil-goggles/README.org][evil-goggles]]: ++ fci: ++ [[file:ui/hl-todo/README.org][hl-todo]]: ++ [[file:ui/modeline/README.org][modeline]]: ++ [[file:ui/nav-flash/README.org][nav-flash]]: ++ [[file:ui/neotree/README.org][neotree]]: ++ treemacs: ++ [[file:ui/popup/README.org][popup]] =+all +defaults=: Makes temporary/disposable windows less intrusive ++ pretty-code: ++ [[file:ui/tabbar/README.org][tabbar]]: ++ [[file:ui/unicode/README.org][unicode]]: ++ vc-gutter: ++ vi-tilde-fringe: ++ [[file:ui/window-select/README.org][window-select]]: + +* :editor +Modules that affect and augment your ability to write and edit text. + ++ [[file:editor/fold/README.org][fold]]: universal code folding ++ [[file:editor/format/README.org][format]] =+onsave=: ++ [[file:editor/lispy/README.org][lispy]]: ++ multiple-cursors: ++ [[file:editor/parinfer/README.org][parinfer]]: ++ rotate-text: + +* :emacs +Modules that reconfigure packages or features built into Emacs + ++ dired =+ranger +icons=: ++ electric: ++ eshell: ++ imenu: ++ term: ++ vc: + +* :tools +Small modules that give Emacs access to external tools & services. + ++ ansible: ++ docker: ++ [[file:tools/editorconfig/README.org][editorconfig]]: ++ [[file:tools/ein/README.org][ein]]: ++ flyspell: Spell checking ++ flycheck: Live error/warning highlights ++ gist: ++ [[file:tools/lsp/README.org][lsp]]: ++ macos: ++ make: ++ magit: ++ password-store: ++ pdf: ++ prodigy: ++ rgb: ++ terraform: ++ tmux: ++ upload: ++ [[file:tools/wakatime/README.org][wakatime]]: ++ vterm: + +* :lang +Modules that bring support for a language or group of languages to Emacs. + ++ assembly: ++ [[file:lang/cc/README.org][cc]] =+lsp=: ++ clojure: ++ common-lisp: ++ [[file:lang/coq/README.org][coq]]: ++ crystal: ++ [[file:lang/csharp/README.org][csharp]]: ++ data: ++ erlang: ++ elixir: ++ elm: ++ emacs-lisp: ++ [[file:lang/ess/README.org][ess]]: ++ [[file:lang/go/README.org][go]] =+lsp=: ++ [[file:lang/haskell/README.org][haskell]] =+intero +dante=: ++ hy: ++ [[file:lang/idris/README.org][idris]]: ++ java =+meghanada=: ++ [[file:lang/javascript/README.org][javascript]] =+lsp=: ++ julia: ++ [[file:lang/latex/README.org][latex]]: ++ ledger: ++ lua: ++ markdown: ++ [[file:lang/nim/README.org][nim]]: ++ nix: ++ [[file:lang/ocaml/README.org][ocaml]] =+lsp=: ++ [[file:lang/org/README.org][org]] =+attach +babel +capture +export +present +ipython=: ++ [[file:lang/perl/README.org][perl]]: ++ [[file:lang/php/README.org][php]] =+lsp=: ++ plantuml: ++ purescript: ++ python =+lsp=: ++ qt: ++ racket: ++ [[file:lang/rest/README.org][rest]]: ++ ruby =+lsp=: ++ [[file:lang/rust/README.org][rust]] =+lsp=: ++ scala: ++ [[file:lang/sh/README.org][sh]] =+fish +lsp=: ++ [[file:lang/solidity/README.org][solidity]]: ++ swift: ++ web =+lsp=: ++ vala: + +* :app +Large, opinionated modules that transform and take over Emacs, i.e. +Doom-specific porcelains. + ++ calendar: ++ [[file:app/email/README.org][email]] =+gmail=: ++ [[file:app/irc/README.org][irc]]: ++ regex: ++ rss =+org=: ++ twitter: ++ [[file:app/write/README.org][write]] =+wordnut +langtool=: + +* :collab +Modules that enable collaborative programming over the internet. + ++ floobits: ++ impatient-mode: + +* :config +Modules that configure Emacs one way or another, or focus on making it easier +for you to customize it yourself. + ++ literate: ++ [[file:config/default/README.org][default]] =+bindings +smartparens=: diff --git a/modules/app/calendar/config.el b/modules/app/calendar/config.el index c1c9b0a43..197ed9f55 100644 --- a/modules/app/calendar/config.el +++ b/modules/app/calendar/config.el @@ -28,8 +28,7 @@ (define-key cfw:calendar-mode-map "q" #'+calendar/quit) - (when (featurep 'solaire-mode) - (add-hook 'cfw:calendar-mode-hook #'solaire-mode)) + (add-hook 'cfw:calendar-mode-hook #'doom|mark-buffer-as-real) (add-hook 'cfw:calendar-mode-hook 'hide-mode-line-mode) (advice-add #'cfw:render-button :override #'+calendar*cfw:render-button)) diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el index c8415601c..b247dddde 100644 --- a/modules/completion/company/config.el +++ b/modules/completion/company/config.el @@ -18,6 +18,8 @@ company-echo-metadata-frontend)) :config (add-hook 'company-mode-hook #'+company|init-backends) + (when (featurep! :feature evil) + (add-hook 'company-mode-hook #'evil-normalize-keymaps)) (global-company-mode +1)) @@ -36,10 +38,7 @@ (add-to-list 'company-frontends 'company-tng-frontend) (define-key! company-active-map "RET" nil - [return] nil "TAB" #'company-select-next - [tab] #'company-select-next - "" #'company-select-previous [backtab] #'company-select-previous)) @@ -60,44 +59,60 @@ (setq company-box-show-single-candidate t company-box-backends-colors nil company-box-max-candidates 50 - company-box-icons-yasnippet (all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green) - company-box-icons-unknown (all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple) - company-box-icons-elisp - (list (all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red) - (all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue) - (all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange) - (all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink)) - company-box-icons-lsp - `((1 . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green)) ; text - (2 . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) ; method - (3 . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) ; function - (4 . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) ; constructor - (5 . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) ; field - (6 . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue)) ; variable - (7 . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) ; class - (8 . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red)) ; interface - (9 . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red)) ; module - (10 . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red)) ; property - (11 . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red)) ; unit - (12 . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red)) ; value - (13 . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red)) ; enum - (14 . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red)) ; keyword - (15 . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red)) ; snippet - (16 . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red)) ; color - (17 . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red)) ; file - (18 . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red)) ; reference - (19 . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red)) ; folder - (20 . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red)) ; enumMember - (21 . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red)) ; constant - (22 . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red)) ; struct - (23 . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red)) ; event - (24 . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red)) ; operator - (25 . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))))) + company-box-icons-alist 'company-box-icons-all-the-icons + company-box-icons-functions + '(+company-box-icons--yasnippet company-box-icons--lsp +company-box-icons--elisp company-box-icons--acphp) + company-box-icons-all-the-icons + `((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple)) + (Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green)) + (Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) + (Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) + (Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) + (Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) + (Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue)) + (Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) + (Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red)) + (Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red)) + (Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red)) + (Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red)) + (Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red)) + (Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red)) + (Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red)) + (Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red)) + (Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red)) + (File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red)) + (Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red)) + (Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red)) + (EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red)) + (Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red)) + (Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red)) + (Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red)) + (Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red)) + (TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) + ;; (Template . ,(company-box-icons-image "Template.png")))) + (Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green)) + (ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) + (ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue)) + (ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange)) + (ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink)))) + + (defun +company-box-icons--yasnippet (candidate) + (when (get-text-property 0 'yas-annotation candidate) + 'Yasnippet)) + + (defun +company-box-icons--elisp (candidate) + (when (derived-mode-p 'emacs-lisp-mode) + (let ((sym (intern candidate))) + (cond ((fboundp sym) 'ElispFunction) + ((boundp sym) 'ElispVariable) + ((featurep sym) 'ElispFeature) + ((facep sym) 'ElispFace)))))) (def-package! company-dict :defer t :config + (setq company-dict-dir (expand-file-name "dicts" doom-private-dir)) (defun +company|enable-project-dicts (mode &rest _) "Enable per-project dictionaries." (if (symbol-value mode) diff --git a/modules/completion/helm/autoload/helm.el b/modules/completion/helm/autoload/helm.el index e7d6376f5..04cd60a1b 100644 --- a/modules/completion/helm/autoload/helm.el +++ b/modules/completion/helm/autoload/helm.el @@ -162,9 +162,10 @@ order. (string-join (delq nil (cdr command)) " ") (abbreviate-file-name directory)) helm-source-do-ag) - (cl-letf ((+helm-global-prompt prompt) - ((symbol-function 'helm-do-ag--helm) + (helm-attrset '+helm-command command helm-source-do-ag) + (cl-letf (((symbol-function 'helm-do-ag--helm) (lambda () (helm :sources '(helm-source-do-ag) + :prompt prompt :buffer "*helm-ag*" :keymap helm-do-ag-map :input query @@ -177,7 +178,7 @@ order. return (intern (format format tool)))) ;;;###autoload -(defun +helm/project-search (&optional all-files-p) +(defun +helm/project-search (&optional arg) "Performs a project search from the project root. Uses the first available search backend from `+helm-project-search-engines'. If @@ -186,10 +187,10 @@ ones, in the search." (interactive "P") (funcall (or (+helm--get-command "+helm/%s") #'+helm/grep) - (or all-files-p current-prefix-arg))) + arg)) ;;;###autoload -(defun +helm/project-search-from-cwd (&optional all-files-p) +(defun +helm/project-search-from-cwd (&optional arg) "Performs a project search recursively from the current directory. Uses the first available search backend from `+helm-project-search-engines'. If @@ -198,7 +199,7 @@ ones." (interactive "P") (funcall (or (+helm--get-command "+helm/%s-from-cwd") #'+helm/grep-from-cwd) - (or all-files-p current-prefix-arg))) + arg)) ;; Relative to project root @@ -213,21 +214,31 @@ ones." (dolist (engine `(,@(cl-remove-duplicates +helm-project-search-engines :from-end t) grep)) (defalias (intern (format "+helm/%s" engine)) - (lambda (all-files-p &optional query directory) + (lambda (arg &optional query directory) (interactive "P") - (+helm-file-search engine :query query :in directory :all-files all-files-p)) + (+helm-file-search engine + :query query + :in directory + :all-files (and (not (null arg)) + (listp arg)))) (format "Perform a project file search using %s. QUERY is a regexp. If omitted, the current selection is used. If no selection is active, the last known search is used. +ARG is the universal argument. If a number is passed through it, e.g. C-u 3, then + If ALL-FILES-P, search compressed and hidden files as well." engine)) (defalias (intern (format "+helm/%s-from-cwd" engine)) - (lambda (all-files-p &optional query) + (lambda (arg &optional query) (interactive "P") - (+helm-file-search engine :query query :in default-directory :all-files all-files-p)) + (+helm-file-search engine + :query query + :in default-directory + :all-files (and (not (null arg)) + (listp arg)))) (format "Perform a project file search from the current directory using %s. QUERY is a regexp. If omitted, the current selection is used. If no selection is diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index dc39f863c..c05856ea0 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -1,6 +1,12 @@ ;;; completion/ivy/autoload/ivy.el -*- lexical-binding: t; -*- -(defun +ivy--is-workspace-or-other-buffer-p (buffer) +(defun +ivy--is-workspace-buffer-p (buffer) + (let ((buffer (car buffer))) + (when (stringp buffer) + (setq buffer (get-buffer buffer))) + (+workspace-contains-buffer-p buffer))) + +(defun +ivy--is-workspace-other-buffer-p (buffer) (let ((buffer (car buffer))) (when (stringp buffer) (setq buffer (get-buffer buffer))) @@ -28,21 +34,73 @@ temporary/special buffers in `font-lock-comment-face'." ;; ;; Library +(defun +ivy--switch-buffer-preview () + (let (ivy-use-virtual-buffers ivy--virtual-buffers) + (counsel--switch-buffer-update-fn))) + +(defalias '+ivy--switch-buffer-preview-all #'counsel--switch-buffer-update-fn) +(defalias '+ivy--switch-buffer-unwind #'counsel--switch-buffer-unwind) + +(defun +ivy--switch-buffer (workspace other) + (let ((current (not other)) + prompt action filter update unwind) + (cond ((and workspace current) + (setq prompt "Switch to workspace buffer: " + action #'ivy--switch-buffer-action + filter #'+ivy--is-workspace-other-buffer-p)) + (workspace + (setq prompt "Switch to workspace buffer in other window: " + action #'ivy--switch-buffer-other-window-action + filter #'+ivy--is-workspace-buffer-p)) + (current + (setq prompt "Switch to buffer: " + action #'ivy--switch-buffer-action)) + (t + (setq prompt "Switch to buffer in other window: " + action #'ivy--switch-buffer-other-window-action))) + (when +ivy-buffer-preview + (cond ((not (and ivy-use-virtual-buffers + (eq +ivy-buffer-preview 'everything))) + (setq update #'+ivy--switch-buffer-preview + unwind #'+ivy--switch-buffer-unwind)) + (t + (setq update #'+ivy--switch-buffer-preview-all + unwind #'+ivy--switch-buffer-unwind)))) + (ivy-read prompt 'internal-complete-buffer + :action action + :predicate filter + :update-fn update + :unwind unwind + :preselect (buffer-name (other-buffer (current-buffer))) + :matcher #'ivy--switch-buffer-matcher + :keymap ivy-switch-buffer-map + :caller #'+ivy--switch-buffer))) + ;;;###autoload (defun +ivy/switch-workspace-buffer (&optional arg) "Switch to another buffer within the current workspace. If ARG (universal argument), open selection in other-window." (interactive "P") - (ivy-read "Switch to workspace buffer: " - 'internal-complete-buffer - :predicate #'+ivy--is-workspace-or-other-buffer-p - :action (if arg - #'ivy--switch-buffer-other-window-action - #'ivy--switch-buffer-action) - :matcher #'ivy--switch-buffer-matcher - :keymap ivy-switch-buffer-map - :caller #'+ivy/switch-workspace-buffer)) + (+ivy--switch-buffer t arg)) + +;;;###autoload +(defun +ivy/switch-workspace-buffer-other-window () + "Switch another window to a buffer within the current workspace." + (interactive) + (+ivy--switch-buffer t t)) + +;;;###autoload +(defun +ivy/switch-buffer () + "Switch to another buffer." + (interactive) + (+ivy--switch-buffer nil nil)) + +;;;###autoload +(defun +ivy/switch-buffer-other-window () + "Switch to another buffer in another window." + (interactive) + (+ivy--switch-buffer nil t)) (defun +ivy--tasks-candidates (tasks) "Generate a list of task tags (specified by `+ivy-task-tags') for @@ -230,7 +288,7 @@ order. 'grep) (error "No search engine specified (is ag, rg, pt or git installed?)"))) (query - (or query + (or (if query (rxt-quote-pcre query)) (when (use-region-p) (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (end (or (bound-and-true-p evil-visual-end) (region-end)))) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index e171d4957..e07c7db23 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -3,6 +3,13 @@ (defvar +ivy-buffer-icons nil "If non-nil, show buffer mode icons in `ivy-switch-buffer' and the like.") +(defvar +ivy-buffer-preview nil + "If non-nil, preview buffers while switching, à la `counsel-switch-buffer'. + +When nil, don't preview anything. +When non-nil, preview non-virtual buffers. +When 'everything, also preview virtual buffers") + (defvar +ivy-task-tags '(("TODO" . warning) ("FIXME" . error)) @@ -58,9 +65,11 @@ immediately runs it on the current candidate (ending the ivy session)." (after! yasnippet (add-to-list 'yas-prompt-functions #'+ivy-yas-prompt nil #'eq)) - (map! [remap switch-to-buffer] #'ivy-switch-buffer - [remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer - [remap imenu-anywhere] #'ivy-imenu-anywhere) + (map! :map ivy-mode-map + [remap switch-to-buffer] #'+ivy/switch-buffer + [remap switch-to-buffer-other-window] #'+ivy/switch-buffer-other-window + [remap persp-switch-to-buffer] #'+ivy/switch-workspace-buffer + [remap imenu-anywhere] #'ivy-imenu-anywhere) (ivy-mode +1) @@ -68,15 +77,15 @@ immediately runs it on the current candidate (ending the ivy session)." :commands (ivy-dispatching-done-hydra ivy--matcher-desc) :init (define-key! ivy-minibuffer-map - "\C-o" #'+ivy-coo-hydra/body - (kbd "M-o") #'ivy-dispatching-done-hydra))) + "C-o" #'+ivy-coo-hydra/body + "M-o" #'ivy-dispatching-done-hydra))) (def-package! ivy-rich :hook (ivy-mode . ivy-rich-mode) :config ;; Show more buffer information in other switch-buffer commands too - (dolist (cmd '(+ivy/switch-workspace-buffer + (dolist (cmd '(+ivy--switch-buffer counsel-projectile-switch-to-buffer)) (ivy-set-display-transformer cmd 'ivy-rich--ivy-switch-buffer-transformer)) ;; Use `+ivy-rich-buffer-name' to display buffer names @@ -95,6 +104,8 @@ immediately runs it on the current candidate (ending the ivy session)." [remap describe-face] #'counsel-faces [remap describe-function] #'counsel-describe-function [remap describe-variable] #'counsel-describe-variable + [remap describe-bindings] #'counsel-descbinds + [remap set-variable] #'counsel-set-variable [remap execute-extended-command] #'counsel-M-x [remap find-file] #'counsel-find-file [remap find-library] #'counsel-find-library @@ -203,6 +214,10 @@ immediately runs it on the current candidate (ending the ivy session)." ;; default to posframe display function (setf (alist-get t ivy-display-functions-alist) #'+ivy-display-at-frame-center-near-bottom) + ;; Fix #1017: stop session persistence from restoring a broken posframe + (defun +workspace|delete-all-posframes (&rest _) (posframe-delete-all)) + (add-hook 'persp-after-load-state-functions #'+workspace|delete-all-posframes) + ;; posframe doesn't work well with async sources (dolist (fn '(swiper counsel-ag counsel-grep counsel-git-grep)) (setf (alist-get fn ivy-display-functions-alist) #'ivy-display-function-fallback))) diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index 92a46fb2f..2aeb0c1bb 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -1,304 +1,352 @@ ;;; config/default/+emacs-bindings.el -*- lexical-binding: t; -*- ;; Sensible deafult key bindings for non-evil users +(setq doom-leader-alt-key "C-c" + doom-localleader-alt-key "C-c l") ;; persp-mode and projectile in different prefixes -(setq persp-keymap-prefix (kbd "C-c e")) +(setq persp-keymap-prefix (kbd "C-c w")) (after! projectile (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)) -(which-key-add-key-based-replacements "C-c !" "checking") -(which-key-add-key-based-replacements "C-c e" "perspective") -(which-key-add-key-based-replacements "C-c p" "projectile") +(after! which-key + (which-key-add-key-based-replacements "C-c !" "checking") + (which-key-add-key-based-replacements "C-c l" "")) -;; Prefix key to invoke doom related commands -(setq doom-leader-alt-key "C-c") -(setq doom-localleader-alt-key "C-c l") -(map! - ;; Text scaling - "" #'text-scale-increase - "" #'text-scale-decrease - "" (λ! (text-scale-set 0)) - "M-+" (λ! (text-scale-set 0)) - "M-=" #'text-scale-increase - "M--" #'text-scale-decrease - ;; Editor related bindings - [remap newline] #'newline-and-indent - "C-j" #'+default/newline - (:when (featurep! :completion ivy) - "C-S-s" #'swiper - "C-S-r" #'ivy-resume) - (:when (featurep! :completion helm) - "C-S-s" #'swiper-helm - "C-S-r" #'helm-resume) - ;; Buffer related bindings - "C-x b" #'persp-switch-to-buffer - "C-x C-b" #'ibuffer-list-buffers - "C-x B" #'switch-to-buffer - "C-x k" #'doom/kill-this-buffer-in-all-windows - ;; Popup bindigns - "C-x p" #'+popup/other - "C-`" #'+popup/toggle - "C-~" #'+popup/raise - ;; Doom emacs bindings - (:leader - (:prefix ("d" . "doom") - :desc "Dashboard" "d" #'+doom-dashboard/open - :desc "Recent files" "f" #'recentf-open-files - (:when (featurep! :ui neotree) - :desc "Open neotree" "n" #'+neotree/open - :desc "File in neotree" "N" #'neotree/find-this-file) - (:when (featurep! :ui treemacs) - :desc "Toggle treemacs" "n" #'+treemacs/toggle - :desc "File in treemacs" "N" #'+treemacs/find-file) - :desc "Popup other" "o" #'+popup/other - :desc "Popup toggle" "t" #'+popup/toggle - :desc "Popup close" "c" #'+popup/close - :desc "Popup close all" "C" #'+popup/close-all - :desc "Popup raise" "r" #'+popup/raise - :desc "Popup restore" "R" #'+popup/restore - :desc "Scratch buffer" "s" #'doom/open-scratch-buffer - :desc "Switch to scratch buffer" "S" #'doom/switch-to-scratch-buffer - :desc "Sudo this file" "u" #'doom/sudo-this-file - :desc "Sudo find file" "U" #'doom/sudo-find-file - :desc "Eshell popup" "e" #'+eshell/open-popup - :desc "Eshell open" "E" #'+eshell/open - :desc "Reload Private Config" "R" #'doom/reload) - ;; Org related bindings - "o" nil ; we need to unbind it first as Org claims this - (:prefix ("o". "org") - :desc "Do what I mean" "o" #'+org/dwim-at-point - :desc "Sync org caldav" "s" #'org-caldav-sync - (:prefix ("a" . "org agenda") - :desc "Agenda" "a" #'org-agenda - :desc "Todo list" "t" #'org-todo-list - :desc "Tags view" "m" #'org-tags-view - :desc "View search" "v" #'org-search-view) - :desc "Capture" "c" #'org-capture - :desc "Goto capture" "C" (λ! (require 'org-capture) (call-interactively #'org-capture-goto-target)) - :desc "Switch org buffers" "b" #'org-switchb - (:prefix ("e" . "org export") - :desc "Export beamer to latex" "l b" #'org-beamer-export-to-latex - :desc "Export beamer as latex" "l B" #'org-beamer-export-as-latex - :desc "Export beamer as pdf" "l P" #'org-beamer-export-to-pdf) - :desc "Link store" "l" #'org-store-link) - ;; Quit/Restart - (:prefix ("q" . "quit/restart") - :desc "Quit Emacs" "q" #'kill-emacs - :desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal - (:when (featurep! :feature workspaces) - :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit - :desc "Restart & restore Emacs" "r" #'+workspace/restart-emacs-then-restore) - :desc "Restart Emacs" "R" #'restart-emacs) - ;; Snippets - "&" nil ; yasnippet creates this prefix, we use a different one - (:prefix ("s" . "snippets") - :desc "New snippet" "n" #'yas-new-snippet - :desc "Insert snippet" "i" #'yas-insert-snippet - :desc "Find global snippet" "/" #'yas-visit-snippet-file - :desc "Reload snippets" "r" #'yas-reload-all - :desc "Create Temp Template" "c" #'aya-create - :desc "Use Temp Template" "e" #'aya-expand) - ;; Version control bindings - (:prefix ("v" . "versioning") - :desc "Browse issues tracker" "i" #'forge-browse-issues - :desc "Browse remote" "o" #'forge-browse-remote - :desc "Diff current file" "d" #'magit-diff-buffer-file - :desc "Git revert hunk" "r" #'git-gutter:revert-hunk - :desc "Git stage file" "S" #'magit-stage-file - :desc "Git stage hunk" "s" #'git-gutter:stage-hunk - :desc "Git time machine" "t" #'git-timemachine-toggle - :desc "Git unstage file" "U" #'magit-unstage-file - :desc "Initialize repo" "I" #'magit-init - :desc "List repositories" "L" #'magit-list-repositories - :desc "Magit blame" "b" #'magit-blame-addition - :desc "Magit buffer log" "l" #'magit-log-buffer-file - :desc "Magit commit" "c" #'magit-commit-create - :desc "Magit status" "g" #'magit-status - :desc "Next hunk" "]" #'git-gutter:next-hunk - :desc "Previous hunk" "[" #'git-gutter:previous-hunk) - ;; Worspace and window management bindings - (:prefix ("w". "workspaces") - :desc "Autosave session" "a" #'+workspace/save-session - :desc "Display workspaces" "d" #'+workspace/display - :desc "Rename workspace" "r" #'+workspace/rename - :desc "Create workspace" "c" #'+workspace/new - :desc "Delete workspace" "k" #'+workspace/delete - :desc "Save session" "s" (λ! (let ((current-prefix-arg '(4))) (call-interactively #'+workspace/save-session))) - :desc "Save workspace" "S" #'+workspace/save - :desc "Load session" "l" #'+workspace/load-session - :desc "Load last autosaved session" "L" #'+workspace/load-last-session - :desc "Kill other buffers" "o" #'doom/kill-other-buffers - :desc "Undo window config" "u" #'winner-undo - :desc "Redo window config" "U" #'winner-redo - :desc "Switch to left workspace" "p" #'+workspace/switch-left - :desc "Switch to right workspace" "n" #'+workspace/switch-right - :desc "Switch to" "w" #'+workspace/switch-to - :desc "Switch to workspace 1" "1" (λ! (+workspace/switch-to 0)) - :desc "Switch to workspace 2" "2" (λ! (+workspace/switch-to 1)) - :desc "Switch to workspace 3" "3" (λ! (+workspace/switch-to 2)) - :desc "Switch to workspace 4" "4" (λ! (+workspace/switch-to 3)) - :desc "Switch to workspace 5" "5" (λ! (+workspace/switch-to 4)) - :desc "Switch to workspace 6" "6" (λ! (+workspace/switch-to 5)) - :desc "Switch to workspace 7" "7" (λ! (+workspace/switch-to 6)) - :desc "Switch to workspace 8" "8" (λ! (+workspace/switch-to 7)) - :desc "Switch to workspace 9" "9" (λ! (+workspace/switch-to 8)) - :desc "Switch to last workspace" "0" #'+workspace/switch-to-last) - ;; Multiple Cursors - (:when (featurep! :editor multiple-cursors) - (:prefix ("m" . "multiple cursors") - :desc "Edit lines" "l" #'mc/edit-lines - :desc "Mark next" "n" #'mc/mark-next-like-this - :desc "Unmark next" "N" #'mc/unmark-next-like-this - :desc "Mark previous" "p" #'mc/mark-previous-like-this - :desc "Unmark previous" "P" #'mc/unmark-previous-like-this - :desc "Mark all" "t" #'mc/mark-all-like-this - :desc "Mark all DWIM" "m" #'mc/mark-all-like-this-dwim - :desc "Edit line endings" "e" #'mc/edit-ends-of-lines - :desc "Edit line starts" "a" #'mc/edit-beginnings-of-lines - :desc "Mark tag" "s" #'mc/mark-sgml-tag-pair - :desc "Mark in defun" "d" #'mc/mark-all-like-this-in-defun - :desc "Add cursor w/mouse" "" #'mc/add-cursor-on-click)) +;; +;;; Global keybinds - ;; APPs +(map! "C-'" #'imenu + ;; Text scaling + "" #'text-scale-increase + "" #'text-scale-decrease + "" (λ! (text-scale-set 0)) + "M-+" (λ! (text-scale-set 0)) + "M-=" #'text-scale-increase + "M--" #'text-scale-decrease + ;; Editor related bindings + [remap newline] #'newline-and-indent + "C-j" #'+default/newline + (:when (featurep! :completion ivy) + "C-S-s" #'swiper + "C-S-r" #'ivy-resume) + (:when (featurep! :completion helm) + "C-S-s" #'swiper-helm + "C-S-r" #'helm-resume) + ;; Buffer related bindings + "C-x b" #'persp-switch-to-buffer + (:when (featurep! :completion ivy) + "C-x 4 b" #'+ivy/switch-workspace-buffer-other-window) + "C-x C-b" #'ibuffer-list-buffers + "C-x B" #'switch-to-buffer + "C-x 4 B" #'switch-to-buffer-other-window + "C-x k" #'doom/kill-this-buffer-in-all-windows + ;; Popup bindigns + "C-x p" #'+popup/other + "C-`" #'+popup/toggle + "C-~" #'+popup/raise) - ;; Email - (:when (featurep! :app email) - (:prefix ("M" . "email") - :desc "Open email app" "m" #'=email - :desc "Compose email" "c" #'+email/compose)) - ;; IRC - (:when (featurep! :app irc) - (:prefix ("I" . "irc") - :desc "Open irc app" "i" #'=irc - :desc "Next unread buffer" "a" #'tracking-next-buffer - :desc "Quit irc" "q" #'+irc/quit - :desc "Reconnect all" "r" #'circe-reconnect-all - :desc "Send message" "s" #'+irc/send-message - (:when (featurep! :completion ivy) - :desc "Jump to channel" "j" #'irc/ivy-jump-to-channel))) - ;; Twitter - (:when (featurep! :app twitter) - (:prefix ("T" . "twitter") - :desc "Open twitter app" "t" #'=twitter - :desc "Quit twitter" "q" #'+twitter/quit - :desc "Rerender twits" "r" #'+twitter/rerender-all - :desc "Ace link" "l" #'+twitter/ace-link))) - ;; Plugins +;; +;;; Leader keys - ;; misc plugins - (:when (featurep! :ui neotree) - "" #'+neotree/open) - (:when (featurep! :ui treemacs) - "" #'+treemacs/toggle) - "C-=" #'er/expand-region - "C--" #'er/contract-region - ;; smartparens - (:after smartparens - (:map smartparens-mode-map - "C-M-a" #'sp-beginning-of-sexp - "C-M-e" #'sp-end-of-sexp - "C-M-f" #'sp-forward-sexp - "C-M-b" #'sp-backward-sexp - "C-M-d" #'sp-splice-sexp - "C-M-k" #'sp-kill-sexp - "C-M-t" #'sp-transpose-sexp - "C-" #'sp-forward-slurp-sexp - "M-" #'sp-forward-barf-sexp - "C-" #'sp-backward-slurp-sexp - "M-" #'sp-backward-barf-sexp)) - ;; company mode - "C-;" #'+company/complete - ;; Counsel - (: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)) - "C-h b" #'counsel-descbinds - "C-M-y" #'counsel-yank-pop - "C-h F" #'counsel-faces - "C-h p" #'counsel-package - "C-h a" #'counsel-apropos - "C-h V" #'counsel-set-variable - "C-'" #'counsel-imenu)) - ;; repl toggle - "C-c C-z" #'+eval/open-repl - ;; company mode - (:after company - (:map company-active-map - "C-o" #'company-search-kill-others - "C-n" #'company-select-next - "C-p" #'company-select-previous - "C-h" #'company-quickhelp-manual-begin - "C-S-h" #'company-show-doc-buffer - "C-s" #'company-search-candidates - "M-s" #'company-filter-candidates - "" #'company-complete-common-or-cycle - [tab] #'company-complete-common-or-cycle - [backtab] #'company-select-previous - "C-RET" #'counsel-company) - (:map company-search-map - "C-n" #'company-search-repeat-forward - "C-p" #'company-search-repeat-backward - "C-s" (λ! (company-search-abort) (company-filter-candidates)))) - ;; neotree bindings - (:after neotree - :map neotree-mode-map - "q" #'neotree-hide - [return] #'neotree-enter - "RET" #'neotree-enter - "SPC" #'neotree-quick-look - "v" #'neotree-enter-vertical-split - "s" #'neotree-enter-horizontal-split - "c" #'neotree-create-node - "D" #'neotree-delete-node - "g" #'neotree-refresh - "r" #'neotree-rename-node - "R" #'neotree-refresh - "h" #'+neotree/collapse-or-up - "l" #'+neotree/expand-or-open - "n" #'neotree-next-line - "p" #'neotree-previous-line - "N" #'neotree-select-next-sibling-node - "P" #'neotree-select-previous-sibling-node) - ;; help and info - (:after help-mode - (:map help-mode-map - "o" #'ace-link-help - ">" #'help-go-forward - "<" #'help-go-back)) - (:after helpful-mode - (:map helpful-mode-map - "o" #'ace-link-help)) - (:after info - (:map Info-mode-map - "o" #'ace-link-info)) - ;; yasnippet - (:after yasnippet - ;; keymap while editing an inserted snippet - (:map yas-keymap - "C-e" #'+snippets/goto-end-of-field - "C-a" #'+snippets/goto-start-of-field - "" #'yas-prev-field - "" #'+snippets/delete-to-start-of-field - [backspace] #'+snippets/delete-backward-char - [delete] #'+snippets/delete-forward-char-or-field)) - ;; flycheck - (:after flycheck - (:map flycheck-error-list-mode-map - "C-n" #'flycheck-error-list-next-error - "C-p" #'flycheck-error-list-previous-error - "RET" #'flycheck-error-list-goto-error)) - ;; ivy - (:after ivy - (:map ivy-minibuffer-map - "TAB" #'ivy-alt-done - "C-g" #'keyboard-escape-quit)) - ;; ein notebokks - (:after ein:notebook-multilang - (:map ein:notebook-multilang-mode-map - "C-c h" #'+ein/hydra/body))) +(map! :leader + :desc "Find file in project" "C-f" #'projectile-find-file + :desc "Evaluate line/region" "e" #'+eval/line-or-region + :desc "Pop up scratch buffer" "x" #'doom/open-to-scratch-buffer + :desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer + + (:when (featurep! :emacs term) + :desc "Terminal" "`" #'+term/open + :desc "Terminal in popup" "~" #'+term/open-popup-in-project) + (:when (featurep! :tools vterm) + :desc "Terminal" "`" #'+vterm/open + :desc "Terminal in popup" "~" #'+vterm/open-popup-in-project) + (:when (featurep! :emacs eshell) + :desc "Eshell" "`" #'+eshell/open + :desc "Eshell in popup" "~" #'+eshell/open-popup) + + ;; Add labels to prefixes defined elsewhere + :desc "project" "p" nil + + (:prefix ("f" . "file") + :desc "Find other file" "a" #'projectile-find-other-file + :desc "Browse private config" "c" #'doom/open-private-config + :desc "Find file in private config" "C" #'doom/find-file-in-private-config + :desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig + :desc "Find directory" "d" #'dired + :desc "Find file in emacs.d" "e" #'+default/find-in-emacsd + :desc "Browse emacs.d" "E" #'+default/browse-emacsd + :desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file) + :desc "Find file in other project" "F" #'doom/browse-in-other-project + :desc "Find file in project" "p" #'projectile-find-file + :desc "Find file in other project" "P" #'doom/find-file-in-other-project + :desc "Recent files" "r" #'recentf-open-files + :desc "Recent project files" "R" #'projectile-recentf + :desc "Sudo this file" "s" #'doom/sudo-this-file + :desc "Sudo find file" "S" #'doom/sudo-find-file + :desc "Delete this file" "X" #'doom/delete-this-file + :desc "Yank filename" "y" #'+default/yank-buffer-filename) + + "o" nil ; we need to unbind it first as Org claims this + (:prefix ("o". "org") + (:prefix ("a" . "org agenda") + :desc "Agenda" "a" #'org-agenda + :desc "Todo list" "t" #'org-todo-list + :desc "Tags view" "m" #'org-tags-view + :desc "View search" "v" #'org-search-view) + :desc "Switch org buffers" "b" #'org-switchb + :desc "Capture" "c" #'org-capture + :desc "Goto capture" "C" (λ! (require 'org-capture) (call-interactively #'org-capture-goto-target)) + :desc "Link store" "l" #'org-store-link + :desc "Sync org caldav" "s" #'org-caldav-sync) + + (:prefix ("q" . "quit/restart") + :desc "Quit Emacs" "q" #'kill-emacs + :desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal + (:when (featurep! :feature workspaces) + :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit) + :desc "Restart & restore Emacs" "r" #'doom/restart-and-restore + :desc "Restart Emacs" "R" #'doom/restart) + + (:prefix ("&" . "snippets") + :desc "New snippet" "n" #'yas-new-snippet + :desc "Insert snippet" "i" #'yas-insert-snippet + :desc "Find global snippet" "/" #'yas-visit-snippet-file + :desc "Reload snippets" "r" #'yas-reload-all + :desc "Create Temp Template" "c" #'aya-create + :desc "Use Temp Template" "e" #'aya-expand) + + (:prefix ("v" . "versioning") + :desc "Git revert file" "R" #'vc-revert + (:when (featurep! :ui vc-gutter) + :desc "Git revert hunk" "r" #'git-gutter:revert-hunk + :desc "Git stage hunk" "s" #'git-gutter:stage-hunk + :desc "Git time machine" "t" #'git-timemachine-toggle + :desc "Jump to next hunk" "n" #'git-gutter:next-hunk + :desc "Jump to previous hunk" "p" #'git-gutter:previous-hunk) + (:when (featurep! :tools magit) + :desc "Magit dispatch" "/" #'magit-dispatch + :desc "Forge dispatch" "'" #'forge-dispatch + :desc "Magit status" "g" #'magit-status + :desc "Magit file delete" "x" #'magit-file-delete + :desc "Magit blame" "B" #'magit-blame-addition + :desc "Magit clone" "C" #'+magit/clone + :desc "Magit fetch" "F" #'magit-fetch + :desc "Magit buffer log" "L" #'magit-log + :desc "Git stage file" "S" #'magit-stage-file + :desc "Git unstage file" "U" #'magit-unstage-file + (:prefix ("f" . "find") + :desc "Find file" "f" #'magit-find-file + :desc "Find gitconfig file" "g" #'magit-find-git-config-file + :desc "Find commit" "c" #'magit-show-commit + :desc "Find issue" "i" #'forge-visit-issue + :desc "Find pull request" "p" #'forge-visit-pullreq) + (:prefix ("o" . "open in browser") + :desc "Browse region or line" "." #'+vc/git-browse-region-or-line + :desc "Browse remote" "r" #'forge-browse-remote + :desc "Browse commit" "c" #'forge-browse-commit + :desc "Browse an issue" "i" #'forge-browse-issue + :desc "Browse a pull request" "p" #'forge-browse-pullreq + :desc "Browse issues" "I" #'forge-browse-issues + :desc "Browse pull requests" "P" #'forge-browse-pullreqs) + (:prefix ("l" . "list") + (:when (featurep! :tools gist) + :desc "List gists" "g" #'+gist:list) + :desc "List repositories" "r" #'magit-list-repositories + :desc "List submodules" "s" #'magit-list-submodules + :desc "List issues" "i" #'forge-list-issues + :desc "List pull requests" "p" #'forge-list-pullreqs + :desc "List notifications" "n" #'forge-list-notifications) + (:prefix ("c" . "create") + :desc "Initialize repo" "r" #'magit-init + :desc "Clone repo" "R" #'+magit/clone + :desc "Commit" "c" #'magit-commit-create + :desc "Issue" "i" #'forge-create-issue + :desc "Pull request" "p" #'forge-create-pullreq))) + + (:prefix ("w" . "workspaces/windows") + :desc "Autosave session" "a" #'doom/quicksave-session + :desc "Display workspaces" "d" #'+workspace/display + :desc "Rename workspace" "r" #'+workspace/rename + :desc "Create workspace" "c" #'+workspace/new + :desc "Delete workspace" "k" #'+workspace/delete + :desc "Save session" "s" #'doom/save-session + :desc "Save workspace" "S" #'+workspace/save + :desc "Load session" "l" #'doom/load-session + :desc "Load last autosaved session" "L" #'doom/quickload-session + :desc "Kill other buffers" "o" #'doom/kill-other-buffers + :desc "Undo window config" "u" #'winner-undo + :desc "Redo window config" "U" #'winner-redo + :desc "Switch to left workspace" "p" #'+workspace/switch-left + :desc "Switch to right workspace" "n" #'+workspace/switch-right + :desc "Switch to" "w" #'+workspace/switch-to + :desc "Switch to workspace 1" "1" (λ! (+workspace/switch-to 0)) + :desc "Switch to workspace 2" "2" (λ! (+workspace/switch-to 1)) + :desc "Switch to workspace 3" "3" (λ! (+workspace/switch-to 2)) + :desc "Switch to workspace 4" "4" (λ! (+workspace/switch-to 3)) + :desc "Switch to workspace 5" "5" (λ! (+workspace/switch-to 4)) + :desc "Switch to workspace 6" "6" (λ! (+workspace/switch-to 5)) + :desc "Switch to workspace 7" "7" (λ! (+workspace/switch-to 6)) + :desc "Switch to workspace 8" "8" (λ! (+workspace/switch-to 7)) + :desc "Switch to workspace 9" "9" (λ! (+workspace/switch-to 8)) + :desc "Switch to last workspace" "0" #'+workspace/switch-to-last) + + (:when (featurep! :editor multiple-cursors) + (:prefix ("m" . "multiple cursors") + :desc "Edit lines" "l" #'mc/edit-lines + :desc "Mark next" "n" #'mc/mark-next-like-this + :desc "Unmark next" "N" #'mc/unmark-next-like-this + :desc "Mark previous" "p" #'mc/mark-previous-like-this + :desc "Unmark previous" "P" #'mc/unmark-previous-like-this + :desc "Mark all" "t" #'mc/mark-all-like-this + :desc "Mark all DWIM" "m" #'mc/mark-all-like-this-dwim + :desc "Edit line endings" "e" #'mc/edit-ends-of-lines + :desc "Edit line starts" "a" #'mc/edit-beginnings-of-lines + :desc "Mark tag" "s" #'mc/mark-sgml-tag-pair + :desc "Mark in defun" "d" #'mc/mark-all-like-this-in-defun + :desc "Add cursor w/mouse" "" #'mc/add-cursor-on-click)) + + ;; APPs + (:when (featurep! :app email) + (:prefix ("M" . "email") + :desc "Open email app" "M" #'=email + :desc "Compose email" "c" #'+email/compose)) + + (:when (featurep! :app irc) + (:prefix ("I" . "irc") + :desc "Open irc app" "I" #'=irc + :desc "Next unread buffer" "a" #'tracking-next-buffer + :desc "Quit irc" "q" #'+irc/quit + :desc "Reconnect all" "r" #'circe-reconnect-all + :desc "Send message" "s" #'+irc/send-message + (:when (featurep! :completion ivy) + :desc "Jump to channel" "j" #'irc/ivy-jump-to-channel))) + + (:when (featurep! :app twitter) + (:prefix ("T" . "twitter") + :desc "Open twitter app" "T" #'=twitter + :desc "Quit twitter" "q" #'+twitter/quit + :desc "Rerender twits" "r" #'+twitter/rerender-all + :desc "Ace link" "l" #'+twitter/ace-link))) + + +;; +;;; Plugins + +(map! "C-=" #'er/expand-region + "C--" #'er/contract-region + (:when (featurep! :ui neotree) + "" #'+neotree/open + "" #'+neotree/find-this-file) + (:when (featurep! :ui treemacs) + "" #'+treemacs/open + "" #'+treemacs/find-file) + ;; smartparens + (:after smartparens + :map smartparens-mode-map + "C-M-a" #'sp-beginning-of-sexp + "C-M-e" #'sp-end-of-sexp + "C-M-f" #'sp-forward-sexp + "C-M-b" #'sp-backward-sexp + "C-M-d" #'sp-splice-sexp + "C-M-k" #'sp-kill-sexp + "C-M-t" #'sp-transpose-sexp + "C-" #'sp-forward-slurp-sexp + "M-" #'sp-forward-barf-sexp + "C-" #'sp-backward-slurp-sexp + "M-" #'sp-backward-barf-sexp) + ;; company mode + "C-;" #'+company/complete + ;; Counsel + (: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)) + "C-M-y" #'counsel-yank-pop) + ;; repl toggle + "C-c C-z" #'+eval/open-repl-other-window + ;; company mode + (:after company + :map company-active-map + "C-o" #'company-search-kill-others + "C-n" #'company-select-next + "C-p" #'company-select-previous + "C-h" #'company-quickhelp-manual-begin + "C-S-h" #'company-show-doc-buffer + "C-s" #'company-search-candidates + "M-s" #'company-filter-candidates + "" #'company-complete-common-or-cycle + [tab] #'company-complete-common-or-cycle + [backtab] #'company-select-previous + "C-RET" #'counsel-company + :map company-search-map + "C-n" #'company-search-repeat-forward + "C-p" #'company-search-repeat-backward + "C-s" (λ! (company-search-abort) (company-filter-candidates))) + ;; neotree bindings + (:after neotree + :map neotree-mode-map + "q" #'neotree-hide + "RET" #'neotree-enter + "SPC" #'neotree-quick-look + "v" #'neotree-enter-vertical-split + "s" #'neotree-enter-horizontal-split + "c" #'neotree-create-node + "D" #'neotree-delete-node + "g" #'neotree-refresh + "r" #'neotree-rename-node + "R" #'neotree-refresh + "h" #'+neotree/collapse-or-up + "l" #'+neotree/expand-or-open + "n" #'neotree-next-line + "p" #'neotree-previous-line + "N" #'neotree-select-next-sibling-node + "P" #'neotree-select-previous-sibling-node) + ;; help and info + (:after help-mode + :map help-mode-map + "o" #'ace-link-help + ">" #'help-go-forward + "<" #'help-go-back + "n" #'forward-button + "p" #'backward-button) + (:after helpful + :map helpful-mode-map + "o" #'ace-link-help) + (:after apropos + :map apropos-mode-map + "o" #'ace-link-help + "n" #'forward-button + "p" #'backward-button) + (:after info + :map Info-mode-map + "o" #'ace-link-info) + ;; yasnippet + (:after yasnippet + ;; keymap while editing an inserted snippet + :map yas-keymap + "C-e" #'+snippets/goto-end-of-field + "C-a" #'+snippets/goto-start-of-field + "" #'yas-prev-field + "" #'+snippets/delete-to-start-of-field + [backspace] #'+snippets/delete-backward-char + [delete] #'+snippets/delete-forward-char-or-field) + ;; flycheck + (:after flycheck + :map flycheck-error-list-mode-map + "C-n" #'flycheck-error-list-next-error + "C-p" #'flycheck-error-list-previous-error + "RET" #'flycheck-error-list-goto-error) + ;; ivy + (:after ivy + :map ivy-minibuffer-map + "TAB" #'ivy-alt-done + "C-g" #'keyboard-escape-quit) + ;; ein notebokks + (:after ein:notebook-multilang + :map ein:notebook-multilang-mode-map + "C-c h" #'+ein/hydra/body)) diff --git a/modules/config/default/+emacs.el b/modules/config/default/+emacs.el new file mode 100644 index 000000000..c9188f3d5 --- /dev/null +++ b/modules/config/default/+emacs.el @@ -0,0 +1,32 @@ +;;; config/default/+emacs.el -*- lexical-binding: t; -*- + +(require 'projectile) ; we need its keybinds immediately + + +;; +;;; Reasonable defaults + +(setq shift-select-mode t) +(delete-selection-mode +1) + +(def-package! expand-region + :commands (er/contract-region er/mark-symbol er/mark-word) + :config + (defun doom*quit-expand-region () + "Properly abort an expand-region region." + (when (memq last-command '(er/expand-region er/contract-region)) + (er/contract-region 0))) + (advice-add #'evil-escape :before #'doom*quit-expand-region) + (advice-add #'doom/escape :before #'doom*quit-expand-region)) + + +(def-package! winum + :after-call (doom-switch-window-hook) + :config (winum-mode +1)) + + +;; +;;; Keybinds + +(when (featurep! +bindings) + (load! "+emacs-bindings")) diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index dd216510a..f5ce134b5 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -2,10 +2,6 @@ ;; This file defines a Spacemacs-esque keybinding scheme -;; expand-region's prompt can't tell what key contract-region is bound to, so we -;; tell it explicitly. -(setq expand-region-contract-fast-key "C-v") - ;; Don't let evil-collection interfere with certain keys (setq evil-collection-key-blacklist (list "C-j" "C-k" "gd" "gf" "K" "[" "]" "gz" @@ -21,9 +17,6 @@ "M-;" #'eval-expression "A-;" #'eval-expression) - [remap evil-jump-to-tag] #'projectile-find-tag - [remap find-tag] #'projectile-find-tag - ;; Smart tab :i [tab] (general-predicate-dispatch nil ; fall back to nearest keymap (and (featurep! :feature snippets) @@ -34,10 +27,6 @@ (+company-has-completion-p)) '+company/complete) :n [tab] (general-predicate-dispatch nil - (derived-mode-p 'magit-mode) - 'magit-section-toggle - (derived-mode-p 'deadgrep-mode) - 'deadgrep-toggle-file-results (and (featurep! :editor fold) (save-excursion (end-of-line) (invisible-p (point)))) '+fold/toggle @@ -59,18 +48,111 @@ :i [remap newline] #'newline-and-indent ; auto-indent on newline :i "C-j" #'+default/newline ; default behavior - ;; expand-region - :v "v" (general-predicate-dispatch 'er/expand-region - (eq (evil-visual-type) 'line) - 'evil-visual-char) - :v "C-v" #'er/contract-region - (:after vc-annotate :map vc-annotate-mode-map [remap quit-window] #'kill-this-buffer) ;; misc - :n "C-S-f" #'toggle-frame-fullscreen) + :n "C-S-f" #'toggle-frame-fullscreen + + ;; Global evil keybinds + :m "]a" #'evil-forward-arg + :m "[a" #'evil-backward-arg + :m "]o" #'outline-next-visible-heading + :m "[o" #'outline-previous-visible-heading + :n "]b" #'next-buffer + :n "[b" #'previous-buffer + :n "zx" #'kill-this-buffer + :n "ZX" #'bury-buffer + :n "gp" #'+evil/reselect-paste + :n "g=" #'widen + :v "g=" #'+evil:narrow-buffer + :nv "g@" #'+evil:apply-macro + :nv "gc" #'evil-commentary + :nv "gx" #'evil-exchange + :nv "C-a" #'evil-numbers/inc-at-pt + :nv "C-S-a" #'evil-numbers/dec-at-pt + :v "gp" #'+evil/paste-preserve-register + :v "@" #'+evil:apply-macro + ;; repeat in visual mode (FIXME buggy) + :v "." #'+evil:apply-macro + ;; don't leave visual mode after shifting + :v "<" #'+evil/visual-dedent ; vnoremap < " #'+evil/visual-indent ; vnoremap > >gv + + ;; window management (prefix "C-w") + (:map evil-window-map + ;; Navigation + "C-h" #'evil-window-left + "C-j" #'evil-window-down + "C-k" #'evil-window-up + "C-l" #'evil-window-right + "C-w" #'other-window + ;; Swapping windows + "H" #'+evil/window-move-left + "J" #'+evil/window-move-down + "K" #'+evil/window-move-up + "L" #'+evil/window-move-right + "C-S-w" #'ace-swap-window + ;; Window undo/redo + "u" #'winner-undo + "C-u" #'winner-undo + "C-r" #'winner-redo + "o" #'doom/window-enlargen + "O" #'doom/window-zoom + ;; Delete window + "c" #'+workspace/close-window-or-workspace + "C-C" #'ace-delete-window) + + ;; Plugins + ;; evil-easymotion + :m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion' + (:after evil-easymotion + :map evilem-map + "a" (evilem-create #'evil-forward-arg) + "A" (evilem-create #'evil-backward-arg) + "s" (evilem-create #'evil-snipe-repeat + :name 'evil-easymotion-snipe-forward + :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))) + "S" (evilem-create #'evil-snipe-repeat + :name 'evil-easymotion-snipe-backward + :pre-hook (save-excursion (call-interactively #'evil-snipe-S)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))) + "SPC" #'avy-goto-char-timer + "/" (evilem-create #'evil-ex-search-next + :pre-hook (save-excursion (call-interactively #'evil-ex-search-forward)) + :bind ((evil-search-wrap))) + "?" (evilem-create #'evil-ex-search-previous + :pre-hook (save-excursion (call-interactively #'evil-ex-search-backward)) + :bind ((evil-search-wrap)))) + + ;; text object plugins + :textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr + :textobj "a" #'evil-inner-arg #'evil-outer-arg + :textobj "B" #'evil-textobj-anyblock-inner-block #'evil-textobj-anyblock-a-block + :textobj "i" #'evil-indent-plus-i-indent #'evil-indent-plus-a-indent + :textobj "k" #'evil-indent-plus-i-indent-up #'evil-indent-plus-a-indent-up + :textobj "j" #'evil-indent-plus-i-indent-up-down #'evil-indent-plus-a-indent-up-down + + ;; evil-snipe + (:after evil-snipe + :map evil-snipe-parent-transient-map + "C-;" (λ! (require 'evil-easymotion) + (call-interactively + (evilem-create #'evil-snipe-repeat + :bind ((evil-snipe-scope 'whole-buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight)))))) + + ;; evil-surround + :v "S" #'evil-surround-region + :o "s" #'evil-surround-edit + :o "S" #'evil-Surround-edit) ;; @@ -95,105 +177,6 @@ :n "gR" #'+eval/buffer :v "gR" #'+eval:replace-region) - (:when (featurep! :feature evil) - :m "]a" #'evil-forward-arg - :m "[a" #'evil-backward-arg - :m "]o" #'outline-next-visible-heading - :m "[o" #'outline-previous-visible-heading - :n "]b" #'next-buffer - :n "[b" #'previous-buffer - :n "zx" #'kill-this-buffer - :n "ZX" #'bury-buffer - :n "gp" #'+evil/reselect-paste - :n "g=" #'widen - :v "g=" #'+evil:narrow-buffer - :nv "g@" #'+evil:apply-macro - :nv "gc" #'evil-commentary - :nv "gx" #'evil-exchange - :nv "C-a" #'evil-numbers/inc-at-pt - :nv "C-S-a" #'evil-numbers/dec-at-pt - :v "gp" #'+evil/paste-preserve-register - :v "@" #'+evil:apply-macro - ;; repeat in visual mode (FIXME buggy) - :v "." #'+evil:apply-macro - ;; don't leave visual mode after shifting - :v "<" #'+evil/visual-dedent ; vnoremap < " #'+evil/visual-indent ; vnoremap > >gv - - ;; window management (prefix "C-w") - (:map evil-window-map - ;; Navigation - "C-h" #'evil-window-left - "C-j" #'evil-window-down - "C-k" #'evil-window-up - "C-l" #'evil-window-right - "C-w" #'other-window - ;; Swapping windows - "H" #'+evil/window-move-left - "J" #'+evil/window-move-down - "K" #'+evil/window-move-up - "L" #'+evil/window-move-right - "C-S-w" #'ace-swap-window - ;; Window undo/redo - "u" #'winner-undo - "C-u" #'winner-undo - "C-r" #'winner-redo - "o" #'doom/window-enlargen - "O" #'doom/window-zoom - ;; Delete window - "c" #'+workspace/close-window-or-workspace - "C-C" #'ace-delete-window) - - ;; Plugins - ;; evil-easymotion - :m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion' - (:after evil-easymotion - :map evilem-map - "a" (evilem-create #'evil-forward-arg) - "A" (evilem-create #'evil-backward-arg) - "s" (evilem-create #'evil-snipe-repeat - :name 'evil-easymotion-snipe-forward - :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) - :bind ((evil-snipe-scope 'buffer) - (evil-snipe-enable-highlight) - (evil-snipe-enable-incremental-highlight))) - "S" (evilem-create #'evil-snipe-repeat - :name 'evil-easymotion-snipe-backward - :pre-hook (save-excursion (call-interactively #'evil-snipe-S)) - :bind ((evil-snipe-scope 'buffer) - (evil-snipe-enable-highlight) - (evil-snipe-enable-incremental-highlight))) - "SPC" #'avy-goto-char-timer - "/" (evilem-create #'evil-ex-search-next - :pre-hook (save-excursion (call-interactively #'evil-ex-search-forward)) - :bind ((evil-search-wrap))) - "?" (evilem-create #'evil-ex-search-previous - :pre-hook (save-excursion (call-interactively #'evil-ex-search-backward)) - :bind ((evil-search-wrap)))) - - ;; text object plugins - :textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr - :textobj "a" #'evil-inner-arg #'evil-outer-arg - :textobj "B" #'evil-textobj-anyblock-inner-block #'evil-textobj-anyblock-a-block - :textobj "i" #'evil-indent-plus-i-indent #'evil-indent-plus-a-indent - :textobj "k" #'evil-indent-plus-i-indent-up #'evil-indent-plus-a-indent-up - :textobj "j" #'evil-indent-plus-i-indent-up-down #'evil-indent-plus-a-indent-up-down - - ;; evil-snipe - (:after evil-snipe - :map evil-snipe-parent-transient-map - "C-;" (λ! (require 'evil-easymotion) - (call-interactively - (evilem-create #'evil-snipe-repeat - :bind ((evil-snipe-scope 'whole-buffer) - (evil-snipe-enable-highlight) - (evil-snipe-enable-incremental-highlight)))))) - - ;; evil-surround - :v "S" #'evil-surround-region - :o "s" #'evil-surround-edit - :o "S" #'evil-Surround-edit) - (:when (featurep! :feature lookup) :nv "K" #'+lookup/documentation :nv "gd" #'+lookup/definition @@ -216,10 +199,13 @@ [delete] #'+snippets/delete-forward-char-or-field))) (:when (featurep! :tools flyspell) + :m "]s" #'evil-next-flyspell-error + :m "[s" #'evil-prev-flyspell-error :m "]S" #'flyspell-correct-word-generic :m "[S" #'flyspell-correct-previous-word-generic (:map flyspell-mouse-map "RET" #'flyspell-correct-word-generic + [return] #'flyspell-correct-word-generic [mouse-1] #'flyspell-correct-word-generic)) (:when (featurep! :tools flycheck) @@ -227,11 +213,12 @@ :m "[e" #'previous-error (:after flycheck :map flycheck-error-list-mode-map - :n "C-n" #'flycheck-error-list-next-error - :n "C-p" #'flycheck-error-list-previous-error - :n "j" #'flycheck-error-list-next-error - :n "k" #'flycheck-error-list-previous-error - :n "RET" #'flycheck-error-list-goto-error)) + :n "C-n" #'flycheck-error-list-next-error + :n "C-p" #'flycheck-error-list-previous-error + :n "j" #'flycheck-error-list-next-error + :n "k" #'flycheck-error-list-previous-error + :n "RET" #'flycheck-error-list-goto-error + :n [return] #'flycheck-error-list-goto-error)) (:when (featurep! :feature workspaces) :n "gt" #'+workspace/switch-right @@ -279,6 +266,7 @@ "C-S-s" (cond ((featurep! :completion helm) #'helm-company) ((featurep! :completion ivy) #'counsel-company)) "C-SPC" #'company-complete-common + "TAB" #'company-complete-common-or-cycle [tab] #'company-complete-common-or-cycle [backtab] #'company-select-previous) (:map company-search-map ; applies to `company-filter-map' too @@ -287,9 +275,11 @@ "C-j" #'company-select-next-or-abort "C-k" #'company-select-previous-or-abort "C-s" (λ! (company-search-abort) (company-filter-candidates)) - [escape] #'company-search-abort) + "ESC" #'company-search-abort) ;; TAB auto-completion in term buffers - :map comint-mode-map [tab] #'company-complete)) + (:map comint-mode-map + "TAB" #'company-complete + [tab] #'company-complete))) (:when (featurep! :completion ivy) (:map (help-mode-map helpful-mode-map) @@ -302,6 +292,7 @@ (:after counsel :map counsel-ag-map "C-SPC" #'ivy-call-and-recenter ; preview + "C-l" #'ivy-done [backtab] #'+ivy/wgrep-occur ; search/replace on results [C-return] (+ivy-do-action! #'+ivy-git-grep-other-window-action)) (:after swiper @@ -327,6 +318,7 @@ "C-s" #'helm-minibuffer-history "C-b" #'backward-word ;; Swap TAB and C-z + "TAB" #'helm-execute-persistent-action [tab] #'helm-execute-persistent-action "C-z" #'helm-select-action) (:after swiper-helm @@ -363,29 +355,31 @@ (:when (featurep! :ui neotree) :after neotree :map neotree-mode-map - :n "g" nil - :n [tab] #'neotree-quick-look - :n [return] #'neotree-enter - :n [backspace] #'evil-window-prev - :n "c" #'neotree-create-node - :n "r" #'neotree-rename-node - :n "d" #'neotree-delete-node - :n "j" #'neotree-next-line - :n "k" #'neotree-previous-line - :n "n" #'neotree-next-line - :n "p" #'neotree-previous-line - :n "h" #'+neotree/collapse-or-up - :n "l" #'+neotree/expand-or-open - :n "J" #'neotree-select-next-sibling-node - :n "K" #'neotree-select-previous-sibling-node - :n "H" #'neotree-select-up-node - :n "L" #'neotree-select-down-node - :n "G" #'evil-goto-line - :n "gg" #'evil-goto-first-line - :n "v" #'neotree-enter-vertical-split - :n "s" #'neotree-enter-horizontal-split - :n "q" #'neotree-hide - :n "R" #'neotree-refresh) + :n "g" nil + :n "TAB" #'neotree-quick-look + :n "RET" #'neotree-enter + :n [tab] #'neotree-quick-look + :n [return] #'neotree-enter + :n "DEL" #'evil-window-prev + :n "c" #'neotree-create-node + :n "r" #'neotree-rename-node + :n "d" #'neotree-delete-node + :n "j" #'neotree-next-line + :n "k" #'neotree-previous-line + :n "n" #'neotree-next-line + :n "p" #'neotree-previous-line + :n "h" #'+neotree/collapse-or-up + :n "l" #'+neotree/expand-or-open + :n "J" #'neotree-select-next-sibling-node + :n "K" #'neotree-select-previous-sibling-node + :n "H" #'neotree-select-up-node + :n "L" #'neotree-select-down-node + :n "G" #'evil-goto-line + :n "gg" #'evil-goto-first-line + :n "v" #'neotree-enter-vertical-split + :n "s" #'neotree-enter-horizontal-split + :n "q" #'neotree-hide + :n "R" #'neotree-refresh) (:when (featurep! :ui popup) :n "C-`" #'+popup/toggle @@ -415,8 +409,9 @@ :nv "N" #'evil-mc-make-and-goto-last-cursor :nv "p" #'evil-mc-make-and-goto-prev-cursor :nv "P" #'evil-mc-make-and-goto-first-cursor + :nv "q" #'evil-mc-undo-all-cursors :nv "t" #'+multiple-cursors/evil-mc-toggle-cursors - :nv "u" #'evil-mc-undo-all-cursors + :nv "u" #'evil-mc-undo-last-added-cursor :nv "z" #'+multiple-cursors/evil-mc-make-cursor-here) (:after evil-mc :map evil-mc-key-map @@ -433,9 +428,10 @@ :nv "C-M-d" #'evil-multiedit-restore (:after evil-multiedit (:map evil-multiedit-state-map - "M-d" #'evil-multiedit-match-and-next - "M-D" #'evil-multiedit-match-and-prev - "RET" #'evil-multiedit-toggle-or-restrict-region) + "M-d" #'evil-multiedit-match-and-next + "M-D" #'evil-multiedit-match-and-prev + "RET" #'evil-multiedit-toggle-or-restrict-region + [return] #'evil-multiedit-toggle-or-restrict-region) (:map (evil-multiedit-state-map evil-multiedit-insert-state-map) "C-n" #'evil-multiedit-next "C-p" #'evil-multiedit-prev))) @@ -467,7 +463,8 @@ (:when (featurep! :tools gist) :after gist :map gist-list-menu-mode-map - :n "RET" #'+gist/open-current + :n "RET" #'+gist/open-current + :n [return] #'+gist/open-current :n "b" #'gist-browse-current-url :n "c" #'gist-add-buffer :n "d" #'gist-kill-current @@ -497,16 +494,16 @@ ;; C-u is used by evil :desc "Universal argument" "u" #'universal-argument - :desc "Window management" "w" #'evil-window-map + :desc "window" "w" evil-window-map + :desc "help" "h" help-map :desc "Toggle last popup" "~" #'+popup/toggle :desc "Find file" "." #'find-file + :desc "Switch buffer" "," #'switch-to-buffer (:when (featurep! :feature workspaces) :desc "Switch workspace buffer" "," #'persp-switch-to-buffer :desc "Switch buffer" "<" #'switch-to-buffer) - (:unless (featurep! :feature workspaces) - :desc "Switch buffer" "," #'switch-to-buffer) :desc "Resume last search" "'" (cond ((featurep! :completion ivy) #'ivy-resume) @@ -520,49 +517,23 @@ (:prefix ("/" . "search") :desc "Jump to symbol across buffers" "I" #'imenu-anywhere :desc "Search buffer" "b" #'swiper - :desc "Search current directory" "d" - (cond ((featurep! :completion helm) #'+helm/project-search-from-cwd) - ((featurep! :completion ivy) #'+ivy/project-search-from-cwd)) + :desc "Search current directory" "d" #'+default/search-from-cwd :desc "Jump to symbol" "i" #'imenu :desc "Jump to link" "l" #'ace-link :desc "Look up online" "o" #'+lookup/online-select - :desc "Search project" "p" - (cond ((featurep! :completion ivy) #'+ivy/project-search) - ((featurep! :completion helm) #'+helm/project-search))) - - (:prefix ("]" . "next") - :desc "Increase text size" "]" #'text-scale-increase - :desc "Next buffer" "b" #'next-buffer - :desc "Next diff Hunk" "d" #'git-gutter:next-hunk - :desc "Next todo" "t" #'hl-todo-next - :desc "Next error" "e" #'next-error - :desc "Next workspace" "w" #'+workspace/switch-right - :desc "Next spelling error" "s" #'evil-next-flyspell-error - :desc "Next spelling correction" "S" #'flyspell-correct-next-word-generic) - - (:prefix ("[" . "previous") - :desc "Decrease text size" "[" #'text-scale-decrease - :desc "Previous buffer" "b" #'previous-buffer - :desc "Previous diff Hunk" "d" #'git-gutter:previous-hunk - :desc "Previous todo" "t" #'hl-todo-previous - :desc "Previous error" "e" #'previous-error - :desc "Previous workspace" "w" #'+workspace/switch-left - :desc "Previous spelling error" "s" #'evil-prev-flyspell-error - :desc "Previous spelling correction" "S" #'flyspell-correct-word-generic) + :desc "Search project" "p" #'+default/search-project) (:when (featurep! :feature workspaces) - (:prefix ([tab] . "workspace") + (:prefix ("TAB" . "workspace") :desc "Display tab bar" "TAB" #'+workspace/display + :desc "Switch workspace" "." #'+workspace/switch-to :desc "New workspace" "n" #'+workspace/new :desc "Load workspace from file" "l" #'+workspace/load - :desc "Load a past session" "L" #'+workspace/load-session :desc "Save workspace to file" "s" #'+workspace/save - :desc "Autosave current session" "S" #'+workspace/save-session - :desc "Switch workspace" "." #'+workspace/switch-to :desc "Delete session" "x" #'+workspace/kill-session :desc "Delete this workspace" "d" #'+workspace/delete :desc "Rename workspace" "r" #'+workspace/rename - :desc "Restore last session" "R" #'+workspace/load-last-session + :desc "Restore last session" "R" #'+workspace/restore-last-session :desc "Next workspace" "]" #'+workspace/switch-right :desc "Previous workspace" "[" #'+workspace/switch-left :desc "Switch to 1st workspace" "1" (λ! (+workspace/switch-to 0)) @@ -578,8 +549,6 @@ (:prefix ("b" . "buffer") :desc "Toggle narrowing" "-" #'doom/clone-and-narrow-buffer - :desc "New empty buffer" "N" #'evil-buffer-new - :desc "Sudo edit this file" "S" #'doom/sudo-this-file :desc "Previous buffer" "[" #'previous-buffer :desc "Next buffer" "]" #'next-buffer (:when (featurep! :feature workspaces) @@ -589,40 +558,44 @@ :desc "Switch buffer" "b" #'switch-to-buffer) :desc "Kill buffer" "k" #'kill-this-buffer :desc "Next buffer" "n" #'next-buffer + :desc "New empty buffer" "N" #'evil-buffer-new :desc "Kill other buffers" "o" #'doom/kill-other-buffers :desc "Previous buffer" "p" #'previous-buffer :desc "Save buffer" "s" #'save-buffer + :desc "Sudo edit this file" "S" #'doom/sudo-this-file :desc "Pop scratch buffer" "x" #'doom/open-scratch-buffer :desc "Bury buffer" "z" #'bury-buffer) (:prefix ("c" . "code") - :desc "Jump to references" "D" #'+lookup/references - :desc "Evaluate & replace region" "E" #'+eval:replace-region - :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines - :desc "Build tasks" "b" #'+eval/build + :desc "Compile project" "c" #'projectile-compile-project :desc "Jump to definition" "d" #'+lookup/definition + :desc "Jump to references" "D" #'+lookup/references :desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region + :desc "Evaluate & replace region" "E" #'+eval:replace-region :desc "Format buffer/region" "f" #'+format/region-or-buffer :desc "Open REPL" "r" #'+eval/open-repl-other-window :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace + :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines :desc "List errors" "x" #'flycheck-list-errors) (:prefix ("f" . "file") - :desc "Find file" "." #'find-file + :desc "Find file" "." (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file) + :desc "Find file in other project" ">" #'doom/browse-in-other-project :desc "Find file in project" "/" #'projectile-find-file - :desc "Sudo find file" ">" #'doom/sudo-find-file - :desc "Find file from here" "?" #'counsel-file-jump - :desc "Browse emacs.d" "E" #'+default/browse-emacsd - :desc "Browse private config" "P" #'+default/browse-config - :desc "Recent project files" "R" #'projectile-recentf - :desc "Delete this file" "X" #'doom/delete-this-file + :desc "Find file in other project" "?" #'doom/find-file-in-other-project :desc "Find other file" "a" #'projectile-find-other-file :desc "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig :desc "Find directory" "d" #'dired :desc "Find file in emacs.d" "e" #'+default/find-in-emacsd - :desc "Find file in private config" "p" #'+default/find-in-config + :desc "Browse emacs.d" "E" #'+default/browse-emacsd + :desc "Find file from here" "f" #'find-file + :desc "Find file in private config" "p" #'doom/find-file-in-private-config + :desc "Browse private config" "P" #'doom/open-private-config :desc "Recent files" "r" #'recentf-open-files + :desc "Recent project files" "R" #'projectile-recentf :desc "Save file" "s" #'save-buffer + :desc "Sudo find file" "S" #'doom/sudo-find-file + :desc "Delete this file" "X" #'doom/delete-this-file :desc "Yank filename" "y" #'+default/yank-buffer-filename) (:prefix ("g" . "git") @@ -673,34 +646,6 @@ :desc "Issue" "i" #'forge-create-issue :desc "Pull request" "p" #'forge-create-pullreq))) - (:prefix ("h" . "help") - :desc "What face" "'" #'doom/what-face - :desc "Describe at point" "." #'helpful-at-point - :desc "Describe active minor modes" ";" #'doom/describe-active-minor-mode - :desc "Open Doom manual" "D" #'doom/open-manual - :desc "Open vanilla sandbox" "E" #'doom/open-vanilla-sandbox - :desc "Describe face" "F" #'describe-face - :desc "Find documentation" "K" #'+lookup/documentation - :desc "Command log" "L" #'global-command-log-mode - :desc "Describe mode" "M" #'describe-mode - :desc "Reload private config" "R" #'doom/reload - :desc "Print Doom version" "V" #'doom/version - :desc "Apropos" "a" #'apropos - :desc "Open Bug Report" "b" #'doom/open-bug-report - :desc "Describe char" "c" #'describe-char - :desc "Describe DOOM module" "d" #'doom/describe-module - :desc "Describe function" "f" #'describe-function - :desc "Emacs help map" "h" help-map - :desc "Info" "i" #'info-lookup-symbol - :desc "Describe key" "k" #'describe-key - :desc "Find library" "l" #'find-library - :desc "View *Messages*" "m" #'view-echo-area-messages - :desc "Toggle profiler" "p" #'doom/toggle-profiler - :desc "Reload theme" "r" #'doom/reload-theme - :desc "Describe DOOM setting" "s" #'doom/describe-setters - :desc "Describe variable" "v" #'describe-variable - :desc "Man pages" "w" #'+default/man-or-woman) - (:prefix ("i" . "insert") :desc "Insert from clipboard" "y" #'+default/yank-pop :desc "Insert from evil register" "r" #'evil-ex-registers @@ -710,10 +655,17 @@ :desc "Open deft" "d" #'deft :desc "Find file in notes" "n" #'+default/find-in-notes :desc "Browse notes" "N" #'+default/browse-notes - :desc "Org capture" "x" #'org-capture) + :desc "Pop scratch buffer" "s" #'doom/open-scratch-buffer + :desc "Org capture" "x" #'org-capture + :desc "Org store link" "l" #'org-store-link) (:prefix ("o" . "open") - :desc "Org agenda" "a" #'org-agenda + :desc "Org agenda" "A" #'org-agenda + (:prefix ("a" . "org agenda") + :desc "Agenda" "a" #'org-agenda + :desc "Todo list" "t" #'org-todo-list + :desc "Tags search" "m" #'org-tags-view + :desc "View search" "v" #'org-search-view) :desc "Default browser" "b" #'browse-url-of-file :desc "Debugger" "d" #'+debug/open :desc "REPL" "r" #'+eval/open-repl-other-window @@ -767,12 +719,15 @@ :desc "List project tasks" "t" #'+default/project-tasks :desc "Invalidate cache" "x" #'projectile-invalidate-cache) - (:prefix ("q" . "quit/restart") + (:prefix ("q" . "session") :desc "Quit Emacs" "q" #'evil-quit-all :desc "Save and quit Emacs" "Q" #'evil-save-and-quit - :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit - :desc "Restart & restore Emacs" "r" #'+workspace/restart-emacs-then-restore - :desc "Restart Emacs" "R" #'restart-emacs) + :desc "Quick save current session" "s" #'doom/quicksave-session + :desc "Restore last session" "l" #'doom/quickload-session + :desc "Save session to file" "S" #'doom/save-session + :desc "Restore session from file" "L" #'doom/load-session + :desc "Restart & restore Emacs" "r" #'doom/restart-and-restore + :desc "Restart Emacs" "R" #'doom/restart) (:when (featurep! :tools upload) (:prefix ("r" . "remote") @@ -790,15 +745,16 @@ :desc "Jump to mode snippet" "/" #'yas-visit-snippet-file :desc "Jump to snippet" "s" #'+snippets/find-file :desc "Browse snippets" "S" #'+snippets/browse - :desc "Reload snippets" "r" #'yas-reload-all)) + :desc "Reload snippets" "r" #'yas-reload-all + :desc "Create temporary snippet" "c" #'aya-create + :desc "Use temporary snippet" "e" #'aya-expand)) (:prefix ("t" . "toggle") :desc "Flyspell" "s" #'flyspell-mode :desc "Flycheck" "f" #'flycheck-mode :desc "Line numbers" "l" #'doom/toggle-line-numbers :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen - :desc "Indent guides" "i" #'highlight-indentation-mode - :desc "Indent guides (column)" "I" #'highlight-indentation-current-column-mode + :desc "Indent guides" "i" #'highlight-indent-guides-mode :desc "Impatient mode" "h" #'+impatient-mode/toggle :desc "Big mode" "b" #'doom-big-font-mode :desc "Evil goggles" "g" #'evil-goggles-mode diff --git a/modules/config/default/+evil.el b/modules/config/default/+evil.el new file mode 100644 index 000000000..5d624abed --- /dev/null +++ b/modules/config/default/+evil.el @@ -0,0 +1,109 @@ +;;; config/default/+evil.el -*- lexical-binding: t; -*- + +(defun +default|disable-delete-selection-mode () + (delete-selection-mode -1)) +(add-hook 'evil-insert-state-entry-hook #'delete-selection-mode) +(add-hook 'evil-insert-state-exit-hook #'+default|disable-delete-selection-mode) + + +;; +;;; Smartparens config + +(when (featurep! +smartparens) + ;; You can disable :unless predicates with (sp-pair "'" nil :unless nil) + ;; And disable :post-handlers with (sp-pair "{" nil :post-handlers nil) + ;; or specific :post-handlers with: + ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) + (after! smartparens + ;; Autopair quotes more conservatively; if I'm next to a word/before another + ;; quote, I likely don't want to open a new pair. + (let ((unless-list '(sp-point-before-word-p + sp-point-after-word-p + sp-point-before-same-p))) + (sp-pair "'" nil :unless unless-list) + (sp-pair "\"" nil :unless unless-list)) + + ;; Expand {|} => { | } + ;; Expand {|} => { + ;; | + ;; } + (dolist (brace '("(" "{" "[")) + (sp-pair brace nil + :post-handlers '(("||\n[i]" "RET") ("| " "SPC")) + ;; I likely don't want a new pair if adjacent to a word or opening brace + :unless '(sp-point-before-word-p sp-point-before-same-p))) + + ;; Major-mode specific fixes + (sp-local-pair '(ruby-mode enh-ruby-mode) "{" "}" + :pre-handlers '(:rem sp-ruby-pre-handler) + :post-handlers '(:rem sp-ruby-post-handler)) + + ;; Don't do square-bracket space-expansion where it doesn't make sense to + (sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode) + "[" nil :post-handlers '(:rem ("| " "SPC"))) + + ;; Reasonable default pairs for HTML-style comments + (sp-local-pair (append sp--html-modes '(markdown-mode gfm-mode)) + "" + :unless '(sp-point-before-word-p sp-point-before-same-p) + :actions '(insert) :post-handlers '(("| " "SPC"))) + + ;; Disable electric keys in C modes because it interferes with smartparens + ;; and custom bindings. We'll do it ourselves (mostly). + (after! cc-mode + (c-toggle-electric-state -1) + (c-toggle-auto-newline -1) + (setq c-electric-flag nil) + (dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177")) + (define-key c-mode-base-map key nil))) + + ;; Expand C-style doc comment blocks. Must be done manually because some of + ;; these languages use specialized (and deferred) parsers, whose state we + ;; can't access while smartparens is doing its thing. + (defun +default-expand-doc-comment-block (&rest _ignored) + (let ((indent (current-indentation))) + (newline-and-indent) + (save-excursion + (newline) + (insert (make-string indent 32) " */") + (delete-char 2)))) + (sp-local-pair + '(js2-mode typescript-mode rjsx-mode rust-mode c-mode c++-mode objc-mode + csharp-mode java-mode php-mode css-mode scss-mode less-css-mode + stylus-mode) + "/*" "*/" + :actions '(insert) + :post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET") (+default-expand-doc-comment-block "*"))) + + ;; Highjacks backspace to: + ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) + ;; b) delete space-indented `tab-width' steps at a time + ;; c) close empty multiline brace blocks in one step: + ;; { + ;; | + ;; } + ;; becomes {|} + ;; d) refresh smartparens' :post-handlers, so SPC and RET expansions work + ;; even after a backspace. + ;; e) properly delete smartparen pairs when they are encountered, without + ;; the need for strict mode. + ;; f) do none of this when inside a string + (advice-add #'delete-backward-char :override #'+default*delete-backward-char) + + ;; Makes `newline-and-indent' continue comments (and more reliably) + (advice-add #'newline-and-indent :around #'+default*newline-indent-and-continue-comments))) + + +;; +;;; Keybindings + +;; This section is dedicated to "fixing" certain keys so that they behave +;; sensibly (and consistently with similar contexts). + +;; Make SPC u SPC u [...] possible (#747) +(map! :map universal-argument-map + :prefix doom-leader-key "u" #'universal-argument-more + :prefix doom-leader-alt-key "u" #'universal-argument-more) + +(when (featurep! +bindings) + (load! "+evil-bindings")) diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el index 654f40f41..dcdce7fb3 100644 --- a/modules/config/default/autoload/default.el +++ b/modules/config/default/autoload/default.el @@ -34,18 +34,6 @@ (defun +default/find-in-notes () (interactive) (doom-project-find-file org-directory)) -;;;###autoload -(defun +default/find-in-config () - "Open a file somewhere in `doom-private-dir' via a fuzzy filename search." - (interactive) - (doom-project-find-file doom-private-dir)) - -;;;###autoload -(defun +default/browse-config () - "Browse the files in `doom-private-dir'." - (interactive) - (doom-project-browse doom-private-dir)) - ;;;###autoload (defun +default/compile (arg) "Runs `compile' from the root of the current project. @@ -230,3 +218,34 @@ possible, or just one char if that's not possible." ((doom--backward-delete-whitespace-to-column))))))) ;; Otherwise, do simple deletion. ((delete-char (- n) killflag)))) + +;;;###autoload +(defun +default/search-from-cwd (&optional arg) + "Conduct a text search in files under the current folder. +If prefix ARG is set, prompt for a directory to search from." + (interactive "P") + (let ((default-directory + (if arg + (read-directory-name "Switch to project: " default-directory) + default-directory))) + (call-interactively + (cond ((featurep! :completion ivy) #'+ivy/project-search-from-cwd) + ((featurep! :completion helm) #'+helm/project-search-from-cwd) + (#'projectile-grep))))) + +;;;###autoload +(defun +default/search-project (&optional arg) + "Conduct a text search in files under the project root. +If prefix ARG is set, prompt for a project to search from." + (interactive "P") + (let ((default-directory + (if arg + (if-let* ((projects (projectile-relevant-known-projects))) + (completing-read "Switch to project: " projects + nil t nil nil (doom-project-root)) + (user-error "There are no known projects")) + default-directory))) + (call-interactively + (cond ((featurep! :completion ivy) #'+ivy/project-search) + ((featurep! :completion helm) #'+helm/project-search) + (#'rgrep))))) diff --git a/modules/config/default/autoload/evil.el b/modules/config/default/autoload/evil.el deleted file mode 100644 index a813659a1..000000000 --- a/modules/config/default/autoload/evil.el +++ /dev/null @@ -1,37 +0,0 @@ -;; config/default/autoload/evil.el -*- lexical-binding: t; -*- -;;;###if (featurep! :feature evil) - -;;;###autoload (autoload '+default:multi-next-line "config/default/autoload/evil" nil t) -(evil-define-motion +default:multi-next-line (count) - "Move down 6 lines." - :type line - (let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode 'magit-mode)))) - (evil-line-move (* 6 (or count 1))))) - -;;;###autoload (autoload '+default:multi-previous-line "config/default/autoload/evil" nil t) -(evil-define-motion +default:multi-previous-line (count) - "Move up 6 lines." - :type line - (let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode 'magit-mode)))) - (evil-line-move (- (* 6 (or count 1)))))) - -;;;###autoload (autoload '+default:cd "config/default/autoload/evil" nil t) -(evil-define-command +default:cd () - "Change `default-directory' with `cd'." - (interactive "") - (cd input)) - -;;;###autoload (autoload '+default:kill-all-buffers "config/default/autoload/evil" nil t) -(evil-define-command +default:kill-all-buffers (&optional bang) - "Kill all buffers. If BANG, kill current session too." - (interactive "") - (if bang - (+workspace/kill-session) - (doom/kill-all-buffers))) - -;;;###autoload (autoload '+default:kill-matching-buffers "config/default/autoload/evil" nil t) -(evil-define-command +default:kill-matching-buffers (&optional bang pattern) - "Kill all buffers matching PATTERN regexp. If BANG, only match project -buffers." - (interactive "") - (doom/kill-matching-buffers pattern bang)) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index c1263ac10..76cd083f9 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -12,7 +12,7 @@ ;; -;; Reasonable defaults +;;; Reasonable defaults (after! epa (setq epa-file-encrypt-to @@ -27,113 +27,12 @@ epa-pinentry-mode 'loopback)) -(when (featurep! +smartparens) - ;; You can disable :unless predicates with (sp-pair "'" nil :unless nil) - ;; And disable :post-handlers with (sp-pair "{" nil :post-handlers nil) - ;; or specific :post-handlers with: - ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) - (after! smartparens - ;; Autopair quotes more conservatively; if I'm next to a word/before another - ;; quote, I likely don't want to open a new pair. - (let ((unless-list '(sp-point-before-word-p - sp-point-after-word-p - sp-point-before-same-p))) - (sp-pair "'" nil :unless unless-list) - (sp-pair "\"" nil :unless unless-list)) - - ;; Expand {|} => { | } - ;; Expand {|} => { - ;; | - ;; } - (dolist (brace '("(" "{" "[")) - (sp-pair brace nil - :post-handlers '(("||\n[i]" "RET") ("| " "SPC")) - ;; I likely don't want a new pair if adjacent to a word or opening brace - :unless '(sp-point-before-word-p sp-point-before-same-p))) - - ;; Major-mode specific fixes - (sp-local-pair '(ruby-mode enh-ruby-mode) "{" "}" - :pre-handlers '(:rem sp-ruby-pre-handler) - :post-handlers '(:rem sp-ruby-post-handler)) - - ;; Don't do square-bracket space-expansion where it doesn't make sense to - (sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode) - "[" nil :post-handlers '(:rem ("| " "SPC"))) - - ;; Reasonable default pairs for HTML-style comments - (sp-local-pair (append sp--html-modes '(markdown-mode gfm-mode)) - "" :actions '(insert) :post-handlers '(("| " "SPC"))) - - ;; Disable electric keys in C modes because it interferes with smartparens - ;; and custom bindings. We'll do it ourselves (mostly). - (after! cc-mode - (c-toggle-electric-state -1) - (c-toggle-auto-newline -1) - (setq c-tab-always-indent nil - c-electric-flag nil) - (dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177")) - (define-key c-mode-base-map key nil))) - - ;; Expand C-style doc comment blocks. Must be done manually because some of - ;; these languages use specialized (and deferred) parsers, whose state we - ;; can't access while smartparens is doing its thing. - (defun +default-expand-doc-comment-block (&rest _ignored) - (let ((indent (current-indentation))) - (newline-and-indent) - (save-excursion - (newline) - (insert (make-string indent 32) " */") - (delete-char 2)))) - (sp-local-pair - '(js2-mode typescript-mode rjsx-mode rust-mode c-mode c++-mode objc-mode - csharp-mode java-mode php-mode css-mode scss-mode less-css-mode - stylus-mode) - "/*" "*/" - :actions '(insert) - :post-handlers '(("| " "SPC") ("|\n*/[i][d-2]" "RET") (+default-expand-doc-comment-block "*"))) - - ;; Highjacks backspace to: - ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) - ;; b) delete space-indented `tab-width' steps at a time - ;; c) close empty multiline brace blocks in one step: - ;; { - ;; | - ;; } - ;; becomes {|} - ;; d) refresh smartparens' :post-handlers, so SPC and RET expansions work - ;; even after a backspace. - ;; e) properly delete smartparen pairs when they are encountered, without - ;; the need for strict mode. - ;; f) do none of this when inside a string - (advice-add #'delete-backward-char :override #'+default*delete-backward-char) - - ;; Makes `newline-and-indent' continue comments (and more reliably) - (advice-add #'newline-and-indent :around #'+default*newline-indent-and-continue-comments))) - - ;; -;; Keybinding fixes +;;; Keybinding fixes ;; This section is dedicated to "fixing" certain keys so that they behave ;; sensibly (and consistently with similar contexts). -;; Make SPC u SPC u [...] possible (#747) -(map! :map universal-argument-map - :prefix doom-leader-key "u" #'universal-argument-more - :prefix doom-leader-alt-key "u" #'universal-argument-more) - -(defun +default|setup-input-decode-map () - "Ensure TAB and [tab] are treated the same in TTY Emacs." - (define-key input-decode-map (kbd "TAB") [tab])) -(add-hook 'tty-setup-hook #'+default|setup-input-decode-map) - -;; A Doom convention where C-s on popups and interactive searches will invoke -;; ivy/helm for their superior filtering. -(define-key! :keymaps +default-minibuffer-maps - "C-s" (if (featurep! :completion ivy) - #'counsel-minibuffer-history - #'helm-minibuffer-history)) - ;; Consistently use q to quit windows (after! tabulated-list (define-key tabulated-list-mode-map "q" #'quit-window)) @@ -171,7 +70,7 @@ :n "s-/" #'evil-commentary-line :v "s-/" #'evil-commentary :gni [s-return] #'+default/newline-below - :gni [s-S-return] #'+default/newline-above + :gni [S-s-return] #'+default/newline-above :gi [s-backspace] #'doom/backward-kill-to-bol-and-indent :gi [s-left] #'doom/backward-to-bol-or-indent :gi [s-right] #'doom/forward-to-last-non-comment-or-eol @@ -181,7 +80,48 @@ ;; -;; Doom's keybinding scheme +;;; Keybind schemes + +;; Custom help keys -- these aren't under `+bindings' because they ought to be +;; universal. +(map! :map help-map + "'" #'describe-char + "a" #'apropos ; replaces `apropos-command' + "A" #'doom/describe-autodefs + "B" #'doom/open-bug-report + "C-c" #'describe-coding-system ; replaces `describe-copying' b/c not useful + "d" #'doom/describe-module ; replaces `apropos-documentation' b/c `apropos' covers this + "D" #'doom/open-manual + "E" #'doom/open-vanilla-sandbox + "F" #'describe-face ; replaces `Info-got-emacs-command-node' b/c redundant w/ `Info-goto-node' + "h" #'doom/describe-symbol ; replaces `view-hello-file' b/c annoying + "C-k" #'describe-key-briefly + "L" #'global-command-log-mode ; replaces `describe-language-environment' b/c remapped to C-l + "C-l" #'describe-language-environment + "M" #'doom/describe-active-minor-mode + "C-m" #'info-emacs-manual + "n" #'doom/open-news ; replaces `view-emacs-news' b/c it's on C-n too + "O" #'+lookup/online + "p" #'doom/describe-package ; replaces `finder-by-keyword' + "P" #'find-library ; replaces `describe-package' b/c redundant w/ `doom/describe-package' + "r" nil ; replaces `info-emacs-manual' b/c it's on C-m now + (:prefix "r" + "r" #'doom/reload + "t" #'doom/reload-theme + "p" #'doom/reload-packages + "f" #'doom/reload-font + "P" #'doom/reload-project) + "T" #'doom/toggle-profiler + "V" #'set-variable + "C-v" #'doom/version + "W" #'+default/man-or-woman) + +(after! which-key + (which-key-add-key-based-replacements "C-h r" "reload") + (when (featurep 'evil) + (which-key-add-key-based-replacements (concat doom-leader-key " r") "reload") + (which-key-add-key-based-replacements (concat doom-leader-alt-key " r") "reload"))) + (when (featurep! +bindings) ;; Make M-x harder to miss @@ -189,6 +129,13 @@ "M-x" #'execute-extended-command "A-x" #'execute-extended-command) + ;; A Doom convention where C-s on popups and interactive searches will invoke + ;; ivy/helm for their superior filtering. + (define-key! :keymaps +default-minibuffer-maps + "C-s" (if (featurep! :completion ivy) + #'counsel-minibuffer-history + #'helm-minibuffer-history)) + ;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation. ;; Pressing it again will send you to the true bol. Same goes for C-e, except ;; it will ignore comments+trailing whitespace before jumping to eol. @@ -199,8 +146,12 @@ :gni [M-return] #'+default/newline-below :gni [M-S-return] #'+default/newline-above :gni [C-return] #'+default/newline-below - :gni [C-S-return] #'+default/newline-above) + :gni [C-S-return] #'+default/newline-above)) - (if (featurep 'evil) - (load! "+evil-bindings") - (load! "+emacs-bindings"))) + +;; +;;; Bootstrap configs + +(if (featurep 'evil) + (load! "+evil") + (load! "+emacs")) diff --git a/modules/config/default/packages.el b/modules/config/default/packages.el new file mode 100644 index 000000000..54cfa79b1 --- /dev/null +++ b/modules/config/default/packages.el @@ -0,0 +1,6 @@ +;; -*- no-byte-compile: t; -*- +;;; config/default/packages.el + +(unless (featurep! :feature evil) + (package! winum) + (package! expand-region)) diff --git a/modules/editor/fold/autoload/evil.el b/modules/editor/fold/autoload/evil.el index c6190807b..3a16c060e 100644 --- a/modules/editor/fold/autoload/evil.el +++ b/modules/editor/fold/autoload/evil.el @@ -45,11 +45,11 @@ Targets `vimmish-fold', `hideshow' and `outline' folds." (interactive) (save-excursion (cond ((+fold--vimish-fold-p) (vimish-fold-toggle)) - ((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding))) ((+fold--outline-fold-p) (cl-letf (((symbol-function #'outline-hide-subtree) (symbol-function #'outline-hide-entry))) - (outline-toggle-children)))))) + (outline-toggle-children))) + ((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding)))))) ;;;###autoload (defun +fold/open () @@ -59,10 +59,10 @@ Targets `vimmish-fold', `hideshow' and `outline' folds." (interactive) (save-excursion (cond ((+fold--vimish-fold-p) (vimish-fold-unfold)) - ((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block))) ((+fold--outline-fold-p) (outline-show-children) - (outline-show-entry))))) + (outline-show-entry)) + ((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block)))))) ;;;###autoload (defun +fold/close () diff --git a/modules/editor/format/autoload/settings.el b/modules/editor/format/autoload/settings.el index 31f0988b6..669a3f1a3 100644 --- a/modules/editor/format/autoload/settings.el +++ b/modules/editor/format/autoload/settings.el @@ -34,16 +34,14 @@ (insert input) (condition-case e (progn - (when doom-debug-mode - (message "formatter (commandp) %s" #',formatter)) + (doom-log "formatter (commandp) %s" #',formatter) (call-interactively #',formatter) (list nil "")) (error (list t (error-message-string e)))))))) (defun +format--make-function (formatter &rest _) `(progn - (when doom-debug-mode - (message "formatter (functionp) %s" #',formatter)) + (doom-log "formatter (functionp) %s" #',formatter) (format-all-buffer-thunk #',formatter))) (defun +format--make-shell-command (command ok-statuses error-regexp) @@ -66,8 +64,7 @@ ((stringp this) (push this subargs)))) (setq args (append subargs args))))))) - (when doom-debug-mode - (message "formatter (arglist) %s" args)) + (doom-log "formatter (arglist) %s" args) (if ,(and (or ok-statuses error-regexp) t) (apply #'format-all-buffer-hard ',ok-statuses ,error-regexp diff --git a/modules/editor/multiple-cursors/config.el b/modules/editor/multiple-cursors/config.el index 8f70046f3..86ec2bb0c 100644 --- a/modules/editor/multiple-cursors/config.el +++ b/modules/editor/multiple-cursors/config.el @@ -17,6 +17,7 @@ (defvar evil-mc-key-map (make-sparse-keymap)) :config (global-evil-mc-mode +1) + (setq evil-mc-enable-bar-cursor (not (or IS-MAC IS-WINDOWS))) (after! smartparens ;; Make evil-mc cooperate with smartparens better @@ -56,7 +57,7 @@ :evil-mc t)) -(after! multiple-cursors +(after! multiple-cursors-core (setq mc/list-file (concat doom-etc-dir "mc-lists.el")) ;; TODO multiple-cursors config for Emacs users? diff --git a/modules/emacs/dired/config.el b/modules/emacs/dired/config.el index eb5eb76fb..052bfcf66 100644 --- a/modules/emacs/dired/config.el +++ b/modules/emacs/dired/config.el @@ -17,17 +17,18 @@ image-dired-temp-image-file (concat image-dired-dir "temp-image") image-dired-temp-rotate-image-file (concat image-dired-dir "temp-rotate-image")) :config - (setq dired-listing-switches "-aBhl --group-directories-first") - - (when IS-BSD - ;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq - ;; dired-use-ls-dired nil)' to your config to suppress the Dired warning - ;; when not using GNU ls. We must look for `gls' after - ;; `exec-path-from-shell' was initialized to make sure that `gls' is in - ;; `exec-path' - (if-let* ((gls (executable-find "gls"))) - (setq insert-directory-program gls) - (message "Cannot find `gls` (GNU ls). Install coreutils via your system package manager"))) + (let ((args (list "-aBhl" "--group-directories-first"))) + (when IS-BSD + ;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq + ;; dired-use-ls-dired nil)' to your config to suppress the Dired warning + ;; when not using GNU ls. We must look for `gls' after + ;; `exec-path-from-shell' was initialized to make sure that `gls' is in + ;; `exec-path' + (if-let* ((gls (executable-find "gls"))) + (setq insert-directory-program gls) + (setq args (delete "--group-directories-first" args)) + (message "Cannot find `gls` (GNU ls). Install coreutils via your system package manager"))) + (setq dired-listing-switches (string-join args " "))) (defun +dired|sort-directories-first () "List directories first in dired buffers." @@ -91,6 +92,7 @@ ranger-excluded-extensions '("mkv" "iso" "mp4") ranger-deer-show-details nil ranger-max-preview-size 10 + ranger-show-literal nil dired-omit-verbose nil)) diff --git a/modules/emacs/electric/autoload.el b/modules/emacs/electric/autoload.el index 3dbe4ec2f..f4e33728b 100644 --- a/modules/emacs/electric/autoload.el +++ b/modules/emacs/electric/autoload.el @@ -17,8 +17,10 @@ Enables `electric-indent-local-mode' in MODES. (unintern fn nil)) ((fset fn (lambda () - (cl-destructuring-bind (&key chars words) plist - (electric-indent-local-mode +1) - (if chars (setq electric-indent-chars chars)) - (if words (setq +electric-indent-words words))))) + (when (eq major-mode mode) + (setq-local electric-indent-inhibit nil) + (cl-destructuring-bind (&key chars words) plist + (electric-indent-local-mode +1) + (if chars (setq electric-indent-chars chars)) + (if words (setq +electric-indent-words words)))))) (add-hook hook fn)))))) diff --git a/modules/emacs/eshell/config.el b/modules/emacs/eshell/config.el index 7612d941b..2665b6df9 100644 --- a/modules/emacs/eshell/config.el +++ b/modules/emacs/eshell/config.el @@ -107,7 +107,7 @@ You should use `det-eshell-alias!' to change this.") ;; Visual commands require a proper terminal. Eshell can't handle that, so ;; it delegates these commands to a term buffer. (after! em-term - (dolist (cmd '("tmux" "htop" "bash" "zsh" "fish" "vim" "nvim" "ncmpcpp")) + (dolist (cmd '("tmux" "htop" "vim" "nvim" "ncmpcpp")) (add-to-list 'eshell-visual-commands cmd))) (defun +eshell|init-aliases () diff --git a/modules/feature/eval/autoload/eval.el b/modules/feature/eval/autoload/eval.el index 2a1738cd9..d7f8df29c 100644 --- a/modules/feature/eval/autoload/eval.el +++ b/modules/feature/eval/autoload/eval.el @@ -17,6 +17,14 @@ (funcall runner beg end) (quickrun-region beg end)))) +;;;###autoload +(defun +eval/line-or-region () + "Evaluate the current line or selected region." + (interactive) + (if (use-region-p) + (call-interactively #'+eval/region) + (+eval/region (line-beginning-position) (line-end-position)))) + ;;;###autoload (defun +eval/buffer-or-region () "Evaluate the whole buffer." diff --git a/modules/feature/evil/+commands.el b/modules/feature/evil/+commands.el index bfe2b4342..82dedee36 100644 --- a/modules/feature/evil/+commands.el +++ b/modules/feature/evil/+commands.el @@ -1,9 +1,5 @@ ;;; feature/evil/+commands.el -*- lexical-binding: t; -*- -(evil-define-command +evil:cleanup-session (bang) - (interactive "") - (doom/cleanup-session bang)) - (evil-define-operator +evil:open-scratch-buffer (bang) (interactive "") (doom/open-scratch-buffer bang)) @@ -46,14 +42,30 @@ This command understands vim file modifiers (like %:p:h). See (interactive "") (reverse-region beg end)) +(evil-define-command +evil:cd (&optional path) + "Change `default-directory' with `cd'." + (interactive "") + (let ((path (or path "~"))) + (cd path) + (message "Changed directory to '%s'" (abbreviate-file-name (expand-file-name path))))) + +(evil-define-command +evil:kill-all-buffers (&optional bang) + "Kill all buffers. If BANG, kill current session too." + (interactive "") + (if (and bang (fboundp '+workspace/kill-session)) + (+workspace/kill-session) + (doom/kill-all-buffers))) + +(evil-define-command +evil:kill-matching-buffers (&optional bang pattern) + "Kill all buffers matching PATTERN regexp. If BANG, only match project +buffers." + (interactive "") + (doom/kill-matching-buffers pattern bang)) + ;; ;; Commands -;;; these are defined in feature/evil -;;(evil-ex-define-cmd "al[ign]" #'+evil:align) -;;(evil-ex-define-cmd "g[lobal]" #'+evil:global) - ;;; Custom commands ;; Editing (evil-ex-define-cmd "@" #'+evil:macro-on-all-lines) ; TODO Test me @@ -94,18 +106,18 @@ This command understands vim file modifiers (like %:p:h). See (evil-ex-define-cmd "grevert" #'git-gutter:revert-hunk) ;;; Dealing with buffers -(evil-ex-define-cmd "clean[up]" #'+evil:cleanup-session) (evil-ex-define-cmd "k[ill]" #'doom/kill-this-buffer) -(evil-ex-define-cmd "k[ill]all" #'+default:kill-all-buffers) -(evil-ex-define-cmd "k[ill]m" #'+default:kill-matching-buffers) +(evil-ex-define-cmd "k[ill]all" #'+evil:kill-all-buffers) +(evil-ex-define-cmd "k[ill]m" #'+evil:kill-matching-buffers) (evil-ex-define-cmd "k[ill]o" #'doom/kill-other-buffers) +(evil-ex-define-cmd "k[ill]b" #'doom/kill-buried-buffers) (evil-ex-define-cmd "l[ast]" #'doom/popup-restore) (evil-ex-define-cmd "m[sg]" #'view-echo-area-messages) (evil-ex-define-cmd "pop[up]" #'doom/popup-this-buffer) ;;; Project navigation (evil-ex-define-cmd "a" #'projectile-find-other-file) -(evil-ex-define-cmd "cd" #'+default:cd) +(evil-ex-define-cmd "cd" #'+evil:cd) (evil-ex-define-cmd "pwd" #'+evil:pwd) (cond ((featurep! :completion ivy) @@ -135,7 +147,7 @@ This command understands vim file modifiers (like %:p:h). See ;;; Project tools (evil-ex-define-cmd "compile" #'+evil:compile) (evil-ex-define-cmd "mak[e]" #'+evil:make) -(evil-ex-define-cmd "debug" #'+debug/run) +;; (evil-ex-define-cmd "debug" #'+debug/run) (evil-ex-define-cmd "er[rors]" #'flycheck-list-errors) ;;; File operations @@ -145,8 +157,8 @@ This command understands vim file modifiers (like %:p:h). See ;;; Sessions/tabs (evil-ex-define-cmd "sclear" #'+workspace/kill-session) -(evil-ex-define-cmd "sl[oad]" #'+workspace:load-session) -(evil-ex-define-cmd "ss[ave]" #'+workspace:save-session) +(evil-ex-define-cmd "sl[oad]" #'doom/quickload-session) +(evil-ex-define-cmd "ss[ave]" #'doom/quicksave-session) (evil-ex-define-cmd "tabc[lose]" #'+workspace:delete) (evil-ex-define-cmd "tabclear" #'doom/kill-all-buffers) (evil-ex-define-cmd "tabl[ast]" #'+workspace/switch-to-last) diff --git a/modules/feature/evil/+everywhere.el b/modules/feature/evil/+everywhere.el index 31abcf6a1..f52e31202 100644 --- a/modules/feature/evil/+everywhere.el +++ b/modules/feature/evil/+everywhere.el @@ -163,8 +163,7 @@ variable for an explanation of the defaults (in comments). See (defun +evil-collection-init (module) (unless (memq (or (car-safe module) module) +evil-collection-disabled-list) - (when doom-debug-mode - (message "Loaded evil-collection-%s" (or (car-safe module) module))) + (doom-log "Initialized evil-collection-%s" (or (car-safe module) module)) (with-demoted-errors "evil-collection error: %s" (evil-collection-init (list module))))) @@ -195,6 +194,10 @@ variable for an explanation of the defaults (in comments). See "]l" #'forward-button "[l" #'backward-button)) +(evil-define-key* 'normal process-menu-mode-map + "q" #'kill-this-buffer + "d" #'process-menu-delete-process) + ;; Load the rest (dolist (mode evil-collection-mode-list) (dolist (req (or (cdr-safe mode) (list mode))) diff --git a/modules/feature/evil/README.org b/modules/feature/evil/README.org index b3b089d1d..a39ad4af9 100644 --- a/modules/feature/evil/README.org +++ b/modules/feature/evil/README.org @@ -4,26 +4,25 @@ #+STARTUP: inlineimages * Table of Contents :TOC_3:noexport: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] - - [[Hacks][Hacks]] -- [[Prerequisites][Prerequisites]] -- [[Features][Features]] - - [[Ported vim plugins][Ported vim plugins]] - - [[Custom Text Objects][Custom Text Objects]] - - [[Custom Ex Commands][Custom Ex Commands]] - - [[A hybrid code-folding system][A hybrid code-folding system]] - - [[Differences from vim][Differences from vim]] -- [[Configuration][Configuration]] - - [[Removing evil-mode][Removing evil-mode]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + - [[#ported-vim-plugins][Ported vim plugins]] + - [[#custom-text-objects][Custom Text Objects]] + - [[#custom-ex-commands][Custom Ex Commands]] +- [[#configuration][Configuration]] + - [[#removing-evil-mode][Removing evil-mode]] + - [[#restoring-old-substitution-behavior-on-ss][Restoring old substitution behavior on s/S]] * Description This holy module brings the vim experience to Emacs. ** Module Flags -+ =+everywhere= Enables evilified keybinds everywhere possible, utilizing the - ~evil-collection~ plugin. ++ =+everywhere= Enables evilified keybinds everywhere possible. Uses the + [[https://github.com/emacs-evil/evil-collection][evil-collection]] plugin as a foundation. ** Plugins + [[https://github.com/emacs-evil/evil][evil]] @@ -48,10 +47,9 @@ This holy module brings the vim experience to Emacs. + When a window is split, the new window will be focused. + The o/O keys will respect and continue commented lines (can be disabled by setting ~+evil-want-o/O-to-continue-comments~ to ~nil~). -+ From visual mode, =*= and =#= will search for the current selection instead of ++ In visual mode, =*= and =#= will search for the current selection instead of the word-at-point. -+ The ~:g[lobal]~ ex command has been modified to highlight matches - incrementally. ++ The ~:g[lobal]~ ex command has been modified to highlight matches. + More of vim's filename modifiers are supported in ex commands (like ~:p~, ~:p:h~ or ~:t~) than vanilla evil-mode offers. + A custom filename modifier is available in Doom: ~:P~, which expands to the @@ -64,64 +62,115 @@ This module has no external prerequisites. ** Ported vim plugins The following vim plugins have been ported to evil: -| Vim Plugin | Emacs Plugin | Keybind(s) | -|-----------------------+--------------------------------+---------------------| -| vim-commentary | evil-commentary | omap =gc= | -| vim-easymotion | evil-easymotion | omap =gs= | -| vim-seek or vim-sneak | evil-snipe | mmap =s=/=S= | -| vim-surround | evil-embrace and evil-surround | vmap =S=, omap =ys= | +| Vim Plugin | Emacs Plugin | Keybind(s) | +|-----------------------+--------------------------------+--------------------------------------| +| vim-commentary | evil-commentary | omap =gc= | +| vim-easymotion | evil-easymotion | omap =gs= | +| vim-seek or vim-sneak | evil-snipe | mmap =s=/=S=, omap =z=/=Z= & =x=/=x= | +| vim-surround | evil-embrace and evil-surround | vmap =S=, omap =ys= | In other modules: -+ The tools/neotree & tools/treemacs modules contain a =NERDTree= equivalent. -+ The editor/multiple-cursors module contains: - + ~vim-multiedit~ => evil-multiedit - + ~vim-multiple-cursors~ => evil-mc ++ The tools/neotree & tools/treemacs modules provide a =NERDTree= equivalent. ++ The editor/multiple-cursors module contains functionality equal to the + following vim plugins: + + evil-multiedit => vim-multiedit + + evil-mc => vim-multiple-cursors ** Custom Text Objects -+ A list of new text objects: - + Blocks: ~B~ (from ~evil-textobj-anyblock~) - + Args: ~a~ (from ~evil-args~) - + Indentation: ~i~ / ~I~ / ~J~ (from ~evil-indent-plus~) +This module provides a couple extra text objects, along with the built-in ones. +For posterity, here are the built-in ones: + ++ =w W= words ++ =s= sentences ++ =p= paragraphs ++ =b= parenthesized blocks ++ =b ( ) { } [ ] < >= braces, parentheses and brackets ++ =' " `= quotes ++ =t= tags ++ =o= symbols + +And these are text objects added by this module: + ++ =B= any block delimited by braces, parentheses or backets (provided by + ~evil-textobj-anyblock~) ++ =a= C-style fucntion arguments (provided by ~evil-args~) ++ =i I J= By indentation (I includes on line above and J includes one line + below) (provided by ~evil-indent-plus~) ++ =x= XML attributes (provided by ~exato~) ** Custom Ex Commands -| Ex Command | Description | -|----------------------+----------------------------------------------------------------------------------| -| ~:al[ign][!] REGEXP~ | Align text to the first match of REGEXP. If BANG, align all matches on each line | -| ~:mv[!] NEWPATH~ | Move the current file to NEWPATH | -| ~:cp[!] NEWPATH~ | Copy the current file to NEWPATH | -| ~:rm[!] [PATH]~ | Delete the current buffer's file and buffer | - -** A hybrid code-folding system -This module combines ~vimish-fold~ and ~hideshow~. The former allows arbitrary -folds and the latter allows folds on markers and indentation. Together, they -create a more consistent (and feature-complete) code-folding system. - -Most vim folding keys should work, e.g. =zr=, =zm=, =za=, =zo=, etc. - -** Differences from vim -+ Column-wise ranges in ex commands are enabled by default. i.e. the range in - =:'<,'>s/a/b= will only affects the visual selection, not full lines (see - ~evil-ex-visual-char-range~). -+ =:g= will incrementally highlight buffer matches. +| Ex Command | Description | +|----------------------+--------------------------------------------------------------------------------------| +| ~:@~ | Apply macro on selected lines | +| ~:ag[!] REGEXP~ | Perform a project search with ag | +| ~:agcwd[!] REGEXP~ | Perform a project search with ag | +| ~:al[ign] REGEXP~ | Align text that matches REGEXP | +| ~:al[ign][!] REGEXP~ | Align text to the first match of REGEXP. If BANG, align all matches on each line | +| ~:cp[!] NEWPATH~ | Copy the current file to NEWPATH | +| ~:dash QUERY~ | Look up QUERY (or the symbol at point) in dash docsets | +| ~:dehtml [INPUT]~ | HTML decode selected text / inserts result if INPUT is given | +| ~:enhtml [INPUT]~ | HTML encode selected text / inserts result if INPUT is given | +| ~:grep[!]~ | Perform a project search with git-grep | +| ~:grepcwd[!]~ | Perform a project search with git-grep | +| ~:iedit REGEXP~ | Invoke iedit on all matches for REGEXP | +| ~:k[ill]all[!]~ | Kill all buffers (if BANG, affect buffer across workspaces) | +| ~:k[ill]b~ | Kill all buried buffers | +| ~:k[ill]m[!] REGEXP~ | Kill buffers whose name matches REGEXP (if BANG, affect buffers across workspaces) | +| ~:k[ill]o~ | Kill all other buffers besides the selected one | +| ~:k[ill]~ | Kill the current buffer | +| ~:lo[okup] QUERY~ | Look up QUERY on an online search engine | +| ~:mc REGEXP~ | Invoke multiple cursors on all matches for REGEXP | +| ~:mv[!] NEWPATH~ | Move the current file to NEWPATH | +| ~:na[rrow]~ | Narrow the buffer to the selection | +| ~:pad~ | Open a scratch pad for running code quickly | +| ~:pt[!]~ | Perform a project search with pt | +| ~:ptcwd[!]~ | Perform a project search with pt | +| ~:ral[ign] REGEXP~ | Right-align text that matches REGEXP | +| ~:repl~ | Open a REPL and/or copy the current selection to it | +| ~:retab~ | Convert indentation to the default within the selection | +| ~:rev[erse]~ | Reverse the selected lines | +| ~:rg[!]~ | Perform a project search with ripgrep | +| ~:rgcwd[!]~ | Perform a project search with rigprep | +| ~:rm[!] [PATH]~ | Delete the current buffer's file and buffer | +| ~:tcd[!]~ | Send =cd X= to tmux. X = the project root if BANG, X = ~default-directory~ otherwise | * Configuration ** Removing evil-mode You must do two things to remove Evil: 1. Remove =:feature evil= from =~/.doom.d/init.el=, -2. Run ~bin/doom refresh~ to clean up lingering dependencies and refresh yuor +2. Run ~doom refresh~ to clean up lingering dependencies and refresh yuor autoloads files. -3. [OPTIONAL] You may want a new ~doom-leader-alt-key~ and - ~doom-localleader-alt-key~. By default, these are bound to =M-SPC= and =M-SPC - m=. +3. [OPTIONAL] You may want to assign new values to ~doom-leader-alt-key~ and + ~doom-localleader-alt-key~. These are bound to =C-c= and =C-c l= by default. #+begin_quote Ignore ~doom-leader-key~ and ~doom-localleader-key~, they don't apply to non-evil sessions. #+end_quote -Note that evil-specific configuration and keybinds (defined with ~map!~) will be -ignored without evil present (and stripped out when byte-compiling). +Evil-specific configuration and keybindings (defined with ~map!~) will be +ignored without =:feature evil= present (and omitted when byte-compiling). -Unfortunately, since Doom was designed by a vimmer, for vimmers, little -consideration into a keybinding scheme for vanilla Emacs users. +Keep in mind that, at the time of this writing, Doom was designed by a vimmer, +for vimmers. Little consideration has been put into designing a keybind scheme +for vanilla Emacs users (though it's being worked on!). + +That means that much of Doom's functionality will be orphaned in an evil-less +setup. You'll have to set your own keybinds. + +I suggest studying [[file:../../config/default/+emacs-bindings.el][config/default/+emacs-bindings.el]] to see what keybinds are +available for non-evil users. Otherwise, you may find inspiration [[file:../../../docs/example_configs.org][on the example +Doom configurations page]]. + +** Restoring old substitution behavior on s/S +Doom replaces the =s= and =S= keys with the =evil-snipe= package (a port of +vim-seek/vim-sneak for 2-character versions of f/F/t/T). + +To disable evil-snipe on s/S, you can either: + +1. Disable ~evil-snipe-mode~ by adding ~(after! evil-snipe (evil-snipe-mode + -1))~ to =$DOOMDIR/config.el=, +2. Or disable =evil-snipe= completely with ~(package! evil-snipe :disable t)~ + added to =$DOOMDIR/packages.el=, but this will also disable incremental + highlighting for the f/F/t/T motions keys. diff --git a/modules/feature/evil/autoload/advice.el b/modules/feature/evil/autoload/advice.el index f831c2608..ee16df71d 100644 --- a/modules/feature/evil/autoload/advice.el +++ b/modules/feature/evil/autoload/advice.el @@ -185,3 +185,23 @@ more information on modifiers." "Call `doom/escape' if `evil-force-normal-state' is called interactively." (when (called-interactively-p 'any) (call-interactively #'doom/escape))) + +;;;###autoload +(defun +evil*make-numbered-markers-global (orig-fn char) + (or (and (>= char ?2) (<= char ?9)) + (funcall orig-fn char))) + +;;;###autoload +(defun +evil*set-jump (orig-fn &rest args) + "Set a jump point and ensure ORIG-FN doesn't set any new jump points." + (evil-set-jump (if (markerp (car args)) (car args))) + (let ((evil--jumps-jumping t)) + (apply orig-fn args))) + +;;;###autoload +(defun +evil*fix-dabbrev-in-minibuffer () + "Make `try-expand-dabbrev' from `hippie-expand' work in minibuffer. See +`he-dabbrev-beg', so we need to redefine syntax for '/'." + (set-syntax-table (let* ((table (make-syntax-table))) + (modify-syntax-entry ?/ "." table) + table))) diff --git a/modules/feature/evil/autoload/embrace.el b/modules/feature/evil/autoload/embrace.el new file mode 100644 index 000000000..82737b825 --- /dev/null +++ b/modules/feature/evil/autoload/embrace.el @@ -0,0 +1,33 @@ +;;; feature/evil/autoload/embrace.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +evil--embrace-get-pair (char) + (if-let* ((pair (cdr-safe (assoc (string-to-char char) evil-surround-pairs-alist)))) + pair + (if-let* ((pair (assoc-default char embrace--pairs-list))) + (if-let* ((real-pair (and (functionp (embrace-pair-struct-read-function pair)) + (funcall (embrace-pair-struct-read-function pair))))) + real-pair + (cons (embrace-pair-struct-left pair) (embrace-pair-struct-right pair))) + (cons char char)))) + +;;;###autoload +(defun +evil--embrace-escaped () + "Backslash-escaped surround character support for embrace." + (let ((char (read-char "\\"))) + (if (eq char 27) + (cons "" "") + (let ((pair (+evil--embrace-get-pair (string char))) + (text (if (sp-point-in-string) "\\\\%s" "\\%s"))) + (cons (format text (car pair)) + (format text (cdr pair))))))) + +;;;###autoload +(defun +evil--embrace-latex () + "LaTeX command support for embrace." + (cons (format "\\%s{" (read-string "\\")) "}")) + +;;;###autoload +(defun +evil--embrace-elisp-fn () + "Elisp function support for embrace." + (cons (format "(%s " (or (read-string "(") "")) ")")) diff --git a/modules/feature/evil/autoload/evil.el b/modules/feature/evil/autoload/evil.el index 1da151300..cac9a022e 100644 --- a/modules/feature/evil/autoload/evil.el +++ b/modules/feature/evil/autoload/evil.el @@ -13,7 +13,7 @@ ;; -;; Commands +;;; Commands ;;;###autoload (defun +evil/visual-indent () @@ -50,10 +50,10 @@ (call-interactively #'evil-paste-after))) (defun +evil--window-swap (direction) - "Move current window to the next window in DIRECTION. If there are no windows -there and there is only one window, split in that direction and place this -window there. If there are no windows and this isn't the only window, use -evil-window-move-* (e.g. `evil-window-move-far-left')" + "Move current window to the next window in DIRECTION. +If there are no windows there and there is only one window, split in that +direction and place this window there. If there are no windows and this isn't +the only window, use evil-window-move-* (e.g. `evil-window-move-far-left')." (when (window-dedicated-p) (user-error "Cannot swap a dedicated window")) (let* ((this-window (selected-window)) @@ -111,7 +111,7 @@ integration." ;; -;; Evil commands/operators +;;; Evil commands/operators ;;;###autoload (autoload '+evil:apply-macro "feature/evil/autoload/evil" nil t) (evil-define-operator +evil:apply-macro (beg end) @@ -154,7 +154,8 @@ integration." (doom/clone-and-narrow-buffer beg end bang)) -;; --- custom arg handlers ---------------- +;; +;;; Custom arg handlers (defvar +evil--flag nil) @@ -241,7 +242,8 @@ the first match on each line)." -1 1 bang)) -;; --- wgrep ------------------------------ +;; +;;; wgrep ;;;###autoload (autoload '+evil-delete "feature/evil/autoload/evil" nil t) (evil-define-operator +evil-delete (beg end type register yank-handler) diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 48cbcb6d4..471f97899 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -14,7 +14,9 @@ line with a linewise comment.") (defvar evil-want-Y-yank-to-eol t) (def-package! evil - :init + :hook (doom-init-modules . evil-mode) + :demand t + :preface (setq evil-want-visual-char-semi-exclusive t evil-magic t evil-echo-state t @@ -27,8 +29,6 @@ line with a linewise comment.") evil-respect-visual-line-mode t ;; more vim-like behavior evil-symbol-word-search t - ;; don't activate mark on shift-click - shift-select-mode nil ;; cursor appearance evil-default-cursor '+evil-default-cursor evil-normal-state-cursor 'box @@ -39,18 +39,16 @@ line with a linewise comment.") evil-want-keybinding (not (featurep! +everywhere))) :config - (load! "+commands") - - (add-hook 'doom-post-init-hook #'evil-mode) (evil-select-search-module 'evil-search-module 'evil-search) (put 'evil-define-key* 'lisp-indent-function 'defun) + ;; Done in a hook to ensure the popup rules load as late as possible (defun +evil|init-popup-rules () (set-popup-rules! '(("^\\*evil-registers" :size 0.3) ("^\\*Command Line" :size 8)))) - (add-hook 'doom-post-init-hook #'+evil|init-popup-rules) + (add-hook 'doom-init-modules-hook #'+evil|init-popup-rules) ;; Change the cursor color in emacs mode (defvar +evil--default-cursor-color @@ -104,12 +102,7 @@ line with a linewise comment.") ;; and one custom one: %:P (expand to the project root). (advice-add #'evil-ex-replace-special-filenames :override #'+evil*resolve-vim-path) - ;; make `try-expand-dabbrev' from `hippie-expand' work in minibuffer. See - ;; `he-dabbrev-beg', so we need to redefine syntax for '/' - (defun +evil*fix-dabbrev-in-minibuffer () - (set-syntax-table (let* ((table (make-syntax-table))) - (modify-syntax-entry ?/ "." table) - table))) + ;; make `try-expand-dabbrev' (from `hippie-expand') work in minibuffer (add-hook 'minibuffer-inactive-mode-hook #'+evil*fix-dabbrev-in-minibuffer) ;; Focus and recenter new splits @@ -117,25 +110,26 @@ line with a linewise comment.") (advice-add #'evil-window-vsplit :override #'+evil*window-vsplit) ;; Integrate evil's jump-list into some navigational commands - (defun +evil*set-jump (orig-fn &rest args) - "Set a jump point and ensure ORIG-FN doesn't set any new jump points." - (evil-set-jump (if (markerp (car args)) (car args))) - (let ((evil--jumps-jumping t)) - (apply orig-fn args))) (advice-add #'counsel-git-grep-action :around #'+evil*set-jump) (advice-add #'helm-ag--find-file-action :around #'+evil*set-jump) (advice-add #'xref-push-marker-stack :around #'+evil*set-jump) ;; In evil, registers 2-9 are buffer-local. In vim, they're global, so... - (defun +evil*make-numbered-markers-global (orig-fn char) - (or (and (>= char ?2) (<= char ?9)) - (funcall orig-fn char))) (advice-add #'evil-global-marker-p :around #'+evil*make-numbered-markers-global) ;; Make o/O continue comments (see `+evil-want-o/O-to-continue-comments') (advice-add #'evil-open-above :around #'+evil*insert-newline-above-and-respect-comments) (advice-add #'evil-open-below :around #'+evil*insert-newline-below-and-respect-comments) + ;; Recenter screen after most searches + (advice-add! '(evil-visualstar/begin-search-forward + evil-visualstar/begin-search-backward + evil-ex-search-word-backward + evil-ex-search-word-backward + evil-ex-search-forward + evil-ex-search-backward) + :after #'doom*recenter) + ;; --- custom interactive codes ----------- ;; These arg types will highlight matches in the current buffer (evil-ex-define-argument-type buffer-match :runner +evil-ex-buffer-match) @@ -159,7 +153,10 @@ line with a linewise comment.") ;; `evil-collection' (when (featurep! +everywhere) - (load! "+everywhere"))) + (load! "+everywhere")) + + ;; Custom evil ex commands + (load! "+commands")) ;; @@ -186,49 +183,24 @@ line with a linewise comment.") (def-package! evil-embrace - :after evil-surround :commands (embrace-add-pair embrace-add-pair-regexp) :hook (LaTeX-mode . embrace-LaTeX-mode-hook) :hook (org-mode . embrace-org-mode-hook) + :hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook) + :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) + :hook ((emacs-lisp-mode lisp-mode) . +evil|embrace-lisp-mode-hook) + :hook ((org-mode LaTeX-mode) . +evil|embrace-latex-mode-hook) :init - ;; Add extra pairs - (add-hook! emacs-lisp-mode - (embrace-add-pair ?\` "`" "'")) - (add-hook! (emacs-lisp-mode lisp-mode) - (embrace-add-pair-regexp ?f "([^ ]+ " ")" #'+evil--embrace-elisp-fn)) - (add-hook! (org-mode LaTeX-mode) - (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) + (after! evil-surround + (evil-embrace-enable-evil-surround-integration)) :config (setq evil-embrace-show-help-p nil) - (evil-embrace-enable-evil-surround-integration) - (defun +evil--embrace-get-pair (char) - (if-let* ((pair (cdr-safe (assoc (string-to-char char) evil-surround-pairs-alist)))) - pair - (if-let* ((pair (assoc-default char embrace--pairs-list))) - (if-let* ((real-pair (and (functionp (embrace-pair-struct-read-function pair)) - (funcall (embrace-pair-struct-read-function pair))))) - real-pair - (cons (embrace-pair-struct-left pair) (embrace-pair-struct-right pair))) - (cons char char)))) + (defun +evil|embrace-latex-mode-hook () + (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) - (defun +evil--embrace-escaped () - "Backslash-escaped surround character support for embrace." - (let ((char (read-char "\\"))) - (if (eq char 27) - (cons "" "") - (let ((pair (+evil--embrace-get-pair (string char))) - (text (if (sp-point-in-string) "\\\\%s" "\\%s"))) - (cons (format text (car pair)) - (format text (cdr pair))))))) - - (defun +evil--embrace-latex () - "LaTeX command support for embrace." - (cons (format "\\%s{" (read-string "\\")) "}")) - - (defun +evil--embrace-elisp-fn () - "Elisp function support for embrace." - (cons (format "(%s " (or (read-string "(") "")) ")")) + (defun +evil|embrace-lisp-mode-hook () + (embrace-add-pair-regexp ?f "([^ ]+ " ")" #'+evil--embrace-elisp-fn)) ;; Add escaped-sequence support to embrace (setf (alist-get ?\\ (default-value 'embrace--pairs-list)) @@ -240,13 +212,13 @@ line with a linewise comment.") (def-package! evil-escape - :commands (evil-escape evil-escape-mode evil-escape-pre-command-hook) + :commands (evil-escape) + :after-call (evil-normal-state-exit-hook) :init (setq evil-escape-excluded-states '(normal visual multiedit emacs motion) evil-escape-excluded-major-modes '(neotree-mode treemacs-mode term-mode) evil-escape-key-sequence "jk" evil-escape-delay 0.25) - (add-hook 'pre-command-hook #'evil-escape-pre-command-hook) (evil-define-key* '(insert replace visual operator) 'global "\C-g" #'evil-escape) :config ;; no `evil-escape' in minibuffer @@ -311,9 +283,7 @@ the new algorithm is confusing, like in python or ruby." :config (global-evil-surround-mode 1)) -;; Without `evil-visualstar', * and # grab the word at point and search, no -;; matter what mode you're in. I want to be able to visually select a region and -;; search for other occurrences of it. +;; Allows you to use the selection for * and # (def-package! evil-visualstar :commands (evil-visualstar/begin-search evil-visualstar/begin-search-forward diff --git a/modules/feature/lookup/autoload/lookup.el b/modules/feature/lookup/autoload/lookup.el index da2b4fc25..4a6c2c9b1 100644 --- a/modules/feature/lookup/autoload/lookup.el +++ b/modules/feature/lookup/autoload/lookup.el @@ -84,7 +84,7 @@ Otherwise, these properties are available to be set: ;; -;; Library +;; Helpers ;; Helpers (defun +lookup--online-provider (&optional force-p namespace) @@ -100,59 +100,64 @@ Otherwise, these properties are available to be set: provider)))) (defun +lookup--symbol-or-region (&optional initial) - (cond (initial) + (cond ((stringp initial) + initial) ((use-region-p) (buffer-substring-no-properties (region-beginning) (region-end))) ((require 'xref nil t) (xref-backend-identifier-at-point (xref-find-backend))))) -(defun +lookup--jump-to (prop identifier &optional other-window) - ;; TODO Refactor me - (let ((origin (point-marker))) - (cl-loop for fn - in (plist-get (list :definition +lookup-definition-functions - :references +lookup-references-functions - :documentation +lookup-documentation-functions - :file +lookup-file-functions) - prop) - for cmd = (or (command-remapping fn) fn) - if (get fn '+lookup-async) - return - (progn - (when other-window - ;; If async, we can't catch the window change or destination buffer - ;; reliably, so we set up the new window ahead of time. - (switch-to-buffer-other-window (current-buffer)) - (goto-char (marker-position origin))) - (call-interactively fn) - t) - if (condition-case e - (save-window-excursion - (when (or (if (commandp cmd) - (call-interactively cmd) - (funcall cmd identifier)) - (/= (point-marker) origin)) - (point-marker))) - (error (ignore (message "%s" e)))) - return - (progn - (funcall (if other-window - #'switch-to-buffer-other-window - #'switch-to-buffer) - (marker-buffer it)) - (goto-char it))))) +(defun +lookup--run-hooks (hook identifier origin &optional other-window) + (doom-log "Looking up '%s' with '%s'" identifier hook) + (condition-case-unless-debug e + (if (get hook '+lookup-async) + (progn + (when other-window + ;; If async, we can't catch the window change or destination buffer + ;; reliably, so we set up the new window ahead of time. + (switch-to-buffer-other-window (current-buffer)) + (goto-char (marker-position origin))) + (if (commandp hook) + (call-interactively hook) + (funcall hook identifier)) + t) + (save-window-excursion + (when (or (if (commandp hook) + (call-interactively hook) + (funcall hook identifier)) + (null origin) + (/= (point-marker) origin)) + (point-marker)))) + ((error user-error) + (message "%s" e) + nil))) -(defun +lookup--file-search (identifier) - (unless identifier - (let ((query (rxt-quote-pcre identifier))) - (ignore-errors - (cond ((featurep! :completion ivy) - (+ivy-file-search nil :query query) - t) - ((featurep! :completion helm) - (+helm-file-search nil :query query) - t)))))) +(defun +lookup--jump-to (prop identifier &optional other-window) + (let ((ret + (condition-case e + (run-hook-wrapped + (plist-get (list :definition '+lookup-definition-functions + :references '+lookup-references-functions + :documentation '+lookup-documentation-functions + :file '+lookup-file-functions) + prop) + '+lookup--run-hooks + identifier + (point-marker) + other-window) + (quit (user-error "Aborted %s lookup" prop))))) + (cond ((null ret) + (message "Could not find '%s'" identifier) + nil) + ((markerp ret) + (funcall (if other-window + #'switch-to-buffer-other-window + #'switch-to-buffer) + (marker-buffer ret)) + (goto-char ret) + (recenter) + t)))) ;; @@ -196,8 +201,7 @@ falling back to git-grep)." (defun +lookup-evil-goto-definition-backend (identifier) "Uses `evil-goto-definition' to conduct a text search for IDENTIFIER in the current buffer." - (and (featurep 'evil) - evil-mode + (and (fboundp 'evil-goto-definition) (ignore-errors (cl-destructuring-bind (beg . end) (bounds-of-thing-at-point 'symbol) @@ -268,7 +272,7 @@ search otherwise." ((error "Couldn't find references of '%s'" identifier)))) ;;;###autoload -(defun +lookup/documentation (identifier &optional other-window) +(defun +lookup/documentation (identifier &optional arg) "Show documentation for IDENTIFIER (defaults to symbol at point or selection. First attempts the :documentation handler specified with `set-lookup-handlers!' @@ -277,9 +281,7 @@ for the current mode/buffer (if any), then falls back to the backends in (interactive (list (+lookup--symbol-or-region) current-prefix-arg)) - (cond ((null identifier) (user-error "Nothing under point")) - - ((+lookup--jump-to :documentation identifier other-window)) + (cond ((+lookup--jump-to :documentation identifier t)) ((user-error "Couldn't find documentation for '%s'" identifier)))) diff --git a/modules/feature/lookup/packages.el b/modules/feature/lookup/packages.el index 67e05165f..c127ebbfc 100644 --- a/modules/feature/lookup/packages.el +++ b/modules/feature/lookup/packages.el @@ -5,7 +5,7 @@ ;; be loaded before it is byte-compiled during installation. To ensure this, we ;; declare helm before dumb-jump. (when (featurep! :completion helm) - (depends-on! :completion helm)) + (package! helm)) ;; (package! dumb-jump) diff --git a/modules/feature/snippets/config.el b/modules/feature/snippets/config.el index c2f47b2a6..50ce7fce7 100644 --- a/modules/feature/snippets/config.el +++ b/modules/feature/snippets/config.el @@ -21,13 +21,15 @@ :config (setq yas-verbosity (if doom-debug-mode 3 0) yas-also-auto-indent-first-line t - yas-triggers-in-field t) ; Allow nested snippets + yas-triggers-in-field t ; Allow nested snippets + ;; Remove default ~/.emacs.d/snippets + yas-snippet-dirs (delete yas--default-user-snippets-dir yas-snippet-dirs)) ;; Allow private snippets in DOOMDIR/snippets (add-to-list 'yas-snippet-dirs '+snippets-dir nil #'eq) ;; Remove GUI dropdown prompt (prefer ivy/helm) - (delq #'yas-dropdown-prompt yas-prompt-functions) + (setq yas-prompt-functions (delq 'yas-dropdown-prompt yas-prompt-functions)) ;; Prioritize private snippets in `+snippets-dir' over built-in ones if there ;; are multiple choices. (add-to-list 'yas-prompt-functions #'+snippets-prompt-private nil #'eq) diff --git a/modules/feature/workspaces/README.org b/modules/feature/workspaces/README.org index 29f8dec1a..877280479 100644 --- a/modules/feature/workspaces/README.org +++ b/modules/feature/workspaces/README.org @@ -61,9 +61,9 @@ in [[../../private/default/+evil-commands.el][private/default/+evil-commands.el] | ~+workspace/new~ | =SPC TAB n= | Create a new, blank workspace | | ~+workspace/display~ | =SPC TAB TAB= | Display open workspaces in the mode-line | | ~+workspace/load~ | =SPC TAB l= | Load a saved workspace into the current session | -| ~+workspace/load-session~ | =SPC TAB L= / =:sl[oad]= | Replace current session with a saved one | +| ~doom/quicksave-load~ | =SPC TAB L= / =:sl[oad]= | Replace current session with a saved one | | ~+workspace/save~ | =SPC TAB s= | Save the current workspace to a file | -| ~+workspace/save-session~ | =SPC TAB S= / =:ss[ave]= | Save current session | +| ~doom/quicksave-save~ | =SPC TAB S= / =:ss[ave]= | Save current session | | ~+workspace/switch-to~ | =SPC TAB .= | Switch to an open workspace | | ~+workspace/switch-left~ | =SPC TAB [= / =[ w= / =gT= | Switch to previous workspace | | ~+workspace/switch-right~ | =SPC TAB [= / =] w= / =gt= | Switch to next workspace | diff --git a/modules/feature/workspaces/autoload/evil.el b/modules/feature/workspaces/autoload/evil.el index 16f75c4e6..a18d06f74 100644 --- a/modules/feature/workspaces/autoload/evil.el +++ b/modules/feature/workspaces/autoload/evil.el @@ -1,20 +1,6 @@ ;;; feature/workspaces/autoload/evil.el -*- lexical-binding: t; -*- ;;;###if (featurep! :feature evil) -;;;###autoload (autoload '+workspace:save-session "feature/workspaces/autoload/evil" nil t) -(evil-define-command +workspace:save-session (&optional bang name) - "Ex wrapper around `+workspace/save-session'. If BANG, then autosave -(pointless if autosaving/loading is off). If NAME is nil, default to 'last'." - (interactive "") - (+workspace/save-session (if bang persp-auto-save-fname name))) - -;;;###autoload (autoload '+workspace:load-session "feature/workspaces/autoload/evil" nil t) -(evil-define-command +workspace:load-session (&optional bang name) - "Ex wrapper around `+workspace/load-session'. If BANG, then load last autosave -(pointless if autosaving/loading is off). If NAME is nil, defaults to 'last'." - (interactive "") - (+workspace/load-session (if bang persp-auto-save-fname name))) - ;;;###autoload (autoload '+workspace:save "feature/workspaces/autoload/evil" nil t) (evil-define-command +workspace:save (&optional name) "Ex wrapper around `+workspace/save-session'." diff --git a/modules/feature/workspaces/autoload/workspaces.el b/modules/feature/workspaces/autoload/workspaces.el index ebd1ffc0c..5f10c9695 100644 --- a/modules/feature/workspaces/autoload/workspaces.el +++ b/modules/feature/workspaces/autoload/workspaces.el @@ -112,14 +112,6 @@ Returns t if successful, nil otherwise." *persp-hash* (list name)) (+workspace-exists-p name)) -;;;###autoload -(defun +workspace-load-session (&optional name) - "Replace current session with the entire session named NAME. If NAME is nil, -use `persp-auto-save-fname'." - (mapc #'+workspace-delete (+workspace-list-names)) - (persp-load-state-from-file - (expand-file-name (or name persp-auto-save-fname) persp-save-dir))) - ;;;###autoload (defun +workspace-save (name) "Saves a single workspace (NAME) from the current session. Can be loaded again @@ -134,18 +126,6 @@ Returns t on success, nil otherwise." (and (member name (persp-list-persp-names-in-file fname)) t))) -;;;###autoload -(defun +workspace-save-session (&optional name) - "Save a whole session as NAME. If NAME is nil, use `persp-auto-save-fname'. -Return t on success, nil otherwise." - (let ((fname (expand-file-name (or name persp-auto-save-fname) - persp-save-dir))) - ;; disable auto-saving on kill-emacs if autosaving (i.e. name is nil) - (when (or (not name) - (string= name persp-auto-save-fname)) - (setq persp-auto-save-opt 0)) - (and (persp-save-state-to-file fname) t))) - ;;;###autoload (defun +workspace-new (name) "Create a new workspace named NAME. If one already exists, return nil. @@ -206,6 +186,9 @@ throws an error." ;; ;; Commands +;;;###autoload +(defalias '+workspace/restore-last-session #'doom/quickload-session) + ;;;###autoload (defun +workspace/load (name) "Load a workspace and switch to it. If called with C-u, try to reload the @@ -236,46 +219,6 @@ workspace." (+workspace-message (format "'%s' workspace saved" name) 'success) (+workspace-error (format "Couldn't save workspace %s" name)))) -;;;###autoload -(defun +workspace/load-session (&optional name) - "Load a session and switch to it. If called with C-u, try to load the last -session." - (interactive - (list - (unless current-prefix-arg - (completing-read - "Session to load: " - (directory-files persp-save-dir nil "^[^_.]") - nil t)))) - (condition-case ex - (let ((name (or name persp-auto-save-fname))) - (+workspace-load-session name) - (+workspace-message (format "'%s' workspace loaded" name) 'success)) - '(error (+workspace-error (cadr ex) t)))) - -;;;###autoload -(defun +workspace/load-last-session () - "Restore last session and switch to it." - (interactive) - (+workspace/load-session)) - -;;;###autoload -(defun +workspace/save-session (&optional name) - "Save the current session. If called with C-u, prompt you for the name to save -the session as." - (interactive - (list - (when current-prefix-arg - (completing-read - "Save session as: " - (directory-files persp-save-dir nil "^[^_.]"))))) - (condition-case-unless-debug ex - (let ((name (or name persp-auto-save-fname))) - (if (+workspace-save-session name) - (+workspace-message (format "Saved session as '%s'" name) 'success) - (error "Couldn't save session as '%s'" name))) - ('error (+workspace-error ex t)))) - ;;;###autoload (defun +workspace/rename (new-name) "Rename the current workspace." @@ -437,12 +380,6 @@ the next." (t (+workspace-error "Can't delete last workspace" t))))))) -;;;###autoload -(defun +workspace/restart-emacs-then-restore () - "Restarts Emacs, then restores the session." - (interactive) - (restart-emacs (list "--restore"))) - ;; ;; Tabs display in minibuffer @@ -570,7 +507,7 @@ This be hooked to `projectile-after-switch-project-hook'." (+workspace-message (format "Switched to '%s' in new workspace" new-name) 'success)) - (with-current-buffer (switch-to-buffer (doom-fallback-buffer)) + (with-current-buffer (doom-fallback-buffer) (setq default-directory +workspaces--project-dir) (message "Switched to '%s'" (doom-project-name +workspaces--project-dir))) (unless current-prefix-arg diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index a42f7e62e..32e15e33f 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -5,11 +5,6 @@ ;; it because it was unstable and slow; `persp-mode' is neither (and still ;; maintained). ;; -;; By default, sessions are autosaved, but not autoloaded. Use :ss or -;; `+workspace/save-session' to save, and :sl or `+workspace/load-session' to -;; load the last autosaved session. You can give sessions a custom name so they -;; can be loaded later. -;; ;; NOTE persp-mode requires `workgroups' for file persistence in Emacs 24.4. (defvar +workspaces-main "main" @@ -20,26 +15,20 @@ `counsel-projectile-switch-project'. This function must take one argument: the new project directory.") -;; FIXME actually use this for wconf bookmark system -(defvar +workspaces-data-file "_workspaces" - "The basename of the file to store single workspace perspectives. Will be -stored in `persp-save-dir'.") - (defvar +workspaces-on-switch-project-behavior 'non-empty "Controls the behavior of workspaces when switching to a new project. Can be one of the following: t Always create a new workspace for the project -'non-empty Only create a new workspace if the current one has no buffers +'non-empty Only create a new workspace if the current one already has buffers associated with it. nil Never create a new workspace on project switch.") -;; If emacs is passed --restore, restore the last session on startup. This is -;; used by the `+workspace/restart-emacs-then-restore' command. -(defun +workspaces-restore-last-session (&rest _) - (add-hook 'emacs-startup-hook #'+workspace/load-session :append)) -(add-to-list 'command-switch-alist (cons "--restore" #'+workspaces-restore-last-session)) +;; FIXME actually use this for wconf bookmark system +(defvar +workspaces-data-file "_workspaces" + "The basename of the file to store single workspace perspectives. Will be +stored in `persp-save-dir'.") ;; @@ -64,31 +53,31 @@ workspace. Also ensures that the *Warnings* buffer will be visible in main. Uses `+workspaces-main' to determine the name of the main workspace." (unless persp-mode - (persp-mode +1)) - (unless noninteractive - (let (persp-before-switch-functions persp-activated-functions) - (with-selected-frame frame - ;; The default perspective persp-mode creates (`persp-nil-name') is - ;; special and doesn't represent a real persp object, so buffers can't - ;; really be assigned to it, among other quirks. We create a *real* - ;; main workspace to fill this role. - (unless (persp-get-by-name +workspaces-main) - (persp-add-new +workspaces-main)) - ;; Switch to it if we aren't auto-loading the last session - (when (and (string= (safe-persp-name (get-current-persp)) persp-nil-name) - (= persp-auto-resume-time -1)) - (persp-frame-switch +workspaces-main frame) - ;; We want to know where we are in every new daemon frame - (when (daemonp) - (run-at-time 0.1 nil #'+workspace/display)) - ;; Fix #319: the warnings buffer gets swallowed by creating - ;; `+workspaces-main', so we display it manually, if it exists. - (when-let* ((warnings (get-buffer "*Warnings*"))) - (save-excursion - (display-buffer-in-side-window - warnings '((window-height . shrink-window-if-larger-than-buffer)))))))))) + (persp-mode +1) + (unless noninteractive + (let (persp-before-switch-functions persp-activated-functions) + (with-selected-frame frame + ;; The default perspective persp-mode creates (`persp-nil-name') is + ;; special and doesn't represent a real persp object, so buffers can't + ;; really be assigned to it, among other quirks. We create a *real* + ;; main workspace to fill this role. + (unless (persp-get-by-name +workspaces-main) + (persp-add-new +workspaces-main)) + ;; Switch to it if we aren't auto-loading the last session + (when (and (string= (safe-persp-name (get-current-persp)) persp-nil-name) + (= persp-auto-resume-time -1)) + (persp-frame-switch +workspaces-main frame) + ;; We want to know where we are in every new daemon frame + (when (daemonp) + (run-at-time 0.1 nil #'+workspace/display)) + ;; Fix #319: the warnings buffer gets swallowed by creating + ;; `+workspaces-main', so we display it manually, if it exists. + (when-let* ((warnings (get-buffer "*Warnings*"))) + (save-excursion + (display-buffer-in-side-window + warnings '((window-height . shrink-window-if-larger-than-buffer))))))))))) - (add-hook 'doom-post-init-hook #'+workspaces|init t) + (add-hook 'doom-init-modules-hook #'+workspaces|init t) :config (setq persp-autokill-buffer-on-remove 'kill-weak persp-nil-hidden t @@ -102,8 +91,6 @@ Uses `+workspaces-main' to determine the name of the main workspace." (advice-add #'persp-asave-on-exit :around #'+workspaces*autosave-real-buffers) - (add-hook 'doom-cleanup-hook #'+workspaces|cleanup-unassociated-buffers) - ;; Ensure buffers we've opened/switched to are auto-added to the current ;; perspective (setq persp-add-buffer-on-find-file t diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index c354e84de..e50ea0665 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -56,7 +56,16 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." ;;;###autoload (defun +cc-c-c++-objc-mode () - "Sets either `c-mode', `objc-mode' or `c++-mode', whichever is appropriate." + "Uses heuristics to detect `c-mode', `objc-mode' or `c++-mode'. + +1. Checks if there are nearby cpp/cc/m/mm files with the same name. +2. Checks for ObjC and C++-specific keywords and libraries. +3. Falls back to `+cc-default-header-file-mode', if set. +4. Otherwise, activates `c-mode'. + +This is meant to replace `c-or-c++-mode' (introduced in Emacs 26.1), which +doesn't support specification of the fallback mode and whose heuristics are +simpler." (let ((base (file-name-sans-extension (buffer-file-name (buffer-base-buffer))))) (cond ((file-exists-p! (or (concat base ".cpp") (concat base ".cc"))) @@ -70,13 +79,11 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." "\\|[-+] ([a-zA-Z0-9_]+)" "\\)"))) (objc-mode)) - ((fboundp 'c-or-c++-mode) ; introduced in Emacs 26.1 - (c-or-c++-mode)) - ((+cc--re-search-for ; TODO Remove this along with Emacs 25 support + ((+cc--re-search-for (let ((id "[a-zA-Z0-9_]+") (ws "[ \t\r]+") (ws-maybe "[ \t\r]*")) (concat "^" ws-maybe "\\(?:" - "using" ws "\\(?:namespace" ws "std;\\|std::\\)" - "\\|" "namespace" "\\(:?" ws id "\\)?" ws-maybe "{" + "using" ws "\\(?:namespace" ws "std;\\|std::\\)" + "\\|" "namespace" "\\(?:" ws id "\\)?" ws-maybe "{" "\\|" "class" ws id ws-maybe "[:{\n]" "\\|" "template" ws-maybe "<.*>" "\\|" "#include" ws-maybe "<\\(?:string\\|iostream\\|map\\)>" @@ -86,6 +93,14 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." (funcall +cc-default-header-file-mode)) ((c-mode))))) +(defun +cc-resolve-include-paths () + (cl-loop with path = (or buffer-file-name default-directory) + for dir in +cc-default-include-paths + if (file-name-absolute-p dir) + collect dir + else if (projectile-locate-dominating-file path dir) + collect (expand-file-name dir it))) + ;; ;; Commands @@ -99,7 +114,7 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." ;; first rtag (when (and (featurep 'rtags) rtags-enabled - (executable-find "rc")) + (executable-find rtags-rc-binary-name)) (with-temp-buffer (message "Reloaded compile commands for rtags daemon") (rtags-call-rc :silent t "-J" (or (doom-project-root) default-directory)))) @@ -128,12 +143,13 @@ preceded by the opening brace or a comma (disregarding whitespace in between)." "Better fontification for preprocessor constants" (when (memq major-mode '(c-mode c++-mode)) (font-lock-add-keywords - nil '(("\\<[A-Z]*_[A-Z_]+\\>" . font-lock-constant-face) + nil '(("\\<[A-Z]*_[0-9A-Z_]+\\>" . font-lock-constant-face) ("\\<[A-Z]\\{3,\\}\\>" . font-lock-constant-face)) t))) +(defvar +cc--project-includes-alist nil) ;;;###autoload -(defun +cc|irony-init-compile-options () +(defun +cc|init-irony-compile-options () "Initialize compiler options for irony-mode. It searches for the nearest compilation database and initailizes it, otherwise falling back on `+cc-default-compiler-options' and `+cc-default-include-paths'. @@ -143,33 +159,47 @@ compilation dbs." (when (memq major-mode '(c-mode c++-mode objc-mode)) (require 'irony-cdb) (unless (irony-cdb-autosetup-compile-options) - (irony-cdb--update-compile-options - (delq nil - (append (cdr-safe (assq major-mode +cc-default-compiler-options)) - (cl-loop with path = (or buffer-file-name default-directory) - for dir in '("include" "includes") - if (projectile-locate-dominating-file path dir) - collect it) - (cl-loop for path in +cc-default-include-paths - if (stringp path) - nconc (list "-I" path)))) - (doom-project-root))) - ;; Make ffap aware of include paths - (when irony--working-directory - (require 'ffap) - (make-local-variable 'ffap-c-path) - (make-local-variable 'ffap-c++-path) - (cl-loop for opt in irony--compile-options - if (and (stringp opt) - (string-match "^-I\\(.+\\)" opt)) - do (add-to-list (pcase major-mode - (`c-mode 'ffap-c-path) - (`c++-mode 'ffap-c++-path)) - (expand-file-name (match-string 1 opt) - irony--working-directory)))))) + (let ((project-root (doom-project-root)) + (include-paths (+cc-resolve-include-paths))) + (setf (alist-get project-root +cc--project-includes-alist) + include-paths) + (irony-cdb--update-compile-options + (append (delq nil (cdr-safe (assq major-mode +cc-default-compiler-options))) + (cl-loop for path in include-paths + collect (format "-I%s" path))) + project-root))))) + +;; ;;;###autoload +;; (defun +cc|init-ccls-compile-options () +;; "TODO" +;; (when (memq major-mode '(c-mode c++-mode objc-mode)) +;; (when-let* ((include-paths (+cc-resolve-include-paths))) +;; (let ((args (delq nil (cdr-safe (assq major-mode +cc-default-compiler-options))))) +;; (setf (alist-get (or (lsp-workspace-root) +;; (lsp--suggest-project-root) +;; (doom-project-root)) +;; +cc--project-includes-alist) +;; include-paths) +;; (setq ccls-initialization-options +;; `(:clang (:extraArgs +;; [,@(cl-loop for path in include-paths +;; collect (format "-I%s" path))]))))))) ;;;###autoload -(defun +cc|cleanup-rtags () - "Kill rtags server(s) if there are no C/C++ buffers open." - (unless (doom-buffers-in-mode '(c-mode c++-mode) (buffer-list)) - (rtags-cancel-process))) +(defun +cc|init-ffap-integration () + "Takes the local project include paths and registers them with ffap. +This way, `find-file-at-point' (and `+lookup/file') will know where to find most +header files." + (when-let* ((project-root (or (bound-and-true-p irony--working-directory) + (and (featurep 'lsp) + (or (lsp-workspace-root) + (doom-project-root)))))) + (require 'ffap) + (make-local-variable 'ffap-c-path) + (make-local-variable 'ffap-c++-path) + (cl-loop for dir in (or (cdr (assoc project-root +cc--project-includes-alist)) + (+cc-resolve-include-paths)) + do (add-to-list (pcase major-mode + (`c-mode 'ffap-c-path) + (`c++-mode 'ffap-c++-path)) + (expand-file-name dir project-root))))) diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index f0535061c..c53273bea 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -1,9 +1,13 @@ ;;; lang/cc/config.el --- c, c++, and obj-c -*- lexical-binding: t; -*- -(defvar +cc-default-include-paths (list "include/") - "A list of default paths, relative to a project root, to search for headers in -C/C++. Paths can be absolute. This is ignored if your project has a compilation -database.") +(defvar +cc-default-include-paths + (list "include" + "includes") + "A list of default relative paths which will be searched for up from the +current file, to be passed to irony as extra header search paths. Paths can be +absolute. This is ignored if your project has a compilation database. + +This is ignored by ccls.") (defvar +cc-default-header-file-mode 'c-mode "Fallback major mode for .h files if all other heuristics fail (in @@ -20,7 +24,9 @@ database.") "-stdlib=libc++"))) (objc-mode . nil)) "A list of default compiler options for the C family. These are ignored if a -compilation database is present in the project.") +compilation database is present in the project. + +This is ignored by ccls.") ;; @@ -42,8 +48,13 @@ compilation database is present in the project.") ;; Activate `c-mode', `c++-mode' or `objc-mode' depending on heuristics (add-to-list 'auto-mode-alist '("\\.h\\'" . +cc-c-c++-objc-mode)) + ;; Ensure find-file-at-point works in C modes, must be added before irony + ;; and/or lsp hooks are run. + (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) + #'+cc|init-ffap-integration) + :config - (set-electric! '(c-mode c++-mode objc-mode java-mode) :chars '(?\n ?\})) + (set-electric! '(c-mode c++-mode objc-mode java-mode) :chars '(?\n ?\} ?\{)) (set-docsets! 'c-mode "C") (set-docsets! 'c++-mode "C++" "Boost") @@ -69,12 +80,11 @@ compilation database is present in the project.") ;;; Better fontification (also see `modern-cpp-font-lock') (add-hook 'c-mode-common-hook #'rainbow-delimiters-mode) - (add-hook! '(c-mode-hook c++-mode-hook) #'+cc|fontify-constants) + (add-hook! (c-mode c++-mode) #'+cc|fontify-constants) ;; Custom style, based off of linux - (unless (assoc "doom" c-style-alist) - (push '("doom" - (c-basic-offset . tab-width) + (c-add-style + "doom" '((c-basic-offset . tab-width) (c-comment-only-line-offset . 0) (c-hanging-braces-alist (brace-list-open) (brace-entry-open) @@ -102,8 +112,7 @@ compilation database is present in the project.") ;; another level (access-label . -) (inclass +cc-c++-lineup-inclass +) - (label . 0))) - c-style-alist)) + (label . 0)))) ;;; Keybindings ;; Smartparens and cc-mode both try to autoclose angle-brackets intelligently. @@ -131,16 +140,17 @@ compilation database is present in the project.") (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) :init (defun +cc|init-irony-mode () - (when (and (memq major-mode '(c-mode c++-mode objc-mode)) - (file-directory-p irony-server-install-prefix)) - (irony-mode +1))) - (add-hook 'c-mode-common-hook #'+cc|init-irony-mode) + (if (file-directory-p irony-server-install-prefix) + (irony-mode +1) + (message "Irony server isn't installed"))) + (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) + #'+cc|init-irony-mode) :config (setq irony-cdb-search-directory-list '("." "build" "build-conda")) ;; Initialize compilation database, if present. Otherwise, fall back on ;; `+cc-default-compiler-options'. - (add-hook 'irony-mode-hook #'+cc|irony-init-compile-options) + (add-hook 'irony-mode-hook #'+cc|init-irony-compile-options) (def-package! irony-eldoc :hook (irony-mode . irony-eldoc)) @@ -188,10 +198,11 @@ compilation database is present in the project.") :init (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 "rdm")) + (when (and (require 'rtags nil t) + (rtags-executable-find rtags-rdm-binary-name)) (rtags-start-process-unless-running))) - (add-hook 'c-mode-common-hook #'+cc|init-rtags) + (add-hook! (c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) + #'+cc|init-rtags) :config (setq rtags-autostart-diagnostics t rtags-use-bookmarks nil @@ -210,7 +221,6 @@ compilation database is present in the project.") :definition #'rtags-find-symbol-at-point :references #'rtags-find-references-at-point) - (add-hook 'doom-cleanup-hook #'+cc|cleanup-rtags) (add-hook! 'kill-emacs-hook (ignore-errors (rtags-cancel-process))) ;; Use rtags-imenu instead of imenu/counsel-imenu @@ -224,17 +234,16 @@ compilation database is present in the project.") ;; ;; LSP -(def-package! cquery +(def-package! ccls :when (featurep! +lsp) - :hook ((c-mode c++-mode objc-mode) . +lsp|init-cquery) + :hook ((c-mode-local-vars c++-mode-local-vars objc-mode-local-vars) . +cc|init-ccls) :config - (defun +lsp|init-cquery () + (defun +cc|init-ccls () (setq-local company-transformers nil) + (setq-local company-lsp-async t) (setq-local company-lsp-cache-candidates nil) - (condition-case nil - (lsp) - (user-error nil))) - (setq cquery-extra-init-params - '(:index (:comments 2) - :cacheFormat "msgpack" - :completion (:detailedLabel t)))) + (lsp)) + (after! projectile + (add-to-list 'projectile-globally-ignored-directories ".ccls-cache") + (add-to-list 'projectile-project-root-files-bottom-up ".ccls-root") + (add-to-list 'projectile-project-root-files-top-down-recurring "compile_commands.json"))) diff --git a/modules/lang/cc/doctor.el b/modules/lang/cc/doctor.el index 5f9799ecf..bb1d22c3e 100644 --- a/modules/lang/cc/doctor.el +++ b/modules/lang/cc/doctor.el @@ -1,10 +1,11 @@ ;; -*- lexical-binding: t; no-byte-compile: t; -*- ;;; lang/cc/doctor.el -;; rtags -(let ((bins (cl-remove-if #'executable-find '("rdm" "rc")))) - (when (/= (length bins) 0) - (warn! "Couldn't find the rtag client and/or server programs %s. Disabling rtags support" bins))) +(when (require 'rtags nil t) + ;; rtags + (let ((bins (cl-remove-if #'executable-find `(,rtags-rdm-binary-name ,rtags-rc-binary-name)))) + (when (/= (length bins) 0) + (warn! "Couldn't find the rtag client and/or server programs %s. Disabling rtags support" bins)))) ;; irony server (when (require 'irony nil t) diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el index d0213da5c..5a73da2f9 100644 --- a/modules/lang/cc/packages.el +++ b/modules/lang/cc/packages.el @@ -13,7 +13,7 @@ (package! company-glsl :recipe (:fetcher github :repo "Kaali/company-glsl")))) (if (featurep! +lsp) - (package! cquery) + (package! ccls) (when (package! irony) (package! irony-eldoc) (when (featurep! :tools flycheck) diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index ab6d8823b..bc1a16beb 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -1,110 +1,112 @@ ;;; lang/clojure/config.el -*- lexical-binding: t; -*- ;; `clojure-mode' -(after! clojure-mode - (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) +(add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) + +(def-package! cider + ;; NOTE: if you don't have an org directory set (the dir doesn't exist), + ;; cider jack in won't work. + :commands (cider-jack-in cider-jack-in-clojurescript) + :hook (clojure-mode-local-vars . cider-mode) + :init + (set-repl-handler! 'clojure-mode #'+clojure/repl) + (set-eval-handler! 'clojure-mode #'cider-eval-region) + (set-lookup-handlers! 'clojure-mode + :definition #'cider-find-dwim + :documentation #'cider-doc) + (add-hook 'cider-mode-hook #'eldoc-mode) + :config (set-popup-rules! '(("^\\*cider-error*" :ignore t) ("^\\*cider-repl" :quit nil) ("^\\*cider-repl-history" :vslot 2 :ttl nil))) - - (def-package! cider - ;; NOTE: if you don't have an org directory set (the dir doesn't exist), - ;; cider jack in won't work. - :commands (cider-jack-in cider-jack-in-clojurescript) - :hook (clojure-mode . cider-mode) - :init - (set-repl-handler! 'clojure-mode #'+clojure/repl) - (set-eval-handler! 'clojure-mode #'cider-eval-region) - (set-lookup-handlers! 'clojure-mode - :definition #'cider-find-dwim - :documentation #'cider-doc) - (add-hook 'cider-mode-hook #'eldoc-mode) - :config - (setq nrepl-hide-special-buffers t - nrepl-log-messages nil - cider-font-lock-dynamically '(macro core function var) - cider-overlays-use-font-lock t - cider-prompt-for-symbol nil - cider-repl-display-help-banner nil - cider-repl-history-display-duplicates nil - cider-repl-history-display-style 'one-line - cider-repl-history-file (concat doom-cache-dir "cider-repl-history") - cider-repl-history-highlight-current-entry t - cider-repl-history-quit-action 'delete-and-restore - cider-repl-history-highlight-inserted-item t - cider-repl-history-size 1000 - cider-repl-pop-to-buffer-on-connect 'display-only - cider-repl-result-prefix ";; => " - cider-repl-print-length 100 - cider-repl-use-clojure-font-lock t - cider-repl-use-pretty-printing t - cider-repl-wrap-history nil - cider-stacktrace-default-filters '(tooling dup)) - (map! (:localleader - (:map clojure-mode-map - "'" #'cider-jack-in - "\"" #'cider-jack-in-clojurescript + (setq nrepl-hide-special-buffers t + nrepl-log-messages nil + cider-font-lock-dynamically '(macro core function var) + cider-overlays-use-font-lock t + cider-prompt-for-symbol nil + cider-repl-display-help-banner nil + cider-repl-history-display-duplicates nil + cider-repl-history-display-style 'one-line + cider-repl-history-file (concat doom-cache-dir "cider-repl-history") + cider-repl-history-highlight-current-entry t + cider-repl-history-quit-action 'delete-and-restore + cider-repl-history-highlight-inserted-item t + cider-repl-history-size 1000 + cider-repl-pop-to-buffer-on-connect 'display-only + cider-repl-result-prefix ";; => " + cider-repl-print-length 100 + cider-repl-use-clojure-font-lock t + cider-repl-use-pretty-printing t + cider-repl-wrap-history nil + cider-stacktrace-default-filters '(tooling dup)) - (:prefix ("e" . "eval") - "d" #'cider-eval-defun-at-point - "D" #'cider-insert-defun-in-repl - "e" #'cider-eval-last-sexp - "E" #'cider-insert-last-sexp-in-repl - "r" #'cider-eval-region - "R" #'cider-insert-region-in-repl - "u" #'cider-undef) - (:prefix ("g" . "go/jump") - "b" #'cider-pop-back - "g" #'cider-find-var - "n" #'cider-find-ns) - (:prefix ("h" . "help") - "n" #'cider-find-ns - "a" #'cider-apropos - "d" #'cider-doc - "g" #'cider-grimoire-web - "j" #'cider-javadoc) - (:prefix ("i" . "inspect") - "i" #'cider-inspect - "r" #'cider-inspect-last-result) - (:prefix ("m" . "macro") - "e" #'cider-macroexpand-1 - "E" #'cider-macroexpand-al) - (:prefix ("n" . "namespace") - "n" #'cider-browse-ns - "N" #'cider-browse-ns-all) - (:prefix ("r" . "repl") - "n" #'cider-repl-set-ns - "q" #'cider-quit - "r" #'cider-refresh - "R" #'cider-restart - "b" #'cider-switch-to-repl-buffer - "B" #'+clojure/cider-switch-to-repl-buffer-and-switch-ns - "c" #'cider-repl-clear-buffer))) + (map! (:localleader + (:map clojure-mode-map + "'" #'cider-jack-in + "\"" #'cider-jack-in-clojurescript - (:when (featurep! :feature evil +everywhere) - :map cider-repl-mode-map - :i [S-return] #'cider-repl-newline-and-indent - :map cider-repl-history-mode-map - :i [return] #'cider-repl-history-insert-and-quit - :i "q" #'cider-repl-history-quit - :i "l" #'cider-repl-history-occur - :i "s" #'cider-repl-history-search-forward - :i "r" #'cider-repl-history-search-backward - :i "U" #'cider-repl-history-undo-other-window))) + (:prefix ("e" . "eval") + "d" #'cider-eval-defun-at-point + "D" #'cider-insert-defun-in-repl + "e" #'cider-eval-last-sexp + "E" #'cider-insert-last-sexp-in-repl + "r" #'cider-eval-region + "R" #'cider-insert-region-in-repl + "u" #'cider-undef) + (:prefix ("g" . "go/jump") + "b" #'cider-pop-back + "g" #'cider-find-var + "n" #'cider-find-ns) + (:prefix ("h" . "help") + "n" #'cider-find-ns + "a" #'cider-apropos + "d" #'cider-doc + "g" #'cider-grimoire-web + "j" #'cider-javadoc) + (:prefix ("i" . "inspect") + "i" #'cider-inspect + "r" #'cider-inspect-last-result) + (:prefix ("m" . "macro") + "e" #'cider-macroexpand-1 + "E" #'cider-macroexpand-al) + (:prefix ("n" . "namespace") + "n" #'cider-browse-ns + "N" #'cider-browse-ns-all) + (:prefix ("r" . "repl") + "n" #'cider-repl-set-ns + "q" #'cider-quit + "r" #'cider-refresh + "R" #'cider-restart + "b" #'cider-switch-to-repl-buffer + "B" #'+clojure/cider-switch-to-repl-buffer-and-switch-ns + "c" #'cider-repl-clear-buffer))) - (def-package! clj-refactor - :hook (clojure-mode . clj-refactor-mode) - :init - (set-lookup-handlers! 'clojure-mode - :references #'cljr-find-usages) - :config - (map! :map clojure-mode-map - :localleader - :desc "refactor" "R" #'hydra-cljr-help-menu/body)) + (:when (featurep! :feature evil +everywhere) + :map cider-repl-mode-map + :i [S-return] #'cider-repl-newline-and-indent + :map cider-repl-history-mode-map + :i [return] #'cider-repl-history-insert-and-quit + :i "q" #'cider-repl-history-quit + :i "l" #'cider-repl-history-occur + :i "s" #'cider-repl-history-search-forward + :i "r" #'cider-repl-history-search-backward + :i "U" #'cider-repl-history-undo-other-window))) - (def-package! flycheck-joker - :when (featurep! :tools flycheck) - :after flycheck)) + +(def-package! clj-refactor + :hook (clojure-mode . clj-refactor-mode) + :init + (set-lookup-handlers! 'clojure-mode + :references #'cljr-find-usages) + :config + (map! :map clojure-mode-map + :localleader + :desc "refactor" "R" #'hydra-cljr-help-menu/body)) + + +(def-package! flycheck-joker + :when (featurep! :tools flycheck) + :after flycheck) diff --git a/modules/lang/coq/config.el b/modules/lang/coq/config.el index da7283ac6..8bfdcf90e 100644 --- a/modules/lang/coq/config.el +++ b/modules/lang/coq/config.el @@ -3,8 +3,11 @@ ;; `coq' (setq proof-electric-terminator-enable t) +;; We've replaced coq-mode abbrevs with yasnippet snippets (in the snippets +;; library included with Doom). (setq coq-mode-abbrev-table '()) + (after! company-coq (set-popup-rule! "^\\*\\(?:response\\|goals\\)\\*" :ignore t) (set-lookup-handlers! 'company-coq-mode diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index 718dd46e8..71c4973e0 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -3,20 +3,10 @@ (def-package! elixir-mode :defer t :init - ;; Disable default smartparens config; there are too many, they're intrusive - ;; and we only want a subset of them (defined below). + ;; Disable default smartparens config. There are too many pairs; we only want + ;; a subset of them (defined below). (provide 'smartparens-elixir) :config - ;; ...and only complete the basics - (after! smartparens - (sp-with-modes 'elixir-mode - (sp-local-pair "do" "end" - :when '(("RET" "")) - :unless '(sp-in-comment-p sp-in-string-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)))) - (set-pretty-symbols! 'elixir-mode ;; Functional :def "def" @@ -30,6 +20,16 @@ :for "for" :return "return" :yield "use") + ;; ...and only complete the basics + (after! smartparens + (sp-with-modes 'elixir-mode + (sp-local-pair "do" "end" + :when '(("RET" "")) + :unless '(sp-in-comment-p sp-in-string-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-company :when (featurep! :completion company) :commands alchemist-company diff --git a/modules/lang/elm/config.el b/modules/lang/elm/config.el index 3899a89f9..b10dde8c1 100644 --- a/modules/lang/elm/config.el +++ b/modules/lang/elm/config.el @@ -5,6 +5,7 @@ (after! elm-mode (add-hook 'elm-mode-hook #'rainbow-delimiters-mode) + (set-company-backend! 'elm-mode 'company-elm) (set-repl-handler! 'elm-mode #'run-elm-interactive) (set-pretty-symbols! 'elm-mode @@ -22,4 +23,3 @@ :when (featurep! :tools flycheck) :after elm-mode :config (add-to-list 'flycheck-checkers 'elm nil #'eq)) - diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 0a21cc4cf..e85c851ff 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -9,7 +9,8 @@ to a pop up buffer." (require 'pp) (let ((result - (let ((debug-on-error t)) + (let ((debug-on-error t) + (doom--current-module (ignore-errors (doom-module-from-path buffer-file-name)))) (eval (read (concat "(progn " (buffer-substring-no-properties beg end) @@ -97,16 +98,17 @@ library/userland functions" (defun +emacs-lisp|extend-imenu () "Improve imenu support with better expression regexps and Doom-specific forms." (setq imenu-generic-expression - '(("Evil Commands" "^\\s-*(evil-define-\\(?:command\\|operator\\|motion\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1) + '(("Evil commands" "^\\s-*(evil-define-\\(?:command\\|operator\\|motion\\) +\\(\\_<[^ ()\n]+\\_>\\)" 1) ("Unit tests" "^\\s-*(\\(?:ert-deftest\\|describe\\) +\"\\([^\")]+\\)\"" 1) ("Package" "^\\s-*(\\(?:def-\\)?package! +\\(\\_<[^ ()\n]+\\_>\\)" 1) + ("Package" "^\\s-*;;;###package\\s-+\\(\\_<[^ ()\n]+\\_>\\)$" 1) ("Major modes" "^\\s-*(define-derived-mode +\\([^ ()\n]+\\)" 1) ("Modelines" "^\\s-*(def-modeline! +\\([^ ()\n]+\\)" 1) - ("Modeline Segments" "^\\s-*(def-modeline-segment! +\\([^ ()\n]+\\)" 1) + ("Modeline segments" "^\\s-*(def-modeline-segment! +\\([^ ()\n]+\\)" 1) ("Advice" "^\\s-*(def\\(?:\\(?:ine-\\)?advice\\))") ("Modes" "^\\s-*(define-\\(?:global\\(?:ized\\)?-minor\\|generic\\|minor\\)-mode +\\([^ ()\n]+\\)" 1) ("Macros" "^\\s-*(\\(?:cl-\\)?def\\(?:ine-compile-macro\\|macro\\) +\\([^ )\n]+\\)" 1) - ("Inline Functions" "\\s-*(\\(?:cl-\\)?defsubst +\\([^ )\n]+\\)" 1) + ("Inline functions" "\\s-*(\\(?:cl-\\)?defsubst +\\([^ )\n]+\\)" 1) ("Functions" "^\\s-*(\\(?:cl-\\)?def\\(?:un\\|un\\*\\|method\\|generic\\|-memoized!\\) +\\([^ ,)\n]+\\)" 1) ("Variables" "^\\s-*(\\(def\\(?:c\\(?:onst\\(?:ant\\)?\\|ustom\\)\\|ine-symbol-macro\\|parameter\\|var\\(?:-local\\)?\\)\\)\\s-+\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" 2) ("Types" "^\\s-*(\\(cl-def\\(?:struct\\|type\\)\\|def\\(?:class\\|face\\|group\\|ine-\\(?:condition\\|error\\|widget\\)\\|package\\|struct\\|t\\(?:\\(?:hem\\|yp\\)e\\)\\)\\)\\s-+'?\\(\\(?:\\sw\\|\\s_\\|\\\\.\\)+\\)" 2)))) @@ -121,3 +123,11 @@ library/userland functions" if (file-in-directory-p buffer-file-name dir) return t))) (flycheck-mode -1))) + +;;;###autoload +(defun +emacs-lisp-lookup-documentation (thing) + "Lookup THING with `helpful-variable' if it's a variable, `helpful-callable' +if it's callable, `apropos' otherwise." + (if thing + (doom/describe-symbol thing) + (call-interactively #'doom/describe-symbol))) diff --git a/modules/lang/emacs-lisp/config.el b/modules/lang/emacs-lisp/config.el index a67312d0c..63faeb3bc 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -3,6 +3,11 @@ (defvar +emacs-lisp-enable-extra-fontification t "If non-nil, highlight special forms, and defined functions and variables.") +(defvar +emacs-lisp-outline-regexp "[ \t]*;;;;* [^ \t\n]" + "Regexp to use for `outline-regexp' in `emacs-lisp-mode'. +This marks a foldable marker for `outline-minor-mode' in elisp buffers.") + + ;; `elisp-mode' is loaded at startup. In order to lazy load its config we need ;; to pretend it isn't loaded (defer-feature! elisp-mode emacs-lisp-mode) @@ -11,14 +16,14 @@ ;; ;; Config -(add-to-list 'auto-mode-alist '("\\.Cask\\'" . emacs-lisp-mode)) - -(after! elisp-mode +(def-package! elisp-mode + :mode ("\\.Cask\\'" . emacs-lisp-mode) + :config (set-repl-handler! 'emacs-lisp-mode #'+emacs-lisp/open-repl) (set-eval-handler! 'emacs-lisp-mode #'+emacs-lisp-eval) (set-lookup-handlers! 'emacs-lisp-mode :definition #'elisp-def - :documentation #'info-lookup-symbol) + :documentation #'+emacs-lisp-lookup-documentation) (set-docsets! 'emacs-lisp-mode "Emacs Lisp") (set-pretty-symbols! 'emacs-lisp-mode :lambda "lambda") (set-rotate-patterns! 'emacs-lisp-mode @@ -35,14 +40,15 @@ mode-name "Elisp" ;; Don't treat autoloads or sexp openers as outline headers, we have ;; hideshow for that. - outline-regexp ";;;;* [^ \t\n]") + outline-regexp +emacs-lisp-outline-regexp) ;; variable-width indentation is superior in elisp (add-to-list 'doom-detect-indentation-excluded-modes 'emacs-lisp-mode nil #'eq) (add-hook! 'emacs-lisp-mode-hook #'(;; 3rd-party functionality - auto-compile-on-save-mode outline-minor-mode + auto-compile-on-save-mode + outline-minor-mode ;; initialization +emacs-lisp|extend-imenu)) @@ -54,7 +60,7 @@ (font-lock-add-keywords 'emacs-lisp-mode (append `(;; custom Doom cookies - ("^;;;###\\(autodef\\|if\\)[ \n]" (1 font-lock-warning-face t))) + ("^;;;###\\(autodef\\|if\\|package\\)[ \n]" (1 font-lock-warning-face t))) ;; highlight defined, special variables & functions (when +emacs-lisp-enable-extra-fontification `((+emacs-lisp-highlight-vars-and-faces . +emacs-lisp--face))))) @@ -104,6 +110,7 @@ ;; `overseer' (autoload 'overseer-test "overseer" nil t) +(remove-hook 'emacs-lisp-mode-hook 'overseer-enable-mode) (def-package! flycheck-cask @@ -114,12 +121,20 @@ (add-hook 'flycheck-mode-hook #'flycheck-cask-setup nil t))) +(def-package! elisp-demos + :defer t + :init + (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1) + (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update)) + + ;; ;; Project modes (def-project-mode! +emacs-lisp-ert-mode :modes (emacs-lisp-mode) - :match "/test[/-].+\\.el$") + :match "/test[/-].+\\.el$" + :add-hooks (overseer-enable-mode)) (associate! buttercup-minor-mode :modes (emacs-lisp-mode) diff --git a/modules/lang/emacs-lisp/packages.el b/modules/lang/emacs-lisp/packages.el index 8bbf4dd3b..67b572ecd 100644 --- a/modules/lang/emacs-lisp/packages.el +++ b/modules/lang/emacs-lisp/packages.el @@ -1,11 +1,14 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/emacs-lisp/packages.el +(package! elisp-mode :built-in t) + (package! auto-compile) (package! highlight-quoted) (package! macrostep) (package! overseer) (package! elisp-def) +(package! elisp-demos) (when (featurep! :tools flycheck) (package! flycheck-cask)) diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index fbc17bc31..ed91e5229 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -1,11 +1,9 @@ -;;; private/erlang/config.el -*- lexical-binding: t; -*- +;;; lang/erlang/config.el -*- lexical-binding: t; -*- -(dolist (regexp '("\\.erlang$" - ;; rebar files - "/rebar\\.config\\(?:\\.script\\)?$" - ;; erlang configs - "/\\(?:app\\|sys\\)\\.config$")) - (add-to-list 'auto-mode-alist (cons regexp 'erlang-mode))) +(def-package! erlang + :mode ("\\.erlang$" . erlang-mode) + :mode ("/rebar\\.config\\(?:\\.script\\)?$" . erlang-mode) + :mode ("/\\(?:app\\|sys\\)\\.config$" . erlang-mode)) (def-package! flycheck-rebar3 diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index 42d337541..249c9264a 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -9,6 +9,7 @@ :config (setq ess-offset-continued 'straight ess-expression-offset 2 + ess-use-flymake (not (featurep! :tools flycheck)) ess-nuke-trailing-whitespace-p t ess-default-style 'DEFAULT ess-history-directory (expand-file-name "ess-history/" doom-cache-dir)) diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el index 0952be4e8..564aa4418 100644 --- a/modules/lang/go/config.el +++ b/modules/lang/go/config.el @@ -21,7 +21,7 @@ "goimports")))) (if (featurep! +lsp) - (add-hook 'go-mode-hook #'+lsp|init) + (add-hook 'go-mode-hook #'lsp!) (add-hook 'go-mode-hook #'go-eldoc-setup)) (map! :map go-mode-map diff --git a/modules/lang/haskell/+lsp.el b/modules/lang/haskell/+lsp.el new file mode 100644 index 000000000..71967a30b --- /dev/null +++ b/modules/lang/haskell/+lsp.el @@ -0,0 +1,8 @@ +;;; lang/haskell/+lsp.el -*- lexical-binding: t; -*- + +(def-package! lsp-haskell + :after haskell-mode + :init (add-hook 'haskell-mode-hook #'lsp!) + :config + ;; Does some strange indentation if it pastes in the snippet + (setq-hook! 'haskell-mode-hook yas-indent-line 'fixed)) diff --git a/modules/lang/haskell/config.el b/modules/lang/haskell/config.el index aedfce663..6f8b016bd 100644 --- a/modules/lang/haskell/config.el +++ b/modules/lang/haskell/config.el @@ -1,23 +1,26 @@ ;;; lang/haskell/config.el -*- lexical-binding: t; -*- (cond ((featurep! +intero) (load! "+intero")) - ((featurep! +dante) (load! "+dante"))) + ((featurep! +dante) (load! "+dante")) + ((featurep! +lsp) (load! "+lsp"))) + ;; ;; Common packages (after! haskell-mode (setq haskell-process-suggest-remove-import-lines t ; warnings for redundant imports etc - haskell-process-auto-import-loaded-modules t) - (when (featurep! :tools flycheck) - (setq haskell-process-show-overlays nil)) ; flycheck makes this unnecessary - (add-hook! 'haskell-mode-hook - #'(haskell-collapse-mode ; support folding haskell code blocks - interactive-haskell-mode)) + haskell-process-auto-import-loaded-modules t + haskell-process-show-overlays (not (featurep! :tools flycheck))) ; redundant with flycheck + (set-lookup-handlers! 'haskell-mode :definition #'haskell-mode-jump-to-def-or-tag) (set-file-template! 'haskell-mode :trigger #'haskell-auto-insert-module-template :project t) (set-repl-handler! '(haskell-mode haskell-cabal-mode literate-haskell-mode) #'+haskell/open-repl) + (add-hook! 'haskell-mode-hook + #'(haskell-collapse-mode ; support folding haskell code blocks + interactive-haskell-mode)) + (add-to-list 'completion-ignored-extensions ".hi") (map! :localleader diff --git a/modules/lang/haskell/packages.el b/modules/lang/haskell/packages.el index ec400afaf..b2fd48e5e 100644 --- a/modules/lang/haskell/packages.el +++ b/modules/lang/haskell/packages.el @@ -7,4 +7,6 @@ (package! dante) (package! attrap)) ((featurep! +intero) - (package! intero))) + (package! intero)) + ((featurep! +lsp) + (package! lsp-haskell))) diff --git a/modules/lang/java/+lsp.el b/modules/lang/java/+lsp.el index 88898614d..cd6f4c15a 100644 --- a/modules/lang/java/+lsp.el +++ b/modules/lang/java/+lsp.el @@ -3,7 +3,7 @@ (def-package! lsp-java :after-call java-mode - :init (add-hook 'java-mode-hook #'+lsp|init) + :init (add-hook 'java-mode-hook #'lsp!) :config ;; TODO keybinds ;; TODO treemacs integration (?) diff --git a/modules/lang/java/config.el b/modules/lang/java/config.el index 955651aaf..f0fc35743 100644 --- a/modules/lang/java/config.el +++ b/modules/lang/java/config.el @@ -23,11 +23,8 @@ If the depth is 2, the first two directories are removed: net.lissner.game.") (add-hook 'java-mode-hook #'rainbow-delimiters-mode) -(cond ((featurep! +lsp) (load! "+lsp")) - ((featurep! +meghanada) (load! "+meghanada")) - ;; TODO lang/java +lsp (lsp-java?) - ;; ((featurep! +lsp) (load! "+lsp")) - ) +(cond ((featurep! +lsp) (load! "+lsp")) + ((featurep! +meghanada) (load! "+meghanada"))) ;; diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index e77d90987..a4b46b448 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -54,6 +54,9 @@ (set-electric! 'js2-mode :chars '(?\} ?\) ?. ?:)) (set-repl-handler! 'js2-mode #'+javascript/open-repl) + (after! projectile + (add-to-list 'projectile-globally-ignored-directories "node_modules")) + (map! :map js2-mode-map :localleader "S" #'+javascript/skewer-this-buffer)) @@ -123,7 +126,7 @@ ;; Tools (when (featurep! +lsp) - (add-hook! (js2-mode rjsx-mode typescript-mode) #'+lsp|init)) + (add-hook! (js2-mode rjsx-mode typescript-mode) #'lsp!)) (def-package! tide @@ -157,14 +160,15 @@ ;; navigation (set-lookup-handlers! 'tide-mode :async t :definition #'tide-jump-to-definition - :references #'tide-references - :documentation #'tide-documentation-at-point) + :references #'tide-references) ;; resolve to `doom-project-root' if `tide-project-root' fails (advice-add #'tide-project-root :override #'+javascript*tide-project-root) ;; cleanup tsserver when no tide buffers are left (add-hook! 'tide-mode-hook (add-hook 'kill-buffer-hook #'+javascript|cleanup-tide-processes nil t)) + (define-key tide-mode-map [remap +lookup/documentation] #'tide-documentation-at-point) + (map! :localleader :map tide-mode-map "R" #'tide-restart-server diff --git a/modules/lang/latex/+ref.el b/modules/lang/latex/+ref.el index 11deac57e..321337ef7 100644 --- a/modules/lang/latex/+ref.el +++ b/modules/lang/latex/+ref.el @@ -1,5 +1,10 @@ ;;; lang/latex/+ref.el -*- lexical-binding: t; -*- +(when (stringp +latex-bibtex-file) + (setq bibtex-completion-bibliography (list (expand-file-name +latex-bibtex-file)) + reftex-default-bibliography bibtex-completion-bibliography)) + + (def-package! reftex :hook (LaTeX-mode . reftex-mode) :config diff --git a/modules/lang/latex/+viewers.el b/modules/lang/latex/+viewers.el index 447d189f3..89251647f 100644 --- a/modules/lang/latex/+viewers.el +++ b/modules/lang/latex/+viewers.el @@ -1,6 +1,6 @@ ;;; lang/latex/+viewers.el -*- lexical-binding: t; -*- -(cl-block 'viewer +(catch 'found-viewer (dolist (viewer +latex-viewers) (if (pcase viewer (`skim @@ -33,7 +33,7 @@ ;; Update PDF buffers after successful LaTeX runs (add-hook 'TeX-after-compilation-finished-function #'TeX-revert-document-buffer)))) - (cl-return-from 'viewer))) + (throw 'found-viewer t))) ;; fall back to latex-preview-pane (add-to-list 'TeX-view-program-list '("preview-pane" latex-preview-pane-mode)) @@ -44,7 +44,6 @@ (setq latex-preview-pane-multifile-mode 'auctex) (define-key! doc-view-mode-map - (kbd "ESC") #'delete-window - "q" #'delete-window - "k" (λ! (quit-window) (delete-window)))) - + "ESC" #'delete-window + "q" #'delete-window + "k" (λ! (quit-window) (delete-window)))) diff --git a/modules/lang/latex/autoload.el b/modules/lang/latex/autoload.el index de168cdba..c63b7b6be 100644 --- a/modules/lang/latex/autoload.el +++ b/modules/lang/latex/autoload.el @@ -14,7 +14,7 @@ (let* ((offset LaTeX-indent-level) (contin (or (and (boundp '+latex-indent-level-item-continuation) +latex-indent-level-item-continuation) - (* 4 LaTeX-indent-level))) + (* 4 offset))) (re-beg "\\\\begin{") (re-end "\\\\end{") (re-env "\\(itemize\\|\\enumerate\\|description\\)") @@ -37,8 +37,7 @@ indent) ((looking-at "\\\\item") (+ offset indent)) - (t - (+ contin indent)))))) + ((+ contin indent)))))) ;;;###autoload (defun +latex-symbols-company-backend (command &optional arg &rest _ignored) diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 5a1c9921a..82efbbecf 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -54,6 +54,8 @@ If no viewers are found, `latex-preview-pane' is used.") (add-hook 'TeX-mode-hook #'visual-line-mode) ;; Fold TeX macros (add-hook 'TeX-mode-hook #'TeX-fold-mode) + ;; Enable rainbow mode after applying styles to the buffer + (add-hook 'TeX-mode-hook #'rainbow-delimiters-mode) ;; display output of latex commands in popup (set-popup-rule! " output\\*$" :size 15) ;; Do not prompt for Master files, this allows auto-insert to add templates to @@ -63,10 +65,7 @@ If no viewers are found, `latex-preview-pane' is used.") (remove-hook 'find-file-hook (cl-find-if #'byte-code-function-p find-file-hook) 'local)) - ;; Enable rainbow mode after applying styles to the buffer - (add-hook 'TeX-update-style-hook #'rainbow-delimiters-mode) - (when (featurep! :tools flyspell) - (add-hook 'latex-mode-local-vars-hook #'flyspell-mode)) + (add-hook 'latex-mode-local-vars-hook #'flyspell-mode!) ;; All these excess pairs dramatically slow down typing in latex buffers, so ;; we remove them. Let snippets do their job. (after! smartparens-latex diff --git a/modules/lang/markdown/autoload.el b/modules/lang/markdown/autoload.el index e7ef946c7..f1c2c3904 100644 --- a/modules/lang/markdown/autoload.el +++ b/modules/lang/markdown/autoload.el @@ -11,7 +11,7 @@ (let ((delim "~~")) (if (markdown-use-region-p) ;; Active region - (cl-destructuring-bind (beg end) + (cl-destructuring-bind (beg . end) (markdown-unwrap-things-in-region (region-beginning) (region-end) +markdown--regex-del 2 4) @@ -20,3 +20,23 @@ (if (thing-at-point-looking-at +markdown--regex-del) (markdown-unwrap-thing-at-point nil 2 4) (markdown-wrap-or-insert delim delim 'word nil nil))))) + +;;;###autoload +(defun +markdown-flyspell-word-p () + "Return t if point is on a word that should be spell checked. + +Return nil if on a link url, markup, html, or references." + (let ((faces (doom-enlist (get-text-property (point) 'face)))) + (or (and (memq 'font-lock-comment-face faces) + (memq 'markdown-code-face faces)) + (not (cl-loop with unsafe-faces = '(markdown-reference-face + markdown-url-face + markdown-markup-face + markdown-comment-face + markdown-html-attr-name-face + markdown-html-attr-value-face + markdown-html-tag-name-face + markdown-code-face) + for face in faces + if (memq face unsafe-faces) + return t))))) diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index ef0a43f9e..aca6c7333 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -3,9 +3,6 @@ (def-package! markdown-mode :mode ("/README\\(?:\\.\\(?:markdown\\|md\\)\\)?\\'" . gfm-mode) :init - (when (featurep! +pandoc) - (setq markdown-command "pandoc --from=markdown --to=html --standalone --mathjax --highlight-style=pygments")) - (setq markdown-enable-wiki-links t markdown-italic-underscore t markdown-asymmetric-header t @@ -17,18 +14,25 @@ markdown-gfm-uppercase-checkbox t) ; for compat with org-mode :config + (set-flyspell-predicate! '(markdown-mode gfm-mode) + #'+markdown-flyspell-word-p) + (set-lookup-handlers! '(markdown-mode gfm-mode) + :file #'markdown-follow-thing-at-point) + (defun +markdown|set-fill-column-and-line-spacing () (setq-local line-spacing 2) (setq-local fill-column 80)) (add-hook 'markdown-mode-hook #'+markdown|set-fill-column-and-line-spacing) (add-hook 'markdown-mode-hook #'auto-fill-mode) + (sp-with-modes '(markdown-mode gfm-mode) + (sp-local-pair "```" "```" :post-handlers '(:add ("||\n[i]" "RET")))) + (map! :map markdown-mode-map - [remap find-file-at-point] #'markdown-follow-thing-at-point - "M-*" #'markdown-insert-list-item - "M-b" #'markdown-insert-bold - "M-i" #'markdown-insert-italic - "M-`" #'+markdown/insert-del + :i "M-*" #'markdown-insert-list-item + :i "M-b" #'markdown-insert-bold + :i "M-i" #'markdown-insert-italic + :i "M-`" #'+markdown/insert-del (:when (featurep! :feature evil +everywhere) :m "gj" #'markdown-next-visible-heading :m "gk" #'markdown-previous-visible-heading @@ -49,7 +53,9 @@ "i" #'markdown-insert-image "l" #'markdown-insert-link)))) + (def-package! pandoc-mode :when (featurep! +pandoc) :commands pandoc-mode - :hook (markdown-mode . conditionally-turn-on-pandoc)) + :hook (markdown-mode . conditionally-turn-on-pandoc) + :init (setq markdown-command "pandoc --from=markdown --to=html --standalone --mathjax --highlight-style=pygments")) diff --git a/modules/lang/ocaml/config.el b/modules/lang/ocaml/config.el index 9a4a173b3..db17fbdbb 100644 --- a/modules/lang/ocaml/config.el +++ b/modules/lang/ocaml/config.el @@ -1,7 +1,7 @@ ;;; lang/ocaml/config.el -*- lexical-binding: t; -*- (when (featurep! +lsp) - (add-hook! (tuareg-mode reason-mode) #'+lsp|init)) + (add-hook! (tuareg-mode reason-mode) #'lsp!)) (after! tuareg diff --git a/modules/lang/org/+attach.el b/modules/lang/org/+attach.el index eee6c0223..bade75ea5 100644 --- a/modules/lang/org/+attach.el +++ b/modules/lang/org/+attach.el @@ -1,7 +1,5 @@ ;;; lang/org/+attach.el -*- lexical-binding: t; -*- -(add-hook 'org-load-hook #'+org|init-attach) - ;; I believe Org's native attachment system is over-complicated and litters ;; files with metadata I don't want. So I wrote my own, which: ;; @@ -25,7 +23,37 @@ ;; -;; Packages +;;; Bootstrap + +(setq org-attach-directory (expand-file-name +org-attach-dir org-directory) + org-download-image-dir org-attach-directory + org-download-heading-lvl nil + org-download-timestamp "_%Y%m%d_%H%M%S") + +;; A shorter link to attachments +(add-to-list 'org-link-abbrev-alist (cons "attach" (abbreviate-file-name org-attach-directory))) + +(org-link-set-parameters + "attach" + :follow (lambda (link) (find-file (expand-file-name link org-attach-directory))) + :complete (lambda (&optional _arg) + (+org--relpath (+org-link-read-file "attach" org-attach-directory) + org-attach-directory)) + :face (lambda (link) + (if (file-exists-p (expand-file-name link org-attach-directory)) + 'org-link + 'error))) + +(after! projectile + (add-to-list 'projectile-globally-ignored-directories + (car (last (split-string +org-attach-dir "/" t))))) + +(after! recentf + (add-to-list 'recentf-exclude (format "%s.+$" (regexp-quote org-attach-directory)))) + + +;; +;;; Packages (def-package! org-download :commands (org-download-dnd org-download-dnd-base64) @@ -62,35 +90,3 @@ (advice-add #'org-download--dir-2 :override #'ignore) (advice-add #'org-download--fullname :filter-return #'+org-attach*download-fullname)) - - -;; -;; Bootstrap - -(defun +org|init-attach () - (setq org-attach-directory (expand-file-name +org-attach-dir org-directory)) - (setq-default org-download-image-dir org-attach-directory - org-download-heading-lvl nil - org-download-timestamp "_%Y%m%d_%H%M%S") - - ;; A shorter link to attachments - (add-to-list 'org-link-abbrev-alist (cons "attach" (abbreviate-file-name org-attach-directory))) - - (org-link-set-parameters - "attach" - :follow (lambda (link) (find-file (expand-file-name link org-attach-directory))) - :complete (lambda (&optional _arg) - (+org--relpath (+org-link-read-file "attach" org-attach-directory) - org-attach-directory)) - :face (lambda (link) - (if (file-exists-p (expand-file-name link org-attach-directory)) - 'org-link - 'error))) - - (after! projectile - (add-to-list 'projectile-globally-ignored-directories - (car (last (split-string +org-attach-dir "/" t))))) - - (after! recentf - (add-to-list 'recentf-exclude (format "%s.+$" (regexp-quote org-attach-directory))))) - diff --git a/modules/lang/org/+babel.el b/modules/lang/org/+babel.el index f78d10a6b..1495a063f 100644 --- a/modules/lang/org/+babel.el +++ b/modules/lang/org/+babel.el @@ -1,7 +1,5 @@ ;;; lang/org/+babel.el -*- lexical-binding: t; -*- -(add-hook 'org-load-hook #'+org|init-babel) - (defvar +org-babel-mode-alist '((cpp . C) (C++ . C) @@ -16,48 +14,51 @@ For example, with (fish . shell) will cause #+BEGIN_SRC fish to load ob-shell.el when executed.") (defvar +org-babel-load-functions () - "A list of functions for loading the current executing src block. They take -one argument (the language specified in the src block, as a string). Stops at -the first function to return non-nil.") - -(defun +org|init-babel () - (setq org-src-fontify-natively t ; make code pretty - org-src-preserve-indentation t ; use native major-mode indentation - org-src-tab-acts-natively t - org-src-window-setup 'current-window - org-confirm-babel-evaluate nil) ; you don't need my permission - - (defun +org*babel-lazy-load-library (info) - "Load babel libraries as needed when babel blocks are executed." - (let* ((lang (nth 0 info)) - (lang (if (symbolp lang) lang (intern lang))) - (lang (or (cdr (assq lang +org-babel-mode-alist)) - lang))) - (when (and (not (cdr (assq lang org-babel-load-languages))) - (or (run-hook-with-args-until-success '+org-babel-load-functions lang) - (require (intern (format "ob-%s" lang)) nil t))) - (when (assq :async (nth 2 info)) - ;; ob-async has its own agenda for lazy loading packages (in the - ;; child process), so we only need to make sure it's loaded. - (require 'ob-async nil t)) - (add-to-list 'org-babel-load-languages (cons lang t))) - t)) - (advice-add #'org-babel-confirm-evaluate :after-while #'+org*babel-lazy-load-library) - - ;; I prefer C-c C-c over C-c ' (more consistent) - (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) - - ;; `org-babel-get-header' was removed from org in 9.0. Quite a few babel - ;; plugins use it, so until those plugins update, this polyfill will do: - (defun org-babel-get-header (params key &optional others) - (cl-loop with fn = (if others #'not #'identity) - for p in params - if (funcall fn (eq (car p) key)) - collect p))) + "A list of functions executed to load the current executing src block. They +take one argument (the language specified in the src block, as a string). Stops +at the first function to return non-nil.") ;; -;; Packages +;;; Bootstrap + +(setq org-src-fontify-natively t ; make code pretty + org-src-preserve-indentation t ; use native major-mode indentation + org-src-tab-acts-natively t + org-src-window-setup 'current-window + org-confirm-babel-evaluate nil) ; you don't need my permission + +(defun +org*babel-lazy-load-library (info) + "Load babel libraries lazily when babel blocks are executed." + (let* ((lang (nth 0 info)) + (lang (if (symbolp lang) lang (intern lang))) + (lang (or (cdr (assq lang +org-babel-mode-alist)) + lang))) + (when (and (not (cdr (assq lang org-babel-load-languages))) + (or (run-hook-with-args-until-success '+org-babel-load-functions lang) + (require (intern (format "ob-%s" lang)) nil t))) + (when (assq :async (nth 2 info)) + ;; ob-async has its own agenda for lazy loading packages (in the + ;; child process), so we only need to make sure it's loaded. + (require 'ob-async nil t)) + (add-to-list 'org-babel-load-languages (cons lang t))) + t)) +(advice-add #'org-babel-confirm-evaluate :after-while #'+org*babel-lazy-load-library) + +;; I prefer C-c C-c over C-c ' (more consistent) +(define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) + +;; `org-babel-get-header' was removed from org in 9.0. Quite a few babel +;; plugins use it, so until those plugins update, this polyfill will do: +(defun org-babel-get-header (params key &optional others) + (cl-loop with fn = (if others #'not #'identity) + for p in params + if (funcall fn (eq (car p) key)) + collect p)) + + +;; +;;; Packages (def-package! ob-ipython :when (featurep! +ipython) @@ -83,7 +84,6 @@ the first function to return non-nil.") ("\\*Python:.*" :slot 0 :side right :size 100 :select nil :quit nil :transient nil))) - ;; TODO Add more popup styles ;; advices for remote kernel and org-src-edit (advice-add 'org-babel-edit-prep:ipython :override #'+org*org-babel-edit-prep:ipython) diff --git a/modules/lang/org/+capture.el b/modules/lang/org/+capture.el index 3b8edc8c7..bec8bdf71 100644 --- a/modules/lang/org/+capture.el +++ b/modules/lang/org/+capture.el @@ -1,7 +1,5 @@ ;;; lang/org/+capture.el -*- lexical-binding: t; -*- -(add-hook 'org-load-hook #'+org|init-capture) - ;; Sets up some reasonable defaults, as well as two `org-capture' workflows that ;; I like: ;; @@ -12,84 +10,84 @@ ;; vimperator, dmenu or a global keybinding. (defvar +org-capture-todo-file "todo.org" - "The path to your personal todo file. - -Is relative to `org-directory', unless it is absolute. Is used in Doom's default -`org-capture-templates'.") - -(defvar +org-capture-notes-file "notes.org" - "The path to your personal notes file. + "Default target for todo entries. Is relative to `org-directory', unless it is absolute. Is used in Doom's default `org-capture-templates'.") (defvar +org-capture-changelog-file "changelog.org" - "The filename to use for project changelog files. + "Default target for changelog entries. -It is used in Doom's default `org-capture-templates'.") +Is relative to `org-directory' unless it is absolute. Is used in Doom's default +`org-capture-templates'.") -(defvar org-capture-templates - '(("t" "Personal todo" entry - (file+headline +org-capture-todo-file "Inbox") - "* [ ] %?\n%i\n%a" :prepend t :kill-buffer t) - ("n" "Personal notes" entry - (file+headline +org-capture-notes-file "Inbox") - "* %u %?\n%i\n%a" :prepend t :kill-buffer t) +(defvar +org-capture-notes-file "notes.org" + "Default target for storing notes. - ;; Will use {project-root}/{todo,notes,changelog}.org, unless a - ;; {todo,notes,changelog}.org file is found in a parent directory. - ("p" "Templates for projects") - ("pt" "Project todo" entry ; {project-root}/todo.org - (file+headline +org-capture-project-todo-file "Inbox") - "* TODO %?\n%i\n%a" :prepend t :kill-buffer t) - ("pn" "Project notes" entry ; {project-root}/notes.org - (file+headline +org-capture-project-notes-file "Inbox") - "* TODO %?\n%i\n%a" :prepend t :kill-buffer t) - ("pc" "Project changelog" entry ; {project-root}/changelog.org - (file+headline +org-capture-project-notes-file "Unreleased") - "* TODO %?\n%i\n%a" :prepend t :kill-buffer t))) +Used as a fall back file for org-capture.el, for templates that do not specify a +target file. +Is relative to `org-directory', unless it is absolute. Is used in Doom's default +`org-capture-templates'.") -(defvar org-default-notes-file nil) ; defined in org.el +(setq org-default-notes-file (expand-file-name +org-capture-notes-file org-directory)) ;; -(defun +org|init-capture () - (dolist (var '(+org-capture-todo-file - +org-capture-notes-file)) - (set var (expand-file-name (symbol-value var) org-directory))) - (unless org-default-notes-file - (setq org-default-notes-file +org-capture-notes-file)) +;;; Bootstrap - (add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame) +(setq org-capture-templates + '(("t" "Personal todo" entry + (file+headline +org-capture-todo-file "Inbox") + "* TODO %?\n%i\n%a" :prepend t :kill-buffer t) + ("n" "Personal notes" entry + (file+headline +org-capture-notes-file "Inbox") + "* %u %?\n%i\n%a" :prepend t :kill-buffer t) - (defun +org*expand-variable-paths (file) - "If a variable is used for a file path in `org-capture-template', it is used + ;; Will use {project-root}/{todo,notes,changelog}.org, unless a + ;; {todo,notes,changelog}.org file is found in a parent directory. + ;; Uses the basename from `+org-capture-todo-file', + ;; `+org-capture-changelog-file' and `+org-capture-notes-file'. + ("p" "Templates for projects") + ("pt" "Project todo" entry ; {project-root}/todo.org + (file+headline +org-capture-project-todo-file "Inbox") + "* TODO %?\n%i\n%a" :prepend t :kill-buffer t) + ("pn" "Project notes" entry ; {project-root}/notes.org + (file+headline +org-capture-project-notes-file "Inbox") + "* TODO %?\n%i\n%a" :prepend t :kill-buffer t) + ("pc" "Project changelog" entry ; {project-root}/changelog.org + (file+headline +org-capture-project-notes-file "Unreleased") + "* TODO %?\n%i\n%a" :prepend t :kill-buffer t))) + +(add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame) + +(defun +org*capture-expand-variable-file (file) + "If a variable is used for a file path in `org-capture-template', it is used as is, and expanded relative to `default-directory'. This changes it to be relative to `org-directory', unless it is an absolute path." - (if (and (symbolp file) (boundp file)) - (expand-file-name (symbol-value file) org-directory) - file)) - (advice-add #'org-capture-expand-file :filter-args #'+org*expand-variable-paths) + (if (and (symbolp file) (boundp file)) + (expand-file-name (symbol-value file) org-directory) + file)) +(advice-add #'org-capture-expand-file :filter-args #'+org*capture-expand-variable-file) - (defun +org*prevent-save-prompts-when-refiling (&rest _) - "Fix #462: when refiling from org-capture, Emacs prompts to kill the +(defun +org*prevent-save-prompts-when-refiling (&rest _) + "Fix #462: when refiling from org-capture, Emacs prompts to kill the underlying, modified buffer. This fixes that." - (when (bound-and-true-p org-capture-is-refiling) - (org-save-all-org-buffers))) - (advice-add 'org-refile :after #'+org*prevent-save-prompts-when-refiling) + (when (bound-and-true-p org-capture-is-refiling) + (org-save-all-org-buffers))) +(advice-add 'org-refile :after #'+org*prevent-save-prompts-when-refiling) - (defun +org|show-target-in-capture-header () - (setq header-line-format - (format "%s%s%s" - (propertize (abbreviate-file-name (buffer-file-name (buffer-base-buffer))) - 'face 'font-lock-string-face) - org-eldoc-breadcrumb-separator - header-line-format))) - (add-hook 'org-capture-mode-hook #'+org|show-target-in-capture-header) +(defun +org|show-target-in-capture-header () + (setq header-line-format + (format "%s%s%s" + (propertize (abbreviate-file-name (buffer-file-name (buffer-base-buffer))) + 'face 'font-lock-string-face) + org-eldoc-breadcrumb-separator + header-line-format))) +(add-hook 'org-capture-mode-hook #'+org|show-target-in-capture-header) - (when (featurep! :feature evil) - (add-hook 'org-capture-mode-hook #'evil-insert-state)) +(when (featurep! :feature evil) + (add-hook 'org-capture-mode-hook #'evil-insert-state)) - (when (featurep! :ui doom-dashboard) - (add-hook '+doom-dashboard-inhibit-functions #'+org-capture-frame-p))) +(when (featurep! :ui doom-dashboard) + (add-hook '+doom-dashboard-inhibit-functions #'+org-capture-frame-p)) diff --git a/modules/lang/org/+export.el b/modules/lang/org/+export.el index ac418cc66..1e93bfe48 100644 --- a/modules/lang/org/+export.el +++ b/modules/lang/org/+export.el @@ -1,7 +1,5 @@ ;;; lang/org/+export.el -*- lexical-binding: t; -*- -(add-hook 'org-load-hook #'+org|init-export) - ;; I don't have any beef with org's built-in export system, but I do wish it ;; would export to a central directory (by default), rather than ;; `default-directory'. This is because all my org files are usually in one @@ -14,30 +12,29 @@ path too.") ;; -(defun +org|init-export () - (setq org-export-backends '(ascii html latex md) - org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/")) +(setq org-export-backends '(ascii html latex md) + org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/")) - (when (and (executable-find "pandoc") - (require 'ox-pandoc nil t)) - (add-to-list 'org-export-backends 'pandoc nil #'eq) - (setq org-pandoc-options - '((standalone . t) - (mathjax . t) - (variable . "revealjs-url=https://cdn.jsdelivr.net/npm/reveal.js@3/")))) +(when (and (executable-find "pandoc") + (require 'ox-pandoc nil t)) + (add-to-list 'org-export-backends 'pandoc nil #'eq) + (setq org-pandoc-options + '((standalone . t) + (mathjax . t) + (variable . "revealjs-url=https://cdn.jsdelivr.net/npm/reveal.js@3/")))) - ;; Export to a central location by default or if target isn't in - ;; `org-directory'. - (defun +org*export-output-file-name (args) - "Return a centralized export location unless one is provided or the current +;; Export to a central location by default or if target isn't in +;; `org-directory'. +(defun +org*export-output-file-name (args) + "Return a centralized export location unless one is provided or the current file isn't in `org-directory'." - (when (and (not (nth 2 args)) - buffer-file-name - (file-in-directory-p buffer-file-name org-directory)) - (cl-destructuring-bind (extension &optional subtreep _pubdir) args - (let ((dir (expand-file-name +org-export-dir org-directory))) - (unless (file-directory-p dir) - (make-directory dir t)) - (setq args (list extension subtreep dir))))) - args) - (advice-add #'org-export-output-file-name :filter-args #'+org*export-output-file-name)) + (when (and (not (nth 2 args)) + buffer-file-name + (file-in-directory-p buffer-file-name org-directory)) + (cl-destructuring-bind (extension &optional subtreep _pubdir) args + (let ((dir (expand-file-name +org-export-dir org-directory))) + (unless (file-directory-p dir) + (make-directory dir t)) + (setq args (list extension subtreep dir))))) + args) +(advice-add #'org-export-output-file-name :filter-args #'+org*export-output-file-name) diff --git a/modules/lang/org/+present.el b/modules/lang/org/+present.el index ae054aa1d..bc6938a4f 100644 --- a/modules/lang/org/+present.el +++ b/modules/lang/org/+present.el @@ -1,7 +1,5 @@ ;;; lang/org/+present.el -*- lexical-binding: t; -*- -(add-hook 'org-load-hook #'+org|init-present) - (defvar +org-present-text-scale 7 "The `text-scale-amount' for `org-tree-slide-mode'.") @@ -10,7 +8,7 @@ ;; Packages (def-package! ox-reveal - :defer t + :after ox :init ;; Fix #1127, where ox-reveal adds an errant entry to ;; `org-structure-template-alist' @@ -34,18 +32,10 @@ :n [left] #'org-tree-slide-move-previous-tree) (add-hook! 'org-tree-slide-mode-after-narrow-hook - #'(+org-present|detect-slide +org-present|add-overlays org-display-inline-images)) + #'(+org-present|detect-slide + +org-present|add-overlays + org-display-inline-images)) (add-hook 'org-tree-slide-mode-hook #'+org-present|init-org-tree-window) (advice-add #'org-tree-slide--display-tree-with-narrow :around #'+org-present*narrow-to-subtree)) - - -(def-package! centered-window :commands centered-window-mode) - - -;; -;; Bootstrap - -(defun +org|init-present () - (require 'ox-reveal)) diff --git a/modules/lang/org/autoload/org-capture.el b/modules/lang/org/autoload/org-capture.el index 046a8083f..e11d46d97 100644 --- a/modules/lang/org/autoload/org-capture.el +++ b/modules/lang/org/autoload/org-capture.el @@ -85,6 +85,18 @@ you're done. This can be called from an external shell script." (doom-project-root) (user-error "Couldn't detect a project"))))) +;;;###autoload +(defun +org-capture-todo-file () + "Expand `+org-capture-todo-file' from `org-directory'. +If it is an absolute path return `+org-capture-todo-file' verbatim." + (expand-file-name +org-capture-todo-file org-directory)) + +;;;###autoload +(defun +org-capture-notes-file () + "Expand `+org-capture-notes-file' from `org-directory'. +If it is an absolute path return `+org-capture-todo-file' verbatim." + (expand-file-name +org-capture-notes-file org-directory)) + ;;;###autoload (defun +org-capture-project-todo-file () "Find the nearest `+org-capture-todo-file' in a parent directory, otherwise, diff --git a/modules/lang/org/autoload/org-link.el b/modules/lang/org/autoload/org-link.el index 8d55ef8d0..62e37f7c0 100644 --- a/modules/lang/org/autoload/org-link.el +++ b/modules/lang/org/autoload/org-link.el @@ -13,3 +13,20 @@ (format "%s:%s" key (file-relative-name file dir)))) + +;;;###autoload +(defun +org-inline-data-image (_protocol link _description) + "Interpret LINK as base64-encoded image data." + (base64-decode-string link)) + +;;;###autoload +(defun +org-image-link (protocol link _description) + "Interpret LINK as base64-encoded image data." + (when (image-type-from-file-name link) + (if-let* ((buf (url-retrieve-synchronously (concat protocol ":" link)))) + (with-current-buffer buf + (goto-char (point-min)) + (re-search-forward "\r?\n\r?\n" nil t) + (buffer-substring-no-properties (point) (point-max))) + (message "Download of image \"%s\" failed" link) + nil))) diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index 00c7d706c..c74c10821 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -83,7 +83,11 @@ If on a: (org-toggle-checkbox (if (equal match "[ ]") '(16))))) (`headline - (cond ((org-element-property :todo-type context) + (cond ((and (fboundp 'toc-org-insert-toc) + (member "TOC" (org-get-tags))) + (toc-org-insert-toc) + (message "Updating table of contents")) + ((org-element-property :todo-type context) (org-todo (if (eq (org-element-property :todo-type context) 'done) (or (car (+org-get-todo-keywords-for (org-element-property :todo-keyword context))) @@ -463,3 +467,10 @@ an effect when `evil-org-special-o/O' has `item' in it (not the default)." (backward-char 1) (evil-append nil)))) (funcall orig-fn count))) + +;;;###autoload +(defun +org*display-link-in-eldoc (orig-fn &rest args) + "Display the link at point in eldoc." + (or (when-let* ((link (org-element-property :raw-link (org-element-context)))) + (format "Link: %s" link)) + (apply orig-fn args))) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 6dbcfba64..9632f266b 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -1,8 +1,5 @@ ;;; lang/org/config.el -*- lexical-binding: t; -*- -;; FIXME deprecated -(define-obsolete-variable-alias '+org-dir 'org-directory "2.1.0") - ;; Changed org defaults (should be set before org loads) (defvar org-directory "~/org/") (defvar org-modules @@ -17,25 +14,58 @@ ;; org-rmail )) -;; Sub-modules -(if (featurep! +attach) (load! "+attach")) -(if (featurep! +babel) (load! "+babel")) -(if (featurep! +capture) (load! "+capture")) -(if (featurep! +export) (load! "+export")) -(if (featurep! +present) (load! "+present")) -;; TODO (if (featurep! +publish) (load! "+publish")) -(doom-load-packages-incrementally - '(calendar find-func format-spec org-macs org-compat - org-faces org-entities org-list org-pcomplete org-src - org-footnote org-macro ob org org-agenda org-capture)) +;; +;;; Bootstrap + +(def-package! org + :defer-incrementally + (calendar find-func format-spec org-macs org-compat org-faces org-entities + org-list org-pcomplete org-src org-footnote org-macro ob org org-agenda + org-capture) + :init + (add-hook! 'org-load-hook + #'(+org|setup-ui + +org|setup-popup-rules + +org|setup-agenda + +org|setup-keybinds + +org|setup-hacks + +org|setup-pretty-code + +org|setup-custom-links)) + + (add-hook! 'org-mode-hook + #'(org-bullets-mode ; "prettier" bullets + org-indent-mode ; margin-based indentation + toc-org-enable ; auto-table of contents + auto-fill-mode ; line wrapping + ;; `show-paren-mode' causes flickering with indentation margins made by + ;; `org-indent-mode', so we simply turn off show-paren-mode altogether." + doom|disable-show-paren-mode + + +org|enable-auto-reformat-tables + +org|enable-auto-update-cookies + +org|smartparens-compatibility-config + +org|unfold-to-2nd-level-or-point)) + + :config + ;; Sub-modules + (if (featurep! +attach) (load! "+attach")) + (if (featurep! +babel) (load! "+babel")) + (if (featurep! +capture) (load! "+capture")) + (if (featurep! +export) (load! "+export")) + (if (featurep! +present) (load! "+present"))) ;; -;; Packages +;;; Packages ;; `toc-org' -(setq toc-org-hrefify-default "org") +(setq toc-org-hrefify-default "gh") +(defun +org*unfold-toc (&rest _) + (save-excursion + (when (re-search-forward toc-org-toc-org-regexp (point-max) t) + (+org/open-fold)))) +(advice-add #'toc-org-insert-toc :before #'+org*unfold-toc) (def-package! evil-org :when (featurep! :feature evil +everywhere) @@ -43,9 +73,10 @@ :init (defvar evil-org-key-theme '(navigation insert textobjects)) (defvar evil-org-special-o/O '(table-row)) - (add-hook 'org-load-hook #'+org|setup-evil) + (add-hook 'org-load-hook #'+org|setup-evil-keybinds) (add-hook 'evil-org-mode-hook #'evil-normalize-keymaps) :config + (add-hook 'org-open-at-point-functions #'evil-set-jump) ;; change `evil-org-key-theme' instead (advice-add #'evil-org-set-key-theme :override #'ignore) (def-package! evil-org-agenda @@ -59,63 +90,32 @@ (after! org (delete '("\\.pdf\\'" . default) org-file-apps) ;; org links to pdf files are opened in pdf-view-mode - (add-to-list 'org-file-apps '("\\.pdf\\'" . (lambda (_file link) (org-pdfview-open link)))) + (add-to-list 'org-file-apps '("\\.pdf\\'" . (lambda (_file link) (org-pdfview-open link)))) ;; support for links to specific pages (add-to-list 'org-file-apps '("\\.pdf::\\([[:digit:]]+\\)\\'" . (lambda (_file link) (org-pdfview-open link)))))) -(def-package! org-yt - :after org +(def-package! org-crypt ; built-in + :commands org-encrypt-entries + :hook (org-reveal-start . org-decrypt-entry) + :init + (add-hook! 'org-mode-hook + (add-hook 'before-save-hook 'org-encrypt-entries nil t)) :config - (defun +org-inline-data-image (_protocol link _description) - "Interpret LINK as base64-encoded image data." - (base64-decode-string link)) + (setq org-tags-exclude-from-inheritance '("crypt") + org-crypt-key user-mail-address)) - (defun +org-image-link (protocol link _description) - "Interpret LINK as base64-encoded image data." - (when (image-type-from-file-name link) - (if-let* ((buf (url-retrieve-synchronously (concat protocol ":" link)))) - (with-current-buffer buf - (goto-char (point-min)) - (re-search-forward "\r?\n\r?\n" nil t) - (buffer-substring-no-properties (point) (point-max))) - (message "Download of image \"%s\" failed" link) - nil))) - - (org-link-set-parameters "http" :image-data-fun #'+org-image-link) - (org-link-set-parameters "https" :image-data-fun #'+org-image-link) - (org-link-set-parameters "img" :image-data-fun #'+org-inline-data-image)) +(def-package! org-clock ; built-in + :commands org-clock-save + :hook (org-mode . org-clock-load) + :init + (setq org-clock-persist 'history + org-clock-persist-file (concat doom-etc-dir "org-clock-save.el")) + :config + (add-hook 'kill-emacs-hook #'org-clock-save)) ;; -;; Bootstrap - -(add-hook! 'org-load-hook - #'(+org|setup-ui - +org|setup-popup-rules - +org|setup-agenda - +org|setup-keybinds - +org|setup-hacks - +org|setup-pretty-code - +org|setup-custom-links)) - -(add-hook! 'org-mode-hook - #'(doom|disable-line-numbers ; org doesn't really need em - org-bullets-mode ; "prettier" bullets - org-indent-mode ; margin-based indentation - toc-org-enable ; auto-table of contents - auto-fill-mode ; line wrapping - ;; `show-paren-mode' causes flickering with indentation margins made by - ;; `org-indent-mode', so we simply turn off show-paren-mode altogether." - doom|disable-show-paren-mode - - +org|enable-auto-reformat-tables - +org|enable-auto-update-cookies - +org|smartparens-compatibility-config - +org|unfold-to-2nd-level-or-point)) - - -;; -;; `org-mode' hooks +;;; `org-mode' hooks (defun +org|unfold-to-2nd-level-or-point () "My version of the 'overview' #+STARTUP option: expand first-level headings. @@ -164,7 +164,7 @@ unfold to point on startup." ;; -;; `org-load' hooks +;;; `org-load' hooks (defun +org|setup-agenda () (unless org-agenda-files @@ -180,6 +180,7 @@ unfold to point on startup." org-agenda-start-on-weekday nil org-agenda-start-day "-3d")) + (defun +org|setup-popup-rules () "Defines popup rules for org-mode (does nothing if :ui popup is disabled)." (set-popup-rules! @@ -190,6 +191,7 @@ unfold to point on startup." ("^\\*Org Src" :size 0.3 :quit nil :select t :autosave t :ttl nil) ("^CAPTURE.*\\.org$" :size 0.2 :quit nil :select t :autosave t)))) + (defun +org|setup-pretty-code () "Setup the default pretty symbols for" (set-pretty-symbols! 'org-mode @@ -197,6 +199,7 @@ unfold to point on startup." :src_block "#+BEGIN_SRC" :src_block_end "#+END_SRC")) + (defun +org|setup-custom-links () "Set up custom org links." (setq org-link-abbrev-alist @@ -236,7 +239,14 @@ unfold to point on startup." (+org-def-link "org" org-directory) (+org-def-link "doom" doom-emacs-dir) (+org-def-link "doom-docs" doom-docs-dir) - (+org-def-link "doom-modules" doom-modules-dir)) + (+org-def-link "doom-modules" doom-modules-dir) + + (def-package! org-yt + :config + (org-link-set-parameters "http" :image-data-fun #'+org-image-link) + (org-link-set-parameters "https" :image-data-fun #'+org-image-link) + (org-link-set-parameters "img" :image-data-fun #'+org-inline-data-image))) + (defun +org|setup-ui () "Configures the UI for `org-mode'." @@ -273,20 +283,22 @@ unfold to point on startup." org-startup-with-inline-images nil org-tags-column 0 org-todo-keywords - '((sequence "[ ](t)" "[-](p)" "[?](m)" "|" "[X](d)") - (sequence "TODO(T)" "|" "DONE(D)") + '((sequence "TODO(t)" "|" "DONE(d)") + (sequence "[ ](T)" "[-](p)" "[?](m)" "|" "[X](D)") (sequence "NEXT(n)" "WAITING(w)" "LATER(l)" "|" "CANCELLED(c)")) org-todo-keyword-faces - '(("[-]" :inherit font-lock-constant-face :weight bold) - ("[?]" :inherit warning :weight bold) - ("WAITING" :inherit default :weight bold) - ("LATER" :inherit warning :weight bold)) + '(("[-]" :inherit (font-lock-constant-face bold)) + ("[?]" :inherit (warning bold)) + ("WAITING" :inherit bold) + ("LATER" :inherit (warning bold))) org-use-sub-superscripts '{} ;; Scale up LaTeX previews a bit (default is too small) org-preview-latex-image-directory (concat doom-cache-dir "org-latex/") org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) + (advice-add #'org-eldoc-documentation-function :around #'+org*display-link-in-eldoc) + ;; Don't do automatic indent detection in org files (add-to-list 'doom-detect-indentation-excluded-modes 'org-mode nil #'eq) @@ -302,6 +314,7 @@ unfold to point on startup." :background nil t)))) (add-hook 'doom-load-theme-hook #'+org|update-latex-preview-background-color)) + (defun +org|setup-keybinds () "Sets up org-mode and evil keybindings. Tries to fix the idiosyncrasies between the two." @@ -330,7 +343,8 @@ between the two." [remap doom/backward-to-bol-or-indent] #'org-beginning-of-line [remap doom/forward-to-last-non-comment-or-eol] #'org-end-of-line)) -(defun +org|setup-evil (&rest args) + +(defun +org|setup-evil-keybinds (&rest args) ;; In case this hook is used in an advice on `evil-org-set-key-theme', this ;; prevents recursive requires. (unless args (require 'evil-org)) @@ -374,7 +388,9 @@ between the two." :ni "C-S-j" #'org-metadown ;; more intuitive RET keybinds :i [return] #'org-return-indent + :i "RET" #'org-return-indent :n [return] #'+org/dwim-at-point + :n "RET" #'+org/dwim-at-point ;; more vim-esque org motion keys (not covered by evil-org-mode) :m "]]" (λ! (org-forward-heading-same-level nil) (org-beginning-of-line)) :m "[[" (λ! (org-backward-heading-same-level nil) (org-beginning-of-line)) @@ -411,13 +427,26 @@ between the two." :localleader :map org-mode-map "d" #'org-deadline + "b" #'org-switchb "t" #'org-todo + "T" #'org-todo-list + "l" #'org-store-link (:prefix ("c" . "clock") "c" #'org-clock-in "C" #'org-clock-out "g" #'org-clock-goto "G" (λ! (org-clock-goto 'select)) - "x" #'org-clock-cancel))) + "x" #'org-clock-cancel) + (:prefix ("e" . "export") + :desc "to markdown" "m" #'org-md-export-to-markdown + :desc "to markdown & open" "M" #'org-md-export-as-markdown + :desc "to reveal.js" "r" #'org-reveal-export-to-html + :desc "to reveal.js & open" "R" #'org-reveal-export-to-html-and-browse + (:prefix ("b" . "from beamer") + :desc "to latex" "l" #'org-beamer-export-to-latex + :desc "to latex & open" "L" #'org-beamer-export-as-latex + :desc "as pdf" "p" #'org-beamer-export-to-pdf)))) + (defun +org|setup-hacks () "Getting org to behave." @@ -468,29 +497,7 @@ conditions where a window's buffer hasn't changed at the time this hook is run." ;; -;; Built-in libraries +;;; In case org has already been loaded (or you're running `doom/reload') -(def-package! org-crypt ; built-in - :commands org-encrypt-entries - :hook (org-reveal-start . org-decrypt-entry) - :init - (add-hook! 'org-mode-hook - (add-hook 'before-save-hook 'org-encrypt-entries nil t)) - :config - (setq org-tags-exclude-from-inheritance '("crypt") - org-crypt-key user-mail-address)) - -(def-package! org-clock - :commands org-clock-save - :hook (org-mode . org-clock-load) - :defer-incrementally t - :init - (setq org-clock-persist 'history - org-clock-persist-file (concat doom-etc-dir "org-clock-save.el")) - :config - (add-hook 'kill-emacs-hook #'org-clock-save)) - - -;; In case org has already been loaded (or you're running `doom/reload') (when (featurep 'org) (run-hooks 'org-load-hook)) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 92d2b33ad..a69d34e8b 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -3,7 +3,14 @@ ;; Installs a cutting-edge version of org-mode (package! org-plus-contrib) -(package! org :ignore t) ; ignore org from ELPA + +;; Prevent built-in Org from playing into the byte-compilation of +;; `org-plus-contrib'. +(when-let* ((orglib (locate-library "org" nil doom-site-load-path))) + (setq load-path (delete (substring (file-name-directory orglib) 0 -1) + load-path))) +;; Ignore org on ELPA, if possible +(package! org :ignore t) (package! org-bullets :recipe (:fetcher github :repo "Kaligule/org-bullets")) (package! org-yt :recipe (:fetcher github :repo "TobiasZawada/org-yt")) diff --git a/modules/lang/php/config.el b/modules/lang/php/config.el index b3e8ae1d8..bb09c887e 100644 --- a/modules/lang/php/config.el +++ b/modules/lang/php/config.el @@ -13,7 +13,7 @@ (set-formatter! 'php-mode #'php-cs-fixer-fix) (if (featurep! +lsp) - (add-hook 'php-mode-hook #'+lsp|init) + (add-hook 'php-mode-hook #'lsp!) ;; `+php-company-backend' uses `company-phpactor', `php-extras-company' or ;; `company-dabbrev-code', in that order. (set-company-backend! 'php-mode '+php-company-backend 'company-dabbrev-code)) diff --git a/modules/lang/python/autoload/python.el b/modules/lang/python/autoload/python.el index af9d19724..84fcbcecd 100644 --- a/modules/lang/python/autoload/python.el +++ b/modules/lang/python/autoload/python.el @@ -1,8 +1,5 @@ ;;; lang/python/autoload/python.el -*- lexical-binding: t; -*- -(defvar +python-version-cache (make-hash-table :test 'equal) - "TODO") - ;;;###autoload (defun +python/open-repl () "Open the Python REPL." @@ -11,7 +8,7 @@ (user-error "`python-shell-interpreter' isn't set")) (pop-to-buffer (process-buffer - (if-let* ((pipenv (executable-find "pipenv")) + (if-let* ((pipenv (+python-executable-find "pipenv")) (pipenv-project (pipenv-project-p))) (let ((default-directory pipenv-project) (python-shell-interpreter-args @@ -26,8 +23,8 @@ (defun +python/open-ipython-repl () "Open an IPython REPL." (interactive) - (let ((python-shell-interpreter "ipython") - (python-shell-interpreter-args +python-jupyter-repl-args)) + (let ((python-shell-interpreter (or (+python-executable-find "ipython") "ipython")) + (python-shell-interpreter-args (string-join +python-ipython-repl-args " "))) (+python/open-repl))) ;;;###autoload @@ -35,56 +32,23 @@ "Open a Jupyter console." (interactive) (add-to-list 'python-shell-completion-native-disabled-interpreters "jupyter") - (let ((python-shell-interpreter "jupyter") - (python-shell-interpreter-args (format "console %s" +python-jupyter-repl-args))) + (let ((python-shell-interpreter (or (+python-executable-find "jupyter") "jupyter")) + (python-shell-interpreter-args (format "console %s" (string-join +python-jupyter-repl-args " ")))) (+python/open-repl))) -(defun +python--extract-version (prefix str) - (when str - (format "%s%s" prefix (cadr (split-string str " "))))) - ;;;###autoload -(defun +python-version () - "Return the currently installed version of python on your system or active in -the current pipenv. - -This is not necessarily aware of env management tools like virtualenv, pyenv or -pipenv, unless those tools have modified the PATH that Emacs picked up when you -started it." - (condition-case _ - (if-let* ((proot (and (fboundp 'pipenv-project-p) - (pipenv-project-p)))) - (let* ((default-directory proot) - (v (car (process-lines "pipenv" "run" "python" "--version")))) - (puthash proot - (+python--extract-version "Pipenv " v) - +python-version-cache)) - (puthash (or (doom-project-root) default-directory) - (+python--extract-version - "Python " - (car (process-lines python-shell-interpreter "--version"))) - +python-version-cache)) - (error "Python"))) - - -;; -;; Hooks - -;;;###autoload -(defun +python|update-version (&rest _) - "Update `+python--version' by consulting `+python-version' function." - (setq +python--version - (or (gethash (or (and (fboundp 'pipenv-project-p) - (pipenv-project-p)) - (doom-project-root) - default-directory) - +python-version-cache) - (+python-version)))) - -;;;###autoload -(defun +python|update-version-in-all-buffers (&rest _) - "Update `+python-version' in all buffers in `python-mode'." - (dolist (buffer (doom-buffers-in-mode 'python-mode)) - (setq +python-version-cache (clrhash +python-version-cache)) - (with-current-buffer buffer - (+python|update-version)))) +(defun +python-executable-find (exe) + "TODO" + (if (file-name-absolute-p exe) + (file-executable-p exe) + (let ((exe-root (format "bin/%s" exe))) + (cond ((when python-shell-virtualenv-root + (let ((bin (expand-file-name exe-root python-shell-virtualenv-root))) + (if (file-exists-p bin) bin)))) + ((when (require 'conda nil t) + (let ((bin (expand-file-name (concat conda-env-current-name "/" exe-root) + (conda-env-location)))) + (if (file-executable-p bin) bin)))) + ((when-let* ((bin (projectile-locate-dominating-file default-directory "bin/python"))) + (setq-local doom-modeline-python-executable (expand-file-name "bin/python" bin)))) + ((executable-find exe)))))) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 717725a4e..62888bdf0 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -1,19 +1,13 @@ ;;; lang/python/config.el -*- lexical-binding: t; -*- -(defconst +python-mode-line-indicator '("" +python--version) - "Format for the python version/env indicator in the mode-line.") - -(defvar +python-ipython-repl-args "-i --simple-prompt --no-color-info" +(defvar +python-ipython-repl-args '("-i" "--simple-prompt" "--no-color-info") "CLI arguments to initialize ipython with when `+python/open-ipython-repl' is called.") -(defvar +python-jupyter-repl-args "--simple-prompt" +(defvar +python-jupyter-repl-args '("--simple-prompt") "CLI arguments to initialize 'jupiter console %s' with when `+python/open-ipython-repl' is called.") -(defvar-local +python--version nil - "The python version in the current buffer.") - ;; ;; Packages @@ -47,7 +41,7 @@ called.") :return "return" :yield "yield") (when (featurep! +lsp) - (add-hook 'python-mode-hook #'+lsp|init)) + (add-hook 'python-mode-local-vars-hook #'lsp!)) (define-key python-mode-map (kbd "DEL") nil) ; interferes with smartparens (sp-local-pair 'python-mode "'" nil @@ -55,19 +49,16 @@ called.") sp-point-after-word-p sp-point-before-same-p)) - (setq-hook! 'python-mode-hook tab-width python-indent-offset) + ;; Affects pyenv and conda + (advice-add #'pythonic-activate :after-while #'+modeline|update-env-in-all-windows) + (advice-add #'pythonic-deactivate :after #'+modeline|clear-env-in-all-windows) - ;; Add python/pipenv version string to the major mode in the modeline - (defun +python|adjust-mode-line () - (setq mode-name +python-mode-line-indicator)) - (add-hook 'python-mode-hook #'+python|adjust-mode-line) - - (add-hook 'python-mode-hook #'+python|update-version)) + (setq-hook! 'python-mode-hook tab-width python-indent-offset)) (def-package! anaconda-mode :unless (featurep! +lsp) - :hook python-mode + :hook python-mode-local-vars :init (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") anaconda-mode-eldoc-as-single-line t) @@ -151,10 +142,20 @@ called.") (_ (pipenv-project-p))) (format "PIPENV_MAX_DEPTH=9999 %s run %%c %%o %%s %%a" bin) "%c %o %s %a"))) - (:description . "Run Python script"))) + (:description . "Run Python script")))) - (advice-add #'pipenv-activate :after-while #'+python|update-version-in-all-buffers) - (advice-add #'pipenv-deactivate :after-while #'+python|update-version-in-all-buffers)) + +(def-package! pyvenv + :after python + :init + (when (featurep! :ui modeline) + (add-hook 'pyvenv-post-activate-hooks #'+modeline|update-env-in-all-windows) + (add-hook 'pyvenv-pre-deactivate-hooks #'+modeline|clear-env-in-all-windows)) + :config + (add-hook 'hack-local-variables-hook #'pyvenv-track-virtualenv) + (add-to-list 'global-mode-string + '(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name " ")) + 'append)) (def-package! pyenv-mode @@ -163,21 +164,7 @@ called.") :config (pyenv-mode +1) (when (executable-find "pyenv") - (add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv")))) - (advice-add #'pyenv-mode-set :after #'+python|update-version-in-all-buffers) - (advice-add #'pyenv-mode-unset :after #'+python|update-version-in-all-buffers)) - - -(def-package! pyvenv - :when (featurep! +pyvenv) - :after python - :config - (defun +python-current-pyvenv () pyvenv-virtual-env-name) - (add-hook 'pyvenv-post-activate-hooks #'+python|update-version-in-all-buffers) - (add-hook 'pyvenv-post-deactivate-hooks #'+python|update-version-in-all-buffers) - (add-to-list '+python-mode-line-indicator - '(pyvenv-virtual-env-name (" venv:" pyvenv-virtual-env-name)) - 'append)) + (add-to-list 'exec-path (expand-file-name "shims" (or (getenv "PYENV_ROOT") "~/.pyenv"))))) (def-package! conda @@ -213,9 +200,7 @@ called.") ;; integration with term/eshell (conda-env-initialize-interactive-shells) (after! eshell (conda-env-initialize-eshell)) - - (add-hook 'conda-postactivate-hook #'+python|update-version-in-all-buffers) - (add-hook 'conda-postdeactivate-hook #'+python|update-version-in-all-buffers) - (add-to-list '+python-mode-line-indicator - '(conda-env-current-name (" conda:" conda-env-current-name)) + + (add-to-list 'global-mode-string + '(conda-env-current-name (" conda:" conda-env-current-name " ")) 'append)) diff --git a/modules/lang/python/packages.el b/modules/lang/python/packages.el index f492a90d9..193f5d4bd 100644 --- a/modules/lang/python/packages.el +++ b/modules/lang/python/packages.el @@ -9,10 +9,9 @@ ;; Environmet management (package! pipenv) +(package! pyvenv) (when (featurep! +pyenv) (package! pyenv-mode)) -(when (featurep! +pyvenv) - (package! pyvenv)) (when (featurep! +conda) (package! conda)) diff --git a/modules/lang/ruby/autoload.el b/modules/lang/ruby/autoload.el index cc2b21323..8f9519283 100644 --- a/modules/lang/ruby/autoload.el +++ b/modules/lang/ruby/autoload.el @@ -1,8 +1,5 @@ ;;; lang/ruby/autoload.el -*- lexical-binding: t; -*- -(defvar +ruby-version-cache (make-hash-table :test 'equal) - "TODO") - ;;;###autoload (defun +ruby|cleanup-robe-servers () "Clean up dangling inf robe processes if there are no more `enh-ruby-mode' @@ -16,38 +13,3 @@ buffers open." (when (processp process) (kill-process (get-buffer-process inf-buffer)) (kill-buffer inf-buffer))))))) - -;;;###autoload -(defun +ruby-version () - "Return the currently installed version of ruby on your system (the first -ruby executable found in your PATH). - -This is not necessarily aware of env management tools like virtualenv, pyenv or -pipenv, unless those tools have modified the PATH that Emacs picked up when you -started it." - (condition-case _ - (let ((version-str (car (process-lines "ruby" "--version")))) - (puthash (or (doom-project-root) default-directory) - (format "Ruby %s" (cadr (split-string version-str " "))) - +ruby-version-cache)) - (error "Ruby"))) - - -;; -;; Hooks - -;;;###autoload -(defun +ruby|update-version (&rest _) - "Update `+ruby--version' by consulting `+ruby-version' function." - (setq +ruby--version - (or (gethash (or (doom-project-root) default-directory) - +ruby-version-cache) - (+ruby-version)))) - -;;;###autoload -(defun +ruby|update-version-in-all-buffers (&rest _) - "Update `+ruby--version' in all `enh-ruby-mode' buffers." - (dolist (buffer (doom-buffers-in-mode 'enh-ruby-mode)) - (setq +ruby-version-cache (clrhash +ruby-version-cache)) - (with-current-buffer buffer - (+ruby|update-version)))) diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index cb391bd37..bf4384ca3 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -1,12 +1,5 @@ ;;; lang/ruby/config.el -*- lexical-binding: t; -*- -(defvar +ruby-mode-line-indicator '("" +ruby--version) - "Format for the ruby version/env indicator in the mode-line.") - -(defvar-local +ruby--version nil - "The ruby version in the current buffer.") - - ;; ;; Packages @@ -27,21 +20,14 @@ (set-repl-handler! '(ruby-mode enh-ruby-mode) #'inf-ruby) (when (featurep! +lsp) - (add-hook 'enh-ruby-mode-hook #'+lsp|init)) + (add-hook 'enh-ruby-mode-hook #'lsp!)) (after! company-dabbrev-code (add-to-list 'company-dabbrev-code-modes 'enh-ruby-mode nil #'eq) (add-to-list 'company-dabbrev-code-modes 'ruby-mode nil #'eq)) ;; so class and module pairs work - (setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6) - - ;; Add ruby version string to the major mode in the modeline - (defun +ruby|adjust-mode-line () - (setq mode-name +ruby-mode-line-indicator)) - (add-hook 'enh-ruby-mode-hook #'+ruby|adjust-mode-line) - - (add-hook 'enh-ruby-mode-hook #'+ruby|update-version)) + (setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6)) (def-package! robe @@ -135,25 +121,29 @@ ;; Rake (("task" "namespace") () "end"))) - (if (featurep! :feature evil) - (add-hook 'rspec-mode-hook #'evil-normalize-keymaps) - (setq rspec-verifiable-mode-keymap (make-sparse-keymap) - rspec-mode-keymap (make-sparse-keymap))) + (when (featurep! :feature evil) + (add-hook 'rspec-mode-hook #'evil-normalize-keymaps)) :config (map! :localleader - :map rspec-mode-map :prefix "t" - "r" #'rspec-rerun + :map (rspec-verifiable-mode-map rspec-dired-mode-map rspec-mode-map) "a" #'rspec-verify-all - "s" #'rspec-verify-single + "r" #'rspec-rerun + :map (rspec-verifiable-mode-map rspec-mode-map) "v" #'rspec-verify "c" #'rspec-verify-continue - "e" #'rspec-toggle-example-pendingness - "f" #'rspec-verify-method "l" #'rspec-run-last-failed - "m" #'rspec-verify-matching + "T" #'rspec-toggle-spec-and-target "t" #'rspec-toggle-spec-and-target-find-example - "T" #'rspec-toggle-spec-and-target)) + :map rspec-verifiable-mode-map + "f" #'rspec-verify-method + "m" #'rspec-verify-matching + :map rspec-mode-map + "s" #'rspec-verify-single + "e" #'rspec-toggle-example-pendingness + :map rspec-dired-mode-map + "v" #'rspec-dired-verify + "s" #'rspec-dired-verify-single)) (def-package! minitest diff --git a/modules/lang/ruby/packages.el b/modules/lang/ruby/packages.el index c0b98741e..3c010a6e6 100644 --- a/modules/lang/ruby/packages.el +++ b/modules/lang/ruby/packages.el @@ -6,7 +6,8 @@ (package! enh-ruby-mode) (package! yard-mode) (package! inf-ruby) -(package! robe) +(unless (featurep! +lsp) + (package! robe)) (when (featurep! :completion company) (package! company-inf-ruby)) diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el index 67eac44f8..8f3153e3e 100644 --- a/modules/lang/rust/config.el +++ b/modules/lang/rust/config.el @@ -6,7 +6,7 @@ (setq rust-indent-method-chain t) (when (featurep! +lsp) - (add-hook 'rust-mode-hook #'+lsp|init)) + (add-hook 'rust-mode-hook #'lsp!)) (map! :map rust-mode-map :localleader diff --git a/modules/lang/scala/config.el b/modules/lang/scala/config.el index 96870e0b5..ff3b9cb63 100644 --- a/modules/lang/scala/config.el +++ b/modules/lang/scala/config.el @@ -31,4 +31,4 @@ (def-package! lsp-scala :when (featurep! +lsp) :after scala-mode - :init (add-hook 'scala-mode-hook #'+lsp|init)) + :init (add-hook 'scala-mode-hook #'lsp!)) diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el index ec566c392..0129d78ac 100644 --- a/modules/lang/swift/config.el +++ b/modules/lang/swift/config.el @@ -22,4 +22,4 @@ (def-package! lsp-sourcekit :when (featurep! +lsp) :after swift-mode - :init (add-hook 'swift-mode-hook #'+lsp|init)) + :init (add-hook 'swift-mode-hook #'lsp!)) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index 2c9ea541e..3d9635125 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -36,7 +36,7 @@ ;; Tools (when (featurep! +lsp) - (add-hook! (css-mode sass-mode less-css-mode) #'+lsp|init)) + (add-hook! (css-mode sass-mode less-css-mode) #'lsp!)) (def-package! counsel-css diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index e11894818..7b6bb02bb 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -142,4 +142,4 @@ (when (featurep! +lsp) - (add-hook! (html-mode web-mode) #'+lsp|init)) + (add-hook! (html-mode web-mode) #'lsp!)) diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index 6c74a7a1d..cff636d44 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -18,8 +18,7 @@ ;; Handles whitespace (tabs/spaces) settings externally. This way projects can ;; specify their own formatting rules. (def-package! editorconfig - :defer 3 - :after-call (doom-enter-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config ;; Register missing indent variables (unless (assq 'mips-mode editorconfig-indentation-alist) @@ -56,7 +55,7 @@ extension, try to guess one." specified by editorconfig." (when (or (gethash 'indent_style props) (gethash 'indent_size props)) - (setq doom-inhibit-indent-detection t))) + (setq doom-inhibit-indent-detection 'editorconfig))) (add-hook 'editorconfig-after-apply-functions #'+editorconfig|disable-indent-detection) ;; Editorconfig makes indentation too rigid in Lisp modes, so tell diff --git a/modules/tools/flycheck/autoload.el b/modules/tools/flycheck/autoload.el index ccbaa1c94..cc8167c4e 100644 --- a/modules/tools/flycheck/autoload.el +++ b/modules/tools/flycheck/autoload.el @@ -1,44 +1,13 @@ ;;; tools/flycheck/autoload.el -*- lexical-binding: t; -*- -(defun +flycheck-show-popup (errors) - "TODO" - (if (and EMACS26+ - (featurep! +childframe) - (display-graphic-p)) - (flycheck-posframe-show-posframe errors) - (flycheck-popup-tip-show-popup errors))) - -(defun +flycheck-cleanup-popup () - "TODO" - (when (display-graphic-p) - (flycheck-popup-tip-delete-popup))) - ;;;###autoload -(define-minor-mode +flycheck-popup-mode - "TODO" - :lighter nil - :group 'doom - (require 'flycheck-popup-tip) - (let ((hooks '(post-command-hook focus-out-hook))) - (cond - ;; Use our display function and remember the old one but only if we haven't - ;; yet configured it, to avoid activating twice. - ((and +flycheck-popup-mode - (not (eq flycheck-display-errors-function - #'+flycheck-show-popup))) - (setq flycheck-popup-tip-old-display-function - flycheck-display-errors-function - flycheck-display-errors-function - #'+flycheck-show-popup) - (dolist (hook hooks) - (add-hook hook #'+flycheck-cleanup-popup nil t))) - ;; Reset the display function and remove ourselves from all hooks but only - ;; if the mode is still active. - ((and (not +flycheck-popup-mode) - (eq flycheck-display-errors-function - #'+flycheck-show-popup)) - (setq flycheck-display-errors-function - flycheck-popup-tip-old-display-function - flycheck-popup-tip-old-display-function nil) - (dolist (hook hooks) - (remove-hook hook '+flycheck-cleanup-popup t)))))) +(defun +flycheck|init-popups () + "Activate `flycheck-posframe-mode' if available and in GUI Emacs. +Activate `flycheck-popup-tip-mode' otherwise. +Do nothing if `lsp-ui-mode' is active and `lsp-ui-sideline-enable' is non-nil." + (unless (and (bound-and-true-p lsp-ui-mode) + lsp-ui-sideline-enable) + (if (and (fboundp 'flycheck-posframe-mode) + (display-graphic-p)) + (flycheck-posframe-mode +1) + (flycheck-popup-tip-mode +1)))) diff --git a/modules/tools/flycheck/config.el b/modules/tools/flycheck/config.el index 0d1cf2af8..57c98bfda 100644 --- a/modules/tools/flycheck/config.el +++ b/modules/tools/flycheck/config.el @@ -9,7 +9,7 @@ (def-package! flycheck :commands (flycheck-list-errors flycheck-buffer) - :after-call (doom-enter-buffer-hook after-find-file) + :after-call (doom-switch-buffer-hook after-find-file) :config ;; Emacs feels snappier without checks on newline (setq flycheck-check-syntax-automatically (delq 'new-line flycheck-check-syntax-automatically)) @@ -32,13 +32,14 @@ (def-package! flycheck-popup-tip :commands (flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup) - :init (add-hook 'flycheck-mode-hook #'+flycheck-popup-mode) + :init (add-hook 'flycheck-mode-hook #'+flycheck|init-popups) :config (setq flycheck-popup-tip-error-prefix "✕ ")) (def-package! flycheck-posframe :when (and EMACS26+ (featurep! +childframe)) - :commands flycheck-posframe-show-posframe + :defer t + :init (add-hook 'flycheck-mode-hook #'+flycheck|init-popups) :config (setq flycheck-posframe-warning-prefix "⚠ " flycheck-posframe-info-prefix "··· " diff --git a/modules/tools/flyspell/autoload.el b/modules/tools/flyspell/autoload.el new file mode 100644 index 000000000..4e6b162cf --- /dev/null +++ b/modules/tools/flyspell/autoload.el @@ -0,0 +1,27 @@ +;;; tools/flyspell/autoload.el -*- lexical-binding: t; -*- + +;;;###autodef +(defalias 'flyspell-mode! #'flyspell-mode) + +(defvar +flyspell--predicate-alist nil + "TODO") + +;;;###autodef +(defun set-flyspell-predicate! (modes predicate) + "TODO" + (declare (indent defun)) + (dolist (mode (doom-enlist modes) +flyspell--predicate-alist) + (add-to-list '+flyspell--predicate-alist (cons mode predicate)))) + +;;;###autoload +(defun +flyspell|init-predicate () + "TODO" + (when-let* ((pred (assq major-mode +flyspell--predicate-alist))) + (setq-local flyspell-generic-check-word-predicate (cdr pred)))) + +;;;###autoload +(defun +flyspell-correction-at-point-p (&optional point) + "TODO" + (cl-loop for ov in (overlays-at (or point (point))) + if (overlay-get ov 'flyspell-overlay) + return t)) diff --git a/modules/tools/flyspell/config.el b/modules/tools/flyspell/config.el index 2b6958c5e..b80c0e54b 100644 --- a/modules/tools/flyspell/config.el +++ b/modules/tools/flyspell/config.el @@ -19,7 +19,15 @@ Since spellchecking can be slow in some buffers, this can be disabled with: ispell-extra-args '("--sug-mode=ultra" "--run-together")) (setq-hook! 'text-mode-hook - ispell-extra-args (remove "--run-together" ispell-extra-args))) + ispell-extra-args (remove "--run-together" ispell-extra-args)) + + (defun +flyspell*setup-ispell-extra-args (orig-fun &rest args) + (let ((ispell-extra-args (remove "--run-together" ispell-extra-args))) + (ispell-kill-ispell t) + (apply orig-fun args) + (ispell-kill-ispell t))) + (advice-add #'ispell-word :around #'+flyspell*setup-ispell-extra-args) + (advice-add #'flyspell-auto-correct-word :around #'+flyspell*setup-ispell-extra-args)) ((executable-find "hunspell") (setq ispell-program-name "hunspell" @@ -35,25 +43,30 @@ Since spellchecking can be slow in some buffers, this can be disabled with: nil utf-8))))) - (add-to-list 'ispell-extra-args "--dont-tex-check-comments") - - (defun +flyspell*setup-ispell-extra-args (orig-fun &rest args) - (let ((ispell-extra-args (remove "--run-together" ispell-extra-args))) - (ispell-kill-ispell t) - (apply orig-fun args) - (ispell-kill-ispell t))) - (advice-add #'ispell-word :around #'+flyspell*setup-ispell-extra-args) - (advice-add #'flyspell-auto-correct-word :around #'+flyspell*setup-ispell-extra-args)) + (add-to-list 'ispell-extra-args "--dont-tex-check-comments")) ;; `flyspell' (built-in) -(setq flyspell-issue-welcome-flag nil) +(progn + (setq flyspell-issue-welcome-flag nil) -(defun +flyspell|immediately () - "Spellcheck the buffer when `flyspell-mode' is enabled." - (when (and flyspell-mode +flyspell-immediately) - (flyspell-buffer))) -(add-hook 'flyspell-mode-hook #'+flyspell|immediately) + (defun +flyspell|inhibit-duplicate-detection-maybe () + "Don't mark duplicates when style/grammar linters are present. +e.g. proselint and langtool." + (when (or (executable-find "proselint") + (featurep 'langtool)) + (setq-local flyspell-mark-duplications-flag nil))) + (add-hook 'flyspell-mode-hook #'+flyspell|inhibit-duplicate-detection-maybe) + + (defun +flyspell|immediately () + "Spellcheck the buffer when `flyspell-mode' is enabled." + (when (and flyspell-mode +flyspell-immediately) + (flyspell-buffer))) + (add-hook 'flyspell-mode-hook #'+flyspell|immediately) + + ;; Ensure mode-local predicates declared with `set-flyspell-predicate!' are + ;; used in their respective major modes. + (add-hook 'flyspell-mode-hook #'+flyspell|init-predicate)) (def-package! flyspell-correct diff --git a/modules/tools/flyspell/doctor.el b/modules/tools/flyspell/doctor.el new file mode 100644 index 000000000..9d1cb3286 --- /dev/null +++ b/modules/tools/flyspell/doctor.el @@ -0,0 +1,4 @@ + +(unless (or (executable-find "aspell") + (executable-find "hunspell")) + (warn! "Could not find aspell or hunspell. Flyspell will fall back to ispell, which may not work.")) diff --git a/modules/tools/lsp/autoload.el b/modules/tools/lsp/autoload.el index c13d9efd7..5dd061dba 100644 --- a/modules/tools/lsp/autoload.el +++ b/modules/tools/lsp/autoload.el @@ -1,7 +1,4 @@ ;;; feature/lsp/autoload.el -*- lexical-binding: t; -*- -;;;###autoload -(defun +lsp|init () - "Enable LSP as late as possible, to allow users to customize it via file or -dir local variables." - (add-hook 'hack-local-variables-hook #'lsp nil t)) +;;;###autodef +(defalias 'lsp! #'lsp) diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el index e2e690289..723c4dfd2 100644 --- a/modules/tools/lsp/config.el +++ b/modules/tools/lsp/config.el @@ -1,5 +1,13 @@ ;;; tools/lsp/config.el -*- lexical-binding: t; -*- +(setq lsp-session-file (concat doom-etc-dir "lsp-session") + lsp-auto-guess-root t + lsp-keep-workspace-alive nil) + +;; Don't prompt to restart LSP servers while quitting Emacs +(add-hook! 'kill-emacs-hook (setq lsp-restart 'ignore)) + + (def-package! lsp-ui :hook (lsp-mode . lsp-ui-mode) :config @@ -14,6 +22,7 @@ :definition #'lsp-ui-peek-find-definitions :references #'lsp-ui-peek-find-references)) + (def-package! company-lsp :when (featurep! :completion company) :after lsp-mode diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index d474bfca4..6ae496461 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -34,10 +34,6 @@ It is passed a user and repository name.") (not (eq major-mode 'magit-process-mode))))) (add-to-list 'doom-real-buffer-functions #'+magit-buffer-p nil #'eq) - ;; modeline isn't helpful in magit - (add-hook! '(magit-mode-hook magit-popup-mode-hook) - #'hide-mode-line-mode) - ;; properly kill leftover magit buffers on quit (define-key magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit) @@ -83,7 +79,7 @@ It is passed a user and repository name.") "zz" #'evil-scroll-line-to-center "%" #'magit-gitflow-popup) ;; Don't use ESC to close magit - (evil-define-key* 'normal magit-mode-map [escape] nil) + (evil-define-key* 'normal magit-status-mode-map [tab] #'magit-section-toggle) (after! git-rebase (dolist (key '(("M-k" . "gk") ("M-j" . "gj"))) (when-let* ((desc (assoc (car key) evil-magit-rebase-commands-w-descriptions))) diff --git a/modules/tools/pdf/+modeline.el b/modules/tools/pdf/+modeline.el deleted file mode 100644 index f3fe0532c..000000000 --- a/modules/tools/pdf/+modeline.el +++ /dev/null @@ -1,20 +0,0 @@ -;;; tools/pdf/+modeline.el -*- lexical-binding: t; -*- - -(def-modeline-segment! +pdf-pages - "Current and total page indicator for PDF documents." - (format "P %d/%d" (pdf-view-current-page) (pdf-cache-number-of-pages))) - -(if (featurep! :ui modeline) - (def-modeline-format! '+pdf - '(+modeline-matches " " +modeline-buffer-id " " +pdf-pages) - '(+modeline-major-mode (vc-mode (" " +modeline-vcs)))) - (def-modeline! '+pdf - '(bar matches " " buffer-info " " +pdf-pages) - '(major-mode vcs))) - -(defun +pdf|init-modeline () - (funcall (if (featurep! :ui modeline) - #'set-modeline! - #'doom-set-modeline) - '+pdf)) -(add-hook 'pdf-tools-enabled-hook #'+pdf|init-modeline) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index 681cb29f2..2f22d3983 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -6,12 +6,7 @@ (unless noninteractive (pdf-tools-install)) - (define-key! pdf-view-mode-map - "q" #'kill-this-buffer) - - (when (featurep! :feature evil +everywhere) - (evil-define-key* 'normal pdf-view-mode-map - "q" #'kill-this-buffer)) + (map! :map pdf-view-mode-map :gn "q" #'kill-this-buffer) (defun +pdf|cleanup-windows () "Kill left-over annotation buffers when the document is killed." @@ -28,9 +23,6 @@ (setq-default pdf-view-display-size 'fit-page) ;; Turn off cua so copy works (add-hook! 'pdf-view-mode-hook (cua-mode 0)) - ;; Custom modeline that removes useless info and adds page numbers - (when (or (featurep! :ui doom-modeline) (featurep! :ui modeline)) - (load! "+modeline")) ;; Handle PDF-tools related popups better (set-popup-rule! "^\\*Outline*" :side 'right :size 40 :select nil) ;; The next rules are not needed, they are defined in modules/ui/popups/+hacks.el diff --git a/modules/tools/vterm/autoload.el b/modules/tools/vterm/autoload.el index 52f663a73..d3b36d73a 100644 --- a/modules/tools/vterm/autoload.el +++ b/modules/tools/vterm/autoload.el @@ -5,6 +5,8 @@ "Open a terminal buffer in the current window. If ARG (universal argument) is non-nil, cd into the current project's root." (interactive "P") + (unless (fboundp 'module-load) + (user-error "Your build of Emacs lacks dynamic modules support and cannot load vterm")) (let ((default-directory (if arg (or (doom-project-root) default-directory) @@ -16,6 +18,8 @@ non-nil, cd into the current project's root." "Open a terminal popup window. If ARG (universal argument) is non-nil, cd into the current project's root." (interactive "P") + (unless (fboundp 'module-load) + (user-error "Your build of Emacs lacks dynamic modules support and cannot load vterm")) (let ((default-directory (if arg (or (doom-project-root) default-directory) diff --git a/modules/tools/vterm/config.el b/modules/tools/vterm/config.el index ab89ee5ee..900678c4a 100644 --- a/modules/tools/vterm/config.el +++ b/modules/tools/vterm/config.el @@ -3,7 +3,7 @@ (def-package! vterm :when (fboundp 'module-load) :defer t - :init (setq vterm-install t) + :preface (setq vterm-install t) :config (set-env! "SHELL") (set-popup-rule! "^vterm" :size 0.25 :vslot -4 :select t :quit nil :ttl 0) diff --git a/modules/tools/vterm/doctor.el b/modules/tools/vterm/doctor.el index 866c4b62d..0700bafdb 100644 --- a/modules/tools/vterm/doctor.el +++ b/modules/tools/vterm/doctor.el @@ -1,13 +1,13 @@ ;;; tools/vterm/doctor.el -*- lexical-binding: t; -*- (unless (executable-find "vterm-ctrl") - (warn! "Couldn't find libvterm. Vterm module won't compile.")) + (warn! "Couldn't find libvterm. Vterm module won't compile")) (unless (executable-find "make") - (warn! "Couldn't find make command. Vterm module won't compile.")) + (warn! "Couldn't find make command. Vterm module won't compile")) (unless (executable-find "cmake") (warn! "Couldn't find cmake command. Vterm module won't compile")) (unless (fboundp 'module-load) - (warn! "Your emacs don't have MODULES support. Vterm module won't work.")) + (warn! "Your emacs doesn't have MODULES support. Vterm module won't work")) diff --git a/modules/tools/wakatime/autoload.el b/modules/tools/wakatime/autoload.el index 4e9a81da7..e07702ab8 100644 --- a/modules/tools/wakatime/autoload.el +++ b/modules/tools/wakatime/autoload.el @@ -7,7 +7,7 @@ "If non-nil, obfuscate files and only show what projects you're working on.") ;;;###autoload -(add-hook 'doom-post-init-hook #'+wakatime|delayed-autostart) +(add-hook 'doom-init-modules-hook #'+wakatime|delayed-autostart) ;;;###autoload (defun +wakatime/setup () @@ -44,14 +44,14 @@ warning)." (make-directory +wakatime-home t))) (global-wakatime-mode +1)) ;; - (remove-hook 'doom-exit-buffer-hook #'+wakatime|autostart) + (remove-hook 'doom-switch-buffer-hook #'+wakatime|autostart) (advice-remove 'after-find-file #'+wakatime|autostart)) ;;;###autoload (defun +wakatime|delayed-autostart (&rest _) "Lazily initialize `wakatime-mode' until the next time you switch buffers or open a file." - (add-hook 'doom-exit-buffer-hook #'+wakatime|autostart) + (add-hook 'doom-switch-buffer-hook #'+wakatime|autostart) ;; this is necessary in case the user opens emacs with file arguments (advice-add 'after-find-file :before #'+wakatime|autostart)) diff --git a/modules/ui/doom-dashboard/autoload.el b/modules/ui/doom-dashboard/autoload.el index 63a55b153..898ae8a4f 100644 --- a/modules/ui/doom-dashboard/autoload.el +++ b/modules/ui/doom-dashboard/autoload.el @@ -1,5 +1,10 @@ ;;; ui/doom-dashboard/autoload.el -*- lexical-binding: t; -*- +(defun +doom-dashboard--help-echo () + (when-let* ((btn (button-at (point))) + (msg (button-get btn 'help-echo))) + (message "%s" msg))) + ;;;###autoload (defun +doom-dashboard/open (frame) "Switch to the dashboard in the current window, of the current FRAME." @@ -12,10 +17,12 @@ (defun +doom-dashboard/forward-button (n) "Like `forward-button', but don't wrap." (interactive "p") - (forward-button n nil)) + (forward-button n nil) + (+doom-dashboard--help-echo)) ;;;###autoload (defun +doom-dashboard/backward-button (n) "Like `backward-button', but don't wrap." (interactive "p") - (backward-button n nil)) + (backward-button n nil) + (+doom-dashboard--help-echo)) diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index abfcf4837..cfaa23df9 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -44,11 +44,12 @@ Possible values: (defvar +doom-dashboard-menu-sections '(("Reload last session" :icon (all-the-icons-octicon "history" :face 'font-lock-keyword-face) - :when (and (bound-and-true-p persp-mode) - (file-exists-p (expand-file-name persp-auto-save-fname - persp-save-dir))) + :when (cond ((require 'persp-mode nil t) + (file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir))) + ((require 'desktop nil t) + (file-exists-p (desktop-full-file-name)))) :face (:inherit (font-lock-keyword-face bold)) - :action +workspace/load-last-session) + :action doom/quickload-session) ("Open org-agenda" :icon (all-the-icons-octicon "calendar" :face 'font-lock-keyword-face) :when (fboundp 'org-agenda) @@ -65,7 +66,7 @@ Possible values: ("Open private configuration" :icon (all-the-icons-octicon "tools" :face 'font-lock-keyword-face) :when (file-directory-p doom-private-dir) - :action +default/find-in-config) + :action doom/open-private-config) ("Open user manual" :icon (all-the-icons-octicon "book" :face 'font-lock-keyword-face) :when (file-exists-p (expand-file-name "index.org" doom-docs-dir)) @@ -107,12 +108,13 @@ PLIST can have the following properties: initial-buffer-choice (when (or (daemonp) (not (cl-loop for arg in (cdr command-line-args) - if (and (string-match-p "^[^-]" arg) - (file-exists-p arg)) + if (or (equal arg "--restore") + (and (string-match-p "^[^-]" arg) + (file-exists-p arg))) return t))) #'+doom-dashboard-initial-buffer)) -(add-hook 'window-setup-hook #'+doom-dashboard|init) +(add-hook 'doom-init-ui-hook #'+doom-dashboard|init 'append) ;; @@ -194,7 +196,7 @@ PLIST can have the following properties: (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-enter-buffer-hook #'+doom-dashboard|reload-on-kill) + (add-hook 'doom-switch-buffer-hook #'+doom-dashboard|reload-on-kill) ;; `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) @@ -226,7 +228,8 @@ whose dimensions may not be fully initialized by the time this is run." (defun +doom-dashboard|resize (&rest _) "Recenter the dashboard, and reset its margins and fringes." - (let ((windows (get-buffer-window-list (doom-fallback-buffer) nil t))) + (let ((windows (get-buffer-window-list (doom-fallback-buffer) nil t)) + buffer-list-update-hook) (dolist (win windows) (set-window-start win 0) (set-window-fringes win 0 0) @@ -272,7 +275,8 @@ project (which may be different across perspective)." (defun +doom-dashboard-initial-buffer () "Returns buffer to display on startup. Designed for `initial-buffer-choice'." - (get-buffer-create +doom-dashboard-name)) + (let (buffer-list-update-hook) + (get-buffer-create +doom-dashboard-name))) (defun +doom-dashboard-p (buffer) "Returns t if BUFFER is the dashboard buffer." @@ -416,16 +420,21 @@ controlled by `+doom-dashboard-pwd-policy'." #',action))) 'face (or face 'font-lock-keyword-face) 'follow-link t - 'help-echo label) + 'help-echo + (format "%s (%s)" label + (propertize (symbol-name action) 'face 'font-lock-constant-face))) (format "%-37s" (buffer-string))) ;; Lookup command keys dynamically (or (when-let* ((key (where-is-internal action nil t))) - (propertize (with-temp-buffer - (save-excursion (insert (key-description key))) - (while (re-search-forward "<\\([^>]+\\)>" nil t) - (replace-match (upcase (substring (match-string 1) 0 3)))) - (buffer-string)) - 'face 'font-lock-constant-face)) + (with-temp-buffer + (save-excursion (insert (key-description key))) + (while (re-search-forward "<\\([^>]+\\)>" nil t) + (let ((str (match-string 1))) + (replace-match + (upcase (if (< (length str) 3) + str + (substring str 0 3)))))) + (propertize (buffer-string) 'face 'font-lock-constant-face))) "")))) (if (display-graphic-p) "\n\n" diff --git a/modules/ui/doom-modeline/README.org b/modules/ui/doom-modeline/README.org deleted file mode 100644 index 28590ef8f..000000000 --- a/modules/ui/doom-modeline/README.org +++ /dev/null @@ -1,52 +0,0 @@ -#+TITLE: :ui doom-modeline - -This module customizes the Emacs mode-line. - -The DOOM modeline was designed for minimalism, and offers: - -+ A match count panel (for ~evil-search~, ~iedit~ and ~evil-substitute~) -+ An indicator for recording a macro -+ Local python/ruby version in the major-mode -+ A customizable mode-line height (see ~+doom-modeline-height~) -+ An error/warning count segment for flycheck - -[[/../screenshots/ml.png]] -[[/../screenshots/ml-search.png]] -[[/../screenshots/ml-subst.png]] -[[/../screenshots/ml-macro.png]] -[[/../screenshots/ml-version.png]] -[[/../screenshots/ml-errors.png]] - -* Table of Contents :TOC: -- [[#install][Install]] -- [[#extracting-my-modeline][Extracting my modeline]] -- [[#troubleshooting][Troubleshooting]] - - [[#where-are-my-minor-modes][Where are my minor modes?]] - -* Install -This module requires the fonts included with ~all-the-icons~ to be installed. - -Run ~M-x all-the-icons-install-fonts~ to do so. - -* Extracting my modeline -Some might want my modeline without the DOOM config altogether. I've tried to make this easier for you, but there are a few things you'll need to do: - -+ Ensure [[https://github.com/bbatsov/projectile][projectile]] and [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] are installed. -+ Ensure ~projectile-mode~ is enabled. -+ Ensure the fonts included with ~all-the-icons~ are installed (~M-x all-the-icons-install-fonts~). -+ Replace ~def-package!~ calls with ~use-package~. -+ Replace ~doom-project-root~ calls with ~projectile-project-root~. -+ The ~+doom-modeline--make-xpm~ function is memoized with the ~def-memoized!~ macro. Change ~def-memoized!~ to ~defun~. -+ Copy the ~add-hook!~ macro definition from [[/core/core-lib.el][core/core-lib.el]]. -+ Copy the following macros and functions from [[/core/core-ui.el][core/core-ui.el]]: - + ~def-modeline-segment!~ - + ~def-modeline!~ - + ~doom--prepare-modeline-segments~ - + ~doom-modeline~ - + ~doom-set-modeline~ - -That /should/ be everything. As I have never used this out of my config I can't guarantee immediate success, but I'd be happy to help you out if you file an issue. - -* Troubleshooting -** Where are my minor modes? -I didn't need it, so I removed it. Run ~M-x doom/what-minor-mode~ to investigate what minor modes are currently active. diff --git a/modules/ui/doom-modeline/autoload.el b/modules/ui/doom-modeline/autoload.el deleted file mode 100644 index 1c188ef9f..000000000 --- a/modules/ui/doom-modeline/autoload.el +++ /dev/null @@ -1,19 +0,0 @@ -;;; ui/doom-modeline/autoload.el -*- lexical-binding: t; -*- - -(defvar +doom-modeline--old-bar-height nil) -;;;###autoload -(defun +doom-modeline|resize-for-big-font () - "Adjust the modeline's height when `doom-big-font-mode' is enabled. This was -made to be added to `doom-big-font-mode-hook'." - (unless +doom-modeline--old-bar-height - (setq +doom-modeline--old-bar-height +doom-modeline-height)) - (let ((default-height +doom-modeline--old-bar-height)) - (if doom-big-font-mode - (let* ((font-size (font-get doom-font :size)) - (big-size (font-get doom-big-font :size)) - (ratio (/ (float big-size) font-size))) - (setq +doom-modeline-height (ceiling (* default-height ratio 0.75)))) - (setq +doom-modeline-height default-height)) - ;; already has a variable watcher in Emacs 26+ - (unless EMACS26+ (+doom-modeline|refresh-bars)))) - diff --git a/modules/ui/doom-modeline/config.el b/modules/ui/doom-modeline/config.el deleted file mode 100644 index f5f8d708f..000000000 --- a/modules/ui/doom-modeline/config.el +++ /dev/null @@ -1,830 +0,0 @@ -;;; ui/doom-modeline/config.el -*- lexical-binding: t; -*- - -;; We handle this ourselves -(setq projectile-dynamic-mode-line nil) - - -;; -;; Modeline library - -(defvar doom--modeline-fn-alist ()) -(defvar doom--modeline-var-alist ()) - -(defmacro def-modeline-segment! (name &rest body) - "Defines a modeline segment and byte compiles it." - (declare (indent defun) (doc-string 2)) - (let ((sym (intern (format "doom-modeline-segment--%s" name))) - (docstring (if (stringp (car body)) - (pop body) - (format "%s modeline segment" name)))) - (cond ((and (symbolp (car body)) - (not (cdr body))) - (add-to-list 'doom--modeline-var-alist (cons name (car body))) - `(add-to-list 'doom--modeline-var-alist (cons ',name ',(car body)))) - (t - (add-to-list 'doom--modeline-fn-alist (cons name sym)) - `(progn - (fset ',sym (lambda () ,docstring ,@body)) - (add-to-list 'doom--modeline-fn-alist (cons ',name ',sym)) - ,(unless (bound-and-true-p byte-compile-current-file) - `(let (byte-compile-warnings) - (byte-compile #',sym)))))))) - -(defun doom--prepare-modeline-segments (segments) - (let (forms it) - (dolist (seg segments) - (cond ((stringp seg) - (push seg forms)) - ((symbolp seg) - (cond ((setq it (cdr (assq seg doom--modeline-fn-alist))) - (push (list it) forms)) - ((setq it (cdr (assq seg doom--modeline-var-alist))) - (push it forms)) - ((error "%s is not a defined segment" seg)))) - ((error "%s is not a valid segment" seg)))) - (nreverse forms))) - -(defun def-modeline! (name lhs &optional rhs) - "Defines a modeline format and byte-compiles it. NAME is a symbol to identify -it (used by `doom-modeline' for retrieval). LHS and RHS are lists of symbols of -modeline segments defined with `def-modeline-segment!'. - -Example: - (def-modeline! 'minimal - '(bar matches \" \" buffer-info) - '(media-info major-mode)) - (doom-set-modeline 'minimal t)" - (let ((sym (intern (format "doom-modeline-format--%s" name))) - (lhs-forms (doom--prepare-modeline-segments lhs)) - (rhs-forms (doom--prepare-modeline-segments rhs))) - (defalias sym - (lambda () - (let ((lhs (eval `(list ,@lhs-forms) t)) - (rhs (eval `(list ,@rhs-forms) t))) - (let ((rhs-str (format-mode-line rhs))) - (list lhs - (propertize - " " 'display - `((space :align-to (- (+ right right-fringe right-margin) - ,(+ 1 (string-width rhs-str)))))) - rhs-str)))) - (concat "Modeline:\n" - (format " %s\n %s" - (prin1-to-string lhs) - (prin1-to-string rhs)))))) - -(defun doom-modeline (key) - "Returns a mode-line configuration associated with KEY (a symbol). Throws an -error if it doesn't exist." - (let ((fn (intern-soft (format "doom-modeline-format--%s" key)))) - (when (functionp fn) - `(:eval (,fn))))) - -(defun doom-set-modeline (key &optional default) - "Set the modeline format. Does nothing if the modeline KEY doesn't exist. If -DEFAULT is non-nil, set the default mode-line for all buffers." - (when-let* ((modeline (doom-modeline key))) - (setf (if default - (default-value 'mode-line-format) - (buffer-local-value 'mode-line-format (current-buffer))) - (list "%e" modeline)))) - - -;; -;; Custom faces - -(defgroup +doom-modeline nil - "TODO" - :group 'faces) - -(defface doom-modeline-buffer-path - '((t (:inherit (mode-line-emphasis bold)))) - "Face used for the dirname part of the buffer path." - :group '+doom-modeline) - -(defface doom-modeline-buffer-file - '((t (:inherit (mode-line-buffer-id bold)))) - "Face used for the filename part of the mode-line buffer path." - :group '+doom-modeline) - -(defface doom-modeline-buffer-modified - '((t (:inherit (error bold) :background nil))) - "Face used for the 'unsaved' symbol in the mode-line." - :group '+doom-modeline) - -(defface doom-modeline-buffer-major-mode - '((t (:inherit (mode-line-emphasis bold)))) - "Face used for the major-mode segment in the mode-line." - :group '+doom-modeline) - -(defface doom-modeline-highlight - '((t (:inherit mode-line-emphasis))) - "Face for bright segments of the mode-line." - :group '+doom-modeline) - -(defface doom-modeline-panel - '((t (:inherit mode-line-highlight))) - "Face for 'X out of Y' segments, such as `+doom-modeline--anzu', `+doom-modeline--evil-substitute' and -`iedit'" - :group '+doom-modeline) - -(defface doom-modeline-info - `((t (:inherit (success bold)))) - "Face for info-level messages in the modeline. Used by `*vc'." - :group '+doom-modeline) - -(defface doom-modeline-warning - `((t (:inherit (warning bold)))) - "Face for warnings in the modeline. Used by `*flycheck'" - :group '+doom-modeline) - -(defface doom-modeline-urgent - `((t (:inherit (error bold)))) - "Face for errors in the modeline. Used by `*flycheck'" - :group '+doom-modeline) - -;; Bar -(defface doom-modeline-bar '((t (:inherit highlight))) - "The face used for the left-most bar on the mode-line of an active window." - :group '+doom-modeline) - -(defface doom-modeline-eldoc-bar '((t (:inherit shadow))) - "The face used for the left-most bar on the mode-line when eldoc-eval is -active." - :group '+doom-modeline) - -(defface doom-modeline-inactive-bar '((t (:inherit warning :inverse-video t))) - "The face used for the left-most bar on the mode-line of an inactive window." - :group '+doom-modeline) - - -;; -;; Packages - -;; anzu and evil-anzu expose current/total state that can be displayed in the -;; mode-line. -(def-package! anzu - :after-call isearch-mode - :config - (setq anzu-cons-mode-line-p nil - anzu-minimum-input-length 1 - anzu-search-threshold 250) - (global-anzu-mode +1) - - (defun +doom-modeline*fix-anzu-count (positions here) - (cl-loop for (start . end) in positions - collect t into before - when (and (>= here start) (<= here end)) - return (length before) - finally return 0)) - (advice-add #'anzu--where-is-here :override #'+doom-modeline*fix-anzu-count) - - ;; Avoid anzu conflicts across buffers - (mapc #'make-variable-buffer-local - '(anzu--total-matched anzu--current-position anzu--state - anzu--cached-count anzu--cached-positions anzu--last-command - anzu--last-isearch-string anzu--overflow-p)) - ;; Ensure anzu state is cleared when searches & iedit are done - (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) - (add-hook 'doom-escape-hook #'anzu--reset-status t) - (add-hook 'iedit-mode-end-hook #'anzu--reset-status)) - - -(def-package! evil-anzu - :when (featurep! :feature evil) - :after-call (evil-ex-start-search evil-ex-start-word-search)) - - -;; fish-style modeline -(def-package! shrink-path - :commands (shrink-path-prompt shrink-path-file-mixed)) - - -;; Keep `+doom-modeline-current-window' up-to-date -(defvar +doom-modeline-current-window (frame-selected-window)) -(defun +doom-modeline|set-selected-window (&rest _) - "Sets `+doom-modeline-current-window' appropriately" - (when-let* ((win (frame-selected-window))) - (unless (minibuffer-window-active-p win) - (setq +doom-modeline-current-window win) - (force-mode-line-update)))) - -(defun +doom-modeline|unset-selected-window () - (setq +doom-modeline-current-window nil) - (force-mode-line-update)) - -(add-hook 'window-configuration-change-hook #'+doom-modeline|set-selected-window) -(add-hook 'doom-enter-window-hook #'+doom-modeline|set-selected-window) -(with-no-warnings - (cond ((not (boundp 'after-focus-change-function)) - (add-hook 'focus-in-hook #'+doom-modeline|set-selected-window) - (add-hook 'focus-out-hook #'+doom-modeline|unset-selected-window)) - ((defun +doom-modeline|refresh-frame () - (setq +doom-modeline-current-window nil) - (cl-loop for frame in (frame-list) - if (eq (frame-focus-state frame) t) - return (setq +doom-modeline-current-window (frame-selected-window frame))) - (force-mode-line-update)) - (add-function :after after-focus-change-function #'+doom-modeline|refresh-frame)))) - - -;; -;; Variables - -(defvar +doom-modeline-height 23 - "How tall the mode-line should be (only respected in GUI emacs).") - -(defvar +doom-modeline-bar-width 3 - "How wide the mode-line bar should be (only respected in GUI emacs).") - -(defvar +doom-modeline-buffer-file-name-style 'truncate-upto-project - "Determines the style used by `+doom-modeline-buffer-file-name'. - -Given ~/Projects/FOSS/emacs/lisp/comint.el -truncate-upto-project => ~/P/F/emacs/lisp/comint.el -truncate-upto-root => ~/P/F/e/lisp/comint.el -truncate-all => ~/P/F/e/l/comint.el -relative-from-project => emacs/lisp/comint.el -relative-to-project => lisp/comint.el -file-name => comint.el") - -;; externs -(defvar anzu--state nil) -(defvar evil-mode nil) -(defvar evil-state nil) -(defvar evil-visual-selection nil) -(defvar iedit-mode nil) -(defvar all-the-icons-scale-factor) -(defvar all-the-icons-default-adjust) - - -;; -;; Modeline helpers - -(defun active () - (eq (selected-window) +doom-modeline-current-window)) - -(defun +doom-modeline--make-xpm (face width height) - "Create an XPM bitmap. Inspired by `powerline''s `pl/make-xpm'." - (propertize - " " 'display - (let ((data (make-list height (make-list width 1))) - (color (or (face-background face nil t) "None"))) - (ignore-errors - (create-image - (concat - (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," - (length (car data)) - (length data) - color - color) - (apply #'concat - (cl-loop with idx = 0 - with len = (length data) - for dl in data - do (cl-incf idx) - collect - (concat "\"" - (cl-loop for d in dl - if (= d 0) collect (string-to-char " ") - else collect (string-to-char ".")) - (if (eq idx len) "\"};" "\",\n"))))) - 'xpm t :ascent 'center))))) - -(defun +doom-modeline-buffer-file-name () - "Propertized `buffer-file-name' based on `+doom-modeline-buffer-file-name-style'." - (let ((buffer-file-name (or (buffer-file-name (buffer-base-buffer)) ""))) - (unless buffer-file-truename - (setq buffer-file-truename (file-truename buffer-file-name))) - (propertize - (pcase +doom-modeline-buffer-file-name-style - (`truncate-upto-project - (+doom-modeline--buffer-file-name buffer-file-name buffer-file-truename 'shrink)) - (`truncate-upto-root - (+doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename)) - (`truncate-all - (+doom-modeline--buffer-file-name-truncate buffer-file-name buffer-file-truename t)) - (`relative-to-project - (+doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename)) - (`relative-from-project - (+doom-modeline--buffer-file-name-relative buffer-file-name buffer-file-truename 'include-project)) - (`file-name - (propertize (file-name-nondirectory buffer-file-name) - 'face - (let ((face (or (and (buffer-modified-p) - 'doom-modeline-buffer-modified) - (and (active) - 'doom-modeline-buffer-file)))) - (when face `(:inherit ,face)))))) - 'help-echo buffer-file-truename))) - -(defun +doom-modeline--buffer-file-name-truncate (file-path true-file-path &optional truncate-tail) - "Propertized `buffer-file-name' that truncates every dir along path. -If TRUNCATE-TAIL is t also truncate the parent directory of the file." - (let ((dirs (shrink-path-prompt (file-name-directory true-file-path))) - (active (active))) - (if (null dirs) - (propertize "%b" 'face (if active 'doom-modeline-buffer-file)) - (let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified))) - (let ((dirname (car dirs)) - (basename (cdr dirs)) - (dir-faces (or modified-faces (if active 'doom-modeline-project-root-dir))) - (file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) - (concat (propertize (concat dirname - (if truncate-tail (substring basename 0 1) basename) - "/") - 'face (if dir-faces `(:inherit ,dir-faces))) - (propertize (file-name-nondirectory file-path) - 'face (if file-faces `(:inherit ,file-faces))))))))) - -(defun +doom-modeline--buffer-file-name-relative (_file-path true-file-path &optional include-project) - "Propertized `buffer-file-name' showing directories relative to project's root only." - (let ((root (or (doom-project-root) default-directory)) - (active (active))) - (if (null root) - (propertize "%b" 'face (if active 'doom-modeline-buffer-file)) - (let* ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified)) - (relative-dirs (file-relative-name (file-name-directory true-file-path) - (if include-project (concat root "../") root))) - (relative-faces (or modified-faces (if active 'doom-modeline-buffer-path))) - (file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) - (if (equal "./" relative-dirs) (setq relative-dirs "")) - (concat (propertize relative-dirs 'face (if relative-faces `(:inherit ,relative-faces))) - (propertize (file-name-nondirectory true-file-path) - 'face (if file-faces `(:inherit ,file-faces)))))))) - -(defun +doom-modeline--buffer-file-name (file-path _true-file-path &optional truncate-project-root-parent) - "Propertized `buffer-file-name'. -If TRUNCATE-PROJECT-ROOT-PARENT is t space will be saved by truncating it down -fish-shell style. - -Example: -~/Projects/FOSS/emacs/lisp/comint.el => ~/P/F/emacs/lisp/comint.el" - (let* ((project-root (or (doom-project-root) default-directory)) - (file-name-split (shrink-path-file-mixed project-root - (file-name-directory file-path) - file-path)) - (active (active))) - (if (null file-name-split) - (propertize "%b" 'face (if active 'doom-modeline-buffer-file)) - (pcase-let ((`(,root-path-parent ,project ,relative-path ,file-path) file-name-split)) - (let ((modified-faces (if (buffer-modified-p) 'doom-modeline-buffer-modified))) - (let ((sp-faces (or modified-faces (if active 'font-lock-comment-face))) - (project-faces (or modified-faces (if active 'font-lock-string-face))) - (relative-faces (or modified-faces (if active 'doom-modeline-buffer-path))) - (file-faces (or modified-faces (if active 'doom-modeline-buffer-file)))) - (let ((sp-props `(,@(if sp-faces `(:inherit ,sp-faces)) ,@(if active '(:weight bold)))) - (project-props `(,@(if project-faces `(:inherit ,project-faces)) ,@(if active '(:weight bold)))) - (relative-props `(,@(if relative-faces `(:inherit ,relative-faces)))) - (file-props `(,@(if file-faces `(:inherit ,file-faces))))) - (concat (propertize (if truncate-project-root-parent - root-path-parent - (abbreviate-file-name project-root)) - 'face sp-props) - (propertize (concat project "/") 'face project-props) - (if relative-path (propertize relative-path 'face relative-props)) - (propertize file-path 'face file-props))))))))) - - -;; -;; buffer information - -(def-modeline-segment! buffer-default-directory - "Displays `default-directory'. This is for special buffers like the scratch -buffer where knowing the current project directory is important." - (let ((face (if (active) 'doom-modeline-buffer-path))) - (concat (if (display-graphic-p) " ") - (all-the-icons-octicon - "file-directory" - :face face - :v-adjust -0.05 - :height 1.25) - (propertize (concat " " (abbreviate-file-name default-directory)) - 'face face)))) - -(def-modeline-segment! buffer-info - "Combined information about the current buffer, including the current working -directory, the file name, and its state (modified, read-only or non-existent)." - (concat (cond (buffer-read-only - (concat (all-the-icons-octicon - "lock" - :face 'doom-modeline-warning - :v-adjust -0.05) - " ")) - ((buffer-modified-p) - (concat (all-the-icons-faicon - "floppy-o" - :face 'doom-modeline-buffer-modified - :v-adjust -0.0575) - " ")) - ((and buffer-file-name - (not (file-exists-p buffer-file-name))) - (concat (all-the-icons-octicon - "circle-slash" - :face 'doom-modeline-urgent - :v-adjust -0.05) - " ")) - ((buffer-narrowed-p) - (concat (all-the-icons-octicon - "fold" - :face 'doom-modeline-warning - :v-adjust -0.05) - " "))) - (if buffer-file-name - (+doom-modeline-buffer-file-name) - "%b"))) - -(def-modeline-segment! buffer-info-simple - "Display only the current buffer's name, but with fontification." - (propertize - "%b" - 'face (cond ((and buffer-file-name (buffer-modified-p)) - 'doom-modeline-buffer-modified) - ((active) 'doom-modeline-buffer-file)))) - -;; (defvar +doom-modeline--encoding nil) -;; (def-modeline-segment! buffer-encoding -;; "TODO" -;; +doom-modeline--encoding) - -;; (add-variable-watcher -;; 'buffer-file-coding-system -;; (lambda (_sym val op _where) -;; (when (eq op 'set) -;; (setq +doom-modeline--encoding -;; (concat (pcase (coding-system-eol-type val) -;; (0 "LF ") -;; (1 "CRLF ") -;; (2 "CR ")) -;; (let ((sys (coding-system-plist val))) -;; (if (memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) -;; "UTF-8" -;; (upcase (symbol-name (plist-get sys :name))))) -;; " "))))) - -(def-modeline-segment! buffer-encoding - "Displays the encoding and eol style of the buffer the same way Atom does." - (concat (pcase (coding-system-eol-type buffer-file-coding-system) - (0 "LF ") - (1 "CRLF ") - (2 "CR ")) - (let ((sys (coding-system-plist buffer-file-coding-system))) - (cond ((memq (plist-get sys :category) '(coding-category-undecided coding-category-utf-8)) - "UTF-8") - (t (upcase (symbol-name (plist-get sys :name)))))) - " ")) - - -;; -;; major-mode - -(def-modeline-segment! major-mode - "The major mode, including process, environment and text-scale info." - (propertize - (concat (format-mode-line mode-name) - (when (stringp mode-line-process) - mode-line-process) - (and (boundp 'text-scale-mode-amount) - (/= text-scale-mode-amount 0) - (format " (%+d)" text-scale-mode-amount))) - 'face (if (active) 'doom-modeline-buffer-major-mode))) - - -;; -;; vcs - -(defvar-local +doom-modeline--vcs nil) -(defun +doom-modeline--update-vcs () - (setq +doom-modeline--vcs - (when (and vc-mode buffer-file-name) - (let* ((backend (vc-backend buffer-file-name)) - (state (vc-state buffer-file-name backend))) - (let ((face 'mode-line-inactive) - (active (active)) - (all-the-icons-default-adjust -0.1)) - (concat " " - (cond ((memq state '(edited added)) - (if active (setq face 'doom-modeline-info)) - (all-the-icons-octicon - "git-compare" - :face face - :v-adjust -0.05)) - ((eq state 'needs-merge) - (if active (setq face 'doom-modeline-info)) - (all-the-icons-octicon "git-merge" :face face)) - ((eq state 'needs-update) - (if active (setq face 'doom-modeline-warning)) - (all-the-icons-octicon "arrow-down" :face face)) - ((memq state '(removed conflict unregistered)) - (if active (setq face 'doom-modeline-urgent)) - (all-the-icons-octicon "alert" :face face)) - (t - (if active (setq face 'font-lock-doc-face)) - (all-the-icons-octicon - "git-compare" - :face face - :v-adjust -0.05))) - " " - (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) - 'face (if active face)) - " ")))))) -(add-hook 'after-revert-hook #'+doom-modeline--update-vcs) -(add-hook 'after-save-hook #'+doom-modeline--update-vcs) -(add-hook 'find-file-hook #'+doom-modeline--update-vcs t) -(advice-add #'vc-refresh-state :after #'+doom-modeline--update-vcs) - -(def-modeline-segment! vcs - "Displays the current branch, colored based on its state." - +doom-modeline--vcs) - - -;; -;; flycheck - -(defvar +doom-modeline-vspc - (propertize " " 'face 'variable-pitch) - "TODO") - -(defun +doom-ml-icon (icon &optional text face voffset) - "Displays an octicon ICON with FACE, followed by TEXT. Uses -`all-the-icons-octicon' to fetch the icon." - (concat (if vc-mode " " " ") - (when icon - (concat - (all-the-icons-material icon :face face :height 1.1 :v-adjust (or voffset -0.2)) - (if text +doom-modeline-vspc))) - (if text (propertize text 'face face)) - (if vc-mode " " " "))) - -(defvar-local +doom-modeline--flycheck nil) -(add-hook 'flycheck-status-changed-functions #'+doom-modeline|update-flycheck-segment) -(add-hook 'flycheck-mode-hook #'+doom-modeline|update-flycheck-segment) - -(defun +doom-modeline|update-flycheck-segment (&optional status) - (setq +doom-modeline--flycheck - (pcase status - ('finished (if flycheck-current-errors - (let-alist (flycheck-count-errors flycheck-current-errors) - (let ((sum (+ (or .error 0) (or .warning 0)))) - (+doom-ml-icon "do_not_disturb_alt" - (number-to-string sum) - (if .error 'doom-modeline-urgent 'doom-modeline-warning) - -0.25))) - (+doom-ml-icon "check" nil 'doom-modeline-info))) - ('running (+doom-ml-icon "access_time" nil 'font-lock-doc-face -0.25)) - ('no-checker (+doom-ml-icon "sim_card_alert" "-" 'font-lock-doc-face)) - ('errored (+doom-ml-icon "sim_card_alert" "Error" 'doom-modeline-urgent)) - ('interrupted (+doom-ml-icon "pause" "Interrupted" 'font-lock-doc-face))))) - -(def-modeline-segment! flycheck - "Displays color-coded flycheck error status in the current buffer with pretty -icons." - +doom-modeline--flycheck) - - -;; -;; selection-info - -(defsubst doom-column (pos) - (save-excursion (goto-char pos) - (current-column))) - -(defvar-local +doom-modeline-enable-word-count nil - "If non-nil, a word count will be added to the selection-info modeline -segment.") - -(defun +doom-modeline|enable-word-count () (setq +doom-modeline-enable-word-count t)) -(add-hook 'text-mode-hook #'+doom-modeline|enable-word-count) - -(def-modeline-segment! selection-info - "Information about the current selection, such as how many characters and -lines are selected, or the NxM dimensions of a block selection." - (when (and (active) (or mark-active (eq evil-state 'visual))) - (cl-destructuring-bind (beg . end) - (if (eq evil-state 'visual) - (cons evil-visual-beginning evil-visual-end) - (cons (region-beginning) (region-end))) - (propertize - (let ((lines (count-lines beg (min end (point-max))))) - (concat (cond ((or (bound-and-true-p rectangle-mark-mode) - (eq 'block evil-visual-selection)) - (let ((cols (abs (- (doom-column end) - (doom-column beg))))) - (format "%dx%dB" lines cols))) - ((eq evil-visual-selection 'line) - (format "%dL" lines)) - ((> lines 1) - (format "%dC %dL" (- end beg) lines)) - ((format "%dC" (- end beg)))) - (when +doom-modeline-enable-word-count - (format " %dW" (count-words beg end))))) - 'face 'doom-modeline-highlight)))) - - -;; -;; matches (anzu, evil-substitute, iedit, macro) - -(defun +doom-modeline--macro-recording () - "Display current Emacs or evil macro being recorded." - (when (and (active) (or defining-kbd-macro executing-kbd-macro)) - (let ((sep (propertize " " 'face 'doom-modeline-panel))) - (concat sep - (propertize (if (bound-and-true-p evil-this-macro) - (char-to-string evil-this-macro) - "Macro") - 'face 'doom-modeline-panel) - sep - (all-the-icons-octicon "triangle-right" - :face 'doom-modeline-panel - :v-adjust -0.05) - sep)))) - -(defsubst +doom-modeline--anzu () - "Show the match index and total number thereof. Requires `anzu', also -`evil-anzu' if using `evil-mode' for compatibility with `evil-search'." - (when (and anzu--state (not iedit-mode)) - (propertize - (let ((here anzu--current-position) - (total anzu--total-matched)) - (cond ((eq anzu--state 'replace-query) - (format " %d replace " total)) - ((eq anzu--state 'replace) - (format " %d/%d " here total)) - (anzu--overflow-p - (format " %s+ " total)) - (t - (format " %s/%d " here total)))) - 'face (if (active) 'doom-modeline-panel)))) - -(defsubst +doom-modeline--evil-substitute () - "Show number of matches for evil-ex substitutions and highlights in real time." - (when (and evil-mode - (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) - (assq 'evil-ex-global-match evil-ex-active-highlights-alist) - (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) - (propertize - (let ((range (if evil-ex-range - (cons (car evil-ex-range) (cadr evil-ex-range)) - (cons (line-beginning-position) (line-end-position)))) - (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) - (if pattern - (format " %s matches " (how-many pattern (car range) (cdr range))) - " - ")) - 'face (if (active) 'doom-modeline-panel)))) - -(defun doom-themes--overlay-sort (a b) - (< (overlay-start a) (overlay-start b))) - -(defsubst +doom-modeline--iedit () - "Show the number of iedit regions matches + what match you're on." - (when (and iedit-mode iedit-occurrences-overlays) - (propertize - (let ((this-oc (or (let ((inhibit-message t)) - (iedit-find-current-occurrence-overlay)) - (progn (iedit-prev-occurrence) - (iedit-find-current-occurrence-overlay)))) - (length (length iedit-occurrences-overlays))) - (format " %s/%d " - (if this-oc - (- length - (length (memq this-oc (sort (append iedit-occurrences-overlays nil) - #'doom-themes--overlay-sort))) - -1) - "-") - length)) - 'face (if (active) 'doom-modeline-panel)))) - -(def-modeline-segment! matches - "Displays: 1. the currently recording macro, 2. A current/total for the -current search term (with anzu), 3. The number of substitutions being conducted -with `evil-ex-substitute', and/or 4. The number of active `iedit' regions." - (let ((meta (concat (+doom-modeline--macro-recording) - (+doom-modeline--anzu) - (+doom-modeline--evil-substitute) - (+doom-modeline--iedit)))) - (or (and (not (equal meta "")) meta) - (if buffer-file-name " %I ")))) - - -;; -;; media-info - -(def-modeline-segment! media-info - "Metadata regarding the current file, such as dimensions for images." - ;; TODO Include other information - (cond ((eq major-mode 'image-mode) - (cl-destructuring-bind (width . height) - (image-size (image-get-display-property) :pixels) - (format " %dx%d " width height))))) - - -;; -;; bar - -(defvar +doom-modeline--bar-active nil) -(defvar +doom-modeline--bar-inactive nil) -(def-modeline-segment! bar - "The bar regulates the height of the mode-line in GUI Emacs. -Returns \"\" to not break --no-window-system." - (if window-system - (if (active) - +doom-modeline--bar-active - +doom-modeline--bar-inactive) - "")) - -(when EMACS26+ - (add-variable-watcher - '+doom-modeline-height - (lambda (_sym val op _where) - (when (and (eq op 'set) (integerp val)) - (+doom-modeline|refresh-bars +doom-modeline-bar-width val)))) - - (add-variable-watcher - '+doom-modeline-bar-width - (lambda (_sym val op _where) - (when (and (eq op 'set) (integerp val)) - (+doom-modeline|refresh-bars val +doom-modeline-height)))) - - (add-hook 'doom-big-font-mode-hook #'+doom-modeline|resize-for-big-font)) - - -;; -;; Mode lines - -(def-modeline! 'main - '(bar matches " " buffer-info " %l:%c %p " selection-info) - '(buffer-encoding major-mode vcs flycheck)) - -(def-modeline! 'minimal - '(bar matches " " buffer-info) - '(media-info major-mode)) - -(def-modeline! 'special - '(bar matches " " buffer-info-simple " %l:%c %p " selection-info) - '(buffer-encoding major-mode flycheck)) - -(def-modeline! 'project - '(bar buffer-default-directory) - '(major-mode)) - -(def-modeline! 'media - '(bar " %b ") - '(media-info major-mode)) - - -;; -;; Hooks - -(defun +doom-modeline|refresh-bars (&optional width height) - (setq +doom-modeline--bar-active - (+doom-modeline--make-xpm 'doom-modeline-bar - (or width +doom-modeline-bar-width) - (or height +doom-modeline-height)) - +doom-modeline--bar-inactive - (+doom-modeline--make-xpm 'doom-modeline-inactive-bar - (or width +doom-modeline-bar-width) - (or height +doom-modeline-height)))) - -(defun +doom-modeline|init () - ;; Create bars - (+doom-modeline|refresh-bars) - (unless after-init-time - ;; These buffers are already created and don't get modelines. For the love - ;; of Emacs, someone give the man a modeline! - (dolist (bname '("*scratch*" "*Messages*")) - (with-current-buffer bname - (doom-set-modeline 'main))))) - -(defun +doom-modeline|set-special-modeline () - (doom-set-modeline 'special)) - -(defun +doom-modeline|set-media-modeline () - (doom-set-modeline 'media)) - -(defun +doom-modeline|set-project-modeline () - (doom-set-modeline 'project)) - - -;; -;; Bootstrap - -(doom-set-modeline 'main t) ; set default modeline - -(add-hook 'doom-load-theme-hook #'+doom-modeline|init) -(add-hook 'doom-scratch-buffer-hook #'+doom-modeline|set-special-modeline) -(add-hook '+doom-dashboard-mode-hook #'+doom-modeline|set-project-modeline) - -(add-hook 'image-mode-hook #'+doom-modeline|set-media-modeline) -(add-hook 'circe-mode-hook #'+doom-modeline|set-special-modeline) - -;; Ensure modeline is inactive when Emacs is unfocused (and active otherwise) -(defvar +doom-modeline-remap-face-cookie nil) -(defun +doom-modeline|focus () - (when +doom-modeline-remap-face-cookie - (require 'face-remap) - (face-remap-remove-relative +doom-modeline-remap-face-cookie))) -(defun +doom-modeline|unfocus () - (setq +doom-modeline-remap-face-cookie (face-remap-add-relative 'mode-line 'mode-line-inactive))) - -(add-hook 'focus-in-hook #'+doom-modeline|focus) -(add-hook 'focus-out-hook #'+doom-modeline|unfocus) diff --git a/modules/ui/doom-modeline/packages.el b/modules/ui/doom-modeline/packages.el deleted file mode 100644 index 72f5044df..000000000 --- a/modules/ui/doom-modeline/packages.el +++ /dev/null @@ -1,20 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; ui/doom-modeline/packages.el - -;;; These are the invisible dependencies -;; Required -;;(require 'evil) -;;(require 'projectile) -;;(require 'all-the-icons) - -;; Optional -;;(require 'flycheck) -;;(require 'iedit) -;;(require 'evil-multiedit) - -(package! anzu) - -(when (featurep! :feature evil) - (package! evil-anzu)) - -(package! shrink-path) diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index 59edd4386..26082e7e8 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -3,7 +3,7 @@ (defvar +doom-solaire-themes '((doom-city-lights . t) (doom-dracula . t) - (doom-molokai . t) + (doom-molokai) (doom-nord . t) (doom-nord-light . t) (doom-nova) @@ -27,6 +27,7 @@ :init (unless doom-theme (setq doom-theme 'doom-one)) + :config ;; improve integration w/ org-mode (add-hook 'doom-load-theme-hook #'doom-themes-org-config) ;; more Atom-esque file icons for neotree/treemacs @@ -58,6 +59,22 @@ ;; considered an unreal buffer, so solaire-mode must be restored. (add-hook 'org-capture-mode-hook #'turn-on-solaire-mode) + ;; On Emacs 26+, when point is on the last line and solaire-mode is remapping + ;; the hl-line face, hl-line's highlight bleeds into the rest of the window + ;; after eob. + (when EMACS26+ + (defun +doom--line-range () + (cons (line-beginning-position) + (cond ((let ((eol (line-end-position))) + (and (= eol (point-max)) + (/= eol (line-beginning-position)))) + (1- (line-end-position))) + ((or (eobp) + (= (line-end-position 2) (point-max))) + (line-end-position)) + ((line-beginning-position 2))))) + (setq hl-line-range-function #'+doom--line-range)) + ;; Because fringes can't be given a buffer-local face, they can look odd, so ;; we remove them in the minibuffer and which-key popups (they serve no ;; purpose there anyway). @@ -73,13 +90,4 @@ (add-hook! '(minibuffer-setup-hook window-configuration-change-hook) #'+doom|disable-fringes-in-minibuffer) - (solaire-global-mode +1) - - ;; Fix incompatibility with the mixed-pitch package which causes all buffers - ;; to be affected (by `mixed-pitch-mode') - (defun +doom*fix-mixed-pitch-mode (&optional arg) - (when (and mixed-pitch-mode (not arg)) - (mixed-pitch-mode -1)) - (solaire-mode -1) - (turn-on-solaire-mode)) - (advice-add #'mixed-pitch-mode :before #'+doom*fix-mixed-pitch-mode)) + (solaire-global-mode +1)) diff --git a/modules/ui/indent-guides/config.el b/modules/ui/indent-guides/config.el new file mode 100644 index 000000000..202d6efa0 --- /dev/null +++ b/modules/ui/indent-guides/config.el @@ -0,0 +1,20 @@ +;;; ui/indent-guides/config.el -*- lexical-binding: t; -*- + +(def-package! highlight-indent-guides + :hook ((prog-mode text-mode conf-mode) . highlight-indent-guides-mode) + :init + (setq highlight-indent-guides-method 'character + highlight-indent-guides-responsive 'top) + :config + ;; Don't display first level of indentation + (defun +indent-guides-for-all-but-first-column (level responsive display) + (unless (< level 1) + (highlight-indent-guides--highlighter-default level responsive display))) + (setq highlight-indent-guides-highlighter-function #'+indent-guides-for-all-but-first-column) + + (defun +indent-guides|disable-maybe () + (when highlight-indent-guides + (highlight-indent-guides -1))) + ;; `highlight-indent-guides' breaks in `visual-line-mode' + (add-hook 'visual-line-mode-hook #'+indent-guides|disable-maybe) + (add-hook 'org-indent-mode-hook #'+indent-guides|disable-maybe)) diff --git a/modules/ui/indent-guides/packages.el b/modules/ui/indent-guides/packages.el new file mode 100644 index 000000000..210c00e9d --- /dev/null +++ b/modules/ui/indent-guides/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; ui/indent-guides/packages.el + +(package! highlight-indent-guides) diff --git a/modules/ui/modeline/autoload.el b/modules/ui/modeline/autoload.el new file mode 100644 index 000000000..3c7478a65 --- /dev/null +++ b/modules/ui/modeline/autoload.el @@ -0,0 +1,45 @@ +;;; ui/modeline/autoload/modeline.el -*- lexical-binding: t; -*- + +;;;###autodef +(defalias 'def-modeline-format! #'doom-modeline-def-modeline) + +;;;###autodef +(defalias 'def-modeline-segment! #'doom-modeline-def-segment) + +;;;###autodef +(defalias 'set-modeline! #'doom-modeline-set-modeline) + + +(defvar +modeline--old-bar-height nil) +;;;###autoload +(defun +modeline|resize-for-big-font () + "Adjust the modeline's height when `doom-big-font-mode' is enabled. This was +made to be added to `doom-big-font-mode-hook'." + (unless +modeline--old-bar-height + (setq +modeline--old-bar-height doom-modeline-height)) + (let ((default-height +modeline--old-bar-height)) + (if doom-big-font-mode + (let* ((font-size (font-get doom-font :size)) + (big-size (font-get doom-big-font :size)) + (ratio (/ (float big-size) font-size))) + (setq doom-modeline-height (ceiling (* default-height ratio 0.75)))) + (setq doom-modeline-height default-height)) + ;; already has a variable watcher in Emacs 26+ + (unless EMACS26+ (doom-modeline-refresh-bars)))) + +;;;###autoload +(defun +modeline|update-env-in-all-windows (&rest _) + "Update version strings in all buffers." + (dolist (window (window-list)) + (with-selected-window window + (doom-modeline-update-env) + (force-mode-line-update)))) + +;;;###autoload +(defun +modeline|clear-env-in-all-windows (&rest _) + "Blank out version strings in all buffers." + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (setq doom-modeline-env--version + (bound-and-true-p doom-modeline-load-string)))) + (force-mode-line-update t)) diff --git a/modules/ui/modeline/autoload/modeline.el b/modules/ui/modeline/autoload/modeline.el deleted file mode 100644 index ef9d21a59..000000000 --- a/modules/ui/modeline/autoload/modeline.el +++ /dev/null @@ -1,19 +0,0 @@ -;;; ui/modeline/autoload/modeline.el -*- lexical-binding: t; -*- - -;; (defvar +modeline--old-bar-height nil) -;; ;;;###autoload -;; (defun +modeline|resize-for-big-font () -;; "Adjust the modeline's height when `doom-big-font-mode' is enabled. This was -;; made to be added to `doom-big-font-mode-hook'." -;; (unless +modeline--old-bar-height -;; (setq +modeline--old-bar-height +doom-modeline-height)) -;; (let ((default-height +modeline--old-bar-height)) -;; (if doom-big-font-mode -;; (let* ((font-size (font-get doom-font :size)) -;; (big-size (font-get doom-big-font :size)) -;; (ratio (/ (float big-size) font-size))) -;; (setq +doom-modeline-height (ceiling (* default-height ratio 0.75)))) -;; (setq +doom-modeline-height default-height)) -;; ;; already has a variable watcher in Emacs 26+ -;; (unless EMACS26+ (+doom-modeline|refresh-bars)))) - diff --git a/modules/ui/modeline/autoload/settings.el b/modules/ui/modeline/autoload/settings.el deleted file mode 100644 index 3e0441aae..000000000 --- a/modules/ui/modeline/autoload/settings.el +++ /dev/null @@ -1,104 +0,0 @@ -;;; ui/modeline/autoload/settings.el -*- lexical-binding: t; -*- - -(defvar +modeline--alist nil) - -(defun +modeline--segment-active-p (segment xs) - (cond ((null xs) nil) - ((listp xs) - (or (+modeline--segment-active-p segment (car xs)) - (+modeline--segment-active-p segment (cdr xs)))) - ((eq xs segment)))) - -;;;###autoload -(defun +modeline-segment-active-p (segment) - (or (+modeline--segment-active-p segment +modeline-format-left) - (+modeline--segment-active-p segment +modeline-format-right))) - -;;;###autodef -(defun def-modeline-format! (name left &optional right) - "Define a preset modeline format by name. - -NAME is a symbol. The convention is to use keywords for global formats, like -:main or :project, but to use regular symbols for buffer-local formats, like -'twitter and 'pdf. - -LEFT and RIGHT are lists that assume the same structure as `mode-line-format', -and make up the mode-line in two parts, separated by variable-width space, to -keep them left and right aligned respectively." - (setf (alist-get name +modeline--alist) (list left right))) - -;;;###autodef -(defmacro def-modeline-segment! (name &rest rest) - "TODO" - (declare (doc-string 2)) - (let ((docstring (if (and (stringp (car rest)) (cdr rest)) (pop rest))) - body) - (macroexp-progn - (if (not (keywordp (car rest))) - (append `((defvar-local ,name nil ,docstring) - (put ',name 'risky-local-variable t)) - (if (or (stringp (car rest)) - (memq (car (car-safe rest)) '(:eval :propertize))) - `((setq-default ,name ,(car rest))) - (let ((fn (intern (format "+modeline--%s" name)))) - `((fset ',fn (lambda () ,@rest)) - (byte-compile ',fn) - (setq-default ,name (quote (:eval (,fn)))))))) - ;; isolate body - (setq body rest) - (while (keywordp (car body)) - (setq body (cddr body))) - ;; - (cl-destructuring-bind (&key init faces on-hooks on-set &allow-other-keys) - rest - (let ((realvar (if (and body faces) - (intern (format "+modeline--var-%s" name)) - name))) - (append (when body - (if (or on-hooks on-set) - (let ((setterfn (intern (format "+modeline--set-%s" name))) - (varsetterfn (intern (format "+modeline--setvar-%s" name)))) - (append `((fset ',setterfn - (lambda (&rest _) - (when (+modeline-segment-active-p ',name) - (setq-local ,realvar ,(macroexp-progn body))))) - (byte-compile ',setterfn)) - (mapcar (lambda (hook) `(add-hook ',hook #',setterfn)) - on-hooks) - (when on-set - `((fset ',varsetterfn - (lambda (sym val op where) - (and (eq op 'set) where - (with-current-buffer where - (set sym val) - (,setterfn))))) - ,@(mapcan (lambda (var) `((add-variable-watcher ',var #',varsetterfn))) - on-set))))) - (setq init `(quote (:eval ,(macroexp-progn body)))) - nil)) - (if (eq realvar name) - `((defvar-local ,name nil ,docstring) - (setq-default ,name ,init)) - `((defvar-local ,realvar ,init) - (defvar-local ,name nil ,docstring) - (setq-default - ,name '(:eval (cond ((active) ,realvar) - (,realvar (substring-no-properties ,realvar))))))) - `((put ',name 'risky-local-variable t))))))))) - -;;;###autodef -(defun set-modeline! (name &optional default) - "Replace the current buffer's modeline with a preset mode-line format defined -with `def-modeline-format!'. - -If DEFAULT is non-nil, make it the default mode-line for all buffers." - (cl-check-type name symbol) - (let ((modeline (cdr (assq name +modeline--alist)))) - (unless modeline - (error "The %s modeline format does not exist" name)) - (if default - (setq-default +modeline-format-left `("" ,@(car modeline)) - +modeline-format-right `("" ,@(cadr modeline))) - (setq +modeline-format-left `("" ,@(car modeline)) - +modeline-format-right `("" ,@(cadr modeline)))) - (force-mode-line-update))) diff --git a/modules/ui/modeline/config.el b/modules/ui/modeline/config.el index a7212ee61..c4adbd6b3 100644 --- a/modules/ui/modeline/config.el +++ b/modules/ui/modeline/config.el @@ -1,723 +1,102 @@ ;;; ui/modeline/config.el -*- lexical-binding: t; -*- -;; This mode-line is experimental, Emacs 26+ only, may have buggy and is likely -;; to change. It also isn't feature complete, compared to :ui doom-modeline, but -;; it will eventually replace it. -;; -;; However, it is at least ten times faster than the original modeline, and more -;; flexible, what with `+modeline-format-left', `+modeline-format-right', and a -;; more powerful API for defining modelines and modeline segments. +;; TODO Add themes (default, minimal, spacemacs, etc) -;;;; Benchmarks -;; (benchmark-run 1000 (format-mode-line mode-line-format)) -;; Old system: ~0.563 - 0.604 -;; New system: ~0.036 - 0.061 +(def-package! doom-modeline + :hook (after-init . doom-modeline-mode) + :init + ;; prevent flash of unstyled modeline at startup + (setq-default mode-line-format nil) + ;; We display project info in the modeline ourselves + (setq projectile-dynamic-mode-line nil) + ;; Set these early so they don't trigger variable watchers + (setq doom-modeline-bar-width 3 + doom-modeline-github nil + doom-modeline-mu4e nil + doom-modeline-persp-name nil + doom-modeline-minor-modes nil + doom-modeline-major-mode-icon nil + doom-modeline-buffer-file-name-style 'relative-from-project) + :config + (add-hook 'doom-modeline-mode-hook #'size-indication-mode) ; filesize in modeline + (add-hook 'doom-modeline-mode-hook #'column-number-mode) ; cursor column in modeline -(defvar +modeline-width 3 - "How wide the mode-line bar should be (only respected in GUI emacs).") + (add-hook 'doom-big-font-mode-hook #'+modeline|resize-for-big-font) + (add-hook 'doom-load-theme-hook #'doom-modeline-refresh-bars) -(defvar +modeline-height 25 - "How tall the mode-line should be (only respected in GUI emacs).") + (add-hook '+doom-dashboard-mode-hook #'doom-modeline-set-project-modeline) -(defvar +modeline-bar-at-end nil - "If non-nil, the bar is placed at the end, instead of at the beginning of the -modeline.") + ;; Don't eager-load project.el. Doom only uses projectile anyway, for now. + (defun +modeline*project-root () + (or doom-modeline-project-root + (setq doom-modeline-project-root + (file-local-name + (or (and (featurep 'projectile) (ignore-errors (projectile-project-root))) + default-directory))))) + (advice-add #'doom-modeline-project-root :override #'+modeline*project-root) -(defvar +modeline-bar-invisible nil - "If non-nil, the bar is transparent, and only used to police the height of the -mode-line.") + ;; Magit -- modeline only where it's useful + (defun +modeline|hide-in-non-status-buffer () + (if (eq major-mode 'magit-status-mode) + (doom-modeline-set-project-modeline) + (hide-mode-line-mode))) + (add-hook 'magit-mode-hook #'+modeline|hide-in-non-status-buffer) -(defvar +modeline-buffer-path-function #'+modeline-file-path-with-project - "A function that returns, in list form, components of the buffer file name -display in the mode-line. + ;; Show indentation style in modeline. I'm not using + ;; `doom-modeline-def-segment' to prevent eager macro expansion from loading + ;; the package too soon. + (defun +modeline-indent-segment () + "indent modeline segment" + (propertize (format "%s%d" + (if indent-tabs-mode "⭾" "␣") + tab-width) + 'face (if (doom-modeline--active) 'mode-line 'mode-line-inactive) + 'mouse-face 'mode-line-highlight + 'help-echo + (let ((subsegs + (list (format "Indentation style: %s (%d wide)" + (if indent-tabs-mode "tabs" "spaces") + tab-width) + (cond ((eq doom-inhibit-indent-detection 'editorconfig) + (propertize "✓ Editorconfig applied" 'face 'success)) + (doom-inhibit-indent-detection + (propertize "✘ Indentation auto-detection disabled" 'face 'warning)) + ((bound-and-true-p dtrt-indent-original-indent) + (propertize (format "✓ Indentation auto-detected (original: %s)" + dtrt-indent-original-indent) + 'face 'success))) + (when (bound-and-true-p ws-butler-mode) + (propertize "✓ ws-butler active (whitespace cleanup on save)" + 'face 'success))))) + (string-join (delq nil subsegs) " ")))) + (add-to-list 'doom-modeline-fn-alist '(indent . +modeline-indent-segment)) -Each item should either be a string or a a cons cell whose CAR is the path -component and CDR is the name of a face. + ;; Remove unused segments & extra padding + (doom-modeline-def-modeline 'main + '(bar window-number matches buffer-info remote-host buffer-position selection-info) + '(misc-info persp-name irc mu4e github debug indent input-method buffer-encoding lsp major-mode process vcs checker)) -Currently available functions: + (doom-modeline-def-modeline 'special + '(bar window-number matches buffer-info-simple buffer-position selection-info) + '(misc-info persp-name debug input-method irc-buffers buffer-encoding lsp major-mode process checker)) -+ `+modeline-file-path-with-project': project/src/lib/file.c -+ `+modeline-file-path-from-project': src/lib/file.c -+ `+modeline-file-path-truncated-with-project': project/s/l/file.c -+ `+modeline-file-path-truncated-upto-project': ~/w/project/src/lib/file.c -+ `+modeline-file-path-truncated-upto-project-root': ~/w/p/s/lib/file.c -+ `+modeline-file-path-truncated': ~/w/p/s/l/file.c -+ `+modeline-file-name': file.c") + (doom-modeline-def-modeline 'project + '(bar window-number buffer-default-directory) + '(misc-info mu4e github debug fancy-battery " " major-mode process)) -;; Convenience aliases -(defvaralias 'mode-line-format-left '+modeline-format-left) -(defvaralias 'mode-line-format-right '+modeline-format-right) -;; -(defvar-local +modeline-format-left () "TODO") -(defvar-local +modeline-format-right () "TODO") -(put '+modeline-format-left 'risky-local-variable t) -(put '+modeline-format-right 'risky-local-variable t) - -;; Otherwise appended segments will produce *Invalid* -(setq global-mode-string '("")) -;; We handle this ourselves -(setq projectile-dynamic-mode-line nil) - -;; -(defvar +modeline--vspc (propertize " " 'face 'variable-pitch)) - -;; externs -(defvar anzu--state nil) -(defvar evil-mode nil) -(defvar evil-state nil) -(defvar evil-visual-selection nil) -(defvar evil-visual-beginning nil) -(defvar evil-visual-end nil) -(defvar iedit-mode nil) -(defvar all-the-icons-scale-factor) -(defvar all-the-icons-default-adjust) + ;; Some functions modify the buffer, causing the modeline to show a false + ;; modified state, so we try to force them to behave. + (defun +modeline*inhibit-modification-hooks (orig-fn &rest args) + (with-silent-modifications (apply orig-fn args))) + (advice-add #'ws-butler-after-save :around #'+modeline*inhibit-modification-hooks)) ;; -;; Custom faces - -(defgroup +modeline nil - "TODO" - :group 'faces) - -(defface doom-modeline-buffer-path - '((t (:inherit (mode-line-emphasis bold)))) - "Face used for the dirname part of the buffer path." - :group '+modeline) - -(defface doom-modeline-buffer-file - '((t (:inherit (mode-line-buffer-id bold)))) - "Face used for the filename part of the mode-line buffer path." - :group '+modeline) - -(defface doom-modeline-buffer-project-root - '((t (:inherit doom-modeline-buffer-path))) - "Face used for the project root at the beginning of the mode-line path." - :group '+modeline) - -(defface doom-modeline-buffer-modified '((t (:inherit (error bold) :background nil))) - "Face used for the 'unsaved' symbol in the mode-line." - :group '+modeline) - -(defface doom-modeline-buffer-major-mode '((t (:inherit (mode-line-emphasis bold)))) - "Face used for the major-mode segment in the mode-line." - :group '+modeline) - -(defface doom-modeline-highlight '((t (:inherit mode-line-emphasis))) - "Face for bright segments of the mode-line." - :group '+modeline) - -(defface doom-modeline-panel '((t (:inherit mode-line-highlight))) - "Face for 'X out of Y' segments, such as `+modeline--anzu', -`+modeline--evil-substitute' and `iedit'" - :group '+modeline) - -(defface doom-modeline-info `((t (:inherit (success bold)))) - "Face for info-level messages in the modeline. Used by `*vc'." - :group '+modeline) - -(defface doom-modeline-warning `((t (:inherit (warning bold)))) - "Face for warnings in the modeline. Used by `*flycheck'" - :group '+modeline) - -(defface doom-modeline-urgent `((t (:inherit (error bold)))) - "Face for errors in the modeline. Used by `*flycheck'" - :group '+modeline) - -(defface doom-modeline-bar '((t (:inherit highlight))) - "The face used for the left-most bar on the mode-line of an active window." - :group '+modeline) - - -;; -;; Packages +;; Extensions (def-package! anzu - :after-call isearch-mode - :config - (setq anzu-cons-mode-line-p nil - anzu-minimum-input-length 1 - anzu-search-threshold 250) - (global-anzu-mode +1) - - (defun +modeline*fix-anzu-count (positions here) - (cl-loop for (start . end) in positions - collect t into before - when (and (>= here start) (<= here end)) - return (length before) - finally return 0)) - (advice-add #'anzu--where-is-here :override #'+modeline*fix-anzu-count) - - ;; Avoid anzu conflicts across buffers - (mapc #'make-variable-buffer-local - '(anzu--total-matched anzu--current-position anzu--state - anzu--cached-count anzu--cached-positions anzu--last-command - anzu--last-isearch-string anzu--overflow-p)) - ;; Ensure anzu state is cleared when searches & iedit are done - (add-hook 'isearch-mode-end-hook #'anzu--reset-status t) - (add-hook 'doom-escape-hook #'anzu--reset-status t) - (add-hook 'iedit-mode-end-hook #'anzu--reset-status)) - + :after-call isearch-mode) (def-package! evil-anzu :when (featurep! :feature evil) :after-call (evil-ex-start-search evil-ex-start-word-search)) - - -;; -;; Hacks - -;; Keep `+modeline-current-window' up-to-date -(defvar +modeline-current-window (frame-selected-window)) - -(defun +modeline|set-selected-window (&rest _) - "Sets `+modeline-current-window' appropriately" - (when-let* ((win (frame-selected-window))) - (unless (minibuffer-window-active-p win) - (setq +modeline-current-window win) - (force-mode-line-update)))) - -(defun +modeline|unset-selected-window () - (setq +modeline-current-window nil) - (force-mode-line-update)) - -(add-hook 'window-configuration-change-hook #'+modeline|set-selected-window) -(add-hook 'doom-enter-window-hook #'+modeline|set-selected-window) -(if (not (boundp 'after-focus-change-function)) - (progn - (add-hook 'focus-in-hook #'+modeline|set-selected-window) - (add-hook 'focus-out-hook #'+modeline|unset-selected-window)) - (defun +modeline|refresh-frame () - (setq +modeline-current-window nil) - (cl-loop for frame in (frame-list) - if (eq (frame-focus-state frame) t) - return (setq +modeline-current-window (frame-selected-window frame))) - (force-mode-line-update t)) - (add-function :after after-focus-change-function #'+modeline|refresh-frame)) - -(defsubst active () - (eq (selected-window) +modeline-current-window)) - -;; Ensure modeline is inactive when Emacs is unfocused (and active otherwise) -(defvar +modeline-remap-face-cookies nil) - -(defun +modeline|focus-all-windows (&rest _) - (cl-loop for (buffer . cookie) in +modeline-remap-face-cookies - if (buffer-live-p buffer) - do (with-current-buffer buffer - (face-remap-remove-relative cookie)))) - -(defun +modeline|unfocus-all-windows (&rest _) - (setq +modeline-remap-face-cookies - (cl-loop for window in (window-list) - for buffer = (window-buffer window) - if (buffer-live-p buffer) - collect - (with-current-buffer buffer - (cons buffer - (face-remap-add-relative 'mode-line - 'mode-line-inactive)))))) - -(add-hook 'focus-in-hook #'+modeline|focus-all-windows) -(add-hook 'focus-out-hook #'+modeline|unfocus-all-windows) -(advice-add #'posframe-hide :after #'+modeline|focus-all-windows) -(advice-add #'posframe-delete :after #'+modeline|focus-all-windows) -(when (featurep! :completion helm) - (add-hook 'helm-before-initialize-hook #'+modeline|unfocus-all-windows) - (add-hook 'helm-cleanup-hook #'+modeline|focus-all-windows)) - - -;; -;; Helpers - -(defun +modeline--make-xpm (width height &optional color) - "Create an XPM bitmap. Inspired by `powerline''s `pl/make-xpm'." - (propertize - " " 'display - (let ((data (make-list height (make-list width 1))) - (color (or color "None"))) - (ignore-errors - (create-image - (concat - (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," - (length (car data)) (length data) color color) - (cl-loop with idx = 0 - with len = (length data) - for dl in data - do (cl-incf idx) - concat "\"" - concat (cl-loop for d in dl - if (= d 0) collect (string-to-char " ") - else collect (string-to-char ".")) - concat (if (eq idx len) "\"};" "\",\n"))) - 'xpm t :ascent 'center))))) - -(defun +modeline-build-path (path) - "Construct the file path for the `+modeline-buffer-id' segment using -`+mdoeline-buffer-path-function'. If the buffer has no `buffer-file-name', just -use `buffer-name'." - (let ((buffer-file-name (or path buffer-file-name))) - (if (or (eq major-mode 'dired-mode) - (null buffer-file-name)) - (propertize "%s" 'face 'doom-modeline-buffer-path) - (cl-loop for spec in (funcall +modeline-buffer-path-function) - if (stringp spec) concat spec - else if (not (null spec)) - concat (propertize (car spec) 'face (cdr spec)))))) - - -;; -;; Buffer file path styles - -(defun +modeline-file-path-with-project () - "Returns the unaltered buffer file path relative to the project root's -parent. - -e.g. project/src/lib/file.c" - (let* ((base (buffer-base-buffer)) - (filename (file-truename (buffer-file-name base)))) - (append (if (doom-project-p) - (let* ((project-root (doom-project-root)) - (relative-dirs (file-relative-name (file-name-directory filename) - (file-truename project-root)))) - (list (cons (concat (doom-project-name) "/") - 'doom-modeline-buffer-project-root) - (unless (equal "./" relative-dirs) - (cons relative-dirs 'doom-modeline-buffer-path)))) - (list nil (cons (abbreviate-file-name (file-name-directory filename)) - 'doom-modeline-buffer-path))) - (list (cons (file-name-nondirectory filename) - 'doom-modeline-buffer-file))))) - -(defun +modeline-file-path-from-project () - "Returns file path relative to the project root. - -e.g. src/lib/file.c - -Meant for `+modeline-buffer-path-function'." - (cdr (+modeline-file-path-with-project))) - -(defun +modeline-file-path-truncated-with-project () - "Returns file path relative to (and including) project root, with descendent -folders truncated. - -e.g. project/s/l/file.c - -Meant for `+modeline-buffer-path-function'." - (let* ((parts (+modeline-file-path-with-project)) - (dirs (car (nth 1 parts)))) - (setcar (nth 1 parts) - (shrink-path--dirs-internal dirs t)) - parts)) - -(defun +modeline-file-path-truncated-upto-project () - "Returns file path, truncating segments prior to the project. - -e.g. ~/w/project/src/lib/file.c - -Meant for `+modeline-buffer-path-function'." - (pcase-let - ((`(,root-parent ,root ,dir, file) - (let ((buffer-file-name (or buffer-file-name (buffer-file-name (buffer-base-buffer))))) - (shrink-path-file-mixed (or (doom-project-root) default-directory) - (file-name-directory buffer-file-name) - buffer-file-name)))) - (list (cons root-parent 'font-lock-comment-face) - (cons root 'doom-modeline-buffer-project-root) - (cons (concat "/" dir) 'doom-modeline-buffer-path) - (cons file 'doom-modeline-buffer-file)))) - -(defun +modeline-file-path-truncated-upto-project-root () - "Return file path, truncating segemnts prior to (and including) the project -root. - -e.g. ~/w/p/src/lib/file.c - -Meant for `+modeline-buffer-path-function'." - (let* ((parts (+modeline-file-path-truncated-upto-project)) - (root (car (nth 1 parts)))) - (setcar (nth 1 parts) - (let ((first (substring root 0 1))) - (if (equal first ".") - (substring root 0 2) - first))) - parts)) - -(defun +modeline-file-path-truncated () - "Return absolute file path with all directories truncated. - -e.g. ~/w/p/s/l/file.c - -Meant for `+modeline-buffer-path-function'." - (pcase-let ((`(,dir . ,file) - (shrink-path-prompt (buffer-file-name (buffer-base-buffer))))) - (list (cons dir 'doom-modeline-buffer-path) - (cons file 'doom-modeline-buffer-file)))) - -(defun +modeline-file-name () - "Return buffer name. - -e.g. file.c - -Meant for `+modeline-buffer-path-function'." - (list (cons "%b" 'doom-modeline-buffer-path))) - - -;; -;; Bars - -(defvar +modeline-bar-start nil "TODO") -(put '+modeline-bar-start 'risky-local-variable t) -(defvar +modeline-bar-end nil "TODO") -(put '+modeline-bar-end 'risky-local-variable t) - -(defvar +modeline-bar-active nil "TODO") -(defvar +modeline-bar-inactive nil "TODO") -(defun +modeline|setup-bars () - (setq +modeline-bar-active - (+modeline--make-xpm +modeline-width +modeline-height - (unless +modeline-bar-invisible - (face-background 'doom-modeline-bar nil t))) - +modeline-bar-inactive - (+modeline--make-xpm +modeline-width +modeline-height)) - (setq +modeline-bar-start nil - +modeline-bar-end nil) - (if +modeline-bar-at-end - (setq +modeline-bar-end '+modeline-bar) - (setq +modeline-bar-start '+modeline-bar))) -(add-hook 'doom-load-theme-hook #'+modeline|setup-bars) - -(defun +modeline|setup-bars-after-change (sym val op _where) - (when (eq op 'set) - (set sym val) - (+modeline|setup-bars))) -(add-variable-watcher '+modeline-width #'+modeline|setup-bars-after-change) -(add-variable-watcher '+modeline-height #'+modeline|setup-bars-after-change) -(add-variable-watcher '+modeline-bar-at-end #'+modeline|setup-bars-after-change) -(add-variable-watcher '+modeline-bar-invisible #'+modeline|setup-bars-after-change) - -(def-modeline-segment! +modeline-bar - (if (active) +modeline-bar-active +modeline-bar-inactive)) - - -;; -;; Segments - -(def-modeline-segment! +modeline-buffer-state - (let* ((base (buffer-base-buffer)) - (icon (cond (buffer-read-only - (all-the-icons-octicon - "lock" - :face 'doom-modeline-warning - :v-adjust -0.05)) - ((buffer-modified-p base) - (all-the-icons-faicon - "floppy-o" - :face 'doom-modeline-buffer-modified - :v-adjust -0.05)) - ((and (buffer-file-name base) - (not (file-exists-p (buffer-file-name base)))) - (all-the-icons-octicon - "circle-slash" - :face 'doom-modeline-urgent - :v-adjust -0.05))))) - (if icon (concat icon " ")))) - -(def-modeline-segment! +modeline-buffer-id - :on-hooks (find-file-hook after-save-hook after-revert-hook) - :init (propertize "%b" 'face 'doom-modeline-buffer-file) - :faces t - (let ((file-path (buffer-file-name (buffer-base-buffer)))) - (propertize (+modeline-build-path file-path) - 'help-echo file-path))) - -(def-modeline-segment! +modeline-buffer-directory - (let ((face (if (active) 'doom-modeline-buffer-path))) - (propertize - (concat (if (display-graphic-p) " ") - (all-the-icons-octicon - "file-directory" - :face face - :v-adjust -0.1 - :height 1.25) - " " - (propertize (abbreviate-file-name default-directory) - 'face face)) - 'help-echo default-directory))) - -(def-modeline-segment! +modeline-vcs - :on-set (vc-mode) - (when (and vc-mode buffer-file-name) - (let* ((backend (vc-backend buffer-file-name)) - (state (vc-state buffer-file-name backend))) - (let ((face 'mode-line-inactive) - (active (active)) - (all-the-icons-default-adjust -0.1)) - (concat (cond ((memq state '(edited added)) - (if active (setq face 'doom-modeline-info)) - (all-the-icons-octicon - "git-compare" - :face face - :v-adjust -0.05)) - ((eq state 'needs-merge) - (if active (setq face 'doom-modeline-info)) - (all-the-icons-octicon "git-merge" :face face)) - ((eq state 'needs-update) - (if active (setq face 'doom-modeline-warning)) - (all-the-icons-octicon "arrow-down" :face face)) - ((memq state '(removed conflict unregistered)) - (if active (setq face 'doom-modeline-urgent)) - (all-the-icons-octicon "alert" :face face)) - (t - (if active (setq face 'font-lock-doc-face)) - (all-the-icons-octicon - "git-compare" - :face face - :v-adjust -0.05))) - +modeline--vspc - (propertize (substring vc-mode (+ (if (eq backend 'Hg) 2 3) 2)) - 'face (if active face))))))) - -(def-modeline-segment! +modeline-indent-style - :on-hooks (after-revert-hook after-save-hook find-file-hook) - :on-set (indent-tabs-mode tab-width) - (propertize (format "%s%d " - (if indent-tabs-mode "⭾" "␣") - tab-width) - 'help-echo - (format "Indentation: %d %s wide" - tab-width - (if indent-tabs-mode "tabs" "spaces")))) - -(def-modeline-segment! +modeline-encoding - :on-hooks (after-revert-hook after-save-hook find-file-hook) - :on-set (buffer-file-coding-system) - (concat (pcase (coding-system-eol-type buffer-file-coding-system) - (0 (propertize "LF" 'help-echo "EOL convention: \\n (Unix)")) - (1 (propertize "CRLF" 'help-echo "EOL convention: \\r\\n (Windows, Symbian OS, etc)")) - (2 (propertize "CR" 'help-echo "EOL convention: \\r (pre-OSX MacOS)"))) - " " - (let* ((sys (coding-system-plist buffer-file-coding-system)) - (category (plist-get sys :category))) - (propertize - (cond ((eq category 'coding-category-undecided) - "") - ((or (eq category 'coding-category-utf-8) - (string-match-p "utf-8" (symbol-name (plist-get sys :name)))) - "") - ((concat (upcase (symbol-name (plist-get sys :name))) - " "))) - 'help-echo (plist-get (coding-system-plist buffer-file-coding-system) :docstring))))) - -(def-modeline-segment! +modeline-major-mode - (propertize (format-mode-line mode-name) - 'face (if (active) 'doom-modeline-buffer-major-mode))) - -(defun +modeline--macro-recording () - "Display current Emacs or evil macro being recorded." - (when (and (active) (or defining-kbd-macro executing-kbd-macro)) - (let ((sep (propertize " " 'face 'doom-modeline-panel))) - (concat sep - (propertize (if (bound-and-true-p evil-this-macro) - (char-to-string evil-this-macro) - "Macro") - 'face 'doom-modeline-panel) - sep - (all-the-icons-octicon "triangle-right" - :face 'doom-modeline-panel - :v-adjust -0.05) - sep)))) - -(defsubst +modeline--anzu () - "Show the match index and total number thereof. Requires `anzu', also -`evil-anzu' if using `evil-mode' for compatibility with `evil-search'." - (when (and anzu--state (not iedit-mode)) - (propertize - (let ((here anzu--current-position) - (total anzu--total-matched)) - (cond ((eq anzu--state 'replace-query) - (format " %d replace " total)) - ((eq anzu--state 'replace) - (format " %d/%d " here total)) - (anzu--overflow-p - (format " %s+ " total)) - ((format " %s/%d " here total)))) - 'face (if (active) 'doom-modeline-panel)))) - -(defsubst +modeline--evil-substitute () - "Show number of matches for evil-ex substitutions and highlights in real time." - (when (and evil-mode - (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) - (assq 'evil-ex-global-match evil-ex-active-highlights-alist) - (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) - (propertize - (let ((range (if evil-ex-range - (cons (car evil-ex-range) (cadr evil-ex-range)) - (cons (line-beginning-position) (line-end-position)))) - (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) - (if pattern - (format " %s matches " (how-many pattern (car range) (cdr range))) - " - ")) - 'face (if (active) 'doom-modeline-panel)))) - -(defun doom-themes--overlay-sort (a b) - (< (overlay-start a) (overlay-start b))) - -(defsubst +modeline--iedit () - "Show the number of iedit regions matches + what match you're on." - (when (and iedit-mode iedit-occurrences-overlays) - (propertize - (let ((this-oc (or (let ((inhibit-message t)) - (iedit-find-current-occurrence-overlay)) - (progn (iedit-prev-occurrence) - (iedit-find-current-occurrence-overlay)))) - (length (length iedit-occurrences-overlays))) - (format " %s/%d " - (if this-oc - (- length - (length (memq this-oc (sort (append iedit-occurrences-overlays nil) - #'doom-themes--overlay-sort))) - -1) - "-") - length)) - 'face (if (active) 'doom-modeline-panel)))) - -(def-modeline-segment! +modeline-matches - "Displays: 1. the currently recording macro, 2. A current/total for the -current search term (with anzu), 3. The number of substitutions being conducted -with `evil-ex-substitute', and/or 4. The number of active `iedit' regions." - (let ((meta (concat (+modeline--macro-recording) - (+modeline--anzu) - (+modeline--evil-substitute) - (+modeline--iedit) - " "))) - (or (and (not (equal meta " ")) meta) - (if buffer-file-name " %I ")))) - -;; -(defsubst doom-column (pos) - (save-excursion (goto-char pos) - (current-column))) - -(defvar-local +modeline-enable-word-count nil - "If non-nil, a word count will be added to the selection-info modeline -segment.") - -(defun +modeline|enable-word-count () - (setq +modeline-enable-word-count t)) -(add-hook 'text-mode-hook #'+modeline|enable-word-count) - -(def-modeline-segment! +modeline-selection-info - (let ((beg (or evil-visual-beginning (region-beginning))) - (end (or evil-visual-end (region-end)))) - (propertize - (let ((lines (count-lines beg (min end (point-max))))) - (concat (cond ((or (bound-and-true-p rectangle-mark-mode) - (eq 'block evil-visual-selection)) - (let ((cols (abs (- (doom-column end) - (doom-column beg))))) - (format "%dx%dB" lines cols))) - ((eq evil-visual-selection 'line) - (format "%dL" lines)) - ((> lines 1) - (format "%dC %dL" (- end beg) lines)) - ((format "%dC" (- end beg)))) - (when +modeline-enable-word-count - (format " %dW" (count-words beg end))))) - 'face 'doom-modeline-highlight))) - -(defun +modeline|enable-selection-info () - (add-to-list '+modeline-format-left '+modeline-selection-info t #'eq)) -(defun +modeline|disable-selection-info () - (setq +modeline-format-left (delq '+modeline-selection-info +modeline-format-left))) -(cond ((featurep 'evil) - (add-hook 'evil-visual-state-entry-hook #'+modeline|enable-selection-info) - (add-hook 'evil-visual-state-exit-hook #'+modeline|disable-selection-info)) - ((add-hook 'activate-mark-hook #'+modeline|enable-selection-info) - (add-hook 'deactivate-mark-hook #'+modeline|disable-selection-info))) - -;; flycheck -(defun +doom-ml-icon (icon &optional text face voffset) - "Displays an octicon ICON with FACE, followed by TEXT. Uses -`all-the-icons-octicon' to fetch the icon." - (concat (when icon - (concat - (all-the-icons-material icon :face face :height 1.1 :v-adjust (or voffset -0.2)) - (if text +modeline--vspc))) - (if text (propertize text 'face face)))) - -(defun +modeline-flycheck-status (status) - (pcase status - (`finished (if flycheck-current-errors - (let-alist (flycheck-count-errors flycheck-current-errors) - (let ((sum (+ (or .error 0) (or .warning 0)))) - (+doom-ml-icon "do_not_disturb_alt" - (number-to-string sum) - (if .error 'doom-modeline-urgent 'doom-modeline-warning) - -0.25))) - (+doom-ml-icon "check" nil 'doom-modeline-info))) - (`running (+doom-ml-icon "access_time" nil 'font-lock-doc-face -0.25)) - ;; (`no-checker (+doom-ml-icon "sim_card_alert" "-" 'font-lock-doc-face)) - (`errored (+doom-ml-icon "sim_card_alert" "Error" 'doom-modeline-urgent)) - (`interrupted (+doom-ml-icon "pause" "Interrupted" 'font-lock-doc-face)))) - -(defun +doom-modeline|update-flycheck-segment (&optional status) - (setq +modeline-flycheck - (when-let* ((status-str (+modeline-flycheck-status status))) - (concat +modeline--vspc status-str " ")))) -(add-hook 'flycheck-mode-hook #'+doom-modeline|update-flycheck-segment) -(add-hook 'flycheck-status-changed-functions #'+doom-modeline|update-flycheck-segment) - -(def-modeline-segment! +modeline-flycheck - "Displays color-coded flycheck error status in the current buffer with pretty -icons." - :init nil) - - -;; -;; Preset modeline formats - -(def-modeline-format! :main - '(+modeline-matches " " - +modeline-buffer-state - +modeline-buffer-id - " %2l:%c %p ") - `(mode-line-misc-info - +modeline-indent-style - +modeline-encoding - +modeline-major-mode " " - (vc-mode (" " +modeline-vcs " ")) - mode-line-process - +modeline-flycheck)) - -(def-modeline-format! :minimal - '(+modeline-matches " " - +modeline-buffer-state - +modeline-buffer-id) - '(+modeline-major-mode)) - -(def-modeline-format! :special - '(+modeline-matches +modeline-buffer-state " %b " +modeline-buffer-position) - '(+modeline-encoding +modeline-major-mode mode-line-process)) - -(def-modeline-format! :project - '(+modeline-buffer-directory) - '(+modeline-major-mode)) - - -;; -(def-modeline-segment! +modeline--rest - (let ((rhs-str (format-mode-line +modeline-format-right))) - (list (propertize - " " 'display - `((space :align-to (- (+ right right-fringe right-margin) - ,(1+ (string-width rhs-str)))))) - rhs-str))) - -(setq-default mode-line-format '("" +modeline-bar-start +modeline-format-left +modeline--rest +modeline-bar-end)) - - -;; -(set-modeline! :main t) -(add-hook! '+doom-dashboard-mode-hook (set-modeline! :project)) -(add-hook! 'doom-scratch-buffer-hook (set-modeline! :special)) diff --git a/modules/ui/modeline/packages.el b/modules/ui/modeline/packages.el index 9c484100f..5933333f4 100644 --- a/modules/ui/modeline/packages.el +++ b/modules/ui/modeline/packages.el @@ -1,20 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/modeline/packages.el -;;; These are the invisible dependencies -;; Required -;;(require 'evil) -;;(require 'projectile) -;;(require 'all-the-icons) - -;; Optional -;;(require 'flycheck) -;;(require 'iedit) -;;(require 'evil-multiedit) - +(package! doom-modeline) (package! anzu) - (when (featurep! :feature evil) (package! evil-anzu)) - -(package! shrink-path) diff --git a/modules/ui/nav-flash/config.el b/modules/ui/nav-flash/config.el index d59b7e2be..e84837bbb 100644 --- a/modules/ui/nav-flash/config.el +++ b/modules/ui/nav-flash/config.el @@ -11,7 +11,7 @@ ;; NOTE In :feature lookup `recenter' is hooked to a bunch of jumping ;; commands, which will trigger nav-flash. (add-hook! - '(doom-enter-window-hook + '(doom-switch-window-hook imenu-after-jump-hook evil-jumps-post-jump-hook counsel-grep-post-action-hook dumb-jump-after-jump-hook) #'+nav-flash|blink-cursor-maybe) diff --git a/modules/ui/popup/+hacks.el b/modules/ui/popup/+hacks.el index deebf9b06..8b05cff9a 100644 --- a/modules/ui/popup/+hacks.el +++ b/modules/ui/popup/+hacks.el @@ -316,6 +316,14 @@ instead of switch-to-buffer-*." (set-popup-rule! "\\(^\\*Contents\\|'s annots\\*$\\)" :ignore t)) +;; `profiler' +(defun doom*profiler-report-find-entry-in-other-window (orig-fn function) + (cl-letf (((symbol-function 'find-function) + (symbol-function 'find-function-other-window))) + (funcall orig-fn function))) +(advice-add #'profiler-report-find-entry :around #'doom*profiler-report-find-entry-in-other-window) + + ;; `wgrep' (progn ;; close the popup after you're done with a wgrep buffer diff --git a/modules/ui/popup/README.org b/modules/ui/popup/README.org index 9a36b1270..3545e7f76 100644 --- a/modules/ui/popup/README.org +++ b/modules/ui/popup/README.org @@ -1,50 +1,59 @@ -#+TITLE: :feature popup +#+TITLE: :ui popup -This module provides a highly customizable popup window management system. +* Table of Contents :TOC: +- [[#description][Description]] + - [[#module-flags][Module Flags]] +- [[#prerequisites][Prerequisites]] +- [[#configuration][Configuration]] + - [[#set-popup-rule-and-set-popup-rules][~set-popup-rule!~ and ~set-popup-rules!~]] + - [[#disabling-aggressive-mode-line-hiding-in-popups][Disabling aggressive mode-line hiding in popups]] +- [[#appendix][Appendix]] + - [[#commands][Commands]] + - [[#library][Library]] + - [[#hacks][Hacks]] + +* Description +This module provides a customizable popup window management system. -#+begin_quote Not all windows are created equally. Some are less important. Some I want gone once they have served their purpose, like code output or a help buffer. Others I want to stick around, like a scratch buffer or org-capture popup. More than that, popups ought to be be the second class citizens of my editor; -spawned off to the side, discarded with the simple push of a button -(Escape/C-g), and easily restored if I want to see them again. Of course, this -system should clean up after itself and kill off buffers I mark as transient. -#+end_quote +spawned off to the side, discarded with the push of a button (e.g. =ESC= or +=C-g=), and easily restored if I want to see them again. Of course, this system +should clean up after itself and kill off buffers I mark as transient. -* Table of Contents :TOC: -- [[Configuration][Configuration]] - - [[~set-popup-rules!~][~set-popup-rules!~]] - - [[Disabling aggressive mode-line hiding in popups][Disabling aggressive mode-line hiding in popups]] -- [[Appendix][Appendix]] - - [[Commands][Commands]] - - [[Library][Library]] - - [[Hacks][Hacks]] +** Module Flags ++ =+all= Enables fallback rules to ensure all temporary/special buffers (whose + name begins with a space or asterix) are treated as popups. ++ =+defaults= Enables reasonable default popup rules for a variety of buffers. + +* Prerequisites +This module has no external prerequisites. * Configuration -** ~set-popup-rules!~ -This module has one setting for defining your own rules for popups: +** ~set-popup-rule!~ and ~set-popup-rules!~ +This module has two functions for defining your own rules for popups: #+BEGIN_SRC emacs-lisp +(set-popup-rule! PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS) (set-popup-rules! &rest RULESETS) #+END_SRC -+ ~RULESETS~ consist of a function or regexp string that matches the buffer's - name, and a list of settings. See ~display-buffer~'s, - ~display-window-parameters~'s, and ~+popup-window-parameters~'s documentation - for what parameters are supported. +~PREDICATE~ is a predicate function or regexp string to match against the +buffer's name. To see what the other keywords do, check out the documentation +for ~set-popup-rule!~ (=SPC h f set-popup-rule!=). +#+begin_quote Rules are added to ~display-buffer-alist~, which instructs ~display-buffer~ calls on how to set up windows for buffers that meet certain conditions. -#+begin_quote The ~switch-to-buffer~ command (and its ~switch-to-buffer-*~ variants) are not affected by ~display-buffer-alist~. #+end_quote -Here are a couple example rules: - +e.g. #+BEGIN_SRC emacs-lisp (set-popup-rules! '(("^ \\*" :slot -1) ; fallback rule for special buffers @@ -57,25 +66,27 @@ Here are a couple example rules: #+END_SRC Omitted parameters in a ~set-popup-rules!~ will use the defaults set in -~+popup-default-alist~ and ~+popup-default-parameters~. +~+popup-defaults~. ** Disabling aggressive mode-line hiding in popups -There are two ways to go about this. You can turn on modelines by changing the -default ~'modeline~ window parameter in ~+popup-default-parameters~: +There are two ways to go about this. -#+BEGIN_SRC emacs-lisp -;; put in private/$USER/config.el -(map-put +popup-default-parameters 'modeline t) -#+END_SRC +1. Turn on modelines by changing the ~:modeline~ property in ~+popup-defaults~: -This will ensure all popups have a modeline /by default/, but allows you to override this on a per-popup basis. + #+BEGIN_SRC emacs-lisp + ;; put in private/$USER/config.el + (map-put +popup-defaults :modeline t) + #+END_SRC -*Alternatively*, you can disable modeline-hiding entirely: + This will ensure all popups have a modeline /by default/, but allows you to + override this on a per-popup basis. -#+BEGIN_SRC emacs-lisp -;; put in private/$USER/config.el -(remove-hook '+popup-buffer-mode-hook '+popup|set-modeline) -#+END_SRC +2. Disable modeline-hiding entirely: + + #+BEGIN_SRC emacs-lisp + ;; in ~/.doom.d/config.el + (remove-hook '+popup-buffer-mode-hook #'+popup|set-modeline-on-enable) + #+END_SRC * Appendix ** Commands diff --git a/modules/ui/popup/autoload/settings.el b/modules/ui/popup/autoload/settings.el index 56a092f61..ad4bead4f 100644 --- a/modules/ui/popup/autoload/settings.el +++ b/modules/ui/popup/autoload/settings.el @@ -166,7 +166,9 @@ PLIST can be made up of any of the following properties: An alist of custom window parameters. See `(elisp)Window Parameters'. If any of these are omitted, defaults derived from `+popup-defaults' will be -used." +used. + +\(fn PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS)" (declare (indent defun)) (push (+popup--make predicate plist) +popup--display-buffer-alist) (when (bound-and-true-p +popup-mode) diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index cb49d1e8d..f3f257ddb 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -56,7 +56,6 @@ adjustment.") :keymap +popup-mode-map (cond (+popup-mode (add-hook 'doom-escape-hook #'+popup|close-on-escape t) - (add-hook 'doom-cleanup-hook #'+popup|cleanup-rules) (setq +popup--old-display-buffer-alist display-buffer-alist display-buffer-alist +popup--display-buffer-alist window--sides-inhibit-check t) @@ -64,7 +63,6 @@ adjustment.") (push (cons prop 'writable) window-persistent-parameters))) (t (remove-hook 'doom-escape-hook #'+popup|close-on-escape) - (remove-hook 'doom-cleanup-hook #'+popup|cleanup-rules) (setq display-buffer-alist +popup--old-display-buffer-alist window--sides-inhibit-check nil) (+popup|cleanup-rules) @@ -161,7 +159,9 @@ prevent the popup(s) from messing up the UI (or vice versa)." ;; `Info-mode' ("^\\*info\\*$" :slot 2 :vslot 2 :size 0.45 :select t))) - '(("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil))) + '(("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil) + ("^\\*CPU-Profiler-Report " :side bottom :vslot 100 :slot 1 :height 0.4 :width 0.5 :quit nil) + ("^\\*Memory-Profiler-Report " :side bottom :vslot 100 :slot 2 :height 0.4 :width 0.5 :quit nil))) (add-hook 'doom-init-ui-hook #'+popup-mode :append) diff --git a/modules/ui/treemacs/config.el b/modules/ui/treemacs/config.el index 176100df7..b45958886 100644 --- a/modules/ui/treemacs/config.el +++ b/modules/ui/treemacs/config.el @@ -1,19 +1,16 @@ ;;; ui/treemacs/config.el -*- lexical-binding: t; -*- (setq treemacs-follow-after-init t - treemacs-width 35 - treemacs-position 'left treemacs-is-never-other-window t - treemacs-silent-refresh nil - treemacs-indentation 2 - treemacs-sorting 'alphabetic-desc - treemacs-show-hidden-files t - treemacs-goto-tag-strategy 'refetch-index - treemacs-display-in-side-window t + treemacs-sorting 'alphabetic-case-insensitive-desc treemacs-persist-file (concat doom-cache-dir "treemacs-persist")) (after! treemacs-persistence - (setq treemacs--last-error-persist-file (concat doom-cache-dir "treemacs-persist-at-last-error"))) + ;; This variable is defined with defconst, so we must wait to change it until + ;; it has loaded. + (setq treemacs--last-error-persist-file + (concat doom-cache-dir + "treemacs-persist-at-last-error"))) (after! treemacs @@ -23,29 +20,8 @@ :quit nil :ttl 0) - (defvar +treemacs-use-git-mode - (pcase (cons (not (null (executable-find "git"))) - (not (null (executable-find "python3")))) - (`(t . t) 'extended) - (`(t) 'simple)) - "Type of git integration for `treemacs-git-mode'. -There are 2 possible values: -1) simple, which highlights only files based on their git status, and is - slightly faster -2) extended, which highlights both files and directories, but requires python") - - (defvar treemacs-collapse-dirs - (if (executable-find "python3") 3 0)) - - (defun +treemacs|improve-hl-line-contrast () - "`hl-line' doesn't stand out enough in some themes." - (face-remap-add-relative 'hl-line 'region)) - (add-hook 'treemacs-mode-hook #'+treemacs|improve-hl-line-contrast) - + ;; Don't follow the cursor (treemacs-follow-mode -1) - (treemacs-filewatch-mode t) - (when (memq +treemacs-use-git-mode '(simple extended)) - (treemacs-git-mode +treemacs-use-git-mode)) (after! ace-window (setq aw-ignored-buffers (delq 'treemacs-mode aw-ignored-buffers)))) @@ -53,7 +29,12 @@ There are 2 possible values: (def-package! treemacs-evil :when (featurep! :feature evil +everywhere) - :after treemacs) + :after treemacs + :config + (define-key! evil-treemacs-state-map + [return] #'treemacs-RET-action + [tab] #'treemacs-TAB-action + "TAB" #'treemacs-TAB-action)) (def-package! treemacs-projectile diff --git a/modules/ui/unicode/autoload.el b/modules/ui/unicode/autoload.el index 1af675c04..0be6bd27e 100644 --- a/modules/ui/unicode/autoload.el +++ b/modules/ui/unicode/autoload.el @@ -1,7 +1,7 @@ ;;; ui/unicode/autoload.el -*- lexical-binding: t; -*- ;;;###autoload -(add-hook 'doom-post-init-hook #'+unicode|init-fonts) +(add-hook 'doom-init-ui-hook #'+unicode|init-fonts) ;;;###autoload (defun +unicode|init-fonts () diff --git a/modules/ui/vc-gutter/config.el b/modules/ui/vc-gutter/config.el index 9ee858511..daed7ec42 100644 --- a/modules/ui/vc-gutter/config.el +++ b/modules/ui/vc-gutter/config.el @@ -40,7 +40,7 @@ to the right fringe.") (setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos) (setq-local git-gutter:window-width 1)) (git-gutter-mode +1))) - (add-hook! (text-mode prog-mode conf-mode after-save) + (add-hook! (text-mode prog-mode conf-mode) #'+version-control|git-gutter-maybe) ;; standardize default fringe width (if (fboundp 'fringe-mode) (fringe-mode '4)) @@ -80,70 +80,3 @@ to the right fringe.") ;; A non-descript, left-pointing arrow (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow [16 48 112 240 112 48 16] nil nil 'center)))) - - -;; (def-package! diff-hl -;; :defer t -;; :init -;; (defun +vc-gutter|init () -;; "Start `diff-hl-mode' if in a file-visiting and tracked buffer." -;; (when (and buffer-file-name -;; (vc-state buffer-file-name) -;; (or +vc-gutter-in-remote-files -;; (not (file-remote-p buffer-file-name)))) -;; (diff-hl-mode +1))) -;; (add-hook! (text-mode prog-mode conf-mode after-save) -;; #'+vc-gutter|init) -;; ;; standardize fringe size -;; (if (fboundp 'fringe-mode) (fringe-mode '4)) -;; :config -;; (setq vc-git-diff-switches '("--histogram")) -;; ;; Update diffs when it makes sense too, without being too slow -;; (if (not +vc-gutter-diff-unsaved-buffer) -;; (add-hook! '(doom-escape-hook focus-in-hook) #'diff-hl-update) -;; (diff-hl-flydiff-mode +1) -;; (add-hook! '(doom-escape-hook focus-in-hook) #'diff-hl-flydiff-update) -;; (when (featurep! :feature evil) -;; (when diff-hl-flydiff-timer -;; (cancel-timer diff-hl-flydiff-timer)) -;; (add-hook 'evil-insert-state-exit-hook #'diff-hl-flydiff-update))) -;; ;; Don't delete the current hunk's indicators while we're editing -;; (advice-remove #'diff-hl-overlay-modified #'ignore) -;; ;; Update diff-hl when magit refreshes -;; (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh) -;; ;; update git-gutter when using these commands -;; (advice-add #'magit-stage :after #'+version-control|update-git-gutter) -;; (advice-add #'magit-unstage :after #'+version-control|update-git-gutter) -;; (advice-add #'magit-stage-file :after #'+version-control|update-git-gutter) -;; (advice-add #'magit-unstage-file :after #'+version-control|update-git-gutter) -;; ;; Draw me like one of your French editors -;; (setq-default fringes-outside-margins t) -;; (cond ((or +vc-gutter-in-margin (not (display-graphic-p))) -;; (diff-hl-margin-mode) -;; (setq diff-hl-margin-symbols-alist -;; '((insert . "❙") (delete . "^") (change . "❙") -;; (unknown . "❙") (ignored . "❙")))) -;; (t -;; ;; Because diff-hl is in the left fringe -;; (setq flycheck-indication-mode 'right-fringe) -;; (defun +vc-gutter|setup-fringe-bitmaps () -;; "Define thin fringe bitmaps for maximum sexiness." -;; (define-fringe-bitmap 'diff-hl-bmp-top [224] nil nil '(center repeated)) -;; (define-fringe-bitmap 'diff-hl-bmp-middle [224] nil nil '(center repeated)) -;; (define-fringe-bitmap 'diff-hl-bmp-bottom [224] nil nil '(center repeated)) -;; (define-fringe-bitmap 'diff-hl-bmp-insert [224] nil nil '(center repeated)) -;; (define-fringe-bitmap 'diff-hl-bmp-single [224] nil nil '(center repeated)) -;; (define-fringe-bitmap 'diff-hl-bmp-delete [240 224 192 128] nil nil 'top)) -;; (defun +vc-gutter-type-at-pos (type _pos) -;; "Return the bitmap for `diff-hl' to use for change at point." -;; (pcase type -;; (`unknown 'question-mark) -;; (`delete 'diff-hl-bmp-delete) -;; (`change 'diff-hl-bmp-middle) -;; (`ignored 'diff-hl-bmp-i) -;; (x (intern (format "diff-hl-bmp-%s" x))))) -;; ;; Tweak the fringe bitmaps so we get long, elegant bars -;; (setq diff-hl-fringe-bmp-function #'+vc-gutter-type-at-pos -;; diff-hl-draw-borders nil) -;; (add-hook 'diff-hl-mode-hook #'+vc-gutter|setup-fringe-bitmaps)))) - diff --git a/modules/ui/vc-gutter/packages.el b/modules/ui/vc-gutter/packages.el index 921b14a94..a12b41b8f 100644 --- a/modules/ui/vc-gutter/packages.el +++ b/modules/ui/vc-gutter/packages.el @@ -2,4 +2,3 @@ ;;; ui/vc-gutter/packages.el (package! git-gutter-fringe) -;; (package! diff-hl)