diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 000000000..ff0b5bb67 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,39 @@ +Here are some things you should try before filing a bug report: + ++ Run `make install` to ensure all plugins are installed. ++ `void-function` or `void-variable` errors could signal an out-of-date autoloads file. Run `make autoloads` or `M-x doom//reload-autoloads` to update it. ++ Scan for common OS/environment issues with `make doctor`. ++ Never debug byte-compiled code. It will interfere in subtle ways. Clean up \*.elc files with `make clean` or `M-x doom//clean-byte-compiled-files`. ++ Check [the FAQ](https://github.com/hlissner/doom-emacs/wiki/FAQ#troubleshooting) to see if your issue is mentioned. ++ Check the relevant module's README.org, if one exists. There may be extra steps to getting certain features to work. + +If none of those help, remove this section and fill out the four sections in the template below. + +--- + +### Observed behavior + +Describe what happened. Any aids you can include (that you think could be relevant) are a tremendous help; like a screencast gif, video, or link to your customizations for Doom (e.g. a repo or a pastebin). + +### Expected behavior + +Describe what you _expected_ to happen. + +### Steps to reproduce + +1. Select these example steps, +2. Delete them, +3. And replace them with precise steps to reproduce your issue. + +### System information + +
+Click to expand + +``` +Replace this line with the output of *one* of these commands: + ++ `M-x doom/info` (from inside Emacs) ++ `DEBUG=1 make doctor` (command line) +``` +
diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE new file mode 100644 index 000000000..c7cab1bfb --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE @@ -0,0 +1,5 @@ +Thank you for contributing to Doom! + +Before you submit this PR, please make sure your PR is targeted at develop, not +master (unless this is a fix for a critical error). Then replace this message +with a description of your changes. diff --git a/.gitignore b/.gitignore index f29afbc1e..47cf11c70 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .cask/ var/ /init.el +modules/private/ # emacs tempfiles that shouldn't be there .mc-lists.el diff --git a/CHANGELOG.org b/CHANGELOG.org index 113e95879..ce6b4c018 100644 --- a/CHANGELOG.org +++ b/CHANGELOG.org @@ -12,6 +12,91 @@ - [[#200-jan-17-2017][2.0.0 (Jan 17, 2017)]] * Unreleased (develop) ++ *Module changes:* + + Add =private/default= to replace =private/hlissner=, which is a more generic + "starter" module for new users. My private module is now at + [[https://github.com/hlissner/doom-emacs-private][hlissner/doom-emacs-private]]. ++ =general= + + Fix "peculiar error" messages when running =bin/doom-doctor=, caused by + poorly thought out error handling (see [[https://github.com/hlissner/doom-emacs/issues/175][#175]]). + + Fix ~doom/am-i-secure~ command. + + Add unicode symbols to package management commands. Looks a little nicer <3. + + Transform =recentf= entries into their absolute paths with symlinks resolved + (using ~file-truename~). + + Local data and cache files are no longer host-namespaced. Originally, this + was meant to facilitate using the same config files (symlinked) across + different computers (e.g. via dropbox). Byte-compiled packages on one + computer would cause errors on another, so I abandoned this practice, making + ~doom-host-dir~ unnecessary. + + Fix an issue during live byte-compilation (with auto-compile-on-save-mode) + where ~gc-cons-threshold~ would get set to 304mb in the user's active + session, causing freezes and stuttering. + + Improve error handling when byte compiling Doom. It now cleans up after + itself if something goes wrong, with improved error handling overall. + + Fix =stringp= error thrown by ~doom-fetch~ in noninteractive sessions. + + =core= Improve GPG integration by setting ~epa-file-encrypt-to~ to + ~user-mail-address~, and ~epa-pinentry-mode~ to ~'loopback~ (so that Emacs + will prompt you for the passphrase in the minibuffer). + + =core-packages= Fix ~doom-module-pairs~ returning pairs in arbitrary order, + causing load order errors. This is because the ~hash-table-values~ function + in Emacs 25 and under uses ~maphash~, which reverses the hash-table, while + newer versions use ~cl-loop~, which doesn't. + + =core-packages= Fix ~doom//*~ not running from the correct + default-directory, causing errors. + + =core-ui= Fix cryptic missing-font errors (also, they are warnings now). + + =core-ui= Account for Emacs 26 line numbers when calculating + ~visual-fill-column-width~. + + =core-editor= the =KILL= argument for ~quit-window &optional KILL WINDOW~ + has been flipped and now kills the window's buffer by default. + + =core-projects= Add ~doom-project-find-file~ and ~doom-project-browse~ for + interactively fuzzy-finding and opening files in a directory/project. + + Add ~doom/info~ command for collecting information about your system and + session and puts it in the clipboard. This is to make it easier for those + reporting bugs to easily include it. + + Fix ibuffer windows not being handed off to popup system (see [[https://github.com/hlissner/doom-emacs/issues/309][#309]]). ++ =feature= + + =file-templates= + + Add a file template for fish shell scripts (thanks to [[https://github.com/amosbird][amosbird]]). + + Fix some file templates not being inserted, like module README.org files. + + =version-control= When evil-mode is available, start git commit message + buffers in insert mode (see [[https://github.com/hlissner/doom-emacs/issues/300][#300]]). + + =jump= Always prompt for provider when no major-mode specific online jumper + is defined (for ~+jump/online~ and ~+jump/documentation~). ++ =ui= + + =doom-dashboard= + + Fix ~whitespace-mode~ and ~show-trailing-whitespace~ turning the dashboard + into a Christmas tree. + + Add the ~+doom-dashboard-pwd-policy~ option, giving you control over how + ~default-directory~ is set in the dashboard. By default this is set to + ~'last-project~, meaning the dashboard's cwd will match the project of the + last buffer you killed. ++ =completion= + + =ivy= Fix TAB only half-triggering auto-completion (see [[https://github.com/hlissner/doom-emacs/issues/303][#303]]). ++ =lang= + + =cc= + + Add =rtags= support for better code navigation. This replaces gxtag and + etag support. Also includes automatic management of the rdm daemon. + + Improved support for JSON compilation databases. Irony-mode (and by + extension its flycheck and company plugins will now pick them up with more + consistency). This ensures all these tools run with the same compile + options as your project. + + New ~+cc/reload-compile-db~ commands forcibly refreshes the compilation db + of your current project, updating irony and running rtags daemon, if any. + + The ~+cc-include-paths~ and ~+cc-compiler-options~ options have been + renamed to ~+cc-default-include-paths~ and ~+cc-default-compiler-options~ + for clarity. + + Fix irony-mode complaining when it is enabled in non-C major-modes. + + =org= + + Change repo source for org-plus-contrib to emacsmirror. + + Fix ~+org-dir~ being resolved too soon, robbing the user of the + opportunity to change it in their private module. + + Fix ~invalid file location~ error when capturing to a TODO template. + + Prevent =org-plus-contrib= from being installed on Emacs 26+, as 9.1.4 is + included with it. + + Fix invalid function errors when drag'n'dropping files into org buffers + (see [[https://github.com/hlissner/doom-emacs/issues/307][#307]]). + + =java= Fix Doom install meghanada server while byte compiling your Emacs + configuration. * 2.0.8 (Dec 09, 2017) + *Module changes:* diff --git a/bin/doom-doctor b/bin/doom-doctor index a57693582..eb8b55108 100755 --- a/bin/doom-doctor +++ b/bin/doom-doctor @@ -8,6 +8,7 @@ ;; In case it isn't defined (in really old versions of Emacs, like the one that ;; ships with MacOS). (defvar user-emacs-directory (expand-file-name "~/.emacs.d/")) +(defvar doom-debug-mode (getenv "DEBUG")) (unless (equal (expand-file-name user-emacs-directory) (expand-file-name "~/.emacs.d/")) @@ -15,14 +16,20 @@ (require 'pp) +(defsubst string-trim-right (string &optional regexp) + (if (string-match (concat "\\(?:" (or regexp "[ \t\n\r]+") "\\)\\'") string) + (replace-match "" t t string) + string)) + ;; (defvar doom-init-p nil) (defvar doom-errors 0) (defmacro check! (cond &rest body) (declare (indent defun)) - `(when ,cond - ,@body - (setq doom-errors (1+ doom-errors)))) + `(let ((it ,cond)) + (when it + ,@body + (setq doom-errors (1+ doom-errors))))) (defun indented (spc msg) (declare (indent defun)) @@ -57,26 +64,18 @@ "\n"))) (buffer-string))) -(defmacro wait-for! (var if-body &optional else-body) - (declare (indent defun)) - `(let ((i 0)) - (while (and (not ,var) - (< i 5)) - (sleep-for 1) - (setq i (1+ i))) - (if ,var - ,if-body - ,else-body))) +(defun sh (cmd) + (string-trim-right (shell-command-to-string cmd))) (defun color (code msg &rest args) (format "\e[%dm%s\e[%dm" code (apply #'format msg args) 0)) (defalias 'msg! #'message) -(defmacro error! (&rest args) `(message (color 1 (color 31 ,@args)))) -(defmacro warn! (&rest args) `(message (color 1 (color 33 ,@args)))) -(defmacro success! (&rest args) `(message (color 1 (color 32 ,@args)))) -(defmacro log! (&rest args) `(if doom-debug-mode (message (color 34 ,@args)))) -(defmacro explain! (&rest args) `(message (indented 2 (autofill ,@args)))) +(defmacro error! (&rest args) `(msg! (color 1 (color 31 ,@args)))) +(defmacro warn! (&rest args) `(msg! (color 1 (color 33 ,@args)))) +(defmacro success! (&rest args) `(msg! (color 1 (color 32 ,@args)))) +(defmacro section! (&rest args) `(msg! (color 34 ,@args))) +(defmacro explain! (&rest args) `(msg! (indented 2 (autofill ,@args)))) ;;; Polyfills ;; early versions of emacs won't have this @@ -88,15 +87,21 @@ ;; --- start a'doctorin' -------------------------------------- -(msg! "%s\nRunning Emacs v%s, commit %s" +(msg! "%s\nRunning Emacs v%s, commit %s\n" (color 1 "DOOM Doctor") (color 1 emacs-version) (if (executable-find "git") - (shell-command-to-string "git rev-parse HEAD") + (sh "git rev-parse HEAD") "n/a")) + +(msg! "shell: %s%s" + (getenv "SHELL") + (if (equal (getenv "SHELL") (sh "echo $SHELL")) + "" + (color 31 " (mismatch)"))) (when (boundp 'system-configuration-features) (msg! "Compiled with:\n%s" (indented 2 (autofill system-configuration-features)))) -(msg! "uname -a:\n%s" (indented 2 (autofill (shell-command-to-string "uname -a")))) +(msg! "uname -a:\n%s\n" (indented 2 (autofill (sh "uname -a")))) (let (doom-core-packages doom-debug-mode) (condition-case ex @@ -108,11 +113,10 @@ (doom|finalize) (success! "Attempt to load DOOM: success! Loaded v%s" doom-version) (when (executable-find "git") - (msg! "Revision %s" - (or (ignore-errors - (let ((default-directory user-emacs-directory)) - (shell-command-to-string "git rev-parse HEAD"))) - "\n")))) + (msg! "Revision %s\n" + (ignore-errors + (let ((default-directory user-emacs-directory)) + (sh "git rev-parse HEAD")))))) ('error (warn! "Attempt to load DOOM: failed\n %s\n" (or (cdr-safe ex) (car ex)))))) @@ -120,7 +124,7 @@ ;; --- is emacs set up properly? ------------------------------ -(log! "test-emacs") +(section! "test-emacs") (check! (version< emacs-version "25.1") (error! "Important: Emacs %s detected [%s]" emacs-version (executable-find "emacs")) (explain! @@ -133,13 +137,13 @@ ;; --- is the environment set up properly? -------------------- ;; windows? windows -(log! "test-windows") +(section! "test-windows") (check! (memq system-type '(windows-nt ms-dos cygwin)) (warn! "Warning: Windows detected") (explain! "DOOM was designed for MacOS and Linux. Expect a bumpy ride!")) ;; are all default fonts present -(log! "test-fonts") +(section! "test-fonts") (if (not (fboundp 'find-font)) (progn (warn! "Warning: unable to detect font") @@ -162,19 +166,19 @@ "case, ignore this warning.")))))) ;; gnutls-cli & openssl -(log! "test-gnutls") +(section! "test-gnutls") (cond ((executable-find "gnutls-cli")) ((executable-find "openssl") - (let* ((output (shell-command-to-string "openssl ciphers -v")) + (let* ((output (sh "openssl ciphers -v")) (protocols (let (protos) (mapcar (lambda (row) (add-to-list 'protos (cadr (split-string row " " t)))) - (split-string (shell-command-to-string "openssl ciphers -v") "\n")) + (split-string (sh "openssl ciphers -v") "\n")) (delq nil protos)))) (check! (not (or (member "TLSv1.1" protocols) (member "TLSv1.2" protocols))) - (let ((version (cadr (split-string (shell-command-to-string "openssl version") " " t)))) + (let ((version (cadr (split-string (sh "openssl version") " " t)))) (warn! "Warning: couldn't find gnutls-cli, and OpenSSL is out-of-date (v%s)" version) (explain! "This may not affect your Emacs experience, but there are security " @@ -198,7 +202,7 @@ "network, provider, government, neckbearded mother-in-laws, geeky roommates, " "or just about anyone who knows more about computers than you do!")))) -(log! "test-tls") +(section! "test-tls") (cond ((not (string-match-p "\\_" system-configuration-features)) (warn! "Warning: You didn't install Emacs with gnutls support") (explain! @@ -211,53 +215,50 @@ " brew tap d12frosted/emacs-plus" " brew install emacs-plus")))) + ((not (fboundp 'url-retrieve-synchronously)) + (error! "Can't find url-retrieve-synchronously function. Are you running Emacs 24+?")) + ((or (executable-find "gnutls-cli") (executable-find "openssl")) (let ((tls-checktrust t) (gnutls-verify-error t)) - (dolist (url '("https://elpa.gnu.org" - "https://melpa.org")) - (condition-case-unless-debug ex - (let (result) - (let ((inhibit-message t)) - (url-retrieve url (lambda (status &rest _) (setq result status)))) - (wait-for! result - (when (getenv "DEBUG") - (success! "Verified %s" (nth 2 (split-string url "/")))) - (signal 'timed-out url))) - ('timed-out - (error! "Timed out trying to contact %s" ex)) - ('error - (check! t - (error! "Rejected %s" url) - (explain! (pp-to-string ex)))))) + (dolist (url '("https://elpa.gnu.org" "https://melpa.org")) + (check! (condition-case-unless-debug e + (if (let ((inhibit-message t)) (url-retrieve-synchronously url)) + (ignore (success! "Validated %s" url)) + 'empty) + ('timed-out 'timeout) + ('error e)) + (pcase it + (`empty (error! "Couldn't reach %s" url)) + (`timeout (error! "Timed out trying to contact %s" ex)) + (_ + (error! "Failed to validate %s" url) + (when doom-debug-mode + (explain! (pp-to-string it))))))) (dolist (url '("https://self-signed.badssl.com" "https://wrong.host.badssl.com/")) - (condition-case-unless-debug ex - (let (result) - (let ((inhibit-message t)) - (url-retrieve url (lambda (status &rest _) (setq result status)))) - (wait-for! result - (check! t - (warn! "Verified %s (this shouldn't happen!)" (nth 2 (split-string url "/"))) - (explain! (pp-to-string result))) - (signal 'timed-out url))) - ('timed-out - (error! "Timed out trying to contact %s" ex)) - ('error - (when (getenv "DEBUG") - (success! "Rejected %s (a good thing!)" url) - (explain! (pp-to-string ex)))))))) + (check! (condition-case-unless-debug e + (if (let ((inhibit-message t)) (url-retrieve-synchronously url)) + t + 'empty) + ('timed-out 'timeout) + ('error (ignore (success! "Successfully rejected %s" url)))) + (pcase it + (`empty (error! "Couldn't reach %s" url)) + (`timeout (error! "Timed out trying to contact %s" ex)) + (_ + (error! "Validated %s (this shouldn't happen!)" url))))))) (t (error! "Nope!"))) ;; bsd vs gnu tar -(log! "test-tar") +(section! "test-tar") (let ((tar-bin (or (executable-find "gtar") (executable-find "tar")))) (if tar-bin - (check! (not (string-match-p "(GNU tar)" (shell-command-to-string (format "%s --version" tar-bin)))) + (check! (not (string-match-p "(GNU tar)" (sh (format "%s --version" tar-bin)))) (warn! "Warning: BSD tar detected") (explain! "QUELPA (through package-build) uses the system tar to build plugins, but it " @@ -275,50 +276,51 @@ ;; --- report! ------------------------------------------------ -(when (getenv "DEBUG") +(when doom-debug-mode (msg! "\n====\nHave some debug information:\n") (when (bound-and-true-p doom-modules) (msg! " + enabled modules:\n%s" - (indented 4 - (columns 3 23 - (mapcar (lambda (x) (format "+%s" x)) - (mapcar #'cdr (doom-module-pairs))))))) + (indented 4 + (columns 3 23 + (mapcar (lambda (x) (format "+%s" x)) + (mapcar #'cdr (doom-module-pairs))))))) (when (and (bound-and-true-p doom-packages) (require 'package nil t)) (msg! " + enabled packages:\n%s" - (indented 4 - (columns 2 35 - (mapcar (lambda (pkg) - (let ((desc (cadr (assq pkg package-alist)))) - (when desc - (package-desc-full-name desc)))) - (sort (mapcar #'car doom-packages) #'string-lessp)))))) + (indented 4 + (columns 2 35 + (delq nil + (mapcar (lambda (pkg) + (let ((desc (cadr (assq pkg package-alist)))) + (when desc + (package-desc-full-name desc)))) + (sort (mapcar #'car doom-packages) #'string-lessp))))))) (msg! " + byte-compiled files:\n%s" - (indented 4 - (columns 2 39 - (let ((files (append (directory-files-recursively doom-core-dir ".elc$") - (directory-files-recursively doom-modules-dir ".elc$")))) - (or (and files (mapcar (lambda (file) (file-relative-name file doom-emacs-dir)) - (nreverse files))) - (list "n/a")))))) + (indented 4 + (columns 2 39 + (let ((files (append (directory-files-recursively doom-core-dir ".elc$") + (directory-files-recursively doom-modules-dir ".elc$")))) + (or (and files (mapcar (lambda (file) (file-relative-name file doom-emacs-dir)) + (nreverse files))) + (list "n/a")))))) (msg! " + exec-path:\n%s" - (indented 4 - (columns 1 79 exec-path))) + (indented 4 + (columns 1 79 exec-path))) (msg! " + PATH:\n%s" - (indented 4 - (columns 1 79 (split-string (getenv "PATH") ":"))))) + (indented 4 + (columns 1 79 (split-string (getenv "PATH") ":"))))) ;; (if (= doom-errors 0) (success! "Everything seems fine, happy Emacs'ing!") (message "\n----") (warn! "There were issues!") - (unless (getenv "DEBUG") + (unless doom-debug-mode (msg! "\nHopefully these can help you find problems. If not, run this doctor again with DEBUG=1:") (msg! "\n DEBUG=1 make doctor\n") (msg! "And file a bug report with its output at https://github.com/hlissner/.emacs.d/issues"))) diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el index 0b6815021..83369cd4c 100644 --- a/core/autoload/buffers.el +++ b/core/autoload/buffers.el @@ -4,8 +4,12 @@ ;;;###autoload (defvar doom-real-buffer-functions '() - "A list of functions that are run to determine if a buffer is real.") + "A list of predicate functions run to determine if a buffer is real. These +functions are iterated over with one argument, the buffer in question. If any +function returns non-nil, the procession stops and the buffer is qualified as +real.") +;;;###autoload (defvar-local doom-real-buffer-p nil "If non-nil, this buffer should be considered real no matter what.") @@ -14,36 +18,17 @@ "The name of the buffer to fall back to if no other buffers exist (will create it if it doesn't exist).") + +;; +;; Functions +;; + ;;;###autoload (defun doom-fallback-buffer () "Returns the fallback buffer, creating it if necessary. By default this is the scratch buffer." (get-buffer-create doom-fallback-buffer)) -;;;###autoload -(defun doom-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 "r") - (cond ((region-active-p) - (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)))) - - -;; Buffer Life and Death ;;;;;;;;;;;;;;; ;;;###autoload (defalias 'doom-buffer-list #'buffer-list) @@ -66,6 +51,28 @@ If no project is active, return all buffers." if (doom-real-buffer-p buf) collect buf)) +;;;###autoload +(defun doom-real-buffer-p (&optional buffer-or-name) + "Returns t if BUFFER-OR-NAME is a 'real' buffer. The complete criteria for a +real buffer is: + + 1. The buffer-local value of `doom-real-buffer-p' (variable) is non-nil OR + 2. Any function in `doom-real-buffer-functions' must return non-nil when + passed this buffer OR + 3. The current buffer: + a) has a `buffer-file-name' defined AND + b) is not in a popup window (see `doom-popup-p') AND + c) is not a special buffer (its name isn't something like *Help*) + +If BUFFER-OR-NAME is omitted or nil, the current buffer is tested." + (when-let* ((buf (ignore-errors (window-normalize-buffer buffer-or-name)))) + (or (buffer-local-value 'doom-real-buffer-p buf) + (run-hook-with-args-until-success 'doom-real-buffer-functions buf) + (not (or (doom-popup-p buf) + (minibufferp buf) + (string-match-p "^\\s-*\\*" (buffer-name buf)) + (not (buffer-file-name buf))))))) + ;;;###autoload (defun doom-buffers-in-mode (modes &optional buffer-list derived-p) "Return a list of buffers whose `major-mode' is `eq' to MODE(S). @@ -112,8 +119,7 @@ If DERIVED-P, test with `derived-mode-p', otherwise use `eq'." "Switch to the next buffer N times (previous, if N < 0), skipping over unreal buffers. If there's nothing left, switch to `doom-fallback-buffer'. See `doom-real-buffer-p' for what 'real' means." - (let ((buffers (delq (current-buffer) (doom-real-buffer-list))) - (project-dir (doom-project-root))) + (let ((buffers (delq (current-buffer) (doom-real-buffer-list)))) (cond ((or (not buffers) (zerop (% n (1+ (length buffers))))) (switch-to-buffer (doom-fallback-buffer) nil t)) @@ -130,96 +136,49 @@ buffers. If there's nothing left, switch to `doom-fallback-buffer'. See do (dotimes (_i (abs n)) (funcall move-func))))) - (when (eq (current-buffer) (doom-fallback-buffer)) - (cd project-dir)) + (force-mode-line-update) (current-buffer))) ;;;###autoload -(defun doom-real-buffer-p (&optional buffer-or-name) - "Returns t if BUFFER-OR-NAME is a 'real' buffer. The complete criteria for a -real buffer is: - - 1. The buffer-local value of `doom-real-buffer-p' (variable) is non-nil OR - 2. Any function in `doom-real-buffer-functions' must return non-nil when - passed this buffer OR - 3. The current buffer: - a) has a `buffer-file-name' defined AND - b) is not in a popup window (see `doom-popup-p') AND - c) is not a special buffer (its name isn't something like *Help*) - -If BUFFER-OR-NAME is omitted or nil, the current buffer is tested." - (when-let* ((buf (ignore-errors (window-normalize-buffer buffer-or-name)))) - (or (buffer-local-value 'doom-real-buffer-p buf) - (run-hook-with-args-until-success 'doom-real-buffer-functions buf) - (not (or (doom-popup-p buf) - (minibufferp buf) - (string-match-p "^\\s-*\\*" (buffer-name buf)) - (not (buffer-file-name buf))))))) - -;;;###autoload -(defun doom/next-buffer () - "Switch to the next real buffer, skipping non-real buffers. See -`doom-real-buffer-p' for what 'real' means." - (interactive) - (doom--cycle-real-buffers +1)) - -;;;###autoload -(defun doom/previous-buffer () - "Switch to the previous real buffer, skipping non-real buffers. See -`doom-real-buffer-p' for what 'real' means." - (interactive) - (doom--cycle-real-buffers -1)) +(defun doom-set-buffer-real (buffer flag) + "Forcibly mark BUFFER as FLAG (non-nil = real)." + (with-current-buffer buffer + (setq doom-real-buffer-p flag))) ;;;###autoload (defun doom-kill-buffer (&optional buffer dont-save) - "Kill BUFFER (falls back to current buffer if omitted) then switch to a real -buffer. If the buffer is present in another window, only bury it. + "Kill BUFFER (defaults to current buffer), but make sure we land on a real +buffer. Bury the buffer if the buffer is present in another window. Will prompt to save unsaved buffers when attempting to kill them, unless DONT-SAVE is non-nil. See `doom-real-buffer-p' for what 'real' means." - (setq buffer (or buffer (current-buffer))) - (when (and (bufferp buffer) (buffer-live-p buffer)) - (let ((buffer-win (get-buffer-window buffer)) - (only-buffer-window-p (= 1 (length (get-buffer-window-list buffer nil t))))) - ;; deal with unsaved buffers - (when (and only-buffer-window-p - (buffer-file-name buffer) + (unless buffer + (setq buffer (current-buffer))) + (when (and (bufferp buffer) + (buffer-live-p buffer)) + (let ((buffer-win (get-buffer-window buffer))) + ;; deal with modified buffers + (when (and (buffer-file-name buffer) (buffer-modified-p buffer)) (with-current-buffer buffer (if (and (not dont-save) (yes-or-no-p "Buffer is unsaved, save it?")) (save-buffer) (set-buffer-modified-p nil)))) - (if buffer-win - ;; deal with dedicated windows - (if (window-dedicated-p buffer-win) - (unless (window--delete buffer-win t t) - (split-window buffer-win) - (window--delete buffer-win t t)) - ;; cycle to a real buffer - (with-selected-window buffer-win - (doom--cycle-real-buffers -1) - (when buffer-win - (unrecord-window-buffer buffer-win buffer)) - (when only-buffer-window-p - (kill-buffer buffer))) - (not (eq (current-buffer) buffer))) - (kill-buffer buffer))))) - -;;;###autoload -(defun doom-force-kill-buffer (&optional buffer dont-save) - "Kill BUFFER globally and ensure all windows previously showing BUFFER have -switched to a real buffer." - (interactive) - (let* ((buffer (or buffer (current-buffer))) - (windows (get-buffer-window-list buffer nil t))) - (doom-kill-buffer buffer dont-save) - (dolist (win windows) - (with-selected-window win - (unless (doom-real-buffer-p) - (doom/previous-buffer)))))) + ;; kill the buffer (or close dedicated window) + (cond ((not buffer-win) + (kill-buffer buffer)) + ((window-dedicated-p buffer-win) + (unless (window--delete buffer-win t t) + (split-window buffer-win) + (window--delete buffer-win t t))) + (t ; cycle to a real buffer + (with-selected-window buffer-win + (doom--cycle-real-buffers -1) + (kill-buffer buffer))))) + (not (eq (current-buffer) buffer)))) ;;;###autoload (defun doom-kill-buffer-and-windows (buffer) @@ -229,23 +188,6 @@ switched to a real buffer." (delete-window window))) (kill-buffer buffer)) -;;;###autoload -(defun doom-kill-process-buffers () - "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)) - ;;;###autoload (defun doom-kill-matching-buffers (pattern &optional buffer-list) "Kill all buffers (in current workspace OR in BUFFER-LIST) that match the @@ -254,13 +196,33 @@ regex PATTERN. Returns the number of killed buffers." (dolist (buf buffers (length buffers)) (doom-kill-buffer buf t)))) + +;; +;; Interactive commands +;; + ;;;###autoload -(defun doom/kill-this-buffer () +(defun doom/kill-this-buffer (&optional interactive-p) "Use `doom-kill-buffer' on the current buffer." - (interactive) - (when (and (not (doom-kill-buffer)) (called-interactively-p 'interactive)) + (interactive (list 'interactive)) + (when (and (not (doom-kill-buffer)) interactive-p) (message "Nowhere left to go!"))) +;;;###autoload +(defun doom/kill-this-buffer-in-all-windows (buffer &optional dont-save) + "Kill BUFFER globally and ensure all windows previously showing this buffer +have switched to a real buffer. + +If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)." + (interactive + (list (current-buffer) current-prefix-arg)) + (cl-assert (bufferp buffer) t) + (let ((windows (get-buffer-window-list buffer nil t))) + (doom-kill-buffer buffer dont-save) + (cl-loop for win in windows + if (doom-real-buffer-p (window-buffer win)) + do (with-selected-window win (doom/previous-buffer))))) + ;;;###autoload (defun doom/kill-all-buffers (&optional project-p) "Kill all buffers and closes their windows. @@ -305,18 +267,46 @@ project." (message "Killed %s buffers" n)))) ;;;###autoload -(defun doom/cleanup-buffers (&optional all-p) - "Clean up buried and inactive process buffers in the current workspace." +(defun doom/cleanup-session (&optional all-p) + "Clean up buried buries and orphaned processes in the current workspace. If +ALL-P (universal argument), clean them up globally." (interactive "P") + (run-hooks 'doom-cleanup-hook) (let ((buffers (doom-buried-buffers (if all-p (buffer-list)))) - (n 0)) + (n 0) + kill-buffer-query-functions) (mapc #'kill-buffer buffers) - (setq n (+ n (length buffers) (doom-kill-process-buffers))) + (setq n (+ n (length buffers) (doom/cleanup-processes))) (when (called-interactively-p 'interactive) (message "Cleaned up %s buffers" n)))) ;;;###autoload -(defun doom-set-buffer-real (buffer flag) - "Forcibly mark a buffer's real property, no matter what." - (with-current-buffer buffer - (setq doom-real-buffer-p flag))) +(defun doom/cleanup-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)) + +;;;###autoload +(defun doom/next-buffer () + "Switch to the next real buffer, skipping non-real buffers. See +`doom-real-buffer-p' for what 'real' means." + (interactive) + (doom--cycle-real-buffers +1)) + +;;;###autoload +(defun doom/previous-buffer () + "Switch to the previous real buffer, skipping non-real buffers. See +`doom-real-buffer-p' for what 'real' means." + (interactive) + (doom--cycle-real-buffers -1)) diff --git a/core/autoload/debug.el b/core/autoload/debug.el index 77b48b4c5..9cd01271f 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -65,7 +65,7 @@ selection of all minor-modes, active or not." in '("https://wrong.host.badssl.com/" "https://self-signed.badssl.com/") if (condition-case _e - (url-retrieve bad (lambda (_retrieved) t)) + (url-retrieve-synchronously bad) (error nil)) collect bad))) (error (format "tls seems to be misconfigured (it got %s)." @@ -86,3 +86,15 @@ selection of all minor-modes, active or not." (profiler-report) (profiler-stop)) (setq doom--profiler (not doom--profiler))) + +;;;###autoload +(defun doom/info () + "Collects information about this session of Doom Emacs and copies it to the +clipboard. Helpful when filing bug reports!" + (interactive) + (with-temp-buffer + (message "Producing information about your system...") + (call-process (expand-file-name "bin/doom-doctor" doom-emacs-dir) nil t) + (ansi-color-apply-on-region (point-min) (point-max)) + (kill-new (buffer-string)) + (message "Done. Copied to clipboard!"))) diff --git a/core/autoload/editor.el b/core/autoload/editor.el index 3188c27c9..b98d53e75 100644 --- a/core/autoload/editor.el +++ b/core/autoload/editor.el @@ -2,7 +2,7 @@ ;;;###autoload (defun doom/sudo-find-file (file) - "Open a file as root." + "Open FILE as root." (interactive (list (read-file-name "Open as root: "))) (find-file (if (file-writable-p file) @@ -218,8 +218,29 @@ consistent throughout a selected region, depending on `indent-tab-mode'." (tabify beg end) (untabify beg end))) +;;;###autoload +(defun doom/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 "r") + (cond ((region-active-p) + (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|enable-delete-trailing-whitespace () "Attaches `delete-trailing-whitespace' to a buffer-local `before-save-hook'." (add-hook 'before-save-hook #'delete-trailing-whitespace nil t)) - diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 7fb7356ad..c2049afdd 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -1,11 +1,13 @@ ;;; core/autoload/packages.el -*- lexical-binding: t; -*- +(require 'use-package) +(require 'quelpa) + (defvar doom--last-refresh nil) ;;;###autoload (defun doom-refresh-packages (&optional force-p) "Refresh ELPA packages." - (doom-initialize) (when force-p (doom-refresh-clear-cache)) (unless (or (persistent-soft-fetch 'last-pkg-refresh "emacs") @@ -31,13 +33,14 @@ "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." (cl-assert (symbolp name) t) - (doom-initialize) (cond ((and (or (quelpa-setup-p) (error "Could not initialize quelpa")) (assq name quelpa-cache)) 'quelpa) ((assq name package-alist) 'elpa) + ((package-built-in-p name) + 'emacs) ((not noerror) (error "%s package is not installed" name)))) @@ -84,7 +87,7 @@ installed with. Returns nil otherwise, or if package isn't installed." (doom-initialize-packages) (and (package-installed-p name) (let* ((plist (cdr (assq name doom-packages))) - (old-backend (doom-package-backend name t)) + (old-backend (doom-package-backend name 'noerror)) (new-backend (if (plist-get plist :recipe) 'quelpa 'elpa))) (not (eq old-backend new-backend))))) @@ -114,14 +117,12 @@ If INSTALLED-ONLY-P, only return packages that are installed." ;;;###autoload (defun doom-get-depending-on (name) "Return a list of packages that depend on the package named NAME." - (doom-initialize) (when-let* ((desc (cadr (assq name package-alist)))) (mapcar #'package-desc-name (package--used-elsewhere-p desc nil t)))) ;;;###autoload (defun doom-get-dependencies-for (name &optional only) "Return a list of dependencies for a package." - (doom-initialize) (package--get-deps name only)) ;;;###autoload @@ -156,8 +157,9 @@ Used by `doom//packages-update'." (load ,(expand-file-name "core.el" doom-core-dir))) (doom-package-outdated-p ',pkg))) futures)) - (append (delq nil (mapcar #'doom-package-outdated-p elpa-pkgs)) - (delq nil (mapcar #'async-get (reverse futures))))))) + (delq nil + (append (mapcar #'doom-package-outdated-p elpa-pkgs) + (mapcar #'async-get (reverse futures))))))) ;;;###autoload (defun doom-get-orphaned-packages () @@ -170,7 +172,8 @@ Used by `doom//packages-autoremove'." (append (mapcar #'car doom-packages) doom-core-packages))) (append (package--removable-packages) (cl-loop for pkg in package-selected-packages - if (doom-package-different-backend-p pkg) + if (and (doom-package-different-backend-p pkg) + (not (package-built-in-p pkg))) collect pkg)))) ;;;###autoload @@ -213,7 +216,6 @@ Used by `doom//packages-install'." (symbol-name (car other)))) (defun doom--packages-choose (prompt) - (doom-initialize) (let ((table (cl-loop for pkg in package-alist unless (package-built-in-p (cdr pkg)) collect (cons (package-desc-full-name (cdr pkg)) @@ -269,7 +271,6 @@ example; the package name can be omitted)." (defun doom-update-package (name &optional force-p) "Updates package NAME (a symbol) if it is out of date, using quelpa or package.el as appropriate." - (doom-initialize) (unless (package-installed-p name) (user-error "%s isn't installed" name)) (when (doom-package-different-backend-p name) @@ -298,7 +299,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'." - (doom-initialize) (unless (package-installed-p name) (user-error "%s isn't installed" name)) (let ((inhibit-message (not doom-debug-mode)) @@ -318,13 +318,14 @@ package.el as appropriate." ;; -;; Interactive commands +;; Batch/interactive commands ;; ;;;###autoload (defun doom//packages-install () "Interactive command for installing missing packages." (interactive) + (message! "Looking for packages to install...") (let ((packages (doom-get-missing-packages))) (cond ((not packages) (message! (green "No packages to install!"))) @@ -354,20 +355,20 @@ package.el as appropriate." (dolist (pkg packages) (message! "Installing %s" (car pkg)) (doom--condition-case! - (message! " %s%s" + (message! "%s%s" (cond ((and (package-installed-p (car pkg)) (not (doom-package-different-backend-p (car pkg)))) - (dark (white "ALREADY INSTALLED"))) + (dark (white "⚠ ALREADY INSTALLED"))) ((doom-install-package (car pkg) (cdr pkg)) - (green "DONE")) + (green "✓ DONE")) (t - (red "FAILED"))) + (red "✕ FAILED"))) (if (plist-member (cdr pkg) :pin) (format " [pinned: %s]" (plist-get (cdr pkg) :pin)) - ""))))) + "")))) - (message! (bold (green "Finished!"))) - (doom//reload-load-path)))) + (message! (bold (green "Finished!"))) + (doom//reload-load-path))))) ;;;###autoload (defun doom//packages-update () @@ -404,8 +405,7 @@ package.el as appropriate." (message! (let ((result (doom-update-package (car pkg) t))) (color (if result 'green 'red) - " %s" - (if result "DONE" "FAILED")))))) + (if result "✓ DONE" "✕ FAILED")))))) (message! (bold (green "Finished!"))) (doom//reload-load-path))))) @@ -414,29 +414,28 @@ package.el as appropriate." (defun doom//packages-autoremove () "Interactive command for auto-removing orphaned packages." (interactive) + (message! "Looking for orphaned packages...") (let ((packages (doom-get-orphaned-packages))) (cond ((not packages) (message! (green "No unused packages to remove"))) - ((not (or (getenv "YES") - (y-or-n-p - (format "%s packages will be deleted:\n\n%s\n\nProceed?" - (length packages) - (mapconcat - (lambda (sym) - (format - "+ %s (%s)" - sym - (let ((backend (doom-package-backend sym))) - (if (doom-package-different-backend-p sym) - (if (eq backend 'quelpa) - "QUELPA->ELPA" - "ELPA->QUELPA") - (if (eq backend 'quelpa) - "QUELPA" - "ELPA"))))) - (sort (cl-copy-list packages) #'string-lessp) - "\n"))))) + ((not + (or (getenv "YES") + (y-or-n-p + (format + "%s packages will be deleted:\n\n%s\n\nProceed?" + (length packages) + (mapconcat + (lambda (sym) + (format "+ %s (%s)" sym + (let ((backend (doom-package-backend sym))) + (if (doom-package-different-backend-p sym) + (if (eq backend 'quelpa) + "QUELPA->ELPA" + "ELPA->QUELPA") + (upcase (symbol-name backend)))))) + (sort (cl-copy-list packages) #'string-lessp) + "\n"))))) (message! (yellow "Aborted!"))) (t @@ -446,13 +445,17 @@ package.el as appropriate." (let ((result (doom-delete-package pkg t))) (color (if result 'green 'red) "%s %s" - (if result "Removed" "Failed to remove") + (if result "✓ Removed" "✕ Failed to remove") pkg))))) (message! (bold (green "Finished!"))) (doom//reload-load-path))))) +;; +;; Interactive commands +;; + ;;;###autoload (defalias 'doom/install-package #'package-install) @@ -478,7 +481,9 @@ Use this interactively. Use `doom-delete-package' for direct calls." (if (package-installed-p package) (if (y-or-n-p (format "%s will be deleted. Confirm?" package)) (message "%s %s" - (if (doom-delete-package package t) "Deleted" "Failed to delete") + (if (doom-delete-package package t) + "Deleted" + "Failed to delete") package) (message "Aborted")) (message "%s isn't installed" package)))) diff --git a/core/autoload/popups.el b/core/autoload/popups.el index a182d50c7..c903a964b 100644 --- a/core/autoload/popups.el +++ b/core/autoload/popups.el @@ -239,7 +239,7 @@ without leaving any trace behind (muahaha)." (if (featurep 'evil) #'evil-force-normal-state #'keyboard-quit)) - (delete-window))) + (quit-restore-window nil 'kill))) ;;;###autoload (defun doom/popup-this-buffer () @@ -413,12 +413,4 @@ properties." (when (doom-popup-p window) (setq doom-popup-windows (delq window doom-popup-windows)) (when doom-popup-remember-history - (setq doom-popup-history (list (doom--popup-data window)))) - (let ((autokill-p (and (not doom-popup-inhibit-autokill) - (doom-popup-property :autokill window)))) - (with-selected-window window - (doom-popup-mode -1) - (when autokill-p - (when-let* ((process (get-buffer-process (current-buffer)))) - (set-process-query-on-exit-flag process nil)) - (kill-buffer (current-buffer))))))) + (setq doom-popup-history (list (doom--popup-data window)))))) diff --git a/core/autoload/system.el b/core/autoload/system.el index 6fb264fc3..97b44e2da 100644 --- a/core/autoload/system.el +++ b/core/autoload/system.el @@ -48,8 +48,8 @@ is given, returns t if it matches the current system, and nil otherwise." Requires the corresponding client, e.g. git for git repos, hg for mercurial, etc." (let* ((command (pcase fetcher - (:github "git clone --depth 1 --recursive https://github.com/%s.git") - (:git "git clone --depth 1 --recursive %s") + (:github "git clone --recursive https://github.com/%s.git") + (:git "git clone --recursive %s") (:gist "git clone https://gist.github.com/%s.git") ;; TODO Add hg (_ (error "%s is not a valid fetcher" fetcher)))) @@ -61,8 +61,8 @@ etc." (error "%s couldn't be found" command)) (unless (file-directory-p dest) (funcall (if noninteractive - (lambda (&rest args) (princ (shell-command-to-string args))) + (lambda (c) (princ (shell-command-to-string c))) #'async-shell-command) (format "%s %s %s" bin args (shell-quote-argument dest))) - (message! "Cloning %s -> %s" location dest)))) + (message! "Cloning %s -> %s" location (file-relative-name dest))))) diff --git a/core/autoload/test.el b/core/autoload/test.el index 60669a57e..e41cd424c 100644 --- a/core/autoload/test.el +++ b/core/autoload/test.el @@ -74,17 +74,32 @@ If neither is available, run all tests in all enabled modules." (defmacro def-test! (name &rest body) "Define a namespaced ERT test." (declare (indent defun) (doc-string 2)) - (unless (plist-get body :disabled) + (let (plist) + (while (keywordp (car body)) + (push (pop body) plist)) + (setq plist (reverse plist)) + (when (plist-get plist :skip) + (setq body `((ert-skip nil) ,@body))) + (when-let* ((modes (doom-enlist (plist-get plist :minor-mode)))) + (dolist (mode modes) + (setq body `((with-minor-mode!! ,mode ,@body))))) + (when-let* ((before (plist-get plist :before))) + (setq body `(,@before ,@body))) + (when-let* ((after (plist-get plist :after))) + (setq body `(,@body @after))) `(ert-deftest ,(cl-loop with path = (file-relative-name (file-name-sans-extension load-file-name) doom-emacs-dir) for (rep . with) in '(("/test/" . "/") ("/" . ":")) do (setq path (replace-regexp-in-string rep with path t t)) - finally return (intern (format "%s::%s" path name))) () - () - ,@body))) + finally return (intern (format "%s::%s" path name))) + () + (with-temp-buffer + (save-mark-and-excursion + (save-window-excursion + ,@body)))))) -(defmacro should-buffer! (initial expected &rest body) +(defmacro should-buffer!! (initial expected &rest body) "Test that a buffer with INITIAL text, run BODY, then test it against EXPECTED. INITIAL will recognize cursor markers in the form {[0-9]}. A {0} marker marks @@ -112,7 +127,7 @@ against." (lambda (m1 m2) (< (marker-position m1) (marker-position m2)))) (when (equal (caar marker-list) "0") - (goto-char! 0))) + (goto-char!! 0))) ,@body (let ((result-text (buffer-substring-no-properties (point-min) (point-max))) (point (point)) @@ -130,15 +145,22 @@ against." (should (equal expected-text result-text)) (should same-point))))))) -(defmacro goto-char! (index) - "Meant to be used with `should-buffer!'. Will move the cursor to one of the -cursor markers. e.g. Go to marker {2} with (goto-char! 2)." - `(goto-char (point! ,index))) +(defmacro goto-char!! (index) + "Meant to be used with `should-buffer!!'. Will move the cursor to one of the +cursor markers. e.g. Go to marker {2} with (goto-char!! 2)." + `(goto-char (point!! ,index))) -(defmacro point! (index) - "Meant to be used with `should-buffer!'. Returns the position of a cursor -marker. e.g. {2} can be retrieved with (point! 2)." +(defmacro point!! (index) + "Meant to be used with `should-buffer!!'. Returns the position of a cursor +marker. e.g. {2} can be retrieved with (point!! 2)." `(cdr (assoc ,(cond ((numberp index) (number-to-string index)) ((symbolp index) (symbol-name index)) ((stringp index) index)) marker-list))) + +(defmacro with-minor-mode!! (mode &rest body) + "TODO" + (declare (indent defun)) + `(progn (,mode +1) + ,@body + (,mode -1))) diff --git a/core/core-editor.el b/core/core-editor.el index 4b178e97c..96de4e8ab 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -58,6 +58,12 @@ modes are active and the buffer is read-only.") (ignore (bury-buffer)))) (add-hook 'kill-buffer-query-functions #'doom|dont-kill-scratch-buffer) +;; temporary windows often have q bound to `quit-window', which only buries the +;; contained buffer. I rarely don't want that buffer killed, so... +(defun doom*quit-window (orig-fn &optional kill window) + (funcall orig-fn (not kill) window)) +(advice-add #'quit-window :around #'doom*quit-window) + (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 @@ -75,43 +81,6 @@ fundamental-mode) for performance sake." (fundamental-mode)))) (add-hook 'find-file-hook #'doom|check-large-file) -;; Automatic minor modes -(defvar doom-auto-minor-mode-alist '() - "Alist mapping filename patterns to corresponding minor mode functions, like -`auto-mode-alist'. All elements of this alist are checked, meaning you can -enable multiple minor modes for the same regexp.") - -(defun doom|enable-minor-mode-maybe () - "Check file name against `doom-auto-minor-mode-alist'." - (when buffer-file-name - (let ((name buffer-file-name) - (remote-id (file-remote-p buffer-file-name)) - (alist doom-auto-minor-mode-alist)) - ;; Remove backup-suffixes from file name. - (setq name (file-name-sans-versions name)) - ;; Remove remote file name identification. - (when (and (stringp remote-id) - (string-match-p (regexp-quote remote-id) name)) - (setq name (substring name (match-end 0)))) - (while (and alist (caar alist) (cdar alist)) - (if (string-match-p (caar alist) name) - (funcall (cdar alist) 1)) - (setq alist (cdr alist)))))) -(add-hook 'find-file-hook #'doom|enable-minor-mode-maybe) - -(defun doom*set-indirect-buffer-filename (orig-fn base-buffer name &optional clone) - "In indirect buffers, `buffer-file-name' is nil, which can cause problems -with functions that require it (like modeline segments)." - (let ((file-name (buffer-file-name base-buffer)) - (buffer (funcall orig-fn base-buffer name clone))) - (when (and file-name buffer) - (with-current-buffer buffer - (unless buffer-file-name - (setq buffer-file-name file-name - buffer-file-truename (file-truename file-name))))) - buffer)) -(advice-add #'make-indirect-buffer :around #'doom*set-indirect-buffer-filename) - (push '("/LICENSE$" . text-mode) auto-mode-alist) @@ -138,16 +107,15 @@ with functions that require it (like modeline segments)." (def-package! recentf :hook (doom-init . recentf-mode) :config - (setq recentf-save-file (concat doom-etc-dir "recentf") + (setq recentf-save-file (concat doom-cache-dir "recentf") recentf-max-menu-items 0 recentf-max-saved-items 300 + recentf-filename-handlers '(file-truename) recentf-exclude (list "^/tmp/" "^/ssh:" "\\.?ido\\.last$" "\\.revive$" "/TAGS$" "^/var/folders/.+$" ;; ignore private DOOM temp files (but not all of them) - (concat "^" (replace-regexp-in-string - (concat "@" (regexp-quote (system-name))) - "@" (abbreviate-file-name doom-host-dir)))))) + (concat "^" (file-truename doom-local-dir))))) ;; @@ -206,8 +174,8 @@ extension, try to guess one." ;; Auto-close delimiters and blocks as you type (def-package! smartparens + :hook (doom-init . smartparens-global-mode) :config - (add-hook 'doom-init-hook #'smartparens-global-mode) (require 'smartparens-config) (setq sp-autowrap-region nil ; let evil-surround handle this diff --git a/core/core-lib.el b/core/core-lib.el index 4bf932203..d812d2b3e 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -1,6 +1,5 @@ ;;; core-lib.el -*- lexical-binding: t; -*- -(require 'cl-lib) (require 'subr-x) (load "async-autoloads" nil t) (load "persistent-soft-autoloads" nil t) diff --git a/core/core-packages.el b/core/core-packages.el index e7013fbc4..c40ef27b5 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -51,11 +51,6 @@ "Non-nil if doom is done initializing (once `doom-post-init-hook' is done). If this is nil after Emacs has started something is wrong.") -(defvar doom-package-init-p nil - "If non-nil, doom's package system has been initialized (by -`doom-initialize'). This will be nill if you byte-compile your configuration (as -intended).") - (defvar doom-init-time nil "The time it took, in seconds, for DOOM Emacs to initialize.") @@ -82,7 +77,7 @@ missing) and shouldn't be deleted.") "The load path of built in Emacs libraries.") (defvar doom--package-load-path () - "The load path of package libraries installed via ELPA or QUELPA.") + "The load path of package libraries installed via ELPA and QUELPA.") (defvar doom--base-load-path (append (list doom-core-dir doom-modules-dir) @@ -135,19 +130,37 @@ are installed. If you byte-compile core/core.el, this function will be avoided to speed up startup." - ;; Called early during initialization; only use native functions! - (when (or (not doom-package-init-p) force-p) - (setq load-path doom--base-load-path - package-activated-list nil) - ;; Ensure core folders exist - (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir package-user-dir)) - (unless (file-directory-p dir) - (make-directory dir t))) - (condition-case _ (package-initialize t) - ('error - (package-refresh-contents) - (setq doom--refreshed-p t) - (package-initialize t))) + ;; Called early during initialization; only use native (and cl-lib) functions! + (when (or force-p (not doom-init-p)) + ;; Speed things up with a `load-path' for only the bare essentials + (let ((load-path doom--base-load-path)) + ;; Ensure core folders exist, otherwise we get errors + (dolist (dir (list doom-local-dir doom-etc-dir doom-cache-dir doom-packages-dir)) + (unless (file-directory-p dir) + (make-directory dir t))) + ;; Ensure package.el is initialized; we use its state + (setq package-activated-list nil) + (condition-case _ (package-initialize t) + ('error (package-refresh-contents) + (setq doom--refreshed-p t) + (package-initialize t))) + ;; Ensure core packages are installed + (let ((core-packages (cl-remove-if #'package-installed-p doom-core-packages))) + (when core-packages + (message "Installing core packages") + (unless doom--refreshed-p + (package-refresh-contents)) + (dolist (package core-packages) + (let ((inhibit-message t)) + (package-install package)) + (if (package-installed-p package) + (message "✓ Installed %s" package) + (error "✕ Couldn't install %s" package))) + (message "Installing core packages...done"))) + (setq doom-init-p t)))) + +(defun doom-initialize-load-path (&optional force-p) + (when (or force-p (not doom--package-load-path)) ;; We could let `package-initialize' fill `load-path', but it does more than ;; that alone (like load autoload files). If you want something prematurely ;; optimizated right, ya gotta do it yourself. @@ -155,23 +168,7 @@ startup." ;; Also, in some edge cases involving package initialization during a ;; non-interactive session, `package-initialize' fails to fill `load-path'. (setq doom--package-load-path (directory-files package-user-dir t "^[^.]" t) - load-path (append load-path doom--package-load-path)) - ;; Ensure core packages are installed - (dolist (pkg doom-core-packages) - (unless (package-installed-p pkg) - (unless doom--refreshed-p - (package-refresh-contents) - (setq doom--refreshed-p t)) - (let ((inhibit-message t)) - (package-install pkg)) - (if (package-installed-p pkg) - (message "Installed %s" pkg) - (error "Couldn't install %s" pkg)))) - (load "quelpa" nil t) - (load "use-package" nil t) - (setq doom-package-init-p t) - (unless noninteractive - (message "Doom initialized")))) + load-path (append doom--base-load-path doom--package-load-path)))) (defun doom-initialize-autoloads () "Ensures that `doom-autoload-file' exists and is loaded. Otherwise run @@ -186,39 +183,35 @@ startup." If FORCE-P is non-nil, do it even if they are. This aggressively reloads core autoload files." - (doom-initialize force-p) + (doom-initialize-load-path force-p) (with-temp-buffer ; prevent buffer-local settings from propagating - (let ((noninteractive t) - (load-prefer-newer t) - (load-fn - (lambda (file &optional noerror) - (condition-case-unless-debug ex - (load file noerror :nomessage :nosuffix) - ('error - (error (format "(doom-initialize-packages) %s in %s: %s" - (car ex) - (file-relative-name file doom-emacs-dir) - (error-message-string ex)) - :error)))))) + (cl-flet + ((_load + (file &optional noerror interactive) + (condition-case-unless-debug ex + (let ((load-prefer-newer t) + (noninteractive (not interactive))) + (load file noerror :nomessage :nosuffix)) + ('error + (lwarn 'doom-initialize-packages :warning + "%s in %s: %s" + (car ex) + (file-relative-name file doom-emacs-dir) + (error-message-string ex)))))) (when (or force-p (not doom-modules)) - (setq doom-modules nil) - (let (noninteractive) - (load (concat doom-core-dir "core.el") nil t)) - (funcall load-fn (expand-file-name "init.el" doom-emacs-dir)) + (setq doom-modules nil + doom-packages nil) + (_load (concat doom-core-dir "core.el") nil 'interactive) + (_load (expand-file-name "init.el" doom-emacs-dir)) (when load-p - (let (noninteractive) - (funcall load-fn (doom-module-path :private user-login-name "init.el") t)) - (mapc load-fn (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))) - (cl-loop for (module . submodule) in (doom-module-pairs) - for path = (doom-module-path module submodule "config.el") - do (funcall load-fn path t)))) + (mapc #'_load (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))) + (_load (expand-file-name "init.el" doom-emacs-dir) nil 'interactive))) (when (or force-p (not doom-packages)) (setq doom-packages nil) - (funcall load-fn (expand-file-name "packages.el" doom-core-dir)) + (_load (expand-file-name "packages.el" doom-core-dir)) (cl-loop for (module . submodule) in (doom-module-pairs) for path = (doom-module-path module submodule "packages.el") - do (funcall load-fn path t))))) - (doom|finalize)) + do (_load path 'noerror)))))) (defun doom-initialize-modules (modules) "Adds MODULES to `doom-modules'. MODULES must be in mplist format. @@ -295,7 +288,7 @@ include all modules, enabled or otherwise." ;; Certainly imprecise, especially where custom additions to ;; load-path are concerned, but I don't mind a [small] margin of ;; error in the plugin count in exchange for faster startup. - (- (length load-path) (length doom--base-load-path)) + (length doom--package-load-path) (hash-table-size doom-modules) (setq doom-init-time (float-time (time-subtract after-init-time before-init-time))))) @@ -313,19 +306,17 @@ MODULES is an malformed plist of modules to load." (doom-initialize-modules modules) `(let (file-name-handler-alist) (setq doom-modules ',doom-modules) - (unless noninteractive + (message "Doom initialized") ,@(cl-loop for (module . submodule) in (doom-module-pairs) for module-path = (doom-module-path module submodule) collect `(load! init ,module-path t) into inits collect `(load! config ,module-path t) into configs finally return (append inits configs)) - (when (display-graphic-p) (require 'server) (unless (server-running-p) (server-start))) - (add-hook 'doom-init-hook #'doom-packages--display-benchmark t) (message "Doom modules initialized")))) @@ -334,7 +325,7 @@ MODULES is an malformed plist of modules to load." ;; Ignore package if NAME is in `doom-disabled-packages' (when (and (memq name doom-disabled-packages) (not (memq :disabled plist))) - (setq plist (append (list :disabled t) plist))) + (setq plist `(:disabled t ,@plist))) ;; If byte-compiling, ignore this package if it doesn't meet the condition. ;; This avoids false-positive load errors. (unless (and (bound-and-true-p byte-compile-current-file) @@ -377,31 +368,26 @@ to have them return non-nil (or exploit that to overwrite Doom's config)." "Load a file relative to the current executing file (`load-file-name'). FILESYM is either a symbol or string representing the file to load. PATH is -where to look for the file (a string representing a directory path), by default -it is relative to `load-file-name', `byte-compile-current-file' or +where to look for the file (a string representing a directory path). If omitted, +the lookup is relative to `load-file-name', `byte-compile-current-file' or `buffer-file-name' (in that order). If NOERROR is non-nil, don't throw an error if the file doesn't exist." - (let ((path (or (and path (or (and (symbolp path) (symbol-value path)) - (and (stringp path) path) - (and (listp path) (eval path)))) + (cl-assert (symbolp filesym) t) + (let ((path (or path (and load-file-name (file-name-directory load-file-name)) (and (bound-and-true-p byte-compile-current-file) (file-name-directory byte-compile-current-file)) (and buffer-file-name - (file-name-directory buffer-file-name)))) - (filename (cond ((stringp filesym) filesym) - ((symbolp filesym) (symbol-name filesym)) - (t (error "load! expected a string or symbol, got %s (a %s)" - filesym (type-of filesym)))))) - (unless path - (error "Could not find %s" filename)) + (file-name-directory buffer-file-name)) + (error "Could not detect path to look for '%s' in" filesym))) + (filename (symbol-name filesym))) (let ((file (expand-file-name (concat filename ".el") path))) (if (file-exists-p file) `(load ,(file-name-sans-extension file) ,noerror ,(not doom-debug-mode)) (unless noerror - (error "Could not load! file %s" file)))))) + (error "Could not load file '%s' from '%s'" file path)))))) (defmacro require! (module submodule &optional flags reload-p) "Loads the module specified by MODULE (a property) and SUBMODULE (a symbol). @@ -507,8 +493,9 @@ loads MODULE SUBMODULE's packages.el file." t))) (defun doom-packages--async-run (fn) - (let ((compilation-filter-hook - (list (lambda () (ansi-color-apply-on-region compilation-filter-start (point)))))) + (let* ((default-directory doom-emacs-dir) + (compilation-filter-hook + (list (lambda () (ansi-color-apply-on-region compilation-filter-start (point)))))) (compile (format "%s --quick --batch -l core/core.el -f %s" (executable-find "emacs") (symbol-name fn))) @@ -527,15 +514,14 @@ call `doom/reload-load-path' remotely (through emacsclient)." (interactive) (byte-recompile-file (expand-file-name "core.el" doom-core-dir) t) (cond (noninteractive - (message "Reloading...") (require 'server) (when (server-running-p) + (message "Reloading active Emacs session...") (server-eval-at server-name '(doom//reload-load-path)))) - (t - (doom-initialize t) - (message "Reloaded %d packages" (length doom--package-load-path)) - (run-with-timer 1 nil #'redraw-display) - (run-hooks 'doom-reload-hook)))) + ((let ((noninteractive t)) + (doom-initialize-load-path t) + (message "%d packages reloaded" (length doom--package-load-path)) + (run-hooks 'doom-reload-hook))))) (defun doom//reload-autoloads () "Refreshes the autoloads.el file, specified by `doom-autoload-file'. @@ -555,7 +541,7 @@ This should be run whenever init.el or an autoload file is modified. Running ;; state. `doom-initialize-packages' will have side effects otherwise. (and (doom-packages--async-run 'doom//reload-autoloads) (load doom-autoload-file)) - (doom-initialize-packages) + (doom-initialize-packages t) (let ((targets (file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir)))) @@ -571,13 +557,15 @@ This should be run whenever init.el or an autoload file is modified. Running (delete-file doom-autoload-file) (message "Deleted old autoloads.el")) (dolist (file (reverse targets)) - (message (cond ((not (doom-packages--read-if-cookies file)) - "Ignoring %s") - ((update-file-autoloads file nil doom-autoload-file) - "Nothing in %s") - (t - "Scanned %s")) - (file-relative-name file doom-emacs-dir))) + (message + (cond ((not (doom-packages--read-if-cookies file)) + "⚠ Ignoring %s") + ((update-file-autoloads file nil doom-autoload-file) + "✕ Nothing in %s") + (t + "✓ Scanned %s")) + (file-relative-name file doom-emacs-dir))) + (make-directory (file-name-directory doom-autoload-file) t) (let ((buf (get-file-buffer doom-autoload-file)) current-sexp) (unwind-protect @@ -646,33 +634,42 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." (error "No targets to compile")) (let ((use-package-expand-minimally t)) (push (expand-file-name "init.el" doom-emacs-dir) compile-targets) - (dolist (target compile-targets) - (when (or (not recompile-p) - (let ((elc-file (byte-compile-dest-file target))) - (and (file-exists-p elc-file) - (file-newer-than-file-p file elc-file)))) - (let ((result (if (doom-packages--read-if-cookies target) - (byte-compile-file target) - 'no-byte-compile)) - (short-name (file-relative-name target doom-emacs-dir))) - (cl-incf - (cond ((eq result 'no-byte-compile) - (message! (dark (white "Ignored %s" short-name))) - total-noop) - ((null result) - (message! (red "Failed to compile %s" short-name)) - total-fail) - (t - (message! (green "Compiled %s" short-name)) - (quiet! (load target t t)) - total-ok)))))) - (message! - (bold - (color (if (= total-fail 0) 'green 'red) - "%s %s file(s) %s" - (if recompile-p "Recompiled" "Compiled") - (format "%d/%d" total-ok (- (length compile-targets) total-noop)) - (format "(%s ignored)" total-noop))))))))) + (condition-case ex + (progn + (dolist (target compile-targets) + (when (or (not recompile-p) + (let ((elc-file (byte-compile-dest-file target))) + (and (file-exists-p elc-file) + (file-newer-than-file-p file elc-file)))) + (let ((result (if (doom-packages--read-if-cookies target) + (byte-compile-file target) + 'no-byte-compile)) + (short-name (file-relative-name target doom-emacs-dir))) + (cl-incf + (cond ((eq result 'no-byte-compile) + (message! (dark (white "⚠ Ignored %s" short-name))) + total-noop) + ((null result) + (message! (red "✕ Failed to compile %s" short-name)) + total-fail) + (t + (message! (green "✓ Compiled %s" short-name)) + (quiet! (load target t t)) + total-ok)))))) + (message! + (bold + (color (if (= total-fail 0) 'green 'red) + "%s %s file(s) %s" + (if recompile-p "Recompiled" "Compiled") + (format "%d/%d" total-ok (- (length compile-targets) total-noop)) + (format "(%s ignored)" total-noop))))) + (error + (message! (red "\n%%s\n\n%%s\n\n%%s") + "There were breaking errors." + (error-message-string ex) + "Reverting changes...") + (doom//clean-byte-compiled-files) + (message! (green "Finished (nothing was byte-compiled)"))))))))) (defun doom//byte-compile-core (&optional recompile-p) "Byte compile the core Doom files. @@ -683,7 +680,11 @@ likely change core files directly). If RECOMPILE-P is non-nil, only recompile out-of-date core files." (interactive "P") - (doom//byte-compile (list "core") recompile-p)) + (if (not noninteractive) + ;; This is done in another instance to protect the current session's + ;; state. `doom-initialize-packages' will have side effects otherwise. + (doom-packages--async-run 'doom//byte-compile-core) + (doom//byte-compile (list "core") recompile-p))) (defun doom//byte-recompile-plugins () "Recompile all installed plugins. If you're getting odd errors after upgrading @@ -692,18 +693,18 @@ If RECOMPILE-P is non-nil, only recompile out-of-date core files." (byte-recompile-directory package-user-dir 0 t)) (defun doom//clean-byte-compiled-files () - "Delete all the compiled elc files in your Emacs configuration. - -This excludes compiled packages in `doom-packages-dir'.'" + "Delete all the compiled elc files in your Emacs configuration. This excludes +compiled packages.'" (interactive) (let ((targets (append (list (expand-file-name "init.elc" doom-emacs-dir)) (directory-files-recursively doom-core-dir "\\.elc$") - (directory-files-recursively doom-modules-dir "\\.elc$")))) + (directory-files-recursively doom-modules-dir "\\.elc$"))) + (default-directory doom-emacs-dir)) (unless (cl-loop for path in targets if (file-exists-p path) collect path and do (delete-file path) - and do (message "Deleted %s" (file-relative-name path))) + and do (message "✓ Deleted %s" (file-relative-name path))) (message "Everything is clean")))) diff --git a/core/core-popups.el b/core/core-popups.el index 210e27fea..5ca2fd044 100644 --- a/core/core-popups.el +++ b/core/core-popups.el @@ -87,13 +87,13 @@ recognized by DOOM's popup system. They are: ;; ;; -(defvar doom-popup-parameters - '(:esc :modeline :transient :fit :align :size) - "TODO") +;; (defvar doom-popup-parameters +;; '(:esc :modeline :transient :fit :align :size) +;; "TODO") -(defvar doom-popup-whitelist - '(("^ ?\\*" :size 15 :noselect t :autokill t :autoclose t)) - "TODO") +;; (defvar doom-popup-whitelist +;; '(("^ ?\\*" :size 15 :noselect t :autokill t :autoclose t)) +;; "TODO") (defvar doom-popup-blacklist '("^\\*magit") @@ -122,7 +122,7 @@ recognized by DOOM's popup system. They are: ("*Backtrace*" :size 20 :noselect t) ("*Warnings*" :size 12 :noselect t :autofit t) ("*Messages*" :size 12 :noselect t) - ("*Help*" :size 0.3) + ("*Help*" :size 0.3 :autokill t) ("^\\*.*Shell Command.*\\*$" :regexp t :size 20 :noselect t :autokill t) (apropos-mode :size 0.3 :autokill t :autoclose t) (Buffer-menu-mode :size 20 :autokill t) @@ -144,10 +144,20 @@ recognized by DOOM's popup system. They are: (defun doom-display-buffer-action (buffer alist) (shackle-display-buffer buffer alist (shackle-match buffer))) + (defun doom|autokill-popups () + (or (not (doom-popup-p)) + (prog1 (when (and (not doom-popup-inhibit-autokill) + (plist-get doom-popup-rules :autokill)) + (doom-popup-mode -1) + (when-let* ((process (get-buffer-process (current-buffer)))) + (set-process-query-on-exit-flag process nil)) + t)))) + (add-hook! doom-post-init (setq display-buffer-alist (cons '(doom-display-buffer-condition doom-display-buffer-action) - display-buffer-alist))) + display-buffer-alist)) + (add-hook 'kill-buffer-query-functions #'doom|autokill-popups)) ;; no modeline in popups (add-hook 'doom-popup-mode-hook #'doom|hide-modeline-in-popup) @@ -170,7 +180,7 @@ recognized by DOOM's popup system. They are: (define-key map [escape] #'doom/popup-close-maybe) (define-key map (kbd "ESC") #'doom/popup-close-maybe) (define-key map [remap quit-window] #'doom/popup-close-maybe) - (define-key map [remap doom/kill-this-buffer] #'delete-window) + (define-key map [remap doom/kill-this-buffer] #'doom/popup-close-maybe) (define-key map [remap split-window-right] #'ignore) (define-key map [remap split-window-below] #'ignore) (define-key map [remap split-window-horizontally] #'ignore) diff --git a/core/core-projects.el b/core/core-projects.el index 01e8e88f4..24db2ee04 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -1,10 +1,7 @@ ;;; core-projects.el -*- lexical-binding: t; -*- -(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.") - (def-package! projectile + :hook (doom-init . projectile-mode) :init (setq projectile-cache-file (concat doom-cache-dir "projectile.cache") projectile-enable-caching (not noninteractive) @@ -14,17 +11,23 @@ state are passed in.") projectile-globally-ignored-files '(".DS_Store" "Icon " "TAGS") projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")) - (add-hook 'doom-init-hook #'projectile-mode) :config + (add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook) + (add-hook 'find-file-hook #'doom|autoload-project-mode) + ;; a more generic project root file (push ".project" projectile-project-root-files-bottom-up) - (nconc projectile-globally-ignored-directories (list (abbreviate-file-name doom-local-dir) ".sync")) - (nconc projectile-other-file-alist '(("css" . ("scss" "sass" "less" "style")) - ("scss" . ("css")) - ("sass" . ("css")) - ("less" . ("css")) - ("styl" . ("css")))) + (setq projectile-globally-ignored-directories + (append projectile-globally-ignored-directories + (list (abbreviate-file-name doom-local-dir) ".sync")) + projectile-other-file-alist + (append projectile-other-file-alist + '(("css" . ("scss" "sass" "less" "styl")) + ("scss" . ("css")) + ("sass" . ("css")) + ("less" . ("css")) + ("styl" . ("css"))))) ;; Projectile root-searching functions can cause an infinite loop on TRAMP ;; connections, so disable them. @@ -76,21 +79,48 @@ which case they're relative to `default-directory'). If they start with a slash, they are absolute." (doom--resolve-path-forms files (doom-project-root))) +(defun doom-project-find-file (dir) + "Fuzzy-find a file under DIR." + (let ((default-directory dir) + ;; Necessary to isolate this search from the current project + projectile-project-name + projectile-require-project-root + projectile-cached-buffer-file-name + projectile-cached-project-root) + (call-interactively + ;; completion modules may remap this command + (or (command-remapping #'projectile-find-file) + #'projectile-find-file)))) + +(defun doom-project-browse (dir) + "Traverse a file structure starting linearly from DIR." + (let ((default-directory dir)) + (call-interactively + ;; completion modules may remap this command + (or (command-remapping #'find-file) + #'find-file)))) + ;; ;; Projects ;; (defvar-local doom-project nil - "A list of project mode to enable. Used for .dir-locals.el.") + "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|autoload-project-mode () - "Auto-enable projects listed in `doom-project', which is meant to be set from -.dir-locals.el files." - (cl-loop for mode in doom-project - unless (symbol-value mode) - do (funcall mode))) -(add-hook 'after-change-major-mode-hook #'doom|autoload-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))))) (defmacro def-project-mode! (name &rest plist) "Define a project minor-mode named NAME (a symbol) and declare where and how @@ -101,8 +131,7 @@ own settings, keymaps, hooks, snippets, etc. This creates NAME-hook and NAME-map as well. -A project can be enabled through .dir-locals.el too, if `doom-project' is set to -the name (symbol) of the project mode(s) to enable. +A project can be enabled through .dir-locals.el too, by setting `doom-project'. PLIST may contain any of these properties, which are all checked to see if NAME should be activated. If they are *all* true, NAME is activated. @@ -151,7 +180,7 @@ Relevant: `doom-project-hook'." :keymap (make-sparse-keymap) (if (not ,name) ,exit-form - (run-hook-with-args 'doom-project-hook ',name) + (run-hook-with-args 'doom-project-hook ',name ,name) ,(when load-form `(unless ,init-var ,load-form diff --git a/core/core-ui.el b/core/core-ui.el index a2f3f011a..ecf053cae 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -50,6 +50,7 @@ shorter major mode name in the mode-line. See `doom|set-mode-name'.") max-mini-window-height 0.3 mode-line-default-help-echo nil ; disable mode-line mouseovers mouse-yank-at-point t ; middle-click paste at point, not at click + ibuffer-use-other-window t resize-mini-windows 'grow-only ; Minibuffer resizing show-help-function nil ; hide :help-echo text split-width-threshold 160 ; favor horizontal splits @@ -182,9 +183,13 @@ local value, whether or not it's permanent-local. Therefore, we cycle (when (fontp doom-variable-pitch-font) (set-face-attribute 'variable-pitch frame :font doom-variable-pitch-font))) ('error - (lwarn 'doom-ui :error - "Failed to set fonts because %s" - (error-message-string ex)))) + (if (string-prefix-p "Font not available: " (error-message-string ex)) + (lwarn 'doom-ui :warning + "Could not find the '%s' font on your system, falling back to system font" + (font-get (caddr ex) :family)) + (lwarn 'doom-ui :error + "Unexpected error while initializing fonts: %s" + (error-message-string ex))))) (run-hooks 'doom-init-ui-hook)) (defun doom|reload-ui-in-daemon (frame) @@ -224,6 +229,14 @@ local value, whether or not it's permanent-local. Therefore, we cycle (add-hook! '(doom-post-init-hook minibuffer-setup-hook) #'doom|no-fringes-in-minibuffer) +(defun doom|protect-visible-buffers () + "Don't kill the current buffer if it is visible in another window (bury it +instead)." + (not (delq (selected-window) + (get-buffer-window-list nil nil t)))) +(add-hook! doom-post-init + (add-hook 'kill-buffer-query-functions #'doom|protect-visible-buffers)) + ;; ;; Plugins @@ -237,14 +250,11 @@ local value, whether or not it's permanent-local. Therefore, we cycle (defun doom*disable-all-the-icons-in-tty (orig-fn &rest args) (when (display-graphic-p) (apply orig-fn args))) - ;; all-the-icons doesn't work in the terminal, so we "disable" it. - (advice-add #'all-the-icons-octicon :around #'doom*disable-all-the-icons-in-tty) - (advice-add #'all-the-icons-material :around #'doom*disable-all-the-icons-in-tty) - (advice-add #'all-the-icons-faicon :around #'doom*disable-all-the-icons-in-tty) - (advice-add #'all-the-icons-fileicon :around #'doom*disable-all-the-icons-in-tty) - (advice-add #'all-the-icons-wicon :around #'doom*disable-all-the-icons-in-tty) - (advice-add #'all-the-icons-alltheicon :around #'doom*disable-all-the-icons-in-tty)) + (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))) (def-package! fringe-helper :commands (fringe-helper-define fringe-helper-convert) @@ -255,8 +265,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle (def-package! hideshow ; built-in :commands (hs-minor-mode hs-toggle-hiding hs-already-hidden-p) - :config - (setq hs-hide-comments-when-hiding-all nil)) + :config (setq hs-hide-comments-when-hiding-all nil)) (def-package! highlight-indentation :commands (highlight-indentation-mode highlight-indentation-current-column-mode)) @@ -306,17 +315,20 @@ local value, whether or not it's permanent-local. Therefore, we cycle ;; Helps us distinguish stacked delimiter pairs. Especially in parentheses-drunk ;; languages like Lisp. (def-package! rainbow-delimiters - :commands rainbow-delimiters-mode - :config (setq rainbow-delimiters-max-face-count 3) - :init (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode)) + :hook (lisp-mode . rainbow-delimiters-mode) + :config (setq rainbow-delimiters-max-face-count 3)) ;; For a distractions-free-like UI, that dynamically resizes margets and can ;; center a buffer. (def-package! visual-fill-column :commands visual-fill-column-mode :config - (setq-default visual-fill-column-center-text nil - visual-fill-column-width fill-column)) + (setq-default + visual-fill-column-center-text t + visual-fill-column-width + ;; take Emacs 26 line numbers into account + (+ (if (boundp 'display-line-numbers) 6 0) + fill-column))) ;; diff --git a/core/core.el b/core/core.el index f3eaa03bd..fbcc2348d 100644 --- a/core/core.el +++ b/core/core.el @@ -18,14 +18,14 @@ ;; Autoloaded functions are in core/autoload/*.el and modules/*/*/autoload.el or ;; modules/*/*/autoload/*.el. -(defvar doom-version "2.0.8" +(defvar doom-version "2.0.9" "Current version of DOOM emacs.") (defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug) "If non-nil, all doom functions will be verbose. Set DEBUG=1 in the command line or use --debug-init to enable this.") -(defvar doom-emacs-dir (expand-file-name user-emacs-directory) +(defvar doom-emacs-dir (file-truename user-emacs-directory) "The path to this emacs.d directory.") (defvar doom-core-dir (concat doom-emacs-dir "core/") @@ -34,38 +34,27 @@ line or use --debug-init to enable this.") (defvar doom-modules-dir (concat doom-emacs-dir "modules/") "Where configuration modules are stored.") - -;; Multi-host directories: I namespace `doom-etc-dir' and `doom-cache-dir' with -;; host names because I use the same (often symlinked) emacs.d across several -;; computers -- often simultaneously. Cache or other temporary files would -;; conflict otherwise. - (defvar doom-local-dir (concat doom-emacs-dir ".local/") "Root directory for local Emacs files. Use this as permanent storage for files that are safe to share across systems (if this config is symlinked across several computers).") -(defvar doom-host-dir (concat doom-local-dir "@" (system-name)) - "Directory for hostname-specific file storage. Used by `doom-etc-dir' and -`doom-cache-dir'.") +(defvar doom-etc-dir (concat doom-local-dir "etc/") + "Directory for non-volatile storage. -(defvar doom-etc-dir (concat doom-host-dir "/etc/") - "Host-namespaced directory for non-volatile storage. These are not deleted or -tampored with by DOOM functions. Use this for dependencies like servers or -config files that are stable (i.e. it should be unlikely that you need to delete -them if something goes wrong).") +Use this for files that don't change much, like servers binaries, external +dependencies or long-term shared data.") -(defvar doom-cache-dir (concat doom-host-dir "/cache/") - "Host-namespaced directory for volatile storage. Deleted when `doom/reset' is -called. Use this for transient files that are generated on the fly like caches -and temporary files. Anything that may need to be cleared if there are -problems.") +(defvar doom-cache-dir (concat doom-local-dir "cache/") + "Directory for volatile storage. + +Use this for files that change often, like cache files.") (defvar doom-packages-dir (concat doom-local-dir "packages/") "Where package.el and quelpa plugins (and their caches) are stored.") (defvar doom-autoload-file (concat doom-local-dir "autoloads.el") - "Location of the autoloads file generated by `doom/reload-autoloads'.") + "Where `doom//reload-autoloads' will generate its autoloads file.") (defgroup doom nil "DOOM Emacs, an Emacs configuration for a stubborn, shell-dwelling and @@ -121,13 +110,14 @@ melodramatic ex-vimmer disappointed with the text-editor status quo." (load custom-file t t) ;; be quiet at startup; don't load or display anything unnecessary -(advice-add #'display-startup-echo-area-message :override #'ignore) -(setq inhibit-startup-message t - inhibit-startup-echo-area-message user-login-name - inhibit-default-init t - initial-major-mode 'fundamental-mode - initial-scratch-message nil - mode-line-format nil) +(unless noninteractive + (advice-add #'display-startup-echo-area-message :override #'ignore) + (setq inhibit-startup-message t + inhibit-startup-echo-area-message user-login-name + inhibit-default-init t + initial-major-mode 'fundamental-mode + initial-scratch-message nil + mode-line-format nil)) ;; Custom init hooks; clearer than `after-init-hook', `emacs-startup-hook', and ;; `window-setup-hook'. @@ -152,33 +142,23 @@ ability to invoke the debugger in debug mode." (car ex) fn (error-message-string ex)))) nil) -(defun doom|finalize () - (unless (or doom-init-p noninteractive) - (dolist (hook '(doom-init-hook doom-post-init-hook)) - (run-hook-wrapped hook #'doom-try-run-hook hook)) - (setq doom-init-p t)) - - ;; Don't keep gc-cons-threshold too high. It helps to stave off the GC while - ;; Emacs starts up, but afterwards it causes stuttering and random freezes. So - ;; reset it to a reasonable default. - (setq gc-cons-threshold 16777216 - gc-cons-percentage 0.1 - file-name-handler-alist doom--file-name-handler-alist) - t) - ;;; ;; Initialize (eval-and-compile (defvar doom--file-name-handler-alist file-name-handler-alist) - (setq gc-cons-threshold 402653184 - gc-cons-percentage 0.6 - file-name-handler-alist nil) + (unless (or after-init-time noninteractive) + ;; One of the contributors to long startup times is the garbage collector, + ;; so we up its memory threshold, temporarily. It is reset later in + ;; `doom|finalize'. + (setq gc-cons-threshold 402653184 + gc-cons-percentage 0.6 + file-name-handler-alist nil)) - (require 'core-packages (concat doom-core-dir "core-packages")) - (eval-when-compile - (doom-initialize)) - (setq load-path (eval-when-compile load-path) + (require 'cl-lib) + (load (concat doom-core-dir "core-packages") nil t) + (setq load-path (eval-when-compile (doom-initialize t) + (doom-initialize-load-path t)) doom--package-load-path (eval-when-compile doom--package-load-path)) (load! core-lib) @@ -195,10 +175,71 @@ ability to invoke the debugger in debug mode." (load! core-popups) ; taming sudden yet inevitable windows (load! core-editor) ; baseline configuration for text editing (load! core-projects) ; making Emacs project-aware - (load! core-keybinds))) ; centralized keybind system + which-key + (load! core-keybinds)) ; centralized keybind system + which-key -(add-hook! '(emacs-startup-hook doom-reload-hook) - #'doom|finalize) + (defun doom|finalize () + "Run `doom-init-hook', `doom-post-init-hook' and reset `gc-cons-threshold', +`gc-cons-percentage' and `file-name-handler-alist'." + (unless (or (not after-init-time) noninteractive) + (dolist (hook '(doom-init-hook doom-post-init-hook)) + (run-hook-wrapped hook #'doom-try-run-hook hook))) + + ;; If you forget to reset this, you'll get stuttering and random freezes! + (setq gc-cons-threshold 16777216 + gc-cons-percentage 0.1 + file-name-handler-alist doom--file-name-handler-alist) + t) + + (add-hook! '(emacs-startup-hook doom-reload-hook) + #'doom|finalize)) + + +;; +;; Emacs fixes/hacks +;; + +;; Automatic minor modes +(defvar doom-auto-minor-mode-alist '() + "Alist mapping filename patterns to corresponding minor mode functions, like +`auto-mode-alist'. All elements of this alist are checked, meaning you can +enable multiple minor modes for the same regexp.") + +(defun doom|enable-minor-mode-maybe () + "Check file name against `doom-auto-minor-mode-alist'." + (when buffer-file-name + (let ((name buffer-file-name) + (remote-id (file-remote-p buffer-file-name)) + (alist doom-auto-minor-mode-alist)) + ;; Remove backup-suffixes from file name. + (setq name (file-name-sans-versions name)) + ;; Remove remote file name identification. + (when (and (stringp remote-id) + (string-match-p (regexp-quote remote-id) name)) + (setq name (substring name (match-end 0)))) + (while (and alist (caar alist) (cdar alist)) + (if (string-match-p (caar alist) name) + (funcall (cdar alist) 1)) + (setq alist (cdr alist)))))) +(add-hook 'find-file-hook #'doom|enable-minor-mode-maybe) + +(defun doom*set-indirect-buffer-filename (orig-fn base-buffer name &optional clone) + "In indirect buffers, `buffer-file-name' is nil, which can cause problems +with functions that require it (like modeline segments)." + (let ((file-name (buffer-file-name base-buffer)) + (buffer (funcall orig-fn base-buffer name clone))) + (when (and file-name buffer) + (with-current-buffer buffer + (unless buffer-file-name + (setq buffer-file-name file-name + buffer-file-truename (file-truename file-name))))) + buffer)) +(advice-add #'make-indirect-buffer :around #'doom*set-indirect-buffer-filename) + +(defun doom*no-authinfo-for-tramp (orig-fn &rest args) + "Don't look into .authinfo for local sudo TRAMP buffers." + (let ((auth-sources (if (equal tramp-current-method "sudo") nil auth-sources))) + (apply orig-fn args))) +(advice-add #'tramp-read-passwd :around #'doom*no-authinfo-for-tramp) (provide 'core) ;;; core.el ends here diff --git a/core/test/autoload-buffers.el b/core/test/autoload-buffers.el index abf5c4245..b39c75b3c 100644 --- a/core/test/autoload-buffers.el +++ b/core/test/autoload-buffers.el @@ -1,24 +1,23 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/autoload-buffers.el -(defmacro -with-temp-buffers! (buffer-args &rest body) +(defmacro with-temp-buffers!! (buffer-args &rest body) (declare (indent defun)) (let (buffers) (dolist (bsym buffer-args) (push `(,bsym (get-buffer-create ,(symbol-name bsym))) buffers)) - `(save-window-excursion - (cl-flet ((buffer-list - (lambda () - (cl-remove-if-not #'buffer-live-p (list ,@(reverse (mapcar #'car buffers))))))) - (let* (persp-mode - ,@buffers) - ,@body - (mapc #'kill-buffer (buffer-list))))))) + `(cl-flet ((buffer-list + (lambda () + (cl-remove-if-not #'buffer-live-p (list ,@(reverse (mapcar #'car buffers))))))) + (let* (persp-mode + ,@buffers) + ,@body + (mapc #'kill-buffer (buffer-list)))))) ;; (def-test! get-buffers - (-with-temp-buffers! (a b c) + (with-temp-buffers!! (a b c) (should (cl-every #'buffer-live-p (buffer-list))) (should (equal (buffer-list) (list a b c))) (dolist (buf (list (cons a doom-emacs-dir) @@ -26,6 +25,7 @@ (cons c "/tmp/"))) (with-current-buffer (car buf) (setq-local default-directory (cdr buf)))) + (projectile-mode +1) (with-current-buffer a ;; should produce all buffers (let ((buffers (doom-buffer-list))) @@ -37,11 +37,12 @@ ;; If no project is available, just get all buffers (with-current-buffer c (let ((buffers (doom-project-buffer-list))) - (should (cl-every (lambda (x) (memq x buffers)) (list a b c))))))) + (should (cl-every (lambda (x) (memq x buffers)) (list a b c))))) + (projectile-mode -1))) (def-test! real-buffers (let (doom-real-buffer-functions) - (-with-temp-buffers! (a b c d) + (with-temp-buffers!! (a b c d) (dolist (buf (list a b)) (with-current-buffer buf (setq-local buffer-file-name "x"))) @@ -62,7 +63,7 @@ ;; `doom-visible-buffers' ;; `doom-buried-buffers' (def-test! visible-buffers-and-windows - (-with-temp-buffers! (a b c d) + (with-temp-buffers!! (a b c d) (switch-to-buffer a) (should (eq (current-buffer) a)) (should (eq (selected-window) (get-buffer-window a))) @@ -77,7 +78,7 @@ ;; `doom-matching-buffers' (def-test! matching-buffers - (-with-temp-buffers! (a b c) + (with-temp-buffers!! (a b c) (let ((buffers (doom-matching-buffers "^[ac]$"))) (should (= 2 (length buffers))) (should (cl-every #'bufferp buffers)) @@ -86,7 +87,7 @@ ;; `doom-buffers-in-mode' (def-test! buffers-in-mode - (-with-temp-buffers! (a b c d e) + (with-temp-buffers!! (a b c d e) (dolist (buf (list a b)) (with-current-buffer buf (emacs-lisp-mode))) @@ -101,7 +102,7 @@ ;; `doom-kill-buffer' (def-test! kill-buffer - (-with-temp-buffers! (a b) + (with-temp-buffers!! (a b) (doom-kill-buffer a) (should-not (buffer-live-p a)) ;; modified buffer @@ -112,7 +113,7 @@ ;; `doom--cycle-real-buffers' (def-test! kill-buffer-then-show-real-buffer - (-with-temp-buffers! (a b c d) + (with-temp-buffers!! (a b c d) (dolist (buf (list a b d)) (with-current-buffer buf (setq-local buffer-file-name "x"))) @@ -131,4 +132,4 @@ ;; TODO doom/kill-all-buffers ;; TODO doom/kill-other-buffers ;; TODO doom/kill-matching-buffers -;; TODO doom/cleanup-buffers +;; TODO doom/cleanup-session diff --git a/core/test/autoload-debug.el b/core/test/autoload-debug.el index 32f1090a7..062a16de5 100644 --- a/core/test/autoload-debug.el +++ b/core/test/autoload-debug.el @@ -2,18 +2,16 @@ ;;; core/test/autoload-debug.el (def-test! what-face - (with-temp-buffer - (insert (propertize "Hello " 'face 'font-lock-keyword-face)) - (insert "world") + (insert (propertize "Hello " 'face 'font-lock-keyword-face)) + (insert "world") - (should (equal (doom/what-face (point-min)) '((font-lock-keyword-face) ()))) - (should-not (doom/what-face (point-max))))) + (should (equal (doom/what-face (point-min)) '((font-lock-keyword-face) ()))) + (should-not (doom/what-face (point-max)))) (def-test! what-face-overlays - (with-temp-buffer - (insert "Hello world") - (let ((ov (make-overlay 1 6))) - (overlay-put ov 'face 'font-lock-keyword-face)) + (insert "Hello world") + (let ((ov (make-overlay 1 6))) + (overlay-put ov 'face 'font-lock-keyword-face)) - (should (equal (doom/what-face (point-min)) '(() (font-lock-keyword-face)))) - (should-not (doom/what-face (point-max))))) + (should (equal (doom/what-face (point-min)) '(() (font-lock-keyword-face)))) + (should-not (doom/what-face (point-max)))) diff --git a/core/test/autoload-package.el b/core/test/autoload-package.el index 23db6873e..5d074b0b7 100644 --- a/core/test/autoload-package.el +++ b/core/test/autoload-package.el @@ -1,30 +1,21 @@ ;; -*- no-byte-compile: t; -*- ;;; core/test/autoload-package.el -(defun -new-package (name version &optional reqs) +(defun -pkg (name version &optional reqs) (package-desc-create :name name :version version :reqs reqs)) -(defmacro -with-temp-packages! (&rest forms) - "Run FORMS in the context of a temporary package setup (as in, it won't -affects your Emacs packages)." - `(let* ((doom-local-dir ,(expand-file-name "test/.local/" doom-emacs-dir)) - (doom-packages-dir (concat doom-local-dir "packages/")) - (doom-etc-dir (concat doom-local-dir "etc/")) - (doom-cache-dir (concat doom-local-dir "cache/")) - (package-user-dir (expand-file-name "elpa" doom-packages-dir)) - package-alist - package-archive-contents - package-initialize) - (package-initialize) - ,@forms)) - -(defmacro -with-packages! (packages package-descs &rest body) - `(let ((doom-packages ,packages) +(defmacro with-packages!! (packages package-descs &rest body) +`(let* ((doom-packages-dir ,(expand-file-name "packages/" (file-name-directory load-file-name))) + (package-user-dir ,(expand-file-name "elpa" doom-packages-dir)) + (quelpa-dir ,(expand-file-name "quelpa" doom-packages-dir))) + ;; (make-directory doom-packages-dir t) + (let ((doom-packages ,packages) (package-alist ,package-descs) doom-core-packages) - (cl-letf (((symbol-function 'doom-initialize-packages) (lambda (&rest _))) - ((symbol-function 'package-installed-p) (lambda (name &rest _) (assq name package-alist)))) - ,@body))) + (cl-letf (((symbol-function 'doom-initialize-packages) (lambda (&rest _)))) + ,@body)) + ;; (delete-directory doom-packages-dir t) + )) ;; @@ -32,18 +23,19 @@ affects your Emacs packages)." ;; (def-test! backend-detection - (let ((package-alist `((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234))))) + (let ((package-alist `((doom-dummy ,(-pkg 'doom-dummy '(20160405 1234))))) (quelpa-cache '((doom-quelpa-dummy :fetcher github :repo "hlissner/does-not-exist"))) (quelpa-initialized-p t)) (should (eq (doom-package-backend 'doom-dummy) 'elpa)) - (should (eq (doom-package-backend 'doom-quelpa-dummy) 'quelpa)))) + (should (eq (doom-package-backend 'doom-quelpa-dummy) 'quelpa)) + (should (eq (doom-package-backend 'org) 'emacs)))) (def-test! elpa-outdated-detection (let* ((doom--last-refresh (current-time)) (package-alist - `((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234))))) + `((doom-dummy ,(-pkg 'doom-dummy '(20160405 1234))))) (package-archive-contents - `((doom-dummy ,(-new-package 'doom-dummy '(20170405 1234)))))) + `((doom-dummy ,(-pkg 'doom-dummy '(20170405 1234)))))) (cl-letf (((symbol-function 'package-refresh-contents) (lambda (&rest _)))) (should (equal (doom-package-outdated-p 'doom-dummy) '(doom-dummy (20160405 1234) (20170405 1234))))))) @@ -52,7 +44,7 @@ affects your Emacs packages)." (def-test! get-packages (let ((quelpa-initialized-p t)) - (-with-packages! + (with-packages!! '((doom-dummy)) '((doom-dummy nil) (doom-dummy-unwanted nil) @@ -62,17 +54,17 @@ affects your Emacs packages)." (def-test! orphaned-packages "Test `doom-get-orphaned-packages', which gets a list of packages that are no longer enabled or depended on." - (-with-packages! + (with-packages!! '((doom-dummy)) - `((doom-dummy ,(-new-package 'doom-dummy '(20160405 1234) '((doom-dummy-dep (1 0))))) - (doom-dummy-unwanted ,(-new-package 'doom-dummy-unwanted '(20160601 1234))) - (doom-dummy-dep ,(-new-package 'doom-dummy-dep '(20160301 1234)))) + `((doom-dummy ,(-pkg 'doom-dummy '(20160405 1234) '((doom-dummy-dep (1 0))))) + (doom-dummy-unwanted ,(-pkg 'doom-dummy-unwanted '(20160601 1234))) + (doom-dummy-dep ,(-pkg 'doom-dummy-dep '(20160301 1234)))) (should (equal (doom-get-orphaned-packages) '(doom-dummy-unwanted))))) (def-test! missing-packages "Test `doom-get-missing-packages, which gets a list of enabled packages that aren't installed." - (-with-packages! + (with-packages!! '((doom-dummy) (doom-dummy-installed)) - `((doom-dummy-installed ,(-new-package 'doom-dummy-installed '(20160405 1234)))) + `((doom-dummy-installed ,(-pkg 'doom-dummy-installed '(20160405 1234)))) (should (equal (doom-get-missing-packages) '((doom-dummy)))))) diff --git a/core/test/core-lib.el b/core/test/core-lib.el index fba4c22e5..30e59a690 100644 --- a/core/test/core-lib.el +++ b/core/test/core-lib.el @@ -147,10 +147,13 @@ ;; --- Settings --------------------------- -(def-setting! :-test-setting (x) x) - (def-test! set - (should (assq :-test-setting doom-settings)) - (should (set! :-test-setting t)) - (let ((inhibit-message t)) - (should-not (set! :non-existant-setting (error "This shouldn't trigger"))))) + (eval-and-compile + (let (doom-settings) + (def-setting! :-test-setting (x) `(setq result ,x)) + (should (assq :-test-setting doom-settings)) + (let ((inhibit-message t) + result) + (set! :-test-setting t) + (should result) + (set! :non-existant-setting (error "This shouldn't trigger")))))) diff --git a/core/test/core-projects.el b/core/test/core-projects.el new file mode 100644 index 000000000..3ebf2bc9b --- /dev/null +++ b/core/test/core-projects.el @@ -0,0 +1,43 @@ +;; -*- no-byte-compile: t; -*- +;;; ../core/test/core-projects.el + +(require 'projectile) + +;; +;; `doom-project-p' +(def-test! project-p + :minor-mode projectile-mode + (let ((default-directory doom-emacs-dir)) + (should (doom-project-p))) + (let ((default-directory (expand-file-name "~"))) + (should-not (doom-project-p)))) + +;; `doom-project-p' +(def-test! project-root + :minor-mode projectile-mode + ;; Should resolve to project root + (let ((default-directory doom-core-dir)) + (should (equal (doom-project-root) doom-emacs-dir))) + ;; Should resolve to `default-directory' if not a project + (let ((default-directory (expand-file-name "~"))) + (should (equal (doom-project-root) default-directory)))) + +;; `doom-project-expand' +(def-test! project-expand + :minor-mode projectile-mode + (let ((default-directory doom-core-dir)) + (should (equal (doom-project-expand "init.el") + (expand-file-name "init.el" (doom-project-root)))))) + +;; `doom-project-has!' +(def-test! project-has! + :minor-mode projectile-mode + (let ((default-directory doom-core-dir)) + ;; Resolve from project root + (should (doom-project-has! "init.el")) + ;; Chained file checks + (should (doom-project-has! (and "init.el" "LICENSE"))) + (should (doom-project-has! (or "init.el" "does-not-exist"))) + (should (doom-project-has! (and "init.el" (or "LICENSE" "does-not-exist")))) + ;; Should resolve relative paths from `default-directory' + (should (doom-project-has! (and "./core.el" "../init.el"))))) diff --git a/core/test/core-ui.el b/core/test/core-ui.el new file mode 100644 index 000000000..c1c82a01d --- /dev/null +++ b/core/test/core-ui.el @@ -0,0 +1,45 @@ +;; -*- no-byte-compile: t; -*- +;;; ../core/test/core-ui.el + +(defmacro with-temp-windows!! (&rest body) + (declare (indent defun)) + `(progn + (delete-other-windows) + (cl-flet ((split-window (symbol-function #'split-window-horizontally))) + (let ((a (get-buffer-create "a")) + (b (get-buffer-create "b")) + (split-width-threshold 0) + (window-min-width 0)) + ,@body)))) + +;; +(def-test! set-mode-name + (let ((doom-major-mode-names '((text-mode . "abc") + (lisp-mode . (lambda () "xyz")) + (js-mode . t)))) + (text-mode) + (should (equal mode-name "abc")) + (lisp-mode) + (should (equal mode-name "xyz")) + (should-error (js-mode)))) + +(def-test! protect-visible-buffers + (with-temp-windows!! + (let ((kill-buffer-query-functions '(doom|protect-visible-buffers))) + (switch-to-buffer a) (split-window) + (switch-to-buffer b) (split-window) + (switch-to-buffer a) + (should-not (kill-buffer)) + (select-window (get-buffer-window b)) + (should (kill-buffer))))) + +(def-test! *quit-window + (with-temp-windows!! + (let (kill-buffer-query-functions) + (switch-to-buffer a) (split-window) + (switch-to-buffer b) + (save-window-excursion + (quit-window t) + (should (buffer-live-p b))) + (quit-window) + (should-not (buffer-live-p b))))) diff --git a/init.example.el b/init.example.el index 752f734b6..3a7a691a7 100644 --- a/init.example.el +++ b/init.example.el @@ -130,7 +130,10 @@ ;twitter ; twitter client https://twitter.com/vnought ;write ; emacs as a word processor (latex + org + markdown) - ;; This is the private module of Doom's maintainer; use it as a reference + ;; Private modules are where you place your personal configuration files. + ;; By default, they are not tracked. There is one module included here, + ;; the defaults module. It contains a Spacemacs-inspired keybinding + ;; scheme and additional ex commands for evil-mode. Use it as a reference ;; for your own. - :private hlissner) + :private default) diff --git a/init.test.el b/init.test.el index 038114c87..8d529cd2f 100644 --- a/init.test.el +++ b/init.test.el @@ -9,6 +9,9 @@ :completion company + :ui + doom-dashboard + :tools password-store diff --git a/modules/app/rss/config.el b/modules/app/rss/config.el index 343e74bd2..6cdce9a3c 100644 --- a/modules/app/rss/config.el +++ b/modules/app/rss/config.el @@ -56,7 +56,7 @@ (def-package! elfeed-org - :after elfeed + :after (:all org elfeed) :config (setq rmh-elfeed-org-files (let ((default-directory +org-dir)) diff --git a/modules/completion/company/README.org b/modules/completion/company/README.org index 76e9ad4ac..f2a1bc6fc 100644 --- a/modules/completion/company/README.org +++ b/modules/completion/company/README.org @@ -14,20 +14,23 @@ This module adds code-completion support, powered by [[https://github.com/compan - [[#troubleshooting][Troubleshooting]] * Install -Certain languages may require additional setup, and some languages may have no completion support at all. +Some languages require additional setup, and some languages may have no +completion support at all. Check the README.org in that language's module for details. * Configure ** Auto-completion -By default, I've disabled auto-completion. This is my preference. I prefer to invoke company when I need it by calling ~company-complete~ manually (typically, bound to =C-SPC= in insert mode). However, some may not share my preference. +By default, I've disabled auto-completion. This is my preference. I prefer to +invoke company when I need it by calling ~company-complete~ manually (typically, +bound to =C-SPC= in insert mode). However, some may not share my preference. To enable auto-completion you must: 1. Load ~company~, 2. and change ~company-idle-delay~ to a non-nil float (the default is 0.5) -For example, add the following to your ~modules/private//config.el~ module: +For example: #+BEGIN_SRC emacs-lisp (require 'company) @@ -36,8 +39,10 @@ For example, add the following to your ~modules/private//config.el~ mo #+END_SRC * Troubleshooting -If completion isn't working for you, please consider the following before posting a bug report: +If completion isn't working for you, please consider the following before +posting a bug report: -+ If what you are expecting is popup-as-you-type completion (which is disabled by default), see the "Customize" section above; it includes instructions on how to enable this. -+ Certain languages may have extra dependencies in order for auto-completion to work. Please look for that module's README.org for details. -+ Some languages don't have any auto-completion support. ++ If what you are expecting is popup-as-you-type completion (which is disabled + by default), see the "Configure > Auto-completion" section above, which will + instruct you on how to enable this. ++ Some languages don't have any auto-completion support at all. diff --git a/modules/completion/company/test/company.el b/modules/completion/company/test/company.el index d8969d398..4f7244c14 100644 --- a/modules/completion/company/test/company.el +++ b/modules/completion/company/test/company.el @@ -4,20 +4,21 @@ (require! :completion company) (require 'company) +;; (def-test! set-company-backend - (let ((default-backends (default-value 'company-backends))) + :minor-mode company-mode + (let ((company-backends '(default))) (set! :company-backend 'emacs-lisp-mode '(backend-1)) (set! :company-backend 'lisp-interaction-mode 'backend-1 'backend-2) (set! :company-backend 'text-mode 'backend-1) (with-temp-buffer (emacs-lisp-mode) - (should (equal (car company-backends) '(backend-1)))) + (should (equal company-backends '((backend-1) default)))) (with-temp-buffer (lisp-interaction-mode) - (should (equal company-backends - (append '(backend-1 backend-2) default-backends)))) + (should (equal company-backends '(backend-1 backend-2 default)))) (with-temp-buffer (text-mode) - (should (eq (car company-backends) 'backend-1))) + (should (equal company-backends '(backend-1 default)))) ;; global backends shouldn't be affected - (should (equal company-backends default-backends)))) + (should (equal company-backends '(default))))) diff --git a/modules/completion/ivy/README.org b/modules/completion/ivy/README.org index b0732f935..18f399739 100644 --- a/modules/completion/ivy/README.org +++ b/modules/completion/ivy/README.org @@ -3,11 +3,13 @@ This module adds Ivy, a completion backend. #+begin_quote -I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's lighter. +I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's +lighter. #+end_quote + Project-wide search & replace powered by ~rg~ or ~ag~ -+ Project jump-to navigation ala Command-T, Sublime Text's Jump-to-anywhere or Vim's CtrlP plugin. ++ Project jump-to navigation ala Command-T, Sublime Text's Jump-to-anywhere or + Vim's CtrlP plugin. + Ivy integration for ~M-x~, ~imenu~, ~recentf~ and others. + A powerful, interactive in-buffer search using ~swiper~. + Ivy-powered TODO/FIXME navigation @@ -28,7 +30,9 @@ I prefer ivy over ido for its flexibility. I prefer ivy over helm because it's l * Install This module optionally depends on [[https://github.com/BurntSushi/ripgrep][ripgrep]] and [[https://github.com/ggreer/the_silver_searcher][the_silver_searcher]]. -~rg~ is faster, but its results aren't deterministic, neither does it support multiline search or full PCRE, that's where ~ag~ is useful. +~rg~ is faster, but its results aren't deterministic, neither does it support +multiline search or full PCRE (at the time of writing), that's where ~ag~ is +useful. ** MacOS #+BEGIN_SRC sh :tangle (if (doom-system-os 'macos) "yes") @@ -41,37 +45,58 @@ sudo pacman --needed --noconfirm -S ripgrep the_silver_searcher #+END_SRC * Usage -Here is some insight into how I use this module. Keep in mind that the referenced commands and keybindings are defined [[/modules/private/hlissner][in my private module]]. +Here is some insight into how I use this module. ** Project search & replace -Ex interfaces to Ag (the silver searcher) and Rg (ripgrep) are available: ~:ag[!]~ and ~:rg[!]~, or their current-directory counterparts ~:agcwd[!]~ and ~:rgcwd[!]~. +There are four Ex interfaces for the silver searcher and ripgrep. They are: + ++ ~:ag[!]~ ++ ~:agcwd[!]~ ++ ~:rg[!]~ ++ ~:rgcwd[!]~ + +The optional BANG tells ag/rg to include ignored files in the search. And the +\*cwd variant of each command will only search in the current directory +(non-recursively). [[/../screenshots/modules/completion/ivy/ivy-search.gif]] -From this session, you can press =S+Tab= to create a writeable occur-buffer in wgrep mode. +Now, how do we do text replacements? With the ivy popup open you can press +=S+Tab= to create an wgrep buffer out of the results. [[/../screenshots/modules/completion/ivy/ivy-search-replace.gif]] -Make your modifications and press =C-c C-c= to commit them, or =C-c C-k= to abort. +Make your modifications and press =C-c C-c= to commit them, or =C-c C-k= to +abort. ** Jump-to-file project navigation -Inspired by Sublime Text's jump-to-anywhere, Vim's CtrlP or Unite plugins, and Textmate's Command-T, a marriage of ~projectile~ and ~ivy~ makes this available to you in Emacs. Invoke it with =SPC f /=, =SPC SPC= or ~counsel-projectile-find-file~. +Inspired by Sublime Text's jump-to-anywhere, Vim's CtrlP/Unite plugins, and +Textmate's Command-T, a marriage of ~projectile~ and ~ivy~ makes this available +in Emacs. + +Invoke it with =SPC f /=, =SPC SPC= or ~M-x counsel-projectile-find-file~. [[/../screenshots/modules/completion/ivy/ivy-projectile.gif]] ** In-buffer searching -I use ~evil-search~ (invoked by pressing =/= in normal mode) when jumping small/moderate (or predictable) distances. However, there are occasions where I need more feedback, so I turn to ~swiper~ (available directly with =M-x swiper RET=, or via ~:sw[iper]~). +I use ~evil-search~ (invoked by pressing =/= in normal mode) when jumping +small/moderate (or predictable) distances. However, there are occasions where I +need more feedback, so I turn to ~swiper~ (available directly with =M-x swiper +RET=, or via ~:sw[iper]~). [[/../screenshots/modules/completion/ivy/ivy-swiper.gif]] ** Task lookup -I sprinkle my projects with TODO's & FIXME's. You can navigate to and peruse them via ~M-x +ivy/tasks~ or ~:todo[!]~ (ex command). +I sprinkle my projects with TODO's & FIXME's. You can navigate to and peruse +them via ~M-x +ivy/tasks~ or ~:todo[!]~ (ex command). [[/../screenshots/modules/completion/ivy/ivy-todo.gif]] * Appendix ** Commands -Here is a list of my commonly used commands, their default keybinds (defined in [[../../private/hlissner/+bindings.el][private/hlissner/+bindings.el]]), and their corresponding ex command (defined in [[../../private/hlissner/+commands.el][private/hlissner/+commands.el]]). +Here is a list of my commonly used commands, their default keybinds (defined in +[[../../private/default/+bindings.el][private/default/+bindings.el]]), and their corresponding ex command (defined in +[[../../private/default/+evil-commands.el][private/default/+evil-commands.el]]). | command | key / ex command | description | |-------------------------------------+------------------------+------------------------------------------------------------------| @@ -90,7 +115,8 @@ Here is a list of my commonly used commands, their default keybinds (defined in | ~+ivy:swiper~ | ~:sw[iper] [QUERY]~ | Search current buffer | | ~+ivy:todo~ | ~:todo[!]~ | List all TODO/FIXMEs in project (or current file if BANG) | -While in a search (e.g. invoked from ~+ivy:ag~ or ~+ivy:rg~), these new keybindings are available to you: +While in a search (e.g. invoked from ~+ivy:ag~ or ~+ivy:rg~), these new +keybindings are available to you: | key | description | |-------------+--------------------------------------------------------------------------------| @@ -99,10 +125,10 @@ While in a search (e.g. invoked from ~+ivy:ag~ or ~+ivy:rg~), these new keybindi | =M-RET= | Open the selected candidate in other-window | ** Hacks -+ Functions with ivy/counsel equivalents have been globally remapped (like ~find-file~ => ~counsel-find-file~). So a keybinding to ~find-file~ will invoke ~counsel-find-file~ instead. -+ ~counsel-[arp]g~'s 3-character limit was reduced to 1 (mainly for the ex command) -+ ~counsel-[arp]g~'s parentheses quoting behavior was reversed. Now, if you - want literal parentheses, you must escape them: e.g. ~\(match\)~ is literal, - ~(match)~ is a regexp group. ++ Functions with ivy/counsel equivalents have been globally remapped (like + ~find-file~ => ~counsel-find-file~). So a keybinding to ~find-file~ will + invoke ~counsel-find-file~ instead. ++ ~counsel-[arp]g~'s 3-character limit was reduced to 1 (mainly for the ex + command) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index bcd027078..fa707c5ea 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -43,8 +43,7 @@ immediately runs it on the current candidate (ending the ivy session)." (after! magit (setq magit-completing-read-function #'ivy-completing-read)) (after! yasnippet (push #'+ivy-yas-prompt yas-prompt-functions)) - (map! :map ivy-mode-map - [remap apropos] #'counsel-apropos + (map! [remap apropos] #'counsel-apropos [remap describe-face] #'counsel-describe-face [remap find-file] #'counsel-find-file [remap switch-to-buffer] #'ivy-switch-buffer @@ -52,7 +51,6 @@ immediately runs it on the current candidate (ending the ivy session)." [remap recentf-open-files] #'counsel-recentf [remap imenu] #'counsel-imenu [remap bookmark-jump] #'counsel-bookmark - [remap projectile-switch-project] #'counsel-projectile-switch-project [remap projectile-find-file] #'counsel-projectile-find-file [remap imenu-anywhere] #'ivy-imenu-anywhere [remap execute-extended-command] #'counsel-M-x diff --git a/modules/feature/eval/README.org b/modules/feature/eval/README.org index eb9cf3084..851ea6a48 100644 --- a/modules/feature/eval/README.org +++ b/modules/feature/eval/README.org @@ -1,43 +1,52 @@ #+TITLE: :feature eval -This modules adds support for evaluating code from inside Emacs. This includes REPLs and direct access to the interpreters and compilers of many languages. +This modules adds support for evaluating code from inside Emacs, including +REPLs. * Table of Contents :TOC: - [[#install][Install]] - [[#usage][Usage]] -- [[#configuration][Configuration]] - [[#repls][REPLs]] - - [[#code-evaluation][Code Evaluation]] + - [[#code-evaluation][*Code Evaluation*]] +- [[#configuration][Configuration]] + - [[#repls-1][REPLs]] + - [[#code-evaluation-1][Code Evaluation]] * Install -This module has no external dependencies. However, specific languages may require additional setup. +This module has no external dependencies. However, specific languages may +require additional setup. Check the README.org in that language's module for details. * Usage -+ *REPLs* - Invoked via: - + ~:repl~ (evil ex-command) - + = o r= in normal mode (or visual mode, which sends the selection to the open REPL) - + ~M-x +eval/open-repl~ - + ~M-x +eval/send-region-to-repl~ while a selection (and REPL) is active +** REPLs +Invoked via: ++ ~:repl~ (evil ex-command) ++ = o r= in normal mode (or visual mode, which sends the selection to + the open REPL) ++ ~M-x +eval/open-repl~ ++ ~M-x +eval/send-region-to-repl~ while a selection (and REPL) is active -+ *Code Evaluation* - Quickrun can be invoked via: - + ~M-x +eval/buffer~ (or ~gR~, or ~M-r~) - + ~M-x +eval/region~ - + ~M-x +eval/region-and-replace~ - + Evil users can use the ~gr~ operator to select and run a region. +** *Code Evaluation* +Quickrun can be invoked via: ++ ~M-x +eval/buffer~ (or ~gR~, or ~M-r~) ++ ~M-x +eval/region~ ++ ~M-x +eval/region-and-replace~ ++ Evil users can use the ~gr~ operator to select and run a region. * Configuration ** REPLs -REPLs are defined for most of the languages Doom supports (check its README.org to see if it does). +REPLs are defined for most of the languages Doom supports (check its README.org +to see if it does). -Otherwise, you can define your own for a specified major-mode with the =:repl= setting. +Otherwise, you can define your own for a specified major-mode with the =:repl= +setting. ~(set! :repl MAJOR-MODE FUNCTION)~ -FUNCTION must return the repl buffer. Any window changes are ignored, then handed off to shackle (assuming shackle-mode is on) to display in a popup window. +FUNCTION must return the repl buffer. Any window changes are ignored, then +handed off to shackle (assuming shackle-mode is on) to display in a popup +window. #+BEGIN_SRC emacs-lisp (defun +emacs-lisp/repl () @@ -53,9 +62,14 @@ FUNCTION must return the repl buffer. Any window changes are ignored, then hande #+END_SRC ** Code Evaluation -Run regions or entire buffers with [[https://github.com/syohex/emacs-quickrun][Quickrun]]. Output will be sent to a popup window. +Run regions or entire buffers with [[https://github.com/syohex/emacs-quickrun][Quickrun]]. Output is show in a popup window. -Quickrun includes support for many languages, but occasionally, you'll find a language without support, such as [[https://crystal-lang.org/][Crystal]]. A "runner" can be defined like so: +Quickrun includes support for many languages, usually by sending text directly +to interpreters or compilers. However, occasionally, you'll find a language +without support (like [[https://crystal-lang.org/][Crystal]]), or a language with better Emacs integration +(like elisp). + +Here's how you define a "runner": #+BEGIN_SRC emacs-lisp (set! :eval 'crystal-mode diff --git a/modules/feature/evil/README.org b/modules/feature/evil/README.org index 1ba70d4cb..4093c770a 100644 --- a/modules/feature/evil/README.org +++ b/modules/feature/evil/README.org @@ -11,12 +11,14 @@ This holy module brings the vim experience to Emacs. - [[#differences-from-vim][Differences from vim]] * Removing evil-mode -To get back a more vanilla Emacs experience, remove =:feature evil= from init.el. Evil-specific configuration and keybindings (defined with ~map!~) will be ignored without evil present (and removed when byte-compiling). +See the [[https://github.com/hlissner/doom-emacs/wiki/FAQ#remove-vimevil-for-a-more-vanilla-emacs-experience][corresponding question in the FAQ]]. * Features + A better ~:g[lobal]~ command with incremental highlighting. -+ Adds the ~:al[ign]~ ex command: offers an ex interface to ~align-regexp~ with incremental highlighting. -+ Support for more of vim's filename modifiers in ex commands (like ~:p~, ~:p:h~ or ~:t~) than vanilla evil-mode offers. ++ Adds the ~:al[ign]~ ex command: offers an ex interface to ~align-regexp~ with + incremental highlighting. ++ Support for more of vim's filename modifiers in ex commands (like ~:p~, ~:p:h~ + or ~:t~) than vanilla evil-mode offers. + A list of new text objects: + Blocks: ~B~ (from ~evil-textobj-anyblock~) + Args: ~a~ (from ~evil-args~) @@ -31,20 +33,29 @@ To get back a more vanilla Emacs experience, remove =:feature evil= from init.el + =NERDTree= equivalent is available in =:tools neotree= ** Multiple-cursors -Two multiple-cursor implementations exist in this module: ~evil-mc~ and ~evil-multiedit~. Together, these provide the functionality of ~vim-multiple-cursors~. +Two multiple-cursor implementations exist in this module: ~evil-mc~ and +~evil-multiedit~. Together, these provide the functionality of +~vim-multiple-cursors~. -The former lets you place "clone" cursors. The latter lets you interactively edit many regions from one place (like an interactive version of ~:%s~). +The former lets you place "clone" cursors. The latter lets you interactively +edit many regions at once (like an interactive version of ~:%s~). ** A hybrid code-folding system -This module combines ~evil-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. +This module combines ~evil-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. ** Hacks + Automatically moves to new window when splitting -+ If in visual mode, =*= and =#= will search for the current selection instead of the word-at-point. ++ From visual mode, =*= and =#= will search for the current selection instead of + the word-at-point. ** 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~). ++ 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. diff --git a/modules/feature/evil/autoload/files.el b/modules/feature/evil/autoload/files.el index c247f6f46..c7d293281 100644 --- a/modules/feature/evil/autoload/files.el +++ b/modules/feature/evil/autoload/files.el @@ -35,7 +35,7 @@ kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)." (error "Failed to delete %s" short-path) ;; Ensures that windows displaying this buffer will be switched ;; to real buffers (`doom-real-buffer-p') - (doom-force-kill-buffer buf t) + (doom/kill-this-buffer-in-all-windows buf t) (+evil--forget-file fname) (message "Successfully deleted %s" short-path)))))))) diff --git a/modules/feature/evil/config.el b/modules/feature/evil/config.el index 4d24b856e..bf0efc808 100644 --- a/modules/feature/evil/config.el +++ b/modules/feature/evil/config.el @@ -388,8 +388,8 @@ the new algorithm is confusing, like in python or ruby." ;; ;; mc doesn't play well with evil, this attempts to assuage some of its problems -;; so that certain plugins (which I have no control over) can still use it in -;; relative safety. +;; so that any plugins that depend on multiple-cursors (which I have no control +;; over) can still use it in relative safety. (after! multiple-cursors-core (map! :map mc/keymap :ne "" #'mc/keyboard-quit) diff --git a/modules/feature/evil/test/autoload-files.el b/modules/feature/evil/test/autoload-files.el index 762e39fbe..b14185cd1 100644 --- a/modules/feature/evil/test/autoload-files.el +++ b/modules/feature/evil/test/autoload-files.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; feature/evil/test/autoload-files.el -(defmacro -with-temp-files! (src dest &rest body) +(defmacro with-temp-files!! (src dest &rest body) "Run FORMS in the context of a temporary package setup (as in, it won't affects your Emacs packages)." (declare (indent 2) (doc-string 3)) @@ -23,7 +23,7 @@ affects your Emacs packages)." ;; (def-test! move-this-file ":mv" - (-with-temp-files! "/tmp/doom-buffer" "/tmp/doom-buffer-new" + (with-temp-files!! "/tmp/doom-buffer" "/tmp/doom-buffer-new" (should-error (+evil:move-this-file it)) (should (+evil:move-this-file other t)) (should (file-exists-p other)) @@ -31,7 +31,7 @@ affects your Emacs packages)." (def-test! copy-this-file ":cp" - (-with-temp-files! "/tmp/doom-buffer-2" "/tmp/doom-buffer-2-new" + (with-temp-files!! "/tmp/doom-buffer-2" "/tmp/doom-buffer-2-new" (should-error (+evil:copy-this-file it)) (should (+evil:copy-this-file other t)) (should (file-exists-p other)) @@ -39,7 +39,7 @@ affects your Emacs packages)." (def-test! delete-this-file ":rm" - (-with-temp-files! "/tmp/doom-buffer-3" nil + (with-temp-files!! "/tmp/doom-buffer-3" nil (should-error (+evil:delete-this-file "this-file-does-not-exist")) (should (+evil:delete-this-file nil t)) (should (not (file-exists-p it))))) diff --git a/modules/feature/evil/test/evil.el b/modules/feature/evil/test/evil.el index 7fd0626a3..ffe6f92a1 100644 --- a/modules/feature/evil/test/evil.el +++ b/modules/feature/evil/test/evil.el @@ -3,6 +3,7 @@ (require! :feature evil) +;; ;; `evil-ex-replace-special-filenames' ;; NOTE The majority of this function is tested in core/test/core-lib.el, this ;; only tests the evil-mode-specific functionality. diff --git a/modules/feature/file-templates/config.el b/modules/feature/file-templates/config.el index b8d6fcf10..db8feaafe 100644 --- a/modules/feature/file-templates/config.el +++ b/modules/feature/file-templates/config.el @@ -6,6 +6,11 @@ (expand-file-name "templates/" (file-name-directory load-file-name)) "The path to a directory of yasnippet folders to use for file templates.") + +;; +;; Plugins +;; + (def-package! autoinsert ; built-in :defer 1 :init @@ -18,12 +23,16 @@ (auto-insert-mode 1) (defun +file-templates--expand (key &optional mode project-only) - "Auto insert a snippet of yasnippet into new file." + "Auto insert a yasnippet snippet into the blank file." (when (if project-only (doom-project-p) t) (require 'yasnippet) - (unless yas-minor-mode (yas-minor-mode-on)) + (unless yas-minor-mode + (yas-minor-mode-on)) (when (and yas-minor-mode - (yas-expand-snippet (yas-lookup-snippet key mode t)) + (yas-expand-snippet + (yas--template-content + (cl-find key (yas--all-templates (yas--get-snippet-tables mode)) + :key #'yas--template-key :test #'equal))) (and (featurep 'evil) evil-mode) (and yas--active-field-overlay (overlay-buffer yas--active-field-overlay) @@ -56,10 +65,9 @@ ("\\.el$" "__initfile" emacs-lisp-mode) ("/.dir-locals.el$" nil) ("-test\\.el$" "__" emacs-ert-mode) - ("/.emacs.d/.+\\.el$" "__doom-module" emacs-lisp-mode) - ("/.emacs.d/.+/packages\\.el$" "__doom-packages" emacs-lisp-mode) - ("/.emacs.d/.+/test\\.el$" "__doom-test" emacs-lisp-mode) - ("/.emacs.d/.+/README\\.org$" "__doom-readme" org-mode) + ("/\\(?:.emacs.d\\|doom-emacs\\)?/.+\\.el$" "__doom-module" emacs-lisp-mode) + ("/\\(?:.emacs.d\\|doom-emacs\\)?/.+/packages\\.el$" "__doom-packages" emacs-lisp-mode) + ("/\\(?:.emacs.d\\|doom-emacs\\)?/.+/test/.+\\.el$" "__doom-test" emacs-lisp-mode) (snippet-mode "__" snippet-mode) ;; Go ("\\.go$" "__.go" go-mode) @@ -82,7 +90,8 @@ ;; Markdown ("\\.md$" "__" markdown-mode) ;; Org - ("\\.org$" "__" org-mode) + ("\\.org$" "__" org-mode) + ("/\\(?:.emacs.d\\|doom-emacs\\)?/.+/README\\.org$" "__doom-readme" org-mode) ;; PHP ("\\.php$" "__" php-mode) ("\\.class\\.php$" "__.class.php" php-mode) @@ -110,5 +119,5 @@ ("/\\(index\\|main\\)\\.slim$" "__" slim-mode) ;; Shell scripts ("\\.z?sh$" "__" sh-mode) + ("\\.fish$" "__" fish-mode) ("\\.zunit$" "__zunit" sh-mode)))) - diff --git a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-module b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-module index d2e44ba28..4f5468293 100644 --- a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-module +++ b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-module @@ -1,3 +1,3 @@ -;;; `(file-relative-name buffer-file-name doom-modules-dir)` -*- lexical-binding: t; -*- +;;; `(file-relative-name buffer-file-truename doom-modules-dir)` -*- lexical-binding: t; -*- $0 diff --git a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-packages b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-packages index 789b9b983..a87f1eae8 100644 --- a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-packages +++ b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-packages @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- -;;; `(file-relative-name buffer-file-name doom-modules-dir)` +;;; `(file-relative-name buffer-file-truename doom-modules-dir)` $0 diff --git a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-test b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-test index 9cd77f6ac..af428094a 100644 --- a/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-test +++ b/modules/feature/file-templates/templates/emacs-lisp-mode/__doom-test @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- -;;; `(file-relative-name buffer-file-name doom-modules-dir)` +;;; `(file-relative-name buffer-file-truename doom-modules-dir)` $0 \ No newline at end of file diff --git a/modules/feature/file-templates/templates/fish-mode/__ b/modules/feature/file-templates/templates/fish-mode/__ new file mode 100644 index 000000000..a4ef95db3 --- /dev/null +++ b/modules/feature/file-templates/templates/fish-mode/__ @@ -0,0 +1,3 @@ +#!/usr/bin/env fish + +$0 diff --git a/modules/feature/jump/autoload/jump.el b/modules/feature/jump/autoload/jump.el index 21d951f3b..36ad3c49c 100644 --- a/modules/feature/jump/autoload/jump.el +++ b/modules/feature/jump/autoload/jump.el @@ -97,7 +97,9 @@ Tries `xref-find-references' and falls back to rg/ag." (cond ((plist-member +jump-current-functions :documentation) (+jump-to :documentation identifier)) (t - (+jump/online (caar +jump-search-provider-alist) identifier)))) + (+jump/online + identifier + (+jump--online-get-provider (not current-prefix-arg)))))) (defun +jump--online-get-provider (&optional force-p) (or (and (not force-p) diff --git a/modules/feature/jump/config.el b/modules/feature/jump/config.el index db7a65069..f4e005446 100644 --- a/modules/feature/jump/config.el +++ b/modules/feature/jump/config.el @@ -29,7 +29,7 @@ "An alist that maps online resources to their search url or a function that produces an url. Used by `+jump/online'.") -(defconst +jump-search-browser-fn #'browse-url +(defvar +jump-search-browser-fn #'browse-url "Function to use to open search urls.") (defvar +jump-function-alist nil @@ -82,6 +82,18 @@ properties: ;; Packages ;; +(def-package! ivy-xref + :when (featurep! :completion ivy) + :after xref + :config (setq xref-show-xrefs-function #'ivy-xref-show-xrefs)) + + +(def-package! helm-xref + :when (featurep! :completion helm) + :after xref + :config (setq xref-show-xrefs-function #'helm-xref-show-xrefs)) + + (def-package! dumb-jump :commands (dumb-jump-go dumb-jump-quick-look dumb-jump-back dumb-jump-result-follow) @@ -92,28 +104,3 @@ properties: ((featurep! :completion helm) 'helm) (t 'popup)))) - -(def-package! gxref - :commands (gxref-xref-backend - gxref-create-db - gxref-update-db - gxref-single-update-db - gxref-set-project-dir) - :init - (setq-default xref-backend-functions '(gxref-xref-backend t))) - - -;; (def-package! ggtags -;; :commands (ggtags-find-tag-dwim -;; ggtags-find-tag-mouse -;; ggtags-find-definition -;; ggtags-find-reference -;; ggtags-find-other-symbol -;; ggtags-find-tag-regexp -;; ggtags-idutils-query -;; ggtags-grep -;; ggtags-find-file -;; ggtags-query-replace -;; ggtags-delete-tags -;; ggtags-explain-tags)) - diff --git a/modules/feature/jump/packages.el b/modules/feature/jump/packages.el index 4dce3ac0d..712158bb2 100644 --- a/modules/feature/jump/packages.el +++ b/modules/feature/jump/packages.el @@ -2,10 +2,7 @@ ;;; feature/jump/packages.el (package! dumb-jump) -(package! gxref) -;; (package! ggtags) -;; (cond ((featurep! :completion ivy) -;; (package! counsel-gtags)) -;; ((featurep! :completion helm) -;; (package! helm-gtags))) - +(when (featurep! :completion ivy) + (package! ivy-xref)) +(when (featurep! :completion helm) + (package! helm-xref)) diff --git a/modules/feature/snippets/README.org b/modules/feature/snippets/README.org index 3a81372b6..d22278f1b 100644 --- a/modules/feature/snippets/README.org +++ b/modules/feature/snippets/README.org @@ -8,20 +8,5 @@ This module adds snippets to Emacs, powered by yasnippet. * Install There are no extra dependencies for this module. -By default, this module uses the snippet library included with yasnippet. - -For the best experience, I'd suggest installing mine from https://github.com/hlissner/emacs-snippets -- they have been tailored specifically for Doom. - -1. Clone the repo to your private module: - #+BEGIN_SRC bash - git clone https://github.com/hlissner/emacs-snippets ~/.emacs.d/modules/private/$(whoami)/snippets - #+END_SRC -2. Tell yasnippet where to look for them: - #+BEGIN_SRC emacs-lisp - ;; modules/private/{USERNAME}/config.el - (after! yasnippet - (setq yas-snippet-dirs - (append (list (expand-file-name "snippets/" (file-name-directory load-file-name))) - (delq 'yas-installed-snippets-dir yas-snippet-dirs)))) - #+END_SRC - +By default, =private/default= installs a snippet library tailored exclusively +for Doom Emacs. diff --git a/modules/feature/snippets/config.el b/modules/feature/snippets/config.el index a00450d79..68c1a9a97 100644 --- a/modules/feature/snippets/config.el +++ b/modules/feature/snippets/config.el @@ -11,8 +11,6 @@ (defvar yas-minor-mode-map (make-sparse-keymap)) :init - (setq yas-snippet-dirs '(yas-installed-snippets-dir)) - ;; Ensure `yas-reload-all' is called as late as possible. Other modules could ;; have additional configuration for yasnippet. For example, file-templates. (add-transient-hook! 'yas-minor-mode-hook (yas-reload-all)) @@ -21,11 +19,9 @@ #'yas-minor-mode-on) :config - (setq yas-verbosity 0 - yas-indent-line 'auto + (setq yas-verbosity (if doom-debug-mode 3 0) yas-also-auto-indent-first-line t - yas-prompt-functions '(yas-completing-prompt yas-ido-prompt yas-no-prompt) - yas-use-menu nil + yas-prompt-functions (delq 'yas-dropdown-prompt yas-prompt-functions) ;; Allow nested snippets yas-triggers-in-field t) @@ -40,27 +36,8 @@ ;; fix an error caused by smartparens interfering with yasnippet bindings (advice-add #'yas-expand :before #'sp-remove-active-pair-overlay) - (after! evil - ;; Exit snippets on ESC in normal mode - (add-hook '+evil-esc-hook #'yas-exit-all-snippets) - ;; Once you're in normal mode, you're out - (add-hook 'evil-normal-state-entry-hook #'yas-abort-snippet) - ;; Strip out whitespace before a line selection - (defun +snippets|yas-before-expand () - "Strip out the shitespace before a line selection." - (when (and (evil-visual-state-p) - (eq (evil-visual-type) 'line)) - (setq yas-selected-text - (replace-regexp-in-string - "\\(^\\s-*\\|\n? $\\)" "" - (buffer-substring-no-properties evil-visual-beginning - evil-visual-end))))) - (add-hook 'yas-before-expand-snippet-hook #'+snippets|yas-before-expand) - - (defun +snippets|yas-after-expand () - "Fix previous hook persisting yas-selected-text between expansions." - (setq yas-selected-text nil)) - (add-hook 'yas-after-exit-snippet-hook #'+snippets|yas-after-expand))) + ;; Exit snippets on ESC from normal mode + (add-hook '+evil-esc-hook #'yas-exit-all-snippets)) (def-package! auto-yasnippet diff --git a/modules/feature/version-control/+git.el b/modules/feature/version-control/+git.el index 4467441bf..8a8770880 100644 --- a/modules/feature/version-control/+git.el +++ b/modules/feature/version-control/+git.el @@ -1,6 +1,10 @@ ;;; feature/version-control/+git.el -*- lexical-binding: t; -*- ;;;###if (not (featurep! -git)) +(when (featurep! :feature evil) + (add-hook 'git-commit-mode-hook #'evil-insert-state)) + + (def-package! gitconfig-mode :mode "/\\.?git/?config$" :mode "/\\.gitmodules$") diff --git a/modules/feature/workspaces/README.org b/modules/feature/workspaces/README.org index c2e3f2c71..29f8dec1a 100644 --- a/modules/feature/workspaces/README.org +++ b/modules/feature/workspaces/README.org @@ -1,9 +1,15 @@ #+TITLE: :feature workspaces -This module adds support for workspaces, powered by persp_mode, as well as a unified API for manipulating them. +This module adds support for workspaces, powered by persp_mode, as well as a API +for manipulating them. #+begin_quote -There are many ways to use workspaces. Some use them to group buffers/windows by project or categories (views, models, logic, etc). I use them differently: on a per-task basis, which may traverse multiple projects or aspects, but are tied to an objective. For example: implement a specific feature or fix a certain bug; sometimes unrelated to the project at hand. +There are many ways to use workspaces. I spawn a workspace per task. Say I'm +working in the main workspace, when I realize there is a bug in another part of +my project. I open a new workspace and deal with it in there. In the meantime, I +need to check my email, so mu4e gets its own workspace. + +Once I've completed the task, I close the workspace and return to main. #+end_quote * Table of Contents :TOC: @@ -22,25 +28,33 @@ This module has no additional dependencies. * Features ** Isolated buffer-list -When persp-mode is active, ~doom-buffer-list~ becomes workspace-restricted. You can overcome this by using ~buffer-list~. +When persp-mode is active, ~doom-buffer-list~ becomes workspace-restricted. You +can overcome this by using ~buffer-list~. ** Automatic workspaces A workspace is automatically created (and switched to) when you: -+ Create a new frame (with =make-frame=; bound to =M-N= by default) -+ Switch to a project using ~projectile-switch-project~ (or its ivy/helm equivalents) ++ Create a new frame (with =make-frame=; bound to =M-N= by default). ++ Switch to a project using ~projectile-switch-project~. ** Session persistence -By default, your session is autosaved when you quit Emacs (or disable ~persp-mode~). You can load a previous session with ~M-x +workspace/load-session~ or ~:sl[oad]~ (ex command). +By default, your session is autosaved when you quit Emacs (or disable +~persp-mode~). You can load a previous session with ~M-x ++workspace/load-session~ or ~:sl[oad]~ (ex command). -You can supply either a name to load a specific session to replace your current one. +You can supply either a name to load a specific session to replace your current +one. ** Workspace persistence -If you'd like to save a specific workspace, use ~M-x +workspace/save~, which can be loaded into the current session (as another workspace) with ~M-x +workspace/load~. +If you'd like to save a specific workspace, use ~M-x +workspace/save~, which can +be loaded into the current session (as another workspace) with ~M-x ++workspace/load~. * Appendix ** Commands & Keybindings -Here is a list of available commands, their default keybindings (defined in private/hlissner/+bindings.el), and corresponding ex commands (if any -- defined in private/hlissner/+commands.el). +Here is a list of available commands, their default keybindings (defined in +[[../../private/default/+bindings.el][private/default/+bindings.el]]), and corresponding ex commands (if any -- defined +in [[../../private/default/+evil-commands.el][private/default/+evil-commands.el]]). | command | key / ex command | description | |---------------------------+----------------------------+------------------------------------------------------------| diff --git a/modules/feature/workspaces/autoload/workspaces.el b/modules/feature/workspaces/autoload/workspaces.el index 8566cd860..7aff9382c 100644 --- a/modules/feature/workspaces/autoload/workspaces.el +++ b/modules/feature/workspaces/autoload/workspaces.el @@ -310,7 +310,7 @@ workspace to delete." (doom/kill-all-buffers) (let ((fallback-buf (doom-fallback-buffer))) (switch-to-buffer fallback-buf) - (doom/cleanup-buffers))) + (doom/cleanup-session))) ;;;###autoload (defun +workspace/kill-session-and-quit () diff --git a/modules/feature/workspaces/config.el b/modules/feature/workspaces/config.el index 6bcb58ffe..f03fb236f 100644 --- a/modules/feature/workspaces/config.el +++ b/modules/feature/workspaces/config.el @@ -47,10 +47,22 @@ renamed.") ;; per-frame and per-project workspaces (setq persp-init-new-frame-behaviour-override nil - persp-interactive-init-frame-behaviour-override #'+workspace-on-new-frame - projectile-switch-project-action #'projectile-find-file) + persp-interactive-init-frame-behaviour-override #'+workspace-on-new-frame) (add-hook 'delete-frame-functions #'+workspaces|delete-associated-workspace-maybe) - (advice-add #'projectile-switch-project-by-name :around #'+workspaces*switch-project-by-name) + + (defun +workspaces|per-project (&optional root) + "Open a new workspace when switching to another project. + +Ensures the scratch (or dashboard) buffers are CDed into the project's root." + (when persp-mode + (let ((cwd default-directory)) + (+workspace-switch (projectile-project-name) t) + (switch-to-buffer (doom-fallback-buffer)) + (setq default-directory cwd) + (+workspace-message + (format "Switched to '%s' in new workspace" (+workspace-current-name)) + 'success)))) + (setq projectile-switch-project-action #'+workspaces|per-project) ;; only auto-save when real buffers are present (advice-add #'persp-asave-on-exit :around #'+workspaces*autosave-real-buffers) @@ -68,7 +80,10 @@ renamed.") (remove-hook 'delayed-warnings-hook #'display-delayed-warnings) (defun +workspaces|init (&optional frame) (unless persp-mode - (persp-mode +1)) + (persp-mode +1) + ;; Ensure `persp-kill-buffer-query-function' is last in kill-buffer-query-functions + (remove-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function) + (add-hook 'kill-buffer-query-functions 'persp-kill-buffer-query-function t)) (let ((frame (or frame (selected-frame)))) (unless noninteractive ;; The default perspective persp-mode makes (defined by diff --git a/modules/feature/workspaces/test/autoload-workspaces.el b/modules/feature/workspaces/test/autoload-workspaces.el index 3f1637aa2..ff69bfb67 100644 --- a/modules/feature/workspaces/test/autoload-workspaces.el +++ b/modules/feature/workspaces/test/autoload-workspaces.el @@ -3,7 +3,7 @@ (require! :feature workspaces) -(defmacro -with-workspace! (buffer-args &rest body) +(defmacro with-workspace!! (buffer-args &rest body) (declare (indent defun)) (let ((buffers (cl-loop for bsym in buffer-args @@ -27,14 +27,19 @@ ;; (def-test! init - (-with-workspace! () + (with-workspace!! () (should (equal (+workspace-current-name) +workspaces-main)))) -(def-test! advice - (should (advice-member-p #'+workspaces*auto-add-buffer #'switch-to-buffer))) +(def-test! auto-add-buffer-to-persp + (let ((a (generate-new-buffer "a"))) + (doom-set-buffer-real a t) + (with-workspace!! () + (should-not (+workspace-contains-buffer-p a)) + (switch-to-buffer a) + (should (+workspace-contains-buffer-p a))))) (def-test! current - (-with-workspace! () + (with-workspace!! () (should (equal (+workspace-current-name) +workspaces-main)) (should (+workspace-exists-p +workspaces-main)) (let ((workspace (+workspace-get +workspaces-main)) @@ -45,7 +50,7 @@ (should (equal workspace current-workspace))))) (def-test! workspace-list - (-with-workspace! () + (with-workspace!! () (should (equal (+workspace-list-names) (list (+workspace-current-name)))) (should (equal (+workspace-list) @@ -53,7 +58,7 @@ (def-test! workspace-crud "Creating, reading, updating and deleting workspaces." - (-with-workspace! () + (with-workspace!! () (let ((new-workspace-name "*new-test*") (renamed-workspace-name "*old-test*")) (should (+workspace-new new-workspace-name)) @@ -67,14 +72,14 @@ (should (= (length (+workspace-list-names)) 1))))) (def-test! workspace-switch - (-with-workspace! () + (with-workspace!! () (let ((new-workspace-name "*new-test*")) (should-error (+workspace-switch new-workspace-name)) (should (+workspace-switch new-workspace-name t)) (should (equal (+workspace-current-name) new-workspace-name))))) (def-test! buffer-list - (-with-workspace! (a b) + (with-workspace!! (a b) (let ((c (get-buffer-create "c")) (d (get-buffer-create "d"))) (should (+workspace-contains-buffer-p a)) diff --git a/modules/lang/cc/README.org b/modules/lang/cc/README.org index 767519879..ecb9d6f88 100644 --- a/modules/lang/cc/README.org +++ b/modules/lang/cc/README.org @@ -5,26 +5,36 @@ This module adds support for the C-family of languages: C, C++, and Objective-C. + Code completion (~company-irony~) + eldoc support (~irony-eldoc~) + Syntax-checking (~flycheck-irony~) -+ Code navigation (~irony~) ++ Code navigation (~rtags~) + File Templates ([[../../feature/file-templates/templates/c-mode][c-mode]], [[../../feature/file-templates/templates/c++-mode][c++-mode]]) + Snippets ([[https://github.com/hlissner/emacs-snippets/tree/master/cc-mode][cc-mode]], [[https://github.com/hlissner/emacs-snippets/tree/master/c-mode][c-mode]], [[https://github.com/hlissner/emacs-snippets/tree/master/c++-mode][c++-mode]]) + Several improvements to C++11 indentation and syntax highlighting. #+begin_quote -C contends with Haskell and Ruby for my favorite language. That said, it's more accurate to say I write C, but with two or three C++ features. +C contends with Haskell and Ruby for my favorite language. That said, it's more +accurate to say I write C, but a C++ feature or three. -The module provides nominal support for Objective-C, which I really only use to inspect generated glue code for iOS mobile apps. Otherwise, I prefer Swift. +The module provides nominal support for Objective-C, which I really only use to +inspect generated glue code for iOS mobile apps. Otherwise, I prefer Swift. #+end_quote * Table of Contents :TOC: - [[#install][Install]] - - [[#macos][MacOS]] - - [[#arch-linux][Arch Linux]] + - [[#irony-server][irony-server]] + - [[#rtags][rtags]] +- [[#configure][Configure]] + - [[#compile-settings][Compile settings]] * Install -This module requires ~irony-server~ for most of its features, which depends on ~cmake~ and ~libclang~. +This module requires: -** MacOS ++ irony-server ++ rtags + +** irony-server +Irony powers the code completion, eldoc and syntax checking systems. + +*** MacOS Due to linking issues, MacOS users must compile irony-server manually: #+BEGIN_SRC sh :tangle (if (doom-system-os 'macos) "yes") @@ -49,10 +59,46 @@ popd rm -rf irony-mode #+END_SRC -** Arch Linux +*** Arch Linux #+BEGIN_SRC sh :tangle (if (doom-system-os 'arch) "yes") sudo pacman --needed --noconfirm -S clang cmake #+END_SRC Then run ~M-x irony-install-server~ in Emacs. +** rtags +Code navigation requires an [[https://github.com/Andersbakken/rtags][rtags]] server (~rdm~) installed and running. This +should be available through your OS's package manager. + +This module will auto-start ~rdm~ when you open C/C++ buffers (so long as one +isn't already). If you prefer to run it yourself, outside of Emacs: + +#+BEGIN_SRC sh +rdm & +rc -J $PROJECT_ROOT # loads PROJECT_ROOT's compile_commands.json +#+END_SRC + +* Configure +** Compile settings +By default, a set of default compile settings are defined in +~+cc-default-compiler-options~ for C, C++ and Objective C. Irony, rtags and +flycheck will fall back to these. + +To make these tools aware of project specific build settings, you need a JSON +[[https://sarcasm.github.io/notes/dev/compilation-database.html#ninja][compilation database]] present (i.e. a ~compile_commands.json~ file). + +There are [[https://sarcasm.github.io/notes/dev/compilation-database.html][many ways to generate one]]. I use [[http://www.cmake.org/][CMake]] and [[https://github.com/rizsotto/Bear][bear]]: + +#+BEGIN_SRC sh +# For CMake projects +cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + +# For non-CMake projects +make clean +bear make +#+END_SRC + +#+begin_quote +Use ~M-x +cc/reload-compile-db~ to reload your compile db in an already-open +C/C++/ObjC buffer. +#+end_quote diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index 046ed54db..d40bcd104 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -1,5 +1,24 @@ ;;; lang/cc/autoload.el -*- lexical-binding: t; -*- +;;;###autoload +(defun +cc/reload-compile-db (&optional force-p) + "Reload the current project's JSON compilation database." + (interactive "P") + (unless (memq major-mode '(c-mode c++-mode objc-mode)) + (user-error "Not a C/C++/ObjC buffer")) + (unless (doom-project-has! "compile_commands.json") + (user-error "No compile_commands.json file")) + ;; first rtag + (when (and (featurep 'rtags) + rtags-enabled + (executable-find "rc")) + (with-temp-buffer + (message "Reloaded compile commands for rtags daemon") + (rtags-call-rc :silent t "-J" (doom-project-root)))) + ;; then irony + (when (and (featurep 'irony) irony-mode) + (+cc|irony-init-compile-options))) + ;;;###autoload (defun +cc*align-lambda-arglist (orig-fun &rest args) "Improve indentation of continued C++11 lambda function opened as argument." @@ -66,8 +85,8 @@ ;;;###autoload (defun +cc|irony-init-compile-options () "Initialize compiler options for irony-mode. It searches for the nearest -compilation database and initailizes it. If none was found, it uses -`+cc-c++-compiler-options'. +compilation database and initailizes it, otherwise falling back on +`+cc-default-compiler-options' and `+cc-default-include-paths'. See https://github.com/Sarcasm/irony-mode#compilation-database for details on compilation dbs." @@ -75,8 +94,8 @@ compilation dbs." (require 'irony-cdb) (unless (irony-cdb-autosetup-compile-options) (irony-cdb--update-compile-options - (append (delq nil (cdr-safe (assq major-mode +cc-compiler-options))) - (cl-loop for path in +cc-include-paths + (append (delq nil (cdr-safe (assq major-mode +cc-default-compiler-options))) + (cl-loop for path in +cc-default-include-paths nconc (list "-I" path))) (doom-project-root))))) diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index 9563ef452..f5b61e172 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -1,13 +1,11 @@ ;;; lang/cc/config.el --- c, c++, and obj-c -*- lexical-binding: t; -*- -(defvar +cc-include-paths (list "include/") - "A list of paths, relative to a project root, to search for headers in C/C++. -Paths can be absolute. +(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 JSON +compilation database.") -The purpose of this variable is to ensure syntax checkers and code-completion -knows where to look for headers.") - -(defvar +cc-compiler-options +(defvar +cc-default-compiler-options `((c-mode . nil) (c++-mode . ,(list "-std=c++11" ; use C++11 by default @@ -112,12 +110,16 @@ compilation database is present in the project.") :after cc-mode :commands irony-install-server :preface (setq irony-server-install-prefix (concat doom-etc-dir "irony-server/")) - :hook ((c-mode c++-mode objc-mode) . irony-mode) + :init + (defun +cc|init-irony-mode () + (when (memq major-mode '(c-mode c++-mode objc-mode)) + (irony-mode +1))) + (add-hook! (c-mode c++-mode objc-mode) #'+cc|init-irony-mode) :config (unless (file-directory-p irony-server-install-prefix) (warn "irony-mode: server isn't installed; run M-x irony-install-server")) ;; Initialize compilation database, if present. Otherwise, fall back on - ;; `+cc-compiler-options'. + ;; `+cc-default-compiler-options'. (add-hook 'irony-mode-hook #'+cc|irony-init-compile-options)) (def-package! irony-eldoc @@ -144,7 +146,8 @@ compilation database is present in the project.") ;; (def-package! cmake-mode - :mode "CMakeLists\\.txt$" + :mode "/CMakeLists\\.txt$" + :mode "\\.cmake\\$" :config (set! :company-backend 'cmake-mode '(company-cmake company-yasnippet))) @@ -163,19 +166,76 @@ compilation database is present in the project.") ;; -;; Plugins +;; Company plugins ;; -(when (featurep! :completion company) - (def-package! company-cmake :after cmake-mode) +(def-package! company-cmake + :when (featurep! :completion company) + :after cmake-mode) - (def-package! company-irony :after irony) +(def-package! company-irony + :when (featurep! :completion company) + :after irony) - (def-package! company-irony-c-headers :after company-irony) +(def-package! company-irony-c-headers + :when (featurep! :completion company) + :after company-irony) - (def-package! company-glsl - :after glsl-mode - :config - (if (executable-find "glslangValidator") - (warn "glsl-mode: couldn't find glslangValidator, disabling company-glsl") - (set! :company-backend 'glsl-mode '(company-glsl))))) +(def-package! company-glsl + :when (featurep! :completion company) + :after glsl-mode + :config + (if (executable-find "glslangValidator") + (warn "glsl-mode: couldn't find glslangValidator, disabling company-glsl") + (set! :company-backend 'glsl-mode '(company-glsl)))) + + +;; +;; Rtags Support +;; + +(def-package! rtags + :after cc-mode + :config + (setq rtags-autostart-diagnostics t + rtags-use-bookmarks nil + rtags-completions-enabled nil + ;; If not using ivy or helm to view results, use a pop-up window rather + ;; than displaying it in the current window... + rtags-results-buffer-other-window t + ;; ...and don't auto-jump to first match before making a selection. + rtags-jump-to-first-match nil) + + (let ((bins (cl-remove-if-not #'executable-find '("rdm" "rc")))) + (if (/= (length bins) 2) + (warn "cc-mode: couldn't find %s, disabling rtags support" bins) + (add-hook! (c-mode c++-mode) #'rtags-start-process-unless-running) + (set! :jump '(c-mode c++-mode) + :definition #'rtags-find-symbol-at-point + :references #'rtags-find-references-at-point))) + + (add-hook 'doom-cleanup-hook #'rtags-cancel-process) + (add-hook! kill-emacs (ignore-errors (rtags-cancel-process))) + + ;; Use rtags-imenu instead of imenu/counsel-imenu + (map! :map (c-mode-map c++-mode-map) [remap imenu] #'rtags-imenu) + + (add-hook 'rtags-jump-hook #'evil-set-jump) + (add-hook 'rtags-after-find-file-hook #'recenter)) + +(def-package! ivy-rtags + :when (featurep! :completion ivy) + :after rtags + :init + ;; NOTE Ivy integration breaks when rtags is byte-compiled with Emacs 26 or + ;; later, so we un-byte-compile it before we load it. + (eval-when-compile + (when (>= emacs-major-version 26) + (when-let* ((elc-file (locate-library "rtags.elc" t doom--package-load-path))) + (delete-file elc-file)))) + :config (setq rtags-display-result-backend 'ivy)) + +(def-package! helm-rtags + :when (featurep! :completion helm) + :after rtags + :config (setq rtags-display-result-backend 'helm)) diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el index 8109c1886..8151bc4fd 100644 --- a/modules/lang/cc/packages.el +++ b/modules/lang/cc/packages.el @@ -8,8 +8,8 @@ (package! glsl-mode) (package! irony) (package! irony-eldoc) -(package! opencl-mode) (package! modern-cpp-font-lock) +(package! opencl-mode) (when (featurep! :feature syntax-checker) (package! flycheck-irony)) @@ -19,3 +19,8 @@ (package! company-irony) (package! company-irony-c-headers)) +(package! rtags) +(when (featurep! :completion ivy) + (package! ivy-rtags)) +(when (featurep! :completion helm) + (package! helm-rtags)) diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 50549485c..949a401fc 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -16,7 +16,7 @@ "Evaluate a region and print it to the echo area (if one line long), otherwise to a pop up buffer." (require 'pp) - (let ((result (eval (read (buffer-substring-no-properties beg end)))) + (let ((result (eval (read (concat "(progn " (buffer-substring-no-properties beg end) "\n)")))) (buf (get-buffer-create "*doom eval*")) (inhibit-read-only t) lines) diff --git a/modules/lang/java/+meghanada.el b/modules/lang/java/+meghanada.el index 4b7fe91cf..70fc4d404 100644 --- a/modules/lang/java/+meghanada.el +++ b/modules/lang/java/+meghanada.el @@ -10,19 +10,12 @@ meghanada-use-eldoc t meghanada-use-auto-start t) - (add-hook 'java-mode-hook #'rainbow-delimiters-mode) - - ;; Setup on first use - (unless (bound-and-true-p byte-compile-current-file) - (meghanada-install-server) - (if (file-exists-p (meghanada--locate-server-jar)) - (add-hook! 'meghanada-mode-hook #'(flycheck-mode eldoc-mode)) - (warn "java-mode: meghanada-server not installed, java-mode will run with reduced functionality"))) - (set! :jump 'java-mode :definition #'meghanada-jump-declaration :references #'meghanada-reference) + (add-hook! 'meghanada-mode-hook #'(flycheck-mode eldoc-mode)) + ;; (def-menu! +java/refactor-menu "Refactoring commands for `java-mode' buffers." diff --git a/modules/lang/java/config.el b/modules/lang/java/config.el index e96aaee00..8194c7923 100644 --- a/modules/lang/java/config.el +++ b/modules/lang/java/config.el @@ -1,5 +1,7 @@ ;;; lang/java/config.el -*- lexical-binding: t; -*- +(add-hook 'java-mode-hook #'rainbow-delimiters-mode) + (cond ((featurep! +meghanada) (load! +meghanada)) ((featurep! +eclim) ; FIXME lang/java +eclim ;;(load! +eclim) diff --git a/modules/lang/org/+attach.el b/modules/lang/org/+attach.el index 94988d99e..564bb02e8 100644 --- a/modules/lang/org/+attach.el +++ b/modules/lang/org/+attach.el @@ -15,7 +15,7 @@ ;; + `+org-attach/url' ;; + :org [FILE/URL] -(defvar +org-attach-dir (expand-file-name ".attach/" +org-dir) +(defvar +org-attach-dir ".attach/" "Where to store attachments (relative to current org file).") @@ -30,7 +30,7 @@ (advice-add #'org-download-enable :override #'ignore) :config - (setq-default org-download-image-dir +org-attach-dir + (setq-default org-download-image-dir org-attach-directory org-download-heading-lvl nil org-download-timestamp "_%Y%m%d_%H%M%S") @@ -61,12 +61,12 @@ ;; (after! org - (setq org-attach-directory +org-attach-dir) + (setq org-attach-directory (expand-file-name +org-attach-dir +org-dir)) (push (car (last (split-string +org-attach-dir "/" t))) projectile-globally-ignored-directories) (after! recentf - (push (format "%s.+$" (regexp-quote +org-attach-dir)) + (push (format "%s.+$" (regexp-quote org-attach-directory)) recentf-exclude))) diff --git a/modules/lang/org/+capture.el b/modules/lang/org/+capture.el index b7c34a869..5debb99a4 100644 --- a/modules/lang/org/+capture.el +++ b/modules/lang/org/+capture.el @@ -10,16 +10,19 @@ ;; anywhere I can call org-capture (whether or not Emacs is open/running), ;; like, say, from qutebrowser, vimperator, dmenu or a global keybinding. +(defvar +org-default-todo-file "todo.org" + "TODO") + (defvar +org-default-notes-file "notes.org" "TODO") (defvar org-capture-templates '(("t" "Todo" entry - (file+headline (expand-file-name "todo.org" +org-dir) "Inbox") + (file+headline +org-default-todo-file "Inbox") "* [ ] %?\n%i" :prepend t :kill-buffer t) ("n" "Notes" entry - (file+headline org-default-notes-file "Inbox") + (file+headline +org-default-notes-file "Inbox") "* %u %?\n%i" :prepend t :kill-buffer t))) diff --git a/modules/lang/org/README.org b/modules/lang/org/README.org index e69de29bb..ecda00c6c 100644 --- a/modules/lang/org/README.org +++ b/modules/lang/org/README.org @@ -0,0 +1,59 @@ +#+TITLE: :lang org + +This module provides support for org-mode. + ++ A custom attachment system that keeps files in a centralized location. ++ Drag-and-drop support for images (with inline preview) and media files (drops + a file icon and a short link). ++ Executable code blocks with support for a variety of languages and tools, + including REST requests, SQL, google translate, plantuml, and matlab. ++ An org-capture workflow that works from outside Emacs (through the + =bin/org-capture= shell script). ++ Exported documents are saved to a centralized location. ++ A configuration for using org-mode for slide-show presentations, or exporting + org files to reveal.js slideshows. ++ (TODO) A static site generator based in org-mode and Emacs. + +#+begin_quote +org-mode is a beast, and Doom's most difficult module to maintain. And its most +important. This module is /highly/ opinionated and experimental; my foray into +learning org is a neverending quest. +#+end_quote + +* Table of Contents :TOC: +- [[#install][Install]] + - [[#macos][MacOS]] + - [[#arch-linux][Arch Linux]] +- [[#configuration][Configuration]] +- [[#usage][Usage]] +- [[#appendix][Appendix]] + +* Install +Org has no hard dependencies, but there are some things you'll need to make use of Org's more esoteric features. + ++ For inline LaTeX previews, you need ~latex~ and ~dvipng~. ++ To run babel code blocks, you need whatever dependencies those languages + need. It is recommended you enable the associated module in =lang/= and ensure + its dependencies are met. ++ The =+crm= module uses a sqlite database to manage your contacts, invoices, + and projects; this needs sqlite installed. + +** MacOS +#+BEGIN_SRC sh +brew cask install mactex +brew install sqlite +#+END_SRC + +** Arch Linux +#+BEGIN_SRC sh +sudo pacman --needed --noconfirm -S texlive-core texlive-bin texlive-science sqlite +#+END_SRC + +* Configuration +(Coming soon) + +* Usage +(Coming soon) + +* Appendix +(Coming soon) diff --git a/modules/lang/org/autoload/org-attach.el b/modules/lang/org/autoload/org-attach.el index 0cd9a7380..554549575 100644 --- a/modules/lang/org/autoload/org-attach.el +++ b/modules/lang/org/autoload/org-attach.el @@ -78,7 +78,7 @@ the cursor." (defun +org-attach-download-dnd (uri action) "TODO" (if (eq major-mode 'org-mode) - (+org-attach:url uri) + (+org-attach/uri uri) (let ((dnd-protocol-alist (rassq-delete-all '+org-attach-download-dnd (copy-alist dnd-protocol-alist)))) @@ -98,7 +98,7 @@ the cursor." (delete-region (match-beginning 0) (match-end 0)) (newline)) (cond ((image-type-from-file-name filename) - (when (file-in-directory-p filename +org-attach-dir) + (when (file-in-directory-p filename org-attach-directory) (setq filename (file-relative-name filename +org-dir))) (insert (concat (if (= org-download-image-html-width 0) @@ -113,9 +113,9 @@ the cursor." (t (insert (format "%s [[./%s][%s]] " - (org-attach--icon filename) + (+org-attach--icon filename) (file-relative-name filename buffer-file-name) - (file-name-nondirectory (directory-file-name rel-path))))))) + (file-name-nondirectory (directory-file-name filename))))))) ;;;###autoload (defun +org-attach*relative-to-attach-dir (orig-fn &rest args) @@ -124,9 +124,7 @@ the cursor." (let* ((context (save-match-data (org-element-context))) (file (org-link-unescape (org-element-property :path context))) (default-directory - (if (string-prefix-p - (concat "./" (car (last (split-string +org-attach-dir "/" t)))) - file) + (if (file-in-directory-p file org-attach-directory) +org-dir default-directory))) (apply orig-fn args)) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index 182afcf31..1f0096f43 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -28,8 +28,7 @@ :commands org-crypt-use-before-save-magic :config (setq org-tags-exclude-from-inheritance '("crypt") - org-crypt-key user-mail-address - epa-file-encrypt-to user-mail-address)) + org-crypt-key user-mail-address)) (def-package! org-bullets :commands org-bullets-mode) @@ -91,12 +90,13 @@ unfold to point on startup." (sp--looking-at-p "\\s-*]"))) ;; make delimiter auto-closing a little more conservative - (sp-with-modes 'org-mode - (sp-local-pair "*" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-at-bol-p)) - (sp-local-pair "_" nil :unless '(sp-point-after-word-p sp-point-before-word-p)) - (sp-local-pair "/" nil :unless '(sp-point-after-word-p sp-point-before-word-p +org-sp-point-in-checkbox-p)) - (sp-local-pair "~" nil :unless '(sp-point-after-word-p sp-point-before-word-p)) - (sp-local-pair "=" nil :unless '(sp-point-after-word-p sp-point-before-word-p)))) + (after! smartparens + (sp-with-modes 'org-mode + (sp-local-pair "*" nil :unless '(sp-point-after-word-p sp-point-before-word-p sp-point-at-bol-p)) + (sp-local-pair "_" nil :unless '(sp-point-after-word-p sp-point-before-word-p)) + (sp-local-pair "/" nil :unless '(sp-point-after-word-p sp-point-before-word-p +org-sp-point-in-checkbox-p)) + (sp-local-pair "~" nil :unless '(sp-point-after-word-p sp-point-before-word-p)) + (sp-local-pair "=" nil :unless '(sp-point-after-word-p sp-point-before-word-p))))) (defun +org|enable-auto-reformat-tables () "Realign tables exiting insert mode (`evil-mode')." diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 84999f094..37f729a8f 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -1,11 +1,12 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/org/packages.el -;; NOTE This is an insecure source, but unavoidable if we want org 9.0+ (which -;; this module requires). orgmode.org offers no secure access to this repo. If -;; this bothers you, comment out this `package!' block and download -;; org-plus-contrib from orgmode.org. -(package! org-plus-contrib :recipe (:fetcher git :url "http://orgmode.org/org-mode.git")) +(when (version< emacs-version "26.1") + ;; We want org 9.1.x, but the org packaged with Emacs 25.x and under is 8.x. + ;; The only secure (and reasonably trustworthy) source for this is via + ;; emacsmirror. Emacs 26+ comes with Org 9.1.4. + (package! org-plus-contrib + :recipe (:fetcher github :repo "emacsmirror/org" :files (:defaults "contrib/lisp/*.el")))) (package! org-bullets :recipe (:fetcher github :repo "hlissner/org-bullets")) (package! toc-org) diff --git a/modules/lang/org/test/autoload-org.el b/modules/lang/org/test/autoload-org.el index 2402f9aac..567f1abcf 100644 --- a/modules/lang/org/test/autoload-org.el +++ b/modules/lang/org/test/autoload-org.el @@ -1,40 +1,40 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/org/test/autoload-org.el -(defmacro should-org-buffer! (source expected &rest body) - `(should-buffer! ,source ,expected +(defmacro should-org-buffer!! (source expected &rest body) + `(should-buffer!! ,source ,expected (org-mode) ,@body)) - +;; ;; `+org/insert-item' (def-test! insert-item-h1 "Should append/prepend new first-level headers with an extra newline." - (should-org-buffer! ("* {0}Header") ("* Header\n\n* {|}") + (should-org-buffer!! ("* {0}Header") ("* Header\n\n* {|}") (+org/insert-item 'below)) - (should-org-buffer! ("* {0}Header") ("* {|}\n\n* Header") + (should-org-buffer!! ("* {0}Header") ("* {|}\n\n* Header") (+org/insert-item 'above))) (def-test! insert-item-h2 "Should append/prepend new second-level (and higher) headers without an extra newline." - (should-org-buffer! ("** {0}Header") ("** Header\n** {|}") + (should-org-buffer!! ("** {0}Header") ("** Header\n** {|}") (+org/insert-item 'below)) - (should-org-buffer! ("** {0}Header") ("** {|}\n** Header") + (should-org-buffer!! ("** {0}Header") ("** {|}\n** Header") (+org/insert-item 'above))) (def-test! insert-item-plain-list "Should append/prepend new second-level (and higher) headers without an extra newline." - (should-org-buffer! ("+ {0}List item") ("+ List item\n+ {|}") + (should-org-buffer!! ("+ {0}List item") ("+ List item\n+ {|}") (+org/insert-item 'below)) - (should-org-buffer! ("+ {0}List item" + (should-org-buffer!! ("+ {0}List item" " + Sub item") ("+ List item" " + Sub item" "+ {|}") (+org/insert-item 'below)) - (should-org-buffer! ("+ {0}List item" + (should-org-buffer!! ("+ {0}List item" "+ Next item") ("+ List item" "+ {|}" diff --git a/modules/lang/org/test/org.el b/modules/lang/org/test/org.el index e69f7a261..9b77e67db 100644 --- a/modules/lang/org/test/org.el +++ b/modules/lang/org/test/org.el @@ -5,3 +5,5 @@ (require! :lang org) (require 'org (locate-library "org" nil doom--package-load-path)) + +;; diff --git a/modules/lang/php/config.el b/modules/lang/php/config.el index 33b9d3373..3dfc1aa4e 100644 --- a/modules/lang/php/config.el +++ b/modules/lang/php/config.el @@ -84,8 +84,7 @@ :config (unless (executable-find "phpctags") (warn "php-mode: phpctags isn't installed, auto-completion will be gimped")) - - (setq ac-php-tags-path (concat doom-etc-dir "ac-php/"))) + (setq ac-php-tags-path (concat doom-cache-dir "ac-php/"))) ;; diff --git a/modules/private/README.org b/modules/private/README.org index 1046141fa..b9723dbc7 100644 --- a/modules/private/README.org +++ b/modules/private/README.org @@ -5,4 +5,4 @@ Use this directory to store your private configuration modules. Mine is included as a reference. I recommend you neither delete nor rename it, to avoid merge conflicts upstream. ----- -You'll find [[/wiki/Customization][more information about customizing Doom]] on the [[/wiki][wiki]]. +You'll find [[https://github.com/hlissner/doom-emacs/wiki/Customization][more information about customizing Doom]] on the [[https://github.com/hlissner/doom-emacs/wiki][wiki]]. diff --git a/modules/private/default/+bindings.el b/modules/private/default/+bindings.el new file mode 100644 index 000000000..a525a5a1c --- /dev/null +++ b/modules/private/default/+bindings.el @@ -0,0 +1,733 @@ +;;; private/default/+bindings.el -*- lexical-binding: t; -*- + +;; This files defines a Spacemacs-esque keybinding scheme + +(map! [remap evil-jump-to-tag] #'projectile-find-tag + [remap find-tag] #'projectile-find-tag + + ;; Ensure there are no conflicts + :nmvo doom-leader-key nil + :nmvo doom-localleader-key nil + + ;; --- Global keybindings --------------------------- + ;; Make M-x available everywhere + :gnvime "M-x" #'execute-extended-command + :gnvime "A-x" #'execute-extended-command + + ;; A little sandbox to run code in + :gnvime "M-;" #'eval-expression + :gnvime "M-:" #'doom/open-scratch-buffer + + ;; Text-scaling + "M-+" (λ! (text-scale-set 0)) + "M-=" #'text-scale-increase + "M--" #'text-scale-decrease + + ;; Simple window navigation/manipulation + "C-`" #'doom/popup-toggle + "C-~" #'doom/popup-raise + "M-t" #'+workspace/new + "M-T" #'+workspace/display + "M-w" #'delete-window + "M-W" #'+workspace/close-workspace-or-frame + "M-n" #'evil-buffer-new + "M-N" #'make-frame + "M-1" (λ! (+workspace/switch-to 0)) + "M-2" (λ! (+workspace/switch-to 1)) + "M-3" (λ! (+workspace/switch-to 2)) + "M-4" (λ! (+workspace/switch-to 3)) + "M-5" (λ! (+workspace/switch-to 4)) + "M-6" (λ! (+workspace/switch-to 5)) + "M-7" (λ! (+workspace/switch-to 6)) + "M-8" (λ! (+workspace/switch-to 7)) + "M-9" (λ! (+workspace/switch-to 8)) + "M-0" #'+workspace/switch-to-last + + ;; Other sensible, textmate-esque global bindings + :ne "M-r" #'+eval/buffer + :ne "M-R" #'+eval/region-and-replace + :ne "M-b" #'+eval/build + :ne "M-a" #'mark-whole-buffer + :ne "M-c" #'evil-yank + :ne "M-q" (if (daemonp) #'delete-frame #'save-buffers-kill-emacs) + :ne "M-f" #'swiper + :ne "C-M-f" #'doom/toggle-fullscreen + :n "M-s" #'save-buffer + :m "A-j" #'+default:multi-next-line + :m "A-k" #'+default:multi-previous-line + :nv "C-SPC" #'+evil:fold-toggle + :gnvimer "M-v" #'clipboard-yank + ;; Easier window navigation + :en "C-h" #'evil-window-left + :en "C-j" #'evil-window-down + :en "C-k" #'evil-window-up + :en "C-l" #'evil-window-right + + "C-x p" #'doom/other-popup + + + ;; --- ------------------------------------- + (:leader + :desc "Ex command" :nv ";" #'evil-ex + :desc "M-x" :nv ":" #'execute-extended-command + :desc "Pop up scratch buffer" :nv "x" #'doom/open-scratch-buffer + :desc "Org Capture" :nv "X" #'+org-capture/open + + ;; Most commonly used + :desc "Find file in project" :n "SPC" #'projectile-find-file + :desc "Switch workspace buffer" :n "," #'persp-switch-to-buffer + :desc "Switch buffer" :n "<" #'switch-to-buffer + :desc "Browse files" :n "." #'find-file + :desc "Toggle last popup" :n "~" #'doom/popup-toggle + :desc "Eval expression" :n "`" #'eval-expression + :desc "Blink cursor line" :n "DEL" #'+doom/blink-cursor + :desc "Jump to bookmark" :n "RET" #'bookmark-jump + + ;; C-u is used by evil + :desc "Universal argument" :n "u" #'universal-argument + :desc "window" :n "w" evil-window-map + + (:desc "previous..." :prefix "[" + :desc "Text size" :nv "[" #'text-scale-decrease + :desc "Buffer" :nv "b" #'doom/previous-buffer + :desc "Diff Hunk" :nv "d" #'git-gutter:previous-hunk + :desc "Todo" :nv "t" #'hl-todo-previous + :desc "Error" :nv "e" #'previous-error + :desc "Workspace" :nv "w" #'+workspace/switch-left + :desc "Smart jump" :nv "h" #'smart-backward + :desc "Spelling error" :nv "s" #'evil-prev-flyspell-error + :desc "Spelling correction" :n "S" #'flyspell-correct-previous-word-generic) + + (:desc "next..." :prefix "]" + :desc "Text size" :nv "]" #'text-scale-increase + :desc "Buffer" :nv "b" #'doom/next-buffer + :desc "Diff Hunk" :nv "d" #'git-gutter:next-hunk + :desc "Todo" :nv "t" #'hl-todo-next + :desc "Error" :nv "e" #'next-error + :desc "Workspace" :nv "w" #'+workspace/switch-right + :desc "Smart jump" :nv "l" #'smart-forward + :desc "Spelling error" :nv "s" #'evil-next-flyspell-error + :desc "Spelling correction" :n "S" #'flyspell-correct-word-generic) + + (:desc "search" :prefix "/" + :desc "Swiper" :nv "/" #'swiper + :desc "Imenu" :nv "i" #'imenu + :desc "Imenu across buffers" :nv "I" #'imenu-anywhere + :desc "Online providers" :nv "o" #'+jump/online-select) + + (:desc "workspace" :prefix "TAB" + :desc "Display tab bar" :n "TAB" #'+workspace/display + :desc "New workspace" :n "n" #'+workspace/new + :desc "Load workspace from file" :n "l" #'+workspace/load + :desc "Load last session" :n "L" (λ! (+workspace/load-session)) + :desc "Save workspace to file" :n "s" #'+workspace/save + :desc "Autosave current session" :n "S" #'+workspace/save-session + :desc "Switch workspace" :n "." #'+workspace/switch-to + :desc "Kill all buffers" :n "x" #'doom/kill-all-buffers + :desc "Delete session" :n "X" #'+workspace/kill-session + :desc "Delete this workspace" :n "d" #'+workspace/delete + :desc "Load session" :n "L" #'+workspace/load-session + :desc "Next workspace" :n "]" #'+workspace/switch-right + :desc "Previous workspace" :n "[" #'+workspace/switch-left + :desc "Switch to 1st workspace" :n "1" (λ! (+workspace/switch-to 0)) + :desc "Switch to 2nd workspace" :n "2" (λ! (+workspace/switch-to 1)) + :desc "Switch to 3rd workspace" :n "3" (λ! (+workspace/switch-to 2)) + :desc "Switch to 4th workspace" :n "4" (λ! (+workspace/switch-to 3)) + :desc "Switch to 5th workspace" :n "5" (λ! (+workspace/switch-to 4)) + :desc "Switch to 6th workspace" :n "6" (λ! (+workspace/switch-to 5)) + :desc "Switch to 7th workspace" :n "7" (λ! (+workspace/switch-to 6)) + :desc "Switch to 8th workspace" :n "8" (λ! (+workspace/switch-to 7)) + :desc "Switch to 9th workspace" :n "9" (λ! (+workspace/switch-to 8)) + :desc "Switch to last workspace" :n "0" #'+workspace/switch-to-last) + + (:desc "buffer" :prefix "b" + :desc "New empty buffer" :n "n" #'evil-buffer-new + :desc "Switch workspace buffer" :n "b" #'persp-switch-to-buffer + :desc "Switch buffer" :n "B" #'switch-to-buffer + :desc "Kill buffer" :n "k" #'doom/kill-this-buffer + :desc "Kill other buffers" :n "o" #'doom/kill-other-buffers + :desc "Save buffer" :n "s" #'save-buffer + :desc "Pop scratch buffer" :n "x" #'doom/open-scratch-buffer + :desc "Bury buffer" :n "z" #'bury-buffer + :desc "Next buffer" :n "]" #'doom/next-buffer + :desc "Previous buffer" :n "[" #'doom/previous-buffer + :desc "Sudo edit this file" :n "S" #'doom/sudo-this-file) + + (:desc "code" :prefix "c" + :desc "List errors" :n "x" #'flycheck-list-errors + :desc "Evaluate buffer/region" :n "e" #'+eval/buffer + :v "e" #'+eval/region + :desc "Evaluate & replace region" :nv "E" #'+eval:replace-region + :desc "Build tasks" :nv "b" #'+eval/build + :desc "Jump to definition" :n "d" #'+jump/definition + :desc "Jump to references" :n "D" #'+jump/references + :desc "Open REPL" :n "r" #'+eval/open-repl + :v "r" #'+eval:repl) + + (:desc "file" :prefix "f" + :desc "Find file" :n "." #'find-file + :desc "Sudo find file" :n ">" #'doom/sudo-find-file + :desc "Find file in project" :n "/" #'projectile-find-file + :desc "Find file from here" :n "?" #'counsel-file-jump + :desc "Find other file" :n "a" #'projectile-find-other-file + :desc "Open project editorconfig" :n "c" #'editorconfig-find-current-editorconfig + :desc "Find file in dotfiles" :n "d" #'+default/find-in-dotfiles + :desc "Browse dotfiles" :n "D" #'+default/browse-dotfiles + :desc "Find file in emacs.d" :n "e" #'+default/find-in-emacsd + :desc "Browse emacs.d" :n "E" #'+default/browse-emacsd + :desc "Recent files" :n "r" #'recentf-open-files + :desc "Recent project files" :n "R" #'projectile-recentf + :desc "Yank filename" :n "y" #'+default/yank-buffer-filename) + + (:desc "git" :prefix "g" + :desc "Git status" :n "S" #'magit-status + :desc "Git blame" :n "b" #'magit-blame + :desc "Git time machine" :n "t" #'git-timemachine-toggle + :desc "Git stage hunk" :n "s" #'git-gutter:stage-hunk + :desc "Git revert hunk" :n "r" #'git-gutter:revert-hunk + :desc "Git revert buffer" :n "R" #'vc-revert + :desc "List gists" :n "g" #'+gist:list + :desc "Next hunk" :nv "]" #'git-gutter:next-hunk + :desc "Previous hunk" :nv "[" #'git-gutter:previous-hunk) + + (:desc "help" :prefix "h" + :n "h" help-map + :desc "Apropos" :n "a" #'apropos + :desc "Reload theme" :n "R" #'doom//reload-theme + :desc "Find library" :n "l" #'find-library + :desc "Toggle Emacs log" :n "m" #'doom/popup-toggle-messages + :desc "Command log" :n "L" #'global-command-log-mode + :desc "Describe function" :n "f" #'describe-function + :desc "Describe key" :n "k" #'describe-key + :desc "Describe char" :n "c" #'describe-char + :desc "Describe mode" :n "M" #'describe-mode + :desc "Describe variable" :n "v" #'describe-variable + :desc "Describe face" :n "F" #'describe-face + :desc "Describe DOOM setting" :n "s" #'doom/describe-setting + :desc "Describe DOOM module" :n "d" #'doom/describe-module + :desc "Find definition" :n "." #'+jump/definition + :desc "Find references" :n "/" #'+jump/references + :desc "Find documentation" :n "h" #'+jump/documentation + :desc "What face" :n "'" #'doom/what-face + :desc "What minor modes" :n ";" #'doom/what-minor-mode + :desc "Info" :n "i" #'info + :desc "Toggle profiler" :n "p" #'doom/toggle-profiler) + + (:desc "insert" :prefix "i" + :desc "From kill-ring" :nv "y" #'counsel-yank-pop + :desc "From snippet" :nv "s" #'yas-insert-snippet) + + (:desc "notes" :prefix "n" + :desc "Find file in notes" :n "n" #'+default/find-in-notes + :desc "Browse notes" :n "N" #'+default/browse-notes + :desc "Org capture" :n "x" #'+org-capture/open + :desc "Browse mode notes" :n "m" #'+org/browse-notes-for-major-mode + :desc "Browse project notes" :n "p" #'+org/browse-notes-for-project) + + (:desc "open" :prefix "o" + :desc "Default browser" :n "b" #'browse-url-of-file + :desc "Debugger" :n "d" #'+debug/open + :desc "REPL" :n "r" #'+eval/open-repl + :v "r" #'+eval:repl + :desc "Neotree" :n "n" #'+neotree/toggle + :desc "Terminal" :n "t" #'+term/open-popup + :desc "Terminal in project" :n "T" #'+term/open-popup-in-project + + ;; applications + :desc "APP: elfeed" :n "E" #'=rss + :desc "APP: email" :n "M" #'=email + :desc "APP: twitter" :n "T" #'=twitter + :desc "APP: regex" :n "X" #'=regex + + ;; macos + (:when IS-MAC + :desc "Reveal in Finder" :n "o" #'+macos/reveal-in-finder + :desc "Reveal project in Finder" :n "O" #'+macos/reveal-project-in-finder + :desc "Send to Transmit" :n "u" #'+macos/send-to-transmit + :desc "Send project to Transmit" :n "U" #'+macos/send-project-to-transmit + :desc "Send to Launchbar" :n "l" #'+macos/send-to-launchbar + :desc "Send project to Launchbar" :n "L" #'+macos/send-project-to-launchbar)) + + (:desc "project" :prefix "p" + :desc "Browse project" :n "." #'+default/browse-project + :desc "Find file in project" :n "/" #'projectile-find-file + :desc "Run cmd in project root" :nv "!" #'projectile-run-shell-command-in-root + :desc "Switch project" :n "p" #'projectile-switch-project + :desc "Recent project files" :n "r" #'projectile-recentf + :desc "List project tasks" :n "t" #'+ivy/tasks + :desc "Pop term in project" :n "o" #'+term/open-popup-in-project + :desc "Invalidate cache" :n "x" #'projectile-invalidate-cache) + + (:desc "quit" :prefix "q" + :desc "Quit" :n "q" #'evil-save-and-quit + :desc "Quit (forget session)" :n "Q" #'+workspace/kill-session-and-quit) + + (:desc "remote" :prefix "r" + :desc "Upload local" :n "u" #'+upload/local + :desc "Upload local (force)" :n "U" (λ! (+upload/local t)) + :desc "Download remote" :n "d" #'+upload/remote-download + :desc "Diff local & remote" :n "D" #'+upload/diff + :desc "Browse remote files" :n "." #'+upload/browse + :desc "Detect remote changes" :n ">" #'+upload/check-remote) + + (:desc "snippets" :prefix "s" + :desc "New snippet" :n "n" #'yas-new-snippet + :desc "Insert snippet" :nv "i" #'yas-insert-snippet + :desc "Find snippet for mode" :n "s" #'yas-visit-snippet-file + :desc "Find snippet" :n "S" #'+default/find-in-snippets) + + (:desc "toggle" :prefix "t" + :desc "Flyspell" :n "s" #'flyspell-mode + :desc "Flycheck" :n "f" #'flycheck-mode + :desc "Line numbers" :n "l" #'doom/toggle-line-numbers + :desc "Fullscreen" :n "f" #'doom/toggle-fullscreen + :desc "Indent guides" :n "i" #'highlight-indentation-mode + :desc "Indent guides (column)" :n "I" #'highlight-indentation-current-column-mode + :desc "Impatient mode" :n "h" #'+impatient-mode/toggle + :desc "Big mode" :n "b" #'doom-big-font-mode + :desc "Evil goggles" :n "g" #'+evil-goggles/toggle)) + + + ;; --- Personal vim-esque bindings ------------------ + :n "zx" #'doom/kill-this-buffer + :n "ZX" #'bury-buffer + :n "]b" #'doom/next-buffer + :n "[b" #'doom/previous-buffer + :n "]w" #'+workspace/switch-right + :n "[w" #'+workspace/switch-left + :m "gt" #'+workspace/switch-right + :m "gT" #'+workspace/switch-left + :m "gd" #'+jump/definition + :m "gD" #'+jump/references + :m "gh" #'+jump/documentation + :n "gp" #'+evil/reselect-paste + :n "gr" #'+eval:region + :n "gR" #'+eval/buffer + :v "gR" #'+eval:replace-region + :v "@" #'+evil:macro-on-all-lines + :n "g@" #'+evil:macro-on-all-lines + ;; repeat in visual mode (FIXME buggy) + :v "." #'evil-repeat + ;; don't leave visual mode after shifting + :v "<" #'+evil/visual-dedent ; vnoremap < " #'+evil/visual-indent ; vnoremap > >gv + ;; paste from recent yank register (which isn't overwritten) + :v "C-p" "\"0p" + + (:map evil-window-map ; prefix "C-w" + ;; Navigation + "C-h" #'evil-window-left + "C-j" #'evil-window-down + "C-k" #'evil-window-up + "C-l" #'evil-window-right + "C-w" #'ace-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 + ;; Delete window + "c" #'+workspace/close-window-or-workspace + "C-C" #'ace-delete-window) + + + ;; --- Plugin bindings ------------------------------ + ;; auto-yasnippet + :i [C-tab] #'aya-expand + :nv [C-tab] #'aya-create + + ;; company-mode (vim-like omnicompletion) + :i "C-SPC" #'+company/complete + (:prefix "C-x" + :i "C-l" #'+company/whole-lines + :i "C-k" #'+company/dict-or-keywords + :i "C-f" #'company-files + :i "C-]" #'company-etags + :i "s" #'company-ispell + :i "C-s" #'company-yasnippet + :i "C-o" #'company-capf + :i "C-n" #'company-dabbrev-code + :i "C-p" #'+company/dabbrev-code-previous) + (:after company + (:map company-active-map + ;; Don't interfere with `evil-delete-backward-word' in insert mode + "C-w" nil + "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-s" #'company-search-candidates + "C-s" #'company-filter-candidates + "C-SPC" #'company-complete-common + "C-h" #'company-quickhelp-manual-begin + [tab] #'company-complete-common-or-cycle + [backtab] #'company-select-previous + [escape] (λ! (company-abort) (evil-normal-state 1))) + ;; Automatically applies to `company-filter-map' + (:map company-search-map + "C-n" #'company-search-repeat-forward + "C-p" #'company-search-repeat-backward + "C-s" (λ! (company-search-abort) (company-filter-candidates)) + [escape] #'company-search-abort)) + + ;; counsel + (:after counsel + (:map counsel-ag-map + [backtab] #'+ivy/wgrep-occur ; search/replace on results + "C-SPC" #'ivy-call-and-recenter ; preview + "M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action))) + + ;; evil-commentary + :n "gc" #'evil-commentary + + ;; evil-exchange + :n "gx" #'evil-exchange + + ;; evil-matchit + :nv [tab] #'+evil/matchit-or-toggle-fold + + ;; evil-magit + (:after evil-magit + :map (magit-status-mode-map magit-revision-mode-map) + :n "C-j" nil + :n "C-k" nil) + + ;; evil-mc + (:prefix "gz" + :nv "m" #'evil-mc-make-all-cursors + :nv "u" #'evil-mc-undo-all-cursors + :nv "z" #'+evil/mc-make-cursor-here + :nv "t" #'+evil/mc-toggle-cursors + :nv "n" #'evil-mc-make-and-goto-next-cursor + :nv "p" #'evil-mc-make-and-goto-prev-cursor + :nv "N" #'evil-mc-make-and-goto-last-cursor + :nv "P" #'evil-mc-make-and-goto-first-cursor + :nv "d" #'evil-mc-make-and-goto-next-match + :nv "D" #'evil-mc-make-and-goto-prev-match) + (:after evil-mc + :map evil-mc-key-map + :nv "C-n" #'evil-mc-make-and-goto-next-cursor + :nv "C-N" #'evil-mc-make-and-goto-last-cursor + :nv "C-p" #'evil-mc-make-and-goto-prev-cursor + :nv "C-P" #'evil-mc-make-and-goto-first-cursor) + + ;; evil-multiedit + :v "R" #'evil-multiedit-match-all + :n "M-d" #'evil-multiedit-match-symbol-and-next + :n "M-D" #'evil-multiedit-match-symbol-and-prev + :v "M-d" #'evil-multiedit-match-and-next + :v "M-D" #'evil-multiedit-match-and-prev + :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) + (:map (evil-multiedit-state-map evil-multiedit-insert-state-map) + "C-n" #'evil-multiedit-next + "C-p" #'evil-multiedit-prev)) + + ;; evil-snipe + (:after evil-snipe + (:after evil-easymotion + ;; Binding to switch to evil-easymotion/avy after a 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 + + ;; expand-region + :v "v" #'er/expand-region + :v "V" #'er/contract-region + + ;; flycheck + :m "]e" #'next-error + :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) + + ;; flyspell + :m "]S" #'flyspell-correct-word-generic + :m "[S" #'flyspell-correct-previous-word-generic + + ;; git-gutter + :m "]d" #'git-gutter:next-hunk + :m "[d" #'git-gutter:previous-hunk + + ;; git-timemachine + (:after git-timemachine + (:map git-timemachine-mode-map + :n "C-p" #'git-timemachine-show-previous-revision + :n "C-n" #'git-timemachine-show-next-revision + :n "[[" #'git-timemachine-show-previous-revision + :n "]]" #'git-timemachine-show-next-revision + :n "q" #'git-timemachine-quit + :n "gb" #'git-timemachine-blame)) + + ;; gist + (:after gist + :map gist-list-menu-mode-map + :n "RET" #'+gist/open-current + :n "b" #'gist-browse-current-url + :n "c" #'gist-add-buffer + :n "d" #'gist-kill-current + :n "f" #'gist-fork + :n "q" #'quit-window + :n "r" #'gist-list-reload + :n "s" #'gist-star + :n "S" #'gist-unstar + :n "y" #'gist-print-current-url) + + ;; helm + (:after helm + (:map helm-map + "ESC" nil + "C-S-n" #'helm-next-source + "C-S-p" #'helm-previous-source + "C-u" #'helm-delete-minibuffer-contents + "C-w" #'backward-kill-word + "C-r" #'evil-paste-from-register ; Evil registers in helm! Glorious! + "C-b" #'backward-word + [left] #'backward-char + [right] #'forward-char + [escape] #'helm-keyboard-quit + [tab] #'helm-execute-persistent-action) + + (:after helm-files + (:map helm-generic-files-map + :e "ESC" #'helm-keyboard-quit) + (:map helm-find-files-map + "C-w" #'helm-find-files-up-one-level + "TAB" #'helm-execute-persistent-action)) + + (:after helm-ag + (:map helm-ag-map + "" #'helm-ag-edit))) + + ;; hl-todo + :m "]t" #'hl-todo-next + :m "[t" #'hl-todo-previous + + ;; ivy + (:after ivy + :map ivy-minibuffer-map + [escape] #'keyboard-escape-quit + "C-SPC" #'ivy-call-and-recenter + "M-v" #'yank + "M-z" #'undo + "C-r" #'evil-paste-from-register + "C-k" #'ivy-previous-line + "C-j" #'ivy-next-line + "C-l" #'ivy-alt-done + "C-w" #'ivy-backward-kill-word + "C-u" #'ivy-kill-line + "C-b" #'backward-word + "C-f" #'forward-word) + + ;; neotree + (:after neotree + :map neotree-mode-map + :n "g" nil + :n [tab] #'neotree-quick-look + :n "RET" #'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) + + ;; realgud + (:after realgud + :map realgud:shortkey-mode-map + :n "j" #'evil-next-line + :n "k" #'evil-previous-line + :n "h" #'evil-backward-char + :n "l" #'evil-forward-char + :m "n" #'realgud:cmd-next + :m "b" #'realgud:cmd-break + :m "B" #'realgud:cmd-clear + :n "c" #'realgud:cmd-continue) + + ;; rotate-text + :n "!" #'rotate-text + + ;; smart-forward + :nv "K" #'smart-up + :m "g]" #'smart-forward + :m "g[" #'smart-backward + + ;; undo-tree -- undo/redo for visual regions + :v "C-u" #'undo-tree-undo + :v "C-r" #'undo-tree-redo + + ;; yasnippet + (:after yasnippet + (:map yas-keymap + "C-e" #'+snippets/goto-end-of-field + "C-a" #'+snippets/goto-start-of-field + "" #'+snippets/goto-end-of-field + "" #'+snippets/goto-start-of-field + "" #'+snippets/delete-to-start-of-field + [escape] #'evil-normal-state + [backspace] #'+snippets/delete-backward-char + [delete] #'+snippets/delete-forward-char-or-field) + (:map yas-minor-mode-map + :i "" yas-maybe-expand + :v "" #'+snippets/expand-on-region)) + + + ;; --- Major mode bindings -------------------------- + (:after markdown-mode + (:map markdown-mode-map + ;; fix conflicts with private bindings + "" nil + "" nil + "" nil)) + + + ;; --- Custom evil text-objects --------------------- + :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 "I" #'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 + + + ;; --- Built-in plugins ----------------------------- + (:after comint + ;; TAB auto-completion in term buffers + :map comint-mode-map [tab] #'company-complete) + + (:after debug + ;; For elisp debugging + :map debugger-mode-map + :n "RET" #'debug-help-follow + :n "e" #'debugger-eval-expression + :n "n" #'debugger-step-through + :n "c" #'debugger-continue) + + (:map help-mode-map + :n "[[" #'help-go-back + :n "]]" #'help-go-forward + :n "o" #'ace-link-help + :n "q" #'quit-window + :n "Q" #'+ivy-quit-and-resume) + + (:after vc-annotate + :map vc-annotate-mode-map + :n "q" #'kill-this-buffer + :n "d" #'vc-annotate-show-diff-revision-at-line + :n "D" #'vc-annotate-show-changeset-diff-revision-at-line + :n "SPC" #'vc-annotate-show-log-revision-at-line + :n "]]" #'vc-annotate-next-revision + :n "[[" #'vc-annotate-prev-revision + :n "TAB" #'vc-annotate-toggle-annotation-visibility + :n "RET" #'vc-annotate-find-revision-at-line)) + + +;; +;; Keybinding fixes +;; + +;; This section is dedicated to "fixing" certain keys so that they behave +;; properly, more like vim, or how I like it. + +(map! (:map input-decode-map + [S-iso-lefttab] [backtab] + (:unless window-system "TAB" [tab])) ; Fix TAB in terminal + + ;; I want C-a and C-e to be a little smarter. 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 and trailing whitespace before + ;; jumping to eol. + :i "C-a" #'doom/backward-to-bol-or-indent + :i "C-e" #'doom/forward-to-last-non-comment-or-eol + :i "C-u" #'doom/backward-kill-to-bol-and-indent + + ;; textmate-esque newline insertion + :i [M-return] #'evil-open-below + :i [S-M-return] #'evil-open-above + ;; textmate-esque deletion + [M-backspace] #'doom/backward-kill-to-bol-and-indent + :i [backspace] #'delete-backward-char + :i [M-backspace] #'doom/backward-kill-to-bol-and-indent + ;; Emacsien motions for insert mode + :i "C-b" #'backward-word + :i "C-f" #'forward-word + + ;; Highjacks space/backspace to: + ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) + ;; b) delete space-indented blocks intelligently + ;; c) do none of this when inside a string + :i "SPC" #'doom/inflate-space-maybe + :i [remap delete-backward-char] #'doom/deflate-space-maybe + :i [remap newline] #'doom/newline-and-indent + + (:after org + (:map org-mode-map + :i [remap doom/inflate-space-maybe] #'org-self-insert-command + :i "C-e" #'org-end-of-line + :i "C-a" #'org-beginning-of-line)) + + ;; Restore common editing keys (and ESC) in minibuffer + (:map (minibuffer-local-map + minibuffer-local-ns-map + minibuffer-local-completion-map + minibuffer-local-must-match-map + minibuffer-local-isearch-map + evil-ex-completion-map + evil-ex-search-keymap + read-expression-map) + [escape] #'abort-recursive-edit + "C-r" #'evil-paste-from-register + "C-a" #'move-beginning-of-line + "C-w" #'doom/minibuffer-kill-word + "C-u" #'doom/minibuffer-kill-line + "C-b" #'backward-word + "C-f" #'forward-word + "M-z" #'doom/minibuffer-undo) + + (:map messages-buffer-mode-map + "M-;" #'eval-expression + "A-;" #'eval-expression) + + (:map tabulated-list-mode-map + [remap evil-record-macro] #'doom/popup-close-maybe) + + (:after view + (:map view-mode-map "" #'View-quit-all))) diff --git a/modules/private/hlissner/+commands.el b/modules/private/default/+evil-commands.el similarity index 91% rename from modules/private/hlissner/+commands.el rename to modules/private/default/+evil-commands.el index 260a4d761..306ea8834 100644 --- a/modules/private/hlissner/+commands.el +++ b/modules/private/default/+evil-commands.el @@ -1,12 +1,12 @@ -;;; private/hlissner/+commands.el -*- lexical-binding: t; -*- +;;; private/default/+evil-commands.el -*- lexical-binding: t; -*- (defalias 'ex! 'evil-ex-define-cmd) -;;; Commands defined elsewhere + ;;; Commands defined elsewhere ;;(ex! "al[ign]" #'+evil:align) ;;(ex! "g[lobal]" #'+evil:global) -;;; Custom commands + ;;; Custom commands ;; Editing (ex! "@" #'+evil:macro-on-all-lines) ; TODO Test me (ex! "al[ign]" #'+evil:align) @@ -16,7 +16,6 @@ (ex! "iedit" #'evil-multiedit-ex-match) (ex! "na[rrow]" #'+evil:narrow-buffer) (ex! "retab" #'+evil:retab) - ;; External resources ;; TODO (ex! "db" #'doom:db) ;; TODO (ex! "dbu[se]" #'doom:db-select) @@ -29,7 +28,6 @@ (ex! "t[mux]" #'+tmux:run) ; send to tmux (ex! "tcd" #'+tmux:cd-here) ; cd to default-directory in tmux (ex! "x" #'doom/open-project-scratch-buffer) - ;; GIT (ex! "gist" #'+gist:send) ; send current buffer/region to gist (ex! "gistl" #'+gist:list) ; list gists by user @@ -40,20 +38,18 @@ (ex! "gunstage" #'magit-unstage) (ex! "gblame" #'magit-blame) (ex! "grevert" #'git-gutter:revert-hunk) - ;; Dealing with buffers -(ex! "clean[up]" #'doom/cleanup-buffers) +(ex! "clean[up]" #'doom/cleanup-session) (ex! "k[ill]" #'doom/kill-this-buffer) -(ex! "k[ill]all" #'+hlissner:kill-all-buffers) -(ex! "k[ill]m" #'+hlissner:kill-matching-buffers) +(ex! "k[ill]all" #'+default:kill-all-buffers) +(ex! "k[ill]m" #'+default:kill-matching-buffers) (ex! "k[ill]o" #'doom/kill-other-buffers) (ex! "l[ast]" #'doom/popup-restore) (ex! "m[sg]" #'view-echo-area-messages) (ex! "pop[up]" #'doom/popup-this-buffer) - ;; Project navigation (ex! "a" #'projectile-find-other-file) -(ex! "cd" #'+hlissner:cd) +(ex! "cd" #'+default:cd) (cond ((featurep! :completion ivy) (ex! "ag" #'+ivy:ag) (ex! "agc[wd]" #'+ivy:ag-cwd) @@ -68,17 +64,14 @@ (ex! "rgc[wd]" #'+helm:rg-cwd) (ex! "sw[oop]" #'+helm:swoop) (ex! "todo" #'+helm:todo))) - ;; Project tools (ex! "build" #'+eval/build) (ex! "debug" #'+debug/run) (ex! "er[rors]" #'flycheck-list-errors) - ;; File operations (ex! "cp" #'+evil:copy-this-file) (ex! "mv" #'+evil:move-this-file) (ex! "rm" #'+evil:delete-this-file) - ;; Sessions/tabs (ex! "sclear" #'+workspace/kill-session) (ex! "sl[oad]" #'+workspace:load-session) @@ -93,6 +86,6 @@ (ex! "tabr[ename]" #'+workspace:rename) (ex! "tabs" #'+workspace/display) (ex! "tabsave" #'+workspace:save) - ;; Org-mode (ex! "cap" #'+org-capture/dwim) + diff --git a/modules/private/default/autoload/default.el b/modules/private/default/autoload/default.el new file mode 100644 index 000000000..4381c044b --- /dev/null +++ b/modules/private/default/autoload/default.el @@ -0,0 +1,47 @@ +;; private/default/autoload/default.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +default/yank-buffer-filename () + "Copy the current buffer's path to the kill ring." + (interactive) + (if-let* ((filename (or buffer-file-name (bound-and-true-p list-buffers-directory)))) + (message (kill-new (abbreviate-file-name filename))) + (error "Couldn't find filename in current buffer"))) + +;;;###autoload +(defmacro +default--def-browse-in! (name dir) + (let ((prefix (cdr (doom-module-from-path (or load-file-name byte-compile-current-file))))) + `(defun ,(intern (format "%s/browse-%s" prefix name)) () + (interactive) + (doom-project-browse ,dir)))) + +;;;###autoload +(defmacro +default--def-find-in! (name dir) + (let ((prefix (cdr (doom-module-from-path (or load-file-name byte-compile-current-file))))) + `(defun ,(intern (format "+%s/find-in-%s" prefix name)) () + (interactive) + (doom-project-find-file ,dir)))) + + +;;;###autoload (autoload '+default/browse-project "private/default/autoload/default" nil t) +(+default--def-browse-in! project (doom-project-root)) + +;;;###autoload (autoload '+default/find-in-templates "private/default/autoload/default" nil t) +(+default--def-find-in! templates +file-templates-dir) +;;;###autoload (autoload '+default/browse-templates "private/default/autoload/default" nil t) +(+default--def-browse-in! templates +file-templates-dir) + +;;;###autoload (autoload '+default/find-in-emacsd "private/default/autoload/default" nil t) +(+default--def-find-in! emacsd doom-emacs-dir) +;;;###autoload (autoload '+default/browse-emacsd "private/default/autoload/default" nil t) +(+default--def-browse-in! emacsd doom-emacs-dir) + +;;;###autoload (autoload '+default/find-in-notes "private/default/autoload/default" nil t) +(+default--def-find-in! notes +org-dir) +;;;###autoload (autoload '+default/browse-notes "private/default/autoload/default" nil t) +(+default--def-browse-in! notes +org-dir) + +;;;###autoload (autoload '+default/find-in-snippets "private/default/autoload/default" nil t) +(+default--def-find-in! snippets +default-snippets-dir) +;; NOTE No need for a browse-snippets variant, use `yas-visit-snippet-file' + diff --git a/modules/private/default/autoload/evil.el b/modules/private/default/autoload/evil.el new file mode 100644 index 000000000..01a37f28c --- /dev/null +++ b/modules/private/default/autoload/evil.el @@ -0,0 +1,38 @@ +;; private/default/autoload/evil.el -*- lexical-binding: t; -*- +;;;###if (featurep! :feature evil) + +;;;###autoload (autoload '+default:multi-next-line "private/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)))) + (evil-line-move (* 6 (or count 1))))) + +;;;###autoload (autoload '+default:multi-previous-line "private/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)))) + (evil-line-move (- (* 6 (or count 1)))))) + +;;;###autoload (autoload '+default:cd "private/default/autoload/evil" nil t) +(evil-define-command +default:cd () + "Change `default-directory' with `cd'." + (interactive "") + (cd input)) + +;;;###autoload (autoload '+default:kill-all-buffers "private/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 "private/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/private/default/config.el b/modules/private/default/config.el new file mode 100644 index 000000000..4379a6955 --- /dev/null +++ b/modules/private/default/config.el @@ -0,0 +1,80 @@ +;;; private/default/config.el -*- lexical-binding: t; -*- + +(load! +bindings) + + +;; +;; Plugins +;; + +(def-package! emacs-snippets :after yasnippet) + + +;; +;; Config +;; + +(after! epa + (setq epa-file-encrypt-to (or epa-file-encrypt-to user-mail-address) + ;; With GPG 2.1, this forces gpg-agent to use the Emacs minibuffer to + ;; prompt for the key passphrase. + epa-pinentry-mode 'loopback)) + + +(when (featurep 'evil) + (load! +evil-commands) + + ;; Makes ; and , the universal repeat-keys in evil-mode + (defmacro do-repeat! (command next-func prev-func) + "Repeat motions with ;/," + (let ((fn-sym (intern (format "+evil*repeat-%s" command)))) + `(progn + (defun ,fn-sym (&rest _) + (define-key evil-motion-state-map (kbd ";") ',next-func) + (define-key evil-motion-state-map (kbd ",") ',prev-func)) + (advice-add #',command :before #',fn-sym)))) + + ;; n/N + (do-repeat! evil-ex-search-next evil-ex-search-next evil-ex-search-previous) + (do-repeat! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous) + (do-repeat! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous) + (do-repeat! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous) + + ;; f/F/t/T/s/S + (after! evil-snipe + (setq evil-snipe-repeat-keys nil + evil-snipe-override-evil-repeat-keys nil) ; causes problems with remapped ; + + (do-repeat! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse) + (do-repeat! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse)) + + ;; */# + (after! evil-visualstar + (do-repeat! evil-visualstar/begin-search-forward + evil-ex-search-next evil-ex-search-previous) + (do-repeat! evil-visualstar/begin-search-backward + evil-ex-search-previous evil-ex-search-next)) + + (after! evil-easymotion + (let ((prefix (concat doom-leader-key " /"))) + ;; NOTE `evilem-default-keybinds' unsets all other keys on the prefix (in + ;; motion state) + (evilem-default-keybindings prefix) + (evilem-define (kbd (concat prefix " n")) #'evil-ex-search-next) + (evilem-define (kbd (concat prefix " N")) #'evil-ex-search-previous) + (evilem-define (kbd (concat prefix " s")) #'evil-snipe-repeat + :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight))) + (evilem-define (kbd (concat prefix " S")) #'evil-snipe-repeat-reverse + :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) + :bind ((evil-snipe-scope 'buffer) + (evil-snipe-enable-highlight) + (evil-snipe-enable-incremental-highlight)))))) diff --git a/modules/private/default/packages.el b/modules/private/default/packages.el new file mode 100644 index 000000000..08571bf1e --- /dev/null +++ b/modules/private/default/packages.el @@ -0,0 +1,7 @@ +;; -*- no-byte-compile: t; -*- +;;; private/default/packages.el + +(package! emacs-snippets + :recipe (:fetcher github + :repo "hlissner/emacs-snippets" + :files ("*"))) diff --git a/modules/private/hlissner/+bindings.el b/modules/private/hlissner/+bindings.el deleted file mode 100644 index 7ad4807d2..000000000 --- a/modules/private/hlissner/+bindings.el +++ /dev/null @@ -1,795 +0,0 @@ -;;; private/hlissner/+bindings.el -*- lexical-binding: t; -*- - -(defmacro find-file-in! (path &optional project-p) - "Returns an interactive function for searching files." - `(lambda () (interactive) - (let ((default-directory ,path)) - (call-interactively - ',(command-remapping - (if project-p - #'projectile-find-file - #'find-file)))))) - -(map! - [remap evil-jump-to-tag] #'projectile-find-tag - [remap find-tag] #'projectile-find-tag - ;; ensure there are no conflicts - :nmvo doom-leader-key nil - :nmvo doom-localleader-key nil) - -(map! - ;; --- Global keybindings --------------------------- - ;; Make M-x available everywhere - :gnvime "M-x" #'execute-extended-command - :gnvime "A-x" #'execute-extended-command - ;; Emacs debug utilities - :gnvime "M-;" #'eval-expression - :gnvime "M-:" #'doom/open-scratch-buffer - ;; Text-scaling - "M-+" (λ! (text-scale-set 0)) - "M-=" #'text-scale-increase - "M--" #'text-scale-decrease - ;; Simple window navigation/manipulation - "C-`" #'doom/popup-toggle - "C-~" #'doom/popup-raise - "M-t" #'+workspace/new - "M-T" #'+workspace/display - "M-w" #'delete-window - "M-W" #'+workspace/close-workspace-or-frame - "M-n" #'evil-buffer-new - "M-N" #'make-frame - "M-1" (λ! (+workspace/switch-to 0)) - "M-2" (λ! (+workspace/switch-to 1)) - "M-3" (λ! (+workspace/switch-to 2)) - "M-4" (λ! (+workspace/switch-to 3)) - "M-5" (λ! (+workspace/switch-to 4)) - "M-6" (λ! (+workspace/switch-to 5)) - "M-7" (λ! (+workspace/switch-to 6)) - "M-8" (λ! (+workspace/switch-to 7)) - "M-9" (λ! (+workspace/switch-to 8)) - "M-0" #'+workspace/switch-to-last - ;; Other sensible, textmate-esque global bindings - :ne "M-r" #'+eval/buffer - :ne "M-R" #'+eval/region-and-replace - :ne "M-b" #'+eval/build - :ne "M-a" #'mark-whole-buffer - :ne "M-c" #'evil-yank - :ne "M-q" (if (daemonp) #'delete-frame #'save-buffers-kill-emacs) - :ne "M-f" #'swiper - :ne "C-M-f" #'doom/toggle-fullscreen - :n "M-s" #'save-buffer - :m "A-j" #'+hlissner:multi-next-line - :m "A-k" #'+hlissner:multi-previous-line - :nv "C-SPC" #'+evil:fold-toggle - :gnvimer "M-v" #'clipboard-yank - ;; Easier window navigation - :en "C-h" #'evil-window-left - :en "C-j" #'evil-window-down - :en "C-k" #'evil-window-up - :en "C-l" #'evil-window-right - - "C-x p" #'doom/other-popup - - - ;; --- ------------------------------------- - (:leader - :desc "Ex command" :nv ";" #'evil-ex - :desc "M-x" :nv ":" #'execute-extended-command - :desc "Pop up scratch buffer" :nv "x" #'doom/open-scratch-buffer - :desc "Org Capture" :nv "X" #'+org-capture/open - - ;; Most commonly used - :desc "Find file in project" :n "SPC" #'projectile-find-file - :desc "Switch workspace buffer" :n "," #'persp-switch-to-buffer - :desc "Switch buffer" :n "<" #'switch-to-buffer - :desc "Browse files" :n "." #'find-file - :desc "Toggle last popup" :n "~" #'doom/popup-toggle - :desc "Eval expression" :n "`" #'eval-expression - :desc "Blink cursor line" :n "DEL" #'+doom/blink-cursor - :desc "Jump to bookmark" :n "RET" #'bookmark-jump - - ;; C-u is used by evil - :desc "Universal argument" :n "u" #'universal-argument - :desc "window" :n "w" evil-window-map - - (:desc "previous..." :prefix "[" - :desc "Text size" :nv "[" #'text-scale-decrease - :desc "Buffer" :nv "b" #'doom/previous-buffer - :desc "Diff Hunk" :nv "d" #'git-gutter:previous-hunk - :desc "Todo" :nv "t" #'hl-todo-previous - :desc "Error" :nv "e" #'previous-error - :desc "Workspace" :nv "w" #'+workspace/switch-left - :desc "Smart jump" :nv "h" #'smart-backward - :desc "Spelling error" :nv "s" #'evil-prev-flyspell-error - :desc "Spelling correction" :n "S" #'flyspell-correct-previous-word-generic) - - (:desc "next..." :prefix "]" - :desc "Text size" :nv "]" #'text-scale-increase - :desc "Buffer" :nv "b" #'doom/next-buffer - :desc "Diff Hunk" :nv "d" #'git-gutter:next-hunk - :desc "Todo" :nv "t" #'hl-todo-next - :desc "Error" :nv "e" #'next-error - :desc "Workspace" :nv "w" #'+workspace/switch-right - :desc "Smart jump" :nv "l" #'smart-forward - :desc "Spelling error" :nv "s" #'evil-next-flyspell-error - :desc "Spelling correction" :n "S" #'flyspell-correct-word-generic) - - (:desc "search" :prefix "/" - :desc "Swiper" :nv "/" #'swiper - :desc "Imenu" :nv "i" #'imenu - :desc "Imenu across buffers" :nv "I" #'imenu-anywhere - :desc "Online providers" :nv "o" #'+jump/online-select) - - (:desc "workspace" :prefix "TAB" - :desc "Display tab bar" :n "TAB" #'+workspace/display - :desc "New workspace" :n "n" #'+workspace/new - :desc "Load workspace from file" :n "l" #'+workspace/load - :desc "Load last session" :n "L" (λ! (+workspace/load-session)) - :desc "Save workspace to file" :n "s" #'+workspace/save - :desc "Autosave current session" :n "S" #'+workspace/save-session - :desc "Switch workspace" :n "." #'+workspace/switch-to - :desc "Kill all buffers" :n "x" #'doom/kill-all-buffers - :desc "Delete session" :n "X" #'+workspace/kill-session - :desc "Delete this workspace" :n "d" #'+workspace/delete - :desc "Load session" :n "L" #'+workspace/load-session - :desc "Next workspace" :n "]" #'+workspace/switch-right - :desc "Previous workspace" :n "[" #'+workspace/switch-left - :desc "Switch to 1st workspace" :n "1" (λ! (+workspace/switch-to 0)) - :desc "Switch to 2nd workspace" :n "2" (λ! (+workspace/switch-to 1)) - :desc "Switch to 3rd workspace" :n "3" (λ! (+workspace/switch-to 2)) - :desc "Switch to 4th workspace" :n "4" (λ! (+workspace/switch-to 3)) - :desc "Switch to 5th workspace" :n "5" (λ! (+workspace/switch-to 4)) - :desc "Switch to 6th workspace" :n "6" (λ! (+workspace/switch-to 5)) - :desc "Switch to 7th workspace" :n "7" (λ! (+workspace/switch-to 6)) - :desc "Switch to 8th workspace" :n "8" (λ! (+workspace/switch-to 7)) - :desc "Switch to 9th workspace" :n "9" (λ! (+workspace/switch-to 8)) - :desc "Switch to last workspace" :n "0" #'+workspace/switch-to-last) - - (:desc "buffer" :prefix "b" - :desc "New empty buffer" :n "n" #'evil-buffer-new - :desc "Switch workspace buffer" :n "b" #'persp-switch-to-buffer - :desc "Switch buffer" :n "B" #'switch-to-buffer - :desc "Kill buffer" :n "k" #'doom/kill-this-buffer - :desc "Kill other buffers" :n "o" #'doom/kill-other-buffers - :desc "Save buffer" :n "s" #'save-buffer - :desc "Pop scratch buffer" :n "x" #'doom/open-scratch-buffer - :desc "Bury buffer" :n "z" #'bury-buffer - :desc "Next buffer" :n "]" #'doom/next-buffer - :desc "Previous buffer" :n "[" #'doom/previous-buffer - :desc "Sudo edit this file" :n "S" #'doom/sudo-this-file) - - (:desc "code" :prefix "c" - :desc "List errors" :n "x" #'flycheck-list-errors - :desc "Evaluate buffer/region" :n "e" #'+eval/buffer - :v "e" #'+eval/region - :desc "Evaluate & replace region" :nv "E" #'+eval:replace-region - :desc "Build tasks" :nv "b" #'+eval/build - :desc "Jump to definition" :n "d" #'+jump/definition - :desc "Jump to references" :n "D" #'+jump/references - :desc "Open REPL" :n "r" #'+eval/open-repl - :v "r" #'+eval:repl) - - (:desc "file" :prefix "f" - :desc "Find file" :n "." #'find-file - :desc "Sudo find file" :n ">" #'doom/sudo-find-file - :desc "Find file in project" :n "/" #'projectile-find-file - :desc "Find file from here" :n "?" #'counsel-file-jump - :desc "Find other file" :n "a" #'projectile-find-other-file - :desc "Open project editorconfig" :n "c" #'editorconfig-find-current-editorconfig - :desc "Find file in dotfiles" :n "d" #'+hlissner/find-in-dotfiles - :desc "Browse dotfiles" :n "D" #'+hlissner/browse-dotfiles - :desc "Find file in emacs.d" :n "e" #'+hlissner/find-in-emacsd - :desc "Browse emacs.d" :n "E" #'+hlissner/browse-emacsd - :desc "Recent files" :n "r" #'recentf-open-files - :desc "Recent project files" :n "R" #'projectile-recentf - :desc "Yank filename" :n "y" #'+hlissner/yank-buffer-filename) - - (:desc "git" :prefix "g" - :desc "Git status" :n "S" #'magit-status - :desc "Git blame" :n "b" #'magit-blame - :desc "Git time machine" :n "t" #'git-timemachine-toggle - :desc "Git stage hunk" :n "s" #'git-gutter:stage-hunk - :desc "Git revert hunk" :n "r" #'git-gutter:revert-hunk - :desc "Git revert buffer" :n "R" #'vc-revert - :desc "List gists" :n "g" #'+gist:list - :desc "Next hunk" :nv "]" #'git-gutter:next-hunk - :desc "Previous hunk" :nv "[" #'git-gutter:previous-hunk) - - (:desc "help" :prefix "h" - :n "h" help-map - :desc "Apropos" :n "a" #'apropos - :desc "Reload theme" :n "R" #'doom//reload-theme - :desc "Find library" :n "l" #'find-library - :desc "Toggle Emacs log" :n "m" #'doom/popup-toggle-messages - :desc "Command log" :n "L" #'global-command-log-mode - :desc "Describe function" :n "f" #'describe-function - :desc "Describe key" :n "k" #'describe-key - :desc "Describe char" :n "c" #'describe-char - :desc "Describe mode" :n "M" #'describe-mode - :desc "Describe variable" :n "v" #'describe-variable - :desc "Describe face" :n "F" #'describe-face - :desc "Describe DOOM setting" :n "s" #'doom/describe-setting - :desc "Describe DOOM module" :n "d" #'doom/describe-module - :desc "Find definition" :n "." #'+jump/definition - :desc "Find references" :n "/" #'+jump/references - :desc "Find documentation" :n "h" #'+jump/documentation - :desc "What face" :n "'" #'doom/what-face - :desc "What minor modes" :n ";" #'doom/what-minor-mode - :desc "Info" :n "i" #'info - :desc "Toggle profiler" :n "p" #'doom/toggle-profiler) - - (:desc "insert" :prefix "i" - :desc "From kill-ring" :nv "y" #'counsel-yank-pop - :desc "From snippet" :nv "s" #'yas-insert-snippet) - - (:desc "notes" :prefix "n" - :desc "Find file in notes" :n "n" #'+hlissner/find-in-notes - :desc "Browse notes" :n "N" #'+hlissner/browse-notes - :desc "Org capture" :n "x" #'+org-capture/open - :desc "Browse mode notes" :n "m" #'+org/browse-notes-for-major-mode - :desc "Browse project notes" :n "p" #'+org/browse-notes-for-project) - - (:desc "open" :prefix "o" - :desc "Default browser" :n "b" #'browse-url-of-file - :desc "Debugger" :n "d" #'+debug/open - :desc "REPL" :n "r" #'+eval/open-repl - :v "r" #'+eval:repl - :desc "Neotree" :n "n" #'+neotree/toggle - :desc "Terminal" :n "t" #'+term/open-popup - :desc "Terminal in project" :n "T" #'+term/open-popup-in-project - - ;; applications - :desc "APP: elfeed" :n "E" #'=rss - :desc "APP: email" :n "M" #'=email - :desc "APP: twitter" :n "T" #'=twitter - :desc "APP: regex" :n "X" #'=regex - - ;; macos - (:when IS-MAC - :desc "Reveal in Finder" :n "o" #'+macos/reveal-in-finder - :desc "Reveal project in Finder" :n "O" #'+macos/reveal-project-in-finder - :desc "Send to Transmit" :n "u" #'+macos/send-to-transmit - :desc "Send project to Transmit" :n "U" #'+macos/send-project-to-transmit - :desc "Send to Launchbar" :n "l" #'+macos/send-to-launchbar - :desc "Send project to Launchbar" :n "L" #'+macos/send-project-to-launchbar)) - - (:desc "project" :prefix "p" - :desc "Browse project" :n "." (find-file-in! (doom-project-root)) - :desc "Find file in project" :n "/" #'projectile-find-file - :desc "Run cmd in project root" :nv "!" #'projectile-run-shell-command-in-root - :desc "Switch project" :n "p" #'projectile-switch-project - :desc "Recent project files" :n "r" #'projectile-recentf - :desc "List project tasks" :n "t" #'+ivy/tasks - :desc "Pop term in project" :n "o" #'+term/open-popup-in-project - :desc "Invalidate cache" :n "x" #'projectile-invalidate-cache) - - (:desc "quit" :prefix "q" - :desc "Quit" :n "q" #'evil-save-and-quit - :desc "Quit (forget session)" :n "Q" #'+workspace/kill-session-and-quit) - - (:desc "remote" :prefix "r" - :desc "Upload local" :n "u" #'+upload/local - :desc "Upload local (force)" :n "U" (λ! (+upload/local t)) - :desc "Download remote" :n "d" #'+upload/remote-download - :desc "Diff local & remote" :n "D" #'+upload/diff - :desc "Browse remote files" :n "." #'+upload/browse - :desc "Detect remote changes" :n ">" #'+upload/check-remote) - - (:desc "snippets" :prefix "s" - :desc "New snippet" :n "n" #'yas-new-snippet - :desc "Insert snippet" :nv "i" #'yas-insert-snippet - :desc "Find snippet for mode" :n "s" #'yas-visit-snippet-file - :desc "Find snippet" :n "S" #'+hlissner/find-in-snippets) - - (:desc "toggle" :prefix "t" - :desc "Flyspell" :n "s" #'flyspell-mode - :desc "Flycheck" :n "f" #'flycheck-mode - :desc "Line numbers" :n "l" #'doom/toggle-line-numbers - :desc "Fullscreen" :n "f" #'doom/toggle-fullscreen - :desc "Indent guides" :n "i" #'highlight-indentation-mode - :desc "Indent guides (column)" :n "I" #'highlight-indentation-current-column-mode - :desc "Impatient mode" :n "h" #'+impatient-mode/toggle - :desc "Big mode" :n "b" #'doom-big-font-mode - :desc "Evil goggles" :n "g" #'+evil-goggles/toggle)) - - - ;; --- Personal vim-esque bindings ------------------ - :n "zx" #'doom/kill-this-buffer - :n "ZX" #'bury-buffer - :n "]b" #'doom/next-buffer - :n "[b" #'doom/previous-buffer - :n "]w" #'+workspace/switch-right - :n "[w" #'+workspace/switch-left - :m "gt" #'+workspace/switch-right - :m "gT" #'+workspace/switch-left - :m "gd" #'+jump/definition - :m "gD" #'+jump/references - :m "gh" #'+jump/documentation - :n "gp" #'+evil/reselect-paste - :n "gr" #'+eval:region - :n "gR" #'+eval/buffer - :v "gR" #'+eval:replace-region - :v "@" #'+evil:macro-on-all-lines - :n "g@" #'+evil:macro-on-all-lines - ;; repeat in visual mode (FIXME buggy) - :v "." #'evil-repeat - ;; don't leave visual mode after shifting - :v "<" #'+evil/visual-dedent ; vnoremap < " #'+evil/visual-indent ; vnoremap > >gv - ;; paste from recent yank register (which isn't overwritten) - :v "C-p" "\"0p" - - (:map evil-window-map ; prefix "C-w" - ;; Navigation - "C-h" #'evil-window-left - "C-j" #'evil-window-down - "C-k" #'evil-window-up - "C-l" #'evil-window-right - "C-w" #'ace-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 - ;; Delete window - "c" #'+workspace/close-window-or-workspace - "C-C" #'ace-delete-window) - - - ;; --- Plugin bindings ------------------------------ - ;; auto-yasnippet - :i [C-tab] #'aya-expand - :nv [C-tab] #'aya-create - - ;; company-mode (vim-like omnicompletion) - :i "C-SPC" #'+company/complete - (:prefix "C-x" - :i "C-l" #'+company/whole-lines - :i "C-k" #'+company/dict-or-keywords - :i "C-f" #'company-files - :i "C-]" #'company-etags - :i "s" #'company-ispell - :i "C-s" #'company-yasnippet - :i "C-o" #'company-capf - :i "C-n" #'company-dabbrev-code - :i "C-p" #'+company/dabbrev-code-previous) - (:after company - (:map company-active-map - ;; Don't interfere with `evil-delete-backward-word' in insert mode - "C-w" nil - "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-s" #'company-search-candidates - "C-s" #'company-filter-candidates - "C-SPC" #'company-complete-common - "C-h" #'company-quickhelp-manual-begin - [tab] #'company-complete-common-or-cycle - [backtab] #'company-select-previous - [escape] (λ! (company-abort) (evil-normal-state 1))) - ;; Automatically applies to `company-filter-map' - (:map company-search-map - "C-n" #'company-search-repeat-forward - "C-p" #'company-search-repeat-backward - "C-s" (λ! (company-search-abort) (company-filter-candidates)) - [escape] #'company-search-abort)) - - ;; counsel - (:after counsel - (:map counsel-ag-map - [backtab] #'+ivy/wgrep-occur ; search/replace on results - "C-SPC" #'ivy-call-and-recenter ; preview - "M-RET" (+ivy-do-action! #'+ivy-git-grep-other-window-action))) - - ;; evil-commentary - :n "gc" #'evil-commentary - - ;; evil-exchange - :n "gx" #'evil-exchange - - ;; evil-matchit - :nv [tab] #'+evil/matchit-or-toggle-fold - - ;; evil-magit - (:after evil-magit - :map (magit-status-mode-map magit-revision-mode-map) - :n "C-j" nil - :n "C-k" nil) - - ;; evil-mc - (:prefix "gz" - :nv "m" #'evil-mc-make-all-cursors - :nv "u" #'evil-mc-undo-all-cursors - :nv "z" #'+evil/mc-make-cursor-here - :nv "t" #'+evil/mc-toggle-cursors - :nv "n" #'evil-mc-make-and-goto-next-cursor - :nv "p" #'evil-mc-make-and-goto-prev-cursor - :nv "N" #'evil-mc-make-and-goto-last-cursor - :nv "P" #'evil-mc-make-and-goto-first-cursor - :nv "d" #'evil-mc-make-and-goto-next-match - :nv "D" #'evil-mc-make-and-goto-prev-match) - (:after evil-mc - :map evil-mc-key-map - :nv "C-n" #'evil-mc-make-and-goto-next-cursor - :nv "C-N" #'evil-mc-make-and-goto-last-cursor - :nv "C-p" #'evil-mc-make-and-goto-prev-cursor - :nv "C-P" #'evil-mc-make-and-goto-first-cursor) - - ;; evil-multiedit - :v "R" #'evil-multiedit-match-all - :n "M-d" #'evil-multiedit-match-symbol-and-next - :n "M-D" #'evil-multiedit-match-symbol-and-prev - :v "M-d" #'evil-multiedit-match-and-next - :v "M-D" #'evil-multiedit-match-and-prev - :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) - (:map (evil-multiedit-state-map evil-multiedit-insert-state-map) - "C-n" #'evil-multiedit-next - "C-p" #'evil-multiedit-prev)) - - ;; evil-snipe - (:after evil-snipe - ;; Binding to switch to evil-easymotion/avy after a 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 - - ;; expand-region - :v "v" #'er/expand-region - :v "V" #'er/contract-region - - ;; flycheck - :m "]e" #'next-error - :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) - - ;; flyspell - :m "]S" #'flyspell-correct-word-generic - :m "[S" #'flyspell-correct-previous-word-generic - - ;; git-gutter - :m "]d" #'git-gutter:next-hunk - :m "[d" #'git-gutter:previous-hunk - - ;; git-timemachine - (:after git-timemachine - (:map git-timemachine-mode-map - :n "C-p" #'git-timemachine-show-previous-revision - :n "C-n" #'git-timemachine-show-next-revision - :n "[[" #'git-timemachine-show-previous-revision - :n "]]" #'git-timemachine-show-next-revision - :n "q" #'git-timemachine-quit - :n "gb" #'git-timemachine-blame)) - - ;; gist - (:after gist - :map gist-list-menu-mode-map - :n "RET" #'+gist/open-current - :n "b" #'gist-browse-current-url - :n "c" #'gist-add-buffer - :n "d" #'gist-kill-current - :n "f" #'gist-fork - :n "q" #'quit-window - :n "r" #'gist-list-reload - :n "s" #'gist-star - :n "S" #'gist-unstar - :n "y" #'gist-print-current-url) - - ;; helm - (:after helm - (:map helm-map - "ESC" nil - "C-S-n" #'helm-next-source - "C-S-p" #'helm-previous-source - "C-u" #'helm-delete-minibuffer-contents - "C-w" #'backward-kill-word - "C-r" #'evil-paste-from-register ; Evil registers in helm! Glorious! - "C-b" #'backward-word - [left] #'backward-char - [right] #'forward-char - [escape] #'helm-keyboard-quit - [tab] #'helm-execute-persistent-action) - - (:after helm-files - (:map helm-generic-files-map - :e "ESC" #'helm-keyboard-quit) - (:map helm-find-files-map - "C-w" #'helm-find-files-up-one-level - "TAB" #'helm-execute-persistent-action)) - - (:after helm-ag - (:map helm-ag-map - "" #'helm-ag-edit))) - - ;; hl-todo - :m "]t" #'hl-todo-next - :m "[t" #'hl-todo-previous - - ;; ivy - (:after ivy - :map ivy-minibuffer-map - [escape] #'keyboard-escape-quit - "C-SPC" #'ivy-call-and-recenter - "TAB" #'ivy-partial - "M-v" #'yank - "M-z" #'undo - "C-r" #'evil-paste-from-register - "C-k" #'ivy-previous-line - "C-j" #'ivy-next-line - "C-l" #'ivy-alt-done - "C-w" #'ivy-backward-kill-word - "C-u" #'ivy-kill-line - "C-b" #'backward-word - "C-f" #'forward-word) - - ;; neotree - (:after neotree - :map neotree-mode-map - :n "g" nil - :n [tab] #'neotree-quick-look - :n "RET" #'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) - - ;; realgud - (:after realgud - :map realgud:shortkey-mode-map - :n "j" #'evil-next-line - :n "k" #'evil-previous-line - :n "h" #'evil-backward-char - :n "l" #'evil-forward-char - :m "n" #'realgud:cmd-next - :m "b" #'realgud:cmd-break - :m "B" #'realgud:cmd-clear - :n "c" #'realgud:cmd-continue) - - ;; rotate-text - :n "!" #'rotate-text - - ;; smart-forward - :nv "K" #'smart-up - :m "g]" #'smart-forward - :m "g[" #'smart-backward - - ;; undo-tree -- undo/redo for visual regions - :v "C-u" #'undo-tree-undo - :v "C-r" #'undo-tree-redo - - ;; yasnippet - (:after yasnippet - (:map yas-keymap - "C-e" #'+snippets/goto-end-of-field - "C-a" #'+snippets/goto-start-of-field - "" #'+snippets/goto-end-of-field - "" #'+snippets/goto-start-of-field - "" #'+snippets/delete-to-start-of-field - [escape] #'evil-normal-state - [backspace] #'+snippets/delete-backward-char - [delete] #'+snippets/delete-forward-char-or-field) - (:map yas-minor-mode-map - :i "" yas-maybe-expand - :v "" #'+snippets/expand-on-region)) - - - ;; --- Major mode bindings -------------------------- - (:after markdown-mode - (:map markdown-mode-map - ;; fix conflicts with private bindings - "" nil - "" nil - "" nil)) - - - ;; --- Custom evil text-objects --------------------- - :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 "I" #'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 - - - ;; --- Built-in plugins ----------------------------- - (:after comint - ;; TAB auto-completion in term buffers - :map comint-mode-map [tab] #'company-complete) - - (:after debug - ;; For elisp debugging - :map debugger-mode-map - :n "RET" #'debug-help-follow - :n "e" #'debugger-eval-expression - :n "n" #'debugger-step-through - :n "c" #'debugger-continue) - - (:map help-mode-map - :n "[[" #'help-go-back - :n "]]" #'help-go-forward - :n "o" #'ace-link-help - :n "q" #'quit-window - :n "Q" #'+ivy-quit-and-resume) - - (:after vc-annotate - :map vc-annotate-mode-map - :n "q" #'kill-this-buffer - :n "d" #'vc-annotate-show-diff-revision-at-line - :n "D" #'vc-annotate-show-changeset-diff-revision-at-line - :n "SPC" #'vc-annotate-show-log-revision-at-line - :n "]]" #'vc-annotate-next-revision - :n "[[" #'vc-annotate-prev-revision - :n "TAB" #'vc-annotate-toggle-annotation-visibility - :n "RET" #'vc-annotate-find-revision-at-line)) - - -;; --- Custom key functionality --------------------- -(defmacro do-repeat! (command next-func prev-func) - "Repeat motions with ;/," - (let ((fn-sym (intern (format "+evil*repeat-%s" command)))) - `(progn - (defun ,fn-sym (&rest _) - (define-key evil-motion-state-map (kbd ";") ',next-func) - (define-key evil-motion-state-map (kbd ",") ',prev-func)) - (advice-add #',command :before #',fn-sym)))) - -;; n/N -(do-repeat! evil-ex-search-next evil-ex-search-next evil-ex-search-previous) -(do-repeat! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous) -(do-repeat! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous) -(do-repeat! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous) - -;; f/F/t/T/s/S -(after! evil-snipe - (setq evil-snipe-repeat-keys nil - evil-snipe-override-evil-repeat-keys nil) ; causes problems with remapped ; - - (do-repeat! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse) - (do-repeat! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse)) - -;; */# -(after! evil-visualstar - (do-repeat! evil-visualstar/begin-search-forward - evil-ex-search-next evil-ex-search-previous) - (do-repeat! evil-visualstar/begin-search-backward - evil-ex-search-previous evil-ex-search-next)) - -;; evil-easymotion -(after! evil-easymotion - (let ((prefix (concat doom-leader-key " /"))) - ;; NOTE `evilem-default-keybinds' unsets all other keys on the prefix (in - ;; motion state) - (evilem-default-keybindings prefix) - (evilem-define (kbd (concat prefix " n")) #'evil-ex-search-next) - (evilem-define (kbd (concat prefix " N")) #'evil-ex-search-previous) - (evilem-define (kbd (concat prefix " s")) #'evil-snipe-repeat - :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) - :bind ((evil-snipe-scope 'buffer) - (evil-snipe-enable-highlight) - (evil-snipe-enable-incremental-highlight))) - (evilem-define (kbd (concat prefix " S")) #'evil-snipe-repeat-reverse - :pre-hook (save-excursion (call-interactively #'evil-snipe-s)) - :bind ((evil-snipe-scope 'buffer) - (evil-snipe-enable-highlight) - (evil-snipe-enable-incremental-highlight))))) - - -;; -;; Keybinding fixes -;; - -;; This section is dedicated to "fixing" certain keys so that they behave -;; properly, more like vim, or how I like it. - -(map! (:map input-decode-map - [S-iso-lefttab] [backtab] - (:unless window-system "TAB" [tab])) ; Fix TAB in terminal - - ;; I want C-a and C-e to be a little smarter. 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 and trailing whitespace before - ;; jumping to eol. - :i "C-a" #'doom/backward-to-bol-or-indent - :i "C-e" #'doom/forward-to-last-non-comment-or-eol - :i "C-u" #'doom/backward-kill-to-bol-and-indent - - ;; textmate-esque newline insertion - :i [M-return] #'evil-open-below - :i [S-M-return] #'evil-open-above - ;; textmate-esque deletion - [M-backspace] #'doom/backward-kill-to-bol-and-indent - :i [backspace] #'delete-backward-char - :i [M-backspace] #'doom/backward-kill-to-bol-and-indent - ;; Emacsien motions for insert mode - :i "C-b" #'backward-word - :i "C-f" #'forward-word - - ;; Highjacks space/backspace to: - ;; a) balance spaces inside brackets/parentheses ( | ) -> (|) - ;; b) delete space-indented blocks intelligently - ;; c) do none of this when inside a string - :i "SPC" #'doom/inflate-space-maybe - :i [remap delete-backward-char] #'doom/deflate-space-maybe - :i [remap newline] #'doom/newline-and-indent - - (:after org - (:map org-mode-map - :i [remap doom/inflate-space-maybe] #'org-self-insert-command - :i "C-e" #'org-end-of-line - :i "C-a" #'org-beginning-of-line)) - - ;; Restore common editing keys (and ESC) in minibuffer - (:map (minibuffer-local-map - minibuffer-local-ns-map - minibuffer-local-completion-map - minibuffer-local-must-match-map - minibuffer-local-isearch-map - evil-ex-completion-map - evil-ex-search-keymap - read-expression-map) - [escape] #'abort-recursive-edit - "C-r" #'evil-paste-from-register - "C-a" #'move-beginning-of-line - "C-w" #'doom/minibuffer-kill-word - "C-u" #'doom/minibuffer-kill-line - "C-b" #'backward-word - "C-f" #'forward-word - "M-z" #'doom/minibuffer-undo) - - (:map messages-buffer-mode-map - "M-;" #'eval-expression - "A-;" #'eval-expression) - - (:map tabulated-list-mode-map - [remap evil-record-macro] #'doom/popup-close-maybe) - - (:after view - (:map view-mode-map "" #'View-quit-all))) diff --git a/modules/private/hlissner/.gitignore b/modules/private/hlissner/.gitignore deleted file mode 100644 index d31426933..000000000 --- a/modules/private/hlissner/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -snippets -.authinfo.gpg diff --git a/modules/private/hlissner/autoload/evil.el b/modules/private/hlissner/autoload/evil.el deleted file mode 100644 index 0845af2d2..000000000 --- a/modules/private/hlissner/autoload/evil.el +++ /dev/null @@ -1,37 +0,0 @@ -;;; private/hlissner/autoload/evil.el -*- lexical-binding: t; -*- -;;;###if (featurep! :feature evil) - -;;;###autoload (autoload '+hlissner:multi-next-line "private/hlissner/autoload/evil" nil t) -(evil-define-motion +hlissner:multi-next-line (count) - "Move down 6 lines." - :type line - (let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode)))) - (evil-line-move (* 6 (or count 1))))) - -;;;###autoload (autoload '+hlissner:multi-previous-line "private/hlissner/autoload/evil" nil t) -(evil-define-motion +hlissner:multi-previous-line (count) - "Move up 6 lines." - :type line - (let ((line-move-visual (or visual-line-mode (derived-mode-p 'text-mode)))) - (evil-line-move (- (* 6 (or count 1)))))) - -;;;###autoload (autoload '+hlissner:cd "private/hlissner/autoload/evil" nil t) -(evil-define-command +hlissner:cd () - "Change `default-directory' with `cd'." - (interactive "") - (cd input)) - -;;;###autoload (autoload '+hlissner:kill-all-buffers "private/hlissner/autoload/evil" nil t) -(evil-define-command +hlissner: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 '+hlissner:kill-matching-buffers "private/hlissner/autoload/evil" nil t) -(evil-define-command +hlissner: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/private/hlissner/autoload/hlissner.el b/modules/private/hlissner/autoload/hlissner.el deleted file mode 100644 index 236676fc2..000000000 --- a/modules/private/hlissner/autoload/hlissner.el +++ /dev/null @@ -1,53 +0,0 @@ -;;; private/hlissner/autoload/hlissner.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun +hlissner/install-snippets () - "Install my snippets from https://github.com/hlissner/emacs-snippets into -private/hlissner/snippets." - (interactive) - (doom-fetch :github "hlissner/emacs-snippets" - (expand-file-name "snippets" (doom-module-path :private 'hlissner)))) - -;;;###autoload -(defun +hlissner/yank-buffer-filename () - "Copy the current buffer's path to the kill ring." - (interactive) - (if-let* ((filename (or buffer-file-name (bound-and-true-p list-buffers-directory)))) - (message (kill-new (abbreviate-file-name filename))) - (error "Couldn't find filename in current buffer"))) - -(defmacro +hlissner-def-finder! (name dir) - "Define a pair of find-file and browse functions." - `(progn - (defun ,(intern (format "+hlissner/find-in-%s" name)) () - (interactive) - (let ((default-directory ,dir) - projectile-project-name - projectile-require-project-root - projectile-cached-buffer-file-name - projectile-cached-project-root) - (call-interactively (command-remapping #'projectile-find-file)))) - (defun ,(intern (format "+hlissner/browse-%s" name)) () - (interactive) - (let ((default-directory ,dir)) - (call-interactively (command-remapping #'find-file)))))) - -;;;###autoload (autoload '+hlissner/find-in-templates "private/hlissner/autoload/hlissner" nil t) -;;;###autoload (autoload '+hlissner/browse-templates "private/hlissner/autoload/hlissner" nil t) -(+hlissner-def-finder! templates +file-templates-dir) - -;;;###autoload (autoload '+hlissner/find-in-snippets "private/hlissner/autoload/hlissner" nil t) -;;;###autoload (autoload '+hlissner/browse-snippets "private/hlissner/autoload/hlissner" nil t) -(+hlissner-def-finder! snippets +hlissner-snippets-dir) - -;;;###autoload (autoload '+hlissner/find-in-dotfiles "private/hlissner/autoload/hlissner" nil t) -;;;###autoload (autoload '+hlissner/browse-dotfiles "private/hlissner/autoload/hlissner" nil t) -(+hlissner-def-finder! dotfiles (expand-file-name ".dotfiles" "~")) - -;;;###autoload (autoload '+hlissner/find-in-emacsd "private/hlissner/autoload/hlissner" nil t) -;;;###autoload (autoload '+hlissner/browse-emacsd "private/hlissner/autoload/hlissner" nil t) -(+hlissner-def-finder! emacsd doom-emacs-dir) - -;;;###autoload (autoload '+hlissner/find-in-notes "private/hlissner/autoload/hlissner" nil t) -;;;###autoload (autoload '+hlissner/browse-notes "private/hlissner/autoload/hlissner" nil t) -(+hlissner-def-finder! notes +org-dir) diff --git a/modules/private/hlissner/config.el b/modules/private/hlissner/config.el deleted file mode 100644 index b034ad8c9..000000000 --- a/modules/private/hlissner/config.el +++ /dev/null @@ -1,108 +0,0 @@ -;;; private/hlissner/config.el -*- lexical-binding: t; -*- - -(defvar +hlissner-dir (file-name-directory load-file-name)) -(defvar +hlissner-snippets-dir (expand-file-name "snippets/" +hlissner-dir)) - -;; -(when (featurep! :feature evil) - (load! +bindings) ; my key bindings - (load! +commands)) ; my custom ex commands - - -;; -;; Global config -;; - -(setq epa-file-encrypt-to user-mail-address - auth-sources (list (expand-file-name ".authinfo.gpg" +hlissner-dir)) - +doom-modeline-buffer-file-name-style 'relative-from-project) - -(defun +hlissner*no-authinfo-for-tramp (orig-fn &rest args) - "Don't look into .authinfo for local sudo TRAMP buffers." - (let ((auth-sources (if (equal tramp-current-method "sudo") nil auth-sources))) - (apply orig-fn args))) -(advice-add #'tramp-read-passwd :around #'+hlissner*no-authinfo-for-tramp) - - -;; -;; Modules -;; - -(after! smartparens - ;; Auto-close more conservatively and expand braces on RET - (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)) - (sp-pair "{" nil :post-handlers '(("||\n[i]" "RET") ("| " " ")) - :unless '(sp-point-before-word-p sp-point-before-same-p)) - (sp-pair "(" nil :post-handlers '(("||\n[i]" "RET") ("| " " ")) - :unless '(sp-point-before-word-p sp-point-before-same-p)) - (sp-pair "[" nil :post-handlers '(("| " " ")) - :unless '(sp-point-before-word-p sp-point-before-same-p))) - -;; feature/evil -(after! evil-mc - ;; Make evil-mc resume its cursors when I switch to insert mode - (add-hook! 'evil-mc-before-cursors-created - (add-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors nil t)) - (add-hook! 'evil-mc-after-cursors-deleted - (remove-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors t))) - -;; feature/snippets -(after! yasnippet - ;; Don't use default snippets, use mine. - (setq yas-snippet-dirs - (append (list '+hlissner-snippets-dir) - (delq 'yas-installed-snippets-dir yas-snippet-dirs)))) - -;; completion/helm -(after! helm - ;; Hide header lines in helm. I don't like them - (set-face-attribute 'helm-source-header nil :height 0.1)) - -;; lang/org -(after! org-bullets - ;; The standard unicode characters are usually misaligned depending on the - ;; font. This bugs me. Personally, markdown #-marks for headlines are more - ;; elegant, so we use those. - (setq org-bullets-bullet-list '("#"))) - -;; app/irc -(after! circe - (setq +irc-notifications-watch-strings '("v0" "vnought" "hlissner")) - - (set! :irc "irc.snoonet.org" - `(:tls t - :nick "v0" - :port 6697 - :sasl-username ,(+pass-get-user "irc/snoonet.org") - :sasl-password ,(+pass-get-secret "irc/snoonet.org") - :channels (:after-auth "#ynought")))) - -;; app/email -(after! mu4e - (setq smtpmail-stream-type 'starttls - smtpmail-default-smtp-server "smtp.gmail.com" - smtpmail-smtp-server "smtp.gmail.com" - smtpmail-smtp-service 587) - - (set! :email "gmail.com" - '((mu4e-sent-folder . "/gmail.com/Sent Mail") - (mu4e-drafts-folder . "/gmail.com/Drafts") - (mu4e-trash-folder . "/gmail.com/Trash") - (mu4e-refile-folder . "/gmail.com/All Mail") - (smtpmail-smtp-user . "hlissner") - (user-mail-address . "hlissner@gmail.com") - (mu4e-compose-signature . "---\nHenrik"))) - - (set! :email "lissner.net" - '((mu4e-sent-folder . "/lissner.net/Sent Mail") - (mu4e-drafts-folder . "/lissner.net/Drafts") - (mu4e-trash-folder . "/lissner.net/Trash") - (mu4e-refile-folder . "/lissner.net/All Mail") - (smtpmail-smtp-user . "henrik@lissner.net") - (user-mail-address . "henrik@lissner.net") - (mu4e-compose-signature . "---\nHenrik Lissner")) - t)) diff --git a/modules/private/hlissner/init.el b/modules/private/hlissner/init.el deleted file mode 100644 index 37eb5c43a..000000000 --- a/modules/private/hlissner/init.el +++ /dev/null @@ -1,52 +0,0 @@ -;;; private/hlissner/init.el -*- lexical-binding: t; -*- - -;; An extra measure to prevent the flash of unstyled mode-line while Emacs is -;; booting up (when Doom is byte-compiled). -(setq-default mode-line-format nil) - -;; I've swapped these keys on my keyboard -(setq x-super-keysym 'alt - x-alt-keysym 'meta) - -(setq user-mail-address "henrik@lissner.net" - user-full-name "Henrik Lissner") - -(setq doom-big-font (font-spec :family "Fira Mono" :size 19)) - -(pcase (system-name) - ("proteus" - ;; My 13" laptop has very little screen estate, so we use a bitmap font - ;; there. For Doom, that means we need to take up less space! - (setq-default line-spacing 1) - - (setq doom-font (font-spec :family "kakwa kakwafont" :size 12) - doom-variable-pitch-font (font-spec :family "kakwa kakwafont") - doom-unicode-font (font-spec :family "UT Ttyp0") - ;; ui/doom-modeline - +doom-modeline-height 23 - ;; `doom-themes' - doom-neotree-enable-variable-pitch nil - doom-neotree-project-size 1.2 - doom-neotree-line-spacing 0 - doom-neotree-folder-size 1.0 - doom-neotree-chevron-size 0.6) - (add-hook! doom-big-font-mode - (setq +doom-modeline-height (if doom-big-font-mode 37 23))) - - ;; No highlighted bar in the mode-line - (setq +doom-modeline-bar-width 1) - (custom-set-faces '(doom-modeline-bar ((t (:background nil)))))) - - (_ - ;; Everywhere else, I have big displays and plenty of space, so use it! - (setq doom-font (font-spec :family "Fira Mono" :size 12) - doom-variable-pitch-font (font-spec :family "Fira Sans") - doom-unicode-font (font-spec :family "DejaVu Sans Mono") - org-ellipsis "  ") - - ;; Fira Mono doesn't have italics, so we highlight it instead. - (add-hook! doom-post-init - (set-face-attribute 'italic nil :weight 'ultra-light :foreground "#ffffff")) - - (add-hook! doom-big-font-mode - (setq +doom-modeline-height (if doom-big-font-mode 37 29))))) diff --git a/modules/tools/neotree/README.org b/modules/tools/neotree/README.org index 280a97c3d..efbf410c1 100644 --- a/modules/tools/neotree/README.org +++ b/modules/tools/neotree/README.org @@ -1,7 +1,9 @@ #+TITLE: :evil neotree -This module brings a side panel for browsing project files, inspired by vim's NERDTree. +This module brings a side panel for browsing project files, inspired by vim's +NERDTree. #+begin_quote -Sure, there's dired and projectile, but sometimes I'd like a bird's eye view of a project. +Sure, there's dired and projectile, but sometimes I'd like a bird's eye view of +a project. #+end_quote diff --git a/modules/tools/neotree/autoload.el b/modules/tools/neotree/autoload.el index 3a3c87ee9..c24ebe1ff 100644 --- a/modules/tools/neotree/autoload.el +++ b/modules/tools/neotree/autoload.el @@ -9,13 +9,15 @@ (require 'neotree) (cond ((and (neo-global--window-exists-p) (get-buffer-window neo-buffer-name t)) - (neotree-find path project-root)) + (neotree-find path project-root) + (neotree-refresh)) ((not (and (neo-global--window-exists-p) (equal (file-truename (neo-global--with-buffer neo-buffer--start-node)) (file-truename project-root)))) (neotree-dir project-root) (neotree-find path project-root)) - (t (neotree-find path project-root))))) + (t + (neotree-find path project-root))))) ;;;###autoload (defun +neotree/collapse-or-up () diff --git a/modules/tools/password-store/autoload.el b/modules/tools/password-store/autoload.el index 6705fa6a8..f22d18393 100644 --- a/modules/tools/password-store/autoload.el +++ b/modules/tools/password-store/autoload.el @@ -37,7 +37,7 @@ (and (or (string-match-p "https?://" url) (error "Field for %s doesn't look like an url" item)) (browse-url url)) - (error "Username not found."))) + (error "url not found."))) (defun +pass-ivy-action--get-field (item) (let* ((data (+pass--get-entry item)) diff --git a/modules/tools/password-store/test/autoload-pass.el b/modules/tools/password-store/test/autoload-pass.el index 083c2f0d8..d9d190097 100644 --- a/modules/tools/password-store/test/autoload-pass.el +++ b/modules/tools/password-store/test/autoload-pass.el @@ -3,7 +3,7 @@ (load! ../autoload) -(defmacro -with-passwords! (buffer-args &rest body) +(defmacro with-passwords!! (buffer-args &rest body) (declare (indent defun)) `(cl-letf (((symbol-function '+pass--get-entry) @@ -18,7 +18,7 @@ ;; (def-test! get-field - (-with-passwords! + (with-passwords!! (should (equal (+pass-get-field "fake/source" "login") "HL2532-GANDI")) (should (equal (+pass-get-field "fake/source" "email") @@ -29,14 +29,14 @@ "henrik@lissner.net")))) (def-test! missing-fields-return-nil - (-with-passwords! + (with-passwords!! (should-not (+pass-get-field "fake/source" '("x" "y" "z"))))) (def-test! missing-entries-throw-error - (-with-passwords! + (with-passwords!! (should-error (+pass-get-field "nonexistent/source" "login")))) (def-test! get-login - (-with-passwords! + (with-passwords!! (should (equal (+pass-get-user "fake/source") "HL2532-GANDI")) (should (equal (+pass-get-secret "fake/source") "defuse-account-gad")))) diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index 1929f5438..debcc73fc 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -3,20 +3,38 @@ (defvar +doom-dashboard-name " *doom*" "The name to use for the dashboard buffer.") -(defvar +doom-dashboard-inhibit-refresh nil - "If non-nil, the doom buffer won't be refreshed.") - (defvar +doom-dashboard-widgets '(banner shortmenu loaded) "List of widgets to display in a blank scratch buffer.") +(defvar +doom-dashboard-inhibit-refresh nil + "If non-nil, the doom buffer won't be refreshed.") + (defvar +doom-dashboard-inhibit-functions () - "A list of functions that determine whether to inhibit the dashboard the + "A list of functions that determine whether to inhibit the dashboard from loading.") +(defvar +doom-dashboard-pwd-policy 'last-project + "The policy to use when setting the `default-directory' in the dashboard. + +Possible values: + + 'last-project the `doom-project-root' of the last open buffer + 'last the `default-directory' of the last open buffer + a FUNCTION a function run with the `default-directory' of the last + open buffer, that returns a directory path + a STRING a fixed path + nil `default-directory' will never change") + +;; +(defvar +doom-dashboard--last-cwd nil) (defvar +doom-dashboard--width 80) (defvar +doom-dashboard--height 0) (defvar +doom-dashboard--old-fringe-indicator fringe-indicator-alist) +(defvar all-the-icons-scale-factor) +(defvar all-the-icons-default-adjust) + +;; (setq doom-fallback-buffer +doom-dashboard-name) @@ -25,6 +43,8 @@ loading.") "Major mode for the DOOM dashboard buffer." (read-only-mode +1) (setq truncate-lines t) + (setq-local whitespace-style nil) + (setq-local show-trailing-whitespace nil) (cl-loop for (car . _cdr) in fringe-indicator-alist collect (cons car nil) into alist finally do (setq fringe-indicator-alist alist))) @@ -51,15 +71,25 @@ loading.") if in a GUI/non-daemon session." (add-hook 'window-configuration-change-hook #'+doom-dashboard-reload) (add-hook 'focus-in-hook #'+doom-dashboard-reload) - (add-hook 'kill-buffer-query-functions #'+doom-dashboard|kill-buffer-query-fn) + (add-hook 'kill-buffer-query-functions #'+doom-dashboard|reload-on-kill) (when (and (display-graphic-p) (not (daemonp))) (let ((default-directory doom-emacs-dir)) (+doom-dashboard/open (selected-frame))))) -(defun +doom-dashboard|kill-buffer-query-fn () - (or (not (+doom-dashboard-p)) - (ignore (let (+doom-dashboard-inhibit-refresh) - (ignore-errors (+doom-dashboard-reload)))))) +(defun +doom-dashboard|reload-on-kill () + "If this isn't a dashboard buffer, move along, but record its +`default-directory' if the buffer is real. See `doom-real-buffer-p' for an +explanation for what 'real' means. + +If this is the dashboard buffer, reload the dashboard." + (or (unless (+doom-dashboard-p) + (when (doom-real-buffer-p) + (setq +doom-dashboard--last-cwd default-directory) + (+doom-dashboard-update-pwd)) + t) + (ignore + (let (+doom-dashboard-inhibit-refresh) + (ignore-errors (+doom-dashboard-reload)))))) (defun +doom-dashboard|make-frame (frame) "Reload the dashboard after a brief pause. This is necessary for new frames, @@ -85,55 +115,87 @@ whose dimensions may not be fully initialized by the time this is run." (+doom-dashboard-reload))) (setq +doom-dashboard-inhibit-refresh nil))) +; (defun +doom-dashboard-p (&optional buffer) "Returns t if BUFFER is the dashboard buffer." - (let ((buffer (or buffer (current-buffer)))) - (and (buffer-live-p buffer) - (eq buffer (doom-fallback-buffer))))) + (eq (or buffer (current-buffer)) + (doom-fallback-buffer))) -(defun +doom-dashboard-center (len s) - (concat (make-string (ceiling (max 0 (- len (length s))) 2) ? ) - s)) +(defun +doom-dashboard-update-pwd () + "TODO" + (with-current-buffer (doom-fallback-buffer) + (cd (or (+doom-dashboard--get-pwd) + default-directory)))) -(defun +doom-dashboard-reload (&optional dir) +(defun +doom-dashboard-reload (&optional force) "Update the DOOM scratch buffer (or create it, if it doesn't exist)." - (when (get-buffer-window (doom-fallback-buffer)) - (unless (or +doom-dashboard-inhibit-refresh - (window-minibuffer-p (frame-selected-window))) - (let ((old-pwd (or dir default-directory)) - (fallback-buffer (doom-fallback-buffer))) - (with-current-buffer fallback-buffer - (with-silent-modifications - (unless (eq major-mode '+doom-dashboard-mode) - (+doom-dashboard-mode)) - (erase-buffer) - (setq default-directory old-pwd) - (let ((+doom-dashboard--height (window-height (get-buffer-window fallback-buffer))) - (lines 1) - content) - (with-temp-buffer - (dolist (widget-name +doom-dashboard-widgets) - (funcall (intern (format "doom-dashboard-widget--%s" widget-name))) - (insert "\n")) - (setq content (buffer-string) - lines (count-lines (point-min) (point-max)))) - (insert (make-string (max 0 (- (/ +doom-dashboard--height 2) - (/ lines 2))) - ?\n) - content)) - (unless (button-at (point)) - (goto-char (next-button (point-min)))))))) + (let ((fallback-buffer (doom-fallback-buffer))) + (when (or (and after-init-time + (not +doom-dashboard-inhibit-refresh) + (get-buffer-window fallback-buffer) + (not (window-minibuffer-p (frame-selected-window)))) + force) + (with-current-buffer fallback-buffer + (+doom-dashboard-update-pwd) + (with-silent-modifications + (unless (eq major-mode '+doom-dashboard-mode) + (+doom-dashboard-mode)) + (erase-buffer) + (let ((+doom-dashboard--height + (window-height (get-buffer-window fallback-buffer))) + (lines 1) + content) + (with-temp-buffer + (dolist (widget-name +doom-dashboard-widgets) + (funcall (intern (format "doom-dashboard-widget--%s" widget-name))) + (insert "\n")) + (setq content (buffer-string) + lines (count-lines (point-min) (point-max)))) + (insert (make-string (max 0 (- (/ +doom-dashboard--height 2) + (/ lines 2))) + ?\n) + content)) + (unless (button-at (point)) + (goto-char (next-button (point-min))))))) ;; Update all dashboard windows - (dolist (win (get-buffer-window-list (doom-fallback-buffer) nil t)) + (dolist (win (get-buffer-window-list fallback-buffer nil t)) (set-window-fringes win 0 0) (set-window-margins win (max 0 (/ (- (window-total-width win) +doom-dashboard--width) 2))))) t) +;; helpers +(defun +doom-dashboard--center (len s) + (concat (make-string (ceiling (max 0 (- len (length s))) 2) ? ) + s)) + +(defun +doom-dashboard--get-pwd () + (let ((lastcwd +doom-dashboard--last-cwd) + (policy +doom-dashboard-pwd-policy)) + (cond ((null policy) + default-directory) + ((stringp policy) + (expand-file-name policy lastcwd)) + ((functionp policy) + (funcall policy lastcwd)) + ((null lastcwd) + default-directory) + ((eq policy 'last-project) + (let ((cwd default-directory) + (default-directory lastcwd)) + (if (doom-project-p) + (doom-project-root) + cwd))) + ((eq policy 'last) + lastcwd) + (t + (warn "`+doom-dashboard-pwd-policy' has an invalid value of '%s'" + policy))))) + ;; widgets (defun doom-dashboard-widget--banner () (mapc (lambda (line) - (insert (propertize (+doom-dashboard-center +doom-dashboard--width line) + (insert (propertize (+doom-dashboard--center +doom-dashboard--width line) 'face 'font-lock-comment-face) " ") (insert "\n")) '("================= =============== =============== ======== ========" @@ -160,17 +222,15 @@ whose dimensions may not be fully initialized by the time this is run." (insert "\n" (propertize - (+doom-dashboard-center + (+doom-dashboard--center +doom-dashboard--width (format "Loaded %d packages in %d modules in %.02fs" - (- (length load-path) (length doom--base-load-path)) + (length doom--package-load-path) (hash-table-size doom-modules) (if (floatp doom-init-time) doom-init-time 0.0))) 'face 'font-lock-comment-face) "\n")) -(defvar all-the-icons-scale-factor) -(defvar all-the-icons-default-adjust) (defun doom-dashboard-widget--shortmenu () (let ((all-the-icons-scale-factor 1.45) (all-the-icons-default-adjust -0.02)) @@ -184,7 +244,7 @@ whose dimensions may not be fully initialized by the time this is run." (propertize (concat " " label) 'face 'font-lock-keyword-face)) 'action `(lambda (_) ,fn) 'follow-link t) - (+doom-dashboard-center (- +doom-dashboard--width 2) (buffer-string))) + (+doom-dashboard--center (- +doom-dashboard--width 2) (buffer-string))) "\n\n")))) `(("Homepage" "mark-github" (browse-url "https://github.com/hlissner/doom-emacs")) diff --git a/modules/ui/doom-dashboard/test/doom-dashboard.el b/modules/ui/doom-dashboard/test/doom-dashboard.el new file mode 100644 index 000000000..b08188837 --- /dev/null +++ b/modules/ui/doom-dashboard/test/doom-dashboard.el @@ -0,0 +1,51 @@ +;; -*- no-byte-compile: t; -*- +;;; ui/doom-dashboard/test/doom-dashboard.el + +(require! :ui doom-dashboard) +(+doom-dashboard|init) + +(defun -dashboard-test-pwd (spec file) + (let ((kill-buffer-query-functions '(+doom-dashboard|reload-on-kill)) + (+doom-dashboard-pwd-policy (car spec)) + (fallback-buffer (doom-fallback-buffer)) + +doom-dashboard--last-cwd + projectile-enable-caching) + (with-temp-buffer + (setq buffer-file-name file + default-directory (file-name-directory file) + doom-real-buffer-p t)) + (should +doom-dashboard--last-cwd) + (+doom-dashboard-update-pwd) + (should (equal (buffer-local-value 'default-directory fallback-buffer) + (cdr spec))))) + +;; +(def-test! dashboard-p + (let ((fallback-buffer (doom-fallback-buffer))) + (should (equal (buffer-name fallback-buffer) +doom-dashboard-name)) + (should (+doom-dashboard-p fallback-buffer)) + (with-current-buffer fallback-buffer + (should (+doom-dashboard-p))))) + +(def-test! get-pwd + (let ((default-directory doom-core-dir) + (+doom-dashboard--last-cwd doom-core-dir) + projectile-enable-caching) + (dolist (spec (list (cons 'last-project doom-emacs-dir) + (cons 'last doom-core-dir) + (cons (lambda (x) "x") "x") + (cons "~" (expand-file-name "~")) + (cons nil default-directory))) + (let ((+doom-dashboard-pwd-policy (car spec))) + (should (equal (+doom-dashboard--get-pwd) (cdr spec))))))) + +(def-test! pwd-policy + (dolist (spec (list (cons 'last-project doom-emacs-dir) + (cons 'last doom-core-dir) + (cons "~" (expand-file-name "~/")) + (cons (lambda (x) "/tmp") "/tmp/"))) + (-dashboard-test-pwd spec (expand-file-name "core.el" doom-core-dir)))) + +;; +(def-test! inhibit-refresh :skip t) +(def-test! inhibit-functions :skip t)