diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 1a70d0fd7..02bd78ae1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,43 +1,41 @@ --- name: Bug report -about: Something went wrong, please fix it! +about: Doom might be misbehaving labels: is:bug -title: "[BUG] " -assignees: '' --- -**Describe the issue** -Start with a brief 1 or 2 sentence summary of issue. +**What did you expect to happen?** +... -Then follow with a longer explanation, if necessary. Here are some suggestions -on what to include: -- What you expected vs what actually happened -- Screenshots/casts of your issue -- A link to your private config -- Labels for any keys you reference (use `SPC h k` to inspect a key) -- Any warnings or errors logged to \*Messages\* (`SPC h e` or `M-x - view-echo-area-messages`). + +**What actually happened?** +... + + +**Additional details:** +- Include a link to your private config +- Include screenshots/casts of your issue +- If you mention key sequences, include what commands they're bound to (use `SPC + h k KEY` or `C-h h k KEY` to inspect keys). +- Include any warnings or errors logged to \*Messages\* (use `M-x + view-echo-area-messages` to see it).
-If available, please a backtrace of the error here.
+If an error message is involved include a backtrace of it.
 
-To acquire a backtrace, enable `debug-on-error` then recreate the error. Here
-are ways to enable `debug-on-error`:
-- `M-x toggle-debug-on-error`,
-- Start Emacs with `emacs --debug-init`
-- If the error occurred while using `bin/doom`, use the `-d`/`--debug`
-- switches or the `DEBUG` environment variable.
+How to acquire a backtrace:
+https://github.com/hlissner/doom-emacs/blob/develop/docs/getting_started.org#how-to-extract-a-backtrace-from-an-error
 
-**Steps to reproduce** +**Steps to reproduce:** 1. Select these example steps, 2. Delete them, 3. And replace them with precise steps to reproduce your issue. 4. Fill in "system information" below. -**System information** +**System information:**
 Place the output of `M-x doom/info` or `~/.emacs.d/bin/doom info` here.
 
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b3b4514c6..f39feedc1 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,28 +1,20 @@ --- name: Feature request -about: Make suggestions for improving Doom Emacs -labels: is:request -title: "[REQUEST] " -assignees: '' +about: It'd be cool if Doom did/had/would... +labels: is:new --- **Describe the feature** -Start with a brief 1 or 2 sentence summary of feature. - -Then follow up with an extended explanation, if necessary. Here are some -suggestions on what to include: -- How is it helpful? -- How is the feature used? - If the feature involves new plugins, include links to them -- iF the feature involves replacement of current functionality, describe how the - new functionality is better. -- If the feature is implemented in another editor Emacs distro: - - Include screenshots or screencasts of it - - Include links to its implementation - - Include names of commands (and not just the keybinds to invoke them) +- If the feature involves replacement of current functionality, how the new + functionality is better? +- If the feature is implemented in another editor or Emacs distro, include: + - Screenshots or screencasts of it + - Links to its implementation + - Names of relevant commands (and not only the keybinds to invoke them) **System information**
-Include the output of `M-x doom/info` or `~/.emacs.d/bin/doom info` here.
+Place the output of `M-x doom/info` or `~/.emacs.d/bin/doom info` here.
 
diff --git a/.github/ISSUE_TEMPLATE/how_to.md b/.github/ISSUE_TEMPLATE/how_to.md index 033e31a80..d4d0544a6 100644 --- a/.github/ISSUE_TEMPLATE/how_to.md +++ b/.github/ISSUE_TEMPLATE/how_to.md @@ -1,23 +1,26 @@ --- name: How do I... about: How to get Doom/Emacs to behave a certain way -labels: is:howto, status:pending-review -title: "[HOWTO] " -assignees: '' +labels: is:question --- -**What I want to achieve** -Start with a brief 1 or 2 sentence summary of what you're trying to achieve. +> Please visit our Discord server to ask how-to and workflow questions: +> https://discord.gg/qvGgnVx -- post on Doom's issue tracker as a last resort. -Follow up with an expanded explanation, if necessary. A few suggestions of what -you could include are: -- Code or steps you've tried that did not yield the results you wanted. -- Screenshots/casts of a proposed workflow in another editor or Emacs distro, -- Additional material or links to resources that could help clarify what you are - trying to do. -- A link to your private config, if available. -- The names of commands available in other Emacs distros (rather than just the - keybinds that invoke them). +**What are you trying to achieve?** +... + + +**What have you tried?** +... + + +**Additional information** +- Do you have screenshots/casts of this workflow in another editor or Emacs + distro? (If possible, include names of specific commands or functions from + other editors/emacs distros) +- Do you have additional material or links to resources that could help clarify + what you are trying to do? **System information** diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 0853f526f..000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: Question -about: A question about the project or maintainer -labels: is:question, status:pending-review -title: "[QUESTION] " -assignees: '' ---- - -What would you like to know? diff --git a/.github/PULL_REQUEST/bump.md b/.github/PULL_REQUEST/bump.md new file mode 100644 index 000000000..c02624b7e --- /dev/null +++ b/.github/PULL_REQUEST/bump.md @@ -0,0 +1,30 @@ +--- +name: Bump a package +about: Update a pinned package to a new commit +labels: is:update re:packages +--- +> You're about to request a package be bumped to a newer commit. +> +> 1. Please make sure this PR targets the `develop` branch and not `master`. +> 2. Describe why these bumps are necessary below +> 3. Please conform your commit messages to one of the following formats: +> +> Bump to username/repo@a1b2c3d +> +> From username/repo@z9y8x7w +> +> OR, if multiple packages are bumped in one commit: +> +> Bump package1, package2 & package 3 +> +> emacs-lsp/lsp-mode@91e37a6 -> emacs-lsp/lsp-mode@c8188ef +> emacs-lsp/lsp-ui@cf6906c -> emacs-lsp/lsp-ui@582e153 +> +> (Commit hashes should be limited to 7 characters) +> +> 4. You've included links to relevant issues, if any +> 5. You've deleted this template +> +> Thank you for contributing to Doom Emacs! <3 + +Explain why this bump is necessary here... diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST/general.md similarity index 64% rename from .github/PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST/general.md index eb8c16d71..d36a306b9 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST/general.md @@ -1,6 +1,10 @@ -> To ensure that this PR is processed as quickly as possible, please ensure the -> following steps have been taken: -> - [ ] This PR targets the develop branch and not master +--- +name: General Contribution +about: A general code or documentation PR +--- +> Please follow these steps before submitting your PR: +> +> - [ ] This PR targets the `develop` branch and not `master` > - [ ] If your PR is a work in progress, include [WIP] in its title > - [ ] Its commits' summaries are reasonably descriptive > - [ ] You've described what this PR addresses below diff --git a/.gitignore b/.gitignore index fcc169df8..246426a5b 100644 --- a/.gitignore +++ b/.gitignore @@ -7,12 +7,15 @@ modules/private .cask/ cask/ elpa/ +test/.local*/ # emacs tempfiles that shouldn't be there .mc-lists.el .org-id-locations .tern-port -auto-save-list +.extension/ +.dap-breakpoints +auto-save-list/ semanticdb ede-projects.el tramp diff --git a/LICENSE b/LICENSE index 02b93bee4..cb8f2620c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2019 Henrik Lissner. +Copyright (c) 2016-2020 Henrik Lissner. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Makefile b/Makefile deleted file mode 100644 index 5bc6eb0f5..000000000 --- a/Makefile +++ /dev/null @@ -1,85 +0,0 @@ -DOOM = "bin/doom" -MODULES = $(patsubst modules/%/, %, $(sort $(dir $(wildcard modules/*/ modules/*/*/)))) - -all: deprecated - @$(DOOM) refresh - -deprecated: - @echo "Using make to manage your Doom config is deprecated" - @echo - @echo "Use the 'bin/doom' script instead. The equivalent of 'make' is 'doom refresh'." - @echo - @echo "See 'doom help' for a list of commands" - @echo - @read -p "Press enter to continue" - -## Shortcuts -a: autoloads -i: install -u: update -U: upgrade -r: autoremove -c: compile -cc: compile-core -cp: compile-plugins -re: recompile -d: doctor - -quickstart: install - - -## Package management -install: deprecated - @$(DOOM) install -update: deprecated - @$(DOOM) update -autoremove: deprecated - @$(DOOM) autoremove -autoloads: deprecated - @$(DOOM) autoloads -upgrade: deprecated - @$(DOOM) upgrade - -## Byte compilation -compile: deprecated - @$(DOOM) compile -compile-core: deprecated - @$(DOOM) compile :core -compile-private: deprecated - @$(DOOM) compile :private -compile-plugins: deprecated - @$(DOOM) build -recompile: deprecated - @$(DOOM) recompile -clean: deprecated - @$(DOOM) clean -# compile-module -# compile-module/submodule -$(patsubst %, compile-%, $(MODULES)): | .local/autoloads.el - @$(DOOM) $@ $(subst compile-, , $@) - - -## Unit tests -test: - @$(DOOM) test -test-core: - @$(DOOM) test :core -# test-module -# test-module/submodule -$(patsubst %, test-%, $(MODULES)): - @$(DOOM) test $(subst test-, , $@) - - -## Utility tasks -# Runs Emacs from a different folder than ~/.emacs.d; only use this for testing! -run: - @$(DOOM) run $(ARGS) -# Prints debug info about your current setup -info: - @$(DOOM) info - -# Diagnoses potential OS/environment issues -doctor: - @$(DOOM) doctor - -.PHONY: all compile test testi clean diff --git a/README.md b/README.md index 4ad52f31c..cfa4f3dc3 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Build status: develop - + Join our discord server

@@ -35,6 +35,9 @@ git clone https://github.com/hlissner/doom-emacs ~/.emacs.d ~/.emacs.d/bin/doom install ``` +More details, including dependencies and how to install Emacs, can be found [in +the documentation](docs/getting_started.org#install). + **Table of Contents** - [What is Doom Emacs](#what-is-doom-emacs) @@ -55,46 +58,47 @@ It is a story as old as time. A stubborn, shell-dwelling, and melodramatic vimmer -- envious of the features of modern text editors -- spirals into despair before succumbing to the [dark side][url:evil-mode]. This is his config. -Doom is a configuration for [GNU Emacs](https://www.gnu.org/software/emacs/) -designed to make Emacs faster and easier to customize. It can serve as framework -for your own configuration or a resource for fellow Emacs enthusiasts who want -to learn more about our favorite OS. +Doom is a configuration framework for [GNU +Emacs](https://www.gnu.org/software/emacs/) tailored for Emacs bankruptcy +veterans who want less framework in their frameworks and the performance of a +hand rolled config (or better). It can be a foundation for your own config or a +resource for Emacs enthusiasts to learn more about our favorite OS. ## Doom's mantras -- **Gotta go fast.** Startup and run-time performance are high priorities. - Expensive functionality (built-in or in plugins) is modified and optimized - toward this end, otherwise, they must be opt-in. -- **Close to metal.** There's less between you and vanilla Emacs, by design. - There's less to grok. Modules should be syntactically sweet and backend logic - explicit and abstraction-light. The code itself ought to be designed as if - grokking it were part of the user experience; and it is! -- **Opinionated, but not stubborn.** Doom is a bundle of reasonable defaults - and curated opinions, but you aren't stuck with it. Use as little or as much - of it as you like. Use it as-is as a complete Emacs distribution; disable - everything and use it as a baseline for your own; or anywhere in between. -- **Your system, your rules.** There are more ways to set up your programming - environment than there are dislikes on Youtube Rewind '18, so Doom and its - plugins promise not to *automatically* (and definitely not *silently*) install - system dependencies. This means fonts, packages and programs. `doom doctor` - will tell you what's missing though! +- **Gotta go fast.** Startup and run-time performance are priorities. Doom goes + beyond lazy loading packages by modifying them to be snappier and load lazier! +- **Close to metal.** There's less between you and vanilla Emacs by design. + There's less to grok, on top of Emacs. +- **Readability counts.** Internals ought to be written as if reading them were + part of the user experience, and it is! Modules should be syntactically sweet. + Backend logic should be functional (as much as elisp permits), abstraction + light and (hopefully) documented. +- **Opinionated, but not stubborn.** Doom is a bundle of reasonable defaults and + curated opinions, but all of it should be optional. Use as little or as much + of it as you like. +- **Your system, your rules.** There are more ways to set up your development + environment than there are dislikes on Youtube Rewind '18, so Doom leaves it + to you. Doom will not *automatically* install system dependencies (and will + coerce its plugins not to do so either). Use `doom doctor` to figure out + what's missing. ## Features +- Minimalistic good looks inspired by modern editors. +- A modular architecture for a more organized Emacs configuration. +- A custom elisp library to help simplify your config. +- (Optional) Vim-emulation powered by [evil-mode][url:evil-mode], including + ports of popular vim plugins and functionality. +- A declarative [package management system][doom:packages] (powered by + [straight.el][url:straight]) with a command line interface. Install packages + from anywhere, not just (M)ELPA. - A curated set of sane defaults for all packages, all (major) OSes, and Emacs itself. - Support for *many* programming languages. Too many to list. Includes syntax highlighting, linters/checker integration, inline code evaluation, code completion (where possible), REPLs, documentation lookups, snippets, and more! - Support for *many* tools, like docker, pass, ansible, terraform, and more. -- Minimalistic good looks inspired by modern editors. -- A modular architecture for a more organized Emacs configuration. -- A custom elisp library to help you simplify your config. -- A declarative [package management system][doom:packages] (powered by - [straight.el][url:straight]) with a command line interface. Install packages - from anywhere, not just (M)ELPA. -- Vim-emulation powered by [evil-mode][url:evil-mode], including ports of - popular vim plugins and functionality. - A Spacemacs-esque [keybinding scheme][doom:bindings], centered around leader and localleader prefix keys (SPC and SPCm, by default). @@ -104,12 +108,11 @@ to learn more about our favorite OS. integration. Let someone else argue about tabs vs **\_\***spaces**\*\_**. - Project-management tools and framework-specific minor modes with their own snippets libraries. -- Project search (and replace) utilities, powered by - [the_silver_searcher][url:the_silver_searcher] or [ripgrep][url:ripgrep]. +- Project search (and replace) utilities, powered by [ripgrep][url:ripgrep]. - Isolated and persistent workspaces (also substitutes for vim tabs). -- An environment variables file generator and loader, so that Emacs can - perfectly inherit your shell configuration. -- Everything is optional! +- An envvar file generator that captures a snapshot of your shell environment + for Doom to load at startup. No more struggling to get Emacs to inherit your + `PATH`, among other things. # Getting Help @@ -122,7 +125,7 @@ We have [a Discord server][url:discord]! Hop on and say hi! Encountered strange behavior or an error? Here are some things to try before you shoot off that bug report: -- Run `bin/doom refresh`. This ensures Doom is properly set up and its autoloads +- Run `bin/doom sync`. This ensures Doom is properly set up and its autoloads files are up-to-date. - If you have byte-compiled your config (with `bin/doom compile`), see if `bin/doom clean` makes the issue go away. Never debug issues with a @@ -168,11 +171,10 @@ you can do to help; I welcome any contribution! [doom:bindings]: modules/config/default/+evil-bindings.el [doom:packages]: core/autoload/packages.el [doom:popups]: modules/ui/popup/README.org -[url:discord]: https://discord.gg/bcZ6P3y +[url:discord]: https://discord.gg/qvGgnVx [url:liberapay]: https://liberapay.com/hlissner/donate [url:paypal]: https://paypal.me/henriklissner/10 [url:editorconfig]: http://editorconfig.org/ [url:evil-mode]: https://github.com/emacs-evil/evil [url:ripgrep]: https://github.com/BurntSushi/ripgrep -[url:the_silver_searcher]: https://github.com/ggreer/the_silver_searcher [url:straight]: https://github.com/raxod502/straight.el diff --git a/bin/doom b/bin/doom index 834e56afd..919b6ed40 100755 --- a/bin/doom +++ b/bin/doom @@ -9,10 +9,11 @@ :; exec $EMACS --script "$0" -- "$@" :; exit 0 -(let* ((loaddir (file-name-directory (file-truename load-file-name))) +(let* ((load-prefer-newer t) + (loaddir (file-name-directory (file-truename load-file-name))) (emacsdir (getenv "EMACSDIR")) - (user-emacs-directory (or emacsdir (expand-file-name "../" loaddir))) - (load-prefer-newer t)) + (user-emacs-directory + (abbreviate-file-name (or emacsdir (expand-file-name "../" loaddir))))) (push (expand-file-name "core" user-emacs-directory) load-path) (require 'core) @@ -51,9 +52,18 @@ with a different private module." (setq doom-auto-accept t) (print! (info "Auto-yes on"))) (when help-p - (push command args) + (when command + (push command args)) (setq command "help")) + (when (equal (user-real-uid) 0) + (print! + (concat "WARNING: This script is running as root. This likely wasn't intentional, and\n" + "is unnecessary to use this script. This will cause file permissions errors\n" + "later if you use this Doom installation on a non-root account.\n")) + (unless (or doom-auto-accept (y-or-n-p "Continue anyway?")) + (user-error "Aborted"))) + ;; Reload core in case any of the directories were changed. (when (or emacsdir doomdir localdir) (load! "core/core.el" user-emacs-directory)) @@ -69,29 +79,43 @@ with a different private module." ((condition-case e (let ((start-time (current-time))) (and (doom-cli-execute command args) - (terpri) (print! (success "Finished! (%.4fs)") (float-time (time-subtract (current-time) start-time))))) (user-error (print! (error "%s\n") (error-message-string e)) - (print! (yellow "See 'doom help %s' for documentation on this command.") (car args))) + (print! (yellow "See 'doom help %s' for documentation on this command.") (car args)) + (error "")) ; Ensure non-zero exit code ((debug error) - (message "--------------------------------------------------\n") - (message "There was an unexpected error:") - (message " %s (%s)" (get (car e) 'error-message) (car e)) - (dolist (item (cdr e)) - (message " %s" item)) + (print! (error "There was an unexpected error:")) + (print-group! + (print! "%s %s" (bold "Type:") (car e)) + (print! (bold "Message:")) + (print-group! + (print! "%s" (get (car e) 'error-message))) + (print! (bold "Data:")) + (print-group! + (if (cdr e) + (dolist (item (cdr e)) + (print! "%S" item)) + (print! "n/a"))) + (when (featurep 'straight) + (when (string-match-p (regexp-quote straight-process-buffer) + (error-message-string e)) + (print! (bold "Straight output:")) + (print-group! (print! "%s" (straight--process-get-output)))))) (unless debug-on-error - (message - (concat "\nRun the command again with the -d (or --debug) option to enable debug\n" - "mode and, hopefully, generate a stack trace. If you decide to file a bug\n" - "report, please include it!\n\n" - "Emacs outputs to standard error, so you'll need to redirect stderr to\n" - "stdout to pipe this to a file or clipboard!\n\n" - " e.g. doom -d install 2>&1 | clipboard-program\n")) - (signal 'doom-error e))))))) + (terpri) + (print! + (concat "Run the command again with the -d (or --debug) switch to enable debug\n" + "mode and (hopefully) generate a backtrace from this error:\n" + "\n %s\n\n" + "If you file a bug report, please include it!") + (string-join (append (list (file-name-nondirectory load-file-name) "-d" command) + args) + " ")) + (error ""))))))) ; Ensure non-zero exit code (doom-cli-execute :main (cdr (member "--" argv))) (setq argv nil)) diff --git a/bin/org-capture b/bin/org-capture index 15d6f0a34..d2e076cf5 100755 --- a/bin/org-capture +++ b/bin/org-capture @@ -22,8 +22,8 @@ fi # org-capture key mapped to argument flags # keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2) -while getopts hk opt; do - key="\"$opt\"" +while getopts "hk:" opt; do + key="\"$OPTARG\"" break done shift $((OPTIND-1)) diff --git a/core/autoload/buffers.el b/core/autoload/buffers.el index 4f355417a..419284845 100644 --- a/core/autoload/buffers.el +++ b/core/autoload/buffers.el @@ -195,7 +195,9 @@ If DERIVED-P, test with `derived-mode-p', otherwise use `eq'." ;;;###autoload (defun doom-set-buffer-real (buffer flag) - "Forcibly mark BUFFER as FLAG (non-nil = real)." + "Forcibly mark BUFFER as FLAG (non-nil = real). + +See `doom-real-buffer-p' for an explanation for real buffers." (with-current-buffer buffer (setq doom-real-buffer-p flag))) @@ -251,7 +253,9 @@ regex PATTERN. Returns the number of killed buffers." ;;;###autoload (defun doom-mark-buffer-as-real-h () - "Hook function that marks the current buffer as real." + "Hook function that marks the current buffer as real. + +See `doom-real-buffer-p' for an explanation for real buffers." (doom-set-buffer-real (current-buffer) t)) @@ -272,6 +276,12 @@ If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)." (set-buffer-modified-p nil))) (doom-kill-buffer-fixup-windows buffer)) + +(defun doom--message-or-count (interactive message count) + (if interactive + (message message count) + count)) + ;;;###autoload (defun doom/kill-all-buffers (&optional buffer-list interactive) "Kill all buffers and closes their windows. @@ -286,14 +296,14 @@ belong to the current project." (if (null buffer-list) (message "No buffers to kill") (save-some-buffers) + (delete-other-windows) (when (memq (current-buffer) buffer-list) (switch-to-buffer (doom-fallback-buffer))) - (mapc #'doom-kill-buffer-and-windows buffer-list) - (delete-other-windows) - (when interactive - (message "Killed %s buffers" - (- (length buffer-list) - (length (cl-remove-if-not #'buffer-live-p buffer-list))))))) + (mapc #'kill-buffer buffer-list) + (doom--message-or-count + interactive "Killed %d buffers" + (- (length buffer-list) + (length (cl-remove-if-not #'buffer-live-p buffer-list)))))) ;;;###autoload (defun doom/kill-other-buffers (&optional buffer-list interactive) @@ -308,10 +318,10 @@ project." (doom-buffer-list))) t)) (mapc #'doom-kill-buffer-and-windows buffer-list) - (when interactive - (message "Killed %s buffers" - (- (length buffer-list) - (length (cl-remove-if-not #'buffer-live-p buffer-list)))))) + (doom--message-or-count + interactive "Killed %d other buffers" + (- (length buffer-list) + (length (cl-remove-if-not #'buffer-live-p buffer-list))))) ;;;###autoload (defun doom/kill-matching-buffers (pattern &optional buffer-list interactive) @@ -340,7 +350,11 @@ current project." (list (doom-buried-buffers (if current-prefix-arg (doom-project-buffer-list))) t)) - (doom/kill-all-buffers buffer-list interactive)) + (mapc #'kill-buffer buffer-list) + (doom--message-or-count + interactive "Killed %d buried buffers" + (- (length buffer-list) + (length (cl-remove-if-not #'buffer-live-p buffer-list))))) ;;;###autoload (defun doom/kill-project-buffers (project &optional interactive) @@ -358,9 +372,9 @@ current project." nil) t)) (when project - (let ((buffers (doom-project-buffer-list project))) - (doom-kill-buffers-fixup-windows buffers) - (when interactive - (message "Killed %d buffer(s)" - (- (length buffers) - (length (cl-remove-if-not #'buffer-live-p buffers)))))))) + (let ((buffer-list (doom-project-buffer-list project))) + (doom-kill-buffers-fixup-windows buffer-list) + (doom--message-or-count + interactive "Killed %d project buffers" + (- (length buffer-list) + (length (cl-remove-if-not #'buffer-live-p buffer-list))))))) diff --git a/core/autoload/cli.el b/core/autoload/cli.el index 0c455f741..f0582ef1c 100644 --- a/core/autoload/cli.el +++ b/core/autoload/cli.el @@ -1,86 +1,5 @@ ;;; core/autoload/cli.el -*- lexical-binding: t; -*- -;; Externs -(defvar evil-collection-mode-list) - -;;;###autoload -(defun doom--cli-run (command &rest _args) - (when (featurep 'general) - (general-auto-unbind-keys)) - (let* ((evil-collection-mode-list nil) - (default-directory doom-emacs-dir) - (buf (get-buffer-create " *bin/doom*")) - (doom-format-backend 'ansi) - (ignore-window-parameters t) - (noninteractive t) - (standard-output - (lambda (char) - (with-current-buffer buf - (insert char) - (when (memq char '(?\n ?\r)) - (ansi-color-apply-on-region (line-beginning-position -1) (line-end-position)) - (redisplay)))))) - (doom-initialize t) - (setq doom-modules (doom-modules)) - (doom-initialize-modules t) - (doom-initialize-packages t) - (with-current-buffer (switch-to-buffer buf) - (erase-buffer) - (require 'package) - (redisplay) - (doom-dispatch command nil) - (print! (green "\nDone!")))) - (when (featurep 'general) - (general-auto-unbind-keys 'undo)) - (message (format! (green "Done!")))) - - -;;;###autoload -(defun doom//autoloads (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "autoloads"))) - -;;;###autoload -(defun doom//update (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "update"))) - -;;;###autoload -(defun doom//upgrade (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "upgrade")) - (when (y-or-n-p "You must restart Emacs for the upgrade to take effect. Restart?") - (doom/restart-and-restore))) - -;;;###autoload -(defun doom//install (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "install"))) - -;;;###autoload -(defun doom//autoremove (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "autoremove"))) - -;;;###autoload -(defun doom//refresh (&optional yes) - "TODO" - (interactive "P") - (let ((doom-auto-accept yes)) - (doom--cli-run "refresh"))) - - - ;; ;;; Library @@ -113,7 +32,7 @@ Warning: freezes indefinitely on any stdin prompt." :connection-type 'pipe)) done-p) (set-process-filter - process (lambda (process output) + process (lambda (_process output) (princ output (current-buffer)) (princ output))) (set-process-sentinel diff --git a/core/autoload/config.el b/core/autoload/config.el index e345e7ab7..d465d66f4 100644 --- a/core/autoload/config.el +++ b/core/autoload/config.el @@ -1,5 +1,8 @@ ;;; core/autoload/config.el -*- lexical-binding: t; -*- +(defvar doom-bin-dir (concat doom-emacs-dir "bin/")) +(defvar doom-bin (concat doom-bin-dir "doom")) + ;;;###autoload (defvar doom-reload-hook nil "A list of hooks to run when `doom/reload' is called.") @@ -23,8 +26,9 @@ (doom-project-find-file doom-private-dir)) ;;;###autoload -(defun doom/goto-doomblock () - "Open your private init.el and go to your `doom!' block." +(defun doom/goto-private-init-file () + "Open your private init.el file. +And jumps to your `doom!' block." (interactive) (find-file (expand-file-name "init.el" doom-private-dir)) (goto-char @@ -34,46 +38,58 @@ (point)))) ;;;###autoload -(defun doom/goto-config-file () +(defun doom/goto-private-config-file () "Open your private config.el file." (interactive) (find-file (expand-file-name "config.el" doom-private-dir))) ;;;###autoload -(defun doom/goto-packages-file () +(defun doom/goto-private-packages-file () "Open your private packages.el file." (interactive) (find-file (expand-file-name "packages.el" doom-private-dir))) + +;; +;;; Managements + +(cl-defmacro doom--compile (command &key on-success on-failure) + (declare (indent defun)) + `(with-current-buffer (compile ,command) + (add-hook + 'compilation-finish-functions + (lambda (_buf status) + (if (equal status "finished\n") + ,on-success + ,on-failure)) + nil 'local))) + ;;;###autoload (defun doom/reload () "Reloads your private config. -This is experimental! It will try to do as `bin/doom refresh' does, but from -within this Emacs session. i.e. it reload autoloads files (if necessary), -reloads your package list, and lastly, reloads your private config.el. +This is experimental! It will try to do as `bin/doom sync' does, but from within +this Emacs session. i.e. it reload autoloads files (if necessary), reloads your +package list, and lastly, reloads your private config.el. Runs `doom-reload-hook' afterwards." (interactive) - (or (y-or-n-p - (concat "You are about to reload your Doom config from within Emacs. This " - "is highly experimental and may cause issues. It is recommended you " - "use 'bin/doom refresh' on the command line instead.\n\n" - "Reload anyway?")) - (user-error "Aborted")) (require 'core-cli) - (let ((doom-reloading-p t)) - (compile (format "%s/bin/doom refresh -f" doom-emacs-dir)) - (while compilation-in-progress - (sit-for 1)) - (doom-initialize 'force) - (with-demoted-errors "PRIVATE CONFIG ERROR: %s" - (general-auto-unbind-keys) - (unwind-protect - (doom-initialize-modules 'force) - (general-auto-unbind-keys t))) - (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)) - (message "Finished!")) + (when (and IS-WINDOWS (file-exists-p doom-env-file)) + (warn "Can't regenerate envvar file from within Emacs. Run 'doom env' from the console")) + (doom--compile (format "%s sync -e" doom-bin) + :on-success + (let ((doom-reloading-p t)) + (doom-initialize 'force) + (with-demoted-errors "PRIVATE CONFIG ERROR: %s" + (general-auto-unbind-keys) + (unwind-protect + (doom-initialize-modules 'force) + (general-auto-unbind-keys t))) + (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) + (print! (success "Config successfully reloaded!"))) + :on-failure + (user-error "Failed to reload your config"))) ;;;###autoload (defun doom/reload-autoloads () @@ -83,23 +99,45 @@ This is much faster and safer than `doom/reload', but not as comprehensive. This reloads your package and module visibility, but does not install new packages or remove orphaned ones. It also doesn't reload your private config. -It is useful to only pull in changes performed by 'doom refresh' on the command +It is useful to only pull in changes performed by 'doom sync' on the command line." (interactive) (require 'core-cli) (require 'core-packages) (doom-initialize-packages) - (doom-reload-autoloads nil 'force)) + (doom-cli-reload-autoloads)) ;;;###autoload -(defun doom/reload-env () - "Regenerates and reloads your shell environment. +(defun doom/reload-env (&optional arg) + "Regenerates and/or reloads your envvar file. -Uses the same mechanism as 'bin/doom env reload'." +If passed the prefix ARG, clear the envvar file. Uses the same mechanism as +'bin/doom env'. + +An envvar file contains a snapshot of your shell environment, which can be +imported into Emacs." + (interactive "P") + (when IS-WINDOWS + (user-error "Cannot reload envvar file from within Emacs on Windows, run it from cmd.exe")) + (doom--compile + (format "%s -ic '%s env%s'" + (string-trim + (shell-command-to-string + (format "getent passwd %S | cut -d: -f7" + (user-login-name)))) + doom-bin (if arg " -c" "")) + :on-success + (let ((doom-reloading-p t)) + (unless arg + (doom-load-envvars-file doom-env-file))) + :on-failure + (error "Failed to generate env file"))) + +;;;###autoload +(defun doom/upgrade () + "Run 'doom upgrade' then prompt to restart Emacs." (interactive) - (compile (format "%s env" (expand-file-name "bin/doom" doom-emacs-dir))) - (while compilation-in-progress - (sit-for 1)) - (unless (file-readable-p doom-env-file) - (error "Failed to generate env file")) - (doom-load-envvars-file doom-env-file)) + (doom--compile (format "%s upgrade" doom-bin) + :on-success + (when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?") + (doom/restart-and-restore)))) diff --git a/core/autoload/debug.el b/core/autoload/debug.el index 14ae8fc00..6ebf2e2df 100644 --- a/core/autoload/debug.el +++ b/core/autoload/debug.el @@ -23,11 +23,25 @@ (when (file-exists-p file) (insert-file-contents file)))) +(defun doom--collect-forms-in (file form) + (when (file-readable-p file) + (let (forms) + (with-temp-buffer + (insert-file-contents file) + (delay-mode-hooks (emacs-lisp-mode)) + (while (re-search-forward (format "(%s " (regexp-quote form)) nil t) + (unless (doom-point-in-string-or-comment-p) + (save-excursion + (goto-char (match-beginning 0)) + (push (sexp-at-point) forms)))) + (nreverse forms))))) + ;;;###autoload (defun doom-info () "Returns diagnostic information about the current Emacs session in markdown, ready to be pasted in a bug report on github." (require 'vc-git) + (require 'core-packages) (let ((default-directory doom-emacs-dir) (doom-modules (doom-modules))) (cl-letf @@ -46,7 +60,8 @@ ready to be pasted in a bug report on github." 'server-running)))) (doom (version . ,doom-version) - (build . ,(sh "git" "log" "-1" "--format=%D %h %ci"))) + (build . ,(sh "git" "log" "-1" "--format=%D %h %ci")) + (dir . ,(abbreviate-file-name (file-truename doom-private-dir)))) (system (type . ,system-type) (config . ,system-configuration) @@ -78,24 +93,29 @@ ready to be pasted in a bug report on github." (cdr key)))) '("n/a"))) (packages - ,@(or (ignore-errors - (let ((doom-interactive-mode t) - doom-packages - doom-disabled-packages) - (doom--read-module-packages-file - (doom-path doom-private-dir "packages.el") - nil t) - (cl-loop for (name . plist) in (nreverse doom-packages) - collect - (if-let (splist (doom-plist-delete (copy-sequence plist) - :modules)) - (prin1-to-string (cons name splist)) - name)))) + ,@(or (condition-case e + (mapcar + #'cdr (doom--collect-forms-in + (doom-path doom-private-dir "packages.el") + "package!")) + (error (format "<%S>" e))) + '("n/a"))) + (unpin + ,@(or (condition-case e + (mapcan #'identity + (mapcar + #'cdr (doom--collect-forms-in + (doom-path doom-private-dir "packages.el") + "unpin!"))) + (error (format "<%S>" e))) '("n/a"))) (elpa - ,@(or (ignore-errors - (cl-loop for (name . _) in package-alist - collect (format "%s" name))) + ,@(or (condition-case e + (progn + (package-initialize) + (cl-loop for (name . _) in package-alist + collect (format "%s" name))) + (error (format "<%S>" e))) '("n/a")))))))) @@ -197,7 +217,7 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (prin1-to-string (macroexp-progn (append `((setq noninteractive nil - doom-debug-mode t + init-file-debug t load-path ',load-path package--init-file-ensured t package-user-dir ,package-user-dir @@ -209,8 +229,10 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (setq-default buffer-undo-tree (make-undo-tree)))) (pcase mode (`vanilla-doom+ ; Doom core + modules - private config - `((setq doom-init-modules-p t) - (load-file ,user-init-file) + `((load-file ,(expand-file-name "core.el" doom-core-dir)) + (doom-initialize) + (doom-initialize-core) + (add-hook 'window-setup-hook #'doom-display-benchmark-h) (setq doom-modules ',doom-modules) (maphash (lambda (key plist) (let ((doom--current-module key) @@ -225,8 +247,9 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!" (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (doom-run-all-startup-hooks-h))) (`vanilla-doom ; only Doom core - `((setq doom-init-modules-p t) - (load-file ,user-init-file) + `((load-file ,(expand-file-name "core.el" doom-core-dir)) + (doom-initialize) + (doom-initialize-core) (doom-run-all-startup-hooks-h))) (`vanilla ; nothing loaded `((package-initialize))))))) @@ -335,6 +358,10 @@ will be automatically appended to the result." ((> (prefix-numeric-value arg) 0))))) (setq doom-debug-mode value debug-on-error value + garbage-collection-messages value + use-package-verbose value jka-compr-verbose value - lsp-log-io value) + lsp-log-io value + gcmh-verbose value + magit-refresh-verbose value) (message "Debug mode %s" (if value "on" "off")))) diff --git a/core/autoload/files.el b/core/autoload/files.el index fd48fa0e7..3e22607e7 100644 --- a/core/autoload/files.el +++ b/core/autoload/files.el @@ -108,26 +108,26 @@ be relative to it. The search recurses up to DEPTH and no further. DEPTH is an integer. MATCH is a string regexp. Only entries that match it will be included." - (let (file-name-handler-alist - result) + (let (result file-name-handler-alist) (dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths))) (cond ((file-directory-p file) - (nconcq! result - (and (memq type '(t dirs)) - (string-match-p match file) - (not (and filter (funcall filter file))) - (not (and (file-symlink-p file) - (not follow-symlinks))) - (<= mindepth 0) - (list (cond (map (funcall map file)) - (relative-to (file-relative-name file relative-to)) - (file)))) - (and (>= depth 1) - (apply #'doom-files-in file - (append (list :mindepth (1- mindepth) - :depth (1- depth) - :relative-to relative-to) - rest))))) + (appendq! + result + (and (memq type '(t dirs)) + (string-match-p match file) + (not (and filter (funcall filter file))) + (not (and (file-symlink-p file) + (not follow-symlinks))) + (<= mindepth 0) + (list (cond (map (funcall map file)) + (relative-to (file-relative-name file relative-to)) + (file)))) + (and (>= depth 1) + (apply #'doom-files-in file + (append (list :mindepth (1- mindepth) + :depth (1- depth) + :relative-to relative-to) + rest))))) ((and (memq type '(t files)) (string-match-p match file) (not (and filter (funcall filter file))) @@ -178,6 +178,7 @@ single file or nested compound statement of `and' and `or' statements." file)) (nth 7 (file-attributes file)))) +(defvar w32-get-true-file-attributes) ;;;###autoload (defun doom-directory-size (dir) "Returns the size of FILE (in DIR) in kilobytes." @@ -202,16 +203,14 @@ single file or nested compound statement of `and' and `or' statements." ;; ;;; Helpers -(defun doom--forget-file (old-path &optional new-path) +(defun doom--forget-file (path) "Ensure `recentf', `projectile' and `save-place' forget OLD-PATH." (when (bound-and-true-p recentf-mode) - (when new-path - (recentf-add-file new-path)) - (recentf-remove-if-non-kept old-path)) + (recentf-remove-if-non-kept path)) (when (and (bound-and-true-p projectile-mode) (doom-project-p) - (projectile-file-cached-p old-path (doom-project-root))) - (projectile-purge-file-from-cache old-path)) + (projectile-file-cached-p path (doom-project-root))) + (projectile-purge-file-from-cache path)) (when (bound-and-true-p save-place-mode) (save-place-forget-unreadable-files))) @@ -220,7 +219,8 @@ single file or nested compound statement of `and' and `or' statements." (vc-file-clearprops path) (vc-resynch-buffer path nil t)) (when (featurep 'magit) - (magit-refresh))) + (when-let (default-directory (magit-toplevel (file-name-directory path))) + (magit-refresh)))) (defun doom--copy-file (old-path new-path &optional force-p) (let* ((new-path (expand-file-name new-path)) @@ -306,11 +306,18 @@ file if it exists, without confirmation." (let ((old-path (buffer-file-name)) (new-path (expand-file-name new-path))) (when-let (dest (doom--copy-file old-path new-path force-p)) + (doom--forget-file old-path) (when (file-exists-p old-path) (delete-file old-path)) + (mapc #'doom--update-file + (delq + nil (list (if (ignore-errors + (file-equal-p (doom-project-root old-path) + (doom-project-root new-path))) + nil + old-path) + new-path))) (kill-current-buffer) - (doom--forget-file old-path new-path) - (doom--update-file new-path) (find-file new-path) (message "File successfully moved to %s" dest)))) (`overwrite-self (error "Cannot overwrite self")) @@ -339,4 +346,21 @@ file if it exists, without confirmation." (defun doom/sudo-this-file () "Open the current file as root." (interactive) - (find-alternate-file (doom--sudo-file buffer-file-name))) + (find-alternate-file (doom--sudo-file (or buffer-file-name + (when (or (derived-mode-p 'dired-mode) + (derived-mode-p 'wdired-mode)) + default-directory))))) + +;;;###autoload +(defun doom/sudo-save-buffer () + "Save this file as root." + (interactive) + (let ((file (doom--sudo-file buffer-file-name))) + (if-let (buffer (find-file-noselect file)) + (let ((origin (current-buffer))) + (unwind-protect + (with-current-buffer buffer + (save-buffer)) + (unless (eq origin buffer) + (kill-buffer buffer)))) + (user-error "Unable to open %S" file)))) diff --git a/core/autoload/fonts.el b/core/autoload/fonts.el index 1dce4e1ae..8df22d185 100644 --- a/core/autoload/fonts.el +++ b/core/autoload/fonts.el @@ -16,49 +16,51 @@ acceptable values for this variable.") "How many steps to increase the font size (with `doom-font' as the base) when `doom-big-font-mode' is enabled and `doom-big-font' is nil.") -;;;###autoload -(defvar doom-change-font-size-hook nil - "A hook run after adjusting the font size with `doom/increase-font-size', -`doom/decrease-font-size', or `doom/reset-font-size'.") - ;; ;;; Library -(defun doom--font-name (fontname frame) +(defun doom--font-name (fontname) (when (query-fontset fontname) - (when-let (ascii (assq 'ascii (aref (fontset-info fontname frame) 2))) + (when-let (ascii (assq 'ascii (aref (fontset-info fontname) 2))) (setq fontname (nth 2 ascii)))) (or (x-decompose-font-name fontname) (error "Cannot decompose font name"))) -(defun doom--frame-list (&optional frame) - "Return a list consisting of FRAME and all of FRAME's child frames." - (let ((frame (or frame (selected-frame)))) - (cons (selected-frame) - (cl-loop for fr in (frame-list) - if (eq (frame-parameter fr 'parent-frame) frame) - collect fr)))) - +(defvar doom--font-scale nil) ;;;###autoload -(defun doom-adjust-font-size (increment &optional frame) +(defun doom-adjust-font-size (increment) "Increase size of font in FRAME by INCREMENT. FRAME parameter defaults to current frame." - (let* ((frame (or frame (selected-frame))) - (font (frame-parameter frame 'font)) - (font (doom--font-name font frame))) - (let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum)) - increment))) - (unless (> new-size 0) - (error "Font is too small at %d" new-size)) - (aset font xlfd-regexp-pixelsize-subnum (number-to-string new-size))) - ;; Set point size & width to "*", so frame width will adjust to new font size - (aset font xlfd-regexp-pointsize-subnum "*") - (aset font xlfd-regexp-avgwidth-subnum "*") - (setq font (x-compose-font-name font)) - (unless (x-list-fonts font) - (error "Cannot change font size")) - (set-frame-parameter frame 'font font))) + (if (null increment) + (progn + (set-frame-font doom-font 'keep-size t) + (setf (alist-get 'font default-frame-alist) + (cond ((stringp doom-font) doom-font) + ((fontp doom-font) (font-xlfd-name doom-font)) + ((signal 'wrong-type-argument (list '(fontp stringp) + doom-font))))) + t) + (let* ((font (frame-parameter nil 'font)) + (font (doom--font-name font)) + (increment (* increment doom-font-increment)) + (zoom-factor (or doom--font-scale 0))) + (let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum)) + increment))) + (unless (> new-size 0) + (error "Font is too small at %d" new-size)) + (aset font xlfd-regexp-pixelsize-subnum (number-to-string new-size))) + ;; Set point size & width to "*", so frame width will adjust to new font size + (aset font xlfd-regexp-pointsize-subnum "*") + (aset font xlfd-regexp-avgwidth-subnum "*") + (setq font (x-compose-font-name font)) + (unless (x-list-fonts font) + (error "Cannot change font size")) + (set-frame-font font 'keep-size t) + (setf (alist-get 'font default-frame-alist) font) + (setq doom--font-scale (+ zoom-factor increment)) + ;; Unlike `set-frame-font', `set-frame-parameter' won't trigger this + (run-hooks 'after-setting-font-hook)))) ;; @@ -76,20 +78,15 @@ See `doom-init-fonts-h'." ;;;###autoload (defun doom/increase-font-size (count) - "Enlargens the font size across the current frame." + "Enlargens the font size across the current and child frames." (interactive "p") - (let ((zoom-factor (or (frame-parameter nil 'font-scale) 0)) - (increment (* count doom-font-increment))) - (setq zoom-factor (+ zoom-factor increment)) - (doom-adjust-font-size increment) - (set-frame-parameter nil 'font-scale zoom-factor) - (run-hooks 'doom-change-font-size-hook))) + (doom-adjust-font-size count)) ;;;###autoload (defun doom/decrease-font-size (count) - "Shrinks the font size across the current frame." + "Shrinks the font size across the current and child frames." (interactive "p") - (doom/increase-font-size (- count))) + (doom-adjust-font-size (- count))) ;;;###autoload (defun doom/reset-font-size () @@ -103,13 +100,10 @@ Assuming it has been adjusted via `doom/increase-font-size' and (/= text-scale-mode-amount 0)) (text-scale-set 0) (setq success t)) - (when-let (factor (frame-parameter nil 'font-scale)) - (set-frame-font doom-font t) - (set-frame-parameter nil 'font-scale nil) + (when (doom-adjust-font-size nil) (setq success t)) (unless success - (user-error "The font hasn't been resized")) - (run-hooks 'doom-change-font-size-hook))) + (user-error "The font hasn't been resized")))) ;;;###autoload (define-minor-mode doom-big-font-mode @@ -123,12 +117,16 @@ This uses `doom/increase-font-size' under the hood, and enlargens the font by :global t (unless doom-font (user-error "`doom-font' must be set to a valid font")) - (let ((frame (selected-frame))) - (if doom-big-font - (progn - (set-frame-font (if doom-big-font-mode doom-big-font doom-font) - t (doom--frame-list frame)) - (run-hooks 'doom-change-font-size-hook)) - (set-frame-font doom-font t (doom--frame-list frame)) - (when doom-big-font-mode - (doom-adjust-font-size doom-big-font-increment frame))))) + (if doom-big-font + (let ((font (if doom-big-font-mode doom-big-font doom-font))) + (set-frame-font font 'keep-size t) + (setf (alist-get 'font default-frame-alist) + (cond ((stringp doom-font) font) + ((fontp font) (font-xlfd-name font)) + ((signal 'wrong-type-argument (list '(fontp stringp) + font)))))) + (doom-adjust-font-size + (and doom-big-font-mode + (integerp doom-big-font-increment) + (/= doom-big-font-increment 0) + doom-big-font-increment)))) diff --git a/core/autoload/format.el b/core/autoload/format.el index c95440f49..c4429184d 100644 --- a/core/autoload/format.el +++ b/core/autoload/format.el @@ -106,9 +106,8 @@ Accepts 'ansi and 'text-properties. nil means don't render colors.") ;;;###autoload (defun doom--format-print (output) (unless (string-empty-p output) - (if (not noninteractive) - (message "%s" output) - (princ output) + (princ output) + (when (or noninteractive (not (eq standard-output t))) (terpri)) ; newline t)) @@ -213,8 +212,9 @@ into faces or ANSI codes depending on the type of sesssion we're in." ;;;###autoload (defmacro print! (message &rest args) - "Uses `message' in interactive sessions and `princ' otherwise (prints to -standard out). + "Prints MESSAGE, formatted with ARGS, to stdout. + +Returns non-nil if the message is a non-empty string. Can be colored using (color ...) blocks: diff --git a/core/autoload/help.el b/core/autoload/help.el index 731c4737e..3bf6a5c47 100644 --- a/core/autoload/help.el +++ b/core/autoload/help.el @@ -13,6 +13,7 @@ (lisp-mode :lang common-lisp) (csharp-mode :lang csharp) (clojure-mode :lang clojure) + (clojurescript-mode :lang clojure) (graphql-mode :lang data) (toml-mode :lang data) (json-mode :lang data) @@ -95,7 +96,7 @@ the current major-modea.") "Get information on an active minor mode. Use `describe-minor-mode' for a selection of all minor-modes, active or not." (interactive - (list (completing-read "Minor mode: " (doom-active-minor-modes)))) + (list (completing-read "Describe active mode: " (doom-active-minor-modes)))) (let ((symbol (cond ((stringp mode) (intern mode)) ((symbolp mode) mode) @@ -104,21 +105,6 @@ selection of all minor-modes, active or not." (helpful-function symbol) (helpful-variable symbol)))) -;;;###autoload -(defun doom/describe-symbol (symbol) - "Show help for SYMBOL, a variable, function or macro." - (interactive - (list (helpful--read-symbol "Symbol: " #'helpful--bound-p))) - (let* ((sym (intern-soft symbol)) - (bound (boundp sym)) - (fbound (fboundp sym))) - (cond ((and sym bound (not fbound)) - (helpful-variable sym)) - ((and sym fbound (not bound)) - (helpful-callable sym)) - ((apropos (format "^%s\$" symbol))) - ((apropos (format "%s" symbol)))))) - ;; ;;; Documentation commands @@ -132,30 +118,30 @@ selection of all minor-modes, active or not." (depth (if (integerp depth) depth))) (message "Loading search results...") (unwind-protect - (delq nil - (org-map-entries - (lambda () - (cl-destructuring-bind (level _reduced-level _todo _priority text tags) - (org-heading-components) - (let ((path (org-get-outline-path))) - (when (and (or (null depth) - (<= level depth)) - (or (null tags) - (not (string-match-p ":TOC" tags)))) - (propertize - (mapconcat - 'identity - (list (mapconcat #'identity - (append (when include-files - (list (or (+org-get-global-property "TITLE") - (file-relative-name buffer-file-name)))) - path - (list (replace-regexp-in-string org-link-any-re "\\4" text))) - " > ") + (delq + nil + (org-map-entries + (lambda () + (cl-destructuring-bind (level _reduced-level _todo _priority text tags) + (org-heading-components) + (when (and (or (null depth) + (<= level depth)) + (or (null tags) + (not (string-match-p ":TOC" tags)))) + (let ((path (org-get-outline-path))) + (list (string-join + (list (string-join + (append (when include-files + (list (or (+org-get-global-property "TITLE") + (file-relative-name (buffer-file-name))))) + path + (list (replace-regexp-in-string org-link-any-re "\\4" text))) + " > ") tags) " ") - 'location (cons buffer-file-name (point))))))) - t 'agenda)) + (buffer-file-name) + (point)))))) + t 'agenda)) (mapc #'kill-buffer org-agenda-new-buffers) (setq org-agenda-new-buffers nil)))) @@ -163,17 +149,23 @@ selection of all minor-modes, active or not." ;;;###autoload (defun doom-completing-read-org-headings (prompt files &optional depth include-files initial-input extra-candidates) "TODO" - (let (ivy-sort-functions-alist) - (if-let* ((result (completing-read - prompt - (append (doom--org-headings files depth include-files) - extra-candidates) - nil nil initial-input))) - (cl-destructuring-bind (file . location) - (get-text-property 0 'location result) + (let ((alist + (append (doom--org-headings files depth include-files) + extra-candidates)) + ivy-sort-functions-alist) + (if-let (result (completing-read prompt alist nil nil initial-input)) + (cl-destructuring-bind (file &optional location) + (cdr (assoc result alist)) (find-file file) - (when location - (goto-char location))) + (cond ((functionp location) + (funcall location)) + (location + (goto-char location))) + (ignore-errors + (when (outline-invisible-p) + (save-excursion + (outline-previous-visible-heading 1) + (org-show-subtree))))) (user-error "Aborted")))) ;;;###autoload @@ -189,7 +181,7 @@ selection of all minor-modes, active or not." (find-file (expand-file-name "index.org" doom-docs-dir))) ;;;###autoload -(defun doom/help-search (&optional initial-input) +(defun doom/help-search-headings (&optional initial-input) "Search Doom's documentation and jump to a headline." (interactive) (doom-completing-read-org-headings @@ -201,11 +193,29 @@ selection of all minor-modes, active or not." "faq.org") 2 t initial-input (mapcar (lambda (x) - (propertize (concat "Doom Modules > " x) - 'location - (get-text-property (1- (length x)) 'location x))) + (setcar x (concat "Doom Modules > " (car x))) + x) (doom--help-modules-list)))) +;;;###autoload +(defun doom/help-search (&optional initial-input) + "Preform a text search on all of Doom's documentation." + (interactive) + (funcall (cond ((fboundp '+ivy-file-search) + #'+ivy-file-search) + ((fboundp '+helm-file-search) + #'+helm-file-search) + ((rgrep + (read-regexp + "Search for" (or initial-input 'grep-tag-default) + 'grep-regexp-history) + "*.org" doom-emacs-dir) + #'ignore)) + :query initial-input + :args '("-g" "*.org") + :in doom-emacs-dir + :prompt "Search documentation for: ")) + ;;;###autoload (defun doom/help-news-search (&optional initial-input) "Search headlines in Doom's newsletters." @@ -298,30 +308,19 @@ without needing to check if they are available." (describe-function fn)))) (defun doom--help-modules-list () - (cl-loop for path in (doom-module-load-path 'all) + (cl-loop for path in (cdr (doom-module-load-path 'all)) for (cat . mod) = (doom-module-from-path path) - for location = (cons (or (doom-module-locate-path cat mod "README.org") - (doom-module-locate-path cat mod)) - nil) - for format = (propertize (format "%s %s" cat mod) - 'location location) + for readme-path = (or (doom-module-locate-path cat mod "README.org") + (doom-module-locate-path cat mod)) + for format = (format "%s %s" cat mod) if (doom-module-p cat mod) - collect format + collect (list format readme-path) else if (and cat mod) - collect - (propertize - format - 'face 'font-lock-comment-face - 'location location))) + collect (list (propertize format 'face 'font-lock-comment-face) + readme-path))) (defun doom--help-current-module-str () - (cond ((and buffer-file-name - (eq major-mode 'emacs-lisp-mode) - (file-in-directory-p buffer-file-name doom-private-dir) - (save-excursion (goto-char (point-min)) - (re-search-forward "^\\s-*(doom! " nil t)) - (thing-at-point 'sexp t))) - ((save-excursion + (cond ((save-excursion (require 'smartparens) (ignore-errors (sp-beginning-of-sexp) @@ -330,45 +329,54 @@ without needing to check if they are available." (let ((sexp (sexp-at-point))) (when (memq (car-safe sexp) '(featurep! require!)) (format "%s %s" (nth 1 sexp) (nth 2 sexp))))))) - ((and buffer-file-name - (when-let (mod (doom-module-from-path buffer-file-name)) - (format "%s %s" (car mod) (cdr mod))))) + ((when buffer-file-name + (when-let (mod (doom-module-from-path buffer-file-name)) + (unless (memq (car mod) '(:core :private)) + (format "%s %s" (car mod) (cdr mod)))))) ((when-let (mod (cdr (assq major-mode doom--help-major-mode-module-alist))) (format "%s %s" (symbol-name (car mod)) (symbol-name (cadr mod))))))) ;;;###autoload -(defun doom/help-modules (category module) +(defun doom/help-modules (category module &optional visit-dir) "Open the documentation for a Doom module. CATEGORY is a keyword and MODULE is a symbol. e.g. :editor and 'evil. +If VISIT-DIR is non-nil, visit the module's directory rather than its +documentation. + Automatically selects a) the module at point (in private init files), b) the module derived from a `featurep!' or `require!' call, c) the module that the current file is in, or d) the module associated with the current major mode (see `doom--help-major-mode-module-alist')." (interactive - (let* ((module-string - (completing-read "Describe module: " - (doom--help-modules-list) - nil t nil nil - (doom--help-current-module-str))) - (key (doom-module-from-path - (car (get-text-property 0 'location module-string))))) - (list (car key) - (cdr key)))) + (mapcar #'intern + (split-string + (completing-read "Describe module: " + (doom--help-modules-list) + nil t nil nil + (doom--help-current-module-str)) + " " t))) (cl-check-type category symbol) (cl-check-type module symbol) - (let ((path (doom-module-locate-path category module))) + (cl-destructuring-bind (module-string path) + (or (assoc (format "%s %s" category module) (doom--help-modules-list)) + (user-error "'%s %s' is not a valid module" category module)) + (setq module-string (substring-no-properties module-string)) (unless (file-readable-p path) - (error "'%s %s' isn't a valid module; it doesn't exist" category module)) - (if-let* ((readme-path (doom-module-locate-path category module "README.org"))) - (find-file readme-path) - (if (y-or-n-p (format "The '%s %s' module has no README file. Explore its directory?" - category module)) - (doom-project-browse path) - (user-error "Aborted module lookup"))))) + (error "Can't find or read %S module at %S" module-string path)) + (cond ((not (file-directory-p path)) + (if visit-dir + (doom-project-browse (file-name-directory path)) + (find-file path))) + (visit-dir + (doom-project-browse path)) + ((y-or-n-p (format "The %S module has no README file. Explore its directory?" + module-string)) + (doom-project-browse (file-name-directory path))) + ((user-error "Aborted module lookup"))))) ;; @@ -431,9 +439,9 @@ If prefix arg is present, refresh the cache." (list (intern (completing-read (if guess - (format "Select package to search for (default %s): " + (format "Select Doom package to search for (default %s): " guess) - "Describe package: ") + "Describe Doom package: ") packages nil t nil nil (if guess (symbol-name guess)))))))) (require 'core-packages) @@ -511,7 +519,7 @@ If prefix arg is present, refresh the cache." (insert "\n\n"))))) (defvar doom--package-cache nil) -(defun doom--package-list () +(defun doom--package-list (&optional prompt) (let* ((guess (or (function-called-at-point) (symbol-at-point)))) (require 'finder-inf nil t) @@ -527,10 +535,11 @@ If prefix arg is present, refresh the cache." (setq doom--package-cache packages) (unless (memq guess packages) (setq guess nil)) - (intern (completing-read (if guess - (format "Select package to search for (default %s): " - guess) - "Describe package: ") + (intern (completing-read (or prompt + (if guess + (format "Select package to search for (default %s): " + guess) + "Describe package: ")) packages nil t nil nil (if guess (symbol-name guess))))))) @@ -577,7 +586,7 @@ If prefix arg is present, refresh the cache." This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include config blocks in your private config." - (interactive (list (doom--package-list))) + (interactive (list (doom--package-list "Find package config: "))) (cl-destructuring-bind (file line _match) (split-string (completing-read @@ -591,55 +600,49 @@ config blocks in your private config." (recenter))) ;;;###autoload -(defun doom/help-package-homepage (package) - "Open PACKAGE's repo or homepage in your browser." - (interactive (list (doom--package-list))) - (browse-url (doom--package-url package))) +(defalias 'doom/help-package-homepage #'straight-visit-package-website) + +(defun doom--help-search-prompt (prompt) + (let ((query (doom-thing-at-point-or-region))) + (if (featurep 'counsel) + query + (read-string prompt query 'git-grep query)))) + +(defvar counsel-rg-base-command) +(defun doom--help-search (dirs query prompt) + ;; REVIEW Replace with deadgrep + (unless (executable-find "rg") + (user-error "Can't find ripgrep on your system")) + (if (fboundp 'counsel-rg) + (let ((counsel-rg-base-command + (concat counsel-rg-base-command " " + (mapconcat #'shell-quote-argument dirs " ")))) + (counsel-rg query nil "-Lz" prompt)) + ;; TODO Add helm support? + (grep-find + (string-join + (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" + (shell-quote-argument query)) + (mapcar #'shell-quote-argument dirs)) + " ")))) ;;;###autoload (defun doom/help-search-load-path (query) "Perform a text search on your `load-path'. Uses the symbol at point or the current selection, if available." (interactive - (let ((query - ;; TODO Generalize this later; into something the lookup module and - ;; project search commands could as well - (if (use-region-p) - (buffer-substring-no-properties (region-beginning) (region-end)) - (or (symbol-name (symbol-at-point)) "")))) - (list (read-string - (format "Search load-path (default: %s): " query) - nil 'git-grep query)))) - ;; REVIEW Replace with deadgrep - (grep-find - (mapconcat - #'shell-quote-argument - (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query) - (cl-remove-if-not #'file-directory-p load-path)) - " "))) + (list (doom--help-search-prompt "Search load-path: "))) + (doom--help-search (cl-remove-if-not #'file-directory-p load-path) + query "Search load-path: ")) -;; TODO factor our the duplicate code between this and the above ;;;###autoload (defun doom/help-search-loaded-files (query) "Perform a text search on your `load-path'. Uses the symbol at point or the current selection, if available." (interactive - (let ((query - ;; TODO Generalize this later; into something the lookup module and - ;; project search commands could as well. - (if (use-region-p) - (buffer-substring-no-properties (region-beginning) (region-end)) - (or (symbol-name (symbol-at-point)) "")))) - (list (read-string - (format "Search load-path (default: %s): " query) - nil 'git-grep query)))) - (unless (executable-find "rg") - (user-error "Can't find ripgrep on your system")) - (require 'elisp-refs) - ;; REVIEW Replace with deadgrep - (grep-find - (mapconcat - #'shell-quote-argument - (append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query) - (cl-remove-if-not #'file-directory-p (elisp-refs--loaded-paths))) - " "))) + (list (doom--help-search-prompt "Search loaded files: "))) + (let ((paths (cl-loop for (file . _) in load-history + for filebase = (file-name-sans-extension file) + if (file-exists-p! (format "%s.el" filebase)) + collect it))) + (doom--help-search paths query "Search loaded files: "))) diff --git a/core/autoload/packages.el b/core/autoload/packages.el index 7406dddc1..9d6d7eed0 100644 --- a/core/autoload/packages.el +++ b/core/autoload/packages.el @@ -13,6 +13,13 @@ nil-value) plist))) +;;;###autoload +(defun doom-package-set (package prop value) + "Set PROPERTY in PACKAGE's recipe to VALUE." + (setf (alist-get package doom-packages) + (plist-put (alist-get package doom-packages) + prop value))) + ;;;###autoload (defun doom-package-recipe (package &optional prop nil-value) "Returns the `straight' recipe PACKAGE was registered with." @@ -23,6 +30,14 @@ nil-value) plist))) +;;;###autoload +(defun doom-package-recipe-repo (package) + "Resolve and return PACKAGE's (symbol) local-repo property." + (if-let* ((recipe (cdr (straight-recipes-retrieve package))) + (repo (straight-vc-local-repo-name recipe))) + repo + (symbol-name package))) + ;;;###autoload (defun doom-package-build-recipe (package &optional prop nil-value) "Returns the `straight' recipe PACKAGE was installed with." @@ -39,7 +54,7 @@ (car (gethash (symbol-name package) straight--build-cache))) ;;;###autoload -(defun doom-package-dependencies (package &optional recursive noerror) +(defun doom-package-dependencies (package &optional recursive _noerror) "Return a list of dependencies for a package." (let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache)))) (if recursive @@ -47,6 +62,7 @@ deps)) deps))) +;;;###autoload (defun doom-package-depending-on (package &optional noerror) "Return a list of packages that depend on the package named NAME." (cl-check-type name symbol) @@ -155,6 +171,7 @@ was installed with." ((debug error) (signal 'doom-package-error (list (doom-module-from-path file) + file e)))))) ;;;###autoload @@ -167,8 +184,7 @@ If ALL-P, gather packages unconditionally across all modules, including disabled ones." (let ((doom-interactive-mode t) (doom-modules (doom-modules)) - doom-packages - doom-disabled-packages) + doom-packages) (doom--read-module-packages-file (doom-path doom-core-dir "packages.el") all-p t) (let ((private-packages (doom-path doom-private-dir "packages.el"))) @@ -189,6 +205,67 @@ ones." (doom--read-module-packages-file private-packages all-p t)) (nreverse doom-packages))) +;;;###autoload +(defun doom-package-pinned-list () + "Return an alist mapping package names (strings) to pinned commits (strings)." + (let (alist) + (dolist (package doom-packages alist) + (cl-destructuring-bind (name &key disable ignore pin unpin &allow-other-keys) + package + (when (and (not ignore) + (not disable) + (or pin unpin)) + (setf (alist-get (doom-package-recipe-repo name) alist + nil 'remove #'equal) + (unless unpin pin))))))) + +;;;###autoload +(defun doom-package-unpinned-list () + "Return an alist mapping package names (strings) to pinned commits (strings)." + (let (alist) + (dolist (package doom-packages alist) + (cl-destructuring-bind + (_ &key recipe disable ignore pin unpin &allow-other-keys) + package + (when (and (not ignore) + (not disable) + (or unpin + (and (plist-member recipe :pin) + (null pin)))) + (cl-pushnew (doom-package-recipe-repo (car package)) alist + :test #'equal)))))) + +;;;###autoload +(defun doom-package-recipe-list () + "Return straight recipes for non-builtin packages with a local-repo." + (let (recipes) + (dolist (recipe (hash-table-values straight--recipe-cache)) + (cl-destructuring-bind (&key local-repo type no-build &allow-other-keys) + recipe + (unless (or (null local-repo) + (eq type 'built-in) + no-build) + (push recipe recipes)))) + (nreverse recipes))) + +;;;###autoload +(defmacro doom-with-package-recipes (recipes binds &rest body) + "TODO" + (declare (indent 2)) + (let ((recipe-var (make-symbol "recipe")) + (recipes-var (make-symbol "recipes"))) + `(let* ((,recipes-var ,recipes) + (built ()) + (straight-use-package-pre-build-functions + (cons (lambda (pkg) (cl-pushnew pkg built :test #'equal)) + straight-use-package-pre-build-functions))) + (dolist (,recipe-var ,recipes-var) + (cl-block nil + (straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var) + ,(doom-enlist binds) + ,@body))) + (nreverse built)))) + ;; ;;; Main functions @@ -201,3 +278,48 @@ ones." (message "Reloading packages") (doom-initialize-packages t) (message "Reloading packages...DONE")) + +;;;###autoload +(defun doom/update-pinned-package-form (&optional select) + "Inserts or updates a `:pin' for the `package!' statement at point. + +Grabs the latest commit id of the package using 'git'." + (interactive "P") + ;; REVIEW Better error handling + ;; TODO Insert a new `package!' if no `package!' at poin + (require 'straight) + (ignore-errors + (while (and (atom (sexp-at-point)) + (not (bolp))) + (forward-sexp -1))) + (save-excursion + (if (not (eq (sexp-at-point) 'package!)) + (user-error "Not on a `package!' call") + (backward-char) + (let* ((recipe (cdr (sexp-at-point))) + (package (car recipe)) + (oldid (doom-package-get package :pin)) + (id + (cdr (doom-call-process + "git" "ls-remote" + (straight-vc-git--destructure + (doom-plist-merge + (plist-get (cdr recipe) :recipe) + (or (cdr (straight-recipes-retrieve package)) + (plist-get (cdr (assq package doom-packages)) :recipe))) + (upstream-repo upstream-host) + (straight-vc-git--encode-url upstream-repo upstream-host)))))) + (unless id + (user-error "No id for %S package" package)) + (let* ((id (if select + (car (split-string (completing-read "Commit: " (split-string id "\n" t)))) + (car (split-string id)))) + (id (substring id 0 10))) + (if (and oldid (string-match-p (concat "^" oldid) id)) + (user-error "No update necessary") + (if (re-search-forward ":pin +\"\\([^\"]+\\)\"" (cdr (bounds-of-thing-at-point 'sexp)) t) + (replace-match id t t nil 1) + (thing-at-point--end-of-sexp) + (backward-char) + (insert " :pin " (prin1-to-string id))) + (message "Updated %S: %s -> %s" package oldid id))))))) diff --git a/core/autoload/plist.el b/core/autoload/plist.el index aeb9c7f9a..87f897598 100644 --- a/core/autoload/plist.el +++ b/core/autoload/plist.el @@ -66,7 +66,7 @@ BODY." ;;;###autoload (defun doom-plist-merge (from-plist to-plist) - "Destructively merge FROM-PLIST onto TO-PLIST" + "Non-destructively merge FROM-PLIST onto TO-PLIST" (let ((plist (copy-sequence from-plist))) (while plist (plist-put! to-plist (pop plist) (pop plist))) @@ -83,11 +83,11 @@ BODY." p)) ;;;###autoload -(defun doom-plist-delete (plist prop) - "Delete PROP from a copy of PLIST." +(defun doom-plist-delete (plist &rest props) + "Delete PROPS from a copy of PLIST." (let (p) (while plist - (if (not (eq prop (car plist))) + (if (not (memq (car plist) props)) (plist-put! p (car plist) (nth 1 plist))) (setq plist (cddr plist))) p)) diff --git a/core/autoload/projects.el b/core/autoload/projects.el index 6decf3501..edce48b76 100644 --- a/core/autoload/projects.el +++ b/core/autoload/projects.el @@ -1,6 +1,8 @@ ;;; core/autoload/projects.el -*- lexical-binding: t; -*- (defvar projectile-project-root nil) +(defvar projectile-enable-caching) +(defvar projectile-require-project-root) ;;;###autoload (autoload 'projectile-relevant-known-projects "projectile") diff --git a/core/autoload/scratch.el b/core/autoload/scratch.el index a93456ebc..3d4947f0d 100644 --- a/core/autoload/scratch.el +++ b/core/autoload/scratch.el @@ -96,9 +96,10 @@ following: ;; ;;; Commands +(defvar projectile-enable-caching) ;;;###autoload (defun doom/open-scratch-buffer (&optional arg project-p) - "Opens the (persistent) scratch buffer in a popup. + "Pop up a persistent scratch buffer. If passed the prefix ARG, switch to it in the current window. If PROJECT-P is non-nil, open a persistent scratch buffer associated with the diff --git a/core/autoload/sessions.el b/core/autoload/sessions.el index 2e6ec18a6..0d2833802 100644 --- a/core/autoload/sessions.el +++ b/core/autoload/sessions.el @@ -47,10 +47,16 @@ "TODO" (setq file (expand-file-name (or file (doom-session-file)))) (message "Attempting to load %s" file) - (cond ((require 'persp-mode nil t) + (cond ((not (file-readable-p file)) + (message "No session file at %S to read from" file)) + ((require 'persp-mode nil t) (unless persp-mode (persp-mode +1)) - (persp-load-state-from-file file)) + (let ((allowed (persp-list-persp-names-in-file file))) + (cl-loop for name being the hash-keys of *persp-hash* + unless (member name allowed) + do (persp-kill name)) + (persp-load-state-from-file file))) ((and (require 'frameset nil t) (require 'restart-emacs nil t)) (restart-emacs--restore-frames-using-desktop file)) @@ -125,4 +131,7 @@ (setq doom-autosave-session nil) (doom/quicksave-session) (restart-emacs - (delq nil (list (if debug "--debug-init") "--restore")))) + (append (if debug (list "--debug-init")) + (when (boundp 'chemacs-current-emacs-profile) + (list "--with-profile" chemacs-current-emacs-profile)) + (list "--restore")))) diff --git a/core/autoload/text.el b/core/autoload/text.el index 8c08c5a67..ae91e71e7 100644 --- a/core/autoload/text.el +++ b/core/autoload/text.el @@ -1,5 +1,17 @@ ;;; core/autoload/text.el -*- lexical-binding: t; -*- +(defvar doom-point-in-comment-functions () + "List of functions to run to determine if point is in a comment. + +Each function takes one argument: the position of the point. Stops on the first +function to return non-nil. Used by `doom-point-in-comment-p'.") + +(defvar doom-point-in-string-functions () + "List of functions to run to determine if point is in a string. + +Each function takes one argument: the position of the point. Stops on the first +function to return non-nil. Used by `doom-point-in-string-p'.") + ;;;###autoload (defun doom-surrounded-p (pair &optional inline balanced) "Returns t if point is surrounded by a brace delimiter: {[( @@ -28,31 +40,18 @@ lines, above and below, with only whitespace in between." ;;;###autoload (defun doom-point-in-comment-p (&optional pos) "Return non-nil if POS is in a comment. - POS defaults to the current position." - ;; REVIEW Should we cache `syntax-ppss'? - (let* ((pos (or pos (point))) - (ppss (syntax-ppss pos))) - (or (nth 4 ppss) - (nth 8 ppss) - (and (< pos (point-max)) - (memq (char-syntax (char-after pos)) '(?< ?>)) - (not (eq (char-after pos) ?\n))) - (when-let (s (car (syntax-after pos))) - (or (and (/= 0 (logand (lsh 1 16) s)) - (nth 4 (doom-syntax-ppss (+ pos 2)))) - (and (/= 0 (logand (lsh 1 17) s)) - (nth 4 (doom-syntax-ppss (+ pos 1)))) - (and (/= 0 (logand (lsh 1 18) s)) - (nth 4 (doom-syntax-ppss (- pos 1)))) - (and (/= 0 (logand (lsh 1 19) s)) - (nth 4 (doom-syntax-ppss (- pos 2))))))))) + (let ((pos (or pos (point)))) + (or (run-hook-with-args-until-success 'doom-point-in-comment-functions pos) + (sp-point-in-comment pos)))) ;;;###autoload (defun doom-point-in-string-p (&optional pos) "Return non-nil if POS is in a string." ;; REVIEW Should we cache `syntax-ppss'? - (nth 3 (syntax-ppss pos))) + (let ((pos (or pos (point)))) + (or (run-hook-with-args-until-success 'doom-point-in-string-functions pos) + (sp-point-in-string pos)))) ;;;###autoload (defun doom-point-in-string-or-comment-p (&optional pos) @@ -60,74 +59,157 @@ POS defaults to the current position." (or (doom-point-in-string-p pos) (doom-point-in-comment-p pos))) +;;;###autoload +(defun doom-region-active-p () + "Return non-nil if selection is active. +Detects evil visual mode as well." + (declare (side-effect-free t)) + (or (use-region-p) + (and (bound-and-true-p evil-local-mode) + (evil-visual-state-p)))) + +;;;###autoload +(defun doom-region-beginning () + "Return beginning position of selection. +Uses `evil-visual-beginning' if available." + (declare (side-effect-free t)) + (if (bound-and-true-p evil-local-mode) + evil-visual-beginning + (region-beginning))) + +;;;###autoload +(defun doom-region-end () + "Return end position of selection. +Uses `evil-visual-end' if available." + (declare (side-effect-free t)) + (if (bound-and-true-p evil-local-mode) + evil-visual-end + (region-end))) + +;;;###autoload +(defun doom-thing-at-point-or-region (&optional thing prompt) + "Grab the current selection, THING at point, or xref identifier at point. + +Returns THING if it is a string. Otherwise, if nothing is found at point and +PROMPT is non-nil, prompt for a string (if PROMPT is a string it'll be used as +the prompting string). Returns nil if all else fails. + +NOTE: Don't use THING for grabbing symbol-at-point. The xref fallback is smarter +in some cases." + (declare (side-effect-free t)) + (cond ((stringp thing) + thing) + ((doom-region-active-p) + (buffer-substring-no-properties + (doom-region-beginning) + (doom-region-end))) + (thing + (thing-at-point thing t)) + ((require 'xref nil t) + ;; A little smarter than using `symbol-at-point', though in most cases, + ;; xref ends up using `symbol-at-point' anyway. + (xref-backend-identifier-at-point (xref-find-backend))) + (prompt + (read-string (if (stringp prompt) prompt ""))))) + ;; ;;; Commands -(defvar doom--last-backward-pt most-positive-fixnum) +(defun doom--bol-bot-eot-eol (&optional pos) + (save-excursion + (when pos + (goto-char pos)) + (let* ((bol (if visual-line-mode + (save-excursion + (beginning-of-visual-line) + (point)) + (line-beginning-position))) + (bot (save-excursion + (goto-char bol) + (skip-chars-forward " \t\r") + (point))) + (eol (if visual-line-mode + (save-excursion (end-of-visual-line) (point)) + (line-end-position))) + (eot (or (save-excursion + (if (not comment-use-syntax) + (progn + (goto-char bol) + (when (re-search-forward comment-start-skip eol t) + (or (match-end 1) (match-beginning 0)))) + (goto-char eol) + (while (and (doom-point-in-comment-p) + (> (point) bol)) + (backward-char)) + (skip-chars-backward " " bol) + (unless (or (eq (char-after) 32) (eolp)) + (forward-char)) + (point))) + eol))) + (list bol bot eot eol)))) + +(defvar doom--last-backward-pt nil) ;;;###autoload -(defun doom/backward-to-bol-or-indent () +(defun doom/backward-to-bol-or-indent (&optional point) "Jump between the indentation column (first non-whitespace character) and the beginning of the line. The opposite of `doom/forward-to-last-non-comment-or-eol'." - (interactive) - (let ((pt (point))) - (cl-destructuring-bind (bol . bot) - (save-excursion - (beginning-of-visual-line) - (cons (point) - (progn (skip-chars-forward " \t\r") - (point)))) + (interactive "d") + (let ((pt (or point (point)))) + (cl-destructuring-bind (bol bot _eot _eol) + (doom--bol-bot-eot-eol pt) (cond ((> pt bot) (goto-char bot)) ((= pt bol) - (goto-char (min doom--last-backward-pt bot)) - (setq doom--last-backward-pt most-positive-fixnum)) + (or (and doom--last-backward-pt + (= (line-number-at-pos doom--last-backward-pt) + (line-number-at-pos pt))) + (setq doom--last-backward-pt nil)) + (goto-char (or doom--last-backward-pt bot)) + (setq doom--last-backward-pt nil)) ((<= pt bot) (setq doom--last-backward-pt pt) (goto-char bol)))))) -(defvar doom--last-forward-pt -1) +(defvar doom--last-forward-pt nil) ;;;###autoload -(defun doom/forward-to-last-non-comment-or-eol () +(defun doom/forward-to-last-non-comment-or-eol (&optional point) "Jumps between the last non-blank, non-comment character in the line and the true end of the line. The opposite of `doom/backward-to-bol-or-indent'." + (interactive "d") + (let ((pt (or point (point)))) + (cl-destructuring-bind (_bol _bot eot eol) + (doom--bol-bot-eot-eol pt) + (cond ((< pt eot) + (goto-char eot)) + ((= pt eol) + (goto-char (or doom--last-forward-pt eot)) + (setq doom--last-forward-pt nil)) + ((>= pt eot) + (setq doom--last-backward-pt pt) + (goto-char eol)))))) + +;;;###autoload +(defun doom/backward-kill-to-bol-and-indent () + "Kill line to the first non-blank character. If invoked again afterwards, kill +line to beginning of line. Same as `evil-delete-back-to-indentation'." (interactive) - (let ((eol (if (not visual-line-mode) - (line-end-position) - (save-excursion (end-of-visual-line) (point))))) - (if (or (and (< (point) eol) - (sp-point-in-comment)) - (not (sp-point-in-comment eol))) - (if (= (point) eol) - (progn - (goto-char doom--last-forward-pt) - (setq doom--last-forward-pt -1)) - (setq doom--last-forward-pt (point)) - (goto-char eol)) - (let* ((bol (save-excursion (beginning-of-visual-line) (point))) - (boc (or (save-excursion - (if (not comment-use-syntax) - (progn - (goto-char bol) - (when (re-search-forward comment-start-skip eol t) - (or (match-end 1) (match-beginning 0)))) - (goto-char eol) - (while (and (sp-point-in-comment) - (> (point) bol)) - (backward-char)) - (skip-chars-backward " " bol) - (point))) - eol))) - (when (> doom--last-forward-pt boc) - (setq boc doom--last-forward-pt)) - (if (or (= eol (point)) - (> boc (point))) - (progn - (goto-char boc) - (setq doom--last-forward-pt -1)) - (setq doom--last-forward-pt (point)) - (goto-char eol)))))) + (let ((empty-line-p (save-excursion (beginning-of-line) + (looking-at-p "[ \t]*$")))) + (funcall (if (fboundp 'evil-delete) + #'evil-delete + #'delete-region) + (point-at-bol) (point)) + (unless empty-line-p + (indent-according-to-mode)))) + +;;;###autoload +(defun doom/delete-backward-word (arg) + "Like `backward-kill-word', but doesn't affect the kill-ring." + (interactive "p") + (let (kill-ring) + (backward-kill-word arg))) ;;;###autoload (defun doom/dumb-indent () @@ -155,20 +237,6 @@ true end of the line. The opposite of `doom/backward-to-bol-or-indent'." tab-width (- tab-width movement))))))))) -;;;###autoload -(defun doom/backward-kill-to-bol-and-indent () - "Kill line to the first non-blank character. If invoked again -afterwards, kill line to beginning of line." - (interactive) - (let ((empty-line-p (save-excursion (beginning-of-line) - (looking-at-p "[ \t]*$")))) - (funcall (if (fboundp 'evil-delete) - #'evil-delete - #'delete-region) - (point-at-bol) (point)) - (unless empty-line-p - (indent-according-to-mode)))) - ;;;###autoload (defun doom/retab (arg &optional beg end) "Converts tabs-to-spaces or spaces-to-tabs within BEG and END (defaults to diff --git a/core/autoload/themes.el b/core/autoload/themes.el index 363c4b33c..2fb84ef3f 100644 --- a/core/autoload/themes.el +++ b/core/autoload/themes.el @@ -19,12 +19,13 @@ all themes. It will apply to all themes once they are loaded." `(let ((fn (gensym "doom--customize-themes-h-"))) (fset fn (lambda () - (dolist (theme (doom-enlist (or ,theme 'user))) - (when (or (eq theme 'user) - (custom-theme-enabled-p theme)) - (apply #'custom-theme-set-faces 'user - (mapcan #'doom--custom-theme-set-face - (list ,@specs))))))) + (let (custom--inhibit-theme-enable) + (dolist (theme (doom-enlist (or ,theme 'user))) + (when (or (eq theme 'user) + (custom-theme-enabled-p theme)) + (apply #'custom-theme-set-faces theme + (mapcan #'doom--custom-theme-set-face + (list ,@specs)))))))) (when (or doom-init-theme-p (null doom-theme)) (funcall fn)) (add-hook 'doom-load-theme-hook fn 'append))) diff --git a/core/autoload/ui.el b/core/autoload/ui.el index 7a0b8a447..d4c0ea2f8 100644 --- a/core/autoload/ui.el +++ b/core/autoload/ui.el @@ -1,7 +1,7 @@ ;;; core/autoload/ui.el -*- lexical-binding: t; -*- ;; -;; Public library +;;; Public library ;;;###autoload (defun doom-resize-window (window new-size &optional horizontal force-p) @@ -24,7 +24,7 @@ are open." ;; -;; Advice +;;; Advice ;;;###autoload (defun doom-recenter-a (&rest _) @@ -43,7 +43,7 @@ In tty Emacs, messages suppressed completely." ;; -;; Hooks +;;; Hooks ;;;###autoload (defun doom-apply-ansi-color-to-compilation-buffer-h () @@ -57,9 +57,17 @@ In tty Emacs, messages suppressed completely." "Turn off `show-paren-mode' buffer-locally." (setq-local show-paren-mode nil)) +;;;###autoload +(defun doom-enable-line-numbers-h () + (display-line-numbers-mode +1)) + +;;;###autoload +(defun doom-disable-line-numbers-h () + (display-line-numbers-mode -1)) + ;; -;; Commands +;;; Commands ;;;###autoload (defun doom/toggle-line-numbers () @@ -95,6 +103,7 @@ See `display-line-numbers' for what these values mean." (delete-frame)) (save-buffers-kill-emacs))) +(defvar doom--maximize-last-wconf nil) ;;;###autoload (defun doom/window-maximize-buffer () "Close other windows to focus on this one. Activate again to undo this. If the @@ -102,41 +111,40 @@ window changes before then, the undo expires. Alternatively, use `doom/window-enlargen'." (interactive) - (if (and (one-window-p) - (assq ?_ register-alist)) - (jump-to-register ?_) - (when (and (bound-and-true-p +popup-mode) - (+popup-window-p)) - (user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead")) - (window-configuration-to-register ?_) - (delete-other-windows))) + (setq doom--maximize-last-wconf + (if (and (null (cdr (cl-remove-if #'window-dedicated-p (window-list)))) + doom--maximize-last-wconf) + (ignore (set-window-configuration doom--maximize-last-wconf)) + (when (and (bound-and-true-p +popup-mode) + (+popup-window-p)) + (user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead")) + (prog1 (current-window-configuration) + (delete-other-windows))))) -(defvar doom--window-enlargened nil) +(defvar doom--enlargen-last-wconf nil) ;;;###autoload (defun doom/window-enlargen () "Enlargen the current window to focus on this one. Does not close other -windows (unlike `doom/window-maximize-buffer') Activate again to undo." +windows (unlike `doom/window-maximize-buffer'). Activate again to undo." (interactive) - (setq doom--window-enlargened - (if (and doom--window-enlargened - (assq ?_ register-alist)) - (ignore (ignore-errors (jump-to-register ?_))) - (window-configuration-to-register ?_) - (let* ((window (selected-window)) - (dedicated-p (window-dedicated-p window)) - (preserved-p (window-parameter window 'window-preserved-size)) - (ignore-window-parameters t)) - (unwind-protect - (progn - (when dedicated-p - (set-window-dedicated-p window nil)) - (when preserved-p - (set-window-parameter window 'window-preserved-size nil)) - (maximize-window window)) - (set-window-dedicated-p window dedicated-p) - (when preserved-p - (set-window-parameter window 'window-preserved-size preserved-p))) - t)))) + (setq doom--enlargen-last-wconf + (if doom--enlargen-last-wconf + (ignore (set-window-configuration doom--enlargen-last-wconf)) + (prog1 (current-window-configuration) + (let* ((window (selected-window)) + (dedicated-p (window-dedicated-p window)) + (preserved-p (window-parameter window 'window-preserved-size)) + (ignore-window-parameters t)) + (unwind-protect + (progn + (when dedicated-p + (set-window-dedicated-p window nil)) + (when preserved-p + (set-window-parameter window 'window-preserved-size nil)) + (maximize-window window)) + (set-window-dedicated-p window dedicated-p) + (when preserved-p + (set-window-parameter window 'window-preserved-size preserved-p)))))))) ;;;###autoload (defun doom/window-maximize-horizontally () @@ -179,8 +187,7 @@ narrowing doesn't affect other windows displaying the same buffer. Call Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/" (interactive (list (or (bound-and-true-p evil-visual-beginning) (region-beginning)) - (or (bound-and-true-p evil-visual-end) (region-end)) - current-prefix-arg)) + (or (bound-and-true-p evil-visual-end) (region-end)))) (unless (region-active-p) (setq beg (line-beginning-position) end (line-end-position))) diff --git a/core/cli/autoloads.el b/core/cli/autoloads.el index 1e5906cf2..4cd2e5fd0 100644 --- a/core/cli/autoloads.el +++ b/core/cli/autoloads.el @@ -1,418 +1,244 @@ ;;; core/cli/autoloads.el -*- lexical-binding: t; -*- (defvar doom-autoload-excluded-packages '("gh") - "Packages that have silly or destructive autoload files that try to load + "What packages whose autoloads file we won't index. + +These packages have silly or destructive autoload files that try to load everyone in the universe and their dog, causing errors that make babies cry. No one wants that.") +(defvar doom-autoload-cached-vars + '(load-path + auto-mode-alist + interpreter-mode-alist + Info-directory-list + doom-disabled-packages) + "A list of variables to be cached in `doom-package-autoload-file'.") + ;; externs (defvar autoload-timestamps) (defvar generated-autoload-load-name) -(defvar generated-autoload-file) +(defun doom-cli-reload-autoloads () + "Reloads `doom-autoload-file' and `doom-package-autoload-file' files." + (doom-cli-reload-core-autoloads) + (doom-cli-reload-package-autoloads)) -;; -;;; Commands +(defun doom-cli-reload-core-autoloads (&optional file) + (print! (start "(Re)generating core autoloads...")) + (print-group! + (let ((file (or file doom-autoload-file)) + doom-autoload-cached-vars) + (cl-check-type file string) + (and (print! (start "Generating core autoloads...")) + (doom-cli--write-autoloads + file (doom-cli--generate-autoloads + (cl-loop for dir + in (append (list doom-core-dir) + (cdr (doom-module-load-path 'all-p)) + (list doom-private-dir)) + if (doom-glob dir "autoload.el") collect it + if (doom-glob dir "autoload/*.el") append it) + 'scan)) + (print! (start "Byte-compiling core autoloads file...")) + (doom-cli--byte-compile-file file) + (print! (success "Generated %s") + (relpath (byte-compile-dest-file file) + doom-emacs-dir)))))) -(defcli! (autoloads a) () - "Regenerates Doom's autoloads files. - -It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, -modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates and -byte-compiles `doom-autoload-file', as well as `doom-package-autoload-file' -(created from the concatenated autoloads files of all installed packages). - -It also caches `load-path', `Info-directory-list', `doom-disabled-packages', -`package-activated-list' and `auto-mode-alist'." - (straight-check-all) - (doom-cli-reload-autoloads nil 'force)) +(defun doom-cli-reload-package-autoloads (&optional file) + (print! (start "(Re)generating package autoloads...")) + (print-group! + (doom-initialize-packages) + (let ((file (or file doom-package-autoload-file))) + (cl-check-type file string) + (and (print! (start "Generating package autoloads...")) + (doom-cli--write-autoloads + file + (doom-cli--generate-var-cache doom-autoload-cached-vars) + (doom-cli--generate-autoloads + (mapcar #'straight--autoloads-file + (cl-set-difference (hash-table-keys straight--build-cache) + doom-autoload-excluded-packages + :test #'string=)))) + (print! (start "Byte-compiling package autoloads file...")) + (doom-cli--byte-compile-file file) + (print! (success "Generated %s") + (relpath (byte-compile-dest-file file) + doom-emacs-dir)))))) ;; ;;; Helpers -(defun doom--cli-delete-autoloads-file (file) - "Delete FILE (an autoloads file) and accompanying *.elc file, if any." - (cl-check-type file string) - (when (file-exists-p file) - (when-let (buf (find-buffer-visiting file)) - (with-current-buffer buf - (set-buffer-modified-p nil)) - (kill-buffer buf)) - (delete-file file) - (ignore-errors (delete-file (byte-compile-dest-file file))) - t)) +(defun doom-cli--write-autoloads (file &rest forms) + (make-directory (file-name-directory file) 'parents) + (condition-case-unless-debug e + (with-temp-file file + (let ((standard-output (current-buffer)) + (print-quoted t) + (print-level nil) + (print-length nil)) + (insert ";; -*- lexical-binding: t; -*-\n" + ";; This file is autogenerated by Doom, DO NOT EDIT IT!!\n") + (dolist (form (delq nil forms)) + (mapc #'print form)) + t)) + (error (delete-file file) + (signal 'doom-autoload-error (list file e))))) -(defun doom--cli-warn-refresh-session-h () - (message "Restart or reload Doom Emacs for changes to take effect:\n") - (message " M-x doom/restart-and-restore") - (message " M-x doom/restart") - (message " M-x doom/reload")) - -(defun doom--cli-byte-compile-file (file) - (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)) - (byte-compile-dynamic t) - (byte-compile-dynamic-docstrings t)) - (condition-case-unless-debug e +(defun doom-cli--byte-compile-file (file) + (condition-case-unless-debug e + (let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings)) + (byte-compile-dynamic t) + (byte-compile-dynamic-docstrings t)) (when (byte-compile-file file) - (prog1 (load file 'noerror 'nomessage 'nosuffix) - (when noninteractive - (add-hook 'doom-cli-post-success-execute-hook #'doom--cli-warn-refresh-session-h)))) - ((debug error) - (let ((backup-file (concat file ".bk"))) - (print! (warn "Copied backup to %s") (relpath backup-file)) - (copy-file file backup-file 'overwrite)) - (doom--cli-delete-autoloads-file file) - (signal 'doom-autoload-error (list file e)))))) + (unless doom-interactive-mode + (add-hook 'doom-cli-post-success-execute-hook #'doom-cli--warn-refresh-session-h)) + (load (byte-compile-dest-file file) nil t))) + (error + (delete-file (byte-compile-dest-file file)) + (signal 'doom-autoload-error (list file e))))) -(defun doom-cli-reload-autoloads (&optional file force-p) - "Reloads FILE (an autoload file), if it needs reloading. +(defun doom-cli--warn-refresh-session-h () + (print! "Restart or reload Doom Emacs for changes to take effect:") + (print-group! (print! "M-x doom/restart-and-restore") + (print! "M-x doom/restart") + (print! "M-x doom/reload"))) -FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If -it is nil, it will try to reload both. If FORCE-P (universal argument) do it -even if it doesn't need reloading!" - (or (null file) - (stringp file) - (signal 'wrong-type-argument (list 'stringp file))) - (if (stringp file) - (cond ((file-equal-p file doom-autoload-file) - (doom-cli-reload-core-autoloads force-p)) - ((file-equal-p file doom-package-autoload-file) - (doom-cli-reload-package-autoloads force-p)) - ((error "Invalid autoloads file: %s" file))) - (doom-cli-reload-core-autoloads force-p) - (doom-cli-reload-package-autoloads force-p))) +(defun doom-cli--generate-var-cache (vars) + `((setq ,@(cl-loop for var in vars + append `(,var ',(symbol-value var)))))) +(defun doom-cli--filter-form (form &optional expand) + (let ((func (car-safe form))) + (cond ((memq func '(provide custom-autoload)) + nil) + ((and (eq func 'add-to-list) + (memq (doom-unquote (cadr form)) + doom-autoload-cached-vars)) + nil) + ((not (eq func 'autoload)) + form) + ((and expand (not (file-name-absolute-p (nth 2 form)))) + (defvar doom--autoloads-path-cache nil) + (setf (nth 2 form) + (let ((path (nth 2 form))) + (or (cdr (assoc path doom--autoloads-path-cache)) + (when-let* ((libpath (locate-library path)) + (libpath (file-name-sans-extension libpath)) + (libpath (abbreviate-file-name libpath))) + (push (cons path libpath) doom--autoloads-path-cache) + libpath) + path))) + form) + (form)))) -;; -;;; Doom autoloads - -(defun doom--cli-generate-header (func) - (goto-char (point-min)) - (insert ";; -*- lexical-binding:t; -*-\n" - ";; This file is autogenerated by `" (symbol-name func) "', DO NOT EDIT !!\n\n")) - -(defun doom--cli-generate-autoloads (targets) - (let ((n 0)) - (dolist (file targets) - (insert - (with-temp-buffer - (cond ((not (doom-file-cookie-p file "if" t)) - (print! (debug "Ignoring %s") (relpath file))) - - ((let ((generated-autoload-load-name (file-name-sans-extension file))) - (autoload-generate-file-autoloads file (current-buffer))) - (print! (debug "Nothing in %s") (relpath file))) - - ((cl-incf n) - (print! (debug "Scanning %s...") (relpath file)))) - (buffer-string)))) - (print! (class (if (> n 0) 'success 'info) - "Scanned %d file(s)") - n))) - -(defun doom--cli-expand-autoload-paths (&optional allow-internal-paths) - (let ((load-path - ;; NOTE With `doom-private-dir' in `load-path', Doom autoloads files - ;; will be unable to declare autoloads for the built-in autoload.el - ;; Emacs package, should $DOOMDIR/autoload.el exist. Not sure why - ;; they'd want to though, so it's an acceptable compromise. - (append (list doom-private-dir) - doom-modules-dirs - (straight--directory-files (straight--build-dir) nil t) - load-path))) - (defvar doom--autoloads-path-cache nil) - (while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) - (let ((path (match-string 1))) - (replace-match - (or (cdr (assoc path doom--autoloads-path-cache)) - (when-let* ((libpath (or (and allow-internal-paths - (locate-library path nil (cons doom-emacs-dir doom-modules-dirs))) - (locate-library path))) - (libpath (file-name-sans-extension libpath)) - (libpath (abbreviate-file-name libpath))) - (push (cons path libpath) doom--autoloads-path-cache) - libpath) - path) - t t nil 1))))) - -(defun doom--cli-generate-autodefs-1 (path &optional member-p) - (let (forms) +(defun doom-cli--generate-autoloads-autodefs (file buffer module &optional module-enabled-p) + (with-temp-buffer + (insert-file-contents file) (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) - (let* ((sexp (sexp-at-point)) - (alt-sexp (match-string 1)) - (type (car sexp)) - (name (doom-unquote (cadr sexp))) - (origin (doom-module-from-path path))) - (cond - ((and (not member-p) - alt-sexp) - (push (read alt-sexp) forms)) - - ((memq type '(defun defmacro cl-defun cl-defmacro)) - (cl-destructuring-bind (_ _name arglist &rest body) sexp - (appendq! - forms - (list (if member-p - (make-autoload sexp path) - (let ((docstring - (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" - origin - (if (stringp (car body)) - (pop body) - "No documentation.")))) - (condition-case-unless-debug e - (if alt-sexp - (read alt-sexp) - (append - (list (pcase type - (`defun 'defmacro) - (`cl-defun `cl-defmacro) - (_ type)) - name arglist docstring) - (cl-loop for arg in arglist - if (and (symbolp arg) - (not (keywordp arg)) - (not (memq arg cl--lambda-list-keywords))) - collect arg into syms - else if (listp arg) - collect (car arg) into syms - finally return (if syms `((ignore ,@syms)))))) - ('error - (print! "- Ignoring autodef %s (%s)" name e) - nil)))) - `(put ',name 'doom-module ',origin))))) - - ((eq type 'defalias) - (cl-destructuring-bind (_type name target &optional docstring) sexp - (let ((name (doom-unquote name)) - (target (doom-unquote target))) - (unless member-p - (setq target #'ignore - docstring - (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" - origin docstring))) - (appendq! forms `((put ',name 'doom-module ',origin) - (defalias ',name #',target ,docstring)))))) - - (member-p (push sexp forms))))) - forms)) - -(defun doom--cli-generate-autodefs (targets enabled-targets) - (goto-char (point-max)) - (search-backward ";;;***" nil t) - (save-excursion (insert "\n")) - (dolist (path targets) - (insert - (with-temp-buffer - (insert-file-contents path) - (if-let (forms (doom--cli-generate-autodefs-1 path (member path enabled-targets))) - (concat (mapconcat #'prin1-to-string (nreverse forms) "\n") - "\n") - ""))))) - -(defun doom--cli-cleanup-autoloads () - (goto-char (point-min)) - (when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t) - (replace-match "" t t))) - -(defun doom-cli-reload-core-autoloads (&optional force-p) - "Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil). - -It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el, -modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates -`doom-autoload-file'. - -Run this whenever your `doom!' block, or a module autoload file, is modified." - (require 'autoload) - (let* ((default-directory doom-emacs-dir) - (doom-modules (doom-modules)) + (let* ((standard-output buffer) + (form (read (current-buffer))) + (altform (match-string 1)) + (definer (car-safe form)) + (symbol (doom-unquote (cadr form)))) + (cond ((and (not module-enabled-p) altform) + (print (read altform))) + ((memq definer '(defun defmacro cl-defun cl-defmacro)) + (if module-enabled-p + (print (make-autoload form file)) + (cl-destructuring-bind (_ _ arglist &rest body) form + (print + (if altform + (read altform) + (append + (list (pcase definer + (`defun 'defmacro) + (`cl-defun `cl-defmacro) + (_ type)) + symbol arglist + (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + module + (if (stringp (car body)) + (pop body) + "No documentation."))) + (cl-loop for arg in arglist + if (and (symbolp arg) + (not (keywordp arg)) + (not (memq arg cl--lambda-list-keywords))) + collect arg into syms + else if (listp arg) + collect (car arg) into syms + finally return (if syms `((ignore ,@syms))))))))) + (print `(put ',symbol 'doom-module ',module))) + ((eq definer 'defalias) + (cl-destructuring-bind (_ _ target &optional docstring) form + (unless module-enabled-p + (setq target #'ignore + docstring + (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" + module docstring))) + (print `(put ',symbol 'doom-module ',module)) + (print `(defalias ',symbol #',(doom-unquote target) ,docstring)))) + (module-enabled-p (print form))))))) +(defun doom-cli--generate-autoloads-buffer (file) + (let* (;; Prevent `autoload-find-file' from firing file hooks, e.g. adding + ;; to recentf. + find-file-hook + write-file-functions + ;; Prevent a possible source of crashes when there's a syntax error + ;; in the autoloads file + debug-on-error ;; The following bindings are in `package-generate-autoloads'. ;; Presumably for a good reason, so I just copied them - (noninteractive t) (backup-inhibited t) (version-control 'never) - (case-fold-search nil) ; reduce magic - (autoload-timestamps nil) + case-fold-search ; reduce magic + autoload-timestamps ; reduce noise in generated files + ;; Needed for `autoload-generate-file-autoloads' + (generated-autoload-load-name (file-name-sans-extension file)) + (target-buffer (current-buffer)) + (module (doom-module-from-path file)) + (module-enabled-p (and (or (memq (car module) '(:core :private)) + (doom-module-p (car module) (cdr module))) + (doom-file-cookie-p file "if" t)))) + (save-excursion + (when module-enabled-p + (quiet! (autoload-generate-file-autoloads file target-buffer))) + (doom-cli--generate-autoloads-autodefs + file target-buffer module module-enabled-p)))) - ;; Where we'll store the files we'll scan for autoloads. This should - ;; contain *all* autoload files, even in disabled modules, so we can - ;; scan those for autodefs. We start with the core libraries. - (targets (doom-glob doom-core-dir "autoload/*.el")) - ;; A subset of `targets' in enabled modules - (active-targets (copy-sequence targets))) - - (dolist (path (doom-module-load-path 'all-p)) - (when-let* ((files (cons (doom-glob path "autoload.el") - (doom-files-in (doom-path path "autoload") - :match "\\.el$"))) - (files (delq nil files))) - (appendq! targets files) - (when (or (doom-module-from-path path 'enabled-only) - (file-equal-p path doom-private-dir)) - (appendq! active-targets files)))) - - (print! (start "Checking core autoloads file")) - (print-group! - (if (and (not force-p) - (file-exists-p doom-autoload-file) - (not (file-newer-than-file-p doom-emacs-dir doom-autoload-file)) - (not (cl-loop for dir - in (append (doom-glob doom-private-dir "init.el*") - targets) - if (file-newer-than-file-p dir doom-autoload-file) - return t))) - (ignore - (print! (success "Skipping core autoloads, they are up-to-date")) - (doom-load-autoloads-file doom-autoload-file)) - (if (doom--cli-delete-autoloads-file doom-autoload-file) - (print! (success "Deleted old %s") (filename doom-autoload-file)) - (make-directory (file-name-directory doom-autoload-file) t)) - - (print! (start "Regenerating core autoloads file")) - (print-group! - (with-temp-file doom-autoload-file - (doom--cli-generate-header 'doom-cli-reload-core-autoloads) - (save-excursion - (doom--cli-generate-autoloads active-targets) - (print! (success "Generated new autoloads.el"))) - ;; Replace autoload paths (only for module autoloads) with absolute - ;; paths for faster resolution during load and simpler `load-path' - (save-excursion - (doom--cli-expand-autoload-paths 'allow-internal-paths) - (print! (success "Expanded module autoload paths"))) - ;; Generates stub definitions for functions/macros defined in disabled - ;; modules, so that you will never get a void-function when you use - ;; them. - (save-excursion - (doom--cli-generate-autodefs targets (reverse active-targets)) - (print! (success "Generated autodefs"))) - ;; Remove byte-compile-inhibiting file variables so we can byte-compile - ;; the file, and autoload comments. - (doom--cli-cleanup-autoloads) - (print! (success "Cleaned up autoloads")))) - ;; Byte compile it to give the file a chance to reveal errors (and buy us a - ;; few marginal performance boosts) - (print! "> Byte-compiling %s..." (relpath doom-autoload-file)) - (when (doom--cli-byte-compile-file doom-autoload-file) - (print-group! - (print! (success "Compiled %s") (relpath doom-autoload-file))))) - t))) - - -;; -;;; Package autoloads - -(defun doom--generate-package-autoloads () - "Concatenates package autoload files, let-binds `load-file-name' around -them,and remove unnecessary `provide' statements or blank links." - (dolist (pkg (hash-table-keys straight--build-cache)) - (unless (member pkg doom-autoload-excluded-packages) - (let ((file (straight--autoloads-file pkg))) - (when (file-exists-p file) - (insert-file-contents file) - (save-excursion - (while (re-search-forward "\\(?:\\_" nil t) - ;; `load-file-name' is meaningless in a concatenated - ;; mega-autoloads file, so we replace references to it and #$ with - ;; the file they came from. - (unless (doom-point-in-string-or-comment-p) - (replace-match (prin1-to-string (abbreviate-file-name file)) - t t)))) - (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) - (unless (doom-point-in-string-p) - (replace-match "" t t))) - (unless (bolp) (insert "\n"))))))) - -(defun doom--generate-var-cache () - "Print a `setq' form for expensive-to-initialize variables, so we can cache -them in Doom's autoloads file." - (doom-initialize-packages) - (prin1 `(setq load-path ',load-path - auto-mode-alist ',auto-mode-alist - Info-directory-list ',Info-directory-list - doom-disabled-packages ',doom-disabled-packages) - (current-buffer))) - -(defun doom--cleanup-package-autoloads () - "Remove (some) forms that modify `load-path' or `auto-mode-alist'. - -These variables are cached all at once and at later, so these removed statements -served no purpose but to waste cycles." - (while (re-search-forward "^\\s-*\\((\\(?:add-to-list\\|\\(?:when\\|if\\) (boundp\\)\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\)" nil t) - (goto-char (match-beginning 1)) - (kill-sexp))) - -(defun doom-cli-reload-package-autoloads (&optional force-p) - "Compiles `doom-package-autoload-file' from the autoloads files of all -installed packages. It also caches `load-path', `Info-directory-list', -`doom-disabled-packages', `package-activated-list' and `auto-mode-alist'. - -Will do nothing if none of your installed packages have been modified. If -FORCE-P (universal argument) is non-nil, regenerate it anyway. - -This should be run whenever your `doom!' block or update your packages." +(defun doom-cli--generate-autoloads (files &optional scan) (require 'autoload) - (print! (start "Checking package autoloads file")) - (print-group! - (if (and (not force-p) - (file-exists-p doom-package-autoload-file) - (not (file-newer-than-file-p package-user-dir doom-package-autoload-file)) - (not (cl-loop for dir in (straight--directory-files (straight--build-dir)) - if (cl-find-if - (lambda (dir) - (file-newer-than-file-p dir doom-package-autoload-file)) - (doom-glob (straight--build-dir dir) "*.el")) - return t)) - (not (cl-loop with doom-modules = (doom-modules) - for key being the hash-keys of doom-modules - for path = (doom-module-path (car key) (cdr key) "packages.el") - if (file-newer-than-file-p path doom-package-autoload-file) - return t))) - (ignore - (print! (success "Skipping package autoloads, they are up-to-date")) - (doom-load-autoloads-file doom-package-autoload-file)) - (let (;; The following bindings are in `package-generate-autoloads'. - ;; Presumably for a good reason, so I just copied them - (noninteractive t) - (backup-inhibited t) - (version-control 'never) - (case-fold-search nil) ; reduce magic - (autoload-timestamps nil)) - - (if (doom--cli-delete-autoloads-file doom-package-autoload-file) - (print! (success "Deleted old %s") (filename doom-package-autoload-file)) - (make-directory (file-name-directory doom-autoload-file) t)) - - (print! (start "Regenerating package autoloads file")) - (print-group! - (with-temp-file doom-package-autoload-file - (doom--cli-generate-header 'doom-cli-reload-package-autoloads) - - (save-excursion - ;; Cache important and expensive-to-initialize state here. - (doom--generate-var-cache) - (print! (success "Cached package state")) - ;; Concatenate the autoloads of all installed packages. - (doom--generate-package-autoloads) - (print! (success "Package autoloads included"))) - - ;; Replace autoload paths (only for module autoloads) with absolute - ;; paths for faster resolution during load and simpler `load-path' - (save-excursion - (doom--cli-expand-autoload-paths) - (print! (success "Expanded module autoload paths"))) - - ;; Remove `load-path' and `auto-mode-alist' modifications (most of them, - ;; at least); they are cached later, so all those membership checks are - ;; unnecessary overhead. - (doom--cleanup-package-autoloads) - (print! (success "Removed load-path/auto-mode-alist entries")))) - ;; Byte compile it to give the file a chance to reveal errors (and buy us a - ;; few marginal performance boosts) - (print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file)) - (when (doom--cli-byte-compile-file doom-package-autoload-file) - (print-group! - (print! (success "Compiled %s") (relpath doom-package-autoload-file)))))) - t)) + (let (autoloads) + (dolist (file + (cl-remove-if-not #'file-readable-p files) + (nreverse (delq nil autoloads))) + (with-temp-buffer + (print! (debug "- Scanning %s") (relpath file doom-emacs-dir)) + (if scan + (doom-cli--generate-autoloads-buffer file) + (insert-file-contents file)) + (save-excursion + (let ((filestr (prin1-to-string file))) + (while (re-search-forward "\\_" nil t) + ;; `load-file-name' is meaningless in a concatenated + ;; mega-autoloads file, so we replace references to it with the + ;; file they came from. + (let ((ppss (save-excursion (syntax-ppss)))) + (or (nth 3 ppss) + (nth 4 ppss) + (replace-match filestr t t)))))) + (let ((load-file-name file) + (load-path + (append (list doom-private-dir) + doom-modules-dirs + load-path))) + (condition-case _ + (while t + (push (doom-cli--filter-form (read (current-buffer)) + scan) + autoloads)) + (end-of-file))))))) diff --git a/core/cli/byte-compile.el b/core/cli/byte-compile.el index 916a11ba9..49121e0ff 100644 --- a/core/cli/byte-compile.el +++ b/core/cli/byte-compile.el @@ -27,6 +27,7 @@ and your private config files, respectively. To recompile your packages, use (let ((filename (file-name-nondirectory path))) (or (string-prefix-p "." filename) (string-prefix-p "test-" filename) + (string-suffix-p ".example.el" filename) (not (equal (file-name-extension path) "el")) (member filename (list "packages.el" "doctor.el"))))) @@ -92,7 +93,8 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." ;; But first we must be sure that Doom and your private config have been ;; fully loaded. Which usually aren't so in an noninteractive session. (let ((doom-interactive-mode 'byte-compile)) - (doom-initialize 'force) + (doom-initialize) + (doom-initialize-packages) (doom-initialize-core)) ;; @@ -122,9 +124,9 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files." (cl-return nil)) (print! - (info (if recompile-p - "Recompiling stale elc files..." - "Byte-compiling your config (may take a while)..."))) + (start (if recompile-p + "Recompiling stale elc files..." + "Byte-compiling your config (may take a while)..."))) (print-group! (require 'use-package) (condition-case e @@ -199,4 +201,5 @@ module. This does not include your byte-compiled, third party packages.'" finally do (print! (if success (success "All elc files deleted") - (info "No elc files to clean")))))) + (info "No elc files to clean")))) + t)) diff --git a/core/cli/doctor.el b/core/cli/doctor.el index d50cfb564..5f790ccfa 100644 --- a/core/cli/doctor.el +++ b/core/cli/doctor.el @@ -136,7 +136,7 @@ in." (`darwin "~/Library/Fonts/")) (require 'all-the-icons nil t)) (with-temp-buffer - (insert (cdr (doom-call-process "fc-list"))) + (insert (cdr (doom-call-process "fc-list" "" "file"))) (dolist (font all-the-icons-font-names) (if (save-excursion (re-search-backward font nil t)) (success! "Found font %s" font) diff --git a/core/cli/env.el b/core/cli/env.el index 4368f948d..de66a3e32 100644 --- a/core/cli/env.el +++ b/core/cli/env.el @@ -3,9 +3,9 @@ (defcli! env ((clear-p ["-c" "--clear"] "Clear and delete your envvar file") (outputfile ["-o" PATH] - "Generate the envvar file at PATH. Note that envvar files that aren't in -`doom-env-file' won't be loaded automatically at startup. You will need to -load them manually from your private config with the `doom-load-envvars-file' + "Generate the envvar file at PATH. Envvar files that aren't in +`doom-env-file' won't be loaded automatically at startup. You will need to load +them manually from your private config with the `doom-load-envvars-file' function.")) "Creates or regenerates your envvars file. @@ -19,8 +19,8 @@ This is useful in cases where you cannot guarantee that Emacs (or the daemon) will be launched from the correct environment (e.g. on MacOS or through certain app launchers on Linux). -This file is automatically regenerated when you run this command or 'doom -refresh'. However, 'doom refresh' will only regenerate this file if it exists. +This file is automatically regenerated when you run this command or 'doom sync'. +However, 'doom sync' will only regenerate this file if it exists. Why this over exec-path-from-shell? @@ -36,7 +36,7 @@ Why this over exec-path-from-shell? I'd rather it inherit your shell environment /correctly/ (and /completely/) or not at all. It frontloads the debugging process rather than hiding it - until it you least want to deal with it." + until you least want to deal with it." (let ((env-file (expand-file-name (or outputfile doom-env-file)))) (cond (clear-p (unless (file-exists-p env-file) @@ -57,18 +57,20 @@ Why this over exec-path-from-shell? ;; Helpers (defvar doom-env-ignored-vars - '("^PWD$" - "^PS1$" - "^R?PROMPT$" - "^DBUS_SESSION_BUS_ADDRESS$" + '("^DBUS_SESSION_BUS_ADDRESS$" "^GPG_AGENT_INFO$" + "^GPG_TTY$" + "^HOME$" + "^PS1$" + "^PWD$" + "^R?PROMPT$" "^SSH_AGENT_PID$" "^SSH_AUTH_SOCK$" - ;; Doom envvars - "^INSECURE$" - "^DEBUG$" - "^YES$" "^TERM$" + ;; Doom envvars + "^DEBUG$" + "^INSECURE$" + "^YES$" "^__") "Environment variables to not save in `doom-env-file'. @@ -100,7 +102,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in (goto-char (point-min)) (insert (concat - "# -*- mode: dotenv -*-\n" + "# -*- mode: sh -*-\n" (format "# Generated from a %s shell environent\n" shell-file-name) "# ---------------------------------------------------------------------------\n" "# This file was auto-generated by `doom env'. It contains a list of environment\n" @@ -109,7 +111,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in "#\n" (if (file-equal-p env-file doom-env-file) (concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n" - "# run 'doom refresh'. To create a safe-to-edit envvar file use:\n#\n" + "# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n" "# doom env -o ~/.doom.d/myenv\n#\n" "# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n") (concat "# This file is safe to edit by hand, but needs to be loaded manually with:\n#\n" @@ -120,7 +122,7 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in ;; user's interactive shell, therefore we just dump ;; `process-environment' to a file. (dolist (env process-environment) - (if (cl-find-if (doom-rpartial #'string-match-p env) + (if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) doom-env-ignored-vars) (print! (info "Ignoring %s") env) (insert env "\n"))) diff --git a/core/cli/help.el b/core/cli/help.el index 13e82ea77..39e136b54 100644 --- a/core/cli/help.el +++ b/core/cli/help.el @@ -27,7 +27,9 @@ until (memq arg cl--lambda-list-keywords) collect (format "[%s]" (upcase (symbol-name arg))))) " ") - ""))) + "")) + (when-let (aliases (doom-cli-aliases cli)) + (print! "Aliases: %s" (string-join aliases ", ")))) (defun doom--cli-print-desc (cli &optional short) (print! "%s" @@ -43,7 +45,6 @@ (print! (bold "Options:")) (print-group! (cl-loop for opt in optlist - for flags = (doom-cli-option-flags opt) for desc = (doom-cli-option-desc opt) for args = (doom-cli-option-args opt) for flagstr = (string-join (doom-cli-option-flags opt) ", ") diff --git a/core/cli/install.el b/core/cli/install.el index f4bf12126..719ca07b0 100644 --- a/core/cli/install.el +++ b/core/cli/install.el @@ -5,7 +5,7 @@ (noenv-p ["--no-env"] "Don't generate an envvars file (see 'doom help env')") (noinstall-p ["--no-install"] "Don't auto-install packages") (nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts") - &rest args) + &rest _args) "Installs and sets up Doom Emacs for the first time. This command does the following: @@ -46,24 +46,20 @@ DOOMDIR environment variable. e.g. (print! (success "Done!"))))) '(("init.el" . (lambda () - (insert-file-contents (doom-path doom-emacs-dir "init.example.el")))) + (insert-file-contents + (doom-path doom-emacs-dir "init.example.el")))) ("config.el" . (lambda () - (insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n" - ";; Place your private configuration here\n" - ((relpath doom-private-dir))))) + (insert-file-contents + (doom-path doom-core-dir "templates/config.example.el")))) ("packages.el" . (lambda () - (insert! ";; -*- no-byte-compile: t; -*-\n;;; %spackages.el\n\n" - ";;; Examples:\n" - ";; (package! some-package)\n" - ";; (package! another-package :recipe (:host github :repo \"username/repo\"))\n" - ";; (package! builtin-package :disable t)\n" - ((relpath doom-private-dir)))))))) + (insert-file-contents + (doom-path doom-core-dir "templates/packages.example.el"))))))) ;; In case no init.el was present the first time `doom-initialize-modules' was ;; called in core.el (e.g. on first install) - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (doom-initialize-modules) ;; Ask if user would like an envvar file generated @@ -72,7 +68,7 @@ DOOMDIR environment variable. e.g. (if (file-exists-p doom-env-file) (print! (info "Envvar file already exists, skipping")) (when (or doom-auto-accept - (y-or-n-p "Generate an env file? (see `doom help env` for details)")) + (y-or-n-p "Generate an envvar file? (see `doom help env` for details)")) (doom-cli-reload-env-file 'force-p)))) ;; Install Doom packages @@ -82,16 +78,24 @@ DOOMDIR environment variable. e.g. (doom-cli-packages-install)) (print! "Regenerating autoloads files") - (doom-cli-reload-autoloads nil 'force-p) - - (if nofonts-p - (print! (warn "Not installing fonts, as requested")) - (when (or doom-auto-accept - (y-or-n-p "Download and install all-the-icon's fonts?")) - (require 'all-the-icons) - (let ((window-system (cond (IS-MAC 'ns) - (IS-LINUX 'x)))) - (all-the-icons-install-fonts 'yes)))) + (doom-cli-reload-autoloads) + + (cond (nofonts-p) + (IS-WINDOWS + (print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n")) + (print-group! + (print! + (concat "You'll have to do so manually:\n\n" + " 1. Launch Doom Emacs\n" + " 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n" + " 3. Open the download location in windows explorer\n" + " 4. Open each font file to install them")))) + ((or doom-auto-accept + (y-or-n-p "Download and install all-the-icon's fonts?")) + (require 'all-the-icons) + (let ((window-system (cond (IS-MAC 'ns) + (IS-LINUX 'x)))) + (all-the-icons-install-fonts 'yes)))) (when (file-exists-p "~/.emacs") (print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!"))) diff --git a/core/cli/packages.el b/core/cli/packages.el index b25ce2194..0190d2639 100644 --- a/core/cli/packages.el +++ b/core/cli/packages.el @@ -1,7 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; core/cli/packages.el -(defcli! (update u) () +(defcli! (update u) + ((discard-p ["--discard"] "All local changes to packages are discarded")) "Updates packages. This works by fetching all installed package repos and checking the distance @@ -10,10 +11,11 @@ between HEAD and FETCH_HEAD. This can take a while. This excludes packages whose `package!' declaration contains a non-nil :freeze or :ignore property." (straight-check-all) - (doom-cli-reload-core-autoloads) - (when (doom-cli-packages-update) - (doom-cli-reload-package-autoloads 'force-p)) - t) + (let ((doom-auto-discard discard-p)) + (doom-cli-reload-core-autoloads) + (when (doom-cli-packages-update) + (doom-cli-reload-package-autoloads)) + t)) (defcli! (build b) ((rebuild-p ["-r"] "Only rebuild packages that need rebuilding")) @@ -23,7 +25,7 @@ This ensures that all needed files are symlinked from their package repo and their elisp files are byte-compiled. This is especially necessary if you upgrade Emacs (as byte-code is generally not forward-compatible)." (when (doom-cli-packages-build (not rebuild-p)) - (doom-cli-reload-package-autoloads 'force-p)) + (doom-cli-reload-package-autoloads)) t) (defcli! (purge p) @@ -46,7 +48,7 @@ list remains lean." (not norepos-p) (not nobuilds-p) regraft-p) - (doom-cli-reload-package-autoloads 'force-p)) + (doom-cli-reload-package-autoloads)) t) ;; (defcli! rollback () ; TODO doom rollback @@ -57,138 +59,186 @@ list remains lean." ;; ;;; Library +(defun doom--same-commit-p (abbrev-ref ref) + (and (stringp abbrev-ref) + (stringp ref) + (string-match-p (concat "^" (regexp-quote abbrev-ref)) + ref))) + +(defun doom--abbrev-commit (commit &optional full) + (if full commit (substring commit 0 7))) + +(defun doom--commit-log-between (start-ref end-ref) + (and (straight--call + "git" "log" "--oneline" "--no-merges" + "-n" "25" end-ref (concat "^" (regexp-quote start-ref))) + (straight--process-get-output))) + (defun doom-cli-packages-install () "Installs missing packages. This function will install any primary package (i.e. a package with a `package!' declaration) or dependency thereof that hasn't already been." - (print! (start "Installing & building packages...")) - (print-group! - (let ((n 0)) - (dolist (package (hash-table-keys straight--recipe-cache)) - (straight--with-plist (gethash package straight--recipe-cache) - (local-repo) - (let ((existed-p (file-directory-p (straight--repos-dir package)))) - (condition-case-unless-debug e - (and (straight-use-package (intern package) nil nil (make-string (1- (or doom-format-indent 1)) 32)) - (not existed-p) - (file-directory-p (straight--repos-dir package)) - (cl-incf n)) - (error - (signal 'doom-package-error - (list e (straight--process-get-output)))))))) - (if (= n 0) - (ignore (print! (success "No packages need to be installed"))) - (print! (success "Installed & built %d packages") n) - t)))) + (straight--transaction-finalize) + (print! (start "Installing packages...")) + (let ((pinned (doom-package-pinned-list))) + (print-group! + (if-let (built + (doom-with-package-recipes (doom-package-recipe-list) + (recipe package type local-repo) + (condition-case-unless-debug e + (straight-use-package (intern package)) + (error + (signal 'doom-package-error + (list package e (straight--process-get-output))))))) + (print! (success "Installed %d packages") + (length built)) + (print! (info "No packages need to be installed")) + nil)))) (defun doom-cli-packages-build (&optional force-p) "(Re)build all packages." + (straight--transaction-finalize) (print! (start "(Re)building %spackages...") (if force-p "all " "")) (print-group! - (let ((n 0)) - (if force-p - (let ((straight--packages-to-rebuild :all) - (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) - (dolist (package (hash-table-keys straight--recipe-cache)) - (straight-use-package - (intern package) nil (lambda (_) (cl-incf n) nil) - (make-string (1- (or doom-format-indent 1)) 32)))) - (dolist (recipe (hash-table-values straight--recipe-cache)) - (straight--with-plist recipe (package local-repo no-build) - (unless (or no-build (null local-repo)) - ;; REVIEW We do these modification checks manually because - ;; Straight's checks seem to miss stale elc files. Need - ;; more tests to confirm this. - (when (or (ignore-errors - (gethash package straight--packages-to-rebuild)) - (gethash package straight--cached-package-modifications) - (not (file-directory-p (straight--build-dir package))) - (cl-loop for file - in (doom-files-in (straight--build-dir package) - :match "\\.el$" - :full t) - for elc-file = (byte-compile-dest-file file) - if (and (file-exists-p elc-file) - (file-newer-than-file-p file elc-file)) - return t)) - (let ((straight-use-package-pre-build-functions - straight-use-package-pre-build-functions)) - (add-hook 'straight-use-package-pre-build-functions - (lambda (&rest _) (cl-incf n))) - (let ((straight--packages-to-rebuild :all) - (straight--packages-not-to-rebuild (make-hash-table :test #'equal))) - (straight-use-package - (intern package) nil nil - (make-string (or doom-format-indent 0) 32))) - (straight--byte-compile-package recipe) - (dolist (dep (straight--get-dependencies package)) - (when-let (recipe (gethash dep straight--recipe-cache)) - (straight--byte-compile-package recipe))))))))) - (if (= n 0) - (ignore (print! (success "No packages need rebuilding"))) - (doom--finalize-straight) - (print! (success "Rebuilt %d package(s)" n)) - t)))) + (let ((straight-check-for-modifications + (when (file-directory-p (straight--modified-dir)) + '(find-when-checking))) + (straight--allow-find + (and straight-check-for-modifications + (executable-find straight-find-executable) + t)) + (straight--packages-not-to-rebuild + (or straight--packages-not-to-rebuild (make-hash-table :test #'equal))) + (straight--packages-to-rebuild + (or (if force-p :all straight--packages-to-rebuild) + (make-hash-table :test #'equal))) + (recipes (doom-package-recipe-list))) + (unless force-p + (straight--make-build-cache-available)) + (if-let (built + (doom-with-package-recipes recipes (package local-repo) + (unless force-p + ;; Ensure packages with outdated files/bytecode are rebuilt + (let ((build-dir (straight--build-dir package)) + (repo-dir (straight--repos-dir local-repo))) + (and (or (file-newer-than-file-p repo-dir build-dir) + (file-exists-p (straight--modified-dir (or local-repo package))) + ;; Doesn't make sense to compare el and elc files + ;; when the former isn't a symlink to their source. + (when straight-use-symlinks + (cl-loop for file + in (doom-files-in build-dir :match "\\.el$" :full t) + for elc-file = (byte-compile-dest-file file) + if (and (file-exists-p elc-file) + (file-newer-than-file-p file elc-file)) + return t))) + (puthash package t straight--packages-to-rebuild)))) + (straight-use-package (intern package)))) + (print! (success "Rebuilt %d package(s)") (length built)) + (print! (success "No packages need rebuilding")) + nil)))) (defun doom-cli-packages-update () "Updates packages." + (straight--transaction-finalize) (print! (start "Updating packages (this may take a while)...")) - (let ((straight--packages-to-rebuild (make-hash-table :test #'equal)) - (total (hash-table-count straight--repo-cache)) - (i 1) - errors) - (print-group! - (dolist (recipe (hash-table-values straight--repo-cache)) - (straight--with-plist recipe (package type local-repo) + (let* ((repo-dir (straight--repos-dir)) + (pinned (doom-package-pinned-list)) + (packages-to-rebuild (make-hash-table :test 'equal)) + (repos-to-rebuild (make-hash-table :test 'equal)) + (recipes (doom-package-recipe-list)) + (total (length recipes)) + (esc (unless doom-debug-mode "\033[1A")) + (i 0) + errors) + (doom-with-package-recipes recipes (recipe package type local-repo) + (cl-incf i) + (print-group! + (unless (straight--repository-is-available-p recipe) + (print! (error "(%d/%d) Couldn't find local repo for %s") i total package) + (cl-return)) + (when (gethash local-repo repos-to-rebuild) + (puthash package t packages-to-rebuild) + (print! (success "(%d/%d) %s was updated indirectly (with %s)") i total package local-repo) + (cl-return)) + (let ((default-directory (straight--repos-dir local-repo))) + (unless (file-in-directory-p default-directory repo-dir) + (print! (warn "(%d/%d) Skipping %s because it is local") i total package) + (cl-return)) (condition-case-unless-debug e - (let* ((default-directory (straight--repos-dir local-repo)) - (commit (straight-vc-get-commit type local-repo))) - (if (not (straight-vc-fetch-from-remote recipe)) - (print! (warn "(%d/%d) Failed to fetch %s" i total package)) - (let ((output (straight--process-get-output))) - (straight-merge-package package) - (let ((newcommit (straight-vc-get-commit type local-repo))) - (if (string= commit newcommit) - (print! (info "(%d/%d) %s is up-to-date") i total package) + (let ((ref (straight-vc-get-commit type local-repo)) + (target-ref (cdr (assoc local-repo pinned))) + output) + (or (cond + ((not (stringp target-ref)) + (print! (start "\033[K(%d/%d) Fetching %s...%s") i total package esc) + (when (straight-vc-fetch-from-remote recipe) + (setq output (straight--process-get-output)) + (straight-merge-package package) + (setq target-ref (straight-vc-get-commit type local-repo)) + (or (not (doom--same-commit-p target-ref ref)) + (cl-return)))) + + ((doom--same-commit-p target-ref ref) + (print! (info "\033[K(%d/%d) %s is up-to-date...%s") i total package esc) + (cl-return)) + + ((straight-vc-commit-present-p recipe target-ref) + (print! (start "\033[K(%d/%d) Checking out %s (%s)...%s") + i total package (doom--abbrev-commit target-ref) esc) + (straight-vc-check-out-commit recipe target-ref) + (or (not (eq type 'git)) + (setq output (doom--commit-log-between ref target-ref))) + (doom--same-commit-p target-ref (straight-vc-get-commit type local-repo))) + + ((print! (start "\033[K(%d/%d) Re-cloning %s...") i total local-repo esc) + (let ((repo (straight--repos-dir local-repo))) (ignore-errors - (delete-directory (straight--build-dir package) 'recursive)) - (puthash package t straight--packages-to-rebuild) - (print! (success "(%d/%d) %s updated (%s -> %s)") i total package - (substring commit 0 7) - (substring newcommit 0 7)) - (unless (string-empty-p output) - (print-group! - (print! (info "%s") output) - (when (eq type 'git) - (straight--call "git" "log" "--oneline" newcommit (concat "^" commit)) - (print-group! - (print! "%s" (straight--process-get-output)))))))))) - (cl-incf i)) + (delete-directory repo 'recursive)) + (print-group! + (straight-use-package (intern package) nil 'no-build)) + (prog1 (file-directory-p repo) + (or (not (eq type 'git)) + (setq output (doom--commit-log-between ref target-ref))))))) + (progn + (print! (warn "\033[K(%d/%d) Failed to fetch %s") + i total local-repo) + (unless (string-empty-p output) + (print-group! (print! (info "%s" output)))) + (cl-return))) + (puthash local-repo t repos-to-rebuild) + (puthash package t packages-to-rebuild) + (unless (string-empty-p output) + (print! (start "\033[K(%d/%d) Updating %s...") i total local-repo) + (print-group! (print! (indent 2 output)))) + (print! (success "\033[K(%d/%d) %s updated (%s -> %s)") + i total local-repo + (doom--abbrev-commit ref) + (doom--abbrev-commit target-ref))) (user-error (signal 'user-error (error-message-string e))) (error - (print! (warn "(%d/%d) Encountered error with %s" i total package)) + (print! (warn "\033[K(%d/%d) Encountered error with %s" i total package)) (print-group! - (print! (error "%s" e)) + (print! (error "%s") e) (print-group! (print! (info "%s" (straight--process-get-output))))) - (push package errors))))) - (when errors - (print! (error "There were %d errors, the offending packages are: %s") - (length errors) (string-join errors ", "))) - (if (hash-table-empty-p straight--packages-to-rebuild) - (ignore - (print! (success "All %d packages are up-to-date") - (hash-table-count straight--repo-cache))) - (let ((count (hash-table-count straight--packages-to-rebuild)) - (packages (hash-table-keys straight--packages-to-rebuild))) - (sort packages #'string-lessp) - (doom--finalize-straight) - (doom-cli-packages-build) - (print! (success "Updated %d package(s)") count)) - t)))) + (push package errors)))))) + (princ "\033[K") + (when errors + (print! (error "Encountered %d error(s), the offending packages: %s") + (length errors) (string-join errors ", "))) + (if (hash-table-empty-p packages-to-rebuild) + (ignore (print! (success "All %d packages are up-to-date") total)) + (let ((default-directory (straight--build-dir))) + (mapc (doom-rpartial #'delete-directory 'recursive) + (hash-table-keys packages-to-rebuild))) + (print! (success "Updated %d package(s)") + (hash-table-count packages-to-rebuild)) + (doom-cli-packages-build) + t))) ;;; PURGE (for the emperor) @@ -204,34 +254,44 @@ declaration) or dependency thereof that hasn't already been." (if (not builds) (progn (print! (info "No builds to purge")) 0) - (length - (delq nil (mapcar #'doom--cli-packages-purge-build builds))))) + (print! (start "Purging straight builds..." (length builds))) + (print-group! + (length + (delq nil (mapcar #'doom--cli-packages-purge-build builds)))))) -(defun doom--cli-packages-regraft-repo (repo) +(cl-defun doom--cli-packages-regraft-repo (repo) (let ((default-directory (straight--repos-dir repo))) - (if (not (file-directory-p ".git")) - (ignore (print! (warn "repos/%s is not a git repo, skipping" repo))) - (let ((before-size (doom-directory-size default-directory))) - (straight--call "git" "reset" "--hard") - (straight--call "git" "clean" "-ffd") - (if (not (car (straight--call "git" "replace" "--graft" "HEAD"))) - (print! (info "repos/%s is already compact" repo)) - (straight--call "git" "gc") - (print! (success "Regrafted repos/%s (from %0.1fKB to %0.1fKB)") - repo before-size (doom-directory-size default-directory)) - (print-group! (print! "%s" (straight--process-get-output))))) + (unless (file-directory-p ".git") + (print! (warn "\033[Krepos/%s is not a git repo, skipping" repo)) + (cl-return)) + (unless (file-in-directory-p default-directory straight-base-dir) + (print! (warn "\033[KSkipping repos/%s because it is local" repo)) + (cl-return)) + (let ((before-size (doom-directory-size default-directory))) + (straight--call "git" "reset" "--hard") + (straight--call "git" "clean" "-ffd") + (if (not (car (straight--call "git" "replace" "--graft" "HEAD"))) + (print! (info "\033[Krepos/%s is already compact\033[1A" repo)) + (straight--call "git" "reflog" "expire" "--expire=all" "--all") + (straight--call "git" "gc" "--prune=now") + (print! (success "\033[KRegrafted repos/%s (from %0.1fKB to %0.1fKB)") + repo before-size (doom-directory-size default-directory)) + (print-group! (print! "%s" (straight--process-get-output)))) t))) (defun doom--cli-packages-regraft-repos (repos) (if (not repos) (progn (print! (info "No repos to regraft")) 0) + (print! (start "Regrafting %d repos..." (length repos))) (let ((before-size (doom-directory-size (straight--repos-dir)))) - (prog1 (print-group! (delq nil (mapcar #'doom--cli-packages-regraft-repo repos))) - (let ((after-size (doom-directory-size (straight--repos-dir)))) - (print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)") - before-size after-size - (- after-size before-size))))))) + (print-group! + (prog1 (delq nil (mapcar #'doom--cli-packages-regraft-repo repos)) + (princ "\033[K") + (let ((after-size (doom-directory-size (straight--repos-dir)))) + (print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)") + before-size after-size + (- after-size before-size)))))))) (defun doom--cli-packages-purge-repo (repo) (let ((repo-dir (straight--repos-dir repo))) @@ -247,22 +307,29 @@ declaration) or dependency thereof that hasn't already been." (if (not repos) (progn (print! (info "No repos to purge")) 0) - (length - (delq nil (mapcar #'doom--cli-packages-purge-repo repos))))) + (print! (start "Purging straight repositories...")) + (print-group! + (length + (delq nil (mapcar #'doom--cli-packages-purge-repo repos)))))) (defun doom--cli-packages-purge-elpa () - (unless (bound-and-true-p package--initialized) - (package-initialize)) - (let ((packages (cl-loop for (package desc) in package-alist - for dir = (package-desc-dir desc) - if (file-in-directory-p dir package-user-dir) - collect (cons package dir)))) - (if (not package-alist) + (require 'core-packages) + (let ((dirs (doom-files-in package-user-dir :type t :depth 0))) + (if (not dirs) (progn (print! (info "No ELPA packages to purge")) 0) - (mapc (doom-rpartial #'delete-directory 'recursive) - (mapcar #'cdr packages)) - (length packages)))) + (print! (start "Purging ELPA packages...")) + (dolist (path dirs (length dirs)) + (condition-case e + (print-group! + (if (file-directory-p path) + (delete-directory path 'recursive) + (delete-file path)) + (print! (success "Deleted %s") (filename path))) + (error + (print! (error "Failed to delete %s because: %s") + (filename path) + e))))))) (defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p) "Auto-removes orphaned packages and repos. @@ -273,7 +340,7 @@ a `package!' declaration) or isn't depended on by another primary package. If BUILDS-P, include straight package builds. If REPOS-P, include straight repos. If ELPA-P, include packages installed with package.el (M-x package-install)." - (print! (start "Searching for orphaned packages to purge (for the emperor)...")) + (print! (start "Purging orphaned packages (for the emperor)...")) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) (let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) (bdirs (straight--directory-files (straight--build-dir) nil nil 'sort))) @@ -302,6 +369,4 @@ If ELPA-P, include packages installed with package.el (M-x package-install)." (print! (info "Skipping regrafting")) (and (doom--cli-packages-regraft-repos repos-to-regraft) (setq success t))) - (when success - (doom--finalize-straight) - t))))) + success)))) diff --git a/core/cli/test.el b/core/cli/test.el index 4def753a3..1defcf063 100644 --- a/core/cli/test.el +++ b/core/cli/test.el @@ -11,9 +11,9 @@ (defcli! test (&rest targets) "Run Doom unit tests." :bare t - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (require 'ansi-color) - (let (files error read-files) + (let (files read-files) (unless targets (setq targets (cons doom-core-dir @@ -48,11 +48,11 @@ doom-auto-accept t) (require 'core ,(locate-library "core")) (require 'core-cli) - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (doom-initialize-modules) - (doom-cli-reload-core-autoloads 'force) + (doom-cli-reload-core-autoloads) (when (doom-cli-packages-install) - (doom-cli-reload-package-autoloads 'force))))) + (doom-cli-reload-package-autoloads))))) (unless (zerop status) (error "Failed to bootstrap unit tests")))) (with-temp-buffer diff --git a/core/cli/upgrade.el b/core/cli/upgrade.el index 7b9912ecf..5e327f525 100644 --- a/core/cli/upgrade.el +++ b/core/cli/upgrade.el @@ -1,7 +1,8 @@ ;;; core/cli/upgrade.el -*- lexical-binding: t; -*- (defcli! (upgrade up) - ((force-p ["-f" "--force"])) + ((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway") + (packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom")) "Updates Doom and packages. This requires that ~/.emacs.d is a git repo, and is the equivalent of the @@ -10,15 +11,20 @@ following shell commands: cd ~/.emacs.d git pull --rebase bin/doom clean - bin/doom refresh + bin/doom sync bin/doom update" :bare t - (when (doom-cli-upgrade doom-auto-accept force-p) - (require 'core-packages) - (doom-initialize) - (doom-initialize-packages) - (when (doom-cli-packages-update) - (doom-cli-reload-package-autoloads 'force)))) + (let ((doom-auto-discard force-p)) + (if (delq + nil (list + (unless packages-only-p + (doom-cli-upgrade doom-auto-accept doom-auto-discard)) + (doom-cli-execute "refresh") + (when (doom-cli-packages-update) + (doom-cli-reload-package-autoloads) + t))) + (print! (success "Done! Restart Emacs for changes to take effect.")) + (print! "Nothing to do. Doom is up-to-date!")))) ;; @@ -33,7 +39,7 @@ following shell commands: (cl-destructuring-bind (success . stdout) (doom-call-process "git" "status" "--porcelain" "-uno") (if (= 0 success) - (string-match-p "[^ \t\n]" (buffer-string)) + (split-string stdout "\n" t) (error "Failed to check working tree in %s" dir)))) @@ -52,21 +58,22 @@ following shell commands: "Couldn't detect what branch you're on. Is Doom detached?"))) ;; We assume that a dirty .emacs.d is intentional and abort - (when (doom--working-tree-dirty-p default-directory) + (when-let (dirty (doom--working-tree-dirty-p default-directory)) (if (not force-p) - (user-error! "%s\n\n%s" + (user-error! "%s\n\n%s\n\n %s" (format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir)) - "Either stash/undo your changes or run 'doom upgrade -f' to discard local changes.") + "Either stash/undo your changes or run 'doom upgrade -f' to discard local changes." + (string-join dirty "\n")) (print! (info "You have local modifications in Doom's source. Discarding them...")) (doom-call-process "git" "reset" "--hard" (format "origin/%s" branch)) (doom-call-process "git" "clean" "-ffd"))) (doom-call-process "git" "remote" "remove" doom-repo-remote) (unwind-protect - (progn + (let (result) (or (zerop (car (doom-call-process "git" "remote" "add" doom-repo-remote doom-repo-url))) (error "Failed to add %s to remotes" doom-repo-remote)) - (or (zerop (car (doom-call-process "git" "fetch" "--tags" doom-repo-remote branch))) + (or (zerop (car (setq result (doom-call-process "git" "fetch" "--tags" doom-repo-remote branch)))) (error "Failed to fetch from upstream")) (let ((this-rev (vc-git--rev-parse "HEAD")) @@ -99,13 +106,11 @@ following shell commands: (print! (start "Upgrading Doom Emacs...")) (print-group! (doom-clean-byte-compiled-files) - (unless (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote))) - (equal (vc-git--rev-parse "HEAD") new-rev)) + (if (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote))) + (equal (vc-git--rev-parse "HEAD") new-rev)) + (print! (info "%s") (cdr result)) (error "Failed to check out %s" (substring new-rev 0 10))) (print! (success "Finished upgrading Doom Emacs"))) - (doom-cli-execute "refresh" (if auto-accept-p '("-y"))) - t) - - (print! (success "Done! Restart Emacs for changes to take effect.")))))) + t))))) (ignore-errors (doom-call-process "git" "remote" "remove" doom-repo-remote)))))) diff --git a/core/core-cli.el b/core/core-cli.el index e1ca98b87..cc1511b37 100644 --- a/core/core-cli.el +++ b/core/core-cli.el @@ -1,14 +1,5 @@ ;;; -*- lexical-binding: t; no-byte-compile: t; -*- -(require 'seq) - -;; Eagerly load these libraries because we may be in a session that hasn't been -;; fully initialized (e.g. where autoloads files haven't been generated or -;; `load-path' populated). -(mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix) - (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) - - ;; ;;; Variables @@ -17,6 +8,9 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and `doom-packages-autoremove'.") +(defvar doom-auto-discard (getenv "FORCE") + "If non-nil, discard all local changes while updating.") + (defvar doom--cli-p nil) (defvar doom--cli-commands (make-hash-table :test 'equal)) (defvar doom--cli-groups (make-hash-table :test 'equal)) @@ -187,13 +181,14 @@ BODY will be run when this dispatcher is called." :plist plist :fn (lambda (--alist--) + (ignore --alist--) (let ,(cl-loop for opt in speclist for optsym = (if (listp opt) (car opt) opt) unless (memq optsym cl--lambda-list-keywords) collect (list optsym `(cdr (assq ',optsym --alist--)))) ,@(unless (plist-get plist :bare) '((unless doom-init-p - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (doom-initialize-modules)))) ,@body))) doom--cli-commands) @@ -209,15 +204,127 @@ BODY will be run when this dispatcher is called." ,@body)) +;; +;;; Straight hacks + +(defvar doom--cli-straight-discard-options + '("^Delete remote \"[^\"]+\", re-create it with correct " + "^Reset branch " + "^Abort merge$" + "^Discard changes$")) + +;; HACK Remove dired & magit options from prompt, since they're inaccessible in +;; noninteractive sessions. +(advice-add #'straight-vc-git--popup-raw :override #'straight--popup-raw) + +;; HACK Replace GUI popup prompts (which hang indefinitely in tty Emacs) with +;; simple prompts. +(defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt) + :around #'straight-are-you-sure + (or doom-auto-accept + (if noninteractive + (y-or-n-p (format! "%s" (or prompt ""))) + (funcall orig-fn prompt)))) + +(defadvice! doom--straight-fallback-to-tty-prompt-a (orig-fn prompt actions) + "Modifies straight to prompt on the terminal when in noninteractive sessions." + :around #'straight--popup-raw + (if (not noninteractive) + (funcall orig-fn prompt actions) + ;; We can't intercept C-g, so no point displaying any options for this key + ;; when C-c is the proper way to abort batch Emacs. + (delq! "C-g" actions 'assoc) + ;; HACK These are associated with opening dired or magit, which isn't + ;; possible in tty Emacs, so... + (delq! "e" actions 'assoc) + (delq! "g" actions 'assoc) + (if doom-auto-discard + (cl-loop with doom-auto-accept = t + for (_key desc func) in actions + when desc + when (cl-find-if (doom-rpartial #'string-match-p desc) + doom--cli-straight-discard-options) + return (funcall func)) + (print! (start "%s") (red prompt)) + (print-group! + (terpri) + (let (options) + (print-group! + (print! " 1) Abort") + (cl-loop for (_key desc func) in actions + when desc + do (push func options) + and do + (print! "%2s) %s" (1+ (length options)) + (if (cl-find-if (doom-rpartial #'string-match-p desc) + doom--cli-straight-discard-options) + (concat desc " (Recommended)") + desc)))) + (terpri) + (let* ((options + (cons (lambda () + (let ((doom-format-indent 0)) + (terpri) + (print! (warn "Aborted"))) + (kill-emacs 1)) + (nreverse options))) + (prompt + (format! "How to proceed? (%s) " + (mapconcat #'number-to-string + (number-sequence 1 (length options)) + ", "))) + answer fn) + (while (null (nth (setq answer (1- (read-number prompt))) + options)) + (print! (warn "%s is not a valid answer, try again.") + answer)) + (funcall (nth answer options)))))))) + +(defadvice! doom--straight-respect-print-indent-a (args) + "Indent straight progress messages to respect `doom-format-indent', so we +don't have to pass whitespace to `straight-use-package's fourth argument +everywhere we use it (and internally)." + :filter-args #'straight-use-package + (cl-destructuring-bind + (melpa-style-recipe &optional no-clone no-build cause interactive) + args + (list melpa-style-recipe no-clone no-build + (if (and (not cause) + (boundp 'doom-format-indent) + (> doom-format-indent 0)) + (make-string (1- (or doom-format-indent 1)) 32) + cause) + interactive))) + + +;; +;;; Dependencies + +(require 'seq) + +;; Eagerly load these libraries because we may be in a session that hasn't been +;; fully initialized (e.g. where autoloads files haven't been generated or +;; `load-path' populated). +(load! "autoload/cli") +(load! "autoload/debug") +(load! "autoload/files") +(load! "autoload/format") +(load! "autoload/plist") + + ;; ;;; CLI Commands (load! "cli/help") (load! "cli/install") -(defcli! (refresh re) - ((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary")) - "Ensure Doom is properly set up. +(defcligroup! "Maintenance" + "For managing your config and packages" + (defcli! (sync s refresh re) + ((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary") + (inhibit-envvar-p ["-e"] "Don't regenerate the envvar file") + (prune-p ["-p" "--prune"] "Purge orphaned packages & regraft repos")) + "Synchronize your config with Doom Emacs. This is the equivalent of running autoremove, install, autoloads, then recompile. Run this whenever you: @@ -230,22 +337,34 @@ recompile. Run this whenever you: It will ensure that unneeded packages are removed, all needed packages are installed, autoloads files are up-to-date and no byte-compiled files have gone stale." - (print! (green "Initiating a refresh of Doom Emacs...\n")) - (let (success) - (when (file-exists-p doom-env-file) - (doom-cli-reload-env-file 'force)) - (doom-cli-reload-core-autoloads (not if-necessary-p)) - (unwind-protect - (progn - (and (doom-cli-packages-install) - (setq success t)) - (and (doom-cli-packages-build) - (setq success t)) - (and (doom-cli-packages-purge nil 'builds-p nil) - (setq success t))) - (doom-cli-reload-package-autoloads (or success (not if-necessary-p))) - (doom-cli-byte-compile nil 'recompile)) - t)) + :bare t + (let (success) + ;; Ensures that no pre-existing state pollutes the generation of the new + ;; autoloads files. + (dolist (file (list doom-autoload-file doom-package-autoload-file)) + (delete-file file) + (delete-file (byte-compile-dest-file file))) + + (doom-initialize 'force 'noerror) + (doom-initialize-modules) + + (print! (start "Synchronizing your config with Doom Emacs...")) + (print-group! + (when (and (not inhibit-envvar-p) + (file-exists-p doom-env-file)) + (doom-cli-reload-env-file 'force)) + + (doom-cli-reload-core-autoloads) + (doom-cli-packages-install) + (doom-cli-packages-build) + (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p) + (doom-cli-reload-package-autoloads) + t))) + + (load! "cli/env") + (load! "cli/upgrade") + (load! "cli/packages") + (load! "cli/autoloads")) (defcligroup! "Diagnostics" "For troubleshooting and diagnostics" @@ -253,13 +372,6 @@ stale." (load! "cli/debug") (load! "cli/test")) -(defcligroup! "Maintenance" - "For managing your config and packages" - (load! "cli/env") - (load! "cli/upgrade") - (load! "cli/packages") - (load! "cli/autoloads")) - (defcligroup! "Compilation" "For compiling Doom and your config" (load! "cli/byte-compile")) diff --git a/core/core-editor.el b/core/core-editor.el index e71dd4d4f..570a43fc4 100644 --- a/core/core-editor.el +++ b/core/core-editor.el @@ -12,11 +12,14 @@ successfully sets indent_style/indent_size.") (defvar-local doom-large-file-p nil) (put 'doom-large-file-p 'permanent-local t) -(defvar doom-large-file-size 1 - "The threshold above which Doom enables emergency optimizations. +(defvar doom-large-file-size-alist '(("." . 1.0)) + "An alist mapping regexps (like `auto-mode-alist') to filesize thresholds. -This threshold is in MB. See `doom--optimize-for-large-files-a' for -implementation details.") +If a file is opened and discovered to be larger than the threshold, Doom +performs emergency optimizations to prevent Emacs from hanging, crashing or +becoming unusably slow. + +These thresholds are in MB, and is used by `doom--optimize-for-large-files-a'.") (defvar doom-large-file-excluded-modes '(so-long-mode special-mode archive-mode tar-mode jka-compr @@ -31,7 +34,7 @@ implementation details.") (defadvice! doom--optimize-for-large-files-a (orig-fn &rest args) "Set `doom-large-file-p' if the file is too large. -Uses `doom-large-file-size' to determine when a file is too large. When +Uses `doom-large-file-size-alist' to determine when a file is too large. When `doom-large-file-p' is set, other plugins can detect this and reduce their runtime costs (or disable themselves) to ensure the buffer is as fast as possible." @@ -39,19 +42,24 @@ possible." (if (setq doom-large-file-p (and buffer-file-name (not doom-large-file-p) - (file-readable-p buffer-file-name) - (> (nth 7 (file-attributes buffer-file-name)) - (* 1024 1024 doom-large-file-size)))) + (file-exists-p buffer-file-name) + (ignore-errors + (> (nth 7 (file-attributes buffer-file-name)) + (* 1024 1024 + (assoc-default buffer-file-name doom-large-file-size-alist + #'string-match-p)))))) (prog1 (apply orig-fn args) (if (memq major-mode doom-large-file-excluded-modes) (setq doom-large-file-p nil) - (so-long-minor-mode +1) + (when (fboundp 'so-long-minor-mode) ; in case the user disabled it + (so-long-minor-mode +1)) (message "Large file detected! Cutting a few corners to improve performance..."))) (apply orig-fn args))) ;; Resolve symlinks when opening files, so that any operations are conducted ;; from the file's true directory (like `find-file'). -(setq find-file-visit-truename t) +(setq find-file-visit-truename t + vc-follow-symlinks t) ;; Disable the warning "X and Y are the same file". It's fine to ignore this ;; warning as it will redirect you to the existing buffer anyway. @@ -123,16 +131,14 @@ possible." ;; (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) -;; Save clipboard contents into kill-ring before replacing them -(setq save-interprogram-paste-before-kill t) - ;; Fixes the clipboard in tty Emacs by piping clipboard I/O through xclip, xsel, ;; pb{copy,paste}, wl-copy, termux-clipboard-get, or getclip (cygwin). -(add-hook! 'tty-setup-hook - (defun doom-init-clipboard-in-tty-emacs-h () - (and (not (getenv "SSH_CONNECTION")) - (require 'xclip nil t) - (xclip-mode +1)))) +(unless IS-WINDOWS + (add-hook! 'tty-setup-hook + (defun doom-init-clipboard-in-tty-emacs-h () + (and (not (getenv "SSH_CONNECTION")) + (require 'xclip nil t) + (xclip-mode +1))))) ;; @@ -140,6 +146,7 @@ possible." (push '("/LICENSE\\'" . text-mode) auto-mode-alist) (push '("\\.log\\'" . text-mode) auto-mode-alist) +(push '("\\.env\\'" . sh-mode) auto-mode-alist) ;; @@ -188,17 +195,14 @@ possible." (not (file-remote-p file))) (file-truename file) file)) - (setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name)) - - (setq recentf-save-file (concat doom-cache-dir "recentf") + (setq recentf-filename-handlers + '(substring-no-properties + doom--recent-file-truename + abbreviate-file-name) + recentf-save-file (concat doom-cache-dir "recentf") recentf-auto-cleanup 'never recentf-max-menu-items 0 - recentf-max-saved-items 200 - recentf-exclude - (list "\\.\\(?:gz\\|gif\\|svg\\|png\\|jpe?g\\)$" "^/tmp/" "^/ssh:" - "\\.?ido\\.last$" "\\.revive$" "/TAGS$" "^/var/folders/.+$" - ;; ignore private DOOM temp files - (concat "^" (recentf-apply-filename-handlers doom-local-dir)))) + recentf-max-saved-items 200) (add-hook! '(doom-switch-window-hook write-file-functions) (defun doom--recentf-touch-buffer-h () @@ -250,6 +254,11 @@ possible." :after-while #'save-place-find-file-hook (if buffer-file-name (ignore-errors (recenter)))) + (defadvice! doom--inhibit-saveplace-in-long-files-a (orig-fn &rest args) + :around #'save-place-to-alist + (unless doom-large-file-p + (apply orig-fn args))) + (defadvice! doom--dont-prettify-saveplace-cache-a (orig-fn) "`save-place-alist-to-file' uses `pp' to prettify the contents of its cache. `pp' can be expensive for longer lists, and there's no reason to prettify cache @@ -265,6 +274,7 @@ files, so we replace calls to `pp' with the much faster `prin1'." (use-package! server :when (display-graphic-p) :after-call pre-command-hook after-find-file focus-out-hook + :defer 1 :init (when-let (name (getenv "EMACS_SERVER_NAME")) (setq server-name name)) @@ -358,6 +368,10 @@ files, so we replace calls to `pp' with the much faster `prin1'." :around #'dtrt-indent-mode (let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie)) (cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess)) + (old-smie-config--guess (symbol-function 'symbol-config--guess)) + ((symbol-function 'symbol-config--guess) + (lambda (beg end) + (funcall old-smie-config--guess beg (min end 10000)))) ((symbol-function 'smie-config-guess) (lambda () (condition-case e (funcall old-smie-config-guess) @@ -372,12 +386,11 @@ files, so we replace calls to `pp' with the much faster `prin1'." ;; a better *help* buffer :commands helpful--read-symbol :init - (define-key! - [remap describe-function] #'helpful-callable - [remap describe-command] #'helpful-command - [remap describe-variable] #'helpful-variable - [remap describe-key] #'helpful-key - [remap describe-symbol] #'doom/describe-symbol) + (global-set-key [remap describe-function] #'helpful-callable) + (global-set-key [remap describe-command] #'helpful-command) + (global-set-key [remap describe-variable] #'helpful-variable) + (global-set-key [remap describe-key] #'helpful-key) + (global-set-key [remap describe-symbol] #'helpful-symbol) (defun doom-use-helpful-a (orig-fn &rest args) "Force ORIG-FN to use helpful instead of the old describe-* commands." @@ -413,23 +426,25 @@ files, so we replace calls to `pp' with the much faster `prin1'." (require 'smartparens-config) ;; Overlays are too distracting and not terribly helpful. show-parens does - ;; this for us already, so... + ;; this for us already (and is faster), so... (setq sp-highlight-pair-overlay nil sp-highlight-wrap-overlay nil sp-highlight-wrap-tag-overlay nil) - ;; But if someone does want overlays enabled, evil users will be stricken with - ;; an off-by-one issue where smartparens assumes you're outside the pair when - ;; you're really at the last character in insert mode. We must correct this - ;; vile injustice. - (setq sp-show-pair-from-inside t) - ;; ...and stay highlighted until we've truly escaped the pair! - (setq sp-cancel-autoskip-on-backward-movement nil) + (with-eval-after-load 'evil + ;; But if someone does want overlays enabled, evil users will be stricken + ;; with an off-by-one issue where smartparens assumes you're outside the + ;; pair when you're really at the last character in insert mode. We must + ;; correct this vile injustice. + (setq sp-show-pair-from-inside t) + ;; ...and stay highlighted until we've truly escaped the pair! + (setq sp-cancel-autoskip-on-backward-movement nil)) + ;; The default is 100, because smartparen's scans are relatively expensive - ;; (especially with large pair lists for somoe modes), we halve it, as a + ;; (especially with large pair lists for some modes), we reduce it, as a ;; better compromise between performance and accuracy. - (setq sp-max-prefix-length 50) - ;; This speeds up smartparens. No pair has any business being longer than 4 - ;; characters; if they must, the modes that need it set it buffer-locally. + (setq sp-max-prefix-length 25) + ;; No pair has any business being longer than 4 characters; if they must, set + ;; it buffer-locally. It's less work for smartparens. (setq sp-max-pair-length 4) ;; This isn't always smart enough to determine when we're in a string or not. ;; See https://github.com/Fuco1/smartparens/issues/783. @@ -437,13 +452,13 @@ files, so we replace calls to `pp' with the much faster `prin1'." ;; Silence some harmless but annoying echo-area spam (dolist (key '(:unmatched-expression :no-matching-tag)) - (setf (cdr (assq key sp-message-alist)) nil)) + (setf (alist-get key sp-message-alist) nil)) (add-hook! 'minibuffer-setup-hook (defun doom-init-smartparens-in-minibuffer-maybe-h () - "Enable `smartparens-mode' in the minibuffer, during `eval-expression' or -`evil-ex'." - (when (memq this-command '(eval-expression evil-ex)) + "Enable `smartparens-mode' in the minibuffer, during `eval-expression', +`pp-eval-expression' or `evil-ex'." + (when (memq this-command '(eval-expression pp-eval-expression evil-ex)) (smartparens-mode)))) ;; You're likely writing lisp in the minibuffer, therefore, disable these @@ -481,6 +496,10 @@ files, so we replace calls to `pp' with the much faster `prin1'." (delq! 'buffer-read-only so-long-variable-overrides 'assq) ;; ...but at least reduce the level of syntax highlighting (add-to-list 'so-long-variable-overrides '(font-lock-maximum-decoration . 1)) + ;; ...and insist that save-place not operate in large/long files + (add-to-list 'so-long-variable-overrides '(save-place-alist . nil)) + ;; Text files could possibly be too long too + (add-to-list 'so-long-target-modes 'text-mode) ;; But disable everything else that may be unnecessary/expensive for large ;; or wide buffers. (appendq! so-long-minor-modes @@ -494,14 +513,26 @@ files, so we replace calls to `pp' with the much faster `prin1'." auto-composition-mode undo-tree-mode highlight-indent-guides-mode - hl-fill-column-mode))) + hl-fill-column-mode)) + (defun doom-buffer-has-long-lines-p () + ;; HACK Fix #2183: `so-long-detected-long-line-p' tries to parse comment + ;; syntax, but in some buffers comment state isn't initialized, leading + ;; to a wrong-type-argument: stringp error. + (let ((so-long-skip-leading-comments (bound-and-true-p comment-use-syntax))) + ;; HACK If visual-line-mode is on in a text-mode, then long lines are + ;; normal and can be ignored. + (unless (and visual-line-mode (derived-mode-p 'text-mode)) + (so-long-detected-long-line-p)))) + (setq so-long-predicate #'doom-buffer-has-long-lines-p)) (use-package! undo-tree ;; Branching & persistent undo :after-call doom-switch-buffer-hook after-find-file :config - (setq undo-tree-auto-save-history t + (setq undo-tree-visualizer-diff t + undo-tree-auto-save-history t + undo-tree-enable-undo-in-region t ;; Increase undo-limits by a factor of ten to avoid emacs prematurely ;; truncating the undo history and corrupting the tree. See ;; https://github.com/syl20bnr/spacemacs/issues/12110 @@ -532,16 +563,18 @@ files, so we replace calls to `pp' with the much faster `prin1'." (stringp (car item)) (setcar item (substring-no-properties (car item)))))) + ;; Undo-tree is too chatty about saving its history files. This doesn't + ;; totally suppress it logging to *Messages*, it only stops it from appearing + ;; in the echo-area. + (advice-add #'undo-tree-save-history :around #'doom-shut-up-a) + (global-undo-tree-mode +1)) (use-package! ws-butler ;; a less intrusive `delete-trailing-whitespaces' on save :after-call after-find-file - :config - (appendq! ws-butler-global-exempt-modes - '(special-mode comint-mode term-mode eshell-mode)) - (ws-butler-global-mode)) + :config (ws-butler-global-mode +1)) (provide 'core-editor) ;;; core-editor.el ends here diff --git a/core/core-lib.el b/core/core-lib.el index caed77b53..e695cf907 100644 --- a/core/core-lib.el +++ b/core/core-lib.el @@ -93,6 +93,7 @@ Accepts the same arguments as `message'." ARGS is a list of the last N arguments to pass to FUN. The result is a new function which does the same as FUN, except that the last N arguments are fixed at the values with which this function was called." + (declare (pure t) (side-effect-free t)) (lambda (&rest pre-args) (apply fn (append pre-args args)))) @@ -101,14 +102,18 @@ at the values with which this function was called." ;;; Sugars (defmacro λ! (&rest body) - "Expands to (lambda () (interactive) ,@body)." - (declare (doc-string 1)) + "Expands to (lambda () (interactive) ,@body). +A factory for quickly producing interaction commands, particularly for keybinds +or aliases." + (declare (doc-string 1) (pure t) (side-effect-free t)) `(lambda () (interactive) ,@body)) (defalias 'lambda! 'λ!) (defun λ!! (command &optional arg) - "Expands to a command that interactively calls COMMAND with prefix ARG." - (declare (doc-string 1)) + "Expands to a command that interactively calls COMMAND with prefix ARG. +A factory for quickly producing interactive, prefixed commands for keybinds or +aliases." + (declare (doc-string 1) (pure t) (side-effect-free t)) (lambda () (interactive) (let ((current-prefix-arg arg)) (call-interactively command)))) @@ -128,8 +133,62 @@ at the values with which this function was called." (when-let (path (file!)) (directory-file-name (file-name-directory path)))) +(defmacro after! (package &rest body) + "Evaluate BODY after PACKAGE have loaded. + +PACKAGE is a symbol or list of them. These are package names, not modes, +functions or variables. It can be: + +- An unquoted package symbol (the name of a package) + (after! helm BODY...) +- An unquoted list of package symbols (i.e. BODY is evaluated once both magit + and git-gutter have loaded) + (after! (magit git-gutter) BODY...) +- An unquoted, nested list of compound package lists, using any combination of + :or/:any and :and/:all + (after! (:or package-a package-b ...) BODY...) + (after! (:and package-a package-b ...) BODY...) + (after! (:and package-a (:or package-b package-c) ...) BODY...) + Without :or/:any/:and/:all, :and/:all are implied. + +This is a wrapper around `eval-after-load' that: + +1. Suppresses warnings for disabled packages at compile-time +2. No-ops for package that are disabled by the user (via `package!') +3. Supports compound package statements (see below) +4. Prevents eager expansion pulling in autoloaded macros all at once" + (declare (indent defun) (debug t)) + (if (symbolp package) + (unless (memq package (bound-and-true-p doom-disabled-packages)) + (list (if (or (not (bound-and-true-p byte-compile-current-file)) + (require package nil 'noerror)) + #'progn + #'with-no-warnings) + (let ((body (macroexp-progn body))) + `(if (featurep ',package) + ,body + ;; We intentionally avoid `with-eval-after-load' to prevent + ;; eager macro expansion from pulling (or failing to pull) in + ;; autoloaded macros/packages. + (eval-after-load ',package ',body))))) + (let ((p (car package))) + (cond ((not (keywordp p)) + `(after! (:and ,@package) ,@body)) + ((memq p '(:or :any)) + (macroexp-progn + (cl-loop for next in (cdr package) + collect `(after! ,next ,@body)))) + ((memq p '(:and :all)) + (dolist (next (cdr package)) + (setq body `((after! ,next ,@body)))) + (car body)))))) + (defmacro setq! (&rest settings) - "A stripped-down `customize-set-variable' with the syntax of `setq'." + "A stripped-down `customize-set-variable' with the syntax of `setq'. + +Use this instead of `setq' when you know a variable has a custom setter (a :set +property in its `defcustom' declaration). This trigger setters. `setq' does +not." (macroexp-progn (cl-loop for (var val) on settings by 'cddr collect `(funcall (or (get ',var 'custom-set) #'set) @@ -150,10 +209,6 @@ This is a variadic `cl-pushnew'." "Append LISTS to SYM in place." `(setq ,sym (append ,sym ,@lists))) -(defmacro nconcq! (sym &rest lists) - "Append LISTS to SYM by altering them in place." - `(setq ,sym (nconc ,sym ,@lists))) - (defmacro delq! (elt list &optional fetcher) "`delq' ELT from LIST in-place. @@ -164,6 +219,15 @@ If FETCHER is a function, ELT is used as the key in LIST (an alist)." elt) ,list))) +(defmacro letenv! (envvars &rest body) + "Lexically bind ENVVARS in BODY, like `let' but for `process-environment'." + (declare (indent 1)) + `(let ((process-environment (copy-sequence process-environment))) + (dolist (var (list ,@(cl-loop for (var val) in envvars + collect `(cons ,var ,val)))) + (setenv (car var) (cdr var))) + ,@body)) + (defmacro add-load-path! (&rest dirs) "Add DIRS to `load-path', relative to the current file. The current file is the file from which `add-to-load-path!' is used." @@ -203,13 +267,14 @@ If N and M = 1, there's no benefit to using this macro over `add-hook'. This macro accepts, in order: - 1. Optional properties :local and/or :append, which will make the hook + 1. The mode(s) or hook(s) to add to. This is either an unquoted mode, an + unquoted list of modes, a quoted hook variable or a quoted list of hook + variables. + 2. Optional properties :local and/or :append, which will make the hook buffer-local or append to the list of hooks (respectively), - 2. The hook(s) to be added to: either an unquoted mode, an unquoted list of - modes, a quoted hook variable or a quoted list of hook variables. If - unquoted, '-hook' will be appended to each symbol. - 3. The function(s) to be added: this can be one function, a list thereof, a - list of `defun's, or body forms (implicitly wrapped in a closure). + 3. The function(s) to be added: this can be one function, a quoted list + thereof, a list of `defun's, or body forms (implicitly wrapped in a + lambda). \(fn HOOKS [:append :local] FUNCTIONS)" (declare (indent (lambda (indent-point state) @@ -408,10 +473,27 @@ DOCSTRING and BODY are as in `defun'. where-alist)) `(progn (defun ,symbol ,arglist ,docstring ,@body) - ,(when where-alist - `(dolist (targets (list ,@(nreverse where-alist))) - (dolist (target (cdr targets)) - (advice-add target (car targets) #',symbol))))))) + (dolist (targets (list ,@(nreverse where-alist))) + (dolist (target (cdr targets)) + (advice-add target (car targets) #',symbol)))))) + +(defmacro undefadvice! (symbol _arglist &optional docstring &rest body) + "Undefine an advice called SYMBOL. + +This has the same signature as `defadvice!' an exists as an easy undefiner when +testing advice (when combined with `rotate-text'). + +\(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)" + (declare (doc-string 3) (indent defun)) + (let (where-alist) + (unless (stringp docstring) + (push docstring body)) + (while (keywordp (car body)) + (push `(cons ,(pop body) (doom-enlist ,(pop body))) + where-alist)) + `(dolist (targets (list ,@(nreverse where-alist))) + (dolist (target (cdr targets)) + (advice-remove target #',symbol))))) (provide 'core-lib) ;;; core-lib.el ends here diff --git a/core/core-modules.el b/core/core-modules.el index bea41a24a..b4ff99504 100644 --- a/core/core-modules.el +++ b/core/core-modules.el @@ -13,8 +13,8 @@ (defconst doom-obsolete-modules '((:feature (version-control (:emacs vc) (:ui vc-gutter)) - (spellcheck (:tools flyspell)) - (syntax-checker (:tools flycheck)) + (spellcheck (:checkers spell)) + (syntax-checker (:checkers syntax)) (evil (:editor evil)) (snippets (:editor snippets)) (file-templates (:editor file-templates)) @@ -24,7 +24,9 @@ (debugger (:tools debugger))) (:tools (rotate-text (:editor rotate-text)) (vterm (:term vterm)) - (password-store (:tools pass))) + (password-store (:tools pass)) + (flycheck (:checkers syntax)) + (flyspell (:checkers spell))) (:emacs (electric-indent (:emacs electric)) (hideshow (:editor fold)) (eshell (:term eshell)) @@ -40,7 +42,7 @@ Each entry is a three-level tree. For example: (:feature (version-control (:emacs vc) (:ui vc-gutter)) - (spellcheck (:tools flyspell)) + (spellcheck (:checkers spell)) (syntax-checker (:tools flycheck))) This marks :feature version-control, :feature spellcheck and :feature @@ -76,6 +78,7 @@ non-nil." (when (or force-p (not doom-init-modules-p)) (setq doom-init-modules-p t doom-modules nil) + (load custom-file 'noerror 'nomessage) (when (load! "init" doom-private-dir t) (when doom-modules (maphash (lambda (key plist) @@ -101,11 +104,10 @@ non-nil." (defun doom-module-p (category module &optional flag) "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." (declare (pure t) (side-effect-free t)) - (let ((plist (gethash (cons category module) doom-modules))) - (and plist - (or (null flag) - (memq flag (plist-get plist :flags))) - t))) + (when-let (plist (gethash (cons category module) doom-modules)) + (or (null flag) + (and (memq flag (plist-get plist :flags)) + t)))) (defun doom-module-get (category module &optional property) "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." @@ -186,7 +188,8 @@ If ENABLED-ONLY, return nil if the containing module isn't enabled." (cdr doom--current-module)) doom--current-module) doom--current-module) - (doom-module-from-path (file!))) + (ignore-errors + (doom-module-from-path (file!)))) (let* ((file-name-handler-alist nil) (path (file-truename (or path (file!))))) (save-match-data @@ -332,14 +335,15 @@ This value is cached. If REFRESH-P, then don't use the cached value." (defmacro doom! (&rest modules) "Bootstraps DOOM Emacs and its modules. +If the first item in MODULES doesn't satisfy `keywordp', MODULES is evaluated, +otherwise, MODULES is a multiple-property list (a plist where each key can have +multiple, linear values). + The bootstrap process involves making sure the essential directories exist, core packages are installed, `doom-autoload-file' is loaded, `doom-packages-file' cache exists (and is loaded) and, finally, loads your private init.el (which should contain your `doom!' block). -If the cache exists, much of this function isn't run, which substantially -reduces startup time. - The overall load order of Doom is as follows: ~/.emacs.d/init.el @@ -358,9 +362,10 @@ The overall load order of Doom is as follows: Module load order is determined by your `doom!' block. See `doom-modules-dirs' for a list of all recognized module trees. Order defines precedence (from most to least)." - `(let ((modules ',modules)) - (unless (keywordp (car modules)) - (setq modules (eval modules t))) + `(let ((modules + ,@(if (keywordp (car modules)) + (list (list 'quote modules)) + modules))) (unless doom-modules (setq doom-modules (make-hash-table :test 'equal @@ -524,7 +529,7 @@ Module FLAGs are set in your config's `doom!' block, typically in :config (default +flag1 -flag2) CATEGORY and MODULE can be omitted When this macro is used from inside a module -(except your DOOMDIR, which is a special moduel). e.g. (featurep! +flag)" +(except your DOOMDIR, which is a special module). e.g. (featurep! +flag)" (and (cond (flag (memq flag (doom-module-get category module :flags))) (module (doom-module-p category module)) (doom--current-flags (memq category doom--current-flags)) @@ -535,66 +540,16 @@ CATEGORY and MODULE can be omitted When this macro is used from inside a module (memq category (doom-module-get (car module) (cdr module) :flags))))) t)) -(defmacro after! (package &rest body) - "Evaluate BODY after PACKAGE have loaded. - -PACKAGE is a symbol or list of them. These are package names, not modes, -functions or variables. It can be: - -- An unquoted package symbol (the name of a package) - (after! helm BODY...) -- An unquoted list of package symbols (i.e. BODY is evaluated once both magit - and git-gutter have loaded) - (after! (magit git-gutter) BODY...) -- An unquoted, nested list of compound package lists, using any combination of - :or/:any and :and/:all - (after! (:or package-a package-b ...) BODY...) - (after! (:and package-a package-b ...) BODY...) - (after! (:and package-a (:or package-b package-c) ...) BODY...) - Without :or/:any/:and/:all, :and/:all are implied. - -This is a wrapper around `eval-after-load' that: - -1. Suppresses warnings for disabled packages at compile-time -2. No-ops for package that are disabled by the user (via `package!') -3. Supports compound package statements (see below) -4. Prevents eager expansion pulling in autoloaded macros all at once" - (declare (indent defun) (debug t)) - (if (symbolp package) - (unless (memq package (bound-and-true-p doom-disabled-packages)) - (list (if (or (not (bound-and-true-p byte-compile-current-file)) - (require package nil 'noerror)) - #'progn - #'with-no-warnings) - (let ((body (macroexp-progn body))) - `(if (featurep ',package) - ,body - ;; We intentionally avoid `with-eval-after-load' to prevent - ;; eager macro expansion from pulling (or failing to pull) in - ;; autoloaded macros/packages. - (eval-after-load ',package ',body))))) - (let ((p (car package))) - (cond ((not (keywordp p)) - `(after! (:and ,@package) ,@body)) - ((memq p '(:or :any)) - (macroexp-progn - (cl-loop for next in (cdr package) - collect `(after! ,next ,@body)))) - ((memq p '(:and :all)) - (dolist (next (cdr package)) - (setq body `((after! ,next ,@body)))) - (car body)))))) - ;; DEPRECATED (defmacro def-package! (&rest args) - (make-obsolete 'def-package! 'use-package! "2.0.9") - (message "`def-package!' is renamed and is now deprecated; use `use-package!' instead") + (message "`def-package!' was renamed to `use-package!'; use that instead.") `(use-package! ,@args)) +(make-obsolete 'def-package! 'use-package! "2.0.9") (defmacro def-package-hook! (&rest args) - (make-obsolete 'def-package-hook! 'use-package-hook! "2.0.9") - (message "`def-package-hook!' is renamed and is now deprecated; use `use-package-hook!' instead") + (message "`def-package-hook!' was renamed to `use-package-hook!'; use that instead.") `(use-package-hook! ,@args)) +(make-obsolete 'def-package-hook! 'use-package-hook! "2.0.9") (provide 'core-modules) ;;; core-modules.el ends here diff --git a/core/core-packages.el b/core/core-packages.el index fd5bb0a1a..ea21d41e7 100644 --- a/core/core-packages.el +++ b/core/core-packages.el @@ -21,7 +21,7 @@ ;; ;; + `bin/doom install`: a wizard that guides you through setting up Doom and ;; your private config for the first time. -;; + `bin/doom refresh`: your go-to command for making sure Doom is in optimal +;; + `bin/doom sync`: your go-to command for making sure Doom is in optimal ;; condition. It ensures all unneeded packages are removed, all needed ones ;; are installed, and all metadata associated with them is generated. ;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest @@ -43,7 +43,7 @@ package's name as a symbol, and whose CDR is the plist supplied to its `package!' declaration. Set by `doom-initialize-packages'.") -(defvar doom-core-packages '(straight use-package async) +(defvar doom-core-packages '(straight use-package) "A list of packages that must be installed (and will be auto-installed if missing) and shouldn't be deleted.") @@ -72,8 +72,7 @@ missing) and shouldn't be deleted.") ;; Ensure that, if we do need package.el, it is configured correctly. You really ;; shouldn't be using it, but it may be convenient for quick package testing. -(setq package--init-file-ensured t - package-enable-at-startup nil +(setq package-enable-at-startup nil package-user-dir (concat doom-local-dir "elpa/") package-gnupghome-dir (expand-file-name "gpg" package-user-dir) ;; I omit Marmalade because its packages are manually submitted rather @@ -84,6 +83,8 @@ missing) and shouldn't be deleted.") ("melpa" . ,(concat proto "://melpa.org/packages/")) ("org" . ,(concat proto "://orgmode.org/elpa/"))))) +(advice-add #'package--ensure-init-file :override #'ignore) + ;; Don't save `package-selected-packages' to `custom-file' (defadvice! doom--package-inhibit-custom-file-a (&optional value) :override #'package--save-selected-packages @@ -109,73 +110,17 @@ missing) and shouldn't be deleted.") ;; certain things to work (like magit and org), but we can deal with that ;; when we cross that bridge. straight-vc-git-default-clone-depth 1 - ;; Straight's own emacsmirror mirror is a little smaller and faster. - straight-recipes-emacsmirror-use-mirror t ;; Prefix declarations are unneeded bulk added to our autoloads file. Best ;; we just don't have to deal with them at all. - autoload-compute-prefixes nil) + autoload-compute-prefixes nil + ;; We handle it ourselves + straight-fix-org nil) -(defun doom--finalize-straight () - (mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist))) - (setq straight--transaction-alist nil)) - -;;; Getting straight to behave in batch mode -(when noninteractive - ;; HACK Remove dired & magit options from prompt, since they're inaccessible - ;; in noninteractive sessions. - (advice-add #'straight-vc-git--popup-raw :override #'straight--popup-raw)) - -;; HACK Replace GUI popup prompts (which hang indefinitely in tty Emacs) with -;; simple prompts. -(defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt) - :around #'straight-are-you-sure - (if noninteractive - (y-or-n-p (format! "%s" (or prompt ""))) - (funcall orig-fn prompt))) - -(defadvice! doom--straight-fallback-to-tty-prompt-a (orig-fn prompt actions) - "Modifies straight to prompt on the terminal when in noninteractive sessions." - :around #'straight--popup-raw - (if (not noninteractive) - (funcall orig-fn prompt actions) - ;; We can't intercept C-g, so no point displaying any options for this key - ;; Just use C-c - (delq! "C-g" actions 'assoc) - ;; HACK These are associated with opening dired or magit, which isn't - ;; possible in tty Emacs, so... - (delq! "e" actions 'assoc) - (delq! "g" actions 'assoc) - (let ((options (list (lambda () - (let ((doom-format-indent 0)) - (terpri) - (print! (error "Aborted"))) - (kill-emacs))))) - (print! (start "%s") (red prompt)) - (terpri) - (print-group! - (print-group! - (print! " 1) Abort") - (dolist (action actions) - (cl-destructuring-bind (_key desc func) action - (when desc - (push func options) - (print! "%2s) %s" (length options) desc))))) - (terpri) - (let ((options (nreverse options)) - answer fn) - (while - (not - (setq - fn (ignore-errors - (nth (1- (setq answer - (read-number - (format! "How to proceed? (%s) " - (mapconcat #'number-to-string - (number-sequence 1 (length options)) - ", "))))) - options)))) - (print! (warn "%s is not a valid answer, try again.") answer)) - (funcall fn)))))) +(defadvice! doom--read-pinned-packages-a (orig-fn &rest args) + "Read from `doom-pinned-packages' on top of straight's lockfiles." + :around #'straight--lockfile-read-all + (append (apply orig-fn args) + (doom-package-pinned-list))) ;; @@ -190,7 +135,7 @@ This ensure `doom-packages' is populated, if isn't aren't already. Use this before any of straight's or Doom's package management's API to ensure all the necessary package metadata is initialized and available for them." (unless doom-init-packages-p - (setq force-p t)) + (setq force-p t)) (when (or force-p (not (bound-and-true-p package--initialized))) (doom-log "Initializing package.el") (require 'package) @@ -198,63 +143,69 @@ necessary package metadata is initialized and available for them." (when (or force-p (not doom-packages)) (doom-log "Initializing straight") (setq doom-init-packages-p t) - (unless (fboundp 'straight--reset-caches) - (doom-ensure-straight) - (require 'straight)) - (straight--reset-caches) - (mapc #'straight-use-recipes doom-core-package-sources) - (straight-register-package - `(straight :type git :host github - :repo ,(format "%s/straight.el" straight-repository-user) - :files ("straight*.el") - :branch ,straight-repository-branch - :no-byte-compile t)) + (doom-ensure-straight) (mapc #'straight-use-package doom-core-packages) (doom-log "Initializing doom-packages") (setq doom-disabled-packages nil + doom-pinned-packages nil doom-packages (doom-package-list)) - (cl-loop for (pkg . plist) in doom-packages - for ignored = (plist-get plist :ignore) - for disabled = (plist-get plist :disable) - if disabled - do (cl-pushnew pkg doom-disabled-packages) - else if (not ignored) - do (with-demoted-errors "Package error: %s" - (straight-register-package - (if-let (recipe (plist-get plist :recipe)) - (let ((plist (straight-recipes-retrieve pkg))) - `(,pkg ,@(doom-plist-merge recipe (cdr plist)))) - pkg)))) - (unless doom-interactive-mode - (add-hook 'kill-emacs-hook #'doom--finalize-straight)))) + (dolist (package doom-packages) + (let ((name (car package))) + (with-plist! (cdr package) (recipe modules disable ignore pin) + (if ignore + (doom-log "Ignoring package %S" name) + (if (not disable) + (with-demoted-errors "Package error: %s" + (when recipe + (straight-override-recipe (cons name recipe))) + (straight-register-package name)) + (doom-log "Disabling package %S" name) + (cl-pushnew name doom-disabled-packages) + ;; Warn about disabled core packages + (when (cl-find :core modules :key #'car) + (print! (warn "%s\n%s") + (format "You've disabled %S" name) + (indent 2 (concat "This is a core package. Disabling it will cause errors, as Doom assumes\n" + "core packages are always available. Disable their minor-modes or hooks instead."))))))))))) (defun doom-ensure-straight () "Ensure `straight' is installed and was compiled with this version of Emacs." - (defvar bootstrap-version) - (let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of - ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d. - (user-emacs-directory straight-base-dir) - (bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el")) - (bootstrap-version 5)) - (make-directory (doom-path straight-base-dir "straight/build") 'parents) - (unless (featurep 'straight) - (unless (or (require 'straight nil t) - (file-readable-p bootstrap-file)) - (with-current-buffer - (url-retrieve-synchronously - (format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el" - straight-repository-branch) - 'silent 'inhibit-cookies) - (goto-char (point-max)) - (eval-print-last-sexp))) - (load bootstrap-file nil t)))) + (unless (fboundp 'straight--reset-caches) + (defvar bootstrap-version) + (let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of + ;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d. + (user-emacs-directory straight-base-dir) + (bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el")) + (bootstrap-version 5)) + (make-directory (doom-path straight-base-dir "straight/build") 'parents) + (or (require 'straight nil t) + (file-readable-p bootstrap-file) + (with-current-buffer + (url-retrieve-synchronously + (format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el" + straight-repository-branch) + 'silent 'inhibit-cookies) + (goto-char (point-max)) + (eval-print-last-sexp))) + (load bootstrap-file nil t)) + (require 'straight)) + (straight--reset-caches) + (setq straight-recipe-repositories nil + straight-recipe-overrides nil) + (mapc #'straight-use-recipes doom-core-package-sources) + (straight-register-package + `(straight :type git :host github + :repo ,(format "%s/straight.el" straight-repository-user) + :files ("straight*.el") + :branch ,straight-repository-branch + :no-byte-compile t))) ;; ;;; Module package macros (cl-defmacro package! - (name &rest plist &key built-in recipe ignore _disable _freeze) + (name &rest plist &key built-in recipe ignore _pin _disable) "Declares a package and how to install it (if applicable). This macro is declarative and does not load nor install packages. It is used to @@ -266,31 +217,38 @@ Only use this macro in a module's packages.el file. Accepts the following properties: :recipe RECIPE - Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); - for packages to be installed from external sources. + Specifies a straight.el recipe to allow you to acquire packages from external + sources. See https://github.com/raxod502/straight.el#the-recipe-format for + details on this recipe. :disable BOOL Do not install or update this package AND disable all of its `use-package!' - blocks. + and `after!' blocks. :ignore FORM Do not install this package. - :freeze FORM - Do not update this package if FORM is non-nil. - :built-in BOOL - Same as :ignore if the package is a built-in Emacs package. If set to - 'prefer, will use built-in package if it is present. + :pin STR|nil + Pin this package to commit hash STR. Setting this to nil will unpin this + package if previously pinned. + :built-in BOOL|'prefer + Same as :ignore if the package is a built-in Emacs package. This is more to + inform help commands like `doom/help-packages' that this is a built-in + package. If set to 'prefer, the package will not be installed if it is + already provided by Emacs. Returns t if package is successfully registered, and nil if it was disabled elsewhere." (declare (indent defun)) (when (and recipe (keywordp (car-safe recipe))) (plist-put! plist :recipe `(quote ,recipe))) + ;; :built-in t is basically an alias for :ignore (locate-library NAME) (when built-in - (when (and (not ignore) (equal built-in '(quote prefer))) + (when (and (not ignore) + (equal built-in '(quote prefer))) (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path))) (plist-delete! plist :built-in) (plist-put! plist :ignore built-in)) `(let* ((name ',name) (plist (cdr (assq name doom-packages)))) + ;; Record what module this declaration was found in (let ((module-list (plist-get plist :modules)) (module ',(doom-module-from-path))) (unless (member module module-list) @@ -298,27 +256,31 @@ elsewhere." (append module-list (list module) nil)))) - + ;; Merge given plist with pre-existing one (doplist! ((prop val) (list ,@plist) plist) (unless (null val) (plist-put! plist prop val))) - ;; Some basic key validation; error if you're not using a valid key (condition-case e - (cl-destructuring-bind - (&key _local-repo _files _flavor _no-build - _type _repo _host _branch _remote _nonrecursive _fork _depth) - (plist-get plist :recipe)) + (when-let (recipe (plist-get plist :recipe)) + (cl-destructuring-bind + (&key local-repo _files _flavor + _no-build _no-byte-compile _no-autoloads + _type _repo _host _branch _remote _nonrecursive _fork _depth) + recipe + ;; Expand :local-repo from current directory + (when local-repo + (plist-put! plist :recipe + (plist-put recipe :local-repo + (expand-file-name local-repo ,(dir!))))))) (error (signal 'doom-package-error (cons ,(symbol-name name) (error-message-string e))))) - + ;; This is the only side-effect of this macro! (setf (alist-get name doom-packages) plist) - (if (not (plist-get plist :disable)) t - (doom-log "Disabling package %S" name) - (cl-pushnew name doom-disabled-packages) - nil))) + (with-no-warnings + (not (plist-get plist :disable))))) (defmacro disable-packages! (&rest packages) "A convenience macro for disabling packages in bulk. @@ -327,5 +289,43 @@ Only use this macro in a module's (or your private) packages.el file." (cl-loop for p in packages collect `(package! ,p :disable t)))) +(defmacro unpin! (&rest targets) + "Unpin packages in TARGETS. + +This unpins packages, so that 'doom upgrade' downloads their latest version. It +can be used one of five ways: + ++ To disable pinning wholesale: (unpin! t) ++ To unpin individual packages: (unpin! packageA packageB ...) ++ To unpin all packages in a group of modules: (unpin! :lang :tools ...) ++ To unpin packages in individual modules: + (unpin! (:lang python javascript) (:tools docker)) + +Or any combination of the above. + +This macro should only be used from the user's private packages.el. No module +should use it!" + (if (memq t targets) + `(mapc (doom-rpartial #'doom-package-set :unpin t) + (mapcar #'car doom-packages)) + (let (forms) + (dolist (target targets) + (cl-check-type target (or symbol keyword list)) + (cond + ((symbolp target) + (push `(doom-package-set ',target :unpin t) forms)) + ((or (keywordp target) + (listp target)) + (cl-destructuring-bind (category . modules) (doom-enlist target) + (dolist (pkg doom-packages) + (let ((pkg-modules (plist-get (cdr pkg) :modules))) + (and (assq category pkg-modules) + (or (null modules) + (cl-loop for module in modules + if (member (cons category module) pkg-modules) + return t)) + (push `(doom-package-set ',(car pkg) :unpin t) forms)))))))) + (macroexp-progn forms)))) + (provide 'core-packages) ;;; core-packages.el ends here diff --git a/core/core-projects.el b/core/core-projects.el index e2920934b..05c6a0ba7 100644 --- a/core/core-projects.el +++ b/core/core-projects.el @@ -31,15 +31,11 @@ Emacs.") :init (setq projectile-cache-file (concat doom-cache-dir "projectile.cache") projectile-enable-caching doom-interactive-mode - projectile-known-projects-file (concat doom-cache-dir "projectile.projects") - projectile-require-project-root t projectile-globally-ignored-files '(".DS_Store" "Icon " "TAGS") projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o") - projectile-ignored-projects '("~/" "/tmp") projectile-kill-buffers-filter 'kill-only-files - projectile-files-cache-expire 604800 ; expire after a week - projectile-sort-order 'recentf - projectile-use-git-grep t) ; use git-grep for text searches + projectile-known-projects-file (concat doom-cache-dir "projectile.projects") + projectile-ignored-projects '("~/" "/tmp")) (global-set-key [remap evil-jump-to-tag] #'projectile-find-tag) (global-set-key [remap find-tag] #'projectile-find-tag) @@ -47,6 +43,21 @@ Emacs.") :config (projectile-mode +1) + ;; Projectile runs four functions to determine the root (in this order): + ;; + ;; + `projectile-root-local' -> consults the `projectile-project-root' + ;; variable for an explicit path. + ;; + `projectile-root-bottom-up' -> consults + ;; `projectile-project-root-files-bottom-up'; searches from / to your + ;; current directory for certain files (including .project and .git) + ;; + `projectile-root-top-down' -> consults `projectile-project-root-files'; + ;; searches from the current directory down to / for certain project + ;; markers, like package.json, setup.py, or Cargo.toml + ;; + `projectile-root-top-down-recurring' -> consults + ;; `projectile-project-root-files-top-down-recurring'; e.g. searches from + ;; the current directory down to / for a directory that has Makefile but + ;; doesn't have a parent with one of those files. + ;; ;; In the interest of performance, we reduce the number of project root marker ;; files/directories projectile searches for when resolving the project root. (setq projectile-project-root-files-bottom-up @@ -54,20 +65,14 @@ Emacs.") ".git") ; Git VCS root dir (when (executable-find "hg") '(".hg")) ; Mercurial VCS root dir - (when (executable-find "fossil") - '(".fslckout" ; Fossil VCS root dir - "_FOSSIL_")) ; Fossil VCS root DB on Windows (when (executable-find "bzr") - '(".bzr")) ; Bazaar VCS root dir - (when (executable-find "darcs") - '("_darcs"))) ; Darcs VCS root dir + '(".bzr"))) ; Bazaar VCS root dir ;; This will be filled by other modules. We build this list manually so ;; projectile doesn't perform so many file checks every time it resolves ;; a project's root -- particularly when a file has no project. - projectile-project-root-files '("TAGS") - projectile-project-root-files-top-down-recurring '(".svn" "Makefile")) + projectile-project-root-files '() + projectile-project-root-files-top-down-recurring '("Makefile")) - ;; a more generic project root file (push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories) ;; Disable commands that won't work, as is, and that Doom already provides a @@ -125,10 +130,11 @@ c) are not valid projectile projects." ;; that is significantly faster than git ls-files or find, and it respects ;; .gitignore. This is recommended in the projectile docs. ((executable-find doom-projectile-fd-binary) - (setq projectile-git-command (concat - doom-projectile-fd-binary - " . --color=never --type f -0 -H -E .git") - projectile-generic-command projectile-git-command + (setq projectile-generic-command + (format "%s . --color=never --type f -0 -H -E .git" + doom-projectile-fd-binary) + projectile-git-command projectile-generic-command + projectile-git-submodule-command nil ;; ensure Windows users get fd's benefits projectile-indexing-method 'alien)) @@ -138,25 +144,16 @@ c) are not valid projectile projects." (concat "rg -0 --files --color=never --hidden" (cl-loop for dir in projectile-globally-ignored-directories concat (format " --glob '!%s'" dir))) + projectile-git-command projectile-generic-command + projectile-git-submodule-command nil ;; ensure Windows users get rg's benefits - projectile-indexing-method 'alien) - ;; fix breakage on windows in git projects - (unless (executable-find "tr") - (setq projectile-git-submodule-command nil))) + projectile-indexing-method 'alien)) - ((not (executable-find "tr")) + ;; Fix breakage on windows in git projects with submodules, since Windows + ;; doesn't have tr + (IS-WINDOWS (setq projectile-git-submodule-command nil))) - (defadvice! doom--projectile-cache-timers-a () - "Persist `projectile-projects-cache-time' across sessions, so that -`projectile-files-cache-expire' checks won't reset when restarting Emacs." - :before #'projectile-serialize-cache - (projectile-serialize projectile-projects-cache-time doom-projectile-cache-timer-file)) - ;; Restore it - (when (file-readable-p doom-projectile-cache-timer-file) - (setq projectile-projects-cache-time - (projectile-unserialize doom-projectile-cache-timer-file))) - (defadvice! doom--projectile-default-generic-command-a (orig-fn &rest args) "If projectile can't tell what kind of project you're in, it issues an error when using many of projectile's command, e.g. `projectile-compile-command', @@ -171,12 +168,11 @@ the command instead." ;; Projectile root-searching functions can cause an infinite loop on TRAMP ;; connections, so disable them. ;; TODO Is this still necessary? - (defadvice! doom--projectile-locate-dominating-file-a (orig-fn file name) + (defadvice! doom--projectile-locate-dominating-file-a (file _name) "Don't traverse the file system if on a remote connection." - :around #'projectile-locate-dominating-file - (when (and (stringp file) - (not (file-remote-p file nil t))) - (funcall orig-fn file name)))) + :before-while #'projectile-locate-dominating-file + (and (stringp file) + (not (file-remote-p file nil t))))) ;; @@ -195,16 +191,15 @@ state are passed in.") on-load on-enter on-exit) - "Define a project minor-mode named NAME (a symbol) and declare where and how -it is activated. Project modes allow you to configure 'sub-modes' for -major-modes that are specific to a folder, project structure, framework or -whatever arbitrary context you define. These project modes can have their own -settings, keymaps, hooks, snippets, etc. + "Define a project minor mode named NAME and where/how it is activated. + +Project modes allow you to configure 'sub-modes' for major-modes that are +specific to a folder, project structure, framework or whatever arbitrary context +you define. These project modes can have their own settings, keymaps, hooks, +snippets, etc. This creates NAME-hook and NAME-map as well. -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. diff --git a/core/core-ui.el b/core/core-ui.el index 4ac1f96ff..6592485ec 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -173,7 +173,12 @@ read-only or not file-visiting." (setq hscroll-margin 2 hscroll-step 1 - scroll-conservatively 10 + ;; Emacs spends too much effort recentering the screen if you scroll the + ;; cursor more than N lines past window edges (where N is the settings of + ;; `scroll-conservatively'). This is especially slow in larger files + ;; during large-scale scrolling commands. If kept over 100, the window is + ;; never automatically recentered. + scroll-conservatively 101 scroll-margin 0 scroll-preserve-screen-position t ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' @@ -216,19 +221,21 @@ read-only or not file-visiting." (setq confirm-nonexistent-file-or-buffer t) -(defadvice! doom--switch-to-fallback-buffer-maybe-a (orig-fn) +(defadvice! doom--switch-to-fallback-buffer-maybe-a (&rest _) "Switch to `doom-fallback-buffer' if on last real buffer. Advice for `kill-current-buffer'. If in a dedicated window, delete it. If there are no real buffers left OR if all remaining buffers are visible in other windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original `kill-current-buffer'." - :around #'kill-current-buffer + :before-until #'kill-current-buffer (let ((buf (current-buffer))) (cond ((window-dedicated-p) - (delete-window)) + (delete-window) + t) ((eq buf (doom-fallback-buffer)) - (message "Can't kill the fallback buffer.")) + (message "Can't kill the fallback buffer.") + t) ((doom-real-buffer-p buf) (if (and buffer-file-name (buffer-modified-p buf) @@ -247,8 +254,8 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (switch-to-buffer (doom-fallback-buffer))) (unless (delq (selected-window) (get-buffer-window-list buf nil t)) (kill-buffer buf))) - (run-hooks 'buffer-list-update-hook))) - ((funcall orig-fn))))) + (run-hooks 'buffer-list-update-hook)) + t)))) ;; @@ -270,40 +277,46 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original (setq frame-title-format '("%b – Doom Emacs") icon-title-format frame-title-format) -;; Don't resize emacs in steps, it looks weird. +;; Don't resize windows & frames in steps; it's prohibitive to prevent the user +;; from resizing it to exact dimensions, and looks weird. (setq window-resize-pixelwise t frame-resize-pixelwise t) -(unless EMACS27+ ; We already do this in early-init.el - ;; Disable tool and scrollbars; Doom encourages keyboard-centric workflows, so - ;; these are just clutter (the scrollbar also impacts Emacs' performance). - (push '(menu-bar-lines . 0) default-frame-alist) - (push '(tool-bar-lines . 0) default-frame-alist) - (push '(vertical-scroll-bars) default-frame-alist)) +(unless (assq 'menu-bar-lines default-frame-alist) + ;; We do this in early-init.el too, but in case the user is on Emacs 26 we do + ;; it here too: disable tool and scrollbars, as Doom encourages + ;; keyboard-centric workflows, so these are just clutter (the scrollbar also + ;; impacts performance). + (add-to-list 'default-frame-alist '(menu-bar-lines . 0)) + (add-to-list 'default-frame-alist '(tool-bar-lines . 0)) + (add-to-list 'default-frame-alist '(vertical-scroll-bars))) -;; Sets `ns-appearance' and `ns-transparent-titlebar' on GUI frames (and fixes -;; mismatching text color in the frame title) (when IS-MAC ;; Curse Lion and its sudden but inevitable fullscreen mode! ;; NOTE Meaningless to railwaycat's emacs-mac build - (setq ns-use-native-fullscreen nil - ;; Visit files opened outside of Emacs in existing frame, rather than a - ;; new one - ns-pop-up-frames nil) + (setq ns-use-native-fullscreen nil) - ;; Sets ns-transparent-titlebar and ns-appearance frame parameters as is - ;; appropriate for the loaded theme. + ;; Visit files opened outside of Emacs in existing frame, not a new one + (setq ns-pop-up-frames nil) + + ;; Sets `ns-transparent-titlebar' and `ns-appearance' frame parameters so + ;; window borders will match the enabled theme. (and (or (daemonp) (display-graphic-p)) (require 'ns-auto-titlebar nil t) (ns-auto-titlebar-mode +1)) - (add-hook! 'after-make-frame-functions - (defun doom-init-menu-bar-in-gui-frames-h (frame) - "On MacOS, the menu bar isn't part of the frame. Disabling it makes MacOS -treat Emacs as a non-application window." - (when (display-graphic-p frame) - (set-frame-parameter frame 'menu-bar-lines 1))))) + ;; HACK On MacOS, disabling the menu bar makes MacOS treat Emacs as a + ;; non-application window -- which means it doesn't automatically capture + ;; focus when it is started, among other things. We enable menu-bar-lines + ;; there, but we still want it disabled in terminal frames because there + ;; it activates an ugly menu bar. + (add-hook! '(window-setup-hook after-make-frame-functions) + (defun doom-init-menu-bar-in-gui-frames-h (&optional frame) + "Re-enable menu-bar-lines in GUI frames." + (when-let (frame (or frame (selected-frame))) + (when (display-graphic-p frame) + (set-frame-parameter frame 'menu-bar-lines 1)))))) ;; The native border "consumes" a pixel of the fringe on righter-most splits, ;; `window-divider' does not. Available since Emacs 25.1. @@ -317,13 +330,15 @@ treat Emacs as a non-application window." ;; always avoid GUI (setq use-dialog-box nil) -;; Don't display floating tooltips; display their contents in the echo-area. -(if (bound-and-true-p tooltip-mode) (tooltip-mode -1)) -;; native linux tooltips are ugly +;; Don't display floating tooltips; display their contents in the echo-area, +;; because native tooltips are ugly. +(when (bound-and-true-p tooltip-mode) + (tooltip-mode -1)) +;; ...especially on linux (when IS-LINUX (setq x-gtk-use-system-tooltips nil)) - ;; Favor vertical splits over horizontal ones + ;; Favor vertical splits over horizontal ones. Screens are usually wide. (setq split-width-threshold 160 split-height-threshold nil) @@ -332,7 +347,7 @@ treat Emacs as a non-application window." ;;; Minibuffer ;; Allow for minibuffer-ception. Sometimes we need another minibuffer command -;; _while_ we're in the minibuffer. +;; while we're in the minibuffer. (setq enable-recursive-minibuffers t) ;; Show current key-sequence in minibuffer, like vim does. Any feedback after @@ -345,11 +360,6 @@ treat Emacs as a non-application window." ;; But don't let the minibuffer grow beyond this size max-mini-window-height 0.15) -;; Disable help mouse-overs for mode-line segments (i.e. :help-echo text). -;; They're generally unhelpful and only add confusing visual clutter. -(setq mode-line-default-help-echo nil - show-help-function nil) - ;; Typing yes/no is obnoxious when y/n will do (fset #'yes-or-no-p #'y-or-n-p) @@ -406,19 +416,20 @@ treat Emacs as a non-application window." (setq hl-line-sticky-flag nil global-hl-line-sticky-flag nil) - ;; Disable `hl-line' in evil-visual mode (temporarily). `hl-line' can make the - ;; selection region harder to see while in evil visual mode. - (after! evil - (defvar doom-buffer-hl-line-mode nil) - (add-hook! 'evil-visual-state-entry-hook - (defun doom-disable-hl-line-h () - (when hl-line-mode - (setq-local doom-buffer-hl-line-mode t) - (hl-line-mode -1)))) - (add-hook! 'evil-visual-state-exit-hook - (defun doom-enable-hl-line-maybe-h () - (when doom-buffer-hl-line-mode - (hl-line-mode +1)))))) + ;; Temporarily disable `hl-line' when selection is active, since it doesn't + ;; serve much purpose when the selection is so much more visible. + (defvar doom-buffer-hl-line-mode nil) + + (add-hook! '(evil-visual-state-entry-hook activate-mark-hook) + (defun doom-disable-hl-line-h () + (when hl-line-mode + (setq-local doom-buffer-hl-line-mode t) + (hl-line-mode -1)))) + + (add-hook! '(evil-visual-state-exit-hook deactivate-mark-hook) + (defun doom-enable-hl-line-maybe-h () + (when doom-buffer-hl-line-mode + (hl-line-mode +1))))) (use-package! winner @@ -464,15 +475,23 @@ treat Emacs as a non-application window." all-the-icons-wicon all-the-icons-material all-the-icons-alltheicon) - :init - (defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args) - "Return a blank string in tty Emacs, which doesn't support multiple fonts." - :around '(all-the-icons-octicon all-the-icons-material - all-the-icons-faicon all-the-icons-fileicon - all-the-icons-wicon all-the-icons-alltheicon) - (if (display-multi-font-p) - (apply orig-fn args) - ""))) + :config + (cond ((daemonp) + (defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args) + "Return a blank string in tty Emacs, which doesn't support multiple fonts." + :around '(all-the-icons-octicon all-the-icons-material + all-the-icons-faicon all-the-icons-fileicon + all-the-icons-wicon all-the-icons-alltheicon) + (if (or (not after-init-time) (display-multi-font-p)) + (apply orig-fn args) + ""))) + ((not (display-graphic-p)) + (defadvice! doom--disable-all-the-icons-in-tty-a (&rest _) + "Return a blank string for tty users." + :override '(all-the-icons-octicon all-the-icons-material + all-the-icons-faicon all-the-icons-fileicon + all-the-icons-wicon all-the-icons-alltheicon) + "")))) ;;;###package hide-mode-line-mode (add-hook! '(completion-list-mode-hook Man-mode-hook) @@ -499,15 +518,19 @@ treat Emacs as a non-application window." ;; ;;; Line numbers +;; Explicitly define a width to reduce computation (setq-default display-line-numbers-width 3) -;; line numbers in most modes +;; Show absolute line numbers for narrowed regions makes it easier to tell the +;; buffer is narrowed, and where you are, exactly. +(setq-default display-line-numbers-widen t) + +;; Enable line numbers in most text-editing modes. We avoid +;; `global-display-line-numbers-mode' because there are many special and +;; temporary modes where we don't need/want them. (add-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) #'display-line-numbers-mode) -(defun doom-enable-line-numbers-h () (display-line-numbers-mode +1)) -(defun doom-disable-line-numbers-h () (display-line-numbers-mode -1)) - ;; ;;; Theme & font @@ -521,22 +544,30 @@ behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.") (defun doom-init-fonts-h () "Loads `doom-font'." - (cond (doom-font - (cl-pushnew - (cons 'font - (cond ((stringp doom-font) doom-font) - ((fontp doom-font) (font-xlfd-name doom-font)) - ((signal 'wrong-type-argument (list '(fontp stringp) - doom-font))))) - default-frame-alist - :key #'car :test #'eq)) - ((display-graphic-p) - (setq doom-font (face-attribute 'default :font))))) + (cond + (doom-font + (cl-pushnew + ;; Avoiding `set-frame-font' because it does a lot of extra, expensive + ;; work we can avoid by setting the font frame parameter instead. + (cons 'font + (cond ((stringp doom-font) doom-font) + ((fontp doom-font) (font-xlfd-name doom-font)) + ((signal 'wrong-type-argument (list '(fontp stringp) + doom-font))))) + default-frame-alist + :key #'car :test #'eq)) + ((display-graphic-p) + ;; We try our best to record your system font, so `doom-big-font-mode' + ;; can still use it to compute a larger font size with. + (setq font-use-system-font t + doom-font (face-attribute 'default :font))))) (defun doom-init-extra-fonts-h (&optional frame) "Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'." (condition-case e (with-selected-frame (or frame (selected-frame)) + (when doom-font + (set-face-attribute 'fixed-pitch nil :font doom-font)) (when doom-serif-font (set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font)) (when doom-variable-pitch-font @@ -616,7 +647,21 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." ;; ;;; Fixes/hacks -;; doesn't exist in terminal Emacs; we define it to prevent errors +;; Doom doesn't support `customize' and it never will. It's a clumsy interface +;; that sets variables at a time where it can be easily and unpredictably +;; overwritten. Configure things from your $DOOMDIR instead. +(dolist (sym '(customize-option customize-browse customize-group customize-face + customize-rogue customize-saved customize-apropos + customize-changed customize-unsaved customize-variable + customize-set-value customize-customized customize-set-variable + customize-apropos-faces customize-save-variable + customize-apropos-groups customize-apropos-options + customize-changed-options customize-save-customized)) + (put sym 'disabled "Doom doesn't support `customize', configure Emacs from $DOOMDIR/config.el instead")) +(put 'customize-themes 'disabled "Set `doom-theme' or use `load-theme' in $DOOMDIR/config.el instead") + +;; Doesn't exist in terminal Emacs, so we define it to prevent void-function +;; errors emitted from packages use it without checking for it first. (unless (fboundp 'define-fringe-bitmap) (fset 'define-fringe-bitmap #'ignore)) diff --git a/core/core.el b/core/core.el index 6bc56a172..30c666ae2 100644 --- a/core/core.el +++ b/core/core.el @@ -23,13 +23,18 @@ ;; This is consulted on every `require', `load' and various path/io functions. ;; You get a minor speed up by nooping this. -(setq file-name-handler-alist nil) +(unless noninteractive + (setq file-name-handler-alist nil)) + +;; Restore `file-name-handler-alist', because it is needed for handling +;; encrypted or compressed files, among other things. +(defun doom-reset-file-handler-alist-h () + (setq file-name-handler-alist doom--initial-file-name-handler-alist)) +(add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h) ;; Load the bare necessities (require 'core-lib) -(autoload 'doom-initialize-packages "core-packages") - ;; ;;; Global variables @@ -49,10 +54,6 @@ DEBUG envvar will enable this at startup.") (defvar doom-interactive-mode (not noninteractive) "If non-nil, Emacs is in interactive mode.") -(defvar doom-gc-cons-threshold 16777216 ; 16mb - "The default value to use for `gc-cons-threshold'. If you experience freezing, -decrease this. If you experience stuttering, increase this.") - ;;; Directories/files (defconst doom-emacs-dir (eval-when-compile (file-truename user-emacs-directory)) @@ -115,7 +116,7 @@ This file is compiled from the autoloads files of all installed packages combined.") (defconst doom-env-file (concat doom-local-dir "env") - "The location of your envvar file, generated by `doom env refresh`. + "The location of your envvar file, generated by `doom env`. This file contains environment variables scraped from your shell environment, which is loaded at startup (if it exists). This is helpful if Emacs can't @@ -134,6 +135,9 @@ users).") ;; ;;; Emacs core configuration +;; lo', longer logs ahoy, so we may reliably locate lapses in doom's logic +(setq message-log-max 8192) + ;; Reduce debug output, well, unless we've asked for it. (setq debug-on-error doom-debug-mode jka-compr-verbose doom-debug-mode) @@ -194,11 +198,12 @@ users).") (when IS-WINDOWS (setq abbreviated-home-dir "\\`'")) -;; Don't litter `doom-emacs-dir' +;; Don't litter `doom-emacs-dir'. We don't use `no-littering' because it's a +;; mote too opinionated for our needs. (setq abbrev-file-name (concat doom-local-dir "abbrev.el") async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") bookmark-default-file (concat doom-etc-dir "bookmarks") - custom-file (concat doom-private-dir "init.el") + custom-file (concat doom-local-dir "custom.el") custom-theme-directory (concat doom-private-dir "themes/") desktop-dirname (concat doom-etc-dir "desktop") desktop-base-file-name "autosave" @@ -210,7 +215,6 @@ users).") tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/") tramp-backup-directory-alist backup-directory-alist tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el") - tramp-histfile-override (concat doom-cache-dir "tramp-histfile.el") url-cache-directory (concat doom-cache-dir "url/") url-configuration-directory (concat doom-etc-dir "url/") gamegrid-user-score-file-directory (concat doom-etc-dir "games/")) @@ -227,7 +231,8 @@ users).") ;; Disable bidirectional text rendering for a modest performance boost. Of ;; course, this renders Emacs unable to detect/display right-to-left languages ;; (sorry!), but for us left-to-right language speakers/writers, it's a boon. -(setq-default bidi-display-reordering 'left-to-right) +(setq-default bidi-display-reordering 'left-to-right + bidi-paragraph-direction 'left-to-right) ;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions ;; in non-focused windows. @@ -246,8 +251,8 @@ users).") ;; Don't ping things that look like domain names. (setq ffap-machine-p-known 'reject) -;; Performance on Windows is considerably worse than elsewhere. We'll need -;; everything we can get. +;; Performance on Windows is considerably worse than elsewhere, especially if +;; WSL is involved. We'll need everything we can get. (when IS-WINDOWS ;; Reduce the workload when doing file IO (setq w32-get-true-file-attributes nil) @@ -257,39 +262,34 @@ users).") ;; been determined. (setq inhibit-compacting-font-caches t)) -;; Remove command line options that aren't relevant to our current OS; that -;; means less to process at startup. +;; Remove command line options that aren't relevant to our current OS; means +;; slightly less to process at startup. (unless IS-MAC (setq command-line-ns-option-alist nil)) (unless IS-LINUX (setq command-line-x-option-alist nil)) -;; Restore `file-name-handler-alist' because it is necessary for handling -;; encrypted or compressed files, among other things. -(defun doom-restore-file-name-handler-alist-h () - (setq file-name-handler-alist doom--initial-file-name-handler-alist)) -(add-hook 'emacs-startup-hook #'doom-restore-file-name-handler-alist-h) +;; Delete files to trash on macOS, as an extra layer of precaution against +;; accidentally deleting wanted files. +(setq delete-by-moving-to-trash IS-MAC) -;; To speed up minibuffer commands (like helm and ivy), we defer garbage -;; collection while the minibuffer is active. -(defun doom-defer-garbage-collection-h () - "Increase `gc-cons-threshold' to stave off garbage collection." - (setq gc-cons-threshold most-positive-fixnum)) +;; Adopt a sneaky garbage collection strategy of waiting until idle time to +;; collect; staving off the collector while the user is working. +(when doom-interactive-mode + (add-transient-hook! 'pre-command-hook (gcmh-mode +1)) + (with-eval-after-load 'gcmh + (setq gcmh-idle-delay 10 + gcmh-verbose doom-debug-mode + gcmh-high-cons-threshold 16777216) ; 16mb + (add-hook 'focus-out-hook #'gcmh-idle-garbage-collect))) -(defun doom-restore-garbage-collection-h () - "Restore `gc-cons-threshold' to a reasonable value so the GC can do its job." - ;; Defer it so that commands launched immediately after will enjoy the - ;; benefits. - (run-at-time - 1 nil (lambda () (setq gc-cons-threshold doom-gc-cons-threshold)))) - -(add-hook 'minibuffer-setup-hook #'doom-defer-garbage-collection-h) -(add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h) - -;; Not restoring these to their defaults will cause stuttering/freezes. -(add-hook 'emacs-startup-hook #'doom-restore-garbage-collection-h) - -;; When Emacs loses focus seems like a great time to do some garbage collection -;; all sneaky breeky like, so we can return to a fresh(er) Emacs. -(add-hook 'focus-out-hook #'garbage-collect) +;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some +;; reason. Disabling it completely could have many side-effects, so we +;; defer it until later. +(unless (display-graphic-p) + (advice-add #'tty-run-terminal-initialization :override #'ignore) + (add-hook! 'window-setup-hook + (defun doom-init-tty-h () + (advice-remove #'tty-run-terminal-initialization #'ignore) + (tty-run-terminal-initialization (selected-frame) nil t)))) ;; @@ -396,9 +396,7 @@ If this is a daemon session, load them all immediately instead." ;;; Bootstrap helpers (defun doom-try-run-hook (hook) - "Run HOOK (a hook function), but handle errors better, to make debugging -issues easier. - + "Run HOOK (a hook function) with better error handling. Meant to be used with `run-hook-wrapped'." (doom-log "Running doom hook: %s" hook) (condition-case e @@ -421,27 +419,29 @@ If RETURN-P, return the message as a string instead of displaying it." (setq doom-init-time (float-time (time-subtract (current-time) before-init-time)))))) -(defun doom-load-autoloads-file (file) - "Tries to load FILE (an autoloads file). Return t on success, throws an error -in interactive sessions, nil otherwise (but logs a warning)." +(defun doom-load-autoloads-file (file &optional noerror) + "Tries to load FILE (an autoloads file). +Return t on success, nil otherwise (but logs a warning)." (condition-case e - (let (command-switch-alist) - (load (substring file 0 -3) 'noerror 'nomessage)) + (load (substring file 0 -3) noerror 'nomessage) ((debug error) (message "Autoload file error: %s -> %s" (file-name-nondirectory file) e) nil))) (defun doom-load-envvars-file (file &optional noerror) - "Read and set envvars from FILE." + "Read and set envvars from FILE. +If NOERROR is non-nil, don't throw an error if the file doesn't exist or is +unreadable. Returns the names of envvars that were changed." (if (not (file-readable-p file)) (unless noerror (signal 'file-error (list "Couldn't read envvar file" file))) - (let (environment) + (let (envvars environment) (with-temp-buffer (save-excursion (insert "\n") (insert-file-contents file)) (while (re-search-forward "\n *\\([^#= \n]*\\)=" nil t) + (push (match-string 1) envvars) (push (buffer-substring (match-beginning 1) (1- (or (save-excursion @@ -450,24 +450,26 @@ in interactive sessions, nil otherwise (but logs a warning)." (point-max)))) environment))) (when environment - (setq-default - process-environment (nreverse environment) - exec-path (append (parse-colon-path (getenv "PATH")) - (list exec-directory)) - shell-file-name (or (getenv "SHELL") - shell-file-name)) - process-environment)))) + (setq process-environment + (append (nreverse environment) process-environment) + exec-path + (if (member "PATH" envvars) + (append (split-string (getenv "PATH") path-separator t) + (list exec-directory)) + exec-path) + shell-file-name + (if (member "SHELL" envvars) + (or (getenv "SHELL") shell-file-name) + shell-file-name)) + envvars)))) -(defun doom-initialize (&optional force-p) +(defun doom-initialize (&optional force-p noerror) "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). -The bootstrap process involves making sure 1) the essential directories exist, -2) the core packages are installed, 3) `doom-autoload-file' and -`doom-package-autoload-file' exist and have been loaded, and 4) Doom's core -files are loaded. - -If the cache exists, much of this function isn't run, which substantially -reduces startup time. +The bootstrap process ensures that the essential directories exist, all core +packages are installed, `doom-autoload-file' and `doom-package-autoload-file' +exist and are loaded, and that `core-packages' is auto-loaded when `package' or +`straight' are. The overall load order of Doom is as follows: @@ -496,7 +498,9 @@ to least)." load-path doom--initial-load-path process-environment doom--initial-process-environment) - ;; Load shell environment, optionally generated from 'doom env' + ;; Load shell environment, optionally generated from 'doom env'. No need to + ;; do so if we're in terminal Emacs, because Emacs will correctly inherit + ;; your shell environment there. (when (and (or (display-graphic-p) (daemonp)) (file-exists-p doom-env-file)) @@ -506,12 +510,12 @@ to least)." (let (;; `doom-autoload-file' tells Emacs where to load all its functions ;; from. This includes everything in core/autoload/*.el and autoload ;; files in enabled modules. - (core-autoloads-p (doom-load-autoloads-file doom-autoload-file)) + (core-autoloads-p (doom-load-autoloads-file doom-autoload-file noerror)) ;; Loads `doom-package-autoload-file', which loads a concatenated ;; package autoloads file which caches `load-path', `auto-mode-alist', ;; `Info-directory-list', and `doom-disabled-packages'. A big ;; reduction in startup time. - (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file))) + (pkg-autoloads-p (doom-load-autoloads-file doom-package-autoload-file noerror))) (if (and core-autoloads-p pkg-autoloads-p (not force-p)) ;; In case we want to use package.el or straight via M-x @@ -522,12 +526,17 @@ to least)." (require 'core-packages) (doom-initialize-packages))) + ;; Eagerly load these libraries because we may be in a session that hasn't been + ;; fully initialized (e.g. where autoloads files haven't been generated or + ;; `load-path' populated). + (mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix) + (file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) + ;; Create all our core directories to quell file errors - (dolist (dir (list doom-local-dir - doom-etc-dir - doom-cache-dir)) - (unless (file-directory-p dir) - (make-directory dir 'parents))) + (mapc (doom-rpartial #'make-directory 'parents) + (list doom-local-dir + doom-etc-dir + doom-cache-dir)) ;; Ensure the package management system (and straight) are ready for ;; action (and all core packages/repos are installed) @@ -535,13 +544,16 @@ to least)." (doom-initialize-packages force-p)) (unless (or (and core-autoloads-p pkg-autoloads-p) - force-p - (not doom-interactive-mode)) + noerror) (unless core-autoloads-p (warn "Your Doom core autoloads file is missing")) (unless pkg-autoloads-p (warn "Your package autoloads file is missing")) - (signal 'doom-autoload-error (list "Run `bin/doom refresh' to generate them")))) + (signal 'doom-autoload-error (list "Run `bin/doom refresh' to generate them"))) + + (when doom-interactive-mode + (add-hook 'window-setup-hook #'doom-display-benchmark-h 'append) + (add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler)))) t)) (defun doom-initialize-core () diff --git a/core/packages.el b/core/packages.el index ff2ee9115..4dc671942 100644 --- a/core/packages.el +++ b/core/packages.el @@ -2,39 +2,46 @@ ;;; core/packages.el ;; core.el -(package! dotenv-mode) -(package! auto-minor-mode) +(package! auto-minor-mode :pin "17cfa1b548") +(package! gcmh :pin "8867533a73") ;; core-ui.el -(package! all-the-icons) -(package! hide-mode-line) -(package! highlight-numbers) -(package! rainbow-delimiters) -(package! restart-emacs) +(package! all-the-icons :pin "1416f37984") +(package! hide-mode-line :pin "88888825b5") +(package! highlight-numbers :pin "8b4744c7f4") +(package! rainbow-delimiters :pin "5125f4e476") +(package! restart-emacs :pin "9aa90d3df9") ;; core-editor.el -(package! better-jumper) -(package! dtrt-indent) -(package! helpful) -(package! ns-auto-titlebar :ignore (not IS-MAC)) -(package! pcre2el) -(package! smartparens) +(package! better-jumper :pin "6d240032ca") +(package! dtrt-indent :pin "48221c928b") +(package! helpful :pin "c54e9ddbd6") +(when IS-MAC + (package! ns-auto-titlebar :pin "1efc30d385")) +(package! pcre2el :pin "0b5b2a2c17") +(package! smartparens :pin "be8d5c9a63") (package! so-long - :built-in 'prefer + :built-in 'prefer ; included in Emacs 27+ ;; REVIEW so-long is slated to be published to ELPA eventually, but until then - ;; I've created my own mirror for it because git.savannah.gnu.org runs on a - ;; potato. - :recipe (:host github :repo "hlissner/emacs-so-long")) -(package! undo-tree - ;; Version 0.6.5 is on ELPA which lacks a fix we need, so we install 0.6.6 - ;; from emacsmirror/undo-tree instead. - :recipe (:host github :repo "emacsmirror/undo-tree")) -(package! ws-butler) -(package! xclip) + ;; I've created my own mirror for it because git.savannah.gnu.org runs + ;; on a potato. + :recipe (:host github :repo "hlissner/emacs-so-long") + :pin "ed666b0716") +(package! undo-tree :pin "5b6df03781") +(package! ws-butler + ;; Use my fork of ws-butler, which has a few choice improvements and + ;; optimizations (the original has been abandoned). + :recipe (:host github :repo "hlissner/ws-butler") + :pin "e4430d3778") +(unless IS-WINDOWS + (package! xclip :pin "d022cf947d")) ;; core-projects.el -(package! projectile) +(package! projectile :pin "341150c0e7") ;; core-keybinds.el -(package! general) -(package! which-key) +(package! general :pin "f6e928622d") +(package! which-key :pin "7b068f3e95") + +;; autoload/cache.el +(package! persistent-soft :pin "a1e0ddf2a1") diff --git a/core/templates/QUICKSTART_INTRO b/core/templates/QUICKSTART_INTRO index 8a410349e..f7d101ac7 100644 --- a/core/templates/QUICKSTART_INTRO +++ b/core/templates/QUICKSTART_INTRO @@ -15,7 +15,7 @@ Before you doom yourself, there are a few things you should know: give you clues about what is wrong. 3. Use `bin/doom upgrade` to update Doom. Doing it any other way may require - additional work. When in doubt, run `bin/doom refresh`. + additional work. When in doubt, run `bin/doom sync`. 4. Check out `bin/doom help` to see what else `bin/doom` can do (and it is recommended you add ~/.emacs.d/bin to your PATH). diff --git a/core/templates/config.example.el b/core/templates/config.example.el new file mode 100644 index 000000000..2f476f919 --- /dev/null +++ b/core/templates/config.example.el @@ -0,0 +1,53 @@ +;;; $DOOMDIR/config.el -*- lexical-binding: t; -*- + +;; Place your private configuration here! Remember, you do not need to run 'doom +;; sync' after modifying this file! + + +;; Some functionality uses this to identify you, e.g. GPG configuration, email +;; clients, file templates and snippets. +(setq user-full-name "John Doe" + user-mail-address "john@doe.com") + +;; Doom exposes five (optional) variables for controlling fonts in Doom. Here +;; are the three important ones: +;; +;; + `doom-font' +;; + `doom-variable-pitch-font' +;; + `doom-big-font' -- used for `doom-big-font-mode'; use this for +;; presentations or streaming. +;; +;; They all accept either a font-spec, font string ("Input Mono-12"), or xlfd +;; font string. You generally only need these two: +(setq doom-font (font-spec :family "monospace" :size 14)) + +;; There are two ways to load a theme. Both assume the theme is installed and +;; available. You can either set `doom-theme' or manually load a theme with the +;; `load-theme' function. This is the default: +(setq doom-theme 'doom-one) + +;; If you use `org' and don't want your org files in the default location below, +;; change `org-directory'. It must be set before org loads! +(setq org-directory "~/org/") + +;; This determines the style of line numbers in effect. If set to `nil', line +;; numbers are disabled. For relative line numbers, set this to `relative'. +(setq display-line-numbers-type t) + + +;; Here are some additional functions/macros that could help you configure Doom: +;; +;; - `load!' for loading external *.el files relative to this one +;; - `use-package' for configuring packages +;; - `after!' for running code after a package has loaded +;; - `add-load-path!' for adding directories to the `load-path', relative to +;; this file. Emacs searches the `load-path' when you load packages with +;; `require' or `use-package'. +;; - `map!' for binding new keys +;; +;; To get information about any of these functions/macros, move the cursor over +;; the highlighted symbol at press 'K' (non-evil users must press 'C-c g k'). +;; This will open documentation for it, including demos of how they are used. +;; +;; You can also try 'gd' (or 'C-c g d') to jump to their definition and see how +;; they are implemented. diff --git a/core/templates/packages.example.el b/core/templates/packages.example.el new file mode 100644 index 000000000..2afbc7e28 --- /dev/null +++ b/core/templates/packages.example.el @@ -0,0 +1,51 @@ +;; -*- no-byte-compile: t; -*- +;;; $DOOMDIR/packages.el + +;; To install a package with Doom you must declare them here, run 'doom sync' on +;; the command line, then restart Emacs for the changes to take effect. +;; Alternatively, use M-x doom/reload. +;; +;; WARNING: Disabling core packages listed in ~/.emacs.d/core/packages.el may +;; have nasty side-effects and is not recommended. + + +;; All of Doom's packages are pinned to a specific commit, and updated from +;; release to release. To un-pin all packages and live on the edge, do: +;(unpin! t) + +;; ...but to unpin a single package: +;(unpin! pinned-package) +;; Use it to unpin multiple packages +;(unpin! pinned-package another-pinned-package) + + +;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror: +;(package! some-package) + +;; To install a package directly from a particular repo, you'll need to specify +;; a `:recipe'. You'll find documentation on what `:recipe' accepts here: +;; https://github.com/raxod502/straight.el#the-recipe-format +;(package! another-package +; :recipe (:host github :repo "username/repo")) + +;; If the package you are trying to install does not contain a PACKAGENAME.el +;; file, or is located in a subdirectory of the repo, you'll need to specify +;; `:files' in the `:recipe': +;(package! this-package +; :recipe (:host github :repo "username/repo" +; :files ("some-file.el" "src/lisp/*.el"))) + +;; If you'd like to disable a package included with Doom, for whatever reason, +;; you can do so here with the `:disable' property: +;(package! builtin-package :disable t) + +;; You can override the recipe of a built in package without having to specify +;; all the properties for `:recipe'. These will inherit the rest of its recipe +;; from Doom or MELPA/ELPA/Emacsmirror: +;(package! builtin-package :recipe (:nonrecursive t)) +;(package! builtin-package-2 :recipe (:repo "myfork/package")) + +;; Specify a `:branch' to install a package from a particular branch or tag. +;; This is required for some packages whose default branch isn't 'master' (which +;; our package manager can't deal with; see raxod502/straight.el#279) +;(package! builtin-package :recipe (:branch "develop")) diff --git a/core/test/helpers.el b/core/test/helpers.el index 618087fb4..446ec12d6 100644 --- a/core/test/helpers.el +++ b/core/test/helpers.el @@ -3,7 +3,7 @@ (eval-and-compile (setq doom-interactive-mode 'test) - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (require 'buttercup) (setq split-width-threshold 0 split-height-threshold 0 diff --git a/core/test/test-core-lib.el b/core/test/test-core-lib.el index 5391070d1..ea3bacb4b 100644 --- a/core/test/test-core-lib.el +++ b/core/test/test-core-lib.el @@ -101,12 +101,6 @@ (expect (appendq! list '(d e f)) :to-equal '(a b c d e f))))) - (describe "nconcq!" - (it "nconc's a list to a list symbol" - (let ((list '(a b c))) - (expect (nconcq! list '(d e f)) - :to-equal '(a b c d e f))))) - (describe "delq!" (it "delete's a symbol from a list" (let ((list '(a b c))) diff --git a/core/test/test-core.el b/core/test/test-core.el index 0207390b4..8a0844cd5 100644 --- a/core/test/test-core.el +++ b/core/test/test-core.el @@ -13,15 +13,15 @@ (setq doom-init-p nil)) (it "initializes once" - (expect (doom-initialize)) - (expect (not (doom-initialize))) - (expect (not (doom-initialize))) + (expect (doom-initialize nil 'noerror)) + (expect (not (doom-initialize nil 'noerror))) + (expect (not (doom-initialize nil 'noerror))) (expect doom-init-p)) (it "initializes multiple times, if forced" - (expect (doom-initialize)) - (expect (not (doom-initialize))) - (expect (doom-initialize 'force))) + (expect (doom-initialize nil 'noerror)) + (expect (not (doom-initialize nil 'noerror))) + (expect (doom-initialize 'force 'noerror))) (describe "package initialization" (before-each @@ -29,18 +29,18 @@ (it "initializes packages if core autoload file doesn't exist" (let ((doom-autoload-file "doesnotexist")) - (doom-initialize)) + (expect (doom-initialize nil 'noerror)) (expect 'doom-initialize-packages :to-have-been-called)) (it "doesn't initialize packages if core autoload file was loaded" (let ((doom-interactive-mode t)) (spy-on 'doom-load-autoloads-file :and-return-value t) - (doom-initialize) + (doom-initialize nil 'noerror) (expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file) (expect 'doom-initialize-packages :to-have-been-called))) (it "initializes packages when forced" - (doom-initialize 'force) + (doom-initialize 'force 'noerror) (expect 'doom-initialize-packages :to-have-been-called))) (describe "autoloads files" @@ -49,15 +49,14 @@ (spy-on 'warn :and-return-value t)) (it "loads autoloads files" - (ignore-errors (doom-initialize)) + (ignore-errors (doom-initialize nil 'noerror)) (expect 'doom-load-autoloads-file :to-have-been-called-with doom-autoload-file) (expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file)) - (it "throws doom-autoload-error in interactive session where autoload files don't exist" - (let ((doom-interactive-mode t) - (doom-autoload-file "doesnotexist") + (it "throws doom-autoload-error when autoload files don't exist" + (let ((doom-autoload-file "doesnotexist") (doom-package-autoload-file "doesnotexist")) (expect (doom-initialize) :to-throw 'doom-autoload-error))))) @@ -117,7 +116,7 @@ (it "returns the new value for `process-environment'" (expect (doom-load-envvars-file doom-env-file) - :to-equal '("A=1" "B=2" "C=3"))) + :to-have-same-items-as '("A" "B" "C"))) (it "alters environment variables" (dolist (key '("A" "B" "C")) diff --git a/docs/api.org b/docs/api.org index 578418981..e4702ffc5 100644 --- a/docs/api.org +++ b/docs/api.org @@ -5,11 +5,12 @@ This appendix serves as a reference on how to use Doom Emacs' standard library. It is integrated into Helpful, in Doom. * Table of Contents :TOC_3: -- [[#examples-for-dooms-core-library][Examples for Doom's core library]] +- [[#examples-for-dooms-library][Examples for Doom's library]] - [[#core-lib][core-lib]] - [[#add-hook][add-hook!]] - [[#add-transient-hook][add-transient-hook!]] - [[#after][after!]] + - [[#appendq][appendq!]] - [[#custom-set-faces][custom-set-faces!]] - [[#custom-theme-set-faces][custom-theme-set-faces!]] - [[#defer-feature][defer-feature!]] @@ -19,21 +20,24 @@ It is integrated into Helpful, in Doom. - [[#file-exists-p][file-exists-p!]] - [[#lambda][lambda!]] - [[#lambda-1][lambda!!]] + - [[#letenv][letenv!]] - [[#load][load!]] - [[#map][map!]] - [[#package][package!]] - [[#pushnew][pushnew!]] + - [[#prependq][prependq!]] - [[#quiet][quiet!]] - [[#remove-hook][remove-hook!]] + - [[#setq][setq!]] - [[#setq-hook][setq-hook!]] - [[#unsetq-hook][unsetq-hook!]] - [[#use-package][use-package!]] - [[#interesting-snippets][Interesting snippets]] - - [[#persist-emacs-initial-frame-size-across-sessions][Persist Emacs' initial frame size across sessions]] + - [[#center-emacs-initial-frame-with-a-fixed-size][Center Emacs' initial frame with a fixed size]] - [[#persist-emacs-initial-frame-position-dimensions-andor-full-screen-state-across-sessions][Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions]] - [[#update-cursor-shape-under-terminal-emacs][Update cursor shape under terminal Emacs]] -* Examples for Doom's core library +* Examples for Doom's library ** core-lib *** add-hook! #+BEGIN_SRC elisp :eval no @@ -90,6 +94,26 @@ It is integrated into Helpful, in Doom. (after! rustic ...) (after! python ...) #+END_SRC +*** appendq! +#+BEGIN_SRC elisp +(let ((x '(a b c))) + (appendq! x '(c d e)) + x) +#+END_SRC + +#+RESULTS: +: (a b c c d e) + +#+BEGIN_SRC elisp +(let ((x '(a b c)) + (y '(c d e)) + (z '(f g))) + (appendq! x y z '(h)) + x) +#+END_SRC + +#+RESULTS: +: (a b c c d e f g h) *** custom-set-faces! #+BEGIN_SRC elisp :eval no @@ -214,8 +238,47 @@ It is integrated into Helpful, in Doom. #+RESULTS: : /home/hlissner/.emacs.d/LICENSE -*** TODO lambda! -*** TODO lambda!! +*** lambda! +#+BEGIN_SRC elisp :eval no +(map! "C-j" (lambda! (newline) (indent-according-to-mode))) + +;; The `λ!' short-form alias exists. If you have the snippets module enabled and +;; Doom's default snippets, the 'lam' snippet will expand into 'λ!'. Otherwise, +;; you can use `lambda!'. +(map! "C-j" (λ! (newline) (indent-according-to-mode))) +#+END_SRC +*** lambda!! +When ~newline~ is passed a numerical prefix argument (=C-u 5 M-x newline=), it +inserts N newlines. We can use ~lambda!!~ to easily create a keybinds that bakes +in the prefix arg into the command call: + +#+BEGIN_SRC elisp :eval no +(map! "C-j" (lambda!! #'newline 5)) + +;; The `λ!!' short-form alias exists. If you have the snippets module enabled +;; and Doom's default snippets, a 'lam' snippet is available to expand into +;; 'λ!'. Otherwise, you can use `lambda!!'. +(map! "C-j" (λ!! #'newline 5)) +#+END_SRC + +Or to create aliases for functions that behave differently: + +#+BEGIN_SRC elisp :eval no +(fset 'insert-5-newlines (lambda!! #'newline 5)) + +;; The equivalent of C-u M-x org-global-cycle, which resets the org document to +;; its startup visibility settings. +(fset 'org-reset-global-visibility (lambda!! #'org-global-cycle '(4)) +#+END_SRC +*** letenv! +#+BEGIN_SRC elisp +(letenv! (("SHELL" "/bin/sh")) + (shell-command-to-string "echo $SHELL")) +#+END_SRC + +#+RESULTS: +: "/bin/sh\n" + *** load! #+BEGIN_SRC elisp :eval no ;;; Lets say we're in ~/.doom.d/config.el @@ -269,9 +332,9 @@ These are side-by-side comparisons, showing how to bind keys with and without (map! :map lua-mode-map "SPC m b" nil) ;; bind multiple keys -(global-set-key "C-x x" #'do-something) -(global-set-key "C-x y" #'do-something-else) -(global-set-key "C-x z" #'do-another-thing) +(global-set-key (kbd "C-x x") #'do-something) +(global-set-key (kbd "C-x y") #'do-something-else) +(global-set-key (kbd "C-x z") #'do-another-thing) (map! "C-x x" #'do-something "C-x y" #'do-something-else "C-x z" #'do-another-thing) @@ -325,7 +388,7 @@ These are side-by-side comparisons, showing how to bind keys with and without *** package! #+BEGIN_SRC elisp :eval no ;; To install a package that can be found on ELPA or any of the sources -;; specified in `doom-core-package-sources': +;; specified in `straight-recipe-repositories': (package! evil) (package! js2-mode) (package! rainbow-delimiters) @@ -342,22 +405,49 @@ These are side-by-side comparisons, showing how to bind keys with and without ;; you can tell the package manager not to clone the repo recursively: (package! ansible :recipe (:nonrecursive t)) -;; To install a particular branch, commit or tag: -(package! evil - ;; if :host and :fetcher aren't specified, the package manager will fall back - ;; to evil's default source provided by their (M)ELPA recipes: - :recipe (:commit "e7bc39de2f961505e8e112da8c1b315ae8afce52")) - +;; To pin a package to a specific commit: +(package! evil :pin "e7bc39de2f9") +;; ...or branch: (package! evil :recipe (:branch "stable")) - -(package! evil :recipe (:tag "1.2.9")) +;; To unpin a pinned package: +(package! evil :pin nil) ;; If you share your config between two computers, and don't want bin/doom ;; refresh to delete packages used only on one system, use :ignore (package! evil :ignore (not (equal system-name "my-desktop"))) #+END_SRC -*** TODO pushnew! +*** pushnew! +#+BEGIN_SRC elisp +(let ((list '(a b c))) + (pushnew! list 'c 'd 'e) + list) +#+END_SRC + +#+RESULTS: +: (e d a b c) + +*** prependq! +#+BEGIN_SRC elisp +(let ((x '(a b c))) + (prependq! x '(c d e)) + x) +#+END_SRC + +#+RESULTS: +: (c d e a b c) + +#+BEGIN_SRC elisp +(let ((x '(a b c)) + (y '(c d e)) + (z '(f g))) + (prependq! x y z '(h)) + x) +#+END_SRC + +#+RESULTS: +: (c d e f g h a b c) + *** quiet! #+BEGIN_SRC elisp :eval no ;; Enters recentf-mode without extra output @@ -381,6 +471,14 @@ These are side-by-side comparisons, showing how to bind keys with and without ;; Removing arbitrary forms (must be exactly the same as the definition) (remove-hook! (one-mode second-mode) (setq v 5) (setq a 2)) #+END_SRC +*** setq! +#+BEGIN_SRC elisp +;; Each of these have a setter associated with them, which must be triggered in +;; order for their new values to have an effect. +(setq! evil-want-Y-yank-to-eol nil + evil-want-C-u-scroll nil + evil-want-C-d-scroll nil) +#+END_SRC *** setq-hook! #+BEGIN_SRC elisp :eval no ;; Set multiple variables after a hook @@ -423,15 +521,15 @@ These are side-by-side comparisons, showing how to bind keys with and without :defer-incrementally t) #+END_SRC * Interesting snippets -** Persist Emacs' initial frame size across sessions +** Center Emacs' initial frame with a fixed size #+BEGIN_SRC elisp (let ((display-height (display-pixel-height)) (display-width (display-pixel-width))) - (add-to-list 'initial-frame-alist - `((left . ,(/ new-frame-width 2)) - (top . ,(/ new-frame-height 2)) - (width . ,(/ display-width 2)) - (height . ,(/ display-height 2))))) + (pushnew! initial-frame-alist + `(left . ,(/ display-width 2)) + `(top . ,(/ display-height 2)) + `(width . ,display-width) + `(height . ,display-height))) #+END_SRC ** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions diff --git a/docs/contributing.org b/docs/contributing.org index 46feb468f..fe3525faa 100644 --- a/docs/contributing.org +++ b/docs/contributing.org @@ -1,14 +1,14 @@ #+TITLE: Contributing #+STARTUP: nofold -I can't say Doom Emacs is a one man show anymore. It wouldn't have gotten this -far without the help of folks like you! Still, I struggle to maintain it all; -especially the modules I do not use. If Doom has been useful to you, consider -pitching in and helping me out. +Doom Emacs is an active and ongoing project, maintained mostly by a single +person, but includes the efforts of 200 contributors and growing. There is no +shortage of things that need doing; bugs that need stomping, features that need +implementing, and documentation that needs documenting. If Doom's been useful to +you, convert some caffiene into code; it'd be a huge help! -Help can range from reporting bugs, proposing enhancements, submitting code and -documentation, or just spreading the good word. To ensure no toes get stepped on -or wires crossed, this guide was created to help you help us. +You are welcome to [[https://discord.gg/qvGgnVx][join us on our Discord server]], otherwise read on to learn how +to contribute to our fine corner of the interwebs. * Table of Contents :TOC_3: - [[#where-can-i-help][Where can I help?]] @@ -32,19 +32,20 @@ or wires crossed, this guide was created to help you help us. - [[#contributing-documentation][Contributing documentation]] - [[#contributing-to-dooms-manual][Contributing to Doom's manual]] - [[#contributing-module-documentation][Contributing module documentation]] +- [[#help-keep-packages-up-to-date][Help keep packages up-to-date!]] - [[#other-ways-to-support-doom-emacs][Other ways to support Doom Emacs]] - [[#special-thanks][Special thanks]] * Where can I help? + Our [[https://github.com/hlissner/doom-emacs/issues][issue tracker]] has many issues. If you find one that you have an answer to, - please don't hold back! + it would be a huge help! + Look for issues tagged [[https://github.com/hlissner/doom-emacs/labels/good%20first%20issue][good first issue]]. These were judged to have a low barrier of entry. + Look for issues tagged [[https://github.com/hlissner/doom-emacs/labels/help%20wanted][help wanted]]. These tend to be a little (or a lot) harder, and are issues outside my own expertise. + If you've encountered a bug, [[https://github.com/hlissner/doom-emacs/issues/new/choose][file a bug report]]. + The [[https://github.com/hlissner/doom-emacs/projects/3][development roadmap board]] is a rough timeline of what is being worked on - and when. It will give you some idea of what will change and where you can + and when. It will give you an idea of what will change and where you can redirect your efforts. + The [[https://github.com/hlissner/doom-emacs/projects/2][plugins under review board]] lists third party plugins being considered (or rejected) for inclusion in Doom Emacs. Approved and unclaimed packages are @@ -54,30 +55,21 @@ or wires crossed, this guide was created to help you help us. cause) perhaps you can address them at the source. * TODO Reporting issues -So you've found a problem. Before you fire off that bug report, there are a few -things you should try first: +You've found a problem and you're ready to fire off that bug report. Hold up! +Before you do that, [[file:getting_started.org::*Troubleshoot][have a look at our Troubleshooting guide]]. If none of these +suggestions pan out, /then/ it is time to file a bug report. -+ Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom - clean~ to ensure it isn't. *Byte-compilation interferes with debugging!* -+ Run ~bin/doom refresh~ to ensure all plugins are installed and autoload files - generated. -+ Run ~bin/doom doctor~ to diagnose common issues with your system. -+ Check [[file:faq.org::*Common%20Issues][Common Issues]] in the FAQ to see if yours is a known issue. -+ If you happen to know what module(s) are relevant to your issue, check their - documentation (press = h m= to jump to a module's documentation). Your - issue may be documented. -+ If possible, check if the issue can be reproduced in vanilla Emacs (Emacs - without Doom) and/or vanilla Doom (Doom without your private config). To test - this, use ~M-x doom/sandbox~ (bound to = h E=). [[file:getting_started.org::*Use the sandbox][A guide for using the - sandbox can be found in the manual]]. -+ Make sure your issue hasn't already been reported by searching the [[https://github.com/hlissner/doom-emacs/issues][issue - tracker]]. -+ Make sure your issue hasn't been resolved on the =develop= branch of Doom. +An effective bug report is informative. Please try to provide: -If these suggestions haven't worked for you, it's time [[https://github.com/hlissner/doom-emacs/issues/new/choose][to write a bug report]]. -Please make sure of the following before you submit: ++ A backtrace of all mentioned errors. ++ A step-by-step reproduction of the issue. ++ Information about your Doom config and system environment. ++ Screenshots/casts of the issue (if possible). -** TODO Collect backtraces of any error messages +This section will show you how to collect this information. + +** Acquire a backtrace from errors +See "[[file:getting_started.org::*How to extract a backtrace from an error][How to extract a backtrace from an error]]" in the [[file:getting_started.org][Getting Started]] guide. ** TODO Create a step-by-step reproduction guide @@ -165,6 +157,11 @@ contact via our [[https://discord.gg/bcZ6P3y][Discord server]] or [[mailto:henri ** TODO Contributing module documentation +* TODO Help keep packages up-to-date! +Doom pins all its packages to reduce the likelihood of upstream breakage leaking +into Doom Emacs. However, we may miss when a package releases hotfixes for +critical issues. Let us know or PR a bump to our pinned packages. + * TODO Other ways to support Doom Emacs * TODO Special thanks diff --git a/docs/faq.org b/docs/faq.org index 695ee890c..c966f780c 100644 --- a/docs/faq.org +++ b/docs/faq.org @@ -20,11 +20,6 @@ - [[#should-i-fork-doom-to-customize-it][Should I fork Doom to customize it?]] - [[#how-do-i-configure-doom-emacs][How do I configure Doom Emacs?]] - [[#how-do-i-enable-or-disable-a-doom-module][How do I enable or disable a Doom module?]] - - [[#how-do-i-install-a-package-from-elpa][How do I install a package from ELPA?]] - - [[#how-do-i-install-a-package-from-githubanother-source][How do I install a package from github/another source?]] - - [[#how-do-i-change-where-an-existing-package-is-installed-from][How do I change where an existing package is installed from?]] - - [[#how-do-i-disable-a-package-completely][How do I disable a package completely?]] - - [[#how-do-i-reconfigure-a-package-included-in-doom][How do I reconfigure a package included in Doom?]] - [[#how-do-i-change-the-theme][How do I change the theme?]] - [[#how-do-i-change-the-fonts][How do I change the fonts?]] - [[#how-do-i-bind-my-own-keys-or-change-existing-ones][How do I bind my own keys (or change existing ones)?]] @@ -32,12 +27,20 @@ - [[#how-do-i-change-the-leaderlocalleader-keys][How do I change the leader/localleader keys?]] - [[#how-do-i-change-the-style-of-line-numbers-or-disable-them-altogether][How do I change the style of line-numbers (or disable them altogether)?]] - [[#how-do-i-change-the-behavior-and-appearance-of-popup-windows][How do I change the behavior and appearance of popup windows?]] + - [[#how-do-i-change-the-appearance-a-face-or-faces][How do I change the appearance a face (or faces)?]] - [[#can-doom-be-customized-without-restarting-emacs][Can Doom be customized without restarting Emacs?]] - [[#can-vimevil-be-removed-for-a-more-vanilla-emacs-experience][Can Vim/Evil be removed for a more vanilla Emacs experience?]] - [[#should-i-use-make-or-bindoom][Should I use ~make~ or ~bin/doom~?]] - [[#when-should-and-shouldnt-i-use-bindoom][When should and shouldn't I use ~bin/doom~?]] - - [[#when-to-run-doom-refresh][When to run ~doom refresh~]] + - [[#when-to-run-doom-sync][When to run ~doom sync~]] - [[#how-to-suppress-confirmation-prompts-while-bindoom-is-running][How to suppress confirmation prompts while ~bin/doom~ is running]] +- [[#package-management][Package Management]] + - [[#how-do-i-install-a-package-from-elpa][How do I install a package from ELPA?]] + - [[#how-do-i-install-a-package-from-githubanother-source][How do I install a package from github/another source?]] + - [[#how-do-i-change-where-an-existing-package-is-installed-from][How do I change where an existing package is installed from?]] + - [[#how-do-i-disable-a-package-completely][How do I disable a package completely?]] + - [[#how-do-i-reconfigure-a-package-included-in-doom][How do I reconfigure a package included in Doom?]] + - [[#where-does-straight-clonebuild-packages-to][Where does straight clone/build packages to?]] - [[#defaults][Defaults]] - [[#why-ivy-over-helm][Why Ivy over Helm?]] - [[#why-are-there-no-default-keybinds-for-smartparens-for-evil-users][Why are there no default keybinds for Smartparens (for evil users)?]] @@ -55,34 +58,38 @@ - [[#changes-to-my-config-arent-taking-effect][Changes to my config aren't taking effect]] - [[#the-frame-goes-black-on-macos-while-in-full-screen-mode][The frame goes black on MacOS, while in full-screen mode]] - [[#doom-crashes-when][Doom crashes when...]] + - [[#cant-load-my-theme-unable-to-find-theme-file-for-x-errors][Can't load my theme; ~unable to find theme file for X~ errors]] + - [[#tramp-connections-hang-forever-when-connecting][TRAMP connections hang forever when connecting]] + - [[#an-upstream-package-was-broken-and-i-cant-update-it][An upstream package was broken and I can't update it]] + - [[#why-do-i-see-ugly-indentation-highlights-for-tabs][Why do I see ugly indentation highlights for tabs?]] - [[#contributing][Contributing]] * General ** Why is it called Doom? -An homage to idsoftware's classic game, whose open sourced code was my first -exposure to programming. +It's an homage to idsoftware's classic game, whose open sourced code was +Henrik's (Doom's maintainer) first exposure to programming. -Also, if you're obsessed enough with a text editor that you write a community -config for it, you're doomed from the get go. +And if you're obsessed enough with a text editor that you write a community +config for it, you're doomed from the start. ** Does Doom work on Windows? -Windows support is weak and will generally lag behind Linux/MacOS support, so -your mileage will vary. However, many have reported success installing Doom -Emacs on Windows (using WSL, WSL2 or scope/chocolatey). You'll find install -instructions for Windows in the [[file:getting_started.org::On Windows][Getting Starting guide]]. +Windows support is weak and generally lags behind Linux/MacOS support, so your +mileage will vary. However, some have reported success using Doom Emacs on +Windows (using WSL, WSL2 or scoop/chocolatey). You'll find install instructions +in the [[file:getting_started.org::On Windows][Getting Starting guide]]. -If you're a Windows user, help us improve our documentation on Windows support! +If you're a Windows user, help us improve our documentation! ** Is Doom only for vimmers? -Henrik is a dyed-in-the-wool vimmer with more than a decade of vim muscle -memory. Vim's is the only paradigm he truly knows, so vimmers will always be his -primary audience. +No, but it is Doom's primary audience. Its maintainer is a dyed-in-the-wool +vimmer with almost two decades of vim muscle memory, and he came to Emacs to +find a better vim. -That's not to say Doom won't work without evil, only that it is less polished in -that respect. Our growing non-evil userbase are slowly improving the situation -however. We welcome suggestions and PRs to help accommodate a non-evil workflow. +Although Doom is less polished without evil, its growing non-evil user base is +slowly improving the situation. We welcome suggestions and PRs to help +accommodate a non-evil workflow. -If you'd still like a go at it, see the [[file:../modules/editor/evil/README.org::Removing%20evil-mode][Removing evil-mode]] section in the +If you'd still like a go at it, see the [[file:../modules/editor/evil/README.org::Removing%20evil-mode][removing evil-mode]] section in the [[file:../modules/editor/evil/README.org][:editor evil]] module's documentation. ** I am a beginner. Can I use Doom? @@ -115,12 +122,12 @@ To paraphrase (and expand upon) a [[https://www.reddit.com/r/emacs/comments/6pa0 you into Doom. + *Doom manages its packages outside of Emacs.* Spacemacs installs (and checks for packages) on startup or on demand. Doom leaves package management to be - done externally, through the ~bin/doom~ script. This allows package management - can be scripted on the command line and enables a number of startup + done externally, through the ~bin/doom~ script. This allows for package + management to be scripted on the command line and enables a number of startup optimizations we wouldn't have otherwise. ** Why such a complicated package management system? -Doom had ++four++ *five* goals for its package management system: +Doom had +four+ *five* goals for its package management system: 1. *Scriptability:* package management should be shell-scriptable, so updating can be automated. @@ -129,15 +136,15 @@ Doom had ++four++ *five* goals for its package management system: are out-of-date through official channels, have changed hands, have a superior fork, or aren't available in ELPA repos. 3. *Performance:* lazy-loading the package management system is a tremendous - boon to start up speed. Initializing package.el and quelpa (and/or checking - that your packages are installed) every time you start up is expensive. + boon to start up speed. Initializing package.el and straight (and/or checking + that your packages are installed) each time you start up is expensive. 4. *Organization:* an Emacs configuration grows so quickly, in complexity and size. A clear separation of concerns (configuration of packages from their installation) is more organized. 5. *Reproducibility:* /This goal hasn't been implemented yet/, but all our work up until now is aimed at this goal. Emacs is a tumultuous ecosystem; packages break left and right, and we rely on hundreds of them. Eventually, we want - package versions to be locked to versions of Doom so that Doom installs are + package versions to be locked to Doom's releases so that Doom installs are reproducible. ** How does Doom start up so quickly? @@ -155,10 +162,10 @@ up ~gc-cons-threshold~ (and perhaps ~gc-cons-percentage~) temporarily: ;; ... your emacs config here ... #+END_SRC -However, it is important to reset it eventually (as late as possible). Not doing -so will cause garbage collection freezes during long-term interactive use. -Conversely, a ~gc-cons-threshold~ that is too small will cause stuttering. We -use 16mb as our default. +However, it is important to reset it eventually. Not doing so will cause garbage +collection freezes during long-term interactive use. Conversely, a +~gc-cons-threshold~ that is too small will cause stuttering. We use 16mb as our +default. #+BEGIN_SRC emacs-lisp (add-hook 'emacs-startup-hook @@ -185,14 +192,16 @@ helm and ivy). Here is how Doom does it: (add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h) #+END_SRC +Another alternative (which is [[https://github.com/hlissner/doom-emacs/blob/develop/core/core.el#L269-L274][what Doom uses]]) is to use the [[https://gitlab.com/koral/gcmh/][gcmh]] package to +stave off the GC until you are idle or unfocus the Emacs frame. + *** Unset ~file-name-handler-alist~ temporarily Emacs consults this variable every time a file is read or library loaded, or when certain functions in the file API are used (like ~expand-file-name~ or ~file-truename~). -They do so to check if a special handler is needed to read it, but none of these -handlers are necessary for the initialization work we do at startup, so it is -generally safe to disable it (temporarily!): +Emacs does to check if a special handler is needed to read that file, but none +of them are (typically) necessary at startup, so we disable them (temporarily!): #+BEGIN_SRC emacs-lisp (defvar doom--file-name-handler-alist file-name-handler-alist) @@ -209,60 +218,38 @@ generally safe to disable it (temporarily!): (setq file-name-handler-alist doom--file-name-handler-alist))) #+END_SRC -It is important to restore this variable, otherwise you won't be able to use -TRAMP and Emacs will be unable to read compressed/encrypted files. - -*** Cut down on ~load-path~ lookups -Each ~load~ and ~require~ call (without an second argument) costs an O(n) lookup -on ~load-path~. The average Doom config has approximately 260 packages including -dependencies, and around 40 built-in packages. That means a minimum of 300 -entries in ~load-path~ with a worst case of =n=300= for /each/ package load (but -realistically, =n= will be somewhere between =2= and =20=). - -The cost isn't great, but it does add up. There isn't much to do about this, -except be mindful of it where we can: - -+ Paths in Doom's autoloads file are replaced with absolute ones, thus incurring - no lookup cost to lazy load them. -+ The ~load!~ macro is used instead of ~require~ where possible. This builds - paths with string concatenation (which is baked in at compile time, removing - most of the associated cost). -+ ~load-path~ is let-bound to a subset of itself where possible (the - ~doom--initial-load-path~ variable contains the value of ~load-path~ before it - was touched by Doom). +Don't forget to restore ~file-name-handler-alist~, otherwise TRAMP won't work +and compressed/encrypted files won't open. *** Concatenate package autoloads When you install a package, a PACKAGE-autoloads.el file is generated. This file -contains a map of autoloaded functions and snippets declared by the package -(that's what those ~;;;###autoload~ comments are for in packages). They tell -Emacs where to find them, when they are eventually called. In your conventional -Emacs config, every single one of these autoloads files are loaded immediately -at startup. +contains a map of autoloaded functions and snippets declared by the package. +They tell Emacs where to find them when they are eventually called. In your +conventional Emacs config, every one of these autoloads files are loaded +immediately at startup (when ~package-initialize~ is called). Since you'll commonly have hundreds of packages, loading hundreds of autoloads -file can hurt startup times. We get around this by concatenating these autoloads -files into one giant one (in =~/.emacs.d/.local/autoloads.pkg.el=) when you run -~doom refresh~. +file can hurt startup times, especially without an SSD. We get around this by +concatenating these files into one giant one when you run ~doom sync~. -Emacs 27+ will introduce a ~package-quickstart~ feature that will do this for -you -- the =straight= package manager does this for you too -- but Doom Emacs -has its own specialized mechanism for doing this, and has tacked a number of -Doom-specific optimizations on top of it. +Emacs 27+ introduces a ~package-quickstart~ command does this for you, and +=straight=, our package manager, does this for you too, but [[https://github.com/hlissner/doom-emacs/tree/develop/core/cli/autoloads.el][Doom Emacs has its +own specialized mechanism]] for this, topped off with a few Doom-specific +optimizations. *** Lazy load package management system(s) Initializing package.el or straight.el at startup is expensive. We can save some time by delaying that initialization until we actually need these libraries (and -only eagerly load them when we're doing package management, e.g. when we run -~doom refresh~). +load them only when we're doing package management, e.g. when we run ~doom +sync~). -Among other things, ~doom refresh~ does a lot for us. It generates concatenated +Among other things, ~doom sync~ does a lot for us. It generates concatenated autoloads files; caches expensive variables like caches ~load-path~, ~Info-directory-list~ and ~auto-mode-alist~; and preforms all your package management activities there -- far away from your interactive sessions. -How exactly Doom accomplishes all this is a little complex, so instead, here is -a boiled-down version you can use in your own configs (for package.el, not -straight.el): +How exactly Doom accomplishes all this is a long story, so here is a boiled-down +version you can use in your own configs (for package.el, not straight.el): #+BEGIN_SRC emacs-lisp (defvar cache-file "~/.emacs.d/cache/autoloads") @@ -273,26 +260,12 @@ straight.el): (package-initialize) (with-temp-buffer (cl-pushnew doom-core-dir load-path :test #'string=) - (dolist (spec package-alist) - (when-let (desc (cdr spec)) - (let ((file (concat (package--autoloads-file-name desc) ".el"))) - (when (file-readable-p file) - ;; Ensure that the contents of this autoloads file believes they - ;; haven't been moved: - (insert "(let ((load-file-name " (prin1-to-string (abbreviate-file-name file)) "))\n") - (insert-file-contents file) - (save-excursion - ;; Delete forms that modify `load-path' and `auto-mode-alist', we - ;; will set them once, later. - (while (re-search-forward "^\\s-*\\((\\(?:add-to-list\\|\\(?:when\\|if\\) (boundp\\)\\s-+'\\(?:load-path\\|auto-mode-alist\\)\\)" nil t) - (goto-char (match-beginning 1)) - (kill-sexp))) - ;; Remove unnecessary comment lines and (provide ...) forms - (while (re-search-forward "^\\(?:;;\\(.*\n\\)\\|\n\\|(provide '[^\n]+\\)" nil t) - (unless (nth 8 (syntax-ppss)) - (replace-match "" t t))) - (unless (bolp) (insert "\n")) - (insert ")\n"))))) + (dolist (desc (delq nil (mapcar #'cdr package-alist))) + (let ((load-file-name (concat (package--autoloads-file-name desc) ".el"))) + (when (file-readable-p load-file-name) + (condition-case _ + (while t (insert (read (current-buffer)))) + (end-of-file))))) (prin1 `(setq load-path ',load-path auto-mode-alist ',auto-mode-alist Info-directory-list ',Info-directory-list) @@ -304,10 +277,9 @@ straight.el): #+END_SRC You'll need to delete ~cache-files~ any time you install, remove, or update a -new package, however. In that case you could advise ~package-install~ and -~package-delete~ to call ~initialize~ when they succeed. Or, you could make -~initialize~ interactive and call it manually when you determine it's necessary. -Up to you! +new package. You could advise ~package-install~ and ~package-delete~ to call +~initialize~ when they succeed, or make ~initialize~ interactive and call it +manually when necessary. Up to you! Note: package.el is sneaky, and will initialize itself if you're not careful. *Not on my watch, criminal scum!* @@ -322,20 +294,21 @@ Note: package.el is sneaky, and will initialize itself if you're not careful. *** Lazy load more than everything ~use-package~ can defer your packages. Using it is a no-brainer, but Doom goes a little further with lazy loading. There are some massive plugins out there. For -many of them, ordinary lazy loading techniques simply don't work. To name a few: +some of them, ordinary lazy loading techniques don't work. To name a few: + The =lang/org= module defers loading babel packages until their src blocks are - executed. You no longer need ~org-babel-do-load-languages~ in your config. + executed or read. You no longer need ~org-babel-do-load-languages~ in your + config -- in fact, you shouldn't use it at all! + Company and yasnippet are loaded as late as possible (waiting until the user opens a non-read-only, file-visiting buffer (that isn't in fundamental-mode)). -+ The =evil-easymotion= package has many keybinds. You'd need to load the - package for them to all take effect, so instead, =gs= is bound to a command - that loads the package and then invisibly populates =gs=, then simulates the - =gs= keypress as though those new keys had always been there. -+ A number of packages are "incrementally" loaded. This is a Doom feature where, - after a few seconds of idle time post-startup, Doom will load packages - piecemeal while Emacs. It will quickly abort if it detects input, as to make - the process as subtle as possible. ++ The =evil-easymotion= package binds many keys, none of which are available + until you load the package. Instead of loading it at startup, =gs= is bound to + a command that loads the package, populates =gs=, then simulates the =gs= key + press as though those new keys had always been there. ++ Doom loads some packages "incrementally". i.e. after a few seconds of idle + time post-startup, Doom loads packages piecemeal (one dependency at a time) + while Emacs. It aborts if it detects input, as to make the process as subtle + as possible. For example, instead of loading =org= (a giant package), it will load these dependencies, one at a time, before finally loading =org=: @@ -349,21 +322,6 @@ many of them, ordinary lazy loading techniques simply don't work. To name a few: This ensures packages load as quickly as possible when you first load an org file. -*** +Exploit byte-compilation!+ -It used to be that byte-compilation bought a 40-60% improvement in startup -times, because expensive operations (like ~package-initialize~ or -~exec-path-from-shell~) were evaluated at compile time, but Doom has changed. - -I've since adopted a pre-cache approach (when running ~doom refresh~), which -brings these startup benefits to uncompiled Emacs. This renders byte-compilation -significantly less beneficial for startup time. - -That said, compilation will still benefit Doom's snappiness in general. - -Run ~doom compile :core~ to only compile Doom's core files, or ~doom compile~ to -compile the /entire/ config (=~/.emacs.d= and =~/.doom.d=) -- which may take a -while. - *** Use [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Lexical-Binding.html][lexical-binding]] everywhere Add ~;; -*- lexical-binding: t; -*-~ to the top of your elisp files. This can break code if you've written it to depend on undeclared dynamic variables, but @@ -376,32 +334,30 @@ find more about it in: + [[http://nullprogram.com/blog/2016/12/22/]["Some Performance Advantages of Lexical Scope."]] ** Why is startup time important? Why not use the daemon? -One of my motivations for a config that starts up fast (aside from the learning -experience) was to shape Emacs into a viable alternative to vim for one-shot -editing in the terminal (without ~-Q~). This also facilitates: +The central motivation for a config that starts up fast (aside from the learning +experience) was to have a viable alternative to vim for quick, one-shot editing +in the terminal (without ~-Q~). -- Running multiple, independent instances of Emacs (e.g. on a per-project basis, or - for nix-shell users, or to isolate one instance for IRC from an instance for - writing code, etc). +Besides that, it happens to facilitate: + +- Running multiple, independent instances of Emacs (e.g. on a per-project basis, + or for nix-shell users, or to isolate one instance for IRC from an instance + for writing code, etc). - Quicker restarting of Emacs, to reload package settings or recover from disastrous errors which can leave Emacs in a broken state. - Faster integration with "edit in Emacs" solutions (like [[https://github.com/alpha22jp/atomic-chrome][atomic-chrome]]), and - the potential to use them without a running daemon. + without a daemon. -What's more, I don't like using more tools than I need. We should not need a -second program just to make the first run comfortably. +What's more, I believe a daemon shouldn't be necessary to get a sane startup +time out of Emacs. ** How do I use Doom alongside other Emacs configs? I recommend [[https://github.com/plexus/chemacs][Chemacs]]. You can think of it as a bootloader for Emacs. You'll [[file:getting_started.org::*Alongside other Emacs configs (with Chemacs)][find instructions on how to use it with Doom in the user manual]]. -If you only want to try it out without affecting your current config, it is safe -to install Doom anywhere you like. The ~bin/doom~ utility will only address the -config the script is located under. - You'll still need a separate folder for personal configuration (=~/.doom.d= or -=~/.config/doom= by default), but the =-p PATH= flag (or ~DOOMDIR~ environment -variable) will allow you to use a different location: +=~/.config/doom= by default), but the =--doomdir PATH= switch (or ~DOOMDIR~ +environment variable) will allow you to use a different location: #+BEGIN_SRC bash # First install Doom somewhere @@ -467,153 +423,57 @@ to as your ~$DOOMDIR~. Your private config is typically comprised of an =init.el=, =config.el= and =packages.el= file. Put all your config in =config.el=, install packages by -adding ~package!~ declarations to =packagse.el=, and enable/disable modules in +adding ~package!~ declarations to =packages.el=, and enable/disable modules in you ~doom!~ block, which should have been created in your =init.el= when you first ran ~doom install~. Check out the [[file:getting_started.org::Customize][Customize section]] in the [[file:getting_started.org][Getting Started]] guide for details. ** How do I enable or disable a Doom module? -You'll find your ~doom!~ block in =~/.doom.d/init.el=. This block contains a -list of modules you want enabled and what order to load them in. Disable modules -by commenting them out with semicolons. To enable them, remove those leading -semicolons: +Comment or uncomment the module in your ~doom!~ block, found in +=$DOOMDIR/init.el=. -#+BEGIN_SRC emacs-lisp -(doom! :lang - python ; this is enabled - ;;ruby ; this is disabled - rust) -#+END_SRC - -Remember to run ~bin/doom refresh~ afterwards, on the command line, to sync your +Remember to run ~bin/doom sync~ afterwards, on the command line, to sync your module list with Doom. -You can find a comprehensive list of modules in the [[file:index.org::*Module list][Module Index]]. - -** How do I install a package from ELPA? -Add a ~package!~ declaration to =~/.doom.d/packages.el= for each package you -want installed. - -#+BEGIN_SRC elisp -(package! winum) -#+END_SRC - -Remember to run ~doom refresh~ afterwards to ensure the package is installed. - -You'll find more information in the "[[file:getting_started.org::*Installing%20packages][Installing packages]]" section of the [[file:getting_started.org][Getting -Started]] guide. - -** How do I install a package from github/another source? -The ~package!~ macro can be passed a MELPA style recipe, allowing you to install -packages from just about anywhere: - -#+BEGIN_SRC elisp -(package! evil :recipe (:host github :repo "hlissner/my-evil-fork")) -#+END_SRC - -Remember to run ~doom refresh~ every time you modify you package list, to ensure -your packages are set up and installed. - -You can find more information about the recipe format [[https://github.com/raxod502/straight.el#the-recipe-format][in the straight.el package -readme]]. - -#+begin_quote -If a MELPA recipe exists for the package you are writing a ~package!~ -declaration for, you may omit keywords and Doom's package manager will fill them -in with values from its original recipe. -#+end_quote - -You'll find more information in the "[[file:getting_started.org::*Installing%20packages%20from%20external%20sources][Installing packages from external sources]]" -section of the [[file:getting_started.org][Getting Started]] guide. - -** How do I change where an existing package is installed from? -~package!~ declarations in your private =packages.el= file have precedence over -modules (even your own). Simply add a new one for that package with the new -recipe. - -You'll find more information in the "[[file:getting_started.org::*Changing%20a%20built-in%20recipe%20for%20a%20package][Changing a built-in recipe for a package]]" -section of the [[file:getting_started.org][Getting Started]] guide. - -** How do I disable a package completely? -With the ~package!~ macro's ~:disable~ property: - -#+BEGIN_SRC elisp -;;; in DOOMDIR/packages.el -(package! irony :disable t) -#+END_SRC - -Remember to run ~doom refresh~ afterwards to ensure that the package is -uninstalled and disabled. - -You'll find more information in the "[[file:getting_started.org::*Disabling%20packages][Disabling packages]]" section of the [[file:getting_started.org][Getting -Started]] guide. - -** How do I reconfigure a package included in Doom? -~use-package!~ and ~after!~ (wrappers around ~use-package~ and -~eval-after-load~, respectively) are your bread and butter for configuring -packages in Doom. - -#+BEGIN_SRC elisp -;; Takes a feature symbol or a library name (string) -(after! evil - (setq evil-magic nil)) - -;; Takes a major-mode, a quoted hook function or a list of either -(add-hook! python-mode - (setq python-shell-interpreter "bpython")) - -(use-package! hl-todo - ;; if you omit :defer, :hook, :commands, or :after, then the package is loaded - ;; immediately. By using :hook here, the `hl-todo` package won't be loaded - ;; until prog-mode-hook is triggered (by activating a major mode derived from - ;; it, e.g. python-mode) - :hook (prog-mode . hl-todo-mode) - :init - ;; code here will run immediately - :config - ;; code here will run after the package is loaded - (setq hl-todo-highlight-punctuation ":")) - -;; There's also `setq-hook!' for setting variables buffer-locally -(setq-hook! python-mode python-indent-offset 2) -#+END_SRC - -See the "[[file:getting_started.org::*Configuring%20Doom][Configuring Doom]]" section of the [[file:getting_started.org][Getting Started]] guide for more -explanation and examples. +See the "[[file:getting_started.org::*Configuration modules][Configuration modules]]" section of the [[file:getting_started.org][Getting Started]] guide for more +information. ** How do I change the theme? There are two ways to load a theme. Both assume the theme is installed and -available. They are: +available. You can either set ~doom-theme~ or manually load a theme with the +~load-theme~ function. #+BEGIN_SRC emacs-lisp -;;; in ~/.doom.d/config.el +;;; add to ~/.doom.d/config.el (setq doom-theme 'doom-tomorrow-night) ;; or (load-theme 'doom-tomorrow-night t) #+END_SRC +#+begin_quote At the moment, the only difference between the two is that ~doom-theme~ is loaded when Emacs has finished initializing at startup and ~load-theme~ loads the theme immediately. Which you choose depends on your needs, but I recommend setting ~doom-theme~ because, if I later discover a better way to load themes, I can easily change how Doom uses ~doom-theme~, but I can't (easily) control how you use the ~load-theme~ function. +#+end_quote *** Installing a third party theme To install a theme from a third party plugin, say, [[https://github.com/bbatsov/solarized-emacs][solarized]], you need only install it, then load it: #+BEGIN_SRC emacs-lisp -;; in ~/.doom.d/packages.el -(package! solarized) +;;; add to ~/.doom.d/packages.el +(package! solarized-theme) -;; in ~/.doom.d/config.el +;;; add to ~/.doom.d/config.el (setq doom-theme 'solarized-dark) #+END_SRC -Don't forget to run ~doom refresh~ afterwards to ensure the package is -installed. +Don't forget to run ~doom sync~ after adding that ~package!~ statement to ensure +the package is installed. ** How do I change the fonts? Doom exposes five (optional) variables for controlling fonts in Doom, they are: @@ -624,12 +484,12 @@ Doom exposes five (optional) variables for controlling fonts in Doom, they are: + ~doom-unicode-font~ + ~doom-big-font~ (used for ~doom-big-font-mode~) -Each of these will accept either a =font-spec=, font string (="Input Mono-12"=), -or [[https://wiki.archlinux.org/index.php/X_Logical_Font_Description][xlfd font string]]. +They all accept either a =font-spec=, font string (="Input Mono-12"=), or [[https://wiki.archlinux.org/index.php/X_Logical_Font_Description][xlfd +font string]]. e.g. #+BEGIN_SRC emacs-lisp -;; ~/.doom.d/config.el +;;; Add to ~/.doom.d/config.el (setq doom-font (font-spec :family "Input Mono Narrow" :size 12 :weight 'semi-light) doom-variable-pitch-font (font-spec :family "Fira Sans") ; inherits `doom-font''s :size doom-unicode-font (font-spec :family "Input Mono Narrow" :size 12) @@ -697,7 +557,7 @@ These variables control what key to use for leader and localleader keys: e.g. #+BEGIN_SRC emacs-lisp -;; in ~/.doom.d/config.el +;;; add to ~/.doom.d/config.el (setq doom-leader-key "," doom-localleader-key "\\") #+END_SRC @@ -705,13 +565,9 @@ e.g. ** How do I change the style of line-numbers (or disable them altogether)? Doom uses the ~display-line-numbers~ package, which is built into Emacs 26+. -#+begin_quote -This package has been backported for Emacs 25 users, but is powered by =nlinum= -there (which will be removed when we drop 25 support). -#+end_quote - *** Disabling line numbers entirely #+BEGIN_SRC elisp +;;; add to ~/.doom.d/config.el (setq display-line-numbers-type nil) ;; or (remove-hook! '(prog-mode-hook text-mode-hook conf-mode-hook) @@ -720,16 +576,25 @@ there (which will be removed when we drop 25 support). *** Switching to relative line numbers (permanently) To change the style of line numbers, change the value of the -~display-line-numbers-type~ variable. It accepts =t= (normal line numbers), -='relative= (relative line numbers), ='visual= (relative line numbers in screen -space) and =nil= (no line numbers). +~display-line-numbers-type~ variable. It accepts the following values: -You'll find more precise documentation on the variable through =SPC h v -display-line-numbers-type= or =C-h v display-line-numbers-type=. +#+begin_example +t normal line numbers +'relative relative line numbers +'visual relative line numbers in screen space +nil no line numbers +#+end_example -#+begin_quote -The ~'visual~ option is unavailable in Emacs 25. -#+end_quote +For example: + +#+BEGIN_SRC elisp +;;; add to ~/.doom.d/config.el +(setq display-line-numbers-type 'relative) +#+END_SRC + +You'll find more precise documentation on the variable through = v +display-line-numbers-type= (== is =SPC h= for evil users, =C-h= +otherwise). *** Switching the style of line numbers (temporarily) Use ~M-x doom/toggle-line-numbers~ (bound to =SPC t l= by default) to cycle @@ -748,6 +613,19 @@ rules. You'll find more comprehensive documentation on ~set-popup-rule!~ in its docstring (available through =SPC h f= -- or =C-h f= for non-evil users). +** How do I change the appearance a face (or faces)? +Doom provides the ~custom-set-faces!~ and ~custom-theme-set-faces!~ macros as a +convenience. + +#+begin_quote +*Do not use ~M-x customize~ or any of the built-in Emacs customize-* API.* Doom +does not support it and never will; those settings could break at any time. +#+end_quote + +See = f custom-set-faces\!= (or =M-x helpful-function custom-set-faces\!=) +for documentation and examples on how to use it. == is =SPC h= for evil +users and =C-h= for non-evil users. + ** Can Doom be customized without restarting Emacs? Short answer: You can, but you shouldn't. @@ -757,20 +635,17 @@ tools for experienced Emacs users to skirt around it (most of the time): - Evaluate your changes on-the-fly with ~+eval/region~ (bound to the =gr= operator for evil users) or ~eval-last-sexp~ (bound to =C-x C-e=). Changes take effect immediately. -- On-the-fly evaluation won't work for all changes. For instance, changing your - ~doom!~ block (i.e. the list of modules for Doom to enable) will always - require a restart (and ~bin/doom refresh~). +- On-the-fly evaluation won't work for all changes. e.g. Changing your ~doom!~ + block (i.e. the list of modules for Doom to enable). - Doom provides ~M-x doom/reload~ for your convenience, which will run ~doom - refresh~, restart the Doom initialization process, and re-evaluate your - personal config, but this won't clear pre-existing state. That may or may not - be a problem, this hasn't be thoroughly tested and Doom cannot anticipate - complications arising from your private config. - - If you intend to use ~doom/reload~, you must design your config to be - idempotent. -- Many ~bin/doom~ commands are available as elisp commands with the ~doom//*~ - prefix. e.g. ~doom//refresh~, ~doom//update~, etc. Feel free to use them, but + But rather than running ~doom sync~ and restarting Emacs, Doom provides ~M-x + doom/reload~ for your convenience (bound to =SPC h r r= and =C-h r r=). This + runs ~doom sync~, restarts the Doom initialization process and re-evaluates + your personal config. However, this won't clear pre-existing state; Doom won't + unload modules/packages that have already been loaded and it can't anticipate + complications arising from a private config that isn't idempotent. +- Some ~bin/doom~ commands are available as elisp commands. e.g. ~doom/reload~ + for ~doom sync~, ~doom/upgrade~ for ~doom upgrade~ ~doom//s~, ~doom//update~, etc. Feel free to use them, but consider them highly experimental and subject to change without notice. - You can quickly restart Emacs and restore the last session with ~doom/restart-and-restore~ (bound to =SPC q r=). @@ -792,7 +667,7 @@ commands that you may find particularly useful: + ~doom doctor~ :: Diagnose common issues in your environment and list missing external dependencies for your enabled modules. -+ ~doom refresh~ :: Ensures that all missing packages are installed, orphaned ++ ~doom sync~ :: Ensures that all missing packages are installed, orphaned packages are removed, and metadata properly generated. + ~doom install~ :: Install any missing packages. + ~doom update~ :: Update all packages that Doom's (enabled) modules use. @@ -806,12 +681,12 @@ commands that you may find particularly useful: #+BEGIN_SRC bash git pull - doom refresh + doom sync doom update #+END_SRC -** When to run ~doom refresh~ -As a rule of thumb you should run ~doom refresh~ whenever you: +** When to run ~doom sync~ +As a rule of thumb you should run ~doom sync~ whenever you: + Update Doom with ~git pull~ instead of ~doom upgrade~, + Change your ~doom!~ block in =$DOOMDIR/init.el=, @@ -820,8 +695,8 @@ As a rule of thumb you should run ~doom refresh~ whenever you: + Install an Emacs package or dependency outside of Emacs (i.e. through your OS package manager). -If anything is misbehaving, it's a good idea to run ~doom refresh~ first. ~doom -refresh~ is responsible for regenerating your autoloads file (which tells Doom +If anything is misbehaving, it's a good idea to run ~doom sync~ first. ~doom +sync~ is responsible for regenerating your autoloads file (which tells Doom where to find lazy-loaded functions and libraries), installing missing packages, and uninstall orphaned (unneeded) packages. @@ -835,9 +710,32 @@ doom --yes update YES=1 doom update #+END_SRC +* Package Management +** How do I install a package from ELPA? +See the "[[file:getting_started.org::*Installing%20packages][Installing packages]]" section of the [[file:getting_started.org][Getting Started]] guide. + +** How do I install a package from github/another source? +See the "[[file:getting_started.org::*Installing%20packages%20from%20external%20sources][Installing packages from external sources]]" section of the [[file:getting_started.org][Getting +Started]] guide. + +** How do I change where an existing package is installed from? +See the "[[file:getting_started.org::*Changing a recipe for a included package][Changing a recipe for a included package]]" section of the [[file:getting_started.org][Getting +Started]] guide. + +** How do I disable a package completely? +See the "[[file:getting_started.org::*Disabling%20packages][disabling packages]]" section of the [[file:getting_started.org][Getting Started]] guide. + +** How do I reconfigure a package included in Doom? +See the "[[file:getting_started.org::*Configuring packages][configuring packages]]" section of the Getting Started guide. + +** Where does straight clone/build packages to? +Straight clones packages to =~/.emacs.d/.local/straight/repos/REPO-NAME=, then +later symlinks and byte-compiles them to +=~/.emacs.d/.local/straight/build/PACKAGE-NAME= when they are "built". + * Defaults ** Why Ivy over Helm? -Short answer: I chose ivy because it is the simpler of the two. +Short answer: ivy is simpler to maintain. Long answer: Features and performance appear to be the main talking points when comparing the two, but as far as I'm concerned they are equal in both respects @@ -898,39 +796,38 @@ putting in the time to learn them. Otherwise, it is trivial to install expand-region and binds keys to it yourself: #+BEGIN_SRC elisp -;; in ~/.doom.d/packages.el +;;; add to ~/.doom.d/packages.el (package! expand-region) -;; in ~/.doom.d/config.el +;;; add to ~/.doom.d/config.el (map! :nv "C-=" #'er/contract-region :nv "C-+" #'er/expand-region) #+END_SRC ** Why not use exec-path-from-shell instead of ~doom env~? -In a nutshell, the ~doom env~ approach is a faster and more robust solution. +The ~doom env~ approach is a faster and more reliable solution. 1. ~exec-path-from-shell~ must spawn (at least) one process at startup to scrape - your shell environment. This can be arbitrarily slow depending on the user's - shell configuration. A single program (like pyenv or nvm) or config framework - (like oh-my-zsh) could undo all of Doom's startup optimizations in one fell - swoop. + your shell environment. This can be slow depending on the user's shell + configuration. A single program (like pyenv or nvm) or config framework (like + oh-my-zsh) could undo Doom's startup optimizations in one fell swoop. -2. ~exec-path-from-shell~ only scrapes /some/ state from your shell. You have to - be proactive in order to get it to capture all the envvars relevant to your - development environment. +2. ~exec-path-from-shell~ takes a whitelist approach and captures only ~PATH~ + and ~MANPATH~ by default. You must be proactive in order to capture all the + envvars relevant to your development environment and tools. - I'd rather it inherit your shell environment /correctly/ (and /completely/) - or not at all. It frontloads the debugging process rather than hiding it - until it you least want to deal with it. +~doom env~ takes the blacklist approach and captures all of your shell +environment. This front loads the debugging process, which is nicer than dealing +with it later, while you're getting work done. That said, if you still want ~exec-path-from-shell~, it is trivial to install yourself: #+BEGIN_SRC emacs-lisp -;; in ~/.doom.d/packages.el +;;; add to ~/.doom.d/packages.el (package! exec-path-from-shell) -;; in ~/.doom.d/config.el +;;; add to ~/.doom.d/config.el (require 'exec-path-from-shell) (when (display-graphic-p) (exec-path-from-shell-initialize)) @@ -941,16 +838,16 @@ TL;DR: =ws-butler= is less imposing. Don't be that guy who PRs 99 whitespace adjustments around his one-line contribution. Don't automate this aggressive behavior by attaching ~delete-trailing-whitespace~ (or ~whitespace-cleanup~) to ~before-save-hook~. If -you have rambunctious colleagues peppering trailing whitespace into your project, -you need to have a talk (with wiffle bats, preferably) rather than play this -passive-aggressive game of whack-a-mole. +you have rambunctious colleagues peppering trailing whitespace into your +project, you need to have a talk (with wiffle bats, preferably) rather than play +a passive-aggressive game of whack-a-mole. -Here at Doom Inc we believe that operations that mutate entire files should -never be automated. Rather, they should be invoked deliberately -- by someone -that is aware of the potential consequences. This is where =ws-butler= comes in. -It only cleans up whitespace /on the lines you've touched/ *and* it leaves -behind virtual whitespace (which is never written to the file, but remains there -so your cursor doesn't get thrown around in all that cleanup work). +Here at Doom Inc we believe that operations that mutate entire files should not +be automated. Rather, they should be invoked deliberately, when and where it is +needed, by someone that is aware of the potential consequences. This is where +=ws-butler= comes in. It only cleans up whitespace /on the lines you've touched/ +*and* it leaves behind virtual whitespace (which is never written to the file) +so your cursor doesn't get thrown around in all that cleanup work. In any case, if you had used =ws-butler= from the beginning, trailing whitespace and newlines would never be a problem! @@ -987,14 +884,14 @@ manually (e.g. by double-clicking each file in explorer). ** ~void-variable~ and ~void-function~ errors on startup The most common culprit for these types of errors are: -1. An out-of-date autoloads file. To regenerate it, run ~doom refresh~. +1. An out-of-date autoloads file. Run ~doom sync~ to regenerate them. - To avoid this issue, remember to run ~doom refresh~ whenever you modify your + To avoid this issue, remember to run ~doom sync~ whenever you modify your ~doom!~ block in =~/.doom.d/init.el=, or add ~package!~ declarations to =~/.doom.d/packages.el=. Or if you modify =~/.emacs.d/.local= by hand, for whatever reason. - See ~doom help refresh~ for details on what this command does and when you + See ~doom help sync~ for details on what this command does and when you should use it. 2. Emacs byte-code isn't forward compatible. If you've recently switched to a @@ -1025,7 +922,7 @@ issues #1 and #3: generate an envvar file by running ~doom env~. This scrapes your shell environment into a file that is loaded when Doom Emacs starts up. Check out ~doom help env~ for details on how this works. -For issue #2, you'll need to investigate your launcher. [[https://discord.gg/bcZ6P3y][Our Discord]] is a good +For issue #2, you'll need to investigate your launcher. [[https://discord.gg/qvGgnVx][Our Discord]] is a good place to ask about it. ** There's artefacting on my icon fonts in GUI Emacs ([[https://github.com/hlissner/doom-emacs/issues/956][#956]]) @@ -1052,8 +949,8 @@ If you still want to restore the old behavior, simply disable evil-snipe-mode: 1. Make sure you don't have both =~/.doom.d= and =~/.config/doom= directories. Doom will ignore the former if the latter exists. -2. Remember to run ~doom refresh~ when it is necessary. To get to know when, - exactly, you should run this command, run ~doom help refresh~. +2. Remember to run ~doom sync~ when it is necessary. To get to know when, + exactly, you should run this command, run ~doom help sync~. If neither of these solve your issue, try ~bin/doom doctor~. It will detect a variety of common issues, and may give you some clues as to what is wrong. @@ -1068,7 +965,6 @@ known fix for this. To work around it, you must either: support it), 3. Install Emacs via the =emacs-mac= homebrew formula. - ** Doom crashes when... Here are a few common causes for random crashes: @@ -1091,4 +987,82 @@ Here are a few common causes for random crashes: Or disable the =:ui doom-dashboard= & =:tools magit= modules (see [[https://github.com/hlissner/doom-emacs/issues/1170][#1170]]). +** Can't load my theme; ~unable to find theme file for X~ errors +This means Emacs can't find the X-theme.el file for the theme you want to load. +Emacs will search for this file in ~custom-theme-load-path~ and +~custom-theme-directory~. There are a couple reasons why it can't be found: + +1. It is generally expected that third party themes will [[https://github.com/hlissner/emacs-doom-themes/blob/master/doom-themes.el#L400-L405][add themselves]] to + ~custom-theme-load-path~, but you will occasionally encounter a theme that + does not. This should be reported upstream. + + In the meantime, you can get around this by eagerly loading the package: + + #+BEGIN_SRC elisp + (require 'third-party-theme) + (setq doom-theme 'third-party) + #+END_SRC +2. You've appended ~-theme~ to the end of your theme's name. + + #+BEGIN_SRC elisp + (setq doom-theme 'third-party-theme) + #+END_SRC + + When you load a theme Emacs searches for ~X-theme.el~. If you set + ~doom-theme~ to ~'third-party-theme~, it will search for + ~third-party-theme-theme.el~. This is rarely intentional. Omit the ~-theme~ + suffix. +3. Did you run ~doom sync~ after adding your third party theme plugin's + ~package!~ declaration to =~/.doom.d/packages.el=? +** TRAMP connections hang forever when connecting +You'll find solutions [[https://www.emacswiki.org/emacs/TrampMode#toc7][on the emacswiki]]. +** An upstream package was broken and I can't update it +Sometimes, if you've installed a [[https://github.com/hlissner/doom-emacs/issues/2213][broken package]] which was subsequently fixed +upstream, you can't run ~doom update~ to get the latest fixes due to evaluation +errors. + +In those cases, you need to delete the broken local copy before you can install +the new one, which is achieved by either deleting it from +=~/.emacs.d/.local/straight/repos=, or by cycling the module that installs it: + +1. Comment out the broken module/package. +2. Run ~doom sync~. +3. Uncomment the module/package. +4. Run ~doom sync~. + +** Why do I see ugly indentation highlights for tabs? +[[https://github.com/hlissner/doom-emacs/blob/develop/core/core-ui.el#L132-L150][Doom highlights non-standard indentation]]. i.e. Indentation that doesn't match +the indent style you've set for that file. Spaces are Doom's default style for +most languages (excluding languages where tabs are the norm, like Go). + +There are a couple ways to address this: + +1. Fix your indentation! If it's highlighted, you have tabs when you should have + spaces (or spaces when you should be using tabs). + + Two easy commands for that: + + - =M-x tabify= + - =M-x untabify= + +2. Change ~indent-tabs-mode~ (nil = spaces, t = tabs) in =~/.doom.d/config.el=: + + #+BEGIN_SRC elisp + ;; use tab indentation everywhere + (setq-default indent-tabs-mode t) + + ;; or only in certain modes + (setq-hook! 'sh-mode-hook indent-tabs-mode t) ; shell scripts + (setq-hook! '(c-mode-hook c++-mode-hook) indent-tabs-mode t) ; C/C++ + #+END_SRC + +3. Use [[https://editorconfig.org/][editorconfig]] to configure code style on a per-project basis. If you + enable Doom's =:tools editorconfig= module, Doom will recognize + =.editorconfigrc= files. + +4. Or trust in dtrt-indent; a plugin Doom uses to analyze and detect indentation + when you open a file (that isn't in a project with an editorconfig file). + This isn't foolproof, and won't work for files that have no content in them, + but it can help in one-off scenarios. + * TODO Contributing diff --git a/docs/getting_started.org b/docs/getting_started.org index 54de52c43..33fd8118a 100644 --- a/docs/getting_started.org +++ b/docs/getting_started.org @@ -1,6 +1,16 @@ #+TITLE: Getting Started Guide #+STARTUP: nofold +GNU Emacs is one grand ol' adventure, let alone Doom Emacs. Before you start +you'll need to set up Emacs, Doom, and its packages, then learn how to take care +of your new puppy slash operating system. This guide will walk you through +installing, using, configuring and troubleshooting Doom Emacs. + +#+begin_quote +If you feel like we've missed something, [[https://discord.gg/qvGgnVx][join us on our Discord server]] and let +us know! +#+end_quote + * Table of Contents :TOC_4: - [[#install][Install]] - [[#emacs--dependencies][Emacs & dependencies]] @@ -9,52 +19,57 @@ - [[#ubuntu][Ubuntu:]] - [[#nixos][NixOS]] - [[#on-macos][On macOS]] - - [[#where-not-to-install-emacs-from][Where *not* to install Emacs from]] + - [[#with-homebrew][With Homebrew]] + - [[#with-macports][With MacPorts]] - [[#on-windows][On Windows]] - [[#chocolatey--scoop][chocolatey / scoop]] - [[#wsl][WSL]] - [[#wsl2][WSL2]] - [[#doom-emacs][Doom Emacs]] + - [[#the-bindoom-utility][The ~bin/doom~ utility]] - [[#install-doom-manually][Install Doom Manually]] - - [[#alongside-other-emacs-configs-with-chemacs][Alongside other Emacs configs (with Chemacs)]] + - [[#install-doom-alongside-other-configs-with-chemacs][Install Doom alongside other configs (with Chemacs)]] - [[#externalsystem-dependencies][External/system dependencies]] -- [[#update][Update]] - - [[#doom][Doom]] - - [[#plugins][Plugins]] +- [[#update--rollback][Update & Rollback]] - [[#rollback][Rollback]] -- [[#customize][Customize]] - - [[#how-to-enable-or-disable-modules][How to enable or disable modules]] + - [[#updowngrading-emacs][Up/Downgrading Emacs]] +- [[#migrate][Migrate]] + - [[#from-vanilla-emacs][From vanilla Emacs]] + - [[#from-spacemacs][From Spacemacs]] +- [[#configure][Configure]] + - [[#modules][Modules]] - [[#package-management][Package management]] - [[#installing-packages][Installing packages]] - [[#installing-packages-from-external-sources][Installing packages from external sources]] + - [[#pinning-packages-to-specific-commits][Pinning packages to specific commits]] - [[#disabling-packages][Disabling packages]] - - [[#changing-a-built-in-recipe-for-a-package][Changing a built-in recipe for a package]] + - [[#changing-a-recipe-for-a-included-package][Changing a recipe for a included package]] - [[#usingloading-local-packages][Using/loading local packages]] + - [[#adjust-your-load-path][Adjust your ~load-path~]] + - [[#local-repo][:local-repo]] - [[#configuring-doom][Configuring Doom]] - [[#configuring-packages][Configuring packages]] - [[#reloading-your-config][Reloading your config]] - [[#binding-keys][Binding keys]] - - [[#doomdir-file-structure][DOOMDIR file structure]] - [[#writing-your-own-modules][Writing your own modules]] - - [[#structure-of-a-module][Structure of a module]] + - [[#load-order][Load order]] + - [[#location][Location]] + - [[#file-structure][File structure]] - [[#initel][=init.el=]] - [[#configel][=config.el=]] - [[#packagesel][=packages.el=]] - [[#autoloadel-or-autoloadel][=autoload/*.el= OR =autoload.el=]] - [[#doctorel][=doctor.el=]] - [[#additional-files][Additional files]] - - [[#load-order][Load order]] - - [[#module-flags][Module flags]] - - [[#testing-for-flags][Testing for flags]] - - [[#module-settings][Module settings]] + - [[#flags][Flags]] - [[#module-cookies][Module cookies]] + - [[#autodefs][Autodefs]] - [[#common-mistakes-when-configuring-doom-emacs][Common mistakes when configuring Doom Emacs]] - [[#packages-are-eagerly-loaded][Packages are eagerly loaded]] - [[#manual-package-management][Manual package management]] - - [[#using-org-babel-do-load-languages-to-load-your-babel-plugins][Using ~org-babel-do-load-languages~ to load your babel plugins]] + - [[#using-org-babel-do-load-languages-to-load-your-babel-packages][Using ~org-babel-do-load-languages~ to load your babel packages]] - [[#using-delete-trailing-whitespaces-or-whitespace-cleanup-to-manage-leftover-whitespace][Using ~delete-trailing-whitespaces~ or ~whitespace-cleanup~ to manage leftover whitespace]] - [[#troubleshoot][Troubleshoot]] - - [[#ive-run-into-an-issue-where-do-i-start][I've run into an issue, where do I start?]] - [[#looking-up-documentation-and-state-from-within-emacs][Looking up documentation and state from within Emacs]] - [[#variables-functions-faces-etc][Variables, functions, faces, etc.]] - [[#for-doom-modules-packages-autodefs-etc][For Doom Modules, packages, autodefs, etc.]] @@ -71,53 +86,56 @@ - [[#bisecting-doom-emacs][Bisecting Doom Emacs]] * Install -To embark on this grand Emacs adventure, you'll need a couple things installed, -including Emacs (shocking, I know), Doom Emacs, the plugins Doom depends on, and -any external tools /they/ depend on as well. +This is what you'll have installed by the end of this guide: -In summary, you'll be installing: ++ *Required:* + - Git 2.23+ + - Emacs 26.3+ + - [[https://github.com/BurntSushi/ripgrep][ripgrep]] 11.0+ + - GNU Find ++ *Optional:* + - [[https://github.com/sharkdp/fd][fd]] 7.3.0+ -- improves performance for many file indexing commands + - GNU ~tar~ -- needed to read compressed elisp files and install packages with + package.el + - GNU ~ls~ -- to overcome limitations with BSD ls on MacOS or BSD systems + - ~gcc~ or ~clang~ (preferred) -- needed to build some module dependencies + like irony-server, emacsqlite for magit, epdfinfo for pdf-tools or vterm -+ *git* -+ *Emacs 26.1+* -+ *ripgrep* -+ *all-the-icons fonts* -- unnecessary for exclusive use of terminal Emacs - -And then some optional dependencies that you will likely want, as the will -optimize Doom's performance and stability. - -+ [[https://github.com/sharkdp/fd][fd]] -+ *GNU ls* (BSD ls on macOS/BSD Linux has some limitations) -+ *clang* -- with which to compile certain external dependencies, like the - emacsqlite binary, irony server (requires clang), or vterm module - -The following sections will cover how to install Emacs and these dependencies -across various operating systems. +These packages ought to be available through the package managers of most Linux +distributions, or homebrew & macports on macOS, or scoop/chocolatey on Windows. +The following sections will go over how to install them, beginning with Emacs. #+BEGIN_QUOTE -If any of these install instructions are outdated, or your OS is missing, please -help us by [[https://github.com/hlissner/doom-emacs/issues/new][letting us know]] (or correcting it yourself; pull requests are -welcome). +If any of these install instructions are outdated, or instructions for your OS +is missing, [[https://github.com/hlissner/doom-emacs/issues/new/choose][let us know]] (or correct it yourself; pull requests are welcome). #+END_QUOTE ** Emacs & dependencies *** On Linux -Emacs should be available through your distribution's package manager. -Otherwise, it can be [[https://www.gnu.org/software/emacs/manual/html_node/efaq/Installing-Emacs.html][built from source]]. +In the unusual case that Emacs is unavailable through your package manager, +you'll have to [[https://www.gnu.org/software/emacs/manual/html_node/efaq/Installing-Emacs.html][build it from source]]. Otherwise: **** Arch Linux: #+BEGIN_SRC bash -pacman -S git tar clang emacs ripgrep fd +# required dependencies +pacman -S git emacs ripgrep +# optional dependencies +pacman -S clang tar fd #+END_SRC -Emacs 27 (HEAD) can be installed through [[https://aur.archlinux.org/packages/emacs-git/][emacs-git]], available on the AUR. +The above installs Emacs 26.3 (at the time of writing). If you'd prefer Emacs +27/28 (HEAD), it is available through the AUR in the [[https://aur.archlinux.org/packages/emacs-git/][emacs-git]] package. **** Ubuntu: #+BEGIN_SRC bash -apt-get install git tar clang ripgrep fd-find +# required dependencies +apt-get install git ripgrep +# optional dependencies +apt-get install tar fd-find clang #+END_SRC -On Ubuntu 18.04, the latest version of Emacs available is 25.3 (and 24.3 on -Ubuntu 16 or 14). Therefore, we have a few extra steps to install 26.1+: +Only 25.3 is available on Ubuntu 18.04 (and 24.3 on Ubuntu 14 or 16), which Doom +does not support. Extra steps are necessary to acquire 26.3: #+BEGIN_SRC bash add-apt-repository ppa:kelleyk/emacs @@ -126,161 +144,218 @@ apt-get install emacs26 #+END_SRC **** NixOS -On NixOS Emacs 26.x can be installed via ~nix-env --install emacs~, or more -permanently by adding the following entry to ~etc/nixos/configuration.nix~: +On NixOS Emacs 26.3 can be installed via ~nix-env -Ai nixos.emacs~, or +permanently with the following added to ~etc/nixos/configuration.nix~: #+BEGIN_SRC nix environment.systemPackages = with pkgs; [ - coreutils # basic GNU utilities + # required dependencies git - clang - emacs + emacs # Emacs 26.3 ripgrep + # optional dependencies + coreutils # basic GNU utilities fd + clang +]; +#+END_SRC + +To acquire Emacs 27/28+, look into [[https://github.com/nix-community/emacs-overlay/issues][nix-community/emacs-overlay]], which can be +quickly integrated into your configuration.nix with: + +#+BEGIN_SRC nix +nixpkgs.overlays = [ + (import (builtins.fetchTarball https://github.com/nix-community/emacs-overlay/archive/master.tar.gz)) +]; + +environment.systemPackages = with pkgs; [ + emacsGit ]; #+END_SRC *** On macOS -Mac users several options to install Emacs, but only a few of them are -recommended for Doom Emacs (you'll need to [[http://brew.sh/][install Homebrew]] first). To start -with: +MacOS users have many options for installing Emacs, but not all of them are well +suited to Doom. Before we get to that you'll need either the Homebrew or +MacPorts package manager installed (you only need one): + ++ [[http://brew.sh/][How to install Homebrew]] ++ [[https://www.macports.org/install.php][How to install MacPorts]] + +**** With Homebrew +First, Doom's dependencies: #+BEGIN_SRC bash -brew install git clang ripgrep fd coreutils +# required dependencies +brew install git ripgrep +# optional dependencies +brew install coreutils fd +# Installs clang +xcode-select --install #+END_SRC -As for Emacs, there are several formulas to choose from. There are the best -options, in order from most to least recommended for Doom. - -- [[https://github.com/d12frosted/homebrew-emacs-plus][emacs-plus]] (the safest option): +For Emacs itself, these three formulas are the best options, ordered from most +to least recommended for Doom (based on compatibility). +- [[https://github.com/d12frosted/homebrew-emacs-plus][emacs-plus]]: #+BEGIN_SRC bash brew tap d12frosted/emacs-plus brew install emacs-plus ln -s /usr/local/opt/emacs-plus/Emacs.app /Applications/Emacs.app #+END_SRC -- [[https://formulae.brew.sh/formula/emacs][emacs]] is another acceptable option. - - #+BEGIN_SRC bash - brew install emacs - #+END_SRC - -- [[https://bitbucket.org/mituharu/emacs-mac/overview][emacs-mac]] is also acceptable. It offers slightly better integration into - macOS, with native emojis and better childframe support. However, at the time - of writing, it [[https://github.com/railwaycat/homebrew-emacsmacport/issues/52][lacks multi-tty support]] (which impacts daemon usage). Use it if - you experience crashing or performance issues with emacs-plus. - +- [[https://bitbucket.org/mituharu/emacs-mac/overview][emacs-mac]] is another acceptable option. It offers slightly better integration + with macOS, native emojis and better childframe support. However, at the time + of writing, it [[https://github.com/railwaycat/homebrew-emacsmacport/issues/52][lacks multi-tty support]] (which impacts daemon usage): #+BEGIN_SRC bash brew tap railwaycat/emacsmacport brew install emacs-mac ln -s /usr/local/opt/emacs-mac/Emacs.app /Applications/Emacs.app #+END_SRC -**** Where *not* to install Emacs from -These builds/forks have known compatibility issues with Doom and are likely to -cause you issues later on. Do not use them: +- [[https://formulae.brew.sh/formula/emacs][emacs]] is another acceptable option, **but does not provide a Emacs.app**: + #+BEGIN_SRC bash + brew install emacs + #+END_SRC + +***** Where *not* to install Emacs from +These builds/forks have known compatibility issues with Doom and are *very +likely* to cause issues later on. Do not use them: + emacsformacosx.com + ~brew cask install emacs~ (installs from emacsformacosx.com) + AquaMacs + XEmacs +**** With MacPorts +There are four ports (at time of writing) available through MacPorts, and they +are all acceptable options: + ++ [[https://ports.macports.org/port/emacs/summary][emacs]] (26.3) and [[https://ports.macports.org/port/emacs-devel/summary][emacs-devel]] (27) -- Installs terminal-only Emacs ++ [[https://ports.macports.org/port/emacs-app/summary][emacs-app]] (26.3), [[https://ports.macports.org/port/emacs-app-devel/summary][emacs-app-devel]] (27) -- Installs GUI Emacs ++ [[https://ports.macports.org/port/emacs-mac-app/summary][emacs-mac-app]] (26.3) -- the [[https://bitbucket.org/mituharu/emacs-mac][Mitsuharu Yamamoto mac port]] + +Some of these ports do not add an =emacs= binary to your ~PATH~, which is +necessary for Doom's installation process. You'll have to do so yourself by +adding this to your shell config: + +#+BEGIN_SRC sh +# Add this to ~/.zshrc or ~/.bash_profile +export PATH="/Applications/MacPorts/Emacs.app/Contents/MacOS:$PATH" +#+END_SRC + +Or by creating a shim script at ~/usr/local/bin/emacs~: + +#+BEGIN_SRC +#!/bin/sh +/Applications/MacPorts/Emacs.app/Contents/MacOS/Emacs "$@" +#+END_SRC + *** On Windows -*Support for Windows is immature,* so your mileage will vary. Some have reported -success with installing Doom via WSL, chocolatey on git-bash or cygwin. +*Support for Windows is immature* so your mileage there will vary. Some have +reported success using Doom with WSL or WSL2. The maintainer has only (lightly) +tested installing Doom with chocolatey through [[https://gitforwindows.org/][git-bash]]. #+BEGIN_QUOTE -If you manage to get Doom on Windows and found this wasn't enough, or could be -improved, please help us expand this section! +If you manage to get Doom running on Windows and found this guide wasn't enough +or could be improved, please help us expand this section! #+END_QUOTE **** [[https://chocolatey.org/][chocolatey]] / scoop Chocolatey is the simplest to get Doom up and running with: #+BEGIN_SRC sh -choco install git llvm emacs ripgrep fd +choco install git emacs ripgrep fd llvm #+END_SRC #+begin_quote -You can also use [[https://scoop.sh/][scoop]] by simply replacing ~choco~ with ~scoop~ in the above -snippet to achieve the same result. This hasn't been tested, however. +Scoop is also a viable way of installing Emacs. However, because Emacs is a GUI +application, it is relegated to the 'extras' Scoop bucket and that will need to +be enabled. #+end_quote -You will also need to [[https://mywindowshub.com/how-to-edit-system-environment-variables-for-a-user-in-windows-10/][add a ~HOME~ system variable]], pointing to -=C:\Users\USERNAME\=, otherwise Emacs will treat -=C:\Users\USERNAME\AppData\Roaming= is your ~HOME~, which causes issues. +#+BEGIN_SRC sh +scoop enable extras +scoop install git emacs ripgrep fd llvm +#+END_SRC + +You will need [[https://mywindowshub.com/how-to-edit-system-environment-variables-for-a-user-in-windows-10/][the ~HOME~ system variable]] set to =C:\Users\USERNAME\=, otherwise +Emacs will treat =C:\Users\USERNAME\AppData\Roaming= as your ~HOME~, which +causes issues. It's also a good idea to add =C:\Users\USERNAME\.emacs.d\bin= to your ~PATH~. +#+begin_quote +A pre-existing PATH variable should already exist among your system variables. +It contains a string of file paths separated by colons; ~pathA:pathB:pathC~. +Prepend the path to bin/doom to that string: +~C:\Users\username\.emacs.d\bin:pathA:pathB:pathC~ +#+end_quote + **** TODO WSL **** TODO WSL2 ** Doom Emacs -The quickest way to get Doom up and running is: +With Emacs and Doom's dependencies installed, next is to install Doom Emacs +itself: #+BEGIN_SRC bash git clone https://github.com/hlissner/doom-emacs ~/.emacs.d ~/.emacs.d/bin/doom install #+END_SRC -=doom install= performs the following for you: - -1. It creates your =DOOMDIR= at =~/.doom.d=, if it (or =~/.config/doom=) don't - already exist. -2. Copies =~/.emacs.d/init.example.el= to =$DOOMDIR/init.el=, which contains a - ~doom!~ statement that controls what modules to enable and in what order they - are loaded. -3. Creates dummy config.el and packages.el files in ~$DOOMDIR~. -4. Optionally generates an envvar file (equivalent to using ~doom env~), which - stores your shell environment in an env file that Doom will load at startup. - *This is essential for macOS users!* -5. Installs all dependencies for enabled modules (specified by - =$DOOMDIR/init.el=), -6. And prompts to install the icon fonts required by the [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] package. +=doom install= will set up your =DOOMDIR= at =~/.doom.d= (if it doesn't already +exist) and will work you through the first-time setup of Doom Emacs. #+BEGIN_QUOTE -You'll find a break down of ~doom install~ into shell commands in the next -section. +If you'd like a more technical break down of ~doom install~, it's been +translated into shell commands below, in the "Install Doom Manually" section. #+END_QUOTE -Consider the =~/.emacs.d/bin/doom= script your new best friend. It performs a -variety of essential functions to help you manage your Doom Emacs configuration, -not least of which is installing or updating it or its plugins. If nothing else, -get to know these four commands: +*** The ~bin/doom~ utility +This utility is your new best friend. It won't spot you a beer, but it'll +shoulder much of the work associated with managing and maintaining your Doom +Emacs configuration, and then some. Not least of which is installation of and +updating Doom and your installed packages. -- ~doom refresh~: Ensures that Doom is in a proper state to be used (i.e. needed - packages are installed, orphaned packages are removed and necessary metadata - correctly generated). -- ~doom upgrade~: Updates Doom Emacs (if available) and its packages. -- ~doom env~: Generates an "envvar file", which scrapes your shell environment - into a file that is loaded by Doom Emacs at startup. This is especially - necessary for macOS users who open Emacs through an Emacs.app bundle. -- ~doom doctor~: If Doom misbehaves, the doc will diagnose common issues with - your installation and environment. If all else fails, you'll find help on - Doom's [[https://discord.gg/bcZ6P3y][Discord server]] and [[https://github.com/hlissner/doom-emacs/issues][issue tracker]]. +It exposes a variety of commands. ~bin/doom help~ will list them all, but here +is a summary of the most important ones: -Run ~doom help ~ for documentation on these commands, or ~doom help~ -for an overview of what the =bin/doom= script is capable of. ++ ~bin/doom sync~: This synchronizes your config with Doom Emacs. It ensures + that needed packages are installed, orphaned packages are removed and + necessary metadata correctly generated. Run this whenever you modify your + ~doom!~ block or =packages.el= file. ++ ~bin/doom upgrade~: Updates Doom Emacs (if available) and all its packages. ++ ~bin/doom env~: (Re)generates an "envvar file", which is a snapshot of your + shell environment that Doom loads at startup. If your app launcher or OS + launches Emacs in the wrong environment you will need this. **This is required + for GUI Emacs users on MacOS.** ++ ~bin/doom doctor~: If Doom misbehaves, the doc will diagnose common issues + with your installation, system and environment. ++ ~bin/doom purge~: Over time, the repositories for Doom's plugins will + accumulate. Run this command from time to time to delete old, orphaned + packages, and with the ~-g~ switch to compact existing package repos. + +Use ~bin/doom help~ to see an overview of the available commands that =bin/doom= +provides, and ~bin/doom help COMMAND~ to display documentation for a particular +~COMMAND~. #+begin_quote I recommend you add =~/.emacs.d/bin= to your ~PATH~ so you can call =doom= -directly, from anywhere. You don't need to be CDed into =~/.emacs.d/bin= to use -it. A quick way to do so is to add this to your .bashrc or .zshrc file: +directly and from anywhere. Accomplish this by adding this to your .bashrc or +.zshrc file: ~export PATH="$HOME/.emacs.d/bin:$PATH"~ #+end_quote *** Install Doom Manually -If you'd rather install Doom yourself, without the magic of =bin/doom install=, -here is its equivalent in bash shell commands: +If you'd rather install Doom yourself, instead of rely on the magic of =bin/doom +install=, here is its equivalent in bash shell commands (assuming +=hlissner/doom-emacs= has been cloned to =~/.emacs.d=): #+BEGIN_SRC bash -git clone https://github.com/hlissner/doom-emacs ~/.emacs.d - # So we don't have to write ~/.emacs.d/bin/doom every time -export PATH="$HOME/.emacs.d/bin:$PATH" +PATH="$HOME/.emacs.d/bin:$PATH" # Create a directory for our private config mkdir ~/.doom.d # or ~/.config/doom @@ -288,6 +363,8 @@ mkdir ~/.doom.d # or ~/.config/doom # The init.example.el file contains an example doom! call, which tells Doom what # modules to load and in what order. cp ~/.emacs.d/init.example.el ~/.doom.d/init.el +cp ~/.emacs.d/core/templates/config.example.el ~/.doom.d/config.el +cp ~/.emacs.d/core/templates/packages.example.el ~/.doom.d/packages.el # If your ISP or proxy doesn't allow you to install from # raw.githubusercontent.com, then you'll have to install straight (our package @@ -295,52 +372,57 @@ cp ~/.emacs.d/init.example.el ~/.doom.d/init.el mkdir -p ~/.emacs.d/.local/straight/repos git clone -b develop https://github.com/raxod502/straight.el ~/.emacs.d/.local/straight/repos/straight.el -# Edit ~/.doom.d/init.el and adjust the modules list to your liking before -# running this: -doom install +# You might want to edit ~/.doom.d/init.el here and make sure you only have the +# modules you want enabled. + +# Then synchronize Doom with your config: +doom sync # If you know Emacs won't be launched from your shell environment (e.g. you're # on macOS or use an app launcher that doesn't launch programs with the correct -# shell), then creating an envvar file is necessary to ensure Doom inherits your -# shell environment. +# shell) then create an envvar file to ensure Doom correctly inherits your shell +# environment. # -# If you don't know whether you need this or not, no harm in doing it anyway. -# `doom install` will prompt you to generate an envvar file. If you responded -# no, you can generate it later with the following command: +# If you don't know whether you need this or not, there's no harm in doing it +# anyway. `doom install` will have prompted you to generate one. If you +# responded no, you can generate it later with the following command: doom env -# Install the icon fonts Doom uses +# Lastly, install the icon fonts Doom uses: emacs --batch -f all-the-icons-install-fonts #+END_SRC To understand the purpose of the =~/.doom.d= directory and =~/.doom.d/init.el= -file, see the [[#customize][Customize]] section further below. +file, see the [[#configure][Configure]] section further below. -*** Alongside other Emacs configs (with Chemacs) -[[https://github.com/plexus/chemacs][Chemacs]] is a bootloader for Emacs. It makes it easy to switch between multiple +*** Install Doom alongside other configs (with Chemacs) +[[https://github.com/plexus/chemacs][Chemacs]] is a bootloader for Emacs. It allows you to switch between multiple Emacs configurations. Here is a quick guide for setting it up with Doom Emacs as -the default config. +the default config: -After you've followed the installation instructions for Doom and Emacs, outlined -above, deploy [[https://raw.githubusercontent.com/plexus/chemacs/master/.emacs][the Chemacs' startup script]] to =~/.emacs=: +1. First, install Doom somewhere: + #+BEGIN_SRC sh :eval no + git clone https://github.com/hlissner/doom-emacs ~/doom-emacs + ~/doom-emacs/bin/doom install + #+END_SRC -#+BEGIN_SRC bash -wget -O ~/.emacs https://raw.githubusercontent.com/plexus/chemacs/master/.emacs -#+END_SRC +2. Download [[https://raw.githubusercontent.com/plexus/chemacs/master/.emacs][the Chemacs' startup script]] to =~/.emacs=: + #+BEGIN_SRC bash :eval no + wget -O ~/.emacs https://raw.githubusercontent.com/plexus/chemacs/master/.emacs + #+END_SRC -#+begin_quote -*Warning: the =~/.emacs.d= directory must not exist for this to work.* -#+end_quote + #+begin_quote + *WARNING:* the =~/.emacs.d= directory must not exist for this to work. + #+end_quote -Then create =~/.emacs-profile.el= with a list of your Emacs profiles. This file -is structured like a =.dir-locals.el= file. Here is an example with Doom (as the -default), Spacemacs, and Prelude: - -#+BEGIN_SRC emacs-lisp -(("default" . ((user-emacs-directory . "~/doom-emacs"))) - ("spacemacs" . ((user-emacs-directory . "~/spacemacs"))) - ("prelude" . ((user-emacs-directory . "~/prelude")))) -#+END_SRC +3. Create =~/.emacs-profiles.el= with a list of your Emacs profiles. This file + is structured like a =.dir-locals.el= file. Here is an example with Doom (as + the default), Spacemacs, and Prelude: + #+BEGIN_SRC emacs-lisp :eval no + (("default" . ((user-emacs-directory . "~/doom-emacs"))) + ("spacemacs" . ((user-emacs-directory . "~/spacemacs"))) + ("prelude" . ((user-emacs-directory . "~/prelude")))) + #+END_SRC To start Emacs with a specific config, use the =--with-profile= option: @@ -351,43 +433,35 @@ emacs --with-profile spacemacs If no profile is specified, the =default= profile is used. ** External/system dependencies -Your system, your rules. There are as many ways to set up a programming -environment as there are dislikes on Youtube Rewind 2018, so Doom entrusts this -task to you, dear user. - Doom is comprised of modules which provide most of its features, including -language support and integration with external tools. However, some of these -have external dependencies that you must install yourself. You'll find what -modules need what and how to install them in that module's README.org file. If -you find a module without a README file, helps us out by creating one for us! - -~doom doctor~ will provide an overview of missing dependencies (only for the -modules you have enabled) by reporting which ones haven't been installed yet. -Once you know what's missing, have a look at the documentation for that module. - -Use ~M-x doom/help-modules~ (bound to =SPC h d m=) to quickly jump to a module's -documentation from inside Doom. Otherwise, check out the [[file:index.org::*Module list][Module Index]]. - -* Update -Doom is an active project and many of its 300+ plugins are in active development -as well. It is wise to occasionally update them. The following section will go -over how to do so. +language support and integration with external tools. Many of them have external +dependencies that you must install yourself. You'll find what a module needs and +how to install them in that module's README.org file or by running ~bin/doom +doctor~. #+begin_quote -*Important: you may encounter errors after up/downgrading Emacs.* Emacs bytecode -is not forward compatible, so you must recompile or reinstall your plugins to -fix this, i.e. +Use ~M-x doom/help-modules~ (bound to =SPC h d m= or =C-h d m=) to jump to a +module's documentation from within Doom, otherwise, place your cursor on a +module in your ~doom!~ block (in =~/.doom.d/init.el=) and press =K= to jump to +its documentation (or =gd= to jump to its source code). =C-c g k= and =C-c g d= +for non-evil users, respectively. -+ ~doom build~, to rebuild all your installed plugins, -+ Or delete =~/.emacs.d/.local= then ~doom refresh~ to reinstall them +Otherwise, check out the [[file:modules.org][Module Index]]. + +Keep in mind that documentation is an ongoing effort. Some modules may not have +README.org files yet. #+end_quote -** Doom -The =bin/doom= script provides a simple command for upgrading Doom (which will -also update your plugins): +* Update & Rollback +Doom is an active project and many of its 300+ packages are in active +development as well. It is wise to occasionally update. Doom strives to make +this as painless a process as possible. -#+BEGIN_SRC bash -doom upgrade # short version: doom up +The =bin/doom= script provides one simple command for upgrading Doom and your +packages: + +#+BEGIN_SRC bash :eval no +doom upgrade # or 'doom up' #+END_SRC If you want to update Doom manually, ~doom upgrade~ is equivalent to: @@ -395,45 +469,106 @@ If you want to update Doom manually, ~doom upgrade~ is equivalent to: #+BEGIN_SRC bash cd ~/.emacs.d git pull # updates Doom -doom refresh # refreshes plugins & autoloads -doom update # updates installed plugins +doom clean # Ensure your config isn't byte-compiled +doom sync # synchronizes your config with Doom Emacs +doom update # updates installed packages #+END_SRC +To upgrade only your packages (and not Doom itself): + +#+BEGIN_SRC bash +doom upgrade --packages +#+END_SRC + +#+begin_quote To minimize issues while upgrading, avoid modifying Doom's source files. All -your customization should be kept in your =DOOMDIR= (typically, =~/.doom.d=). -Read the [[#customize][Customize]] section for more on configuring Doom. +your customization should be kept in your =DOOMDIR= (e.g. =~/.doom.d=). Read the +[[#Configure][Configure]] section for more on configuring Doom. +#+end_quote -** Plugins -To update /only/ your plugins (i.e. not Doom), run ~doom update~ (short version: -~doom u~). - -** Rollback +** TODO Rollback The =bin/doom= script doesn't currently offer rollback support for Doom or its -plugins (yet). +packages (yet). -* Customize -Your private configuration is located in =~/.doom.d=, by default (if -=~/.config.d/doom= exists, that will be used instead). This directory is -referred to as your ~$DOOMDIR~ or your "private module". +** Up/Downgrading Emacs +*Important: you may encounter errors after up/downgrading Emacs.* Emacs bytecode +is generally not forward compatible. You will have to recompile or reinstall +your packages to fix this, i.e. -~doom install~ will create three files in your DOOMDIR to start you off: ++ ~doom build~, to rebuild all your installed packages, ++ Or delete =~/.emacs.d/.local= then ~doom sync~ to reinstall them -+ init.el :: This is where you'll find your ~doom!~ block, which controls what - modules are enabled and in what order they are loaded. This is copied from - =~/.emacs.d/init.example.el=. -+ config.el :: This is where the bulk of your private configuration will go. -+ packages.el :: This is where you tell Doom what packages you want to install - and where from. +* TODO Migrate +If you're here from another Emacs distribution (or your own), here are a few +things to be aware of while you convert your old config to Doom: -** How to enable or disable modules -Every private config starts with a ~doom!~ block, found in =$DOOMDIR/init.el=. -If you followed the Doom installation instructions and ran ~doom install~, this -file should exist and will contain one. ++ Doom does not use =package.el= to manage its packages, but ~use-package~ does! + You will see errors if you have ~:ensure ...~ properties in your ~use-package~ + blocks. Remove these and, instead, add ~package!~ declarations to + =~/.doom.d/packages.el= to install your packages. -This block controls what modules are enabled and in what order they are loaded. -To enable a module, add it to this list. To disable it, either remove it or -comment it out (in Emacs Lisp, anything following a semicolon is ignored by the -Elisp interpreter; i.e. it's "commented out"). + See [[*Package management]["Package Management"]], earlier in this guide. + +(This section is incomplete) + +** TODO From vanilla Emacs +#+begin_quote +Have you migrated from your own config? Help me flesh out this section by +letting me know what kind of hurdles you faced in doing so. You'll find me [[https://discord.gg/qvGgnVx][on +our Discord server]]. +#+end_quote + +** TODO From Spacemacs +#+begin_quote +Have you migrated from Spacemacs? Help me flesh out this section by letting me +know what kind of hurdles you faced in doing so. You'll find me [[https://discord.gg/qvGgnVx][on our Discord +server]]. +#+end_quote + +* Configure +Doom looks for your private configuration in: + +1. =$XDG_CONFIG_HOME/doom= +2. or =~/.doom.d= + +This directory is referred to as your =DOOMDIR=. + +#+begin_quote +You can override the location of your =DOOMDIR= by changing the environment +variable of the same name. Symlinks will work as well. +#+end_quote + +~doom install~ will deploy three files to your =DOOMDIR=: + ++ init.el :: Where you'll find your ~doom!~ block, which controls what Doom + modules are enabled and in what order they will be loaded. + + This file is evaluated early in the startup process, before any other module + has loaded. ++ config.el :: Where 99.99% of your private configuration should go. Anything + put here will run /after/ all other modules have loaded. ++ packages.el :: Where you declare what packages to install and where from. + +#+begin_quote +Note: do not use ~M-x customize~ or the customize API in general. Doom is +designed to be configured programmatically from your config.el, which can +conflict with Customize's way of writing variables to ~custom-file~. + +Doom provides the ~setq!~ macro for triggering ~defcustom~ setters. +#+end_quote + +** Modules +Doom consists of around 130 modules. A Doom module is a bundle of packages, +configuration and commands, organized into a unit that can be enabled or +disabled by adding or removing them from your ~doom!~ block (found in +=$DOOMDIR/init.el=). + +#+begin_quote +If =$DOOMDIR/init.el= doesn't exist, you haven't installed Doom yet. See [[*Install][the +"Install" section]] above. +#+end_quote + +Your ~doom!~ block will look something like this: #+BEGIN_SRC emacs-lisp ;; To comment something out, you insert at least one semicolon before it. The @@ -446,43 +581,51 @@ Elisp interpreter; i.e. it's "commented out"). php) ; this module is enabled #+END_SRC -Some modules have optional features that can be enabled by passing them flags -like so: +It controls what modules are enabled and in what order they are loaded. Some +modules have *optional features* that can be enabled by passing them flags, +denoted by a plus prefix: #+BEGIN_SRC emacs-lisp (doom! :completion (company +auto) :lang (csharp +unity) - (org +attach +babel +capture +export +present +protocol) + (org +brain +dragndrop +gnuplot +hugo +jupyter) (sh +fish)) #+END_SRC -Different modules support different flags. To see a quick list of what modules -support what flags in [[file:index.org::*Module list][the Module Index]]. - -*WARNING:* when changing your ~doom!~ block you *must* run =~/.emacs.d/bin/doom -refresh= and restart Emacs for the changes to take effect. This ensures the -needed packages are installed, orphaned packages are removed, and necessary -metadata for your Doom Emacs config has been generated. - -** Package management -Doom's package manager is declarative. Your ~DOOMDIR~ is a module, and modules -may optionally possess a packages.el file, where you may declare what packages -you want to install (and where from) using the ~package!~ macro. It can be used -to: - -1. Install packages (conditionally, even), -2. Disable packages (uninstalling them and disabling their configuration), -3. Or change where a package is installed from. - -If a package is installed via ELPA and does not have a ~package!~ declaration, -Doom will assume the package is unwanted and uninstall it for you next time -~doom refresh~ is executed. +Different modules support different flags. Flags that a module doesn't recognize +will be silently ignored. You'll find a comprehensive list of available modules +and their supported flags summarized in [[file:index.org::*Module list][the Module Index]]. #+begin_quote -Remember to run ~doom refresh~ after modifying your packages, to ensure they are -installed and properly integrated into Doom. +*IMPORTANT:* don't forget to run =bin/doom sync= after changing your ~doom!~ +block, then restart Emacs for the changes to take effect. +#+end_quote + +#+begin_quote +Run ~doom doctor~ to determine if there are any issues with your ~doom!~ block, +such as duplicate or misspelled modules. +#+end_quote + +** Package management +**Doom Emacs does not use package.el** (the package manager built into Emacs). +Instead, it uses its own declarative package manager built on top of [[https://github.com/raxod502/straight.el][Straight]]. + +#+begin_quote +If you are coming from another Emacs distro (or vanilla Emacs), be wary of the +~:ensure~ property in ~use-package~ blocks, because it will attempt (and fail) +to install packages through package.el. +#+end_quote + +Packages are declared in ~packages.el~ files located in Doom's modules. This +applies to your ~DOOMDIR~ as well, which is considered a module. You can install +your own packages in =~/.doom.d/packages.el=. + +#+begin_quote +If a package is installed without an accompanying ~package!~ declaration (e.g. +with ~M-x package-install~ or ~M-x straight-use-package~), it will be +uninstalled the next time you run ~bin/doom sync~ or ~bin/doom purge~. #+end_quote *** Installing packages @@ -493,22 +636,13 @@ To install a package, add a ~package!~ declaration for it to ;; Install a package named "example" from ELPA or MELPA (package! example) -;; Tell Doom to install it from a particular archive (e.g. elpa). By default, it -;; will search orgmode.org and melpa.org before searching elpa.gnu.org. See -;; `package-archives' to adjust this order (or to see what values :pin will -;; accept). -(package! example :pin "elpa") - -;; Instruct Doom to install this package once, but never update it when you run -;; `doom update` or `doom upgrade`: -(package! example :freeze t) - ;; Or tell Doom to not manage a particular package at all. (package! example :ignore t) #+END_SRC -~package!~ will return non-nil if the package isn't disabled and is cleared for -install. Use this fact to conditionally install other packages, e.g. +~package!~ will return non-nil if the package is cleared for install and hasn't +been disabled elsewhere. Use this fact to chain package dependencies together. +e.g. #+BEGIN_SRC elisp (when (package! example) @@ -523,33 +657,76 @@ Here are a few examples: #+BEGIN_SRC elisp ;; Install it directly from a github repository. For this to work, the package -;; must have an appropriate .el and must have at least a Package-Version -;; or Version line in its header. -(package! example :recipe (:host github :repo "username/my-example-fork")) +;; must have an appropriate PACKAGENAME.el file which must contain at least a +;; Package-Version or Version line in its header. +(package! example + :recipe (:host github :repo "username/my-example-fork")) -;; If the source files for a package are in a subdirectory in said repo, you'll -;; need to specify what files to pull in. +;; If the source files for a package are in a subdirectory in said repo, use +;; `:files' to target them. (package! example :recipe (:host github :repo "username/my-example-fork" :files ("*.el" "src/lisp/*.el"))) -;; To grab a particular commit: +;; To grab a particular branch or tag: (package! example :recipe (:host gitlab :repo "username/my-example-fork" :branch "develop")) ;; If a package has a default recipe on MELPA or emacsmirror, you may omit -;; keywords and the recipe will inherit from their original. +;; keywords and the recipe will inherit the rest of the recipe from their +;; original. (package! example :recipe (:branch "develop")) ;; If the repo pulls in many unneeded submodules, you can disable recursive cloning (package! example :recipe (:nonrecursive t)) + +;; A package can be installed straight from a git repo by setting :host to nil: +(package! example + :recipe (:host nil :repo "https://some/git/repo")) +#+END_SRC + +The specification for the ~package!~ macro's ~:recipe~ is laid out [[https://github.com/raxod502/straight.el#the-recipe-format][in +Straight.el's README]]. + +#+begin_quote +*IMPORTANT:* Run ~bin/doom sync~ whenever you modify packages.el files to +ensure your changes take effect. +#+end_quote + +*** Pinning packages to specific commits +All of Doom's packages are pinned by default. A pinned package is a package +locked to a specific commit, like so: + +#+BEGIN_SRC elisp +(package! evil :pin "e00626d9fd") +#+END_SRC + +To unpin a package, use the ~unpin!~ macro: + +#+BEGIN_SRC elisp +(unpin! evil) + +;; It can be used to unpin multiple packages at once +(unpin! evil helm org-mode) + +;; Or to unpin all packages in modules +(unpin! (:lang python ruby rust) (:tools docker)) + +;; Or to unpin an entire category of modules +(unpin! :completion :lang :tools) +#+END_SRC + +To unpin all packages and make Doom Emacs rolling release, use + +#+BEGIN_SRC elisp +(unpin! t) #+END_SRC *** Disabling packages -The ~package!~ macro possesses a ~:disable~ property. +The ~package!~ macro possesses a ~:disable~ property: #+BEGIN_SRC emacs-lisp (package! irony :disable t) @@ -557,38 +734,83 @@ The ~package!~ macro possesses a ~:disable~ property. #+END_SRC Once a package is disabled, ~use-packages!~ and ~after!~ blocks for it will be -ignored, and the package will be removed the next time you run ~doom refresh~. -Use this to disable undesirable packages included with the built-in modules. +ignored, and the package is removed the next time you run ~bin/doom sync~. Use +this to disable Doom's packages that you don't want or need. -Alternatively, the ~disable-packages!~ macro exists for more concisely disabling -multiple packages: +There is also the ~disable-packages!~ macro for conveniently disabling multiple +packages: #+BEGIN_SRC elisp (disable-packages! irony rtags) #+END_SRC -*** Changing a built-in recipe for a package -If a module installs package X, but you'd like to install it from somewhere else -(say, a superior fork or a fork with a bugfix), simple add a ~package!~ -declaration for it in your =DOOMDIR/packages.el=. Your private declarations -always have precedence over modules (even your own modules). +#+begin_quote +*IMPORTANT:* Run ~bin/doom sync~ whenever you modify packages.el files to +ensure your changes take effect. +#+end_quote + +*** Changing a recipe for a included package +If a Doom module installs package X from one place, but you'd like to install it +from another (say, a superior fork), add a ~package!~ declaration for it in your +=DOOMDIR/packages.el=. Your private declarations always have precedence over +modules (even your own). #+BEGIN_SRC elisp -;; modules/editor/evil/packages.el +;; in modules/editor/evil/packages.el (package! evil) ; installs from MELPA -;; DOOMDIR/packages.el +;; in DOOMDIR/packages.el (package! evil :recipe (:host github :repo "username/my-evil-fork")) #+END_SRC -You will need to run ~doom refresh~ for this change to take effect. +To install a package only if a built-in package doesn't exist, use ~:built-in +'prefer~: -*** TODO Using/loading local packages +#+BEGIN_SRC elisp +(package! so-long :built-in 'prefer) +#+END_SRC + +#+begin_quote +*IMPORTANT:* Run ~bin/doom sync~ whenever you modify packages.el files to +ensure your changes take effect. +#+end_quote + +*** Using/loading local packages +Say you have a local elisp package you want to install. You have two options: + +**** Adjust your ~load-path~ +Emacs searches for packages in your ~load-path~. Add the path to your package +and Emacs will find it when it tries to load it. e.g. + +#+BEGIN_SRC elisp +(add-load-path! "lisp/package") + +;; or + +(use-package my-package + :load-path "/path/to/my/package") +#+END_SRC + +**** :local-repo +Alternatively, you can specify a ~:local-repo~ in a ~package!~'s ~:recipe~ +declaration: + +#+BEGIN_SRC elisp +(package! my-package :recipe (:local-repo "/path/to/my/package")) + +;; Don't forget to use :files to include files in an unconventional project structure: +(package! my-package + :recipe (:local-repo "/path/to/my/package" + :files ("*.el" "src/lisp/*.el"))) +#+END_SRC + +Remember to run ~doom sync~ to rebuild your package after you've changed it, and +to re-index any autoloads in it. ** Configuring Doom *** Configuring packages If your configuration needs are simple, the ~use-package!~, ~after!~, -~add-hook!~ and ~setq-hook!~ emacros can help you reconfigure packages: +~add-hook!~ and ~setq-hook!~ macros are your bread and butter. #+BEGIN_SRC emacs-lisp ;;; ~/.doom.d/config.el (example) @@ -676,11 +898,30 @@ also be helpful for debugging. + unmap! + define-key! -*** TODO DOOMDIR file structure - ** Writing your own modules -Modules are made up of several files, all of which are optional. This is a -comprehensive list of what they are: +*** Load order +Module files are loaded in a precise order: + +1. =~/.emacs.d/early-init.el= (Emacs 27+ only) +2. =~/.emacs.d/init.el= +3. =$DOOMDIR/init.el= +4. ={~/.emacs.d,$DOOMDIR}/modules/*/*/init.el= +5. ={~/.emacs.d,$DOOMDIR}/modules/*/*/config.el= +6. =$DOOMDIR/config.el= + +*** Location +Doom searches for modules in =~/.emacs.d/modules/CATEGORY/MODULE/= and +=$DOOMDIR/modules/CATEGORY/MODULE/=. If you have a private module with the same +name as an included Doom module, yours will shadow the included one (as if the +included one never existed). + +#+begin_quote +Doom refers to modules in one of two formats: ~:category module~ or +~category/module~. +#+end_quote + +*** File structure +A module consists of several files, all of which are optional. They are: #+begin_example modules/ @@ -695,18 +936,13 @@ modules/ doctor.el #+end_example -By default, doom looks for modules in two places: =.emacs.d/modules/= where doom's -own modules are located and =$DOOMDIR/modules/= where you can define your -own private modules. -*** Structure of a module **** =init.el= -This file is loaded first, before anything else, but after Doom core is loaded. +This file is loaded early, before anything else, but after Doom core is loaded. Use this file to: -+ Configure Emacs or perform setup/teardown operations that must be set before - other modules are (or this module is) loaded. Tampering with ~load-path~, for - instance. ++ Configure Emacs or perform setup/teardown operations that must be set early; + before other modules are (or this module is) loaded. + Reconfigure packages defined in Doom modules with ~use-package-hook!~ (as a last resort, when ~after!~ and hooks aren't enough). + To change the behavior of ~bin/doom~. @@ -722,9 +958,9 @@ This file is the heart of every module. Code in this file should expect that dependencies (in =packages.el=) are installed and available, but shouldn't make assumptions about what /modules/ are -activated (use ~featurep!~ for this). +activated (use ~featurep!~ to detect them). -Packages should be configured using ~after!~ or ~use-package!~. +Packages should be configured using ~after!~ or ~use-package!~: #+BEGIN_SRC emacs-lisp ;; from modules/completion/company/config.el @@ -756,36 +992,7 @@ shouldn't produce side effects and should be deterministic. Because this file gets evaluated in an environment isolated from your interactive session, code within should make no assumptions about the current session. -The ~package!~ macro is the star of the show in =packages.el= files: - -#+BEGIN_SRC emacs-lisp -;; from modules/lang/org/packages.el -(package! org-bullets) - -;; from modules/tools/rotate-text/packages.el -(package! rotate-text :recipe (:host github :repo "debug-ito/rotate-text.el")) -#+END_SRC - -Its ~:recipe~ property accepts [[https://github.com/melpa/melpa#recipe-format][a MELPA recipe]], which provides a lot of control -over where to fetch a package, including specific commit, tags or branches: - -#+BEGIN_SRC emacs-lisp -(package! rotate-text - :recipe (:host github - :repo "debug-ito/rotate-text.el" - :commit "1a2b3c4d")) -#+END_SRC - -You can also use this ~package!~ to disable other packages: - -#+BEGIN_SRC emacs-lisp -;; Uninstalls evil, keeps it uninstalled, and tells Doom to ignore any -;; use-package! and after! blocks for it -(package! evil :disable t) - -;; disable-packages! can be used to disable multiple packages in one statement -(disable-packages! evil evil-snipe evil-escape) -#+END_SRC +See the "[[*Package management][Package Management]]" section for details. **** =autoload/*.el= OR =autoload.el= Functions marked with an autoload cookie (~;;;###autoload~) in these files will @@ -846,72 +1053,61 @@ These additional files are *not* loaded automatically. You will need to use the The ~load!~ macro will try to load a =+git.el= relative to the current file. -*** Load order -Module files are loaded in a precise order: +*** Flags +A module flag is an arbitrary symbol. By convention, these symbols are prefixed +with a ~+~ or a ~-~, to respectively denote the addition or removal of a +feature. There is no functional significance to this notation. -#+BEGIN_SRC sh -~/.emacs.d/early-init.el # in Emacs 27+ only -~/.emacs.d/init.el -$DOOMDIR/init.el -{~/.emacs.d,$DOOMDIR}/modules/*/*/init.el -{~/.emacs.d,$DOOMDIR}/modules/*/*/config.el -$DOOMDIR/config.el +A module may choose to interpret flags however it likes. They can be tested for +with the ~featurep!~ macro: + +#+BEGIN_SRC elisp +;; Has the current module been enabled with the +my-feature flag? +(when (featurep! +my-feature) ...) + +;; It can be used to check the presence of flags in other modules: +(when (featurep! :lang python +lsp) ...) #+END_SRC -*** Module flags -In the code examples of the previous section, you may have noticed something odd -about that haskell entry: ~(haskell +intero)~. ~+intero~ is a module flag. You -may specify these for any module that supports them. Unsupported flags are -ignored. +*** Module cookies +A special syntax exists called module cookies. Like autoload cookies +(~;;;###autoload~), module files may have ~;;;###if FORM~ at or near the top of +the file. FORM is read determine whether or not to ignore this file when +scanning it for autoloads (~doom sync~) or byte-compiling it (~doom compile~). -You can find out what flags a module supports by looking at its documentation (a -README.org in the module's directory; which can be jumped to quickly with ~M-x -doom/describe-module~). - -For example, the haskell module supports the ~+intero~ and ~+dante~ flags, which -represent the two Haskell backends available to Emacs. You may choose one or the -other (or neither, or both) by specifying the appropriate flags in you ~doom!~ -block: +Use this to prevent errors that may occur if that file contains (for example) +calls to functions that won't exist if a certain feature isn't available to that +module, e.g. #+BEGIN_SRC emacs-lisp -(doom! :lang (haskell +dante)) +;;;###if (featurep! +lsp) #+END_SRC -You may specify as many flags are you like: - #+BEGIN_SRC emacs-lisp -(doom! :lang (org +attach +babel +capture +export +present)) +;;;###if (not (locate-library "so-long")) #+END_SRC -#+begin_quote -=+flagname= is simply a naming convention and has no syntactical or functional -significance. -#+end_quote +Remember that these run in a limited, non-interactive sub-session, so do not +call anything that wouldn't be available in a Doom session without any modules +enabled. -**** Testing for flags -Modules are free to interpret flags however they like. If you are writing your -own module(s), you can test for flags using the ~featurep! MODULE SUBMODULE -&optional FLAG~ macro: +*** Autodefs +An autodef is a special kind of autoloaded function or macro which Doom +guarantees will always be defined, whether or not its containing module is +enabled (but will no-op without evaluating its arguments when it is disabled). -#+BEGIN_SRC emacs-lisp -(when (featurep! :lang haskell +dante) - [...]) +You can browse the available autodefs in your current session with ~M-x +doom/help-autodefs~ (=SPC h d u= or =C-h d u=). + +What distinguishes an autodef from a regular autoload is the ~;;;###autodef~ +cookie: + +#+BEGIN_SRC elisp +;;;###autodef +(defun set-something! (value) + ...) #+END_SRC -The first two arguments if ~featurep!~ may be skipped if it is used from inside -a module. For example: - -#+BEGIN_SRC emacs-lisp -;; In modules/lang/haskell/config.el -(when (featurep! +dante) ; same as (featurep! :lang haskell +dante) - [...]) -#+END_SRC - -*** Module settings -Some modules expose settings that can be configured from other modules. Use ~M-x -doom/help-autdefs~ (=SPC h d a= or =C-h d a=) to see what is available and how -to use them. - An example would be the ~set-company-backend!~ function that the =:completion company= module exposes. It lets you register company completion backends with certain major modes. For instance: @@ -920,30 +1116,6 @@ certain major modes. For instance: (set-company-backend! 'python-mode '(company-anaconda)) #+END_SRC -You'll find what settings a module exposes in its documentation (remember to use -~M-x doom/help-modules~ on =SPC h d m= or =C-h d m=). -*** Module cookies -There is a special syntax available to module files called module cookies. Like -autoload cookies (~;;;###autoload~), module files may have ~;;;###if FORM~ at or -near the top of the file. FORM is read by ~doom refresh~ and ~doom compile~ to -determine whether or not to ignore this file. - -If FORM returns nil, the file won't be scanned for autoloads nor will it be -byte-compiled. Use this to prevent errors that may occur if that file contains -(for example) calls to functions that won't exist if a certain feature isn't -available to that module, e.g. - -#+BEGIN_SRC emacs-lisp -;;;###if (featurep! +intero) -#+END_SRC - -#+BEGIN_SRC emacs-lisp -;;;###if (not (featurep 'evil-mode)) -#+END_SRC - -Remember that these run in a limited, non-interactive sub-session, so do not -call anything that wouldn't be available in a Doom session without any modules -enabled. ** Common mistakes when configuring Doom Emacs Having helped many users configure Doom, I've spotted a few recurring oversights that I will list here, in the hopes that it will help you avoid the same @@ -952,7 +1124,7 @@ mistakes: *** Packages are eagerly loaded Using ~use-package!~ without a deferring keyword (one of: ~:defer :after :commands :defer-incrementally :after-call~) will load the package immediately. -This can cause other packages to be pulled in and loaded, which will compromise +This causes other packages to be pulled in and loaded, which will compromise many of Doom's startup optimizations. This is usually by accident. Choosing which keyword to use depends on the @@ -966,10 +1138,10 @@ has its own package management system. Migrating ~use-package~ code to Doom is usually a case of removing the ~:ensure~ keyword and adding a ~(package! PACKAGENAME)~ to =~/.doom.d/packages.el= (and -running ~doom refresh~ to sync your config). +running ~doom sync~ to sync your config). -*** Using ~org-babel-do-load-languages~ to load your babel plugins -You don't need ~org-babel-do-load-languages~. Doom lazy loads babel plugins +*** Using ~org-babel-do-load-languages~ to load your babel packages +You don't need ~org-babel-do-load-languages~. Doom lazy loads babel packages based on the language name in ~#+BEGIN_SRC~ blocks needed. As long as the babel plugin is installed and the plugin is named after its language (e.g. ~#+BEGIN_SRC rust~ will load ~ob-rust~), you don't need to do anything else. @@ -1015,31 +1187,47 @@ imposing than its alternatives: If you use it, it's there. If you don't, it isn't written to the file. * Troubleshoot -When problems arise, and they will, you will need to debug them. Fortunately, -Emacs (and Doom) provide you with tools to make this easier. I recommend -becoming acquainted with them. They will be yours (and our) best tool for -understanding the problem. +When problems arise, you should be prepared to collect information in order to +solve them, or for the bug report you're about to write. Both Emacs and Doom +provide tools to make this easier. Here are a few things you can try, first: -** I've run into an issue, where do I start? -Before you file a bug report, there are a number of things you should try first: ++ Investigate the =*Messages*= log for warnings or error messages. This log can + be opened with =SPC h e=, =C-h e= or =M-x view-echo-area-messages=. -+ You'll find [[file:faq.org::Common%20Issues][a list of common issues & errors in the FAQ]]. That is a good place - to start. You can access and search this FAQ from inside Doom with =SPC h d f= - (or =C-h d f= for non-evil users). ++ Look up errors/warnings [[file:faq.org::Common%20Issues][on the FAQ]] and [[https://github.com/hlissner/doom-emacs/issues][Doom's issue tracker]]. It is possible + that a solution for your issue already exists. The FAQ can be searched from + inside Doom with =SPC h d f= (or =C-h d f= for non-evil users). -+ Run ~doom doctor~ to diagnose any common issues with your environment or - config. ++ Run ~bin/doom doctor~ on the command line to diagnose common issues with your + environment and config. It will suggest solutions for them as well. -+ Run ~doom refresh~ to ensure the problem isn't caused by missing packages or - outdated autoloads files. ++ ~bin/doom clean~ will ensure the problem isn't stale bytecode in your private + config or Doom core. If you haven't used ~bin/doom compile~, there's no need + to do this. -+ See if your issue is mentioned in the Common Issues section below. ++ ~bin/doom sync~ will ensure the problem isn't missing packages or outdated + autoloads files -+ Search Doom's issue tracker to see if your issue is mentioned there. ++ ~bin/doom build~ will ensure the problem isn't stale package bytecode or + broken symlinks. -+ Ask for help on [[https://discord.gg/bcZ6P3y][our Discord server]]. This may not be immediately available to - everyone, so I won't fault you for skipping this step, but you'll sometimes - find help there quicker. In many cases, Henrik fixes issues. ++ ~bin/doom update~ will ensure that your packages are up-to-date, eliminating + issues that originate from upstream. + ++ If you happen to know what module(s) are relevant to your issue, check their + documentation (press = h m= to jump to a module's documentation). Your + issue may be documented. + ++ If possible, see if the issue can be reproduced in vanilla Emacs (Emacs + without Doom) and/or vanilla Doom (Doom without your private config). [[*Use the sandbox][Doom's + sandbox can help you check]]. + ++ Ask for help on [[https://discord.gg/qvGgnVx][our Discord server]]. It is the quickest way to get help, + sometimes straight from Doom's maintainer, who is very active there. + +If none of these things have helped you, then it's time to open a bug report. +See "[[file:contributing.org::*Reporting issues][Reporting Issues]]" in the [[file:contributing.org][contributing guidelines]] on how to file an +effective bug report. ** Looking up documentation and state from within Emacs ... @@ -1080,7 +1268,7 @@ You can also evaluate code with ~eval-expression~ (=M-;= or =SPC ;=). ** How to extract a backtrace from an error If you encounter an error while using Doom Emacs, you're probably about to head -off and file a bug report (or request help on [[https://discord.gg/bcZ6P3y][our Discord server]]). Before you +off and file a bug report (or request help on [[https://discord.gg/qvGgnVx][our Discord server]]). Before you do, please generate a backtrace to include with it. To do so you must enable ~debug-on-error~ then recreate the error. @@ -1102,7 +1290,7 @@ same command with the ~-d~ or ~--debug~ switches to force it to emit a backtrace when an error occurs. The ~DEBUG~ environment variable will work to. #+BEGIN_SRC sh -doom -d refresh +doom -d sync doom --debug install DEBUG=1 doom update #+END_SRC @@ -1129,7 +1317,7 @@ all of it, or somewhere in between). This can be helpful for isolating bugs to determine who you should report a bug to. If you can recreate a bug in vanilla Emacs than it should be reported to the -developers of the relevant plugins or, perhaps, the Emacs devs themselves. +developers of the relevant packages or, perhaps, the Emacs devs themselves. Otherwise, it is best to bring it up on the Doom Emacs issue list, rather than confusing and inundating the Emacs community with Doom-specific issues. diff --git a/docs/index.org b/docs/index.org index eadfd4f4c..d035eb6e6 100644 --- a/docs/index.org +++ b/docs/index.org @@ -1,16 +1,20 @@ #+TITLE: Doom Emacs Documentation #+STARTUP: nofold -Doom Emacs is a configuration for [[https://www.gnu.org/software/emacs/][GNU Emacs]] written by a stubborn, -shell-dwelling, and melodramatic ex-vimmer. It is designed to be a foundation -for your own Emacs configuration or a resource for enthusiasts to learn more -about our favorite OS. +Doom is a configuration framework for [[https://www.gnu.org/software/emacs/][GNU Emacs 26.3+]] tailored for Emacs +bankruptcy veterans who want less framework in their frameworks and the +performance of a hand rolled config (or better). It can be a foundation for your +own config or a resource for Emacs enthusiasts to learn more about our favorite +OS. + +Doom is an opinionated collection of reasonable (and optional) defaults with a +focus on performance (both runtime and startup) and on abstraction-light, +readable code design, so that there is less between you and Emacs. #+begin_quote -Github fails to render org links to sub-sections, so it is recommended that you -view the documentation from within Doom Emacs by pressing = d h= (== -is =SPC h= for evil users and =C-h= for vanilla users) or searching it with -= d /=. +The documentation is designed to be viewed within Doom Emacs. Access it by +pressing =SPC h d h= (or =C-h d h= for non-evil users), or search it with =SPC h +d s= (or =C-h d s=). #+end_quote * Table of Contents :TOC: @@ -20,53 +24,47 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with - [[#frequently-asked-questions][Frequently Asked Questions]] - [[#contributing][Contributing]] - [[#workflow-tips-tricks--tutorials][Workflow Tips, Tricks & Tutorials]] + - [[#module-appendix][Module Appendix]] - [[#community-resources][Community Resources]] - [[#asking-for-help][Asking for help]] - [[#project-roadmap][Project roadmap]] - [[#tutorials--guides][Tutorials & guides]] -- [[#module-list][Module list]] - - [[#app][:app]] - - [[#completion][:completion]] - - [[#config][:config]] - - [[#editor][:editor]] - - [[#emacs][:emacs]] - - [[#email][:email]] - - [[#input][:input]] - - [[#lang][:lang]] - - [[#term][:term]] - - [[#tools][:tools]] - - [[#ui][:ui]] + - [[#projects-that-supportcompliment-doom][Projects that support/compliment Doom]] + - [[#similar-projects][Similar projects]] * TODO Release Notes * Documentation ** [[file:getting_started.org][Getting Started]] -- [[file:getting_started.org::*Install][Install]] - How to install Emacs, Doom and its plugins -- [[file:getting_started.org::*Update][Update]] - Keep Doom and its packages up-to-date -- [[file:getting_started.org::*Customize][Customize]] - A primer on customizing and reconfiguring Doom -- [[file:getting_started.org::*Troubleshoot][Troubleshoot]] - How to debug Emacs & Doom, find help or look up documentation +- [[file:getting_started.org::*Install][Install]] +- [[file:getting_started.org::*Update & Rollback][Update & Rollback]] +- [[file:getting_started.org::*Configure][Configure]] +- [[file:getting_started.org::*Migrate][Migrate]] +- [[file:getting_started.org::*Troubleshoot][Troubleshoot]] ** [[file:faq.org][Frequently Asked Questions]] - [[file:faq.org::*General][General]] - [[file:faq.org::*Configuration][Configuration]] +- [[file:faq.org::*Package Management][Package Management]] - [[file:faq.org::*Defaults][Defaults]] - [[file:faq.org::Common Issues][Common Issues]] - [[file:faq.org::Contributing][Contributing]] ** TODO [[file:contributing.org][Contributing]] -- [[file:contributing.org::*Where can I help?][Where to get help]] -- Writing an effective bug report -- Suggesting features, keybinds or enhancements -- Contributing code -- Contributing documentation +- [[file:contributing.org::*Where can I help?][Where to get help?]] +- Reporting issues +- Suggesting features, keybinds and enhancements +- Contributing code or documentation - Other ways to support Doom Emacs - Special thanks ** TODO [[file:workflow.org][Workflow Tips, Tricks & Tutorials]] +** [[file:modules.org][Module Appendix]] + * Community Resources ** Asking for help -- [[https://discord.gg/bcZ6P3y][Our Discord server]] +- [[https://discord.gg/qvGgnVx][Our Discord server]] - [[https://github.com/hlissner/doom-emacs/issues][Our issue tracker]] ** Project roadmap @@ -79,11 +77,12 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with ** Tutorials & guides + *Doom Emacs* + - (video) [[https://www.youtube.com/watch?v=dr_iBj91eeI][Doom Emacs - Getting Started by DistroTube]] + - (video) [[https://www.youtube.com/playlist?list=PLhXZp00uXBk4np17N39WvB80zgxlZfVwj][DoomCasts]] - [[https://noelwelsh.com/posts/2019-01-10-doom-emacs.html][Noel's crash course on Doom Emacs]] - [[https://medium.com/@aria_39488/getting-started-with-doom-emacs-a-great-transition-from-vim-to-emacs-9bab8e0d8458][Getting Started with Doom Emacs -- a great transition from Vim to Emacs]] - [[https://medium.com/@aria_39488/the-niceties-of-evil-in-doom-emacs-cabb46a9446b][The Niceties of evil in Doom Emacs]] - - [[https://www.youtube.com/playlist?list=PLhXZp00uXBk4np17N39WvB80zgxlZfVwj][DoomCasts (youtube series)]] - - [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]] + - (video) [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]] + *Emacs & Emacs Lisp* - [[https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html][The Official Emacs manual]] - A variety of Emacs resources - https://github.com/ema2159/awesome-emacs @@ -92,186 +91,16 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with - http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html - Workflows for customizing Emacs and its packages (and its C/C++ modes): - https://david.rothlis.net/emacs/customize_c.html -+ *Tools in Emacs* - - [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]] + - *Tools in Emacs* + - [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]] + *Vim & Evil* - [[https://gist.github.com/dmsul/8bb08c686b70d5a68da0e2cb81cd857f][A crash course on modal editing and Ex commands]] -* Module list -** :app -Application modules are complex and opinionated modules that transform Emacs -toward a specific purpose. They may have additional dependencies and should be -loaded last, before =:config= modules. +** Projects that support/compliment Doom ++ [[https://github.com/plexus/chemacs][plexus/chemacs]] ++ [[https://github.com/r-darwish/topgrade][r-darwish/topgrade]] -+ [[file:../modules/app/calendar/README.org][calendar]] - TODO -+ [[file:../modules/app/irc/README.org][irc]] - how neckbeards socialize -+ rss =+org= - an RSS client in Emacs -+ twitter - A twitter client for Emacs -+ [[file:../modules/app/write/README.org][write]] =+wordnut +langtool= - Transforms emacs into an IDE for writers, and for - writing fiction, notes, papers and so on. - -** :completion -Modules that provide new interfaces or frameworks for completion, including code -completion. - -+ [[file:../modules/completion/company/README.org][company]] =+childframe +tng= - The ultimate code completion backend -+ helm =+fuzzy +childframe= - *Another* search engine for love and life -+ ido - The /other/ *other* search engine for love and life -+ [[file:../modules/completion/ivy/README.org][ivy]] =+fuzzy +prescient +childframe= - /The/ search engine for love and life - -** :config -Modules that configure Emacs one way or another, or focus on making it easier -for you to customize it yourself. It is best to load these last. - -+ literate - For users with literate configs. This will tangle+compile a - config.org in your ~doom-private-dir~ when it changes. -+ [[file:../modules/config/default/README.org][default]] =+bindings +smartparens= - The default module sets reasonable defaults - for Emacs. It also provides a Spacemacs-inspired keybinding scheme and a - smartparens config. Use it as a reference for your own modules. - -** :editor -Modules that affect and augment your ability to manipulate or insert text. - -+ [[file:../modules/editor/evil/README.org][evil]] =+everywhere= - transforms Emacs into Vim -+ [[file:../modules/editor/file-templates/README.org][file-templates]] - Auto-inserted templates in blank new files -+ [[file:../modules/editor/fold/README.org][fold]] - universal code folding -+ format =+onsave= - TODO -+ god - run Emacs commands without modifier keys -+ [[file:../modules/editor/lispy/README.org][lispy]] - TODO -+ multiple-cursors - TODO -+ objed - TODO -+ [[file:../modules/editor/parinfer/README.org][parinfer]] - TODO -+ rotate-text - TODO -+ [[file:../modules/editor/snippets/README.org][snippets]] - Snippet expansion for lazy typists -+ [[file:../modules/editor/word-wrap/README.org][word-wrap]] - soft wrapping with language-aware indent - -** :emacs -Modules that reconfigure or augment packages or features built into Emacs. - -+ [[file:../modules/emacs/dired/README.org][dired]] =+ranger +icons= - TODO -+ electric - TODO -+ [[file:../modules/emacs/ibuffer/README.org][ibuffer]] =+icons= - TODO -+ vc - TODO - -** :email -+ [[file:../modules/email/mu4e/README.org][mu4e]] =+gmail= - TODO -+ notmuch - TODO -+ wanderlust =+gmail= - TODO - -** :input -+ chinese - TODO -+ japanese - TODO - -** :lang -Modules that bring support for a language or group of languages to Emacs. - -+ agda - TODO -+ assembly - TODO -+ [[file:../modules/lang/cc/README.org][cc]] =+lsp= - TODO -+ clojure - TODO -+ common-lisp - TODO -+ [[file:../modules/lang/coq/README.org][coq]] - TODO -+ crystal - TODO -+ [[file:../modules/lang/csharp/README.org][csharp]] - TODO -+ data - TODO -+ [[file:../modules/lang/elixir/README.org][elixir]] =+lsp= - TODO -+ elm - TODO -+ emacs-lisp - TODO -+ erlang - TODO -+ [[file:../modules/lang/ess/README.org][ess]] - TODO -+ [[file:../modules/lang/faust/README.org][faust]] - TODO -+ [[file:../modules/lang/fsharp/README.org][fsharp]] - TODO -+ [[file:../modules/lang/go/README.org][go]] =+lsp= - TODO -+ [[file:../modules/lang/haskell/README.org][haskell]] =+intero +dante +lsp= - TODO -+ hy - TODO -+ [[file:../modules/lang/idris/README.org][idris]] - TODO -+ java =+meghanada +lsp= - TODO -+ [[file:../modules/lang/javascript/README.org][javascript]] =+lsp= - TODO -+ julia - TODO -+ kotlin - TODO -+ [[file:../modules/lang/latex/README.org][latex]] - TODO -+ lean - TODO -+ ledger - TODO -+ lua =+moonscript= - TODO -+ [[file:../modules/lang/markdown/README.org][markdown]] =+grip= - TODO -+ [[file:../modules/lang/nim/README.org][nim]] - TODO -+ nix - TODO -+ [[file:../modules/lang/ocaml/README.org][ocaml]] =+lsp= - TODO -+ [[file:../modules/lang/org/README.org][org]] =+dragndrop +gnuplot +hugo +ipython +pandoc +pomodoro +present= - TODO -+ [[file:../modules/lang/perl/README.org][perl]] - TODO -+ [[file:../modules/lang/php/README.org][php]] =+lsp= - TODO -+ plantuml - TODO -+ purescript - TODO -+ [[file:../modules/lang/python/README.org][python]] =+lsp +pyenv +conda= - TODO -+ qt - TODO -+ racket - TODO -+ [[file:../modules/lang/rest/README.org][rest]] - TODO -+ ruby =+lsp +rvm +rbenv= - TODO -+ [[file:../modules/lang/rust/README.org][rust]] =+lsp= - TODO -+ scala =+lsp= - TODO -+ [[file:../modules/lang/scheme/README.org][scheme]] - TODO -+ [[file:../modules/lang/sh/README.org][sh]] =+fish +lsp= - TODO -+ [[file:../modules/lang/solidity/README.org][solidity]] - TODO -+ swift =+lsp= - TODO -+ terra - TODO -+ web =+lsp= - HTML and CSS (SCSS/SASS/LESS/Stylus) support. - -** :term -Modules that offer terminal emulation. - -+ eshell - TODO -+ shell - TODO -+ term - TODO -+ [[file:../modules/term/vterm/README.org][vterm]] - TODO - -** :tools -Small modules that give Emacs access to external tools & services. - -+ ansible - TODO -+ debugger - A (nigh-)universal debugger in Emacs -+ [[file:../modules/tools/direnv/README.org][direnv]] - TODO -+ [[file:../modules/tools/docker/README.org][docker]] - TODO -+ [[file:../modules/tools/editorconfig/README.org][editorconfig]] - TODO -+ [[file:../modules/tools/ein/README.org][ein]] - TODO -+ [[file:../modules/tools/eval/README.org][eval]] =+overlay= - REPL & code evaluation support for a variety of languages -+ flycheck - Live error/warning highlights -+ flyspell - Spell checking -+ gist - TODO -+ [[file:../modules/tools/lookup/README.org][lookup]] =+docsets= - Universal jump-to & documentation lookup backend -+ [[file:../modules/tools/lsp/README.org][lsp]] - TODO -+ macos - TODO -+ magit - TODO -+ make - TODO -+ pass - TODO -+ pdf - TODO -+ prodigy - TODO -+ rgb - TODO -+ terraform - TODO -+ tmux - TODO -+ upload - TODO -+ [[file:../modules/tools/wakatime/README.org][wakatime]] - TODO - -** :ui -Aesthetic modules that affect the Emacs interface or user experience. - -+ [[file:../modules/ui/deft/README.org][deft]] - TODO -+ [[file:../modules/ui/doom/README.org][doom]] - TODO -+ [[file:../modules/ui/doom-dashboard/README.org][doom-dashboard]] - TODO -+ [[file:../modules/ui/doom-quit/README.org][doom-quit]] - TODO -+ fill-column - TODO -+ [[file:../modules/ui/hl-todo/README.org][hl-todo]] - TODO -+ hydra - TODO -+ indent-guides - TODO -+ [[file:../modules/ui/modeline/README.org][modeline]] - TODO -+ [[file:../modules/ui/nav-flash/README.org][nav-flash]] - TODO -+ [[file:../modules/ui/neotree/README.org][neotree]] - TODO -+ [[file:../modules/ui/ophints/README.org][ophints]] - TODO -+ [[file:../modules/ui/popup/README.org][popup]] =+all +defaults= - Makes temporary/disposable windows less intrusive -+ pretty-code - TODO -+ [[file:../modules/ui/tabs/README.org][tabs]] - TODO -+ treemacs - TODO -+ [[file:../modules/ui/unicode/README.org][unicode]] - TODO -+ vc-gutter - TODO -+ vi-tilde-fringe - TODO -+ [[file:../modules/ui/window-select/README.org][window-select]] =+switch-window +numbers= - TODO -+ [[file:../modules/ui/workspaces/README.org][workspaces]] - Isolated workspaces +** Similar projects ++ [[https://github.com/purcell/emacs.d][purcell/emacs.d]] ++ [[https://github.com/seagle0128/.emacs.d][seagle0128/.emacs.d]] ++ [[https://github.com/syl20bnr/spacemacs][syl20bnr/spacemacs]] diff --git a/docs/modules.org b/docs/modules.org new file mode 100644 index 000000000..1e4249e76 --- /dev/null +++ b/docs/modules.org @@ -0,0 +1,201 @@ +#+TITLE: Module Appendix +#+STARTUP: nofold + +Functionality in Doom is divided into collections of code called modules (à la +Spacemacs' layers). A module is a bundle of packages, configuration and +commands, organized into a unit that can be enabled or disabled by adding or +removing them from your ~doom!~ block (found in =$DOOMDIR/init.el=). + +* Table of Contents :TOC: +- [[#app][:app]] +- [[#checkers][:checkers]] +- [[#completion][:completion]] +- [[#config][:config]] +- [[#editor][:editor]] +- [[#emacs][:emacs]] +- [[#email][:email]] +- [[#input][:input]] +- [[#lang][:lang]] +- [[#term][:term]] +- [[#tools][:tools]] +- [[#ui][:ui]] + +* :app +Application modules are complex and opinionated modules that transform Emacs +toward a specific purpose. They may have additional dependencies and should be +loaded last, before =:config= modules. + ++ [[file:../modules/app/calendar/README.org][calendar]] - TODO ++ [[file:../modules/app/irc/README.org][irc]] - how neckbeards socialize ++ rss =+org= - an RSS client in Emacs ++ [[file:../modules/app/twitter/README.org][twitter]] - A twitter client for Emacs + +* :checkers ++ syntax =+childframe= - Live error/warning highlights ++ spell =+everywhere= - Spell checking ++ grammar - TODO + +* :completion +Modules that provide new interfaces or frameworks for completion, including code +completion. + ++ [[file:../modules/completion/company/README.org][company]] =+childframe +tng= - The ultimate code completion backend ++ helm =+fuzzy +childframe= - *Another* search engine for love and life ++ ido - The /other/ *other* search engine for love and life ++ [[file:../modules/completion/ivy/README.org][ivy]] =+fuzzy +prescient +childframe= - /The/ search engine for love and life + +* :config +Modules that configure Emacs one way or another, or focus on making it easier +for you to customize it yourself. It is best to load these last. + ++ literate - For users with literate configs. This will tangle+compile a + config.org in your ~doom-private-dir~ when it changes. ++ [[file:../modules/config/default/README.org][default]] =+bindings +smartparens= - The default module sets reasonable defaults + for Emacs. It also provides a Spacemacs-inspired keybinding scheme and a + smartparens config. Use it as a reference for your own modules. + +* :editor +Modules that affect and augment your ability to manipulate or insert text. + ++ [[file:../modules/editor/evil/README.org][evil]] =+everywhere= - transforms Emacs into Vim ++ [[file:../modules/editor/file-templates/README.org][file-templates]] - Auto-inserted templates in blank new files ++ [[file:../modules/editor/fold/README.org][fold]] - universal code folding ++ format =+onsave= - TODO ++ [[file:../modules/editor/lispy/README.org][lispy]] - TODO ++ multiple-cursors - TODO ++ [[file:../modules/editor/objed/README.org][objed]] - TODO ++ [[file:../modules/editor/parinfer/README.org][parinfer]] - TODO ++ rotate-text - TODO ++ [[file:../modules/editor/snippets/README.org][snippets]] - Snippet expansion for lazy typists ++ [[file:../modules/editor/word-wrap/README.org][word-wrap]] - soft wrapping with language-aware indent + +* :emacs +Modules that reconfigure or augment packages or features built into Emacs. + ++ [[file:../modules/emacs/dired/README.org][dired]] =+ranger +icons= - TODO ++ electric - TODO ++ [[file:../modules/emacs/ibuffer/README.org][ibuffer]] =+icons= - TODO ++ vc - TODO + +* :email ++ [[file:../modules/email/mu4e/README.org][mu4e]] =+gmail= - TODO ++ notmuch - TODO ++ wanderlust =+gmail= - TODO + +* :input ++ [[file:../modules/input/chinese/README.org][chinese]] - TODO ++ [[file:../modules/input/japanese/README.org][japanese]] - TODO + +* :lang +Modules that bring support for a language or group of languages to Emacs. + ++ [[file:../modules/lang/agda/README.org][agda]] - TODO ++ assembly - TODO ++ [[file:../modules/lang/cc/README.org][cc]] =+lsp= - TODO ++ [[file:/mnt/projects/conf/doom-emacs/modules/lang/clojure/README.org][clojure]] =+lsp= - TODO ++ common-lisp - TODO ++ [[file:../modules/lang/coq/README.org][coq]] - TODO ++ crystal - TODO ++ [[file:../modules/lang/csharp/README.org][csharp]] - TODO ++ data - TODO ++ [[file:../modules/lang/elixir/README.org][elixir]] =+lsp= - TODO ++ elm - TODO ++ emacs-lisp - TODO ++ erlang - TODO ++ [[file:../modules/lang/ess/README.org][ess]] =+lsp= - TODO ++ [[file:../modules/lang/faust/README.org][faust]] - TODO ++ [[file:../modules/lang/fsharp/README.org][fsharp]] - TODO ++ [[file:../modules/lang/fstar/README.org][fstar]] - F* support ++ [[file:../modules/lang/go/README.org][go]] =+lsp= - TODO ++ [[file:../modules/lang/haskell/README.org][haskell]] =+dante +intero +lsp= - TODO ++ hy - TODO ++ [[file:../modules/lang/idris/README.org][idris]] - TODO ++ java =+meghanada +lsp= - TODO ++ [[file:../modules/lang/javascript/README.org][javascript]] =+lsp= - JavaScript, TypeScript, and CoffeeScript support ++ julia - TODO ++ kotlin =+lsp+= - TODO ++ [[file:../modules/lang/latex/README.org][latex]] =+latexmk +cdlatex= - TODO ++ lean - TODO ++ [[file:../modules/lang/ledger/README.org][ledger]] - TODO ++ lua =+moonscript= - TODO ++ [[file:../modules/lang/markdown/README.org][markdown]] =+grip= - TODO ++ [[file:../modules/lang/nim/README.org][nim]] - TODO ++ nix - TODO ++ [[file:../modules/lang/ocaml/README.org][ocaml]] =+lsp= - TODO ++ [[file:../modules/lang/org/README.org][org]] =+brain +dragndrop +gnuplot +hugo +ipython +journal +jupyter +pandoc +pomodoro +present= - TODO ++ [[file:../modules/lang/perl/README.org][perl]] - TODO ++ [[file:../modules/lang/php/README.org][php]] =+lsp= - TODO ++ plantuml - TODO ++ purescript - TODO ++ [[file:../modules/lang/python/README.org][python]] =+lsp +pyenv +conda= - TODO ++ qt - TODO ++ racket - TODO ++ [[file:../modules/lang/rest/README.org][rest]] - TODO ++ ruby =+lsp +rvm +rbenv= - TODO ++ [[file:../modules/lang/rust/README.org][rust]] =+lsp= - TODO ++ scala =+lsp= - TODO ++ [[file:../modules/lang/scheme/README.org][scheme]] - TODO ++ [[file:../modules/lang/sh/README.org][sh]] =+fish +lsp= - TODO ++ [[file:../modules/lang/solidity/README.org][solidity]] - TODO ++ swift =+lsp= - TODO ++ terra - TODO ++ web =+lsp= - HTML and CSS (SCSS/SASS/LESS/Stylus) support. + +* :term +Modules that offer terminal emulation. + ++ eshell - TODO ++ shell - TODO ++ term - TODO ++ [[file:../modules/term/vterm/README.org][vterm]] - TODO + +* :tools +Small modules that give Emacs access to external tools & services. + ++ ansible - TODO ++ debugger - A (nigh-)universal debugger in Emacs ++ [[file:../modules/tools/direnv/README.org][direnv]] - TODO ++ [[file:../modules/tools/docker/README.org][docker]] =+lsp= - TODO ++ [[file:../modules/tools/editorconfig/README.org][editorconfig]] - TODO ++ [[file:../modules/tools/ein/README.org][ein]] - TODO ++ [[file:../modules/tools/eval/README.org][eval]] =+overlay= - REPL & code evaluation support for a variety of languages ++ gist - TODO ++ [[file:../modules/tools/lookup/README.org][lookup]] =+dictionary +docsets= - Universal jump-to & documentation lookup + backend ++ [[file:../modules/tools/lsp/README.org][lsp]] - TODO ++ macos - TODO ++ magit - TODO ++ make - TODO ++ pass - TODO ++ pdf - TODO ++ prodigy - TODO ++ rgb - TODO ++ [[file:../modules/tools/terraform/README.org][terraform]] ++ tmux - TODO ++ upload - TODO + +* :ui +Aesthetic modules that affect the Emacs interface or user experience. + ++ [[file:../modules/ui/deft/README.org][deft]] - TODO ++ [[file:../modules/ui/doom/README.org][doom]] - TODO ++ [[file:../modules/ui/doom-dashboard/README.org][doom-dashboard]] - TODO ++ [[file:../modules/ui/doom-quit/README.org][doom-quit]] - TODO ++ fill-column - TODO ++ [[file:../modules/ui/hl-todo/README.org][hl-todo]] - TODO ++ [[file:../modules/ui/hydra/README.org][hydra]] - TODO ++ indent-guides - TODO ++ [[file:../modules/ui/modeline/README.org][modeline]] - TODO ++ [[file:../modules/ui/nav-flash/README.org][nav-flash]] - TODO ++ [[file:../modules/ui/neotree/README.org][neotree]] - TODO ++ [[file:../modules/ui/ophints/README.org][ophints]] - TODO ++ [[file:../modules/ui/popup/README.org][popup]] =+all +defaults= - Makes temporary/disposable windows less intrusive ++ pretty-code - TODO ++ [[file:../modules/ui/tabs/README.org][tabs]] - TODO ++ treemacs - TODO ++ [[file:../modules/ui/unicode/README.org][unicode]] - TODO ++ vc-gutter - TODO ++ vi-tilde-fringe - TODO ++ [[file:../modules/ui/window-select/README.org][window-select]] =+switch-window +numbers= - TODO ++ [[file:../modules/ui/workspaces/README.org][workspaces]] - Isolated workspaces ++ [[file:../modules/ui/zen/README.org][zen]] - Distraction-free coding (or writing) diff --git a/docs/workflow.org b/docs/workflow.org index 50d3bd173..ab8f1bc40 100644 --- a/docs/workflow.org +++ b/docs/workflow.org @@ -1,7 +1,15 @@ -#+TITLE: Workflow tips, tricks & tutorials +#+TITLE: Getting to know Doom Emacs #+STARTUP: nofold -This page is a WIP. +Once you've installed Doom and launched it, the next step of your masochistic +journey is to master it. This guide will walk you through many Doom-centric +workflows you'll commonly find in a text editor (and beyond). It isn't +exhaustive because I don't have enough lives to make it so. + +#+begin_quote +If you feel like we've missed something, don't hesitate to let us know! You're +welcome to [[https://discord.gg/qvGgnVx][join us on our Discord server]]. +#+end_quote * Table of Contents :TOC: - [[#day-1-in-doom-emacs][Day 1 in Doom Emacs]] @@ -14,6 +22,7 @@ This page is a WIP. - [[#pipe-text-through-ex-commands-and-programs][Pipe text through ex commands and programs]] - [[#transposingswapping-text][Transposing/swapping text]] - [[#managing-your-projects][Managing your projects]] + - [[#reconfiguring-emacs-on-a-per-project-basis][Reconfiguring Emacs on a per-project basis]] - [[#search--replace][Search & replace]] - [[#project-wide-text-search][Project-wide text search]] - [[#search--replace-1][Search & replace]] @@ -32,6 +41,8 @@ This page is a WIP. - [[#using-emacs-for][Using Emacs for...]] - [[#writing-fiction][Writing fiction]] - [[#writing-papers][Writing papers]] + - [[#note-keeping][Note-keeping]] + - [[#a-personal-organizer][A Personal Organizer]] - [[#composing-music][Composing music]] - [[#game-development][Game development]] - [[#web-development][Web development]] @@ -48,6 +59,9 @@ This page is a WIP. ** TODO Pipe text through ex commands and programs ** TODO Transposing/swapping text * TODO Managing your projects +** TODO Reconfiguring Emacs on a per-project basis +*** TODO .dir-locals.el +*** TODO editorconfig * TODO Search & replace ** TODO Project-wide text search ** TODO Search & replace @@ -66,6 +80,8 @@ This page is a WIP. * TODO Using Emacs for... ** TODO Writing fiction ** TODO Writing papers +** TODO Note-keeping +** TODO A Personal Organizer ** TODO Composing music ** TODO Game development ** TODO Web development diff --git a/early-init.el b/early-init.el index 25005c3ef..cf3138571 100644 --- a/early-init.el +++ b/early-init.el @@ -10,6 +10,7 @@ ;; loaded, but after `early-init-file'. Doom handles package initialization, so ;; we must prevent Emacs from doing it early! (setq package-enable-at-startup nil) +(advice-add #'package--ensure-init-file :override #'ignore) ;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early. (push '(menu-bar-lines . 0) default-frame-alist) diff --git a/init.el b/init.el index 4ddc67d78..13ed45647 100644 --- a/init.el +++ b/init.el @@ -28,14 +28,13 @@ ;;; License: MIT ;; A big contributor to startup times is garbage collection. We up the gc -;; threshold to temporarily prevent it from running, then reset it later with -;; `doom-restore-garbage-collection-h'. Not resetting it will cause -;; stuttering/freezes. +;; threshold to temporarily prevent it from running, then reset it later by +;; enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes. (setq gc-cons-threshold most-positive-fixnum) ;; In noninteractive sessions, prioritize non-byte-compiled source files to ;; prevent the use of stale byte-code. Otherwise, it saves us a little IO time -;; to skip the mtime checks on every *.elc file we load. +;; to skip the mtime checks on every *.elc file. (setq load-prefer-newer noninteractive) (let (file-name-handler-alist) @@ -51,6 +50,4 @@ (if noninteractive (doom-initialize-packages) (doom-initialize-core) - (doom-initialize-modules) - (add-hook 'window-setup-hook #'doom-display-benchmark-h) - (add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler))) + (doom-initialize-modules)) diff --git a/init.example.el b/init.example.el index 7965ffdab..7e970506d 100644 --- a/init.example.el +++ b/init.example.el @@ -1,12 +1,18 @@ ;;; init.el -*- lexical-binding: t; -*- -;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install' -;; will do this for you). The `doom!' block below controls what modules are -;; enabled and in what order they will be loaded. Remember to run 'doom refresh' -;; after modifying it. +;; This file controls what Doom modules are enabled and what order they load in. +;; Remember to run 'doom sync' after modifying it! + +;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's +;; documentation. There you'll find information about all of Doom's modules +;; and what flags they support. + +;; NOTE Move your cursor over a module's name (or its flags) and press 'K' (or +;; 'C-c g k' for non-vim users) to view its documentation. This works on +;; flags as well (those symbols that start with a plus). ;; -;; More information about these modules (and what flags they support) can be -;; found in modules/README.org. +;; Alternatively, press 'gd' (or 'C-c g d') on a module to browse its +;; directory (for easy access to its source code). (doom! :input ;;chinese @@ -42,6 +48,7 @@ vi-tilde-fringe ; fringe tildes to mark beyond EOB window-select ; visually switch windows workspaces ; tab emulation, persistence & separate workspaces + ;;zen ; distraction-free coding or writing :editor (evil +everywhere); come to the dark side, we have cookies @@ -69,6 +76,11 @@ ;;term ; terminals in Emacs ;;vterm ; another terminals in Emacs + :checkers + syntax ; tasing you for every semicolon you forget + ;;spell ; tasing you for misspelling mispelling + ;;grammar ; tasing grammar mistake every you make + :tools ;;ansible ;;debugger ; FIXME stepping through code, to help you add bugs @@ -77,8 +89,6 @@ ;;editorconfig ; let someone else argue about tabs vs spaces ;;ein ; tame Jupyter notebooks with emacs (eval +overlay) ; run code, run (also, repls) - flycheck ; tasing you for every semicolon you forget - ;;flyspell ; tasing you for misspelling mispelling ;;gist ; interacting with github gists (lookup ; helps you navigate your code and documentation +docsets) ; ...or in Dash docsets locally @@ -93,7 +103,6 @@ ;;terraform ; infrastructure as code ;;tmux ; an API for interacting with tmux ;;upload ; map local to remote projects via ssh/ftp - ;;wakatime :lang ;;agda ; types of types of types of types... @@ -112,8 +121,9 @@ ;;ess ; emacs speaks statistics ;;faust ; dsp, but you get to keep your soul ;;fsharp ; ML stands for Microsoft's Language + ;;fstar ; (dependent) types and (monadic) effects and Z3 ;;go ; the hipster dialect - ;;(haskell +intero) ; a language that's lazier than I am + ;;(haskell +dante) ; a language that's lazier than I am ;;hy ; readability of scheme w/ speed of python ;;idris ; ;;(java +meghanada) ; the poster child for carpal tunnel syndrome @@ -122,6 +132,7 @@ ;;kotlin ; a better, slicker Java(Script) ;;latex ; writing papers in Emacs has never been so fun ;;lean + ;;factor ;;ledger ; an accounting system in Emacs ;;lua ; one-based indices? one-based indices markdown ; writing docs for people to ignore @@ -131,8 +142,8 @@ (org ; organize your plain life in plain text +dragndrop ; drag & drop files/images into org buffers ;;+hugo ; use Emacs for hugo blogging - +ipython ; ipython/jupyter support for babel - +pandoc ; export-with-pandoc support + ;;+jupyter ; ipython/jupyter support for babel + ;;+pandoc ; export-with-pandoc support ;;+pomodoro ; be fruitful with the tomato technique +present) ; using org-mode for presentations ;;perl ; write code no one else can comprehend @@ -164,7 +175,6 @@ ;;irc ; how neckbeards socialize ;;(rss +org) ; emacs as an RSS reader ;;twitter ; twitter client https://twitter.com/vnought - ;;write ; emacs for writers (fiction, notes, papers, etc.) :config ;;literate diff --git a/modules/app/calendar/autoload.el b/modules/app/calendar/autoload.el index f642c184b..7a6a1b73f 100644 --- a/modules/app/calendar/autoload.el +++ b/modules/app/calendar/autoload.el @@ -3,9 +3,9 @@ (defvar +calendar--wconf nil) (defun +calendar--init () - (if-let* ((win (cl-loop for win in (doom-visible-windows) - if (string-match-p "^\\*cfw:" (buffer-name (window-buffer win))) - return win))) + (if-let (win (cl-find-if (lambda (b) (string-match-p "^\\*cfw:" (buffer-name b))) + (doom-visible-windows) + :key #'window-buffer)) (select-window win) (call-interactively +calendar-open-function))) diff --git a/modules/app/calendar/packages.el b/modules/app/calendar/packages.el index 41bd347ec..9f10dc730 100644 --- a/modules/app/calendar/packages.el +++ b/modules/app/calendar/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; app/calendar/packages.el -(package! calfw) -(package! calfw-org) -(package! org-gcal) +(package! calfw :pin "03abce9762") +(package! calfw-org :pin "03abce9762") +(package! org-gcal :pin "6821e34967") diff --git a/modules/app/irc/README.org b/modules/app/irc/README.org index f2d56d0e7..5e9ab0f60 100644 --- a/modules/app/irc/README.org +++ b/modules/app/irc/README.org @@ -7,8 +7,11 @@ - [[#description][Description]] - [[#module-flags][Module Flags]] - [[#plugins][Plugins]] -- [[#dependencies][Dependencies]] - [[#prerequisites][Prerequisites]] + - [[#macos][macOS]] + - [[#debian--ubuntu][Debian / Ubuntu]] + - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] - [[#features][Features]] - [[#an-irc-client-in-emacs][An IRC Client in Emacs]] - [[#configuration][Configuration]] @@ -17,7 +20,7 @@ - [[#troubleshooting][Troubleshooting]] * Description -This module turns adds an IRC client to Emacs with OS notifications. +This module turns Emacs into an IRC client, capable of OS notifications. ** Module Flags This module provides no flags. @@ -26,11 +29,27 @@ This module provides no flags. + [[https://github.com/jorgenschaefer/circe][circe]] + [[https://github.com/eqyiel/circe-notifications][circe-notifications]] -* Dependencies -This module requires =gnutls-cli= or =openssl= for secure connections. - * Prerequisites -This module has no direct prerequisites. +This module requires =gnutls= for secure IRC connections to work. + +** macOS +#+BEGIN_SRC sh +brew install gnutls +#+END_SRC + +** Debian / Ubuntu +#+BEGIN_SRC sh +apt install gnutls-bin +#+END_SRC + +** Arch Linux +#+BEGIN_SRC sh +pacman -S gnutls +#+END_SRC +** NixOS +#+BEGIN_SRC nix +environment.systemPackages = [ pkgs.gnutls ]; +#+END_SRC * Features ** An IRC Client in Emacs @@ -54,20 +73,23 @@ When in a circe buffer these keybindings will be available. | ~circe-reconnect~ | =SPC m R= | Reconnect the current server | * Configuration -Use ~set-irc-server!~ to configure IRC servers. Its second argument (a plist) +Use ~set-irc-server! SERVER PLIST~ to configure IRC servers. Its second argument (a plist) takes the same arguments as ~circe-network-options~. #+BEGIN_SRC emacs-lisp :tangle no -(set-irc-server! "chat.freenode.net" - `(:tls t - :nick "doom" - :sasl-username "myusername" - :sasl-password "mypassword" - :channels ("#emacs"))) +;; if you omit =:host=, ~SERVER~ will be used instead. +(after! circe + (set-irc-server! "chat.freenode.net" + `(:tls t + :port 6697 + :nick "doom" + :sasl-username "myusername" + :sasl-password "mypassword" + :channels ("#emacs")))) #+END_SRC -*It is a obviously a bad idea to store auth-details in plaintext,* so here are -some ways to avoid that: +However, *it is a obviously a bad idea to store your password in plaintext,* so +here are ways to avoid that: ** Pass: the unix password manager [[https://www.passwordstore.org/][Pass]] is my tool of choice. I use it to manage my passwords. If you activate the @@ -80,6 +102,7 @@ password store. #+BEGIN_SRC emacs-lisp :tangle no (set-irc-server! "chat.freenode.net" `(:tls t + :port 6697 :nick "doom" :sasl-username ,(+pass-get-user "irc/freenode.net") :sasl-password ,(+pass-get-secret "irc/freenode.net") @@ -105,10 +128,10 @@ Note that =+pass-get-user= tries to find your username by looking for the fields listed in =+pass-user-fields= (by default =login=, =user==, =username== and =email=)=). An example configuration looks like -#+BEGIN_SRC txt :tangle no +#+begin_example mysecretpassword username: myusername -#+END_SRC +#+end_example ** Emacs' auth-source API ~auth-source~ is built into Emacs. As suggested [[https://github.com/jorgenschaefer/circe/wiki/Configuration#safer-password-management][in the circe wiki]], you can store diff --git a/modules/app/irc/autoload/irc.el b/modules/app/irc/autoload/irc.el index 8ff58ee24..b560563fe 100644 --- a/modules/app/irc/autoload/irc.el +++ b/modules/app/irc/autoload/irc.el @@ -3,8 +3,9 @@ (defvar +irc--workspace-name "*IRC*") (defun +irc-setup-wconf (&optional inhibit-workspace) - (unless inhibit-workspace - (+workspace-switch +irc--workspace-name t)) + (when (and (featurep! :ui workspaces) + (not inhibit-workspace)) + (+workspace-switch +irc--workspace-name 'auto-create)) (let ((buffers (doom-buffers-in-mode 'circe-mode nil t))) (if buffers (ignore (switch-to-buffer (car buffers))) @@ -20,17 +21,12 @@ If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new workspace for it." (interactive "P") - (cond ((and (featurep! :ui workspaces) - (+workspace-exists-p +irc--workspace-name)) - (+workspace-switch +irc--workspace-name)) - ((not (+irc-setup-wconf inhibit-workspace)) - (user-error "Couldn't start up a workspace for IRC"))) - (if (doom-buffers-in-mode 'circe-mode (buffer-list) t) - (message "Circe buffers are already open") - (if circe-network-options - (cl-loop for network in circe-network-options - collect (circe (car network))) - (call-interactively #'circe)))) + (+irc-setup-wconf inhibit-workspace) + (cond ((doom-buffers-in-mode 'circe-mode (doom-buffer-list) t) + (message "Circe buffers are already open")) + (circe-network-options + (mapc #'circe (mapcar #'car circe-network-options))) + ((call-interactively #'circe)))) ;;;###autoload (defun +irc/connect (&optional inhibit-workspace) @@ -52,16 +48,17 @@ workspace for it." (defun +irc/quit () "Kill current circe session and workgroup." (interactive) - (if (y-or-n-p "Really kill IRC session?") - (let (circe-channel-killed-confirmation - circe-server-killed-confirmation) - (when +irc--defer-timer - (cancel-timer +irc--defer-timer)) - (disable-circe-notifications) - (mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t)) - (when (equal (+workspace-current-name) +irc--workspace-name) - (+workspace/delete +irc--workspace-name))) - (message "Aborted"))) + (unless (y-or-n-p "Really kill IRC session?") + (user-error "Aborted")) + (let (circe-channel-killed-confirmation + circe-server-killed-confirmation) + (when +irc--defer-timer + (cancel-timer +irc--defer-timer)) + (disable-circe-notifications) + (mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t)) + (when (featurep! :ui workspaces) + (when (equal (+workspace-current-name) +irc--workspace-name) + (+workspace/delete +irc--workspace-name))))) ;;;###autoload (defun +irc/ivy-jump-to-channel (&optional this-server) @@ -93,7 +90,7 @@ argument) is non-nil only show channels in current server." ;;;###autoload (defun +irc/tracking-next-buffer () - "Dissables switching to an unread buffer unless in the irc workspace." + "Disables switching to an unread buffer unless in the irc workspace." (interactive) (when (derived-mode-p 'circe-mode) (tracking-next-buffer))) @@ -106,13 +103,12 @@ argument) is non-nil only show channels in current server." (defun +circe-buffer-p (buf) "Return non-nil if BUF is a `circe-mode' buffer." (with-current-buffer buf - (and (derived-mode-p 'circe-mode) - (eq (safe-persp-name (get-current-persp)) - +irc--workspace-name)))) + (derived-mode-p 'circe-mode))) ;;;###autoload (defun +irc--add-circe-buffer-to-persp-h () - (when (bound-and-true-p persp-mode) + (when (and (bound-and-true-p persp-mode) + (+workspace-exists-p +irc--workspace-name)) (let ((persp (get-current-persp)) (buf (current-buffer))) ;; Add a new circe buffer to irc workspace when we're in another workspace diff --git a/modules/app/irc/autoload/settings.el b/modules/app/irc/autoload/settings.el index 59615a760..5f88eb993 100644 --- a/modules/app/irc/autoload/settings.el +++ b/modules/app/irc/autoload/settings.el @@ -1,9 +1,17 @@ ;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*- ;;;###autodef -(defun set-irc-server! (server letvars) +(defun set-irc-server! (server plist) "Registers an irc SERVER for circe. +SERVER can either be a name for the network (in which case you must specify a +:host), or it may be the hostname itself, in which case it will be used as the +:host. + See `circe-network-options' for details." (after! circe - (push (cons server letvars) circe-network-options))) + (unless (plist-member plist :host) + (plist-put! plist :host server)) + (setf (alist-get server circe-network-options + nil nil #'equal) + plist))) diff --git a/modules/app/irc/config.el b/modules/app/irc/config.el index eda3563ab..38f910a0c 100644 --- a/modules/app/irc/config.el +++ b/modules/app/irc/config.el @@ -93,6 +93,7 @@ playback.") (add-hook 'doom-real-buffer-functions #'+circe-buffer-p) (add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode) (add-hook 'circe-mode-hook #'+irc--add-circe-buffer-to-persp-h) + (add-hook 'circe-mode-hook #'turn-off-smartparens-mode) (defadvice! +irc--circe-run-disconnect-hook-a (&rest _) "Runs `+irc-disconnect-hook' after circe disconnects." @@ -175,7 +176,7 @@ playback.") (define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line) (setq lui-fill-type nil) - (when (featurep! :tools flyspell) + (when (featurep! :checkers spell) (setq lui-flyspell-p t)) (after! evil diff --git a/modules/app/irc/packages.el b/modules/app/irc/packages.el index 58b486c76..fe52975f8 100644 --- a/modules/app/irc/packages.el +++ b/modules/app/irc/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; app/irc/packages.el -(package! circe) -(package! circe-notifications) +(package! circe :pin "0c79138fb2") +(package! circe-notifications :pin "291149ac12") diff --git a/modules/app/regex/config.el b/modules/app/regex/config.el index f48488d01..595d619e4 100644 --- a/modules/app/regex/config.el +++ b/modules/app/regex/config.el @@ -49,4 +49,3 @@ https://mediatemple.net" (set-popup-rules! '(("^\\*doom-regex\\*$" :size 4 :quit nil) ("^\\*doom-regex-groups" :side 'left :size 28 :select nil :quit nil))) - diff --git a/modules/app/rss/config.el b/modules/app/rss/config.el index 7e1af21a5..a17947150 100644 --- a/modules/app/rss/config.el +++ b/modules/app/rss/config.el @@ -4,10 +4,6 @@ ;; by apps Reeder and Readkit. It can be invoked via `=rss'. Otherwise, if you ;; don't care for the UI you can invoke elfeed directly with `elfeed'. -(defvar +rss-elfeed-files (list "elfeed.org") - "Where to look for elfeed.org files, relative to `org-directory'. Can be -absolute paths.") - (defvar +rss-split-direction 'below "What direction to pop up the entry buffer in elfeed.") @@ -67,8 +63,11 @@ easier to scroll through.") (use-package! elfeed-org :when (featurep! +org) :after elfeed + :init + (setq rmh-elfeed-org-files (list "elfeed.org")) :config - (let ((default-directory org-directory)) - (setq rmh-elfeed-org-files - (mapcar #'expand-file-name +rss-elfeed-files))) - (elfeed-org)) + (and (let ((default-directory org-directory)) + (setq rmh-elfeed-org-files + (cl-remove-if-not + #'file-exists-p (mapcar #'expand-file-name rmh-elfeed-org-files)))) + (elfeed-org))) diff --git a/modules/app/rss/packages.el b/modules/app/rss/packages.el index b1356ab8d..1d33be22a 100644 --- a/modules/app/rss/packages.el +++ b/modules/app/rss/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; app/rss/packages.el -(package! elfeed) -(package! elfeed-org) +(package! elfeed :pin "3f0edb1737") +(package! elfeed-org :pin "77b6bbf222") diff --git a/modules/app/twitter/autoload.el b/modules/app/twitter/autoload.el index 1116633be..7c6d7c065 100644 --- a/modules/app/twitter/autoload.el +++ b/modules/app/twitter/autoload.el @@ -83,6 +83,7 @@ that works with the feature/popup module." "Open a visible link, username or hashtag in a `twittering-mode' buffer." (interactive) (require 'avy) + ;; REVIEW Is this necessary anymore with `link-hint' (let ((pt (avy-with +twitter/ace-link (avy--process (+twitter--collect-links) diff --git a/modules/app/twitter/packages.el b/modules/app/twitter/packages.el index 85b1da56d..d5ef4732c 100644 --- a/modules/app/twitter/packages.el +++ b/modules/app/twitter/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; app/twitter/packages.el -(package! twittering-mode) -(package! avy) +(package! twittering-mode :pin "114891e8fd") +(package! avy :pin "cf95ba9582") diff --git a/modules/app/write/README.org b/modules/app/write/README.org deleted file mode 100644 index dc8159fff..000000000 --- a/modules/app/write/README.org +++ /dev/null @@ -1,131 +0,0 @@ -#+TITLE: app/write -#+DATE: October 10, 2019 -#+SINCE: v1.3 -#+STARTUP: inlineimages - -* Table of Contents :TOC_3:noexport: -- [[#description][Description]] - - [[#module-flags][Module Flags]] - - [[#plugins][Plugins]] -- [[#prerequisites][Prerequisites]] - - [[#language-tool][Language Tool]] - - [[#wordnut][Wordnut]] -- [[#features][Features]] - - [[#m-x-write-mode][~M-x +write-mode~]] - - [[#language-tool-langtool][Language Tool ~+langtool~]] - - [[#commands][Commands]] - - [[#wordnut-wordnut][Wordnut ~+wordnut~]] - - [[#commands-1][Commands]] - - [[#synosaurus][Synosaurus]] - - [[#commands-2][Commands]] -- [[#configuration][Configuration]] - - [[#mixed-pitch-mode][mixed-pitch-mode]] -- [[#appendix][Appendix]] - - [[#minor-modes][Minor modes]] - - [[#commands-3][Commands]] - -* Description -Adds word processing tools and the ~+write-mode~ minor mode, which converts -Emacs into a more comfortable writing environment. - -** Module Flags -This module provides two module flags: - -- ~+langtool~ Enables language tool integration. -- ~+wordnut~ Enables wordnet integration. - -** Plugins -+ [[https://github.com/hpdeifel/synosaurus][synosaurus]] -+ [[https://gitlab.com/jabranham/mixed-pitch][mixed-pitch]] -+ [[https://github.com/joostkremers/visual-fill-column][visual-fill-column]] -+ [[https://github.com/mhayashi1120/Emacs-langtool][langtool]]* (=+langtool=) -+ [[https://github.com/gromnitsky/wordnut][wordnut]]* (=+wordnut=) - -* Prerequisites -** Language Tool -Either download and deploy it from https://languagetool.org/ or install it -through your OS package manager: - -#+BEGIN_SRC sh -# MacOS/Homebrew users: -brew install languagetool - -# Arch Linux users: -sudo pacman -S languagetool -#+END_SRC - -This module tries to guess the location of languagetool-commandline.jar. If you -get a warning that Doom =couldn't find languagetool-commandline.jar=, you will -need to find langaugetool-commandline.jar and set ~langtool-language-tool-jar~ -to its path. - -** Wordnut -This requires =wordnet= to be installed, which should be available through your -OS package manager: - -#+BEGIN_SRC sh -# MacOS/Homebrew users: -brew install wordnet - -# Arch Linux users: -sudo pacaur -S wordnet # on the AUR -#+END_SRC - -* Features -** ~M-x +write-mode~ -Write mode makes Emacs a more comfortable writing environment by: - -- Centering the buffer (with ~visual-fill-column-mode~), ala distraction-free - mode from other text editors. -- Soft-wrapping long text lines with ~visual-line-mode~. -- Enabling ~mixed-pitch-mode~, allowing fixed-width and variable-pitch fonts to - co-exist in one buffer. For example, a monospace font for SRC blocks and Arial - for everything else. -- In org-mode: - - Turns on ~org-indent-mode~ - - Turns on ~+org-pretty-mode~ - -** Language Tool ~+langtool~ -[[https://www.languagetool.org/][Language Tool]] is a polyglot proofreader service that checks for grammar and -stylistic issues in your writing. This requires Java 1.8+. - -#+begin_quote -This requires Java 1.8+ -#+end_quote - -*** Commands -- ~langtool-check~ -- ~langtool-correct-buffer~ - -** Wordnut ~+wordnut~ -Wordnut provides a searchable dictionary frontend for Emacs. This requires -~wordnet~, which should be available in your OS's package manager. - -*** Commands -- ~wordnut-search~ -- ~wordnut-lookup-curent-word~ - -** Synosaurus -Synosaurus provides a service for looking up synonyms. It requires an internet -connection. - -*** Commands -- ~synosaurus-lookup~ -- ~synosaurus-choose-and-replace~ - -* Configuration -** mixed-pitch-mode -To configure which faces are displayed with fixed-pitch fonts in -~mixed-pitch-mode~, look into ~mixed-pitch-fixed-pitch-faces~. - -* Appendix -** Minor modes -- ~+write-mode~ -- ~mixed-pitch-mode~ -** Commands -- ~langtool-check~ -- ~langtool-correct-buffer~ -- ~synosaurus-choose-and-replace~ -- ~synosaurus-lookup~ -- ~wordnut-lookup-curent-word~ -- ~wordnut-search~ diff --git a/modules/app/write/autoload.el b/modules/app/write/autoload.el deleted file mode 100644 index 8a2f21044..000000000 --- a/modules/app/write/autoload.el +++ /dev/null @@ -1,43 +0,0 @@ -;;; app/write/autoload.el -*- lexical-binding: t; -*- - -;;;###autoload -(defvar +write-mode-map (make-sparse-keymap) - "TODO") - -;;;###autoload -(define-minor-mode +write-mode - "Turns Emacs into a more comfortable writing environment and word processor." - :init-value nil - :keymap +write-mode-map - (setq-local visual-fill-column-center-text t) - (when +write-text-scale - (text-scale-set (if +write-mode 2 0))) - (when +write-line-spacing - (setq-local line-spacing +write-line-spacing))) - -;;;###autoload -(defun +write|init-org-mode () - "Initializes `org-mode' specific settings for `+write-mode'." - (when (eq major-mode 'org-mode) - (+org-pretty-mode (if +write-mode +1 -1)))) - -;;;###autoload -(defun +write|init-line-numbers () - (display-line-numbers-mode (if +write-mode +1 -1))) - -;;;###autoload -(defun +write|init-mixed-pitch () - (mixed-pitch-mode (if +write-mode +1 -1))) - -;;;###autoload -(defun +write|init-visual-fill-column () - (visual-fill-column-mode (if +write-mode +1 -1))) - -;;;###autoload -(add-hook! '+write-mode-hook - #'(flyspell-mode - visual-line-mode - +write|init-mixed-pitch - +write|init-visual-fill-column - +write|init-line-numbers - +write|init-org-mode)) diff --git a/modules/app/write/config.el b/modules/app/write/config.el deleted file mode 100644 index e42620df0..000000000 --- a/modules/app/write/config.el +++ /dev/null @@ -1,56 +0,0 @@ -;;; app/write/config.el -*- lexical-binding: t; -*- - -(defvar +write-text-scale nil - "What to scale the text up to in `+write-mode'. Uses `text-scale-set'.") - -(defvar +write-line-spacing nil - "What to set `line-spacing' in `+write-mode'.") - -;; -;; Packages - -(use-package! langtool - :when (featurep! +langtool) - :commands (langtool-check - langtool-check-done - langtool-show-message-at-point - langtool-correct-buffer) - :init (setq langtool-default-language "en-US") - :config - (unless langtool-language-tool-jar - (setq langtool-language-tool-jar - (cond (IS-MAC - (locate-file "libexec/languagetool-commandline.jar" - (doom-files-in "/usr/local/Cellar/languagetool" - :type 'dirs - :depth 2))) - (IS-LINUX - "/usr/share/java/languagetool/languagetool-commandline.jar"))))) - - -;; `synosaurus' -(setq synosaurus-choose-method 'default) - - -;; `mixed-pitch' -(after! mixed-pitch - (setq mixed-pitch-fixed-pitch-faces - (append mixed-pitch-fixed-pitch-faces - '(org-todo-keyword-todo - org-todo-keyword-habt - org-todo-keyword-done - org-todo-keyword-wait - org-todo-keyword-kill - org-todo-keyword-outd - org-todo - org-indent - line-number - line-number-current-line - org-special-keyword - org-date - org-property-value - org-special-keyword - org-property-value - org-ref-cite-face - org-tag - font-lock-comment-face)))) diff --git a/modules/app/write/doctor.el b/modules/app/write/doctor.el deleted file mode 100644 index 655adea7d..000000000 --- a/modules/app/write/doctor.el +++ /dev/null @@ -1,7 +0,0 @@ -;; -*- lexical-binding: t; no-byte-compile: t; -*- -;;; app/write/doctor.el - -(when (featurep! +langtool) - (require 'langtool) - (unless (file-exists-p langtool-language-tool-jar) - (warn! "Couldn't find languagetool-commandline.jar"))) diff --git a/modules/app/write/packages.el b/modules/app/write/packages.el deleted file mode 100644 index 4827e984c..000000000 --- a/modules/app/write/packages.el +++ /dev/null @@ -1,12 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; app/write/packages.el - -(package! synosaurus) -(package! mixed-pitch) - -(when (featurep! +langtool) - (package! langtool)) -(when (featurep! +wordnut) - (package! wordnut)) - -(package! visual-fill-column) diff --git a/modules/checkers/grammar/README.org b/modules/checkers/grammar/README.org new file mode 100644 index 000000000..1f51b3e03 --- /dev/null +++ b/modules/checkers/grammar/README.org @@ -0,0 +1,68 @@ +#+TITLE: checkers/grammar +#+DATE: January 9, 2020 +#+SINCE: v3.0.0 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + - [[#language-tool][Language Tool]] + - [[#commands][Commands]] + - [[#writegood-mode][writegood-mode]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module adds grammar checking to Emacs to aid your writing by combining +=lang-tool= and =writegood-mode=. + +** Maintainers +This module has no dedicated maintainers. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/mhayashi1120/Emacs-langtool][langtool]] ++ [[https://github.com/bnbeckwith/writegood-mode][writegood-mode]] + +* Prerequisites +This module requires langtool (which requires =Java 1.8+=). + +It can be acquired either from https://languagetool.org/ or your OS's package +manager: + ++ macOS: ~brew install languagetool~ ++ Arch Linux: ~pacman -S languagetool~ + +This module tries to guess the location of languagetool-commandline.jar. If you +get a warning that Doom =couldn't find languagetool-commandline.jar=, you will +need to set ~langtool-language-tool-jar~ to its location. + +* Features +An in-depth list of features, how to use them, and their dependencies. + +** Language Tool +[[https://www.languagetool.org/][Language Tool]] is a polyglot proofreader service that checks for grammar and +stylistic issues in your writing. This requires Java 1.8+. + +#+begin_quote +This requires Java 1.8+ +#+end_quote + +*** Commands +- ~langtool-check~ +- ~langtool-correct-buffer~ + +** writegood-mode +This minor mode highlights weasel words, duplication and passive voice. + +* Configuration +How to configure this module, including common problems and how to address them. + +* Troubleshooting +Common issues and their solution, or places to look for help. diff --git a/modules/checkers/grammar/config.el b/modules/checkers/grammar/config.el new file mode 100644 index 000000000..581f207c0 --- /dev/null +++ b/modules/checkers/grammar/config.el @@ -0,0 +1,29 @@ +;;; checkers/grammar/config.el -*- lexical-binding: t; -*- + +(use-package! langtool + :commands (langtool-check + langtool-check-done + langtool-show-message-at-point + langtool-correct-buffer) + :init (setq langtool-default-language "en-US") + :config + (unless langtool-language-tool-jar + (setq langtool-language-tool-jar + (cond (IS-MAC + (locate-file "libexec/languagetool-commandline.jar" + (doom-files-in "/usr/local/Cellar/languagetool" + :type 'dirs + :depth 2))) + (IS-LINUX + "/usr/share/java/languagetool/languagetool-commandline.jar"))))) + + +;; Detects weasel words, passive voice and duplicates. Proselint would be a +;; better choice. +(use-package! writegood-mode + :hook (org-mode markdown-mode rst-mode asciidoc-mode latex-mode) + :config + (map! :localleader + :map writegood-mode-map + "g" #'writegood-grade-level + "r" #'writegood-reading-ease)) diff --git a/modules/checkers/grammar/packages.el b/modules/checkers/grammar/packages.el new file mode 100644 index 000000000..052e9207c --- /dev/null +++ b/modules/checkers/grammar/packages.el @@ -0,0 +1,5 @@ +;; -*- no-byte-compile: t; -*- +;;; checkers/grammar/packages.el + +(package! langtool :pin "a71ed02ce0") +(package! writegood-mode :pin "b71757ec33") diff --git a/modules/checkers/spell/autoload.el b/modules/checkers/spell/autoload.el new file mode 100644 index 000000000..0674dc5c1 --- /dev/null +++ b/modules/checkers/spell/autoload.el @@ -0,0 +1,27 @@ +;;; checkers/spell/autoload.el -*- lexical-binding: t; -*- + +;;;###autodef +(defalias 'flyspell-mode! #'flyspell-mode) + +(defvar +spell--flyspell-predicate-alist nil + "TODO") + +;;;###autodef +(defun set-flyspell-predicate! (modes predicate) + "TODO" + (declare (indent defun)) + (dolist (mode (doom-enlist modes) +spell--flyspell-predicate-alist) + (add-to-list '+spell--flyspell-predicate-alist (cons mode predicate)))) + +;;;###autoload +(defun +spell-init-flyspell-predicate-h () + "TODO" + (when-let (pred (assq major-mode +spell--flyspell-predicate-alist)) + (setq-local flyspell-generic-check-word-predicate (cdr pred)))) + +;;;###autoload +(defun +spell-correction-at-point-p (&optional point) + "TODO" + (cl-loop for ov in (overlays-at (or point (point))) + if (overlay-get ov 'flyspell-overlay) + return t)) diff --git a/modules/tools/flyspell/config.el b/modules/checkers/spell/config.el similarity index 64% rename from modules/tools/flyspell/config.el rename to modules/checkers/spell/config.el index 61fcd75a3..760c3f1ef 100644 --- a/modules/tools/flyspell/config.el +++ b/modules/checkers/spell/config.el @@ -1,11 +1,8 @@ -;;; tools/flyspell/config.el -*- lexical-binding: t; -*- +;;; checkers/spell/config.el -*- lexical-binding: t; -*- -;; -;;; Packages +(defvar ispell-dictionary "en_US") (after! ispell - (add-to-list 'ispell-extra-args "--dont-tex-check-comments") - ;; Don't spellcheck org blocks (pushnew! ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:") @@ -23,13 +20,13 @@ ((executable-find "hunspell") 'hunspell)) (`aspell (setq ispell-program-name "aspell" - ispell-extra-args '("--sug-mode=ultra" "--run-together")) + ispell-extra-args '("--sug-mode=ultra" "--run-together" "--dont-tex-check-comments")) (add-hook! 'text-mode-hook - (defun +flyspell-remove-run-together-switch-for-aspell-h () + (defun +spell-remove-run-together-switch-for-aspell-h () (setq-local ispell-extra-args (remove "--run-together" ispell-extra-args)))) - (defun +flyspell-setup-ispell-extra-args-a (orig-fun &rest args) + (defun +spell-init-ispell-extra-args-a (orig-fun &rest args) :around '(ispell-word flyspell-auto-correct-word) (let ((ispell-extra-args (remove "--run-together" ispell-extra-args))) (ispell-kill-ispell t) @@ -49,12 +46,23 @@ ;; messages for every word when checking the entire buffer flyspell-issue-message-flag nil) - (add-hook 'text-mode-hook #'flyspell-mode) - (when (featurep! +prog) - (add-hook 'prog-mode-hook #'flyspell-prog-mode)) + (add-hook! '(org-mode-hook + markdown-mode-hook + TeX-mode-hook + rst-mode-hook + mu4e-compose-mode-hook + message-mode-hook + git-commit-mode-hook) + #'flyspell-mode) + + (when (featurep! +everywhere) + (add-hook! '(yaml-mode-hook + conf-mode-hook + prog-mode-hook) + #'flyspell-prog-mode)) (add-hook! 'flyspell-mode-hook - (defun +flyspell-inhibit-duplicate-detection-maybe-h () + (defun +spell-inhibit-duplicate-detection-maybe-h () "Don't mark duplicates when style/grammar linters are present. e.g. proselint and langtool." (when (or (and (bound-and-true-p flycheck-mode) @@ -64,16 +72,21 @@ e.g. proselint and langtool." ;; Ensure mode-local predicates declared with `set-flyspell-predicate!' are ;; used in their respective major modes. - (add-hook 'flyspell-mode-hook #'+flyspell-init-predicate-h) + (add-hook 'flyspell-mode-hook #'+spell-init-flyspell-predicate-h) - (map! :map flyspell-mouse-map - "RET" #'flyspell-correct-word-generic - [return] #'flyspell-correct-word-generic - [mouse-1] #'flyspell-correct-word-generic)) + (let ((flyspell-correct + (general-predicate-dispatch nil + (and (not (or mark-active (ignore-errors (evil-insert-state-p)))) + (memq 'flyspell-incorrect (face-at-point nil t))) + #'flyspell-correct-at-point))) + (map! :map flyspell-mouse-map + "RET" flyspell-correct + [return] flyspell-correct + [mouse-1] #'flyspell-correct-at-point))) (use-package! flyspell-correct - :commands flyspell-correct-word-generic flyspell-correct-previous-word-generic + :commands flyspell-correct-at-point flyspell-correct-previous :config (cond ((and (featurep! :completion helm) (require 'flyspell-correct-helm nil t))) @@ -82,3 +95,7 @@ e.g. proselint and langtool." ((require 'flyspell-correct-popup nil t) (setq flyspell-popup-correct-delay 0.8) (define-key popup-menu-keymap [escape] #'keyboard-quit)))) + + +(use-package! flyspell-lazy + :after flyspell) diff --git a/modules/checkers/spell/packages.el b/modules/checkers/spell/packages.el new file mode 100644 index 000000000..873abb64d --- /dev/null +++ b/modules/checkers/spell/packages.el @@ -0,0 +1,11 @@ +;; -*- no-byte-compile: t; -*- +;;; checkers/spell/packages.el + +(package! flyspell-correct :pin "b0353a41a7") +(cond ((featurep! :completion ivy) + (package! flyspell-correct-ivy :pin "b0353a41a7")) + ((featurep! :completion helm) + (package! flyspell-correct-helm :pin "b0353a41a7")) + ((package! flyspell-correct-popup :pin "b0353a41a7"))) + +(package! flyspell-lazy :pin "3ebf68cc9e") diff --git a/modules/checkers/syntax/autoload.el b/modules/checkers/syntax/autoload.el new file mode 100644 index 000000000..4dc963572 --- /dev/null +++ b/modules/checkers/syntax/autoload.el @@ -0,0 +1,25 @@ +;;; checkers/syntax/autoload.el -*- lexical-binding: t; -*- + +;;;###autodef +(defun set-next-checker! (mode checker next &optional append) + "TODO" + (let ((fn (intern (format "+syntax--init-checkers-for-%s-h" mode)))) + (fset fn + (lambda () + (if (not (bound-and-true-p flycheck-mode)) + (add-hook 'flycheck-mode-hook fn 'append 'local) + (flycheck-add-next-checker checker next append) + (remove-hook 'flycheck-mode-hook fn 'local)))) + (add-hook (intern (format "%s-hook" mode)) fn))) + +;;;###autoload +(defun +syntax-init-popups-h () + "Activate `flycheck-posframe-mode' if available and in GUI Emacs. +Activate `flycheck-popup-tip-mode' otherwise. +Do nothing if `lsp-ui-mode' is active and `lsp-ui-sideline-enable' is non-nil." + (unless (and (bound-and-true-p lsp-ui-mode) + lsp-ui-sideline-enable) + (if (and (fboundp 'flycheck-posframe-mode) + (display-graphic-p)) + (flycheck-posframe-mode +1) + (flycheck-popup-tip-mode +1)))) diff --git a/modules/tools/flycheck/config.el b/modules/checkers/syntax/config.el similarity index 83% rename from modules/tools/flycheck/config.el rename to modules/checkers/syntax/config.el index dc77b4790..268fe2bc9 100644 --- a/modules/tools/flycheck/config.el +++ b/modules/checkers/syntax/config.el @@ -1,12 +1,7 @@ -;;; tools/flycheck/config.el -*- lexical-binding: t; -*- - -(defvar +flycheck-lazy-idle-delay 3.0 - "The delay before flycheck checks the buffer, after a check that produces no -errors.") - +;;; checkers/syntax/config.el -*- lexical-binding: t; -*- ;; -;;; Packages +;;; Flycheck (use-package! flycheck :commands flycheck-list-errors flycheck-buffer @@ -24,11 +19,10 @@ errors.") ;; Don't commandeer input focus if the error message pops up (happens when ;; tooltips and childframes are disabled). - (after! flycheck - (set-popup-rule! flycheck-error-message-buffer :select nil)) + (set-popup-rule! "^\\*Flycheck error messages\\*" :select nil) (add-hook! 'doom-escape-hook :append - (defun +flycheck-buffer-h () + (defun +syntax-check-buffer-h () "Flycheck buffer on ESC in normal mode." (when flycheck-mode (ignore-errors (flycheck-buffer)) @@ -47,7 +41,7 @@ errors.") (use-package! flycheck-popup-tip :commands flycheck-popup-tip-show-popup flycheck-popup-tip-delete-popup - :init (add-hook 'flycheck-mode-hook #'+flycheck-init-popups-h) + :init (add-hook 'flycheck-mode-hook #'+syntax-init-popups-h) :config (setq flycheck-popup-tip-error-prefix "✕ ") (after! evil @@ -55,7 +49,7 @@ errors.") ;; the cursor's position or cause disruptive input delays. (add-hook! '(evil-insert-state-entry-hook evil-replace-state-entry-hook) #'flycheck-popup-tip-delete-popup) - (defadvice! +flycheck--disable-popup-tip-maybe-a (&rest _) + (defadvice! +syntax--disable-flycheck-popup-tip-maybe-a (&rest _) :before-while #'flycheck-popup-tip-show-popup (if evil-local-mode (eq evil-state 'normal) @@ -65,7 +59,7 @@ errors.") (use-package! flycheck-posframe :when (featurep! +childframe) :defer t - :init (add-hook 'flycheck-mode-hook #'+flycheck-init-popups-h) + :init (add-hook 'flycheck-mode-hook #'+syntax-init-popups-h) :config (setq flycheck-posframe-warning-prefix "⚠ " flycheck-posframe-info-prefix "··· " @@ -79,3 +73,7 @@ errors.") (add-hook! 'flycheck-posframe-inhibit-functions #'evil-insert-state-p #'evil-replace-state-p))) + + +;; +;;; TODO Flymake diff --git a/modules/checkers/syntax/packages.el b/modules/checkers/syntax/packages.el new file mode 100644 index 000000000..98a04b57a --- /dev/null +++ b/modules/checkers/syntax/packages.el @@ -0,0 +1,9 @@ +;; -*- no-byte-compile: t; -*- +;;; checkers/syntax/packages.el + +(package! flycheck :pin "74377fa9c7") +(package! flycheck-popup-tip :pin "ef86aad907") +(when (featurep! +childframe) + (package! flycheck-posframe :pin "2b3e94c2e4")) + +;; TODO flymake? diff --git a/modules/completion/company/README.org b/modules/completion/company/README.org index 595a8bf88..1ca8d72d8 100644 --- a/modules/completion/company/README.org +++ b/modules/completion/company/README.org @@ -26,9 +26,10 @@ https://assets.doomemacs.org/completion/company/overlay.png ** Module Flags + =+childframe= Enables displaying completion candidates in a child frame, rather than an overlay or tooltip (among with other UI enhancements). *This - requires GUI Emacs 26.1+.* + requires GUI Emacs 26.1+ and is incompatible with the =+tng= flag* + =+tng= Enables completion using only ~TAB~. Pressing ~TAB~ will select the - next completion suggestion, while ~S-TAB~ will select the previous one. + next completion suggestion, while ~S-TAB~ will select the previous one. *This + is incompatible with the =+childframe= flag* ** Plugins + [[https://github.com/company-mode/company-mode][company-mode]] diff --git a/modules/completion/company/config.el b/modules/completion/company/config.el index a98bcca9c..b1bda000e 100644 --- a/modules/completion/company/config.el +++ b/modules/completion/company/config.el @@ -20,7 +20,8 @@ :config (when (featurep! :editor evil) (add-hook 'company-mode-hook #'evil-normalize-keymaps) - + ;; Don't persist company popups when switching back to normal mode. + (add-hook 'evil-normal-state-entry-hook #'company-abort) ;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; by C-x C-n, will switch from `company-yasnippet' to ;; `company-dabbrev-code'. @@ -70,47 +71,42 @@ company-box-max-candidates 50 company-box-icons-alist 'company-box-icons-all-the-icons company-box-icons-functions - '(+company-box-icons--yasnippet-fn - company-box-icons--lsp - +company-box-icons--elisp-fn - company-box-icons--acphp) + (cons #'+company-box-icons--elisp-fn + (delq 'company-box-icons--elisp + company-box-icons-functions)) company-box-icons-all-the-icons - `((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple)) - (Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green)) - (Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) - (Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) - (Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) - (Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) - (Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue)) - (Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) - (Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red)) - (Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red)) - (Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red)) - (Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red)) - (Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red)) - (Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red)) - (Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red)) - (Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red)) - (Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red)) - (File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red)) - (Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red)) - (Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red)) - (EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red)) - (Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red)) - (Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red)) - (Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red)) - (Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red)) - (TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) - ;; (Template . ,(company-box-icons-image "Template.png")))) - (Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green)) - (ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) - (ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue)) - (ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange)) - (ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink)))) - - (defun +company-box-icons--yasnippet-fn (candidate) - (when (get-text-property 0 'yas-annotation candidate) - 'Yasnippet)) + (let ((all-the-icons-scale-factor 0.8)) + `((Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple)) + (Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green)) + (Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) + (Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) + (Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) + (Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) + (Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue)) + (Class . ,(all-the-icons-material "class" :face 'all-the-icons-red)) + (Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red)) + (Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red)) + (Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red)) + (Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red)) + (Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red)) + (Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red)) + (Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red)) + (Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red)) + (Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red)) + (File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red)) + (Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red)) + (Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red)) + (EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red)) + (Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red)) + (Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red)) + (Event . ,(all-the-icons-material "event" :face 'all-the-icons-red)) + (Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red)) + (TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red)) + (Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green)) + (ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red)) + (ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue)) + (ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange)) + (ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink))))) (defun +company-box-icons--elisp-fn (candidate) (when (derived-mode-p 'emacs-lisp-mode) @@ -118,7 +114,15 @@ (cond ((fboundp sym) 'ElispFunction) ((boundp sym) 'ElispVariable) ((featurep sym) 'ElispFeature) - ((facep sym) 'ElispFace)))))) + ((facep sym) 'ElispFace))))) + + (defadvice! +company-remove-scrollbar-a (orig-fn &rest args) + "This disables the company-box scrollbar, because: +https://github.com/sebastiencs/company-box/issues/44" + :around #'company-box--update-scrollbar + (cl-letf (((symbol-function #'display-buffer-in-side-window) + (symbol-function #'ignore))) + (apply orig-fn args)))) (use-package! company-dict diff --git a/modules/completion/company/packages.el b/modules/completion/company/packages.el index 91b9e88a7..87a5be858 100644 --- a/modules/completion/company/packages.el +++ b/modules/completion/company/packages.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/company/packages.el -(package! company) -(package! company-dict) -(package! company-prescient) +(package! company :pin "9de9905ed2") +(package! company-dict :pin "cd7b8394f6") +(package! company-prescient :pin "7fd8c3b802") (when (featurep! +childframe) - (package! company-box)) + (package! company-box :pin "8fc6168f2d")) diff --git a/modules/completion/helm/autoload/helm.el b/modules/completion/helm/autoload/helm.el index 70105d239..c453eabad 100644 --- a/modules/completion/helm/autoload/helm.el +++ b/modules/completion/helm/autoload/helm.el @@ -38,7 +38,7 @@ workspace." ;;; Project search ;;;###autoload -(cl-defun +helm-file-search (&key query in all-files (recursive t)) +(cl-defun +helm-file-search (&key query in all-files (recursive t) _prompt args) "Conduct a file search using ripgrep. :query STRING @@ -51,46 +51,21 @@ workspace." (declare (indent defun)) (unless (executable-find "rg") (user-error "Couldn't find ripgrep in your PATH")) - (require 'helm-ag) - (helm-ag--init-state) - (let* ((project-root (or (doom-project-root) default-directory)) - (directory (or in project-root)) - (default-directory directory) - (helm-ag--default-directory directory) - (helm-ag--default-target (list directory)) - (query (or query - (when (use-region-p) - (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) - (end (or (bound-and-true-p evil-visual-end) (region-end)))) - (when (> (abs (- end beg)) 1) - (rxt-quote-pcre (buffer-substring-no-properties beg end))))) - "")) - (prompt (format "[rg %s] " - (cond ((file-equal-p directory project-root) - (projectile-project-name)) - ((file-equal-p directory default-directory) - "./") - ((file-relative-name directory project-root))))) - (command - (list "rg --no-heading --line-number --color never" - "-S" - (when all-files "-z -uu") - (unless recursive "--maxdepth 1"))) - (helm-ag-base-command (string-join (delq nil command) " "))) - ;; TODO Define our own sources instead - (helm-attrset 'name (format "[rg %s] Searching %s" - (string-join (delq nil (cdr command)) " ") - (abbreviate-file-name directory)) - helm-source-do-ag) - (helm-attrset '+helm-command command helm-source-do-ag) - (cl-letf (((symbol-function 'helm-do-ag--helm) - (lambda () (helm :sources '(helm-source-do-ag) - :prompt prompt - :buffer "*helm-rg*" - :keymap helm-do-ag-map - :input query - :history 'helm-ag--helm-history)))) - (helm-do-ag directory)))) + (require 'helm-rg) + (let ((this-command 'helm-rg) + (helm-rg-default-directory (or in (doom-project-root) default-directory)) + (helm-rg-default-extra-args + (delq nil (append (list (when all-files "-z -uu") + (unless recursive "--maxdepth 1")) + args)))) + (setq deactivate-mark t) + (helm-rg (or query + (when (use-region-p) + (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) + (end (or (bound-and-true-p evil-visual-end) (region-end)))) + (when (> (abs (- end beg)) 1) + (buffer-substring-no-properties beg end)))) + "")))) ;;;###autoload (defun +helm/project-search (&optional arg initial-query directory) diff --git a/modules/completion/helm/autoload/posframe.el b/modules/completion/helm/autoload/posframe.el index 25ac3fd09..9ed53bb27 100644 --- a/modules/completion/helm/autoload/posframe.el +++ b/modules/completion/helm/autoload/posframe.el @@ -23,6 +23,7 @@ bottom, which is easier on the eyes on big displays." (setq +helm--posframe-buffer buffer) :position (point) :poshandler +helm-posframe-handler + :respect-header-line helm-echo-input-in-header-line :width (max (cl-typecase .width (integer .width) diff --git a/modules/completion/helm/config.el b/modules/completion/helm/config.el index 071c33335..5f1666850 100644 --- a/modules/completion/helm/config.el +++ b/modules/completion/helm/config.el @@ -57,8 +57,6 @@ be negative.") helm-mode-line-string nil helm-ff-auto-update-initial-value nil helm-find-files-doc-header nil - ;; Don't override evil-ex's completion - helm-mode-handle-completion-in-region nil ;; Default helm window sizes helm-display-buffer-default-width nil helm-display-buffer-default-height 0.25 @@ -73,11 +71,13 @@ be negative.") :init (when (featurep! +childframe) + ;; If this is set to 'iconify-top-level then Emacs will be minimized upon + ;; helm completion. + (setq iconify-child-frame 'make-invisible) (setq helm-display-function #'+helm-posframe-display-fn)) (let ((fuzzy (featurep! +fuzzy))) (setq helm-M-x-fuzzy-match fuzzy - helm-ag-fuzzy-match fuzzy helm-apropos-fuzzy-match fuzzy helm-apropos-fuzzy-match fuzzy helm-bookmark-show-location fuzzy @@ -101,9 +101,9 @@ be negative.") ;; HACK Doom doesn't support these commands, which invite the user to install ;; the package via ELPA. Force them to use +helm/* instead, because they work ;; out of the box. - (advice-add #'helm-projectile-rg :override #'+helm/rg) - (advice-add #'helm-projectile-ag :override #'+helm/ag) - (advice-add #'helm-projectile-grep :override #'+helm/grep) + (advice-add #'helm-projectile-rg :override #'+helm/project-search) + (advice-add #'helm-projectile-ag :override #'+helm/project-search) + (advice-add #'helm-projectile-grep :override #'+helm/project-search) ;; Hide the modeline (defun +helm--hide-mode-line (&rest _) @@ -114,6 +114,9 @@ be negative.") (advice-add #'helm-display-mode-line :override #'+helm--hide-mode-line) (advice-add #'helm-ag-show-status-default-mode-line :override #'ignore) + ;; Hide minibuffer if `helm-echo-input-in-header-line' + (add-hook 'helm-minibuffer-set-up-hook #'helm-hide-minibuffer-maybe) + ;; Use helpful instead of describe-* to display documentation (dolist (fn '(helm-describe-variable helm-describe-function)) (advice-add fn :around #'doom-use-helpful-a))) @@ -125,14 +128,16 @@ be negative.") :config (helm-flx-mode +1)) -(after! helm-ag - (map! :map helm-ag-edit-map :n "RET" #'compile-goto-error) - (define-key helm-ag-edit-map [remap quit-window] #'helm-ag--edit-abort) - (set-popup-rule! "^\\*helm-ag-edit" :size 0.35 :ttl 0 :quit nil) - ;; Recenter after jumping to match - (advice-add #'helm-ag--find-file-action :after-while #'doom-recenter-a) - ;; And record position before jumping - (advice-add #'helm-ag--find-file-action :around #'doom-set-jump-maybe-a)) +(after! helm-rg + (setq helm-rg-display-buffer-normal-method #'pop-to-buffer) + (set-popup-rule! "^helm-rg-" :ttl nil :select t :size 0.45) + (map! :map helm-rg-map + "C-c C-e" #'helm-rg--bounce) + (map! :map helm-rg--bounce-mode-map + "q" #'kill-current-buffer + "C-c C-c" (λ! (helm-rg--bounce-dump) (kill-current-buffer)) + "C-x C-c" #'helm-rg--bounce-dump-current-file + "C-c C-k" #'kill-current-buffer)) ;;;###package helm-bookmark @@ -176,6 +181,7 @@ be negative.") (set-keymap-parent helm-projectile-find-file-map helm-map)) +(setq ivy-height 20) ; for `swiper-isearch' (after! swiper-helm (setq swiper-helm-display-function (lambda (buf &optional _resume) (pop-to-buffer buf))) diff --git a/modules/completion/helm/packages.el b/modules/completion/helm/packages.el index eb343b566..444ccb8a7 100644 --- a/modules/completion/helm/packages.el +++ b/modules/completion/helm/packages.el @@ -1,16 +1,18 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/helm/packages.el -(package! helm) -(package! helm-ag) -(package! helm-c-yasnippet) -(package! helm-company) -(package! helm-describe-modes :recipe (:host github :repo "emacs-helm/helm-describe-modes")) -(package! helm-projectile) -(package! swiper-helm) +(package! helm :pin "8f56312053") +(package! helm-rg :pin "785a80fe5c") +(package! helm-c-yasnippet :pin "65ca732b51") +(package! helm-company :pin "6eb5c2d730") +(package! helm-describe-modes + :recipe (:host github :repo "emacs-helm/helm-describe-modes") + :pin "11fb36af11") +(package! helm-projectile :pin "5328b74ddd") +(package! swiper-helm :pin "93fb6db87b") (when (featurep! +fuzzy) - (package! helm-flx)) + (package! helm-flx :pin "6640fac5cb")) (when (featurep! +childframe) - (package! posframe)) + (package! posframe :pin "087a7fc3c8")) (when (featurep! :lang org) - (package! helm-org)) + (package! helm-org :pin "8457e1e462")) diff --git a/modules/completion/ido/packages.el b/modules/completion/ido/packages.el index 368665425..e90c485bd 100644 --- a/modules/completion/ido/packages.el +++ b/modules/completion/ido/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/ido/packages.el -(package! flx-ido) -(package! ido-completing-read+) -(package! ido-vertical-mode) -(package! crm-custom) +(package! flx-ido :pin "17f5c9cb2a") +(package! ido-completing-read+ :pin "74861eabd0") +(package! ido-vertical-mode :pin "16c4c1a112") +(package! crm-custom :pin "f1aaccf643") diff --git a/modules/completion/ivy/README.org b/modules/completion/ivy/README.org index 0bc51166f..b21b92c61 100644 --- a/modules/completion/ivy/README.org +++ b/modules/completion/ivy/README.org @@ -14,7 +14,7 @@ - [[#arch-linux][Arch Linux]] - [[#opensuse][openSUSE]] - [[#features][Features]] - - [[#jump-to-file-project-navigation][Jump-to-file project navigation]] + - [[#jump-to-navigation][Jump-to navigation]] - [[#project-search--replace][Project search & replace]] - [[#in-buffer-searching][In-buffer searching]] - [[#ivy-integration-for-various-completing-commands][Ivy integration for various completing commands]] @@ -88,7 +88,7 @@ sudo zypper install ripgrep Ivy and its ilk are large plugins. Covering everything about them is outside of this documentation's scope, so only Doom-specific Ivy features are listed here: -** Jump-to-file project navigation +** Jump-to navigation Inspired by Sublime Text's jump-to-anywhere, CtrlP/Unite in Vim, and Textmate's Command-T, this module provides similar functionality by bringing ~projectile~ and ~ivy~ together. @@ -99,22 +99,23 @@ https://assets.doomemacs.org/completion/ivy/projectile.png |----------------------+-------------------------------------| | =SPC p f=, =SPC SPC= | Jump to file in project | | =SPC f f=, =SPC .= | Jump to file from current directory | +| =SPC s i= | Jump to symbol in file | ** Project search & replace This module provides interactive text search and replace using ripgrep. -| Keybind | Description | -|-----------+---------------------------------| -| =SPC s b= | Search the current buffer | -| =SPC s p= | Search project | -| =SPC s d= | Search this directory | -| =SPC p t= | List all TODO/FIXMEs in project | +| Keybind | Description | +|-----------+--------------------------| +| =SPC s p= | Search project | +| =SPC s P= | Search another project | +| =SPC s d= | Search this directory | +| =SPC s D= | Search another directory | https://assets.doomemacs.org/completion/ivy/search.png -The universal argument (=SPC u= for evil users; =C-u= otherwise) changes the -behavior of these commands, instructing the underlying search engine to include -ignored files. +Prefixing these keys with the universal argument (=SPC u= for evil users; =C-u= +otherwise) changes the behavior of these commands, instructing the underlying +search engine to include ignored files. This module also provides Ex Commands for evil users: @@ -123,15 +124,16 @@ This module also provides Ex Commands for evil users: | ~:pg[rep][!] [QUERY]~ | Search project (if ~!~, include hidden files) | | ~:pg[rep]d[!] [QUERY]~ | Search from current directory (if ~!~, don't search recursively) | -The optional BANG functions is equivalent to the universal argument for the -previous commands. +The optional `!` is equivalent to the universal argument for the previous +commands. ----- -While in a search these extra keybindings are available to you: +These keybindings are available while a search is active: | Keybind | Description | |-----------+-----------------------------------------------| +| =C-c C-o= | Open a buffer with your search results | | =C-c C-e= | Open a writable buffer of your search results | | =C-SPC= | Preview the current candidate | | =M-RET= | Open the selected candidate in other-window | @@ -146,8 +148,9 @@ https://assets.doomemacs.org/completion/ivy/search-replace.png The =swiper= package provides an interactive buffer search powered by ivy. It can be invoked with: -+ =SPC s s= -+ =SPC s S= (uses thing at point as initial input) ++ =SPC s s= (~swiper-isearch~) ++ =SPC s S= (~swiper-isearch-thing-at-point~) ++ =SPC s b= (~swiper~) + ~:sw[iper] [QUERY]~ https://assets.doomemacs.org/completion/ivy/swiper.png diff --git a/modules/completion/ivy/autoload/ivy.el b/modules/completion/ivy/autoload/ivy.el index c596e4168..3c34ae2c8 100644 --- a/modules/completion/ivy/autoload/ivy.el +++ b/modules/completion/ivy/autoload/ivy.el @@ -21,16 +21,7 @@ Buffers that are considered unreal (see `doom-real-buffer-p') are dimmed with `+ivy-buffer-unreal-face'." (let ((b (get-buffer candidate))) (when (null uniquify-buffer-name-style) - (when-let* ((file-path (buffer-file-name b)) - (uniquify-buffer-name-style 'forward)) - (setq candidate - (uniquify-get-proposed-name - (replace-regexp-in-string "<[0-9]+>$" "" (buffer-name b)) - (directory-file-name - (if file-path - (file-name-directory file-path) - default-directory)) - 1)))) + (setq candidate (replace-regexp-in-string "<[0-9]+>$" "" candidate))) (cond ((ignore-errors (file-remote-p (buffer-local-value 'default-directory b))) @@ -165,10 +156,10 @@ If ARG (universal argument), open selection in other-window." (user-error "No completion session is active")) (require 'wgrep) (let ((caller (ivy-state-caller ivy-last))) - (if-let* ((occur-fn (plist-get +ivy-edit-functions caller))) + (if-let (occur-fn (plist-get +ivy-edit-functions caller)) (ivy-exit-with-action (lambda (_) (funcall occur-fn))) - (if-let* ((occur-fn (plist-get ivy--occurs-list caller))) + (if-let (occur-fn (plist-get ivy--occurs-list caller)) (let ((buffer (generate-new-buffer (format "*ivy-occur%s \"%s\"*" (if caller (concat " " (prin1-to-string caller)) "") @@ -222,22 +213,26 @@ non-project, `projectile-find-file' if in a big project (more than The point of this is to avoid Emacs locking up indexing massive file trees." (interactive) - (call-interactively - (cond ((or (file-equal-p default-directory "~") - (when-let (proot (doom-project-root)) - (file-equal-p proot "~"))) - #'counsel-find-file) + ;; Spoof the command so that ivy/counsel will display the (well fleshed-out) + ;; actions list for `counsel-find-file' on C-o. The actions list for the other + ;; commands aren't as well configured or are empty. + (let ((this-command 'counsel-find-file)) + (call-interactively + (cond ((or (file-equal-p default-directory "~") + (when-let (proot (doom-project-root)) + (file-equal-p proot "~"))) + #'counsel-find-file) - ((doom-project-p) - (let ((files (projectile-current-project-files))) - (if (<= (length files) ivy-sort-max-size) - #'counsel-projectile-find-file - #'projectile-find-file))) + ((doom-project-p) + (let ((files (projectile-current-project-files))) + (if (<= (length files) ivy-sort-max-size) + #'counsel-projectile-find-file + #'projectile-find-file))) - (#'counsel-file-jump)))) + (#'counsel-file-jump))))) ;;;###autoload -(cl-defun +ivy-file-search (&key query in all-files (recursive t)) +(cl-defun +ivy-file-search (&key query in all-files (recursive t) prompt args) "Conduct a file search using ripgrep. :query STRING @@ -251,36 +246,35 @@ The point of this is to avoid Emacs locking up indexing massive file trees." (unless (executable-find "rg") (user-error "Couldn't find ripgrep in your PATH")) (require 'counsel) - (let* ((ivy-more-chars-alist '((t . 1))) + (let* ((this-command 'counsel-rg) (project-root (or (doom-project-root) default-directory)) (directory (or in project-root)) - (default-directory directory) (args (concat (if all-files " -uu") - (unless recursive " --maxdepth 1")))) + (unless recursive " --maxdepth 1") + " " + (mapconcat #'shell-quote-argument args " ")))) + (setq deactivate-mark t) (counsel-rg - (or (if query query) - (when (use-region-p) - (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) - (end (or (bound-and-true-p evil-visual-end) (region-end)))) - (when (> (abs (- end beg)) 1) - (let ((query (buffer-substring-no-properties beg end))) - ;; Escape characters that are special to ivy searches - (replace-regexp-in-string "[! |]" (lambda (substr) - (cond ((and (string= substr " ") - (not (featurep! +fuzzy))) - " ") - ((string= substr "|") - "\\\\\\\\|") - ((concat "\\\\" substr)))) - (rxt-quote-pcre query))))))) + (or query + (when (doom-region-active-p) + (replace-regexp-in-string + "[! |]" (lambda (substr) + (cond ((and (string= substr " ") + (not (featurep! +fuzzy))) + " ") + ((string= substr "|") + "\\\\\\\\|") + ((concat "\\\\" substr)))) + (rxt-quote-pcre (doom-thing-at-point-or-region))))) directory args - (format "rg%s %s" - args - (cond ((equal directory default-directory) - "./") - ((equal directory project-root) - (projectile-project-name)) - ((file-relative-name directory project-root))))))) + (or prompt + (format "rg%s [%s]: " + args + (cond ((equal directory default-directory) + "./") + ((equal directory project-root) + (projectile-project-name)) + ((file-relative-name directory project-root)))))))) ;;;###autoload (defun +ivy/project-search (&optional arg initial-query directory) diff --git a/modules/completion/ivy/config.el b/modules/completion/ivy/config.el index 4cb329f78..0d1eef6d6 100644 --- a/modules/completion/ivy/config.el +++ b/modules/completion/ivy/config.el @@ -7,16 +7,6 @@ When nil, don't preview anything. When non-nil, preview non-virtual buffers. When 'everything, also preview virtual buffers") -(defvar +ivy-project-search-engines '(rg ag) - "What search tools for `+ivy/project-search' (and `+ivy-file-search' when no -ENGINE is specified) to try, and in what order. - -To disable a particular tool, remove it from this list. To prioritize a tool -over others, move it to the front of the list. Later duplicates in this list are -silently ignored. - -If you want to already use git-grep or grep, set this to nil.") - (defvar +ivy-buffer-unreal-face 'font-lock-comment-face "The face for unreal buffers in `ivy-switch-to-buffer'.") @@ -24,18 +14,6 @@ If you want to already use git-grep or grep, set this to nil.") "A plist mapping ivy/counsel commands to commands that generate an editable results buffer.") -(defvar +ivy-standard-search-fn - (if (featurep! +prescient) - #'+ivy-prescient-non-fuzzy - #'ivy--regex-plus) - "Function to use for non-fuzzy search commands.") - -(defvar +ivy-alternative-search-fn - (cond ((featurep! +prescient) #'ivy-prescient-re-builder) - ((featurep! +fuzzy) #'ivy--regex-fuzzy) - (#'ivy--regex-ignore-order)) - "Function to use for fuzzy search commands.") - ;; ;;; Packages @@ -43,15 +21,24 @@ results buffer.") (use-package! ivy :after-call pre-command-hook :init - (setq ivy-re-builders-alist - `(,@(cl-loop for cmd in '(counsel-ag - counsel-rg - counsel-grep - swiper - swiper-isearch) - collect (cons cmd +ivy-standard-search-fn)) - ;; Ignore order for non-fuzzy searches by default - (t . ,+ivy-alternative-search-fn))) + (let ((standard-search-fn + (if (featurep! +prescient) + #'+ivy-prescient-non-fuzzy + #'ivy--regex-plus)) + (alt-search-fn + (if (featurep! +fuzzy) + #'ivy--regex-fuzzy + ;; Ignore order for non-fuzzy searches by default + #'ivy--regex-ignore-order))) + (setq ivy-re-builders-alist + `((counsel-rg . ,standard-search-fn) + (swiper . ,standard-search-fn) + (swiper-isearch . ,standard-search-fn) + (t . ,alt-search-fn)) + ivy-more-chars-alist + '((counsel-rg . 1) + (counsel-search . 2) + (t . 3)))) (define-key! [remap switch-to-buffer] #'+ivy/switch-buffer @@ -75,7 +62,7 @@ results buffer.") ;; ...but if that ever changes, show their full path ivy-virtual-abbreviate 'full ;; don't quit minibuffer on delete-error - ivy-on-del-error-function nil + ivy-on-del-error-function #'ignore ;; enable ability to select prompt (alternative to `ivy-immediate-done') ivy-use-selectable-prompt t) @@ -110,7 +97,9 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (let ((completion-in-region-function #'completion--in-region)) (apply orig-fn args))) - (define-key ivy-minibuffer-map (kbd "C-c C-e") #'+ivy/woccur) + (define-key! ivy-minibuffer-map + "C-c C-e" #'+ivy/woccur + [remap doom/delete-backward-word] #'ivy-backward-kill-word) (ivy-mode +1) @@ -128,6 +117,8 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (use-package! ivy-rich :after ivy :config + (setq ivy-rich-parse-remote-buffer nil) + (when (featurep! +icons) (cl-pushnew '(+ivy-rich-buffer-icon) (cadr (plist-get ivy-rich-display-transformers-list @@ -182,61 +173,52 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (define-key! [remap apropos] #'counsel-apropos [remap bookmark-jump] #'counsel-bookmark + [remap compile] #'+ivy/compile + [remap describe-bindings] #'counsel-descbinds [remap describe-face] #'counsel-faces [remap describe-function] #'counsel-describe-function [remap describe-variable] #'counsel-describe-variable - [remap describe-bindings] #'counsel-descbinds - [remap set-variable] #'counsel-set-variable + [remap evil-ex-registers] #'counsel-evil-registers + [remap evil-show-marks] #'counsel-mark-ring [remap execute-extended-command] #'counsel-M-x [remap find-file] #'counsel-find-file [remap find-library] #'counsel-find-library - [remap info-lookup-symbol] #'counsel-info-lookup-symbol [remap imenu] #'counsel-imenu - [remap recentf-open-files] #'counsel-recentf - [remap swiper] #'counsel-grep-or-swiper - [remap evil-ex-registers] #'counsel-evil-registers - [remap evil-show-marks] #'counsel-mark-ring - [remap yank-pop] #'counsel-yank-pop + [remap info-lookup-symbol] #'counsel-info-lookup-symbol + [remap load-theme] #'counsel-load-theme [remap locate] #'counsel-locate + [remap org-set-tags-command] #'counsel-org-tag + [remap projectile-compile-project] #'+ivy/project-compile + [remap recentf-open-files] #'counsel-recentf + [remap set-variable] #'counsel-set-variable + [remap swiper] #'counsel-grep-or-swiper [remap unicode-chars-list-chars] #'counsel-unicode-char - [remap compile] #'+ivy/compile - [remap projectile-compile-project] #'+ivy/project-compile) + [remap yank-pop] #'counsel-yank-pop) :config (set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil) + ;; HACK Fix an issue where `counsel-projectile-find-file-action' would try to + ;; open a candidate in an occur buffer relative to the wrong buffer, + ;; causing it to fail to find the file we want. + (defadvice! +ivy--run-from-ivy-directory-a (orig-fn &rest args) + :around #'counsel-projectile-find-file-action + (let ((default-directory (ivy-state-directory ivy-last))) + (apply orig-fn args))) + ;; Don't use ^ as initial input. Set this here because `counsel' defines more ;; of its own, on top of the defaults. (setq ivy-initial-inputs-alist nil) - ;; REVIEW Move this somewhere else and perhaps generalize this so both - ;; ivy/helm users can enjoy it. - (defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args) - "Change `counsel-file-jump' to use fd or ripgrep, if they are available." - :override #'counsel--find-return-list - (cl-destructuring-bind (find-program . args) - (cond ((executable-find "fd") - (cons "fd" (list "-t" "f" "-E" ".git"))) - ((executable-find "rg") - (cons "rg" (list "--files" "--hidden" "--no-messages"))) - ((cons find-program args))) - (unless (listp args) - (user-error "`counsel-file-jump-args' is a list now, please customize accordingly.")) - (counsel--call - (cons find-program args) - (lambda () - (goto-char (point-min)) - (let ((offset (if (member find-program '("fd" "rg")) 0 2)) - files) - (while (< (point) (point-max)) - (push (buffer-substring - (+ offset (line-beginning-position)) (line-end-position)) files) - (forward-line 1)) - (nreverse files)))))) - ;; Integrate with `helpful' (setq counsel-describe-function-function #'helpful-callable counsel-describe-variable-function #'helpful-variable) + ;; Record in jumplist when opening files via counsel-{ag,rg,pt,git-grep} + (add-hook 'counsel-grep-post-action-hook #'better-jumper-set-jump) + (ivy-add-actions + 'counsel-rg ; also applies to `counsel-rg' + '(("O" +ivy-git-grep-other-window-action "open in other window"))) + ;; Make `counsel-compile' projectile-aware (if you prefer it over ;; `+ivy/compile' and `+ivy/project-compile') (add-to-list 'counsel-compile-root-functions #'projectile-project-root) @@ -244,51 +226,62 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." ;; Persist `counsel-compile' history (add-to-list 'savehist-additional-variables 'counsel-compile-history)) - ;; Use spotlight on mac for `counsel-locate' by default + ;; `counsel-imenu' -- no sorting for imenu. Sort it by appearance in page. + (add-to-list 'ivy-sort-functions-alist '(counsel-imenu)) + + ;; `counsel-locate' (when IS-MAC + ;; Use spotlight on mac by default since it doesn't need any additional setup (setq counsel-locate-cmd #'counsel-locate-cmd-mdfind)) + ;; `swiper' ;; Don't mess with font-locking on the dashboard; it causes breakages (add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode) - ;; Record in jumplist when opening files via counsel-{ag,rg,pt,git-grep} - (add-hook 'counsel-grep-post-action-hook #'better-jumper-set-jump) - - ;; Factories - (defun +ivy-action-reloading (cmd) - (lambda (x) - (funcall cmd x) - (ivy--reset-state ivy-last))) - - (defun +ivy-action-given-file (cmd prompt) - (lambda (source) - (let* ((enable-recursive-minibuffers t) - (target (read-file-name (format "%s %s to:" prompt source)))) - (funcall cmd source target 1)))) - - ;; Configure `counsel-find-file' + ;; `counsel-find-file' (setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)") - (ivy-add-actions - 'counsel-find-file - `(("b" counsel-find-file-cd-bookmark-action "cd bookmark") - ("s" counsel-find-file-as-root "open as root") - ("m" counsel-find-file-mkdir-action "mkdir") - ("c" ,(+ivy-action-given-file #'copy-file "Copy file") "copy file") - ("d" ,(+ivy-action-reloading #'+ivy-confirm-delete-file) "delete") - ("r" (lambda (path) (rename-file path (read-string "New name: "))) "rename") - ("R" ,(+ivy-action-reloading (+ivy-action-given-file #'rename-file "Move")) "move") - ("f" find-file-other-window "other window") - ("F" find-file-other-frame "other frame") - ("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory)))) "insert relative path") - ("P" (lambda (path) (with-ivy-window (insert path))) "insert absolute path") - ("l" (lambda (path) "Insert org-link with relative path" - (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory))))) "insert org-link (rel. path)") - ("L" (lambda (path) "Insert org-link with absolute path" - (with-ivy-window (insert (format "[[%s]]" path)))) "insert org-link (abs. path)"))) + (dolist (fn '(counsel-rg counsel-find-file)) + (ivy-add-actions + fn '(("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory)))) + "insert relative path") + ("P" (lambda (path) (with-ivy-window (insert path))) + "insert absolute path") + ("l" (lambda (path) (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory))))) + "insert relative org-link") + ("L" (lambda (path) (with-ivy-window (insert (format "[[%s]]" path)))) + "Insert absolute org-link")))) - (ivy-add-actions - 'counsel-ag ; also applies to `counsel-rg' - '(("O" +ivy-git-grep-other-window-action "open in other window")))) + (ivy-add-actions 'counsel-file-jump (plist-get ivy--actions-list 'counsel-find-file)) + + ;; `counsel-search': use normal page for displaying results, so that we see + ;; custom ddg themes (if one is set). + (setf (nth 1 (alist-get 'ddg counsel-search-engines-alist)) + "https://duckduckgo.com/?q=") + + ;; REVIEW Move this somewhere else and perhaps generalize this so both + ;; ivy/helm users can enjoy it. + (defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args) + "Change `counsel-file-jump' to use fd or ripgrep, if they are available." + :override #'counsel--find-return-list + (cl-destructuring-bind (find-program . args) + (cond ((executable-find doom-projectile-fd-binary) + (cons doom-projectile-fd-binary (list "-t" "f" "-E" ".git"))) + ((executable-find "rg") + (split-string (format counsel-rg-base-command "--files --no-messages") " " t)) + ((cons find-program args))) + (unless (listp args) + (user-error "`counsel-file-jump-args' is a list now, please customize accordingly.")) + (counsel--call + (cons find-program args) + (lambda () + (goto-char (point-min)) + (let ((offset (if (member find-program (list "rg" doom-projectile-fd-binary)) 0 2)) + files) + (while (< (point) (point-max)) + (push (buffer-substring + (+ offset (line-beginning-position)) (line-end-position)) files) + (forward-line 1)) + (nreverse files))))))) (use-package! counsel-projectile @@ -310,7 +303,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." #'+ivy/projectile-find-file) ;; no highlighting visited files; slows down the filtering - (ivy-set-display-transformer #'counsel-projectile-find-file nil)) + (ivy-set-display-transformer #'counsel-projectile-find-file nil) + + (if (featurep! +prescient) + (setq counsel-projectile-sort-files t))) (use-package! wgrep @@ -332,8 +328,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (setf (alist-get t ivy-posframe-display-functions-alist) #'+ivy-display-at-frame-center-near-bottom-fn) - ;; posframe doesn't work well with async sources - (dolist (fn '(swiper counsel-ag counsel-grep counsel-git-grep)) + ;; posframe doesn't work well with async sources (the posframe will + ;; occasionally stop responding/redrawing), and causes violent resizing of the + ;; posframe. + (dolist (fn '(swiper counsel-rg counsel-grep counsel-git-grep)) (setf (alist-get fn ivy-posframe-display-functions-alist) #'ivy-display-function-fallback))) @@ -353,7 +351,6 @@ evil-ex-specific constructs, so we disable it solely in evil-ex." (if (featurep! +fuzzy) '(literal regexp initialism fuzzy) '(literal regexp initialism)) - ivy-prescient-enable-filtering nil ; we do this ourselves ivy-prescient-retain-classic-highlighting t) :config diff --git a/modules/completion/ivy/packages.el b/modules/completion/ivy/packages.el index 5571641fd..7fb20ae1b 100644 --- a/modules/completion/ivy/packages.el +++ b/modules/completion/ivy/packages.el @@ -1,22 +1,23 @@ ;; -*- no-byte-compile: t; -*- ;;; completion/ivy/packages.el -(package! amx) +(package! swiper :pin "7084d60312") (package! ivy) -(package! counsel) -(package! counsel-projectile) -(package! swiper) (package! ivy-hydra) -(package! ivy-rich) -(package! wgrep) +(package! counsel) + +(package! amx :pin "e512e74e83") +(package! counsel-projectile :pin "b556ed8995") +(package! ivy-rich :pin "af43abad5c") +(package! wgrep :pin "5977b8e000") (if (featurep! +prescient) - (package! ivy-prescient) + (package! ivy-prescient :pin "7fd8c3b802") (when (featurep! +fuzzy) - (package! flx))) + (package! flx :pin "17f5c9cb2a"))) (when (featurep! +childframe) - (package! ivy-posframe)) + (package! ivy-posframe :pin "6d697ff00a")) (when (featurep! +icons) - (package! all-the-icons-ivy)) + (package! all-the-icons-ivy :pin "a70cbfa1ef")) diff --git a/modules/config/default/+emacs-bindings.el b/modules/config/default/+emacs-bindings.el index e63e544ce..9d884186a 100644 --- a/modules/config/default/+emacs-bindings.el +++ b/modules/config/default/+emacs-bindings.el @@ -25,28 +25,80 @@ (:prefix ("l" . "")) ; bound locally (:prefix ("!" . "checkers")) ; bound by flycheck + ;;; c --- code + (:prefix-map ("c" . "code") + :desc "Compile" "c" #'compile + :desc "Recompile" "C" #'recompile + :desc "Jump to definition" "d" #'+lookup/definition + :desc "Jump to references" "D" #'+lookup/references + :desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region + :desc "Evaluate & replace region" "E" #'+eval/region-and-replace + :desc "Format buffer/region" "f" #'+format/region-or-buffer + :desc "Jump to documentation" "k" #'+lookup/documentation + :desc "Send to repl" "s" #'+eval/send-region-to-repl + :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace + :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines + :desc "List errors" "x" #'flymake-show-diagnostics-buffer + (:when (featurep! :tools flycheck) + :desc "List errors" "x" #'flycheck-list-errors) + (:when (featurep! :tools lsp) + :desc "LSP Code actions" "a" #'lsp-execute-code-action + :desc "LSP Format buffer/region" "F" #'+default/lsp-format-region-or-buffer + :desc "LSP Organize imports" "i" #'lsp-organize-imports + :desc "LSP Rename" "r" #'lsp-rename + (:when (featurep! :completion ivy) + :desc "Jump to symbol in current workspace" "j" #'lsp-ivy-workspace-symbol + :desc "Jump to symbol in any workspace" "J" #'lsp-ivy-global-workspace-symbol) + (:when (featurep! :completion helm) + :desc "Jump to symbol in current workspace" "j" #'helm-lsp-workspace-symbol + :desc "Jump to symbol in any workspace" "J" #'helm-lsp-global-workspace-symbol))) + ;;; f --- file (:prefix-map ("f" . "file") - :desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig - :desc "Find other file" "a" #'projectile-find-other-file - :desc "Browse private config" "c" #'doom/open-private-config + :desc "Copy this file" "C" #'doom/copy-this-file :desc "Find file in private config" "C" #'doom/find-file-in-private-config - :desc "Find directory" "d" #'dired + :desc "Delete this file" "D" #'doom/delete-this-file :desc "Find file in emacs.d" "e" #'+default/find-in-emacsd :desc "Browse emacs.d" "E" #'+default/browse-emacsd - :desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file) - :desc "Find file in other project" "F" #'doom/browse-in-other-project - :desc "Delete this file" "K" #'doom/delete-this-file - :desc "Move this file" "m" #'doom/move-this-file - :desc "Find file in project" "p" #'projectile-find-file - :desc "Find file in other project" "P" #'doom/find-file-in-other-project + :desc "Find file from here" "f" #'+default/find-file-under-here + :desc "Rename/move this file" "m" #'doom/move-this-file + :desc "Browse private config" "p" #'doom/open-private-config + :desc "Browse private config" "P" #'doom/open-private-config :desc "Recent files" "r" #'recentf-open-files :desc "Recent project files" "R" #'projectile-recentf :desc "Sudo this file" "s" #'doom/sudo-this-file :desc "Sudo find file" "S" #'doom/sudo-find-file :desc "Yank filename" "y" #'+default/yank-buffer-filename :desc "Open scratch buffer" "x" #'doom/open-scratch-buffer - :desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer) + :desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer + (:when (featurep! :tools editorconfig) + :desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig)) + + ;;; r --- remote + (:when (featurep! :tools upload) + (:prefix-map ("r" . "remote") + :desc "Upload local" "u" #'ssh-deploy-upload-handler + :desc "Upload local (force)" "U" #'ssh-deploy-upload-handler-forced + :desc "Download remote" "d" #'ssh-deploy-download-handler + :desc "Diff local & remote" "D" #'ssh-deploy-diff-handler + :desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler + :desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler)) + + ;;; s --- search + (:prefix-map ("s" . "search") + :desc "Search buffer" "b" #'swiper + :desc "Search current directory" "d" #'+default/search-cwd + :desc "Search other directory" "D" #'+default/search-other-cwd + :desc "Locate file" "f" #'locate + :desc "Jump to symbol" "i" #'imenu + :desc "Jump to visible link" "l" #'link-hint-open-link + :desc "Jump to link" "L" #'ffap-menu + :desc "Jump list" "j" #'evil-show-jumps + :desc "Jump to mark" "m" #'evil-show-marks + :desc "Search project" "p" #'+default/search-project + :desc "Search other project" "P" #'+default/search-other-project + :desc "Search buffer" "s" #'swiper-isearch + :desc "Search buffer for thing at point" "S" #'swiper-isearch-thing-at-point) ;;; g --- lookup (:when (featurep! :tools lookup) @@ -59,24 +111,72 @@ "i" #'+lookup/in-docsets "I" #'+lookup/in-all-docsets)) - ;;; o --- org - "o" nil ; we need to unbind it first as Org claims this - (:prefix-map ("o". "org") - :desc "Do what I mean" "o" #'+org/dwim-at-point - :desc "Display inline images" "i" #'org-display-inline-images - :desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point - (:prefix ("a" . "org agenda") - :desc "Agenda" "a" #'org-agenda - :desc "Todo list" "t" #'org-todo-list - :desc "Tags view" "m" #'org-tags-view - :desc "View search" "v" #'org-search-view) - :desc "Browse notes" "f" #'+default/browse-notes - :desc "Search org-directory" "s" #'+default/org-notes-search - :desc "Switch org buffers" "b" #'org-switchb - :desc "Capture" "c" #'org-capture - :desc "Goto capture" "C" #'org-capture-goto-target - :desc "Link store" "l" #'org-store-link - :desc "Sync org caldav" "S" #'org-caldav-sync) + ;;; i --- insert + (:prefix-map ("i" . "insert") + :desc "Current file name" "f" #'+default/insert-file-path + :desc "Current file path" "F" (λ!! #'+default/insert-file-path t) + :desc "Snippet" "s" #'yas-insert-snippet + :desc "Unicode" "u" #'unicode-chars-list-chars + :desc "From clipboard" "y" #'+default/yank-pop) + + ;;; n --- notes + (:prefix-map ("n" . "notes") + :desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point + :desc "Org agenda" "a" #'org-agenda + :desc "Find file in notes" "f" #'+default/find-in-notes + :desc "Browse notes" "F" #'+default/browse-notes + :desc "Org store link" "l" #'org-store-link + :desc "Tags search" "m" #'org-tags-view + :desc "Org capture" "n" #'org-capture + :desc "Goto capture" "N" #'org-capture-goto-target + :desc "Active org-clock" "o" #'org-clock-goto + :desc "Todo list" "t" #'org-todo-list + :desc "Search notes" "s" #'+default/org-notes-search + :desc "Search org agenda headlines" "S" #'+default/org-notes-headlines + :desc "View search" "v" #'org-search-view + :desc "Org export to clipboard" "y" #'+org/export-to-clipboard + :desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text + (:when (featurep! :lang org +journal) + (:prefix ("j" . "journal") + :desc "New Entry" "j" #'org-journal-new-entry + :desc "Search Forever" "s" #'org-journal-search-forever))) + + ;;; o --- open + "o" nil ; we need to unbind it first as Org claims this prefix + (:prefix-map ("o" . "open") + :desc "Browser" "b" #'browse-url-of-file + :desc "Debugger" "d" #'+debugger/start + :desc "New frame" "f" #'make-frame + :desc "REPL" "r" #'+eval/open-repl-other-window + :desc "REPL (same window)" "R" #'+eval/open-repl-same-window + :desc "Dired" "-" #'dired-jump + (:when (featurep! :ui neotree) + :desc "Project sidebar" "p" #'+neotree/open + :desc "Find file in project sidebar" "P" #'+neotree/find-this-file) + (:when (featurep! :ui treemacs) + :desc "Project sidebar" "p" #'+treemacs/toggle + :desc "Find file in project rsidebar" "P" #'+treemacs/find-file) + (:when (featurep! :term shell) + :desc "Toggle shell popup" "t" #'+shell/toggle + :desc "Open shell here" "T" #'+shell/here) + (:when (featurep! :term term) + :desc "Toggle terminal popup" "t" #'+term/toggle + :desc "Open terminal here" "T" #'+term/here) + (:when (featurep! :term vterm) + :desc "Toggle vterm popup" "t" #'+vterm/toggle + :desc "Open vterm here" "T" #'+vterm/here) + (:when (featurep! :term eshell) + :desc "Toggle eshell popup" "e" #'+eshell/toggle + :desc "Open eshell here" "E" #'+eshell/here) + (:when (featurep! :tools macos) + :desc "Reveal in Finder" "o" #'+macos/reveal-in-finder + :desc "Reveal project in Finder" "O" #'+macos/reveal-project-in-finder + :desc "Send to Transmit" "u" #'+macos/send-to-transmit + :desc "Send project to Transmit" "U" #'+macos/send-project-to-transmit + :desc "Send to Launchbar" "l" #'+macos/send-to-launchbar + :desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar) + (:when (featurep! :tools docker) + :desc "Docker" "D" #'docker)) ;;; p --- project (:prefix ("p" . "project") @@ -91,10 +191,16 @@ ;;; q --- quit/restart (:prefix-map ("q" . "quit/restart") + :desc "Restart emacs server" "d" #'+default/restart-server + :desc "Delete frame" "f" #'delete-frame + :desc "Clear current frame" "F" #'doom/kill-all-buffers + :desc "Kill Emacs (and daemon)" "K" #'save-buffers-kill-emacs :desc "Quit Emacs" "q" #'kill-emacs :desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal - (:when (featurep! :ui workspaces) - :desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit) + :desc "Quick save current session" "s" #'doom/quicksave-session + :desc "Restore last session" "l" #'doom/quickload-session + :desc "Save session to file" "S" #'doom/save-session + :desc "Restore session from file" "L" #'doom/load-session :desc "Restart & restore Emacs" "r" #'doom/restart-and-restore :desc "Restart Emacs" "R" #'doom/restart) @@ -107,20 +213,33 @@ :desc "Create Temp Template" "c" #'aya-create :desc "Use Temp Template" "e" #'aya-expand) - (:prefix-map ("t" . "terminal") - (:when (featurep! :term term) - :desc "Toggle term popup" "t" #'+term/toggle - :desc "Open term here" "T" #'+term/here) - (:when (featurep! :term vterm) - :desc "Toggle vterm popup" "t" #'+vterm/toggle - :desc "Open vterm here" "T" #'+vterm/here) - (:when (featurep! :term eshell) - :desc "Toggle eshell popup" "t" #'+eshell/toggle - :desc "Open eshell here" "T" #'+eshell/here)) + ;;; t --- toggle + (:prefix-map ("t" . "toggle") + :desc "Big mode" "b" #'doom-big-font-mode + :desc "Flymake" "f" #'flymake-mode + :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen + :desc "Indent style" "I" #'doom/toggle-indent-style + :desc "Line numbers" "l" #'doom/toggle-line-numbers + :desc "Word-wrap mode" "w" #'+word-wrap-mode + (:when (featurep! :tools flycheck) + :desc "Flycheck" "f" #'flycheck-mode) + (:when (featurep! :ui indent-guides) + :desc "Indent guides" "i" #'highlight-indent-guides-mode) + (:when (featurep! :lang org +present) + :desc "org-tree-slide mode" "p" #'+org-present/start) + :desc "Read-only mode" "r" #'read-only-mode + (:when (featurep! :tools flyspell) + :desc "Flyspell" "s" #'flyspell-mode) + (:when (featurep! :lang org +pomodoro) + :desc "Pomodoro timer" "t" #'org-pomodoro) + (:when (featurep! :ui zen) + :desc "Zen mode" "z" #'writeroom-mode)) ;;; v --- versioning (:prefix-map ("v" . "versioning") :desc "Git revert file" "R" #'vc-revert + :desc "Kill link to remote" "y" #'+vc/browse-at-remote-kill-file-or-region + :desc "Kill link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage (:when (featurep! :ui vc-gutter) :desc "Git revert hunk" "r" #'git-gutter:revert-hunk :desc "Git stage hunk" "s" #'git-gutter:stage-hunk @@ -133,7 +252,7 @@ :desc "Magit status" "g" #'magit-status :desc "Magit file delete" "x" #'magit-file-delete :desc "Magit blame" "B" #'magit-blame-addition - :desc "Magit clone" "C" #'+magit/clone + :desc "Magit clone" "C" #'magit-clone :desc "Magit fetch" "F" #'magit-fetch :desc "Magit buffer log" "L" #'magit-log :desc "Git stage file" "S" #'magit-stage-file @@ -145,7 +264,8 @@ :desc "Find issue" "i" #'forge-visit-issue :desc "Find pull request" "p" #'forge-visit-pullreq) (:prefix ("o" . "open in browser") - :desc "Browse region or line" "." #'+vc/git-browse-region-or-line + :desc "Browse file or region" "." #'+vc/browse-at-remote-file-or-region + :desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage :desc "Browse remote" "r" #'forge-browse-remote :desc "Browse commit" "c" #'forge-browse-commit :desc "Browse an issue" "i" #'forge-browse-issue @@ -162,7 +282,7 @@ :desc "List notifications" "n" #'forge-list-notifications) (:prefix ("c" . "create") :desc "Initialize repo" "r" #'magit-init - :desc "Clone repo" "R" #'+magit/clone + :desc "Clone repo" "R" #'magit-clone :desc "Commit" "c" #'magit-commit-create :desc "Fixup" "f" #'magit-commit-fixup :desc "Issue" "i" #'forge-create-issue @@ -269,12 +389,15 @@ "M-SPC" #'objed-activate) ;;; buffer management - "C-x b" #'persp-switch-to-buffer - (:when (featurep! :completion ivy) - "C-x 4 b" #'+ivy/switch-workspace-buffer-other-window) + "C-x b" #'switch-to-buffer + "C-x 4 b" #'switch-to-buffer-other-window + (:when (featurep! :ui workspaces) + "C-x b" #'persp-switch-to-buffer + "C-x B" #'switch-to-buffer + "C-x 4 B" #'switch-to-buffer-other-window + (:when (featurep! :completion ivy) + "C-x 4 b" #'+ivy/switch-workspace-buffer-other-window)) "C-x C-b" #'ibuffer-list-buffers - "C-x B" #'switch-to-buffer - "C-x 4 B" #'switch-to-buffer-other-window "C-x K" #'doom/kill-this-buffer-in-all-windows ;;; company-mode @@ -288,7 +411,7 @@ "C-S-h" #'company-show-doc-buffer "C-s" #'company-search-candidates "M-s" #'company-filter-candidates - "" #'company-complete-common-or-cycle + [C-tab] #'company-complete-common-or-cycle [tab] #'company-complete-common-or-cycle [backtab] #'company-select-previous "C-RET" #'counsel-company @@ -316,22 +439,22 @@ ;;; help and info (:after help-mode :map help-mode-map - "o" #'ace-link-help + "o" #'link-hint-open-link ">" #'help-go-forward "<" #'help-go-back "n" #'forward-button "p" #'backward-button) (:after helpful :map helpful-mode-map - "o" #'ace-link-help) + "o" #'link-hint-open-link) (:after apropos :map apropos-mode-map - "o" #'ace-link-help + "o" #'link-hint-open-link "n" #'forward-button "p" #'backward-button) (:after info :map Info-mode-map - "o" #'ace-link-info) + "o" #'link-hint-open-link) ;;; ivy & counsel (:when (featurep! :completion ivy) @@ -374,10 +497,6 @@ "C-`" #'+popup/toggle "C-~" #'+popup/raise) - ;;; repl - "C-c C-z" #'+eval/open-repl-other-window - "C-c C-S-z" #'+eval/open-repl-same-window - ;;; smartparens (:after smartparens :map smartparens-mode-map diff --git a/modules/config/default/+evil-bindings.el b/modules/config/default/+evil-bindings.el index 855eb6393..70ab26aba 100644 --- a/modules/config/default/+evil-bindings.el +++ b/modules/config/default/+evil-bindings.el @@ -1,18 +1,12 @@ ;;; config/default/+bindings.el -*- lexical-binding: t; -*- (when (featurep! :editor evil +everywhere) - ;; Have C-u behave similarly to `doom/backward-to-bol-or-indent'. ;; NOTE SPC u replaces C-u as the universal argument. - (map! :i "C-u" #'doom/backward-kill-to-bol-and-indent - :i "C-w" #'backward-kill-word - ;; Vimmish ex motion keys - :i "C-b" #'backward-word - :i "C-f" #'forward-word) ;; Minibuffer (define-key! evil-ex-completion-map - "C-a" #'move-beginning-of-line - "C-b" #'backward-word + "C-a" #'evil-beginning-of-line + "C-b" #'evil-backward-char "C-s" (if (featurep! :completion ivy) #'counsel-minibuffer-history #'helm-minibuffer-history)) @@ -20,12 +14,10 @@ (define-key! :keymaps +default-minibuffer-maps [escape] #'abort-recursive-edit "C-a" #'move-beginning-of-line - "C-b" #'backward-word - "C-f" #'forward-word "C-r" #'evil-paste-from-register - "C-u" #'doom/backward-kill-to-bol-and-indent + "C-u" #'evil-delete-back-to-indentation "C-v" #'yank - "C-w" #'backward-kill-word + "C-w" #'doom/delete-backward-word "C-z" (λ! (ignore-errors (call-interactively #'undo))) ;; Scrolling lines "C-j" #'next-line @@ -69,13 +61,13 @@ :i "C-j" #'+default/newline ; default behavior (:after help :map help-mode-map - :n "o" #'ace-link-help) + :n "o" #'link-hint-open-link) (:after helpful :map helpful-mode-map - :n "o" #'ace-link-help) + :n "o" #'link-hint-open-link) (:after info :map Info-mode-map - :n "o" #'ace-link-info) + :n "o" #'link-hint-open-link) (:after apropos :map apropos-mode-map - :n "o" #'ace-link-help + :n "o" #'link-hint-open-link :n "TAB" #'forward-button :n [tab] #'forward-button :n [backtab] #'backward-button) @@ -85,6 +77,10 @@ :n "q" #'kill-current-buffer) :m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion' + (:after evil-org + :map evil-org-mode-map + :m "gsh" #'+org/goto-visible) + (:when (featurep! :editor multiple-cursors) :prefix "gz" :nv "d" #'evil-mc-make-and-goto-next-match @@ -137,14 +133,15 @@ "C-SPC" #'company-complete-common "TAB" #'company-complete-common-or-cycle [tab] #'company-complete-common-or-cycle - [backtab] #'company-select-previous) + [backtab] #'company-select-previous + [f1] nil) (:map company-search-map ; applies to `company-filter-map' too "C-n" #'company-select-next-or-abort "C-p" #'company-select-previous-or-abort "C-j" #'company-select-next-or-abort "C-k" #'company-select-previous-or-abort "C-s" (λ! (company-search-abort) (company-filter-candidates)) - "ESC" #'company-search-abort)) + [escape] #'company-search-abort)) ;; TAB auto-completion in term buffers (:after comint :map comint-mode-map "TAB" #'company-complete @@ -335,6 +332,8 @@ :desc "Kill buffer" "k" #'kill-current-buffer :desc "Kill all buffers" "K" #'doom/kill-all-buffers :desc "Switch to last buffer" "l" #'evil-switch-to-windows-last-buffer + :desc "Set bookmark" "m" #'bookmark-set + :desc "Delete bookmark" "M" #'bookmark-delete :desc "Next buffer" "n" #'next-buffer :desc "New empty buffer" "N" #'evil-buffer-new :desc "Kill other buffers" "O" #'doom/kill-other-buffers @@ -342,6 +341,7 @@ :desc "Revert buffer" "r" #'revert-buffer :desc "Save buffer" "s" #'basic-save-buffer :desc "Save all buffers" "S" #'evil-write-all + :desc "Save buffer as root" "u" #'doom/sudo-save-buffer :desc "Pop up scratch buffer" "x" #'doom/open-scratch-buffer :desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer :desc "Bury buffer" "z" #'bury-buffer @@ -349,23 +349,30 @@ ;;; c --- code (:prefix-map ("c" . "code") - :desc "Compile" "c" #'compile - :desc "Recompile" "C" #'recompile - :desc "Jump to definition" "d" #'+lookup/definition - :desc "Jump to references" "D" #'+lookup/references - :desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region - :desc "Evaluate & replace region" "E" #'+eval:replace-region - :desc "Format buffer/region" "f" #'+format/region-or-buffer - :desc "LSP Format buffer/region" "F" #'+default/lsp-format-region-or-buffer - :desc "LSP Organize imports" "i" #'lsp-organize-imports - :desc "Jump to documentation" "k" #'+lookup/documentation - :desc "LSP Rename" "r" #'lsp-rename - :desc "Send to repl" "s" #'+eval/send-region-to-repl - :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace - :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines - :desc "List errors" "x" #'flymake-show-diagnostics-buffer - (:when (featurep! :tools flycheck) - :desc "List errors" "x" #'flycheck-list-errors)) + :desc "LSP Execute code action" "a" #'lsp-execute-code-action + :desc "Compile" "c" #'compile + :desc "Recompile" "C" #'recompile + :desc "Jump to definition" "d" #'+lookup/definition + :desc "Jump to references" "D" #'+lookup/references + :desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region + :desc "Evaluate & replace region" "E" #'+eval:replace-region + :desc "Format buffer/region" "f" #'+format/region-or-buffer + :desc "LSP Format buffer/region" "F" #'+default/lsp-format-region-or-buffer + :desc "LSP Organize imports" "i" #'lsp-organize-imports + (:when (featurep! :completion ivy) + :desc "Jump to symbol in current workspace" "j" #'lsp-ivy-workspace-symbol + :desc "Jump to symbol in any workspace" "J" #'lsp-ivy-global-workspace-symbol) + (:when (featurep! :completion helm) + :desc "Jump to symbol in current workspace" "j" #'helm-lsp-workspace-symbol + :desc "Jump to symbol in any workspace" "J" #'helm-lsp-global-workspace-symbol) + :desc "Jump to documentation" "k" #'+lookup/documentation + :desc "LSP Rename" "r" #'lsp-rename + :desc "Send to repl" "s" #'+eval/send-region-to-repl + :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace + :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines + :desc "List errors" "x" #'flymake-show-diagnostics-buffer + (:when (featurep! :checkers syntax) + :desc "List errors" "x" #'flycheck-list-errors)) ;;; f --- file (:prefix-map ("f" . "file") @@ -378,11 +385,10 @@ :desc "Find file" "f" #'find-file :desc "Find file from here" "F" #'+default/find-file-under-here :desc "Locate file" "l" #'locate - :desc "Move/rename file" "m" #'doom/move-this-file :desc "Find file in private config" "p" #'doom/find-file-in-private-config :desc "Browse private config" "P" #'doom/open-private-config :desc "Recent files" "r" #'recentf-open-files - :desc "Recent project files" "R" #'projectile-recentf + :desc "Rename/move file" "R" #'doom/move-this-file :desc "Save file" "s" #'save-buffer :desc "Save file as..." "S" #'write-file :desc "Sudo find file" "u" #'doom/sudo-find-file @@ -392,8 +398,10 @@ ;;; g --- git (:prefix-map ("g" . "git") :desc "Git revert file" "R" #'vc-revert - :desc "Copy git link" "y" #'git-link - :desc "Copy git link to homepage" "Y" #'git-link-homepage + :desc "Copy link to remote" "y" #'+vc/browse-at-remote-kill-file-or-region + :desc "Copy link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage + (:when (featurep! :ui hydra) + :desc "SMerge" "m" #'+vc/smerge-hydra/body) (:when (featurep! :ui vc-gutter) :desc "Git revert hunk" "r" #'git-gutter:revert-hunk :desc "Git stage hunk" "s" #'git-gutter:stage-hunk @@ -407,7 +415,7 @@ :desc "Magit status" "g" #'magit-status :desc "Magit file delete" "D" #'magit-file-delete :desc "Magit blame" "B" #'magit-blame-addition - :desc "Magit clone" "C" #'+magit/clone + :desc "Magit clone" "C" #'magit-clone :desc "Magit fetch" "F" #'magit-fetch :desc "Magit buffer log" "L" #'magit-log :desc "Git stage file" "S" #'magit-stage-file @@ -419,7 +427,8 @@ :desc "Find issue" "i" #'forge-visit-issue :desc "Find pull request" "p" #'forge-visit-pullreq) (:prefix ("o" . "open in browser") - :desc "Browse region or line" "o" #'+vc/git-browse-region-or-line + :desc "Browse file or region" "o" #'browse-at-remote + :desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage :desc "Browse remote" "r" #'forge-browse-remote :desc "Browse commit" "c" #'forge-browse-commit :desc "Browse an issue" "i" #'forge-browse-issue @@ -436,7 +445,7 @@ :desc "List notifications" "n" #'forge-list-notifications) (:prefix ("c" . "create") :desc "Initialize repo" "r" #'magit-init - :desc "Clone repo" "R" #'+magit/clone + :desc "Clone repo" "R" #'magit-clone :desc "Commit" "c" #'magit-commit-create :desc "Fixup" "f" #'magit-commit-fixup :desc "Branch" "b" #'magit-branch-and-checkout @@ -457,15 +466,18 @@ (:prefix-map ("n" . "notes") :desc "Search notes for symbol" "*" #'+default/search-notes-for-symbol-at-point :desc "Org agenda" "a" #'org-agenda - :desc "Org capture" "c" #'org-capture + :desc "Toggle org-clock" "c" #'+org/toggle-clock + :desc "Cancel org-clock" "C" #'org-clock-cancel :desc "Open deft" "d" #'deft - :desc "Search org agenda headlines" "h" #'+default/org-notes-headlines + :desc "Find file in notes" "f" #'+default/find-in-notes + :desc "Browse notes" "F" #'+default/browse-notes :desc "Org store link" "l" #'org-store-link :desc "Tags search" "m" #'org-tags-view - :desc "Find file in notes" "n" #'+default/find-in-notes - :desc "Browse notes" "N" #'+default/browse-notes + :desc "Org capture" "n" #'org-capture + :desc "Active org-clock" "o" #'org-clock-goto :desc "Todo list" "t" #'org-todo-list :desc "Search notes" "s" #'+default/org-notes-search + :desc "Search org agenda headlines" "S" #'+default/org-notes-headlines :desc "View search" "v" #'org-search-view :desc "Org export to clipboard" "y" #'+org/export-to-clipboard :desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text @@ -527,6 +539,7 @@ :desc "Compile in project" "c" #'projectile-compile-project :desc "Repeat last command" "C" #'projectile-repeat-last-command :desc "Remove known project" "d" #'projectile-remove-known-project + :desc "Discover projects in folder" "D" #'+default/discover-projects :desc "Edit project .dir-locals" "e" #'projectile-edit-dir-locals :desc "Find file in project" "f" #'projectile-find-file :desc "Find file in other project" "F" #'doom/find-file-in-other-project @@ -575,23 +588,27 @@ :desc "Search other directory" "D" #'+default/search-other-cwd :desc "Locate file" "f" #'locate :desc "Jump to symbol" "i" #'imenu - :desc "Jump to link" "l" #'ace-link + :desc "Jump to visible link" "l" #'link-hint-open-link + :desc "Jump to link" "L" #'ffap-menu :desc "Jump list" "j" #'evil-show-jumps - :desc "Jump to mark" "m" #'evil-show-marks + :desc "Jump to bookmark" "m" #'bookmark-jump :desc "Look up online" "o" #'+lookup/online :desc "Look up online (w/ prompt)" "O" #'+lookup/online-select :desc "Look up in local docsets" "k" #'+lookup/in-docsets :desc "Look up in all docsets" "K" #'+lookup/in-all-docsets :desc "Search project" "p" #'+default/search-project :desc "Search other project" "P" #'+default/search-other-project + :desc "Jump to mark" "r" #'evil-show-marks :desc "Search buffer" "s" #'swiper-isearch - :desc "Search buffer for thing at point" "S" #'swiper-isearch-thing-at-point) + :desc "Search buffer for thing at point" "S" #'swiper-isearch-thing-at-point + :desc "Dictionary" "t" #'+lookup/dictionary-definition + :desc "Thesaurus" "T" #'+lookup/synonyms) ;;; t --- toggle (:prefix-map ("t" . "toggle") :desc "Big mode" "b" #'doom-big-font-mode :desc "Flymake" "f" #'flymake-mode - (:when (featurep! :tools flycheck) + (:when (featurep! :checkers syntax) :desc "Flycheck" "f" #'flycheck-mode) :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Evil goggles" "g" #'evil-goggles-mode @@ -600,13 +617,14 @@ :desc "Indent style" "I" #'doom/toggle-indent-style :desc "Line numbers" "l" #'doom/toggle-line-numbers (:when (featurep! :lang org +present) - :desc "org-tree-slide mode" "p" #'+org-present/start) + :desc "org-tree-slide mode" "p" #'org-tree-slide-mode) :desc "Read-only mode" "r" #'read-only-mode - (:when (featurep! :tools flyspell) + (:when (featurep! :checkers spell) :desc "Flyspell" "s" #'flyspell-mode) (:when (featurep! :lang org +pomodoro) :desc "Pomodoro timer" "t" #'org-pomodoro) - :desc "Word-wrap mode" "w" #'+word-wrap-mode)) + :desc "Word-wrap mode" "w" #'+word-wrap-mode + :desc "Zen mode" "z" #'writeroom-mode)) (after! which-key (let ((prefix-re (regexp-opt (list doom-leader-key doom-leader-alt-key)))) diff --git a/modules/config/default/autoload/default.el b/modules/config/default/autoload/default.el index ad49e2f6d..10837d9a9 100644 --- a/modules/config/default/autoload/default.el +++ b/modules/config/default/autoload/default.el @@ -1,39 +1,5 @@ ;; config/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 -(defun +default/browse-project () - (interactive) (doom-project-browse (doom-project-root))) -;; NOTE No need for find-in-project, use `projectile-find-file' - -;;;###autoload -(defun +default/browse-templates () - (interactive) (doom-project-browse +file-templates-dir)) -;;;###autoload -(defun +default/find-in-templates () - (interactive) (doom-project-find-file +file-templates-dir)) - -;;;###autoload -(defun +default/browse-emacsd () - (interactive) (doom-project-browse doom-emacs-dir)) -;;;###autoload -(defun +default/find-in-emacsd () - (interactive) (doom-project-find-file doom-emacs-dir)) - -;;;###autoload -(defun +default/browse-notes () - (interactive) (doom-project-browse org-directory)) -;;;###autoload -(defun +default/find-in-notes () - (interactive) (doom-project-find-file org-directory)) - ;;;###autoload (defun +default/compile (arg) "Runs `compile' from the root of the current project. @@ -59,9 +25,6 @@ If ARG (universal argument), runs `compile' from the current directory." #'man #'woman))) -;;;###autoload -(defalias '+default/newline #'newline) - ;;;###autoload (defun +default/new-buffer () "TODO" @@ -73,246 +36,14 @@ If ARG (universal argument), runs `compile' from the current directory." (with-current-buffer buffer (funcall (default-value 'major-mode)))))) -;;;###autoload -(defun +default/newline-above () - "Insert an indented new line before the current one." - (interactive) - (if (featurep 'evil) - (call-interactively 'evil-open-above) - (beginning-of-line) - (save-excursion (newline)) - (indent-according-to-mode))) - -;;;###autoload -(defun +default/newline-below () - "Insert an indented new line after the current one." - (interactive) - (if (featurep 'evil) - (call-interactively 'evil-open-below) - (end-of-line) - (newline-and-indent))) - -;;;###autoload -(defun +default/yank-pop () - "Interactively select what text to insert from the kill ring." - (interactive) - (call-interactively - (cond ((fboundp 'counsel-yank-pop) #'counsel-yank-pop) - ((fboundp 'helm-show-kill-ring) #'helm-show-kill-ring) - ((error "No kill-ring search backend available. Enable ivy or helm!"))))) - -;;;###autoload -(defun +default--newline-indent-and-continue-comments-a () - "A replacement for `newline-and-indent'. - -Continues comments if executed from a commented line, with special support for -languages with weak native comment continuation support (like C-family -languages)." - (interactive) - (if (and (sp-point-in-comment) - comment-line-break-function) - (funcall comment-line-break-function nil) - (delete-horizontal-space t) - (newline nil t) - (indent-according-to-mode))) - -(defun doom--backward-delete-whitespace-to-column () - "Delete back to the previous column of whitespace, or as much whitespace as -possible, or just one char if that's not possible." - (interactive) - (let* ((context (ignore-errors (sp-get-thing))) - (op (plist-get context :op)) - (cl (plist-get context :cl)) - open-len close-len) - (cond ;; When in strings (sp acts weird with quotes; this is the fix) - ;; Also, skip closing delimiters - ((and op cl - (string= op cl) - (and (string= (char-to-string (or (char-before) 0)) op) - (setq open-len (length op))) - (and (string= (char-to-string (or (char-after) 0)) cl) - (setq close-len (length cl)))) - (delete-char (- open-len)) - (delete-char close-len)) - - ;; Delete up to the nearest tab column IF only whitespace between - ;; point and bol. - ((and (not indent-tabs-mode) - (not (bolp)) - (not (sp-point-in-string)) - (save-excursion (>= (- (skip-chars-backward " \t")) tab-width))) - (let ((movement (% (current-column) tab-width))) - (when (= movement 0) - (setq movement tab-width)) - (delete-char (- movement))) - (unless (memq (char-before) (list ?\n ?\ )) - (insert " "))) - - ;; Otherwise do a regular delete - ((delete-char -1))))) - -;;;###autoload -(defun +default--delete-backward-char-a (n &optional killflag) - "Same as `delete-backward-char', but preforms these additional checks: - -+ If point is surrounded by (balanced) whitespace and a brace delimiter ({} [] - ()), delete a space on either side of the cursor. -+ If point is at BOL and surrounded by braces on adjacent lines, collapse - newlines: - { - | - } => {|} -+ Otherwise, resort to `doom--backward-delete-whitespace-to-column'. -+ Resorts to `delete-char' if n > 1" - (interactive "p\nP") - (or (integerp n) - (signal 'wrong-type-argument (list 'integerp n))) - (cond ((and (use-region-p) - delete-active-region - (= n 1)) - ;; If a region is active, kill or delete it. - (if (eq delete-active-region 'kill) - (kill-region (region-beginning) (region-end) 'region) - (funcall region-extract-function 'delete-only))) - ;; In Overwrite mode, maybe untabify while deleting - ((null (or (null overwrite-mode) - (<= n 0) - (memq (char-before) '(?\t ?\n)) - (eobp) - (eq (char-after) ?\n))) - (let ((ocol (current-column))) - (delete-char (- n) killflag) - (save-excursion - (insert-char ?\s (- ocol (current-column)) nil)))) - ;; - ((and (= n 1) (bound-and-true-p smartparens-mode)) - (cond ((and (memq (char-before) (list ?\ ?\t)) - (save-excursion - (and (/= (skip-chars-backward " \t" (line-beginning-position)) 0) - (bolp)))) - (doom--backward-delete-whitespace-to-column)) - ((let* ((pair (ignore-errors (sp-get-thing))) - (op (plist-get pair :op)) - (cl (plist-get pair :cl)) - (beg (plist-get pair :beg)) - (end (plist-get pair :end))) - (cond ((and end beg (= end (+ beg (length op) (length cl)))) - (sp-backward-delete-char 1)) - ((doom-surrounded-p pair 'inline 'balanced) - (delete-char -1 killflag) - (delete-char 1) - (when (= (point) (+ (length cl) beg)) - (sp-backward-delete-char 1) - (sp-insert-pair op))) - ((and (bolp) (doom-surrounded-p pair nil 'balanced)) - (delete-region beg end) - (sp-insert-pair op) - t) - ((run-hook-with-args-until-success 'doom-delete-backward-functions)) - ((doom--backward-delete-whitespace-to-column))))))) - ;; Otherwise, do simple deletion. - ((delete-char (- n) killflag)))) - -;;;###autoload -(defun +default/search-cwd (&optional arg) - "Conduct a text search in files under the current folder. -If prefix ARG is set, prompt for a directory to search from." - (interactive "P") - (let ((default-directory - (if arg - (read-directory-name "Search directory: ") - default-directory))) - (call-interactively - (cond ((featurep! :completion ivy) #'+ivy/project-search-from-cwd) - ((featurep! :completion helm) #'+helm/project-search-from-cwd) - (#'rgrep))))) - -;;;###autoload -(defun +default/search-other-cwd () - "Conduct a text search in another directory." - (interactive) - (+default/search-cwd 'other)) - -;;;###autoload -(defun +default/search-project (&optional arg) - "Conduct a text search in the current project root. -If prefix ARG is set, prompt for a known project to search from." - (interactive "P") - (let ((default-directory - (if arg - (if-let (projects (projectile-relevant-known-projects)) - (completing-read "Search project: " projects - nil t nil nil (doom-project-root)) - (user-error "There are no known projects")) - default-directory))) - (call-interactively - (cond ((featurep! :completion ivy) #'+ivy/project-search) - ((featurep! :completion helm) #'+helm/project-search) - (#'projectile-grep))))) - -;;;###autoload -(defun +default/search-other-project () - "Conduct a text search in a known project." - (interactive) - (+default/search-project 'other)) - -;;;###autoload -(defun +default/search-project-for-symbol-at-point (&optional arg symbol) - "Conduct a text search in the current project for symbol at point. -If prefix ARG is set, prompt for a known project to search from." - (interactive - (list current-prefix-arg - (or (and (use-region-p) - (rxt-quote-pcre - (buffer-substring-no-properties (region-beginning) - (region-end)))) - (rxt-quote-pcre (thing-at-point 'symbol t)) - ""))) - (let ((default-directory - (if arg - (if-let (projects (projectile-relevant-known-projects)) - (completing-read "Switch to project: " projects - nil t nil nil (doom-project-root)) - (user-error "There are no known projects")) - default-directory))) - (cond ((featurep! :completion ivy) - (+ivy/project-search nil symbol)) - ((featurep! :completion helm) - (+helm/project-search nil symbol)) - ((rgrep (regexp-quote symbol)))))) - -;;;###autoload -(defun +default/search-notes-for-symbol-at-point (&optional symbol) - "Conduct a text search in the current project for symbol at point. If prefix -ARG is set, prompt for a known project to search from." - (interactive - (list (rxt-quote-pcre (or (thing-at-point 'symbol t) "")))) - (require 'org) - (let ((default-directory org-directory)) - (+default/search-project-for-symbol-at-point - nil symbol))) - -;;;###autoload -(defun +default/org-notes-search () - "Perform a text search on `org-directory'." - (interactive) - (require 'org) - (let ((default-directory org-directory)) - (+default/search-project-for-symbol-at-point nil ""))) - -;;;###autoload -(defun +default/org-notes-headlines () - "Jump to an Org headline in `org-agenda-files'." - (interactive) - (doom-completing-read-org-headings - "Jump to org headline: " org-agenda-files 3 t)) - ;;;###autoload (defun +default/lsp-format-region-or-buffer () "Format the buffer (or selection) with LSP." (interactive) + (unless (bound-and-true-p lsp-mode) + (user-error "Not in an LSP buffer")) (call-interactively - (if (use-region-p) + (if (doom-region-active-p) #'lsp-format-region #'lsp-format-buffer))) @@ -322,24 +53,5 @@ ARG is set, prompt for a known project to search from." (interactive) (server-force-delete) (while (server-running-p) - (sit-for 1)) + (sleep-for 1)) (server-start)) - -;;;###autoload -(defun +default/find-file-under-here () - "Perform a recursive file search from the current directory." - (interactive) - (if (featurep! :completion ivy) - (call-interactively #'counsel-file-jump) - (λ! (doom-project-find-file default-directory)))) - -;;;###autoload -(defun +default/insert-file-path (arg) - "Insert the file name (absolute path if prefix ARG). -If `buffer-file-name' isn't set, uses `default-directory'." - (interactive "P") - (let ((path (or buffer-file-name default-directory))) - (insert - (if arg - (abbreviate-file-name path) - (file-name-nondirectory path))))) diff --git a/modules/config/default/autoload/files.el b/modules/config/default/autoload/files.el new file mode 100644 index 000000000..3c90f6d23 --- /dev/null +++ b/modules/config/default/autoload/files.el @@ -0,0 +1,51 @@ +;;; config/default/autoload/files.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +default/browse-project () + "Browse files from the current project's root." + (interactive) (doom-project-browse (doom-project-root))) +;; NOTE No need for find-in-project, use `projectile-find-file' + +;;;###autoload +(defun +default/browse-templates () + "Browse files from `+file-templates-dir'." + (interactive) (doom-project-browse +file-templates-dir)) +;;;###autoload +(defun +default/find-in-templates () + "Find a file under `+file-templates-dir', recursively." + (interactive) (doom-project-find-file +file-templates-dir)) + +;;;###autoload +(defun +default/browse-emacsd () + "Browse files from `doom-emacs-dir'." + (interactive) (doom-project-browse doom-emacs-dir)) +;;;###autoload +(defun +default/find-in-emacsd () + "Find a file under `doom-emacs-dir', recursively." + (interactive) (doom-project-find-file doom-emacs-dir)) + +;;;###autoload +(defun +default/browse-notes () + "Browse files from `org-directory'." + (interactive) (doom-project-browse org-directory)) +;;;###autoload +(defun +default/find-in-notes () + "Find a file under `org-directory', recursively." + (interactive) (doom-project-find-file org-directory)) + +;;;###autoload +(defun +default/find-file-under-here () + "Perform a recursive file search from the current directory." + (interactive) + (if (featurep! :completion ivy) + (call-interactively #'counsel-file-jump) + (λ! (doom-project-find-file default-directory)))) + +;;;###autoload +(defun +default/discover-projects (arg) + "Discover projects in `projectile-project-search-path'. +If prefix ARG is non-nil, prompt for the search path." + (interactive "P") + (if arg + (call-interactively #'projectile-discover-projects-in-directory) + (mapc #'projectile-discover-projects-in-directory projectile-project-search-path))) diff --git a/modules/config/default/autoload/search.el b/modules/config/default/autoload/search.el new file mode 100644 index 000000000..dcd704895 --- /dev/null +++ b/modules/config/default/autoload/search.el @@ -0,0 +1,91 @@ +;;; config/default/autoload/search.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +default/search-cwd (&optional arg) + "Conduct a text search in files under the current folder. +If prefix ARG is set, prompt for a directory to search from." + (interactive "P") + (let ((default-directory + (if arg + (read-directory-name "Search directory: ") + default-directory))) + (call-interactively + (cond ((featurep! :completion ivy) #'+ivy/project-search-from-cwd) + ((featurep! :completion helm) #'+helm/project-search-from-cwd) + (#'rgrep))))) + +;;;###autoload +(defun +default/search-other-cwd () + "Conduct a text search in another directory." + (interactive) + (+default/search-cwd 'other)) + +;;;###autoload +(defun +default/search-project (&optional arg) + "Conduct a text search in the current project root. +If prefix ARG is set, prompt for a known project to search from." + (interactive "P") + (let* ((disabled-command-function nil) + (default-directory + (if arg + (if-let (projects (projectile-relevant-known-projects)) + (completing-read "Search project: " projects + nil t nil nil (doom-project-root)) + (user-error "There are no known projects")) + default-directory))) + (call-interactively + (cond ((featurep! :completion ivy) #'+ivy/project-search) + ((featurep! :completion helm) #'+helm/project-search) + (#'projectile-ripgrep))))) + +;;;###autoload +(defun +default/search-other-project () + "Conduct a text search in a known project." + (interactive) + (+default/search-project 'other)) + +;;;###autoload +(defun +default/search-project-for-symbol-at-point (&optional symbol arg) + "Search current project for symbol at point. +If prefix ARG is set, prompt for a known project to search from." + (interactive + (list (rxt-quote-pcre (or (doom-thing-at-point-or-region) "")) + current-prefix-arg)) + (let ((default-directory + (if arg + (if-let (projects (projectile-relevant-known-projects)) + (completing-read "Switch to project: " projects + nil t nil nil (doom-project-root)) + (user-error "There are no known projects")) + default-directory))) + (cond ((featurep! :completion ivy) + (+ivy/project-search nil symbol)) + ((featurep! :completion helm) + (+helm/project-search nil symbol)) + ((rgrep (regexp-quote symbol)))))) + +;;;###autoload +(defun +default/search-notes-for-symbol-at-point (&optional symbol) + "Conduct a text search in the current project for symbol at point. If prefix +ARG is set, prompt for a known project to search from." + (interactive + (list (rxt-quote-pcre (or (doom-thing-at-point-or-region) "")))) + (require 'org) + (let ((default-directory org-directory)) + (+default/search-project-for-symbol-at-point + nil symbol))) + +;;;###autoload +(defun +default/org-notes-search () + "Perform a text search on `org-directory'." + (interactive) + (require 'org) + (let ((default-directory org-directory)) + (+default/search-project-for-symbol-at-point ""))) + +;;;###autoload +(defun +default/org-notes-headlines () + "Jump to an Org headline in `org-agenda-files'." + (interactive) + (doom-completing-read-org-headings + "Jump to org headline: " org-agenda-files 3 t)) diff --git a/modules/config/default/autoload/text.el b/modules/config/default/autoload/text.el new file mode 100644 index 000000000..d8f8c0293 --- /dev/null +++ b/modules/config/default/autoload/text.el @@ -0,0 +1,164 @@ +;;; config/default/autoload/text.el -*- lexical-binding: t; -*- + +;;;###autoload +(defalias '+default/newline #'newline) + +;;;###autoload +(defun +default/newline-above () + "Insert an indented new line before the current one." + (interactive) + (if (featurep 'evil) + (call-interactively 'evil-open-above) + (beginning-of-line) + (save-excursion (newline)) + (indent-according-to-mode))) + +;;;###autoload +(defun +default/newline-below () + "Insert an indented new line after the current one." + (interactive) + (if (featurep 'evil) + (call-interactively 'evil-open-below) + (end-of-line) + (newline-and-indent))) + +;;;###autoload +(defun +default/yank-pop () + "Interactively select what text to insert from the kill ring." + (interactive) + (call-interactively + (cond ((fboundp 'counsel-yank-pop) #'counsel-yank-pop) + ((fboundp 'helm-show-kill-ring) #'helm-show-kill-ring) + ((error "No kill-ring search backend available. Enable ivy or helm!"))))) + +;;;###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 +(defun +default/insert-file-path (arg) + "Insert the file name (absolute path if prefix ARG). +If `buffer-file-name' isn't set, uses `default-directory'." + (interactive "P") + (let ((path (or buffer-file-name default-directory))) + (insert + (if arg + (abbreviate-file-name path) + (file-name-nondirectory path))))) + +;;;###autoload +(defun +default--newline-indent-and-continue-comments-a () + "A replacement for `newline-and-indent'. + +Continues comments if executed from a commented line, with special support for +languages with weak native comment continuation support (like C-family +languages)." + (interactive) + (if (and (sp-point-in-comment) + comment-line-break-function) + (funcall comment-line-break-function nil) + (delete-horizontal-space t) + (newline nil t) + (indent-according-to-mode))) + + +(defun doom--backward-delete-whitespace-to-column () + "Delete back to the previous column of whitespace, or as much whitespace as +possible, or just one char if that's not possible." + (interactive) + (let* ((context (ignore-errors (sp-get-thing))) + (op (plist-get context :op)) + (cl (plist-get context :cl)) + open-len close-len) + (cond ;; When in strings (sp acts weird with quotes; this is the fix) + ;; Also, skip closing delimiters + ((and op cl + (string= op cl) + (and (string= (char-to-string (or (char-before) 0)) op) + (setq open-len (length op))) + (and (string= (char-to-string (or (char-after) 0)) cl) + (setq close-len (length cl)))) + (delete-char (- open-len)) + (delete-char close-len)) + + ;; Delete up to the nearest tab column IF only whitespace between + ;; point and bol. + ((and (not indent-tabs-mode) + (not (bolp)) + (not (sp-point-in-string)) + (save-excursion (>= (- (skip-chars-backward " \t")) tab-width))) + (let ((movement (% (current-column) tab-width))) + (when (= movement 0) + (setq movement tab-width)) + (delete-char (- movement))) + (unless (memq (char-before) (list ?\n ?\ )) + (insert " "))) + + ;; Otherwise do a regular delete + ((delete-char -1))))) + +;;;###autoload +(defun +default--delete-backward-char-a (n &optional killflag) + "Same as `delete-backward-char', but preforms these additional checks: + ++ If point is surrounded by (balanced) whitespace and a brace delimiter ({} [] + ()), delete a space on either side of the cursor. ++ If point is at BOL and surrounded by braces on adjacent lines, collapse + newlines: + { + | + } => {|} ++ Otherwise, resort to `doom--backward-delete-whitespace-to-column'. ++ Resorts to `delete-char' if n > 1" + (interactive "p\nP") + (or (integerp n) + (signal 'wrong-type-argument (list 'integerp n))) + (cond ((and (use-region-p) + delete-active-region + (= n 1)) + ;; If a region is active, kill or delete it. + (if (eq delete-active-region 'kill) + (kill-region (region-beginning) (region-end) 'region) + (funcall region-extract-function 'delete-only))) + ;; In Overwrite mode, maybe untabify while deleting + ((null (or (null overwrite-mode) + (<= n 0) + (memq (char-before) '(?\t ?\n)) + (eobp) + (eq (char-after) ?\n))) + (let ((ocol (current-column))) + (delete-char (- n) killflag) + (save-excursion + (insert-char ?\s (- ocol (current-column)) nil)))) + ;; + ((and (= n 1) (bound-and-true-p smartparens-mode)) + (cond ((and (memq (char-before) (list ?\ ?\t)) + (save-excursion + (and (/= (skip-chars-backward " \t" (line-beginning-position)) 0) + (bolp)))) + (doom--backward-delete-whitespace-to-column)) + ((let* ((pair (ignore-errors (sp-get-thing))) + (op (plist-get pair :op)) + (cl (plist-get pair :cl)) + (beg (plist-get pair :beg)) + (end (plist-get pair :end))) + (cond ((and end beg (= end (+ beg (length op) (length cl)))) + (sp-backward-delete-char 1)) + ((doom-surrounded-p pair 'inline 'balanced) + (delete-char -1 killflag) + (delete-char 1) + (when (= (point) (+ (length cl) beg)) + (sp-backward-delete-char 1) + (sp-insert-pair op))) + ((and (bolp) (doom-surrounded-p pair nil 'balanced)) + (delete-region beg end) + (sp-insert-pair op) + t) + ((run-hook-with-args-until-success 'doom-delete-backward-functions)) + ((doom--backward-delete-whitespace-to-column))))))) + ;; Otherwise, do simple deletion. + ((delete-char (- n) killflag)))) diff --git a/modules/config/default/config.el b/modules/config/default/config.el index 49f68c592..3ab8601cc 100644 --- a/modules/config/default/config.el +++ b/modules/config/default/config.el @@ -23,20 +23,29 @@ ;;;###package avy (setq avy-all-windows nil avy-all-windows-alt t - avy-background t) + avy-background t + ;; the unpredictability of this (when enabled) makes it a poor default + avy-single-candidate-jump nil) (after! epa - (setq epa-file-encrypt-to - (or epa-file-encrypt-to - ;; Collect all public key IDs with your username - (unless (string-empty-p user-full-name) - (cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) user-full-name)) - collect (epg-sub-key-id (car (epg-key-sub-key-list key))))) - 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)) + ;; With GPG 2.1+, this forces gpg-agent to use the Emacs minibuffer to prompt + ;; for the key passphrase. + (setq epa-pinentry-mode 'loopback) + ;; Default to the first secret key available in your keyring. + (setq-default + epa-file-encrypt-to + (or (default-value 'epa-file-encrypt-to) + (unless (string-empty-p user-full-name) + (cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) user-full-name)) + collect (epg-sub-key-id (car (epg-key-sub-key-list key))))) + user-mail-address)) + ;; And suppress prompts if epa-file-encrypt-to has a default value (without + ;; overwriting file-local values). + (defadvice! +default--dont-prompt-for-keys-a (&rest _) + :before #'epa-file-write-region + (unless (local-variable-p 'epa-file-encrypt-to) + (setq-local epa-file-encrypt-to (default-value 'epa-file-encrypt-to))))) (use-package! drag-stuff @@ -62,11 +71,6 @@ ;; or specific :post-handlers with: ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) (after! smartparens - ;; Smartparens is broken in `cc-mode' as of Emacs 27. See - ;; . - (unless EMACS27+ - (pushnew! sp--special-self-insert-commands 'c-electric-paren 'c-electric-brace)) - ;; Smartparens' navigation feature is neat, but does not justify how ;; expensive it is. It's also less useful for evil users. This may need to ;; be reactivated for non-evil users though. Needs more testing! @@ -76,7 +80,7 @@ sp-navigate-consider-sgml-tags nil))) ;; Autopair quotes more conservatively; if I'm next to a word/before another - ;; quote, I likely don't want to open a new pair. + ;; quote, I don't want to open a new pair or it would unbalance them. (let ((unless-list '(sp-point-before-word-p sp-point-after-word-p sp-point-before-same-p))) @@ -114,9 +118,7 @@ ;; Disable electric keys in C modes because it interferes with smartparens ;; and custom bindings. We'll do it ourselves (mostly). (after! cc-mode - (c-toggle-electric-state -1) - (c-toggle-auto-newline -1) - (setq c-electric-flag nil) + (setq-default c-electric-flag nil) (dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177")) (define-key c-mode-base-map key nil)) @@ -278,6 +280,7 @@ (define-key! help-map ;; new keybinds "'" #'describe-char + "u" #'doom/help-autodefs "E" #'doom/sandbox "M" #'doom/describe-active-minor-mode "O" #'+lookup/online @@ -301,32 +304,43 @@ "rf" #'doom/reload-font "re" #'doom/reload-env + ;; make `describe-bindings' available under the b prefix which it previously + ;; occupied. Add more binding related commands under that prefix as well + "b" nil + "bb" #'describe-bindings + "bi" #'which-key-show-minor-mode-keymap + "bm" #'which-key-show-major-mode + "bt" #'which-key-show-top-level + "bf" #'which-key-show-full-keymap + "bk" #'which-key-show-keymap + ;; replaces `apropos-documentation' b/c `apropos' covers this "d" nil - "d/" #'doom/help-search - "da" #'doom/help-autodefs "db" #'doom/report-bug + "dc" #'doom/goto-private-config-file + "dC" #'doom/goto-private-init-file "dd" #'doom/toggle-debug-mode "df" #'doom/help-faq "dh" #'doom/help - "dk" #'doom/goto-packages-file "dl" #'doom/help-search-load-path + "dL" #'doom/help-search-loaded-files "dm" #'doom/help-modules "dn" #'doom/help-news "dN" #'doom/help-news-search - "di" #'doom/goto-doomblock - "dp" #'doom/help-packages - "dP" #'doom/help-package-homepage - "dc" #'doom/goto-config-file - "dC" #'doom/help-package-config - "ds" #'doom/sandbox + "dpc" #'doom/help-package-config + "dpd" #'doom/goto-private-packages-file + "dph" #'doom/help-package-homepage + "dpp" #'doom/help-packages + "ds" #'doom/help-search-headings + "dS" #'doom/help-search "dt" #'doom/toggle-profiler + "du" #'doom/help-autodefs "dv" #'doom/version + "dx" #'doom/sandbox ;; replaces `apropos-command' "a" #'apropos "A" #'apropos-documentation - "/" #'apropos-documentation ;; replaces `describe-copying' b/c not useful "C-c" #'describe-coding-system ;; replaces `Info-got-emacs-command-node' b/c redundant w/ `Info-goto-node' @@ -335,9 +349,11 @@ "h" nil ;; replaces `view-emacs-news' b/c it's on C-n too "n" #'doom/help-news - ;; replaces `finder-by-keyword' b/c not usefull + ;; replaces `help-with-tutorial', b/c it's less useful than `load-theme' + "t" #'load-theme + ;; replaces `finder-by-keyword' b/c not useful "p" #'doom/help-packages - ;; replaces `describe-package' b/c redundant w/ `doom/describe-package' + ;; replaces `describe-package' b/c redundant w/ `doom/help-packages' "P" #'find-library) (after! which-key @@ -347,6 +363,9 @@ which-key-replacement-alist) (cl-pushnew `((,(format "\\`\\(?:<\\(?:\\(?:f1\\|help\\)>\\)\\|C-h\\|%s h\\) r\\'" prefix-re)) nil . "reload") + which-key-replacement-alist) + (cl-pushnew `((,(format "\\`\\(?:<\\(?:\\(?:f1\\|help\\)>\\)\\|C-h\\|%s h\\) b\\'" prefix-re)) + nil . "bindings") which-key-replacement-alist))) @@ -373,14 +392,11 @@ ;; which ctrl+RET will add a new "item" below the current one and ;; cmd+RET (Mac) / meta+RET (elsewhere) will add a new, blank line below ;; the current one. - :gni [C-return] #'+default/newline-below - :gni [C-S-return] #'+default/newline-above + :gn [C-return] #'+default/newline-below + :gn [C-S-return] #'+default/newline-above (:when IS-MAC - :gni [s-return] #'+default/newline-below - :gni [S-s-return] #'+default/newline-above) - (:unless IS-MAC - :gni [M-return] #'+default/newline-below - :gni [M-S-return] #'+default/newline-above))) + :gn [s-return] #'+default/newline-below + :gn [S-s-return] #'+default/newline-above))) ;; diff --git a/modules/config/default/packages.el b/modules/config/default/packages.el index 14a4de010..ad818e198 100644 --- a/modules/config/default/packages.el +++ b/modules/config/default/packages.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; config/default/packages.el -(package! avy) -(package! ace-link) -(package! drag-stuff) +(package! avy :pin "cf95ba9582") +(package! drag-stuff :pin "6d06d846cd") +(package! link-hint :pin "0d9cabcdb7") (unless (featurep! :editor evil) - (package! expand-region)) + (package! expand-region :pin "1603d01fbf")) diff --git a/modules/editor/evil/+commands.el b/modules/editor/evil/+commands.el index 56f36286b..69b9082ba 100644 --- a/modules/editor/evil/+commands.el +++ b/modules/editor/evil/+commands.el @@ -14,6 +14,7 @@ (evil-ex-define-cmd "na[rrow]" #'+evil:narrow-buffer) (evil-ex-define-cmd "retab" #'+evil:retab) (evil-ex-define-cmd "rev[erse]" #'+evil:reverse-lines) +(evil-ex-define-cmd "l[ine]diff" #'evil-quick-diff) ;;; External resources ;; TODO (evil-ex-define-cmd "db" #'doom:db) @@ -34,7 +35,7 @@ ;;; GIT (evil-ex-define-cmd "gist" #'+gist:send) ; send current buffer/region to gist (evil-ex-define-cmd "gistl" #'+gist:list) ; list gists by user -(evil-ex-define-cmd "gbrowse" #'+vc:git-browse) ; show file/region in github/gitlab +(evil-ex-define-cmd "gbrowse" #'+vc/browse-at-remote-file-or-region) ; show file/region in github/gitlab (evil-ex-define-cmd "gissues" #'forge-browse-issues) ; show github issues (evil-ex-define-cmd "git" #'magit-status) ; open magit status window (evil-ex-define-cmd "gstage" #'magit-stage) @@ -72,8 +73,9 @@ (evil-ex-define-cmd "pg[grep]d" #'+helm:project-search-from-cwd))) ;;; Project tools -(evil-ex-define-cmd "compile" #'+evil:compile) -(evil-ex-define-cmd "mak[e]" #'+evil:make) +(evil-ex-define-cmd "com[pile]" #'+evil:compile) +(evil-ex-define-cmd "make" #'+evil:make) +(evil-ex-define-cmd "mk" #'+evil:make) ; convenience alias (evil-ex-define-cmd "debug" #'+debugger/start) (evil-ex-define-cmd "er[rors]" #'flycheck-list-errors) diff --git a/modules/editor/evil/+everywhere.el b/modules/editor/evil/+everywhere.el index c5e1312eb..fcc34b980 100644 --- a/modules/editor/evil/+everywhere.el +++ b/modules/editor/evil/+everywhere.el @@ -11,8 +11,8 @@ ;; their changes will override evil-collection's. ;; 3. Ideally, we'd do away with evil-collection entirely. It changes too often, ;; introduces breaking bugs too frequently, and I don't agree with all their -;; design choices. Regardless, it does mork than it causes trouble, so it may -;; be here to stay. +;; design choices. Regardless, it does more good than trouble, so it may be +;; here to stay. ;; 4. Adds `+evil-collection-disabled-list', to make it easier for users to ;; disable modules, and to reduce the effort required to maintain our copy of ;; `evil-collection-list' (now I can just copy it from time to time). @@ -35,7 +35,8 @@ package-menu ruby-mode simple - slime) + slime + lispy) "A list of `evil-collection' modules to ignore. See the definition of this variable for an explanation of the defaults (in comments). See `evil-collection-mode-list' for a list of available options.") @@ -45,6 +46,9 @@ variable for an explanation of the defaults (in comments). See ;; We do this ourselves, and better. (defvar evil-collection-want-unimpaired-p nil) +;; We handle loading evil-collection ourselves +(defvar evil-collection--supported-modes nil) + ;; This has to be defined here since evil-collection doesn't autoload its own. ;; It must be updated whenever evil-collection updates theirs. Here's an easy ;; way to update it: @@ -78,11 +82,13 @@ variable for an explanation of the defaults (in comments). See (custom cus-edit) cus-theme daemons + dashboard deadgrep debbugs debug diff-mode dired + dired-sidebar disk-usage doc-view docker @@ -108,6 +114,7 @@ variable for an explanation of the defaults (in comments). See geiser ggtags git-timemachine + gnus go-mode grep guix @@ -127,6 +134,7 @@ variable for an explanation of the defaults (in comments). See ivy js2-mode leetcode + lispy log-edit log-view lsp-ui-imenu @@ -169,6 +177,7 @@ variable for an explanation of the defaults (in comments). See slime sly tablist + tar-mode (term term ansi-term multi-term) tetris tide @@ -220,7 +229,7 @@ and complains if a module is loaded too early (during startup)." (add-transient-hook! 'emacs-lisp-mode (+evil-collection-init 'elisp-mode)) (add-transient-hook! 'occur-mode - (+evil-collection-init 'replace)) + (+evil-collection-init 'occur)) (evil-define-key* 'normal process-menu-mode-map "q" #'kill-current-buffer @@ -231,7 +240,10 @@ and complains if a module is loaded too early (during startup)." (list doom-leader-key doom-localleader-key doom-leader-alt-key doom-localleader-alt-key)) -;; Load the rest +;; HACK Do this ourselves because evil-collection break's `eval-after-load' load +;; order by loading their target plugin before applying keys. It'd be too +;; much work to accommodate this eveywhere we want to bind our own evil +;; keybinds. (dolist (mode evil-collection-mode-list) (dolist (req (or (cdr-safe mode) (list mode))) (with-eval-after-load req diff --git a/modules/editor/evil/README.org b/modules/editor/evil/README.org index f1f81294a..d83e57182 100644 --- a/modules/editor/evil/README.org +++ b/modules/editor/evil/README.org @@ -1,7 +1,7 @@ #+TITLE: feature/evil #+DATE: February 2, 2017 #+SINCE: v2.0 -#+STARTUP: inlineimages +#+STARTUP: inlineimages nofold * Table of Contents :TOC_3:noexport: - [[#description][Description]] @@ -16,6 +16,8 @@ - [[#configuration][Configuration]] - [[#removing-evil-mode][Removing evil-mode]] - [[#restoring-old-substitution-behavior-on-ss][Restoring old substitution behavior on s/S]] + - [[#restoring-old-y-behavior-yank-the-whole-line][Restoring old Y behavior (yank the whole line)]] + - [[#disabling-cursor-movement-when-exiting-insert-mode][Disabling cursor movement when exiting insert mode]] * Description This holy module brings the vim experience to Emacs. @@ -32,6 +34,7 @@ This holy module brings the vim experience to Emacs. + [[https://github.com/syl20bnr/evil-escape][evil-escape]] + [[https://github.com/Dewdrops/evil-exchange][evil-exchange]] + [[https://github.com/TheBB/evil-indent-plus][evil-indent-plus]] ++ [[https://github.com/edkolev/evil-lion][evil-lion]] + [[https://github.com/redguardtoo/evil-nerd-commenter][evil-nerd-commentary]] + [[https://github.com/redguardtoo/evil-matchit][evil-matchit]] + [[https://github.com/cofi/evil-numbers][evil-numbers]] @@ -42,9 +45,9 @@ This holy module brings the vim experience to Emacs. + [[https://github.com/bling/evil-visualstar][evil-visualstar]] + [[https://github.com/ninrod/exato][exato]] + [[https://github.com/emacs-evil/evil-collection][evil-collection]]* ++ [[https://www.github.com/rgrinberg/evil-quick-diff][evil-quick-diff]] ** Hacks -+ When a window is split, the new window will be focused. + The o/O keys will respect and continue commented lines (can be disabled by setting ~+evil-want-o/O-to-continue-comments~ to ~nil~). + In visual mode, =*= and =#= will search for the current selection instead of @@ -66,6 +69,7 @@ The following vim plugins have been ported to evil: |-----------------------+--------------------------------+--------------------------------------------| | vim-commentary | evil-nerd-commenter | omap =gc= | | vim-easymotion | evil-easymotion | omap =gs= | +| vim-lion | evil-lion | omap =gl= / =gL= | | vim-seek or vim-sneak | evil-snipe | mmap =s= / =S=, omap =z= / =Z= & =x= / =X= | | vim-surround | evil-embrace and evil-surround | vmap =S=, omap =ys= | @@ -96,6 +100,9 @@ And these are text objects added by this module: + =a= C-style function arguments (provided by ~evil-args~) + =B= any block delimited by braces, parentheses or brackets (provided by ~evil-textobj-anyblock~) ++ =c= Comments ++ =f= For functions (but relies on the major mode to have defined a sane ++ =g= The entire buffer + =i j k= by indentation (=k= includes one line above; =j= includes one line above and below) (provided by ~evil-indent-plus~) + =x= XML attributes (provided by ~exato~) @@ -104,15 +111,11 @@ And these are text objects added by this module: | Ex Command | Description | |-----------------------+--------------------------------------------------------------------------------------| | ~:@~ | Apply macro on selected lines | -| ~:ag[!] REGEXP~ | Perform a project search with ag | -| ~:agcwd[!] REGEXP~ | Perform a project search with ag from the current directory | | ~:al[ign][!] REGEXP~ | Align text to the first match of REGEXP. If BANG, align all matches on each line | | ~:cp[!] NEWPATH~ | Copy the current file to NEWPATH | | ~:dash QUERY~ | Look up QUERY (or the symbol at point) in dash docsets | | ~:dehtml [INPUT]~ | HTML decode selected text / inserts result if INPUT is given | | ~:enhtml [INPUT]~ | HTML encode selected text / inserts result if INPUT is given | -| ~:grep[!]~ | Perform a project search with git-grep | -| ~:grepcwd[!]~ | Perform a project search with git-grep from the current directory | | ~:iedit REGEXP~ | Invoke iedit on all matches for REGEXP | | ~:k[ill]all[!]~ | Kill all buffers (if BANG, affect buffer across workspaces) | | ~:k[ill]b~ | Kill all buried buffers | @@ -128,8 +131,6 @@ And these are text objects added by this module: | ~:repl~ | Open a REPL and/or copy the current selection to it | | ~:retab~ | Convert indentation to the default within the selection | | ~:rev[erse]~ | Reverse the selected lines | -| ~:rg[!]~ | Perform a project search with ripgrep | -| ~:rgcwd[!]~ | Perform a project search with ripgrep from the current directory | | ~:rm[!] [PATH]~ | Delete the current buffer's file and buffer | | ~:tcd[!]~ | Send =cd X= to tmux. X = the project root if BANG, X = ~default-directory~ otherwise | @@ -138,7 +139,7 @@ And these are text objects added by this module: You must do two things to remove Evil: 1. Remove =:editor evil= from =~/.doom.d/init.el=, -2. Run ~doom refresh~ to clean up lingering dependencies and refresh your +2. Run ~doom sync~ to clean up lingering dependencies and regenerate your autoloads files. 3. [OPTIONAL] You may want to assign new values to ~doom-leader-alt-key~ and ~doom-localleader-alt-key~. These are bound to =C-c= and =C-c l= by default. @@ -174,3 +175,23 @@ To disable evil-snipe on s/S, you can either: added to =$DOOMDIR/packages.el=, but this will also disable incremental highlighting for the f/F/t/T motions keys. 3. Or use =cl= and =cc=, respectively; they do the same thing. +** Restoring old Y behavior (yank the whole line) +Doom changes the behavior of the =Y= key in normal mode to yank-to-EOL +(equivalent to =y$=). This was to make it consistent with the =C= and =D= +capital operators, and because it was redundant with =yy=, which is easier to +type than =y$=. + +If you prefer the old behavior, it can be reversed with: + +#+BEGIN_SRC elisp +;; add to ~/.doom.d/config.el +(setq! evil-want-Y-yank-to-eol nil) +#+END_SRC +** Disabling cursor movement when exiting insert mode +Vim (and evil) move the cursor one character back when exiting insert mode. If +you prefer that it didn't, set: + +#+BEGIN_SRC elisp +;; add to ~/.doom.d/config.el +(setq evil-move-cursor-back nil) +#+END_SRC diff --git a/modules/editor/evil/autoload/advice.el b/modules/editor/evil/autoload/advice.el index 651393858..834f40315 100644 --- a/modules/editor/evil/autoload/advice.el +++ b/modules/editor/evil/autoload/advice.el @@ -15,76 +15,66 @@ support for most vim file modifiers, as well as: See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers for more information on modifiers." - (let* (case-fold-search - (regexp (concat "\\(?:^\\|[^\\\\]\\)" - "\\([#%]\\)" - "\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)")) - (matches - (cl-loop with i = 0 - while (and (< i (length file-name)) - (string-match regexp file-name i)) - do (setq i (1+ (match-beginning 0))) - and collect - (cl-loop for j to (/ (length (match-data)) 2) - collect (match-string j file-name))))) - (dolist (match matches) - (let ((flags (split-string (car (cdr (cdr match))) ":" t)) - (path (and buffer-file-name - (pcase (car (cdr match)) - ("%" (file-relative-name buffer-file-name)) - ("#" (save-excursion (other-window 1) (file-relative-name buffer-file-name)))))) - flag global) - (if (not path) - (setq path "") - (while flags - (setq flag (pop flags)) - (when (string-suffix-p "\\" flag) - (setq flag (concat flag (pop flags)))) - (when (string-prefix-p "gs" flag) - (setq global t - flag (substring flag 1))) - (setq path - (or (pcase (substring flag 0 1) - ("p" (expand-file-name path)) - ("~" (concat "~/" (file-relative-name path "~"))) - ("." (file-relative-name path default-directory)) - ("t" (file-name-nondirectory (directory-file-name path))) - ("r" (file-name-sans-extension path)) - ("e" (file-name-extension path)) - ("S" (shell-quote-argument path)) - ("h" - (let ((parent (file-name-directory (expand-file-name path)))) - (unless (equal (file-truename path) - (file-truename parent)) - (if (file-name-absolute-p path) - (directory-file-name parent) - (file-relative-name parent))))) - ("s" - (if (featurep 'evil) - (when-let (args (evil-delimited-arguments (substring flag 1) 2)) - (let ((pattern (evil-transform-vim-style-regexp (car args))) - (replace (cadr args))) - (replace-regexp-in-string - (if global pattern (concat "\\(" pattern "\\).*\\'")) - (evil-transform-vim-style-regexp replace) path t t - (unless global 1)))) - path)) - ("P" - (let ((project-root (doom-project-root (file-name-directory (expand-file-name path))))) - (unless project-root - (user-error "Not in a project")) - (abbreviate-file-name project-root))) - (_ path)) - ""))) - ;; strip trailing slash, if applicable - (when (and (not (string= path "")) (equal (substring path -1) "/")) - (setq path (substring path 0 -1)))) - (setq file-name - (replace-regexp-in-string - (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" - (regexp-quote (string-trim-left (car match)))) - path file-name t t 1)))) - (replace-regexp-in-string regexp "\\1" file-name t))) + (let (case-fold-search) + (with-temp-buffer + (save-excursion (insert file-name)) + (while (re-search-forward "\\(^\\|[^\\\\]\\)\\(\\([%#]\\)\\(:\\([PphtreS~.]\\|g?s\\)\\)*\\)" nil t) + (catch 'continue + (unless buffer-file-name + (replace-match (match-string 1) t t nil 2) + (throw 'continue t)) + (let ((beg (match-beginning 2)) + (end (match-end 3)) + (path (pcase (match-string 3) + ("%" (file-relative-name buffer-file-name)) + ("#" (and (other-buffer) + (buffer-file-name (other-buffer))))))) + (save-match-data + (goto-char beg) + (while (re-search-forward ":\\([PphtreS~.]\\|g?s\\)" (+ (point) 3) t) + (let* ((modifier (match-string 1)) + (global (string-prefix-p "gs" modifier))) + (when global + (setq modifier (substring modifier 1))) + (setq end (match-end 1) + path + (or (when path + (pcase (substring modifier 0 1) + ("p" (expand-file-name path)) + ("~" (concat "~/" (file-relative-name path "~"))) + ("." (file-relative-name path default-directory)) + ("t" (file-name-nondirectory (directory-file-name path))) + ("r" (file-name-sans-extension path)) + ("e" (file-name-extension path)) + ("S" (shell-quote-argument path)) + ("h" + (let ((parent (file-name-directory (expand-file-name path)))) + (unless (file-equal-p path parent) + (if (file-name-absolute-p path) + (directory-file-name parent) + (file-relative-name parent))))) + ("s" + (if (featurep 'evil) + (when-let (args (evil-delimited-arguments (substring modifier 1) 2)) + (let ((pattern (evil-transform-vim-style-regexp (car args))) + (replace (cadr args))) + (replace-regexp-in-string + (if global pattern (concat "\\(" pattern "\\).*\\'")) + (evil-transform-vim-style-regexp replace) path t t + (unless global 1)))) + path)) + ("P" + (let ((project-root (doom-project-root (file-name-directory (expand-file-name path))))) + (unless project-root + (user-error "Not in a project")) + (abbreviate-file-name project-root))))) + "")) + ;; strip trailing slash, if applicable + (or (string-empty-p path) + (not (equal (substring path -1) "/")) + (setq path (substring path 0 -1)))))) + (replace-match path t t nil 2)))) + (replace-regexp-in-string "\\\\\\([#%]\\)" "\\1" (buffer-string) t)))) (defun +evil--insert-newline (&optional above _noextranewline) (let ((pos (save-excursion (beginning-of-line-text) (point))) @@ -158,9 +148,9 @@ more information on modifiers." ;; HACK This ping-ponging between the destination and source windows is to ;; update the window focus history, so that, if you close either split ;; afterwards you won't be sent to some random window. - (let* ((doom-inhibit-switch-window-hooks t) - (origwin (selected-window)) - (win (select-window (split-window origwin count 'below)))) + (let ((doom-inhibit-switch-window-hooks t) + (origwin (selected-window))) + (select-window (split-window origwin count 'below)) (unless evil-split-window-below (select-window origwin)) (run-hooks 'doom-switch-window-hook)) @@ -177,9 +167,9 @@ more information on modifiers." ;; HACK This ping-ponging between the destination and source windows is to ;; update the window focus history, so that, if you close either split ;; afterwards you won't be sent to some random window. - (let* ((doom-inhibit-switch-window-hooks t) - (origwin (selected-window)) - (win (select-window (split-window origwin count 'right)))) + (let ((doom-inhibit-switch-window-hooks t) + (origwin (selected-window))) + (select-window (split-window origwin count 'right)) (unless evil-vsplit-window-right (select-window origwin)) (run-hooks 'doom-switch-window-hook)) diff --git a/modules/editor/evil/autoload/evil.el b/modules/editor/evil/autoload/evil.el index a6548357a..575f97077 100644 --- a/modules/editor/evil/autoload/evil.el +++ b/modules/editor/evil/autoload/evil.el @@ -31,11 +31,12 @@ (evil-visual-restore)) ;;;###autoload -(defun +evil/paste-preserve-register () - "Call `evil-paste-after' without overwriting the clipboard (by writing to the -0 register instead). This allows you to paste the same text again afterwards." +(defun +evil/alt-paste () + "Call `evil-paste-after' but invert `evil-kill-on-visual-paste'. +By default, this replaces the selection with what's in the clipboard without +replacing its contents." (interactive) - (let ((evil-this-register ?0)) + (let ((evil-kill-on-visual-paste (not evil-kill-on-visual-paste))) (call-interactively #'evil-paste-after))) (defun +evil--window-swap (direction) @@ -75,28 +76,35 @@ the only window, use evil-window-move-* (e.g. `evil-window-move-far-left')." (select-window that-window)))) ;;;###autoload -(defun +evil/window-move-left () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'left)) +(defun +evil/window-move-left () + "Swap windows to the left." + (interactive) (+evil--window-swap 'left)) ;;;###autoload -(defun +evil/window-move-right () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'right)) +(defun +evil/window-move-right () + "Swap windows to the right" + (interactive) (+evil--window-swap 'right)) ;;;###autoload -(defun +evil/window-move-up () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'up)) +(defun +evil/window-move-up () + "Swap windows upward." + (interactive) (+evil--window-swap 'up)) ;;;###autoload -(defun +evil/window-move-down () "See `+evil--window-swap'" (interactive) (+evil--window-swap 'down)) +(defun +evil/window-move-down () + "Swap windows downward." + (interactive) (+evil--window-swap 'down)) ;;;###autoload -(defun +evil/easymotion () - "Invoke and lazy-load `evil-easymotion' without compromising which-key -integration." - (interactive) +(defun +evil/easymotion (&optional state keymap) + "Invoke `evil-easymotion' lazily without compromising which-key integration." + (interactive (list 'motion 'global)) (let ((prefix (this-command-keys))) - (evil-define-key* 'motion 'global prefix nil) - (evilem-default-keybindings (key-description prefix)) + (require 'evil-easymotion) + (evil-define-key* state keymap prefix evilem-map) (setq prefix-arg current-prefix-arg unread-command-events (mapcar (lambda (e) (cons t e)) (vconcat (when evil-this-operator (where-is-internal evil-this-operator - evil-normal-state-map + nil t)) prefix))))) @@ -149,7 +157,7 @@ Widens narrowed buffers first. If BANG, use indirect buffer clones instead." (doom/narrow-buffer-indirectly beg end))) ;;;###autoload (autoload '+evil:yank-unindented "editor/evil/autoload/evil" nil t) -(evil-define-operator +evil:yank-unindented (beg end type register yank-handler) +(evil-define-operator +evil:yank-unindented (beg end _type _register _yank-handler) "Saves the (reindented) characters in motion into the kill-ring." :move-point nil :repeat nil diff --git a/modules/editor/evil/autoload/ex.el b/modules/editor/evil/autoload/ex.el index 6ad458106..09b18ed70 100644 --- a/modules/editor/evil/autoload/ex.el +++ b/modules/editor/evil/autoload/ex.el @@ -110,10 +110,11 @@ If BANG is non-nil, open compilation output in a comint buffer. If BANG, then run ARGUMENTS as a full command. This command understands vim file modifiers (like %:p:h). See `+evil-resolve-vim-path-a' for details." (interactive "") - (+evil:compile (format "make %s" - (evil-ex-replace-special-filenames - arguments)) - bang)) + (let ((compile-command "make")) + (+evil:compile (if (stringp arguments) + (evil-ex-replace-special-filenames arguments) + "") + bang))) ;;;###autoload (autoload '+evil:compile "editor/evil/autoload/ex" nil t) (evil-define-command +evil:compile (arguments &optional bang) @@ -168,10 +169,11 @@ function and open its documentation with `helpful-function'. Otherwise, it will search for it with `apropos'. If QUERY is empty, this runs the equivalent of 'M-x apropos'. If BANG is -non-nil, a search is preformed against Doom's manual (wiht `doom/help-search')." +non-nil, a search is preformed against Doom's manual (with +`doom/help-search-headings')." (interactive "") (if bang - (doom/help-search query) + (doom/help-search-headings query) (save-match-data (cond ((or (null query) (string-empty-p (string-trim query))) (call-interactively diff --git a/modules/editor/evil/autoload/textobjects.el b/modules/editor/evil/autoload/textobjects.el index 6c7028bd6..a1c53b03f 100644 --- a/modules/editor/evil/autoload/textobjects.el +++ b/modules/editor/evil/autoload/textobjects.el @@ -1,13 +1,43 @@ ;;; editor/evil/autoload/textobjects.el -*- lexical-binding: t; -*- ;;;###autoload (autoload '+evil:whole-buffer-txtobj "editor/evil/autoload/textobjects" nil nil) -(evil-define-text-object +evil:whole-buffer-txtobj (_count &optional _beg _end type) +(evil-define-text-object +evil:whole-buffer-txtobj (count &optional _beg _end type) "Text object to select the whole buffer." (evil-range (point-min) (point-max) type)) ;;;###autoload (autoload '+evil:defun-txtobj "editor/evil/autoload/textobjects" nil nil) -(evil-define-text-object +evil:defun-txtobj (_count &optional _beg _end type) +(evil-define-text-object +evil:defun-txtobj (count &optional _beg _end type) "Text object to select the whole buffer." (cl-destructuring-bind (beg . end) (bounds-of-thing-at-point 'defun) (evil-range beg end type))) + +;;;###autoload (autoload '+evil:inner-url-txtobj "editor/evil/autoload/textobjects" nil nil) +(evil-define-text-object +evil:inner-url-txtobj (count &optional _beg _end type) + "Text object to select the inner url at point. + +This excludes the protocol and querystring." + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'url) + (evil-range + (save-excursion + (goto-char beg) + (re-search-forward "://" end t)) + (save-excursion + (goto-char end) + (- (if-let (pos (re-search-backward "[?#]" beg t)) + pos + end) + (if (evil-visual-state-p) + 1 + 0))) + type))) + +;;;###autoload (autoload '+evil:outer-url-txtobj "editor/evil/autoload/textobjects" nil nil) +(evil-define-text-object +evil:outer-url-txtobj (count &optional _beg _end type) + "Text object to select the whole url at point." + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'url) + (evil-range + beg (- end (if (evil-visual-state-p) 1 0)) + type))) diff --git a/modules/editor/evil/config.el b/modules/editor/evil/config.el index 0873bea44..910922427 100644 --- a/modules/editor/evil/config.el +++ b/modules/editor/evil/config.el @@ -21,8 +21,10 @@ directives. By default, this only recognizes C directives.") ;; Set these defaults before `evil'; use `defvar' so they can be changed prior ;; to loading. (defvar evil-want-C-i-jump (or (daemonp) (display-graphic-p))) -(defvar evil-want-C-u-scroll t) +(defvar evil-want-C-u-scroll t) ; moved the universal arg to u +(defvar evil-want-C-u-delete t) (defvar evil-want-C-w-scroll t) +(defvar evil-want-C-w-delete t) (defvar evil-want-Y-yank-to-eol t) (defvar evil-want-abbrev-expand-on-insert-exit nil) @@ -31,15 +33,10 @@ directives. By default, this only recognizes C directives.") :demand t :preface (setq evil-want-visual-char-semi-exclusive t - evil-magic t - evil-echo-state t - evil-indent-convert-tabs t evil-ex-search-vim-style-regexp t evil-ex-substitute-global t evil-ex-visual-char-range t ; column range for ex commands - evil-insert-skip-empty-lines t evil-mode-line-format 'nil - evil-respect-visual-line-mode t ;; more vim-like behavior evil-symbol-word-search t ;; cursor appearance @@ -49,7 +46,15 @@ directives. By default, this only recognizes C directives.") evil-insert-state-cursor 'bar evil-visual-state-cursor 'hollow ;; must be set before evil/evil-collection is loaded - evil-want-keybinding (not (featurep! +everywhere))) + evil-want-keybinding (not (featurep! +everywhere)) + ;; Only do highlighting in selected window so that Emacs has less work + ;; to do highlighting them all. + evil-ex-interactive-search-highlight 'selected-window) + + ;; Slow this down from 0.02 to prevent blocking in large or folded buffers + ;; like magit while incrementally highlighting matches. + (setq-hook! 'magit-mode-hook evil-ex-hl-update-delay 0.2) + (setq-hook! 'so-long-minor-mode-hook evil-ex-hl-update-delay 0.25) :config (evil-select-search-module 'evil-search-module 'evil-search) @@ -121,17 +126,26 @@ directives. By default, this only recognizes C directives.") (count-lines (point-min) (point-max)) (buffer-size))))) - ;; 'gq' moves the cursor to the beginning of selection. Disable this, since - ;; it's more disruptive than helpful. + ;; HACK '=' moves the cursor to the beginning of selection. Disable this, + ;; since it's more disruptive than helpful. (defadvice! +evil--dont-move-cursor-a (orig-fn &rest args) :around #'evil-indent (save-excursion (apply orig-fn args))) - ;; In evil, registers 2-9 are buffer-local. In vim, they're global, so... - (defadvice! +evil--make-numbered-markers-global-a (arg) + ;; REVIEW In evil, registers 2-9 are buffer-local. In vim, they're global, + ;; so... Perhaps this should be PRed upstream? + (defadvice! +evil--make-numbered-markers-global-a (char) :after-until #'evil-global-marker-p (and (>= char ?2) (<= char ?9))) + ;; REVIEW Fix #2493: dir-locals cannot target fundamental-mode when evil-mode + ;; is active. See https://github.com/hlissner/doom-emacs/issues/2493. + ;; Revert this if this is ever fixed upstream. + (defadvice! +evil--fix-local-vars-a (&rest _) + :before #'turn-on-evil-mode + (when (eq major-mode 'fundamental-mode) + (hack-local-variables))) + ;; Make ESC (from normal mode) the universal escaper. See `doom-escape-hook'. (advice-add #'evil-force-normal-state :after #'+evil-escape-a) @@ -154,8 +168,10 @@ directives. By default, this only recognizes C directives.") ;; Recenter screen after most searches (dolist (fn '(evil-visualstar/begin-search-forward evil-visualstar/begin-search-backward + evil-ex-search-word-forward evil-ex-search-word-backward - evil-ex-search-word-backward + evil-ex-search-next + evil-ex-search-previous evil-ex-search-forward evil-ex-search-backward)) (advice-add fn :after #'doom-recenter-a)) @@ -218,10 +234,10 @@ directives. By default, this only recognizes C directives.") :hook (org-mode . embrace-org-mode-hook) :hook ((ruby-mode enh-ruby-mode) . embrace-ruby-mode-hook) :hook (emacs-lisp-mode . embrace-emacs-lisp-mode-hook) - :hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode) + :hook ((lisp-mode emacs-lisp-mode clojure-mode racket-mode hy-mode) . +evil-embrace-lisp-mode-hook-h) :hook ((org-mode LaTeX-mode) . +evil-embrace-latex-mode-hook-h) - :hook ((c++-mode rust-mode rustic-mode csharp-mode java-mode swift-mode typescript-mode) + :hook ((c++-mode rustic-mode csharp-mode java-mode swift-mode typescript-mode) . +evil-embrace-angle-bracket-modes-hook-h) :init (after! evil-surround @@ -233,6 +249,8 @@ directives. By default, this only recognizes C directives.") (embrace-add-pair-regexp ?l "\\[a-z]+{" "}" #'+evil--embrace-latex)) (defun +evil-embrace-lisp-mode-hook-h () + ;; Avoid `embrace-add-pair-regexp' because it would overwrite the default + ;; `f' rule, which we want for other modes (push (cons ?f (make-embrace-pair-struct :key ?f :read-function #'+evil--embrace-elisp-fn @@ -241,14 +259,11 @@ directives. By default, this only recognizes C directives.") embrace--pairs-list)) (defun +evil-embrace-angle-bracket-modes-hook-h () - (set (make-local-variable 'evil-embrace-evil-surround-keys) - (delq ?< evil-embrace-evil-surround-keys)) - (push (cons ?< (make-embrace-pair-struct - :key ?< - :read-function #'+evil--embrace-angle-brackets - :left-regexp "\\[a-z]+<" - :right-regexp ">")) - embrace--pairs-list)) + (let ((var (make-local-variable 'evil-embrace-evil-surround-keys))) + (set var (delq ?< evil-embrace-evil-surround-keys)) + (set var (delq ?> evil-embrace-evil-surround-keys))) + (embrace-add-pair-regexp ?< "\\_<[a-z0-9-_]+<" ">" #'+evil--embrace-angle-brackets) + (embrace-add-pair ?> "<" ">")) ;; Add escaped-sequence support to embrace (setf (alist-get ?\\ (default-value 'embrace--pairs-list)) @@ -285,6 +300,10 @@ directives. By default, this only recognizes C directives.") t)))) +(use-package! evil-quick-diff + :commands (evil-quick-diff evil-quick-diff-cancel)) + + (use-package! evil-nerd-commenter :commands (evilnc-comment-operator evilnc-inner-comment @@ -386,11 +405,11 @@ To change these keys see `+evil-repeat-keys'." ;; `evil-collection' (when (featurep! +everywhere) + (setq evil-collection-company-use-tng (featurep! :completion company +tng)) + (unless doom-reloading-p (load! "+everywhere")) - (setq evil-collection-company-use-tng (featurep! :completion company +tng)) - ;; Don't let evil-collection interfere with certain keys (appendq! evil-collection-key-blacklist (append (when (featurep! :tools lookup) @@ -402,7 +421,7 @@ To change these keys see `+evil-repeat-keys'." (defadvice! +evil-collection-disable-blacklist-a (orig-fn) :around #'evil-collection-vterm-toggle-send-escape ; allow binding to ESC (let (evil-collection-key-blacklist) - (apply orig-fn)))) + (funcall-interactively orig-fn)))) ;; Keybinds that have no Emacs+evil analogues (i.e. don't exist): ;; zq - mark word at point as good word @@ -440,6 +459,9 @@ To change these keys see `+evil-repeat-keys'." :n "gT" #'+workspace:switch-previous :n "]w" #'+workspace/switch-right :n "[w" #'+workspace/switch-left) + (:when (featurep! :ui tabs) + :n "gt" #'centaur-tabs-forward + :n "gT" #'centaur-tabs-backward) ;; custom vim-unmpaired-esque keys :m "]#" #'+evil/next-preproc-directive @@ -461,7 +483,7 @@ To change these keys see `+evil-repeat-keys'." :n "[o" #'+evil/insert-newline-above :n "]o" #'+evil/insert-newline-below :n "gp" #'+evil/reselect-paste - :v "gp" #'+evil/paste-preserve-register + :v "gp" #'+evil/alt-paste :nv "g@" #'+evil:apply-macro :nv "gc" #'evilnc-comment-operator :nv "gx" #'evil-exchange @@ -494,7 +516,7 @@ To change these keys see `+evil-repeat-keys'." :n "gr" #'elfeed-search-update--force :n "gR" #'elfeed-search-fetch)) - :nv "z=" #'flyspell-correct-word-generic + :nv "z=" #'flyspell-correct-at-point ;; custom evil keybinds :nv "zn" #'+evil:narrow-buffer :n "zN" #'doom/widen-indirectly-narrowed-buffer @@ -540,18 +562,19 @@ To change these keys see `+evil-repeat-keys'." :textobj "i" #'evil-indent-plus-i-indent #'evil-indent-plus-a-indent :textobj "j" #'evil-indent-plus-i-indent-up-down #'evil-indent-plus-a-indent-up-down :textobj "k" #'evil-indent-plus-i-indent-up #'evil-indent-plus-a-indent-up + :textobj "u" #'+evil:inner-url-txtobj #'+evil:outer-url-txtobj :textobj "x" #'evil-inner-xml-attr #'evil-outer-xml-attr - ;; evil-easymotion + ;; evil-easymotion (see `+evil/easymotion') (:after evil-easymotion - :map evilem-map - "a" (evilem-create #'evil-forward-arg) - "A" (evilem-create #'evil-backward-arg) - "s" #'evil-avy-goto-char-2 - "SPC" (λ!! #'evil-avy-goto-char-timer t) - "/" #'evil-avy-goto-char-timer) + (:map evilem-map + "a" (evilem-create #'evil-forward-arg) + "A" (evilem-create #'evil-backward-arg) + "s" #'evil-avy-goto-char-2 + "SPC" (λ!! #'evil-avy-goto-char-timer t) + "/" #'evil-avy-goto-char-timer)) - ;; evil-snipe + ;; evil-snipe (:after evil-snipe :map evil-snipe-parent-transient-map "C-;" (λ! (require 'evil-easymotion) @@ -566,6 +589,12 @@ To change these keys see `+evil-repeat-keys'." :o "s" #'evil-surround-edit :o "S" #'evil-Surround-edit + ;; evil-lion + :n "gl" #'evil-lion-left + :n "gL" #'evil-lion-right + :v "gl" #'evil-lion-left + :v "gL" #'evil-lion-right + ;; Omni-completion (:when (featurep! :completion company) (:prefix "C-x" diff --git a/modules/editor/evil/packages.el b/modules/editor/evil/packages.el index 7e08dfe70..9e116ce96 100644 --- a/modules/editor/evil/packages.el +++ b/modules/editor/evil/packages.el @@ -1,21 +1,27 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/evil/packages.el -(package! evil) -(package! evil-args) -(package! evil-easymotion) -(package! evil-embrace) -(package! evil-escape) -(package! evil-exchange) -(package! evil-indent-plus) -(package! evil-nerd-commenter) -(package! evil-numbers :recipe (:host github :repo "janpath/evil-numbers")) -(package! evil-snipe) -(package! evil-surround) -(package! evil-textobj-anyblock) -(package! evil-traces) -(package! evil-visualstar) -(package! exato) +(package! evil :pin "7c42ba4de0") +(package! evil-args :pin "758ad5ae54") +(package! evil-easymotion :pin "79c13ed3bc") +(package! evil-embrace :pin "4379adea03") +(package! evil-escape :pin "f4e9116bfb") +(package! evil-exchange :pin "3030e21ee1") +(package! evil-indent-plus :pin "0c7501e6ef") +(package! evil-lion :pin "6b03593f5d") +(package! evil-nerd-commenter :pin "fa40dab8d2") +(package! evil-numbers + :recipe (:host github :repo "janpath/evil-numbers") + :pin "d988041c1f") +(package! evil-snipe :pin "3ec8adfd49") +(package! evil-surround :pin "9b0b17f06c") +(package! evil-textobj-anyblock :pin "ff00980f06") +(package! evil-traces :pin "bc25cae9fa") +(package! evil-visualstar :pin "06c053d8f7") +(package! exato :pin "88266fa7fc") +(package! evil-quick-diff + :recipe (:host github :repo "rgrinberg/evil-quick-diff") + :pin "69c883720b") ;; (when (featurep! +everywhere) @@ -25,4 +31,4 @@ (package! neotree) (autoload 'neotree-make-executor "neotree" nil nil 'macro)) - (package! evil-collection)) + (package! evil-collection :pin "e6a4ba695e")) diff --git a/modules/editor/evil/test/test-evil.el b/modules/editor/evil/test/test-evil.el index 99694f11e..dd77fd0f4 100644 --- a/modules/editor/evil/test/test-evil.el +++ b/modules/editor/evil/test/test-evil.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/evil/test/test-evil.el -(describe "feature/evil" +(describe "editor/evil" :var (resv project-root) (require! :editor evil) diff --git a/modules/editor/file-templates/README.org b/modules/editor/file-templates/README.org index 519c0ee47..9a195f6e5 100644 --- a/modules/editor/file-templates/README.org +++ b/modules/editor/file-templates/README.org @@ -4,21 +4,21 @@ #+STARTUP: inlineimages * Table of Contents :TOC_2:noexport: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] -- [[Prerequisites][Prerequisites]] -- [[Usage][Usage]] - - [[Inserting OSS licenses][Inserting OSS licenses]] -- [[Configuration][Configuration]] - - [[Registering a new file template][Registering a new file template]] - - [[Changing existing file templates][Changing existing file templates]] - - [[Adding new OSS licenses][Adding new OSS licenses]] -- [[Troubleshooting][Troubleshooting]] -- [[Appendix][Appendix]] - - [[API][API]] - - [[Commands][Commands]] - - [[Variables][Variables]] +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#usage][Usage]] + - [[#inserting-oss-licenses][Inserting OSS licenses]] +- [[#configuration][Configuration]] + - [[#registering-a-new-file-template][Registering a new file template]] + - [[#changing-existing-file-templates][Changing existing file templates]] + - [[#adding-new-oss-licenses][Adding new OSS licenses]] +- [[#troubleshooting][Troubleshooting]] +- [[#appendix][Appendix]] + - [[#api][API]] + - [[#commands][Commands]] + - [[#variables][Variables]] * Description This module adds file templates for blank files, powered by yasnippet. @@ -49,8 +49,12 @@ for comment headers in code. #+end_quote * Configuration -** TODO Registering a new file template -** TODO Changing existing file templates +** Registering a new file template +Look into the documentation of ~set-file-template!~ and ~set-file-templates!~. +** Changing existing file templates +Simply register a new template (using ~set-file-template!~) that matches the same +file. The new template would be pushed to the top of ~+file-template-alist~ and +thus would take priority while searching for a template to insert. ** Adding new OSS licenses The ~+file-templates/insert-license~ command searches for snippets under ~text-mode~ that are named ~__license-ABC~, where ABC is the short name of the diff --git a/modules/editor/file-templates/autoload.el b/modules/editor/file-templates/autoload.el index e77622729..1ff309528 100644 --- a/modules/editor/file-templates/autoload.el +++ b/modules/editor/file-templates/autoload.el @@ -41,7 +41,7 @@ these properties: ;;;###autodef (defun set-file-templates! (&rest templates) - "Like `set-file-templates!', but can register multiple file templates at once. + "Like `set-file-template!', but can register multiple file templates at once. \(fn &rest (PRED &key WHEN TRIGGER MODE PROJECT IGNORE))" (defer-until! (boundp '+file-templates-alist) @@ -100,6 +100,8 @@ evil is loaded and enabled)." "Insert a license file template into the current file." (interactive) (require 'yasnippet) + (unless (gethash 'text-mode yas--tables) + (yas-reload-all t)) (let ((templates (let (yas-choose-tables-first ; avoid prompts yas-choose-keys-first) diff --git a/modules/editor/file-templates/config.el b/modules/editor/file-templates/config.el index 46248b5c7..537cd1afd 100644 --- a/modules/editor/file-templates/config.el +++ b/modules/editor/file-templates/config.el @@ -9,7 +9,7 @@ don't have a :trigger property in `+file-templates-alist'.") (defvar +file-templates-alist - `(;; General + '(;; General (gitignore-mode) (dockerfile-mode) ("/docker-compose\\.yml$" :mode yaml-mode) @@ -29,7 +29,7 @@ don't have a :trigger property in `+file-templates-alist'.") :trigger "__doom-module" :mode emacs-lisp-mode) ("-test\\.el$" :mode emacs-ert-mode) - (emacs-lisp-mode :trigger "__initfile") + (emacs-lisp-mode :trigger "__package") (snippet-mode) ;; C/C++ ("/main\\.c\\(?:c\\|pp\\)$" :trigger "__main.cpp" :mode c++-mode) @@ -118,11 +118,14 @@ information.") "Return t if RULE applies to the current buffer." (let ((pred (car rule)) (plist (cdr rule))) - (and (cond ((symbolp pred) (eq major-mode pred)) - ((and (stringp pred) buffer-file-name) - (string-match-p pred buffer-file-name)) - ((not (plist-member plist :when)) t) - ((funcall (plist-get plist :when) buffer-file-name))) + (and (or (and (symbolp pred) + (eq major-mode pred)) + (and (stringp pred) + (stringp buffer-file-name) + (string-match-p pred buffer-file-name) + (or (not (plist-member plist :when)) + (funcall (plist-get plist :when) + buffer-file-name)))) rule))) (defun +file-templates-check-h () @@ -134,10 +137,7 @@ must be non-read-only, empty, and there must be a rule in (bobp) (eobp) (not (member (substring (buffer-name) 0 1) '("*" " "))) (not (file-exists-p buffer-file-name)) - ;; Prevent file-templates from breaking org-capture when target file - ;; doesn't exist and has a file template. - (or (not (fboundp 'org-capture-get)) - (not (org-capture-get :new-buffer))) + (not (buffer-modified-p)) (when-let (rule (cl-find-if #'+file-template-p +file-templates-alist)) (apply #'+file-templates--expand rule)))) @@ -156,4 +156,4 @@ must be non-read-only, empty, and there must be a rule in (yas-reload-all))) ;; -(add-hook 'find-file-hook #'+file-templates-check-h) +(add-hook 'doom-switch-buffer-hook #'+file-templates-check-h) diff --git a/modules/editor/file-templates/packages.el b/modules/editor/file-templates/packages.el index 7a059ca3d..67d4ada0e 100644 --- a/modules/editor/file-templates/packages.el +++ b/modules/editor/file-templates/packages.el @@ -1,5 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/file-templates/packages.el -(package! yasnippet) - +(package! yasnippet :pin "3bf9a3b1af") diff --git a/modules/editor/file-templates/templates/emacs-lisp-mode/__initfile b/modules/editor/file-templates/templates/emacs-lisp-mode/__initfile deleted file mode 100644 index 23a130181..000000000 --- a/modules/editor/file-templates/templates/emacs-lisp-mode/__initfile +++ /dev/null @@ -1,6 +0,0 @@ -;;; `(file-name-nondirectory buffer-file-name)`${1: --- ${2:description}} -*- lexical-binding: t; -*- - -$0 - -(provide '`(file-name-base buffer-file-name)`) -;;; `(file-name-nondirectory buffer-file-name)` ends here diff --git a/modules/editor/file-templates/templates/emacs-lisp-mode/__package b/modules/editor/file-templates/templates/emacs-lisp-mode/__package new file mode 100644 index 000000000..b040adbb6 --- /dev/null +++ b/modules/editor/file-templates/templates/emacs-lisp-mode/__package @@ -0,0 +1,25 @@ +;;; `(file-name-nondirectory buffer-file-name)`${1: --- ${2:description}} -*- lexical-binding: t; -*- +;; +;; Copyright (C) `(format-time-string "%Y")` `user-full-name` +;; +;; Author: `user-full-name` +;; Maintainer: `user-full-name` <`user-mail-address`> +;; Created: `(format-time-string "%B %d, %Y")` +;; Modified: `(format-time-string "%B %d, %Y")` +;; Version: 0.0.1 +;; Keywords: +;; Homepage: https://github.com/`user-login-name`/`(file-name-base buffer-file-name)` +;; Package-Requires: ((emacs `emacs-version`) (cl-lib "0.5")) +;; +;; This file is not part of GNU Emacs. +;; +;;; Commentary: +;; +;; $2 +;; +;;; Code: + +`%`$0 + +(provide '`(file-name-base buffer-file-name)`) +;;; `(file-name-nondirectory buffer-file-name)` ends here diff --git a/modules/editor/file-templates/templates/gitignore-mode/__ b/modules/editor/file-templates/templates/gitignore-mode/__ index cb95699c3..ef1380d91 100644 --- a/modules/editor/file-templates/templates/gitignore-mode/__ +++ b/modules/editor/file-templates/templates/gitignore-mode/__ @@ -20,5 +20,12 @@ tmp/ '("*.class" "build")) ((string= type-ignore "js") - '("*.tern-port"))) + '("*.tern-port" + "node_modules/" + "npm-debug.log*" + "yarn-debug.log*" + "yarn-error.log*" + "*.tsbuildinfo" + ".npm" + ".eslintcache"))) "\n"))` diff --git a/modules/editor/file-templates/templates/org-mode/__doom-readme b/modules/editor/file-templates/templates/org-mode/__doom-readme index 8e8af8fa6..ca0dd707c 100644 --- a/modules/editor/file-templates/templates/org-mode/__doom-readme +++ b/modules/editor/file-templates/templates/org-mode/__doom-readme @@ -1,5 +1,5 @@ # -*- mode: snippet -*- -# name: Doom module readme +# name: Doom module readme # -- #+TITLE: ${1:`(if (string-match "modules/\\([^/]+\\)/\\([^/]+\\)/.+" buffer-file-name) (format "%s/%s" @@ -8,35 +8,63 @@ "")`} #+DATE: `(format (format-time-string "%B %%s, %Y") (string-to-number (format-time-string "%d")))` #+SINCE: ${2:{replace with next tagged release version}} -#+STARTUP: inlineimages +#+STARTUP: inlineimages nofold * Table of Contents :TOC_3:noexport: * Description -${3:A summary of what this module does.} +${3:# A summary of what this module does.} + If possible, include a brief list of feature highlights here + Like code completion, syntax checking or available snippets + Include links to packages & external things where possible +** Maintainers ++ @username_linked_to_gihub (Author) ++ @username_linked_to_gihub ++ @username_linked_to_gihub + +# If this module has no maintainers, then... +This module has no dedicated maintainers. + ** Module Flags ++ =+flag1= A short description of what this flag does and what it might need + when enabled. ++ =+flag2= A short description of what this flag does and what it might need + when enabled. ++ =+flag3= A short description of what this flag does and what it might need + when enabled. + +# If this module has no flags, then... This module provides no flags. ** Plugins -{A list of linked plugins} +# A list of linked plugins ++ [[https://orgmode.org/][org-plus-contrib]] ++ [[https://github.com/sabof/org-bullets][org-bullets]] ++ [[https://github.com/TobiasZawada/org-yt][org-yt]] ++ [[https://github.com/sebastiencs/company-box][company-box]]* (=+childframe=) ++ =:lang crystal= + + [[https://github.com/brantou/ob-crystal][ob-crystal]] ++ =:lang go= + + [[https://github.com/pope/ob-go][ob-go]] ++ =+present= + + [[https://github.com/anler/centered-window-mode][centered-window]] + + [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] + + [[https://gitlab.com/oer/org-re-reveal][org-re-reveal]] ** Hacks -{A list of internal modifications to included packages} +# A list of internal modifications to included packages; omit if unneeded * Prerequisites This module has no prereqisites. * Features -An in-depth list of features, how to use them, and their dependencies. +# An in-depth list of features, how to use them, and their dependencies. * Configuration -How to configure this module, including common problems and how to address them. +# How to configure this module, including common problems and how to address them. * Troubleshooting -Common issues and their solution, or places to look for help. +# Common issues and their solution, or places to look for help. $0 \ No newline at end of file diff --git a/modules/editor/fold/autoload/fold.el b/modules/editor/fold/autoload/fold.el index 7105885d9..69726ebaa 100644 --- a/modules/editor/fold/autoload/fold.el +++ b/modules/editor/fold/autoload/fold.el @@ -27,7 +27,10 @@ (save-excursion (ignore-errors (or (hs-looking-at-block-start-p) - (hs-find-block-beginning))))) + (hs-find-block-beginning) + (unless (eolp) + (end-of-line) + (+fold--hideshow-fold-p)))))) (defun +fold--invisible-points (count) (let (points) diff --git a/modules/editor/fold/config.el b/modules/editor/fold/config.el index 05ab1000f..f72f2fe53 100644 --- a/modules/editor/fold/config.el +++ b/modules/editor/fold/config.el @@ -29,7 +29,7 @@ hs-set-up-overlay #'+fold-hideshow-set-up-overlay-fn) (defadvice! +fold--hideshow-ensure-mode-a (&rest _) - "Ensure `hs-minor-mode' is enabled." + "Ensure `hs-minor-mode' is enabled when we need it, no sooner or later." :before '(hs-toggle-hiding hs-hide-block hs-hide-level hs-show-all hs-hide-all) (unless (bound-and-true-p hs-minor-mode) (hs-minor-mode +1))) diff --git a/modules/editor/fold/packages.el b/modules/editor/fold/packages.el index bb672122e..9420d41a5 100644 --- a/modules/editor/fold/packages.el +++ b/modules/editor/fold/packages.el @@ -1,5 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/fold/packages.el +(package! hideshow :built-in t) + +(package! vimish-fold :pin "d3248a41a7") (when (featurep! :editor evil) - (package! evil-vimish-fold)) + (package! evil-vimish-fold :pin "b6e0e6b91b")) diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index 8b6a19441..00123f9fb 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -95,7 +95,7 @@ Stolen shamelessly from go-mode" (require 'format-all) (let* ((fmtlist (mapcar #'symbol-name (hash-table-keys format-all--format-table))) (fmt (completing-read "Formatter: " fmtlist))) - (if fmt (cons (intern fmt) t)))) + (if fmt (intern fmt)))) ;;;###autoload (defun +format-probe-a (orig-fn) @@ -131,15 +131,19 @@ See `+format/buffer' for the interactive version of this function, and ;; like `gofmt') widen the buffer, in order to only format a region of ;; text, we must make a copy of the buffer to apply formatting to. (let ((output (buffer-substring-no-properties (point-min) (point-max))) - (origin-buffer-file-name (buffer-file-name (buffer-base-buffer))) - (origin-default-directory default-directory)) + (origin-buffer (or (buffer-base-buffer) (current-buffer)))) (with-temp-buffer (with-silent-modifications (insert output) ;; Ensure this temp buffer _seems_ as much like the origin ;; buffer as possible. - (setq default-directory origin-default-directory - buffer-file-name origin-buffer-file-name) + (cl-loop for (var . val) + in (cl-remove-if-not #'listp (buffer-local-variables origin-buffer)) + ;; Making enable-multibyte-characters buffer-local + ;; causes an error. + unless (eq var 'enable-multibyte-characters) + ;; Using setq-local would quote var. + do (set (make-local-variable var) val)) ;; Since we're piping a region of text to the formatter, remove ;; any leading indentation to make it look like a file. (when preserve-indent-p diff --git a/modules/editor/format/autoload/settings.el b/modules/editor/format/autoload/settings.el index b912af50e..359480671 100644 --- a/modules/editor/format/autoload/settings.el +++ b/modules/editor/format/autoload/settings.el @@ -67,7 +67,7 @@ (doom-log "formatter (arglist) %s" args) (if ,(and (or ok-statuses error-regexp) t) (apply #'format-all--buffer-hard - ',ok-statuses ,error-regexp + ',ok-statuses ,error-regexp nil (reverse args)) (apply #'format-all--buffer-easy (reverse args))))) diff --git a/modules/editor/format/packages.el b/modules/editor/format/packages.el index 858b6d9ee..e56316cac 100644 --- a/modules/editor/format/packages.el +++ b/modules/editor/format/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/format/packages.el -(package! format-all) +(package! format-all :pin "f57a2a8abb") diff --git a/modules/editor/god/packages.el b/modules/editor/god/packages.el index a62830687..f5455d0d8 100644 --- a/modules/editor/god/packages.el +++ b/modules/editor/god/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/god/packages.el -(package! god-mode) +(package! god-mode :pin "344167ed9b") diff --git a/modules/editor/lispy/README.org b/modules/editor/lispy/README.org index 51e88eabb..f5f8a4a3b 100644 --- a/modules/editor/lispy/README.org +++ b/modules/editor/lispy/README.org @@ -1,5 +1,15 @@ -#+TITLE: :editor lispy +#+TITLE: editor/lispy +#+DATE: October 27, 2018 +#+SINCE: v2.0 +#+STARTUP: inlineimages +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] +- [[#prerequisites][Prerequisites]] +- [[#troubleshooting][Troubleshooting]] + - [[#mouse-wheel-and-wrapadditional-wrap-key-themes-in-tty-emacs-2573][Mouse wheel and =wrap=/=additional-wrap= key themes in TTY Emacs (#2573)]] + +* Description This module adds [[https://github.com/noctuid/lispyville][lispy]] key functionality in Lisp languages. This includes: @@ -24,13 +34,19 @@ The default key themes that are set are as follows: (prettify insert) (atom-movement normal visual) slurp/barf-lispy - (wrap normal insert) additional - additional-insert - (additional-wrap normal insert) - (escape insert))) + additional-insert)) #+END_SRC See noctuid's [[https://github.com/noctuid/lispyville/blob/master/README.org][README]] for more info on specific keybindings (starting [[https://github.com/noctuid/lispyville#operators-key-theme][here]]) of each key theme. Think of ~lispyville-set-key-theme~ as adding ~parinfer-extensions~ via ~(setq parinfer-extensions '(blah blah blah))~. + +* Prerequisites +This module has no prerequisites. + +* Troubleshooting +** Mouse wheel and =wrap=/=additional-wrap= key themes in TTY Emacs ([[https://github.com/hlissner/doom-emacs/issues/2573][#2573]]) +lispyville binds =M-[= to ~lispyville-wrap-brackets~ when the =wrap= or +=additional-wrap= key theme are enabled. In terminal Emacs, this is the key +Emacs receives when you scroll with your mouse wheel. diff --git a/modules/editor/lispy/config.el b/modules/editor/lispy/config.el index 3d78c72f5..f2860b816 100644 --- a/modules/editor/lispy/config.el +++ b/modules/editor/lispy/config.el @@ -7,11 +7,13 @@ (racket-mode . lispy-mode) (hy-mode . lispy-mode) (lfe-mode . lispy-mode) + (dune-mode . lispy-mode) (clojure-mode . lispy-mode)) :config (setq lispy-close-quotes-at-end-p t) (add-hook 'lispy-mode-hook #'turn-off-smartparens-mode)) + (use-package! lispyville :when (featurep! :editor evil) :hook (lispy-mode . lispyville-mode) @@ -22,8 +24,5 @@ (prettify insert) (atom-movement normal visual) slurp/barf-lispy - (wrap normal insert) additional - additional-insert - (additional-wrap normal insert) - (escape insert)))) + additional-insert))) diff --git a/modules/editor/lispy/packages.el b/modules/editor/lispy/packages.el index a423e54a6..4504bb409 100644 --- a/modules/editor/lispy/packages.el +++ b/modules/editor/lispy/packages.el @@ -1,7 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/lispyville/packages.el -(package! lispy) - +(package! lispy :pin "d6b19fe2c3") (when (featurep! :editor evil) - (package! lispyville)) + (package! lispyville :pin "56198f1c44")) diff --git a/modules/editor/multiple-cursors/autoload/evil-mc.el b/modules/editor/multiple-cursors/autoload/evil-mc.el index f6511e96f..886ca9c0d 100644 --- a/modules/editor/multiple-cursors/autoload/evil-mc.el +++ b/modules/editor/multiple-cursors/autoload/evil-mc.el @@ -51,39 +51,41 @@ pauses cursors." (evil-define-command +multiple-cursors:evil-mc (beg end type pattern &optional flags bang) "Create mc cursors at each match of PATTERN within BEG and END. -This leaves the cursor at the final match. If BANG, then treat PATTERN as -literal. PATTERN is a delimited regexp (the same that :g or :s uses)." - :move-point nil +This leaves the cursor where the final cursor would be. If BANG, then treat +PATTERN as literal. PATTERN is a delimited regexp (the same that :g or :s uses). +FLAGS can be g and/or i; which mean the same thing they do in +`evil-ex-substitute'." :evil-mc t (interactive "") (unless (and (stringp pattern) (not (string-empty-p pattern))) (user-error "A regexp pattern is required")) (require 'evil-mc) - (setq evil-mc-pattern - (cons (evil-ex-make-search-pattern - (if bang (regexp-quote pattern) pattern)) - (list beg end type))) - (evil-with-restriction beg end - (let ((point (point))) - (save-excursion - (goto-char (point-min)) - (while (eq (evil-ex-find-next (evil-mc-get-pattern) 'forward t) t) - (goto-char (1- (point))) - (when (/= point (point)) - (evil-mc-run-cursors-before) - (evil-mc-make-cursor-at-pos (point))) - (goto-char - (if (memq ?g flags) - (line-beginning-position 2) - (1+ (point)))))))) - (evil-exit-visual-state) - (evil-mc-goto-cursor - (if (= (evil-visual-direction) 1) - (evil-mc-find-last-cursor) - (evil-mc-find-first-cursor)) - nil) - (evil-mc-undo-cursor-at-pos (point)) - (if (evil-mc-has-cursors-p) - (evil-mc-print-cursors-info "Created") - (evil-mc-message "No cursors were created"))) + (let ((m (evil-ex-make-pattern + (if bang (regexp-quote pattern) pattern) + (cond ((memq ?i flags) 'insensitive) + ((memq ?I flags) 'sensitive) + ((not +multiple-cursors-evil-mc-ex-case) + evil-ex-search-case) + (t +multiple-cursors-evil-mc-ex-case)) + (or (and +multiple-cursors-evil-mc-ex-global + (not (memq ?g flags))) + (and (not +multiple-cursors-evil-mc-ex-global) + (memq ?g flags)))))) + (evil-mc-run-cursors-before) + (setq evil-mc-pattern (cons m (list beg end type))) + (evil-with-restriction beg end + (goto-char beg) + (while (eq (evil-ex-find-next m 'forward t) t) + (evil-mc-make-cursor-at-pos (1- (point))) + (unless (evil-ex-pattern-whole-line m) + (goto-char (line-beginning-position 2))))) + (evil-mc-goto-cursor + (if (= (evil-visual-direction) 1) + (evil-mc-find-last-cursor) + (evil-mc-find-first-cursor)) + nil) + (evil-mc-undo-cursor-at-pos (1- (point))) + (if (evil-mc-has-cursors-p) + (evil-mc-print-cursors-info "Created") + (evil-mc-message "No cursors were created")))) diff --git a/modules/editor/multiple-cursors/config.el b/modules/editor/multiple-cursors/config.el index c132feba0..a52ffea66 100644 --- a/modules/editor/multiple-cursors/config.el +++ b/modules/editor/multiple-cursors/config.el @@ -1,5 +1,15 @@ ;;; editor/multiple-cursors/config.el -*- lexical-binding: t; -*- +(defvar +multiple-cursors-evil-mc-ex-global t + "TODO") + +(defvar +multiple-cursors-evil-mc-ex-case nil + "TODO") + + +;; +;;; Packages + (use-package! evil-multiedit :when (featurep! :editor evil) :defer t @@ -57,13 +67,16 @@ (company-complete-common . evil-mc-execute-default-complete) (doom/backward-to-bol-or-indent . evil-mc-execute-default-call) (doom/forward-to-last-non-comment-or-eol . evil-mc-execute-default-call) - (doom/backward-kill-to-bol-and-indent . evil-mc-execute-default-call) + (evil-delete-back-to-indentation . evil-mc-execute-default-call) ;; Have evil-mc work with explicit `evil-escape' (on C-g) (evil-escape . evil-mc-execute-default-evil-normal-state) ;; Add `evil-org' support (evil-org-delete . evil-mc-execute-default-evil-delete) (evil-org-delete-char . evil-mc-execute-default-evil-delete) - (evil-org-delete-backward-char . evil-mc-execute-default-evil-delete))) + (evil-org-delete-backward-char . evil-mc-execute-default-evil-delete) + ;; `evil-numbers' + (evil-numbers/inc-at-pt-incremental) + (evil-numbers/dec-at-pt-incremental))) (cl-pushnew `(,(car fn) (:default . ,(or (cdr fn) #'evil-mc-execute-default-call-with-count))) evil-mc-custom-known-commands :test #'eq diff --git a/modules/editor/multiple-cursors/packages.el b/modules/editor/multiple-cursors/packages.el index 470ebbea6..38e273546 100644 --- a/modules/editor/multiple-cursors/packages.el +++ b/modules/editor/multiple-cursors/packages.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/multiple-cursors/packages.el -(cond ((featurep! :editor evil) - (package! evil-multiedit) - (package! evil-mc)) - - ((package! multiple-cursors))) +(cond + ((featurep! :editor evil) + (package! evil-multiedit :pin "c3d43b1a65") + (package! evil-mc :pin "007d471e26")) + ((package! multiple-cursors :pin "b880554d04"))) diff --git a/modules/editor/objed/packages.el b/modules/editor/objed/packages.el index 56bc61702..49c15be40 100644 --- a/modules/editor/objed/packages.el +++ b/modules/editor/objed/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/objed/packages.el -(package! objed) +(package! objed :pin "8dc17701d1") diff --git a/modules/editor/parinfer/config.el b/modules/editor/parinfer/config.el index 93e481da2..912dfff39 100644 --- a/modules/editor/parinfer/config.el +++ b/modules/editor/parinfer/config.el @@ -1,7 +1,12 @@ ;;; editor/parinfer/config.el -*- lexical-binding: t; -*- (use-package! parinfer - :hook ((emacs-lisp-mode clojure-mode scheme-mode lisp-mode) . parinfer-mode) + :hook ((emacs-lisp-mode + clojure-mode + scheme-mode + lisp-mode + racket-mode + hy-mode) . parinfer-mode) :init (setq parinfer-extensions '(defaults diff --git a/modules/editor/parinfer/packages.el b/modules/editor/parinfer/packages.el index 377bb70b1..df109114b 100644 --- a/modules/editor/parinfer/packages.el +++ b/modules/editor/parinfer/packages.el @@ -11,4 +11,4 @@ ;; separate session: (autoload 'evil-define-key "evil-core" nil nil 'macro)) -(package! parinfer) +(package! parinfer :pin "eaad857ae4") diff --git a/modules/editor/rotate-text/packages.el b/modules/editor/rotate-text/packages.el index 5d6486109..15c7216dd 100644 --- a/modules/editor/rotate-text/packages.el +++ b/modules/editor/rotate-text/packages.el @@ -1,4 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/rotate-text/packages.el -(package! rotate-text :recipe (:host github :repo "debug-ito/rotate-text.el")) +(package! rotate-text + :recipe (:host github :repo "debug-ito/rotate-text.el") + :pin "48f193697d") diff --git a/modules/editor/snippets/config.el b/modules/editor/snippets/config.el index a2ada2080..cf50175c5 100644 --- a/modules/editor/snippets/config.el +++ b/modules/editor/snippets/config.el @@ -16,6 +16,9 @@ yas-new-snippet yas-visit-snippet-file) :init + ;; Remove default ~/.emacs.d/snippets + (defvar yas-snippet-dirs nil) + ;; 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)) @@ -28,17 +31,17 @@ :config (setq yas-verbosity (if doom-debug-mode 3 0) - yas-also-auto-indent-first-line t - ;; Remove default ~/.emacs.d/snippets - yas-snippet-dirs (delete yas--default-user-snippets-dir - yas-snippet-dirs)) + yas-also-auto-indent-first-line t) (add-to-list 'load-path +snippets-dir) ;; default snippets library, if available (require 'doom-snippets nil t) ;; Allow private snippets in DOOMDIR/snippets - (add-to-list 'yas-snippet-dirs '+snippets-dir nil #'eq) + (add-to-list 'yas-snippet-dirs '+snippets-dir) + + ;; In case `+snippets-dir' and `doom-snippets-dir' are the same + (advice-add #'yas-snippet-dirs :filter-return #'delete-dups) ;; Remove GUI dropdown prompt (prefer ivy/helm) (delq! 'yas-dropdown-prompt yas-prompt-functions) diff --git a/modules/editor/snippets/packages.el b/modules/editor/snippets/packages.el index b131860bb..a75cce163 100644 --- a/modules/editor/snippets/packages.el +++ b/modules/editor/snippets/packages.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/snippets/packages.el -(package! yasnippet) -(package! auto-yasnippet) - +(package! yasnippet :pin "3bf9a3b1af") +(package! auto-yasnippet :pin "db9e0dd433") (package! doom-snippets :recipe (:host github :repo "hlissner/doom-snippets" - :files ("*.el" "*"))) + :files ("*.el" "*")) + :pin "2781b782a3") diff --git a/modules/editor/word-wrap/autoload.el b/modules/editor/word-wrap/autoload.el index 53bec3372..5e1b95ed8 100644 --- a/modules/editor/word-wrap/autoload.el +++ b/modules/editor/word-wrap/autoload.el @@ -6,6 +6,7 @@ (defvar +word-wrap--enable-visual-line-mode nil) (defvar +word-wrap--major-mode-indent-var nil) +(defvar adaptive-wrap-extra-indent) (defun +word-wrap--adjust-extra-indent-a (orig-fn beg end) "Contextually adjust extra word-wrap indentation." (let ((adaptive-wrap-extra-indent (+word-wrap--calc-extra-indent beg))) diff --git a/modules/editor/word-wrap/packages.el b/modules/editor/word-wrap/packages.el index 85d7b2c55..8a82b406d 100644 --- a/modules/editor/word-wrap/packages.el +++ b/modules/editor/word-wrap/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; editor/word-wrap/packages.el -(package! adaptive-wrap) +(package! adaptive-wrap :pin "1810c0ee8d") diff --git a/modules/emacs/dired/autoload.el b/modules/emacs/dired/autoload.el index 9b8a097ab..e44d3dda0 100644 --- a/modules/emacs/dired/autoload.el +++ b/modules/emacs/dired/autoload.el @@ -6,3 +6,10 @@ (interactive) (mapc #'kill-buffer (doom-buffers-in-mode 'dired-mode)) (message "Killed all dired buffers")) + +;;;###autoload +(defun +dired-enable-git-info-h () + "Enable `dired-git-info-mode' in git repos." + (and (not (file-remote-p default-directory)) + (locate-dominating-file "." ".git") + (dired-git-info-mode 1))) diff --git a/modules/emacs/dired/config.el b/modules/emacs/dired/config.el index cbce83d3f..d78831c3e 100755 --- a/modules/emacs/dired/config.el +++ b/modules/emacs/dired/config.el @@ -3,24 +3,25 @@ (use-package! dired :commands dired-jump :init - (setq ;; Always copy/delete recursively + (setq dired-auto-revert-buffer t ; don't prompt to revert; just do it + dired-dwim-target t ; suggest a target for moving/copying intelligently + dired-hide-details-hide-symlink-targets nil + ;; Always copy/delete recursively dired-recursive-copies 'always dired-recursive-deletes 'top - ;; Instantly revert Dired buffers on re-visiting them, with no message. - ;; (A message is shown if insta-revert is either disabled or determined - ;; dynamically by setting this variable to a function.) - dired-auto-revert-buffer t - ;; Auto refresh dired, but be quiet about it - dired-hide-details-hide-symlink-targets nil - ;; make dired suggest a target for moving/copying intelligently - dired-dwim-target t - ;; files + ;; Where to store image caches image-dired-dir (concat doom-cache-dir "image-dired/") image-dired-db-file (concat image-dired-dir "db.el") image-dired-gallery-dir (concat image-dired-dir "gallery/") image-dired-temp-image-file (concat image-dired-dir "temp-image") - image-dired-temp-rotate-image-file (concat image-dired-dir "temp-rotate-image")) + image-dired-temp-rotate-image-file (concat image-dired-dir "temp-rotate-image") + ;; Screens are larger nowadays, we can afford slightly larger thumbnails + image-dired-thumb-size 150) :config + (set-popup-rule! "^\\*image-dired" + :slot 20 :size 0.8 :select t :quit nil :ttl 0) + (set-evil-initial-state! 'image-dired-display-image-mode 'emacs) + (let ((args (list "-aBhl" "--group-directories-first"))) (when IS-BSD ;; Use GNU ls as `gls' from `coreutils' if available. Add `(setq @@ -64,7 +65,7 @@ only variant that supports --group-directories-first." (use-package! diff-hl - :hook (dired-mode . diff-hl-dired-mode) + :hook (dired-mode . diff-hl-dired-mode-unless-remote) :hook (magit-post-refresh . diff-hl-magit-post-refresh) :config ;; use margin instead of fringe @@ -74,10 +75,7 @@ only variant that supports --group-directories-first." (use-package! ranger :when (featurep! +ranger) :after dired - :init - ;; set up image-dired to allow picture resize - (setq image-dired-dir (concat doom-cache-dir "image-dir") - ranger-override-dired t) + :init (setq ranger-override-dired t) :config (unless (file-directory-p image-dired-dir) (make-directory image-dired-dir)) @@ -161,10 +159,36 @@ we have to clean it up ourselves." ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'" ,cmd) ("\\.\\(?:mp3\\|flac\\)\\'" ,cmd) ("\\.html?\\'" ,cmd) - ("\\.md\\'" ,cmd))))) + ("\\.md\\'" ,cmd)))) + (map! :map dired-mode-map + :localleader + "h" #'dired-omit-mode)) (use-package! fd-dired :when (executable-find doom-projectile-fd-binary) :defer t - :init (advice-add #'find-dired :override #'fd-dired)) + :init + (global-set-key [remap find-dired] #'fd-dired) + (set-popup-rule! "^\\*F\\(?:d\\|ind\\)\\*$" :ignore t)) + + +;;;###package dired-git-info +(map! :after dired + :map (dired-mode-map ranger-mode-map) + :ng ")" #'dired-git-info-mode) +(after! wdired + ;; Temporarily disable `dired-git-info-mode' when entering wdired, due to + ;; reported incompatibilities. + (defvar +dired--git-info-p nil) + (defadvice! +dired--disable-git-info-a (&rest _) + :before #'wdired-change-to-wdired-mode + (setq +dired--git-info-p (bound-and-true-p dired-git-info-mode)) + (when +dired--git-info-p + (dired-git-info-mode -1))) + (defadvice! +dired--reactivate-git-info-a (&rest _) + :after '(wdired-exit + wdired-abort-changes + wdired-finish-edit) + (when +dired--git-info-p + (dired-git-info-mode +1)))) diff --git a/modules/emacs/dired/packages.el b/modules/emacs/dired/packages.el index 4eaa7ec7a..c33be02f3 100644 --- a/modules/emacs/dired/packages.el +++ b/modules/emacs/dired/packages.el @@ -1,11 +1,12 @@ ;; -*- no-byte-compile: t; -*- ;;; emacs/dired/packages.el -(package! diredfl) -(package! diff-hl) -(package! dired-rsync) +(package! diredfl :pin "83567d00af") +(package! dired-git-info :pin "b47f2b0c3a") +(package! diff-hl :pin "fb9eb1cd3c") +(package! dired-rsync :pin "698294cbd4") (when (featurep! +ranger) - (package! ranger)) + (package! ranger :pin "af6f781a60")) (when (featurep! +icons) - (package! all-the-icons-dired)) -(package! fd-dired) + (package! all-the-icons-dired :pin "980b7747d6")) +(package! fd-dired :pin "fd4c3f490b") diff --git a/modules/emacs/ibuffer/config.el b/modules/emacs/ibuffer/config.el index 72755c9d3..9ad9e56d2 100644 --- a/modules/emacs/ibuffer/config.el +++ b/modules/emacs/ibuffer/config.el @@ -39,8 +39,25 @@ :header-mouse-map ibuffer-size-header-map) (file-size-human-readable (buffer-size))) + (when (featurep! :ui workspaces) + (define-ibuffer-filter workspace-buffers + "Filter for workspace buffers" + (:reader + (+workspace-get (read-string "workspace name: ")) :description "workspace") + (memq buf (+workspace-buffer-list qualifier))) + + (defun +ibuffer/workspace (workspace-name) + "Open an ibuffer window for a workspace" + (ibuffer nil (format "%s buffers" workspace-name) + (list (cons 'workspace-buffers (+workspace-get workspace-name))))) + + (defun +ibuffer-current-workspace () + "Open an ibuffer window for the current workspace" + (interactive) + (+ibuffer/workspace (+workspace-current-name)))) + (when (featurep! :completion ivy) - (defadvice! +ibuffer-use-counsel-maybe-a (file &optional wildcards) + (defadvice! +ibuffer-use-counsel-maybe-a (_file &optional _wildcards) "Use `counsel-find-file' instead of `find-file'." :override #'ibuffer-find-file (interactive) diff --git a/modules/emacs/ibuffer/packages.el b/modules/emacs/ibuffer/packages.el index 5328e5d93..f458bd25d 100644 --- a/modules/emacs/ibuffer/packages.el +++ b/modules/emacs/ibuffer/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; emacs/ibuffer/packages.el -(package! ibuffer-projectile) -(package! ibuffer-vc) +(package! ibuffer-projectile :pin "7649621414") +(package! ibuffer-vc :pin "64cb03887b") diff --git a/modules/emacs/vc/autoload/evil.el b/modules/emacs/vc/autoload/evil.el deleted file mode 100644 index 085fc2ed1..000000000 --- a/modules/emacs/vc/autoload/evil.el +++ /dev/null @@ -1,8 +0,0 @@ -;;; emacs/vc/autoload/evil.el -*- lexical-binding: t; -*- -;;;###if (featurep! :editor evil) - -;;;###autoload (autoload '+vc:git-browse "emacs/vc/autoload/evil" nil t) -(evil-define-command +vc:git-browse (bang) - "Ex interface to `+vc/git-browse-region-or-line'." - (interactive "") - (+vc/git-browse-region-or-line bang)) diff --git a/modules/emacs/vc/autoload/vc.el b/modules/emacs/vc/autoload/vc.el index f0a4a8096..e49fed36a 100644 --- a/modules/emacs/vc/autoload/vc.el +++ b/modules/emacs/vc/autoload/vc.el @@ -1,45 +1,34 @@ ;;; emacs/vc/autoload/vc.el -*- lexical-binding: t; -*- -;;;###autoload -(defun +vc-git-root-url () - "Return the root git repo URL for the current file." - (require 'git-link) - (let* ((remote (git-link--select-remote)) - (remote-url (git-link--remote-url remote)) - (remote-info (if remote-url (git-link--parse-remote remote-url)))) - (if remote-info - (format "https://%s/%s" (car remote-info) (cadr remote-info)) - (error "Remote `%s' is unknown or contains an unsupported URL" remote)))) - -(defvar git-link-open-in-browser) -(defvar git-link-use-commit) -;;;###autoload -(defun +vc/git-browse-region-or-line (&optional arg) - "Open the website for the current line of this version controlled file. -Uses the currently checked out branch. If prefix ARG, then use 'master' branch. -If an url can't be ascertained, opens the repository's root." - (interactive "P") - (require 'git-link) - (let ((git-link-default-branch (if arg "master" git-link-default-branch)) - current-prefix-arg ; don't propagate to `git-link' - git-link-use-commit) - (cl-destructuring-bind (beg end) - (if buffer-file-name (git-link--get-region)) - (let ((git-link-open-in-browser t)) - (git-link (git-link--select-remote) beg end))))) +(autoload 'browse-at-remote-get-url "browse-at-remote") +(autoload 'browse-at-remote--file-url "browse-at-remote") ;;;###autoload -(defun +vc-update-header-line-a (revision) - "Show revision details in the header-line, instead of the minibuffer. +(defun +vc/browse-at-remote-kill-file-or-region () + "Copy the current file's remote URL to your clipboard. +If a selection is active, highlight them. Otherwise omits the #L suffix in +the URL." + (interactive) + (let ((url (browse-at-remote-get-url))) + (kill-new url) + (message "Copied to clipboard: %S" url))) -Sometimes I forget `git-timemachine' is enabled in a buffer. Putting revision -info in the `header-line-format' is a good indication." - (let* ((date-relative (nth 3 revision)) - (date-full (nth 4 revision)) - (author (if git-timemachine-show-author (concat (nth 6 revision) ": ") "")) - (sha-or-subject (if (eq git-timemachine-minibuffer-detail 'commit) (car revision) (nth 5 revision)))) - (setq header-line-format - (format "%s%s [%s (%s)]" - (propertize author 'face 'git-timemachine-minibuffer-author-face) - (propertize sha-or-subject 'face 'git-timemachine-minibuffer-detail-face) - date-full date-relative)))) + +(defun +vc--remote-homepage () + (or (let ((url (browse-at-remote--remote-ref))) + (cdr (browse-at-remote--get-url-from-remote (car url)))) + (user-error "Can't find homepage for current project"))) + +;;;###autoload +(defun +vc/browse-at-remote-homepage () + "Open homepage for current project in browser." + (interactive) + (browse-url (+vc--remote-homepage))) + +;;;###autoload +(defun +vc/browse-at-remote-kill-homepage () + "Copy homepage URL of current project to clipboard." + (interactive) + (let ((url (+vc--remote-homepage))) + (kill-new url) + (message "Copied to clipboard: %S" url))) diff --git a/modules/emacs/vc/config.el b/modules/emacs/vc/config.el index a8a595052..3fea39ef2 100644 --- a/modules/emacs/vc/config.el +++ b/modules/emacs/vc/config.el @@ -4,11 +4,6 @@ (setenv "GIT_ASKPASS" "git-gui--askpass")) -;;;###package vc -(setq vc-make-backup-files nil - vc-follow-symlinks t) - - (after! vc-annotate (set-popup-rules! '(("^\\vc-d" :select nil) ; *vc-diff* @@ -21,14 +16,27 @@ (define-key vc-annotate-mode-map [remap quit-window] #'kill-current-buffer)) - (after! git-timemachine ;; Sometimes I forget `git-timemachine' is enabled in a buffer, so instead of ;; showing revision details in the minibuffer, show them in ;; `header-line-format', which has better visibility. (setq git-timemachine-show-minibuffer-details t) - (advice-add #'git-timemachine--show-minibuffer-details - :override #'+vc-update-header-line-a) + + (defadvice! +vc-update-header-line-a (revision) + "Show revision details in the header-line, instead of the minibuffer. + +Sometimes I forget `git-timemachine' is enabled in a buffer. Putting revision +info in the `header-line-format' is a good indication." + :override #'git-timemachine--show-minibuffer-details + (let* ((date-relative (nth 3 revision)) + (date-full (nth 4 revision)) + (author (if git-timemachine-show-author (concat (nth 6 revision) ": ") "")) + (sha-or-subject (if (eq git-timemachine-minibuffer-detail 'commit) (car revision) (nth 5 revision)))) + (setq header-line-format + (format "%s%s [%s (%s)]" + (propertize author 'face 'git-timemachine-minibuffer-author-face) + (propertize sha-or-subject 'face 'git-timemachine-minibuffer-detail-face) + date-full date-relative)))) (after! evil ;; rehash evil keybindings so they are recognized @@ -40,9 +48,6 @@ (map! :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 :n "gtc" #'git-timemachine-show-commit)) @@ -60,9 +65,18 @@ (setq-hook! 'git-commit-mode-hook fill-column 72) (add-hook! 'git-commit-setup-hook - (defun +vc-start-in-insert-state-maybe () + (defun +vc-start-in-insert-state-maybe-h () "Start git-commit-mode in insert state if in a blank commit message, otherwise in default state." (when (and (bound-and-true-p evil-mode) (bobp) (eolp)) (evil-insert-state))))) + + +;; HACK `browse-at-remote' produces urls with `nil' in them, when the repo +;; detached. This creates broken links. I think it is more sensible to at +;; least refer to master in those case. +(defadvice! +vc--fallback-to-master-branch-a () + "Return 'master' in detached state." + :after-until #'browse-at-remote--get-local-branch + "master") diff --git a/modules/emacs/vc/packages.el b/modules/emacs/vc/packages.el index 8a5c76dad..70bd4075f 100644 --- a/modules/emacs/vc/packages.el +++ b/modules/emacs/vc/packages.el @@ -5,7 +5,7 @@ (package! vc-annotate :built-in t) (package! smerge-mode :built-in t) -(package! git-link) -(package! git-timemachine) -(package! gitconfig-mode) -(package! gitignore-mode) +(package! browse-at-remote :pin "aeee6bf38f") +(package! git-timemachine :pin "391eb61050") +(package! gitconfig-mode :pin "55468314a5") +(package! gitignore-mode :pin "55468314a5") diff --git a/modules/email/mu4e/README.org b/modules/email/mu4e/README.org index 06c43b859..a29f675b3 100644 --- a/modules/email/mu4e/README.org +++ b/modules/email/mu4e/README.org @@ -43,19 +43,27 @@ This module requires: ** MacOS #+BEGIN_SRC sh -brew install mu --with-emacs +brew install mu # And one of the following brew install isync # mbsync brew install offlineimap #+END_SRC ** Arch Linux +Run one of the following commands. + #+BEGIN_SRC sh -sudo pacman --noconfirm --needed -S mu -# And one of the following sudo pacman -S isync # mbsync sudo pacman -S offlineimap #+END_SRC + +Install ~mu~, which is not available in the main repositories but in the AUR, by +using for example the AUR helper ~yay~. + +#+BEGIN_SRC sh +yay -S mu +#+END_SRC + ** NixOS #+BEGIN_SRC nix environment.systemPackages = with pkgs; [ @@ -69,11 +77,13 @@ environment.systemPackages = with pkgs; [ [[https://github.com/Emiller88/dotfiles/blob/master/modules/shell/mail.nix][An example of setting up mbsync with home-manager]] ** openSUSE +Remove ~#~ in ~#sync_program=offlineimap~ to choose ~offlineimap~ instead of +~mbsync~. -Remove ~#~ in ~#sync_program=offlineimap~ to choose ~offlineimap~ instead of ~mbsync~. #+BEGIN_SRC sh :dir /sudo:: sync_program=isync # mbsync #sync_program=offlineimap + sudo zypper install maildir-utils $sync_programm #+END_SRC diff --git a/modules/email/mu4e/autoload/email.el b/modules/email/mu4e/autoload/email.el index 1fb11cf76..27d9a10db 100644 --- a/modules/email/mu4e/autoload/email.el +++ b/modules/email/mu4e/autoload/email.el @@ -44,6 +44,7 @@ default/fallback account." (defvar +mu4e-workspace-name "*mu4e*" "TODO") +(defvar +mu4e--old-wconf nil) (add-hook 'mu4e-main-mode-hook #'+mu4e-init-h) @@ -52,7 +53,11 @@ default/fallback account." "Start email client." (interactive) (require 'mu4e) - (+workspace-switch +mu4e-workspace-name t) + (if (featurep! :ui workspaces) + (+workspace-switch +mu4e-workspace-name t) + (setq +mu4e--old-wconf (current-window-configuration)) + (delete-other-windows) + (switch-to-buffer (doom-fallback-buffer))) (mu4e~start 'mu4e~main-view) ;; (save-selected-window ;; (prolusion-mail-show)) @@ -74,5 +79,10 @@ default/fallback account." (defun +mu4e-kill-mu4e-h () ;; (prolusion-mail-hide) - (when (+workspace-exists-p +mu4e-workspace-name) - (+workspace/delete +mu4e-workspace-name))) + (cond + ((and (featurep! :ui workspaces) (+workspace-exists-p +mu4e-workspace-name)) + (+workspace/delete +mu4e-workspace-name)) + + (+mu4e--old-wconf + (set-window-configuration +mu4e--old-wconf) + (setq +mu4e--old-wconf nil)))) diff --git a/modules/email/mu4e/config.el b/modules/email/mu4e/config.el index 870a5348e..b05c00c38 100644 --- a/modules/email/mu4e/config.el +++ b/modules/email/mu4e/config.el @@ -91,23 +91,12 @@ (defadvice! +mu4e--refresh-current-view-a (&rest _) :after #'mu4e-mark-execute-all (mu4e-headers-rerun-search)) - (when (featurep! :tools flyspell) - (add-hook 'mu4e-compose-mode-hook #'flyspell-mode)) - ;; Wrap text in messages (setq-hook! 'mu4e-view-mode-hook truncate-lines nil) (when (fboundp 'imagemagick-register-types) (imagemagick-register-types)) - (set-evil-initial-state! - '(mu4e-main-mode - mu4e-view-mode - mu4e-headers-mode - mu4e-compose-mode - mu4e~update-mail-mode) - 'normal) - (map! :localleader :map mu4e-compose-mode-map :desc "send and exit" "s" #'message-send-and-exit @@ -166,9 +155,9 @@ :action #'+mu4e--mark-seen) ;; Refile will be my "archive" function. (alist-get 'refile mu4e-marks) - (list :char '("d" . "▼") - :prompt "dtrash" - :dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg)) + (list :char '("r" . "▼") + :prompt "rrefile" + :dyn-target (lambda (_target msg) (mu4e-get-refile-folder msg)) :action #'+mu4e--mark-seen)) ;; This hook correctly modifies gmail flags on emails when they are marked. diff --git a/modules/email/mu4e/packages.el b/modules/email/mu4e/packages.el index d999fbc5c..d30ae6e06 100644 --- a/modules/email/mu4e/packages.el +++ b/modules/email/mu4e/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; email/mu4e/packages.el -(package! mu4e-maildirs-extension) +(package! mu4e-maildirs-extension :pin "3ef4c48516") diff --git a/modules/email/notmuch/config.el b/modules/email/notmuch/config.el index 1bf57da94..931f58b1d 100644 --- a/modules/email/notmuch/config.el +++ b/modules/email/notmuch/config.el @@ -11,6 +11,10 @@ (defvar +notmuch-mail-folder "~/.mail/account.gmail" "Where your email folder is located (for use with gmailieer).") + +;; +;;; Packages + (after! notmuch (set-company-backend! 'notmuch-message-mode '(notmuch-company :with company-ispell company-yasnippet)) @@ -58,18 +62,17 @@ notmuch-search-mode-hook) #'hide-mode-line-mode) - (map! - :localleader - :map (notmuch-search-mode-map notmuch-tree-mode-map notmuch-show-mode-map) - :desc "compose email" "c" #'+notmuch/compose - :desc "fetch new email" "u" #'+notmuch/update - :desc "quit notmuch" "q" #'+notmuch/quit - :map notmuch-search-mode-map - :desc "mark as deleted" "d" #'+notmuch/search-delete - :desc "mark as spam" "s" #'+notmuch/search-spam - :map notmuch-tree-mode-map - :desc "mark as deleted" "d" #'+notmuch/tree-delete - :desc "mark as spam" "s" #'+notmuch/tree-spam)) + (map! :localleader + :map (notmuch-search-mode-map notmuch-tree-mode-map notmuch-show-mode-map) + :desc "Compose email" "c" #'+notmuch/compose + :desc "Fetch new email" "u" #'+notmuch/update + :desc "Quit notmuch" "q" #'+notmuch/quit + :map notmuch-search-mode-map + :desc "Mark as deleted" "d" #'+notmuch/search-delete + :desc "Mark as spam" "s" #'+notmuch/search-spam + :map notmuch-tree-mode-map + :desc "Mark as deleted" "d" #'+notmuch/tree-delete + :desc "Mark as spam" "s" #'+notmuch/tree-spam)) (use-package! org-mime @@ -82,6 +85,7 @@ :commands counsel-notmuch :after notmuch) + (use-package! helm-notmuch :when (featurep! :completion helm) :commands helm-notmuch diff --git a/modules/email/notmuch/packages.el b/modules/email/notmuch/packages.el index 72474420b..d1f108bf7 100644 --- a/modules/email/notmuch/packages.el +++ b/modules/email/notmuch/packages.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; email/notmuch/packages.el -(package! notmuch) -(package! org-mime) +(package! notmuch :pin "aba7fb375b") +(package! org-mime :pin "b189976217") (when (featurep! :completion ivy) - (package! counsel-notmuch)) + (package! counsel-notmuch :pin "a4a1562935")) (when (featurep! :completion helm) - (package! helm-notmuch)) + (package! helm-notmuch :pin "97a01497e0")) diff --git a/modules/email/wanderlust/packages.el b/modules/email/wanderlust/packages.el index b2d336172..04963b55b 100644 --- a/modules/email/wanderlust/packages.el +++ b/modules/email/wanderlust/packages.el @@ -1,4 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; app/wanderlust/packages.el -(package! wanderlust) +;; HACK These are wanderlust's dependencies (wanderlust depends on semi, semi +;; depends on flim, flim on apel), but they all have non-standard default +;; branches which straight cannot detect without our help. +(package! apel :recipe (:branch "apel-wl") :pin "d146ddbf88") +(package! flim :recipe (:branch "flim-1_14-wl") :pin "e4bd54fd7d") +(package! semi :recipe (:branch "semi-1_14-wl") :pin "16228dc2d1") + +(package! wanderlust :pin "7a919e422a") diff --git a/modules/input/chinese/packages.el b/modules/input/chinese/packages.el index e2f11169c..0e7b5300d 100644 --- a/modules/input/chinese/packages.el +++ b/modules/input/chinese/packages.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; input/chinese/packages.el -(package! pyim) -(package! fcitx) -(package! ace-pinyin) -(package! pangu-spacing) -(package! pyim) +(package! pyim :pin "bbeb68605e") +(package! fcitx :pin "12dc2638dd") +(package! ace-pinyin :pin "8b2e9335b0") +(package! pangu-spacing :pin "f92898949b") +(package! pyim :pin "bbeb68605e") diff --git a/modules/input/japanese/packages.el b/modules/input/japanese/packages.el index 32afb1631..cddf2a4b6 100644 --- a/modules/input/japanese/packages.el +++ b/modules/input/japanese/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; input/japanese/packages.el -(package! migemo) -(package! avy-migemo) -(package! ddskk) -(package! pangu-spacing) +(package! migemo :pin "f42832c8ac") +(package! avy-migemo :pin "922a6dd82c") +(package! ddskk :pin "51747f7afb") +(package! pangu-spacing :pin "f92898949b") diff --git a/modules/lang/agda/config.el b/modules/lang/agda/config.el index 0fa2bdbc7..d54bddc3b 100644 --- a/modules/lang/agda/config.el +++ b/modules/lang/agda/config.el @@ -1,5 +1,13 @@ ;;; lang/agda/config.el -*- lexical-binding: t; -*- +(when (and (featurep! +local) + (executable-find "agda-mode")) + (add-load-path! + (file-name-directory (shell-command-to-string "agda-mode locate"))) + (unless (require 'agda2 nil t) + (message "Failed to find the `agda2' package"))) + + (map! :after agda2-mode :map agda2-mode-map :localleader diff --git a/modules/lang/agda/packages.el b/modules/lang/agda/packages.el index 35bead2a1..e5b27f296 100644 --- a/modules/lang/agda/packages.el +++ b/modules/lang/agda/packages.el @@ -1,15 +1,16 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/agda/packages.el +(unless (featurep! +local) + (package! agda-input + :recipe (:host github :repo "agda/agda" + :files ("src/data/emacs-mode/agda-input.el") + :nonrecursive t) + :pin "74d9fd53cd") -(package! agda-input - :recipe - (:host github :repo "agda/agda" - :files ("src/data/emacs-mode/agda-input.el"))) - -(package! agda2-mode - :recipe - (:host github :repo "agda/agda" - :files - ("src/data/emacs-mode/*.el" - (:exclude "agda-input.el")))) + (package! agda2-mode + :recipe (:host github :repo "agda/agda" + :files ("src/data/emacs-mode/*.el" + (:exclude "agda-input.el")) + :nonrecursive t) + :pin "74d9fd53cd")) diff --git a/modules/lang/assembly/packages.el b/modules/lang/assembly/packages.el index df770051e..78e44be3d 100644 --- a/modules/lang/assembly/packages.el +++ b/modules/lang/assembly/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/assembly/packages.el -(package! mips-mode) -(package! haxor-mode) -(package! nasm-mode) +(package! mips-mode :pin "75152fc78b") +(package! haxor-mode :pin "6fa25a8e6b") +(package! nasm-mode :pin "65ca6546fc") diff --git a/modules/lang/cc/README.org b/modules/lang/cc/README.org index ce27d8100..666f25f5d 100644 --- a/modules/lang/cc/README.org +++ b/modules/lang/cc/README.org @@ -76,7 +76,8 @@ mkdir irony-mode/server/build pushd irony-mode/server/build DEST="$HOME/.emacs.d/.local/etc/irony-server/" -cmake -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ +cmake -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm \ + -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \ -DCMAKE_INSTALL_PREFIX="$DEST" ../ cmake --build . --use-stderr --config Release --target install diff --git a/modules/lang/cc/autoload.el b/modules/lang/cc/autoload.el index 251840b85..3cc5f3f82 100644 --- a/modules/lang/cc/autoload.el +++ b/modules/lang/cc/autoload.el @@ -3,6 +3,11 @@ ;;;###autoload (add-to-list 'auto-mode-alist '("\\.cl\\'" . opencl-mode)) +;; The plusses in c++-mode can be annoying to search for ivy/helm (which reads +;; queries as regexps), so we add these for convenience. +;;;###autoload (defalias 'cpp-mode 'c++-mode) +;;;###autoload (defvaralias 'cpp-mode-map 'c++-mode-map) + ;; ;; Library diff --git a/modules/lang/cc/config.el b/modules/lang/cc/config.el index 4698c382f..9430e6a77 100644 --- a/modules/lang/cc/config.el +++ b/modules/lang/cc/config.el @@ -36,11 +36,6 @@ This is ignored by ccls.") :commands (c-mode c++-mode objc-mode java-mode) :mode ("\\.mm\\'" . objc-mode) :init - ;; The plusses in c++-mode can be annoying to search for ivy/helm (which reads - ;; queries as regexps), so we add these for convenience. - (defalias 'cpp-mode 'c++-mode) - (defvaralias 'cpp-mode-map 'c++-mode-map) - ;; Activate `c-mode', `c++-mode' or `objc-mode' depending on heuristics (add-to-list 'auto-mode-alist '("\\.h\\'" . +cc-c-c++-objc-mode)) @@ -114,7 +109,11 @@ This is ignored by ccls.") (inclass +cc-c++-lineup-inclass +) (label . 0)))) - (setf (alist-get 'other c-default-style) "doom")) + (when (listp c-default-style) + (setf (alist-get 'other c-default-style) "doom")) + + (after! ffap + (add-to-list 'ffap-alist '(c-mode . ffap-c-mode)))) (use-package! modern-cpp-font-lock @@ -145,7 +144,7 @@ This is ignored by ccls.") :hook (irony-mode . irony-eldoc)) (use-package! flycheck-irony - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :config (flycheck-irony-setup)) (use-package! company-irony @@ -252,4 +251,11 @@ This is ignored by ccls.") (after! projectile (add-to-list 'projectile-globally-ignored-directories ".ccls-cache") (add-to-list 'projectile-project-root-files-bottom-up ".ccls-root") - (add-to-list 'projectile-project-root-files-top-down-recurring "compile_commands.json"))) + (add-to-list 'projectile-project-root-files-top-down-recurring "compile_commands.json")) + :config + (when IS-MAC + (setq ccls-initialization-options + `(:clang ,(list :extraArgs ["-isystem/Library/Developer/CommandLineTools/usr/include/c++/v1" + "-isystem/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include" + "-isystem/usr/local/include"] + :resourceDir (string-trim (shell-command-to-string "clang -print-resource-dir"))))))) diff --git a/modules/lang/cc/packages.el b/modules/lang/cc/packages.el index ae5401fc5..dae001853 100644 --- a/modules/lang/cc/packages.el +++ b/modules/lang/cc/packages.el @@ -1,28 +1,32 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/cc/packages.el -(package! cmake-mode :recipe (:host github :repo "emacsmirror/cmake-mode" :files (:defaults "*"))) -(package! cuda-mode) -(package! demangle-mode) -(package! disaster) -(package! modern-cpp-font-lock) -(package! opencl-mode) +(package! cmake-mode + :recipe (:host github :repo "emacsmirror/cmake-mode" :files (:defaults "*")) + :pin "bfe85bc009") +(package! cuda-mode :pin "9ae9eacfdb") +(package! demangle-mode :pin "06903d731d") +(package! disaster :pin "10a785facc") +(package! modern-cpp-font-lock :pin "02f104701b") +(package! opencl-mode :pin "55cb49c824") -(when (package! glsl-mode) +(when (package! glsl-mode :pin "43d906688a") (when (featurep! :completion company) - (package! company-glsl :recipe (:host github :repo "Kaali/company-glsl")))) + (package! company-glsl + :recipe (:host github :repo "Kaali/company-glsl") + :pin "404cd0694a"))) (if (featurep! +lsp) - (package! ccls) - (when (package! irony) - (package! irony-eldoc) - (when (featurep! :tools flycheck) - (package! flycheck-irony)) + (package! ccls :pin "e5cc4c3e6f") + (when (package! irony :pin "8387098286") + (package! irony-eldoc :pin "0df5831eaa") + (when (featurep! :checkers syntax) + (package! flycheck-irony :pin "42dbecd4a8")) (when (featurep! :completion company) - (package! company-irony) - (package! company-irony-c-headers))) - (when (package! rtags) + (package! company-irony :pin "b44711dfce") + (package! company-irony-c-headers :pin "72c386aeb0"))) + (when (package! rtags :pin "31f7842015") (when (featurep! :completion ivy) - (package! ivy-rtags)) + (package! ivy-rtags :pin "31f7842015")) (when (featurep! :completion helm) - (package! helm-rtags)))) + (package! helm-rtags :pin "31f7842015")))) diff --git a/modules/lang/clojure/README.org b/modules/lang/clojure/README.org new file mode 100644 index 000000000..08d5f3227 --- /dev/null +++ b/modules/lang/clojure/README.org @@ -0,0 +1,62 @@ +#+TITLE: lang/clojure +#+DATE: May 30, 2017 +#+SINCE: v2.0 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +# A summary of what this module does. + ++ If possible, include a brief list of feature highlights here ++ Like code completion, syntax checking or available snippets ++ Include links to packages & external things where possible + +** Maintainers ++ @username_linked_to_gihub (Author) ++ @username_linked_to_gihub ++ @username_linked_to_gihub + +# If this module has no maintainers, then... +This module has no dedicated maintainers. + +** Module Flags ++ =+lsp= Enables LSP support. Disables cider. + +** Plugins ++ [[https://github.com/clojure-emacs/cider][cider]] (unless =+lsp=) ++ [[https://github.com/clojure-emacs/clj-refactor.el][clj-refactor]] ++ [[https://github.com/candid82/flycheck-joker][flycheck-joker]] + +** Hacks ++ Error messages emitted from CIDER are piped into the REPL buffer when it is + first opened, to make them easier to notice. ++ Disabled the help banner when opening the cider REPL. + +* Prerequisites +This module requires: + ++ clojure ++ With =+lsp= + + clojure-lsp ++ Without =+lsp= + + leiningen (REPL) + + joker (linter) + +* TODO Features +# An in-depth list of features, how to use them, and their dependencies. + +* TODO Configuration +# How to configure this module, including common problems and how to address them. + +* TODO Troubleshooting +# Common issues and their solution, or places to look for help. diff --git a/modules/lang/clojure/config.el b/modules/lang/clojure/config.el index ade5c0713..746199de3 100644 --- a/modules/lang/clojure/config.el +++ b/modules/lang/clojure/config.el @@ -3,17 +3,34 @@ (after! projectile (pushnew! projectile-project-root-files "project.clj" "build.boot" "deps.edn")) +;; Large clojure buffers tend to be slower than large buffers of other modes, so +;; it should have a lower threshold too. +(add-to-list 'doom-large-file-size-alist '("\\.\\(?:clj[sc]?\\|dtm\\|edn\\)\\'" . 0.5)) + ;; ;;; Packages ;;;###package clojure-mode (add-hook 'clojure-mode-hook #'rainbow-delimiters-mode) +(when (featurep! +lsp) + (add-hook! '(clojure-mode-local-vars-hook + clojurec-mode-local-vars-hook + clojurescript-mode-local-vars-hook) + (defun +clojure-disable-lsp-indentation-h () + (setq-local lsp-enable-indentation nil)) + #'lsp!) + (after! lsp-clojure + (dolist (m '(clojure-mode + clojurec-mode + clojurescript-mode + clojurex-mode)) + (add-to-list 'lsp-language-id-configuration (cons m "clojure"))))) (use-package! cider - ;; NOTE: if you don't have an org directory set (the dir doesn't exist), cider - ;; jack in won't work. + ;; NOTE if `org-directory' doesn't exist, `cider-jack' in won't work + :unless (featurep! +lsp) :hook (clojure-mode-local-vars . cider-mode) :init (after! clojure-mode @@ -22,7 +39,7 @@ (set-eval-handler! '(clojure-mode clojurescript-mode) #'cider-eval-region)) :config (add-hook 'cider-mode-hook #'eldoc-mode) - (set-lookup-handlers! 'cider-mode + (set-lookup-handlers! '(cider-mode cider-repl-mode) :definition #'+clojure-cider-lookup-definition :documentation #'cider-doc) (set-popup-rules! @@ -90,9 +107,10 @@ (:prefix ("h" . "help") "n" #'cider-find-ns "a" #'cider-apropos + "c" #'cider-clojuredocs "d" #'cider-doc - "g" #'cider-grimoire-web - "j" #'cider-javadoc) + "j" #'cider-javadoc + "w" #'cider-clojuredocs-web) (:prefix ("i" . "inspect") "e" #'cider-enlighten-mode "i" #'cider-inspect @@ -107,7 +125,7 @@ (:prefix ("r" . "repl") "n" #'cider-repl-set-ns "q" #'cider-quit - "r" #'cider-refresh + "r" #'cider-ns-refresh "R" #'cider-restart "b" #'cider-switch-to-repl-buffer "B" #'+clojure/cider-switch-to-repl-buffer-and-switch-ns @@ -152,6 +170,6 @@ :desc "refactor" "R" #'hydra-cljr-help-menu/body)) -(use-package! flycheck-joker - :when (featurep! :tools flycheck) +(use-package! flycheck-clj-kondo + :when (featurep! :checkers syntax) :after flycheck) diff --git a/modules/lang/clojure/packages.el b/modules/lang/clojure/packages.el index e289c1e81..e01ef2068 100644 --- a/modules/lang/clojure/packages.el +++ b/modules/lang/clojure/packages.el @@ -1,8 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/clojure/packages.el -(package! cider) -(package! clj-refactor) +(unless (featurep! +lsp) + (package! cider :pin "7437c67f0e")) -(when (featurep! :tools flycheck) - (package! flycheck-joker)) +(package! clj-refactor :pin "e24ba62843") + +(when (featurep! :checkers syntax) + (package! flycheck-clj-kondo :pin "f652a8dc4c")) diff --git a/modules/lang/common-lisp/config.el b/modules/lang/common-lisp/config.el index bfed909f7..a0b1a450c 100644 --- a/modules/lang/common-lisp/config.el +++ b/modules/lang/common-lisp/config.el @@ -8,19 +8,28 @@ ;; ;; packages +;;;###package lisp-mode (defvar inferior-lisp-program "sbcl") - -(after! lisp-mode - (set-repl-handler! 'lisp-mode #'sly-mrepl) - (set-eval-handler! 'lisp-mode #'sly-eval-region) - (set-lookup-handlers! 'lisp-mode - :definition #'sly-edit-definition - :documentation #'sly-describe-symbol) - - (add-hook 'lisp-mode-hook #'rainbow-delimiters-mode)) +(add-hook 'lisp-mode-hook #'rainbow-delimiters-mode) -(after! sly +(use-package! sly + :defer t + :init + (after! lisp-mode + (set-repl-handler! 'lisp-mode #'sly-mrepl) + (set-eval-handler! 'lisp-mode #'sly-eval-region) + (set-lookup-handlers! 'lisp-mode + :definition #'sly-edit-definition + :documentation #'sly-describe-symbol)) + + ;; HACK Ensures that sly's contrib modules are loaded as soon as possible, but + ;; also as late as possible, so users have an opportunity to override + ;; `sly-contrib' in an `after!' block. + (add-hook! 'doom-after-init-modules-hook + (after! sly (sly-setup))) + + :config (setq sly-mrepl-history-file-name (concat doom-cache-dir "sly-mrepl-history") sly-kill-without-query-p t sly-net-coding-system 'utf-8-unix @@ -32,6 +41,7 @@ '(("^\\*sly-mrepl" :vslot 2 :size 0.3 :quit nil :ttl nil) ("^\\*sly-compilation" :vslot 3 :ttl nil) ("^\\*sly-traces" :vslot 4 :ttl nil) + ("^\\*sly-description" :vslot 5 :size 0.3 :ttl 0) ;; Do not display debugger or inspector buffers in a popup window. These ;; buffers are meant to be displayed with sufficient vertical space. ("^\\*sly-\\(?:db\\|inspector\\)" :ignore t))) @@ -132,4 +142,4 @@ (use-package! sly-repl-ansi-color :defer t :init - (add-to-list 'sly-contribs 'sly-repl-ansi-color nil #'eq)) + (add-to-list 'sly-contribs 'sly-repl-ansi-color)) diff --git a/modules/lang/common-lisp/packages.el b/modules/lang/common-lisp/packages.el index cc0549ea0..d6447f4da 100644 --- a/modules/lang/common-lisp/packages.el +++ b/modules/lang/common-lisp/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/common-lisp/packages.el -(package! sly) -(package! sly-macrostep) -(package! sly-repl-ansi-color) +(package! sly :pin "cfecd21410") +(package! sly-macrostep :pin "5113e4e926") +(package! sly-repl-ansi-color :pin "b9cd52d1cf") diff --git a/modules/lang/coq/config.el b/modules/lang/coq/config.el index 86cd6ee0b..98b202787 100644 --- a/modules/lang/coq/config.el +++ b/modules/lang/coq/config.el @@ -1,22 +1,17 @@ ;;; lang/coq/config.el -*- lexical-binding: t; -*- ;;;###package proof-general -;; HACK `proof-general' ascertains its own library path at compile time in its -;; autoloads file using `byte-compile-current-file' (and stores it in -;; `pg-init--script-full-path'). This means that when -;; `doom-package-autoload-file' is created and byte-compiled, -;; `pg-init--script-full-path' will be wrong, causing file-missing errors as it -;; tries to load `proof-site'. We prevent this by defining these two variables -;; early, in our own autoloads file. -(setq pg-init--script-full-path (locate-library "proof-general") - pg-init--pg-root (file-name-directory pg-init--script-full-path) - proof-splash-enable nil) +(setq proof-splash-enable nil) ;;;###package coq -;; Doom syncs other indent variables with `tab-width'; we trust major modes to -;; set it -- which most of them do -- but coq-mode doesn't, so... -(setq-hook! 'coq-mode-hook tab-width proof-indent) +(setq-hook! 'coq-mode-hook + ;; Doom syncs other indent variables with `tab-width'; we trust major modes to + ;; set it -- which most of them do -- but coq-mode doesn't, so... + tab-width proof-indent + ;; HACK Fix #2081: Doom continues comments on RET, but coq-mode doesn't have a + ;; sane `comment-line-break-function', so... + comment-line-break-function nil) ;; We've replaced coq-mode abbrevs with yasnippet snippets (in the snippets ;; library included with Doom). diff --git a/modules/lang/coq/packages.el b/modules/lang/coq/packages.el index 78a5c61be..f51b087e9 100644 --- a/modules/lang/coq/packages.el +++ b/modules/lang/coq/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/coq/packages.el -(package! proof-general) +(package! proof-general :pin "2a17093f6a") -(package! company-coq) +(package! company-coq :pin "6e8bc2e367") diff --git a/modules/lang/crystal/config.el b/modules/lang/crystal/config.el index be955030f..07df73ccb 100644 --- a/modules/lang/crystal/config.el +++ b/modules/lang/crystal/config.el @@ -11,12 +11,12 @@ (use-package! flycheck-crystal - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after crystal-mode) (use-package! flycheck-ameba - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after crystal-mode :config (flycheck-ameba-setup)) diff --git a/modules/lang/crystal/packages.el b/modules/lang/crystal/packages.el index 0df4c86b9..434f80387 100644 --- a/modules/lang/crystal/packages.el +++ b/modules/lang/crystal/packages.el @@ -1,8 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/crystal/packages.el -(package! crystal-mode) -(package! inf-crystal) -(when (featurep! :tools flycheck) - (package! flycheck-crystal) - (package! flycheck-ameba)) +(package! crystal-mode :pin "2428b01624") +(package! inf-crystal :pin "02007b2a2a") +(when (featurep! :checkers syntax) + (package! flycheck-crystal :pin "2428b01624") + (package! flycheck-ameba :pin "0c4925ae0e")) diff --git a/modules/lang/csharp/packages.el b/modules/lang/csharp/packages.el index 07db938df..b05b5d4c4 100644 --- a/modules/lang/csharp/packages.el +++ b/modules/lang/csharp/packages.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/csharp/packages.el -(package! csharp-mode) +(package! csharp-mode :pin "57bd21bda4") (unless (featurep! +lsp) - (package! omnisharp)) + (package! omnisharp :pin "e658a18a76")) (when (featurep! +unity) - (package! shader-mode)) + (package! shader-mode :pin "d7dc8d0d6f")) diff --git a/modules/lang/data/packages.el b/modules/lang/data/packages.el index 9cb3322eb..e27bf57ea 100644 --- a/modules/lang/data/packages.el +++ b/modules/lang/data/packages.el @@ -1,11 +1,12 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/data/packages.el -(package! graphql-mode) -(package! json-mode) -(package! jsonnet-mode) -(package! yaml-mode) -(package! csv-mode) -(package! dhall-mode) +(package! graphql-mode :pin "7c37aee28b") +(package! json-mode :pin "0e819e519a") +(package! jsonnet-mode :pin "2b90b4e12a") +(package! yaml-mode :pin "cecf4b106b") +(package! csv-mode :pin "fbf942e127") +(package! dhall-mode :pin "ef4d33debe") (package! protobuf-mode - :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*"))) + :recipe (:host github :repo "emacsmirror/protobuf-mode" :files (:defaults "*")) + :pin "94b7bd7e8b") diff --git a/modules/lang/elixir/README.org b/modules/lang/elixir/README.org index 6756eacc6..1ae98cf91 100644 --- a/modules/lang/elixir/README.org +++ b/modules/lang/elixir/README.org @@ -22,7 +22,7 @@ or [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]]. + ~+lsp~ Enable LSP support. Requires [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]]. ** Plugins -+ [[https://github.com/rust-lang/rust-mode][elixir-mode]] ++ [[https://github.com/elixir-editors/emacs-elixir][elixir-mode]] + [[https://github.com/tonini/alchemist.el][alchemist.el]] + [[https://github.com/aaronjensen/flycheck-credo][flycheck-credo]] @@ -33,7 +33,7 @@ manager or a version management tool such as [[https://github.com/asdf-vm/asdf-e If you want to add support for LSP ([[modules/tools/lsp][:tools lsp]]), be sure to install [[https://github.com/JakeBecker/elixir-ls/][elixir-ls]] and enable ~:tools lsp~ in your ~init.el~. -To support linting with [[https://github.com/rrrene/credo][credo]], add ~:tools flycheck~ to your ~init.el~ +To support linting with [[https://github.com/rrrene/credo][credo]], add ~:checkers syntax~ to your ~init.el~ ** Install Elixir *** With ~asdf~ #+BEGIN_SRC sh @@ -59,4 +59,4 @@ sudo zypper install elixir - Mix integration - Phoenix support - ~iex~ integration (~:tools eval~) -- Syntax checking (~:tools flycheck~, using [[https://github.com/aaronjensen/flycheck-credo][flycheck-credo]]~) +- Syntax checking (~:checkers syntax~, using [[https://github.com/aaronjensen/flycheck-credo][flycheck-credo]]~) diff --git a/modules/lang/elixir/config.el b/modules/lang/elixir/config.el index ce19d9e1d..20915990d 100644 --- a/modules/lang/elixir/config.el +++ b/modules/lang/elixir/config.el @@ -40,7 +40,7 @@ (add-hook 'elixir-mode-local-vars-hook #'lsp!)) (use-package! flycheck-credo - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :config (flycheck-credo-setup))) diff --git a/modules/lang/elixir/packages.el b/modules/lang/elixir/packages.el index a50e54ce7..81d4f4f05 100644 --- a/modules/lang/elixir/packages.el +++ b/modules/lang/elixir/packages.el @@ -2,7 +2,7 @@ ;;; lang/elixir/packages.el ;; +elixir.el -(package! elixir-mode) -(package! alchemist) -(when (featurep! :tools flycheck) - (package! flycheck-credo)) +(package! elixir-mode :pin "231291ecad") +(package! alchemist :pin "6f99367511") +(when (featurep! :checkers syntax) + (package! flycheck-credo :pin "e88f11ead5")) diff --git a/modules/lang/elm/config.el b/modules/lang/elm/config.el index 479800eed..71c3dd97c 100644 --- a/modules/lang/elm/config.el +++ b/modules/lang/elm/config.el @@ -3,6 +3,9 @@ (after! elm-mode (add-hook 'elm-mode-hook #'rainbow-delimiters-mode) + (when (featurep! +lsp) + (add-hook 'elm-mode-local-vars-hook #'lsp!)) + (set-company-backend! 'elm-mode 'company-elm) (set-repl-handler! 'elm-mode #'run-elm-interactive) (set-pretty-symbols! 'elm-mode @@ -17,6 +20,6 @@ (use-package! flycheck-elm - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after elm-mode :config (add-to-list 'flycheck-checkers 'elm nil #'eq)) diff --git a/modules/lang/elm/packages.el b/modules/lang/elm/packages.el index 23bf4d45d..6485cbe2f 100644 --- a/modules/lang/elm/packages.el +++ b/modules/lang/elm/packages.el @@ -1,7 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/elm/packages.el -(package! elm-mode) -(when (featurep! :tools flycheck) - (package! flycheck-elm)) - +(package! elm-mode :pin "dd868e55ff") +(when (featurep! :checkers syntax) + (package! flycheck-elm :pin "debd0af563")) diff --git a/modules/lang/emacs-lisp/autoload.el b/modules/lang/emacs-lisp/autoload.el index 38505be87..228a3a8ef 100644 --- a/modules/lang/emacs-lisp/autoload.el +++ b/modules/lang/emacs-lisp/autoload.el @@ -8,19 +8,18 @@ "Evaluate a region and print it to the echo area (if one line long), otherwise to a pop up buffer." (+eval-display-results - (let* ((buffer-file-name (buffer-file-name (buffer-base-buffer)))) - (string-trim-right - (condition-case-unless-debug e - (let ((result - (let ((debug-on-error t)) - (eval (read (buffer-substring-no-properties beg end)) - `((buffer-file-name . ,buffer-file-name) - (doom--current-module - . ,(ignore-errors - (doom-module-from-path buffer-file-name)))))))) - (require 'pp) - (replace-regexp-in-string "\\\\n" "\n" (pp-to-string result))) - (error (error-message-string e))))) + (string-trim-right + (condition-case-unless-debug e + (let ((result + (let ((debug-on-error t)) + (eval (read (format "(progn %s)" (buffer-substring-no-properties beg end))) + `((buffer-file-name . ,(buffer-file-name (buffer-base-buffer))) + (doom--current-module + . ,(ignore-errors + (doom-module-from-path buffer-file-name)))))))) + (require 'pp) + (replace-regexp-in-string "\\\\n" "\n" (pp-to-string result))) + (error (error-message-string e)))) (current-buffer))) (defvar +emacs-lisp--face nil) @@ -67,13 +66,71 @@ library/userland functions" (with-no-warnings (byte-compile #'+emacs-lisp-highlight-vars-and-faces))) + +(defun +emacs-lisp--module-at-point () + (let ((origin (point))) + (save-excursion + (goto-char (point-min)) + (when (re-search-forward "(doom! " nil 'noerror) + (goto-char (match-beginning 0)) + (cl-destructuring-bind (beg . end) + (bounds-of-thing-at-point 'sexp) + (when (and (>= origin beg) + (<= origin end)) + (goto-char origin) + (while (not (sexp-at-point)) + (forward-symbol -1)) + (let (category module flag) + (cond ((keywordp (setq category (sexp-at-point))) + (while (keywordp (sexp-at-point)) + (forward-sexp 1)) + (setq module (car (doom-enlist (sexp-at-point))))) + ((and (symbolp (setq module (sexp-at-point))) + (string-prefix-p "+" (symbol-name module))) + (while (symbolp (sexp-at-point)) + (thing-at-point--beginning-of-sexp)) + (setq flag module + module (car (sexp-at-point))) + (when (re-search-backward "\\_<:\\w+\\_>" nil t) + (setq category (sexp-at-point)))) + ((symbolp module) + (when (re-search-backward "\\_<:\\w+\\_>" nil t) + (setq category (sexp-at-point))))) + (list category module flag)))))))) + +;;;###autoload +(defun +emacs-lisp-lookup-definition (_thing) + "Lookup definition of THING." + (if-let (module (+emacs-lisp--module-at-point)) + (doom/help-modules (car module) (cadr module) 'visit-dir) + (call-interactively #'elisp-def))) + ;;;###autoload (defun +emacs-lisp-lookup-documentation (thing) "Lookup THING with `helpful-variable' if it's a variable, `helpful-callable' if it's callable, `apropos' otherwise." - (if thing - (doom/describe-symbol thing) - (call-interactively #'doom/describe-symbol))) + (cond ((when-let (module (+emacs-lisp--module-at-point)) + (doom/help-modules (car module) (cadr module)) + (when (eq major-mode 'org-mode) + (with-demoted-errors "%s" + (re-search-forward + (if (caddr module) + "\\* Module Flags$" + "\\* Description$")) + (when (caddr module) + (re-search-forward (format "=\\%s=" (caddr module)) + nil t)) + (when (invisible-p (point)) + (org-show-hidden-entry)))) + 'deferred)) + (thing (helpful-symbol (intern thing))) + ((call-interactively #'helpful-at-point)))) + +;; FIXME +;; (defun +emacs-lisp-lookup-file (thing) +;; (when-let (module (+emacs-lisp--module-at-point thing)) +;; (doom/help-modules (car module) (cadr module) 'visit-dir) +;; t)) ;; @@ -129,7 +186,7 @@ if it's callable, `apropos' otherwise." ("Minor modes" "^\\s-*(define-\\(?:global\\(?:ized\\)?-minor\\|generic\\|minor\\)-mode +\\([^ ()\n]+\\)" 1) ("Modelines" "^\\s-*(def-modeline! +\\([^ ()\n]+\\)" 1) ("Modeline segments" "^\\s-*(def-modeline-segment! +\\([^ ()\n]+\\)" 1) - ("Advice" "^\\s-*(\\(?:def\\(?:\\(?:ine\\)?-advice\\)\\) +\\([^ )\n]+\\)" 1) + ("Advice" "^\\s-*(\\(?:def\\(?:\\(?:ine-\\)?advice!?\\)\\) +\\([^ )\n]+\\)" 1) ("Macros" "^\\s-*(\\(?:cl-\\)?def\\(?:ine-compile-macro\\|macro\\) +\\([^ )\n]+\\)" 1) ("Inline functions" "\\s-*(\\(?:cl-\\)?defsubst +\\([^ )\n]+\\)" 1) ("Functions" "^\\s-*(\\(?:cl-\\)?def\\(?:un\\|un\\*\\|method\\|generic\\|-memoized!\\) +\\([^ ,)\n]+\\)" 1) diff --git a/modules/lang/emacs-lisp/config.el b/modules/lang/emacs-lisp/config.el index 3d22d6c57..5aebdf93d 100644 --- a/modules/lang/emacs-lisp/config.el +++ b/modules/lang/emacs-lisp/config.el @@ -19,18 +19,19 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (use-package! elisp-mode :mode ("\\.Cask\\'" . emacs-lisp-mode) :config - (set-repl-handler! 'emacs-lisp-mode #'+emacs-lisp/open-repl) - (set-eval-handler! 'emacs-lisp-mode #'+emacs-lisp-eval) + (set-repl-handler! '(emacs-lisp-mode lisp-interaction-mode) #'+emacs-lisp/open-repl) + (set-eval-handler! '(emacs-lisp-mode lisp-interaction-mode) #'+emacs-lisp-eval) (set-lookup-handlers! 'emacs-lisp-mode - :definition #'elisp-def + :definition #'+emacs-lisp-lookup-definition :documentation #'+emacs-lisp-lookup-documentation) - (set-docsets! 'emacs-lisp-mode "Emacs Lisp") + (set-docsets! '(emacs-lisp-mode lisp-interaction-mode) "Emacs Lisp") (set-pretty-symbols! 'emacs-lisp-mode :lambda "lambda") (set-rotate-patterns! 'emacs-lisp-mode :symbols '(("t" "nil") ("let" "let*") ("when" "unless") ("advice-add" "advice-remove") + ("defadvice!" "undefadvice!") ("add-hook" "remove-hook") ("add-hook!" "remove-hook!") ("it" "xit") @@ -58,6 +59,13 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") ;; initialization #'+emacs-lisp-extend-imenu-h) + (autoload 'straight-register-file-modification "straight") + (add-hook! 'emacs-lisp-mode-hook + (defun +emacs-lisp-init-straight-h () + (when (file-in-directory-p (or buffer-file-name default-directory) doom-local-dir) + (add-hook 'after-save-hook #'straight-register-file-modification + nil 'local)))) + ;; Flycheck's two emacs-lisp checkers produce a *lot* of false positives in ;; emacs configs, so we disable `emacs-lisp-checkdoc' and reduce the ;; `emacs-lisp' checker's verbosity. @@ -121,7 +129,7 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (use-package! flycheck-cask - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :defer t :init (add-hook! 'emacs-lisp-mode-hook @@ -167,8 +175,8 @@ This marks a foldable marker for `outline-minor-mode' in elisp buffers.") (set-yas-minor-mode! 'buttercup-minor-mode) (when (featurep 'evil) (add-hook 'buttercup-minor-mode-hook #'evil-normalize-keymaps)) - (map! :map buttercup-minor-mode-map - :localleader + (map! :localleader + :map buttercup-minor-mode-map :prefix "t" "t" #'+emacs-lisp/buttercup-run-file "a" #'+emacs-lisp/buttercup-run-project diff --git a/modules/lang/emacs-lisp/packages.el b/modules/lang/emacs-lisp/packages.el index 5f7405ac1..97a112caa 100644 --- a/modules/lang/emacs-lisp/packages.el +++ b/modules/lang/emacs-lisp/packages.el @@ -3,13 +3,13 @@ (package! elisp-mode :built-in t) -(package! highlight-quoted) -(package! macrostep) -(package! overseer) -(package! elisp-def) -(package! elisp-demos) +(package! highlight-quoted :pin "2410347815") +(package! macrostep :pin "424e3734a1") +(package! overseer :pin "02d49f582e") +(package! elisp-def :pin "368b04da68") +(package! elisp-demos :pin "bec206bf1b") -(when (featurep! :tools flycheck) - (package! flycheck-cask)) +(when (featurep! :checkers syntax) + (package! flycheck-cask :pin "3457ae553c")) -(package! buttercup) +(package! buttercup :pin "178c7954f8") diff --git a/modules/lang/erlang/README.org b/modules/lang/erlang/README.org new file mode 100644 index 000000000..b5b540c97 --- /dev/null +++ b/modules/lang/erlang/README.org @@ -0,0 +1,39 @@ +#+TITLE: lang/erlang +#+DATE: January 14, 2020 +#+SINCE: {replace with next tagged release version} +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + +* Description + +This module provides support [[https://www.erlang.org/][Erlang programming language]]. Support for the +[[https://github.com/erlang/sourcer][sourcer]] language server is optional. + +** Maintainers +This module has no dedicated maintainers. + +** Module Flags ++ ~+lsp~ Enable LSP Support. Requires [[https://github.com/erlang/sourcer][sourcer]]. + + +** Plugins ++ Erlang Mode (bundled with Erlang installations) ++ [[https://github.com/joedevivo/flycheck-rebar3][flycheck-rebar3]] ++ [[https://github.com/s-kostyaev/ivy-erlang-complete][ivy-erlang-complete]] + +* Prerequisites +You should have Erlang installed. Check your distribution's package manager or a +version management tool such as [[https://github.com/kerl/kerl][kerl]]. + +If you want LSP support, install [[https://github.com/erlang/sourcer][sourcer]]. + +* Features +- Code completion (~+lsp~, ~:completion company~, and ~:completion ivy~) +- Syntax checking (~:checkers syntax~) diff --git a/modules/lang/erlang/config.el b/modules/lang/erlang/config.el index 6324785fc..2c0773b5d 100644 --- a/modules/lang/erlang/config.el +++ b/modules/lang/erlang/config.el @@ -7,7 +7,7 @@ (use-package! flycheck-rebar3 - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after flycheck :config (flycheck-rebar3-setup)) @@ -22,4 +22,8 @@ (use-package! company-erlang :when (featurep! :completion company) + :unless (featurep! +lsp) :hook (erlang-mode . company-erlang-init)) + +(when (featurep! +lsp) + (add-hook 'erlang-mode-local-vars-hook #'lsp!)) diff --git a/modules/lang/erlang/packages.el b/modules/lang/erlang/packages.el index 00a871c99..a047ad6e2 100644 --- a/modules/lang/erlang/packages.el +++ b/modules/lang/erlang/packages.el @@ -1,13 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; private/erlang/packages.el -(package! erlang) - -(when (featurep! :tools flycheck) - (package! flycheck-rebar3)) - -(when (featurep! :completion ivy) - (package! ivy-erlang-complete)) - -(when (featurep! :completion company) - (package! company-erlang)) +(package! erlang :pin "c15eb5fdf7") +(when (featurep! :checkers syntax) + (package! flycheck-rebar3 :pin "3cca1268c5")) +(unless (featurep! +lsp) + (when (featurep! :completion ivy) + (package! ivy-erlang-complete :pin "7d60ed111d")) + (when (featurep! :completion company) + (package! company-erlang :pin "bc0524a16f"))) diff --git a/modules/lang/ess/README.org b/modules/lang/ess/README.org index 55330d7ab..a50f434d7 100644 --- a/modules/lang/ess/README.org +++ b/modules/lang/ess/README.org @@ -4,9 +4,16 @@ This module adds support for various statistics languages, including R, S-Plus, SAS, Julia and Stata. * Table of Contents :TOC: -- [[Appendix][Appendix]] - - [[Keybindings][Keybindings]] +- [[#prequisites][Prequisites]] +- [[#appendix][Appendix]] + - [[#keybindings][Keybindings]] +* Prequisites +This module has several optional dependencies: + ++ [[https://github.com/jimhester/lintr][lintr]]: Enables R linting. ++ [[https://github.com/REditorSupport/languageserver][languageserver]]: Enables LSP support in an R buffer (with =+lsp= flag). + * Appendix ** Keybindings *** :map ess-doc-map diff --git a/modules/lang/ess/config.el b/modules/lang/ess/config.el index 6a9326adc..f68dd01da 100644 --- a/modules/lang/ess/config.el +++ b/modules/lang/ess/config.el @@ -10,17 +10,19 @@ (use-package! ess :commands stata SAS :init - (setq ess-smart-S-assign-key nil) (unless (featurep! :lang julia) (add-to-list 'auto-mode-alist '("\\.jl\\'" . ess-julia-mode))) :config (setq ess-offset-continued 'straight - ess-expression-offset 2 - ess-use-flymake (not (featurep! :tools flycheck)) + ess-use-flymake (not (featurep! :checkers syntax)) ess-nuke-trailing-whitespace-p t - ess-default-style 'DEFAULT + ess-style 'DEFAULT ess-history-directory (expand-file-name "ess-history/" doom-cache-dir)) + (set-docsets! 'ess-r-mode "R") + (when (featurep! +lsp) + (add-hook 'ess-r-mode-local-vars-hook #'lsp!)) + (set-repl-handler! 'ess-r-mode #'+ess/open-r-repl) (set-repl-handler! 'ess-julia-mode #'+ess/open-julia-repl) (set-lookup-handlers! '(ess-r-mode ess-julia-mode) @@ -30,26 +32,32 @@ (set-eval-handler! 'ess-help-mode #'ess-eval-region-and-go) (set-eval-handler! 'ess-r-help-mode #'ess-eval-region-and-go) - (map! (:after ess-help - :map ess-help-mode-map - :n "q" #'kill-current-buffer - :n "Q" #'ess-kill-buffer-and-go - :n "K" #'ess-display-help-on-object - :n "go" #'ess-display-help-in-browser - :n "gO" #'ess-display-help-apropos - :n "gv" #'ess-display-vignettes - :m "]]" #'ess-skip-to-next-section - :m "[[" #'ess-skip-to-previous-section - :map ess-doc-map - "h" #'ess-display-help-on-object - "p" #'ess-R-dv-pprint - "t" #'ess-R-dv-ctable - [C-return] #'ess-eval-line - [up] #'comint-next-input - [down] #'comint-previous-input) + (setq-hook! 'ess-r-mode-hook + ;; HACK Fix #2233: Doom continues comments on RET, but ess-r-mode doesn't + ;; have a sane `comment-line-break-function', so... + comment-line-break-function nil) + + (map! (:after ess-help + (:map ess-help-mode-map + :n "q" #'kill-current-buffer + :n "Q" #'ess-kill-buffer-and-go + :n "K" #'ess-display-help-on-object + :n "go" #'ess-display-help-in-browser + :n "gO" #'ess-display-help-apropos + :n "gv" #'ess-display-vignettes + :m "]]" #'ess-skip-to-next-section + :m "[[" #'ess-skip-to-previous-section) + (:map ess-doc-map + "h" #'ess-display-help-on-object + "p" #'ess-R-dv-pprint + "t" #'ess-R-dv-ctable + [up] #'comint-next-input + [down] #'comint-previous-input + [C-return] #'ess-eval-line)) - :localleader :map ess-mode-map + :n [C-return] #'ess-eval-line + :localleader "," #'ess-eval-region-or-function-or-paragraph-and-step "'" #'R [tab] #'ess-switch-to-inferior-or-script-buffer diff --git a/modules/lang/ess/packages.el b/modules/lang/ess/packages.el index dc9e8a9f8..bd2f024f0 100644 --- a/modules/lang/ess/packages.el +++ b/modules/lang/ess/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/ess/packages.el -(package! ess) -(package! ess-R-data-view) +(package! ess :pin "2812b85880") +(package! ess-R-data-view :pin "d6e98d3ae1") diff --git a/modules/lang/factor/README.org b/modules/lang/factor/README.org new file mode 100644 index 000000000..e697945dc --- /dev/null +++ b/modules/lang/factor/README.org @@ -0,0 +1,43 @@ +#+TITLE: lang/factor +#+DATE: December 3, 2019 +#+SINCE: v3.0.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module adds support to the [[https://github.com/factor/factor][factor]] programming language and its associated +_fuel_ emacs plugin. + ++ If possible, include a brief list of feature highlights here ++ Like code completion, syntax checking or available snippets ++ Include links to packages & external things where possible + +** Module Flags +This module provides no flags. + +** Plugins +{A list of linked plugins} + +** Hacks +{A list of internal modifications to included packages} + +* Prerequisites +You must install [[https://github.com/factor/factor][factor]] to use the advanced functionality of this module. + +* Configuration +This module requires the installation of factor to be available at +=fuel-factor-root-dir=. Here's an example of how to set it: +#+BEGIN_SRC emacs-lisp +(setq fuel-factor-root-dir "/Applications/factor") +#+END_SRC + +* Troubleshooting +Common issues and their solution, or places to look for help. diff --git a/modules/lang/factor/config.el b/modules/lang/factor/config.el new file mode 100644 index 000000000..bf8d725ea --- /dev/null +++ b/modules/lang/factor/config.el @@ -0,0 +1,48 @@ +;;; lang/factor/config.el -*- lexical-binding: t; -*- + +(use-package! fuel-mode + :defer t + :init + (after! factor-mode + (set-eval-handler! 'factor-mode #'fuel-eval-region) + (set-repl-handler! 'factor-mode #'run-factor)) + :config + (set-lookup-handlers! 'factor-mode + :definition #'fuel-edit-word-at-point + :references #'fuel-show-callers + :documentation #'fuel-help)) + +(map! :after factor-mode + :map factor-mode-map + :localleader + "t" #'fuel-test-vocab + (:prefix ("e" . "eval") + "d" #'fuel-eval-definition + "R" #'fuel-eval-extended-region + "r" #'fuel-eval-region) + (:prefix ("h" . "help") + "p" #'fuel-apropos + "v" #'fuel-show-file-words + "c" #'fuel-show-callees + "e" #'fuel-stack-effect-region) + (:prefix ("s" . "scaffold") + "v" #'fuel-scaffold-vocab + "h" #'fuel-scaffold-help + "t" #'fuel-scaffold-tests) + (:prefix ("r" . "refactor") + "s" #'fuel-refactor-extract-sexp + "w" #'fuel-refactor-extract-region + "v" #'fuel-refactor-extract-vocab + "i" #'fuel-refactor-inline-word + "g" #'fuel-refactor-make-generic + "u" #'fuel-update-usings + "r" #'fuel-refactor-rename-word)) + +(map! :after fuel-listener + :map fuel-listener-mode-map + :localleader + "e" #'fuel-edit-vocabulary + "r" #'fuel-refresh-all + "i" #'fuel-stack-mode + "h" #'fuel-help + "s" #'fuel-scaffold-vocab) diff --git a/modules/lang/factor/packages.el b/modules/lang/factor/packages.el new file mode 100644 index 000000000..0d7344b40 --- /dev/null +++ b/modules/lang/factor/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; lang/factor/packages.el + +(package! fuel :pin "a62ea78d73") diff --git a/modules/lang/faust/packages.el b/modules/lang/faust/packages.el index 30734e5e4..10572a3ac 100644 --- a/modules/lang/faust/packages.el +++ b/modules/lang/faust/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/faust/packages.el -(package! faustine) +(package! faustine :pin "07a3896311") diff --git a/modules/lang/fsharp/packages.el b/modules/lang/fsharp/packages.el index 1a303123f..e57e0b023 100644 --- a/modules/lang/fsharp/packages.el +++ b/modules/lang/fsharp/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/fsharp/packages.el -(package! fsharp-mode) +(package! fsharp-mode :pin "8c86e38b93") diff --git a/modules/lang/fstar/README.org b/modules/lang/fstar/README.org new file mode 100644 index 000000000..30b025eaa --- /dev/null +++ b/modules/lang/fstar/README.org @@ -0,0 +1,56 @@ +#+TITLE: lang/fstar +#+DATE: February 2, 2020 +#+SINCE: 2.0.10 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] + - [[#f][F*]] + - [[#macos][macOS]] + - [[#arch-linux][Arch Linux]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module adds [[https://fstar-lang.org/][F*]] support, powered by [[https://github.com/FStarLang/fstar-mode.el][fstar-mode.el]]. + ++ Syntax highlighting ++ Interactively process F* files one definition at a time ++ Query the running F* process to look up definitions, documentation, and + theorems + +** Maintainers ++ [[https://github.com/tchajed][@tchajed]] (Author) + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/FStarLang/fstar-mode.el][fstar-mode]] + +* Prerequisites +** F* +While fstar-mode supports the latest release of F*, you may have a better +experience installing a more recent version from source. See F*'s [[https://github.com/FStarLang/FStar/blob/master/INSTALL.md][INSTALL.md]]. +*** macOS +#+BEGIN_SRC sh +brew install fstar +#+END_SRC +*** Arch Linux +Install ~fstar~ from the AUR: +#+BEGIN_SRC sh +yaourt -S fstar +#+END_SRC + +* Features + +* Configuration + +* Troubleshooting +If you're having trouble getting F* to start correctly, you may need to +configure a few variables in fstar-mode; see its [[https://github.com/FStarLang/fstar-mode.el][README]] for more details. diff --git a/modules/lang/fstar/config.el b/modules/lang/fstar/config.el new file mode 100644 index 000000000..354d3d7b9 --- /dev/null +++ b/modules/lang/fstar/config.el @@ -0,0 +1,55 @@ +;;; lang/fstar/config.el -*- lexical-binding: t; -*- + +(after! fstar-mode + (set-lookup-handlers! 'fstar-mode + :definition #'fstar-jump-to-definition + :documentation #'fstar-doc-at-point-dwim) + + (map! :map fstar-mode-map + :localleader + :desc "F* next" "]" #'fstar-subp-advance-next + :desc "F* go to point" "." #'fstar-subp-advance-or-retract-to-point + :desc "F* previous" "[" #'fstar-subp-retract-last + (:prefix ("p" . "proof") + :desc "go to point (lax)" "l" #'fstar-subp-advance-or-retract-to-point-lax + :desc "compile buffer (lax)" "b" #'fstar-subp-advance-to-point-max-lax + "q" #'fstar-subp-kill-one-or-many + "k" #'fstar-subp-kill-z3 + "r" #'fstar-subp-reload-to-point) + + (:prefix ("l" . "layout") + "c" #'fstar-quit-windows + "o" #'fstar-outline) + + ;; Moving around + "'" #'fstar-jump-to-related-error + (:prefix ("j" . "jump") + "j" #'fstar-jump-to-definition + "f" #'fstar-jump-to-definition-other-frame + "w" #'fstar-jump-to-definition-other-window + "e" #'fstar-jump-to-related-error + "F" #'fstar-jump-to-related-error-other-frame + "W" #'fstar-jump-to-related-error-other-window + "d" #'fstar-visit-dependency + "a" #'fstar-visit-interface-or-implementation + :desc "jump to first unprocessed line" "u" #'fstar-subp-goto-beginning-of-unprocessed) + + ;; Help !!! + (:prefix ("h" . "help") + "y" #'fstar-copy-help-at-point + "w" #'fstar-browse-wiki + "W" #'fstar-browse-wiki-in-browser + "o" #'fstar-list-options + "p" #'fstar-quick-peek) + + (:prefix ("a" . "ask (queries)") + "a" #'fstar-print + "e" #'fstar-eval + "E" #'fstar-eval-custom + "s" #'fstar-search + "d" #'fstar-doc) + + (:prefix ("i" . "insert") + "m" #'fstar-insert-match-dwim + "M" #'fstar-insert-match) + )) diff --git a/modules/lang/fstar/packages.el b/modules/lang/fstar/packages.el new file mode 100644 index 000000000..80c8aa533 --- /dev/null +++ b/modules/lang/fstar/packages.el @@ -0,0 +1,4 @@ +;; -*- no-byte-compile: t; -*- +;;; lang/fstar/packages.el + +(package! fstar-mode) diff --git a/modules/lang/go/README.org b/modules/lang/go/README.org index c1a2f91e7..04ea402fa 100644 --- a/modules/lang/go/README.org +++ b/modules/lang/go/README.org @@ -27,6 +27,7 @@ This module adds [[https://golang.org][Go]] support. + [[../../editor/file-templates/templates/go-mode][File templates]] + [[https://github.com/hlissner/doom-snippets/tree/master/go-mode][Snippets]] + Generate testing code (~go-gen-test~) ++ Code checking (~flycheck-golangci-lint~) ** Module Flags + =+lsp= Enables integration for the gopls LSP server. @@ -39,6 +40,7 @@ This module adds [[https://golang.org][Go]] support. + [[https://github.com/brantou/emacs-go-tag][go-tag]] + [[https://github.com/mdempsky/gocode][company-go]]* + [[https://github.com/s-kostyaev/go-gen-test][go-gen-test]] ++ [[https://github.com/weijiangan/flycheck-golangci-lint][flycheck-golangci-lint]] (if =:checkers syntax= is enabled) * Prerequisites ** Go @@ -75,7 +77,7 @@ This module requires a valid ~GOPATH~, and the following Go packages: export GOPATH=~/work/go go get -u github.com/motemen/gore/cmd/gore -go get -u github.com/mdempsky/gocode +go get -u github.com/stamblerre/gocode go get -u golang.org/x/tools/cmd/godoc go get -u golang.org/x/tools/cmd/goimports go get -u golang.org/x/tools/cmd/gorename @@ -84,6 +86,10 @@ go get -u github.com/cweill/gotests/... go get -u github.com/fatih/gomodifytags #+END_SRC ++ ~golangci-lint~ (optional: for flycheck to integrate golangci-lint results) + it is recommended to *not* use go get to install this one, check the + [[https://github.com/golangci/golangci-lint#binary-release][documentation]]. + * TODO Features * TODO Configuration diff --git a/modules/lang/go/autoload.el b/modules/lang/go/autoload.el index 709bd2a2e..4e13dcab4 100644 --- a/modules/lang/go/autoload.el +++ b/modules/lang/go/autoload.el @@ -41,6 +41,21 @@ (+go--run-tests (concat "-run" "='" (match-string-no-properties 2) "'"))) (error "Must be in a _test.go file"))) +;;;###autoload +(defun +go/bench-all () + (interactive) + (+go--run-tests "-test.run=NONE -test.bench=\".*\"")) + +;;;###autoload +(defun +go/bench-single () + (interactive) + (if (string-match "_test\\.go" buffer-file-name) + (save-excursion + (re-search-backward "^func[ ]+\\(([[:alnum:]]*?[ ]?[*]?[[:alnum:]]+)[ ]+\\)?\\(Benchmark[[:alnum:]_]+\\)(.*)") + (+go--run-tests (concat "-test.run=NONE -test.bench" "='" (match-string-no-properties 2) "'"))) + (error "Must be in a _test.go file"))) + + ;;;###autoload (defun +go/play-buffer-or-region (&optional beg end) "TODO" diff --git a/modules/lang/go/config.el b/modules/lang/go/config.el index 2f656f590..948675616 100644 --- a/modules/lang/go/config.el +++ b/modules/lang/go/config.el @@ -55,7 +55,10 @@ "n" #'+go/test-nested "g" #'go-gen-test-dwim "G" #'go-gen-test-all - "e" #'go-gen-test-exported))) + "e" #'go-gen-test-exported + (:prefix ("b" . "bench") + "s" #'+go/bench-single + "a" #'+go/bench-all)))) (use-package! gorepl-mode @@ -69,3 +72,7 @@ :config (set-company-backend! 'go-mode 'company-go) (setq company-go-show-annotation t)) + +(use-package! flycheck-golangci-lint + :when (featurep! :checkers syntax) + :hook (go-mode . flycheck-golangci-lint-setup)) diff --git a/modules/lang/go/packages.el b/modules/lang/go/packages.el index ab3c2b8c1..ad5a067c0 100644 --- a/modules/lang/go/packages.el +++ b/modules/lang/go/packages.el @@ -1,12 +1,15 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/go/packages.el -(package! go-eldoc) -(package! go-guru) -(package! go-mode) -(package! gorepl-mode) -(package! go-tag) -(package! go-gen-test) +(package! go-eldoc :pin "cbbd2ea1e9") +(package! go-guru :pin "53c76cddf5") +(package! go-mode :pin "53c76cddf5") +(package! gorepl-mode :pin "6a73bf352e") +(package! go-tag :pin "59b243f2fa") +(package! go-gen-test :pin "44c202ac97") (when (featurep! :completion company) - (package! company-go)) + (package! company-go :pin "939b4a677f")) + +(when (featurep! :checkers syntax) + (package! flycheck-golangci-lint :pin "8e446c6831")) diff --git a/modules/lang/haskell/+dante.el b/modules/lang/haskell/+dante.el index 801303b9b..698c2e1d7 100644 --- a/modules/lang/haskell/+dante.el +++ b/modules/lang/haskell/+dante.el @@ -8,13 +8,13 @@ "+c" "-Wwarn=missing-home-modules" "-fno-diagnostics-show-caret" - ;; neccessary to make attrap-attrap useful: + ;; necessary to make attrap-attrap useful: "-Wall" ;; necessary to make company completion useful: "-fdefer-typed-holes" "-fdefer-type-errors")) :config - (when (featurep! :tools flycheck) + (when (featurep! :checkers syntax) (flycheck-add-next-checker 'haskell-dante '(warning . haskell-hlint))) (set-company-backend! 'dante-mode #'dante-company) diff --git a/modules/lang/haskell/+intero.el b/modules/lang/haskell/+intero.el index 309a92aac..2986dd2bd 100644 --- a/modules/lang/haskell/+intero.el +++ b/modules/lang/haskell/+intero.el @@ -16,7 +16,7 @@ This is necessary because `intero-mode' doesn't do its own error checks." (setq haskell-compile-cabal-build-command "stack build --fast") (set-lookup-handlers! 'intero-mode :definition #'intero-goto-definition) (set-company-backend! 'intero-mode 'intero-company) - (when (featurep! :tools flycheck) + (when (featurep! :checkers syntax) (flycheck-add-next-checker 'intero '(warning . haskell-hlint))) (when (featurep 'evil) diff --git a/modules/lang/haskell/README.org b/modules/lang/haskell/README.org index 9f160ca88..bb4762289 100644 --- a/modules/lang/haskell/README.org +++ b/modules/lang/haskell/README.org @@ -9,17 +9,17 @@ - [[#module-flags][Module Flags]] - [[#plugins][Plugins]] - [[#prerequisites][Prerequisites]] - - [[#stack][Stack]] - [[#cabal][Cabal]] - [[#lsp][LSP]] + - [[#stack][Stack]] - [[#haskell-packages][Haskell packages]] - [[#configuration][Configuration]] - [[#using-the-new-style-cabal-repl][Using the new-style cabal REPL]] - [[#troubleshooting][Troubleshooting]] * Description -This module adds [[https://www.haskell.org/][Haskell]] support, powered by either [[https://haskell-lang.org/intero][intero]] (the default), [[https://github.com/jyp/dante][dante]] -or [[https://github.com/emacs-lsp/lsp-haskell][LSP]]. +This module adds [[https://www.haskell.org/][Haskell]] support, powered by either [[https://github.com/jyp/dante][dante]] (the default), LSP or +[[https://haskell-lang.org/intero][intero]]. + Code completion (~company-ghc~) + Look up documentation (~hoogle~) @@ -30,7 +30,7 @@ or [[https://github.com/emacs-lsp/lsp-haskell][LSP]]. + [[https://github.com/hlissner/doom-snippets/tree/master/haskell-mode][Snippets]] ** External resources -Here are a few resources I've found indespensible in my Haskell adventures: +Here are a few resources I've found indispensable in my Haskell adventures: + [[http://learnyouahaskell.com/][Learn you a haskell for great good]] + [[http://haskellbook.com/][Haskell Programming from first principles]] @@ -38,54 +38,32 @@ Here are a few resources I've found indespensible in my Haskell adventures: + [[https://docs.haskellstack.org/en/stable/README/][The Haskell Tool Stack docs]] ** Module Flags -+ =+intero= Enables intero; a comprehensive, stack-based development environment - for Haskell. + =+dante= Enables dante; a fork of intero aimed at lightweightedness. It doesn't depend on =stack=, supports both ~cabal~-only and ~stack~ projects, but lacks eldoc support. + =+lsp= Enables lsp-haskell (this requires the ~:tools lsp~ to be enabled). ++ =+intero= (Deprecated) Enables intero; a comprehensive, stack-based + development environment for Haskell. ** Plugins + [[https://github.com/haskell/haskell-mode][haskell-mode]] + =+dante= + [[https://github.com/jyp/dante][dante]] + [[https://github.com/jyp/attrap][attrap]] -+ =+intero= - + [[https://github.com/chrisdone/intero][intero]] + =+lsp= + [[https://github.com/emacs-lsp/lsp-haskell][lsp-haskell]] ++ =+intero= + + [[https://github.com/chrisdone/intero][intero]] * Prerequisites Depending on whether you use Intero, Dante or LSP, your dependencies will differ: -+ Intero and LSP users need =stack= + Dante users need =cabal=, =ghc= and =ghc-mod= + LSP users need the =haskell-ide-engine= LSP server ++ Intero and LSP users need =stack= + All users will need the =hoogle= package -** Stack -To use Intero, you need =stack=: - -*** MacOS -#+BEGIN_SRC sh -brew install haskell-stack -stack setup -#+END_SRC -*** Arch Linux -#+BEGIN_SRC sh -sudo pacman -S stack -# Replace pacaur with your AUR package manager of choice -pacaur -S ncurses5-compat-lib -stack setup -#+END_SRC - -*** openSUSE -#+BEGIN_SRC sh :dir /sudo:: -sudo zypper install stack -stack setup -#+END_SRC - ** Cabal To use Dante, you need =cabal= (the haskell package builder) and =ghci= (the compiler, syntax checker & repl): @@ -127,6 +105,28 @@ make yay -S haskell-ide-engine-git #+END_SRC +** Stack +To use Intero or LSP, you need =stack=: + +*** MacOS +#+BEGIN_SRC sh +brew install haskell-stack +stack setup +#+END_SRC +*** Arch Linux +#+BEGIN_SRC sh +sudo pacman -S stack +# Replace pacaur with your AUR package manager of choice +pacaur -S ncurses5-compat-lib +stack setup +#+END_SRC + +*** openSUSE +#+BEGIN_SRC sh :dir /sudo:: +sudo zypper install stack +stack setup +#+END_SRC + ** Haskell packages You'll need to install the following packages using ~stack~ or ~cabal~: diff --git a/modules/lang/haskell/autoload.el b/modules/lang/haskell/autoload.el index cfcd12be0..370e5ed91 100644 --- a/modules/lang/haskell/autoload.el +++ b/modules/lang/haskell/autoload.el @@ -12,3 +12,20 @@ (haskell-session-interactive-buffer (haskell-session)))))) (window-buffer window) (error "Failed to display Haskell REPL"))) + +;;;###autoload +(defun +haskell/evil-open-above () + "Opens a line above the current mode" + (interactive) + (evil-digit-argument-or-evil-beginning-of-line) + (haskell-indentation-newline-and-indent) + (evil-previous-line) + (haskell-indentation-indent-line) + (evil-append-line nil)) + +;;;###autoload +(defun +haskell/evil-open-below () + "Opens a line below the current mode" + (interactive) + (evil-append-line nil) + (haskell-indentation-newline-and-indent)) diff --git a/modules/lang/haskell/config.el b/modules/lang/haskell/config.el index 07898eaf1..a961d8cc0 100644 --- a/modules/lang/haskell/config.el +++ b/modules/lang/haskell/config.el @@ -3,7 +3,8 @@ (after! projectile (add-to-list 'projectile-project-root-files "stack.yaml")) -(cond ((featurep! +intero) (load! "+intero")) +;; TODO ghcide? +(cond ((featurep! +intero) (load! "+intero")) ; DEPRECATED ((featurep! +dante) (load! "+dante")) ((featurep! +lsp) (load! "+lsp"))) @@ -14,7 +15,7 @@ (after! haskell-mode (setq haskell-process-suggest-remove-import-lines t ; warnings for redundant imports etc haskell-process-auto-import-loaded-modules t - haskell-process-show-overlays (not (featurep! :tools flycheck))) ; redundant with flycheck + haskell-process-show-overlays (not (featurep! :checkers syntax))) ; redundant with flycheck (set-lookup-handlers! 'haskell-mode :definition #'haskell-mode-jump-to-def-or-tag) @@ -24,6 +25,8 @@ (set-repl-handler! '(haskell-mode haskell-cabal-mode literate-haskell-mode) #'+haskell/open-repl :persist t) + ;; Don't kill REPL popup on ESC/C-g + (set-popup-rule! "^\\*haskell\\*" :quit nil) (add-hook! 'haskell-mode-hook #'haskell-collapse-mode ; support folding haskell code blocks @@ -31,6 +34,10 @@ (add-to-list 'completion-ignored-extensions ".hi") + (map! :map haskell-mode-map + :n "o" #'+haskell/evil-open-below + :n "O" #'+haskell/evil-open-above) + (map! :localleader :map haskell-mode-map ;; this is set to use cabal for dante users and stack for intero users: diff --git a/modules/lang/haskell/doctor.el b/modules/lang/haskell/doctor.el index 9dcb7fc7d..30e023631 100644 --- a/modules/lang/haskell/doctor.el +++ b/modules/lang/haskell/doctor.el @@ -16,5 +16,3 @@ (when (or (featurep! +dante) (featurep! +intero)) (unless (executable-find "hlint") (warn! "Couldn't find hlint. Flycheck may have issues in haskell-mode"))) - - diff --git a/modules/lang/haskell/packages.el b/modules/lang/haskell/packages.el index b2fd48e5e..24d936e95 100644 --- a/modules/lang/haskell/packages.el +++ b/modules/lang/haskell/packages.el @@ -1,12 +1,13 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/haskell/packages.el -(package! haskell-mode) +(package! haskell-mode :pin "4a87d72589") -(cond ((featurep! +dante) - (package! dante) - (package! attrap)) - ((featurep! +intero) - (package! intero)) - ((featurep! +lsp) - (package! lsp-haskell))) +(when (featurep! +dante) + (package! dante :pin "4955bc7363") + (package! attrap :pin "4cf3e4a162")) +(when (featurep! +lsp) + (package! lsp-haskell :pin "6d481f97e6")) +;; DEPRECATED +(when (featurep! +intero) + (package! intero :pin "fdb0550a2d")) diff --git a/modules/lang/hy/packages.el b/modules/lang/hy/packages.el index 0b232824d..5fd2ae15b 100644 --- a/modules/lang/hy/packages.el +++ b/modules/lang/hy/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/hy/packages.el -(package! hy-mode) +(package! hy-mode :pin "e2d5fecdae") diff --git a/modules/lang/idris/config.el b/modules/lang/idris/config.el index 249fd2d08..408a3b0fe 100644 --- a/modules/lang/idris/config.el +++ b/modules/lang/idris/config.el @@ -4,8 +4,7 @@ (add-hook 'idris-mode-hook #'turn-on-idris-simple-indent) (set-repl-handler! 'idris-mode 'idris-pop-to-repl) (set-lookup-handlers! 'idris-mode - :documentation #'idris-docs-at-point - :file #'idris-load-file) + :documentation #'idris-docs-at-point) (map! :localleader :map idris-mode-map "r" #'idris-load-file diff --git a/modules/lang/idris/packages.el b/modules/lang/idris/packages.el index d600f1309..1e1a3438b 100644 --- a/modules/lang/idris/packages.el +++ b/modules/lang/idris/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/idris/packages.el -(package! idris-mode) +(package! idris-mode :pin "acc8835449") diff --git a/modules/lang/java/+lsp.el b/modules/lang/java/+lsp.el index dc917a6c7..7e9bf68ce 100644 --- a/modules/lang/java/+lsp.el +++ b/modules/lang/java/+lsp.el @@ -3,9 +3,11 @@ (use-package! lsp-java :after-call java-mode - :init (add-hook 'java-mode-local-vars-hook #'lsp!) + :init + (add-hook 'java-mode-local-vars-hook #'lsp!) + (setq lsp-java-server-install-dir (concat doom-etc-dir "eclipse.jdt.ls/server/") + lsp-java-workspace-dir (concat doom-etc-dir "java-workspace")) :config - (setq lsp-java-server-install-dir (concat doom-etc-dir "eclipse.jdt.ls/server/")) ;; TODO keybinds ;; TODO treemacs integration (?) ) diff --git a/modules/lang/java/+meghanada.el b/modules/lang/java/+meghanada.el index 4c33f652e..f45007cff 100644 --- a/modules/lang/java/+meghanada.el +++ b/modules/lang/java/+meghanada.el @@ -6,7 +6,7 @@ :init (setq meghanada-server-install-dir (concat doom-etc-dir "meghanada-server/") meghanada-use-company (featurep! :completion company) - meghanada-use-flycheck (featurep! :tools flycheck) + meghanada-use-flycheck (featurep! :checkers syntax) meghanada-use-eldoc t meghanada-use-auto-start t) :config diff --git a/modules/lang/java/config.el b/modules/lang/java/config.el index e4818df76..4b04f3dff 100644 --- a/modules/lang/java/config.el +++ b/modules/lang/java/config.el @@ -46,4 +46,3 @@ If the depth is 2, the first two directories are removed: net.lissner.game.") :mode "\\.g\\(?:radle\\|roovy\\)$" :config (set-eval-handler! 'groovy-mode "groovy")) - diff --git a/modules/lang/java/packages.el b/modules/lang/java/packages.el index c9a0014a2..d98ce9545 100644 --- a/modules/lang/java/packages.el +++ b/modules/lang/java/packages.el @@ -1,16 +1,16 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/java/packages.el -(package! android-mode) -(package! groovy-mode) +(package! android-mode :pin "d5332e339a") +(package! groovy-mode :pin "cafdd98e06") (when (featurep! +meghanada) - (package! meghanada)) + (package! meghanada :pin "70bfbf553c")) (when (featurep! +eclim) - (package! eclim) + (package! eclim :pin "23f5b294f8") (when (featurep! :completion company) - (package! company-emacs-eclim))) + (package! company-emacs-eclim :pin "23f5b294f8"))) (when (featurep! +lsp) - (package! lsp-java)) + (package! lsp-java :pin "dbeeee9c74")) diff --git a/modules/lang/javascript/README.org b/modules/lang/javascript/README.org index cb1add7ab..6a43af3e8 100644 --- a/modules/lang/javascript/README.org +++ b/modules/lang/javascript/README.org @@ -15,7 +15,7 @@ - [[#commands][Commands]] * Description -This module adds Javascript support. +This module adds JavaScript and TypeScript support. + Code completion (tide) + REPL support (nodejs-repl) diff --git a/modules/lang/javascript/config.el b/modules/lang/javascript/config.el index 45168fddf..0fdff1030 100644 --- a/modules/lang/javascript/config.el +++ b/modules/lang/javascript/config.el @@ -79,7 +79,7 @@ (add-to-list 'magic-mode-alist '(+javascript-jsx-file-p . rjsx-mode)) :config (set-electric! 'rjsx-mode :chars '(?\} ?\) ?. ?>)) - (when (featurep! :tools flycheck) + (when (featurep! :checkers syntax) (add-hook! 'rjsx-mode-hook ;; jshint doesn't know how to deal with jsx (push 'javascript-jshint flycheck-disabled-checkers))) @@ -94,7 +94,16 @@ (if (= n 1) (rjsx-maybe-reparse)))) -(after! typescript-mode +(use-package! typescript-mode + :defer t + :init + ;; REVIEW Fix #2252. This is overwritten if the :lang web module is enabled. + ;; We associate TSX files with `web-mode' by default instead because + ;; `typescript-mode' does not officially support JSX/TSX. See + ;; https://github.com/emacs-typescript/typescript.el/issues/4 + (unless (featurep! :lang web) + (add-to-list 'auto-mode-alist '("\\.tsx\\'" . typescript-mode))) + :config (add-hook 'typescript-mode-hook #'rainbow-delimiters-mode) (setq-hook! 'typescript-mode-hook comment-line-break-function #'js2-line-break) @@ -188,7 +197,7 @@ to tide." :map tide-mode-map "R" #'tide-restart-server "f" #'tide-format - "rrs" #'tide-rename-symbol + "rrs" #'tide-rename-symbol "roi" #'tide-organize-imports)) @@ -202,11 +211,9 @@ to tide." (use-package! js2-refactor :hook ((js2-mode rjsx-mode) . js2-refactor-mode) - :config - (when (featurep! :editor evil +everywhere) - (let ((js2-refactor-mode-map (evil-get-auxiliary-keymap js2-refactor-mode-map 'normal t t))) - (js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key)))) - (map! :map js2-mode-map + :init + (map! :after js2-mode + :map js2-mode-map :localleader (:prefix ("r" . "refactor") (:prefix ("a" . "add/arguments")) @@ -223,7 +230,12 @@ to tide." (:prefix ("u" . "unwrap")) (:prefix ("v" . "var")) (:prefix ("w" . "wrap")) - (:prefix ("3" . "ternary"))))) + (:prefix ("3" . "ternary")))) + :config + (when (featurep! :editor evil +everywhere) + (add-hook 'js2-refactor-mode-hook #'evil-normalize-keymaps) + (let ((js2-refactor-mode-map (evil-get-auxiliary-keymap js2-refactor-mode-map 'normal t t))) + (js2r-add-keybindings-with-prefix (format "%s r" doom-localleader-key))))) (use-package! eslintd-fix @@ -235,6 +247,9 @@ to tide." ;;;###package skewer-mode (map! :localleader + (:after js2-mode + :map js2-mode-map + :prefix ("s" . "skewer")) :prefix "s" (:after skewer-mode :map skewer-mode-map @@ -252,28 +267,32 @@ to tide." (:after skewer-html :map skewer-html-mode-map "e" #'skewer-html-eval-tag)) -(map! :map js2-mode-map - :localleader - (:prefix ("s" . "skewer"))) ;;;###package npm-mode (use-package! npm-mode :hook ((js-mode typescript-mode) . npm-mode) :config - (map! :localleader - :map npm-mode-keymap - "n" npm-mode-command-keymap) - (map! :map js2-mode-map - :localleader - (:prefix ("n" . "npm")))) + (map! (:localleader + :map npm-mode-keymap + "n" npm-mode-command-keymap) + (:after js2-mode + :map js2-mode-map + :localleader + (:prefix ("n" . "npm"))))) ;; ;;; Projects (def-project-mode! +javascript-npm-mode - :modes '(html-mode css-mode web-mode markdown-mode js-mode typescript-mode) + :modes '(html-mode + css-mode + web-mode + markdown-mode + js-mode + typescript-mode + solidity-mode) :when (locate-dominating-file default-directory "package.json") :add-hooks '(+javascript-add-node-modules-path-h npm-mode)) diff --git a/modules/lang/javascript/packages.el b/modules/lang/javascript/packages.el index 23e891ad6..7d89725fb 100644 --- a/modules/lang/javascript/packages.el +++ b/modules/lang/javascript/packages.el @@ -2,21 +2,21 @@ ;;; lang/javascript/packages.el ;; Major modes -(package! coffee-mode) -(package! js2-mode) -(package! rjsx-mode) -(package! typescript-mode) +(package! coffee-mode :pin "86ab8aae86") +(package! js2-mode :pin "fe53814dc2") +(package! rjsx-mode :pin "014c760138") +(package! typescript-mode :pin "a0f2c3ebd4") ;; Tools -(package! eslintd-fix) -(package! js2-refactor) -(package! npm-mode) +(package! eslintd-fix :pin "98c669e365") +(package! js2-refactor :pin "d4c40b5fc8") +(package! npm-mode :pin "3ee7c0bad5") ;; Eval -(package! nodejs-repl) -(package! skewer-mode) +(package! nodejs-repl :pin "8b90948265") +(package! skewer-mode :pin "123215dd9b") ;; Programming environment -(package! tide) +(package! tide :pin "1878a097fc") (when (featurep! :tools lookup) - (package! xref-js2)) + (package! xref-js2 :pin "6f1ed5dae0")) diff --git a/modules/lang/julia/packages.el b/modules/lang/julia/packages.el index d7d0b2236..3eb5b27f6 100644 --- a/modules/lang/julia/packages.el +++ b/modules/lang/julia/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/julia/packages.el -(package! julia-mode) -(package! julia-repl) +(package! julia-mode :pin "5238f9adb7") +(package! julia-repl :pin "b11a572970") diff --git a/modules/lang/kotlin/README.org b/modules/lang/kotlin/README.org new file mode 100644 index 000000000..00f53296c --- /dev/null +++ b/modules/lang/kotlin/README.org @@ -0,0 +1,36 @@ +#+TITLE: lang/kotlin +#+DATE: March 28, @019 +#+SINCE: v3.0.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module adds Kotlin support to Emacs. + +** Module Flags ++ =+lsp= Enables integration with [[https://github.com/emacs-lsp/lsp-mode][kotlin-language-server]]. This requires the + =:tools lsp= module. + +** Plugins ++ kotlin-mode ++ flycheck-kotlin + +* Prerequisites +TODO + +* Features +TODO + +* Configuration +TODO + +* Troubleshooting +TODO diff --git a/modules/lang/kotlin/config.el b/modules/lang/kotlin/config.el index 06a1588b8..d5eda36af 100644 --- a/modules/lang/kotlin/config.el +++ b/modules/lang/kotlin/config.el @@ -1,6 +1,8 @@ ;;; lang/kotlin/config.el -*- lexical-binding: t; -*- (after! kotlin-mode + (when (featurep! +lsp) + (add-hook 'kotlin-mode-local-vars-hook #'lsp!)) (set-docsets! 'kotlin-mode "Kotlin") (map! :map kotlin-mode-map @@ -12,5 +14,5 @@ (use-package! flycheck-kotlin - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :hook (kotlin-mode . flycheck-kotlin-setup)) diff --git a/modules/lang/kotlin/doctor.el b/modules/lang/kotlin/doctor.el index 63eabc017..3e8451a82 100644 --- a/modules/lang/kotlin/doctor.el +++ b/modules/lang/kotlin/doctor.el @@ -2,3 +2,7 @@ (unless (executable-find "ktlint") (warn! "ktlint not found. flycheck-kotlin won't work.")) + +(assert! (or (not (featurep! +lsp)) + (featurep! :tools lsp)) + "This module requires (:tools lsp)") diff --git a/modules/lang/kotlin/packages.el b/modules/lang/kotlin/packages.el index f7c3361dc..3c3d5f202 100644 --- a/modules/lang/kotlin/packages.el +++ b/modules/lang/kotlin/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/kotlin/packages.el -(package! kotlin-mode) +(package! kotlin-mode :pin "ab61099682") -(when (featurep! :tools flycheck) - (package! flycheck-kotlin)) +(when (featurep! :checkers syntax) + (package! flycheck-kotlin :pin "5104ee9a3f")) diff --git a/modules/lang/latex/+viewers.el b/modules/lang/latex/+viewers.el index 1e92f3bd9..337e4674d 100644 --- a/modules/lang/latex/+viewers.el +++ b/modules/lang/latex/+viewers.el @@ -7,10 +7,15 @@ (dolist (viewer (reverse +latex-viewers)) (pcase viewer (`skim - (when (and IS-MAC - (file-exists-p! (or "/Applications/Skim.app" - "~/Applications/Skim.app"))) - (add-to-list 'TeX-view-program-selection '(output-pdf "Skim")))) + (when-let + (app-path + (and IS-MAC + (file-exists-p! (or "/Applications/Skim.app" + "~/Applications/Skim.app")))) + (add-to-list 'TeX-view-program-selection '(output-pdf "Skim")) + (add-to-list 'TeX-view-program-list + (list "Skim" (format "%s/Contents/SharedSupport/displayline -b -g %%n %%o %%b" + app-path))))) (`sumatrapdf (when (and IS-WINDOWS diff --git a/modules/lang/latex/README.org b/modules/lang/latex/README.org index 209af8708..0d208c5c7 100644 --- a/modules/lang/latex/README.org +++ b/modules/lang/latex/README.org @@ -7,10 +7,12 @@ - [[#description][Description]] - [[#module-flags][Module Flags]] - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] - [[#features][Features]] - [[#customization][Customization]] - [[#specifying-the-location-of-a-bibtex-file--corresponding-pdfs][Specifying the location of a bibtex file & corresponding PDFs]] - [[#changing-the-pdfs-viewer][Changing the PDFs viewer]] + - [[#using-cdlatexs-snippets-despite-having-yasnippet][Using cdlatex's snippets despite having yasnippet]] * Description Provide a helping hand when working with LaTeX documents. @@ -26,7 +28,8 @@ Provide a helping hand when working with LaTeX documents. + Compile your .tex code only once using LatexMk ** Module Flags -+ ~+latexmk~ Use LatexMk instead of LaTeX to compile documents. ++ =+latexmk= Use LatexMk instead of LaTeX to compile documents. ++ =+cdlatex= Enable [[https://github.com/cdominik/cdlatex][cdlatex]] for fast math insertion. ** Plugins + [[http://www.gnu.org/software/auctex/][auctex]] @@ -37,6 +40,9 @@ Provide a helping hand when working with LaTeX documents. + [[https://github.com/TheBB/company-reftex][company-reftex]]* + [[https://github.com/vspinu/company-math][company-math]]* + [[https://github.com/tmalsburg/helm-bibtex][ivy-bibtex]]* or [[https://github.com/tmalsburg/helm-bibtex][helm-bibtex]]* ++ [[https://github.com/cdominik/cdlatex][cdlatex]] (=+cdlatex=) + +* TODO Prerequisites * TODO Features @@ -73,3 +79,17 @@ tool, for instance: If none of these tools are found, ~latex-preview-pane~ (uses ~DocView~ in Emacs) is used as a fallback. You can use this exclusively by setting ~+latex-viewers~ to ~nil~. + +** Using cdlatex's snippets despite having yasnippet +cdlatex has a snippet insertion capability which is disabled in favor of +yasnippet when using ~:editor snippets~. If you still wanna use it, simply rebind +the ~TAB~ key for cdlatex, which takes care of snippet-related stuff: + +#+BEGIN_SRC emacs-lisp +(map! :map cdlatex-mode-map + :i "TAB" #'cdlatex-tab) +#+END_SRC + +This would favor yasnippet's expansion and cursor movement over cdlatex's +expansion and movement, but that shouldn't matter if you're not using yasnippet +in latex buffers. diff --git a/modules/lang/latex/config.el b/modules/lang/latex/config.el index 7e1e6d101..50d993ef0 100644 --- a/modules/lang/latex/config.el +++ b/modules/lang/latex/config.el @@ -48,8 +48,11 @@ If no viewers are found, `latex-preview-pane' is used.") (setq-default TeX-master t) ;; set-up chktex (setcar (cdr (assoc "Check" TeX-command-list)) "chktex -v6 -H %s") - ;; tell emacs how to parse tex files - (setq-hook! 'TeX-mode-hook ispell-parser 'tex) + (setq-hook! 'TeX-mode-hook + ;; tell emacs how to parse tex files + ispell-parser 'tex + ;; Don't auto-fill in math blocks + fill-nobreak-predicate (cons #'texmathp fill-nobreak-predicate)) ;; Enable word wrapping (add-hook 'TeX-mode-hook #'visual-line-mode) ;; Fold TeX macros @@ -116,6 +119,33 @@ If no viewers are found, `latex-preview-pane' is used.") (lambda () (* (/ 10.0 (preview-document-pt)) preview-scale)))) +(use-package! cdlatex + :defer t + :when (featurep! +cdlatex) + :hook (LaTeX-mode . cdlatex-mode) + :config + ;; Disabling keys that have overlapping functionality with other parts of Doom + (map! :map cdlatex-mode-map + ;; smartparens takes care of inserting closing delimiters, and if you + ;; don't use smartparens you probably won't want these also. + :g "$" nil + :g "(" nil + :g "{" nil + :g "[" nil + :g "|" nil + :g "<" nil + ;; TAB is used for cdlatex's snippets and navigation. But we have + ;; yasnippet for that. + (:when (featurep! :editor snippets) + :g "TAB" nil) + ;; AUCTeX takes care of auto-inserting {} on _^ if you want, with + ;; `TeX-electric-sub-and-superscript' + :g "^" nil + :g "_" nil + ;; AUCTeX already provides this with `LaTeX-insert-item' + :g [(control return)] nil)) + + ;; Nicely indent lines that have wrapped when visual line mode is activated (use-package! adaptive-wrap :hook (LaTeX-mode . adaptive-wrap-prefix-mode) diff --git a/modules/lang/latex/packages.el b/modules/lang/latex/packages.el index b181f5785..db8796c1d 100644 --- a/modules/lang/latex/packages.el +++ b/modules/lang/latex/packages.el @@ -1,23 +1,25 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/latex/packages.el -(package! auctex) -(package! adaptive-wrap) -(package! latex-preview-pane) +(package! auctex :pin "fafa28d542") +(package! adaptive-wrap :pin "1810c0ee8d") +(package! latex-preview-pane :pin "5297668a89") ;; Optional module features: (when (featurep! +latexmk) - (package! auctex-latexmk)) + (package! auctex-latexmk :pin "4d35352265")) + +(when (featurep! +cdlatex) + (package! cdlatex :pin "b7af5a9884")) ;; Features according to other user selected options (when (featurep! :completion company) - (package! company-auctex) - (package! company-reftex) - (package! company-math)) + (package! company-auctex :pin "48c42c58ce") + (package! company-reftex :pin "33935e9654") + (package! company-math :pin "a796053590")) (when (featurep! :completion ivy) - (package! ivy-bibtex)) + (package! ivy-bibtex :pin "d4471232be")) (when (featurep! :completion helm) - (package! helm-bibtex)) - + (package! helm-bibtex :pin "d4471232be")) diff --git a/modules/lang/lean/packages.el b/modules/lang/lean/packages.el index 6061c61f6..a1ed9b8d9 100644 --- a/modules/lang/lean/packages.el +++ b/modules/lang/lean/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/lean/packages.el -(package! lean-mode) +(package! lean-mode :pin "f26e40daad") (when (featurep! :completion company) - (package! company-lean)) + (package! company-lean :pin "f26e40daad")) diff --git a/modules/lang/ledger/config.el b/modules/lang/ledger/config.el index 35f6b83dd..c516e7bfe 100644 --- a/modules/lang/ledger/config.el +++ b/modules/lang/ledger/config.el @@ -1,27 +1,34 @@ ;;; lang/ledger/config.el -*- lexical-binding: t; -*- -;;;###package ledger-mode -(setq ledger-clear-whole-transactions 1) +(use-package! ledger-mode + :defer t + :init + (setq ledger-clear-whole-transactions 1 + ledger-mode-should-check-version nil) -(defadvice! +ledger--check-version-a (orig-fn) - "Fail gracefully if ledger binary isn't available." - :around #'ledger-check-version - (if (executable-find ledger-binary-path) - (funcall orig-fn) - (message "Couldn't find '%s' executable" ledger-binary-path))) + :config + (setq ledger-binary-path + (if (executable-find "hledger") + "hledger" + "ledger")) -;; Restore leader key in ledger reports -(map! :after ledger-mode - :map ledger-report-mode-map - "C-c C-c" #'ledger-report-edit-report - "C-c C-r" #'ledger-report-redo - "C-c C-s" #'ledger-report-save - :map ledger-reconcile-mode-map - [tab] #'ledger-reconcile-toggle) + (defadvice! +ledger--check-version-a (orig-fn) + "Fail gracefully if ledger binary isn't available." + :around #'ledger-check-version + (if (executable-find ledger-binary-path) + (funcall orig-fn) + (message "Couldn't find '%s' executable" ledger-binary-path))) + + (map! :map ledger-report-mode-map + "C-c C-c" #'ledger-report-edit-report + "C-c C-r" #'ledger-report-redo + "C-c C-s" #'ledger-report-save + :map ledger-reconcile-mode-map + [tab] #'ledger-reconcile-toggle)) (use-package! flycheck-ledger - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after ledger-mode) diff --git a/modules/lang/ledger/packages.el b/modules/lang/ledger/packages.el index 7142c61b2..005ef47b3 100644 --- a/modules/lang/ledger/packages.el +++ b/modules/lang/ledger/packages.el @@ -1,10 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/ledger/packages.el -(package! ledger-mode) +(package! ledger-mode :pin "a514953d6a") (when (featurep! :editor evil) - (package! evil-ledger)) + (package! evil-ledger :pin "7a9f9f5d39")) -(when (featurep! :tools flycheck) - (package! flycheck-ledger)) +(when (featurep! :checkers syntax) + (package! flycheck-ledger :pin "2065beab56")) diff --git a/modules/lang/lua/config.el b/modules/lang/lua/config.el index b69e286e5..e54bf0c73 100644 --- a/modules/lang/lua/config.el +++ b/modules/lang/lua/config.el @@ -28,7 +28,7 @@ (add-hook! 'moonscript-mode-hook #'+lua-moonscript-fix-single-quotes-h #'+lua-moonscript-fontify-interpolation-h) - (when (featurep! :tools flycheck) + (when (featurep! :checkers syntax) (require 'flycheck-moonscript nil t))) diff --git a/modules/lang/lua/packages.el b/modules/lang/lua/packages.el index c4e8e4f7b..13f322b6a 100644 --- a/modules/lang/lua/packages.el +++ b/modules/lang/lua/packages.el @@ -1,14 +1,13 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/lua/packages.el -(package! lua-mode) +(package! lua-mode :pin "1f596a93b3") (when (featurep! +moonscript) - (package! moonscript) - (when (featurep! :tools flycheck) + (package! moonscript :pin "56f90471e2") + (when (featurep! :checkers syntax) (package! flycheck-moonscript - :recipe (:host github :repo "hlissner/emacs-flycheck-moonscript")))) + :recipe (:host github :repo "hlissner/emacs-flycheck-moonscript") :pin "fcb99e5efc"))) (when (featurep! :completion company) - (package! company-lua)) - + (package! company-lua :pin "29f6819de4")) diff --git a/modules/lang/markdown/config.el b/modules/lang/markdown/config.el index 9639ef093..f549cee33 100644 --- a/modules/lang/markdown/config.el +++ b/modules/lang/markdown/config.el @@ -40,13 +40,22 @@ capture, the end position, and the output buffer.") (concat "" "" "" - "")) + "")) :config (set-flyspell-predicate! '(markdown-mode gfm-mode) #'+markdown-flyspell-word-p) (set-lookup-handlers! '(markdown-mode gfm-mode) - :file #'markdown-follow-thing-at-point) + ;; `markdown-follow-thing-at-point' may open an external program or a + ;; buffer. No good way to tell, so pretend it's async. + :file '(markdown-follow-thing-at-point :async t)) + + (sp-local-pair '(markdown-mode gfm-mode) "`" "`" + :unless '(:add sp-point-before-word-p sp-point-before-same-p)) + + (setq-hook! 'markdown-mode-hook + fill-nobreak-predicate (cons #'markdown-code-block-at-point-p + fill-nobreak-predicate)) ;; HACK Prevent mis-fontification of YAML metadata blocks in `markdown-mode' ;; which occurs when the first line contains a colon in it. See diff --git a/modules/lang/markdown/packages.el b/modules/lang/markdown/packages.el index 08a2b1eb3..da473ffe8 100644 --- a/modules/lang/markdown/packages.el +++ b/modules/lang/markdown/packages.el @@ -1,13 +1,13 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/markdown/packages.el -(package! markdown-mode) -(package! markdown-toc) -(package! edit-indirect) +(package! markdown-mode :pin "e9dff50d57") +(package! markdown-toc :pin "7038f4f6d5") +(package! edit-indirect :pin "935ded353b") (when (featurep! +grip) - (package! grip-mode)) + (package! grip-mode :pin "1a61bb71a7")) (when (featurep! :editor evil +everywhere) (package! evil-markdown - :recipe (:host github :repo "Somelauw/evil-markdown"))) + :recipe (:host github :repo "Somelauw/evil-markdown") :pin "46cd81b379")) diff --git a/modules/lang/nim/config.el b/modules/lang/nim/config.el index 4d05f63fe..fbf169299 100644 --- a/modules/lang/nim/config.el +++ b/modules/lang/nim/config.el @@ -25,6 +25,6 @@ windows." (use-package! flycheck-nim - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after nim-mode) diff --git a/modules/lang/nim/packages.el b/modules/lang/nim/packages.el index ba994148a..31e63f829 100644 --- a/modules/lang/nim/packages.el +++ b/modules/lang/nim/packages.el @@ -3,7 +3,7 @@ ;;; requires nim nimsuggest nimble -(package! nim-mode) +(package! nim-mode :pin "16a245e497") -(when (featurep! :tools flycheck) - (package! flycheck-nim)) +(when (featurep! :checkers syntax) + (package! flycheck-nim :pin "ddfade5100")) diff --git a/modules/lang/nix/autoload.el b/modules/lang/nix/autoload.el index bce624513..d0f950569 100644 --- a/modules/lang/nix/autoload.el +++ b/modules/lang/nix/autoload.el @@ -39,3 +39,24 @@ ((user-error "No search engine is enabled. Enable helm or ivy!"))) ;; Tell lookup module to let us handle things from here 'deferred) + +;;;###autoload +(defun +nix-shell-init-mode () + "Resolve a (cached-)?nix-shell shebang to the correct major mode." + (save-excursion + (goto-char (point-min)) + (save-match-data + (when (re-search-forward "#! *\\(?:cached-\\)?nix-shell +-i +\\([^ \n]+\\)" 256 t) + (let* ((interp (match-string 1)) + (mode + (assoc-default + interp + (mapcar (lambda (e) + (cons (format "\\`%s\\'" (car e)) + (cdr e))) + interpreter-mode-alist) + #'string-match-p))) + (when mode + (prog1 (set-auto-mode-0 mode) + (when (eq major-mode 'sh-mode) + (sh-set-shell interp))))))))) diff --git a/modules/lang/nix/config.el b/modules/lang/nix/config.el index 8c3c3dc8c..f8c0a0a5e 100644 --- a/modules/lang/nix/config.el +++ b/modules/lang/nix/config.el @@ -1,6 +1,8 @@ ;;; lang/nix/config.el -*- lexical-binding: t; -*- (use-package! nix-mode + :interpreter ("cached-nix-shell" . +nix-shell-init-mode) + :interpreter ("nix-shell" . +nix-shell-init-mode) :mode "\\.nix\\'" :config (set-company-backend! 'nix-mode 'company-nixos-options) diff --git a/modules/lang/nix/packages.el b/modules/lang/nix/packages.el index eb3256b84..ce39dbd09 100644 --- a/modules/lang/nix/packages.el +++ b/modules/lang/nix/packages.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/nix/packages.el -(package! nix-mode) -(package! nix-update) +(package! nix-mode :pin "5b5961780f") +(package! nix-update :pin "fc6c39c2da") (when (featurep! :completion company) - (package! company-nixos-options)) + (package! company-nixos-options :pin "977b9a505f")) (when (featurep! :completion helm) - (package! helm-nixos-options)) + (package! helm-nixos-options :pin "977b9a505f")) diff --git a/modules/lang/ocaml/README.org b/modules/lang/ocaml/README.org index 9f6755935..ac89cab0f 100644 --- a/modules/lang/ocaml/README.org +++ b/modules/lang/ocaml/README.org @@ -55,10 +55,10 @@ opam install merlin utop ocp-indent dune ocamlformat * Configuration + If =:completion company= is enabled then autocomplete is provided by =merlin= -+ When =:tools flycheck= is enabled then =flycheck-ocaml= is activated to do ++ When =:checkers syntax= is enabled then =flycheck-ocaml= is activated to do on-the-fly syntax/type checking via =merlin=, otherwise this is only done when the file is saved. -+ Spell checking is activated in comments if =:tools flyspell= is active ++ Spell checking is activated in comments if =:checkers spell= is active + A REPL is provided if =utop= is installed and =:tools eval= is active + If =:editor format= is enabled, the =ocamlformat= executable is available and there is an =.ocamlformat= file present then =format-all-buffer= is bound to @@ -69,8 +69,8 @@ opam install merlin utop ocp-indent dune ocamlformat + If =:emacs imenu= is enabled then top level symbols (modules, type, functions, etc.) can be looked up using =SPC / i= -Run =bin/doom refresh= to install all packages and =make doctor= to diagnose -missing tools. +Run =doom sync= to install all packages and =doom doctor= to diagnose missing +tools. * Appendix ** Commands diff --git a/modules/lang/ocaml/config.el b/modules/lang/ocaml/config.el index 69ff8282d..d2db49bb6 100644 --- a/modules/lang/ocaml/config.el +++ b/modules/lang/ocaml/config.el @@ -18,7 +18,7 @@ (tuareg-opam-update-env (tuareg-opam-current-compiler)) ;; Spell-check comments - (when (featurep! :tools flyspell) + (when (featurep! :checkers spell) (add-hook 'tuareg-mode-local-vars-hook #'flyspell-prog-mode)) ;; Ensure asterixes in block comments have at least one space of indentation @@ -37,7 +37,9 @@ (set-eval-handler! 'tuareg-mode #'utop-eval-region) (defun +ocaml-init-utop-h () (when (executable-find "utop") - (utop-minor-mode))))) + (utop-minor-mode))) + :config + (set-popup-rule! "^\\*utop\\*" :quit nil))) (use-package! merlin @@ -51,7 +53,7 @@ (after! tuareg (set-company-backend! 'tuareg-mode 'merlin-company-backend) - (set-lookup-handlers! 'tuareg-mode + (set-lookup-handlers! 'tuareg-mode :async t :definition #'merlin-locate :references #'merlin-occurrences :documentation #'merlin-document)) @@ -63,7 +65,7 @@ "t" #'merlin-type-enclosing) (use-package! flycheck-ocaml - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :hook (merlin-mode . +ocaml-init-flycheck-h) :config (defun +ocaml-init-flycheck-h () @@ -111,4 +113,9 @@ (setq +format-with 'ocp-indent) (when (and (executable-find "ocamlformat") (locate-dominating-file default-directory ".ocamlformat")) + (let ((ext (file-name-extension buffer-file-name t))) + (cond ((equal ext ".eliom") + (setq-local ocamlformat-file-kind 'implementation)) + ((equal ext ".eliomi") + (setq-local ocamlformat-file-kind 'interface)))) (setq +format-with 'ocamlformat)))) diff --git a/modules/lang/ocaml/packages.el b/modules/lang/ocaml/packages.el index a3e38a112..60ccb3272 100644 --- a/modules/lang/ocaml/packages.el +++ b/modules/lang/ocaml/packages.el @@ -1,25 +1,25 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/ocaml/packages.el -(package! tuareg) +(package! tuareg :pin "c12061eb80") (unless (featurep! +lsp) - (package! merlin) - (package! merlin-eldoc) - (when (featurep! :tools flycheck) - (package! flycheck-ocaml))) + (package! merlin :pin "f6954e953b") + (package! merlin-eldoc :pin "db7fab1edd") + (when (featurep! :checkers syntax) + (package! flycheck-ocaml :pin "8707a7bf54"))) -(package! ocp-indent) +(package! ocp-indent :pin "9e26c0a269") (when (featurep! :tools eval) - (package! utop)) + (package! utop :pin "7c99d8c904")) (when (featurep! :editor format) ;; by default quelpa generated a version 0pre0.20180929.192844, which got ;; parsed into (0 -1 0 ...), which when compared with version nil (0) in ;; package-installed-p always yielded false (package! ocamlformat :recipe - (:host github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el")))) + (:host github :repo "ocaml-ppx/ocamlformat" :files ("emacs/*.el")) :pin "dba4487820")) (package! dune :recipe - (:host github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el"))) + (:host github :repo "ocaml/dune" :files ("editor-integration/emacs/*.el")) :pin "f3df7abe64") diff --git a/modules/lang/org/README.org b/modules/lang/org/README.org index 650e68850..59c3968cb 100644 --- a/modules/lang/org/README.org +++ b/modules/lang/org/README.org @@ -24,9 +24,9 @@ This module adds org-mode support to Doom Emacs, along with a number of adjustments, extensions and reasonable defaults to make it more performant and intuitive out of the box: -+ A custom, centralized attachment and export system that stores files in one - place, rather than in the same directory as the input file(s) (only applies to - attachments/exporting from files in/under =org-directory=). ++ A custom, centralized attachment system that stores files in one place, rather + than in the same directory as the input file(s) (only applies to attachments + from files in/under =org-directory=). + Executable code blocks with support for a variety of languages and tools (depending on what :lang modules are enabled). + Supports an external org-capture workflow through the =bin/org-capture= shell @@ -35,8 +35,8 @@ intuitive out of the box: org files to reveal.js slideshows. + Drag-and-drop support for images (with inline preview) and media files (drops a file icon and a short link) (requires =+dragndrop= flag). -+ Integration with pandoc, ipython, reveal.js, beamer, and others (requires - flags). ++ Integration with pandoc, ipython, jupyter, reveal.js, beamer, and others + (requires flags). + Export-to-clipboard functionality, for copying text into formatted html, markdown or rich text to the clipboard (see ~+org/export-to-clipboard~ and ~+org/export-to-clipboard-as-rich-text~). @@ -52,27 +52,33 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode #+end_quote ** Module Flags ++ =+brain= Enables [[https://github.com/Kungsgeten/org-brain][org-brain]] integration. + =+dragndrop= Enables drag-and-drop support for images and files; inserts inline previews for images and an icon+link for other media types. + =+gnuplot= Installs gnuplot & gnuplot-mode, which enables rendering images from gnuplot src blocks or plotting tables with ~org-plot/gnuplot~ (bound to =SPC m b p=, by default). -+ =+ipython= Enables ipython+babel integration. ++ =+hugo= Enables integration with [[https://gohugo.io][hugo]] to export from Emacs well-formed + ([[https://github.com/russross/blackfriday][blackfriday]]) markdown. ++ =+ipython= (**DEPRECATED**) Enables ipython integration for babel. ++ =+journal= Enables [[https://github.com/bastibe/org-journal][org-journal]] integration. ++ =+jupyter= Enables Jupyter integration for babel. + =+pandoc= Enables pandoc integration into the Org exporter. + =+pomodoro= Enables a pomodoro timer for clocking time on tasks. + =+present= Enables integration with reveal.js, beamer and org-tree-slide, so Emacs can be used for presentations. -+ =+hugo= Enables integration with [[https://gohugo.io][hugo]] to export from Emacs well-formed - ([[https://github.com/russross/blackfriday][blackfriday]]) markdown. ** Plugins -+ [[https://orgmode.org/][org-plus-contrib]] -+ [[https://github.com/sabof/org-bullets][org-bullets]] -+ [[https://github.com/TobiasZawada/org-yt][org-yt]] -+ [[https://github.com/snosov1/toc-org][toc-org]] -+ [[https://github.com/jkitchin/ox-clip][ox-clip]] + [[https://github.com/hniksic/emacs-htmlize][htmlize]] + [[https://github.com/astahlman/ob-async][ob-async]] ++ [[https://github.com/alphapapa/org-bookmark-heading][org-bookmark-heading]] ++ [[https://github.com/sabof/org-bullets][org-bullets]] ++ [[https://github.com/rexim/org-cliplink][org-cliplink]] ++ [[https://github.com/magit/orgit][orgit]] ++ [[https://orgmode.org/][org-plus-contrib]] ++ [[https://github.com/TobiasZawada/org-yt][org-yt]] ++ [[https://github.com/jkitchin/ox-clip][ox-clip]] ++ [[https://github.com/snosov1/toc-org][toc-org]] + =:lang crystal= + [[https://github.com/brantou/ob-crystal][ob-crystal]] + =:lang go= @@ -96,6 +102,8 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode + [[https://github.com/bruceravel/gnuplot-mode][gnuplot-mode]] + =+ipython= + [[https://github.com/gregsexton/ob-ipython][ob-ipython]] ++ =+jupyter= + + [[https://github.com/dzop/emacs-jupyter][jupyter]] + =+pandoc= + [[https://github.com/kawabata/ox-pandoc][ox-pandoc]] + =+pomodoro= @@ -127,7 +135,7 @@ https://www.mfoot.com/blog/2015/11/22/literate-emacs-configuration-with-org-mode + Statistics cookies are updated when saving the buffer of exiting insert mode (see ~+org-enable-auto-update-cookies-h~). + Org-protocol has been lazy loaded (see ~+org-init-protocol-lazy-loader-h~); - loaded when the server recieves a request for an org-protocol:// url. + loaded when the server receives a request for an org-protocol:// url. + Babel and babel plugins are now lazy loaded (see ~+org-init-babel-lazy-loader-h~); loaded when a src block is executed. No need to use ~org-babel-do-load-languages~ in your config, just install your babel diff --git a/modules/lang/org/autoload/contrib-dragndrop.el b/modules/lang/org/autoload/contrib-dragndrop.el index b54831cf6..618790cee 100644 --- a/modules/lang/org/autoload/contrib-dragndrop.el +++ b/modules/lang/org/autoload/contrib-dragndrop.el @@ -5,8 +5,8 @@ (defun +org-dragndrop-download-dnd-fn (uri action) "Handle file links and base64 data uris." (if (eq major-mode 'org-mode) - (+org-attach/uri uri) + (+org/attach-file-and-insert-link uri) (let ((dnd-protocol-alist - (rassq-delete-all '+org-attach-download-dnd + (rassq-delete-all '+org-dragndrop-download-dnd-fn (copy-alist dnd-protocol-alist)))) (dnd-handle-one-url nil action uri)))) diff --git a/modules/lang/org/autoload/contrib-present.el b/modules/lang/org/autoload/contrib-present.el index 0a4925146..6f40b31af 100644 --- a/modules/lang/org/autoload/contrib-present.el +++ b/modules/lang/org/autoload/contrib-present.el @@ -9,10 +9,13 @@ if (buffer-local-value 'org-tree-slide-mode buf) return t) (org-tree-slide-mode -1) - (remove-hook 'kill-buffer-hook #'+org-present--cleanup-org-tree-slides-mode))) + (remove-hook 'kill-buffer-hook #'+org-present--cleanup-org-tree-slides-mode + 'local))) (defun +org-present--make-invisible (beg end) - (let ((overlay (make-overlay beg end))) + (unless (assq '+org-present buffer-invisibility-spec) + (add-to-invisibility-spec '(+org-present))) + (let ((overlay (make-overlay beg (1+ end)))) (push overlay +org-present--overlays) (overlay-put overlay 'invisible '+org-present))) @@ -21,19 +24,21 @@ ;;; Hooks ;;;###autoload -(defun +org-present-add-overlays-h () - "TODO" - (add-to-invisibility-spec '(+org-present)) +(defun +org-present-hide-blocks-h () + "Hide org #+ constructs." (save-excursion - ;; hide org-mode options starting with #+ (goto-char (point-min)) (while (re-search-forward "^[[:space:]]*\\(#\\+\\)\\(\\(?:BEGIN\\|END\\|ATTR\\)[^[:space:]]+\\).*" nil t) (+org-present--make-invisible (match-beginning 1) - (match-end 0))) - ;; hide stars in headings + (match-end 0))))) + +;;;###autoload +(defun +org-present-hide-leading-stars-h () + "Hide leading stars in headings." + (save-excursion (goto-char (point-min)) - (while (re-search-forward "^\\(\\*+\\s-\\)" nil t) + (while (re-search-forward "^\\(\\*+\\)" nil t) (+org-present--make-invisible (match-beginning 1) (match-end 1))))) ;;;###autoload @@ -55,43 +60,35 @@ (defvar cwm-left-fringe-ratio) (defvar cwm-centered-window-width) ;;;###autoload -(defun +org-present-init-org-tree-window-h () +(defun +org-present-prettify-slide-h () "TODO" "Set up the org window for presentation." (doom/window-maximize-buffer) (let ((arg (if org-tree-slide-mode +1 -1))) (when (fboundp 'centered-window-mode) - (let ((cwm-use-vertical-padding t) - (cwm-frame-internal-border 110) - (cwm-left-fringe-ratio -10) - (cwm-centered-window-width 240)) - (centered-window-mode arg))) - (window-divider-mode (* arg -1)) + (setq-local cwm-use-vertical-padding t) + (setq-local cwm-frame-internal-border 100) + (setq-local cwm-left-fringe-ratio -10) + (setq-local cwm-centered-window-width 300) + (centered-window-mode arg)) (hide-mode-line-mode arg) (+org-pretty-mode arg) (cond (org-tree-slide-mode - (org-indent-mode -1) + (set-window-fringes nil 0 0) + (when (bound-and-true-p solaire-mode) + (solaire-mode -1) + (fringe-mode 0)) + (when (bound-and-true-p flyspell-mode) + (flyspell-mode -1)) + (add-hook 'kill-buffer-hook #'+org-present--cleanup-org-tree-slides-mode + nil 'local) (text-scale-set +org-present-text-scale) - (ignore-errors (org-latex-preview '(4))) - (set-face-attribute 'org-level-2 nil :height 1.4)) + (ignore-errors (org-latex-preview '(4)))) (t - (org-indent-mode +1) (text-scale-set 0) + (set-window-fringes nil fringe-mode fringe-mode) (org-clear-latex-preview) - (set-face-attribute 'org-level-2 nil :height 1.0) (+org-present-remove-overlays-h) - (org-remove-inline-images))))) - - -;; -;;; Commands - -(defvar +org-present--overlays nil) -;;;###autoload -(defun +org-present/start () - "TODO" - (interactive) - (unless (derived-mode-p 'org-mode) - (error "Not in an org buffer")) - (call-interactively #'org-tree-slide-mode) - (add-hook 'kill-buffer-hook #'+org-present--cleanup-org-tree-slides-mode)) + (org-remove-inline-images) + (org-mode))) + (redraw-display))) diff --git a/modules/lang/org/autoload/org-attach.el b/modules/lang/org/autoload/org-attach.el index b3cee8902..fa41685b2 100644 --- a/modules/lang/org/autoload/org-attach.el +++ b/modules/lang/org/autoload/org-attach.el @@ -1,30 +1,5 @@ ;;; lang/org/autoload/org-attach.el -*- lexical-binding: t; -*- -;; -(defvar +org-attachments nil - "A list of all indexed attachments in `org-directory'.") - -(defvar +org-attachments-files nil - "A list of all attachments in `org-attach-id-dir'.") - -(defun +org-list-attachments (&optional beg end) - "Return a list of all attachment file names in the current buffer between BEG -and END (defaults to `point-min' and `point-max')." - (let ((case-fold-search t) - attachments) - (or end (setq end (point-max))) - (org-save-outline-visibility nil - (org-with-wide-buffer - (goto-char (or beg (point-min))) - (while (search-forward "[[attach:" end t) - (let* ((context (save-match-data (org-element-context))) - (link (expand-file-name (org-link-unescape (org-element-property :path context)) - org-attach-id-dir))) - (when (and (equal "file" (org-element-property :type context)) - (file-in-directory-p link org-attach-id-dir)) - (push (file-name-nondirectory link) attachments)))))) - (cl-delete-duplicates attachments :test #'string=))) - ;;;###autoload (defun +org-attach-icon-for (path) (char-to-string @@ -39,63 +14,46 @@ and END (defaults to `point-min' and `point-max')." ((or "zip" "gz" "tar" "7z" "rar") ?) (_ ?)))) +;;;###autoload +(defun +org/open-gallery-from-attachments () + "TODO" + (interactive) + (require 'org-attach) + (if-let (dir (org-attach-dir)) + (pop-to-buffer + ;; Rather than opening dired *and* image-dired windows, suppress them + ;; both and open only the image-dired window. + (save-window-excursion + (image-dired dir) + (current-buffer))) + (user-error "No attachments for this node"))) ;;;###autoload -(defun +org-attach/sync (arg) - "Reindex all attachments in `org-directory' and delete orphaned attachments in -`org-attach-id-dir'. If ARG (universal arg), conduct a dry run." - (declare (interactive-only t)) - (interactive "P") - (message "Reloading") - (setq +org-attachments-files (directory-files org-attach-id-dir nil "^[^.]" t)) - (with-temp-buffer - (delay-mode-hooks (org-mode)) - (dolist (org-file (directory-files-recursively org-directory "\\.org$")) - (insert-file-contents-literally org-file)) - (setq +org-attachments (+org-list-attachments))) - ;; clean up - (let ((deleted 0)) - (dolist (file (cl-set-difference +org-attachments-files +org-attachments - :test #'string=)) - (message "Deleting orphaned attachment: %s" file) - (cl-incf deleted) - (unless arg - (delete-file (expand-file-name file org-attach-id-dir)))) - (message "Buffer's attachments synced (%d deleted)" deleted))) - -;;;###autoload -(defun +org-attach/find-file () +(defun +org/find-file-in-attachments () "Open a file from `org-attach-id-dir'." (interactive) (doom-project-browse org-attach-id-dir)) ;;;###autoload -(defun +org-attach/file (path) - "Copies the file at PATH to `+org-attach-dir' and places an org link to it at -the cursor." - (interactive "fAttach file: ") - (+org-attach/uri path)) - -;;;###autoload -(defun +org-attach/uri (uri) - "Downloads the file at URL and place an org link to it at the cursor." +(defun +org/attach-file-and-insert-link (path) + "Downloads the file at PATH and insert an org link at point. +PATH (a string) can be an url, a local file path, or a base64 encoded datauri." (interactive "sUri/file: ") (unless (eq major-mode 'org-mode) (user-error "Not in an org buffer")) (require 'org-download) - (let ((raw-uri (url-unhex-string uri))) - (condition-case ex - (cond ((string-match-p "^data:image/png;base64," uri) - (org-download-dnd-base64 uri nil)) + (condition-case ex + (let ((raw-uri (url-unhex-string path))) + (cond ((string-match-p "^data:image/png;base64," path) + (org-download-dnd-base64 path nil)) ((image-type-from-file-name raw-uri) (org-download-image raw-uri)) - (t - (let ((new-path (expand-file-name (org-download--fullname raw-uri)))) + ((let ((new-path (expand-file-name (org-download--fullname raw-uri)))) ;; Download the file - (if (string-match-p (concat "^" (regexp-opt '("http" "https" "nfs" "ftp" "file")) ":/") uri) + (if (string-match-p (concat "^" (regexp-opt '("http" "https" "nfs" "ftp" "file")) ":/") path) (url-copy-file raw-uri new-path) - (copy-file uri new-path)) + (copy-file path new-path)) ;; insert the link - (org-download-insert-link raw-uri new-path)))) - (error - (user-error "Failed to attach file: %s" (error-message-string ex)))))) + (org-download-insert-link raw-uri new-path))))) + (error + (user-error "Failed to attach file: %s" (error-message-string ex))))) diff --git a/modules/lang/org/autoload/org-avy.el b/modules/lang/org/autoload/org-avy.el new file mode 100644 index 000000000..6112e2392 --- /dev/null +++ b/modules/lang/org/autoload/org-avy.el @@ -0,0 +1,20 @@ +;;; lang/org/autoload/org-avy.el -*- lexical-binding: t; -*- + +;;;###autoload +(defun +org-headline-avy () + "TODO" + (require 'avy) + (save-excursion + (when-let* ((org-reverse-note-order t) + (pos (avy-with avy-goto-line (avy-jump (rx bol (1+ "*") (1+ blank)))))) + (when (integerp (car pos)) + ;; If avy is aborted with "C-g", it returns `t', so we know it was NOT + ;; aborted when it returns an int. If it doesn't return an int, we + ;; return nil. + (copy-marker (car pos)))))) + +;;;###autoload +(defun +org/goto-visible () + "TODO" + (interactive) + (goto-char (+org-headline-avy))) diff --git a/modules/lang/org/autoload/org-capture.el b/modules/lang/org/autoload/org-capture.el index 4707fab75..ab7270bf7 100644 --- a/modules/lang/org/autoload/org-capture.el +++ b/modules/lang/org/autoload/org-capture.el @@ -12,7 +12,8 @@ (width . 70) (height . 25) (transient . t) - ,(if IS-LINUX '(display . ":0"))) + ,(if IS-LINUX '(display . ":0")) + ,(if IS-MAC '(menu-bar-lines . 1))) "TODO") ;;;###autoload @@ -134,7 +135,7 @@ project." (+org--capture-ensure-heading (cdr headings) (1+ initial-level))))) (defun +org--capture-central-file (file project) - (let ((file (expand-file-name +org-capture-projects-file org-directory))) + (let ((file (expand-file-name file org-directory))) (set-buffer (org-capture-target-buffer file)) (org-capture-put-target-region-and-position) (widen) @@ -148,16 +149,16 @@ project." (defun +org-capture-central-project-todo-file () "TODO" (+org--capture-central-file - +org-capture-todo-file (projectile-project-name))) + +org-capture-projects-file (projectile-project-name))) ;;;###autoload (defun +org-capture-central-project-notes-file () "TODO" (+org--capture-central-file - +org-capture-notes-file (projectile-project-name))) + +org-capture-projects-file (projectile-project-name))) ;;;###autoload (defun +org-capture-central-project-changelog-file () "TODO" (+org--capture-central-file - +org-capture-changelog-file (projectile-project-name))) + +org-capture-projects-file (projectile-project-name))) diff --git a/modules/lang/org/autoload/org-link.el b/modules/lang/org/autoload/org-link.el index 54be1375d..bc4317183 100644 --- a/modules/lang/org/autoload/org-link.el +++ b/modules/lang/org/autoload/org-link.el @@ -1,5 +1,21 @@ ;;; lang/org/autoload/org-link.el -*- lexical-binding: t; -*- +(defun +org--relpath (path root) + (if (and buffer-file-name (file-in-directory-p buffer-file-name root)) + (file-relative-name path) + path)) + +;;;###autoload +(defun +org-def-link (key dir) + (org-link-set-parameters + key + :complete (lambda () (+org--relpath (+org-link-read-file key dir) dir)) + :follow (lambda (link) (find-file (expand-file-name link dir))) + :face (lambda (link) + (if (file-exists-p (expand-file-name link dir)) + 'org-link + 'error)))) + ;;;###autoload (defun +org-link-read-file (key dir) (let ((file (read-file-name (format "%s: " (capitalize key)) dir))) diff --git a/modules/lang/org/autoload/org-refile.el b/modules/lang/org/autoload/org-refile.el index 089484206..a9ac3aaf9 100644 --- a/modules/lang/org/autoload/org-refile.el +++ b/modules/lang/org/autoload/org-refile.el @@ -3,15 +3,27 @@ ;; REVIEW These are all proof-of-concept. Refactor me! ;;;###autoload -(defun +org/refile-to-current-file (arg) +(defun +org/refile-to-current-file (arg &optional file) "TODO" (interactive "P") - (let ((org-refile-targets `((nil :maxlevel . 10))) + (let ((org-refile-targets `((,file :maxlevel . 10))) (org-refile-use-outline-path nil) (org-refile-keep arg) current-prefix-arg) (call-interactively #'org-refile))) +;;;###autoload +(defun +org/refile-to-file (arg file) + "Refile current heading to a particular org file." + (interactive + (list current-prefix-arg + (read-file-name "Select file to refile to: " + default-directory + buffer-file-name + t nil + (lambda (f) (string-match-p "\\.org$" f))))) + (+org/refile-to-current-file arg file)) + ;;;###autoload (defun +org/refile-to-other-window (arg) "TODO" @@ -59,6 +71,25 @@ (user-error "No saved location to refile to")) (let ((org-refile-keep arg) (completing-read-function - (lambda (_p coll _pred _rm _ii _h default &rest _) + (lambda (_p _coll _pred _rm _ii _h default &rest _) default))) (org-refile))) + +(defvar org-after-refile-insert-hook) +;; Inspired by org-teleport and alphapapa/alpha-org +;;;###autoload +(defun +org/refile-to-visible () + "Refile current heading as first child of visible heading selected with Avy." + (interactive) + (when-let (marker (+org-headline-avy)) + (let* ((buffer (marker-buffer marker)) + (filename + (buffer-file-name (or (buffer-base-buffer buffer) + buffer))) + (heading + (org-with-point-at marker + (org-get-heading 'no-tags 'no-todo))) + ;; Won't work with target buffers whose filename is nil + (rfloc (list heading filename nil marker)) + (org-after-refile-insert-hook (cons #'org-reveal org-after-refile-insert-hook))) + (org-refile nil nil rfloc)))) diff --git a/modules/lang/org/autoload/org.el b/modules/lang/org/autoload/org.el index 88241662f..4cc2b3617 100644 --- a/modules/lang/org/autoload/org.el +++ b/modules/lang/org/autoload/org.el @@ -62,14 +62,10 @@ ('above (save-excursion (org-shiftmetadown)) (+org/table-previous-row)))) - ((memq type '(headline inlinetask)) - (let ((level (if (eq (org-element-type context) 'headline) - (org-element-property :level context) - 1))) + ((let ((level (or (org-current-level) 1))) (pcase direction (`below - (let ((at-eol (>= (point) (1- (line-end-position)))) - org-insert-heading-respect-content) + (let (org-insert-heading-respect-content) (goto-char (line-end-position)) (org-end-of-subtree) (insert "\n" (make-string level ?*) " "))) @@ -82,13 +78,12 @@ (org-todo (cond ((eq todo-type 'done) (car (+org-get-todo-keywords-for todo-keyword))) (todo-keyword) - ('todo)))))) - - ((user-error "Not a valid list, heading or table"))) + ('todo))))))) (when (org-invisible-p) (org-show-hidden-entry)) - (when (bound-and-true-p evil-local-mode) + (when (and (bound-and-true-p evil-local-mode) + (not (evil-emacs-state-p))) (evil-insert 1)))) (defun +org--get-property (name &optional bound) @@ -274,6 +269,24 @@ If on a: (ignore-errors (org-promote))) ((call-interactively #'self-insert-command)))) +;;;###autoload +(defun +org/toggle-clock (arg) + "Toggles clock on the last clocked item. + +Clock out if an active clock is running. Clock in otherwise. + +If in an org file, clock in on the item at point. Otherwise clock into the last +task you clocked into. + +See `org-clock-out', `org-clock-in' and `org-clock-in-last' for details on how +the prefix ARG changes this command's behavior." + (interactive "P") + (if (org-clocking-p) + (if arg + (org-clock-cancel) + (org-clock-out)) + (org-clock-in-last arg))) + ;;; Folds ;;;###autoload @@ -348,6 +361,12 @@ Made for `org-tab-first-hook' in evil-mode." ((org-in-src-block-p t) (org-babel-do-in-edit-buffer (call-interactively #'indent-for-tab-command)) + t) + ((and (save-excursion + (skip-chars-backward " \t") + (bolp)) + (org-in-subtree-not-table-p)) + (call-interactively #'tab-to-tab-stop) t))) ;;;###autoload @@ -362,18 +381,26 @@ Made for `org-tab-first-hook' in evil-mode." "Tries to expand a yasnippet snippet, if one is available. Made for `org-tab-first-hook'." (when (bound-and-true-p yas-minor-mode) - (cond ((and (or (not (bound-and-true-p evil-local-mode)) - (evil-insert-state-p)) - (yas--templates-for-key-at-point)) - (call-interactively #'yas-expand) - t) - ((use-region-p) - ;; Triggering mode-specific indentation is expensive in src blocks - ;; (if `org-src-tab-acts-natively' is non-nil), and can cause errors, - ;; so we avoid smart indentation in this case. - (let ((yas-indent-line 'fixed)) - (call-interactively #'yas-insert-snippet)) - t)))) + (let ((major-mode (if (org-in-src-block-p t) + (org-src-get-lang-mode (org-eldoc-get-src-lang)) + major-mode)) + (org-src-tab-acts-natively nil) ; causes breakages + ;; Smart indentation doesn't work with yasnippet, and painfully slow + ;; in the few cases where it does. + (yas-indent-line 'fixed)) + ;; HACK Yasnippet field overlays break org-bullet-mode. Don't ask me why. + (add-hook! 'yas-after-exit-snippet-hook :local + (when (bound-and-true-p org-bullets-mode) + (org-bullets-mode -1) + (org-bullets-mode +1))) + (cond ((and (or (not (bound-and-true-p evil-local-mode)) + (evil-insert-state-p)) + (yas--templates-for-key-at-point)) + (yas-expand) + t) + ((use-region-p) + (yas-insert-snippet) + t))))) ;;;###autoload (defun +org-cycle-only-current-subtree-h (&optional arg) @@ -394,6 +421,14 @@ with `org-cycle')." (org-cycle-internal-local) t))))) +;;;###autoload +(defun +org-clear-babel-results-h () + "Remove the results block for the org babel block at point." + (when (and (org-in-src-block-p t) + (org-babel-where-is-src-block-result)) + (org-babel-remove-result) + t)) + ;;;###autoload (defun +org-unfold-to-2nd-level-or-point-h () "My version of the 'overview' #+STARTUP option: expand first-level headings. diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index a52611d85..d8407ff02 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -6,7 +6,8 @@ (D . C) (sh . shell) (bash . shell) - (matlab . octave)) + (matlab . octave) + (amm . ammonite)) "An alist mapping languages to babel libraries. This is necessary for babel libraries (ob-*.el) that don't match the name of the language. @@ -51,14 +52,6 @@ Is relative to `org-directory', unless it is absolute. Is used in Doom's default (defvar +org-initial-fold-level 2 "The initial fold level of org files when no #+STARTUP options for it.") -(defvar +org-enable-centralized-exports t - "If non-nil, files exported from files in `org-directory' will be stored in -`+org-export-directory', rather than the same directory has the input file(s).") - -(defvar +org-export-directory ".export/" - "Where to store exported files relative to `org-directory'. Can be an absolute -path too.") - (defvar +org-habit-graph-padding 2 "The padding added to the end of the consistency graph") @@ -78,8 +71,6 @@ path too.") (setq-default ;; Don't monopolize the whole frame just for the agenda org-agenda-window-setup 'current-window - ;; Hide blocked tasks in the agenda view. - org-agenda-dim-blocked-tasks 'invisible org-agenda-inhibit-startup t org-agenda-skip-unavailable-files t ;; Move the agenda to show the previous 3 days and the next 7 days for a bit @@ -111,7 +102,7 @@ path too.") (?B . warning) (?C . success)) org-startup-indented t - org-tags-column -80 + org-tags-column 0 org-use-sub-superscripts '{}) (setq org-refile-targets @@ -124,25 +115,20 @@ path too.") org-refile-use-outline-path 'file org-outline-path-complete-in-steps nil) - ;; Scale up LaTeX previews a bit (default is too small) - (setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) - ;; ...and fix their background w/ themes - (add-hook! 'doom-load-theme-hook - (defun +org-refresh-latex-background () - "Previews are usually rendered with light backgrounds, so ensure their -background (and foreground) match the current theme." - (plist-put! org-format-latex-options - :background - (face-attribute (or (cadr (assq 'default face-remapping-alist)) - 'default) - :background nil t)))) + ;; Fontify latex blocks and entities, but not natively -- that's too slow + (setq org-highlight-latex-and-related '(latex script entities)) + (plist-put! org-format-latex-options + :scale 1.5 ; larger previews + :foreground 'auto ; match the theme foreground + :background 'auto) ; ... and its background ;; HACK Face specs fed directly to `org-todo-keyword-faces' don't respect ;; underlying faces like the `org-todo' face does, so we define our own ;; intermediary faces that extend from org-todo. - (custom-declare-face '+org-todo-active '((t (:inherit (bold font-lock-constant-face org-todo)))) "") - (custom-declare-face '+org-todo-project '((t (:inherit (bold font-lock-doc-face org-todo)))) "") - (custom-declare-face '+org-todo-onhold '((t (:inherit (bold warning org-todo)))) "") + (with-no-warnings + (custom-declare-face '+org-todo-active '((t (:inherit (bold font-lock-constant-face org-todo)))) "") + (custom-declare-face '+org-todo-project '((t (:inherit (bold font-lock-doc-face org-todo)))) "") + (custom-declare-face '+org-todo-onhold '((t (:inherit (bold warning org-todo)))) "")) (setq org-todo-keywords '((sequence "TODO(t)" ; A task that needs doing & is ready to do @@ -183,13 +169,14 @@ background (and foreground) match the current theme." (defun +org-init-babel-h () (setq org-src-preserve-indentation t ; use native major-mode indentation - org-src-tab-acts-natively t + org-src-tab-acts-natively t ; we do this ourselves ;; You don't need my permission (just be careful, mkay?) org-confirm-babel-evaluate nil org-link-elisp-confirm-function nil - org-link-shell-confirm-function t ; except you, too dangerous ;; Show src buffer in popup, and don't monopolize the frame - org-src-window-setup 'other-window) + org-src-window-setup 'other-window + ;; Our :lang common-lisp module uses sly, so... + org-babel-lisp-eval-fn #'sly-eval) ;; I prefer C-c C-c over C-c ' (more consistent) (define-key org-src-mode-map (kbd "C-c C-c") #'org-edit-src-exit) @@ -212,13 +199,19 @@ background (and foreground) match the current theme." (defun +org-init-babel-lazy-loader-h () "Load babel libraries lazily when babel blocks are executed." + (defun +org--babel-lazy-load (lang) + (cl-check-type lang symbol) + (or (run-hook-with-args-until-success '+org-babel-load-functions lang) + (require (intern (format "ob-%s" lang)) nil t) + (require lang nil t))) + (defadvice! +org--src-lazy-load-library-a (lang) "Lazy load a babel package to ensure syntax highlighting." :before #'org-src--get-lang-mode (or (cdr (assoc lang org-src-lang-modes)) - (fboundp (intern-soft (format "%s-mode" lang))) - (require (intern-soft (format "ob-%s" lang)) nil t))) + (+org--babel-lazy-load lang))) + ;; This also works for tangling and exporting (defadvice! +org--babel-lazy-load-library-a (info) "Load babel libraries lazily when babel blocks are executed." :after-while #'org-babel-confirm-evaluate @@ -229,14 +222,19 @@ background (and foreground) match the current theme." lang))) (when (and lang (not (cdr (assq lang org-babel-load-languages))) - (or (run-hook-with-args-until-success '+org-babel-load-functions lang) - (require (intern (format "ob-%s" lang)) nil t))) + (+org--babel-lazy-load lang)) (when (assq :async (nth 2 info)) ;; ob-async has its own agenda for lazy loading packages (in the ;; child process), so we only need to make sure it's loaded. (require 'ob-async nil t)) (add-to-list 'org-babel-load-languages (cons lang t))) - t))) + t)) + + (defadvice! +org--noop-org-babel-do-load-languages-a (&rest _) + :override #'org-babel-do-load-languages + (message + (concat "`org-babel-do-load-languages' is redundant with Doom's lazy loading mechanism for babel " + "packages. There is no need to use it, so it has been disabled")))) (defun +org-init-capture-defaults-h () @@ -250,6 +248,8 @@ I like: vimperator, dmenu or a global keybinding." (setq org-default-notes-file (expand-file-name +org-capture-notes-file org-directory) + +org-capture-journal-file + (expand-file-name +org-capture-journal-file org-directory) org-capture-templates '(("t" "Personal todo" entry (file+headline +org-capture-todo-file "Inbox") @@ -258,7 +258,7 @@ I like: (file+headline +org-capture-notes-file "Inbox") "* %u %?\n%i\n%a" :prepend t) ("j" "Journal" entry - (file+olp+datetree +org-capture-journal-file "Inbox") + (file+olp+datetree +org-capture-journal-file) "* %U %?\n%i\n%a" :prepend t) ;; Will use {project-root}/{todo,notes,changelog}.org, unless a @@ -337,90 +337,19 @@ underlying, modified buffer. This fixes that." (add-hook '+doom-dashboard-inhibit-functions #'+org-capture-frame-p))) -(defun +org-init-centralized-attachments-h () - "I believe Org's native attachment system is over-complicated and litters -files with metadata I don't want. So I wrote my own, which: - -+ Places attachments in a centralized location (`org-attach-id-dir' in - `org-directory'). -+ Adds attach:* link abbreviation for quick links to these files from anywhere. -+ Use `+org-attach/sync' to index all attachments in `org-directory' that use - the attach:* abbreviation and delete orphaned ones that are no longer - referenced. -+ This compliments the +dragndrop flag which provides drag'n'drop support for - images (with preview) and media files. - -Some commands of interest: -+ `org-download-screenshot' -+ `+org-attach/file' -+ `+org-attach/url' -+ `+org-attach/sync'" - (setq org-attach-id-dir (doom-path org-directory org-attach-id-dir)) - - ;; A shorter link to attachments - (add-to-list 'org-link-abbrev-alist - (cons "attach" - (abbreviate-file-name org-attach-id-dir))) - - (org-link-set-parameters - "attach" - :follow (lambda (link) (find-file (doom-path org-attach-id-dir link))) - :complete (lambda (&optional _arg) - (+org--relpath (+org-link-read-file "attach" org-attach-id-dir) - org-attach-id-dir)) - :face (lambda (link) - (if (file-exists-p (expand-file-name link org-attach-id-dir)) - 'org-link - 'error))) - +(defun +org-init-attachments-h () + "Sets up org's attachment system." + ;; Centralized attachments directory + (setq org-attach-id-dir (doom-path org-directory org-attach-id-dir) + ;; Store a link to attachments when they are attached + org-attach-store-link-p t + ;; Inherit attachment properties from parent nodes + org-attach-use-inheritance t) (after! projectile - (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir)) - - (after! recentf - (add-to-list 'recentf-exclude - (lambda (file) (file-in-directory-p file org-attach-id-dir))))) - - -(defun +org-init-centralized-exports-h () - "TODO" - ;; I don't have any beef with org's built-in export system, but I do wish it - ;; would export to a central directory (by default), rather than - ;; `default-directory'. This is because all my org files are usually in one - ;; place, and I want to be able to refer back to old exports if needed. - (setq +org-export-directory (expand-file-name +org-export-directory org-directory)) - - (defadvice! +org--export-output-file-name-a (args) - "Return a centralized export location unless one is provided or the current -file isn't in `org-directory'." - :filter-args #'org-export-output-file-name - (when (and +org-enable-centralized-exports - (not (nth 2 args)) - buffer-file-name - (file-in-directory-p buffer-file-name org-directory)) - (cl-destructuring-bind (extension &optional subtreep _pubdir) args - (let ((dir (expand-file-name +org-export-directory org-directory))) - (unless (file-directory-p dir) - (make-directory dir t)) - (setq args (list extension subtreep dir))))) - args)) + (add-to-list 'projectile-globally-ignored-directories org-attach-id-dir))) (defun +org-init-custom-links-h () - (defun +org--relpath (path root) - (if (and buffer-file-name (file-in-directory-p buffer-file-name root)) - (file-relative-name path) - path)) - - (defun +org-def-link (key dir) - (org-link-set-parameters - key - :complete (lambda () (+org--relpath (+org-link-read-file key dir) dir)) - :follow (lambda (link) (find-file (expand-file-name link dir))) - :face (lambda (link) - (if (file-exists-p (expand-file-name link dir)) - 'org-link - 'error)))) - ;; Highlight broken file links (org-link-set-parameters "file" @@ -476,7 +405,7 @@ file isn't in `org-directory'." (setq org-pandoc-options '((standalone . t) (mathjax . t) - (variable . "revealjs-url=https://cdn.jsdelivr.net/npm/reveal.js@3/"))))) + (variable . "revealjs-url=https://revealjs.com"))))) (defun +org-init-habit-h () @@ -522,17 +451,16 @@ conditions where a window's buffer hasn't changed at the time this hook is run." "Remove link syntax and fix variable height text (e.g. org headings) in the eldoc string." :around #'org-format-outline-path - (let ((result (funcall orig-fn path width prefix separator)) - (separator (or separator "/"))) - (string-join - (cl-loop for part - in (cdr (split-string (substring-no-properties result) separator)) - for n from 0 - for face = (nth (% n org-n-level-faces) org-level-faces) - collect - (org-add-props (replace-regexp-in-string org-link-any-re "\\4" part) - nil 'face `(:foreground ,(face-foreground face nil t) :weight bold))) - separator))) + (funcall orig-fn + (cl-loop for part in path + ;; Remove full link syntax + for fixedpart = (replace-regexp-in-string org-link-any-re "\\4" part) + for n from 0 + for face = (nth (% n org-n-level-faces) org-level-faces) + collect + (org-add-props fixedpart + nil 'face `(:foreground ,(face-foreground face nil t) :weight bold))) + width prefix separator)) (defun +org--restart-mode-h () "Restart `org-mode', but only once." @@ -546,10 +474,11 @@ eldoc string." "Prevent temporarily-opened agenda buffers from being associated with the current workspace (and clean them up)." (when (and org-agenda-new-buffers (bound-and-true-p persp-mode)) - (let (persp-autokill-buffer-on-remove) - (persp-remove-buffer org-agenda-new-buffers - (get-current-persp) - nil)) + (unless org-agenda-sticky + (let (persp-autokill-buffer-on-remove) + (persp-remove-buffer org-agenda-new-buffers + (get-current-persp) + nil))) (dolist (buffer org-agenda-new-buffers) (with-current-buffer buffer ;; HACK Org agenda opens temporary agenda incomplete org-mode @@ -595,8 +524,8 @@ between the two." org-insert-heading-respect-content t) (add-hook! 'org-tab-first-hook - #'+org-indent-maybe-h - #'+org-yas-expand-maybe-h) + #'+org-yas-expand-maybe-h + #'+org-indent-maybe-h) (add-hook 'doom-delete-backward-functions #'+org-delete-backward-char-and-realign-table-maybe-h) @@ -607,6 +536,11 @@ between the two." ;; textmate-esque newline insertion [C-return] #'+org/insert-item-below [C-S-return] #'+org/insert-item-above + [C-M-return] #'org-insert-subheading + (:when IS-MAC + [s-return] #'+org/insert-item-below + [s-S-return] #'+org/insert-item-above + [s-M-return] #'org-insert-subheading) ;; Org-aware C-a/C-e [remap doom/backward-to-bol-or-indent] #'org-beginning-of-line [remap doom/forward-to-last-non-comment-or-eol] #'org-end-of-line @@ -628,8 +562,6 @@ between the two." "h" #'org-toggle-heading "i" #'org-toggle-item "I" #'org-toggle-inline-images - "l" #'org-insert-link - "L" #'+org/remove-link "n" #'org-store-link "o" #'org-set-property "p" #'org-priority @@ -638,10 +570,22 @@ between the two." "t" #'org-todo "T" #'org-todo-list (:prefix ("a" . "attachments") - "a" #'+org-attach/file - "u" #'+org-attach/uri - "f" #'+org-attach/find-file - "s" #'+org-attach/sync) + "a" #'org-attach + "d" #'org-attach-delete-one + "D" #'org-attach-delete-all + "f" #'+org/find-file-in-attachments + "l" #'+org/attach-file-and-insert-link + "n" #'org-attach-new + "o" #'org-attach-open + "O" #'org-attach-open-in-emacs + "r" #'org-attach-reveal + "R" #'org-attach-reveal-in-emacs + "u" #'org-attach-url + "s" #'org-attach-set-directory + "S" #'org-attach-sync + (:when (featurep! +dragndrop) + "c" #'org-download-screenshot + "y" #'org-download-yank)) (:prefix ("b" . "tables") "-" #'org-table-insert-hline "a" #'org-table-align @@ -668,24 +612,36 @@ between the two." (:when (featurep! :completion ivy) "g" #'counsel-org-goto "G" #'counsel-org-goto-all) + (:when (featurep! :completion helm) + "g" #'helm-org-in-buffer-headings + "G" #'helm-org-agenda-files-headings) "c" #'org-clock-goto "C" (λ! (org-clock-goto 'select)) "i" #'org-id-goto "r" #'org-refile-goto-last-stored + "v" #'+org/goto-visible "x" #'org-capture-goto-last-stored) + (:prefix ("l" . "links") + "c" #'org-cliplink + "l" #'org-insert-link + "L" #'org-insert-all-links + "s" #'org-store-link + "S" #'org-insert-last-stored-link + "i" #'org-id-store-link + "d" #'+org/remove-link) (:prefix ("r" . "refile") "." #'+org/refile-to-current-file "c" #'+org/refile-to-running-clock "l" #'+org/refile-to-last-location + "f" #'+org/refile-to-file "o" #'+org/refile-to-other-window - "O" #'+org/refile-to-other-buffers + "O" #'+org/refile-to-other-buffer + "v" #'+org/refile-to-visible "r" #'org-refile)) ; to all `org-refile-targets' (map! :after org-agenda :map org-agenda-mode-map - ;; Always clean up after itself - [remap org-agenda-quit] #'org-agenda-exit - [remap org-agenda-Quit] #'org-agenda-exit + :m "C-SPC" #'org-agenda-show-and-scroll-up :localleader "d" #'org-agenda-deadline (:prefix ("c" . "clock") @@ -785,15 +741,14 @@ compelling reason, so..." :config (setq toc-org-hrefify-default "gh")) +(use-package! org-bookmark-heading ; add org heading support to bookmark.el + :after (:or bookmark org)) + + (use-package! org-bullets ; "prettier" bullets :hook (org-mode . org-bullets-mode)) -(use-package! org-fancy-priorities ; priority icons - :hook (org-mode . org-fancy-priorities-mode) - :config (setq org-fancy-priorities-list '("■" "■" "■"))) - - (use-package! org-crypt ; built-in :commands org-encrypt-entries :hook (org-reveal-start . org-decrypt-entry) @@ -844,8 +799,11 @@ compelling reason, so..." (defvar evil-org-use-additional-insert t) :config (evil-org-set-key-theme) - ;; Only fold the current tree, rather than recursively - (add-hook 'org-tab-first-hook #'+org-cycle-only-current-subtree-h 'append) + (add-hook! 'org-tab-first-hook :append + ;; Only fold the current tree, rather than recursively + #'+org-cycle-only-current-subtree-h + ;; Clear babel results if point is inside a src block + #'+org-clear-babel-results-h) (map! :map evil-org-mode-map :ni [C-return] #'+org/insert-item-below :ni [C-S-return] #'+org/insert-item-above @@ -921,10 +879,9 @@ compelling reason, so..." org-capture :preface ;; Change org defaults (should be set before org loads) - (defvar org-directory "~/org/") - (defvar org-attach-id-dir ".attach/") - - (setq org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/") + (setq org-directory "~/org/" + org-attach-id-dir ".attach/" + org-publish-timestamp-directory (concat doom-cache-dir "org-timestamps/") org-preview-latex-image-directory (concat doom-cache-dir "org-latex/")) (defvar org-modules @@ -955,12 +912,11 @@ compelling reason, so..." (add-hook! 'org-load-hook #'+org-init-appearance-h #'+org-init-agenda-h + #'+org-init-attachments-h #'+org-init-babel-h #'+org-init-babel-lazy-loader-h #'+org-init-capture-defaults-h #'+org-init-capture-frame-h - #'+org-init-centralized-attachments-h - #'+org-init-centralized-exports-h #'+org-init-custom-links-h #'+org-init-export-h #'+org-init-habit-h @@ -972,8 +928,11 @@ compelling reason, so..." #'+org-init-smartparens-h) ;;; Custom org modules + (if (featurep! +brain) (load! "contrib/brain")) (if (featurep! +dragndrop) (load! "contrib/dragndrop")) (if (featurep! +ipython) (load! "contrib/ipython")) + (if (featurep! +journal) (load! "contrib/journal")) + (if (featurep! +jupyter) (load! "contrib/jupyter")) (if (featurep! +pomodoro) (load! "contrib/pomodoro")) (if (featurep! +present) (load! "contrib/present")) @@ -985,4 +944,16 @@ compelling reason, so..." (run-hooks 'org-load-hook)) :config + ;; Global ID state means we can have ID links anywhere. This is required for + ;; `org-brain', however. + (setq org-id-track-globally t + org-id-locations-file (expand-file-name ".orgids" org-directory) + org-id-locations-file-relative t) + + ;; HACK `org-id' doesn't check if `org-id-locations-file' exists or is + ;; writeable before trying to read/write to it. + (defadvice! +org--fail-gracefully-a (&rest _) + :before-while '(org-id-locations-save org-id-locations-load) + (file-exists-p org-id-locations-file)) + (add-hook 'org-open-at-point-functions #'doom-set-jump-h)) diff --git a/modules/lang/org/contrib/brain.el b/modules/lang/org/contrib/brain.el new file mode 100644 index 000000000..4e0ed6365 --- /dev/null +++ b/modules/lang/org/contrib/brain.el @@ -0,0 +1,19 @@ +;;; lang/org/contrib/brain.el -*- lexical-binding: t; -*- +;;;###if (featurep! +brain) + +(use-package! org-brain + :defer t + :init + (setq org-brain-visualize-default-choices 'all + org-brain-title-max-length 24 + org-brain-include-file-entries nil + org-brain-file-entries-use-title nil) + + :config + (set-evil-initial-state! 'org-brain-visualize-mode 'emacs) + (set-popup-rule! "^\\*org-brain" :side 'right :size 1.00 :select t :ttl nil) + + (cl-pushnew '("b" "Brain" plain (function org-brain-goto-end) + "* %i%?" :empty-lines 1) + org-capture-templates + :key #'car :test #'equal)) diff --git a/modules/lang/org/contrib/dragndrop.el b/modules/lang/org/contrib/dragndrop.el index 4095e25a4..d1ce37f51 100644 --- a/modules/lang/org/contrib/dragndrop.el +++ b/modules/lang/org/contrib/dragndrop.el @@ -2,15 +2,27 @@ ;;;###if (featurep! +dragndrop) (use-package! org-download - :commands org-download-dnd org-download-dnd-base64 + :commands + org-download-dnd + org-download-yank + org-download-screenshot + org-download-dnd-base64 :init ;; HACK We add these manually so that org-download is truly lazy-loaded (pushnew! dnd-protocol-alist '("^\\(?:https?\\|ftp\\|file\\|nfs\\):" . +org-dragndrop-download-dnd-fn) '("^data:" . org-download-dnd-base64)) (advice-add #'org-download-enable :override #'ignore) + + (after! org + ;; A shorter link to attachments + (+org-def-link "download" org-attach-id-dir) + (setf (alist-get "download" org-link-abbrev-alist nil nil #'equal) + (abbreviate-file-name org-attach-id-dir))) :config - (setq org-download-image-dir org-attach-directory + (setq org-download-image-dir org-attach-id-dir + org-download-link-format "[[download:%s]]\n" + org-download-method 'attach org-download-heading-lvl nil org-download-timestamp "_%Y%m%d_%H%M%S" org-download-screenshot-method @@ -33,15 +45,17 @@ an file icon produced by `+org-attach-icon-for')." (newline)) (cond ((image-type-from-file-name filename) (insert - (concat (if (= org-download-image-html-width 0) "" - (format "#+attr_html: :width %dpx\n" org-download-image-html-width)) - (if (= org-download-image-latex-width 0) "" - (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width)) - (cond ((file-in-directory-p filename org-attach-directory) - (format "[[attach:%s]]" (file-relative-name filename org-attach-directory))) - ((file-in-directory-p filename org-directory) - (format org-download-link-format (file-relative-name filename org-directory))) - ((format org-download-link-format filename))))) + (concat + (if (= org-download-image-html-width 0) "" + (format "#+attr_html: :width %dpx\n" org-download-image-html-width)) + (if (= org-download-image-latex-width 0) "" + (format "#+attr_latex: :width %dcm\n" org-download-image-latex-width)) + (format org-download-link-format + (cond ((file-in-directory-p filename org-attach-directory) + (file-relative-name filename org-download-image-dir)) + ((file-in-directory-p filename org-directory) + (file-relative-name filename org-directory)) + (filename))))) (org-display-inline-images)) ((insert (format "%s [[./%s][%s]] " diff --git a/modules/lang/org/contrib/journal.el b/modules/lang/org/contrib/journal.el index 7324073cc..92eff58af 100644 --- a/modules/lang/org/contrib/journal.el +++ b/modules/lang/org/contrib/journal.el @@ -1,17 +1,41 @@ ;;; lang/org/contrib/journal.el -*- lexical-binding: t; -*- ;;;###if (featurep! +journal) +;; HACK org-journal does some file-path magic at load time that creates +;; duplicate `auto-mode-alist' entries, so we suppress it for now, so we +;; can do it properly later. +(advice-add #'org-journal-update-auto-mode-alist :override #'ignore) + (after! org-journal (setq org-journal-dir (expand-file-name "journal/" org-directory) - org-journal-file-pattern - (expand-file-name "\\(?1:[0-9]\\{4\\}\\)\\(?2:[0-9][0-9]\\)\\(?3:[0-9][0-9]\\)\\'" - org-journal-dir)) + org-journal-cache-file (concat doom-cache-dir "org-journal") + org-journal-file-pattern (org-journal-dir-and-format->regex + org-journal-dir org-journal-file-format)) + + (add-to-list 'auto-mode-alist (cons org-journal-file-pattern 'org-journal-mode)) + + (map! (:map org-journal-mode-map + :n "]f" #'org-journal-open-next-entry + :n "[f" #'org-journal-open-previous-entry + :n "C-n" #'org-journal-open-next-entry + :n "C-p" #'org-journal-open-previous-entry) + (:map org-journal-search-mode-map + "C-n" #'org-journal-search-next + "C-p" #'org-journal-search-previous) - (map! :map org-mode-map :localleader + (:map org-journal-mode-map + "c" #'org-journal-new-entry + "d" #'org-journal-new-date-entry + "n" #'org-journal-open-next-entry + "p" #'org-journal-open-previous-entry + (:prefix "s" + "s" #'org-journal-search + "f" #'org-journal-search-forever + "F" #'org-journal-search-future + "w" #'org-journal-search-calendar-week + "m" #'org-journal-search-calendar-month + "y" #'org-journal-search-calendar-year)) (:map org-journal-search-mode-map "n" #'org-journal-search-next - "p" #'org-journal-search-prev) - (:map org-journal-mode-map - "n" #'org-journal-open-next-entry - "p" #'org-journal-open-previous-entry)) + "p" #'org-journal-search-prev))) diff --git a/modules/lang/org/contrib/jupyter.el b/modules/lang/org/contrib/jupyter.el new file mode 100644 index 000000000..c47494be2 --- /dev/null +++ b/modules/lang/org/contrib/jupyter.el @@ -0,0 +1,27 @@ +;;; lang/org/contrib/jupyter.el -*- lexical-binding: t; -*- +;;;###if (featurep! +jupyter) + +(use-package! ob-jupyter + :defer t + :init + (after! ob-async + (pushnew! ob-async-no-async-languages-alist "jupyter-python" "jupyter-julia")) + + (after! org-src + (dolist (lang '(python julia R)) + (cl-pushnew (cons (format "jupyter-%s" lang) lang) + org-src-lang-modes :key #'car))) + + (add-hook! '+org-babel-load-functions + (defun +org-babel-load-jupyter-h (lang) + (when (string-prefix-p "jupyter-" (symbol-name lang)) + (require 'jupyter) + (let* ((lang-name (symbol-name lang)) + (lang-tail (string-remove-prefix "jupyter-" lang-name))) + (and (not (assoc lang-tail org-src-lang-modes)) + (require (intern (format "ob-%s" lang-tail)) + nil t) + (add-to-list 'org-src-lang-modes (cons lang-name (intern lang-tail))))) + (with-demoted-errors "Jupyter: %s" + (require lang nil t) + (require 'ob-jupyter nil t)))))) diff --git a/modules/lang/org/contrib/present.el b/modules/lang/org/contrib/present.el index 22f2d17fb..cf4b6a461 100644 --- a/modules/lang/org/contrib/present.el +++ b/modules/lang/org/contrib/present.el @@ -1,7 +1,7 @@ ;;; lang/org/contrib/present.el -*- lexical-binding: t; -*- ;;;###if (featurep! +present) -(defvar +org-present-text-scale 7 +(defvar +org-present-text-scale 6 "The `text-scale-amount' for `org-tree-slide-mode'.") (after! ox @@ -11,10 +11,15 @@ ;; ;;; Packages -(use-package! org-re-reveal +(use-package! ox-reveal :after ox + :init + ;; Fix #1127, where ox-reveal adds an errant entry to + ;; `org-structure-template-alist' + (setq org-reveal-note-key-char nil) :config - (setq org-re-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js@3/")) + (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js@3/" + org-reveal-mathjax t)) (use-package! org-tree-slide @@ -24,31 +29,37 @@ (setq org-tree-slide-skip-outline-level 2 org-tree-slide-activate-message " " org-tree-slide-deactivate-message " " - org-tree-slide-modeline-display nil) + org-tree-slide-modeline-display nil + org-tree-slide-heading-emphasis t) - (map! :map org-tree-slide-mode-map - :n [right] #'org-tree-slide-move-next-tree - :n [left] #'org-tree-slide-move-previous-tree) + (add-hook 'org-tree-slide-mode-after-narrow-hook #'org-display-inline-images) + (add-hook! 'org-tree-slide-mode-hook + #'+org-present-hide-blocks-h + #'+org-present-prettify-slide-h) - (add-hook! 'org-tree-slide-mode-after-narrow-hook - #'+org-present-detect-slide-h - #'+org-present-add-overlays-h - #'org-display-inline-images) - - (add-hook 'org-tree-slide-mode-hook #'+org-present-init-org-tree-window-h) + (when (featurep! :editor evil) + (map! :map org-tree-slide-mode-map + :n [C-right] #'org-tree-slide-move-next-tree + :n [C-left] #'org-tree-slide-move-previous-tree) + (add-hook 'org-tree-slide-mode-hook #'evil-normalize-keymaps)) (defadvice! +org-present--narrow-to-subtree-a (orig-fn &rest args) "Narrow to the target subtree when you start the presentation." :around #'org-tree-slide--display-tree-with-narrow (cl-letf (((symbol-function #'org-narrow-to-subtree) - (lambda () (save-excursion - (save-match-data - (org-with-limited-levels - (narrow-to-region - (progn (org-back-to-heading t) - (forward-line 1) - (point)) - (progn (org-end-of-subtree t t) - (when (and (org-at-heading-p) (not (eobp))) (backward-char 1)) - (point))))))))) + (lambda () + (save-excursion + (save-match-data + (org-with-limited-levels + (narrow-to-region + (progn + (when (org-before-first-heading-p) + (org-next-visible-heading 1)) + (ignore-errors (org-up-heading-all 99)) + (forward-line 1) + (point)) + (progn (org-end-of-subtree t t) + (when (and (org-at-heading-p) (not (eobp))) + (backward-char 1)) + (point))))))))) (apply orig-fn args)))) diff --git a/modules/lang/org/packages.el b/modules/lang/org/packages.el index 5f4a7f17e..bc0244e85 100644 --- a/modules/lang/org/packages.el +++ b/modules/lang/org/packages.el @@ -13,67 +13,93 @@ ;; call in the repo's root. Of course, this command won't work in a sparse ;; clone, and more than that, initiating these compilation step is a ;; hassle, so... -(setq straight-fix-org nil) (add-hook! 'straight-use-package-pre-build-functions (defun +org-fix-package-h (package &rest _) - (when (member package '("org" "org-plus-contrib")) - (with-temp-file (expand-file-name "org-version.el" (straight--repos-dir "org")) - (insert "(fset 'org-release (lambda () \"9.3\"))\n" + (when (equal package "org-mode") + (with-temp-file (expand-file-name "org-version.el" (straight--repos-dir "org-mode")) + (insert "(fset 'org-release (lambda () \"9.4\"))\n" "(fset 'org-git-version #'ignore)\n" "(provide 'org-version)\n"))))) -(package! org-plus-contrib) ; install cutting-edge version of org-mode +;; Install cutting-edge version of org-mode, and from a mirror, because +;; code.orgmode.org runs on a potato. +(package! org-mode + :recipe (:host github + :repo "emacs-straight/org-mode" + :files ("*.el" "lisp/*.el" "contrib/lisp/*.el")) + :pin "30d0b6e1f6") +;; ...And prevent other packages from pulling org; org-plus-contrib satisfies +;; the dependency already: https://github.com/raxod502/straight.el/issues/352 +(package! org :recipe (:local-repo nil)) -(package! htmlize) -(package! org-bullets :recipe (:host github :repo "Kaligule/org-bullets")) -(package! org-fancy-priorities) -(package! org-yt :recipe (:host github :repo "TobiasZawada/org-yt")) -(package! ox-clip) -(package! toc-org) +(package! avy) +(package! htmlize :pin "86f22f211e") +(package! org-bullets + :recipe (:host github :repo "Kaligule/org-bullets") + :pin "8b4f0aab6d") +(package! org-yt + :recipe (:host github :repo "TobiasZawada/org-yt") + :pin "40cc1ac76d") +(package! ox-clip :pin "bd36f9fb4e") +(package! toc-org :pin "379b457fcf") +(package! org-cliplink :pin "82402cae7e") +(package! org-bookmark-heading :pin "38a2813f72") (when (featurep! :editor evil +everywhere) - (package! evil-org :recipe (:host github :repo "hlissner/evil-org-mode"))) + (package! evil-org + :recipe (:host github :repo "hlissner/evil-org-mode") + :pin "4d44e9bbdc")) (when (featurep! :tools pdf) - (package! org-pdfview)) + (package! org-pdfview :pin "8b71f31363")) (when (featurep! :tools magit) - (package! orgit)) + (package! orgit :pin "e7cddf39e3")) +(when (featurep! +brain) + (package! org-brain :pin "a0625854df")) (when (featurep! +dragndrop) - (package! org-download)) + (package! org-download :pin "3c48102793")) (when (featurep! +gnuplot) - (package! gnuplot) - (package! gnuplot-mode)) -(when (featurep! +ipython) - (package! ob-ipython)) + (package! gnuplot :pin "a406143d52") + (package! gnuplot-mode :pin "601f639298")) +(when (featurep! +ipython) ; DEPRECATED + (package! ob-ipython :pin "7147455230")) +(when (featurep! +jupyter) + (package! jupyter :pin "9e3c163358")) (when (featurep! +pomodoro) - (package! org-pomodoro)) + (package! org-pomodoro :pin "aa07c11318")) (when (featurep! +present) (package! centered-window - :recipe (:host github :repo "anler/centered-window-mode")) - (package! org-tree-slide) - (package! org-re-reveal)) + :recipe (:host github :repo "anler/centered-window-mode") + :pin "24f7c5be9d") + (package! org-tree-slide :pin "7bf09a02bd") + (package! ox-reveal :pin "aafedfd805")) (when (featurep! +journal) - (package! org-journal)) + (package! org-journal :pin "dc120bf42a")) ;;; Babel -(package! ob-async) +(package! ob-async :pin "80a30b96a0") (when (featurep! :lang crystal) - (package! ob-crystal)) + (package! ob-crystal :pin "d84c1adee4")) (when (featurep! :lang go) - (package! ob-go)) + (package! ob-go :pin "2067ed55f4")) (when (featurep! :lang nim) - (package! ob-nim)) + (package! ob-nim :pin "bf1642cb93")) (when (featurep! :lang racket) - (package! ob-racket :recipe (:host github :repo "DEADB17/ob-racket"))) + (package! ob-racket + :recipe (:host github :repo "DEADB17/ob-racket") + :pin "d8fd51bddb")) (when (featurep! :lang rest) - (package! ob-restclient)) + (package! ob-restclient :pin "c5c22e6035")) (when (featurep! :lang rust) - (package! ob-rust)) + (package! ob-rust :pin "6a82587598")) +(when (featurep! :lang scala) + (package! ob-ammonite :pin "39937dff39")) ;;; Export (when (featurep! +pandoc) - (package! ox-pandoc)) + (package! ox-pandoc :pin "aa37dc7e94")) (when (featurep! +hugo) (package! ox-hugo - :recipe (:host github :repo "kaushalmodi/ox-hugo" :nonrecursive t))) + :recipe (:host github :repo "kaushalmodi/ox-hugo" :nonrecursive t) + :pin "16f1b0c9a9")) (when (featurep! :lang rst) - (package! ox-rst)) + (package! ox-rst :pin "9158bfd180")) diff --git a/modules/lang/perl/config.el b/modules/lang/perl/config.el index 1e433ee1e..9301646b7 100644 --- a/modules/lang/perl/config.el +++ b/modules/lang/perl/config.el @@ -6,5 +6,5 @@ (use-package! flycheck-perl6 - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after perl6-mode) diff --git a/modules/lang/perl/packages.el b/modules/lang/perl/packages.el index e00de4f19..7c7adfd1d 100644 --- a/modules/lang/perl/packages.el +++ b/modules/lang/perl/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/perl/packages.el -(package! perl6-mode) +(package! perl6-mode :pin "88de065795") -(when (featurep! :tools flycheck) - (package! flycheck-perl6)) +(when (featurep! :checkers syntax) + (package! flycheck-perl6 :pin "b804702305")) diff --git a/modules/lang/php/packages.el b/modules/lang/php/packages.el index f2fa57243..296230f7f 100644 --- a/modules/lang/php/packages.el +++ b/modules/lang/php/packages.el @@ -1,19 +1,22 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/php/packages.el -(package! php-boris) -(package! php-extras :recipe (:host github :repo "arnested/php-extras")) -(package! php-mode) -(package! php-refactor-mode) -(package! phpunit) +(package! php-boris :pin "f2faebf610") +(package! php-extras :recipe (:host github :repo "arnested/php-extras") :pin "d410c5af66") +(package! php-mode :pin "cade4cef2b") +(package! php-refactor-mode :pin "7a794b0618") +(package! phpunit :pin "fe6bc91c3b") (when (featurep! +hack) - (package! hack-mode :recipe (:host github :repo "hhvm/hack-mode"))) + (package! hack-mode :recipe (:host github :repo "hhvm/hack-mode") :pin "fd6a661b09")) (unless (featurep! +lsp) - (package! phpactor) + (package! phpactor :pin "5ccf65d59e") (when (featurep! :completion company) - (package! company-phpactor))) + (package! company-phpactor :pin "5ccf65d59e"))) (when (featurep! :editor format) - (package! php-cs-fixer)) + (package! php-cs-fixer :pin "6540006710")) + +;; For building php-extras +(package! async :pin "86aef2c38e") diff --git a/modules/lang/plantuml/config.el b/modules/lang/plantuml/config.el index 4c7d1e749..45a18a685 100644 --- a/modules/lang/plantuml/config.el +++ b/modules/lang/plantuml/config.el @@ -15,7 +15,7 @@ (use-package! flycheck-plantuml - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after plantuml-mode :config (flycheck-plantuml-setup)) diff --git a/modules/lang/plantuml/doctor.el b/modules/lang/plantuml/doctor.el index 3d3174e25..4e07331ea 100644 --- a/modules/lang/plantuml/doctor.el +++ b/modules/lang/plantuml/doctor.el @@ -7,4 +7,4 @@ (warn! "Couldn't find java. PlantUML preview or syntax checking won't work")) ;; plantuml.jar (unless (file-exists-p plantuml-jar-path) - (warn! "Couldn't find plantuml.jar. Install it with-x +plantuml/install"))) + (warn! "Couldn't find plantuml.jar. Install it with M-x plantuml-download-jar"))) diff --git a/modules/lang/plantuml/packages.el b/modules/lang/plantuml/packages.el index 0335841a7..129f2b550 100644 --- a/modules/lang/plantuml/packages.el +++ b/modules/lang/plantuml/packages.el @@ -1,6 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/plantuml/packages.el -(package! plantuml-mode) -(when (featurep! :tools flycheck) - (package! flycheck-plantuml)) +(package! plantuml-mode :pin "ea45a13707") +(when (featurep! :checkers syntax) + (package! flycheck-plantuml :pin "183be89e1d")) + +;; ob-plantuml is provided by org-plus-contrib diff --git a/modules/lang/purescript/packages.el b/modules/lang/purescript/packages.el index cab6c3433..e5726e03c 100644 --- a/modules/lang/purescript/packages.el +++ b/modules/lang/purescript/packages.el @@ -5,8 +5,7 @@ ;; due to expecting the compiler to be psc rather than purs. However, one of ;; purescript-mode or psc-ide seems to handle flycheck, so it might be ;; unnecessary altogether. -;;(package! flycheck-purescript) - -(package! psc-ide) -(package! purescript-mode) +;;(package! flycheck-purescript :pin "30f0435d5e") +(package! psc-ide :pin "2a9394422d") +(package! purescript-mode :pin "8db1d0243c") diff --git a/modules/lang/python/README.org b/modules/lang/python/README.org index 61becaa11..b64c7c6ec 100644 --- a/modules/lang/python/README.org +++ b/modules/lang/python/README.org @@ -7,7 +7,6 @@ - [[#description][Description]] - [[#module-flags][Module Flags]] - [[#plugins][Plugins]] - - [[#hacks][Hacks]] - [[#prerequisites][Prerequisites]] - [[#language-server-protocol-support][Language Server Protocol Support]] - [[#features][Features]] @@ -31,7 +30,7 @@ Adds Python support to Doom Emacs. ** Plugins + [[https://github.com/pythonic-emacs/anaconda-mode][anaconda-mode]]* + [[https://github.com/Wilfred/pyimport][pyimport]]* -+ [[https://github.com/emacs-pe/pyimpsort.el][pyimpsort]]* ++ [[https://github.com/paetzke/py-isort.el][py-isort]]* + [[https://melpa.org/#/nose][nose]]* + [[https://github.com/wbolster/emacs-python-pytest][python-pytest]]* + [[https://github.com/Wilfred/pip-requirements.el][pip-requirements]]* @@ -47,12 +46,7 @@ Adds Python support to Doom Emacs. + [[https://github.com/emacs-lsp/lsp-python-ms][lsp-python-ms]]* + ~+cython~ + [[https://github.com/cython/cython/blob/master/Tools/cython-mode.el][Cython-mode]] - + ~:tools flycheck~ [[https://github.com/lbolla/emacs-flycheck-cython/tree/master][Flycheck-cython]] - -** Hacks -+ As per our "your system your rules" mantra, lsp-python-ms has been modified to - *not* automatically install its server if it cannot find it. Install it with - ~M-x lsp-python-ms-setup~. + + ~:checkers syntax~ [[https://github.com/lbolla/emacs-flycheck-cython/tree/master][Flycheck-cython]] * Prerequisites This module has no direct prerequisites. Here are some of its soft dependencies. @@ -67,6 +61,9 @@ This module has no direct prerequisites. Here are some of its soft dependencies. + ~pyimport~ requires Python's module ~pyflakes~: + ~pip install pyflakes~ ++ ~py-isort~ requires [[https://github.com/timothycrosley/isort][isort]] to be installed: + + ~pip install isort~ + + Python virtual environments install instructions at: + [[https://github.com/pyenv/pyenv][pyenv]] + [[https://conda.io/en/latest/][Conda]] @@ -76,20 +73,18 @@ This module has no direct prerequisites. Here are some of its soft dependencies. + ~cython~ requires [[https://cython.org/][Cython]] ** Language Server Protocol Support -Requires the ~+lsp~ flag and ~:tools lsp~ module to be enabled. -By default LSP will use Microsoft's language server if installed. +This module must be enabled with the =+lsp= flag, and the =:tools lsp= module +must be enabled. LSP will try mspyls, then pyls; whichever is available. -To install the language server: -~M-x lsp-python-ms-setup~ -To update the server: -~M-x lsp-python-ms-update-server~ +mypyls can be installed with ~M-x lsp-install-server~ after opening a python +buffer. -Alternatively you can use the [[https://pypi.org/project/python-language-server/][Python Language Server]] instead. -~pip install 'python-language-server[all]'~ +Alternatively, use the [[https://pypi.org/project/python-language-server/][Python Language Server]] instead. ~pip install +'python-language-server[all]'~ * Features This module supports LSP. It requires installation of [[https://pypi.org/project/python-language-server/][Python Language -Server]] or [[https://github.com/Microsoft/python-language-server][Microsoft Lnaguaje Server]], see [[Language Server Protocol Support][LSP Support]]. +Server]] or [[https://github.com/Microsoft/python-language-server][Microsoft Language Server]], see [[Language Server Protocol Support][LSP Support]]. To enable support for auto-formatting with black enable ~:editor format-all~ in ~init.el~ file. @@ -116,11 +111,11 @@ To enable support for auto-formatting with black enable ~:editor format-all~ in | ~ t m~ | ~python-pytest-function-dwim~ | | ~ t r~ | ~python-pytest-repeat~ | | ~ t p~ | ~python-pytest-popup~ | -| ~ f d~ | ~anaconda-mode-find-definitions~ | -| ~ f h~ | ~anaconda-mode-show-doc~ | -| ~ f a~ | ~anaconda-mode-find-assignments~ | -| ~ f f~ | ~anaconda-mode-find-file~ | -| ~ f u~ | ~anaconda-mode-find-references~ | +| ~ g d~ | ~anaconda-mode-find-definitions~ | +| ~ g h~ | ~anaconda-mode-show-doc~ | +| ~ g a~ | ~anaconda-mode-find-assignments~ | +| ~ g f~ | ~anaconda-mode-find-file~ | +| ~ g u~ | ~anaconda-mode-find-references~ | * Configuration This module has the following variables to set extra arguments to [[https://ipython.org/][ipython]] and diff --git a/modules/lang/python/autoload/python.el b/modules/lang/python/autoload/python.el index b799441a0..4f6ef7f3b 100644 --- a/modules/lang/python/autoload/python.el +++ b/modules/lang/python/autoload/python.el @@ -16,8 +16,8 @@ python-shell-interpreter python-shell-interpreter-args)) (python-shell-interpreter pipenv)) - (run-python nil t t)) - (run-python nil t t))))) + (run-python nil nil t)) + (run-python nil nil t))))) ;;;###autoload (defun +python/open-ipython-repl () @@ -59,4 +59,4 @@ "organize imports" (interactive) (pyimport-remove-unused) - (pyimpsort-buffer)) + (py-isort-buffer)) diff --git a/modules/lang/python/config.el b/modules/lang/python/config.el index 66450c698..63a833029 100644 --- a/modules/lang/python/config.el +++ b/modules/lang/python/config.el @@ -93,14 +93,15 @@ called.") (setq anaconda-mode-installation-directory (concat doom-etc-dir "anaconda/") anaconda-mode-eldoc-as-single-line t) - (add-hook! 'python-mode-local-vars-hook + (add-hook! 'python-mode-local-vars-hook :append (defun +python-init-anaconda-mode-maybe-h () - "Enable `anaconda-mode' if `lsp-mode' isn't." + "Enable `anaconda-mode' if `lsp-mode' is absent and +`python-shell-interpreter' is present." (unless (or (bound-and-true-p lsp-mode) - (bound-and-true-p lsp--buffer-deferred)) + (bound-and-true-p lsp--buffer-deferred) + (not (executable-find python-shell-interpreter))) (anaconda-mode +1)))) :config - (add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode) (set-company-backend! 'anaconda-mode '(company-anaconda)) (set-lookup-handlers! 'anaconda-mode :definition #'anaconda-mode-find-definitions @@ -108,6 +109,8 @@ called.") :documentation #'anaconda-mode-show-doc) (set-popup-rule! "^\\*anaconda-mode" :select nil) + (add-hook 'anaconda-mode-hook #'anaconda-eldoc-mode) + (defun +python-auto-kill-anaconda-processes-h () "Kill anaconda processes if this buffer is the last python buffer." (when (and (eq major-mode 'python-mode) @@ -139,10 +142,19 @@ called.") (:prefix ("i" . "imports") :desc "Insert missing imports" "i" #'pyimport-insert-missing :desc "Remove unused imports" "r" #'pyimport-remove-unused - :desc "Sort imports" "s" #'pyimpsort-buffer :desc "Optimize imports" "o" #'+python/optimize-imports))) +(use-package! py-isort + :defer t + :init + (map! :after python + :map python-mode-map + :localleader + (:prefix ("i" . "imports") + :desc "Sort imports" "s" #'py-isort-buffer + :desc "Sort region" "r" #'py-isort-region))) + (use-package! nose :commands nose-mode :preface (defvar nose-mode-map (make-sparse-keymap)) @@ -270,21 +282,19 @@ called.") (use-package! lsp-python-ms :when (featurep! +lsp) - :after (python lsp-clients) - :init - (setq lsp-python-ms-dir (concat doom-etc-dir "mspyls/")) - + :after lsp-clients + :preface (after! python (setq lsp-python-ms-python-executable-cmd python-shell-interpreter)) - - ;; HACK lsp-python-ms shouldn't install itself if it isn't present. This - ;; circumvents LSP falling back to pyls when lsp-python-ms is absent. - ;; Installing the server should be a deliberate act; either 'M-x - ;; lsp-python-ms-setup' or setting `lsp-python-ms-executable' to an existing - ;; install will do. - (defadvice! +python--dont-auto-install-server-a () - :override #'lsp-python-ms--command-string - lsp-python-ms-executable)) + :init + ;; HACK If you don't have python installed, then opening python buffers with + ;; this on causes a "wrong number of arguments: nil 0" error, because of + ;; careless usage of `cl-destructuring-bind'. This silences that error, + ;; since we may still want to write some python on a system without + ;; python installed! + (defadvice! +python--silence-errors-a (orig-fn &rest args) + :around #'lsp-python-ms--extra-init-params + (ignore-errors (apply orig-fn args)))) (use-package! cython-mode @@ -300,5 +310,5 @@ called.") (use-package! flycheck-cython :when (featurep! +cython) - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after cython-mode) diff --git a/modules/lang/python/doctor.el b/modules/lang/python/doctor.el index 2fcb5d7fb..e4979e5ea 100644 --- a/modules/lang/python/doctor.el +++ b/modules/lang/python/doctor.el @@ -5,7 +5,7 @@ "This module requires (:tools lsp)") (if (not (executable-find "python")) - (warn! "Python isn't installed.") + (error! "Python isn't installed.") (unless (featurep! +lsp) (unless (zerop (shell-command "python -c 'import setuptools'")) (warn! "setuptools wasn't detected, which anaconda-mode requires")))) @@ -20,6 +20,28 @@ (unless (executable-find "conda") (warn! "Couldn't find conda in your PATH"))) +(when (featurep! +cython) + (unless (executable-find "cython") + (warn! "Couldn't find cython. cython-mode will not work."))) + (when (featurep! +ipython) (unless (executable-find "ipython") (warn! "Couldn't find ipython in your PATH"))) + +(unless (executable-find "pytest") + (warn! "Couldn't find pytest. Running tests through pytest will not work.")) + +(unless (executable-find "nosetests") + (warn! "Couldn't find nosetests. Running tests through nose will not work.")) + +(unless (executable-find "pipenv") + (warn! "Couldn't find pipenv. pipenv support will not work.")) + +(unless (executable-find "isort") + (warn! "Couldn't find isort. Import sorting will not work.")) + +(when (featurep! :editor format) + (unless (executable-find "pyflakes") + (warn! "Couldn't find pyflakes. Import management will not work.")) + (unless (executable-find "black") + (warn! "Couldn't find black. Code formatting will not work."))) diff --git a/modules/lang/python/packages.el b/modules/lang/python/packages.el index 600af8529..fe372a51b 100644 --- a/modules/lang/python/packages.el +++ b/modules/lang/python/packages.el @@ -2,33 +2,33 @@ ;;; lang/python/packages.el ;; Major modes -(package! pip-requirements) +(package! pip-requirements :pin "216cd1690f") (when (featurep! +cython) - (package! cython-mode) - (when (featurep! :tools flycheck) - (package! flycheck-cython))) + (package! cython-mode :pin "1bc86b5750") + (when (featurep! :checkers syntax) + (package! flycheck-cython :pin "ecc4454d35"))) ;; LSP (when (featurep! +lsp) - (package! lsp-python-ms)) + (package! lsp-python-ms :pin "83ef84d9a4")) ;; Programming environment -(package! anaconda-mode) +(package! anaconda-mode :pin "1bc301b2d2") (when (featurep! :completion company) - (package! company-anaconda)) + (package! company-anaconda :pin "398fad1916")) ;; Environment management -(package! pipenv) -(package! pyvenv) +(package! pipenv :pin "b730bb509e") +(package! pyvenv :pin "861998b6d1") (when (featurep! +pyenv) - (package! pyenv-mode)) + (package! pyenv-mode :pin "aec6f2aa28")) (when (featurep! +conda) - (package! conda)) + (package! conda :pin "814439dffa")) ;; Testing frameworks -(package! nose) -(package! python-pytest) +(package! nose :pin "f852829751") +(package! python-pytest :pin "09ad688df2") ;; Import managements -(package! pyimport) -(package! pyimpsort) +(package! pyimport :pin "a6f63cf7ed") +(package! py-isort :pin "e67306f459") diff --git a/modules/lang/qt/packages.el b/modules/lang/qt/packages.el index a41b28abb..07f3922e7 100644 --- a/modules/lang/qt/packages.el +++ b/modules/lang/qt/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/qt/packages.el -(package! qml-mode) -(package! qt-pro-mode) +(package! qml-mode :pin "6c5f33ba88") +(package! qt-pro-mode :pin "7a2da323de") diff --git a/modules/lang/racket/README.org b/modules/lang/racket/README.org new file mode 100644 index 000000000..b1698c92e --- /dev/null +++ b/modules/lang/racket/README.org @@ -0,0 +1,45 @@ +#+TITLE: lang/racket +#+DATE: July 29, 2018 +#+SINCE: v2.0.9 +#+STARTUP: inlineimages nofold + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#maintainers][Maintainers]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] + - [[#racket-smart-open-bracket-mode][racket-smart-open-bracket-mode]] +- [[#troubleshooting][Troubleshooting]] + +* Description +This module provide integration for [[https://github.com/greghendershott/racket-mode][racket-mode]]. + +** Maintainers +This module has no dedicated maintainers. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/greghendershott/racket-mode][racket-mode]] + +* Prerequisites +This module only require `racket`. Install it directly from the [[https://download.racket-lang.org/][racket website]], +or check your package manger. + +* TODO Features + +* Configuration +** racket-smart-open-bracket-mode +~racket-smart-open-bracket-mode~ gets turned off automatically if you use ~parinfer~, +~lispy~. If you wish to enable it, add the following to your ~config.el~: +#+BEGIN_SRC elisp +(after! racket-mode + (add-hook! racket-mode + #'racket-smart-open-bracket-mode)) +#+END_SRC + +* TODO Troubleshooting diff --git a/modules/lang/racket/config.el b/modules/lang/racket/config.el index 894eb3b39..c54fb8997 100644 --- a/modules/lang/racket/config.el +++ b/modules/lang/racket/config.el @@ -24,8 +24,11 @@ (add-hook! 'racket-mode-hook #'rainbow-delimiters-mode - #'highlight-quoted-mode - #'racket-smart-open-bracket-mode) + #'highlight-quoted-mode) + + (unless (or (featurep! :editor parinfer) + (featurep! :editor lispy)) + (add-hook! 'racket-mode-hook #'racket-smart-open-bracket-mode)) (map! :localleader :map racket-mode-map diff --git a/modules/lang/racket/packages.el b/modules/lang/racket/packages.el index bfab32f65..cc3f3331c 100644 --- a/modules/lang/racket/packages.el +++ b/modules/lang/racket/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/racket/packages.el -(package! racket-mode) +(package! racket-mode :pin "5f396fa91f") diff --git a/modules/lang/rest/packages.el b/modules/lang/rest/packages.el index edd0b8fba..a6f76fd98 100644 --- a/modules/lang/rest/packages.el +++ b/modules/lang/rest/packages.el @@ -1,7 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/rest/packages.el -(package! restclient) +(package! restclient :pin "e8ca809ace") (when (featurep! :completion company) - (package! company-restclient)) - + (package! company-restclient :pin "e5a3ec54ed")) diff --git a/modules/lang/rst/packages.el b/modules/lang/rst/packages.el index b4f32ff0b..3f77f2d0f 100644 --- a/modules/lang/rst/packages.el +++ b/modules/lang/rst/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/rst/packages.el -(package! sphinx-mode) +(package! sphinx-mode :pin "b5ac514e21") diff --git a/modules/lang/ruby/config.el b/modules/lang/ruby/config.el index 26f7e326a..2ae25cc02 100644 --- a/modules/lang/ruby/config.el +++ b/modules/lang/ruby/config.el @@ -7,13 +7,18 @@ ;; ;;; Packages +(after! ruby-mode + (setq ruby-insert-encoding-magic-comment nil) + (when (require 'enh-ruby-mode nil t) + (rassq-delete-all 'ruby-mode interpreter-mode-alist))) + + (use-package! enh-ruby-mode :mode ("\\.\\(?:pry\\|irb\\)rc\\'" . +ruby-init-h) - :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|jbuilder\\|thor\\)\\'" . +ruby-init-h) - :mode ("/\\(?:Berks\\|Cap\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby-init-h) + :mode ("\\.\\(?:rb\\|rake\\|rabl\\|ru\\|builder\\|gemspec\\|podspec\\|jbuilder\\|thor\\)\\'" . +ruby-init-h) + :mode ("/\\(?:Berks\\|Brew\\|Cap\\|Fast\\|Gem\\|Guard\\|Pod\\|Puppet\\|Rake\\|Thor\\|Vagrant\\)file\\'" . +ruby-init-h) + :interpreter ("j?ruby\\([0-9.]+\\)" . +ruby-init-h) :preface - (after! ruby-mode - (require 'enh-ruby-mode)) (defun +ruby-init-h () "Enable `enh-ruby-mode' if ruby is available, otherwise `ruby-mode'." (if (executable-find "ruby") @@ -27,12 +32,12 @@ (add-hook 'enh-ruby-mode-local-vars-hook #'lsp!)) (after! company-dabbrev-code - (add-to-list 'company-dabbrev-code-modes 'enh-ruby-mode nil #'eq) - (add-to-list 'company-dabbrev-code-modes 'ruby-mode nil #'eq)) + (add-to-list 'company-dabbrev-code-modes 'enh-ruby-mode) + (add-to-list 'company-dabbrev-code-modes 'ruby-mode)) (after! inf-ruby ;; switch to inf-ruby from compile if we detect a breakpoint has been hit - (add-hook 'compilation-filter-hook 'inf-ruby-auto-enter)) + (add-hook 'compilation-filter-hook #'inf-ruby-auto-enter)) ;; so class and module pairs work (setq-hook! (ruby-mode enh-ruby-mode) sp-max-pair-length 6)) diff --git a/modules/lang/ruby/doctor.el b/modules/lang/ruby/doctor.el index fa495022e..26ff01d61 100644 --- a/modules/lang/ruby/doctor.el +++ b/modules/lang/ruby/doctor.el @@ -7,6 +7,6 @@ (unless (executable-find "ruby") (warn! "Ruby isn't installed.")) -(when (executable-find "rbenv") +(when (and (executable-find "rbenv") (featurep! +rbenv)) (unless (split-string (shell-command-to-string "rbenv versions --bare") "\n" t) (warn! "No versions of ruby are available via rbenv, did you forget to install one?"))) diff --git a/modules/lang/ruby/packages.el b/modules/lang/ruby/packages.el index d7708caef..84af6f149 100644 --- a/modules/lang/ruby/packages.el +++ b/modules/lang/ruby/packages.el @@ -2,28 +2,28 @@ ;;; lang/ruby/packages.el ;; Major modes -(package! enh-ruby-mode) -(package! yard-mode) +(package! enh-ruby-mode :pin "732331b99a") +(package! yard-mode :pin "ba74a47463") ;; REPL -(package! inf-ruby) +(package! inf-ruby :pin "fd8d392fef") (when (featurep! :completion company) - (package! company-inf-ruby)) + (package! company-inf-ruby :pin "fe3e4863bc")) ;; Programming environment -(package! rubocop) -(package! robe) +(package! rubocop :pin "03bf15558a") +(package! robe :pin "8190cb7c7b") ;; Project tools -(package! bundler) -(package! rake) +(package! bundler :pin "43efb6be4e") +(package! rake :pin "9c204334b0") ;; Environment management (when (featurep! +rbenv) - (package! rbenv)) + (package! rbenv :pin "2ea1a5bdc1")) (when (featurep! +rvm) - (package! rvm)) + (package! rvm :pin "134497bc46")) ;; Testing frameworks -(package! rspec-mode) -(package! minitest) +(package! rspec-mode :pin "c4353a1bff") +(package! minitest :pin "6d9f6233b7") diff --git a/modules/lang/rust/README.org b/modules/lang/rust/README.org index 2a93bd352..4b31c8c9a 100644 --- a/modules/lang/rust/README.org +++ b/modules/lang/rust/README.org @@ -1,6 +1,6 @@ #+TITLE: lang/rust #+DATE: June 5, 2019 -#+SINCE: {replace with next tagged release version} +#+SINCE: v3.0.0 #+STARTUP: inlineimages * Table of Contents :TOC_3:noexport: @@ -10,76 +10,86 @@ - [[#hacks][Hacks]] - [[#prerequisites][Prerequisites]] - [[#features][Features]] + - [[#keybinds][Keybinds]] - [[#configuration][Configuration]] + - [[#enable-rust-analyzer][Enable rust-analyzer]] + - [[#enabling-elgot-support-for-rust][Enabling elgot support for Rust]] - [[#troubleshooting][Troubleshooting]] - - [[#racer-installation-problems][Racer installation problems]] * Description -Add support to Rust language and cargo commands inside emacs. +This module adds support for the Rust language and integration for its tools, +e.g. ~cargo~. + Code completion (~racer~) + Syntax checking (~flycheck~) ++ LSP support (for rls and rust-analyzer) (~rustic~) + Snippets ** Module Flags + ~+lsp~ to add support Language server protocol. ** Plugins -+ [[https://github.com/rust-lang/rust-mode][Rust-mode]] -+ [[https://github.com/racer-rust/emacs-racer][Racer-mode]] -+ [[https://github.com/racer-rust/emacs-racer][Cargo-mode]] ++ [[https://github.com/brotzeit/rustic][rustic]] ++ [[https://github.com/racer-rust/emacs-racer][racer]]* (unless =+lsp=) ** Hacks -{A list of internal modifications to included packages} ++ rustic has been modified /not/ to automatically install lsp-mode or elgot if + they're missing. Doom expects you to enable the =:tools lsp= module yourself. ++ rustic's LSP integration has been disabled in favor of the rls/rust-analyzer + support provider by the lsp-mode package. * Prerequisites -To get started with Rust, you can either use =rustup= and install rust with: +This module only requires ~rust~, which can be acquired through =rustup=: ~curl https://sh.rustup.rs -sSf | sh~ -Package manager is not recommended to install Nightly version of Rust what is -required for ~racer~ from [[https://github.com/racer-rust/racer#installation][version 2.1]] (more info in [[#Troubleshooting][Troubleshooting]]) +Optionally, this module also uses the following programs: -Some features have additional dependencies: - -+ The ~:editor format~ module uses ~rustfmt~ for rust files: ~rustup component - add rustfmt-preview~ ++ =racer= (if not using LSP): ~cargo +nightly install racer~ (requires rust nightly) ++ =RLS= or =rust-analyzer= (for LSP users) ++ =rustfmt= for ~:editor format~: ~rustup component add rustfmt-preview~ + The following commands require: + ~cargo-process-check~: ~cargo install cargo-check~ + ~cargo-process-clippy~: ~rustup component add clippy-preview~ - + ~cargo-process-{add,rm,upgrade}~: ~cargo install cargo-edit~ * Features -This module also supports LSP, it requires installation of Rust Language Server -[[https://github.com/rust-lang/rls][~RLS~]]. To enable this you need to enable ~lsp~ in ~:tools~ section in ~init.el~ file. - -Keybindings +This module also supports LSP, if you have [[https://github.com/rust-lang/rls][the Rust Language Server]] or +[[https://github.com/rust-analyzer/rust-analyzer][rust-analyzer]] installed. To enable it, you must enable the =:tools lsp= module +and the ~+lsp~ flag on this module. +** Keybinds | Binding | Description | |---------------------+-----------------------------| -| ~ b a~ | ~cargo add~ | +| ~ b a~ | ~cargo audit~ | | ~ b b~ | ~cargo build~ | | ~ b B~ | ~cargo bench~ | | ~ b c~ | ~cargo check~ | | ~ b C~ | ~cargo clippy~ | | ~ b d~ | ~cargo doc~ | +| ~ b n~ | ~cargo update~ | +| ~ b o~ | ~cargo outdated~ | | ~ b r~ | ~cargo run~ | -| ~ b s~ | ~cargo search~ | -| ~ b u~ | ~cargo update~ | | ~ t a~ | ~cargo test~ | -| ~ t f~ | ~run tests in current file~ | | ~ t t~ | ~run current test~ | * TODO Configuration -How to configure this module, including common problems and how to address them. +** Enable rust-analyzer +You'll need [[https://github.com/rust-analyzer/rust-analyzer][rust-analyzer]] installed on your system, then use the following: -* Troubleshooting -** Racer installation problems -Racer need now ([[https://github.com/racer-rust/racer#installation][From 2.1 version]]) nightly version of Rust - -If you installed trough rustup -#+BEGIN_SRC sh -rustup toolchain add nightly - -cargo +nightly install racer +#+BEGIN_SRC elisp +;; in $DOOMDIR/config.el +(setq rustic-lsp-server 'rust-analyzer) #+END_SRC + +** Enabling elgot support for Rust +Doom doesn't have eglot support (yet), but if you've installed it yourself and +want rust to use eglot instead of LSP, use the following: + +#+BEGIN_SRC elisp +;; in $DOOMDIR/config.el +(after! rustic + (setq rustic-lsp-server 'rust-analyzer ; optional + rustic-lsp-client 'eglot)) +#+END_SRC + +* TODO Troubleshooting diff --git a/modules/lang/rust/autoload.el b/modules/lang/rust/autoload.el index 9f584cb44..dab7e68e4 100644 --- a/modules/lang/rust/autoload.el +++ b/modules/lang/rust/autoload.el @@ -14,3 +14,13 @@ (when buf (pop-to-buffer buf) t))) + + +;; +;;; Custom Cargo commands + +;;;###autoload +(defun +rust/cargo-audit () + "Run 'cargo audit' for the current project." + (interactive) + (rustic-run-cargo-command "cargo audit -f")) diff --git a/modules/lang/rust/config.el b/modules/lang/rust/config.el index 87f8bb602..30f224fa4 100644 --- a/modules/lang/rust/config.el +++ b/modules/lang/rust/config.el @@ -9,60 +9,72 @@ (use-package! rustic :mode ("\\.rs$" . rustic-mode) - :preface - (setq rustic-rls-pkg (if (featurep! +lsp) 'lsp-mode)) + :commands rustic-run-cargo-command rustic-cargo-outdated :config (set-docsets! 'rustic-mode "Rust") (setq rustic-indent-method-chain t rustic-flycheck-setup-mode-line-p nil ;; use :editor format instead - rustic-format-on-save nil + rustic-format-trigger nil ;; REVIEW `rust-ordinary-lt-gt-p' is terribly expensive in large rust ;; buffers, so we disable it, but only for evil users, because it ;; affects `forward-sexp' and its ilk. See ;; https://github.com/rust-lang/rust-mode/issues/288. - rustic-match-angle-brackets (not (featurep! :editor evil)) - ;; `rustic-setup-rls' uses `package-installed-p' to determine if - ;; lsp-mode/elgot are available. This breaks because Doom doesn't use - ;; package.el to begin with (and lazy loads it). This is already handled - ;; by the :tools lsp module, so... - rustic-lsp-setup-p nil) + rustic-match-angle-brackets (not (featurep! :editor evil))) (add-hook 'rustic-mode-hook #'rainbow-delimiters-mode) - (when (featurep! +lsp) - (add-hook 'rustic-mode-local-vars-hook #'lsp!))) + (if (featurep! +lsp) + (add-hook 'rustic-mode-local-vars-hook #'lsp!) + (setq rustic-lsp-server nil) + (after! rustic-flycheck + (add-to-list 'flycheck-checkers 'rustic-clippy))) + + (map! :map rustic-mode-map + :localleader + (:prefix ("b" . "build") + :desc "cargo audit" "a" #'+rust/cargo-audit + :desc "cargo build" "b" #'rustic-cargo-build + :desc "cargo bench" "B" #'rustic-cargo-bench + :desc "cargo check" "c" #'rustic-cargo-check + :desc "cargo clippy" "C" #'rustic-cargo-clippy + :desc "cargo doc" "d" #'rustic-cargo-doc + :desc "cargo fmt" "f" #'rustic-cargo-fmt + :desc "cargo new" "n" #'rustic-cargo-new + :desc "cargo outdated" "o" #'rustic-cargo-outdated + :desc "cargo run" "r" #'rustic-cargo-run) + (:prefix ("t" . "cargo test") + :desc "all" "a" #'rustic-cargo-test + :desc "current test" "t" #'rustic-cargo-current-test)) + + ;; HACK Fixes #2541: RLS doesn't appear to support documentSymbol, but + ;; lsp-rust thinks it does, and so yields imenu population to the server. + ;; The result is an empty imenu list. Until RLS supports documentSymbol, + ;; we disable `lsp-enable-imenu' is rust+RLS buffers. + (defadvice! +rust--disable-imenu-for-lsp-mode-a (&rest _) + :before #'rustic-lsp-mode-setup + (when (eq rustic-lsp-server 'rls) + (setq-local lsp-enable-imenu nil))) + + ;; If lsp/elgot isn't available, it attempts to install lsp-mode via + ;; package.el. Doom manages its own dependencies through straight so disable + ;; this behavior to avoid package-not-initialized errors. + (defadvice! +rust--dont-install-packages-a (&rest _) + :override #'rustic-install-lsp-client-p + (message "No LSP server running"))) (use-package! racer :unless (featurep! +lsp) :hook (rustic-mode . racer-mode) + :init + ;; HACK Fix #2132: `racer' depends on `rust-mode', which tries to modify + ;; `auto-mode-alist'. We make extra sure that doesn't stick, especially + ;; when a buffer is reverted, as it is after rustfmt is done wiht it. + (after! rust-mode + (setq auto-mode-alist (delete '("\\.rs\\'" . rust-mode) auto-mode-alist))) :config (set-lookup-handlers! 'rustic-mode :definition '(racer-find-definition :async t) :documentation '+rust-racer-lookup-documentation)) - - -;; -;;; Tools - -(use-package! cargo - :after rustic-mode - :config - (map! :map rustic-mode-map - :localleader - (:prefix ("b" . "build") - :desc "cargo add" "a" #'cargo-process-add - :desc "cargo build" "b" #'cargo-process-build - :desc "cargo bench" "B" #'cargo-process-bench - :desc "cargo check" "c" #'cargo-process-check - :desc "cargo clippy" "C" #'cargo-process-clippy - :desc "cargo doc" "d" #'cargo-process-doc - :desc "cargo run" "r" #'cargo-process-run - :desc "cargo search" "s" #'cargo-process-search - :desc "cargo update" "u" #'cargo-process-update) - (:prefix ("t" . "cargo test") - :desc "all" "a" #'cargo-process-test - :desc "current file" "f" #'cargo-process-current-file-tests - :desc "current test" "t" #'cargo-process-current-test))) diff --git a/modules/lang/rust/doctor.el b/modules/lang/rust/doctor.el index 8cc8af442..b8af96745 100644 --- a/modules/lang/rust/doctor.el +++ b/modules/lang/rust/doctor.el @@ -17,8 +17,8 @@ (setq lsp-server rustic-lsp-server)) (pcase lsp-server (`rust-analyzer - (unless (executable-find "ra_lsp_server") - (warn! "Couldn't find rust analyzer (ra_lsp_server)"))) + (unless (executable-find "rust-analyzer") + (warn! "Couldn't find rust analyzer (rust-analyzer)"))) (`rls (unless (executable-find "rls") (warn! "Couldn't find rls"))))) diff --git a/modules/lang/rust/packages.el b/modules/lang/rust/packages.el index 05fab0577..3b640d7ca 100644 --- a/modules/lang/rust/packages.el +++ b/modules/lang/rust/packages.el @@ -1,9 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/rust/packages.el -(package! rustic) +(package! rustic :pin "6dead0cdd4") (unless (featurep! +lsp) - (package! racer)) - -;; -(package! cargo) + (package! racer :pin "a0bdf778f0")) diff --git a/modules/lang/scala/packages.el b/modules/lang/scala/packages.el index 8a331c698..1ed7723dd 100644 --- a/modules/lang/scala/packages.el +++ b/modules/lang/scala/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/scala/packages.el -(package! sbt-mode) -(package! scala-mode) +(package! sbt-mode :pin "633a315ad4") +(package! scala-mode :pin "46bb948345") diff --git a/modules/lang/scheme/config.el b/modules/lang/scheme/config.el index 9f4e286a0..67e5bfd6a 100644 --- a/modules/lang/scheme/config.el +++ b/modules/lang/scheme/config.el @@ -17,6 +17,9 @@ :definition #'geiser-edit-symbol-at-point :documentation #'geiser-doc-symbol-at-point)) :config + (set-popup-rules! + '(("\\*[Gg]eiser \\(?:[Mm]essages\\|DBG\\|Xref\\)\\*" :quit nil) + ( "\\* [A-Za-z0-9_-]+ REPL \\*" :quit nil))) (map! :localleader :map scheme-mode-map "'" #'geiser-mode-switch-to-repl diff --git a/modules/lang/scheme/packages.el b/modules/lang/scheme/packages.el index 20a43e613..a1720520e 100644 --- a/modules/lang/scheme/packages.el +++ b/modules/lang/scheme/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/scheme/packages.el -(package! geiser) +(package! geiser :pin "645e477542") diff --git a/modules/lang/sh/README.org b/modules/lang/sh/README.org index b2821e7bf..873d4a2a4 100644 --- a/modules/lang/sh/README.org +++ b/modules/lang/sh/README.org @@ -1,19 +1,45 @@ -#+TITLE: :lang sh +#+TITLE: lang/sh +#+DATE: December 19, 2015 +#+SINCE: v0.7 +#+STARTUP: inlineimages +* Table of Contents :TOC: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] +- [[#configuration][Configuration]] +- [[#troubleshooting][Troubleshooting]] + +* Description This module adds support for shell scripting languages. + Code completion (company-shell) + Syntax Checking (flycheck) -+ Added variable interpolation fontification -* Table of Contents :TOC: -- [[#install][Install]] - - [[#dependencies][Dependencies]] +** Module Flags ++ =+lsp= Enables LSP support for sh-mode. This requires the =:tools lsp= module + to be enabled and [[https://github.com/mads-hartmann/bash-language-server][bash-language-server]] to be installed on your system. ++ =+fish= Add syntax highlighting for fish script files. + +** Plugins ++ [[https://github.com/Alexander-Miller/company-shell][company-shell]]* (=:completion company=) ++ [[https://github.com/wwwjfy/emacs-fish][fish-mode]]* (=+fish=) -* Install -** Dependencies -This module has several soft dependencies: +** Hacks ++ Interpolated variables are fontified. -+ ~shellcheck~ Enables shell script linting. -+ ~bashdb~ Enables debugging for bash scripts. -+ ~zshdb~ Enables debugging for zsh scripts. +* Prerequisites +This module has several optional dependencies: + ++ [[https://github.com/koalaman/shellcheck][shellcheck]]: Enables advanced shell script linting. ++ [[https://github.com/mads-hartmann/bash-language-server][bash-language-server]]: Enables LSP support (with =+lsp= flag). ++ With the =:tools debugger= module + + [[http://bashdb.sourceforge.net/][bashdb]]: Enables debugging for bash scripts + + [[https://github.com/rocky/zshdb][zshdb]]: Enables debugging for zsh scripts + +* TODO Features +* TODO Configuration +* TODO Troubleshooting diff --git a/modules/lang/sh/config.el b/modules/lang/sh/config.el index f8853aa37..9777bda89 100755 --- a/modules/lang/sh/config.el +++ b/modules/lang/sh/config.el @@ -17,6 +17,9 @@ (set-electric! 'sh-mode :words '("else" "elif" "fi" "done" "then" "do" "esac" ";;")) (set-repl-handler! 'sh-mode #'+sh/open-repl) + (when (featurep! +lsp) + (add-hook 'sh-mode-local-vars-hook #'lsp!)) + (setq sh-indent-after-continuation 'always) ;; [pedantry intensifies] @@ -52,6 +55,7 @@ (use-package! company-shell :when (featurep! :completion company) + :unless (featurep! +lsp) :after sh-script :config (set-company-backend! 'sh-mode '(company-shell company-files)) diff --git a/modules/lang/sh/doctor.el b/modules/lang/sh/doctor.el index e85643b71..a25f3dfbc 100644 --- a/modules/lang/sh/doctor.el +++ b/modules/lang/sh/doctor.el @@ -1,5 +1,5 @@ ;;; lang/sh/doctor.el -*- lexical-binding: t; -*- -(when (featurep! :tools flycheck) +(when (featurep! :checkers syntax) (unless (executable-find "shellcheck") (warn! "Couldn't find shellcheck. Shell script linting will not work"))) diff --git a/modules/lang/sh/packages.el b/modules/lang/sh/packages.el index b52024fd5..771a56ed8 100644 --- a/modules/lang/sh/packages.el +++ b/modules/lang/sh/packages.el @@ -5,4 +5,4 @@ (package! company-shell)) (when (featurep! +fish) - (package! fish-mode)) + (package! fish-mode :pin "688c82deca")) diff --git a/modules/lang/solidity/README.org b/modules/lang/solidity/README.org index e0800b5a8..d91d419f3 100644 --- a/modules/lang/solidity/README.org +++ b/modules/lang/solidity/README.org @@ -7,11 +7,11 @@ This module adds [[https://github.com/ethereum/solidity][Solidity]] support thro + Gas estimation (~C-c C-g~) * Table of Contents :TOC: -- [[Module Flags][Module Flags]] -- [[Prerequisites][Prerequisites]] - - [[Solc][Solc]] - - [[Solium][Solium]] -- [[TODO][TODO]] +- [[#module-flags][Module Flags]] +- [[#prerequisites][Prerequisites]] + - [[#solc][Solc]] + - [[#solium][Solium]] +- [[#todo][TODO]] * Module Flags This module provides no flags. diff --git a/modules/lang/solidity/config.el b/modules/lang/solidity/config.el index 7921df560..5449423f7 100644 --- a/modules/lang/solidity/config.el +++ b/modules/lang/solidity/config.el @@ -8,9 +8,10 @@ (use-package! solidity-flycheck ; included with solidity-mode - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :after solidity-mode :config + (set-docsets! 'solidity-mode "Solidity") (setq flycheck-solidity-solc-addstd-contracts t) (when (funcall flycheck-executable-find solidity-solc-path) (add-to-list 'flycheck-checkers 'solidity-checker nil #'eq)) diff --git a/modules/lang/solidity/packages.el b/modules/lang/solidity/packages.el index 7d6e78ff5..2f7af778b 100644 --- a/modules/lang/solidity/packages.el +++ b/modules/lang/solidity/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/solidity/packages.el -(package! solidity-mode) -(package! company-solidity) -(package! solidity-flycheck) +(package! solidity-mode :pin "b190993dcb") +(package! company-solidity :pin "b190993dcb") +(package! solidity-flycheck :pin "b190993dcb") diff --git a/modules/lang/swift/config.el b/modules/lang/swift/config.el index 68470f3c7..5a31b95d6 100644 --- a/modules/lang/swift/config.el +++ b/modules/lang/swift/config.el @@ -5,7 +5,7 @@ (use-package! flycheck-swift - :when (featurep! :tools flycheck) + :when (featurep! :checkers syntax) :unless (featurep! +lsp) :after swift-mode :config (flycheck-swift-setup)) diff --git a/modules/lang/swift/packages.el b/modules/lang/swift/packages.el index 6dd3244f0..4057cab08 100644 --- a/modules/lang/swift/packages.el +++ b/modules/lang/swift/packages.el @@ -1,11 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; lang/swift/packages.el -(package! swift-mode) +(package! swift-mode :pin "1268425311") (if (featurep! +lsp) - (package! lsp-sourcekit) + (package! lsp-sourcekit :pin "04d75b6a0b") (when (featurep! :completion company) - (package! company-sourcekit)) - (when (featurep! :tools flycheck) - (package! flycheck-swift))) + (package! company-sourcekit :pin "abf9bc5a01")) + (when (featurep! :checkers syntax) + (package! flycheck-swift :pin "4c5ad40125"))) diff --git a/modules/lang/terra/packages.el b/modules/lang/terra/packages.el index 4a1a1485b..57adda36c 100644 --- a/modules/lang/terra/packages.el +++ b/modules/lang/terra/packages.el @@ -2,8 +2,8 @@ ;;; lang/lua/packages.el (package! terra-mode - :recipe (:host github :repo "StanfordLegion/terra-mode")) + :recipe (:host github :repo "StanfordLegion/terra-mode") + :pin "1e5e82410d") (when (featurep! :completion company) - (package! company-lua)) - + (package! company-lua :pin "29f6819de4")) diff --git a/modules/lang/web/+css.el b/modules/lang/web/+css.el index 7f8162814..f182a6ef8 100644 --- a/modules/lang/web/+css.el +++ b/modules/lang/web/+css.el @@ -36,7 +36,6 @@ (use-package! counsel-css :when (featurep! :completion ivy) - :commands counsel-css :hook (css-mode . counsel-css-imenu-setup) :init (map! :map (css-mode-map scss-mode-map less-css-mode-map) diff --git a/modules/lang/web/+html.el b/modules/lang/web/+html.el index 424cad0c3..dfb57899e 100644 --- a/modules/lang/web/+html.el +++ b/modules/lang/web/+html.el @@ -1,9 +1,24 @@ ;;; lang/web/+html.el -*- lexical-binding: t; -*- (use-package! web-mode - :mode "\\.\\(?:as\\(?:[cp]x\\)\\|blade\\.php\\|erb\\|hbs\\|j\\(?:inja\\|sp\\)\\|mustache\\|p?html?\\|svelte\\|t\\(?:pl\\.php\\|sx\\|wig\\)\\|vue\\)\\'" + :mode "\\.p?html?\\'" + :mode "\\.\\(?:tpl\\|blade\\)\\(?:\\.php\\)?\\'" + :mode "\\.erb\\'" + :mode "\\.eex\\'" + :mode "\\.jsp\\'" + :mode "\\.as[cp]x\\'" + :mode "\\.hbs\\'" + :mode "\\.mustache\\'" + :mode "\\.svelte\\'" + :mode "\\.vue\\'" + :mode "\\.twig\\'" + :mode "\\.jinja\\'" :mode "wp-content/themes/.+/.+\\.php\\'" :mode "templates/.+\\.php\\'" + ;; REVIEW We associate TSX files with `web-mode' because `typescript-mode' + ;; does not officially support JSX/TSX. See + ;; https://github.com/emacs-typescript/typescript.el/issues/4 + :mode "\\.tsx\\'" :config (set-docsets! 'web-mode "HTML" "CSS" "Twig" "WordPress") @@ -46,6 +61,29 @@ "\\(?:>\\|]\\|}\\)+\\'"))))) (delq! nil web-mode-engines-auto-pairs)) + (add-to-list 'web-mode-engines-alist '("elixir" . "\\.eex\\'")) + + (let ((types '("javascript" "jsx"))) + (setq web-mode-comment-formats + (cl-remove-if (lambda (item) (member (car item) types)) + web-mode-comment-formats)) + (dolist (type types) + (push (cons type "//") web-mode-comment-formats))) + + (add-hook! 'web-mode-hook + (defun +web--fix-js-comments-h () + "Fix comment handling in `web-mode' for JavaScript." + (when (member web-mode-content-type '("javascript" "jsx")) + ;; For some reason the default is to insert HTML comments even + ;; in JavaScript. + (setq-local comment-start "//") + (setq-local comment-end "") + ;; Needed since otherwise the default value generated by + ;; `comment-normalize-vars' will key off the syntax and think + ;; that a single "/" starts a comment, which completely borks + ;; auto-fill. + (setq-local comment-start-skip "// *")))) + (map! :map web-mode-map (:localleader :desc "Rehighlight buffer" "h" #'web-mode-buffer-highlight diff --git a/modules/lang/web/autoload/evil.el b/modules/lang/web/autoload/evil.el index fcc563971..ebbd100fa 100644 --- a/modules/lang/web/autoload/evil.el +++ b/modules/lang/web/autoload/evil.el @@ -2,20 +2,25 @@ ;;;###if (featurep! :editor evil) ;;;###autoload (autoload '+web:encode-html-entities "lang/web/autoload/evil" nil t) -(evil-define-operator +web:encode-html-entities (beg end &optional input) +(evil-define-operator +web:encode-html-entities (beg end &optional bang input) "Encodes HTML entities in INPUT or the selected region." - (interactive "") + (interactive "") (cond (input - (insert (+web-encode-entities input))) + (let ((result (+web-encode-entities input))) + (if bang + (kill-new result) + (insert result)))) ((and beg end) (+web/encode-entities-region beg end)))) ;;;###autoload (autoload '+web:decode-html-entities "lang/web/autoload/evil" nil t) -(evil-define-operator +web:decode-html-entities (beg end &optional input) +(evil-define-operator +web:decode-html-entities (beg end &optional bang input) "Decodes HTML entities in INPUT or the selected region." - (interactive "") + (interactive "") (cond (input - (insert (+web-decode-entities input))) + (let ((result (+web-decode-entities input))) + (if bang + (kill-new result) + (insert result)))) ((and beg end) (+web/decode-entities-region beg end)))) - diff --git a/modules/lang/web/autoload/html.el b/modules/lang/web/autoload/html.el index dbba73e60..bfb1de838 100644 --- a/modules/lang/web/autoload/html.el +++ b/modules/lang/web/autoload/html.el @@ -2,6 +2,7 @@ (defvar +web-entities-list [["&" "&"] [" " " "] [" " " "] [" " " "] [" " " "] + ["<" "<"] [">" ">"] ["‏" "‏"] ["‎" "‎"] ["‍" "‍"] ["‌" "‌"] ["¡" "¡"] ["¢" "¢"] ["£" "£"] ["¤" "¤"] ["¥" "¥"] ["¦" "¦"] ["§" "§"] ["¨" "¨"] ["©" "©"] ["ª" "ª"] diff --git a/modules/lang/web/packages.el b/modules/lang/web/packages.el index d0933030a..64c0388b5 100644 --- a/modules/lang/web/packages.el +++ b/modules/lang/web/packages.el @@ -2,23 +2,23 @@ ;;; lang/web/packages.el ;; +html.el -(package! emmet-mode) -(package! haml-mode) -(package! pug-mode) -(package! slim-mode) -(when (package! web-mode) +(package! emmet-mode :pin "1acb821e01") +(package! haml-mode :pin "bf5b6c11b1") +(package! pug-mode :pin "685fd3414d") +(package! slim-mode :pin "3636d18ab1") +(when (package! web-mode :pin "cd000fcfce") (when (featurep! :completion company) - (package! company-web))) + (package! company-web :pin "f0cc9187c9"))) ;; +css.el (package! css-mode :built-in t) -(package! less-css-mode :built-in t) +(package! less-css-mode :built-in t :pin "c7fa3d56d8") -(package! sass-mode) -(package! stylus-mode) -(package! sws-mode) -(package! rainbow-mode) +(package! sass-mode :pin "247a0d4b50") +(package! stylus-mode :pin "4dbde92542") +(package! sws-mode :pin "4dbde92542") +(package! rainbow-mode :pin "3ef813d637") (when (featurep! :completion ivy) - (package! counsel-css)) + (package! counsel-css :pin "61a38c9d50")) (when (featurep! :completion helm) - (package! helm-css-scss)) + (package! helm-css-scss :pin "48b996f73a")) diff --git a/modules/term/eshell/autoload/commands.el b/modules/term/eshell/autoload/commands.el index efb706ec6..1225270a0 100644 --- a/modules/term/eshell/autoload/commands.el +++ b/modules/term/eshell/autoload/commands.el @@ -4,10 +4,10 @@ (defun eshell/emacs (&rest files) "Open a FILES in Emacs. For folks with a habit of using \"emacs\" to open files, even in eshell." - (if args + (if files (mapc #'find-file (mapcar #'expand-file-name - (eshell-flatten-list (reverse args)))) + (eshell-flatten-list (reverse files)))) (bury-buffer))) ;;;###autoload (defalias 'eshell/e #'eshell/emacs) diff --git a/modules/term/eshell/autoload/company.el b/modules/term/eshell/autoload/company.el index 1d24cf99a..22dfd6b4c 100644 --- a/modules/term/eshell/autoload/company.el +++ b/modules/term/eshell/autoload/company.el @@ -6,33 +6,35 @@ (defvar company-pcomplete-available 'unknown) (defun company-pcomplete--prefix () - (let* ((pcomplete-stub) - pcomplete-seen - pcomplete-norm-func - pcomplete-args - pcomplete-last pcomplete-index - (pcomplete-autolist pcomplete-autolist) - (pcomplete-suffix-list pcomplete-suffix-list)) - (pcomplete-completions) - (buffer-substring (pcomplete-begin) (point)))) + (with-no-warnings + (let* ((pcomplete-stub) + pcomplete-seen + pcomplete-norm-func + pcomplete-args + pcomplete-last pcomplete-index + (pcomplete-autolist pcomplete-autolist) + (pcomplete-suffix-list pcomplete-suffix-list)) + (pcomplete-completions) + (buffer-substring (pcomplete-begin) (point))))) (defun company-pcomplete--candidates () - (let* ((pcomplete-stub) - (pcomplete-show-list t) - pcomplete-seen pcomplete-norm-func - pcomplete-args pcomplete-last pcomplete-index - (pcomplete-autolist pcomplete-autolist) - (pcomplete-suffix-list pcomplete-suffix-list) - (candidates (pcomplete-completions)) - (prefix (buffer-substring (pcomplete-begin) (point))) - ;; Collect all possible completions for the current stub - (cnds (all-completions pcomplete-stub candidates)) - (bnds (completion-boundaries pcomplete-stub candidates nil "")) - (skip (- (length pcomplete-stub) (car bnds)))) - ;; Replace the stub at the beginning of each candidate by the prefix - (mapcar (lambda (cand) - (concat prefix (substring cand skip))) - cnds))) + (with-no-warnings + (let* ((pcomplete-stub) + (pcomplete-show-list t) + pcomplete-seen pcomplete-norm-func + pcomplete-args pcomplete-last pcomplete-index + (pcomplete-autolist pcomplete-autolist) + (pcomplete-suffix-list pcomplete-suffix-list) + (candidates (pcomplete-completions)) + (prefix (buffer-substring (pcomplete-begin) (point))) + ;; Collect all possible completions for the current stub + (cnds (all-completions pcomplete-stub candidates)) + (bnds (completion-boundaries pcomplete-stub candidates nil "")) + (skip (- (length pcomplete-stub) (car bnds)))) + ;; Replace the stub at the beginning of each candidate by the prefix + (mapcar (lambda (cand) + (concat prefix (substring cand skip))) + cnds)))) ;;;###autoload (defun company-pcomplete-available () diff --git a/modules/term/eshell/autoload/prompts.el b/modules/term/eshell/autoload/prompts.el index 55e01fb62..0c1dd6a40 100644 --- a/modules/term/eshell/autoload/prompts.el +++ b/modules/term/eshell/autoload/prompts.el @@ -12,11 +12,16 @@ (defun +eshell--current-git-branch () + ;; TODO Refactor me (cl-destructuring-bind (status . output) - (doom-call-process "git" "name-rev" "--name-only" "HEAD") + (doom-call-process "git" "symbolic-ref" "-q" "--short" "HEAD") (if (equal status 0) (format " [%s]" output) - ""))) + (cl-destructuring-bind (status . output) + (doom-call-process "git" "describe" "--all" "--always" "HEAD") + (if (equal status 0) + (format " [%s]" output) + ""))))) ;;;###autoload (defun +eshell-default-prompt-fn () diff --git a/modules/term/eshell/config.el b/modules/term/eshell/config.el index 5b05e0810..6e14d340e 100644 --- a/modules/term/eshell/config.el +++ b/modules/term/eshell/config.el @@ -25,8 +25,8 @@ buffer.") ("d" "dired $1") ("bd" "eshell-up $1") ("rg" "rg --color=always $*") - ("l" "ls -lh") - ("ll" "ls -lah") + ("l" "ls -lh $*") + ("ll" "ls -lah $*") ("clear" "clear-scrollback")) ; more sensible than default "An alist of default eshell aliases, meant to emulate useful shell utilities, like fasd and bd. Note that you may overwrite these in your @@ -39,7 +39,7 @@ You should use `set-eshell-alias!' to change this.") (defvar eshell-directory-name (concat doom-etc-dir "eshell")) ;; These files are exceptions, because they may contain configuration -(defvar eshell-aliases-file (concat +eshell-config-dir "alias")) +(defvar eshell-aliases-file (concat +eshell-config-dir "aliases")) (defvar eshell-rc-script (concat +eshell-config-dir "profile")) (defvar eshell-login-script (concat +eshell-config-dir "login")) @@ -132,6 +132,8 @@ You should use `set-eshell-alias!' to change this.") :n "C" #'+eshell/evil-change-line :n "d" #'+eshell/evil-delete :n "D" #'+eshell/evil-delete-line + :ni "C-j" #'eshell-next-matching-input-from-input + :ni "C-k" #'eshell-previous-matching-input-from-input :ig "C-d" #'+eshell/quit-or-delete-char "TAB" #'+eshell/pcomplete [tab] #'+eshell/pcomplete @@ -150,6 +152,7 @@ You should use `set-eshell-alias!' to change this.") [remap split-window-right] #'+eshell/split-right [remap doom/backward-to-bol-or-indent] #'eshell-bol [remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input + [remap evil-delete-back-to-indentation] #'eshell-kill-input [remap evil-window-split] #'+eshell/split-below [remap evil-window-vsplit] #'+eshell/split-right))) (add-hook! 'eshell-mode-hook diff --git a/modules/term/eshell/packages.el b/modules/term/eshell/packages.el index 31920f973..6eb2b23a9 100644 --- a/modules/term/eshell/packages.el +++ b/modules/term/eshell/packages.el @@ -1,7 +1,7 @@ ;; -*- no-byte-compile: t; -*- ;;; term/eshell/packages.el -(package! eshell-up) -(package! eshell-z) -(package! shrink-path) -(package! esh-help) +(package! eshell-up :pin "9c100bae5c") +(package! eshell-z :pin "337cb241e1") +(package! shrink-path :pin "c14882c859") +(package! esh-help :pin "417673ed18") diff --git a/modules/term/shell/autoload.el b/modules/term/shell/autoload.el index 6549e8b08..47dca9372 100644 --- a/modules/term/shell/autoload.el +++ b/modules/term/shell/autoload.el @@ -41,6 +41,18 @@ prompt." (when (memq (process-status process) '(exit stop)) (kill-buffer (process-buffer process)))) +(defun +shell--send-input (buffer input &optional no-newline) + (when input + (with-current-buffer buffer + (unless (number-or-marker-p (cdr comint-last-prompt)) + (message "Waiting for shell to start up...") + (while (not (number-or-marker-p (cdr comint-last-prompt))) + (sleep-for 0.1))) + (goto-char (cdr comint-last-prompt)) + (delete-region (cdr comint-last-prompt) (point-max)) + (insert input) + (comint-send-input no-newline)))) + ;;;###autoload (defun +shell/toggle (&optional command) @@ -54,7 +66,8 @@ If popup is focused, kill it." (format "*doom:shell-popup:%s*" (if (bound-and-true-p persp-mode) (safe-persp-name (get-current-persp)) - "main"))))) + "main")))) + (dir default-directory)) (if-let (win (get-buffer-window buffer)) (if (eq (selected-window) win) (let (confirm-kill-processes) @@ -68,12 +81,11 @@ If popup is focused, kill it." (with-current-buffer (pop-to-buffer buffer) (if (not (eq major-mode 'shell-mode)) (shell buffer) - (erase-buffer) - (cd dir)) - (let ((process (get-buffer-process (current-buffer)))) - (set-process-sentinel process #'+shell--sentinel) - (when command - (comint-send-string process command))))))) + (cd dir) + (run-mode-hooks 'shell-mode-hook)))) + (when-let (process (get-buffer-process buffer)) + (set-process-sentinel process #'+shell--sentinel) + (+shell--send-input buffer command)))) ;;;###autoload (defun +shell/here (&optional command) @@ -84,14 +96,12 @@ If already in a shell buffer, clear it and cd into the current directory." (let ((buffer (+shell-unused-buffer)) (dir default-directory)) (with-current-buffer (switch-to-buffer buffer) - (if (not (eq major-mode 'shell-mode)) - (shell buffer) - (erase-buffer) - (cd dir)) - (let ((process (get-buffer-process (current-buffer)))) + (if (eq major-mode 'shell-mode) + (+shell--send-input buffer (format "cd %S" dir)) + (shell buffer)) + (let ((process (get-buffer-process buffer))) (set-process-sentinel process #'+shell--sentinel) - (when command - (comint-send-string process command)))) + (+shell--send-input buffer command))) buffer)) diff --git a/modules/term/term/config.el b/modules/term/term/config.el index ad9086cc3..c1efbdb5b 100644 --- a/modules/term/term/config.el +++ b/modules/term/term/config.el @@ -7,4 +7,5 @@ ;;;###package multi-term (setq multi-term-dedicated-window-height 20 - multi-term-switch-after-close 'PREVIOUS) + multi-term-switch-after-close 'PREVIOUS + multi-term-buffer-name "doom terminal") diff --git a/modules/term/term/packages.el b/modules/term/term/packages.el index dd7dd9fcc..f98aec986 100644 --- a/modules/term/term/packages.el +++ b/modules/term/term/packages.el @@ -2,4 +2,4 @@ ;;; term/term/packages.el (package! term :built-in t) -(package! multi-term) +(package! multi-term :pin "7307ddd456") diff --git a/modules/term/vterm/autoload.el b/modules/term/vterm/autoload.el index 85f8b22fc..8d71a56ae 100644 --- a/modules/term/vterm/autoload.el +++ b/modules/term/vterm/autoload.el @@ -55,3 +55,18 @@ If prefix ARG is non-nil, cd into `default-directory' instead of project root." default-directory (or (doom-project-root) default-directory)))) (vterm))) + + +(defvar +vterm--insert-point nil) + +;;;###autoload +(defun +vterm-remember-insert-point-h () + "Remember point when leaving insert mode." + (setq-local +vterm--insert-point (point))) + +;;;###autoload +(defun +vterm-goto-insert-point-h () + "Go to the point we were at when we left insert mode." + (when +vterm--insert-point + (goto-char +vterm--insert-point) + (setq-local +vterm--insert-point nil))) diff --git a/modules/term/vterm/config.el b/modules/term/vterm/config.el index c5d155b6b..319096e80 100644 --- a/modules/term/vterm/config.el +++ b/modules/term/vterm/config.el @@ -1,18 +1,26 @@ ;;; term/vterm/config.el -*- lexical-binding: t; -*- (use-package! vterm - :when (fboundp 'module-load) + :when (boundp 'module-file-suffix) :defer t :preface (setq vterm-install t) :config (set-popup-rule! "^vterm" :size 0.25 :vslot -4 :select t :quit nil :ttl 0) + (setq-hook! 'vterm-mode-hook + ;; Don't prompt about processes when killing vterm + confirm-kill-processes nil + ;; Prevent premature horizontal scrolling + hscroll-margin 0) + + (setq vterm-kill-buffer-on-exit t) + + (when (featurep! :editor evil) + (add-hook! 'vterm-mode-hook + (defun +vterm-init-remember-point-h () + (add-hook 'evil-insert-state-exit-hook #'+vterm-remember-insert-point-h nil t) + (add-hook 'evil-insert-state-entry-hook #'+vterm-goto-insert-point-h nil t)))) + (add-hook 'vterm-mode-hook #'doom-mark-buffer-as-real-h) - ;; Automatically kill buffer when vterm exits. - (add-hook! 'vterm-exit-functions - (defun +vterm-kill-buffer-on-quit-fn (buffer) - (if buffer (kill-buffer buffer)))) ;; Modeline serves no purpose in vterm - (add-hook 'vterm-mode-hook #'hide-mode-line-mode) - ;; Don't prompt about processes when killing vterm - (setq-hook! 'vterm-mode-hook confirm-kill-processes nil)) + (add-hook 'vterm-mode-hook #'hide-mode-line-mode)) diff --git a/modules/term/vterm/packages.el b/modules/term/vterm/packages.el index caa33a78e..18e6712c4 100644 --- a/modules/term/vterm/packages.el +++ b/modules/term/vterm/packages.el @@ -1,4 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; term/vterm/packages.el -(package! vterm :built-in 'prefer) +(package! vterm + :built-in 'prefer + :pin "4fbf8f89ff") diff --git a/modules/tools/ansible/packages.el b/modules/tools/ansible/packages.el index 1d18e47ce..7a70921d4 100644 --- a/modules/tools/ansible/packages.el +++ b/modules/tools/ansible/packages.el @@ -1,10 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/ansible/packages.el -(package! yaml-mode) -(package! ansible :recipe (:nonrecursive t)) -(package! ansible-doc) -(package! jinja2-mode) +(package! ansible :recipe (:nonrecursive t) + :pin "c6532e5216") +(package! ansible-doc :pin "86083a7bb2") +(package! jinja2-mode :pin "cfaa7bbe7b") +(package! yaml-mode :pin "cecf4b106b") (when (featurep! :completion company) - (package! company-ansible)) + (package! company-ansible :pin "8d1ffbc357")) diff --git a/modules/tools/debugger/autoload/debugger.el b/modules/tools/debugger/autoload/debugger.el index 5c3dd1390..73394337c 100644 --- a/modules/tools/debugger/autoload/debugger.el +++ b/modules/tools/debugger/autoload/debugger.el @@ -7,7 +7,7 @@ (bound-and-true-p lsp--buffer-deferred) (require 'dap-mode nil t) dap-mode) - (mapcar #'car dap--debug-template-configurations))) + (mapcar #'car dap-debug-template-configurations))) (defun +debugger-list-for-realgud () (cl-loop for (sym . plist) in +debugger--realgud-alist @@ -49,7 +49,7 @@ for what debugger to use. If the prefix ARG is set, prompt anyway." (unless (fboundp debugger) (user-error "Couldn't find debugger backend %S" debugger)) (setq-local +debugger--last debugger) - (if (assoc debugger dap--debug-template-configurations) + (if (assoc debugger dap-debug-template-configurations) (dap-debug debugger) (call-interactively debugger))) (+debugger/start-last))) diff --git a/modules/tools/debugger/config.el b/modules/tools/debugger/config.el index f75993866..419b172a5 100644 --- a/modules/tools/debugger/config.el +++ b/modules/tools/debugger/config.el @@ -1,31 +1,37 @@ ;;; tools/debugger/config.el -*- lexical-binding: t; -*- (defvar +debugger--realgud-alist - '((realgud:zshdb :modes (sh-mode)) - (realgud:kshdb :modes (sh-mode)) - (realgud:rdebug :modes (ruby-mode enh-ruby-mode)) - (realgud:pdb :modes (python-mode)) - (realgud:trepan2 :modes (python-mode)) - (realgud:gub :modes (go-mode)) + '((realgud:bashdb :modes (sh-mode)) (realgud:gdb) - (realgud:trepan :modes (perl-mode perl6-mode)) - (realgud:trepanpl :modes (perl-mode perl6-mode)) - (realgud:trepanjs :modes (javascript-mode js2-mode js3-mode)) + (realgud:gub :modes (go-mode)) + (realgud:kshdb :modes (sh-mode)) + (realgud:pdb :modes (python-mode)) + (realgud:perldb :modes (perl-mode perl6-mode)) + (realgud:rdebug :modes (ruby-mode enh-ruby-mode)) (realgud:remake) + (realgud:trepan :modes (perl-mode perl6-mode)) + (realgud:trepan2 :modes (python-mode)) (realgud:trepan3k :modes (python-mode)) - (realgud:bashdb :modes (sh-mode)) - (realgud:perldb :modes (perl-mode perl6-mode)))) + (realgud:trepanjs :modes (javascript-mode js2-mode js3-mode)) + (realgud:trepanpl :modes (perl-mode perl6-mode)) + (realgud:zshdb :modes (sh-mode)))) ;; ;;; Packages +;;;###package gdb +(setq gdb-show-main t + gdb-many-windows t) + + (use-package! dap-mode :when (featurep! :tools lsp) - :hook (dap-mode . dap-ui-mode) :after lsp-mode - :init - (setq dap--breakpoints-file (concat doom-etc-dir "dap-breakpoints")) + :preface + (add-hook 'dap-mode-hook #'dap-ui-mode) ; use a hook so users can remove it + (setq dap-breakpoints-file (concat doom-etc-dir "dap-breakpoints") + dap-utils-extension-path (concat doom-etc-dir "dap-extension/")) :config (dap-mode 1) (dolist (module '(((:lang . cc) ccls dap-lldb dap-gdb-lldb) @@ -104,8 +110,8 @@ (if (boundp 'starting-directory) (realgud-cmdbuf-info-starting-directory= starting-directory)) (set minibuffer-history-var - (cl-remove-duplicates - (cons cmd-str (eval minibuffer-history)) :from-end)))))) + (cl-remove-duplicates (cons cmd-str (eval minibuffer-history-var)) + :from-end t)))))) (t (if cmd-buf (switch-to-buffer cmd-buf)) (message "Error running command: %s" (mapconcat #'identity cmd-args " ")))) diff --git a/modules/tools/debugger/packages.el b/modules/tools/debugger/packages.el index 4d83727f8..298982b9f 100644 --- a/modules/tools/debugger/packages.el +++ b/modules/tools/debugger/packages.el @@ -1,9 +1,9 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/debugger/packages.el -(when (package! realgud) +(when (package! realgud :pin "2cca776d28") (when (featurep! :lang javascript) - (package! realgud-trepan-ni))) + (package! realgud-trepan-ni :pin "6e9cac5e80"))) (when (featurep! :tools lsp) - (package! dap-mode)) + (package! dap-mode :pin "d10e254ce4")) diff --git a/modules/tools/direnv/README.org b/modules/tools/direnv/README.org index 20530ae92..75e5e6ae4 100644 --- a/modules/tools/direnv/README.org +++ b/modules/tools/direnv/README.org @@ -67,27 +67,8 @@ Or ~nix-env -i direnv~ * Troubleshooting ** direnv + nix is slow Consider augmenting direnv with [[https://github.com/target/lorri][lorri]], which will cache nix builds and speed up -direnv tremendously. - -At the time of writing, the lorri package in nixpkgs simply emits an error -message, telling you to install it manually. You can get around this with: +direnv tremendously: #+BEGIN_SRC nix -nixpkgs.overlays = [ - (self: super: { - lorri = - let src = (super.fetchFromGitHub { - owner = "target"; - repo = "lorri"; - rev = "38eae3d487526ece9d1b8c9bb0d27fb45cf60816"; - sha256 = "11k9lxg9cv6dlxj4haydvw4dhcfyszwvx7jx9p24jadqsy9jmbj4"; - }); - in import src { inherit src; }; - }) -]; - -environment.systemPackages = [ pkgs.lorri ]; +services.lorri.enable = true; #+END_SRC - -Otherwise, follow [[https://github.com/target/lorri#installing-lorri][the instructions in lorri's README]] on how to install it -manually. diff --git a/modules/tools/direnv/config.el b/modules/tools/direnv/config.el index cb05ee4b9..04abdab15 100644 --- a/modules/tools/direnv/config.el +++ b/modules/tools/direnv/config.el @@ -1,57 +1,34 @@ ;;; tools/direnv/config.el -*- lexical-binding: t; -*- -(defvar +direnv--keywords +(defvar +direnv-keywords '("direnv_layout_dir" "PATH_add" "path_add" "log_status" "log_error" "has" "join_args" "expand_path" "dotenv" "user_rel_path" "find_up" "source_env" "watch_file" "source_up" "direnv_load" "MANPATH_add" "load_prefix" "layout" "use" "rvm" "use_nix" "use_guix") "TODO") + +;; +;;; Packages + (use-package! direnv - :after-call after-find-file dired-initial-position-hook + :hook (before-hack-local-variables . direnv--maybe-update-environment) + :hook (flycheck-before-syntax-check . direnv--maybe-update-environment) + :hook (direnv-envrc-mode . +direnv-envrc-fontify-keywords-h) :config - (add-hook! 'direnv-mode-hook - (defun +direnv-init-h () - "Instead of checking for direnv on `post-command-hook', check only once, -when the file is first opened/major mode is activated. This is significantly -less expensive, but is less sensitive to changes to .envrc done outside of -Emacs." - (direnv--disable) - (when direnv-mode - (add-hook 'after-change-major-mode-hook - #'direnv--maybe-update-environment)))) + (add-to-list 'direnv-non-file-modes 'vterm-mode) - (defadvice! +direnv--make-process-environment-buffer-local-a (items) - :filter-return #'direnv--export - (when items - (mapc 'kill-local-variable '(process-environment exec-path)) - (mapc 'make-local-variable '(process-environment exec-path))) - items) + (defun +direnv-envrc-fontify-keywords-h () + "Fontify special .envrc keywords; it's a good indication of whether or not +we've typed them correctly." + (font-lock-add-keywords + nil `((,(regexp-opt +direnv-keywords 'symbols) + (0 font-lock-keyword-face))))) - ;; Fontify special .envrc keywords; it's a good indication of whether or not - ;; we've typed them correctly. - (add-hook! 'direnv-envrc-mode-hook - (defun +direnv-envrc-fontify-keywords-h () - (font-lock-add-keywords - nil `((,(regexp-opt +direnv--keywords 'symbols) - (0 font-lock-keyword-face))))) - (defun +direnv-update-on-save-h () - (add-hook 'after-save-hook #'direnv--maybe-update-environment - nil 'local))) - - (defadvice! +direnv-update-a (&rest _) - "Update direnv. Useful to advise functions that may run -environment-sensitive logic like `flycheck-default-executable-find'. This fixes -flycheck issues with direnv and on nix." - :before #'flycheck-default-executable-find - (direnv--maybe-update-environment)) - - (defadvice! +direnv--fail-gracefully-a (orig-fn) - "Don't try to update direnv if the executable isn't present." - :around #'direnv--maybe-update-environment - (if (executable-find "direnv") - (when (file-readable-p (or buffer-file-name default-directory)) - (funcall orig-fn)) - (doom-log "Couldn't find direnv executable"))) + (defadvice! +direnv--fail-gracefully-a (&rest _) + "Don't try to use direnv if the executable isn't present." + :before-while #'direnv-update-directory-environment + (or (executable-find "direnv") + (ignore (doom-log "Couldn't find direnv executable")))) (direnv-mode +1)) diff --git a/modules/tools/direnv/packages.el b/modules/tools/direnv/packages.el index a0c843e53..ea105c865 100644 --- a/modules/tools/direnv/packages.el +++ b/modules/tools/direnv/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/direnv/packages.el -(package! direnv) +(package! direnv :pin "fd0b6bbd5e") diff --git a/modules/tools/docker/README.org b/modules/tools/docker/README.org index b3153322c..ddf9eec07 100644 --- a/modules/tools/docker/README.org +++ b/modules/tools/docker/README.org @@ -29,7 +29,7 @@ convenience functions allow images to be built easily. =docker-tramp.el= offers a [[https://www.gnu.org/software/tramp/][TRAMP]] method for Docker containers. ** Module Flags -This module provides no flags. ++ =+lsp= Enables integration for the Dockerfile Language Server. ** Plugins + [[https://github.com/Silex/docker.el][docker]] @@ -40,6 +40,10 @@ This module provides no flags. This module assumes =docker=, =docker-compose= and =docker-machine= binaries are installed and accessible from your PATH. +Optionally, this module also uses the following programs: + ++ =docker-langserver= (for LSP users): ~npm install -g dockerfile-language-server-nodejs~ + * Features ** Docker control Use =M-x docker=, select a resource, and then mark or unmark items using the @@ -95,7 +99,6 @@ or inside a =use-package= declaration: #+BEGIN_SRC emacs-lisp (use-package! docker - :ensure t :bind ("C-c d" . docker) :custom (docker-image-run-arguments '("-i" "-t" "--rm"))) #+END_SRC diff --git a/modules/tools/docker/config.el b/modules/tools/docker/config.el index cf3c8a32f..b6dff7f56 100644 --- a/modules/tools/docker/config.el +++ b/modules/tools/docker/config.el @@ -8,3 +8,9 @@ docker-volume-mode docker-machine-mode) 'emacs)) + +(after! dockerfile-mode + (set-docsets! 'dockerfile-mode "Docker") + + (when (featurep! +lsp) + (add-hook 'dockerfile-mode-local-vars-hook #'lsp!))) diff --git a/modules/tools/docker/packages.el b/modules/tools/docker/packages.el index 9266beee4..76c53507b 100644 --- a/modules/tools/docker/packages.el +++ b/modules/tools/docker/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/docker/packages.el -(package! docker) -(package! docker-tramp) -(package! dockerfile-mode) +(package! docker :pin "baba7f72ea") +(package! docker-tramp :pin "8e2b671eff") +(package! dockerfile-mode :pin "d31f7685eb") diff --git a/modules/tools/editorconfig/config.el b/modules/tools/editorconfig/config.el index 3c4a6d643..abcac7512 100644 --- a/modules/tools/editorconfig/config.el +++ b/modules/tools/editorconfig/config.el @@ -51,10 +51,5 @@ specified by editorconfig." (gethash 'indent_size props)) (setq doom-inhibit-indent-detection 'editorconfig)))) - ;; Editorconfig makes indentation too rigid in Lisp modes, so tell - ;; editorconfig to ignore indentation there. The dynamic indentation support - ;; built into Emacs is superior. - (setq editorconfig-lisp-use-default-indent t) - ;; (editorconfig-mode +1)) diff --git a/modules/tools/editorconfig/packages.el b/modules/tools/editorconfig/packages.el index 5fb9c477e..a0c8feaa4 100644 --- a/modules/tools/editorconfig/packages.el +++ b/modules/tools/editorconfig/packages.el @@ -1,4 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/editorconfig/packages.el -(package! editorconfig :recipe (:nonrecursive t)) +(package! editorconfig + :recipe (:nonrecursive t) + :pin "5c67d22a74") diff --git a/modules/tools/ein/autoload/hydra.el b/modules/tools/ein/autoload/hydra.el index 3bbba5fcd..e743c7e24 100644 --- a/modules/tools/ein/autoload/hydra.el +++ b/modules/tools/ein/autoload/hydra.el @@ -2,7 +2,7 @@ ;;;###if (featurep! :ui hydra) ;;;###autoload (autoload '+ein/hydra/body "tools/ein/autoload/hydra" nil t) -(defhydra +ein/hydra (:hint t :color red) +(defhydra +ein/hydra (:hint nil :color red) " Operations on Cells^^^^^^ Other ----------------------------^^^^^^ ----------------------------------^^^^ diff --git a/modules/tools/ein/packages.el b/modules/tools/ein/packages.el index 2a449f2cc..51b383f6e 100644 --- a/modules/tools/ein/packages.el +++ b/modules/tools/ein/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/ein/packages.el -(package! ein) -(package! avy) +(package! ein :pin "bb97c11d11") +(package! avy :pin "cf95ba9582") diff --git a/modules/tools/eval/autoload/eval.el b/modules/tools/eval/autoload/eval.el index 41be372c4..6ccfdcb3b 100644 --- a/modules/tools/eval/autoload/eval.el +++ b/modules/tools/eval/autoload/eval.el @@ -1,27 +1,24 @@ ;;; tools/eval/autoload/eval.el -*- lexical-binding: t; -*- ;;;###autoload -(defun +eval-display-results-in-popup (output &optional source-buffer) +(defun +eval-display-results-in-popup (output &optional _source-buffer) "Display OUTPUT in a popup buffer." - (if (with-temp-buffer - (insert output) - (>= (count-lines (point-min) (point-max)) - +eval-popup-min-lines)) - (let ((output-buffer (get-buffer-create "*doom eval*")) - (origin (selected-window))) - (with-current-buffer output-buffer - (setq-local scroll-margin 0) - (erase-buffer) - (insert output) - (goto-char (point-min)) - (if (fboundp '+word-wrap-mode) - (+word-wrap-mode +1) - (visual-line-mode +1))) - (when-let (win (display-buffer output-buffer)) - (fit-window-to-buffer win)) - (select-window origin) - output-buffer) - (message "%s" output))) + (let ((output-buffer (get-buffer-create "*doom eval*")) + (origin (selected-window))) + (with-current-buffer output-buffer + (setq-local scroll-margin 0) + (erase-buffer) + (insert output) + (goto-char (point-min)) + (if (fboundp '+word-wrap-mode) + (+word-wrap-mode +1) + (visual-line-mode +1))) + (when-let (win (display-buffer output-buffer)) + (fit-window-to-buffer + win (/ (frame-height) 2) + nil (/ (frame-width) 2))) + (select-window origin) + output-buffer)) ;;;###autoload (defun +eval-display-results-in-overlay (output &optional source-buffer) @@ -40,8 +37,14 @@ (funcall (if (or current-prefix-arg (with-temp-buffer (insert output) - (>= (count-lines (point-min) (point-max)) - +eval-popup-min-lines)) + (or (>= (count-lines (point-min) (point-max)) + +eval-popup-min-lines) + (>= (string-width + (buffer-substring (point-min) + (save-excursion + (goto-char (point-min)) + (line-end-position)))) + (window-width)))) (not (require 'eros nil t))) #'+eval-display-results-in-popup #'+eval-display-results-in-overlay) diff --git a/modules/tools/eval/autoload/evil.el b/modules/tools/eval/autoload/evil.el index 218b12ce6..13085c6bc 100644 --- a/modules/tools/eval/autoload/evil.el +++ b/modules/tools/eval/autoload/evil.el @@ -16,8 +16,8 @@ (+eval/region-and-replace beg end)) ;;;###autoload (autoload '+eval:repl "tools/eval/autoload/evil" nil t) -(evil-define-operator +eval:repl (beg end &optional bang) +(evil-define-operator +eval:repl (_beg _end) "Open REPL and send the current selection to it." :move-point nil - (interactive "") + (interactive "") (+eval/open-repl-other-window)) diff --git a/modules/tools/eval/autoload/repl.el b/modules/tools/eval/autoload/repl.el index 7db09a522..3cf83a8c2 100644 --- a/modules/tools/eval/autoload/repl.el +++ b/modules/tools/eval/autoload/repl.el @@ -49,7 +49,7 @@ buffer))) (defun +eval-open-repl (prompt-p &optional displayfn) - (cl-destructuring-bind (mode fn . plist) + (cl-destructuring-bind (_mode fn . plist) (or (assq major-mode +eval-repls) (list)) (when (or (not fn) prompt-p) diff --git a/modules/tools/eval/autoload/settings.el b/modules/tools/eval/autoload/settings.el index e6cbf0814..90116eaba 100644 --- a/modules/tools/eval/autoload/settings.el +++ b/modules/tools/eval/autoload/settings.el @@ -37,8 +37,10 @@ recognized: "Alist mapping major modes to interactive runner functions.") ;;;###autodef -(defun set-eval-handler! (mode command) - "Define a code evaluator for major mode MODE with `quickrun'. +(defun set-eval-handler! (modes command) + "Define a code evaluator for major mode MODES with `quickrun'. + +MODES can be list of major mode symbols, or a single one. 1. If MODE is a string and COMMAND is the string, MODE is a file regexp and COMMAND is a string key for an entry in `quickrun-file-alist'. @@ -50,17 +52,18 @@ recognized: 4. If MODE is not a string and COMMANd is a symbol, add it to `+eval-runners', which is used by `+eval/region'." (declare (indent defun)) - (cond ((symbolp command) - (push (cons mode command) +eval-runners)) - ((stringp command) - (after! quickrun - (push (cons mode command) - (if (stringp mode) - quickrun-file-alist - quickrun--major-mode-alist)))) - ((listp command) - (after! quickrun - (quickrun-add-command - (or (cdr (assq mode quickrun--major-mode-alist)) - (string-remove-suffix "-mode" (symbol-name mode))) - command :mode mode))))) + (dolist (mode (doom-enlist modes)) + (cond ((symbolp command) + (push (cons mode command) +eval-runners)) + ((stringp command) + (after! quickrun + (push (cons mode command) + (if (stringp mode) + quickrun-file-alist + quickrun--major-mode-alist)))) + ((listp command) + (after! quickrun + (quickrun-add-command + (or (cdr (assq mode quickrun--major-mode-alist)) + (string-remove-suffix "-mode" (symbol-name mode))) + command :mode mode)))))) diff --git a/modules/tools/eval/config.el b/modules/tools/eval/config.el index 0ab3238aa..280bcd348 100644 --- a/modules/tools/eval/config.el +++ b/modules/tools/eval/config.el @@ -68,23 +68,32 @@ buffer rather than an overlay on the line at point or the minibuffer.") (with-selected-window win (goto-char (point-min)))))) - ;; Display evaluation results in an overlay next to the cursor. If the output - ;; is more than 4 lines long, it is displayed in a popup. + ;; Display evaluation results in an overlay at the end of the current line. If + ;; the output is more than `+eval-popup-min-lines' (4) lines long, it is + ;; displayed in a popup. (when (featurep! +overlay) + (defadvice! +eval--show-output-in-overlay-a (fn) + :filter-return #'quickrun--make-sentinel + (lambda (process event) + (funcall fn process event) + (with-current-buffer quickrun--buffer-name + (when (> (buffer-size) 0) + (+eval-display-results + (string-trim (buffer-string)) + quickrun--original-buffer))))) + + ;; Suppress quickrun's popup window because we're using an overlay instead. (defadvice! +eval--inhibit-quickrun-popup-a (buf cb) :override #'quickrun--pop-to-buffer (setq quickrun--original-buffer (current-buffer)) - (with-current-buffer buf - (setq quickrun-option-outputter #'ignore) - (funcall cb))) + (save-window-excursion + (with-current-buffer (pop-to-buffer buf) + (setq quickrun-option-outputter #'ignore) + (funcall cb)))) - (advice-add #'quickrun--recenter :override #'ignore) - (add-hook! 'quickrun-after-run-hook - (defun +eval-display-in-popup-overlay-h () - (+eval-display-results - (with-current-buffer quickrun--buffer-name - (string-trim (buffer-string))) - quickrun--original-buffer))))) + ;; HACK Without this, `+eval--inhibit-quickrun-popup-a' throws a + ;; window-live-p error because no window exists to be recentered! + (advice-add #'quickrun--recenter :override #'ignore))) (use-package! eros diff --git a/modules/tools/eval/packages.el b/modules/tools/eval/packages.el index ba755f7c2..249245705 100644 --- a/modules/tools/eval/packages.el +++ b/modules/tools/eval/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/eval/packages.el -(package! quickrun) +(package! quickrun :pin "55bbe5d54b") (when (featurep! +overlay) - (package! eros)) + (package! eros :pin "dd89102792")) diff --git a/modules/tools/flycheck/autoload.el b/modules/tools/flycheck/autoload.el deleted file mode 100644 index 4f3ea2929..000000000 --- a/modules/tools/flycheck/autoload.el +++ /dev/null @@ -1,13 +0,0 @@ -;;; tools/flycheck/autoload.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun +flycheck-init-popups-h () - "Activate `flycheck-posframe-mode' if available and in GUI Emacs. -Activate `flycheck-popup-tip-mode' otherwise. -Do nothing if `lsp-ui-mode' is active and `lsp-ui-sideline-enable' is non-nil." - (unless (and (bound-and-true-p lsp-ui-mode) - lsp-ui-sideline-enable) - (if (and (fboundp 'flycheck-posframe-mode) - (display-graphic-p)) - (flycheck-posframe-mode +1) - (flycheck-popup-tip-mode +1)))) diff --git a/modules/tools/flycheck/packages.el b/modules/tools/flycheck/packages.el deleted file mode 100644 index b9bcdfd82..000000000 --- a/modules/tools/flycheck/packages.el +++ /dev/null @@ -1,7 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; tools/flycheck/packages.el - -(package! flycheck) -(package! flycheck-popup-tip) -(when (featurep! +childframe) - (package! flycheck-posframe)) diff --git a/modules/tools/flyspell/autoload.el b/modules/tools/flyspell/autoload.el deleted file mode 100644 index e33ab0df4..000000000 --- a/modules/tools/flyspell/autoload.el +++ /dev/null @@ -1,27 +0,0 @@ -;;; tools/flyspell/autoload.el -*- lexical-binding: t; -*- - -;;;###autodef -(defalias 'flyspell-mode! #'flyspell-mode) - -(defvar +flyspell--predicate-alist nil - "TODO") - -;;;###autodef -(defun set-flyspell-predicate! (modes predicate) - "TODO" - (declare (indent defun)) - (dolist (mode (doom-enlist modes) +flyspell--predicate-alist) - (add-to-list '+flyspell--predicate-alist (cons mode predicate)))) - -;;;###autoload -(defun +flyspell-init-predicate-h () - "TODO" - (when-let (pred (assq major-mode +flyspell--predicate-alist)) - (setq-local flyspell-generic-check-word-predicate (cdr pred)))) - -;;;###autoload -(defun +flyspell-correction-at-point-p (&optional point) - "TODO" - (cl-loop for ov in (overlays-at (or point (point))) - if (overlay-get ov 'flyspell-overlay) - return t)) diff --git a/modules/tools/flyspell/doctor.el b/modules/tools/flyspell/doctor.el deleted file mode 100644 index 9d1cb3286..000000000 --- a/modules/tools/flyspell/doctor.el +++ /dev/null @@ -1,4 +0,0 @@ - -(unless (or (executable-find "aspell") - (executable-find "hunspell")) - (warn! "Could not find aspell or hunspell. Flyspell will fall back to ispell, which may not work.")) diff --git a/modules/tools/flyspell/packages.el b/modules/tools/flyspell/packages.el deleted file mode 100644 index efb81d804..000000000 --- a/modules/tools/flyspell/packages.el +++ /dev/null @@ -1,9 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; tools/flyspell/packages.el - -(package! flyspell-correct) -(cond ((featurep! :completion ivy) - (package! flyspell-correct-ivy)) - ((featurep! :completion helm) - (package! flyspell-correct-helm)) - ((package! flyspell-correct-popup))) diff --git a/modules/tools/gist/packages.el b/modules/tools/gist/packages.el index de58b4035..44680cb30 100644 --- a/modules/tools/gist/packages.el +++ b/modules/tools/gist/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/gist/packages.el -(package! gist) +(package! gist :pin "314fe6ab80") diff --git a/modules/tools/lookup/README.org b/modules/tools/lookup/README.org index 53d7feb4b..b69d6b25e 100644 --- a/modules/tools/lookup/README.org +++ b/modules/tools/lookup/README.org @@ -10,6 +10,7 @@ - [[#prerequisites][Prerequisites]] - [[#macos][MacOS]] - [[#arch-linux][Arch Linux]] + - [[#nixos][NixOS]] - [[#features][Features]] - [[#jump-to-definition][Jump to definition]] - [[#find-references][Find references]] @@ -32,8 +33,11 @@ up definitions, references and documentation. + Documentation lookup for a variety of online sources (like devdocs.io, stackoverflow or youtube). + Integration with Dash.app docsets. ++ Support for online (and offline) dictionaries and thesauruses. ** Module Flags ++ ~+dictionary~ Enable word definition and thesaurus lookup functionality. + + ~+offline~ Install and prefer offline dictionary/thesaurus. + ~+docsets~ Enable integration with Dash.app docsets. ** Plugins @@ -44,13 +48,13 @@ up definitions, references and documentation. * Prerequisites This module has several soft dependencies: -+ ~the_silver_searcher~ or ~ripgrep~ (recommended) as a last-resort fallback for - jump-to-definition/find-references. ++ ~ripgrep~ as a last-resort fallback for jump-to-definition/find-references. + ~sqlite3~ for Dash docset support. ++ ~wordnet~ for offline dictionary and thesaurus support. ** MacOS #+BEGIN_SRC sh -brew install the_silver_searcher ripgrep +brew install ripgrep wordnet # An older version of sqlite is included in MacOS. If it causes you problems (and # folks have reported it will), install it through homebrew: @@ -62,7 +66,17 @@ export PATH="/usr/local/opt/sqlite/bin:$PATH" ** Arch Linux #+BEGIN_SRC sh -sudo pacman -S sqlite the_silver_searcher ripgrep +sudo pacman -S sqlite ripgrep +sudo yay -S wordnet-cli +#+END_SRC + +** NixOS +#+BEGIN_SRC nix +environment.systemPackages = with pkgs; [ + ripgrep + sqlite + wordnet +]; #+END_SRC * Features @@ -77,7 +91,7 @@ following sources before giving up: ~:lookup~ setting (see "Configuration" section). 2. Any available xref backends. 3. ~dumb-jump~ (a text search with aides to reduce false positives). -3. An ordinary project-wide text search with ripgrep or the_silver_searcher. +3. An ordinary project-wide text search with ripgrep. 5. If ~evil-mode~ is active, use ~evil-goto-definition~, which preforms a simple text search within the current buffer. @@ -93,7 +107,7 @@ will try: 1. Whatever ~:references~ function is registered for the current buffer with the ~:lookup~ setting (see "Configuration" section). 2. Any available xref backends. -3. An ordinary project-wide text search with ripgrep or the_silver_searcher. +3. An ordinary project-wide text search with ripgrep. If there are multiple results, you will be prompted to select one. diff --git a/modules/tools/lookup/autoload/docsets.el b/modules/tools/lookup/autoload/docsets.el index 0e20f061b..fffbe58a5 100644 --- a/modules/tools/lookup/autoload/docsets.el +++ b/modules/tools/lookup/autoload/docsets.el @@ -90,7 +90,7 @@ installed with `dash-docs-install-docset'." (if arg (dash-docs-installed-docsets) (cl-remove-if-not #'dash-docs-docset-path (or docsets dash-docs-docsets)))) - (query (or query (+lookup-symbol-or-region) ""))) + (query (doom-thing-at-point-or-region query))) (doom-log "Searching docsets %s" dash-docs-docsets) (cond ((featurep! :completion helm) (helm-dash query)) diff --git a/modules/tools/lookup/autoload/lookup.el b/modules/tools/lookup/autoload/lookup.el index 46726eca0..7b012e256 100644 --- a/modules/tools/lookup/autoload/lookup.el +++ b/modules/tools/lookup/autoload/lookup.el @@ -130,20 +130,22 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (defun +lookup--jump-to (prop identifier &optional display-fn arg) (let* ((origin (point-marker)) - (handlers (plist-get (list :definition '+lookup-definition-functions - :references '+lookup-references-functions - :documentation '+lookup-documentation-functions - :file '+lookup-file-functions) - prop)) + (handlers + (plist-get (list :definition '+lookup-definition-functions + :references '+lookup-references-functions + :documentation '+lookup-documentation-functions + :file '+lookup-file-functions) + prop)) (result (if arg - (if-let* - ((handler (intern-soft - (completing-read "Select lookup handler: " - (delete-dups - (remq t (append (symbol-value handlers) - (default-value handlers)))) - nil t)))) + (if-let + (handler + (intern-soft + (completing-read "Select lookup handler: " + (delete-dups + (remq t (append (symbol-value handlers) + (default-value handlers)))) + nil t))) (+lookup--run-handlers handler identifier origin) (user-error "No lookup handler selected")) (run-hook-wrapped handlers #'+lookup--run-handlers identifier origin)))) @@ -160,19 +162,6 @@ This can be passed nil as its second argument to unset handlers for MODES. e.g. (better-jumper-set-jump (marker-position origin))) result))) -;;;###autoload -(defun +lookup-symbol-or-region (&optional initial) - "Grab the symbol at point or selected region." - (cond ((stringp initial) - initial) - ((use-region-p) - (buffer-substring-no-properties (region-beginning) - (region-end))) - ((require 'xref nil t) - ;; A little smarter than using `symbol-at-point', though in most cases, - ;; xref ends up using `symbol-at-point' anyway. - (xref-backend-identifier-at-point (xref-find-backend))))) - ;; ;;; Lookup backends @@ -243,7 +232,7 @@ Each function in `+lookup-definition-functions' is tried until one changes the point or current buffer. Falls back to dumb-jump, naive ripgrep/the_silver_searcher text search, then `evil-goto-definition' if evil-mode is active." - (interactive (list (+lookup-symbol-or-region) + (interactive (list (doom-thing-at-point-or-region) current-prefix-arg)) (cond ((null identifier) (user-error "Nothing under point")) ((+lookup--jump-to :definition identifier nil arg)) @@ -256,7 +245,7 @@ evil-mode is active." Tries each function in `+lookup-references-functions' until one changes the point and/or current buffer. Falls back to a naive ripgrep/the_silver_searcher search otherwise." - (interactive (list (+lookup-symbol-or-region) + (interactive (list (doom-thing-at-point-or-region) current-prefix-arg)) (cond ((null identifier) (user-error "Nothing under point")) ((+lookup--jump-to :references identifier nil arg)) @@ -269,7 +258,7 @@ search otherwise." First attempts the :documentation handler specified with `set-lookup-handlers!' for the current mode/buffer (if any), then falls back to the backends in `+lookup-documentation-functions'." - (interactive (list (+lookup-symbol-or-region) + (interactive (list (doom-thing-at-point-or-region) current-prefix-arg)) (cond ((+lookup--jump-to :documentation identifier #'pop-to-buffer arg)) ((user-error "Couldn't find documentation for %S" identifier)))) @@ -290,33 +279,56 @@ Otherwise, falls back on `find-file-at-point'." (or (ffap-guesser) (ffap-read-file-or-url (if ffap-url-regexp "Find file or URL: " "Find file: ") - (+lookup-symbol-or-region)))))) + (doom-thing-at-point-or-region)))))) (require 'ffap) - (cond ((not path) - (call-interactively #'find-file-at-point)) + (cond ((and path + buffer-file-name + (file-equal-p path buffer-file-name) + (user-error "Already here"))) - ((ffap-url-p path) - (find-file-at-point path)) + ((+lookup--jump-to :file path)) - ((not (+lookup--jump-to :file path)) - (let ((fullpath (doom-path path))) - (when (and buffer-file-name (file-equal-p fullpath buffer-file-name)) - (user-error "Already here")) - (let* ((insert-default-directory t) - (project-root (doom-project-root)) - (ffap-file-finder - (cond ((not (doom-glob fullpath)) - #'find-file) - ((ignore-errors (file-in-directory-p fullpath project-root)) - (lambda (dir) - (let* ((default-directory dir) - projectile-project-name - projectile-project-root - (projectile-project-root-cache (make-hash-table :test 'equal)) - (file (projectile-completing-read "Find file: " - (projectile-current-project-files) - :initial-input path))) - (find-file (expand-file-name file (doom-project-root))) - (run-hooks 'projectile-find-file-hook)))) - (#'doom-project-browse)))) - (find-file-at-point path)))))) + ((stringp path) (find-file-at-point path)) + + ((call-interactively #'find-file-at-point)))) + + +;; +;;; Dictionary + +;;;###autoload +(defun +lookup/dictionary-definition (identifier &optional arg) + "Look up the definition of the word at point (or selection)." + (interactive + (list (or (doom-thing-at-point-or-region 'word) + (read-string "Look up in dictionary: ")) + current-prefix-arg)) + (message "Looking up definition for %S" identifier) + (cond ((and IS-MAC (require 'osx-dictionary nil t)) + (osx-dictionary--view-result identifier)) + ((and +lookup-dictionary-prefer-offline + (require 'wordnut nil t)) + (unless (executable-find wordnut-cmd) + (user-error "Couldn't find %S installed on your system" + wordnut-cmd)) + (wordnut-search identifier)) + ((require 'define-word nil t) + (define-word identifier nil arg)) + ((user-error "No dictionary backend is available")))) + +;;;###autoload +(defun +lookup/synonyms (identifier &optional _arg) + "Look up and insert a synonym for the word at point (or selection)." + (interactive + (list (doom-thing-at-point-or-region 'word) ; TODO actually use this + current-prefix-arg)) + (message "Looking up synonyms for %S" identifier) + (cond ((and +lookup-dictionary-prefer-offline + (require 'synosaurus nil t)) + (unless (executable-find synosaurus-wordnet--command) + (user-error "Couldn't find %S installed on your system" + synosaurus-wordnet--command)) + (synosaurus-choose-and-replace)) + ((require 'powerthesaurus nil t) + (powerthesaurus-lookup-word-dwim)) + ((user-error "No thesaurus backend is available")))) diff --git a/modules/tools/lookup/autoload/online.el b/modules/tools/lookup/autoload/online.el index 89f49e1e4..b7bd0b4a0 100644 --- a/modules/tools/lookup/autoload/online.el +++ b/modules/tools/lookup/autoload/online.el @@ -25,8 +25,8 @@ argument is non-nil)." (+lookup--online-provider (not current-prefix-arg)))) ;;;###autoload -(defun +lookup/online (search &optional provider) - "Looks up SEARCH (a string) in you browser using PROVIDER. +(defun +lookup/online (query provider) + "Looks up QUERY (a string) in you browser usin PROVIDER. PROVIDER should be a key of `+lookup-provider-url-alist'. @@ -35,28 +35,32 @@ provider from `+lookup-provider-url-alist'. On consecutive uses, the last provider will be reused. If the universal argument is supplied, always prompt for the provider." (interactive - (let ((provider (+lookup--online-provider current-prefix-arg))) - (list (or (and (use-region-p) - (buffer-substring-no-properties (region-beginning) - (region-end))) - (read-string (format "Search for (on %s): " provider) - (thing-at-point 'symbol t))) - provider))) - (condition-case-unless-debug e - (let ((url (cdr (assoc provider +lookup-provider-url-alist)))) - (unless url - (user-error "'%s' is an invalid search engine" provider)) - (when (or (functionp url) (symbolp url)) - (setq url (funcall url))) - (cl-assert (stringp url)) - (when (string-empty-p search) - (user-error "The search query is empty")) - (funcall +lookup-open-url-fn (format url (url-encode-url search)))) - (error - (setq +lookup--last-provider - (delq (assq major-mode +lookup--last-provider) - +lookup--last-provider)) - (signal (car e) (cdr e))))) + (list (if (use-region-p) (doom-thing-at-point-or-region)) + (+lookup--online-provider current-prefix-arg))) + (let ((backend (cl-find-if (lambda (x) (or (stringp x) (fboundp x))) + (cdr (assoc provider +lookup-provider-url-alist))))) + (unless (and (functionp backend) + (funcall backend query)) + (unless backend + (user-error "%S is an invalid query engine backend for %S provider" + backend provider)) + (cl-check-type backend (or string function)) + (condition-case-unless-debug e + (progn + (unless query + (setq query + (read-string (format "Search for (on %s): " provider) + (thing-at-point 'symbol t)))) + (when (or (functionp backend) (symbolp backend)) + (setq backend (funcall backend))) + (when (string-empty-p query) + (user-error "The query query is empty")) + (funcall +lookup-open-url-fn (format backend (url-encode-url query)))) + (error + (setq +lookup--last-provider + (delq (assq major-mode +lookup--last-provider) + +lookup--last-provider)) + (signal (car e) (cdr e))))))) ;;;###autoload (defun +lookup/online-select () @@ -64,3 +68,32 @@ for the provider." (interactive) (let ((current-prefix-arg t)) (call-interactively #'+lookup/online))) + + +;; +;;; Special provider frontends + +(defvar ivy-initial-inputs-alist) +(defvar counsel-search-engine) +;;;###autoload +(defun +lookup--online-backend-google (query) + "Search google, starting with QUERY, with live autocompletion." + (cond ((fboundp 'counsel-search) + (let ((ivy-initial-inputs-alist `((t . ,query))) + (counsel-search-engine 'google)) + (call-interactively #'counsel-search) + t)) + ((require 'helm-net nil t) + (helm :sources 'helm-source-google-suggest + :buffer "*helm google*" + :input query) + t))) + +;;;###autoload +(defun +lookup--online-backend-duckduckgo (query) + "Search duckduckgo, starting with QUERY, with live autocompletion." + (cond ((fboundp 'counsel-search) + (let ((ivy-initial-inputs-alist `((t . ,query))) + (counsel-search-engine 'ddg)) + (call-interactively #'counsel-search) + t)))) diff --git a/modules/tools/lookup/config.el b/modules/tools/lookup/config.el index 6dc5adada..59a5e93c4 100644 --- a/modules/tools/lookup/config.el +++ b/modules/tools/lookup/config.el @@ -13,21 +13,27 @@ ;; `dumb-jump' to find what you want. (defvar +lookup-provider-url-alist - (append '(("Google" . "https://google.com/search?q=%s") - ("Google images" . "https://www.google.com/images?q=%s") - ("Google maps" . "https://maps.google.com/maps?q=%s") - ("Project Gutenberg" . "http://www.gutenberg.org/ebooks/search/?query=%s") - ("DuckDuckGo" . "https://duckduckgo.com/?q=%s") - ("DevDocs.io" . "https://devdocs.io/#q=%s") - ("StackOverflow" . "https://stackoverflow.com/search?q=%s") - ("Github" . "https://github.com/search?ref=simplesearch&q=%s") - ("Youtube" . "https://youtube.com/results?aq=f&oq=&search_query=%s") - ("Wolfram alpha" . "https://wolframalpha.com/input/?i=%s") - ("Wikipedia" . "https://wikipedia.org/search-redirect.php?language=en&go=Go&search=%s")) + (append '(("Doom Emacs issues" "https://github.com/hlissner/doom-emacs/issues?q=is%%3Aissue+%s") + ("Google" +lookup--online-backend-google "https://google.com/search?q=%s") + ("Google images" "https://www.google.com/images?q=%s") + ("Google maps" "https://maps.google.com/maps?q=%s") + ("Project Gutenberg" "http://www.gutenberg.org/ebooks/search/?query=%s") + ("DuckDuckGo" +lookup--online-backend-duckduckgo "https://duckduckgo.com/?q=%s") + ("DevDocs.io" "https://devdocs.io/#q=%s") + ("StackOverflow" "https://stackoverflow.com/search?q=%s") + ("Github" "https://github.com/search?ref=simplesearch&q=%s") + ("Youtube" "https://youtube.com/results?aq=f&oq=&search_query=%s") + ("Wolfram alpha" "https://wolframalpha.com/input/?i=%s") + ("Wikipedia" "https://wikipedia.org/search-redirect.php?language=en&go=Go&search=%s")) (when (featurep! :lang rust) - '(("Rust Docs" . "https://doc.rust-lang.org/edition-guide/?search=%s")))) - "An alist that maps online resources to their search url or a function that -produces an url. Used by `+lookup/online'.") + '(("Rust Docs" "https://doc.rust-lang.org/edition-guide/?search=%s")))) + "An alist that maps online resources to either: + + 1. A search url (needs on '%s' to substitute with an url encoded query), + 2. A non-interactive function that returns the search url in #1, + 3. An interactive command that does its own search for that provider. + +Used by `+lookup/online'.") (defvar +lookup-open-url-fn #'browse-url "Function to use to open search urls.") @@ -79,6 +85,17 @@ If the argument is interactive (satisfies `commandp'), it is called with argument: the identifier at point. See `set-lookup-handlers!' about adding to this list.") +(defvar +lookup-dictionary-prefer-offline (featurep! +offline) + "If non-nil, look up dictionaries online. + +Setting this to nil will force it to use offline backends, which may be less +than perfect, but available without an internet connection. + +Used by `+lookup/dictionary-definition' and `+lookup/synonyms'. + +For `+lookup/dictionary-definition', this is ignored on Mac, where Emacs users +Dictionary.app behind the scenes to get definitions.") + ;; ;;; dumb-jump @@ -160,3 +177,26 @@ See https://github.com/magit/ghub/issues/81" (use-package! counsel-dash :when (featurep! :completion ivy))) + + +;; +;;; Dictionary integration + +(use-package! define-word + :when (featurep! +dictionary) + :unless IS-MAC + :defer t + :config + (setq define-word-displayfn-alist + (cl-loop for (service . _) in define-word-services + collect (cons service #'+eval-display-results-in-popup)))) + + +(when (featurep! +dictionary) + (define-key! text-mode-map + [remap +lookup/definition] #'+lookup/dictionary-definition + [remap +lookup/references] #'+lookup/synonyms)) + + +;;;###package synosaurus +(setq synosaurus-choose-method 'default) ; use ivy/helm instead of ido diff --git a/modules/tools/lookup/packages.el b/modules/tools/lookup/packages.el index 1b25d51c0..5509fb090 100644 --- a/modules/tools/lookup/packages.el +++ b/modules/tools/lookup/packages.el @@ -1,22 +1,32 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/lookup/packages.el -;; `dumb-jump' uses the `helm-build-sync-source' macro, but this requires helm -;; be loaded before `dumb-jump' is byte-compiled during installation. To ensure -;; this, we declare helm before dumb-jump. +;; HACK `dumb-jump' uses the `helm-build-sync-source' macro, but this requires +;; helm be loaded before `dumb-jump' is byte-compiled during installation. +;; To ensure this, we declare helm before dumb-jump. (when (featurep! :completion helm) (package! helm)) ;; -(package! dumb-jump) +(package! dumb-jump :pin "b5185e3368") (when (featurep! :completion ivy) - (package! ivy-xref)) + (package! ivy-xref :pin "3d4c35fe2b")) (when (featurep! :completion helm) - (package! helm-xref)) + (package! helm-xref :pin "6b4a8bd91f")) (when (featurep! +docsets) - (package! dash-docs) + (package! dash-docs :pin "111fd9b970") (when (featurep! :completion helm) - (package! helm-dash)) + (package! helm-dash :pin "7f853bd34d")) (when (featurep! :completion ivy) - (package! counsel-dash))) + (package! counsel-dash :pin "370d5f6f14"))) + +(when (featurep! +dictionary) + (if IS-MAC + (package! osx-dictionary :pin "1b79ff64c7") + (package! define-word :pin "d8c76d503b") + (package! powerthesaurus :pin "81a262ec0c") + (package! request) + (when (featurep! +offline) + (package! wordnut :pin "feac531404") + (package! synosaurus :pin "14d34fc92a")))) diff --git a/modules/tools/lsp/README.org b/modules/tools/lsp/README.org index e5faee106..8abbbd92e 100644 --- a/modules/tools/lsp/README.org +++ b/modules/tools/lsp/README.org @@ -9,6 +9,7 @@ - [[#plugins][Plugins]] - [[#prerequisites][Prerequisites]] - [[#features][Features]] + - [[#lsp-powered-project-search][LSP-powered project search]] - [[#configuration][Configuration]] - [[#troubleshooting][Troubleshooting]] @@ -67,6 +68,13 @@ them [[https://github.com/emacs-lsp/lsp-mode#supported-languages][in the lsp-mod targeted language will contain brief instructions as well. * TODO Features +** LSP-powered project search +When =:completion ivy= or =:completion helm= is active, LSP is used to search a +symbol indexed by the LSP server : +| Keybind | Description | +|-----------+-------------------------------------| +| =SPC c j= | Jump to symbol in current workspace | +| =SPC c J= | Jump to symbol in any workspace | * TODO Configuration diff --git a/modules/tools/lsp/autoload.el b/modules/tools/lsp/autoload.el index ae45c1ef1..5d9ed2c52 100644 --- a/modules/tools/lsp/autoload.el +++ b/modules/tools/lsp/autoload.el @@ -2,3 +2,13 @@ ;;;###autodef (defalias 'lsp! #'lsp-deferred) + +;;;###autoload +(defun +lsp/uninstall-server (dir) + "Delete a LSP server from `lsp-server-install-dir'." + (interactive + (list (read-directory-name "Uninstall LSP server: " lsp-server-install-dir nil t))) + (unless (file-directory-p dir) + (user-error "Couldn't find %S directory" dir)) + (delete-directory dir 'recursive) + (message "Uninstalled %S" (file-name-nondirectory dir))) diff --git a/modules/tools/lsp/config.el b/modules/tools/lsp/config.el index 8c323994f..a1df04f25 100644 --- a/modules/tools/lsp/config.el +++ b/modules/tools/lsp/config.el @@ -11,7 +11,7 @@ This can be a single company backend or a list thereof. It can be anything ;;; Packages (use-package! lsp-mode - :defer t + :commands lsp-install-server :init (setq lsp-session-file (concat doom-etc-dir "lsp-session")) ;; Don't prompt the user for the project root every time we open a new @@ -22,25 +22,28 @@ This can be a single company backend or a list thereof. It can be anything (setq lsp-keep-workspace-alive nil) ;; For `lsp-clients' - (setq lsp-fsharp-server-install-dir (concat doom-etc-dir "lsp-fsharp/") - lsp-groovy-server-install-dir (concat doom-etc-dir "lsp-groovy/") + (setq lsp-server-install-dir (concat doom-etc-dir "lsp/") + lsp-groovy-server-install-dir (concat lsp-server-install-dir "lsp-groovy/") lsp-intelephense-storage-path (concat doom-cache-dir "lsp-intelephense/")) :config + (when lsp-auto-configure + (mapc (lambda (package) (require package nil t)) + lsp-client-packages)) + (set-lookup-handlers! 'lsp-mode :async t :documentation 'lsp-describe-thing-at-point :definition 'lsp-find-definition :references 'lsp-find-references) (defvar +lsp--deferred-shutdown-timer nil) - (defadvice! +lsp-defer-server-shutdown-a (orig-fn) + (defadvice! +lsp-defer-server-shutdown-a (orig-fn &optional restart) "Defer server shutdown for a few seconds. This gives the user a chance to open other project files before the server is auto-killed (which is usually an expensive process)." :around #'lsp--shutdown-workspace (if (or lsp-keep-workspace-alive - (eq (lsp--workspace-shutdown-action lsp--cur-workspace) - 'restart)) + restart) (funcall orig-fn) (when (timerp +lsp--deferred-shutdown-timer) (cancel-timer +lsp--deferred-shutdown-timer)) @@ -67,20 +70,22 @@ auto-killed (which is usually an expensive process)." (defadvice! +lsp-init-a (&optional arg) "Enable `lsp-mode' in the current buffer. -Meant to be a lighter alternative to `lsp', which is too eager about -initializing lsp-ui-mode, company, yasnippet and flycheck. Instead, these have -been moved out to their respective modules, or these hooks: +Meant to gimp `lsp', which is too eager about installing LSP servers, or +prompting to do so, or complaining about no LSP servers, or initializing +lsp-ui-mode, company, yasnippet and flycheck. We want LSP to work only if the +server is present, and for server installation to be a deliberate act by the +end-user. Also, setting up these other packages are handled by their respective +modules. +Also see: + `+lsp-init-company-h' (on `lsp-mode-hook') + `+lsp-init-ui-flycheck-or-flymake-h' (on `lsp-ui-mode-hook') -Also logs the resolved project root, if found." +This also logs the resolved project root, if found, so we know where we are." :override #'lsp (interactive "P") - (require 'lsp-mode) - (when lsp-auto-configure - (require 'lsp-clients)) (and (buffer-file-name) + (require 'lsp-mode nil t) (setq-local lsp--buffer-workspaces (or (lsp--try-open-in-library-workspace) @@ -88,6 +93,7 @@ Also logs the resolved project root, if found." (equal arg '(4)) (and arg (not (equal arg 1)))))) (prog1 (lsp-mode 1) + (setq-local lsp-buffer-uri (lsp--buffer-uri)) ;; Announce what project root we're using, for diagnostic purposes (if-let (root (lsp--calculate-root (lsp-session) (buffer-file-name))) (lsp--info "Guessed project root is %s" (abbreviate-file-name root)) @@ -113,7 +119,12 @@ Also logs the resolved project root, if found." (lsp--flymake-setup)) ((require 'flycheck nil t) (require 'lsp-ui-flycheck) - (lsp-ui-flycheck-enable t))))) + (let ((old-checker flycheck-checker)) + (lsp-ui-flycheck-enable t) + (when old-checker + (setq-local flycheck-checker old-checker) + (kill-local-variable 'flycheck-check-syntax-automatically))))))) + :config (setq lsp-prefer-flymake nil lsp-ui-doc-max-height 8 @@ -148,3 +159,13 @@ Also logs the resolved project root, if found." (remove-hook 'company-mode-hook #'+lsp-init-company-h t)))) :config (setq company-lsp-cache-candidates 'auto)) ;; cache candidates for better performance + + +(use-package! helm-lsp + :when (featurep! :completion helm) + :commands helm-lsp-workspace-symbol helm-lsp-global-workspace-symbol) + + +(use-package! lsp-ivy + :when (featurep! :completion ivy) + :commands lsp-ivy-workspace-symbol lsp-ivy-global-workspace-symbol) diff --git a/modules/tools/lsp/packages.el b/modules/tools/lsp/packages.el index efcc53bd1..e2f449548 100644 --- a/modules/tools/lsp/packages.el +++ b/modules/tools/lsp/packages.el @@ -1,7 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/lsp/packages.el -(package! lsp-mode) -(package! lsp-ui) +(package! lsp-mode :pin "2fc0963a50") +(package! lsp-ui :pin "e8200e3b72") (when (featurep! :completion company) - (package! company-lsp)) + (package! company-lsp :pin "f921ffa0cd")) +(when (featurep! :completion ivy) + (package! lsp-ivy :pin "78c1429c62")) +(when (featurep! :completion helm) + (package! helm-lsp :pin "6f62659cc5")) diff --git a/modules/tools/macos/autoload.el b/modules/tools/macos/autoload.el index 0aa9f8b53..6aa1f006c 100644 --- a/modules/tools/macos/autoload.el +++ b/modules/tools/macos/autoload.el @@ -49,3 +49,6 @@ ;;;###autoload (autoload '+macos/send-project-to-launchbar "tools/macos/autoload" nil t) (+macos--open-with send-project-to-launchbar "LaunchBar" (or (doom-project-root) default-directory)) + +;;;###autoload (autoload '+macos/open-in-iterm "tools/macos/autoload" nil t) +(+macos--open-with open-in-iterm "iTerm" default-directory) diff --git a/modules/tools/magit/autoload.el b/modules/tools/magit/autoload.el index 3dd37388c..5b5a40bcc 100644 --- a/modules/tools/magit/autoload.el +++ b/modules/tools/magit/autoload.el @@ -3,7 +3,8 @@ ;; HACK Magit complains loudly when it can't determine its own version, which is ;; the case when magit is built through straight. The warning is harmless, ;; however, so we just need it to shut up. -;;;###autoload (advice-add #'magit-version :override #'ignore) +;;;###autoload +(advice-add #'magit-version :override #'ignore) ;;;###autoload (defun +magit-display-buffer-fn (buffer) @@ -40,28 +41,42 @@ ;; -;; Commands +;;; Auto-revert -(defun +magit--refresh-vc-in-buffer (buffer) +(defvar +magit--stale-p nil) + +(defun +magit--revert-buffer (buffer) (with-current-buffer buffer - (when (and vc-mode (fboundp 'vc-refresh-state)) - (vc-refresh-state)) - (when (and (bound-and-true-p git-gutter-mode) - (fboundp '+version-control|update-git-gutter)) - (+version-control|update-git-gutter)) - (setq +magit--vc-is-stale-p nil))) + (kill-local-variable '+magit--stale-p) + (if (buffer-file-name (buffer-base-buffer)) + (and (or (not (buffer-modified-p)) + (y-or-n-p "Version control data is outdated in this buffer, but it is unsaved. Revert anyway?")) + (revert-buffer t t)) + (when (and vc-mode (fboundp 'vc-refresh-state)) + (vc-refresh-state))))) ;;;###autoload -(defvar-local +magit--vc-is-stale-p nil) +(defun +magit-mark-stale-buffers-h () + "Revert all visible buffers and mark buried buffers as stale. + +Stale buffers are reverted when they are switched to, assuming they haven't been +modified." + (dolist (buffer (buffer-list)) + (when (buffer-live-p buffer) + (if (get-buffer-window buffer) + (+magit--revert-buffer buffer) + (with-current-buffer buffer + (setq-local +magit--stale-p t)))))) ;;;###autoload -(defun +magit-refresh-vc-state-maybe-h () +(defun +magit-revert-buffer-maybe-h () "Update `vc' and `git-gutter' if out of date." - (when +magit--vc-is-stale-p - (+magit--refresh-vc-in-buffer (current-buffer)))) + (when +magit--stale-p + (+magit--revert-buffer (current-buffer)))) -;;;###autoload -(add-hook 'doom-switch-buffer-hook #'+magit-refresh-vc-state-maybe-h) + +;; +;;; Commands ;;;###autoload (defun +magit/quit (&optional kill-buffer) @@ -75,12 +90,7 @@ control in buffers." (eq major-mode 'magit-status-mode))) (window-list))) (mapc #'+magit--kill-buffer (magit-mode-get-buffers)) - (dolist (buffer (buffer-list)) - (when (buffer-live-p buffer) - (if (get-buffer-window buffer) - (+magit--refresh-vc-in-buffer buffer) - (with-current-buffer buffer - (setq +magit--vc-is-stale-p t))))))) + (+magit-mark-stale-buffers-h))) (defun +magit--kill-buffer (buf) "TODO" @@ -94,6 +104,14 @@ control in buffers." (kill-process process) (kill-buffer buf))))))) +;;;###autoload +(defun +magit/start-github-review (arg) + (interactive "P") + (call-interactively + (if (or arg (not (featurep 'forge))) + #'github-review-start + #'github-review-forge-pr-at-point))) + (defvar +magit-clone-history nil "History for `+magit/clone' prompt.") ;;;###autoload diff --git a/modules/tools/magit/config.el b/modules/tools/magit/config.el index 483327e7d..d16d1b198 100644 --- a/modules/tools/magit/config.el +++ b/modules/tools/magit/config.el @@ -1,10 +1,5 @@ ;;; tools/magit/config.el -*- lexical-binding: t; -*- -(defvar +magit-default-clone-url "https://github.com/%s/%s" - "The default location for `+magit/clone' to clone relative URLs from. -It is passed a user and repository name.") - - ;; ;;; Packages @@ -12,7 +7,7 @@ It is passed a user and repository name.") :commands magit-file-delete :defer-incrementally (dash f s with-editor git-commit package eieio lv transient) :init - (setq magit-auto-revert-mode nil) ; we do this ourselves + (setq magit-auto-revert-mode nil) ; we do this ourselves further down ;; Must be set early to prevent ~/.emacs.d/transient from being created (setq transient-levels-file (concat doom-etc-dir "transient/levels") transient-values-file (concat doom-etc-dir "transient/values") @@ -26,19 +21,25 @@ It is passed a user and repository name.") ;; formatters. Trust us to know what we're doing. magit-save-repository-buffers nil) - (defadvice! +magit-invalidate-projectile-cache-a (&rest _args) - ;; We ignore the args to `magit-checkout'. + (defadvice! +magit-revert-repo-buffers-deferred-a (&rest _) :after '(magit-checkout magit-branch-and-checkout) - (projectile-invalidate-cache nil)) + ;; Since the project likely now contains new files, best we undo the + ;; projectile cache so it can be regenerated later. + (projectile-invalidate-cache nil) + ;; Use a more efficient strategy to auto-revert buffers whose git state has + ;; changed: refresh the visible buffers immediately... + (+magit-mark-stale-buffers-h)) + ;; ...then refresh the rest only when we switch to them, not all at once. + (add-hook 'doom-switch-buffer-hook #'+magit-revert-buffer-maybe-h) ;; The default location for git-credential-cache is in - ;; ~/.config/git/credential. However, if ~/.git-credential-cache/ exists, then + ;; ~/.cache/git/credential. However, if ~/.git-credential-cache/ exists, then ;; it is used instead. Magit seems to be hardcoded to use the latter, so here ;; we override it to have more correct behavior. (unless (file-exists-p "~/.git-credential-cache/") (setq magit-credential-cache-daemon-socket - (doom-glob (or (getenv "XDG_CONFIG_HOME") - "~/.config/") + (doom-glob (or (getenv "XDG_CACHE_HOME") + "~/.cache/") "git/credential/socket"))) ;; Magit uses `magit-display-buffer-traditional' to display windows, by @@ -68,7 +69,8 @@ It is passed a user and repository name.") (and (derived-mode-p 'magit-mode) (not (eq major-mode 'magit-process-mode)))))) - ;; properly kill leftover magit buffers on quit + ;; Clean up after magit by killing leftover magit buffers and reverting + ;; affected buffers (or at least marking them as need-to-be-reverted). (define-key magit-status-mode-map [remap magit-mode-bury-buffer] #'+magit/quit) ;; Close transient with ESC @@ -79,6 +81,7 @@ It is passed a user and repository name.") ;; We defer loading even further because forge's dependencies will try to ;; compile emacsql, which is a slow and blocking operation. :after-call magit-status + :commands forge-create-pullreq forge-create-issue :init (setq forge-database-file (concat doom-etc-dir "forge/forge-database.sqlite")) :config @@ -116,6 +119,16 @@ ensure it is built when we actually use Forge." (add-hook hook #'forge-bug-reference-setup))))))) +(use-package! github-review + :after magit + :config + (transient-append-suffix 'magit-merge "i" + '("y" "Review pull request" +magit/start-github-review)) + (after! forge + (transient-append-suffix 'forge-dispatch "c u" + '("c r" "Review pull request" +magit/start-github-review)))) + + (use-package! magit-todos :after magit :config @@ -159,4 +172,21 @@ ensure it is built when we actually use Forge." (setcar desc (cdr key)))) (evil-define-key* evil-magit-state git-rebase-mode-map "gj" #'git-rebase-move-line-down - "gk" #'git-rebase-move-line-up))) + "gk" #'git-rebase-move-line-up)) + + ;; HACK Temporarily fix hlissner/doom-emacs#2446. evil-magit binds yy to + ;; evil-yank-line. This command is what Y is bound to in normal mode and + ;; it respects evil-want-Y-yank-to-eol, which is set to t by default (the + ;; default behavior in vim). + (evil-define-operator evil-magit-yank (beg end type register) + :motion evil-line-or-visual-line + :move-point nil + (interactive "") + (evil-yank beg end type register)) + (evil-magit-define-key 'normal 'magit-mode-map "yy" #'evil-magit-yank) + (after! evil-goggles + (pushnew! evil-goggles--commands + '(evil-magit-yank + :face evil-goggles-yank-face + :switch evil-goggles-enable-yank + :advice evil-goggles--generic-async-advice)))) diff --git a/modules/tools/magit/packages.el b/modules/tools/magit/packages.el index c9e45de54..0f9a09ec0 100644 --- a/modules/tools/magit/packages.el +++ b/modules/tools/magit/packages.el @@ -1,9 +1,10 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/magit/packages.el -(when (package! magit) - (package! forge) - (package! magit-gitflow) - (package! magit-todos) +(when (package! magit :pin "8cb6cdf3e4") + (package! forge :pin "fb04716b64") + (package! magit-gitflow :pin "cc41b561ec") + (package! magit-todos :pin "ad5663aa26") + (package! github-review :pin "1de2d6d148") (when (featurep! :editor evil +everywhere) - (package! evil-magit))) + (package! evil-magit :pin "1bfd546be8"))) diff --git a/modules/tools/make/packages.el b/modules/tools/make/packages.el index 422d37597..d389186b5 100644 --- a/modules/tools/make/packages.el +++ b/modules/tools/make/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/make/packages.el -(package! makefile-executor) +(package! makefile-executor :pin "9a7d78f814") diff --git a/modules/tools/pass/packages.el b/modules/tools/pass/packages.el index a65e113a8..f998eb0e1 100644 --- a/modules/tools/pass/packages.el +++ b/modules/tools/pass/packages.el @@ -1,16 +1,17 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/pass/packages.el -(package! pass) -(package! password-store) -(package! password-store-otp) +(package! pass :pin "919d8e3826") +(package! password-store :pin "88936b11af") +(package! password-store-otp :pin "04998c8578") ;; an older version of `auto-source-pass' is built into Emacs 26+, so we must ;; install the new version directly from the source and with a psuedonym. (package! auth-source-pass - :recipe (:host github :repo "DamienCassou/auth-password-store")) + :recipe (:host github :repo "DamienCassou/auth-password-store") + :pin "ff4940c647") (when (featurep! :completion ivy) - (package! ivy-pass)) + (package! ivy-pass :pin "5b523de115")) (when (featurep! :completion helm) - (package! helm-pass)) + (package! helm-pass :pin "ed5798f2d8")) diff --git a/modules/tools/pdf/config.el b/modules/tools/pdf/config.el index b04753da2..a7d862f2a 100644 --- a/modules/tools/pdf/config.el +++ b/modules/tools/pdf/config.el @@ -3,9 +3,7 @@ (use-package! pdf-tools :mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode) :magic ("%PDF" . pdf-view-mode) - :config - (map! :map pdf-view-mode-map :gn "q" #'kill-current-buffer) - + :init (after! pdf-annot (defun +pdf-cleanup-windows-h () "Kill left-over annotation buffers when the document is killed." @@ -19,6 +17,9 @@ (add-hook! 'pdf-view-mode-hook (add-hook 'kill-buffer-hook #'+pdf-cleanup-windows-h nil t))) + :config + (map! :map pdf-view-mode-map :gn "q" #'kill-current-buffer) + (setq-default pdf-view-display-size 'fit-page pdf-view-use-scaling t pdf-view-use-imagemagick nil) @@ -41,11 +42,16 @@ ;; Turn off cua so copy works (add-hook! 'pdf-view-mode-hook (cua-mode 0)) + ;; Handle PDF-tools related popups better - (set-popup-rule! "^\\*Outline*" :side 'right :size 40 :select nil) - (set-popup-rule! "\\(?:^\\*Contents\\|'s annots\\*$\\)" :ignore t) + (set-popup-rules! + '(("^\\*Outline*" :side right :size 40 :select nil) + ("\\(?:^\\*Contents\\|'s annots\\*$\\)" :ignore t))) + + ;; The mode-line does serve any useful purpose is annotation windows (add-hook 'pdf-annot-list-mode-hook #'hide-mode-line-mode) - ;; Fix #1107: flickering pdfs when evil-mode is enabled + + ;; HACK Fix #1107: flickering pdfs when evil-mode is enabled (setq-hook! 'pdf-view-mode-hook evil-normal-state-cursor (list nil)) ;; Install epdfinfo binary if needed, blocking until it is finished @@ -54,18 +60,20 @@ (pdf-tools-install) (message "Building epdfinfo, this will take a moment...") ;; HACK We reset all `pdf-view-mode' buffers to fundamental mode so that - ;; `pdf-tools-install' has a chance to reinitialize them as - ;; `pdf-view-mode' buffers. This is necessary because `pdf-tools-install' - ;; won't do this to buffers that are already in pdf-view-mode. + ;; `pdf-tools-install' has a chance to reinitialize them as + ;; `pdf-view-mode' buffers. This is necessary because + ;; `pdf-tools-install' won't do this to buffers that are already in + ;; pdf-view-mode. (dolist (buffer (doom-buffers-in-mode 'pdf-view-mode)) (with-current-buffer buffer (fundamental-mode))) (while compilation-in-progress ;; Block until `pdf-tools-install' is done + (redisplay) (sleep-for 1)) ;; HACK If pdf-tools was loaded by you opening a pdf file, once - ;; `pdf-tools-install' completes, `pdf-view-mode' will throw an error - ;; because the compilation buffer is focused, not the pdf buffer. - ;; Therefore, it is imperative that the window config is restored. + ;; `pdf-tools-install' completes, `pdf-view-mode' will throw an error + ;; because the compilation buffer is focused, not the pdf buffer. + ;; Therefore, it is imperative that the window config is restored. (when (file-executable-p pdf-info-epdfinfo-program) (set-window-configuration wconf)))) diff --git a/modules/tools/pdf/packages.el b/modules/tools/pdf/packages.el index 59dae0cdd..06ef963d3 100644 --- a/modules/tools/pdf/packages.el +++ b/modules/tools/pdf/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/pdf/packages.el -(package! pdf-tools) +(package! pdf-tools :pin "0159cb1ab3") diff --git a/modules/tools/prodigy/packages.el b/modules/tools/prodigy/packages.el index bf9396134..edb10f54c 100644 --- a/modules/tools/prodigy/packages.el +++ b/modules/tools/prodigy/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/prodigy/packages.el -(package! prodigy) +(package! prodigy :pin "6ae71f27b0") diff --git a/modules/tools/rgb/packages.el b/modules/tools/rgb/packages.el index e42264f13..22b018737 100644 --- a/modules/tools/rgb/packages.el +++ b/modules/tools/rgb/packages.el @@ -1,5 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/rgb/packages.el -(package! rainbow-mode) -(package! kurecolor) +(package! rainbow-mode :pin "3ef813d637") +(package! kurecolor :pin "3fc84840cb") diff --git a/modules/tools/terraform/README.org b/modules/tools/terraform/README.org new file mode 100644 index 000000000..16eb3d7cd --- /dev/null +++ b/modules/tools/terraform/README.org @@ -0,0 +1,69 @@ +#+TITLE: tools/terraform +#+DATE: November 21, 2019 +#+SINCE: v2.1.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + - [[#syntax-highlighting][Syntax highlighting]] + - [[#code-formatting][Code formatting]] + - [[#code-navigation][Code navigation]] + - [[#code-completion][Code completion]] + - [[#documentation][Documentation]] + - [[#executing-terraform-commands][Executing Terraform commands]] +- [[#appendix][Appendix]] + - [[#keybindings][Keybindings]] + - [[#localleader][:localleader]] + +* Description +This module adds support for working with [[https://www.terraform.io][Terraform]] files in Doom Emacs. This +includes syntax highlighting, intelligent code completion, and the ability to run +Terraform commands directly from Emacs. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/syohex/emacs-terraform-mode][terraform-mode]] ++ [[https://github.com/rafalcieslak/emacs-company-terraform][company-terraform*]] + +* Prerequisites +The =terraform= executable must be installed and accessible from your PATH. + +* Features +** Syntax highlighting +Syntax highlighting is provided from =terraform-mode= and =hcl-mode=. + +** Code formatting +=:tools terraform= does not provide code formatting directly, but =:editor +format= works with Terraform files. + +** Code navigation +Code navigation is supported through =imenu= from =terraform-mode=. + +** Code completion +Code completion of Terraform builtins is provided from =company-terraform= and +generally works well despite being generated through a [[https://github.com/rafalcieslak/emacs-company-terraform/blob/master/company-terraform-data.el][static (outdated) file]]. + +=company-terraform= also provides code completion of resources within your project. + +** Documentation +Documentation is accessible through the normal =company-mode= show documentation +functionality, thanks to =company-terraform=. + +** Executing Terraform commands +=:tools terraform= provides commands under the =localleader= to run the most +common Terraform operations (see Keybindings below). + +* Appendix +** Keybindings +*** :localleader +| key | description | +|-----+-----------------------| +| =i= | Run =terraform init= | +| =p= | Run =terraform plan= | +| =a= | Run =terraform apply= | diff --git a/modules/tools/terraform/config.el b/modules/tools/terraform/config.el index e6ca17f5e..897bb712f 100644 --- a/modules/tools/terraform/config.el +++ b/modules/tools/terraform/config.el @@ -1,5 +1,9 @@ ;;; tools/terraform/config.el -*- lexical-binding: t; -*- +(when (featurep! +lsp) + (add-hook 'terraform-mode-local-vars-hook #'lsp!)) + + (map! :after terraform-mode :map terraform-mode-map :localleader diff --git a/modules/tools/terraform/doctor.el b/modules/tools/terraform/doctor.el new file mode 100644 index 000000000..456d1f907 --- /dev/null +++ b/modules/tools/terraform/doctor.el @@ -0,0 +1,4 @@ +;;; tools/terraform/doctor.el -*- lexical-binding: t; -*- + +(unless (executable-find "terraform") + (warn! "Couldn't find terraform.")) diff --git a/modules/tools/terraform/packages.el b/modules/tools/terraform/packages.el index 9c9217ad1..ccc050277 100644 --- a/modules/tools/terraform/packages.el +++ b/modules/tools/terraform/packages.el @@ -1,6 +1,6 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/terraform/packages.el -(package! terraform-mode) +(package! terraform-mode :pin "6973d1acab") (when (featurep! :completion company) - (package! company-terraform)) + (package! company-terraform :pin "2d11a21fee")) diff --git a/modules/tools/upload/packages.el b/modules/tools/upload/packages.el index 13088da1f..453b0363b 100644 --- a/modules/tools/upload/packages.el +++ b/modules/tools/upload/packages.el @@ -1,5 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; tools/upload/packages.el -(package! ssh-deploy) - +(package! ssh-deploy :pin "93a0e189a0") diff --git a/modules/tools/wakatime/README.org b/modules/tools/wakatime/README.org deleted file mode 100644 index 2427fee03..000000000 --- a/modules/tools/wakatime/README.org +++ /dev/null @@ -1,35 +0,0 @@ -#+TITLE: tools/wakatime -#+DATE: June 7, 2018 -#+SINCE: v2.0.4 -#+STARTUP: inlineimages - -* Table of Contents :TOC_3:noexport: -- [[Description][Description]] - - [[Module Flags][Module Flags]] - - [[Plugins][Plugins]] -- [[Prerequisites][Prerequisites]] -- [[Configuration][Configuration]] - - [[Obfuscate project file names][Obfuscate project file names]] - -* Description -This module integrates [[https://wakatime.com][wakatime]] into Emacs. - -** Module Flags -This module provides no flags. - -** Plugins -+ wakatime-mode - -* Prerequisites -This module requires an account and API key from wakatime.com. - -Run ~M-x +wakatime/setup~ to set things up. - -* Configuration -** Obfuscate project file names -#+BEGIN_SRC emacs-lisp -(setq +wakatime-hide-filenames t) -#+END_SRC - -This will hide what files and folders you're working in from prying eyes on your -profile. diff --git a/modules/tools/wakatime/autoload.el b/modules/tools/wakatime/autoload.el deleted file mode 100644 index 8602b2d37..000000000 --- a/modules/tools/wakatime/autoload.el +++ /dev/null @@ -1,65 +0,0 @@ -;;; tools/wakatime/autoload.el -*- lexical-binding: t; -*- - -(defvar +wakatime-home (concat doom-cache-dir "wakatime/") - "Path to the directory where wakatime files are stored.") - -(defvar +wakatime-hide-filenames nil - "If non-nil, obfuscate files and only show what projects you're working on.") - -;;;###autoload -(add-hook 'doom-init-modules-hook #'+wakatime-delayed-autostart-h) - -;;;###autoload -(defun +wakatime/setup () - "Setup Wakatime in Emacs and start `global-wakatime-mode'. - -This will prompt you for your api key. You only need to run this when your api -changes." - (interactive) - (when (y-or-n-p "No API key is registered. Open a browser on the wakatime api key page?") - (browse-url "https://wakatime.com/settings/api-key")) - (let ((api-key (read-string "Enter your wakatime API key: "))) - (unless api-key - (user-error "No api key was received.")) - (require 'wakatime-mode) - (customize-set-variable 'wakatime-api-key api-key) - (customize-save-customized) - (unless (or (and wakatime-cli-path (file-executable-p wakatime-cli-path)) - (not (equal (wakatime-find-binary "wakatime") "wakatime"))) - (user-error "Couldn't find wakatime executable (%s)" - (or wakatime-cli-path "wakatime"))) - (global-wakatime-mode +1) - (message "Wakatime enabled. You're good to go!"))) - -;;;###autoload -(defun +wakatime-autostart-h (&rest _) - "Initialize wakatime (if `wakatime-api-key' is set, otherwise no-op with a -warning)." - (interactive) - (require 'wakatime-mode) - (if (not wakatime-api-key) - (message "wakatime-mode isn't set up. Run `M-x +wakatime/setup' to do so.") - (when +wakatime-home - (unless (file-directory-p +wakatime-home) - (make-directory +wakatime-home t))) - (global-wakatime-mode +1)) - ;; - (remove-hook 'doom-switch-buffer-hook #'+wakatime-autostart-h) - (advice-remove 'after-find-file #'+wakatime-autostart-h)) - -;;;###autoload -(defun +wakatime-delayed-autostart-h (&rest _) - "Lazily initialize `wakatime-mode' until the next time you switch buffers or -open a file." - (add-hook 'doom-switch-buffer-hook #'+wakatime-autostart-h) - ;; this is necessary in case the user opens emacs with file arguments - (advice-add 'after-find-file :before #'+wakatime-autostart-h)) - -(defadvice! +wakatime--append-options-a (ret) - "Modifies the wakatime command string so that `+wakatime-hide-filenames' and -`+wakatime-home' are respected." - :filter-return #'wakatime-client-command - (concat (when +wakatime-home - (format "WAKATIME_HOME=%s " (shell-quote-argument +wakatime-home))) - ret - (if +wakatime-hide-filenames " --hide-filenames"))) diff --git a/modules/tools/wakatime/packages.el b/modules/tools/wakatime/packages.el deleted file mode 100644 index 2df681c92..000000000 --- a/modules/tools/wakatime/packages.el +++ /dev/null @@ -1,4 +0,0 @@ -;; -*- no-byte-compile: t; -*- -;;; tools/wakatime/packages.el - -(package! wakatime-mode) diff --git a/modules/ui/deft/config.el b/modules/ui/deft/config.el index a84a0a6f0..18cbcc79c 100644 --- a/modules/ui/deft/config.el +++ b/modules/ui/deft/config.el @@ -3,21 +3,33 @@ (use-package! deft :commands deft :init - (setq deft-extensions '("org" "md" "tex" "txt") - deft-default-extension "org" + (setq deft-default-extension "org" ;; de-couples filename and note title: deft-use-filename-as-title nil deft-use-filter-string-for-filename t - deft-org-mode-title-prefix t ;; converts the filter string into a readable file-name using kebab-case: deft-file-naming-rules - '((noslash . "-") - (nospace . "-") - (case-fn . downcase))) + '((noslash . "-") + (nospace . "-") + (case-fn . downcase))) :config + (add-to-list 'deft-extensions "tex") + (add-hook 'deft-mode-hook #'doom-mark-buffer-as-real-h) ;; start filtering immediately (set-evil-initial-state! 'deft-mode 'insert) (map! :map deft-mode-map + :n "gr" #'deft-refresh + :n "C-s" #'deft-filter + :i "C-n" #'deft-new-file + :i "C-m" #'deft-new-file-named + :i "C-d" #'deft-delete-file + :i "C-r" #'deft-rename-file + :n "r" #'deft-rename-file + :n "a" #'deft-new-file + :n "A" #'deft-new-file-named + :n "d" #'deft-delete-file + :n "D" #'deft-archive-file + :n "q" #'kill-current-buffer :localleader "RET" #'deft-new-file-named "a" #'deft-archive-file diff --git a/modules/ui/deft/packages.el b/modules/ui/deft/packages.el index 9c4b8f9c1..18bf7ee99 100644 --- a/modules/ui/deft/packages.el +++ b/modules/ui/deft/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/deft/packages.el -(package! deft) +(package! deft :pin "f54e8a65a7") diff --git a/modules/ui/doom-dashboard/README.org b/modules/ui/doom-dashboard/README.org index 62bf11a99..7120ea470 100644 --- a/modules/ui/doom-dashboard/README.org +++ b/modules/ui/doom-dashboard/README.org @@ -1,20 +1,88 @@ #+TITLE: ui/doom-dashboard #+DATE: October 9, 2019 #+SINCE: v1.3 -#+STARTUP: inlineimages +#+STARTUP: inlineimages nofold * Table of Contents :TOC_3:noexport: - [[#description][Description]] - [[#module-flags][Module Flags]] - [[#prerequisites][Prerequisites]] +- [[#configuration][Configuration]] + - [[#a-custom-banner][A custom banner]] + - [[#adding-text-to-the-dashboard][Adding text to the dashboard]] + - [[#customizing-faces][Customizing Faces]] * Description -This module gives Doom Emacs a dashboard buffer. +This module adds a minimalistic, Atom-inspired dashboard to Emacs. -It is loosely inspired by Atom's dashboard. +Besides eye candy, the dashboard serves two other purposes: + +1. To improve Doom's startup times (the dashboard is lighter than the scratch + buffer in many cases). + +2. And to preserve the "last open directory" you were in. Occasionally, I kill + the last buffer in my project and I end up who-knows-where (in the working + directory of another buffer/project). It can take some work to find my way + back to where I was. Not with the Dashboard. + + Since the dashboard cannot be killed, and it remembers the working directory + of the last open buffer, ~M-x find-file~ will work from the directory I + expect. ** Module Flags This module provides no flags. * Prerequisites -This module only requires that ~all-the-icons~'s icon fonts are installed. Use ~M-x all-the-icons-install-fonts~ to do so. +This module only requires that ~all-the-icons~'s icon fonts are installed. + +It should've been installed when you first installed Doom, but ~M-x +all-the-icons-install-fonts~ will install them again. + +* Configuration +** A custom banner +To use a custom image as your banner, change ~fancy-splash-image~: + +#+BEGIN_SRC elisp +(setq fancy-splash-image "~/my/banners/image.png") +#+END_SRC + +#+begin_quote +Doom will fall back to its ASCII banner in Terminal Emacs. To replace the ASCII +banner, replace the ~doom-dashboard-widget-banner~ function in +~+doom-dashboard-functions~ with a function that inserts your new banner into +the current file. +#+end_quote + +** Adding text to the dashboard +Doom's dashboard iterates over ~+doom-dashboard-functions~ when it is told to +redraw. Add your own functions to operate on the buffer and potentially add +whatever you like to Doom's splash screen. + +#+begin_quote +Keep in mind that inserting text from expensive sources, e.g. your org agenda, +will negate most of Doom's startup benefits. +#+end_quote + +** Customizing Faces +Doom's dashboard defaults to inheriting faces set by the current theme. If you wish +to customize it independently of the theme (or just inherit a different color +from the theme) you can make use of ~custom-set-faces!~ or ~custom-theme-set-faces!~ +#+BEGIN_SRC elisp +(custom-set-faces! + '(doom-dashboard-banner :foreground "red" :background "#000000" :weight bold) + '(doom-dashboard-footer :inherit font-lock-constant-face) + '(doom-dashboard-footer-icon :inherit all-the-icons-red) + '(doom-dashboard-loaded :inherit font-lock-warning-face) + '(doom-dashboard-menu-desc :inherit font-lock-string-face) + '(doom-dashboard-menu-title :inherit font-lock-function-name-face)) +#+END_SRC +or for a per-theme setting +#+BEGIN_SRC elisp +(custom-theme-set-faces! 'doom-tomorrow-night + '(doom-dashboard-banner :foreground "red" :background "#000000" :weight bold) + '(doom-dashboard-footer :inherit font-lock-constant-face) + '(doom-dashboard-footer-icon :inherit all-the-icons-red) + '(doom-dashboard-loaded :inherit font-lock-warning-face) + '(doom-dashboard-menu-desc :inherit font-lock-string-face) + '(doom-dashboard-menu-title :inherit font-lock-function-name-face)) +#+END_SRC diff --git a/modules/ui/doom-dashboard/config.el b/modules/ui/doom-dashboard/config.el index 7df29520c..caa6896e7 100644 --- a/modules/ui/doom-dashboard/config.el +++ b/modules/ui/doom-dashboard/config.el @@ -19,7 +19,7 @@ relative to `+doom-dashboard-banner-dir'. If nil, always use the ASCII banner.") (defvar +doom-dashboard-banner-dir (concat (dir!) "/banners/") "Where to look for `+doom-dashboard-banner-file'.") -(defvar +doom-dashboard-banner-padding '(4 . 4) +(defvar +doom-dashboard-banner-padding '(0 . 4) "Number of newlines to pad the banner with, above and below, respectively.") (defvar +doom-dashboard-inhibit-refresh nil @@ -43,33 +43,33 @@ Possible values: (defvar +doom-dashboard-menu-sections '(("Reload last session" - :icon (all-the-icons-octicon "history" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "history" :face 'doom-dashboard-menu-title) :when (cond ((require 'persp-mode nil t) (file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir))) ((require 'desktop nil t) (file-exists-p (desktop-full-file-name)))) - :face (:inherit (font-lock-keyword-face bold)) + :face (:inherit (doom-dashboard-menu-title bold)) :action doom/quickload-session) ("Open org-agenda" - :icon (all-the-icons-octicon "calendar" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "calendar" :face 'doom-dashboard-menu-title) :when (fboundp 'org-agenda) :action org-agenda) ("Recently opened files" - :icon (all-the-icons-octicon "file-text" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "file-text" :face 'doom-dashboard-menu-title) :action recentf-open-files) ("Open project" - :icon (all-the-icons-octicon "briefcase" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "briefcase" :face 'doom-dashboard-menu-title) :action projectile-switch-project) ("Jump to bookmark" - :icon (all-the-icons-octicon "bookmark" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "bookmark" :face 'doom-dashboard-menu-title) :action bookmark-jump) ("Open private configuration" - :icon (all-the-icons-octicon "tools" :face 'font-lock-keyword-face) + :icon (all-the-icons-octicon "tools" :face 'doom-dashboard-menu-title) :when (file-directory-p doom-private-dir) :action doom/open-private-config) - ("Search Documentation" - :icon (all-the-icons-octicon "book" :face 'font-lock-keyword-face) - :action doom/help-search)) + ("Open documentation" + :icon (all-the-icons-octicon "book" :face 'doom-dashboard-menu-title) + :action doom/help)) "An alist of menu buttons used by `doom-dashboard-widget-shortmenu'. Each element is a cons cell (LABEL . PLIST). LABEL is a string to display after the icon and before the key string. @@ -125,10 +125,44 @@ PLIST can have the following properties: ;; `persp-mode' integration: update `default-directory' when switching perspectives (add-hook 'persp-created-functions #'+doom-dashboard--persp-record-project-h) (add-hook 'persp-activated-functions #'+doom-dashboard--persp-detect-project-h) + ;; HACK Fix #2219 where, in GUI daemon frames, the dashboard loses center + ;; alignment after switching (or killing) workspaces. + (when (daemonp) + (add-hook 'persp-activated-functions #'+doom-dashboard-reload-maybe-h)) (add-hook 'persp-before-switch-functions #'+doom-dashboard--persp-record-project-h))) (add-hook 'doom-init-ui-hook #'+doom-dashboard-init-h) +;; +;;; Faces +(defgroup doom-dashboard nil + "Manage how doom-dashboard is coloured and themed." + :prefix "doom-dashboard" + :group 'doom-themes) + +(defface doom-dashboard-banner '((t (:inherit font-lock-comment-face))) + "Face used for the DOOM banner on the dashboard" + :group 'doom-dashboard) + +(defface doom-dashboard-footer '((t (:inherit font-lock-keyword-face))) + "Face used for the footer on the dashboard" + :group 'doom-dashboard) + +(defface doom-dashboard-footer-icon '((t (:inherit all-the-icons-green))) + "Face used for the icon of the footer on the dashboard" + :group 'doom-dashboard) + +(defface doom-dashboard-loaded '((t (:inherit font-lock-comment-face))) + "Face used for the loaded packages benchmark" + :group 'doom-dashboard) + +(defface doom-dashboard-menu-desc '((t (:inherit font-lock-constant-face))) + "Face used for the key description of menu widgets on the dashboard" + :group 'doom-dashboard) + +(defface doom-dashboard-menu-title '((t (:inherit font-lock-keyword-face))) + "Face used for the title of menu widgets on the dashboard" + :group 'doom-dashboard) ;; ;;; Major mode @@ -202,7 +236,7 @@ PLIST can have the following properties: (goto-char (point-min)) (forward-button 1)))) -(defun +doom-dashboard-reload-maybe-h () +(defun +doom-dashboard-reload-maybe-h (&rest _) "Reload the dashboard or its state. If this isn't a dashboard buffer, move along, but record its `default-directory' @@ -245,10 +279,10 @@ whose dimensions may not be fully initialized by the time this is run." (save-excursion (skip-chars-forward "\n") (point))) (insert (make-string - (max 0 (- (/ (window-height (get-buffer-window)) 2) - (round (/ (+ (count-lines (point-min) (point-max)) - (car +doom-dashboard-banner-padding)) - 2)))) + (+ (max 0 (- (/ (window-height (get-buffer-window)) 2) + (round (/ (count-lines (point-min) (point-max)) + 2)))) + (car +doom-dashboard-banner-padding)) ?\n))))))))) (defun +doom-dashboard--persp-detect-project-h (&rest _) @@ -348,7 +382,7 @@ controlled by `+doom-dashboard-pwd-policy'." (let ((point (point))) (mapc (lambda (line) (insert (propertize (+doom-dashboard--center +doom-dashboard--width line) - 'face 'font-lock-comment-face) " ") + 'face 'doom-dashboard-banner) " ") (insert "\n")) '("================= =============== =============== ======== ========" "\\\\ . . . . . . .\\\\ //. . . . . . .\\\\ //. . . . . . .\\\\ \\\\. . .\\\\// . . //" @@ -393,7 +427,7 @@ controlled by `+doom-dashboard-pwd-policy'." (+doom-dashboard--center +doom-dashboard--width (doom-display-benchmark-h 'return)) - 'face 'font-lock-comment-face) + 'face 'doom-dashboard-loaded) "\n")) (defun doom-dashboard-widget-shortmenu () @@ -418,11 +452,11 @@ controlled by `+doom-dashboard-pwd-policy'." `(lambda (_) (call-interactively (or (command-remapping #',action) #',action))) - 'face (or face 'font-lock-keyword-face) + 'face (or face 'doom-dashboard-menu-title) 'follow-link t 'help-echo (format "%s (%s)" label - (propertize (symbol-name action) 'face 'font-lock-constant-face))) + (propertize (symbol-name action) 'face 'doom-dashboard-menu-desc))) (format "%-37s" (buffer-string))) ;; Lookup command keys dynamically (or (when-let (key (where-is-internal action nil t)) @@ -434,7 +468,7 @@ controlled by `+doom-dashboard-pwd-policy'." (upcase (if (< (length str) 3) str (substring str 0 3)))))) - (propertize (buffer-string) 'face 'font-lock-constant-face))) + (propertize (buffer-string) 'face 'doom-dashboard-menu-desc))) "")))) (if (display-graphic-p) "\n\n" @@ -446,8 +480,8 @@ controlled by `+doom-dashboard-pwd-policy'." (+doom-dashboard--center (- +doom-dashboard--width 2) (with-temp-buffer - (insert-text-button (or (all-the-icons-octicon "octoface" :face 'all-the-icons-green :height 1.3 :v-adjust -0.15) - (propertize "github" 'face 'font-lock-keyword-face)) + (insert-text-button (or (all-the-icons-octicon "octoface" :face 'doom-dashboard-footer-icon :height 1.3 :v-adjust -0.15) + (propertize "github" 'face 'doom-dashboard-footer)) 'action (lambda (_) (browse-url "https://github.com/hlissner/doom-emacs")) 'follow-link t 'help-echo "Open Doom Emacs github page") diff --git a/modules/ui/doom/README.org b/modules/ui/doom/README.org index 27fd15624..fead47300 100644 --- a/modules/ui/doom/README.org +++ b/modules/ui/doom/README.org @@ -6,26 +6,32 @@ * Table of Contents :TOC_3:noexport: - [[#description][Description]] - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] - [[#prerequisites][Prerequisites]] - [[#configuration][Configuration]] - [[#changing-theme][Changing theme]] - [[#changing-fonts][Changing fonts]] - [[#troubleshooting][Troubleshooting]] - [[#strange-font-symbols][Strange font symbols]] + - [[#ugly-background-colors-in-tty-emacs-for-daemon-users][Ugly background colors in tty Emacs for daemon users]] * Description -This module modifies Emacs' user interface. +This module gives Doom its signature look: powered by the =doom-one= theme +(inspired by Atom's One Dark theme) and =solaire-mode=. -Doom's look is loosely inspired by Atom's One Dark theme, and is largely contained in the] plugin. - -+ A colorscheme inspired by Atom's One Dark theme (now available in a separate plugin: [[https://github.com/hlissner/emacs-doom-theme/][doom-themes]]) ++ A colorscheme inspired by Atom's One Dark theme (now available in a separate + plugin: [[https://github.com/hlissner/emacs-doom-theme/][doom-themes]]) + A custom folded-region indicator for ~hideshow~ + "Thin bar" fringe bitmaps for ~git-gutter-fringe~ -+ File-visiting buffers are slightly brighter (powered by solaire-mode) ++ File-visiting buffers are slightly brighter (thanks to solaire-mode) ** Module Flags This module provides no flags. +** Plugins ++ [[https://github.com/hlissner/emacs-doom-themes][doom-themes]] ++ [[https://github.com/hlissner/emacs-solaire-mode][solaire-mode]] + * Prerequisites This module has no prereqisites. @@ -64,4 +70,24 @@ core/core-ui.el has four relevant variables: * Troubleshooting ** Strange font symbols -If you're seeing strange unicode symbols, this is likely because you don't have ~all-the-icons~'s font icon installed. You can install them with ~M-x all-the-icons-install-fonts~. +If you're seeing strange unicode symbols, this is likely because you don't have +~all-the-icons~'s font icon installed. You can install them with ~M-x +all-the-icons-install-fonts~. + +** Ugly background colors in tty Emacs for daemon users +=solaire-mode= is an aesthetic plugin that makes file-visiting buffers brighter +than the rest of the Emacs' frame (to visually differentiate temporary windows +or sidebars from editing windows). This looks great in GUI Emacs, but can look +questionable in the terminal. + +It disables itself if you start tty Emacs with ~emacs -nw~, but if you create a +tty frame from a daemon (which solaire-mode cannot anticipate), you'll get an +ugly background instead. + +If you only use Emacs in the terminal, your best bet is to disable the +solaire-mode package: + +#+BEGIN_SRC elisp +;; in ~/.doom.d/packages.el +(package! solaire-mode :disable t) +#+END_SRC diff --git a/modules/ui/doom/config.el b/modules/ui/doom/config.el index f95c73a0d..2b70d59a0 100644 --- a/modules/ui/doom/config.el +++ b/modules/ui/doom/config.el @@ -1,20 +1,39 @@ ;;; ui/doom/config.el -*- lexical-binding: t; -*- (defvar +doom-solaire-themes - '((doom-city-lights . t) + '((doom-acario-dark . t) + (doom-acario-light . t) + (doom-challenger-deep . t) + (doom-city-lights . t) + (doom-dark+ . t) (doom-dracula . t) - (doom-molokai) + (doom-fairy-floss . t) + (doom-gruvbox . t) + (doom-horizon . t) + (doom-laserwave . t) + (doom-losvkem . t) + (doom-manegarm . t) + (doom-material . t) + (doom-molokai . t) + (doom-moonlight . t) (doom-nord . t) (doom-nord-light . t) - (doom-nova) + (doom-nova . t) + (doom-oceanic-next . t) (doom-one . t) (doom-one-light . t) + (doom-outrun-electric . t) (doom-opera . t) + (doom-palenight . t) + (doom-peacock . t) + (doom-snazzy . t) (doom-solarized-dark . t) (doom-solarized-light) - (doom-spacegrey) - (doom-vibrant) - (doom-tomorrow-night)) + (doom-sourcerer . t) + (doom-spacegrey . t) + (doom-tomorrow-day . t) + (doom-tomorrow-night . t) + (doom-vibrant . t)) "An alist of themes that support `solaire-mode'. If CDR is t, then `solaire-mode-swap-bg' will be used automatically, when the theme is loaded.") @@ -22,7 +41,6 @@ ;; ;;; Packages -;; (use-package! doom-themes :defer t :init @@ -42,6 +60,7 @@ (use-package! solaire-mode + :when (or (daemonp) (display-graphic-p)) :defer t :init (add-hook! 'doom-load-theme-hook :append diff --git a/modules/ui/doom/packages.el b/modules/ui/doom/packages.el index 02507e3e7..5d7acb5bc 100644 --- a/modules/ui/doom/packages.el +++ b/modules/ui/doom/packages.el @@ -1,6 +1,5 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/doom/packages.el -(package! doom-themes) -(package! solaire-mode) - +(package! doom-themes :pin "ecffdf82ff") +(package! solaire-mode :pin "4ac324ccb0") diff --git a/modules/ui/fill-column/packages.el b/modules/ui/fill-column/packages.el index 0117dad31..dcc6e381e 100644 --- a/modules/ui/fill-column/packages.el +++ b/modules/ui/fill-column/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/fill-column/packages.el -(package! hl-fill-column) +(package! hl-fill-column :pin "612441e697") diff --git a/modules/ui/hl-todo/config.el b/modules/ui/hl-todo/config.el index 547b54afe..09c793b74 100644 --- a/modules/ui/hl-todo/config.el +++ b/modules/ui/hl-todo/config.el @@ -5,12 +5,12 @@ :config (setq hl-todo-highlight-punctuation ":" hl-todo-keyword-faces - `(("TODO" . ,(face-foreground 'warning)) - ("FIXME" . ,(face-foreground 'error)) - ("HACK" . ,(face-foreground 'font-lock-constant-face)) - ("REVIEW" . ,(face-foreground 'font-lock-keyword-face)) - ("NOTE" . ,(face-foreground 'success)) - ("DEPRECATED" . ,(face-foreground 'font-lock-doc-face)))) + `(("TODO" warning bold) + ("FIXME" error bold) + ("HACK" font-lock-constant-face bold) + ("REVIEW" font-lock-keyword-face bold) + ("NOTE" success bold) + ("DEPRECATED" font-lock-doc-face bold))) ;; Use a more primitive todo-keyword detection method in major modes that ;; don't use/have a valid syntax table entry for comments. diff --git a/modules/ui/hl-todo/packages.el b/modules/ui/hl-todo/packages.el index eee0fdb53..0d0643348 100644 --- a/modules/ui/hl-todo/packages.el +++ b/modules/ui/hl-todo/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/hl-todo/packages.el -(package! hl-todo) +(package! hl-todo :pin "5d2ea49f83") diff --git a/modules/ui/hydra/autoload/windows.el b/modules/ui/hydra/autoload/windows.el index 6d703ad81..de082eba4 100644 --- a/modules/ui/hydra/autoload/windows.el +++ b/modules/ui/hydra/autoload/windows.el @@ -1,7 +1,7 @@ ;;; ui/hydra/autoload/windows.el -*- lexical-binding: t; -*- ;;;###autoload (autoload '+hydra/text-zoom/body "ui/hydra/autoload/windows" nil t) -(defhydra +hydra/text-zoom (:hint t :color red) +(defhydra +hydra/text-zoom (:hint nil :color red) " Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset " diff --git a/modules/ui/hydra/packages.el b/modules/ui/hydra/packages.el index f92e067e0..5a206be5d 100644 --- a/modules/ui/hydra/packages.el +++ b/modules/ui/hydra/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/hydra/packages.el -(package! hydra) +(package! hydra :pin "e3beffdd80") diff --git a/modules/ui/indent-guides/config.el b/modules/ui/indent-guides/config.el index 214a90ff4..cd5cb5aeb 100644 --- a/modules/ui/indent-guides/config.el +++ b/modules/ui/indent-guides/config.el @@ -7,7 +7,7 @@ :config (add-hook 'focus-in-hook #'highlight-indent-guides-auto-set-faces) ;; `highlight-indent-guides' breaks in these modes - (add-hook! '(visual-line-mode-hook org-indent-mode-hook) + (add-hook! 'org-indent-mode-hook (defun +indent-guides-disable-maybe-h () (when highlight-indent-guides-mode (highlight-indent-guides-mode -1))))) diff --git a/modules/ui/indent-guides/packages.el b/modules/ui/indent-guides/packages.el index 210c00e9d..3bf737416 100644 --- a/modules/ui/indent-guides/packages.el +++ b/modules/ui/indent-guides/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/indent-guides/packages.el -(package! highlight-indent-guides) +(package! highlight-indent-guides :pin "0b10f38c54") diff --git a/modules/ui/modeline/+light.el b/modules/ui/modeline/+light.el new file mode 100644 index 000000000..b6882e169 --- /dev/null +++ b/modules/ui/modeline/+light.el @@ -0,0 +1,555 @@ +;;; ui/modeline/+default.el -*- lexical-binding: t; -*- + +;; This is a slimmed down version of `doom-modeline' that manipulates +;; `mode-line-format' directly. Its purpose is to be truer to the original goal +;; of Doom's modeline: to be more performant and minimalistic alternative to +;; other modeline packages and to be abstraction-light. Too much abstraction is +;; too much magic. +;; +;; Warning: this is still a WIP! + +(defun +modeline--set-var-and-refresh-bars-fn (&optional symbol value) + (when symbol + (set-default symbol value)) + (when doom-init-time + (+modeline-refresh-bars-h))) + + +;; +;;; Variables + +(defcustom +modeline-height 31 + "The height of the modeline. + +This is enforced by the xpm bitmap bar in `+modeline-bar'. Without it (and in +the terminal), this variable does nothing. + +Use `setq!' to adjust this variable live, as it will trigger an refresh of the +bars in the modeline. `setq' will not." + :type 'integer + :set #'+modeline--set-var-and-refresh-bars-fn) + +(defcustom +modeline-bar-width 3 + "The width of the bar in the modeline. + +If nil, the bar will be made transparent and 1 pixel wide, as to be invisible, +but without sacrificing its ability to enforce `+modeline-height'. + +Use `setq!' to adjust this variable live, as it will trigger an refresh of the +bars in the modeline. `setq' will not." + :type 'integer + :set #'+modeline--set-var-and-refresh-bars-fn) + +(defvar +modeline-format-alist () + "An alist of modeline formats defined with `def-modeline!'. + +Each entry's CAR is the name and CDR is a cons cell whose CAR is the left-hand +side of the modeline, and whose CDR is the right-hand side.") + + +;; +;;; Faces + +(defface +modeline-bar-active '((t (:inherit highlight))) + "Face used for left-most bar on the mode-line of an active window.") + +(defface +modeline-bar-inactive '((t (:inherit mode-line-inactive))) + "Face used for left-most bar on the mode-line of an inactive window.") + +(defface +modeline-highlight + '((t (:inherit mode-line-highlight))) + "Face used for highlighted modeline panels (like search counts).") + +(defface +modeline-alternate-highlight + '((t (:inherit mode-line-highlight))) + "Alternative face used for highlighted modeline panels (like search counts).") + + +;; +;;; Helpers + +;;; `active' +(defvar +modeline--active-window (selected-window)) + +(defun +modeline-active () + "Return non-nil if the selected window has an active modeline." + (eq (selected-window) +modeline--active-window)) + +(add-hook! 'pre-redisplay-functions + (defun +modeline-set-selected-window-h (&rest _) + "Track the active modeline's window in `+modeline--active-window'." + (let ((win (selected-window))) + (unless (minibuffer-window-active-p win) + (setq +modeline--active-window (frame-selected-window)))))) + +(defun +modeline--make-xpm (color width height) + "Create an XPM bitmap via COLOR, WIDTH and HEIGHT. Inspired by `powerline''s `pl/+modeline--make-xpm'." + (propertize + " " 'display + (let ((data (make-list height (make-list width 1))) + (color (or color "None"))) + (ignore-errors + (create-image + (concat + (format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\"," + (length (car data)) + (length data) + color + color) + (apply #'concat + (cl-loop with idx = 0 + with len = (length data) + for dl in data + do (cl-incf idx) + collect + (concat "\"" + (cl-loop for d in dl + if (= d 0) collect (string-to-char " ") + else collect (string-to-char ".")) + (if (eq idx len) "\"};" "\",\n"))))) + 'xpm t :ascent 'center))))) + +(defun +modeline-format-icon (icon label &optional face help-echo voffset) + (propertize (concat (all-the-icons-material + icon + :face face + :height 1.1 + :v-adjust (or voffset -0.225)) + (propertize label 'face face)) + 'help-echo help-echo)) + +(defun set-modeline! (name &optional default) + "Set the modeline to NAME. +If DEFAULT is non-nil, apply to all future buffers. Modelines are defined with +`def-modeline!'." + (if-let (format (assq name +modeline-format-alist)) + (cl-destructuring-bind (lhs . rhs) (cdr format) + (if default + (setq-default +modeline-format-left lhs + +modeline-format-right rhs) + (setq +modeline-format-left lhs + +modeline-format-right rhs))) + (error "Could not find %S modeline format" name))) + +(defun set-modeline-hook! (hooks name) + "Set the modeline to NAME on HOOKS. +See `def-modeline!' on how modelines are defined." + (let ((fn (intern (format "+modeline-set-%s-format-h" name)))) + (dolist (hook (doom-enlist hooks)) + (when after-init-time + (dolist (name (mapcar #'car +modeline-format-alist)) + (remove-hook hook (intern (format "+modeline-set-%s-format-h" name))))) + (add-hook hook fn)))) + +(defun def-modeline! (name lhs rhs) + "Define a modeline format by NAME. +LHS and RHS are the formats representing the left and right hand side of the +mode-line, respectively. See the variable `format-mode-line' for details on what +LHS and RHS will accept." + (setf (alist-get name +modeline-format-alist) + (cons lhs rhs)) + (fset (intern (format "+modeline-set-%s-format-h" name)) + (lambda (&rest _) (set-modeline! name)))) + +(defmacro def-modeline-var! (name body &optional docstring &rest plist) + "TODO" + (unless (stringp docstring) + (push docstring plist) + (setq docstring nil)) + `(progn + (,(if (plist-get plist :local) 'defvar-local 'defvar) + ,name ,body ,docstring) + (put ',name 'risky-local-variable t))) + + +;; +;;; Segments + +(def-modeline-var! +modeline-format-left nil + "The left-hand side of the modeline." + :local t) + +(def-modeline-var! +modeline-format-right nil + "The right-hand side of the modeline." + :local t) + + +;;; `+modeline-bar' +(progn + (def-modeline-var! +modeline-bar "") + (def-modeline-var! +modeline-inactive-bar "") + + (add-hook! '(doom-init-ui-hook doom-load-theme-hook) :append + (defun +modeline-refresh-bars-h () + (let ((width (or +modeline-bar-width 1)) + (height (max +modeline-height 0))) + (setq +modeline-bar + (+modeline--make-xpm + (and +modeline-bar-width + (face-background '+modeline-bar-active nil t)) + width height) + +modeline-inactive-bar + (+modeline--make-xpm + (and +modeline-bar-width + (face-background '+modeline-bar-inactive nil t)) + width height))))) + + (add-hook! 'doom-change-font-size-hook + (defun +modeline-adjust-height-h () + (defvar +modeline--old-height +modeline-height) + (let ((default-height +modeline--old-height) + (scale (or (frame-parameter nil 'font-scale) 0))) + (setq +modeline-height + (if (> scale 0) + (+ default-height (* (or (frame-parameter nil 'font-scale) 1) + doom-font-increment)) + default-height)) + (when doom-init-time + (+modeline-refresh-bars-h)))))) + + +;;; `+modeline-matches' +(progn + (use-package! anzu + :after-call isearch-mode + :config + ;; anzu and evil-anzu expose current/total state that can be displayed in the + ;; mode-line. + (defadvice! +modeline-fix-anzu-count-a (positions here) + "Calulate anzu counts via POSITIONS and HERE." + :override #'anzu--where-is-here + (cl-loop for (start . end) in positions + collect t into before + when (and (>= here start) (<= here end)) + return (length before) + finally return 0)) + + (setq anzu-cons-mode-line-p nil) ; manage modeline segment ourselves + ;; Ensure anzu state is cleared when searches & iedit are done + (add-hook 'isearch-mode-end-hook #'anzu--reset-status 'append) + (add-hook 'iedit-mode-end-hook #'anzu--reset-status) + (advice-add #'evil-force-normal-state :before #'anzu--reset-status) + ;; Fix matches segment mirroring across all buffers + (mapc #'make-variable-buffer-local + '(anzu--total-matched anzu--current-position anzu--state + anzu--cached-count anzu--cached-positions anzu--last-command + anzu--last-isearch-string anzu--overflow-p))) + + (use-package! evil-anzu + :when (featurep! :editor evil) + :after-call (evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight)) + + (defun +modeline--anzu () + "Show the match index and total number thereof. +Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with +`evil-search'." + (when (and (bound-and-true-p anzu--state) + (not (bound-and-true-p iedit-mode))) + (propertize + (let ((here anzu--current-position) + (total anzu--total-matched)) + (cond ((eq anzu--state 'replace-query) + (format " %d replace " anzu--cached-count)) + ((eq anzu--state 'replace) + (format " %d/%d " here total)) + (anzu--overflow-p + (format " %s+ " total)) + (t + (format " %s/%d " here total)))) + 'face (if (+modeline-active) '+modeline-highlight)))) + + (defun +modeline--evil-substitute () + "Show number of matches for evil-ex substitutions and highlights in real time." + (when (and (bound-and-true-p evil-local-mode) + (or (assq 'evil-ex-substitute evil-ex-active-highlights-alist) + (assq 'evil-ex-global-match evil-ex-active-highlights-alist) + (assq 'evil-ex-buffer-match evil-ex-active-highlights-alist))) + (propertize + (let ((range (if evil-ex-range + (cons (car evil-ex-range) (cadr evil-ex-range)) + (cons (line-beginning-position) (line-end-position)))) + (pattern (car-safe (evil-delimited-arguments evil-ex-argument 2)))) + (if pattern + (format " %s matches " (how-many pattern (car range) (cdr range))) + " - ")) + 'face (if (+modeline-active) '+modeline-highlight)))) + + (defun +modeline--multiple-cursors () + "Show the number of multiple cursors." + (when (bound-and-true-p evil-mc-cursor-list) + (let ((count (length evil-mc-cursor-list))) + (when (> count 0) + (let ((face (cond ((not (+modeline-active)) 'mode-line-inactive) + (evil-mc-frozen '+modeline-highlight) + ('+modeline-alternate-highlight)))) + (concat (propertize " " 'face face) + (all-the-icons-faicon "i-cursor" :face face :v-adjust -0.0575) + (propertize " " 'face `(:inherit (variable-pitch ,face))) + (propertize (format "%d " count) + 'face face))))))) + + (defun +modeline--overlay< (a b) + "Sort overlay A and B." + (< (overlay-start a) (overlay-start b))) + + (defun +modeline--iedit () + "Show the number of iedit regions matches + what match you're on." + (when (and (bound-and-true-p iedit-mode) + (bound-and-true-p iedit-occurrences-overlays)) + (propertize + (let ((this-oc (or (let ((inhibit-message t)) + (iedit-find-current-occurrence-overlay)) + (save-excursion + (iedit-prev-occurrence) + (iedit-find-current-occurrence-overlay)))) + (length (length iedit-occurrences-overlays))) + (format " %s/%d " + (if this-oc + (- length + (length (memq this-oc (sort (append iedit-occurrences-overlays nil) + #'+modeline--overlay<))) + -1) + "-") + length)) + 'face (if (+modeline-active) '+modeline-highlight)))) + + (defun +modeline--macro-recording () + "Display current Emacs or evil macro being recorded." + (when (and (+modeline-active) + (or defining-kbd-macro + executing-kbd-macro)) + (let ((sep (propertize " " 'face '+modeline-highlight))) + (concat sep + (propertize (if (bound-and-true-p evil-this-macro) + (char-to-string evil-this-macro) + "Macro") + 'face '+modeline-highlight) + sep + (all-the-icons-octicon "triangle-right" + :face '+modeline-highlight + :v-adjust -0.05) + sep)))) + + (def-modeline-var! +modeline-matches + '(:eval + (let ((meta (concat (+modeline--macro-recording) + (+modeline--anzu) + (+modeline--evil-substitute) + (+modeline--iedit) + (+modeline--multiple-cursors)))) + (or (and (not (equal meta "")) meta) + " %I "))))) + + +;;; `+modeline-modes' +(def-modeline-var! +modeline-modes ; remove minor modes + '("" + (:propertize mode-name + face bold + mouse-face +modeline-highlight) + mode-line-process + "%n" + " ")) + + +;;; `+modeline-buffer-identification' +(def-modeline-var! +modeline-buffer-identification ; slightly more informative buffer id + '((:eval + (propertize + (let ((buffer-file-name (buffer-file-name (buffer-base-buffer)))) + (or (when buffer-file-name + (if-let (project (doom-project-root buffer-file-name)) + (let ((filename (or buffer-file-truename (file-truename buffer-file-name)))) + (file-relative-name filename (concat project ".."))))) + "%b")) + 'face (cond ((buffer-modified-p) + '(error bold mode-line-buffer-id)) + ((+modeline-active) + 'mode-line-buffer-id)) + 'help-echo buffer-file-name)) + (buffer-read-only (:propertize " RO" face warning)))) + + +;;; `+modeline-position' +(def-modeline-var! +modeline-position '(" %l:%C %p ")) + + +;;; `+modeline-checker' +(progn + (def-modeline-var! +modeline-checker nil + "Displays color-coded error status & icon for the current buffer." + :local t) + + (add-hook! '(flycheck-status-changed-functions + flycheck-mode-hook) + (defun +modeline-checker-update (&optional status) + "Update flycheck text via STATUS." + (setq +modeline-checker + (pcase status + (`finished + (if flycheck-current-errors + (let-alist (flycheck-count-errors flycheck-current-errors) + (let ((error (or .error 0)) + (warning (or .warning 0)) + (info (or .info 0))) + (+modeline-format-icon "do_not_disturb_alt" + (number-to-string (+ error warning info)) + (cond ((> error 0) 'error) + ((> warning 0) 'warning) + ('success)) + (format "Errors: %d, Warnings: %d, Debug: %d" + error + warning + info)))) + (+modeline-format-icon "check" "" 'success))) + (`running (+modeline-format-icon "access_time" "*" 'font-lock-comment-face "Running...")) + (`errored (+modeline-format-icon "sim_card_alert" "!" 'error "Errored!")) + (`interrupted (+modeline-format-icon "pause" "!" 'font-lock-comment-face "Interrupted")) + (`suspicious (+modeline-format-icon "priority_high" "!" 'error "Suspicious"))))))) + + +;;; `+modeline-selection-info' +(progn + (defsubst +modeline--column (pos) + "Get the column of the position `POS'." + (save-excursion (goto-char pos) + (current-column))) + + (def-modeline-var! +modeline-selection-info + '(:eval + (when (or mark-active + (and (bound-and-true-p evil-local-mode) + (eq evil-state 'visual))) + (cl-destructuring-bind (beg . end) + (if (boundp 'evil-local-mode) + (cons evil-visual-beginning evil-visual-end) + (cons (region-beginning) (region-end))) + (propertize + (let ((lines (count-lines beg (min end (point-max))))) + (concat " " + (cond ((or (bound-and-true-p rectangle-mark-mode) + (and (bound-and-true-p evil-visual-selection) + (eq 'block evil-visual-selection))) + (let ((cols (abs (- (+modeline--column end) + (+modeline--column beg))))) + (format "%dx%dB" lines cols))) + ((and (bound-and-true-p evil-visual-selection) + (eq evil-visual-selection 'line)) + (format "%dL" lines)) + ((> lines 1) + (format "%dC %dL" (- end beg) lines)) + ((format "%dC" (- end beg)))) + (when (derived-mode-p 'text-mode) + (format " %dW" (count-words beg end))) + " ")) + 'face (if (+modeline-active) 'success))))) + "Information about the current selection, such as how many characters and +lines are selected, or the NxM dimensions of a block selection.") + + (defun +modeline-add-selection-segment-h () + (add-to-list '+modeline-format-left '+modeline-selection-info 'append)) + (defun +modeline-remove-selection-segment-h () + (delq! '+modeline-selection-info +modeline-format-left)) + + (if (featurep 'evil) + (progn + (add-hook 'evil-visual-state-entry-hook #'+modeline-add-selection-segment-h) + (add-hook 'evil-visual-state-exit-hook #'+modeline-remove-selection-segment-h)) + (add-hook 'activate-mark-hook #'+modeline-add-selection-segment-h) + (add-hook 'deactivate-mark-hook #'+modeline-remove-selection-segment-h))) + + +;;; `+modeline-encoding' +(def-modeline-var! +modeline-encoding + '(:eval + (concat (coding-system-eol-type-mnemonic buffer-file-coding-system) + " " + (let ((sys (coding-system-plist buffer-file-coding-system))) + (if (memq (plist-get sys :category) + '(coding-category-undecided coding-category-utf-8)) + "UTF-8" + (upcase (symbol-name (plist-get sys :name)))))))) + +;; Clearer mnemonic labels for EOL styles +(setq eol-mnemonic-dos "CRLF" + eol-mnemonic-mac "CR" + eol-mnemonic-unix "LF" + eol-mnemonic-undecided "??") + + +;; +;;; Default modeline + +(def-modeline! :main + '("" + +modeline-matches + " " + +modeline-buffer-identification + +modeline-position) + `("" + mode-line-misc-info + +modeline-modes + (vc-mode (" " + ,(all-the-icons-octicon "git-branch" :v-adjust 0.0) + vc-mode " ")) + " " + +modeline-encoding + " " + (+modeline-checker ("" +modeline-checker " ")))) + +(def-modeline! 'project + `(" " + ,(all-the-icons-octicon + "file-directory" + :face 'bold + :v-adjust -0.05 + :height 1.25) + (:propertize (" " (:eval (abbreviate-file-name default-directory))) + face bold)) + '("" +modeline-modes)) + +(def-modeline! 'special + '("" +modeline-matches + " " +modeline-buffer-identification) + '("" +modeline-modes)) + +;; TODO (def-modeline! pdf ...) +;; TODO (def-modeline! helm ...) + + +;; +;;; Bootstrap + +(size-indication-mode +1) ; filesize in modeline + +(setq-default + mode-line-format + '("" + +modeline-bar + +modeline-format-left + (:eval + (propertize + " " + 'display + `((space :align-to (- (+ right right-fringe right-margin) + ,(string-width + (format-mode-line '("" +modeline-format-right)))))))) + +modeline-format-right)) +(with-current-buffer "*Messages*" + (setq mode-line-format (default-value 'mode-line-format))) + + +;; Other modes +(set-modeline! :main 'default) +(set-modeline-hook! '+doom-dashboard-mode-hook 'project) +;; (set-modeline-hook! 'pdf-tools-enabled-hook 'pdf) +(set-modeline-hook! '(special-mode-hook + image-mode-hook + circe-mode-hook) + 'special) + +(add-hook! 'magit-mode-hook + (defun +modeline-init-project-or-hide-h () + (if (eq major-mode 'magit-status-mode) + (set-modeline! 'project) + (hide-mode-line-mode +1)))) diff --git a/modules/ui/modeline/README.org b/modules/ui/modeline/README.org index 376f290ed..e27ad8371 100644 --- a/modules/ui/modeline/README.org +++ b/modules/ui/modeline/README.org @@ -30,7 +30,8 @@ This module provides an Atom-inspired, minimalistic modeline for Doom Emacs, powered by [[https://github.com/seagle0128/doom-modeline][the doom-modeline package]] (where you can find screenshots). ** Module Flags -This module provides no flags. ++ =+light= Enables a lighter, less featureful version of the modeline that does + not depend on ~doom-modeline~, which has performances issues in some cases. ** Plugins + [[https://github.com/seagle0128/doom-modeline][doom-modeline]] diff --git a/modules/ui/modeline/autoload.el b/modules/ui/modeline/autoload.el index 7162bd069..58ecd562b 100644 --- a/modules/ui/modeline/autoload.el +++ b/modules/ui/modeline/autoload.el @@ -21,14 +21,10 @@ Meant for `doom-change-font-size-hook'." (setq +modeline--old-bar-height doom-modeline-height)) (let ((default-height +modeline--old-bar-height) (scale (or (frame-parameter nil 'font-scale) 0))) - (if (> scale 0) - (let* ((font-size (string-to-number - (aref (doom--font-name (frame-parameter nil 'font) - (selected-frame)) - xlfd-regexp-pixelsize-subnum))) - (scale (frame-parameter nil 'font-scale))) - (setq doom-modeline-height (+ default-height (* scale doom-font-increment)))) - (setq doom-modeline-height default-height)))) + (setq doom-modeline-height + (if (> scale 0) + (+ default-height (* scale doom-font-increment)) + default-height)))) ;;;###autoload (defun +modeline-update-env-in-all-windows-h (&rest _) diff --git a/modules/ui/modeline/config.el b/modules/ui/modeline/config.el index ef23ab0c3..1a4a7e288 100644 --- a/modules/ui/modeline/config.el +++ b/modules/ui/modeline/config.el @@ -1,6 +1,20 @@ ;;; ui/modeline/config.el -*- lexical-binding: t; -*- +(when (featurep! +light) + (load! "+light")) + + +(defvar +modeline--redisplayed-p nil) +(defadvice! modeline-recalculate-height-a (&optional _force &rest _ignored) + "Ensure that window resizing functions take modeline height into account." + :before '(fit-window-to-buffer resize-temp-buffer-window) + (unless +modeline--redisplayed-p + (setq-local +modeline--redisplayed-p t) + (redisplay t))) + + (use-package! doom-modeline + :unless (featurep! +light) :hook (after-init . doom-modeline-mode) :init (unless after-init-time @@ -37,17 +51,6 @@ (add-hook '+doom-dashboard-mode-hook #'doom-modeline-set-project-modeline) - (when (or (featurep! :ui pretty-code +fira) - (featurep! :ui pretty-code +iosevka)) - ;; Fix #1216 and seagle0128/doom-modeline#69: wrong icon displayed for - ;; 'save' icon in modeline. - (defadvice! +modeline-fix-font-conflict-with-ligatures-a (&rest args) - :override #'doom-modeline-icon-material - (when doom-modeline-icon - (pcase (car args) - ("save" (apply 'all-the-icons-faicon (cons "floppy-o" (plist-put (cdr args) :v-adjust -0.0575)))) - (other (apply 'all-the-icons-material args)))))) - (add-hook! 'magit-mode-hook (defun +modeline-hide-in-non-status-buffer-h () "Show minimal modeline in magit-status buffer, no modeline elsewhere." @@ -55,32 +58,18 @@ (doom-modeline-set-project-modeline) (hide-mode-line-mode)))) - ;; Remove unused segments & extra padding - (doom-modeline-def-modeline 'main - '(bar window-number matches buffer-info remote-host buffer-position selection-info) - '(objed-state misc-info persp-name irc mu4e github debug input-method buffer-encoding lsp major-mode process vcs checker)) - - (doom-modeline-def-modeline 'special - '(bar window-number matches buffer-info-simple buffer-position selection-info) - '(objed-state misc-info persp-name debug input-method irc buffer-encoding lsp major-mode process checker)) - - (doom-modeline-def-modeline 'project - '(bar window-number buffer-default-directory) - '(misc-info mu4e github debug battery " " major-mode process)) - ;; Some functions modify the buffer, causing the modeline to show a false ;; modified state, so force them to behave. (defadvice! +modeline--inhibit-modification-hooks-a (orig-fn &rest args) :around #'ws-butler-after-save - (with-silent-modifications (apply orig-fn args)))) + (with-silent-modifications (apply orig-fn args))) -;; -;; Extensions + ;; + ;;; Extensions + (use-package! anzu + :after-call isearch-mode) -(use-package! anzu - :after-call isearch-mode) - -(use-package! evil-anzu - :when (featurep! :editor evil) - :after-call evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight) + (use-package! evil-anzu + :when (featurep! :editor evil) + :after-call evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight)) diff --git a/modules/ui/modeline/packages.el b/modules/ui/modeline/packages.el index f01d2c78a..d08c7c954 100644 --- a/modules/ui/modeline/packages.el +++ b/modules/ui/modeline/packages.el @@ -1,7 +1,8 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/modeline/packages.el -(package! doom-modeline) -(package! anzu) +(unless (featurep! +light) + (package! doom-modeline :pin "41015d72e7")) +(package! anzu :pin "592f8ee6d0") (when (featurep! :editor evil) - (package! evil-anzu)) + (package! evil-anzu :pin "9bca6ca14d")) diff --git a/modules/ui/nav-flash/packages.el b/modules/ui/nav-flash/packages.el index 5dfdcaf5b..12e2a5a1f 100644 --- a/modules/ui/nav-flash/packages.el +++ b/modules/ui/nav-flash/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/nav-flash/packages.el -(package! nav-flash) +(package! nav-flash :pin "dbb9121663") diff --git a/modules/ui/neotree/autoload.el b/modules/ui/neotree/autoload.el index 783c57f20..2b26442f6 100644 --- a/modules/ui/neotree/autoload.el +++ b/modules/ui/neotree/autoload.el @@ -39,10 +39,9 @@ "Collapse an expanded directory node or go to the parent node." (interactive) (when-let (node (neo-buffer--get-filename-current-line)) - (if (file-directory-p node) - (if (neo-buffer--expanded-node-p node) - (+neotree/collapse) - (neotree-select-up-node)) + (if (and (file-directory-p node) + (neo-buffer--expanded-node-p node)) + (+neotree/collapse) (neotree-select-up-node)))) ;;;###autoload diff --git a/modules/ui/neotree/config.el b/modules/ui/neotree/config.el index 705b61979..a3c86353f 100644 --- a/modules/ui/neotree/config.el +++ b/modules/ui/neotree/config.el @@ -8,14 +8,14 @@ neotree-find neo-global--with-buffer neo-global--window-exists-p) - :config + :init (setq neo-create-file-auto-open nil neo-auto-indent-point nil neo-autorefresh nil neo-mode-line-type 'none - neo-window-width 24 + neo-window-width 30 neo-show-updir-line nil - neo-theme 'nerd ; fallback + neo-theme 'icons neo-banner-message nil neo-confirm-create-file #'off-p neo-confirm-create-directory #'off-p @@ -35,6 +35,7 @@ "~$" "^#.*#$")) + :config (set-popup-rule! "^ ?\\*NeoTree" :ignore t) (after! winner @@ -54,28 +55,19 @@ (skip-chars-forward " \t\r")) (map! :map neotree-mode-map - :n "g" nil - :n "TAB" #'neotree-quick-look - :n "RET" #'neotree-enter - :n [tab] #'neotree-quick-look - :n [return] #'neotree-enter + :n [tab] (neotree-make-executor + :dir-fn #'neo-open-dir + :file-fn #'neotree-quick-look) :n "DEL" #'evil-window-prev - :n "c" #'neotree-create-node - :n "r" #'neotree-rename-node - :n "d" #'neotree-delete-node - :n "j" #'neotree-next-line - :n "k" #'neotree-previous-line :n "n" #'neotree-next-line :n "p" #'neotree-previous-line - :n "h" #'+neotree/collapse-or-up - :n "l" #'+neotree/expand-or-open + :m "h" #'+neotree/collapse-or-up + :m "l" #'+neotree/expand-or-open :n "J" #'neotree-select-next-sibling-node :n "K" #'neotree-select-previous-sibling-node :n "H" #'neotree-select-up-node :n "L" #'neotree-select-down-node :n "G" #'evil-goto-line :n "gg" #'evil-goto-first-line - :n "v" #'neotree-enter-vertical-split - :n "s" #'neotree-enter-horizontal-split - :n "q" #'neotree-hide - :n "R" #'neotree-refresh)) + :n "v" (neotree-make-executor :file-fn 'neo-open-file-vertical-split) + :n "s" (neotree-make-executor :file-fn 'neo-open-file-horizontal-split))) diff --git a/modules/ui/neotree/packages.el b/modules/ui/neotree/packages.el index 47aae4652..314efb4b7 100644 --- a/modules/ui/neotree/packages.el +++ b/modules/ui/neotree/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/neotree/packages.el -(package! neotree) +(package! neotree :pin "c2420a4b34") diff --git a/modules/ui/ophints/packages.el b/modules/ui/ophints/packages.el index f1ef7d809..000154257 100644 --- a/modules/ui/ophints/packages.el +++ b/modules/ui/ophints/packages.el @@ -2,5 +2,5 @@ ;;; ui/ophints/packages.el (if (featurep! :editor evil) - (package! evil-goggles) - (package! volatile-highlights)) + (package! evil-goggles :pin "08a22058fd") + (package! volatile-highlights :pin "9a20091f0c")) diff --git a/modules/ui/popup/+hacks.el b/modules/ui/popup/+hacks.el index ae046ae96..7eefa7c7e 100644 --- a/modules/ui/popup/+hacks.el +++ b/modules/ui/popup/+hacks.el @@ -9,7 +9,7 @@ ;; other windows just to pop up one tiny window). ;; 2. Forcing plugins to use `display-buffer' and `pop-to-buffer' instead of ;; `switch-to-buffer' (which is unaffected by `display-buffer-alist', which -;; this module heavily relies on). +;; we must rely on, heavily). ;; 3. Closing popups (temporarily) before functions that are highly destructive ;; to the illusion of popup control get run (with the use of the ;; `save-popups!' macro). @@ -29,6 +29,18 @@ ;; Don't try to resize popup windows (advice-add #'balance-windows :around #'+popup-save-a) +(defun +popup/quit-window () + "The regular `quit-window' sometimes kills the popup buffer and switches to a +buffer that shouldn't be in a popup. We prevent that by remapping `quit-window' +to this commmand." + (interactive) + (let ((orig-buffer (current-buffer))) + (quit-window) + (when (and (eq orig-buffer (current-buffer)) + (+popup-buffer-p)) + (+popup/close nil 'force)))) +(global-set-key [remap quit-window] #'+popup/quit-window) + ;; ;;; External functions @@ -194,17 +206,10 @@ the command buffer." (defadvice! +popup--helm-elisp--persistent-help-a (candidate _fun &optional _name) :before #'helm-elisp--persistent-help (let (win) - (when (and (helm-attr 'help-running-p) - (string= candidate (helm-attr 'help-current-symbol)) - (setq win (get-buffer-window (get-buffer (help-buffer))))) - (delete-window win)))) - - ;; `helm-ag' - (defadvice! +popup--helm-pop-to-buffer-a (orig-fn &rest args) - :around #'helm-ag--edit - (pop-to-buffer - (save-window-excursion (apply orig-fn args) - (current-buffer))))) + (and (helm-attr 'help-running-p) + (string= candidate (helm-attr 'help-current-symbol)) + (setq win (get-buffer-window (get-buffer (help-buffer)))) + (delete-window win))))) ;;;###package Info @@ -215,16 +220,6 @@ the command buffer." (select-window win)))) -;;;###package multi-term -(setq multi-term-buffer-name "doom terminal") - - -;;;###package neotree -(after! neotree - (advice-add #'neo-util--set-window-width :override #'ignore) - (advice-remove #'balance-windows #'ad-Advice-balance-windows)) - - ;;;###package org (after! org ;; Org has a scorched-earth window management policy I'm not fond of. i.e. it @@ -239,6 +234,8 @@ the command buffer." org-fast-todo-selection) (if +popup-mode (cl-letf (((symbol-function #'delete-other-windows) + (symbol-function #'ignore)) + ((symbol-function #'delete-window) (symbol-function #'ignore))) (apply orig-fn args)) (apply orig-fn args))) @@ -269,7 +266,18 @@ Ugh, such an ugly hack." :around #'org-switch-to-buffer-other-window (if +popup-mode (pop-to-buffer buf nil norecord) - (funcall orig-fn buf norecord)))) + (funcall orig-fn buf norecord))) + + ;; HACK `pop-to-buffer-same-window' consults `display-buffer-alist', which is + ;; what our popup manager uses to manage popup windows. However, + ;; `org-src-switch-to-buffer' already does its own window management + ;; prior to calling `pop-to-buffer-same-window', so there's no need to + ;; _then_ hand off the buffer to the pop up manager. + (defadvice! +popup--org-src-switch-to-buffer-a (orig-fn &rest args) + :around #'org-src-switch-to-buffer + (cl-letf (((symbol-function #'pop-to-buffer-same-window) + (symbol-function #'switch-to-buffer))) + (apply orig-fn args)))) ;;;###package persp-mode @@ -315,7 +323,8 @@ Ugh, such an ugly hack." (after! which-key (when (eq which-key-popup-type 'side-window) (setq which-key-popup-type 'custom - which-key-custom-popup-max-dimensions-function (lambda (_) (which-key--side-window-max-dimensions)) + which-key-custom-popup-max-dimensions-function + (lambda (_) (which-key--side-window-max-dimensions)) which-key-custom-hide-popup-function #'which-key--hide-buffer-side-window which-key-custom-show-popup-function (lambda (act-popup-dim) @@ -323,6 +332,8 @@ Ugh, such an ugly hack." (lambda (buffer alist) (+popup-display-buffer-stacked-side-window-fn buffer (append '((vslot . -9999)) alist))))) + ;; HACK Fix #2219 where the which-key popup would get cut off. + (setcar act-popup-dim (1+ (car act-popup-dim))) (which-key--show-buffer-side-window act-popup-dim)))))) diff --git a/modules/ui/popup/README.org b/modules/ui/popup/README.org index 3a8128983..38efc7ccc 100644 --- a/modules/ui/popup/README.org +++ b/modules/ui/popup/README.org @@ -61,24 +61,24 @@ Multiple popup rules can be defined with ~set-popup-rules!~: (set-popup-rules! '(("^ \\*" :slot -1) ; fallback rule for special buffers ("^\\*" :select t) - ("^\\*Completions" :slot -1 :transient 0) - ("^\\*\\(?:scratch\\|Messages\\)" :transient t) + ("^\\*Completions" :slot -1 :ttl 0) + ("^\\*\\(?:scratch\\|Messages\\)" :ttl t) ("^\\*Help" :slot -1 :size 0.2 :select t) ("^\\*doom:" - :size 0.35 :select t :modeline t :quit t :transient t))) + :size 0.35 :select t :modeline t :quit t :ttl t))) #+END_SRC Omitted parameters in a ~set-popup-rules!~ will use the defaults set in ~+popup-defaults~. ** Disabling hidden mode-line in popups -The mode-line is hidden in popups, by default. To disable this, you can either: +By default, the mode-line is hidden in popups. To disable this, you can either: 1. Change the default ~:modeline~ property in ~+popup-defaults~: #+BEGIN_SRC emacs-lisp - ;; put in private/$USER/config.el - (map-put +popup-defaults :modeline t) + ;; add to $DOOMDIR/config.el + (plist-put +popup-defaults :modeline t) #+END_SRC A value of ~t~ will instruct popups to use the default mode-line. Any @@ -87,7 +87,7 @@ The mode-line is hidden in popups, by default. To disable this, you can either: 2. Completely disable management of the mode-line in popups: #+BEGIN_SRC emacs-lisp - ;; in ~/.doom.d/config.el + ;; add to ~/.doom.d/config.el (remove-hook '+popup-buffer-mode-hook #'+popup-set-modeline-on-enable-h) #+END_SRC diff --git a/modules/ui/popup/autoload/popup.el b/modules/ui/popup/autoload/popup.el index 30159d815..34279daea 100644 --- a/modules/ui/popup/autoload/popup.el +++ b/modules/ui/popup/autoload/popup.el @@ -13,26 +13,27 @@ (defun +popup--kill-buffer (buffer ttl) "Tries to kill BUFFER, as was requested by a transient timer. If it fails, eg. the buffer is visible, then set another timer and try again later." - (when (buffer-live-p buffer) - (let ((inhibit-quit t) - (kill-buffer-hook (remq '+popup-kill-buffer-hook-h kill-buffer-hook))) - (cond ((get-buffer-window buffer t) + (let ((inhibit-quit t)) + (cond ((not (buffer-live-p buffer))) + ((not (get-buffer-window buffer t)) + (with-demoted-errors "Error killing transient buffer: %s" + (with-current-buffer buffer + (let ((kill-buffer-hook (remq '+popup-kill-buffer-hook-h kill-buffer-hook)) + confirm-kill-processes) + (when-let (process (get-buffer-process buffer)) + (kill-process process)) + (let (kill-buffer-query-functions) + ;; HACK The debugger backtrace buffer, when killed, called + ;; `top-level'. This causes jumpiness when the popup + ;; manager tries to clean it up. + (cl-letf (((symbol-function #'top-level) #'ignore)) + (kill-buffer buffer))))))) + ((let ((ttl (if (= ttl 0) + (or (plist-get +popup-defaults :ttl) 3) + ttl))) (with-current-buffer buffer (setq +popup--timer - (run-at-time ttl nil #'+popup--kill-buffer buffer ttl)))) - ((eq ttl 0) - (kill-buffer buffer)) - ((with-demoted-errors "Error killing transient buffer: %s" - (with-current-buffer buffer - (let (confirm-kill-processes) - (when-let (process (get-buffer-process buffer)) - (kill-process process)) - (let (kill-buffer-query-functions) - ;; HACK The debugger backtrace buffer, when killed, called - ;; `top-level'. This causes jumpiness when the popup - ;; manager tries to clean it up. - (cl-letf (((symbol-function #'top-level) #'ignore)) - (kill-buffer buffer))))))))))) + (run-at-time ttl nil #'+popup--kill-buffer buffer ttl)))))))) (defun +popup--delete-window (window) "Do housekeeping before destroying a popup window. @@ -132,6 +133,14 @@ the buffer is visible, then set another timer and try again later." (let ((ignore-window-parameters t)) (split-window window size side))) +(defun +popup--maybe-select-window (window origin) + "Select a window based on `+popup--inhibit-select' and this window's `select' parameter." + (unless +popup--inhibit-select + (let ((select (+popup-parameter 'select window))) + (if (functionp select) + (funcall select window origin) + (select-window (if select window origin)))))) + ;;;###autoload (defun +popup--init (window &optional alist) "Initializes a popup window. Run any time a popup is opened. It sets the @@ -172,8 +181,7 @@ and enables `+popup-buffer-mode'." (let ((window (or window (selected-window)))) (and (windowp window) (window-live-p window) - (or (window-parameter window 'popup) - (window-parameter window 'no-other-window)) + (window-parameter window 'popup) window)))) ;;;###autoload @@ -188,18 +196,13 @@ and enables `+popup-buffer-mode'." (alist (remove (assq 'window-height alist) alist)) (window (display-buffer-reuse-window buffer alist))) (when window - (unless +popup--inhibit-select - (select-window window)) + (+popup--maybe-select-window window origin) window)) (when-let (popup (cl-loop for func in actions if (funcall func buffer alist) return it)) (+popup--init popup alist) - (unless +popup--inhibit-select - (let ((select (+popup-parameter 'select popup))) - (if (functionp select) - (funcall select popup origin) - (select-window (if select popup origin))))) + (+popup--maybe-select-window popup origin) popup)))) ;;;###autoload @@ -334,7 +337,7 @@ Any non-nil value besides the above will be used as the raw value for '(+popup-display-buffer-stacked-side-window-fn)) (display-buffer-alist +popup--display-buffer-alist) (buffer (current-buffer))) - (push (+popup--make "." +popup-defaults) display-buffer-alist) + (push (+popup-make-rule "." +popup-defaults) display-buffer-alist) (bury-buffer) (pop-to-buffer buffer))) @@ -390,8 +393,8 @@ This window parameter is ignored if FORCE-P is non-nil." ;;;###autoload (defun +popup/toggle () - "If popups are open, close them. If they aren't, restore the last one or open -the message buffer in a popup window." + "Toggle any visible popups. +If no popups are available, display the *Messages* buffer in a popup window." (interactive) (let ((+popup--inhibit-transient t)) (cond ((+popup-windows) (+popup/close-all t)) @@ -412,8 +415,9 @@ the message buffer in a popup window." ;;;###autoload (defun +popup/raise (window &optional arg) - "Raise the current popup window into a regular window. -If prefix ARG, raise the current popup into a new window." + "Raise the current popup window into a regular window and +return it. If prefix ARG, raise the current popup into a new +window and return that window." (interactive (list (selected-window) current-prefix-arg)) (cl-check-type window window) @@ -425,7 +429,8 @@ If prefix ARG, raise the current popup into a new window." (+popup/close window 'force) (if arg (pop-to-buffer buffer) - (switch-to-buffer buffer)))) + (switch-to-buffer buffer)) + (selected-window))) ;;;###autoload (defun +popup/diagnose () diff --git a/modules/ui/popup/autoload/settings.el b/modules/ui/popup/autoload/settings.el index 7478b362e..15a39daf7 100644 --- a/modules/ui/popup/autoload/settings.el +++ b/modules/ui/popup/autoload/settings.el @@ -14,7 +14,7 @@ "Default properties for popup rules defined with `set-popup-rule!'.") ;;;###autoload -(defun +popup--make (predicate plist) +(defun +popup-make-rule (predicate plist) (if (plist-get plist :ignore) (list predicate nil) (let* ((plist (append plist +popup-defaults)) @@ -160,7 +160,7 @@ used. \(fn PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS)" (declare (indent defun)) - (push (+popup--make predicate plist) +popup--display-buffer-alist) + (push (+popup-make-rule predicate plist) +popup--display-buffer-alist) (when (bound-and-true-p +popup-mode) (setq display-buffer-alist +popup--display-buffer-alist)) +popup--display-buffer-alist) @@ -183,7 +183,7 @@ Example: (declare (indent 0)) (dolist (rules rulesets) (dolist (rule rules) - (push (+popup--make (car rule) (cdr rule)) + (push (+popup-make-rule (car rule) (cdr rule)) +popup--display-buffer-alist))) (when (bound-and-true-p +popup-mode) (setq display-buffer-alist +popup--display-buffer-alist)) diff --git a/modules/ui/popup/config.el b/modules/ui/popup/config.el index f34a49835..e849e4cae 100644 --- a/modules/ui/popup/config.el +++ b/modules/ui/popup/config.el @@ -131,14 +131,16 @@ prevent the popup(s) from messing up the UI (or vice versa)." ("^ \\*" :slot 1 :vslot -1 :size +popup-shrink-to-fit))) (when (featurep! +defaults) '(("^\\*Completions" :ignore t) - ("^\\*\\(?:Compil\\(?:ation\\|e-Log\\)\\|Messages\\)" + ("^\\*Local variables\\*$" + :vslot -1 :slot 1 :size +popup-shrink-to-fit) + ("^\\*\\(?:[Cc]ompil\\(?:ation\\|e-Log\\)\\|Messages\\)" :vslot -2 :size 0.3 :autosave t :quit t :ttl nil) ("^\\*\\(?:doom \\|Pp E\\)" ; transient buffers (no interaction required) :vslot -3 :size +popup-shrink-to-fit :autosave t :select ignore :quit t :ttl 0) ("^\\*doom:" ; editing buffers (interaction required) :vslot -4 :size 0.35 :autosave t :select t :modeline t :quit nil :ttl t) - ("^\\*doom:\\(?:v?term\\|eshell\\)-popup" ; editing buffers (interaction required) - :vslot -5 :size 0.35 :select t :modeline t :quit nil :ttl nil) + ("^\\*doom:\\(?:v?term\\|e?shell\\)-popup" ; editing buffers (interaction required) + :vslot -5 :size 0.35 :select t :modeline nil :quit nil :ttl nil) ("^\\*\\(?:Wo\\)?Man " :vslot -6 :size 0.45 :select t :quit t :ttl 0) ("^\\*Calc" @@ -158,7 +160,7 @@ prevent the popup(s) from messing up the UI (or vice versa)." ("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil) ("^\\*CPU-Profiler-Report " :side bottom :vslot 100 :slot 1 :height 0.4 :width 0.5 :quit nil) ("^\\*Memory-Profiler-Report " :side bottom :vslot 100 :slot 2 :height 0.4 :width 0.5 :quit nil) - ("^\\*unsent mail*" :ignore t))) + ("^\\*\\(?:Proced\\|timer-list\\|Process List\\|Abbrevs\\|Output\\|Occur\\|unsent mail\\)\\*" :ignore t))) (add-hook 'doom-init-ui-hook #'+popup-mode 'append) diff --git a/modules/ui/pretty-code/+fira.el b/modules/ui/pretty-code/+fira.el index 3f236bd75..f1983a99d 100644 --- a/modules/ui/pretty-code/+fira.el +++ b/modules/ui/pretty-code/+fira.el @@ -110,7 +110,12 @@ ("~>" . #Xe167) ("~~" . #Xe168) ("~~>" . #Xe169) - ("%%" . #Xe16a))) + ("%%" . #Xe16a) + ("x" . #Xe16b) + (":" . #Xe16c) + ("+" . #Xe16d) + ("+" . #Xe16e) + ("*" . #Xe16f))) (defun +pretty-code-setup-fira-ligatures-h () (set-fontset-font t '(#Xe100 . #Xe16f) +pretty-code-fira-code-font-name) diff --git a/modules/ui/pretty-code/autoload.el b/modules/ui/pretty-code/autoload.el index 1d2a54cae..ee0942ea9 100644 --- a/modules/ui/pretty-code/autoload.el +++ b/modules/ui/pretty-code/autoload.el @@ -15,14 +15,11 @@ and whose values are strings representing the text to be replaced with that symbol. If the car of PLIST is nil, then unset any pretty symbols previously defined for MODES. -The following properties are special: +This function accepts one special property: :alist ALIST Appends ALIST to `prettify-symbols-alist' literally, without mapping text to `+pretty-code-symbols'. - :merge BOOL - If non-nil, merge with previously defined `prettify-symbols-alist', - otherwise overwrite it. For example, the rule for emacs-lisp-mode is very simple: @@ -38,18 +35,17 @@ Pretty symbols can be unset for emacs-lisp-mode with: (declare (indent defun)) (if (null (car-safe plist)) (dolist (mode (doom-enlist modes)) - (delq (assq mode +pretty-code-symbols-alist) - +pretty-code-symbols-alist)) - (let (results merge key) + (assq-delete-all mode +pretty-code-symbols-alist)) + (let (results) (while plist - (pcase (setq key (pop plist)) - (:merge (setq merge (pop plist))) - (:alist (setq results (append (pop plist) results))) - (_ - (when-let (char (plist-get +pretty-code-symbols key)) - (push (cons (pop plist) char) results))))) + (let ((key (pop plist))) + (if (eq key :alist) + (prependq! results (pop plist)) + (when-let (char (plist-get +pretty-code-symbols key)) + (push (cons (pop plist) char) results))))) (dolist (mode (doom-enlist modes)) - (unless merge - (delq (assq mode +pretty-code-symbols-alist) - +pretty-code-symbols-alist)) - (push (cons mode results) +pretty-code-symbols-alist))))) + (setf (alist-get mode +pretty-code-symbols-alist) + (if-let (old-results (alist-get mode +pretty-code-symbols-alist)) + (dolist (cell results old-results) + (setf (alist-get (car cell) old-results) (cdr cell))) + results)))))) diff --git a/modules/ui/pretty-code/config.el b/modules/ui/pretty-code/config.el index 49dae0cd3..a1f942bc0 100644 --- a/modules/ui/pretty-code/config.el +++ b/modules/ui/pretty-code/config.el @@ -82,8 +82,12 @@ Otherwise it builds `prettify-code-symbols-alist' according to (add-hook 'after-change-major-mode-hook #'+pretty-code-init-pretty-symbols-h) -;; Font-specific ligature support -(cond ((featurep! +fira) +;; The emacs-mac build of Emacs appear to have built-in support for ligatures, +;; so use that instead if this module is enabled. +(cond ((and IS-MAC (fboundp 'mac-auto-operator-composition-mode)) + (mac-auto-operator-composition-mode)) + ;; Font-specific ligature support + ((featurep! +fira) (load! "+fira")) ((featurep! +iosevka) (load! "+iosevka")) diff --git a/modules/ui/tabs/autoload.el b/modules/ui/tabs/autoload.el deleted file mode 100644 index be3e62cfa..000000000 --- a/modules/ui/tabs/autoload.el +++ /dev/null @@ -1,77 +0,0 @@ -;;; ui/tabs/autoload.el -*- lexical-binding: t; -*- - -;;;###autoload -(defun +tabs-buffer-predicate (buffer) - "TODO" - (or (memq buffer (window-parameter nil 'tab-buffers)) - (eq buffer (doom-fallback-buffer)))) - - -;; -;;; Commands - -;;;###autoload -(defun +tabs/close-tab-or-window () - "TODO" - (interactive) - (call-interactively - (cond ((cdr (window-parameter nil 'tab-buffers)) - #'kill-current-buffer) - ((fboundp '+workspace/close-window-or-workspace) - #'+workspace/close-window-or-workspace) - (#'delete-window)))) - - -;; -;;; Advice - -;;;###autoload -(defun +tabs-kill-current-buffer-a (&rest _) - (+tabs-remove-buffer-h)) - -;;;###autoload -(defun +tabs-bury-buffer-a (orig-fn &rest args) - (if centaur-tabs-mode - (let ((b (current-buffer))) - (apply orig-fn args) - (unless (eq b (current-buffer)) - (with-current-buffer b - (+tabs-remove-buffer-h)))) - (apply orig-fn args))) - -;;;###autoload -(defun +tabs-kill-tab-maybe-a (tab) - (let ((buffer (centaur-tabs-tab-value tab))) - (with-current-buffer buffer - ;; `kill-current-buffer' is advised not to kill buffers visible in another - ;; window, so it behaves better than `kill-buffer'. - (kill-current-buffer)) - (centaur-tabs-display-update))) - - -;; -;;; Hooks - -;;;###autoload -(defun +tabs-add-buffer-h () - (when (and centaur-tabs-mode - (doom-real-buffer-p (current-buffer))) - (let* ((this-buf (current-buffer)) - (buffers (window-parameter nil 'tab-buffers))) - (cl-pushnew this-buf buffers) - (add-hook 'kill-buffer-hook #'+tabs-remove-buffer-h nil t) - (set-window-parameter nil 'tab-buffers buffers)))) - -;;;###autoload -(defun +tabs-remove-buffer-h () - (when centaur-tabs-mode - (set-window-parameter - nil - 'tab-buffers (delete (current-buffer) - (window-parameter nil 'tab-buffers))))) - -;;;###autoload -(defun +tabs-new-window-h () - (when centaur-tabs-mode - (unless (window-parameter nil 'tab-buffers) - (+tabs-add-buffer-h)))) diff --git a/modules/ui/tabs/config.el b/modules/ui/tabs/config.el index ff2b0f780..1ac654d6f 100644 --- a/modules/ui/tabs/config.el +++ b/modules/ui/tabs/config.el @@ -3,69 +3,18 @@ (use-package! centaur-tabs :after-call after-find-file dired-initial-position-hook :init - (setq centaur-tabs-height 28 + (setq centaur-tabs-set-icons t + centaur-tabs-gray-out-icons 'buffer centaur-tabs-set-bar 'left - centaur-tabs-set-modified-marker t) + centaur-tabs-set-modified-marker t + centaur-tabs-close-button "✕" + centaur-tabs-modified-marker "⬤") :config - (add-hook! 'centaur-tabs-mode-hook - (defun +tabs-init-frames-h () - (dolist (frame (frame-list)) - (if (not centaur-tabs-mode) - (set-frame-parameter frame 'buffer-predicate (frame-parameter frame 'old-buffer-predicate)) - (set-frame-parameter frame 'old-buffer-predicate (frame-parameter frame 'buffer-predicate)) - (set-frame-parameter frame 'buffer-predicate #'+tabs-buffer-predicate))))) - - (add-to-list 'window-persistent-parameters '(tab-buffers . t)) - - (defun +tabs-window-buffer-list-fn () - (centaur-tabs-filter-out - 'centaur-tabs-hide-tab-cached - (delq nil - (cl-mapcar #'(lambda (b) - (cond - ;; Always include the current buffer. - ((eq (current-buffer) b) b) - ((buffer-file-name b) b) - ((char-equal ?\ (aref (buffer-name b) 0)) nil) - ((buffer-live-p b) b))) - (window-parameter nil 'tab-buffers))))) - - (defun +tabs-buffer-groups-fn () - (list - (cond ((or (string-equal "*" (substring (buffer-name) 0 1)) - (memq major-mode '(magit-process-mode - magit-status-mode - magit-diff-mode - magit-log-mode - magit-file-mode - magit-blob-mode - magit-blame-mode - ))) - "Emacs") - ((derived-mode-p 'eshell-mode) - "EShell") - ((derived-mode-p 'dired-mode) - "Dired") - ((centaur-tabs-get-group-name (current-buffer)))))) - - (setq centaur-tabs-buffer-list-function #'+tabs-window-buffer-list-fn - centaur-tabs-buffer-groups-function #'+tabs-buffer-groups-fn) - - (advice-add #'centaur-tabs-buffer-close-tab :override #'+tabs-kill-tab-maybe-a) - (advice-add #'bury-buffer :around #'+tabs-bury-buffer-a) - (advice-add #'kill-current-buffer :before #'+tabs-kill-current-buffer-a) - (add-hook 'doom-switch-buffer-hook #'+tabs-add-buffer-h) - (add-hook 'doom-switch-window-hook #'+tabs-new-window-h) - (add-hook '+doom-dashboard-mode-hook #'centaur-tabs-local-mode) - (map! (:map centaur-tabs-mode-map - [remap delete-window] #'+tabs/close-tab-or-window - [remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window) - (:after persp-mode - :map persp-mode-map - [remap delete-window] #'+tabs/close-tab-or-window - [remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window)) - (centaur-tabs-mode +1)) + + +;; TODO tab-bar-mode (emacs 27) +;; TODO tab-line-mode (emacs 27) diff --git a/modules/ui/tabs/packages.el b/modules/ui/tabs/packages.el index 1bec6d791..a558bf457 100644 --- a/modules/ui/tabs/packages.el +++ b/modules/ui/tabs/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/tabs/packages.el -(package! centaur-tabs) +(package! centaur-tabs :pin "96b7c90bdc") diff --git a/modules/ui/treemacs/config.el b/modules/ui/treemacs/config.el index 7ca592ff6..df1c70996 100644 --- a/modules/ui/treemacs/config.el +++ b/modules/ui/treemacs/config.el @@ -2,18 +2,12 @@ (setq treemacs-follow-after-init t treemacs-is-never-other-window t - treemacs-sorting 'alphabetic-case-insensitive-desc + treemacs-sorting 'alphabetic-case-insensitive-asc treemacs-persist-file (concat doom-cache-dir "treemacs-persist") treemacs-last-error-persist-file (concat doom-cache-dir "treemacs-last-error-persist")) (after! treemacs - (set-popup-rule! "^ \\*Treemacs" - :side treemacs-position - :size treemacs-width - :quit nil - :ttl 0) - ;; Don't follow the cursor (treemacs-follow-mode -1) @@ -43,3 +37,8 @@ (use-package! treemacs-magit :when (featurep! :tools magit) :after treemacs magit) + + +(use-package! treemacs-persp + :when (featurep! :ui workspaces) + :after treemacs) diff --git a/modules/ui/treemacs/packages.el b/modules/ui/treemacs/packages.el index 929b95f43..6336e7a45 100644 --- a/modules/ui/treemacs/packages.el +++ b/modules/ui/treemacs/packages.el @@ -1,9 +1,11 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/treemacs/packages.el -(package! treemacs) +(package! treemacs :pin "4eb8eb8821") (when (featurep! :editor evil +everywhere) - (package! treemacs-evil)) -(package! treemacs-projectile) + (package! treemacs-evil :pin "4eb8eb8821")) +(package! treemacs-projectile :pin "4eb8eb8821") (when (featurep! :tools magit) - (package! treemacs-magit)) + (package! treemacs-magit :pin "4eb8eb8821")) +(when (featurep! :ui workspaces) + (package! treemacs-persp :pin "4eb8eb8821")) diff --git a/modules/ui/unicode/autoload.el b/modules/ui/unicode/autoload.el index 279176d97..25f194426 100644 --- a/modules/ui/unicode/autoload.el +++ b/modules/ui/unicode/autoload.el @@ -3,7 +3,7 @@ ;;;###autoload (add-hook! 'doom-init-ui-hook (defun +unicode-init-fonts-h () - "Set up `unicode-fonts' to eventually run; accomodating the daemon, if + "Set up `unicode-fonts' to eventually run; accommodating the daemon, if necessary." (setq-default bidi-display-reordering t doom-unicode-font nil) diff --git a/modules/ui/unicode/packages.el b/modules/ui/unicode/packages.el index 987993990..a7c7dfcd7 100644 --- a/modules/ui/unicode/packages.el +++ b/modules/ui/unicode/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/unicode/packages.el -(package! unicode-fonts) +(package! unicode-fonts :pin "7b88ae84e5") diff --git a/modules/ui/vc-gutter/config.el b/modules/ui/vc-gutter/config.el index 0f6ede1f6..a5dd92887 100644 --- a/modules/ui/vc-gutter/config.el +++ b/modules/ui/vc-gutter/config.el @@ -11,9 +11,9 @@ diffing, even for unsaved buffers.") (defvar +vc-gutter-default-style t - "If non-nil, enable the default look of the vc gutter. This means subtle thin -bitmaps on the left, an arrow bitmap for flycheck, and flycheck indicators moved -to the right fringe.") + "If non-nil, enable the default look of the vc gutter. +This means subtle thin bitmaps on the left, an arrow bitmap for flycheck, and +flycheck indicators moved to the right fringe.") ;; @@ -28,27 +28,28 @@ to the right fringe.") If the buffer doesn't represent an existing file, `git-gutter-mode's activation is deferred until the file is saved. Respects `git-gutter:disabled-modes'." - (when (or +vc-gutter-in-remote-files - (not (file-remote-p (or buffer-file-name default-directory)))) - (if (not buffer-file-name) - (add-hook 'after-save-hook #'+vc-gutter-init-maybe-h nil 'local) - (when (and (vc-backend buffer-file-name) - (progn - (require 'git-gutter) - (not (memq major-mode git-gutter:disabled-modes)))) - (if (and (display-graphic-p) - (require 'git-gutter-fringe nil t)) - (progn - (setq-local git-gutter:init-function #'git-gutter-fr:init) - (setq-local git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos) - (setq-local git-gutter:clear-function #'git-gutter-fr:clear) - (setq-local git-gutter:window-width -1)) - (setq-local git-gutter:init-function 'nil) - (setq-local git-gutter:view-diff-function #'git-gutter:view-diff-infos) - (setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos) - (setq-local git-gutter:window-width 1)) - (git-gutter-mode +1) - (remove-hook 'after-save-hook #'+vc-gutter-init-maybe-h 'local)))))) + (let ((file-name (buffer-file-name (buffer-base-buffer)))) + (when (or +vc-gutter-in-remote-files + (not (file-remote-p (or file-name default-directory)))) + (if (null file-name) + (add-hook 'after-save-hook #'+vc-gutter-init-maybe-h nil 'local) + (when (and (vc-backend file-name) + (progn + (require 'git-gutter) + (not (memq major-mode git-gutter:disabled-modes)))) + (if (and (display-graphic-p) + (require 'git-gutter-fringe nil t)) + (progn + (setq-local git-gutter:init-function #'git-gutter-fr:init) + (setq-local git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos) + (setq-local git-gutter:clear-function #'git-gutter-fr:clear) + (setq-local git-gutter:window-width -1)) + (setq-local git-gutter:init-function 'nil) + (setq-local git-gutter:view-diff-function #'git-gutter:view-diff-infos) + (setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos) + (setq-local git-gutter:window-width 1)) + (git-gutter-mode +1) + (remove-hook 'after-save-hook #'+vc-gutter-init-maybe-h 'local))))))) ;; Disable in Org mode, as per ;; and @@ -58,9 +59,6 @@ is deferred until the file is saved. Respects `git-gutter:disabled-modes'." ;; assigned. I don't know why this is the case, but adding `fundamental-mode' ;; here fixes the issue. (setq git-gutter:disabled-modes '(fundamental-mode image-mode pdf-view-mode)) - - ;; standardize default fringe width - (if (fboundp 'fringe-mode) (fringe-mode '4)) :config (set-popup-rule! "^\\*git-gutter" :select nil :size '+popup-shrink-to-fit) @@ -93,6 +91,9 @@ is deferred until the file is saved. Respects `git-gutter:disabled-modes'." ;; subtle diff indicators in the fringe (when +vc-gutter-default-style + ;; standardize default fringe width + (if (fboundp 'fringe-mode) (fringe-mode '4)) + (after! git-gutter-fringe ;; places the git gutter outside the margins. (setq-default fringes-outside-margins t) @@ -102,10 +103,10 @@ is deferred until the file is saved. Respects `git-gutter:disabled-modes'." (define-fringe-bitmap 'git-gutter-fr:modified [224] nil nil '(center repeated)) (define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240] - nil nil 'bottom) + nil nil 'bottom)) + (after! flycheck ;; let diff have left fringe, flycheck can have right fringe - (after! flycheck - (setq flycheck-indication-mode 'right-fringe) - ;; A non-descript, left-pointing arrow - (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow - [16 48 112 240 112 48 16] nil nil 'center)))) + (setq flycheck-indication-mode 'right-fringe) + ;; A non-descript, left-pointing arrow + (define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow + [16 48 112 240 112 48 16] nil nil 'center))) diff --git a/modules/ui/vc-gutter/packages.el b/modules/ui/vc-gutter/packages.el index a12b41b8f..94bbe2fab 100644 --- a/modules/ui/vc-gutter/packages.el +++ b/modules/ui/vc-gutter/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/vc-gutter/packages.el -(package! git-gutter-fringe) +(package! git-gutter-fringe :pin "16226caab4") diff --git a/modules/ui/vi-tilde-fringe/packages.el b/modules/ui/vi-tilde-fringe/packages.el index 10b52d811..2aad44c2f 100644 --- a/modules/ui/vi-tilde-fringe/packages.el +++ b/modules/ui/vi-tilde-fringe/packages.el @@ -1,4 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/vi-tilde-fringe/packages.el -(package! vi-tilde-fringe) +(package! vi-tilde-fringe :pin "f1597a8d54") diff --git a/modules/ui/window-select/README.org b/modules/ui/window-select/README.org index 34f5347b8..1f080e2e5 100644 --- a/modules/ui/window-select/README.org +++ b/modules/ui/window-select/README.org @@ -15,13 +15,11 @@ * Description This module provides several methods for selecting windows without the use of -the mouse or spatial navigation (e.g. =C-w {h,j,k,l}=). The command -~other-window~ is remapped to either ~switch-window~ or ~ace-window~, depending -on which backend you've enabled. +the mouse or spatial navigation (e.g. =C-w {h,j,k,l}=). -#+begin_quote -~other-window~ is bound to ~C-x o~ and ~SPC w o~. -#+end_quote +The command ~other-window~ is remapped to either ~switch-window~ or +~ace-window~, depending on which backend you've enabled. It is bound to ~C-x o~ +(and ~C-w C-w~ for evil users). It also provides numbered windows and selection with the ~winum~ package, if desired. Evil users can jump to window N in =C-w = (where N is a number diff --git a/modules/ui/window-select/config.el b/modules/ui/window-select/config.el index fd34980d4..a82f7eb2a 100644 --- a/modules/ui/window-select/config.el +++ b/modules/ui/window-select/config.el @@ -16,8 +16,9 @@ :init (global-set-key [remap other-window] #'ace-window) :config - (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l) - aw-scope 'frame + (unless (featurep! +numbers) + (setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))) + (setq aw-scope 'frame aw-background t)) diff --git a/modules/ui/window-select/packages.el b/modules/ui/window-select/packages.el index aa9659347..455caf83a 100644 --- a/modules/ui/window-select/packages.el +++ b/modules/ui/window-select/packages.el @@ -2,8 +2,8 @@ ;;; ui/window-select/packages.el (if (featurep! +switch-window) - (package! switch-window) - (package! ace-window)) + (package! switch-window :pin "204f9fc1a3") + (package! ace-window :pin "7e0777b39a")) (when (featurep! +numbers) - (package! winum)) + (package! winum :pin "c5455e866e")) diff --git a/modules/ui/workspaces/autoload/workspaces.el b/modules/ui/workspaces/autoload/workspaces.el index 7821c19af..f3fde1000 100644 --- a/modules/ui/workspaces/autoload/workspaces.el +++ b/modules/ui/workspaces/autoload/workspaces.el @@ -68,20 +68,19 @@ error if NAME doesn't exist." "Return a list of workspace structs (satisifes `+workspace-p')." ;; We don't use `hash-table-values' because it doesn't ensure order in older ;; versions of Emacs - (cdr (cl-loop for persp being the hash-values of *persp-hash* - collect persp))) + (cl-loop for name in persp-names-cache + if (gethash name *persp-hash*) + collect it)) ;;;###autoload (defun +workspace-list-names () "Return the list of names of open workspaces." - (mapcar #'safe-persp-name (+workspace-list))) + persp-names-cache) ;;;###autoload (defun +workspace-buffer-list (&optional persp) "Return a list of buffers in PERSP. -The buffer list is ordered by recency (same as `buffer-list'). - PERSP can be a string (name of a workspace) or a workspace (satisfies `+workspace-p'). If nil or omitted, it defaults to the current workspace." (let ((persp (or persp (+workspace-current)))) @@ -174,11 +173,12 @@ throws an error." (+workspace-new name) (error "%s is not an available workspace" name))) (let ((old-name (+workspace-current-name))) - (setq +workspace--last - (or (and (not (string= old-name persp-nil-name)) - old-name) - +workspaces-main)) - (persp-frame-switch name) + (unless (equal old-name name) + (setq +workspace--last + (or (and (not (string= old-name persp-nil-name)) + old-name) + +workspaces-main)) + (persp-frame-switch name)) (equal (+workspace-current-name) name))) @@ -268,13 +268,20 @@ workspace to delete." ('error (+workspace-error ex t)))) ;;;###autoload -(defun +workspace/kill-session () +(defun +workspace/kill-session (&optional interactive) "Delete the current session, all workspaces, windows and their buffers." - (interactive) - (unless (cl-every #'+workspace-delete (+workspace-list-names)) - (+workspace-error "Could not clear session")) - (+workspace-switch +workspaces-main t) - (doom/kill-all-buffers (buffer-list))) + (interactive (list t)) + (let ((windows (length (window-list))) + (persps (length (+workspace-list-names))) + (buffers 0)) + (let ((persp-autokill-buffer-on-remove t)) + (unless (cl-every #'+workspace-delete (+workspace-list-names)) + (+workspace-error "Could not clear session"))) + (+workspace-switch +workspaces-main t) + (setq buffers (doom/kill-all-buffers (buffer-list))) + (when interactive + (message "Killed %d workspace(s), %d window(s) & %d buffer(s)" + persps windows buffers)))) ;;;###autoload (defun +workspace/kill-session-and-quit () @@ -305,7 +312,12 @@ workspace, otherwise the new workspace is blank." end of the workspace list." (interactive (list (or current-prefix-arg - (completing-read "Switch to workspace: " (+workspace-list-names))))) + (if (featurep! :completion ivy) + (ivy-read "Switch to workspace: " + (+workspace-list-names) + :caller #'+workspace/switch-to + :preselect (+workspace-current-name)) + (completing-read "Switch to workspace: " (+workspace-list-names)))))) (when (and (stringp index) (string-match-p "^[0-9]+$" index)) (setq index (string-to-number index))) @@ -318,9 +330,7 @@ end of the workspace list." (error "No workspace at #%s" (1+ index))) (+workspace-switch dest))) ((stringp index) - (unless (member index names) - (error "No workspace named %s" index)) - (+workspace-switch index)) + (+workspace-switch index t)) (t (error "Not a valid index: %s" index))) (unless (called-interactively-p 'interactive) @@ -332,7 +342,8 @@ end of the workspace list." ;;;###autoload (dotimes (i 9) (defalias (intern (format "+workspace/switch-to-%d" i)) - (lambda () (interactive) (+workspace/switch-to i)))) + (lambda () (interactive) (+workspace/switch-to i)) + (format "Switch to workspace #%d" (1+ i)))) ;;;###autoload (defun +workspace/switch-to-final () @@ -393,6 +404,31 @@ the next." ((+workspace-error "Can't delete last workspace" t))))))) +;;;###autoload +(defun +workspace/swap-left (&optional count) + "Swap the current workspace with the COUNTth workspace on its left." + (interactive "p") + (let* ((current-name (+workspace-current-name)) + (count (or count 1)) + (index (- (cl-position current-name persp-names-cache :test #'equal) + count)) + (names (remove current-name persp-names-cache))) + (unless names + (user-error "Only one workspace")) + (let ((index (min (max 0 index) (length names)))) + (setq persp-names-cache + (append (cl-subseq names 0 index) + (list current-name) + (cl-subseq names index)))) + (when (called-interactively-p 'any) + (+workspace/display)))) + +;;;###autoload +(defun +workspace/swap-right (&optional count) + "Swap the current workspace with the COUNTth workspace on its right." + (interactive "p") + (funcall-interactively #'+workspace/swap-left (- count))) + ;; ;;; Tabs display in minibuffer @@ -490,9 +526,16 @@ This be hooked to `projectile-after-switch-project-hook'." (when dir (setq +workspaces--project-dir dir)) (when (and persp-mode +workspaces--project-dir) + (with-temp-buffer + ;; Load the project dir-local variables into the switch buffer, so the + ;; action can make use of them + (setq default-directory +workspaces--project-dir) + (hack-dir-local-variables-non-file-buffer) + (run-hooks 'projectile-before-switch-project-hook)) (unwind-protect (if (and (not (null +workspaces-on-switch-project-behavior)) (or (eq +workspaces-on-switch-project-behavior t) + (equal (safe-persp-name (get-current-persp)) persp-nil-name) (+workspace-buffer-list))) (let* ((persp (let ((project-name (doom-project-name +workspaces--project-dir))) @@ -514,6 +557,7 @@ This be hooked to `projectile-after-switch-project-hook'." (+workspace-rename (+workspace-current-name) (doom-project-name +workspaces--project-dir))) (unless current-prefix-arg (funcall +workspaces-switch-project-function +workspaces--project-dir))) + (run-hooks 'projectile-after-switch-project-hook) (setq +workspaces--project-dir nil)))) diff --git a/modules/ui/workspaces/config.el b/modules/ui/workspaces/config.el index e401719ca..f7c6cfbf0 100644 --- a/modules/ui/workspaces/config.el +++ b/modules/ui/workspaces/config.el @@ -62,28 +62,37 @@ stored in `persp-save-dir'.") (advice-add #'persp-asave-on-exit :around #'+workspaces-autosave-real-buffers-a) (add-hook! '(persp-mode-hook persp-after-load-state-functions) - (defun +workspaces-ensure-main-workspace-h (&rest _) - "Ensure the main workspace exists and the nil workspace is never active." + (defun +workspaces-ensure-no-nil-workspaces-h (&rest _) (when persp-mode - (let (persp-before-switch-functions) - ;; The default perspective persp-mode creates (`persp-nil-name') is - ;; special and doesn't represent a real persp object, so buffers can't - ;; really be assigned to it, among other quirks. We create a *real* main - ;; workspace to fill this role. - (unless (persp-get-by-name +workspaces-main) - (persp-add-new +workspaces-main)) - ;; Switch to it if we're in the nil perspective - (dolist (frame (frame-list)) - (when (string= (safe-persp-name (get-current-persp frame)) persp-nil-name) - (persp-frame-switch +workspaces-main frame) - ;; Fix #319: the warnings buffer gets swallowed by creating - ;; `+workspaces-main', so we display it manually, if it exists. - (when-let (warnings (get-buffer "*Warnings*")) - (save-excursion - (display-buffer-in-side-window - warnings '((window-height . shrink-window-if-larger-than-buffer))))))))))) + (dolist (frame (frame-list)) + (when (string= (safe-persp-name (get-current-persp frame)) persp-nil-name) + ;; Take extra steps to ensure no frame ends up in the nil perspective + (persp-frame-switch (or (cadr (hash-table-keys *persp-hash*)) + +workspaces-main) + frame)))))) (add-hook! 'persp-mode-hook + (defun +workspaces-init-first-workspace-h (&rest _) + "Ensure a main workspace exists." + (when persp-mode + (let (persp-before-switch-functions) + ;; The default perspective persp-mode creates is special and doesn't + ;; represent a real persp object, so buffers can't really be assigned + ;; to it, among other quirks. We hide the nil persp... + (when (equal (car persp-names-cache) persp-nil-name) + (pop persp-names-cache)) + ;; ...and create a *real* main workspace to fill this role, and hide + ;; the nil perspective. + (unless (or (persp-get-by-name +workspaces-main) + ;; Start from 2 b/c persp-mode counts the nil workspace + (> (hash-table-count *persp-hash*) 2)) + (persp-add-new +workspaces-main)) + ;; HACK Fix #319: the warnings buffer gets swallowed when creating + ;; `+workspaces-main', so display it ourselves, if it exists. + (when-let (warnings (get-buffer "*Warnings*")) + (save-excursion + (display-buffer-in-side-window + warnings '((window-height . shrink-window-if-larger-than-buffer)))))))) (defun +workspaces-init-persp-mode-h () (cond (persp-mode ;; `uniquify' breaks persp-mode. It renames old buffers, which causes @@ -108,15 +117,15 @@ stored in `persp-save-dir'.") ;; add buffers when they are switched to. (setq persp-add-buffer-on-find-file nil persp-add-buffer-on-after-change-major-mode nil) - (add-hook! '(doom-switch-buffer-hook server-visit-hook) (defun +workspaces-add-current-buffer-h () "Add current buffer to focused perspective." - (and persp-mode - (not (persp-buffer-filtered-out-p - (current-buffer) - persp-add-buffer-on-after-change-major-mode-filter-functions)) - (persp-add-buffer (current-buffer) (get-current-persp) nil nil)))) + (or (not persp-mode) + (persp-buffer-filtered-out-p + (or (buffer-base-buffer (current-buffer)) + (current-buffer)) + persp-add-buffer-on-after-change-major-mode-filter-functions) + (persp-add-buffer (current-buffer) (get-current-persp) nil nil)))) (add-hook 'persp-add-buffer-on-after-change-major-mode-filter-functions #'doom-unreal-buffer-p) @@ -162,15 +171,15 @@ stored in `persp-save-dir'.") ("C" counsel-projectile-switch-project-action-configure "run project configure command") ("e" counsel-projectile-switch-project-action-edit-dir-locals "edit project dir-locals") ("v" counsel-projectile-switch-project-action-vc "open project in vc-dir / magit / monky") - ("s" (lambda (project) (let ((projectile-switch-project-action (lambda () (call-interactively #'+ivy/project-search)))) - (counsel-projectile-switch-project-by-name project))) "search project") + ("s" (lambda (project) + (let ((projectile-switch-project-action + (lambda () (call-interactively #'+ivy/project-search)))) + (counsel-projectile-switch-project-by-name project))) "search project") ("xs" counsel-projectile-switch-project-action-run-shell "invoke shell from project root") ("xe" counsel-projectile-switch-project-action-run-eshell "invoke eshell from project root") ("xt" counsel-projectile-switch-project-action-run-term "invoke term from project root") ("X" counsel-projectile-switch-project-action-org-capture "org-capture into project"))) - (add-hook 'projectile-after-switch-project-hook #'+workspaces-switch-to-project-h) - ;; Fix #1973: visual selection surviving workspace changes (add-hook 'persp-before-deactivate-functions #'deactivate-mark) diff --git a/modules/ui/workspaces/packages.el b/modules/ui/workspaces/packages.el index 9b9cb3ffe..6209f670d 100644 --- a/modules/ui/workspaces/packages.el +++ b/modules/ui/workspaces/packages.el @@ -1,5 +1,4 @@ ;; -*- no-byte-compile: t; -*- ;;; ui/workspaces/packages.el -(package! persp-mode) - +(package! persp-mode :pin "e330e6240b") diff --git a/modules/ui/zen/README.org b/modules/ui/zen/README.org new file mode 100644 index 000000000..44bc719ab --- /dev/null +++ b/modules/ui/zen/README.org @@ -0,0 +1,51 @@ +#+TITLE: ui/zen +#+DATE: December 20, 2019 +#+SINCE: v3.0.0 +#+STARTUP: inlineimages + +* Table of Contents :TOC_3:noexport: +- [[#description][Description]] + - [[#module-flags][Module Flags]] + - [[#plugins][Plugins]] + - [[#hacks][Hacks]] +- [[#prerequisites][Prerequisites]] +- [[#features][Features]] + - [[#distractions-free-mode][Distractions-free mode]] +- [[#configuration][Configuration]] + - [[#enable-fullscreen-on-activation][Enable fullscreen on activation]] + +* Description +This module provides ~writeroom-mode~, which transforms Emacs into a +comfortable writing or coding space by centering the buffer, removing +superfluous UI elements, and turning on variable-pitch fonts (but keeps certain +elements fixed-pitch, where it makes sense to). + +If you've used olivetti, sublimity, or tabula-rasa, you will feel right at home. + +** Module Flags +This module provides no flags. + +** Plugins ++ [[https://github.com/joostkremers/writeroom-mode][writeroom-mode]] ++ [[https://gitlab.com/jabranham/mixed-pitch][mixed-pitch]] + +** Hacks ++ =mixed-pitch= has been modified not to override ~:height~ properties on the + faces it remaps. This means text-scale can affect the ~fixed-pitch~ faces as + well; See https://gitlab.com/jabranham/mixed-pitch/issues/6#note_79691741. + +* Prerequisites +This module has no prereqisites. + +* Features +** Distractions-free mode +This can be activated with =SPC t z= for evil users. There is no keybinding for +non-evil users yet, so use 'M-x writeroom-mode'. + +* Configuration +** Enable fullscreen on activation +#+BEGIN_SRC elisp +(setq writeroom-fullscreen-effect t) +#+END_SRC + +Or fullscreen manually with =SPC t F= (or =F11= for non-evil users). diff --git a/modules/ui/zen/config.el b/modules/ui/zen/config.el new file mode 100644 index 000000000..7be558f92 --- /dev/null +++ b/modules/ui/zen/config.el @@ -0,0 +1,64 @@ +;;; ui/zen/config.el -*- lexical-binding: t; -*- + +(defvar +zen-mixed-pitch-modes '(markdown-mode org-mode) + "What major-modes to enable `mixed-pitch-mode' in with `writeroom-mode'.") + +(defvar +zen-text-scale 2 + "The text-scaling level for `writeroom-mode'.") + + +;; +;;; Packages + +(after! writeroom-mode + ;; Users should be able to activate writeroom-mode in one buffer (e.g. an org + ;; buffer) and code in another. Fullscreening/maximizing will be opt-in. + (setq writeroom-maximize-window nil) + (remove-hook 'writeroom-global-effects 'writeroom-set-fullscreen) + + (add-hook! 'writeroom-mode-hook + (defun +zen-enable-text-scaling-mode-h () + "Enable `mixed-pitch-mode' when in `+zen-mixed-pitch-modes'." + (when (/= +zen-text-scale 0) + (text-scale-set (if writeroom-mode +zen-text-scale 0)) + (visual-fill-column-adjust)))) + + ;; Adjust margins when text size is changed + (advice-add #'text-scale-adjust :after #'visual-fill-column-adjust)) + + +(use-package! mixed-pitch + :hook (writeroom-mode . +zen-enable-mixed-pitch-mode-h) + :config + (defun +zen-enable-mixed-pitch-mode-h () + "Enable `mixed-pitch-mode' when in `+zen-mixed-pitch-modes'." + (when (apply #'derived-mode-p +zen-mixed-pitch-modes) + (mixed-pitch-mode (if writeroom-mode +1 -1)))) + + (pushnew! mixed-pitch-fixed-pitch-faces + 'org-date + 'org-special-keyword + 'org-property-value + 'org-ref-cite-face + 'org-tag + 'org-todo-keyword-todo + 'org-todo-keyword-habt + 'org-todo-keyword-done + 'org-todo-keyword-wait + 'org-todo-keyword-kill + 'org-todo-keyword-outd + 'org-todo + 'org-indent + 'font-lock-comment-face + 'line-number + 'line-number-current-line) + + ;; See https://gitlab.com/jabranham/mixed-pitch/issues/6#note_79691741 + (defadvice! +zen--fix-scaled-fixed-pitch-faces-a (orig-fn &rest args) + :around #'mixed-pitch-mode + (cl-letf* ((old-face-remap-add-relative (symbol-function #'face-remap-add-relative)) + ((symbol-function #'face-remap-add-relative) + (lambda (face &rest specs) + (funcall old-face-remap-add-relative + face (doom-plist-delete specs :height))))) + (apply orig-fn args)))) diff --git a/modules/ui/zen/packages.el b/modules/ui/zen/packages.el new file mode 100644 index 000000000..1085cea24 --- /dev/null +++ b/modules/ui/zen/packages.el @@ -0,0 +1,5 @@ +;; -*- no-byte-compile: t; -*- +;;; ui/zen/packages.el + +(package! writeroom-mode :pin "fa17eb6511") +(package! mixed-pitch :pin "fbc566ace3") diff --git a/test/shell.head.nix b/test/shell.head.nix new file mode 100644 index 000000000..dd5e47ea1 --- /dev/null +++ b/test/shell.head.nix @@ -0,0 +1,24 @@ +# Builds an sandbox for Doom Emacs with Emacs HEAD (27/28). Warning: it compiles +# Emacs; this takes a while! +# +# To create a doom environment w/ Emacs 27/28 with the test config: +# +# nix-shell shell.head.nix +# +# Or from a custom DOOMDIR: +# +# nix-shell --argstr doomdir ~/.config/doom + +{ emacsdir ? "$(pwd)/../" +, doomdir ? "$(pwd)" +, doomlocaldir ? "$(pwd)/.local.nix.head" }: + +import ./shell.nix rec { + pkgs = import { + overlays = [ + (import (builtins.fetchTarball https://github.com/nix-community/emacs-overlay/archive/master.tar.gz)) + ]; + }; + emacs = pkgs.emacsGit; + inherit emacsdir doomdir doomlocaldir; +} diff --git a/test/shell.nix b/test/shell.nix new file mode 100644 index 000000000..fb00ab446 --- /dev/null +++ b/test/shell.nix @@ -0,0 +1,47 @@ +# Builds a sandbox for Doom Emacs with the latest stable Emacs (26.3). Use this +# as a basis for module shell.nix's. +# +# Usage examples: +# +# To create a doom environment with the test config: +# +# nix-shell +# +# With your own DOOMDIR: +# +# nix-shell --argstr doomdir ~/.config/doom + +{ pkgs ? (import {}) +, emacs ? pkgs.emacs +, emacsdir ? "$(pwd)/.." +, doomdir ? "$(pwd)" +, doomlocaldir ? "$(pwd)/.local.nix" }: + +pkgs.stdenv.mkDerivation { + name = "doom-emacs"; + buildInputs = with pkgs; [ + emacs + git + (ripgrep.override {withPCRE2 = true;}) + ]; + shellHook = '' + export EMACSDIR="$(readlink -f "${emacsdir}")/" + export DOOMDIR="$(readlink -f "${doomdir}")/" + export DOOMLOCALDIR="$(readlink -f "${doomlocaldir}")/" + export PATH="$EMACSDIR/bin:$PATH" + echo "EMACSDIR=$EMACSDIR" + echo "DOOMDIR=$DOOMDIR" + echo "DOOMLOCALDIR=$DOOMLOCALDIR" + + # Copy your existing repos over to optimize on install times (but not the + # builds, because that may contain stale bytecode). + mkdir -p "$DOOMLOCALDIR/straight" + pushd "$DOOMLOCALDIR/straight" >/dev/null + if [[ -d "$EMACSDIR/.local/straight/repos" && ! -d ./repos ]]; then + cp -r "$EMACSDIR/.local/straight/repos" ./repos + fi + popd >/dev/null + + doom sync + ''; +}