Updating Doom Emacs.

This commit is contained in:
Derek Taylor
2020-06-19 22:43:40 -05:00
parent 0f664d532a
commit a5c86c514a
453 changed files with 13526 additions and 12454 deletions
+13 -4
View File
@@ -6,10 +6,12 @@
# #
# My bash config. Not much to see here. Some pretty standard stuff. # My bash config. Not much to see here. Some pretty standard stuff.
PATH="$HOME/.local/bin${PATH:+:${PATH}}" PATH="$HOME/.local/bin${PATH:+:${PATH}}" # adding .local/bin to $PATH
EDITOR="nvim" export TERM="xterm-256color" # getting proper colors
export TERM="st-256color"
export HISTCONTROL=ignoredups:erasedups # no duplicate entries export HISTCONTROL=ignoredups:erasedups # no duplicate entries
export ALTERNATE_EDITOR="" # setting for emacsclient
export EDITOR="emacsclient -t -a ''" # $EDITOR use Emacs in terminal
export VISUAL="emacsclient -c -a emacs" # $VISUAL use Emacs in GUI mode
[[ $- != *i* ]] && return [[ $- != *i* ]] && return
@@ -150,8 +152,15 @@ alias sudo='sudo '
alias ..='cd ..' alias ..='cd ..'
alias ...='cd ../..' alias ...='cd ../..'
# vim # vim and emacs
alias vim=nvim alias vim=nvim
alias em="/usr/bin/emacs -nw"
alias emacs="emacsclient -c -a ''"
# devour windows
alias mpv="devour mpv"
alias sxiv="devour sxiv"
alias zathura="devour zathura"
# broot # broot
alias br='br -dhp' alias br='br -dhp'
+106 -18
View File
@@ -1,28 +1,64 @@
;;; .doom.d/config.el -*- lexical-binding: t; -*- ;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;; Place your private configuration here! ;; Place your private configuration here! Remember, you do not need to run 'doom
;; ____ _____ ;; sync' after modifying this file!
;; | _ \_ _| Derek Taylor (DistroTube)
;; | | | || | http://www.youtube.com/c/DistroTube
;; | |_| || | http://www.gitlab.com/dwt1/ ;; Some functionality uses this to identify you, e.g. GPG configuration, email
;; |____/ |_| ;; clients, file templates and snippets.
(setq user-full-name "Derek Taylor"
user-mail-address "derek@distrotube.com")
;; Doom exposes five (optional) variables for controlling fonts in Doom. Here
;; are the three important ones:
;; ;;
;; A customized config for Doom Emacs (https://github.com/hlissner/doom-emacs) ;; + `doom-font'
;; Modified by Derek Taylor (http://www.gitlab.com/dwt1/) ;; + `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 "Mononoki Nerd Font" :size 15)
doom-variable-pitch-font (font-spec :family "Mononoki Nerd Font" :size 15))
;; Setting the font. ;; There are two ways to load a theme. Both assume the theme is installed and
(setq doom-font (font-spec :family "Mononoki Nerd Font Mono" :size 15)) ;; available. You can either set `doom-theme' or manually load a theme with the
;;(setq doom-font (font-spec :family "SauceCodePro Nerd Font" :size 15)) ;; `load-theme' function. This is the default:
;; Setting the theme
(setq doom-theme 'doom-palenight) (setq doom-theme 'doom-palenight)
;; 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 "~/Documents/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)
;; Setting the neotree width to be adjustable. ;; Setting the neotree width to be adjustable.
(setq neo-window-fixed-size nil) (setq neo-window-fixed-size nil)
;; Sets binding to toggle line wrap on/off. ;; Sets binding to toggle line wrap on/off.
(global-set-key "\C-x\ t" 'toggle-truncate-lines) (global-set-key "\C-x\ t" 'toggle-truncate-lines)
;; Sets binding to launch elfeed
(global-set-key (kbd "C-x w") 'elfeed)
;; Set browser to eww
(setq browse-url-browser-function 'eww-browse-url)
;; Force splits to open on the right
(defun prefer-horizontal-split ()
(set-variable 'split-height-threshold nil t)
(set-variable 'split-width-threshold 40 t)) ; make this as low as needed
(add-hook 'markdown-mode-hook 'prefer-horizontal-split)
;; Image previews in dired
(global-set-key (kbd "C-x i") 'peep-dired)
(evil-define-key 'normal peep-dired-mode-map (kbd "j") 'peep-dired-next-file
(kbd "k") 'peep-dired-prev-file)
(add-hook 'peep-dired-hook 'evil-normalize-keymaps)
;; Sets transparency for focuses and unfocused frames. ;; Sets transparency for focuses and unfocused frames.
;; (set-frame-parameter (selected-frame) 'alpha '(95 . 80)) ;; (set-frame-parameter (selected-frame) 'alpha '(95 . 80))
;; (add-to-list 'default-frame-alist '(alpha . (95 . 80))) ;; (add-to-list 'default-frame-alist '(alpha . (95 . 80)))
@@ -37,14 +73,33 @@
;; highlight-indent-guides-delay 0.01 ;; highlight-indent-guides-delay 0.01
;; highlight-indent-guides-responsive 'top ;; highlight-indent-guides-responsive 'top
;; highlight-indent-guides-auto-enabled nil)) ;; highlight-indent-guides-auto-enabled nil))
(use-package emms
:ensure t
:config
(require 'emms-setup) (require 'emms-setup)
(emms-standard) (require 'emms-info)
(require 'emms-cue) ;; Required for processing flacs.
(require 'emms-mode-line)
(require 'emms-playing-time)
(setq emms-source-file-default-directory "~/Music/Non-Classical/70s-80s/")
(setq emms-playlist-buffer-name "*Music*")
(setq emms-info-asynchronously t)
(unless (eq system-type 'windows-nt)
(setq emms-source-file-directory-tree-function
'emms-source-file-directory-tree-find))
(emms-all)
(emms-default-players) (emms-default-players)
(emms-mode-line 1)
(emms-playing-time 1))
;; Setting up mu4e which is an email client that works within emacs. ;; Setting up mu4e which is an email client that works within emacs.
;; You must install mu4e and mbsync through your Linux distribution's ;; You must install mu4e and mbsync through your Linux distribution's
;; package manager. ;; package manager.
;; Adding path to mu4e
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(require 'mu4e) (require 'mu4e)
(setq mu4e-get-mail-command "mbsync -c ~/.emacs.d/mu4e/.mbsyncrc -a" (setq mu4e-get-mail-command "mbsync -c ~/.emacs.d/mu4e/.mbsyncrc -a"
mu4e-update-interval 300) mu4e-update-interval 300)
@@ -71,5 +126,38 @@
smtpmail-default-smtp-server "smtp.1and1.com" smtpmail-default-smtp-server "smtp.1and1.com"
smtpmail-smtp-server "smtp.1and1.com" smtpmail-smtp-server "smtp.1and1.com"
smtpmail-smtp-service 587) smtpmail-smtp-service 587)
;; don't keep message buffers around
(setq message-kill-buffer-on-exit 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 c k').
;; This will open documentation for it, including demos of how they are used.
;;
;; You can also try 'gd' (or 'C-c c d') to jump to their definition and see how
;; they are implemented.
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(elfeed-feeds
(quote
("https://www.gamingonlinux.com/article_rss.php" "https://hackaday.com/blog/feed/" "https://opensource.com/feed" "https://linux.softpedia.com/backend.xml" "https://itsfoss.com/feed/" "https://www.zdnet.com/topic/linux/rss.xml" "https://www.phoronix.com/rss.php" "http://feeds.feedburner.com/d0od" "https://www.computerworld.com/index.rss" "https://www.networkworld.com/category/linux/index.rss" "https://www.techrepublic.com/rssfeeds/topic/open-source/" "https://betanews.com/feed" "http://lxer.com/module/newswire/headlines.rss" "https://distrowatch.com/news/dwd.xml")))
'(package-selected-packages (quote (peep-dired nav-flash evil-mu4e emms elfeed))))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
+46 -50
View File
@@ -1,14 +1,20 @@
;;; init.el -*- lexical-binding: t; -*- ;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install' ;; This file controls what Doom modules are enabled and what order they load
;; will do this for you). The `doom!' block below controls what modules are ;; in. Remember to run 'doom sync' after modifying it!
;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; 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 a "Module Index" link where you'll find
;; a comprehensive list 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 c 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 ;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; found in modules/README.org. ;; directory (for easy access to its source code).
;; Adding path to mu4e
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e") (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(doom! :input (doom! :input
@@ -30,21 +36,21 @@
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;hydra ;;hydra
indent-guides ; highlighted indent columns indent-guides ; highlighted indent columns
;;minimap ; show a map of the code on the side
modeline ; snazzy, Atom-inspired modeline, plus API modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink the current line after jumping ;;nav-flash ; blink cursor line after big motions
neotree ; a project drawer, like NERDTree for vim neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on ophints ; highlight the region an operation acts on
(popup ; tame sudden yet inevitable temporary windows (popup +defaults) ; tame sudden yet inevitable temporary windows
+all ; catch all popups that start with an asterix ;;pretty-code ; ligatures or substitute text with pretty symbols
+defaults) ; default popup rules
;;pretty-code ; replace bits of code with pretty symbols
;;tabs ; an tab bar for Emacs ;;tabs ; an tab bar for Emacs
;;treemacs ; a project drawer, like neotree but cooler ;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages ;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB vi-tilde-fringe ; fringe tildes to mark beyond EOB
window-select ; visually switch windows ;;window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces workspaces ; tab emulation, persistence & separate workspaces
;;zen ; distraction-free coding or writing
:editor :editor
(evil +everywhere); come to the dark side, we have cookies (evil +everywhere); come to the dark side, we have cookies
@@ -53,24 +59,30 @@
;;(format +onsave) ; automated prettiness ;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys ;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who don't like vim ;;lispy ; vim for lisp, for people who don't like vim
multiple-cursors ; editing in many places at once ;;multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent ;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of ;;parinfer ; turn lisp into python, sort of
rotate-text ; cycle region at point between text candidates ;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to snippets ; my elves. They type so I don't have to
;;word-wrap ; soft wrapping with language-aware indent ;;word-wrap ; soft wrapping with language-aware indent
:emacs :emacs
dired ; making dired pretty [functional] dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent electric ; smarter, keyword-based electric-indent
ibuffer ; interactive buffer management ;;ibuffer ; interactive buffer management
undo ; persistent, smarter undo for your inevitable mistakes
vc ; version-control and Emacs, sitting in a tree vc ; version-control and Emacs, sitting in a tree
:term :term
eshell ; a consistent, cross-platform shell (WIP) eshell ; the elisp shell that works everywhere
;;shell ; a terminal REPL for Emacs ;;shell ; simple shell REPL for Emacs
;;term ; terminals in Emacs ;;term ; basic terminal emulator for Emacs
vterm ; another terminals in Emacs vterm ; the best terminal emulation 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 :tools
;;ansible ;;ansible
@@ -80,11 +92,8 @@
;;editorconfig ; let someone else argue about tabs vs spaces ;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs ;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls) (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 ;;gist ; interacting with github gists
(lookup ; helps you navigate your code and documentation lookup ; navigate your code and its documentation
+docsets) ; ...or in Dash docsets locally
;;lsp ;;lsp
;;macos ; MacOS-specific commands ;;macos ; MacOS-specific commands
magit ; a git porcelain for Emacs magit ; a git porcelain for Emacs
@@ -93,20 +102,21 @@
;;pdf ; pdf enhancements ;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders ;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings ;;rgb ; creating color strings
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code ;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux ;;tmux ; an API for interacting with tmux
;;upload ; map local to remote projects via ssh/ftp ;;upload ; map local to remote projects via ssh/ftp
:lang :lang
;;agda ; types of types of types of types... ;;agda ; types of types of types of types...
;;assembly ; assembly for fun or debugging
;;cc ; C/C++/Obj-C madness ;;cc ; C/C++/Obj-C madness
;;clojure ; java with a lisp ;;clojure ; java with a lisp
common-lisp ; if you've seen one lisp, you've seen them all ;;common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs ;;coq ; proofs-as-programs
;;crystal ; ruby at the speed of c ;;crystal ; ruby at the speed of c
;;csharp ; unity, .NET, and mono shenanigans ;;csharp ; unity, .NET, and mono shenanigans
data ; config/data formats ;;data ; config/data formats
;;(dart +flutter) ; paint ui and not much else
;;elixir ; erlang done right ;;elixir ; erlang done right
;;elm ; care for a cup of TEA? ;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses emacs-lisp ; drown in parentheses
@@ -114,10 +124,13 @@
;;ess ; emacs speaks statistics ;;ess ; emacs speaks statistics
;;faust ; dsp, but you get to keep your soul ;;faust ; dsp, but you get to keep your soul
;;fsharp ; ML stands for Microsoft's Language ;;fsharp ; ML stands for Microsoft's Language
;;go ; the hipster dialect ;;fstar ; (dependent) types and (monadic) effects and Z3
;;gdscript ; the language you waited for
;;(go +lsp) ; the hipster dialect
(haskell +dante) ; 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 ;;hy ; readability of scheme w/ speed of python
;;idris ; ;;idris ;
json ; At least it ain't XML
;;(java +meghanada) ; the poster child for carpal tunnel syndrome ;;(java +meghanada) ; the poster child for carpal tunnel syndrome
javascript ; all(hope(abandon(ye(who(enter(here)))))) javascript ; all(hope(abandon(ye(who(enter(here))))))
;;julia ; a better, faster MATLAB ;;julia ; a better, faster MATLAB
@@ -131,31 +144,27 @@
;;nim ; python + lisp at the speed of c ;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!" ;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel ;;ocaml ; an objective camel
(org ; organize your plain life in plain text 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
;;+pomodoro ; be fruitful with the tomato technique
+present) ; using org-mode for presentations
perl ; write code no one else can comprehend
php ; perl's insecure younger brother php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more ;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional ;;purescript ; javascript, but functional
python ; beautiful is better than ugly python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever ;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs ;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client ;;rest ; Emacs as a REST client
;;rst ; ReST in peace ;;rst ; ReST in peace
;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() ;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good ;;scala ; java, but good
;;scheme ; a fully conniving family of lisps ;;scheme ; a fully conniving family of lisps
sh ; she sells {ba,z,fi}sh shells on the C xor sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No. ;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables? ;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance. ;;terra ; Earth and Moon in alignment for performance.
web ; the tubes web ; the tubes
yaml ; JSON, but readable
:email :email
;;(mu4e +gmail) ;;(mu4e +gmail)
@@ -167,20 +176,7 @@
;;irc ; how neckbeards socialize ;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader ;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought ;;twitter ; twitter client https://twitter.com/vnought
;;write ; emacs for writers (fiction, notes, papers, etc.)
:config :config
;;literate ;;literate
(default +bindings +smartparens)) (default +bindings +smartparens))
(custom-set-variables
;; custom-set-variables was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
'(org-agenda-files (quote ("~/org-basics.org" "~/Documents/org/orgmode.org"))))
(custom-set-faces
;; custom-set-faces was added by Custom.
;; If you edit it by hand, you could mess it up, so be careful.
;; Your init file should contain only one such instance.
;; If there is more than one, they won't work right.
)
+48 -5
View File
@@ -1,10 +1,53 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; .doom.d/packages.el ;;; $DOOMDIR/packages.el
;;; Examples: ;; To install a package with Doom you must declare them here and run 'doom sync'
;; (package! some-package) ;; on the command line, then restart Emacs for the changes to take effect -- or
;; (package! another-package :recipe (:host github :repo "username/repo")) ;; use 'M-x doom/reload'.
;; (package! builtin-package :disable t)
;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror:
;(package! some-package)
;; To install a package directly from a remote git repo, you must 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, 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"))
;; Use `:pin' to specify a particular commit to install.
;(package! builtin-package :pin "1a2b3c4d5e")
;; Doom's packages are pinned to a specific commit and updated from release to
;; release. The `unpin!' macro allows you to unpin single packages...
;(unpin! pinned-package)
;; ...or multiple packages
;(unpin! pinned-package another-pinned-package)
;; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
;(unpin! t)
(package! emojify) (package! emojify)
(package! elfeed)
(package! peep-dired)
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT) 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 Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
+227 -151
View File
@@ -1,178 +1,254 @@
<a href="http://doomemacs.org"> <div align="center">
<img src="https://img.shields.io/github/tag/hlissner/doom-emacs.svg?label=release&color=orange" alt="Made with Doom Emacs">
</a> # Doom Emacs
<a href="https://emacs.org">
<img src="https://img.shields.io/badge/Made_for-Emacs_26.1+-blueviolet.svg" alt="Made for Emacs 26.1+"> [Install](#install) • [Documentation] • [FAQ] • [Screenshots] • [Contribute](#contribute)
</a>
<a href="https://github.com/hlissner/doom-emacs/actions"> ![Made with Doom Emacs](https://img.shields.io/github/tag/hlissner/doom-emacs.svg?style=flat-square&label=release&color=58839b)
<img src="https://github.com/hlissner/doom-emacs/workflows/CI/badge.svg" alt="Build status: develop"> ![Supports Emacs 26-27](https://img.shields.io/badge/Supports-Emacs_26.1_--_27.x-blueviolet.svg?style=flat-square&logo=GNU%20Emacs&logoColor=white)
</a> ![Latest commit](https://img.shields.io/github/last-commit/hlissner/doom-emacs/develop?style=flat-square)
<a href="https://discord.gg/bcZ6P3y"> ![Build status: develop](https://img.shields.io/github/workflow/status/hlissner/doom-emacs/CI/develop?style=flat-square)
<img src="https://img.shields.io/badge/Discord-blue.svg?logo=discord&label=join" alt="Join our discord server" align="right"> [![Discord Server](https://img.shields.io/discord/406534637242810369?color=blue&label=Discord%20Chat&logo=discord&logoColor=white&style=flat-square)][Discord]
</a>
<br><br>
![Doom Emacs Screenshot](https://raw.githubusercontent.com/hlissner/doom-emacs/screenshots/main.png) ![Doom Emacs Screenshot](https://raw.githubusercontent.com/hlissner/doom-emacs/screenshots/main.png)
<p align="center"> </div>
<b><a href="/../../tree/screenshots">Screenshots</a></b>
|
<b><a href="docs/getting_started.org">Get started</a></b>
|
<b><a href="docs/contributing.org">Contribute</a></b>
|
<b><a href="docs/index.org">Documentation</a></b>
|
<b><a href="docs/faq.org">FAQ</a></b>
</p>
--- ---
**Quick start** ### Table of Contents
- [Introduction](#introduction)
- [Features](#features)
- [Prerequisites](#prerequisites)
- [Install](#install)
- [Roadmap](#roadmap)
- [Getting help](#getting-help)
- [Contribute](#contribute)
```bash
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d # Introduction
<a href="http://ultravioletbat.deviantart.com/art/Yay-Evil-111710573">
<img src="https://raw.githubusercontent.com/hlissner/doom-emacs/screenshots/cacochan.png" align="right" />
</a>
> 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 he succumbs to the [dark side][evil-mode]. This is his config.
Doom is a configuration framework for [GNU Emacs] tailored for Emacs bankruptcy
veterans who want less framework in their frameworks, a modicum of stability
(and reproducibility) from their package manager, 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 operating
system.
Its design is guided by these mantras:
+ **Gotta go fast.** Startup and run-time performance are priorities. Doom goes
beyond by modifying packages to be snappier and load lazier.
+ **Close to metal.** There's less between you and vanilla Emacs by design.
That's less to grok and less to work around when you tinker. Internals ought
to be written as if reading them were part of Doom's UX, and it is!
+ **Opinionated, but not stubborn.** Doom is about reasonable defaults and
curated opinions, but use as little or as much of it as you like.
+ **Your system, your rules.** You know better. At least, Doom hopes so! It
won't *automatically* install system dependencies (and will force plugins not
to either). Rely on `doom doctor` to tell you what's missing.
+ **Nix/Guix is a great idea!** The Emacs ecosystem is temperamental. Things
break and they break often. Disaster recovery should be a priority! Doom's
package management should be declarative and your private config reproducible,
and comes with a means to roll back releases and updates (still a WIP).
Check out [the FAQ][FAQ] for answers to common questions about the project.
# Features
- Minimalistic good looks inspired by modern editors.
- Curated and sane defaults for many packages, (major) OSes, and Emacs itself.
- A modular organizational structure for separating concerns in your config.
- A standard library designed to simplify your elisp bike shedding.
- A declarative [package management system][package-management] (powered by
[straight.el]) with a command line interface. Install packages from anywhere,
not just (M)ELPA, and pin them to any commit.
- Optional vim emulation powered by [evil-mode], including ports of popular vim
plugins like [vim-sneak], [vim-easymotion], [vim-unimpaired] and
[more][ported-vim-plugins]!
- Opt-in LSP integration for many languages, using [lsp-mode] or [eglot]
- Support for *many* programming languages. 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.
- A Spacemacs-esque [keybinding scheme][bindings], centered around leader
and localleader prefix keys (<kbd>SPC</kbd> and <kbd>SPC</kbd><kbd>m</kbd> for
evil users, <kbd>C-c</kbd> and <kbd>C-c l</kbd> for vanilla users).
- A rule-based [popup manager][popup-system] to control how temporary buffers
are displayed (and disposed of).
- Per-file indentation style detection and [editorconfig] integration. Let
someone else can 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 [ripgrep] and [ivy] or
[helm].
- Isolated and persistent workspaces (also substitutes for vim tabs).
- Support for Chinese and Japanese input systems.
- Save a snapshot of your shell environment to a file for Emacs to load at
startup. No more struggling to get Emacs to inherit your `PATH`, among other
things.
# Prerequisites
+ Git 2.23+
+ Emacs 26.1+ (*27 is recommended*) with GNUTLS support
+ [ripgrep] 11.0+
+ GNU `find`
+ *OPTIONAL:* [fd] 7.3.0+ (improves file indexing performance for some commands)
Doom is comprised of [~150 optional modules][Modules], some of which may have
additional dependencies. [Visit their documentation][Modules] or run `bin/doom
doctor` to check for any that you may have missed.
# Install
``` sh
git clone --depth 1 https://github.com/hlissner/doom-emacs ~/.emacs.d
~/.emacs.d/bin/doom install ~/.emacs.d/bin/doom install
``` ```
**Table of Contents** Then [read our Getting Started guide][getting-started] to be walked through
installing, configuring and maintaining Doom Emacs.
- [What is Doom Emacs](#what-is-doom-emacs) It's a good idea to add `~/.emacs.d/bin` to your `PATH`! Other `bin/doom`
- [Doom's mantras](#dooms-mantras) commands you should know about:
- [Features](#features)
- [Getting Help](#getting-help)
- [Community](#community)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
# What is Doom Emacs + `doom sync` to synchronize your private config with Doom by installing missing
packages, removing orphaned packages, and regenerating caches. Run this
whenever you modify your private `init.el` or `packages.el`, or install/remove
an Emacs package through your OS package manager (e.g. mu4e or agda).
+ `doom upgrade` to update Doom to the latest release & all installed packages.
+ `doom doctor` to diagnose common issues with your system and config.
+ `doom env` to dump a snapshot of your shell environment to a file that Doom
will load at startup. This allows Emacs to inherit your `PATH`, among other
things.
+ `doom build` to recompile all installed packages (use this if you up/downgrade
Emacs).
<a href="http://ultravioletbat.deviantart.com/art/Yay-Evil-111710573">
<img src="https://github.com/hlissner/doom-emacs/raw/screenshots/cacochan.png" align="right" />
</a>
It is a story as old as time. A stubborn, shell-dwelling, and melodramatic # Roadmap
vimmer -- envious of the features of modern text editors -- spirals into despair Doom is an active and ongoing project. To make that development more
before succumbing to the [dark side][url:evil-mode]. This is his config. transparent, its roadmap (and other concerns) are published across three github
project boards and a newsletter:
Doom is a configuration for [GNU Emacs](https://www.gnu.org/software/emacs/) + [Development Roadmap](https://github.com/hlissner/doom-emacs/projects/3):
designed to make Emacs faster and easier to customize. It can serve as framework roughly outlines our goals between release milestones and their progress.
for your own configuration or a resource for fellow Emacs enthusiasts who want + [Plugins under review](https://github.com/hlissner/doom-emacs/projects/2):
to learn more about our favorite OS. lists plugins we are watching and considering for inclusion, and what their
status for inclusion is. Please consult this list before requesting new
packages/features.
+ [Upstream bugs](https://github.com/hlissner/doom-emacs/projects/5): lists
issues that originate from elsewhere, and whether or not we have local
workarounds or temporary fixes for them.
+ ~~Doom's newsletter~~ (not finished) will contain changelogs in between
releases.
## Doom's mantras
- **Gotta go fast.** Startup and run-time performance are high priorities. # Getting help
Expensive functionality (built-in or in plugins) is modified and optimized Emacs is no journey of a mere thousand miles. You _will_ run into problems and
toward this end, otherwise, they must be opt-in. mysterious errors. When you do, here are some places you can look for help:
- **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!
## Features + [Our documentation][documentation] covers many use cases.
+ [The Configuration section][configuration] covers how to configure Doom and
its packages.
+ [The Package Management section][package-management] covers how to install
and disable packages.
+ [This section][bin/doom] explains the `bin/doom` script's most important
commands.
+ [This section][common-mistakes] lists some common configuration mistakes new
users make, when migrating a config from another distro or their own.
+ [This answer][change-theme] shows you how to add your own themes to your
private config.
+ [This answer][change-font] shows you how to change the default font.
+ Your issue may be documented in the [FAQ].
+ With Emacs built-in help system documentation is a keystroke away:
+ For functions: <kbd>SPC h f</kbd> or <kbd>C-h f</kbd>
+ For variables: <kbd>SPC h v</kbd> or <kbd>C-h v</kbd>
+ For a keybind: <kbd>SPC h k</kbd> or <kbd>C-h k</kbd>
+ To search available keybinds: <kbd>SPC h b b</kbd> or <kbd>C-h b b</kbd>
+ Run `bin/doom doctor` to detect common issues with your development
environment and private config.
+ Check out the [FAQ], in case your question has already been answered.
+ Search [Doom's issue tracker](/issues) in case your issue was already
reported.
+ Hop on [our Discord server][discord]; it's active and friendly! Keep an eye on
the #announcements channel, where I announce breaking updates and releases.
- 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 (<kbd>SPC</kbd> and <kbd>SPC</kbd><kbd>m</kbd>, by
default).
- A rule-based [popup management system][doom:popups] that dictates how
temporary or disposable buffers are displayed (and disposed of).
- Automatic indentation detection and [editorconfig][url:editorconfig]
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].
- 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!
# Getting Help # Contribute
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Elisp styleguide](https://img.shields.io/badge/elisp-style%20guide-purple?style=flat-square)](https://github.com/bbatsov/emacs-lisp-style-guide)
[![Donate on liberapay](https://img.shields.io/badge/liberapay-donate-1.svg?style=flat-square&logo=liberapay&color=blue)][liberapay]
[![Donate on paypal](https://img.shields.io/badge/paypal-donate-1?style=flat-square&logo=paypal&color=blue)][paypal]
## Community Doom is a labor of love and incurable madness, but I'm only one guy. Doom
wouldn't be where it is today without your help. I welcome contributions of any
kind!
We have [a Discord server][url:discord]! Hop on and say hi! + I :heart: pull requests and bug reports (see the [Contributing
Guidelines][contribute])!
+ Don't hesitate to [tell me my Elisp-fu
sucks](https://github.com/hlissner/doom-emacs/issues/new), but please tell me
why.
+ Hop on [our Discord server][Discord] and say hi! Help others, hang out or talk
to me about Emacs, gamedev, programming, physics, pixel art, anime, gaming --
anything you like. Nourish this lonely soul.
+ If you'd like to support my work financially, buy me a drink through
[liberapay] or [paypal]. My work contends with studies, adventures in indie
gamedev and freelance work. Donations help me allocate more time to my Emacs
and OSS capers.
## Troubleshooting [![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/0)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/0)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/1)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/1)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/2)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/2)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/3)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/3)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/4)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/4)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/5)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/5)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/6)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/6)
[![](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/images/7)](https://sourcerer.io/fame/hlissner/hlissner/doom-emacs/links/7)
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 [contribute]: docs/contributing.org
files are up-to-date. [discord]: https://discord.gg/qvGgnVx
- If you have byte-compiled your config (with `bin/doom compile`), see if [documentation]: docs/index.org
`bin/doom clean` makes the issue go away. Never debug issues with a [faq]: docs/faq.org
byte-compiled config, it will make your job harder. [getting-started]: docs/getting_started.org
- Run `bin/doom doctor` to detect common issues in your development environment. [install]: docs/getting_started.org#install
- Search Doom's issue tracker for mention of any error messages you've received. [backtrace]: docs/getting_started.org#how-to-extract-a-backtrace-from-an-error
- [Visit our FAQ][docs:faq] to see if your issue is listed. [configuration]: docs/getting_started.org#configuring-doom
[package-management]: docs/getting_started.org#package-management
[bin/doom]: docs/getting_started.org#the-bindoom-utility
[common-mistakes]: docs/getting_started.org#common-mistakes-when-configuring-doom-emacs
[change-theme]: docs/faq.org#how-do-i-change-the-theme
[change-font]: docs/faq.org#how-do-i-change-the-fonts
[modules]: docs/modules.org
[popup-system]: modules/ui/popup/README.org
[screenshots]: #screenshots
If all else fails, [file that bug report][github:new-issue]! Please include the [bindings]: modules/config/default/+evil-bindings.el
behavior you've observed, the behavior you expected, and any error messages or [editorconfig]: http://editorconfig.org/
warnings logged to the \*Messages\* buffer (can be opened with <kbd>SPC h [evil-mode]: https://github.com/emacs-evil/evil
e</kbd> or `M-x view-echo-area-messages`). [fd]: https://github.com/sharkdp/fd
[gnu emacs]: https://www.gnu.org/software/emacs/
[helm]: https://github.com/emacs-helm/helm
[ivy]: https://github.com/abo-abo/swiper
[lsp-mode]: https://github.com/emacs-lsp/lsp-mode
[eglot]: https://github.com/joaotavora/eglot
[nix]: https://nixos.org
[ported-vim-plugins]: modules/editor/evil/README.org#ported-vim-plugins
[ripgrep]: https://github.com/BurntSushi/ripgrep
[straight.el]: https://github.com/raxod502/straight.el
[vim-easymotion]: https://github.com/easymotion/vim-easymotion
[vim-lion]: https://github.com/tommcdo/vim-lion
[vim-sneak]: https://github.com/justinmk/vim-sneak
[vim-unimpaired]: https://github.com/tpope/vim-unimpaired
It's a great help if you included a backtrace with errors, i.e. `M-x [liberapay]: https://liberapay.com/hlissner/donate
toggle-debug-on-error` then recreating the error(s). [paypal]: https://paypal.me/henriklissner/10
## Contributing
Doom (and my Emacs work in general) is a labor of love and incurable madness,
done on my spare time. If you'd like to support my work, there are many things
you can do to help; I welcome any contribution!
- I love pull requests and bug reports. Check out the [Contributing
Guidelines][docs:contributing] to find out how you can help out.
- I welcome Elisp pointers! Don't hesitate to [tell me my Elisp-fu
sucks][github:new-issue] (but please tell me why).
- Hop on [our Discord server][url:discord] and say hi! Help others out, hang out
or talk to me about Emacs, or gamedev, or programming, machine learning,
physics, pixel art, anime, gaming -- anything you like. Nourish this lonely
soul!
- If you'd like to support my work financially, consider buying me a drink
through [liberapay][url:liberapay] or [paypal][url:paypal]. Donations are a
great help. My work here contends with studies, ventures in indie gamedev, and
my freelance work.
[docs:wiki]: docs/index.org
[docs:wiki-quickstart]: docs/getting_started.org
[docs:wiki-modules]: docs/index.org#Module%20List
[docs:wiki-customization]: docs/getting_started.org#Customize
[docs:contributing]: docs/contributing.org
[docs:faq]: docs/faq.org
[github:new-issue]: https://github.com/hlissner/doom-emacs/issues/new
[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: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
+61 -40
View File
@@ -1,30 +1,45 @@
#!/usr/bin/env sh #!/usr/bin/env sh
:; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*- :; ( echo "$EMACS" | grep -q "term" ) && EMACS=emacs || EMACS=${EMACS:-emacs} # -*-emacs-lisp-*-
:; command -v $EMACS >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; } :; command -v $EMACS >/dev/null || { >&2 echo "Can't find emacs in your PATH"; exit 1; }
:; VERSION=$($EMACS --version | head -n1) :; _VERSION=$($EMACS --version | head -n1)
:; case "$VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac :; case "$_VERSION" in *\ 2[0-5].[0-9]) echo "Detected Emacs $_VERSION"; echo "Doom only supports Emacs 26.1 and newer"; echo; exit 2 ;; esac
:; DOOMBASE="$(dirname "$0")/.." :; _DOOMBASE="${EMACSDIR:-$(dirname "$0")/..}"
:; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; } :; _DOOMPOST="$_DOOMBASE/.local/.doom.sh"
:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; } :; rm -f "$_DOOMPOST"
:; exec $EMACS --script "$0" -- "$@" :; $EMACS --no-site-file --script "$0" -- "$@"
:; exit 0 :; CODE=$?
:; [ -x "$_DOOMPOST" ] && PATH="$_DOOMBASE/bin:$PATH" "$_DOOMPOST" "$0" "$@"
:; exit $CODE
;; CLI ops tend to eat a lot of memory. To speed it up, stave off the GC, but
;; not to `most-positive-fixnum' like we do in init.el; that's too high -- we
;; don't want to intentionally leak memory.
(setq gc-cons-threshold 134217728) ; 128mb
(let* ((loaddir (file-name-directory (file-truename load-file-name))) (let* ((loaddir (file-name-directory (file-truename load-file-name)))
(emacsdir (getenv "EMACSDIR")) (emacsdir (getenv "EMACSDIR"))
(user-emacs-directory (or emacsdir (expand-file-name "../" loaddir))) (user-emacs-directory
(load-prefer-newer t)) (abbreviate-file-name
(if emacsdir
(file-name-as-directory emacsdir)
(expand-file-name "../" loaddir)))))
(push (expand-file-name "core" user-emacs-directory) load-path) ;;
(require 'core) (load (expand-file-name "core/core.el" user-emacs-directory) nil t)
;; HACK Load `cl' and site files manually so we can stop them from polluting
;; CLI logs with deprecation and file load messages.
(quiet! (when (> emacs-major-version 26)
(require 'cl))
(load "site-start" t t))
(doom-log "Initializing Doom CLI")
(require 'core-cli) (require 'core-cli)
(defcli! :main (defcli! :main
((help-p ["-h" "--help"] "Same as help command") ((help-p ["-h" "--help"] "Same as help command")
(debug-p ["-d" "--debug"] "Turns on doom-debug-mode (and debug-on-error)") (debug-p ["-d" "--debug"] "Turns on doom-debug-p (and debug-on-error)")
(yes-p ["-y" "--yes"] "Auto-accept all confirmation prompts") (yes-p ["-y" "--yes"] "Auto-accept all confirmation prompts")
(emacsdir ["--emacsdir" dir] "Use the emacs config at DIR (e.g. ~/.emacs.d)")
(doomdir ["--doomdir" dir] "Use the private module at DIR (e.g. ~/.doom.d)")
(localdir ["--localdir" dir] "Use DIR as your local storage directory")
&optional command &rest args) &optional command &rest args)
"A command line interface for managing Doom Emacs. "A command line interface for managing Doom Emacs.
@@ -33,46 +48,46 @@ Includes package management, diagnostics, unit tests, and byte-compilation.
This tool also makes it trivial to launch Emacs out of a different folder or This tool also makes it trivial to launch Emacs out of a different folder or
with a different private module." with a different private module."
:bare t :bare t
(when emacsdir
(setq user-emacs-directory (file-name-as-directory emacsdir))
(print! (info "EMACSDIR=%s") localdir))
(when doomdir
(setenv "DOOMDIR" doomdir)
(print! (info "DOOMDIR=%s") localdir))
(when localdir
(setenv "DOOMLOCALDIR" localdir)
(print! (info "DOOMLOCALDIR=%s") localdir))
(when debug-p (when debug-p
(setenv "DEBUG" "1") (setenv "DEBUG" "1")
(setq doom-debug-mode t) (setq doom-debug-p t)
(print! (info "Debug mode on"))) (print! (info "Debug mode on")))
(when yes-p (when yes-p
(setenv "YES" "1") (setenv "YES" "1")
(setq doom-auto-accept t) (setq doom-auto-accept t)
(print! (info "Auto-yes on"))) (print! (info "Auto-yes on")))
(when help-p (when help-p
(push command args) (when command
(push command args))
(setq command "help")) (setq command "help"))
;; Reload core in case any of the directories were changed. (when (equal (user-real-uid) 0)
(when (or emacsdir doomdir localdir) (print!
(load! "core/core.el" user-emacs-directory)) (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")))
(cond ((not noninteractive) ;; Load any the user's private init.el or any cli.el files in modules. This
(print! "Doom launched out of %s (test mode)" (path user-emacs-directory)) ;; gives the user (and modules) an opportunity to define their own CLI
(load! "init.el" user-emacs-directory) ;; commands, or to customize the CLI to better suit them.
(doom-run-all-startup-hooks-h)) (load! doom-module-init-file doom-private-dir t)
(maphash (doom-module-loader doom-cli-file) doom-modules)
(load! doom-cli-file doom-private-dir t)
(run-hooks 'doom-cli-pre-hook)
((null command) (let (print-level print-gensym print-length)
(doom-cli-execute "help")) (condition-case e
(if (null command)
((condition-case e (doom-cli-execute "help")
(let ((start-time (current-time))) (let ((start-time (current-time)))
(and (doom-cli-execute command args) (and (doom-cli-execute command args)
(progn (run-hooks 'doom-cli-post-hook) t)
(print! (success "Finished! (%.4fs)") (print! (success "Finished! (%.4fs)")
(float-time (float-time
(time-subtract (current-time) (time-subtract (current-time)
start-time))))) start-time))))))
(user-error (user-error
(print! (error "%s\n") (error-message-string e)) (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))
@@ -89,7 +104,12 @@ with a different private module."
(if (cdr e) (if (cdr e)
(dolist (item (cdr e)) (dolist (item (cdr e))
(print! "%S" item)) (print! "%S" item))
(print! "n/a")))) (print! "n/a")))
(when (and (bound-and-true-p straight-process-buffer)
(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 (unless debug-on-error
(terpri) (terpri)
(print! (print!
@@ -100,7 +120,8 @@ with a different private module."
(string-join (append (list (file-name-nondirectory load-file-name) "-d" command) (string-join (append (list (file-name-nondirectory load-file-name) "-d" command)
args) args)
" ")) " "))
(error ""))))))) ; Ensure non-zero exit code ;; Ensure non-zero exit code
(error ""))))))
(doom-cli-execute :main (cdr (member "--" argv))) (doom-cli-execute :main (cdr (member "--" argv)))
(setq argv nil)) (setq argv nil))
+4 -4
View File
@@ -14,7 +14,7 @@ cleanup() {
} }
# If emacs isn't running, we start a temporary daemon, solely for this window. # If emacs isn't running, we start a temporary daemon, solely for this window.
if ! emacsclient -e nil; then if ! emacsclient --suppress-output --eval nil; then
emacs --daemon emacs --daemon
trap cleanup EXIT INT TERM trap cleanup EXIT INT TERM
daemon=1 daemon=1
@@ -22,8 +22,8 @@ fi
# org-capture key mapped to argument flags # org-capture key mapped to argument flags
# keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2) # keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2)
while getopts hk opt; do while getopts "hk:" opt; do
key="\"$opt\"" key="\"$OPTARG\""
break break
done done
shift $((OPTIND-1)) shift $((OPTIND-1))
@@ -32,7 +32,7 @@ shift $((OPTIND-1))
if [ $daemon ]; then if [ $daemon ]; then
emacsclient -a "" \ emacsclient -a "" \
-c -F '((name . "org-capture") (width . 70) (height . 25) (transient . t))' \ -c -F '((name . "doom-capture") (width . 70) (height . 25) (transient . t))' \
-e "(+org-capture/open-frame \"$str\" ${key:-nil})" -e "(+org-capture/open-frame \"$str\" ${key:-nil})"
else else
# Non-daemon servers flicker a lot if frames are created from terminal, so we # Non-daemon servers flicker a lot if frames are created from terminal, so we
+32 -20
View File
@@ -36,7 +36,7 @@ it if it doesn't exist).")
;; ;;
;; Functions ;;; Functions
;;;###autoload ;;;###autoload
(defun doom-buffer-frame-predicate (buf) (defun doom-buffer-frame-predicate (buf)
@@ -139,6 +139,8 @@ If BUFFER-OR-NAME is omitted or nil, the current buffer is tested."
(stringp buffer-or-name) (stringp buffer-or-name)
(signal 'wrong-type-argument (list '(bufferp stringp) buffer-or-name))) (signal 'wrong-type-argument (list '(bufferp stringp) buffer-or-name)))
(when-let (buf (get-buffer buffer-or-name)) (when-let (buf (get-buffer buffer-or-name))
(when-let (basebuf (buffer-base-buffer buf))
(setq buf basebuf))
(and (buffer-live-p buf) (and (buffer-live-p buf)
(not (doom-temp-buffer-p buf)) (not (doom-temp-buffer-p buf))
(or (buffer-local-value 'doom-real-buffer-p buf) (or (buffer-local-value 'doom-real-buffer-p buf)
@@ -195,7 +197,9 @@ If DERIVED-P, test with `derived-mode-p', otherwise use `eq'."
;;;###autoload ;;;###autoload
(defun doom-set-buffer-real (buffer flag) (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 (with-current-buffer buffer
(setq doom-real-buffer-p flag))) (setq doom-real-buffer-p flag)))
@@ -251,7 +255,9 @@ regex PATTERN. Returns the number of killed buffers."
;;;###autoload ;;;###autoload
(defun doom-mark-buffer-as-real-h () (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)) (doom-set-buffer-real (current-buffer) t))
@@ -272,6 +278,12 @@ If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)."
(set-buffer-modified-p nil))) (set-buffer-modified-p nil)))
(doom-kill-buffer-fixup-windows buffer)) (doom-kill-buffer-fixup-windows buffer))
(defun doom--message-or-count (interactive message count)
(if interactive
(message message count)
count))
;;;###autoload ;;;###autoload
(defun doom/kill-all-buffers (&optional buffer-list interactive) (defun doom/kill-all-buffers (&optional buffer-list interactive)
"Kill all buffers and closes their windows. "Kill all buffers and closes their windows.
@@ -286,14 +298,14 @@ belong to the current project."
(if (null buffer-list) (if (null buffer-list)
(message "No buffers to kill") (message "No buffers to kill")
(save-some-buffers) (save-some-buffers)
(delete-other-windows)
(when (memq (current-buffer) buffer-list) (when (memq (current-buffer) buffer-list)
(switch-to-buffer (doom-fallback-buffer))) (switch-to-buffer (doom-fallback-buffer)))
(mapc #'doom-kill-buffer-and-windows buffer-list) (mapc #'kill-buffer buffer-list)
(delete-other-windows) (doom--message-or-count
(when interactive interactive "Killed %d buffers"
(message "Killed %s buffers"
(- (length buffer-list) (- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list))))))) (length (cl-remove-if-not #'buffer-live-p buffer-list))))))
;;;###autoload ;;;###autoload
(defun doom/kill-other-buffers (&optional buffer-list interactive) (defun doom/kill-other-buffers (&optional buffer-list interactive)
@@ -308,10 +320,10 @@ project."
(doom-buffer-list))) (doom-buffer-list)))
t)) t))
(mapc #'doom-kill-buffer-and-windows buffer-list) (mapc #'doom-kill-buffer-and-windows buffer-list)
(when interactive (doom--message-or-count
(message "Killed %s buffers" interactive "Killed %d other buffers"
(- (length buffer-list) (- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))) (length (cl-remove-if-not #'buffer-live-p buffer-list)))))
;;;###autoload ;;;###autoload
(defun doom/kill-matching-buffers (pattern &optional buffer-list interactive) (defun doom/kill-matching-buffers (pattern &optional buffer-list interactive)
@@ -341,10 +353,10 @@ current project."
(if current-prefix-arg (doom-project-buffer-list))) (if current-prefix-arg (doom-project-buffer-list)))
t)) t))
(mapc #'kill-buffer buffer-list) (mapc #'kill-buffer buffer-list)
(when interactive (doom--message-or-count
(message "Killed %s buried buffers" interactive "Killed %d buried buffers"
(- (length buffer-list) (- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))) (length (cl-remove-if-not #'buffer-live-p buffer-list)))))
;;;###autoload ;;;###autoload
(defun doom/kill-project-buffers (project &optional interactive) (defun doom/kill-project-buffers (project &optional interactive)
@@ -362,9 +374,9 @@ current project."
nil) nil)
t)) t))
(when project (when project
(let ((buffers (doom-project-buffer-list project))) (let ((buffer-list (doom-project-buffer-list project)))
(doom-kill-buffers-fixup-windows buffers) (doom-kill-buffers-fixup-windows buffer-list)
(when interactive (doom--message-or-count
(message "Killed %d buffer(s)" interactive "Killed %d project buffers"
(- (length buffers) (- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffers)))))))) (length (cl-remove-if-not #'buffer-live-p buffer-list)))))))
-95
View File
@@ -1,95 +0,0 @@
;;; core/autoload/cache.el -*- lexical-binding: t; -*-
;; This little library thinly wraps around persistent-soft (which is a pcache
;; wrapper, how about that). It has three purposes:
;;
;; + To encapsulate the cache backend (persistent-soft/pcache in this case), in
;; case it needs to change.
;; + To provide `doom-cache-persist': a mechanism for easily persisting
;; variables across Emacs sessions.
;; + To lazy-load persistent-soft until it is really needed.
;;
;; Like persistent-soft, caches assume a 2-tier structure, where all caches are
;; namespaced by location.
(defvar doom-cache-alists '(t)
"An alist of alists, containing lists of variables for the doom cache library
to persist across Emacs sessions.")
(defvar doom-cache-location 'doom
"The default location for cache files. This symbol is translated into a file
name under `pcache-directory' (by default a subdirectory under
`doom-cache-dir'). One file may contain multiple cache entries.")
(defun doom-save-persistent-cache-h ()
"Hook to run when an Emacs session is killed. Saves all persisted variables
listed in `doom-cache-alists' to files."
(dolist (alist (butlast doom-cache-alists 1))
(cl-loop with key = (car alist)
for var in (cdr alist)
if (symbol-value var)
do (doom-cache-set var it nil key))))
(add-hook 'kill-emacs-hook #'doom-save-persistent-cache-h)
;;
;; Library
;;;###autoload
(defmacro with-cache! (location &rest body)
"Runs BODY with a different default `doom-cache-location'."
(declare (indent defun))
`(let ((doom-cache-location ',location))
,@body))
;;;###autoload
(defun doom-cache-persist (location variables)
"Persist VARIABLES (list of symbols) in LOCATION (symbol).
This populates these variables with cached values, if one exists, and saves them
to file when Emacs quits.
Warning: this is incompatible with buffer-local variables."
(dolist (var variables)
(when (doom-cache-exists var location)
(set var (doom-cache-get var location))))
(setf (alist-get location doom-cache-alists)
(append variables (cdr (assq location doom-cache-alists)))))
;;;###autoload
(defun doom-cache-desist (location &optional variables)
"Unregisters VARIABLES (list of symbols) in LOCATION (symbol) from
`doom-cache-alists', thus preventing them from being saved between sessions.
Does not affect the actual variables themselves or their values."
(if variables
(setf (alist-get location doom-cache-alists)
(cl-set-difference (cdr (assq location doom-cache-alists))
variables))
(delq (assq location doom-cache-alists)
doom-cache-alists)))
;;;###autoload
(defun doom-cache-get (key &optional location)
"Retrieve KEY from LOCATION (defaults to `doom-cache-location'), if it exists
and hasn't expired."
(persistent-soft-fetch
key (symbol-name (or location doom-cache-location))))
;;;###autoload
(defun doom-cache-set (key value &optional ttl location)
"Set KEY to VALUE in the cache. TTL is the time (in seconds) until this cache
entry expires. LOCATION is the super-key to store this cache item under; the
default is `doom-cache-location'. "
(persistent-soft-store
key value
(symbol-name (or location doom-cache-location)) ttl))
;;;###autoload
(defun doom-cache-exists (key &optional location)
"Returns t if KEY exists at LOCATION (defaults to `doom-cache-location')."
(persistent-soft-exists-p key (or location doom-cache-location)))
;;;###autoload
(defun doom-cache-clear (&optional location)
"Clear a cache LOCATION (defaults to `doom-cache-location')."
(persistent-soft-flush (or location doom-cache-location)))
-45
View File
@@ -1,45 +0,0 @@
;;; core/autoload/cli.el -*- lexical-binding: t; -*-
;;
;;; Library
;;;###autoload
(defun doom-call-process (command &rest args)
"Execute COMMAND with ARGS synchronously.
Returns (STATUS . OUTPUT) when it is done, where STATUS is the returned error
code of the process and OUTPUT is its stdout output."
(with-temp-buffer
(cons (or (apply #'call-process command nil t nil args)
-1)
(string-trim (buffer-string)))))
;;;###autoload
(defun doom-exec-process (command &rest args)
"Execute COMMAND with ARGS synchronously.
Unlike `doom-call-process', this pipes output to `standard-output' on the fly to
simulate 'exec' in the shell, so batch scripts could run external programs
synchronously without sacrificing their output.
Warning: freezes indefinitely on any stdin prompt."
;; FIXME Is there any way to handle prompts?
(with-temp-buffer
(cons (let ((process
(make-process :name "doom-sh"
:buffer (current-buffer)
:command (cons command args)
:connection-type 'pipe))
done-p)
(set-process-filter
process (lambda (_process output)
(princ output (current-buffer))
(princ output)))
(set-process-sentinel
process (lambda (process _event)
(when (memq (process-status process) '(exit stop))
(setq done-p t))))
(while (not done-p)
(sit-for 0.1))
(process-exit-status process))
(string-trim (buffer-string)))))
+28 -24
View File
@@ -26,8 +26,9 @@
(doom-project-find-file doom-private-dir)) (doom-project-find-file doom-private-dir))
;;;###autoload ;;;###autoload
(defun doom/goto-doomblock () (defun doom/goto-private-init-file ()
"Open your private init.el and go to your `doom!' block." "Open your private init.el file.
And jumps to your `doom!' block."
(interactive) (interactive)
(find-file (expand-file-name "init.el" doom-private-dir)) (find-file (expand-file-name "init.el" doom-private-dir))
(goto-char (goto-char
@@ -37,13 +38,13 @@
(point)))) (point))))
;;;###autoload ;;;###autoload
(defun doom/goto-config-file () (defun doom/goto-private-config-file ()
"Open your private config.el file." "Open your private config.el file."
(interactive) (interactive)
(find-file (expand-file-name "config.el" doom-private-dir))) (find-file (expand-file-name "config.el" doom-private-dir)))
;;;###autoload ;;;###autoload
(defun doom/goto-packages-file () (defun doom/goto-private-packages-file ()
"Open your private packages.el file." "Open your private packages.el file."
(interactive) (interactive)
(find-file (expand-file-name "packages.el" doom-private-dir))) (find-file (expand-file-name "packages.el" doom-private-dir)))
@@ -52,32 +53,39 @@
;; ;;
;;; Managements ;;; Managements
(cl-defmacro doom--compile (command &key on-success on-failure) (defmacro doom--if-compile (command on-success &optional on-failure)
(declare (indent defun)) (declare (indent 2))
`(with-current-buffer (compile ,command) (let ((windowsym (make-symbol "doom-sync-window")))
`(with-current-buffer (compile ,command t)
(let ((,windowsym (get-buffer-window (current-buffer))))
(select-window ,windowsym)
(add-hook (add-hook
'compilation-finish-functions 'compilation-finish-functions
(lambda (_buf status) (lambda (_buf status)
(if (equal status "finished\n") (if (equal status "finished\n")
,on-success (progn
(delete-window ,windowsym)
,on-success)
,on-failure)) ,on-failure))
nil 'local))) nil 'local)))))
;;;###autoload ;;;###autoload
(defun doom/reload () (defun doom/reload ()
"Reloads your private config. "Reloads your private config.
This is experimental! It will try to do as `bin/doom refresh' does, but from This is experimental! It will try to do as `bin/doom sync' does, but from within
within this Emacs session. i.e. it reload autoloads files (if necessary), this Emacs session. i.e. it reload autoloads files (if necessary), reloads your
reloads your package list, and lastly, reloads your private config.el. package list, and lastly, reloads your private config.el.
Runs `doom-reload-hook' afterwards." Runs `doom-reload-hook' afterwards."
(interactive) (interactive)
(require 'core-cli) (require 'core-cli)
(when (and IS-WINDOWS (file-exists-p doom-env-file)) (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")) (message "Can't regenerate envvar file from within Emacs. Run 'doom env' from the console"))
(doom--compile (format "%s refresh -e" doom-bin) ;; In case doom/reload is run before incrementally loaded packages are loaded,
:on-success ;; which could cause odd load order issues.
(mapc #'require (cdr doom-incremental-packages))
(doom--if-compile (format "%s sync -e" doom-bin)
(let ((doom-reloading-p t)) (let ((doom-reloading-p t))
(doom-initialize 'force) (doom-initialize 'force)
(with-demoted-errors "PRIVATE CONFIG ERROR: %s" (with-demoted-errors "PRIVATE CONFIG ERROR: %s"
@@ -86,8 +94,7 @@ Runs `doom-reload-hook' afterwards."
(doom-initialize-modules 'force) (doom-initialize-modules 'force)
(general-auto-unbind-keys t))) (general-auto-unbind-keys t)))
(run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook) (run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)
(print! (success "Config successfully reloaded!"))) (message "Config successfully reloaded!"))
:on-failure
(user-error "Failed to reload your config"))) (user-error "Failed to reload your config")))
;;;###autoload ;;;###autoload
@@ -98,13 +105,13 @@ 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 reloads your package and module visibility, but does not install new packages or
remove orphaned ones. It also doesn't reload your private config. 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." line."
(interactive) (interactive)
(require 'core-cli) (require 'core-cli)
(require 'core-packages) (require 'core-packages)
(doom-initialize-packages) (doom-initialize-packages)
(doom-cli-reload-autoloads nil 'force)) (doom-autoloads-reload))
;;;###autoload ;;;###autoload
(defun doom/reload-env (&optional arg) (defun doom/reload-env (&optional arg)
@@ -118,25 +125,22 @@ imported into Emacs."
(interactive "P") (interactive "P")
(when IS-WINDOWS (when IS-WINDOWS
(user-error "Cannot reload envvar file from within Emacs on Windows, run it from cmd.exe")) (user-error "Cannot reload envvar file from within Emacs on Windows, run it from cmd.exe"))
(doom--compile (doom--if-compile
(format "%s -ic '%s env%s'" (format "%s -ic '%s env%s'"
(string-trim (string-trim
(shell-command-to-string (shell-command-to-string
(format "getent passwd %S | cut -d: -f7" (format "getent passwd %S | cut -d: -f7"
(user-login-name)))) (user-login-name))))
doom-bin (if arg " -c" "")) doom-bin (if arg " -c" ""))
:on-success
(let ((doom-reloading-p t)) (let ((doom-reloading-p t))
(unless arg (unless arg
(doom-load-envvars-file doom-env-file))) (doom-load-envvars-file doom-env-file)))
:on-failure
(error "Failed to generate env file"))) (error "Failed to generate env file")))
;;;###autoload ;;;###autoload
(defun doom/upgrade () (defun doom/upgrade ()
"Run 'doom upgrade' then prompt to restart Emacs." "Run 'doom upgrade' then prompt to restart Emacs."
(interactive) (interactive)
(doom--compile (format "%s upgrade" doom-bin) (doom--if-compile (format "%s -y upgrade" doom-bin)
:on-success
(when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?") (when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?")
(doom/restart-and-restore)))) (doom/restart-and-restore))))
+148 -75
View File
@@ -1,5 +1,37 @@
;;; core/autoload/debug.el -*- lexical-binding: t; -*- ;;; core/autoload/debug.el -*- lexical-binding: t; -*-
;;
;;; Doom's debug mode
;;;###autoload
(defvar doom-debug-variables
'(doom-debug-p
init-file-debug
debug-on-error
garbage-collection-messages
use-package-verbose
jka-compr-verbose
lsp-log-io
gcmh-verbose
magit-refresh-verbose
url-debug)
"A list of variable to toggle on `doom-debug-mode'.")
;;;###autoload
(define-minor-mode doom-debug-mode
"Toggle `debug-on-error' and `doom-debug-p' for verbose logging."
:init-value doom-debug-p
:global t
(let ((value
(cond ((eq arg 'toggle) (not doom-debug-mode))
((> (prefix-numeric-value arg) 0)))))
(mapc (doom-rpartial #'set value) doom-debug-variables)
(message "Debug mode %s" (if value "on" "off"))))
;;
;;; Hooks
;;;###autoload ;;;###autoload
(defun doom-run-all-startup-hooks-h () (defun doom-run-all-startup-hooks-h ()
"Run all startup Emacs hooks. Meant to be executed after starting Emacs with "Run all startup Emacs hooks. Meant to be executed after starting Emacs with
@@ -17,36 +49,44 @@
;; ;;
;;; Helpers ;;; Helpers
(defun doom-template-insert (template) (defsubst doom--collect-forms-in (file form)
"TODO" (when (file-readable-p file)
(let ((file (expand-file-name (format "templates/%s" template) doom-core-dir))) (let (forms)
(when (file-exists-p file) (with-temp-buffer
(insert-file-contents file)))) (insert-file-contents file)
(let (emacs-lisp-mode) (emacs-lisp-mode))
(while (re-search-forward (format "(%s " (regexp-quote form)) nil t)
(let ((ppss (syntax-ppss)))
(unless (or (nth 4 ppss)
(nth 3 ppss))
(save-excursion
(goto-char (match-beginning 0))
(push (sexp-at-point) forms)))))
(nreverse forms)))))
;;;###autoload ;;;###autoload
(defun doom-info () (defun doom-info ()
"Returns diagnostic information about the current Emacs session in markdown, "Returns diagnostic information about the current Emacs session in markdown,
ready to be pasted in a bug report on github." ready to be pasted in a bug report on github."
(require 'vc-git) (require 'vc-git)
(require 'core-packages)
(let ((default-directory doom-emacs-dir) (let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))) (doom-modules (doom-module-list)))
(cl-letf (letf! (defun sh (&rest args) (cdr (apply #'doom-call-process args)))
(((symbol-function 'sh)
(lambda (&rest args)
(cdr (apply #'doom-call-process args)))))
`((emacs `((emacs
(version . ,emacs-version) (version . ,emacs-version)
(features ,@system-configuration-features) (features ,@system-configuration-features)
(build . ,(format-time-string "%b %d, %Y" emacs-build-time)) (build . ,(format-time-string "%b %d, %Y" emacs-build-time))
(buildopts ,system-configuration-options) (buildopts ,system-configuration-options)
(windowsys . ,(if noninteractive 'batch window-system)) (windowsys . ,(if doom-interactive-p window-system 'batch))
(daemonp . ,(cond ((daemonp) 'daemon) (daemonp . ,(cond ((daemonp) 'daemon)
((and (require 'server) ((and (require 'server)
(server-running-p)) (server-running-p))
'server-running)))) 'server-running))))
(doom (doom
(version . ,doom-version) (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 (system
(type . ,system-type) (type . ,system-type)
(config . ,system-configuration) (config . ,system-configuration)
@@ -79,14 +119,19 @@ ready to be pasted in a bug report on github."
'("n/a"))) '("n/a")))
(packages (packages
,@(or (condition-case e ,@(or (condition-case e
(cl-loop for (name . plist) in (doom-package-list) (mapcar
if (cl-find :private (plist-get plist :modules) #'cdr (doom--collect-forms-in
:key #'car) (doom-path doom-private-dir "packages.el")
collect "package!"))
(if-let (splist (doom-plist-delete (copy-sequence plist) (error (format "<%S>" e)))
:modules)) '("n/a")))
(prin1-to-string (cons name splist)) (unpin
name)) ,@(or (condition-case e
(mapcan #'identity
(mapcar
#'cdr (doom--collect-forms-in
(doom-path doom-private-dir "packages.el")
"unpin!")))
(error (format "<%S>" e))) (error (format "<%S>" e)))
'("n/a"))) '("n/a")))
(elpa (elpa
@@ -109,13 +154,13 @@ branch and commit."
(interactive) (interactive)
(require 'vc-git) (require 'vc-git)
(let ((default-directory doom-core-dir)) (let ((default-directory doom-core-dir))
(print! "Doom v%s (Emacs v%s)\nBranch: %s\nCommit: %s\nBuild date: %s" (print! "Doom v%s (%s)\nEmacs v%s\nBranch: %s\nBuild date: %s"
doom-version doom-version
(or (vc-git-working-revision doom-core-dir)
"n/a")
emacs-version emacs-version
(or (vc-git--symbolic-ref doom-core-dir) (or (vc-git--symbolic-ref doom-core-dir)
"n/a") "n/a")
(or (vc-git-working-revision doom-core-dir)
"n/a")
(or (cdr (doom-call-process "git" "log" "-1" "--format=%ci")) (or (cdr (doom-call-process "git" "log" "-1" "--format=%ci"))
"n/a")))) "n/a"))))
@@ -127,22 +172,23 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(let ((buffer (get-buffer-create "*doom-info*")) (let ((buffer (get-buffer-create "*doom-info*"))
(info (doom-info))) (info (doom-info)))
(with-current-buffer buffer (with-current-buffer buffer
(unless (or noninteractive (or (not doom-interactive-p)
(eq major-mode 'markdown-mode) (eq major-mode 'markdown-mode)
(not (fboundp 'markdown-mode))) (not (fboundp 'markdown-mode))
(markdown-mode)) (markdown-mode))
(erase-buffer) (erase-buffer)
(if raw (if raw
(progn (progn
(save-excursion (save-excursion
(pp info (current-buffer))) (pp info (current-buffer)))
(when (search-forward "(modules " nil t) (dolist (sym '(modules packages))
(goto-char (match-beginning 0)) (when (re-search-forward (format "^ *\\((%s\\)" sym) nil t)
(goto-char (match-beginning 1))
(cl-destructuring-bind (beg . end) (cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'sexp) (bounds-of-thing-at-point 'sexp)
(let ((sexp (prin1-to-string (sexp-at-point)))) (let ((sexp (prin1-to-string (sexp-at-point))))
(delete-region beg end) (delete-region beg end)
(insert sexp))))) (insert sexp))))))
(insert "<details>\n\n```\n") (insert "<details>\n\n```\n")
(dolist (group info) (dolist (group info)
(insert! "%-8s%-10s %s\n" (insert! "%-8s%-10s %s\n"
@@ -153,7 +199,7 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(insert! (indent 8 "%-10s %s\n") (insert! (indent 8 "%-10s %s\n")
((car spec) (cdr spec))))) ((car spec) (cdr spec)))))
(insert "```\n</details>")) (insert "```\n</details>"))
(if noninteractive (if (not doom-interactive-p)
(print! (buffer-string)) (print! (buffer-string))
(switch-to-buffer buffer) (switch-to-buffer buffer)
(kill-new (buffer-string)) (kill-new (buffer-string))
@@ -161,15 +207,45 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
;;;###autoload ;;;###autoload
(defun doom/am-i-secure () (defun doom/am-i-secure ()
"Test to see if your root certificates are securely configured in emacs." "Test to see if your root certificates are securely configured in emacs.
Some items are not supported by the `nsm.el' module."
(declare (interactive-only t)) (declare (interactive-only t))
(interactive) (interactive)
(unless (string-match-p "\\_<GNUTLS\\_>" system-configuration-features) (unless (string-match-p "\\_<GNUTLS\\_>" system-configuration-features)
(warn "gnutls support isn't built into Emacs, there may be problems")) (warn "gnutls support isn't built into Emacs, there may be problems"))
(if-let* ((bad-hosts (if-let* ((bad-hosts
(cl-loop for bad (cl-loop for bad
in '("https://wrong.host.badssl.com/" in '("https://expired.badssl.com/"
"https://self-signed.badssl.com/") "https://wrong.host.badssl.com/"
"https://self-signed.badssl.com/"
"https://untrusted-root.badssl.com/"
;; "https://revoked.badssl.com/"
;; "https://pinning-test.badssl.com/"
"https://sha1-intermediate.badssl.com/"
"https://rc4-md5.badssl.com/"
"https://rc4.badssl.com/"
"https://3des.badssl.com/"
"https://null.badssl.com/"
"https://sha1-intermediate.badssl.com/"
;; "https://client-cert-missing.badssl.com/"
"https://dh480.badssl.com/"
"https://dh512.badssl.com/"
"https://dh-small-subgroup.badssl.com/"
"https://dh-composite.badssl.com/"
"https://invalid-expected-sct.badssl.com/"
;; "https://no-sct.badssl.com/"
;; "https://mixed-script.badssl.com/"
;; "https://very.badssl.com/"
"https://subdomain.preloaded-hsts.badssl.com/"
"https://superfish.badssl.com/"
"https://edellroot.badssl.com/"
"https://dsdtestprovider.badssl.com/"
"https://preact-cli.badssl.com/"
"https://webpack-dev-server.badssl.com/"
"https://captive-portal.badssl.com/"
"https://mitm-software.badssl.com/"
"https://sha1-2016.badssl.com/"
"https://sha1-2017.badssl.com/")
if (condition-case _e if (condition-case _e
(url-retrieve-synchronously bad) (url-retrieve-synchronously bad)
(error nil)) (error nil))
@@ -193,11 +269,11 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(file (make-temp-file "doom-sandbox-"))) (file (make-temp-file "doom-sandbox-")))
(require 'package) (require 'package)
(with-temp-file file (with-temp-file file
(insert (prin1 `(progn
(prin1-to-string (setq noninteractive nil
(macroexp-progn process-environment ',doom--initial-process-environment
(append `((setq noninteractive nil exec-path ',doom--initial-exec-path
doom-debug-mode t init-file-debug t
load-path ',load-path load-path ',load-path
package--init-file-ensured t package--init-file-ensured t
package-user-dir ,package-user-dir package-user-dir ,package-user-dir
@@ -205,36 +281,47 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
user-emacs-directory ,doom-emacs-dir) user-emacs-directory ,doom-emacs-dir)
(with-eval-after-load 'undo-tree (with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't ;; undo-tree throws errors because `buffer-undo-tree' isn't
;; corrrectly initialized ;; correctly initialized
(setq-default buffer-undo-tree (make-undo-tree)))) (setq-default buffer-undo-tree (make-undo-tree)))
(pcase mode (ignore-errors
(delete-directory ,(expand-file-name "auto-save-list" doom-emacs-dir) 'parents)))
(current-buffer))
(prin1 `(unwind-protect
(defun --run-- () ,(read (concat "(progn\n" contents "\n)")))
(delete-file ,file))
(current-buffer))
(prin1 (pcase mode
(`vanilla-doom+ ; Doom core + modules - private config (`vanilla-doom+ ; Doom core + modules - private config
`((load-file ,(expand-file-name "core.el" doom-core-dir)) `(progn
(load-file ,(expand-file-name "core.el" doom-core-dir))
(setq doom-modules-dirs (list doom-modules-dir))
(let ((doom-init-modules-p t))
(doom-initialize) (doom-initialize)
(doom-initialize-core) (doom-initialize-core-modules))
(add-hook 'window-setup-hook #'doom-display-benchmark-h)
(setq doom-modules ',doom-modules) (setq doom-modules ',doom-modules)
(maphash (lambda (key plist) (maphash (lambda (key plist)
(let ((doom--current-module key) (doom-module-put
(doom--current-flags (plist-get plist :flags))) (car key) (cdr key)
(load! "init" (doom-module-locate-path (car key) (cdr key)) t))) :path (doom-module-locate-path (car key) (cdr key))))
doom-modules)
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "config" (doom-module-locate-path (car key) (cdr key)) t)))
doom-modules) doom-modules)
(--run--)
(maphash (doom-module-loader doom-module-init-file) doom-modules)
(maphash (doom-module-loader doom-module-config-file) doom-modules)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(doom-run-all-startup-hooks-h))) (doom-run-all-startup-hooks-h)))
(`vanilla-doom ; only Doom core (`vanilla-doom ; only Doom core
`((load-file ,(expand-file-name "core.el" doom-core-dir)) `(progn
(load-file ,(expand-file-name "core.el" doom-core-dir))
(let ((doom-init-modules-p t))
(doom-initialize) (doom-initialize)
(doom-initialize-core) (doom-initialize-core-modules))
(--run--)
(doom-run-all-startup-hooks-h))) (doom-run-all-startup-hooks-h)))
(`vanilla ; nothing loaded (`vanilla ; nothing loaded
`((package-initialize))))))) `(progn
"\n(unwind-protect (progn\n" contents "\n)\n" (package-initialize)
(format "(delete-file %S))" file))) (--run--))))
(current-buffer)))
(let ((args (if (eq mode 'doom) (let ((args (if (eq mode 'doom)
(list "-l" file) (list "-l" file)
(list "-Q" "-l" file)))) (list "-Q" "-l" file))))
@@ -254,10 +341,10 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(delete-file file) (delete-file file)
(signal (car e) (cdr e))))))) (signal (car e) (cdr e)))))))
(fset 'doom--run-vanilla-emacs (lambda! (doom--run-sandbox 'vanilla))) (fset 'doom--run-vanilla-emacs (cmd! (doom--run-sandbox 'vanilla)))
(fset 'doom--run-vanilla-doom (lambda! (doom--run-sandbox 'vanilla-doom))) (fset 'doom--run-vanilla-doom (cmd! (doom--run-sandbox 'vanilla-doom)))
(fset 'doom--run-vanilla-doom+ (lambda! (doom--run-sandbox 'vanilla-doom+))) (fset 'doom--run-vanilla-doom+ (cmd! (doom--run-sandbox 'vanilla-doom+)))
(fset 'doom--run-full-doom (lambda! (doom--run-sandbox 'doom))) (fset 'doom--run-full-doom (cmd! (doom--run-sandbox 'doom)))
(defvar doom-sandbox-emacs-lisp-mode-map (defvar doom-sandbox-emacs-lisp-mode-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
@@ -293,7 +380,7 @@ to reproduce bugs and determine if Doom is to blame."
(doom-sandbox-emacs-lisp-mode) (doom-sandbox-emacs-lisp-mode)
(setq-local default-directory doom-emacs-dir) (setq-local default-directory doom-emacs-dir)
(unless (buffer-live-p exists) (unless (buffer-live-p exists)
(doom-template-insert "VANILLA_SANDBOX") (insert-file-contents (doom-glob doom-core-dir "templates/VANILLA_SANDBOX"))
(let ((contents (substitute-command-keys (buffer-string)))) (let ((contents (substitute-command-keys (buffer-string))))
(erase-buffer) (erase-buffer)
(insert contents "\n"))) (insert contents "\n")))
@@ -328,17 +415,3 @@ will be automatically appended to the result."
(profiler-report) (profiler-report)
(profiler-stop)) (profiler-stop))
(setq doom--profiler (not doom--profiler))) (setq doom--profiler (not doom--profiler)))
;;;###autoload
(defun doom/toggle-debug-mode (&optional arg)
"Toggle `debug-on-error' and `doom-debug-mode' for verbose logging."
(interactive (list (or current-prefix-arg 'toggle)))
(let ((value
(cond ((eq arg 'toggle) (not doom-debug-mode))
((> (prefix-numeric-value arg) 0)))))
(setq doom-debug-mode value
debug-on-error value
jka-compr-verbose value
lsp-log-io value
gcmh-verbose value)
(message "Debug mode %s" (if value "on" "off"))))
+92 -96
View File
@@ -22,9 +22,6 @@ Returns (approximately):
This is used by `file-exists-p!' and `project-file-exists-p!'." This is used by `file-exists-p!' and `project-file-exists-p!'."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(let ((exists-fn (if (fboundp 'projectile-file-exists-p)
#'projectile-file-exists-p
#'file-exists-p)))
(if (and (listp spec) (if (and (listp spec)
(memq (car spec) '(or and))) (memq (car spec) '(or and)))
(cons (car spec) (cons (car spec)
@@ -36,9 +33,9 @@ This is used by `file-exists-p!' and `project-file-exists-p!'."
(and (stringp ,filevar) (and (stringp ,filevar)
,(if directory ,(if directory
`(let ((default-directory ,directory)) `(let ((default-directory ,directory))
(,exists-fn ,filevar)) (file-exists-p ,filevar))
(list exists-fn filevar)) `(file-exists-p ,filevar))
,filevar)))))) ,filevar)))))
(defun doom--path (&rest segments) (defun doom--path (&rest segments)
(let (file-name-handler-alist) (let (file-name-handler-alist)
@@ -108,11 +105,11 @@ be relative to it.
The search recurses up to DEPTH and no further. DEPTH is an integer. 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." MATCH is a string regexp. Only entries that match it will be included."
(let (file-name-handler-alist (let (result file-name-handler-alist)
result)
(dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths))) (dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths)))
(cond ((file-directory-p file) (cond ((file-directory-p file)
(nconcq! result (appendq!
result
(and (memq type '(t dirs)) (and (memq type '(t dirs))
(string-match-p match file) (string-match-p match file)
(not (and filter (funcall filter file))) (not (and filter (funcall filter file)))
@@ -203,53 +200,29 @@ single file or nested compound statement of `and' and `or' statements."
;; ;;
;;; Helpers ;;; Helpers
(defun doom--forget-file (old-path &optional new-path) (defun doom--update-files (&rest files)
"Ensure `recentf', `projectile' and `save-place' forget OLD-PATH." "Ensure FILES are updated in `recentf', `magit' and `save-place'."
(let (toplevels)
(dolist (file files)
(when (featurep 'vc)
(vc-file-clearprops file)
(when-let (buffer (get-file-buffer file))
(with-current-buffer buffer
(vc-refresh-state))))
(when (featurep 'magit)
(when-let (default-directory (magit-toplevel (file-name-directory file)))
(cl-pushnew default-directory toplevels)))
(unless (file-readable-p file)
(when (bound-and-true-p recentf-mode) (when (bound-and-true-p recentf-mode)
(when new-path (recentf-remove-if-non-kept file))
(recentf-add-file new-path))
(recentf-remove-if-non-kept old-path))
(when (and (bound-and-true-p projectile-mode) (when (and (bound-and-true-p projectile-mode)
(doom-project-p) (doom-project-p)
(projectile-file-cached-p old-path (doom-project-root))) (projectile-file-cached-p file (doom-project-root)))
(projectile-purge-file-from-cache old-path)) (projectile-purge-file-from-cache file))))
(dolist (default-directory toplevels)
(magit-refresh))
(when (bound-and-true-p save-place-mode) (when (bound-and-true-p save-place-mode)
(save-place-forget-unreadable-files))) (save-place-forget-unreadable-files))))
(defun doom--update-file (path)
(when (featurep 'vc)
(vc-file-clearprops path)
(vc-resynch-buffer path nil t))
(when (featurep 'magit)
(magit-refresh)))
(defun doom--copy-file (old-path new-path &optional force-p)
(let* ((new-path (expand-file-name new-path))
(old-path (file-truename old-path))
(new-path (apply #'expand-file-name
(if (or (directory-name-p new-path)
(file-directory-p new-path))
(list (file-name-nondirectory old-path) new-path)
(list new-path))))
(new-path-dir (file-name-directory new-path))
(project-root (doom-project-root))
(short-new-name (if (and project-root (file-in-directory-p new-path project-root))
(file-relative-name new-path project-root)
(abbreviate-file-name new-path))))
(unless (file-directory-p new-path-dir)
(make-directory new-path-dir t))
(when (buffer-modified-p)
(save-buffer))
(cond ((file-equal-p old-path new-path)
(throw 'status 'overwrite-self))
((and (file-exists-p new-path)
(not force-p)
(not (y-or-n-p (format "File already exists at %s, overwrite?" short-new-name))))
(throw 'status 'aborted))
((file-exists-p old-path)
(copy-file old-path new-path t)
short-new-name)
(short-new-name))))
;; ;;
@@ -257,68 +230,69 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload ;;;###autoload
(defun doom/delete-this-file (&optional path force-p) (defun doom/delete-this-file (&optional path force-p)
"Delete FILENAME (defaults to the file associated with current buffer) and "Delete PATH, kill its buffers and expunge it from vc/magit cache.
kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)."
If PATH is not specified, default to the current buffer's file.
If FORCE-P, delete without confirmation."
(interactive (interactive
(list (file-truename (buffer-file-name)) (list (buffer-file-name (buffer-base-buffer))
current-prefix-arg)) current-prefix-arg))
(let* ((fbase (file-name-sans-extension (file-name-nondirectory path))) (let* ((path (or path (buffer-file-name (buffer-base-buffer))))
(buf (current-buffer))) (short-path (abbreviate-file-name path)))
(cond ((not (file-exists-p path)) (unless (and path (file-exists-p path))
(user-error "Buffer is not visiting any file"))
(unless (file-exists-p path)
(error "File doesn't exist: %s" path)) (error "File doesn't exist: %s" path))
((not (or force-p (y-or-n-p (format "Really delete %s?" fbase)))) (unless (or force-p (y-or-n-p (format "Really delete %S?" short-path)))
(message "Aborted") (user-error "Aborted"))
nil) (let ((buf (current-buffer)))
((unwind-protect (unwind-protect
(progn (delete-file path) t) (progn (delete-file path) t)
(let ((short-path (file-relative-name path (doom-project-root))))
(if (file-exists-p path) (if (file-exists-p path)
(error "Failed to delete %s" short-path) (error "Failed to delete %S" short-path)
;; Ensures that windows displaying this buffer will be switched ;; Ensures that windows displaying this buffer will be switched to
;; to real buffers (`doom-real-buffer-p') ;; real buffers (`doom-real-buffer-p')
(doom/kill-this-buffer-in-all-windows buf t) (doom/kill-this-buffer-in-all-windows buf t)
(doom--forget-file path) (doom--update-files path)
(doom--update-file path) (message "Deleted %S" short-path))))))
(message "Successfully deleted %s" short-path))))))))
;;;###autoload ;;;###autoload
(defun doom/copy-this-file (new-path &optional force-p) (defun doom/copy-this-file (new-path &optional force-p)
"Copy current buffer's file to NEW-PATH. If FORCE-P, overwrite the destination "Copy current buffer's file to NEW-PATH.
file if it exists, without confirmation."
If FORCE-P, overwrite the destination file if it exists, without confirmation."
(interactive (interactive
(list (read-file-name "Copy file to: ") (list (read-file-name "Copy file to: ")
current-prefix-arg)) current-prefix-arg))
(pcase (catch 'status (unless (and buffer-file-name (file-exists-p buffer-file-name))
(when-let (dest (doom--copy-file (buffer-file-name) new-path force-p)) (user-error "Buffer is not visiting any file"))
(doom--update-file new-path) (let ((old-path (buffer-file-name (buffer-base-buffer)))
(message "File successfully copied to %s" dest))) (new-path (expand-file-name new-path)))
(`overwrite-self (error "Cannot overwrite self")) (make-directory (file-name-directory new-path) 't)
(`aborted (message "Aborted")) (copy-file old-path new-path (or force-p 1))
(_ t))) (doom--update-files old-path new-path)
(message "File copied to %S" (abbreviate-file-name new-path))))
;;;###autoload ;;;###autoload
(defun doom/move-this-file (new-path &optional force-p) (defun doom/move-this-file (new-path &optional force-p)
"Move current buffer's file to NEW-PATH. If FORCE-P, overwrite the destination "Move current buffer's file to NEW-PATH.
file if it exists, without confirmation."
If FORCE-P, overwrite the destination file if it exists, without confirmation."
(interactive (interactive
(list (read-file-name "Move file to: ") (list (read-file-name "Move file to: ")
current-prefix-arg)) current-prefix-arg))
(pcase (catch 'status (unless (and buffer-file-name (file-exists-p buffer-file-name))
(let ((old-path (buffer-file-name)) (user-error "Buffer is not visiting any file"))
(let ((old-path (buffer-file-name (buffer-base-buffer)))
(new-path (expand-file-name new-path))) (new-path (expand-file-name new-path)))
(when-let (dest (doom--copy-file old-path new-path force-p)) (make-directory (file-name-directory new-path) 't)
(when (file-exists-p old-path) (rename-file old-path new-path (or force-p 1))
(delete-file old-path)) (set-visited-file-name new-path t t)
(kill-current-buffer) (doom--update-files old-path new-path)
(doom--forget-file old-path new-path) (message "File moved to %S" (abbreviate-file-name new-path))))
(doom--update-file new-path)
(find-file new-path)
(message "File successfully moved to %s" dest))))
(`overwrite-self (error "Cannot overwrite self"))
(`aborted (message "Aborted"))
(_ t)))
(defun doom--sudo-file (file) (defun doom--sudo-file-path (file)
(let ((host (or (file-remote-p file 'host) "localhost"))) (let ((host (or (file-remote-p file 'host) "localhost")))
(concat "/" (when (file-remote-p file) (concat "/" (when (file-remote-p file)
(concat (file-remote-p file 'method) ":" (concat (file-remote-p file 'method) ":"
@@ -334,10 +308,32 @@ file if it exists, without confirmation."
(defun doom/sudo-find-file (file) (defun doom/sudo-find-file (file)
"Open FILE as root." "Open FILE as root."
(interactive "FOpen file as root: ") (interactive "FOpen file as root: ")
(find-file (doom--sudo-file file))) (find-file (doom--sudo-file-path file)))
;;;###autoload ;;;###autoload
(defun doom/sudo-this-file () (defun doom/sudo-this-file ()
"Open the current file as root." "Open the current file as root."
(interactive) (interactive)
(find-alternate-file (doom--sudo-file buffer-file-name))) (find-file
(doom--sudo-file-path
(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-path buffer-file-name)))
(if-let (buffer (find-file-noselect file))
(let ((origin (current-buffer)))
(copy-to-buffer buffer (point-min) (point-max))
(unwind-protect
(with-current-buffer buffer
(save-buffer))
(unless (eq origin buffer)
(kill-buffer buffer))
(with-current-buffer origin
(revert-buffer t t))))
(user-error "Unable to open %S" file))))
+27 -31
View File
@@ -12,7 +12,7 @@ scaled up by `doom-big-font-increment'. See `doom-font' for details on
acceptable values for this variable.") acceptable values for this variable.")
;;;###autoload ;;;###autoload
(defvar doom-big-font-increment 8 (defvar doom-big-font-increment 4
"How many steps to increase the font size (with `doom-font' as the base) when "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.") `doom-big-font-mode' is enabled and `doom-big-font' is nil.")
@@ -20,38 +20,31 @@ acceptable values for this variable.")
;; ;;
;;; Library ;;; Library
(defun doom--font-name (fontname frame) (defun doom--font-name (fontname)
(when (query-fontset 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)))) (setq fontname (nth 2 ascii))))
(or (x-decompose-font-name fontname) (or (x-decompose-font-name fontname)
(error "Cannot decompose font name"))) (error "Cannot decompose font name")))
(defun doom--frame-list (&optional frame) (defvar doom--font-scale nil)
"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))))
;;;###autoload ;;;###autoload
(defun doom-adjust-font-size (increment &optional frame) (defun doom-adjust-font-size (increment)
"Increase size of font in FRAME by INCREMENT. "Increase size of font in FRAME by INCREMENT.
FRAME parameter defaults to current frame." FRAME parameter defaults to current frame."
(if (null increment) (if (null increment)
(let ((frames (doom--frame-list frame))) (progn
(dolist (frame frames) (set-frame-font doom-font 'keep-size t)
(when (frame-parameter frame 'font-scale) (setf (alist-get 'font default-frame-alist)
(set-frame-parameter frame 'font-scale nil))) (cond ((stringp doom-font) doom-font)
(set-frame-font doom-font 'keep-size frames) ((fontp doom-font) (font-xlfd-name doom-font))
(and frames t)) ((signal 'wrong-type-argument (list '(fontp stringp)
(let (success) doom-font)))))
(dolist (frame (doom--frame-list frame)) t)
(let* ((font (frame-parameter frame 'font)) (let* ((font (frame-parameter nil 'font))
(font (doom--font-name font frame)) (font (doom--font-name font))
(increment (* increment doom-font-increment)) (increment (* increment doom-font-increment))
(zoom-factor (or (frame-parameter frame 'font-scale) 0))) (zoom-factor (or doom--font-scale 0)))
(let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum)) (let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum))
increment))) increment)))
(unless (> new-size 0) (unless (> new-size 0)
@@ -63,13 +56,11 @@ FRAME parameter defaults to current frame."
(setq font (x-compose-font-name font)) (setq font (x-compose-font-name font))
(unless (x-list-fonts font) (unless (x-list-fonts font)
(error "Cannot change font size")) (error "Cannot change font size"))
(set-frame-parameter frame 'font font) (set-frame-font font 'keep-size t)
(set-frame-parameter frame 'font-scale (+ zoom-factor increment)) (setf (alist-get 'font default-frame-alist) font)
(setq success t))) (setq doom--font-scale (+ zoom-factor increment))
(when success
;; Unlike `set-frame-font', `set-frame-parameter' won't trigger this ;; Unlike `set-frame-font', `set-frame-parameter' won't trigger this
(run-hooks 'after-setting-font-hook) (run-hooks 'after-setting-font-hook))))
t))))
;; ;;
@@ -127,8 +118,13 @@ This uses `doom/increase-font-size' under the hood, and enlargens the font by
(unless doom-font (unless doom-font
(user-error "`doom-font' must be set to a valid font")) (user-error "`doom-font' must be set to a valid font"))
(if doom-big-font (if doom-big-font
(set-frame-font (if doom-big-font-mode doom-big-font doom-font) (let ((font (if doom-big-font-mode doom-big-font doom-font)))
'keep-size (doom--frame-list)) (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 (doom-adjust-font-size
(and doom-big-font-mode (and doom-big-font-mode
(integerp doom-big-font-increment) (integerp doom-big-font-increment)
-243
View File
@@ -1,243 +0,0 @@
;;; core/autoload/format.el -*- lexical-binding: t; -*-
(defvar doom-format-ansi-alist
'(;; fx
(bold 1 :weight bold)
(dark 2)
(italic 3 :slant italic)
(underscore 4 :underline t)
(blink 5)
(rapid 6)
(contrary 7)
(concealed 8)
(strike 9 :strike-through t)
;; fg
(black 30 term-color-black)
(red 31 term-color-red)
(green 32 term-color-green)
(yellow 33 term-color-yellow)
(blue 34 term-color-blue)
(magenta 35 term-color-magenta)
(cyan 36 term-color-cyan)
(white 37 term-color-white)
;; bg
(on-black 40 term-color-black)
(on-red 41 term-color-red)
(on-green 42 term-color-green)
(on-yellow 43 term-color-yellow)
(on-blue 44 term-color-blue)
(on-magenta 45 term-color-magenta)
(on-cyan 46 term-color-cyan)
(on-white 47 term-color-white))
"An alist of fg/bg/fx names mapped to ansi codes and term-color-* variables.
This serves as the cipher for converting (COLOR ...) function calls in `print!'
and `format!' into colored output, where COLOR is any car of this list.")
(defvar doom-format-class-alist
`((color . doom--format-color)
(class . doom--format-class)
(indent . doom--format-indent)
(autofill . doom--format-autofill)
(success . (lambda (str &rest args)
(apply #'doom--format-color 'green (format "✓ %s" str) args)))
(warn . (lambda (str &rest args)
(apply #'doom--format-color 'yellow (format "! %s" str) args)))
(error . (lambda (str &rest args)
(apply #'doom--format-color 'red (format "x %s" str) args)))
(info . (lambda (str &rest args)
(concat "- " (if args (apply #'format str args) str))))
(start . (lambda (str &rest args)
(concat "> " (if args (apply #'format str args) str))))
(debug . (lambda (str &rest args)
(if doom-debug-mode
(if args
(apply #'format str args)
(format "%s" str))
"")))
(path . abbreviate-file-name)
(symbol . symbol-name)
(relpath . (lambda (str &optional dir)
(if (or (not str)
(not (stringp str))
(string-empty-p str))
str
(let ((dir (or dir (file-truename default-directory)))
(str (file-truename str)))
(if (file-in-directory-p str dir)
(file-relative-name str dir)
(abbreviate-file-name str))))))
(filename . file-name-nondirectory)
(dirname . (lambda (path)
(unless (file-directory-p path)
(setq path (file-name-directory path)))
(directory-file-name path))))
"An alist of text classes that map to transformation functions.
Any of these classes can be called like functions from within `format!' and
`print!' calls, which will transform their input.")
(defvar doom-format-indent 0
"Level to rigidly indent text returned by `format!' and `print!'.")
(defvar doom-format-indent-increment 2
"Steps in which to increment `doom-format-indent' for consecutive levels.")
(defvar doom-format-backend
(if noninteractive 'ansi 'text-properties)
"Determines whether to print colors with ANSI codes or with text properties.
Accepts 'ansi and 'text-properties. nil means don't render colors.")
;;
;;; Library
;;;###autoload
(defun doom--format (output)
(if (string-empty-p (string-trim output))
""
(concat (make-string doom-format-indent 32)
(replace-regexp-in-string
"\n" (concat "\n" (make-string doom-format-indent 32))
output t t))))
;;;###autoload
(defun doom--format-print (output)
(unless (string-empty-p output)
(princ output)
(when (or noninteractive (not (eq standard-output t)))
(terpri)) ; newline
t))
;;;###autoload
(defun doom--format-indent (width text &optional prefix)
"Indent TEXT by WIDTH spaces. If ARGS, format TEXT with them."
(with-temp-buffer
(setq text (format "%s" text))
(insert text)
(indent-rigidly (point-min) (point-max) width)
(when (stringp prefix)
(when (> width 2)
(goto-char (point-min))
(beginning-of-line-text)
(delete-char (- (length prefix)))
(insert prefix)))
(buffer-string)))
;;;###autoload
(defun doom--format-autofill (&rest msgs)
"Ensure MSG is split into lines no longer than `fill-column'."
(with-temp-buffer
(let ((fill-column 76))
(dolist (line msgs)
(when line
(insert (format "%s" line))))
(fill-region (point-min) (point-max))
(buffer-string))))
;;;###autoload
(defun doom--format-color (style format &rest args)
"Apply STYLE to formatted MESSAGE with ARGS.
STYLE is a symbol that correlates to `doom-format-ansi-alist'.
In a noninteractive session, this wraps the result in ansi color codes.
Otherwise, it maps colors to a term-color-* face."
(let* ((code (cadr (assq style doom-format-ansi-alist)))
(format (format "%s" format))
(message (if args (apply #'format format args) format)))
(unless code
(error "%S is an invalid color" style))
(pcase doom-format-backend
(`ansi
(format "\e[%dm%s\e[%dm" code message 0))
(`text-properties
(require 'term) ; piggyback on term's color faces
(propertize
message
'face
(append (get-text-property 0 'face format)
(cond ((>= code 40)
`(:background ,(caddr (assq style doom-format-ansi-alist))))
((>= code 30)
`(:foreground ,(face-foreground (caddr (assq style doom-format-ansi-alist)))))
((cddr (assq style doom-format-ansi-alist)))))))
(_ message))))
;;;###autoload
(defun doom--format-class (class format &rest args)
"Apply CLASS to formatted format with ARGS.
CLASS is derived from `doom-format-class-alist', and can contain any arbitrary,
transformative logic."
(let (fn)
(cond ((setq fn (cdr (assq class doom-format-class-alist)))
(if (functionp fn)
(apply fn format args)
(error "%s does not have a function" class)))
(args (apply #'format format args))
(format))))
;;;###autoload
(defun doom--format-apply (forms &optional sub)
"Replace color-name functions with calls to `doom--format-color'."
(cond ((null forms) nil)
((listp forms)
(append (cond ((not (symbolp (car forms)))
(list (doom--format-apply (car forms))))
(sub
(list (car forms)))
((assq (car forms) doom-format-ansi-alist)
`(doom--format-color ',(car forms)))
((assq (car forms) doom-format-class-alist)
`(doom--format-class ',(car forms)))
((list (car forms))))
(doom--format-apply (cdr forms) t)
nil))
(forms)))
;;;###autoload
(defmacro format! (message &rest args)
"An alternative to `format' that understands (color ...) and converts them
into faces or ANSI codes depending on the type of sesssion we're in."
`(doom--format (format ,@(doom--format-apply `(,message ,@args)))))
;;;###autoload
(defmacro print-group! (&rest body)
"Indents any `print!' or `format!' output within BODY."
`(let ((doom-format-indent (+ doom-format-indent-increment doom-format-indent)))
,@body))
;;;###autoload
(defmacro print! (message &rest args)
"Uses `message' in interactive sessions and `princ' otherwise (prints to
standard out).
Can be colored using (color ...) blocks:
(print! \"Hello %s\" (bold (blue \"How are you?\")))
(print! \"Hello %s\" (red \"World\"))
(print! (green \"Great %s!\") \"success\")
Uses faces in interactive sessions and ANSI codes otherwise."
`(doom--format-print (format! ,message ,@args)))
;;;###autoload
(defmacro insert! (message &rest args)
"Like `insert'; the last argument must be format arguments for MESSAGE.
\(fn MESSAGE... ARGS)"
`(insert (format! (concat ,message ,@(butlast args))
,@(car (last args)))))
;;;###autoload
(defmacro error! (message &rest args)
"Like `error', but with the power of `format!'."
`(error (format! ,message ,@args)))
;;;###autoload
(defmacro user-error! (message &rest args)
"Like `user-error', but with the power of `format!'."
`(user-error (format! ,message ,@args)))
+121 -101
View File
@@ -3,9 +3,6 @@
(defvar doom--help-major-mode-module-alist (defvar doom--help-major-mode-module-alist
'((dockerfile-mode :tools docker) '((dockerfile-mode :tools docker)
(agda2-mode :lang agda) (agda2-mode :lang agda)
(haxor-mode :lang assembly)
(mips-mode :lang assembly)
(nasm-mode :lang assembly)
(c-mode :lang cc) (c-mode :lang cc)
(c++-mode :lang cc) (c++-mode :lang cc)
(objc++-mode :lang cc) (objc++-mode :lang cc)
@@ -34,6 +31,7 @@
(js2-mode :lang javascript) (js2-mode :lang javascript)
(rjsx-mode :lang javascript) (rjsx-mode :lang javascript)
(typescript-mode :lang javascript) (typescript-mode :lang javascript)
(typescript-tsx-mode :lang javascript)
(coffee-mode :lang javascript) (coffee-mode :lang javascript)
(julia-mode :lang julia) (julia-mode :lang julia)
(kotlin-mode :lang kotlin) (kotlin-mode :lang kotlin)
@@ -41,13 +39,14 @@
(LaTeX-mode :lang latex) (LaTeX-mode :lang latex)
(ledger-mode :lang ledger) (ledger-mode :lang ledger)
(lua-mode :lang lua) (lua-mode :lang lua)
(moonscript-mode :lang lua)
(markdown-mode :lang markdown) (markdown-mode :lang markdown)
(gfm-mode :lang markdown) (gfm-mode :lang markdown)
(nim-mode :lang nim) (nim-mode :lang nim)
(nix-mode :lang nix) (nix-mode :lang nix)
(taureg-mode :lang ocaml) (taureg-mode :lang ocaml)
(org-mode :lang org) (org-mode :lang org)
(perl-mode :lang perl) (raku-mode :lang raku)
(php-mode :lang php) (php-mode :lang php)
(hack-mode :lang php) (hack-mode :lang php)
(plantuml-mode :lang plantuml) (plantuml-mode :lang plantuml)
@@ -55,9 +54,10 @@
(python-mode :lang python) (python-mode :lang python)
(restclient-mode :lang rest) (restclient-mode :lang rest)
(ruby-mode :lang ruby) (ruby-mode :lang ruby)
(enh-ruby-mode :lang ruby)
(rust-mode :lang rust) (rust-mode :lang rust)
(rustic-mode :lang rust)
(scala-mode :lang scala) (scala-mode :lang scala)
(scheme-mode :lang scheme)
(sh-mode :lang sh) (sh-mode :lang sh)
(swift-mode :lang swift) (swift-mode :lang swift)
(web-mode :lang web) (web-mode :lang web)
@@ -96,7 +96,7 @@ the current major-modea.")
"Get information on an active minor mode. Use `describe-minor-mode' for a "Get information on an active minor mode. Use `describe-minor-mode' for a
selection of all minor-modes, active or not." selection of all minor-modes, active or not."
(interactive (interactive
(list (completing-read "Minor mode: " (doom-active-minor-modes)))) (list (completing-read "Describe active mode: " (doom-active-minor-modes))))
(let ((symbol (let ((symbol
(cond ((stringp mode) (intern mode)) (cond ((stringp mode) (intern mode))
((symbolp mode) mode) ((symbolp mode) mode)
@@ -105,21 +105,6 @@ selection of all minor-modes, active or not."
(helpful-function symbol) (helpful-function symbol)
(helpful-variable 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 ;;; Documentation commands
@@ -130,7 +115,8 @@ selection of all minor-modes, active or not."
(require 'org) (require 'org)
(let* ((default-directory doom-docs-dir) (let* ((default-directory doom-docs-dir)
(org-agenda-files (mapcar #'expand-file-name (doom-enlist files))) (org-agenda-files (mapcar #'expand-file-name (doom-enlist files)))
(depth (if (integerp depth) depth))) (depth (if (integerp depth) depth))
(org-inhibit-startup t))
(message "Loading search results...") (message "Loading search results...")
(unwind-protect (unwind-protect
(delq (delq
@@ -150,7 +136,8 @@ selection of all minor-modes, active or not."
(list (or (+org-get-global-property "TITLE") (list (or (+org-get-global-property "TITLE")
(file-relative-name (buffer-file-name))))) (file-relative-name (buffer-file-name)))))
path path
(list (replace-regexp-in-string org-link-any-re "\\4" text))) (when text
(list (replace-regexp-in-string org-link-any-re "\\4" text))))
" > ") " > ")
tags) tags)
" ") " ")
@@ -196,7 +183,7 @@ selection of all minor-modes, active or not."
(find-file (expand-file-name "index.org" doom-docs-dir))) (find-file (expand-file-name "index.org" doom-docs-dir)))
;;;###autoload ;;;###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." "Search Doom's documentation and jump to a headline."
(interactive) (interactive)
(doom-completing-read-org-headings (doom-completing-read-org-headings
@@ -213,7 +200,26 @@ selection of all minor-modes, active or not."
(doom--help-modules-list)))) (doom--help-modules-list))))
;;;###autoload ;;;###autoload
(defun doom/help-news-search (&optional initial-input) (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-search-news (&optional initial-input)
"Search headlines in Doom's newsletters." "Search headlines in Doom's newsletters."
(interactive) (interactive)
(doom-completing-read-org-headings (doom-completing-read-org-headings
@@ -316,13 +322,7 @@ without needing to check if they are available."
readme-path))) readme-path)))
(defun doom--help-current-module-str () (defun doom--help-current-module-str ()
(cond ((and buffer-file-name (cond ((save-excursion
(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
(require 'smartparens) (require 'smartparens)
(ignore-errors (ignore-errors
(sp-beginning-of-sexp) (sp-beginning-of-sexp)
@@ -341,11 +341,14 @@ without needing to check if they are available."
(symbol-name (cadr mod))))))) (symbol-name (cadr mod)))))))
;;;###autoload ;;;###autoload
(defun doom/help-modules (category module) (defun doom/help-modules (category module &optional visit-dir)
"Open the documentation for a Doom module. "Open the documentation for a Doom module.
CATEGORY is a keyword and MODULE is a symbol. e.g. :editor and 'evil. 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 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 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 current file is in, or d) the module associated with the current major mode (see
@@ -354,7 +357,8 @@ current file is in, or d) the module associated with the current major mode (see
(mapcar #'intern (mapcar #'intern
(split-string (split-string
(completing-read "Describe module: " (completing-read "Describe module: "
(doom--help-modules-list) nil t nil nil (doom--help-modules-list)
nil t nil nil
(doom--help-current-module-str)) (doom--help-current-module-str))
" " t))) " " t)))
(cl-check-type category symbol) (cl-check-type category symbol)
@@ -362,14 +366,19 @@ current file is in, or d) the module associated with the current major mode (see
(cl-destructuring-bind (module-string path) (cl-destructuring-bind (module-string path)
(or (assoc (format "%s %s" category module) (doom--help-modules-list)) (or (assoc (format "%s %s" category module) (doom--help-modules-list))
(user-error "'%s %s' is not a valid module" category module)) (user-error "'%s %s' is not a valid module" category module))
(setq module-string (substring-no-properties module-string))
(unless (file-readable-p path) (unless (file-readable-p path)
(error "Can't find or read %S module at %S" module-string path)) (error "Can't find or read %S module at %S" module-string path))
(if (not (file-directory-p path)) (cond ((not (file-directory-p path))
(find-file path) (if visit-dir
(if (y-or-n-p (format "The %S module has no README file. Explore its directory?" (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)) module-string))
(doom-project-browse path) (doom-project-browse (file-name-directory path)))
(user-error "Aborted module lookup"))))) ((user-error "Aborted module lookup")))))
;; ;;
@@ -419,8 +428,8 @@ If prefix arg is present, refresh the cache."
(let ((guess (or (function-called-at-point) (let ((guess (or (function-called-at-point)
(symbol-at-point)))) (symbol-at-point))))
(require 'finder-inf nil t) (require 'finder-inf nil t)
(require 'core-packages) (require 'package)
(doom-initialize-packages) (require 'straight)
(let ((packages (delete-dups (let ((packages (delete-dups
(append (mapcar #'car package-alist) (append (mapcar #'car package-alist)
(mapcar #'car package--builtins) (mapcar #'car package--builtins)
@@ -432,9 +441,9 @@ If prefix arg is present, refresh the cache."
(list (list
(intern (intern
(completing-read (if guess (completing-read (if guess
(format "Select package to search for (default %s): " (format "Select Doom package to search for (default %s): "
guess) guess)
"Describe package: ") "Describe Doom package: ")
packages nil t nil nil packages nil t nil nil
(if guess (symbol-name guess)))))))) (if guess (symbol-name guess))))))))
(require 'core-packages) (require 'core-packages)
@@ -462,27 +471,44 @@ If prefix arg is present, refresh the cache."
(insert (symbol-name package) "\n") (insert (symbol-name package) "\n")
(package--print-help-section "Source") (package--print-help-section "Source")
(insert (or (pcase (doom-package-backend package) (pcase (doom-package-backend package)
(`straight (`straight
(format! "Straight (%s)\n%s" (insert "Straight\n")
(let ((default-directory (straight--build-dir (symbol-name package)))) (package--print-help-section "Pinned")
(insert (if-let (pin (plist-get (cdr (assq package doom-packages)) :pin))
pin
"unpinned")
"\n")
(package--print-help-section "Build")
(insert (let ((default-directory (straight--repos-dir (symbol-name package))))
(cdr (cdr
(doom-call-process "git" "log" "-1" "--format=%D %h %ci"))) (doom-call-process "git" "log" "-1" "--format=%D %h %ci")))
(indent
13 (string-trim
(pp-to-string
(doom-package-build-recipe package))))))
(`elpa
(format "[M]ELPA %s" (doom--package-url package)))
(`builtin "Built-in")
(_ (abbreviate-file-name (symbol-file package))))
"unknown")
"\n") "\n")
(let ((recipe (doom-package-build-recipe package)))
(insert (format! "%s\n"
(indent 13
(string-trim (pp-to-string recipe)))))
(when (gethash (symbol-name package) straight--build-cache) (package--print-help-section "Homepage")
(insert (doom--package-url package))))
(`elpa (insert "[M]ELPA " (doom--package-url package)))
(`builtin (insert "Built-in"))
(`other (insert
(abbreviate-file-name
(or (symbol-file package)
(locate-library (symbol-name package))))))
(_ (insert "Not installed")))
(insert "\n")
(when-let
(modules
(if (gethash (symbol-name package) straight--build-cache)
(doom-package-get package :modules)
(plist-get (cdr (assq package (doom-package-list 'all)))
:modules)))
(package--print-help-section "Modules") (package--print-help-section "Modules")
(insert "Declared by the following Doom modules:\n") (insert "Declared by the following Doom modules:\n")
(dolist (m (doom-package-get package :modules)) (dolist (m modules)
(insert indent) (insert indent)
(doom--help-package-insert-button (doom--help-package-insert-button
(format "%s %s" (car m) (or (cdr m) "")) (format "%s %s" (car m) (or (cdr m) ""))
@@ -512,7 +538,7 @@ If prefix arg is present, refresh the cache."
(insert "\n\n"))))) (insert "\n\n")))))
(defvar doom--package-cache nil) (defvar doom--package-cache nil)
(defun doom--package-list () (defun doom--package-list (&optional prompt)
(let* ((guess (or (function-called-at-point) (let* ((guess (or (function-called-at-point)
(symbol-at-point)))) (symbol-at-point))))
(require 'finder-inf nil t) (require 'finder-inf nil t)
@@ -528,10 +554,11 @@ If prefix arg is present, refresh the cache."
(setq doom--package-cache packages) (setq doom--package-cache packages)
(unless (memq guess packages) (unless (memq guess packages)
(setq guess nil)) (setq guess nil))
(intern (completing-read (if guess (intern (completing-read (or prompt
(if guess
(format "Select package to search for (default %s): " (format "Select package to search for (default %s): "
guess) guess)
"Describe package: ") "Describe package: "))
packages nil t nil nil packages nil t nil nil
(if guess (symbol-name guess))))))) (if guess (symbol-name guess)))))))
@@ -578,7 +605,7 @@ If prefix arg is present, refresh the cache."
This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include
config blocks in your private config." 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) (cl-destructuring-bind (file line _match)
(split-string (split-string
(completing-read (completing-read
@@ -592,55 +619,48 @@ config blocks in your private config."
(recenter))) (recenter)))
;;;###autoload ;;;###autoload
(defun doom/help-package-homepage (package) (defalias 'doom/help-package-homepage #'straight-visit-package-website)
"Open PACKAGE's repo or homepage in your browser."
(interactive (list (doom--package-list))) (defun doom--help-search-prompt (prompt)
(browse-url (doom--package-url package))) (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 (append counsel-rg-base-command 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 ;;;###autoload
(defun doom/help-search-load-path (query) (defun doom/help-search-load-path (query)
"Perform a text search on your `load-path'. "Perform a text search on your `load-path'.
Uses the symbol at point or the current selection, if available." Uses the symbol at point or the current selection, if available."
(interactive (interactive
(let ((query (list (doom--help-search-prompt "Search load-path: ")))
;; TODO Generalize this later; into something the lookup module and (doom--help-search (cl-remove-if-not #'file-directory-p load-path)
;; project search commands could as well query "Search load-path: "))
(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))
" ")))
;; TODO factor our the duplicate code between this and the above
;;;###autoload ;;;###autoload
(defun doom/help-search-loaded-files (query) (defun doom/help-search-loaded-files (query)
"Perform a text search on your `load-path'. "Perform a text search on your `load-path'.
Uses the symbol at point or the current selection, if available." Uses the symbol at point or the current selection, if available."
(interactive (interactive
(let ((query (list (doom--help-search-prompt "Search loaded files: ")))
;; TODO Generalize this later; into something the lookup module and (doom--help-search
;; project search commands could as well. (cl-loop for (file . _) in (cl-remove-if-not #'stringp load-history :key #'car)
(if (use-region-p) for filebase = (file-name-sans-extension file)
(buffer-substring-no-properties (region-beginning) (region-end)) if (file-exists-p! (format "%s.el" filebase))
(or (symbol-name (symbol-at-point)) "")))) collect it)
(list (read-string query "Search loaded files: "))
(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)))
" ")))
+187 -193
View File
@@ -1,198 +1,5 @@
;;; core/autoload/packages.el -*- lexical-binding: t; -*- ;;; core/autoload/packages.el -*- lexical-binding: t; -*-
;;
;;; Package metadata
;;;###autoload
(defun doom-package-get (package &optional prop nil-value)
"Returns PACKAGE's `package!' recipe from `doom-packages'."
(let ((plist (cdr (assq package doom-packages))))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was registered with."
(let ((plist (gethash (symbol-name package) straight--recipe-cache)))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-build-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was installed with."
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-build-time (package)
"TODO"
(car (gethash (symbol-name package) straight--build-cache)))
;;;###autoload
(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
(nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t))
deps))
deps)))
(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)
;; can't get dependencies for built-in packages
(unless (or (doom-package-build-recipe name)
noerror)
(error "Couldn't find %s, is it installed?" name))
(cl-loop for pkg in (hash-table-keys straight--build-cache)
for deps = (doom-package-dependencies pkg)
if (memq package deps)
collect pkg
and append (doom-package-depending-on pkg t)))
;;
;;; Predicate functions
;;;###autoload
(defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in."
(eq (doom-package-build-recipe package :type)
'built-in))
;;;###autoload
(defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed."
(file-directory-p (straight--build-dir (symbol-name package))))
;;;###autoload
(defun doom-package-registered-p (package)
"Return non-nil if PACKAGE (a symbol) has been registered with `package!'.
Excludes packages that have a non-nil :built-in property."
(when-let (plist (doom-package-get package))
(not (plist-get plist :ignore))))
;;;###autoload
(defun doom-package-private-p (package)
"Return non-nil if PACKAGE was installed by the user's private config."
(assq :private (doom-package-get package :modules)))
;;;###autoload
(defun doom-package-protected-p (package)
"Return non-nil if PACKAGE is protected.
A protected package cannot be deleted and will be auto-installed if missing."
(memq package doom-core-packages))
;;;###autoload
(defun doom-package-core-p (package)
"Return non-nil if PACKAGE is a core Doom package."
(or (doom-package-protected-p package)
(assq :core (doom-package-get package :modules))))
;;;###autoload
(defun doom-package-backend (package)
"Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is
installed."
(cond ((gethash (symbol-name package) straight--build-cache)
'straight)
((or (doom-package-built-in-p package)
(assq package package--builtins))
'builtin)
((assq package package-alist)
'elpa)
('other)))
;;;###autoload
(defun doom-package-different-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it
was installed with."
(cl-check-type name symbol)
;; TODO
;; (when (doom-package-installed-p name)
;; (when-let* ((doom-recipe (assq name doom-packages))
;; (install-recipe (doom-package-recipe)))
;; (not (equal (cdr quelpa-recipe)
;; (cdr (plist-get (cdr doom-recipe) :recipe))))))
)
;;
;;; Package list getters
(defun doom--read-module-packages-file (file &optional noeval noerror)
(with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e
(if (not noeval)
(load file noerror t t)
(when (file-readable-p file)
(insert-file-contents file)
(delay-mode-hooks (emacs-lisp-mode))
(while (search-forward "(package! " nil t)
(save-excursion
(goto-char (match-beginning 0))
(unless (let ((ppss (syntax-ppss)))
(or (nth 3 ppss)
(nth 4 ppss)))
(cl-destructuring-bind (name . plist)
(cdr (sexp-at-point))
(push (cons
name (plist-put
plist :modules
(list (doom-module-from-path file))))
doom-packages)))))))
((debug error)
(signal 'doom-package-error
(list (doom-module-from-path file)
e))))))
;;;###autoload
(defun doom-package-list (&optional all-p)
"Retrieve a list of explicitly declared packages from enabled modules.
This excludes core packages listed in `doom-core-packages'.
If ALL-P, gather packages unconditionally across all modules, including disabled
ones."
(let ((doom-interactive-mode t)
(doom-modules (doom-modules))
doom-packages
doom-disabled-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")))
(unless all-p
;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten.
(doom--read-module-packages-file private-packages nil t))
(if all-p
(mapc #'doom--read-module-packages-file
(doom-files-in doom-modules-dir
:depth 2
:match "/packages\\.el$"))
(cl-loop for key being the hash-keys of doom-modules
for path = (doom-module-path (car key) (cdr key) "packages.el")
for doom--current-module = key
do (doom--read-module-packages-file path nil t)))
(doom--read-module-packages-file private-packages all-p t))
(nreverse doom-packages)))
;;
;;; Main functions
;;;###autoload ;;;###autoload
(defun doom/reload-packages () (defun doom/reload-packages ()
"Reload `doom-packages', `package' and `quelpa'." "Reload `doom-packages', `package' and `quelpa'."
@@ -201,3 +8,190 @@ ones."
(message "Reloading packages") (message "Reloading packages")
(doom-initialize-packages t) (doom-initialize-packages t)
(message "Reloading packages...DONE")) (message "Reloading packages...DONE"))
;;
;;; Bump commands
(defun doom--package-full-recipe (package plist)
(doom-plist-merge
(plist-get plist :recipe)
(or (cdr (straight-recipes-retrieve package))
(plist-get (cdr (assq package doom-packages))
:recipe))))
(defun doom--package-to-bump-string (package plist)
"Return a PACKAGE and its PLIST in 'username/repo@commit' format."
(format "%s@%s"
(plist-get (doom--package-full-recipe package plist) :repo)
(substring-no-properties (plist-get plist :pin) 0 7)))
(defun doom--package-at-point (&optional point)
"Return the package and plist from the (package! PACKAGE PLIST...) at point."
(save-match-data
(save-excursion
(and point (goto-char point))
(while (and (or (atom (sexp-at-point))
(doom-point-in-string-or-comment-p))
(search-backward "(" nil t)))
(when (eq (car-safe (sexp-at-point)) 'package!)
(cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'sexp)
(let* ((doom-packages nil)
(buffer-file-name
(or buffer-file-name
(bound-and-true-p org-src-source-file-name)))
(package (eval (sexp-at-point) t)))
(list :beg beg
:end end
:package (car package)
:plist (cdr package))))))))
;;;###autoload
(defun doom/bumpify-package-at-point ()
"Convert `package!' call at point to a bump string."
(interactive)
(cl-destructuring-bind (&key package plist beg end)
(doom--package-at-point)
(when-let (str (doom--package-to-bump-string package plist))
(goto-char beg)
(delete-region beg end)
(insert str))))
;;;###autoload
(defun doom/bumpify-packages-in-buffer ()
"Convert all `package!' calls in buffer into bump strings."
(interactive)
(save-excursion
(goto-char (point-min))
(while (search-forward "(package!" nil t)
(unless (doom-point-in-string-or-comment-p)
(doom/bumpify-package-at-point)))))
;;;###autoload
(defun doom/bump-package-at-point (&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")
(doom-initialize-packages)
(cl-destructuring-bind (&key package plist beg end)
(or (doom--package-at-point)
(user-error "Not on a `package!' call"))
(let* ((recipe (doom--package-full-recipe package plist))
(branch (or (plist-get recipe :branch)
straight-vc-git-default-branch))
(oldid (or (plist-get plist :pin)
(doom-package-get package :pin)))
(url (straight-vc-git--destructure recipe (upstream-repo upstream-host)
(straight-vc-git--encode-url upstream-repo upstream-host)))
(id (or (when url
(cdr (doom-call-process
"git" "ls-remote" url
(unless select
(or branch straight-vc-git-default-branch)))))
(user-error "Couldn't find a recipe for %s" package)))
(id (car (split-string
(if select
(completing-read "Commit: " (split-string id "\n" t))
id)))))
(when (and oldid
(plist-member plist :pin)
(equal oldid id))
(user-error "%s: no update necessary" package))
(save-excursion
(if (re-search-forward ":pin +\"\\([^\"]+\\)\"" end t)
(replace-match id t t nil 1)
(goto-char (1- end))
(insert " :pin " (prin1-to-string id))))
(cond ((not oldid)
(message "%s: → %s" package (substring id 0 10)))
((< (length oldid) (length id))
(message "%s: extended to %s..." package id))
((message "%s: %s → %s"
package
(substring oldid 0 10)
(substring id 0 10)))))))
;;;###autoload
(defun doom/bump-packages-in-buffer (&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")
(save-excursion
(goto-char (point-min))
(doom-initialize-packages)
(let (packages)
(while (search-forward "(package! " nil t)
(unless (let ((ppss (syntax-ppss)))
(or (nth 4 ppss)
(nth 3 ppss)
(save-excursion
(and (goto-char (match-beginning 0))
(not (plist-member (sexp-at-point) :pin))))))
(condition-case e
(push (doom/bump-package-at-point) packages)
(user-error (message "%s" (error-message-string e))))))
(if packages
(message "Updated %d packages\n- %s" (length packages) (string-join packages "\n- "))
(message "No packages to update")))))
;;;###autoload
(defun doom/bump-module (category &optional module select)
"Bump packages in CATEGORY MODULE.
If SELECT (prefix arg) is non-nil, prompt you to choose a specific commit for
each package."
(interactive
(let* ((module (completing-read
"Bump module: "
(let ((modules (doom-module-list 'all)))
(mapcar (lambda (m)
(if (listp m)
(format "%s %s" (car m) (cdr m))
(format "%s" m)))
(append '(:private :core)
(delete-dups (mapcar #'car modules))
modules)))
nil t nil nil))
(module (split-string module " " t)))
(list (intern (car module))
(ignore-errors (intern (cadr module)))
current-prefix-arg)))
(mapc (fn! ((cat . mod))
(if-let (packages-file
(pcase cat
(:private (doom-glob doom-private-dir "packages.el"))
(:core (doom-glob doom-core-dir "packages.el"))
(_ (doom-module-locate-path cat mod "packages.el"))))
(with-current-buffer
(or (get-file-buffer packages-file)
(find-file-noselect packages-file))
(doom/bump-packages-in-buffer select)
(save-buffer))
(message "Module %s has no packages.el file" (cons cat mod))))
(if module
(list (cons category module))
(cl-remove-if-not (lambda (m) (eq (car m) category))
(append '((:core) (:private))
(doom-module-list 'all))))))
;;;###autoload
(defun doom/bump-package (package)
"Bump PACKAGE in all modules that install it."
(interactive
(list (completing-read "Bump package: "
(mapcar #'car (doom-package-list 'all)))))
(let* ((packages (doom-package-list 'all))
(modules (plist-get (alist-get package packages) :modules)))
(unless modules
(user-error "This package isn't installed by any Doom module"))
(dolist (module modules)
(when-let (packages-file (doom-module-locate-path (car module) (cdr module)))
(doom/bump-module (car module) (cdr module))))))
;;
;;; Bump commits
;;;###autoload
(defun doom/commit-bumps ()
(interactive))
+4 -22
View File
@@ -35,24 +35,6 @@ list, the pair is destructured into (CAR . CDR)."
"Delete PROP from PLIST in-place." "Delete PROP from PLIST in-place."
`(setq ,plist (doom-plist-delete ,plist ,prop))) `(setq ,plist (doom-plist-delete ,plist ,prop)))
;;;###autoload
(defmacro with-plist! (plist props &rest body)
"With props bound from PLIST to PROPS, evaluate BODY.
PROPS is a list of symbols. Each one is converted to a keyword and then its
value is looked up in the PLIST and bound to the symbol for the duration of
BODY."
(declare (indent 2))
(let ((plist-sym (make-symbol "plist")))
`(let* ((,plist-sym ,plist)
,@(cl-loop for prop in props
collect
`(,prop
(plist-get
,plist-sym
,(doom-keyword-intern (symbol-name prop))))))
,@body)))
;; ;;
;;; Library ;;; Library
@@ -66,7 +48,7 @@ BODY."
;;;###autoload ;;;###autoload
(defun doom-plist-merge (from-plist to-plist) (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))) (let ((plist (copy-sequence from-plist)))
(while plist (while plist
(plist-put! to-plist (pop plist) (pop plist))) (plist-put! to-plist (pop plist) (pop plist)))
@@ -83,11 +65,11 @@ BODY."
p)) p))
;;;###autoload ;;;###autoload
(defun doom-plist-delete (plist prop) (defun doom-plist-delete (plist &rest props)
"Delete PROP from a copy of PLIST." "Delete PROPS from a copy of PLIST."
(let (p) (let (p)
(while plist (while plist
(if (not (eq prop (car plist))) (if (not (memq (car plist) props))
(plist-put! p (car plist) (nth 1 plist))) (plist-put! p (car plist) (nth 1 plist)))
(setq plist (cddr plist))) (setq plist (cddr plist)))
p)) p))
+7 -8
View File
@@ -1,6 +1,8 @@
;;; core/autoload/projects.el -*- lexical-binding: t; -*- ;;; core/autoload/projects.el -*- lexical-binding: t; -*-
(defvar projectile-project-root nil) (defvar projectile-project-root nil)
(defvar projectile-enable-caching)
(defvar projectile-require-project-root)
;;;###autoload (autoload 'projectile-relevant-known-projects "projectile") ;;;###autoload (autoload 'projectile-relevant-known-projects "projectile")
@@ -39,8 +41,7 @@ they are absolute."
"Preforms `projectile-find-file' in a known project of your choosing." "Preforms `projectile-find-file' in a known project of your choosing."
(interactive (interactive
(list (list
(completing-read "Find file in project: " (projectile-relevant-known-projects) (completing-read "Find file in project: " (projectile-relevant-known-projects))))
nil nil nil nil (doom-project-root))))
(unless (file-directory-p project-root) (unless (file-directory-p project-root)
(error "Project directory '%s' doesn't exist" project-root)) (error "Project directory '%s' doesn't exist" project-root))
(doom-project-find-file project-root)) (doom-project-find-file project-root))
@@ -50,8 +51,7 @@ they are absolute."
"Preforms `find-file' in a known project of your choosing." "Preforms `find-file' in a known project of your choosing."
(interactive (interactive
(list (list
(completing-read "Browse in project: " (projectile-relevant-known-projects) (completing-read "Browse in project: " (projectile-relevant-known-projects))))
nil nil nil nil (doom-project-root))))
(unless (file-directory-p project-root) (unless (file-directory-p project-root)
(error "Project directory '%s' doesn't exist" project-root)) (error "Project directory '%s' doesn't exist" project-root))
(doom-project-browse project-root)) (doom-project-browse project-root))
@@ -99,11 +99,10 @@ If DIR is not a project, it will be indexed (but not cached)."
(unless (file-readable-p dir) (unless (file-readable-p dir)
(error "Directory %S isn't readable" dir)) (error "Directory %S isn't readable" dir))
(let* ((default-directory (file-truename (expand-file-name dir))) (let* ((default-directory (file-truename (expand-file-name dir)))
(project-root (doom-project-root default-directory)) (projectile-project-root (doom-project-root default-directory))
(projectile-project-root default-directory)
(projectile-enable-caching projectile-enable-caching)) (projectile-enable-caching projectile-enable-caching))
(cond ((and project-root (file-equal-p project-root projectile-project-root)) (cond ((and projectile-project-root (file-equal-p projectile-project-root default-directory))
(unless (doom-project-p projectile-project-root) (unless (doom-project-p default-directory)
;; Disable caching if this is not a real project; caching ;; Disable caching if this is not a real project; caching
;; non-projects easily has the potential to inflate the projectile ;; non-projects easily has the potential to inflate the projectile
;; cache beyond reason. ;; cache beyond reason.
+67 -41
View File
@@ -8,9 +8,11 @@ Will be saved in `doom-scratch-dir'.")
(defvar doom-scratch-dir (concat doom-etc-dir "scratch") (defvar doom-scratch-dir (concat doom-etc-dir "scratch")
"Where to save persistent scratch buffers.") "Where to save persistent scratch buffers.")
(defvar doom-scratch-buffer-major-mode nil (defvar doom-scratch-initial-major-mode nil
"What major mode to use in scratch buffers. This can be one of the "What major mode to start fresh scratch buffers in.
following:
Scratch buffers preserve their last major mode, however, so this only affects
the first, fresh scratch buffer you create. This accepts:
t Inherits the major mode of the last buffer you had selected. t Inherits the major mode of the last buffer you had selected.
nil Uses `fundamental-mode' nil Uses `fundamental-mode'
@@ -21,43 +23,56 @@ following:
(defvar doom-scratch-current-project nil (defvar doom-scratch-current-project nil
"The name of the project associated with the current scratch buffer.") "The name of the project associated with the current scratch buffer.")
(put 'doom-scratch-current-project 'permanent-local t)
(defvar doom-scratch-buffer-hook () (defvar doom-scratch-buffer-hook ()
"The hooks to run after a scratch buffer is created.") "The hooks to run after a scratch buffer is created.")
(defun doom--load-persistent-scratch-buffer (name) (defun doom--load-persistent-scratch-buffer (project-name)
(setq-local doom-scratch-current-project (setq-local doom-scratch-current-project
(or name (or project-name
doom-scratch-default-file)) doom-scratch-default-file))
(let ((scratch-file (let ((smart-scratch-file
(expand-file-name doom-scratch-current-project (expand-file-name (concat doom-scratch-current-project ".el")
doom-scratch-dir))) doom-scratch-dir)))
(make-directory doom-scratch-dir t) (make-directory doom-scratch-dir t)
(when (file-readable-p scratch-file) (when (file-readable-p smart-scratch-file)
(message "Reading %s" smart-scratch-file)
(cl-destructuring-bind (content point mode)
(with-temp-buffer
(save-excursion (insert-file-contents smart-scratch-file))
(read (current-buffer)))
(erase-buffer) (erase-buffer)
(insert-file-contents scratch-file) (funcall mode)
(set-auto-mode) (insert content)
t))) (goto-char point)
t))))
;;;###autoload ;;;###autoload
(defun doom-scratch-buffer (&optional mode directory project-name) (defun doom-scratch-buffer (&optional dont-restore-p mode directory project-name)
"Return a scratchpad buffer in major MODE." "Return a scratchpad buffer in major MODE."
(with-current-buffer (let* ((buffer-name (if project-name
(get-buffer-create (if project-name
(format "*doom:scratch (%s)*" project-name) (format "*doom:scratch (%s)*" project-name)
"*doom:scratch*")) "*doom:scratch*"))
(buffer (get-buffer buffer-name)))
(with-current-buffer
(or buffer (get-buffer-create buffer-name))
(setq default-directory directory) (setq default-directory directory)
(unless doom-scratch-current-project (setq-local so-long--inhibited t)
(if dont-restore-p
(erase-buffer)
(unless buffer
(doom--load-persistent-scratch-buffer project-name) (doom--load-persistent-scratch-buffer project-name)
(when (and (eq major-mode 'fundamental-mode) (when (and (eq major-mode 'fundamental-mode)
(functionp mode)) (functionp mode))
(funcall mode))) (funcall mode))))
(cl-pushnew (current-buffer) doom-scratch-buffers) (cl-pushnew (current-buffer) doom-scratch-buffers)
(add-transient-hook! 'doom-switch-buffer-hook (doom-persist-scratch-buffers-h))
(add-transient-hook! 'doom-switch-window-hook (doom-persist-scratch-buffers-h))
(add-hook 'kill-buffer-hook #'doom-persist-scratch-buffer-h nil 'local) (add-hook 'kill-buffer-hook #'doom-persist-scratch-buffer-h nil 'local)
(add-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h)
(run-hooks 'doom-scratch-buffer-created-hook) (run-hooks 'doom-scratch-buffer-created-hook)
(current-buffer))) (current-buffer))))
;; ;;
@@ -66,11 +81,18 @@ following:
;;;###autoload ;;;###autoload
(defun doom-persist-scratch-buffer-h () (defun doom-persist-scratch-buffer-h ()
"Save the current buffer to `doom-scratch-dir'." "Save the current buffer to `doom-scratch-dir'."
(write-region (let ((content (buffer-substring-no-properties (point-min) (point-max)))
(point-min) (point-max) (point (point))
(expand-file-name (or doom-scratch-current-project (mode major-mode))
(with-temp-file
(expand-file-name (concat (or doom-scratch-current-project
doom-scratch-default-file) doom-scratch-default-file)
doom-scratch-dir))) ".el")
doom-scratch-dir)
(prin1 (list content
point
mode)
(current-buffer)))))
;;;###autoload ;;;###autoload
(defun doom-persist-scratch-buffers-h () (defun doom-persist-scratch-buffers-h ()
@@ -89,62 +111,66 @@ following:
(remove-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h))) (remove-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h)))
;;;###autoload ;;;###autoload
(unless noninteractive (when doom-interactive-p
(add-hook 'kill-emacs-hook #'doom-persist-scratch-buffers-h)) (add-hook 'kill-emacs-hook #'doom-persist-scratch-buffers-h))
;; ;;
;;; Commands ;;; Commands
(defvar projectile-enable-caching)
;;;###autoload ;;;###autoload
(defun doom/open-scratch-buffer (&optional arg project-p) (defun doom/open-scratch-buffer (&optional arg project-p same-window-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 passed the prefix ARG, do not restore the last scratch buffer.
If PROJECT-P is non-nil, open a persistent scratch buffer associated with the If PROJECT-P is non-nil, open a persistent scratch buffer associated with the
current project." current project."
(interactive "P") (interactive "P")
(let (projectile-enable-caching) (let (projectile-enable-caching)
(funcall (funcall
(if arg (if same-window-p
#'switch-to-buffer #'switch-to-buffer
#'pop-to-buffer) #'pop-to-buffer)
(doom-scratch-buffer (doom-scratch-buffer
(cond ((eq doom-scratch-buffer-major-mode t) arg
(cond ((eq doom-scratch-initial-major-mode t)
(unless (or buffer-read-only (unless (or buffer-read-only
(derived-mode-p 'special-mode) (derived-mode-p 'special-mode)
(string-match-p "^ ?\\*" (buffer-name))) (string-match-p "^ ?\\*" (buffer-name)))
major-mode)) major-mode))
((null doom-scratch-buffer-major-mode) ((null doom-scratch-initial-major-mode)
nil) nil)
((symbolp doom-scratch-buffer-major-mode) ((symbolp doom-scratch-initial-major-mode)
doom-scratch-buffer-major-mode)) doom-scratch-initial-major-mode))
default-directory default-directory
(when project-p (when project-p
(doom-project-name)))))) (doom-project-name))))))
;;;###autoload ;;;###autoload
(defun doom/switch-to-scratch-buffer (&optional project-p) (defun doom/switch-to-scratch-buffer (&optional arg project-p)
"Like `doom/open-scratch-buffer', but switches to it in the current window. "Like `doom/open-scratch-buffer', but switches to it in the current window.
If passed the prefix arg, open project scratch buffer." If passed the prefix ARG, do not restore the last scratch buffer."
(interactive "P") (interactive "P")
(doom/open-scratch-buffer t project-p)) (doom/open-scratch-buffer arg project-p 'same-window))
;;;###autoload ;;;###autoload
(defun doom/open-project-scratch-buffer (&optional current-window) (defun doom/open-project-scratch-buffer (&optional arg same-window-p)
"Opens the (persistent) project scratch buffer in a popup. "Opens the (persistent) project scratch buffer in a popup.
If passed the prefix arg, switch to it in the current window." If passed the prefix ARG, do not restore the last scratch buffer."
(interactive "P") (interactive "P")
(doom/open-scratch-buffer current-window 'project)) (doom/open-scratch-buffer arg 'project same-window-p))
;;;###autoload ;;;###autoload
(defun doom/switch-to-project-scratch-buffer () (defun doom/switch-to-project-scratch-buffer (&optional arg)
"Like `doom/open-project-scratch-buffer', but switches to it in the current "Like `doom/open-project-scratch-buffer', but switches to it in the current
window." window.
(interactive)
(doom/open-project-scratch-buffer t)) If passed the prefix ARG, do not restore the last scratch buffer."
(interactive "P")
(doom/open-project-scratch-buffer arg 'same-window))
;;;###autoload ;;;###autoload
(defun doom/revert-scratch-buffer () (defun doom/revert-scratch-buffer ()
+18 -3
View File
@@ -47,10 +47,16 @@
"TODO" "TODO"
(setq file (expand-file-name (or file (doom-session-file)))) (setq file (expand-file-name (or file (doom-session-file))))
(message "Attempting to load %s" 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 (unless persp-mode
(persp-mode +1)) (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) ((and (require 'frameset nil t)
(require 'restart-emacs nil t)) (require 'restart-emacs nil t))
(restart-emacs--restore-frames-using-desktop file)) (restart-emacs--restore-frames-using-desktop file))
@@ -65,6 +71,9 @@
"TODO" "TODO"
(add-hook 'window-setup-hook #'doom-load-session 'append)) (add-hook 'window-setup-hook #'doom-load-session 'append))
;;;###autoload
(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler))
;; ;;
;;; Commands ;;; Commands
@@ -124,5 +133,11 @@
(interactive "P") (interactive "P")
(setq doom-autosave-session nil) (setq doom-autosave-session nil)
(doom/quicksave-session) (doom/quicksave-session)
(save-some-buffers nil t)
(letf! ((#'save-buffers-kill-emacs #'kill-emacs)
(confirm-kill-emacs))
(restart-emacs (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")))))
+153 -91
View File
@@ -1,5 +1,17 @@
;;; core/autoload/text.el -*- lexical-binding: t; -*- ;;; 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 ;;;###autoload
(defun doom-surrounded-p (pair &optional inline balanced) (defun doom-surrounded-p (pair &optional inline balanced)
"Returns t if point is surrounded by a brace delimiter: {[( "Returns t if point is surrounded by a brace delimiter: {[(
@@ -28,31 +40,18 @@ lines, above and below, with only whitespace in between."
;;;###autoload ;;;###autoload
(defun doom-point-in-comment-p (&optional pos) (defun doom-point-in-comment-p (&optional pos)
"Return non-nil if POS is in a comment. "Return non-nil if POS is in a comment.
POS defaults to the current position." POS defaults to the current position."
;; REVIEW Should we cache `syntax-ppss'? (let ((pos (or pos (point))))
(let* ((pos (or pos (point))) (or (run-hook-with-args-until-success 'doom-point-in-comment-functions pos)
(ppss (syntax-ppss pos))) (sp-point-in-comment 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)))))))))
;;;###autoload ;;;###autoload
(defun doom-point-in-string-p (&optional pos) (defun doom-point-in-string-p (&optional pos)
"Return non-nil if POS is in a string." "Return non-nil if POS is in a string."
;; REVIEW Should we cache `syntax-ppss'? ;; 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 ;;;###autoload
(defun doom-point-in-string-or-comment-p (&optional pos) (defun doom-point-in-string-or-comment-p (&optional pos)
@@ -60,75 +59,158 @@ POS defaults to the current position."
(or (doom-point-in-string-p pos) (or (doom-point-in-string-p pos)
(doom-point-in-comment-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 ;;; Commands
(defvar doom--last-backward-pt most-positive-fixnum) (defun doom--bol-bot-eot-eol (&optional pos)
;;;###autoload (save-mark-and-excursion
(defun doom/backward-to-bol-or-indent () (when pos
"Jump between the indentation column (first non-whitespace character) and the (goto-char pos))
beginning of the line. The opposite of (let* ((bol (if visual-line-mode
`doom/forward-to-last-non-comment-or-eol'."
(interactive)
(let ((pt (point)))
(cl-destructuring-bind (bol . bot)
(save-excursion (save-excursion
(beginning-of-visual-line) (beginning-of-visual-line)
(cons (point) (point))
(progn (skip-chars-forward " \t\r") (line-beginning-position)))
(point)))) (bot (save-excursion
(cond ((> pt bot) (goto-char bol)
(goto-char bot)) (skip-chars-forward " \t\r")
((= pt bol) (point)))
(goto-char (min doom--last-backward-pt bot)) (eol (if visual-line-mode
(setq doom--last-backward-pt most-positive-fixnum)) (save-excursion (end-of-visual-line) (point))
((<= pt bot) (line-end-position)))
(setq doom--last-backward-pt pt) (eot (or (save-excursion
(goto-char bol))))))
(defvar doom--last-forward-pt -1)
;;;###autoload
(defun doom/forward-to-last-non-comment-or-eol ()
"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)
(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) (if (not comment-use-syntax)
(progn (progn
(goto-char bol) (goto-char bol)
(when (re-search-forward comment-start-skip eol t) (when (re-search-forward comment-start-skip eol t)
(or (match-end 1) (match-beginning 0)))) (or (match-end 1) (match-beginning 0))))
(goto-char eol) (goto-char eol)
(while (and (sp-point-in-comment) (while (and (doom-point-in-comment-p)
(> (point) bol)) (> (point) bol))
(backward-char)) (backward-char))
(skip-chars-backward " " bol) (skip-chars-backward " " bol)
(unless (or (eq (char-after) 32) (eolp))
(forward-char))
(point))) (point)))
eol))) eol)))
(when (> doom--last-forward-pt boc) (list bol bot eot eol))))
(setq boc doom--last-forward-pt))
(if (or (= eol (point)) (defvar doom--last-backward-pt nil)
(> boc (point))) ;;;###autoload
(progn (defun doom/backward-to-bol-or-indent (&optional point)
(goto-char boc) "Jump between the indentation column (first non-whitespace character) and the
(setq doom--last-forward-pt -1)) beginning of the line. The opposite of
(setq doom--last-forward-pt (point)) `doom/forward-to-last-non-comment-or-eol'."
(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)
(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 nil)
;;;###autoload
(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)))))) (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 ((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 ;;;###autoload
(defun doom/dumb-indent () (defun doom/dumb-indent ()
"Inserts a tab character (or spaces x tab-width)." "Inserts a tab character (or spaces x tab-width)."
@@ -155,20 +237,6 @@ true end of the line. The opposite of `doom/backward-to-bol-or-indent'."
tab-width tab-width
(- tab-width movement))))))))) (- 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 ;;;###autoload
(defun doom/retab (arg &optional beg end) (defun doom/retab (arg &optional beg end)
"Converts tabs-to-spaces or spaces-to-tabs within BEG and END (defaults to "Converts tabs-to-spaces or spaces-to-tabs within BEG and END (defaults to
@@ -192,15 +260,9 @@ opposite indentation style."
Respects `require-final-newline'." Respects `require-final-newline'."
(interactive) (interactive)
(save-excursion
(goto-char (point-max)) (goto-char (point-max))
(skip-chars-backward " \t\n\v") (delete-blank-lines)))
(when (looking-at "\n\\(\n\\|\\'\\)")
(forward-char 1))
(when require-final-newline
(unless (bolp)
(insert "\n")))
(when (looking-at "\n+")
(replace-match "")))
;;;###autoload ;;;###autoload
(defun doom/dos2unix () (defun doom/dos2unix ()
+16 -8
View File
@@ -9,6 +9,13 @@
`((,(car spec) ((t ,(cdr spec)))))) `((,(car spec) ((t ,(cdr spec))))))
(`((,(car spec) ,(cdr spec)))))) (`((,(car spec) ,(cdr spec))))))
;;;###autoload
(defconst doom-customize-theme-hook nil)
(add-hook! 'doom-load-theme-hook :append
(defun doom-apply-customized-faces-h ()
(run-hooks 'doom-customize-theme-hook)))
;;;###autoload ;;;###autoload
(defmacro custom-theme-set-faces! (theme &rest specs) (defmacro custom-theme-set-faces! (theme &rest specs)
"Apply a list of face SPECS as user customizations for THEME. "Apply a list of face SPECS as user customizations for THEME.
@@ -16,26 +23,27 @@
THEME can be a single symbol or list thereof. If nil, apply these settings to THEME can be a single symbol or list thereof. If nil, apply these settings to
all themes. It will apply to all themes once they are loaded." all themes. It will apply to all themes once they are loaded."
(declare (indent defun)) (declare (indent defun))
`(let ((fn (gensym "doom--customize-themes-h-"))) (let ((fn (gensym "doom--customize-themes-h-")))
(fset `(progn
fn (lambda () (defun ,fn ()
(let (custom--inhibit-theme-enable) (let (custom--inhibit-theme-enable)
(dolist (theme (doom-enlist (or ,theme 'user))) (dolist (theme (doom-enlist (or ,theme 'user)))
(when (or (eq theme 'user) (when (or (eq theme 'user)
(custom-theme-enabled-p theme)) (custom-theme-enabled-p theme))
(apply #'custom-theme-set-faces theme (apply #'custom-theme-set-faces theme
(mapcan #'doom--custom-theme-set-face (mapcan #'doom--custom-theme-set-face
(list ,@specs)))))))) (list ,@specs)))))))
(when (or doom-init-theme-p (null doom-theme)) (when (or doom-init-theme-p (null doom-theme))
(funcall fn)) (funcall #',fn))
(add-hook 'doom-load-theme-hook fn 'append))) (add-hook 'doom-customize-theme-hook #',fn 'append))))
;;;###autoload ;;;###autoload
(defmacro custom-set-faces! (&rest specs) (defmacro custom-set-faces! (&rest specs)
"Apply a list of face SPECS as user customizations. "Apply a list of face SPECS as user customizations.
This is a drop-in replacement for `custom-set-face' that allows for a simplified This is a convenience macro alternative to `custom-set-face' which allows for a
face format." simplified face format, and takes care of load order issues, so you can use
doom-themes' API without worry."
(declare (indent defun)) (declare (indent defun))
`(custom-theme-set-faces! 'user ,@specs)) `(custom-theme-set-faces! 'user ,@specs))
+38 -20
View File
@@ -1,7 +1,7 @@
;;; core/autoload/ui.el -*- lexical-binding: t; -*- ;;; core/autoload/ui.el -*- lexical-binding: t; -*-
;; ;;
;; Public library ;;; Public library
;;;###autoload ;;;###autoload
(defun doom-resize-window (window new-size &optional horizontal force-p) (defun doom-resize-window (window new-size &optional horizontal force-p)
@@ -24,13 +24,23 @@ are open."
;; ;;
;; Advice ;;; Advice
;;;###autoload ;;;###autoload
(defun doom-recenter-a (&rest _) (defun doom-recenter-a (&rest _)
"Generic advisor for recentering window (typically :after other functions)." "Generic advice for recentering window (typically :after other functions)."
(recenter)) (recenter))
;;;###autoload
(defun doom-preserve-window-position-a (orig-fn &rest args)
"Generic advice for preserving cursor position on screen after scrolling."
(let ((row (cdr (posn-col-row (posn-at-point)))))
(prog1 (apply orig-fn args)
(save-excursion
(let ((target-row (- (line-number-at-pos) row)))
(unless (< target-row 0)
(evil-scroll-line-to-top target-row)))))))
;;;###autoload ;;;###autoload
(defun doom-shut-up-a (orig-fn &rest args) (defun doom-shut-up-a (orig-fn &rest args)
"Generic advisor for silencing noisy functions. "Generic advisor for silencing noisy functions.
@@ -43,7 +53,7 @@ In tty Emacs, messages suppressed completely."
;; ;;
;; Hooks ;;; Hooks
;;;###autoload ;;;###autoload
(defun doom-apply-ansi-color-to-compilation-buffer-h () (defun doom-apply-ansi-color-to-compilation-buffer-h ()
@@ -57,9 +67,17 @@ In tty Emacs, messages suppressed completely."
"Turn off `show-paren-mode' buffer-locally." "Turn off `show-paren-mode' buffer-locally."
(setq-local show-paren-mode nil)) (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 ;;;###autoload
(defun doom/toggle-line-numbers () (defun doom/toggle-line-numbers ()
@@ -87,7 +105,7 @@ See `display-line-numbers' for what these values mean."
(_ (symbol-name next)))))) (_ (symbol-name next))))))
;;;###autoload ;;;###autoload
(defun doom/delete-frame () (defun doom/delete-frame-with-prompt ()
"Delete the current frame, but ask for confirmation if it isn't empty." "Delete the current frame, but ask for confirmation if it isn't empty."
(interactive) (interactive)
(if (cdr (frame-list)) (if (cdr (frame-list))
@@ -95,6 +113,7 @@ See `display-line-numbers' for what these values mean."
(delete-frame)) (delete-frame))
(save-buffers-kill-emacs))) (save-buffers-kill-emacs)))
(defvar doom--maximize-last-wconf nil)
;;;###autoload ;;;###autoload
(defun doom/window-maximize-buffer () (defun doom/window-maximize-buffer ()
"Close other windows to focus on this one. Activate again to undo this. If the "Close other windows to focus on this one. Activate again to undo this. If the
@@ -102,26 +121,26 @@ window changes before then, the undo expires.
Alternatively, use `doom/window-enlargen'." Alternatively, use `doom/window-enlargen'."
(interactive) (interactive)
(if (and (one-window-p) (setq doom--maximize-last-wconf
(assq ?_ register-alist)) (if (and (null (cdr (cl-remove-if #'window-dedicated-p (window-list))))
(jump-to-register ?_) doom--maximize-last-wconf)
(ignore (set-window-configuration doom--maximize-last-wconf))
(when (and (bound-and-true-p +popup-mode) (when (and (bound-and-true-p +popup-mode)
(+popup-window-p)) (+popup-window-p))
(user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead")) (user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead"))
(window-configuration-to-register ?_) (prog1 (current-window-configuration)
(delete-other-windows))) (delete-other-windows)))))
(defvar doom--window-enlargened nil) (defvar doom--enlargen-last-wconf nil)
;;;###autoload ;;;###autoload
(defun doom/window-enlargen () (defun doom/window-enlargen ()
"Enlargen the current window to focus on this one. Does not close other "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) (interactive)
(setq doom--window-enlargened (setq doom--enlargen-last-wconf
(if (and doom--window-enlargened (if doom--enlargen-last-wconf
(assq ?_ register-alist)) (ignore (set-window-configuration doom--enlargen-last-wconf))
(ignore (ignore-errors (jump-to-register ?_))) (prog1 (current-window-configuration)
(window-configuration-to-register ?_)
(let* ((window (selected-window)) (let* ((window (selected-window))
(dedicated-p (window-dedicated-p window)) (dedicated-p (window-dedicated-p window))
(preserved-p (window-parameter window 'window-preserved-size)) (preserved-p (window-parameter window 'window-preserved-size))
@@ -135,8 +154,7 @@ windows (unlike `doom/window-maximize-buffer') Activate again to undo."
(maximize-window window)) (maximize-window window))
(set-window-dedicated-p window dedicated-p) (set-window-dedicated-p window dedicated-p)
(when preserved-p (when preserved-p
(set-window-parameter window 'window-preserved-size preserved-p))) (set-window-parameter window 'window-preserved-size preserved-p))))))))
t))))
;;;###autoload ;;;###autoload
(defun doom/window-maximize-horizontally () (defun doom/window-maximize-horizontally ()
+180 -362
View File
@@ -1,166 +1,147 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*- ;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(defvar doom-autoload-excluded-packages '("gh") (defvar doom-autoloads-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 everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.") one wants that.")
;; externs (defvar doom-autoloads-cached-vars
(defvar autoload-timestamps) '(doom-modules
(defvar generated-autoload-load-name) doom-disabled-packages
(defvar generated-autoload-file) load-path
auto-mode-alist
interpreter-mode-alist
Info-directory-list)
"A list of variables to be cached in `doom-autoload-file'.")
(defvar doom-autoloads-files ()
"A list of additional files or file globs to scan for autoloads.")
;; ;;
;;; Helpers ;;; Library
(defun doom--cli-delete-autoloads-file (file) (defun doom-autoloads-reload (&optional file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any." "Regenerates Doom's autoloads and writes them to FILE."
(unless file
(setq file doom-autoload-file))
(print! (start "(Re)generating autoloads file..."))
(print-group!
(cl-check-type file string) (cl-check-type file string)
(when (file-exists-p file) (doom-initialize-packages)
(when-let (buf (find-buffer-visiting file)) (and (print! (start "Generating autoloads file..."))
(with-current-buffer buf (doom-autoloads--write
(set-buffer-modified-p nil)) file
(kill-buffer buf)) `((unless (equal emacs-major-version ,emacs-major-version)
(delete-file file) (signal 'doom-error
(ignore-errors (delete-file (byte-compile-dest-file file))) (list "The installed version of Emacs has changed since last 'doom sync' ran"
t)) "Run 'doom sync && doom build' to bring Doom up to speed")))
(unless (equal doom-version ,doom-version)
(signal 'doom-error
(list "The installed version of Doom has changed since last 'doom sync' ran"
"Run 'doom sync' to bring Doom up to speed"))))
(mapcar (lambda (var) `(set ',var ',(symbol-value var)))
doom-autoloads-cached-vars)
(doom-autoloads--scan
(append (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)
(mapcan #'doom-glob doom-autoloads-files)))
(doom-autoloads--scan
(mapcar #'straight--autoloads-file
(seq-difference (hash-table-keys straight--build-cache)
doom-autoloads-excluded-packages))
'literal))
(print! (start "Byte-compiling autoloads file..."))
(doom-autoloads--compile-file file)
(print! (success "Generated %s")
(relpath (byte-compile-dest-file file)
doom-emacs-dir)))))
(defun doom--cli-warn-refresh-session-h () (defun doom-autoloads--write (file &rest forms)
(message "Restart or reload Doom Emacs for changes to take effect:\n") (make-directory (file-name-directory file) 'parents)
(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 (condition-case-unless-debug e
(when (byte-compile-file file) (with-temp-file file
(unless doom-interactive-mode (setq-local coding-system-for-write 'utf-8)
(add-hook 'doom-cli-post-success-execute-hook #'doom--cli-warn-refresh-session-h)) (let ((standard-output (current-buffer))
(let (noninteractive) (print-quoted t)
(load file 'noerror 'nomessage 'nosuffix))) (print-level nil)
((debug error) (print-length nil))
(let ((backup-file (concat file ".bk"))) (insert ";; -*- lexical-binding: t; coding: utf-8; -*-\n"
(print! (warn "Copied backup to %s") (relpath backup-file)) ";; This file is autogenerated by 'doom sync', DO NOT EDIT IT!!\n")
(copy-file file backup-file 'overwrite)) (dolist (form (delq nil forms))
(doom--cli-delete-autoloads-file file) (mapc #'prin1 form))
(signal 'doom-autoload-error (list file e)))))) t))
(error (delete-file file)
(signal 'doom-autoload-error (list file e)))))
(defun doom-cli-reload-autoloads (&optional file force-p) (defun doom-autoloads--compile-file (file)
"Reloads FILE (an autoload file), if it needs reloading. (condition-case-unless-debug e
(let ((byte-compile-warnings (if doom-debug-p byte-compile-warnings)))
(and (byte-compile-file file)
(load (byte-compile-dest-file file) nil t)))
(error
(delete-file (byte-compile-dest-file file))
(signal 'doom-autoload-error (list file e)))))
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If (defun doom-autoloads--cleanup-form (form &optional expand)
it is nil, it will try to reload both. If FORCE-P (universal argument) do it (let ((func (car-safe form)))
even if it doesn't need reloading!" (cond ((memq func '(provide custom-autoload))
(or (null file) nil)
(stringp file) ((and (eq func 'add-to-list)
(signal 'wrong-type-argument (list 'stringp file))) (memq (doom-unquote (cadr form))
(if (stringp file) doom-autoloads-cached-vars))
(cond ((file-equal-p file doom-autoload-file) nil)
(doom-cli-reload-core-autoloads force-p)) ((not (eq func 'autoload))
((file-equal-p file doom-package-autoload-file) form)
(doom-cli-reload-package-autoloads force-p)) ((and expand (not (file-name-absolute-p (nth 2 form))))
((error "Invalid autoloads file: %s" file)))
(doom-cli-reload-core-autoloads force-p)
(doom-cli-reload-package-autoloads force-p)))
;;
;;; 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))
;; 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)
(quiet! (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) (defvar doom--autoloads-path-cache nil)
(while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t) (setf (nth 2 form)
(let ((path (match-string 1))) (let ((path (nth 2 form)))
(replace-match
(or (cdr (assoc path doom--autoloads-path-cache)) (or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (or (and allow-internal-paths (when-let* ((libpath (locate-library path))
(locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
(locate-library path)))
(libpath (file-name-sans-extension libpath)) (libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath))) (libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache) (push (cons path libpath) doom--autoloads-path-cache)
libpath) libpath)
path) path)))
t t nil 1))))) form)
(form))))
(defun doom--cli-generate-autodefs-1 (path &optional member-p) (defun doom-autoloads--scan-autodefs (file buffer module &optional module-enabled-p)
(let (forms) (with-temp-buffer
(insert-file-contents file)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t) (while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((sexp (sexp-at-point)) (let* ((standard-output buffer)
(alt-sexp (match-string 1)) (form (read (current-buffer)))
(type (car sexp)) (altform (match-string 1))
(name (doom-unquote (cadr sexp))) (definer (car-safe form))
(origin (doom-module-from-path path))) (symbol (doom-unquote (cadr form))))
(cond (cond ((and (not module-enabled-p) altform)
((and (not member-p) (print (read altform)))
alt-sexp) ((memq definer '(defun defmacro cl-defun cl-defmacro))
(push (read alt-sexp) forms)) (if module-enabled-p
(print (make-autoload form file))
((memq type '(defun defmacro cl-defun cl-defmacro)) (cl-destructuring-bind (_ _ arglist &rest body) form
(cl-destructuring-bind (_ _name arglist &rest body) sexp (print
(appendq! (if altform
forms (read altform)
(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 (append
(list (pcase type (list (pcase definer
(`defun 'defmacro) (`defun 'defmacro)
(`cl-defun `cl-defmacro) (`cl-defun `cl-defmacro)
(_ type)) (_ type))
name arglist docstring) 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 (cl-loop for arg in arglist
if (and (symbolp arg) if (and (symbolp arg)
(not (keywordp arg)) (not (keywordp arg))
@@ -168,240 +149,77 @@ even if it doesn't need reloading!"
collect arg into syms collect arg into syms
else if (listp arg) else if (listp arg)
collect (car arg) into syms collect (car arg) into syms
finally return (if syms `((ignore ,@syms)))))) finally return (if syms `((ignore ,@syms)))))))))
('error (print `(put ',symbol 'doom-module ',module)))
(print! "- Ignoring autodef %s (%s)" name e) ((eq definer 'defalias)
nil)))) (cl-destructuring-bind (_ _ target &optional docstring) form
`(put ',name 'doom-module ',origin))))) (unless module-enabled-p
((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 (setq target #'ignore
docstring docstring
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s" (format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin docstring))) module docstring)))
(appendq! forms `((put ',name 'doom-module ',origin) (print `(put ',symbol 'doom-module ',module))
(defalias ',name #',target ,docstring)))))) (print `(defalias ',symbol #',(doom-unquote target) ,docstring))))
(module-enabled-p (print form)))))))
(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))
(defvar autoload-timestamps)
(defvar generated-autoload-load-name)
(defun doom-autoloads--scan-file (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'. ;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them ;; Presumably for a good reason, so I just copied them
(backup-inhibited t) (backup-inhibited t)
(version-control 'never) (version-control 'never)
(case-fold-search nil) ; reduce magic case-fold-search ; reduce magic
(autoload-timestamps nil) autoload-timestamps ; reduce noise in generated files
;; Needed for `autoload-generate-file-autoloads'
;; Where we'll store the files we'll scan for autoloads. This should (generated-autoload-load-name (file-name-sans-extension file))
;; contain *all* autoload files, even in disabled modules, so we can (target-buffer (current-buffer))
;; scan those for autodefs. We start with the core libraries. (module (doom-module-from-path file))
(targets (doom-glob doom-core-dir "autoload/*.el")) (module-enabled-p (and (or (memq (car module) '(:core :private))
;; A subset of `targets' in enabled modules (doom-module-p (car module) (cdr module)))
(active-targets (copy-sequence targets))) (doom-file-cookie-p file "if" t))))
(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 (save-excursion
(doom--cli-generate-autoloads active-targets) (when module-enabled-p
(print! (success "Generated new autoloads.el"))) (quiet! (autoload-generate-file-autoloads file target-buffer)))
;; Replace autoload paths (only for module autoloads) with absolute (doom-autoloads--scan-autodefs
;; paths for faster resolution during load and simpler `load-path' file target-buffer module module-enabled-p))))
(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)))
(defun doom-autoloads--scan (files &optional literal)
;;
;;; 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 "\\(?:\\_<load-file-name\\|#\\$\\)\\_>" 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."
(require 'autoload) (require 'autoload)
(print! (start "Checking package autoloads file")) (let (autoloads)
(print-group! (dolist (file
(if (and (not force-p) (seq-filter #'file-readable-p files)
(file-exists-p doom-package-autoload-file) (nreverse (delq nil autoloads)))
(not (file-newer-than-file-p package-user-dir doom-package-autoload-file)) (with-temp-buffer
(not (cl-loop for dir in (straight--directory-files (straight--build-dir)) (print! (debug "- Scanning %s") (relpath file doom-emacs-dir))
if (cl-find-if (if literal
(lambda (dir) (insert-file-contents file)
(file-newer-than-file-p dir doom-package-autoload-file)) (doom-autoloads--scan-file 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
(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 (save-excursion
;; Cache important and expensive-to-initialize state here. (let ((filestr (prin1-to-string file)))
(doom--generate-var-cache) (while (re-search-forward "\\_<load-file-name\\_>" nil t)
(print! (success "Cached package state")) ;; `load-file-name' is meaningless in a concatenated
;; Concatenate the autoloads of all installed packages. ;; mega-autoloads file, so we replace references to it with the
(doom--generate-package-autoloads) ;; file they came from.
(print! (success "Package autoloads included"))) (let ((ppss (save-excursion (syntax-ppss))))
(or (nth 3 ppss)
;; Replace autoload paths (only for module autoloads) with absolute (nth 4 ppss)
;; paths for faster resolution during load and simpler `load-path' (replace-match filestr t t))))))
(save-excursion (let ((load-file-name file)
(doom--cli-expand-autoload-paths) (load-path
(print! (success "Expanded module autoload paths"))) (append (list doom-private-dir)
doom-modules-dirs
;; Remove `load-path' and `auto-mode-alist' modifications (most of them, load-path)))
;; at least); they are cached later, so all those membership checks are (condition-case _
;; unnecessary overhead. (while t
(doom--cleanup-package-autoloads) (push (doom-autoloads--cleanup-form (read (current-buffer))
(print! (success "Removed load-path/auto-mode-alist entries")))) (not literal))
;; Byte compile it to give the file a chance to reveal errors (and buy us a autoloads))
;; few marginal performance boosts) (end-of-file)))))))
(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))
+103 -110
View File
@@ -2,7 +2,9 @@
(defcli! (compile c) (defcli! (compile c)
((recompile-p ["-r" "--recompile"]) ((recompile-p ["-r" "--recompile"])
&rest targets) (core-p ["-c" "--core"])
(private-p ["-p" "--private"])
(verbose-p ["-v" "--verbose"]))
"Byte-compiles your config or selected modules. "Byte-compiles your config or selected modules.
compile [TARGETS...] compile [TARGETS...]
@@ -11,8 +13,23 @@
Accepts :core and :private as special arguments, which target Doom's core files Accepts :core and :private as special arguments, which target Doom's core files
and your private config files, respectively. To recompile your packages, use and your private config files, respectively. To recompile your packages, use
'doom rebuild' instead." 'doom build' instead."
(doom-cli-byte-compile targets recompile-p)) (doom-cli-byte-compile
(if (or core-p private-p)
(append (when core-p
(list (doom-glob doom-emacs-dir "init.el")
doom-core-dir))
(when private-p
(list doom-private-dir)))
(append (list (doom-glob doom-emacs-dir "init.el")
doom-core-dir)
(cl-remove-if-not
;; Only compile Doom's modules
(lambda (path) (file-in-directory-p path doom-emacs-dir))
;; Omit `doom-private-dir', which is always first
(cdr (doom-module-load-path)))))
recompile-p
verbose-p))
(defcli! clean () (defcli! clean ()
"Delete all *.elc files." "Delete all *.elc files."
@@ -25,16 +42,20 @@ and your private config files, respectively. To recompile your packages, use
(defun doom--byte-compile-ignore-file-p (path) (defun doom--byte-compile-ignore-file-p (path)
(let ((filename (file-name-nondirectory path))) (let ((filename (file-name-nondirectory path)))
(or (string-prefix-p "." filename) (or (not (equal (file-name-extension path) "el"))
(member filename (list "packages.el" "doctor.el"))
(string-prefix-p "." filename)
(string-prefix-p "test-" filename) (string-prefix-p "test-" filename)
(not (equal (file-name-extension path) "el")) (string-prefix-p "flycheck_" filename)
(member filename (list "packages.el" "doctor.el"))))) (string-suffix-p ".example.el" filename))))
(cl-defun doom-cli-byte-compile (&optional modules recompile-p) (cl-defun doom-cli-byte-compile (&optional targets recompile-p verbose-p)
"Byte compiles your emacs configuration. "Byte compiles your emacs configuration.
init.el is always byte-compiled by this. init.el is always byte-compiled by this.
If TARGETS is specified, as a list of direcotries
If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are If MODULES is specified (a list of module strings, e.g. \"lang/php\"), those are
byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom
core. It always ignores unit tests and files with `no-byte-compile' enabled. core. It always ignores unit tests and files with `no-byte-compile' enabled.
@@ -47,94 +68,64 @@ Use `doom-clean-byte-compiled-files' or `make clean' to reverse
byte-compilation. byte-compilation.
If RECOMPILE-P is non-nil, only recompile out-of-date files." If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir) (let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)) (targets (nreverse (delete-dups targets)))
(byte-compile-verbose doom-debug-mode)
(byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
;; In case it is changed during compile-time ;; In case it is changed during compile-time
(auto-mode-alist auto-mode-alist) (auto-mode-alist auto-mode-alist)
(noninteractive t) kill-emacs-hook kill-buffer-query-functions)
targets)
(let (target-dirs)
(dolist (module (delete-dups modules))
(pcase module
(":core"
(push (doom-glob doom-emacs-dir "init.el") targets)
(push doom-core-dir target-dirs))
(":private"
(push doom-private-dir target-dirs))
((pred file-directory-p)
(push module target-dirs))
((pred (string-match "^\\([^/]+\\)/\\([^/]+\\)$"))
(push (doom-module-locate-path
(doom-keyword-intern (match-string 1 module))
(intern (match-string 2 module)))
target-dirs))
(_ (user-error "%S is not a valid target" module))))
(and (or (null modules) (member ":private" modules))
(not recompile-p)
(not (or doom-auto-accept
(y-or-n-p
(concat "Warning: byte compiling is for advanced users. It will interfere with your\n"
"efforts to debug issues. It is not recommended you do it if you frequently\n"
"tinker with your Emacs config.\n\n"
"Alternatively, use `bin/doom compile :core` instead to byte-compile only the\n"
"Doom core files, as these don't change often.\n\n"
"If you have issues, please make sure byte-compilation isn't the cause by using\n"
"`bin/doom clean` to clear out your *.elc files.\n\n"
"Byte-compile anyway?"))))
(user-error "Aborting"))
(let ((after-load-functions
(if (null targets)
after-load-functions
;; Assemble el files we want to compile, and preserve in the order
;; they are loaded in, so we don't run into any scary catch-22s
;; while byte-compiling, like missing macros.
(cons (let ((target-dirs (cl-remove-if-not #'file-directory-p targets)))
(lambda (path)
(and (not (doom--byte-compile-ignore-file-p path))
(cl-find-if (doom-partial #'file-in-directory-p path)
target-dirs)
(cl-pushnew path targets))))
after-load-functions))))
(doom-log "Reloading Doom in preparation for byte-compilation")
;; But first we must be sure that Doom and your private config have been ;; 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. ;; fully loaded. Which usually aren't so in an noninteractive session.
(let ((doom-interactive-mode 'byte-compile)) (let ((load-prefer-newer t)
(doom-initialize) (noninteractive t)
(doom-initialize-packages) doom-interactive-p)
(doom-initialize-core)) (doom-initialize 'force)
(quiet! (doom-initialize-packages))))
;; (if (null targets)
(unless target-dirs (print! (info "No targets to %scompile" (if recompile-p "re" "")))
(push (doom-glob doom-emacs-dir "init.el") targets) (print! (start "%scompiling your config...")
;; If no targets were supplied, then we use your module list. (if recompile-p "Re" "Byte-"))
(appendq! target-dirs
(list doom-core-dir)
(nreverse
(cl-remove-if-not
(lambda (path) (file-in-directory-p path doom-emacs-dir))
;; Omit `doom-private-dir', which is always first
(cdr (doom-module-load-path))))))
;; Assemble el files we want to compile; taking into account that MODULES (dolist (dir
;; may be a list of MODULE/SUBMODULE strings from the command line. (cl-remove-if-not #'file-directory-p targets)
(appendq! targets (setq targets (cl-remove-if #'file-directory-p targets)))
(doom-files-in target-dirs (prependq! targets
:match "\\.el$" (doom-files-in
:filter #'doom--byte-compile-ignore-file-p))) dir :match "\\.el" :filter #'doom--byte-compile-ignore-file-p)))
(unless targets
(print!
(if targets
(warn "Couldn't find any valid targets")
(info "No targets to %scompile" (if recompile-p "re" ""))))
(cl-return nil))
(print!
(start (if recompile-p
"Recompiling stale elc files..."
"Byte-compiling your config (may take a while)...")))
(print-group! (print-group!
(require 'use-package) (require 'use-package)
(condition-case e (condition-case-unless-debug e
(let ((total-ok 0) (let* ((total-ok 0)
(total-fail 0) (total-fail 0)
(total-noop 0) (total-noop 0)
(byte-compile-verbose nil)
(byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
(byte-compile-dynamic-docstrings t)
(use-package-compute-statistics nil)
(use-package-defaults use-package-defaults) (use-package-defaults use-package-defaults)
(use-package-expand-minimally t) (use-package-expand-minimally t)
kill-emacs-hook kill-buffer-query-functions) (targets (delete-dups targets))
(modules (seq-group-by #'doom-module-from-path targets))
(total-files (length targets))
(total-modules (length modules))
(i 0)
last-module)
;; Prevent packages from being loaded at compile time if they ;; Prevent packages from being loaded at compile time if they
;; don't meet their own predicates. ;; don't meet their own predicates.
(push (list :no-require t (push (list :no-require t
@@ -145,40 +136,41 @@ If RECOMPILE-P is non-nil, only recompile out-of-date files."
(when-let (pred (plist-get args :unless)) (when-let (pred (plist-get args :unless))
(eval pred t))))) (eval pred t)))))
use-package-defaults) use-package-defaults)
(dolist (module-files modules)
(unless recompile-p (cl-incf i)
(doom-clean-byte-compiled-files)) (dolist (target (cdr module-files))
(dolist (target (delete-dups (delq nil targets)))
(cl-incf
(if (not (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target))) (let ((elc-file (byte-compile-dest-file target)))
(and (file-exists-p elc-file) (cl-incf
(file-newer-than-file-p target elc-file))))) (if (and recompile-p (not (file-newer-than-file-p target elc-file)))
total-noop total-noop
(pcase (if (doom-file-cookie-p target "if" t) (pcase (if (not (doom-file-cookie-p target "if" t))
'no-byte-compile
(unless (equal last-module (car module-files))
(print! (success "(% 3d/%d) Compiling %s %s module...")
i total-modules (caar module-files) (cdar module-files))
(setq last-module (car module-files)))
(if verbose-p
(byte-compile-file target) (byte-compile-file target)
'no-byte-compile) (quiet! (byte-compile-file target))))
(`no-byte-compile (`no-byte-compile
(print! (info "Ignored %s") (relpath target)) (print! (debug "(% 3d/%d) Ignored %s")
i total-modules (relpath target))
total-noop) total-noop)
(`nil (`nil
(print! (error "Failed to compile %s") (relpath target)) (print! (error "(% 3d/%d) Failed to compile %s")
i total-modules (relpath target))
total-fail) total-fail)
(_ (_ total-ok)))))))
(print! (success "Compiled %s") (relpath target)) (print! (class (if (= total-fail 0) 'success 'warn)
(load target t t)
total-ok)))))
(print! (class (if (= total-fail 0) 'success 'error)
"%s %d/%d file(s) (%d ignored)") "%s %d/%d file(s) (%d ignored)")
(if recompile-p "Recompiled" "Compiled") (if recompile-p "Recompiled" "Byte-compiled")
total-ok (- (length targets) total-noop) total-ok total-files
total-noop) total-noop)
t) (= total-fail 0))
((debug error) ((debug error)
(print! (error "\nThere were breaking errors.\n\n%s") (print! (error "There were breaking errors.\n\n%s")
"Reverting changes...") "Reverting changes...")
(signal 'doom-error (list 'byte-compile e))))))) (signal 'doom-error (list 'byte-compile e))))))))
(defun doom-clean-byte-compiled-files () (defun doom-clean-byte-compiled-files ()
"Delete all the compiled elc files in your Emacs configuration and private "Delete all the compiled elc files in your Emacs configuration and private
@@ -187,7 +179,8 @@ module. This does not include your byte-compiled, third party packages.'"
(print! (start "Cleaning .elc files")) (print! (start "Cleaning .elc files"))
(print-group! (print-group!
(cl-loop with default-directory = doom-emacs-dir (cl-loop with default-directory = doom-emacs-dir
with success = nil with success = 0
with esc = (if doom-debug-p "" "\033[1A")
for path for path
in (append (doom-glob doom-emacs-dir "*.elc") in (append (doom-glob doom-emacs-dir "*.elc")
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1) (doom-files-in doom-private-dir :match "\\.elc$" :depth 1)
@@ -195,10 +188,10 @@ module. This does not include your byte-compiled, third party packages.'"
(doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4)) (doom-files-in doom-modules-dirs :match "\\.elc$" :depth 4))
if (file-exists-p path) if (file-exists-p path)
do (delete-file path) do (delete-file path)
and do (print! (success "Deleted %s") (relpath path)) and do (print! (success "\033[KDeleted %s%s") (relpath path) esc)
and do (setq success t) and do (cl-incf success)
finally do finally do
(print! (if success (print! (if (> success 0)
(success "All elc files deleted") (success "\033[K%d elc files deleted" success)
(info "No elc files to clean")))) (info "\033[KNo elc files to clean"))))
t)) t))
+2 -1
View File
@@ -1,5 +1,7 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*- ;;; core/cli/debug.el -*- lexical-binding: t; -*-
(load! "autoload/debug" doom-core-dir)
;; ;;
;;; Commands ;;; Commands
@@ -24,6 +26,5 @@
(defcli! (version v) () (defcli! (version v) ()
"Show version information for Doom & Emacs." "Show version information for Doom & Emacs."
:bare t
(doom/version) (doom/version)
nil) nil)
+25 -19
View File
@@ -48,8 +48,8 @@ in."
;; REVIEW Refactor me ;; REVIEW Refactor me
(print! (start "Checking your Emacs version...")) (print! (start "Checking your Emacs version..."))
(when EMACS27+ (when EMACS28+
(warn! "Emacs %s detected. Emacs HEAD is unstable and may cause errors." (warn! "Emacs %s detected. Doom doesn't support Emacs 28/HEAD. It is unstable and may cause errors."
emacs-version)) emacs-version))
(print! (start "Checking for Emacs config conflicts...")) (print! (start "Checking for Emacs config conflicts..."))
@@ -79,9 +79,8 @@ in."
(print! (start "Checking Doom Emacs...")) (print! (start "Checking Doom Emacs..."))
(condition-case-unless-debug ex (condition-case-unless-debug ex
(print-group! (print-group!
(let ((doom-interactive-mode 'doctor)) (let ((doom-interactive-p 'doctor))
(doom-initialize 'force) (doom-initialize 'force)
(doom-initialize-core)
(doom-initialize-modules)) (doom-initialize-modules))
(print! (success "Initialized Doom Emacs %s") doom-version) (print! (success "Initialized Doom Emacs %s") doom-version)
@@ -100,13 +99,14 @@ in."
(when-let (size (ignore-errors (doom-file-size file doom-cache-dir))) (when-let (size (ignore-errors (doom-file-size file doom-cache-dir)))
(when (> size 1048576) ; larger than 1mb (when (> size 1048576) ; larger than 1mb
(warn! "%s is too large (%.02fmb). This may cause freezes or odd startup delays" (warn! "%s is too large (%.02fmb). This may cause freezes or odd startup delays"
file (/ size 1024)) file (/ size 1024 1024.0))
(explain! "Consider deleting it from your system (manually)")))) (explain! "Consider deleting it from your system (manually)"))))
(unless (ignore-errors (executable-find doom-projectile-fd-binary))
(warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")
(unless (executable-find "rg") (unless (executable-find "rg")
(warn! "Couldn't find the `rg' binary either; project file searches will be even slower"))) (error! "Couldn't find the `rg' binary; this a hard dependecy for Doom, file searches may not work at all"))
(unless (ignore-errors (executable-find doom-projectile-fd-binary))
(warn! "Couldn't find the `fd' binary; project file searches will be slightly slower"))
(require 'projectile) (require 'projectile)
(when (projectile-project-root "~") (when (projectile-project-root "~")
@@ -123,11 +123,8 @@ in."
"both is rarely intentional; you should one or the other.")) "both is rarely intentional; you should one or the other."))
;; Check for fonts ;; Check for fonts
(if (not (fboundp 'find-font)) (if (not (executable-find "fc-list"))
(progn (warn! "Warning: unable to detect fonts because fontconfig isn't installed")
(warn! "Warning: unable to detect font")
(explain! "The `find-font' function is missing. This could indicate the incorrect "
"version of Emacs is being used!"))
;; all-the-icons fonts ;; all-the-icons fonts
(when (and (pcase system-type (when (and (pcase system-type
(`gnu/linux (concat (or (getenv "XDG_DATA_HOME") (`gnu/linux (concat (or (getenv "XDG_DATA_HOME")
@@ -136,14 +133,21 @@ in."
(`darwin "~/Library/Fonts/")) (`darwin "~/Library/Fonts/"))
(require 'all-the-icons nil t)) (require 'all-the-icons nil t))
(with-temp-buffer (with-temp-buffer
(insert (cdr (doom-call-process "fc-list"))) (let ((errors 0))
(cl-destructuring-bind (status . output)
(doom-call-process "fc-list" "" "file")
(if (not (zerop status))
(print! (error "There was an error running `fc-list'. Is fontconfig installed correctly?"))
(insert (cdr (doom-call-process "fc-list" "" "file")))
(dolist (font all-the-icons-font-names) (dolist (font all-the-icons-font-names)
(if (save-excursion (re-search-backward font nil t)) (if (save-excursion (re-search-backward font nil t))
(success! "Found font %s" font) (success! "Found font %s" font)
(print! (warn "Warning: couldn't find %S font") font) (print! (warn "Warning: couldn't find %S font") font)))
(explain! "You can install it by running `M-x all-the-icons-install-fonts' within Emacs.\n\n" (when (> errors 0)
(explain! "Some all-the-icons fonts were missing.\n\n"
"You can install them by running `M-x all-the-icons-install-fonts' within Emacs.\n"
"This could also mean you've installed them in non-standard locations, in which " "This could also mean you've installed them in non-standard locations, in which "
"case feel free to ignore this warning."))))))) "case feel free to ignore this warning.")))))))))
(print! (start "Checking for stale elc files in your DOOMDIR...")) (print! (start "Checking for stale elc files in your DOOMDIR..."))
(when (file-directory-p doom-private-dir) (when (file-directory-p doom-private-dir)
@@ -161,15 +165,17 @@ in."
(condition-case-unless-debug ex (condition-case-unless-debug ex
(let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el")) (let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
(packages-file (doom-module-path (car key) (cdr key) "packages.el"))) (packages-file (doom-module-path (car key) (cdr key) "packages.el")))
(cl-loop for name in (let (doom-packages (cl-loop with doom-output-indent = 6
for name in (let (doom-packages
doom-disabled-packages) doom-disabled-packages)
(load packages-file 'noerror 'nomessage) (load packages-file 'noerror 'nomessage)
(mapcar #'car doom-packages)) (mapcar #'car doom-packages))
unless (or (doom-package-get name :disable) unless (or (doom-package-get name :disable)
(eval (doom-package-get name :ignore)) (eval (doom-package-get name :ignore))
(plist-member (doom-package-get name :recipe) :local-repo)
(doom-package-built-in-p name) (doom-package-built-in-p name)
(doom-package-installed-p name)) (doom-package-installed-p name))
do (print! (error "%s is not installed") name)) do (print! (error "Missing emacs package: %S") name))
(let ((inhibit-message t)) (let ((inhibit-message t))
(load doctor-file 'noerror 'nomessage))) (load doctor-file 'noerror 'nomessage)))
(file-missing (error! "%s" (error-message-string ex))) (file-missing (error! "%s" (error-message-string ex)))
+46 -49
View File
@@ -1,26 +1,28 @@
;;; core/cli/env.el -*- lexical-binding: t; -*- ;;; core/cli/env.el -*- lexical-binding: t; -*-
(defcli! env (defcli! env
((clear-p ["-c" "--clear"] "Clear and delete your envvar file") ((allow ["-a" "--allow" regexp] "An envvar whitelist regexp")
(outputfile ["-o" PATH] (reject ["-r" "--reject" regexp] "An envvar blacklist regexp")
"Generate the envvar file at PATH. Note that envvar files that aren't in (clear-p ["-c" "--clear"] "Clear and delete your envvar file")
`doom-env-file' won't be loaded automatically at startup. You will need to (outputfile ["-o" path]
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.")) function."))
"Creates or regenerates your envvars file. "Creates or regenerates your envvars file.
The envvars file is created by scraping your (interactive) shell environment The envvars file is created by scraping the current shell environment into
into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env' newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env' (or
(or '$SHELL -c set' on windows). Doom loads this file at startup (if it exists) '$SHELL -c set' on windows). Doom loads this file at startup (if it exists) to
to ensure Emacs mirrors your shell environment (particularly to ensure PATH and ensure Emacs mirrors your shell environment (particularly to ensure PATH and
SHELL are correctly set). SHELL are correctly set).
This is useful in cases where you cannot guarantee that Emacs (or the daemon) 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 will be launched from the correct environment (e.g. on MacOS or through certain
app launchers on Linux). app launchers on Linux).
This file is automatically regenerated when you run this command or 'doom This file is automatically regenerated when you run this command or 'doom sync'.
refresh'. However, 'doom refresh' will only regenerate this file if it exists. However, 'doom sync' will only regenerate this file if it exists.
Why this over exec-path-from-shell? Why this over exec-path-from-shell?
@@ -36,74 +38,64 @@ Why this over exec-path-from-shell?
I'd rather it inherit your shell environment /correctly/ (and /completely/) I'd rather it inherit your shell environment /correctly/ (and /completely/)
or not at all. It frontloads the debugging process rather than hiding it 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)))) (let ((env-file (expand-file-name (or outputfile doom-env-file))))
(cond (clear-p (if (null clear-p)
(doom-cli-reload-env-file
'force env-file (list allow) (list reject))
(unless (file-exists-p env-file) (unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared" (user-error! "%S does not exist to be cleared"
(path env-file))) (path env-file)))
(delete-file env-file) (delete-file env-file)
(print! (success "Successfully deleted %S") (print! (success "Successfully deleted %S")
(path env-file))) (path env-file)))))
(args
(user-error "I don't understand 'doom env %s'"
(string-join args " ")))
((doom-cli-reload-env-file 'force env-file)))))
;; ;;
;; Helpers ;; Helpers
(defvar doom-env-ignored-vars (defvar doom-env-blacklist
'("^DBUS_SESSION_BUS_ADDRESS$" '("^DBUS_SESSION_BUS_ADDRESS$"
"^GPG_AGENT_INFO$" "^GPG_AGENT_INFO$" "^\\(SSH\\|GPG\\)_TTY$"
"^GPG_TTY$" "^SSH_\\(AUTH_SOCK\\|AGENT_PID\\)$"
"^HOME$" "^HOME$" "^PWD$" "^PS1$" "^R?PROMPT$" "^TERM$"
"^PS1$"
"^PWD$"
"^R?PROMPT$"
"^SSH_AGENT_PID$"
"^SSH_AUTH_SOCK$"
"^TERM$"
;; Doom envvars ;; Doom envvars
"^DEBUG$" "^DEBUG$" "^INSECURE$" "^YES$" "^__")
"^INSECURE$"
"^YES$"
"^__")
"Environment variables to not save in `doom-env-file'. "Environment variables to not save in `doom-env-file'.
Each string is a regexp, matched against variable names to omit from Each string is a regexp, matched against variable names to omit from
`doom-env-file'.") `doom-env-file'.")
(defun doom-cli-reload-env-file (&optional force-p env-file) (defvar doom-env-whitelist '()
"A whitelist for envvars to save in `doom-env-file'.
This overrules `doom-env-ignored-vars'. Each string is a regexp, matched against
variable names to omit from `doom-env-file'.")
(defun doom-cli-reload-env-file (&optional force-p env-file whitelist blacklist)
"Generates `doom-env-file', if it doesn't exist (or if FORCE-P). "Generates `doom-env-file', if it doesn't exist (or if FORCE-P).
This scrapes the variables from your shell environment by running This scrapes the variables from your shell environment by running
`doom-env-executable' through `shell-file-name' with `doom-env-switches'. By `doom-env-executable' through `shell-file-name' with `doom-env-switches'. By
default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
`doom-env-ignored-vars' are removed." `doom-env-ignored-vars' are removed."
(let ((env-file (if env-file (let ((env-file (if env-file (expand-file-name env-file) doom-env-file))
(expand-file-name env-file) (process-environment doom--initial-process-environment))
doom-env-file)))
(when (or force-p (not (file-exists-p env-file))) (when (or force-p (not (file-exists-p env-file)))
(with-temp-file env-file (with-temp-file env-file
(setq-local coding-system-for-write 'utf-8)
(print! (start "%s envvars file at %S") (print! (start "%s envvars file at %S")
(if (file-exists-p env-file) (if (file-exists-p env-file)
"Regenerating" "Regenerating"
"Generating") "Generating")
(path env-file)) (path env-file))
(let ((process-environment doom--initial-process-environment))
(print! (info "Scraping shell environment"))
(print-group! (print-group!
(when doom-interactive-mode (when doom-interactive-p
(user-error "'doom env' must be run on the command line, not an interactive session")) (user-error "'doom env' must be run on the command line, not an interactive session"))
(goto-char (point-min)) (goto-char (point-min))
(insert (insert
(concat (concat
"# -*- mode: dotenv -*-\n" "# -*- mode: sh -*-\n"
(format "# Generated from a %s shell environent\n" shell-file-name)
"# ---------------------------------------------------------------------------\n" "# ---------------------------------------------------------------------------\n"
"# This file was auto-generated by `doom env'. It contains a list of environment\n" "# This file was auto-generated by `doom env'. It contains a list of environment\n"
"# variables scraped from your default shell (excluding variables blacklisted\n" "# variables scraped from your default shell (excluding variables blacklisted\n"
@@ -111,21 +103,26 @@ default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
"#\n" "#\n"
(if (file-equal-p env-file doom-env-file) (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" (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" "# doom env -o ~/.doom.d/myenv\n#\n"
"# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\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" (concat "# This file is safe to edit by hand, but remember to preserve the null bytes at\n"
"# the end of each line! needs to be loaded manually with:\n#\n"
"# (doom-load-envvars-file \"path/to/this/file\")\n#\n" "# (doom-load-envvars-file \"path/to/this/file\")\n#\n"
"# Use 'doom env -o path/to/this/file' to regenerate it.")) "# Use 'doom env -o path/to/this/file' to regenerate it."))
"# ---------------------------------------------------------------------------\n\n")) "# ---------------------------------------------------------------------------\n\0\n"))
;; We assume that this noninteractive session was spawned from the ;; We assume that this noninteractive session was spawned from the
;; user's interactive shell, therefore we just dump ;; user's interactive shell, therefore we just dump
;; `process-environment' to a file. ;; `process-environment' to a file.
(dolist (env process-environment) (dolist (env process-environment)
(if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "="))) (if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "=")))
doom-env-ignored-vars) (remq nil (append blacklist doom-env-blacklist)))
(if (not (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "=")))
(remq nil (append whitelist doom-env-whitelist))))
(print! (info "Ignoring %s") env) (print! (info "Ignoring %s") env)
(insert env "\n"))) (print! (info "Whitelisted %s") env)
(insert env "\0\n"))
(insert env "\0\n")))
(print! (success "Successfully generated %S") (print! (success "Successfully generated %S")
(path env-file)) (path env-file))
t)))))) t)))))
+22 -30
View File
@@ -4,8 +4,7 @@
((noconfig-p ["--no-config"] "Don't create DOOMDIR or dummy files therein") ((noconfig-p ["--no-config"] "Don't create DOOMDIR or dummy files therein")
(noenv-p ["--no-env"] "Don't generate an envvars file (see 'doom help env')") (noenv-p ["--no-env"] "Don't generate an envvars file (see 'doom help env')")
(noinstall-p ["--no-install"] "Don't auto-install packages") (noinstall-p ["--no-install"] "Don't auto-install packages")
(nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts") (nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts"))
&rest _args)
"Installs and sets up Doom Emacs for the first time. "Installs and sets up Doom Emacs for the first time.
This command does the following: This command does the following:
@@ -24,7 +23,6 @@ DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install" DOOMDIR=~/.config/doom doom install"
:bare t
(print! (green "Installing Doom Emacs!\n")) (print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-path "~"))) (let ((default-directory (doom-path "~")))
;; Create `doom-private-dir' ;; Create `doom-private-dir'
@@ -37,34 +35,20 @@ DOOMDIR environment variable. e.g.
;; Create init.el, config.el & packages.el ;; Create init.el, config.el & packages.el
(mapc (lambda (file) (mapc (lambda (file)
(cl-destructuring-bind (filename . fn) file (cl-destructuring-bind (filename . template) file
(if (file-exists-p! filename doom-private-dir) (if (file-exists-p! filename doom-private-dir)
(print! (warn "%s already exists, skipping") filename) (print! (warn "%s already exists, skipping") filename)
(print! (info "Creating %s%s") (relpath doom-private-dir) filename) (print! (info "Creating %s%s") (relpath doom-private-dir) filename)
(with-temp-file (doom-path doom-private-dir filename) (with-temp-file (doom-path doom-private-dir filename)
(funcall fn)) (insert-file-contents template))
(print! (success "Done!"))))) (print! (success "Done!")))))
'(("init.el" . `(("init.el" . ,(doom-path doom-emacs-dir "init.example.el"))
(lambda () ("config.el" . ,(doom-path doom-core-dir "templates/config.example.el"))
(insert-file-contents (doom-path doom-emacs-dir "init.example.el")))) ("packages.el" . ,(doom-path doom-core-dir "templates/packages.example.el")))))
("config.el" .
(lambda ()
(insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
";; Place your private configuration here\n"
((relpath doom-private-dir)))))
("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))))))))
;; In case no init.el was present the first time `doom-initialize-modules' was ;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install) ;; called in core.el (e.g. on first install)
(doom-initialize 'force 'noerror) (doom-initialize-modules 'force 'no-config)
(doom-initialize-modules)
;; Ask if user would like an envvar file generated ;; Ask if user would like an envvar file generated
(if noenv-p (if noenv-p
@@ -72,7 +56,7 @@ DOOMDIR environment variable. e.g.
(if (file-exists-p doom-env-file) (if (file-exists-p doom-env-file)
(print! (info "Envvar file already exists, skipping")) (print! (info "Envvar file already exists, skipping"))
(when (or doom-auto-accept (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)))) (doom-cli-reload-env-file 'force-p))))
;; Install Doom packages ;; Install Doom packages
@@ -82,11 +66,19 @@ DOOMDIR environment variable. e.g.
(doom-cli-packages-install)) (doom-cli-packages-install))
(print! "Regenerating autoloads files") (print! "Regenerating autoloads files")
(doom-cli-reload-autoloads nil 'force-p) (doom-autoloads-reload)
(if nofonts-p (cond (nofonts-p)
(print! (warn "Not installing fonts, as requested")) (IS-WINDOWS
(when (or doom-auto-accept (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?")) (y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons) (require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns) (let ((window-system (cond (IS-MAC 'ns)
@@ -98,5 +90,5 @@ DOOMDIR environment variable. e.g.
(print! (success "\nFinished! Doom is ready to go!\n")) (print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer (with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO") (insert-file-contents (doom-glob doom-core-dir "templates/QUICKSTART_INTRO"))
(print! (buffer-string))))) (print! "%s" (buffer-string)))))
+281 -163
View File
@@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el ;;; core/cli/packages.el
(defcli! (update u) () (defcli! (update u)
((discard-p ["--discard"] "All local changes to packages are discarded"))
"Updates packages. "Updates packages.
This works by fetching all installed package repos and checking the distance This works by fetching all installed package repos and checking the distance
@@ -10,10 +11,10 @@ between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property." or :ignore property."
(straight-check-all) (straight-check-all)
(doom-cli-reload-core-autoloads) (let ((doom-auto-discard discard-p))
(when (doom-cli-packages-update) (when (doom-cli-packages-update)
(doom-cli-reload-package-autoloads 'force-p)) (doom-autoloads-reload))
t) t))
(defcli! (build b) (defcli! (build b)
((rebuild-p ["-r"] "Only rebuild packages that need rebuilding")) ((rebuild-p ["-r"] "Only rebuild packages that need rebuilding"))
@@ -23,7 +24,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 their elisp files are byte-compiled. This is especially necessary if you upgrade
Emacs (as byte-code is generally not forward-compatible)." Emacs (as byte-code is generally not forward-compatible)."
(when (doom-cli-packages-build (not rebuild-p)) (when (doom-cli-packages-build (not rebuild-p))
(doom-cli-reload-package-autoloads 'force-p)) (doom-autoloads-reload))
t) t)
(defcli! (purge p) (defcli! (purge p)
@@ -46,7 +47,7 @@ list remains lean."
(not norepos-p) (not norepos-p)
(not nobuilds-p) (not nobuilds-p)
regraft-p) regraft-p)
(doom-cli-reload-package-autoloads 'force-p)) (doom-autoloads-reload))
t) t)
;; (defcli! rollback () ; TODO doom rollback ;; (defcli! rollback () ; TODO doom rollback
@@ -57,144 +58,248 @@ list remains lean."
;; ;;
;;; Library ;;; 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--barf-if-incomplete-packages ()
(let ((straight-safe-mode t))
(condition-case _ (straight-check-all)
(error (user-error "Package state is incomplete. Run 'doom sync' first")))))
(defmacro doom--with-package-recipes (recipes binds &rest body)
(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 &rest _) (cl-pushnew pkg built :test #'equal))
straight-use-package-pre-build-functions)))
(dolist (,recipe-var ,recipes-var (nreverse built))
(cl-block nil
(straight--with-plist (append (list :recipe ,recipe-var) ,recipe-var)
,(doom-enlist binds)
,@body))))))
(defvar doom--cli-updated-recipes nil)
(defun doom--cli-recipes-update ()
"Updates straight and recipe repos."
(unless doom--cli-updated-recipes
(straight--make-build-cache-available)
(print! (start "Updating recipe repos..."))
(print-group!
(doom--with-package-recipes
(delq
nil (mapcar (doom-rpartial #'gethash straight--repo-cache)
(mapcar #'symbol-name straight-recipe-repositories)))
(recipe package type local-repo)
(let ((esc (unless doom-debug-p "\033[1A"))
(ref (straight-vc-get-commit type local-repo))
newref output)
(print! (start "\033[KUpdating recipes for %s...%s") package esc)
(when (straight-vc-fetch-from-remote recipe)
(setq output (straight--process-get-output))
(straight-merge-package package)
(unless (equal ref (setq newref (straight-vc-get-commit type local-repo)))
(print! (success "\033[K%s updated (%s -> %s)")
package
(doom--abbrev-commit ref)
(doom--abbrev-commit newref))
(unless (string-empty-p output)
(print-group! (print! (info "%s" output)))))))))
(setq straight--recipe-lookup-cache (make-hash-table :test #'eq)
doom--cli-updated-recipes t)))
(defun doom-cli-packages-install () (defun doom-cli-packages-install ()
"Installs missing packages. "Installs missing packages.
This function will install any primary package (i.e. a package with a `package!' This function will install any primary package (i.e. a package with a `package!'
declaration) or dependency thereof that hasn't already been." declaration) or dependency thereof that hasn't already been."
(print! (start "Installing & building packages...")) (doom-initialize-packages)
(print! (start "Installing packages..."))
(let ((pinned (doom-package-pinned-list)))
(print-group! (print-group!
(let ((n 0)) (if-let (built
(dolist (package (hash-table-keys straight--recipe-cache)) (doom--with-package-recipes (doom-package-recipe-list)
(straight--with-plist (gethash package straight--recipe-cache) (recipe package type local-repo)
(local-repo) (unless (file-directory-p (straight--repos-dir local-repo))
(let ((existed-p (file-directory-p (straight--repos-dir package)))) (doom--cli-recipes-update))
(condition-case-unless-debug e (condition-case-unless-debug e
(and (straight-use-package (intern package) nil nil (make-string (1- (or doom-format-indent 1)) 32)) (let ((straight-use-package-pre-build-functions
(not existed-p) (cons (lambda (pkg &rest _)
(file-directory-p (straight--repos-dir package)) (when-let (commit (cdr (assoc pkg pinned)))
(cl-incf n)) (print! (info "Checked out %s") commit)))
straight-use-package-pre-build-functions)))
(straight-use-package (intern package)))
(error (error
(signal 'doom-package-error (signal 'doom-package-error (list package e))))))
(list e (straight--process-get-output)))))))) (print! (success "Installed %d packages")
(if (= n 0) (length built))
(ignore (print! (success "No packages need to be installed"))) (print! (info "No packages need to be installed"))
(print! (success "Installed & built %d packages") n) nil))))
t))))
(defun doom-cli-packages-build (&optional force-p) (defun doom-cli-packages-build (&optional force-p)
"(Re)build all packages." "(Re)build all packages."
(doom-initialize-packages)
(print! (start "(Re)building %spackages...") (if force-p "all " "")) (print! (start "(Re)building %spackages...") (if force-p "all " ""))
(print-group! (print-group!
(let ((n 0)) (let ((straight-check-for-modifications
(if force-p (when (file-directory-p (straight--modified-dir))
(let ((straight--packages-to-rebuild :all) '(find-when-checking)))
(straight--packages-not-to-rebuild (make-hash-table :test #'equal))) (straight--allow-find
(dolist (package (hash-table-keys straight--recipe-cache)) (and straight-check-for-modifications
(straight-use-package (executable-find straight-find-executable)
(intern package) nil (lambda (_) (cl-incf n) nil) t))
(make-string (1- (or doom-format-indent 1)) 32)))) (straight--packages-not-to-rebuild
(dolist (recipe (hash-table-values straight--recipe-cache)) (or straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(straight--with-plist recipe (package local-repo no-build) (straight--packages-to-rebuild
(unless (or no-build (null local-repo)) (or (if force-p :all straight--packages-to-rebuild)
;; REVIEW We do these modification checks manually because (make-hash-table :test #'equal)))
;; Straight's checks seem to miss stale elc files. Need (recipes (doom-package-recipe-list)))
;; more tests to confirm this. (unless force-p
(when (or (ignore-errors (straight--make-build-cache-available))
(gethash package straight--packages-to-rebuild)) (if-let (built
(gethash package straight--cached-package-modifications) (doom--with-package-recipes recipes (package local-repo recipe)
(not (file-directory-p (straight--build-dir package))) (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 (cl-loop for file
in (doom-files-in (straight--build-dir package) in (doom-files-in build-dir :match "\\.el$" :full t)
:match "\\.el$"
:full t)
for elc-file = (byte-compile-dest-file file) for elc-file = (byte-compile-dest-file file)
if (and (file-exists-p elc-file) if (and (file-exists-p elc-file)
(file-newer-than-file-p file elc-file)) (file-newer-than-file-p file elc-file))
return t)) return t)))
(let ((straight-use-package-pre-build-functions (not (plist-get recipe :no-build))
straight-use-package-pre-build-functions)) (puthash package t straight--packages-to-rebuild))))
(add-hook 'straight-use-package-pre-build-functions (straight-use-package (intern package))))
(lambda (&rest _) (cl-incf n))) (print! (success "Rebuilt %d package(s)") (length built))
(let ((straight--packages-to-rebuild :all) (print! (success "No packages need rebuilding"))
(straight--packages-not-to-rebuild (make-hash-table :test #'equal))) nil))))
(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))))
(defun doom-cli-packages-update () (defun doom-cli-packages-update ()
"Updates packages." "Updates packages."
(print! (start "Updating packages (this may take a while)...")) (doom-initialize-packages)
;; TODO Refactor me (doom--barf-if-incomplete-packages)
(let ((straight--repos-dir (straight--repos-dir)) (let* ((repo-dir (straight--repos-dir))
(straight--packages-to-rebuild (make-hash-table :test #'equal)) (pinned (doom-package-pinned-list))
(total (hash-table-count straight--repo-cache)) (recipes (doom-package-recipe-list))
(i 1) (packages-to-rebuild (make-hash-table :test 'equal))
(repos-to-rebuild (make-hash-table :test 'equal))
(total (length recipes))
(esc (unless doom-debug-p "\033[1A"))
(i 0)
errors) errors)
(when recipes
(doom--cli-recipes-update))
(print! (start "Updating packages (this may take a while)..."))
(doom--with-package-recipes recipes (recipe package type local-repo)
(cl-incf i)
(print-group! (print-group!
(dolist (recipe (hash-table-values straight--repo-cache)) (unless (straight--repository-is-available-p recipe)
(straight--with-plist recipe (package type local-repo) (print! (error "(%d/%d) Couldn't find local repo for %s") i total package)
(condition-case-unless-debug e (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))) (let ((default-directory (straight--repos-dir local-repo)))
(if (not (file-in-directory-p default-directory straight--repos-dir)) (unless (file-in-directory-p default-directory repo-dir)
(print! (warn "[%d/%d] Skipping %s because it is local") (print! (warn "(%d/%d) Skipping %s because it is local") i total package)
i total package) (cl-return))
(let ((commit (straight-vc-get-commit type local-repo)))
(if (not (straight-vc-fetch-from-remote recipe))
(print! (warn "\033[K(%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! (start "\033[K(%d/%d) %s is up-to-date\033[1A") i total package)
(ignore-errors
(delete-directory (straight--build-dir package) 'recursive))
(puthash package t straight--packages-to-rebuild)
(print! (info "\033[K(%d/%d) Updating %s...") i total package)
(unless (string-empty-p output)
(print-group!
(print! (info "%s") output)
(when (eq type 'git) (when (eq type 'git)
(straight--call "git" "log" "--oneline" newcommit (concat "^" commit)) (unless (file-exists-p ".git")
(error "%S is not a valid repository" package)))
(condition-case-unless-debug e
(let ((ref (straight-vc-get-commit type local-repo))
(target-ref
(cdr (or (assoc local-repo pinned)
(assoc package 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))
((if (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)
(print! (start "\033[K(%d/%d) Fetching %s...%s") i total package esc)
(and (straight-vc-fetch-from-remote recipe)
(straight-vc-commit-present-p recipe target-ref)))
(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))
(straight-vc-git-default-clone-depth 'full))
(delete-directory repo 'recursive)
(print-group! (print-group!
(print! "%s" (straight--process-get-output)))))) (straight-use-package (intern package) nil 'no-build))
(print! (success "(%d/%d) %s updated (%s -> %s)") i total package (prog1 (file-directory-p repo)
(substring commit 0 7) (or (not (eq type 'git))
(substring newcommit 0 7)))))))) (setq output (doom--commit-log-between ref target-ref)))))))
(cl-incf i)) (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! "%s" (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 (user-error
(signal 'user-error (error-message-string e))) (signal 'user-error (error-message-string e)))
(error (error
(print! (warn "(%d/%d) Encountered error with %s" i total package)) (signal 'doom-package-error (list package e)))))))
(print-group! (print-group!
(print! (error "%s" e))
(print-group! (print! (info "%s" (straight--process-get-output)))))
(push package errors)))))
(princ "\033[K") (princ "\033[K")
(when errors (if (hash-table-empty-p packages-to-rebuild)
(print! (error "There were %d errors, the offending packages are: %s") (ignore (print! (success "All %d packages are up-to-date") total))
(length errors) (string-join errors ", "))) (straight--transaction-finalize)
(if (hash-table-empty-p straight--packages-to-rebuild) (let ((default-directory (straight--build-dir)))
(ignore (mapc (doom-rpartial #'delete-directory 'recursive)
(print! (success "All %d packages are up-to-date") (hash-table-keys packages-to-rebuild)))
(hash-table-count straight--repo-cache))) (print! (success "Updated %d package(s)")
(let ((count (hash-table-count straight--packages-to-rebuild)) (hash-table-count packages-to-rebuild))
(packages (hash-table-keys straight--packages-to-rebuild)))
(sort packages #'string-lessp)
(doom--finalize-straight)
(doom-cli-packages-build) (doom-cli-packages-build)
(print! (success "Updated %d package(s)") count))
t)))) t))))
@@ -209,30 +314,38 @@ declaration) or dependency thereof that hasn't already been."
(defun doom--cli-packages-purge-builds (builds) (defun doom--cli-packages-purge-builds (builds)
(if (not builds) (if (not builds)
(progn (print! (info "No builds to purge")) (prog1 0
0) (print! (info "No builds to purge")))
(print! (start "Purging straight builds..." (length builds)))
(print-group!
(length (length
(delq nil (mapcar #'doom--cli-packages-purge-build builds))))) (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))) (let ((default-directory (straight--repos-dir repo)))
(if (not (file-directory-p ".git")) (unless (file-directory-p ".git")
(ignore (print! (warn "\033[Krepos/%s is not a git repo, skipping" repo))) (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))) (let ((before-size (doom-directory-size default-directory)))
(straight--call "git" "reset" "--hard") (straight--call "git" "reset" "--hard")
(straight--call "git" "clean" "-ffd") (straight--call "git" "clean" "-ffd")
(if (not (car (straight--call "git" "replace" "--graft" "HEAD"))) (if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
(print! (info "\033[Krepos/%s is already compact\033[1A" repo)) (print! (info "\033[Krepos/%s is already compact\033[1A" repo))
(straight--call "git" "gc") (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)") (print! (success "\033[KRegrafted repos/%s (from %0.1fKB to %0.1fKB)")
repo before-size (doom-directory-size default-directory)) repo before-size (doom-directory-size default-directory))
(print-group! (print! "%s" (straight--process-get-output))))) (print-group! (print! "%s" (straight--process-get-output))))
t))) t)))
(defun doom--cli-packages-regraft-repos (repos) (defun doom--cli-packages-regraft-repos (repos)
(if (not repos) (if (not repos)
(progn (print! (info "No repos to regraft")) (prog1 0
0) (print! (info "No repos to regraft")))
(print! (start "Regrafting %d repos..." (length repos)))
(let ((before-size (doom-directory-size (straight--repos-dir)))) (let ((before-size (doom-directory-size (straight--repos-dir))))
(print-group! (print-group!
(prog1 (delq nil (mapcar #'doom--cli-packages-regraft-repo repos)) (prog1 (delq nil (mapcar #'doom--cli-packages-regraft-repo repos))
@@ -245,8 +358,7 @@ declaration) or dependency thereof that hasn't already been."
(defun doom--cli-packages-purge-repo (repo) (defun doom--cli-packages-purge-repo (repo)
(let ((repo-dir (straight--repos-dir repo))) (let ((repo-dir (straight--repos-dir repo)))
(delete-directory repo-dir 'recursive) (delete-directory repo-dir 'recursive)
(ignore-errors (delete-file (straight--modified-file repo))
(delete-file (straight--modified-file repo)))
(if (file-directory-p repo-dir) (if (file-directory-p repo-dir)
(ignore (print! (error "Failed to purge repos/%s" repo))) (ignore (print! (error "Failed to purge repos/%s" repo)))
(print! (success "Purged repos/%s" repo)) (print! (success "Purged repos/%s" repo))
@@ -254,24 +366,31 @@ declaration) or dependency thereof that hasn't already been."
(defun doom--cli-packages-purge-repos (repos) (defun doom--cli-packages-purge-repos (repos)
(if (not repos) (if (not repos)
(progn (print! (info "No repos to purge")) (prog1 0
0) (print! (info "No repos to purge")))
(print! (start "Purging straight repositories..."))
(print-group!
(length (length
(delq nil (mapcar #'doom--cli-packages-purge-repo repos))))) (delq nil (mapcar #'doom--cli-packages-purge-repo repos))))))
(defun doom--cli-packages-purge-elpa () (defun doom--cli-packages-purge-elpa ()
(unless (bound-and-true-p package--initialized) (require 'core-packages)
(package-initialize)) (let ((dirs (doom-files-in package-user-dir :type t :depth 0)))
(let ((packages (cl-loop for (package desc) in package-alist (if (not dirs)
for dir = (package-desc-dir desc) (prog1 0
if (file-in-directory-p dir package-user-dir) (print! (info "No ELPA packages to purge")))
collect (cons package dir)))) (print! (start "Purging ELPA packages..."))
(if (not package-alist) (dolist (path dirs (length dirs))
(progn (print! (info "No ELPA packages to purge")) (condition-case e
0) (print-group!
(mapc (doom-rpartial #'delete-directory 'recursive) (if (file-directory-p path)
(mapcar #'cdr packages)) (delete-directory path 'recursive)
(length packages)))) (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) (defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p)
"Auto-removes orphaned packages and repos. "Auto-removes orphaned packages and repos.
@@ -282,36 +401,35 @@ a `package!' declaration) or isn't depended on by another primary package.
If BUILDS-P, include straight package builds. If BUILDS-P, include straight package builds.
If REPOS-P, include straight repos. If REPOS-P, include straight repos.
If ELPA-P, include packages installed with package.el (M-x package-install)." If ELPA-P, include packages installed with package.el (M-x package-install)."
(print! (start "Searching for orphaned packages to purge (for the emperor)...")) (doom-initialize-packages)
(doom--barf-if-incomplete-packages)
(print! (start "Purging orphaned packages (for the emperor)..."))
(cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft) (cl-destructuring-bind (&optional builds-to-purge repos-to-purge repos-to-regraft)
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort)) (let ((rdirs
(bdirs (straight--directory-files (straight--build-dir) nil nil 'sort))) (and (or repos-p regraft-repos-p)
(list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache) (straight--directory-files (straight--repos-dir) nil nil 'sort))))
bdirs) (list (when builds-p
(cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache) (seq-remove (doom-rpartial #'gethash straight--profile-cache)
rdirs) (straight--directory-files (straight--build-dir) nil nil 'sort)))
(cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache) (when repos-p
rdirs))) (seq-remove (doom-rpartial #'straight--checkhash straight--repo-cache)
(let (success) rdirs))
(when regraft-repos-p
(seq-filter (doom-rpartial #'straight--checkhash straight--repo-cache)
rdirs))))
(print-group! (print-group!
(delq
nil (list
(if (not builds-p) (if (not builds-p)
(print! (info "Skipping builds")) (ignore (print! (info "Skipping builds")))
(and (/= 0 (doom--cli-packages-purge-builds builds-to-purge)) (and (/= 0 (doom--cli-packages-purge-builds builds-to-purge))
(setq success t)
(straight-prune-build-cache))) (straight-prune-build-cache)))
(if (not elpa-p) (if (not elpa-p)
(print! (info "Skipping elpa packages")) (ignore (print! (info "Skipping elpa packages")))
(and (/= 0 (doom--cli-packages-purge-elpa)) (/= 0 (doom--cli-packages-purge-elpa)))
(setq success t)))
(if (not repos-p) (if (not repos-p)
(print! (info "Skipping repos")) (ignore (print! (info "Skipping repos")))
(and (/= 0 (doom--cli-packages-purge-repos repos-to-purge)) (/= 0 (doom--cli-packages-purge-repos repos-to-purge)))
(setq success t)))
(if (not regraft-repos-p) (if (not regraft-repos-p)
(print! (info "Skipping regrafting")) (ignore (print! (info "Skipping regrafting")))
(print! (start "Regrafting %d repos..." (length repos-to-regraft))) (doom--cli-packages-regraft-repos repos-to-regraft)))))))
(and (doom--cli-packages-regraft-repos repos-to-regraft)
(setq success t)))
(when success
(doom--finalize-straight)
t)))))
+2 -2
View File
@@ -50,9 +50,9 @@
(require 'core-cli) (require 'core-cli)
(doom-initialize 'force 'noerror) (doom-initialize 'force 'noerror)
(doom-initialize-modules) (doom-initialize-modules)
(doom-cli-reload-core-autoloads 'force) (doom-cli-reload-core-autoloads)
(when (doom-cli-packages-install) (when (doom-cli-packages-install)
(doom-cli-reload-package-autoloads 'force))))) (doom-cli-reload-package-autoloads)))))
(unless (zerop status) (unless (zerop status)
(error "Failed to bootstrap unit tests")))) (error "Failed to bootstrap unit tests"))))
(with-temp-buffer (with-temp-buffer
+26 -16
View File
@@ -1,7 +1,8 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*- ;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(defcli! (upgrade up) (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. "Updates Doom and packages.
This requires that ~/.emacs.d is a git repo, and is the equivalent of the This requires that ~/.emacs.d is a git repo, and is the equivalent of the
@@ -10,18 +11,22 @@ following shell commands:
cd ~/.emacs.d cd ~/.emacs.d
git pull --rebase git pull --rebase
bin/doom clean bin/doom clean
bin/doom refresh bin/doom sync
bin/doom update" bin/doom update"
:bare t :bare t
(if (delq (let ((doom-auto-discard force-p))
nil (list (cond
(doom-cli-upgrade doom-auto-accept force-p) (packages-only-p
(doom-cli-execute "refresh") (doom-cli-execute "sync" '("-u"))
(when (doom-cli-packages-update) (print! (success "Finished upgrading Doom Emacs")))
(doom-cli-reload-package-autoloads 'force)
t))) ((doom-cli-upgrade doom-auto-accept doom-auto-discard)
(print! (success "Done! Restart Emacs for changes to take effect.")) ;; Reload Doom's CLI & libraries, in case there were any upstream changes.
(print! "Nothing to do. Doom is up-to-date!"))) ;; Major changes will still break, however
(print! (info "Reloading Doom Emacs"))
(doom-cli-execute-after "doom" "upgrade" "-p" (if force-p "-f")))
((print! "Nothing to do. Doom is up-to-date!")))))
;; ;;
@@ -47,7 +52,13 @@ following shell commands:
process-file-side-effects) process-file-side-effects)
(print! (start "Preparing to upgrade Doom Emacs and its packages...")) (print! (start "Preparing to upgrade Doom Emacs and its packages..."))
(let* ((branch (vc-git--symbolic-ref doom-emacs-dir)) (let* (;; git name-rev may return BRANCH~X for detached HEADs and fully
;; qualified refs in some other cases, so an effort to strip out all
;; but the branch name is necessary. git symbolic-ref (or
;; `vc-git--symbolic-ref') won't work; it can't deal with submodules.
(branch (replace-regexp-in-string
"^\\(?:[^/]+/[^/]+/\\)?\\(.+\\)\\(?:~[0-9]+\\)?$" "\\1"
(cdr (doom-call-process "git" "name-rev" "--name-only" "HEAD"))))
(target-remote (format "%s/%s" doom-repo-remote branch))) (target-remote (format "%s/%s" doom-repo-remote branch)))
(unless branch (unless branch
(error! (if (file-exists-p! ".git" doom-emacs-dir) (error! (if (file-exists-p! ".git" doom-emacs-dir)
@@ -103,11 +114,10 @@ following shell commands:
(print! (start "Upgrading Doom Emacs...")) (print! (start "Upgrading Doom Emacs..."))
(print-group! (print-group!
(doom-clean-byte-compiled-files) (doom-clean-byte-compiled-files)
(if (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote))) (unless (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote)))
(equal (vc-git--rev-parse "HEAD") new-rev)) (equal (vc-git--rev-parse "HEAD") new-rev))
(print! (info "%s") (cdr result))
(error "Failed to check out %s" (substring new-rev 0 10))) (error "Failed to check out %s" (substring new-rev 0 10)))
(print! (success "Finished upgrading Doom Emacs"))) (print! (info "%s") (cdr result))
t))))) t))))))
(ignore-errors (ignore-errors
(doom-call-process "git" "remote" "remove" doom-repo-remote)))))) (doom-call-process "git" "remote" "remove" doom-repo-remote))))))
+199 -53
View File
@@ -2,14 +2,21 @@
(require 'seq) (require 'seq)
;; Eagerly load these libraries because we may be in a session that hasn't been (load! "autoload/process")
;; 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") (load! "autoload/plist")
(load! "autoload/files")
(load! "autoload/output")
;; Create all our core directories to quell file errors
(mapc (doom-rpartial #'make-directory 'parents)
(list doom-local-dir
doom-etc-dir
doom-cache-dir))
;; Ensure straight and the bare minimum is ready to go
(require 'core-modules)
(require 'core-packages)
(doom-initialize-core-packages)
;; ;;
@@ -20,7 +27,16 @@
commands like `doom-cli-packages-install', `doom-cli-packages-update' and commands like `doom-cli-packages-install', `doom-cli-packages-update' and
`doom-packages-autoremove'.") `doom-packages-autoremove'.")
(defvar doom--cli-p nil) (defvar doom-auto-discard (getenv "FORCE")
"If non-nil, discard all local changes while updating.")
(defvar doom-cli-file "cli"
"The basename of CLI config files for modules.
These are loaded when a Doom's CLI starts up. There users and modules can define
additional CLI commands, or reconfigure existing ones to better suit their
purpose.")
(defvar doom--cli-commands (make-hash-table :test 'equal)) (defvar doom--cli-commands (make-hash-table :test 'equal))
(defvar doom--cli-groups (make-hash-table :test 'equal)) (defvar doom--cli-groups (make-hash-table :test 'equal))
(defvar doom--cli-group nil) (defvar doom--cli-group nil)
@@ -63,7 +79,8 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and
(defun doom--cli-process (cli args) (defun doom--cli-process (cli args)
(let* ((args (copy-sequence args)) (let* ((args (copy-sequence args))
(arglist (copy-sequence (doom-cli-arglist cli))) (arglist (copy-sequence (doom-cli-arglist cli)))
(expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords) (expected
(or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords)
arglist) arglist)
(length arglist))) (length arglist)))
(got 0) (got 0)
@@ -146,9 +163,27 @@ Executes a cli defined with `defcli!' with the name or alias specified by
COMMAND, and passes ARGS to it." COMMAND, and passes ARGS to it."
(if-let (cli (doom-cli-get command)) (if-let (cli (doom-cli-get command))
(funcall (doom-cli-fn cli) (funcall (doom-cli-fn cli)
(doom--cli-process cli args)) (doom--cli-process cli (remq nil args)))
(user-error "Couldn't find any %S command" command))) (user-error "Couldn't find any %S command" command)))
(defun doom-cli-execute-after (&rest args)
"Execute shell command ARGS after this CLI session quits.
This is particularly useful when the capabilities of Emacs' batch terminal are
insufficient (like opening an instance of Emacs, or reloading Doom after a 'doom
upgrade')."
(let ((post-script (concat doom-local-dir ".doom.sh")))
(with-temp-file post-script
(insert "#!/usr/bin/env sh\n"
"rm -f " (prin1-to-string post-script) "\n"
"exec " (mapconcat #'shell-quote-argument (remq nil args) " ")
"\n"))
(let* ((current-mode (file-modes post-script))
(add-mode (logand ?\111 (default-file-modes))))
(or (/= (logand ?\111 current-mode) 0)
(zerop add-mode)
(set-file-modes post-script (logior current-mode add-mode))))))
(defmacro defcli! (name speclist &optional docstring &rest body) (defmacro defcli! (name speclist &optional docstring &rest body)
"Defines a CLI command. "Defines a CLI command.
@@ -195,10 +230,6 @@ BODY will be run when this dispatcher is called."
for optsym = (if (listp opt) (car opt) opt) for optsym = (if (listp opt) (car opt) opt)
unless (memq optsym cl--lambda-list-keywords) unless (memq optsym cl--lambda-list-keywords)
collect (list optsym `(cdr (assq ',optsym --alist--)))) collect (list optsym `(cdr (assq ',optsym --alist--))))
,@(unless (plist-get plist :bare)
'((unless doom-init-p
(doom-initialize 'force 'noerror)
(doom-initialize-modules))))
,@body))) ,@body)))
doom--cli-commands) doom--cli-commands)
(when aliases (when aliases
@@ -213,19 +244,142 @@ BODY will be run when this dispatcher is called."
,@body)) ,@body))
;;
;;; straight.el hacks
;; Straight was designed primarily for interactive use, in an interactive Emacs
;; session, but Doom does its package management in the terminal. Some things
;; must be modified get straight to behave and improve its UX for our users.
(defvar doom--straight-discard-options
'(("has diverged from"
. "^Reset [^ ]+ to branch")
("but recipe specifies a URL of"
. "Delete remote \"[^\"]+\", re-create it with correct URL")
("has a merge conflict:"
. "^Abort merge$")
("has a dirty worktree:"
. "^Discard changes$")
("^In repository "
. "^Reset branch \\|^Delete remote [^,]+, re-create it with correct URL"))
"A list of regexps, mapped to regexps.
Their CAR is tested against the prompt, and CDR is tested against the presented
option, and is used by `straight-vc-git--popup-raw' to select which option to
recommend.
It may not be obvious to users what they should do for some straight prompts,
so Doom will recommend the one that reverts a package back to its (or target)
original state.")
;; 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 doom-interactive-p
(funcall orig-fn prompt)
(y-or-n-p (format! "%s" (or prompt ""))))))
(defun doom--straight-recommended-option-p (prompt option)
(cl-loop for (prompt-re . opt-re) in doom--straight-discard-options
if (string-match-p prompt-re prompt)
return (string-match-p opt-re option)))
(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 doom-interactive-p
(funcall orig-fn prompt actions)
(let ((doom--straight-discard-options doom--straight-discard-options))
;; 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 (doom--straight-recommended-option-p prompt desc)
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 (doom--straight-recommended-option-p prompt desc)
(progn
(setq doom--straight-discard-options nil)
(green (concat desc " (Recommended)")))
desc))))
(terpri)
(let* ((options
(cons (lambda ()
(let ((doom-output-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-output-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-output-indent)
(> doom-output-indent 0))
(make-string (1- (or doom-output-indent 1)) 32)
cause)
interactive)))
;; ;;
;;; CLI Commands ;;; CLI Commands
(load! "cli/help") (load! "cli/help")
(load! "cli/install") (load! "cli/install")
(defcligroup! "Maintenance" (defcli! (refresh re) ()
"For managing your config and packages" "Deprecated for 'doom sync'"
(defcli! (refresh re sync) :hidden t
((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary") (user-error "'doom refresh' has been replaced with 'doom sync'. Use that instead"))
(inhibit-envvar-p ["-e"] "Don't regenerate the envvar file")
(prune-p ["-p" "--prune"] "Purge orphaned packages & regraft repos")) (defcli! (sync s)
"Ensure Doom is properly set up. ((inhibit-envvar-p ["-e"] "Don't regenerate the envvar file")
(inhibit-elc-p ["-c"] "Don't recompile config")
(update-p ["-u"] "Update installed packages after syncing")
(prune-p ["-p" "--prune"] "Purge orphaned package repos & regraft them"))
"Synchronize your config with Doom Emacs.
This is the equivalent of running autoremove, install, autoloads, then This is the equivalent of running autoremove, install, autoloads, then
recompile. Run this whenever you: recompile. Run this whenever you:
@@ -238,53 +392,47 @@ recompile. Run this whenever you:
It will ensure that unneeded packages are removed, all needed packages are 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 installed, autoloads files are up-to-date and no byte-compiled files have gone
stale." stale."
:bare t (print! (start "Synchronizing your config with Doom Emacs..."))
(print! (start "Initiating a refresh of Doom Emacs..."))
(print-group! (print-group!
(let (success) (delete-file doom-autoload-file)
(when (and (not inhibit-envvar-p) (when (and (not inhibit-envvar-p)
(file-exists-p doom-env-file)) (file-exists-p doom-env-file))
(doom-cli-reload-env-file 'force)) (doom-cli-reload-env-file 'force))
(run-hooks 'doom-sync-pre-hook)
;; Ensures that no pre-existing state pollutes the generation of the new (doom-cli-packages-install)
;; autoloads files. (doom-cli-packages-build)
(mapc #'doom--cli-delete-autoloads-file (when update-p
(list doom-autoload-file (doom-cli-packages-update))
doom-package-autoload-file)) (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p)
(doom-initialize 'force 'noerror) (run-hooks 'doom-sync-post-hook)
(doom-initialize-modules) (when (doom-autoloads-reload)
(print! (info "Restart Emacs or use 'M-x doom/reload' for changes to take effect")))
(doom-cli-reload-core-autoloads (not if-necessary-p)) t))
(unwind-protect
(progn
(and (doom-cli-packages-install)
(setq success t))
(and (doom-cli-packages-build)
(setq success t))
(and (doom-cli-packages-purge prune-p 'builds-p prune-p prune-p)
(setq success t)))
(doom-cli-reload-package-autoloads (or success (not if-necessary-p)))
(doom-cli-byte-compile nil 'recompile))
t)))
(load! "cli/env") (load! "cli/env")
(load! "cli/upgrade") (load! "cli/upgrade")
(load! "cli/packages") (load! "cli/packages")
(load! "cli/autoloads")) (load! "cli/autoloads")
(defcligroup! "Diagnostics" (defcligroup! "Diagnostics"
"For troubleshooting and diagnostics" "For troubleshooting and diagnostics"
(load! "cli/doctor") (load! "cli/doctor")
(load! "cli/debug") (load! "cli/debug")
(load! "cli/test"))
;; Our tests are broken at the moment. Working on fixing them, but for now we
;; disable them:
;; (load! "cli/test")
)
(defcligroup! "Compilation" (defcligroup! "Compilation"
"For compiling Doom and your config" "For compiling Doom and your config"
(load! "cli/byte-compile")) (load! "cli/byte-compile"))
(defcligroup! "Utilities" (defcligroup! "Utilities"
"Conveniences for interacting with Doom externally" "Conveniences for interacting with Doom externally"
(defcli! run () (defcli! run (&rest args)
"Run Doom Emacs from bin/doom's parent directory. "Run Doom Emacs from bin/doom's parent directory.
All arguments are passed on to Emacs. All arguments are passed on to Emacs.
@@ -294,11 +442,9 @@ All arguments are passed on to Emacs.
WARNING: this command exists for convenience and testing. Doom will suffer WARNING: this command exists for convenience and testing. Doom will suffer
additional overhead by being started this way. For the best performance, it is additional overhead by being started this way. For the best performance, it is
best to run Doom out of ~/.emacs.d and ~/.doom.d.") best to run Doom out of ~/.emacs.d and ~/.doom.d."
(apply #'doom-cli-execute-after invocation-name args)
;; (load! "cli/batch") nil))
;; (load! "cli/org")
)
(provide 'core-cli) (provide 'core-cli)
;;; core-cli.el ends here ;;; core-cli.el ends here
+160 -164
View File
@@ -12,8 +12,8 @@ successfully sets indent_style/indent_size.")
(defvar-local doom-large-file-p nil) (defvar-local doom-large-file-p nil)
(put 'doom-large-file-p 'permanent-local t) (put 'doom-large-file-p 'permanent-local t)
(defvar doom-large-file-size-alist '((t . 1.0)) (defvar doom-large-file-size-alist '(("." . 1.0))
"An alist mapping major mode to filesize thresholds. "An alist mapping regexps (like `auto-mode-alist') to filesize thresholds.
If a file is opened and discovered to be larger than the threshold, Doom If a file is opened and discovered to be larger than the threshold, Doom
performs emergency optimizations to prevent Emacs from hanging, crashing or performs emergency optimizations to prevent Emacs from hanging, crashing or
@@ -42,15 +42,17 @@ possible."
(if (setq doom-large-file-p (if (setq doom-large-file-p
(and buffer-file-name (and buffer-file-name
(not doom-large-file-p) (not doom-large-file-p)
(file-readable-p buffer-file-name) (file-exists-p buffer-file-name)
(ignore-errors
(> (nth 7 (file-attributes buffer-file-name)) (> (nth 7 (file-attributes buffer-file-name))
(* 1024 1024 (* 1024 1024
(cdr (or (assq major-mode doom-large-file-size-alist) (assoc-default buffer-file-name doom-large-file-size-alist
(assq 't doom-large-file-size-alist))))))) #'string-match-p))))))
(prog1 (apply orig-fn args) (prog1 (apply orig-fn args)
(if (memq major-mode doom-large-file-excluded-modes) (if (memq major-mode doom-large-file-excluded-modes)
(setq doom-large-file-p nil) (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..."))) (message "Large file detected! Cutting a few corners to improve performance...")))
(apply orig-fn args))) (apply orig-fn args)))
@@ -77,68 +79,94 @@ possible."
t)))))) t))))))
;; Don't autosave files or create lock/history/backup files. We don't want ;; Don't autosave files or create lock/history/backup files. We don't want
;; copies of potentially sensitive material floating around, and we'll rely on ;; copies of potentially sensitive material floating around or polluting our
;; git and our own good fortune instead. Fingers crossed! ;; filesystem. We rely on git and our own good fortune instead. Fingers crossed!
(setq auto-save-default nil (setq auto-save-default nil
create-lockfiles nil create-lockfiles nil
make-backup-files nil make-backup-files nil
;; But have a place to store them in case we do use them... ;; But have a place to store them in case we do use them...
auto-save-list-file-name (concat doom-cache-dir "autosave") ;; auto-save-list-file-name (concat doom-cache-dir "autosave")
backup-directory-alist `(("." . ,(concat doom-cache-dir "backup/")))) auto-save-list-file-prefix (concat doom-cache-dir "autosave/")
auto-save-file-name-transforms `((".*" ,auto-save-list-file-prefix t))
backup-directory-alist `((".*" . ,(concat doom-cache-dir "backup/"))))
(after! tramp
(add-to-list 'backup-directory-alist (cons tramp-file-name-regexp nil)))
(add-hook! 'after-save-hook (add-hook! 'after-save-hook
(defun doom-guess-mode-h () (defun doom-guess-mode-h ()
"Guess mode when saving a file in `fundamental-mode'." "Guess mode when saving a file in `fundamental-mode'."
(and (eq major-mode 'fundamental-mode) (when (eq major-mode 'fundamental-mode)
(buffer-file-name (buffer-base-buffer)) (let ((buffer (or (buffer-base-buffer) (current-buffer))))
(eq (current-buffer) (window-buffer (selected-window))) ; only visible buffers (and (buffer-file-name buffer)
(set-auto-mode)))) (eq buffer (window-buffer (selected-window))) ; only visible buffers
(set-auto-mode))))))
;; ;;
;;; Formatting ;;; Formatting
;; Indentation ;; Favor spaces over tabs. Pls dun h8, but I think spaces (and 4 of them) is a
(setq-default tab-width 4 ;; more consistent default than 8-space tabs. It can be changed on a per-mode
tab-always-indent t ;; basis anyway (and is, where tabs are the canonical style, like go-mode).
indent-tabs-mode nil (setq-default indent-tabs-mode nil
fill-column 80) tab-width 4)
;; Word wrapping ;; Make `tabify' and `untabify' only affect indentation. Not tabs/spaces in the
(setq-default word-wrap t ;; middle of a line.
truncate-lines t (setq tabify-regexp "^\t* [ \t]+")
truncate-partial-width-windows nil)
(setq sentence-end-double-space nil ;; An archaic default in the age of widescreen 4k displays? I disagree. We still
delete-trailing-lines nil ;; frequently split our terminals and editor frames, or have them side-by-side,
require-final-newline t ;; using up more of that newly available horizontal real-estate.
tabify-regexp "^\t* [ \t]+") ; for :retab (setq-default fill-column 80)
;; Favor hard-wrapping in text modes ;; Continue wrapped words at whitespace, rather than in the middle of a word.
(add-hook 'text-mode-hook #'auto-fill-mode) (setq-default word-wrap t)
;; ...but don't do any wrapping by default. It's expensive. Enable
;; `visual-line-mode' if you want soft line-wrapping. `auto-fill-mode' for hard
;; line-wrapping.
(setq-default truncate-lines t)
;; If enabled (and `truncate-lines' was disabled), soft wrapping no longer
;; occurs when that window is less than `truncate-partial-width-windows'
;; characters wide. We don't need this, and it's extra work for Emacs otherwise,
;; so off it goes.
(setq truncate-partial-width-windows nil)
;; This was a widespread practice in the days of typewriters. I actually prefer
;; it when writing prose with monospace fonts, but it is obsolete otherwise.
(setq sentence-end-double-space nil)
;; The POSIX standard defines a line is "a sequence of zero or more non-newline
;; characters followed by a terminating newline", so files should end in a
;; newline. Windows doesn't respect this (because it's Windows), but we should,
;; since programmers' tools tend to be POSIX compliant.
(setq require-final-newline t)
;; Default to soft line-wrapping in text modes. It is more sensibile for text
;; modes, even if hard wrapping is more performant.
(add-hook 'text-mode-hook #'visual-line-mode)
;; ;;
;;; Clipboard / kill-ring ;;; Clipboard / kill-ring
;; Eliminate duplicates in the kill ring. That is, if you kill the same thing ;; Cull duplicates in the kill ring to reduce bloat and make the kill ring
;; twice, you won't have to use M-y twice to get past it to older entries in the ;; easier to peruse (with `counsel-yank-pop' or `helm-show-kill-ring'.
;; kill ring.
(setq kill-do-not-save-duplicates t) (setq kill-do-not-save-duplicates t)
;; ;; Allow UTF or composed text from the clipboard, even in the terminal or on
;; non-X systems (like Windows or macOS), where only `STRING' is used.
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)) (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, ;; 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). ;; pb{copy,paste}, wl-copy, termux-clipboard-get, or getclip (cygwin); depending
;; on what is available.
(unless IS-WINDOWS
(add-hook! 'tty-setup-hook (add-hook! 'tty-setup-hook
(defun doom-init-clipboard-in-tty-emacs-h () (defun doom-init-clipboard-in-tty-emacs-h ()
(and (not (getenv "SSH_CONNECTION")) (and (require 'clipetty nil t)
(require 'xclip nil t) (global-clipetty-mode +1)))))
(xclip-mode +1))))
;; ;;
@@ -146,6 +174,8 @@ possible."
(push '("/LICENSE\\'" . text-mode) auto-mode-alist) (push '("/LICENSE\\'" . text-mode) auto-mode-alist)
(push '("\\.log\\'" . text-mode) auto-mode-alist) (push '("\\.log\\'" . text-mode) auto-mode-alist)
(push '("rc\\'" . conf-mode) auto-mode-alist)
(push '("\\.\\(?:hex\\|nes\\)\\'" . hexl-mode) auto-mode-alist)
;; ;;
@@ -164,13 +194,13 @@ possible."
;; Only prompts for confirmation when buffer is unsaved. ;; Only prompts for confirmation when buffer is unsaved.
revert-without-query (list ".")) revert-without-query (list "."))
;; Instead of using `auto-revert-mode' or `global-auto-revert-mode', we employ ;; Instead of `auto-revert-mode' or `global-auto-revert-mode', we lazily auto
;; lazy auto reverting on `focus-in-hook' and `doom-switch-buffer-hook'. ;; revert; when we save a file or switch buffers/windows (or focus on Emacs).
;; ;;
;; This is because autorevert abuses the heck out of inotify handles which can ;; Autorevert normally abuses the heck out of inotify handles which can grind
;; grind Emacs to a halt if you do expensive IO (outside of Emacs) on the ;; Emacs to a halt if you do expensive IO (outside of Emacs) on the files you
;; files you have open (like compression). We only really need to revert ;; have open (like compression). The only alternative is aggressive polling,
;; changes when we switch to a buffer or when we focus the Emacs frame. ;; which is unreliable and expensive with a lot of buffers open.
(defun doom-auto-revert-buffer-h () (defun doom-auto-revert-buffer-h ()
"Auto revert current buffer, if necessary." "Auto revert current buffer, if necessary."
(unless (or auto-revert-mode (active-minibuffer-window)) (unless (or auto-revert-mode (active-minibuffer-window))
@@ -186,7 +216,7 @@ possible."
(use-package! recentf (use-package! recentf
;; Keep track of recently opened files ;; Keep track of recently opened files
:defer-incrementally easymenu tree-widget timer :defer-incrementally easymenu tree-widget timer
:after-call after-find-file :hook (doom-first-file . recentf-mode)
:commands recentf-open-files :commands recentf-open-files
:config :config
(defun doom--recent-file-truename (file) (defun doom--recent-file-truename (file)
@@ -194,9 +224,11 @@ possible."
(not (file-remote-p file))) (not (file-remote-p file)))
(file-truename file) (file-truename file)
file)) file))
(setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name)) (setq recentf-filename-handlers
'(substring-no-properties ; strip out lingering text properties
(setq recentf-save-file (concat doom-cache-dir "recentf") doom--recent-file-truename ; resolve symlinks of local files
abbreviate-file-name) ; replace $HOME with ~
recentf-save-file (concat doom-cache-dir "recentf")
recentf-auto-cleanup 'never recentf-auto-cleanup 'never
recentf-max-menu-items 0 recentf-max-menu-items 0
recentf-max-saved-items 200) recentf-max-saved-items 200)
@@ -214,22 +246,20 @@ possible."
"Add dired directory to recentf file list." "Add dired directory to recentf file list."
(recentf-add-file default-directory))) (recentf-add-file default-directory)))
(when doom-interactive-mode
(add-hook 'kill-emacs-hook #'recentf-cleanup) (add-hook 'kill-emacs-hook #'recentf-cleanup)
(quiet! (recentf-mode +1)))) (advice-add #'recentf-load-list :around #'doom-shut-up-a))
(use-package! savehist (use-package! savehist
;; persist variables across sessions ;; persist variables across sessions
:defer-incrementally custom :defer-incrementally custom
:after-call post-command-hook :hook (doom-first-input . savehist-mode)
:init
(setq savehist-file (concat doom-cache-dir "savehist"))
:config :config
(setq savehist-file (concat doom-cache-dir "savehist") (setq savehist-save-minibuffer-history t
savehist-save-minibuffer-history t
savehist-autosave-interval nil ; save on kill only savehist-autosave-interval nil ; save on kill only
savehist-additional-variables '(kill-ring search-ring regexp-search-ring)) savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
(savehist-mode +1)
(add-hook! 'kill-emacs-hook (add-hook! 'kill-emacs-hook
(defun doom-unpropertize-kill-ring-h () (defun doom-unpropertize-kill-ring-h ()
"Remove text properties from `kill-ring' for a smaller savehist file." "Remove text properties from `kill-ring' for a smaller savehist file."
@@ -241,31 +271,33 @@ possible."
(use-package! saveplace (use-package! saveplace
;; persistent point location in buffers ;; persistent point location in buffers
:after-call after-find-file dired-initial-position-hook :hook (doom-first-file . save-place-mode)
:config :init
(setq save-place-file (concat doom-cache-dir "saveplace") (setq save-place-file (concat doom-cache-dir "saveplace")
save-place-limit 100) save-place-limit 100)
:config
(defadvice! doom--recenter-on-load-saveplace-a (&rest _) (defadvice! doom--recenter-on-load-saveplace-a (&rest _)
"Recenter on cursor when loading a saved place." "Recenter on cursor when loading a saved place."
:after-while #'save-place-find-file-hook :after-while #'save-place-find-file-hook
(if buffer-file-name (ignore-errors (recenter)))) (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) (defadvice! doom--dont-prettify-saveplace-cache-a (orig-fn)
"`save-place-alist-to-file' uses `pp' to prettify the contents of its cache. "`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 `pp' can be expensive for longer lists, and there's no reason to prettify cache
files, so we replace calls to `pp' with the much faster `prin1'." files, so we replace calls to `pp' with the much faster `prin1'."
:around #'save-place-alist-to-file :around #'save-place-alist-to-file
(cl-letf (((symbol-function #'pp) (letf! ((#'pp #'prin1)) (funcall orig-fn))))
(symbol-function #'prin1)))
(funcall orig-fn)))
(save-place-mode +1))
(use-package! server (use-package! server
:when (display-graphic-p) :when (display-graphic-p)
:after-call pre-command-hook after-find-file focus-out-hook :after-call pre-command-hook after-find-file focus-out-hook
:defer 1
:init :init
(when-let (name (getenv "EMACS_SERVER_NAME")) (when-let (name (getenv "EMACS_SERVER_NAME"))
(setq server-name name)) (setq server-name name))
@@ -278,7 +310,8 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;;; Packages ;;; Packages
(use-package! better-jumper (use-package! better-jumper
:after-call pre-command-hook :hook (doom-first-input . better-jumper-mode)
:hook (better-jumper-post-jump . recenter)
:preface :preface
;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at ;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at
;; startup. This can be removed once gilbertw1/better-jumper#2 is merged. ;; startup. This can be removed once gilbertw1/better-jumper#2 is merged.
@@ -288,17 +321,14 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward) (global-set-key [remap evil-jump-backward] #'better-jumper-jump-backward)
(global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward) (global-set-key [remap xref-pop-marker-stack] #'better-jumper-jump-backward)
:config :config
(better-jumper-mode +1) (defun doom-set-jump-a (orig-fn &rest args)
(add-hook 'better-jumper-post-jump-hook #'recenter)
(defadvice! doom-set-jump-a (orig-fn &rest args)
"Set a jump point and ensure ORIG-FN doesn't set any new jump points." "Set a jump point and ensure ORIG-FN doesn't set any new jump points."
(better-jumper-set-jump (if (markerp (car args)) (car args))) (better-jumper-set-jump (if (markerp (car args)) (car args)))
(let ((evil--jumps-jumping t) (let ((evil--jumps-jumping t)
(better-jumper--jumping t)) (better-jumper--jumping t))
(apply orig-fn args))) (apply orig-fn args)))
(defadvice! doom-set-jump-maybe-a (orig-fn &rest args) (defun doom-set-jump-maybe-a (orig-fn &rest args)
"Set a jump point if ORIG-FN returns non-nil." "Set a jump point if ORIG-FN returns non-nil."
(let ((origin (point-marker)) (let ((origin (point-marker))
(result (result
@@ -329,10 +359,9 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(use-package! dtrt-indent (use-package! dtrt-indent
;; Automatic detection of indent settings ;; Automatic detection of indent settings
:when doom-interactive-mode :when doom-interactive-p
:defer t :hook ((change-major-mode-after-body read-only-mode) . doom-detect-indentation-h)
:init :config
(add-hook! '(change-major-mode-after-body-hook read-only-mode-hook)
(defun doom-detect-indentation-h () (defun doom-detect-indentation-h ()
(unless (or (not after-init-time) (unless (or (not after-init-time)
doom-inhibit-indent-detection doom-inhibit-indent-detection
@@ -340,9 +369,9 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(memq major-mode doom-detect-indentation-excluded-modes) (memq major-mode doom-detect-indentation-excluded-modes)
(member (substring (buffer-name) 0 1) '(" " "*"))) (member (substring (buffer-name) 0 1) '(" " "*")))
;; Don't display messages in the echo area, but still log them ;; Don't display messages in the echo area, but still log them
(let ((inhibit-message (not doom-debug-mode))) (let ((inhibit-message (not doom-debug-p)))
(dtrt-indent-mode +1))))) (dtrt-indent-mode +1))))
:config
;; Enable dtrt-indent even in smie modes so that it can update `tab-width', ;; Enable dtrt-indent even in smie modes so that it can update `tab-width',
;; `standard-indent' and `evil-shift-width' there as well. ;; `standard-indent' and `evil-shift-width' there as well.
(setq dtrt-indent-run-after-smie t) (setq dtrt-indent-run-after-smie t)
@@ -358,18 +387,14 @@ files, so we replace calls to `pp' with the much faster `prin1'."
`nim-mode'. This prevents them from leaving Emacs in a broken state." `nim-mode'. This prevents them from leaving Emacs in a broken state."
:around #'dtrt-indent-mode :around #'dtrt-indent-mode
(let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie)) (let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie))
(cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess)) (letf! ((defun symbol-config--guess (beg end)
(old-smie-config--guess (symbol-function 'symbol-config--guess)) (funcall symbol-config--guess beg (min end 10000)))
((symbol-function 'symbol-config--guess) (defun smie-config-guess ()
(lambda (beg end) (condition-case e (funcall smie-config-guess)
(funcall old-smie-config--guess beg (min end 10000))))
((symbol-function 'smie-config-guess)
(lambda ()
(condition-case e (funcall old-smie-config-guess)
(error (setq dtrt-indent-run-after-smie t) (error (setq dtrt-indent-run-after-smie t)
(message "[WARNING] Indent detection: %s" (message "[WARNING] Indent detection: %s"
(error-message-string e)) (error-message-string e))
(message "")))))) ; warn silently (message ""))))) ; warn silently
(funcall orig-fn arg))))) (funcall orig-fn arg)))))
@@ -377,17 +402,16 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;; a better *help* buffer ;; a better *help* buffer
:commands helpful--read-symbol :commands helpful--read-symbol
:init :init
(define-key! (global-set-key [remap describe-function] #'helpful-callable)
[remap describe-function] #'helpful-callable (global-set-key [remap describe-command] #'helpful-command)
[remap describe-command] #'helpful-command (global-set-key [remap describe-variable] #'helpful-variable)
[remap describe-variable] #'helpful-variable (global-set-key [remap describe-key] #'helpful-key)
[remap describe-key] #'helpful-key (global-set-key [remap describe-symbol] #'helpful-symbol)
[remap describe-symbol] #'doom/describe-symbol)
(defun doom-use-helpful-a (orig-fn &rest args) (defun doom-use-helpful-a (orig-fn &rest args)
"Force ORIG-FN to use helpful instead of the old describe-* commands." "Force ORIG-FN to use helpful instead of the old describe-* commands."
(cl-letf (((symbol-function #'describe-function) #'helpful-function) (letf! ((#'describe-function #'helpful-function)
((symbol-function #'describe-variable) #'helpful-variable)) (#'describe-variable #'helpful-variable))
(apply orig-fn args))) (apply orig-fn args)))
(after! apropos (after! apropos
@@ -411,30 +435,33 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(use-package! smartparens (use-package! smartparens
;; Auto-close delimiters and blocks as you type. It's more powerful than that, ;; Auto-close delimiters and blocks as you type. It's more powerful than that,
;; but that is all Doom uses it for. ;; but that is all Doom uses it for.
:after-call doom-switch-buffer-hook after-find-file :hook (doom-first-buffer . smartparens-global-mode)
:commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string :commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string
:config :config
;; smartparens recognizes `slime-mrepl-mode', but not `sly-mrepl-mode', so...
(add-to-list 'sp-lisp-modes 'sly-mrepl-mode)
;; Load default smartparens rules for various languages ;; Load default smartparens rules for various languages
(require 'smartparens-config) (require 'smartparens-config)
;; Overlays are too distracting and not terribly helpful. show-parens does ;; 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 (setq sp-highlight-pair-overlay nil
sp-highlight-wrap-overlay nil sp-highlight-wrap-overlay nil
sp-highlight-wrap-tag-overlay nil) sp-highlight-wrap-tag-overlay nil)
;; But if someone does want overlays enabled, evil users will be stricken with (with-eval-after-load 'evil
;; an off-by-one issue where smartparens assumes you're outside the pair when ;; But if someone does want overlays enabled, evil users will be stricken
;; you're really at the last character in insert mode. We must correct this ;; with an off-by-one issue where smartparens assumes you're outside the
;; vile injustice. ;; 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) (setq sp-show-pair-from-inside t)
;; ...and stay highlighted until we've truly escaped the pair! ;; ...and stay highlighted until we've truly escaped the pair!
(setq sp-cancel-autoskip-on-backward-movement nil) (setq sp-cancel-autoskip-on-backward-movement nil))
;; The default is 100, because smartparen's scans are relatively expensive ;; 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. ;; better compromise between performance and accuracy.
(setq sp-max-prefix-length 50) (setq sp-max-prefix-length 25)
;; This speeds up smartparens. No pair has any business being longer than 4 ;; No pair has any business being longer than 4 characters; if they must, set
;; characters; if they must, the modes that need it set it buffer-locally. ;; it buffer-locally. It's less work for smartparens.
(setq sp-max-pair-length 4) (setq sp-max-pair-length 4)
;; This isn't always smart enough to determine when we're in a string or not. ;; This isn't always smart enough to determine when we're in a string or not.
;; See https://github.com/Fuco1/smartparens/issues/783. ;; See https://github.com/Fuco1/smartparens/issues/783.
@@ -442,13 +469,14 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;; Silence some harmless but annoying echo-area spam ;; Silence some harmless but annoying echo-area spam
(dolist (key '(:unmatched-expression :no-matching-tag)) (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 (add-hook! 'minibuffer-setup-hook
(defun doom-init-smartparens-in-minibuffer-maybe-h () (defun doom-init-smartparens-in-minibuffer-maybe-h ()
"Enable `smartparens-mode' in the minibuffer, during `eval-expression' or "Enable `smartparens-mode' in the minibuffer, during `eval-expression',
`evil-ex'." `pp-eval-expression' or `evil-ex'."
(when (memq this-command '(eval-expression evil-ex)) (and (memq this-command '(eval-expression pp-eval-expression evil-ex))
smartparens-global-mode
(smartparens-mode)))) (smartparens-mode))))
;; You're likely writing lisp in the minibuffer, therefore, disable these ;; You're likely writing lisp in the minibuffer, therefore, disable these
@@ -467,16 +495,13 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(defun doom-disable-smartparens-mode-maybe-h () (defun doom-disable-smartparens-mode-maybe-h ()
(when smartparens-mode (when smartparens-mode
(setq-local doom-buffer-smartparens-mode t) (setq-local doom-buffer-smartparens-mode t)
(turn-off-smartparens-mode)))) (turn-off-smartparens-mode)))))
(smartparens-global-mode +1))
(use-package! so-long (use-package! so-long
:after-call after-find-file :hook (doom-first-file . global-so-long-mode)
:config :config
(when doom-interactive-mode (setq so-long-threshold 400) ; reduce false positives w/ larger threshold
(global-so-long-mode +1))
;; Don't disable syntax highlighting and line numbers, or make the buffer ;; Don't disable syntax highlighting and line numbers, or make the buffer
;; read-only, in `so-long-minor-mode', so we can have a basic editing ;; read-only, in `so-long-minor-mode', so we can have a basic editing
;; experience in them, at least. It will remain off in `so-long-mode', ;; experience in them, at least. It will remain off in `so-long-mode',
@@ -486,6 +511,8 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(delq! 'buffer-read-only so-long-variable-overrides 'assq) (delq! 'buffer-read-only so-long-variable-overrides 'assq)
;; ...but at least reduce the level of syntax highlighting ;; ...but at least reduce the level of syntax highlighting
(add-to-list 'so-long-variable-overrides '(font-lock-maximum-decoration . 1)) (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 ;; Text files could possibly be too long too
(add-to-list 'so-long-target-modes 'text-mode) (add-to-list 'so-long-target-modes 'text-mode)
;; But disable everything else that may be unnecessary/expensive for large ;; But disable everything else that may be unnecessary/expensive for large
@@ -493,6 +520,7 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(appendq! so-long-minor-modes (appendq! so-long-minor-modes
'(flycheck-mode '(flycheck-mode
flyspell-mode flyspell-mode
spell-fu-mode
eldoc-mode eldoc-mode
smartparens-mode smartparens-mode
highlight-numbers-mode highlight-numbers-mode
@@ -502,60 +530,28 @@ files, so we replace calls to `pp' with the much faster `prin1'."
undo-tree-mode undo-tree-mode
highlight-indent-guides-mode highlight-indent-guides-mode
hl-fill-column-mode)) hl-fill-column-mode))
;; 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.
(defun doom-buffer-has-long-lines-p () (defun doom-buffer-has-long-lines-p ()
(when (bound-and-true-p comment-use-syntax) ;; 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, then false positives are more
;; likely, so up the threshold. More so in text-mode, since long
;; paragraphs are the norm.
(so-long-threshold
(if visual-line-mode
(* so-long-threshold
(if (derived-mode-p 'text-mode)
3
2))
so-long-threshold)))
(so-long-detected-long-line-p))) (so-long-detected-long-line-p)))
(setq so-long-predicate #'doom-buffer-has-long-lines-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
;; 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
undo-limit 800000
undo-strong-limit 12000000
undo-outer-limit 120000000
undo-tree-history-directory-alist
`(("." . ,(concat doom-cache-dir "undo-tree-hist/"))))
;; Compress undo-tree history files with zstd, if available. File size isn't
;; the (only) concern here: the file IO barrier is slow for Emacs to cross;
;; reading a tiny file and piping it in-memory through zstd is *slightly*
;; faster than Emacs reading the entire undo-tree file from the get go (on
;; SSDs). Whether or not that's true in practice, we still enjoy zstd's ~80%
;; file savings (these files add up over time and zstd is so incredibly fast).
(when (executable-find "zstd")
(defadvice! doom--undo-tree-make-history-save-file-name-a (file)
:filter-return #'undo-tree-make-history-save-file-name
(concat file ".zst")))
;; Strip text properties from undo-tree data to stave off bloat. File size
;; isn't the concern here; undo cache files bloat easily, which can cause
;; freezing, crashes, GC-induced stuttering or delays when opening files.
(defadvice! doom--undo-tree-strip-text-properties-a (&rest _)
:before #'undo-list-transfer-to-tree
(dolist (item buffer-undo-list)
(and (consp item)
(stringp (car item))
(setcar item (substring-no-properties (car item))))))
(global-undo-tree-mode +1))
(use-package! ws-butler (use-package! ws-butler
;; a less intrusive `delete-trailing-whitespaces' on save ;; a less intrusive `delete-trailing-whitespaces' on save
:after-call after-find-file :hook (doom-first-buffer . ws-butler-global-mode))
:config
(appendq! ws-butler-global-exempt-modes
'(special-mode comint-mode term-mode eshell-mode))
(ws-butler-global-mode))
(provide 'core-editor) (provide 'core-editor)
;;; core-editor.el ends here ;;; core-editor.el ends here
+11 -20
View File
@@ -26,9 +26,12 @@ and Emacs states, and for non-evil users.")
;; ;;
;;; Keybind settings ;;; Keybind settings
(when IS-MAC (cond (IS-MAC
(setq mac-command-modifier 'super (setq mac-command-modifier 'super
mac-option-modifier 'meta)) mac-option-modifier 'meta))
(IS-WINDOWS
(setq w32-lwindow-modifier 'super
w32-rwindow-modifier 'super)))
;; ;;
@@ -77,7 +80,8 @@ all hooks after it are ignored.")
:init :init
;; Convenience aliases ;; Convenience aliases
(defalias 'define-key! #'general-def) (defalias 'define-key! #'general-def)
(defalias 'unmap! #'general-unbind)) (defalias 'undefine-key! #'general-unbind))
;; HACK `map!' uses this instead of `define-leader-key!' because it consumes ;; HACK `map!' uses this instead of `define-leader-key!' because it consumes
;; 20-30% more startup time, so we reimplement it ourselves. ;; 20-30% more startup time, so we reimplement it ourselves.
@@ -108,7 +112,7 @@ all hooks after it are ignored.")
(general--concat t doom-leader-key ,key) (general--concat t doom-leader-key ,key)
,desc)))))))) ,desc))))))))
(macroexp-progn (macroexp-progn
(cons `(after! which-key ,@(nreverse wkforms)) (append (and wkforms `((after! which-key ,@(nreverse wkforms))))
(nreverse forms))))) (nreverse forms)))))
(defmacro define-leader-key! (&rest args) (defmacro define-leader-key! (&rest args)
@@ -175,8 +179,7 @@ localleader prefix."
;;; Packages ;;; Packages
(use-package! which-key (use-package! which-key
:defer 1 :hook (doom-first-input . which-key-mode)
:after-call pre-command-hook
:init :init
(setq which-key-sort-order #'which-key-prefix-then-key-order (setq which-key-sort-order #'which-key-prefix-then-key-order
which-key-sort-uppercase-first nil which-key-sort-uppercase-first nil
@@ -191,9 +194,7 @@ localleader prefix."
(setq-hook! 'which-key-init-buffer-hook line-spacing 3) (setq-hook! 'which-key-init-buffer-hook line-spacing 3)
(which-key-add-key-based-replacements doom-leader-key "<leader>") (which-key-add-key-based-replacements doom-leader-key "<leader>")
(which-key-add-key-based-replacements doom-localleader-key "<localleader>") (which-key-add-key-based-replacements doom-localleader-key "<localleader>"))
(which-key-mode +1))
;; ;;
@@ -215,21 +216,11 @@ localleader prefix."
For example, :nvi will map to (list 'normal 'visual 'insert). See For example, :nvi will map to (list 'normal 'visual 'insert). See
`doom-evil-state-alist' to customize this." `doom-evil-state-alist' to customize this."
(cl-loop for l across (substring (symbol-name keyword) 1) (cl-loop for l across (doom-keyword-name keyword)
if (cdr (assq l doom-evil-state-alist)) collect it if (assq l doom-evil-state-alist) collect (cdr it)
else do (error "not a valid state: %s" l))) else do (error "not a valid state: %s" l)))
;; Register keywords for proper indentation (see `map!')
(put :after 'lisp-indent-function 'defun)
(put :desc 'lisp-indent-function 'defun)
(put :leader 'lisp-indent-function 'defun)
(put :localleader 'lisp-indent-function 'defun)
(put :map 'lisp-indent-function 'defun)
(put :mode 'lisp-indent-function 'defun)
(put :prefix 'lisp-indent-function 'defun)
(put :prefix-map 'lisp-indent-function 'defun)
;; specials ;; specials
(defvar doom--map-forms nil) (defvar doom--map-forms nil)
(defvar doom--map-fn nil) (defvar doom--map-fn nil)
+400 -160
View File
@@ -1,8 +1,5 @@
;;; core-lib.el -*- lexical-binding: t; -*- ;;; core-lib.el -*- lexical-binding: t; -*-
(require 'cl-lib)
(require 'subr-x)
;; ;;
;;; Helpers ;;; Helpers
@@ -69,10 +66,10 @@ list is returned as-is."
(substring (symbol-name keyword) 1)) (substring (symbol-name keyword) 1))
(defmacro doom-log (format-string &rest args) (defmacro doom-log (format-string &rest args)
"Log to *Messages* if `doom-debug-mode' is on. "Log to *Messages* if `doom-debug-p' is on.
Does not interrupt the minibuffer if it is in use, but still logs to *Messages*. Does not interrupt the minibuffer if it is in use, but still logs to *Messages*.
Accepts the same arguments as `message'." Accepts the same arguments as `message'."
`(when doom-debug-mode `(when doom-debug-p
(let ((inhibit-message (active-minibuffer-window))) (let ((inhibit-message (active-minibuffer-window)))
(message (message
,(concat (propertize "DOOM " 'face 'font-lock-comment-face) ,(concat (propertize "DOOM " 'face 'font-lock-comment-face)
@@ -85,6 +82,53 @@ Accepts the same arguments as `message'."
format-string) format-string)
,@args)))) ,@args))))
(defun doom-try-run-hook (hook)
"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
(funcall hook)
((debug error)
(signal 'doom-hook-error (list hook e))))
;; return nil so `run-hook-wrapped' won't short circuit
nil)
(defun doom-load-envvars-file (file &optional noerror)
"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 (null (file-exists-p file))
(unless noerror
(signal 'file-error (list "No envvar file exists" file)))
(when-let
(env
(with-temp-buffer
(save-excursion
(setq-local coding-system-for-read 'utf-8)
(insert "\0\n") ; to prevent off-by-one
(insert-file-contents file))
(save-match-data
(when (re-search-forward "\0\n *\\([^#= \n]*\\)=" nil t)
(setq
env (split-string (buffer-substring (match-beginning 1) (point-max))
"\0\n"
'omit-nulls))))))
(setq-default
process-environment
(append (nreverse env)
(default-value 'process-environment))
exec-path
(append (split-string (getenv "PATH") path-separator t)
(list exec-directory))
shell-file-name
(or (getenv "SHELL")
(default-value 'shell-file-name)))
env)))
;;
;;; Functional library
(defalias 'doom-partial #'apply-partially) (defalias 'doom-partial #'apply-partially)
(defun doom-rpartial (fn &rest args) (defun doom-rpartial (fn &rest args)
@@ -93,6 +137,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 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 function which does the same as FUN, except that the last N arguments are fixed
at the values with which this function was called." at the values with which this function was called."
(declare (side-effect-free t))
(lambda (&rest pre-args) (lambda (&rest pre-args)
(apply fn (append pre-args args)))) (apply fn (append pre-args args))))
@@ -100,23 +145,10 @@ at the values with which this function was called."
;; ;;
;;; Sugars ;;; Sugars
(defmacro λ! (&rest body) (defun dir! ()
"Expands to (lambda () (interactive) ,@body). "Returns the directory of the emacs lisp file this macro is called from."
A factory for quickly producing interaction commands, particularly for keybinds (when-let (path (file!))
or aliases." (directory-file-name (file-name-directory path))))
(declare (doc-string 1))
`(lambda () (interactive) ,@body))
(defalias 'lambda! 'λ!)
(defun λ!! (command &optional arg)
"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))
(lambda () (interactive)
(let ((current-prefix-arg arg))
(call-interactively command))))
(defalias 'lambda!! 'λ!!)
(defun file! () (defun file! ()
"Return the emacs lisp file this macro is called from." "Return the emacs lisp file this macro is called from."
@@ -127,18 +159,152 @@ aliases."
(buffer-file-name) (buffer-file-name)
((error "Cannot get this file-path")))) ((error "Cannot get this file-path"))))
(defun dir! () (defmacro letenv! (envvars &rest body)
"Returns the directory of the emacs lisp file this macro is called from." "Lexically bind ENVVARS in BODY, like `let' but for `process-environment'."
(when-let (path (file!)) (declare (indent 1))
(directory-file-name (file-name-directory path)))) `(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 letf! (bindings &rest body)
"Temporarily rebind function and macros in BODY.
BINDINGS is either a) a list of, or a single, `defun' or `defmacro'-ish form, or
b) a list of (PLACE VALUE) bindings as `cl-letf*' would accept.
TYPE is either `defun' or `defmacro'. NAME is the name of the function. If an
original definition for NAME exists, it can be accessed as a lexical variable by
the same name, for use with `funcall' or `apply'. ARGLIST and BODY are as in
`defun'.
\(fn ((TYPE NAME ARGLIST &rest BODY) ...) BODY...)"
(declare (indent defun))
(setq body (macroexp-progn body))
(when (memq (car bindings) '(defun defmacro))
(setq bindings (list bindings)))
(dolist (binding (reverse bindings) (macroexpand body))
(let ((type (car binding))
(rest (cdr binding)))
(setq
body (pcase type
(`defmacro `(cl-macrolet ((,@rest)) ,body))
(`defun `(cl-letf* ((,(car rest) (symbol-function #',(car rest)))
((symbol-function #',(car rest))
(lambda ,(cadr rest) ,@(cddr rest))))
(ignore ,(car rest))
,body))
(_
(when (eq (car-safe type) 'function)
(setq type (list 'symbol-function type)))
(list 'cl-letf (list (cons type rest)) body)))))))
(defmacro quiet! (&rest forms)
"Run FORMS without generating any output.
This silences calls to `message', `load', `write-region' and anything that
writes to `standard-output'."
`(if doom-debug-p
(progn ,@forms)
,(if doom-interactive-p
`(let ((inhibit-message t)
(save-silently t))
(prog1 ,@forms (message "")))
`(letf! ((standard-output (lambda (&rest _)))
(defun message (&rest _))
(defun load (file &optional noerror nomessage nosuffix must-suffix)
(funcall load file noerror t nosuffix must-suffix))
(defun write-region (start end filename &optional append visit lockname mustbenew)
(unless visit (setq visit 'no-message))
(funcall write-region start end filename append visit lockname mustbenew)))
,@forms))))
(defmacro if! (cond then &rest body)
"Expands to THEN if COND is non-nil, to BODY otherwise.
COND is checked at compile/expansion time, allowing BODY to be omitted
entirely when the elisp is byte-compiled. Use this for forms that contain
expensive macros that could safely be removed at compile time."
(declare (indent 2))
(if (eval cond)
then
(macroexp-progn body)))
(defmacro when! (cond &rest body)
"Expands to BODY if CONDITION is non-nil at compile/expansion time.
See `if!' for details on this macro's purpose."
(declare (indent 1))
(when (eval cond)
(macroexp-progn body)))
;;; Closure factories
(defmacro fn! (arglist &rest body)
"Expands to (cl-function (lambda ARGLIST BODY...))"
(declare (indent defun) (doc-string 1) (pure t) (side-effect-free t))
`(cl-function (lambda ,arglist ,@body)))
(defmacro cmd! (&rest body)
"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 (&rest _) (interactive) ,@body))
(defmacro cmd!! (command &rest args)
"Expands to a closure that interactively calls COMMAND with ARGS.
A factory for quickly producing interactive, prefixed commands for keybinds or
aliases."
(declare (doc-string 1) (pure t) (side-effect-free t))
`(lambda (&rest _) (interactive)
(funcall-interactively ,command ,@args)))
(defmacro cmds! (&rest branches)
"Expands to a `menu-item' dispatcher for keybinds."
(declare (doc-string 1))
(let ((docstring (if (stringp (car branches)) (pop branches) ""))
fallback)
(when (cl-oddp (length branches))
(setq fallback (car (last branches))
branches (butlast branches)))
`(general-predicate-dispatch ,fallback
:docstring ,docstring
,@branches)))
;; For backwards compatibility
(defalias 'λ! 'cmd!)
(defalias 'λ!! 'cmd!!)
;; DEPRECATED These have been superseded by `cmd!' and `cmd!!'
(define-obsolete-function-alias 'lambda! 'cmd! "3.0.0")
(define-obsolete-function-alias 'lambda!! 'cmd!! "3.0.0")
;;; Mutation
(defmacro appendq! (sym &rest lists)
"Append LISTS to SYM in place."
`(setq ,sym (append ,sym ,@lists)))
(defmacro setq! (&rest settings) (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'.
This can be used as a drop-in replacement for `setq'. Particularly when you know
a variable has a custom setter (a :set property in its `defcustom' declaration).
This triggers setters. `setq' does not."
(macroexp-progn (macroexp-progn
(cl-loop for (var val) on settings by 'cddr (cl-loop for (var val) on settings by 'cddr
collect `(funcall (or (get ',var 'custom-set) #'set) collect `(funcall (or (get ',var 'custom-set) #'set)
',var ,val)))) ',var ,val))))
(defmacro delq! (elt list &optional fetcher)
"`delq' ELT from LIST in-place.
If FETCHER is a function, ELT is used as the key in LIST (an alist)."
`(setq ,list
(delq ,(if fetcher
`(funcall ,fetcher ,elt ,list)
elt)
,list)))
(defmacro pushnew! (place &rest values) (defmacro pushnew! (place &rest values)
"Push VALUES sequentially into PLACE, if they aren't already present. "Push VALUES sequentially into PLACE, if they aren't already present.
This is a variadic `cl-pushnew'." This is a variadic `cl-pushnew'."
@@ -150,31 +316,8 @@ This is a variadic `cl-pushnew'."
"Prepend LISTS to SYM in place." "Prepend LISTS to SYM in place."
`(setq ,sym (append ,@lists ,sym))) `(setq ,sym (append ,@lists ,sym)))
(defmacro appendq! (sym &rest lists)
"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.
If FETCHER is a function, ELT is used as the key in LIST (an alist)."
`(setq ,list
(delq ,(if fetcher
`(funcall ,fetcher ,elt ,list)
elt)
,list)))
(defmacro letenv! (envvars &rest body)
"Lexically bind ENVVARS in BODY, like `let' but for `process-environment'."
`(let ((process-environment (copy-sequence process-environment)))
(dolist (var ',envvars)
(setenv (car var) (cadr var)))
,@body))
;;; Loading
(defmacro add-load-path! (&rest dirs) (defmacro add-load-path! (&rest dirs)
"Add DIRS to `load-path', relative to the current file. "Add DIRS to `load-path', relative to the current file.
The current file is the file from which `add-to-load-path!' is used." The current file is the file from which `add-to-load-path!' is used."
@@ -183,6 +326,136 @@ The current file is the file from which `add-to-load-path!' is used."
(dolist (dir (list ,@dirs)) (dolist (dir (list ,@dirs))
(cl-pushnew (expand-file-name dir) load-path)))) (cl-pushnew (expand-file-name dir) load-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))))))
(defun doom--handle-load-error (e target path)
(let* ((source (file-name-sans-extension target))
(err (cond ((not (featurep 'core))
(cons 'error (file-name-directory path)))
((file-in-directory-p source doom-core-dir)
(cons 'doom-error doom-core-dir))
((file-in-directory-p source doom-private-dir)
(cons 'doom-private-error doom-private-dir))
((cons 'doom-module-error doom-emacs-dir)))))
(signal (car err)
(list (file-relative-name
(concat source ".el")
(cdr err))
e))))
(defmacro load! (filename &optional path noerror)
"Load a file relative to the current executing file (`load-file-name').
FILENAME is either a file path string or a form that should evaluate to such a
string at run time. PATH is where to look for the file (a string representing a
directory path). If omitted, the lookup is relative to either `load-file-name',
`byte-compile-current-file' or `buffer-file-name' (checked in that order).
If NOERROR is non-nil, don't throw an error if the file doesn't exist."
(let* ((path (or path
(dir!)
(error "Could not detect path to look for '%s' in"
filename)))
(file (if path
`(expand-file-name ,filename ,path)
filename)))
`(condition-case-unless-debug e
(let (file-name-handler-alist)
(load ,file ,noerror 'nomessage))
(doom-error (signal (car e) (cdr e)))
(error (doom--handle-load-error e ,file ,path)))))
(defmacro defer-until! (condition &rest body)
"Run BODY when CONDITION is true (checks on `after-load-functions'). Meant to
serve as a predicated alternative to `after!'."
(declare (indent defun) (debug t))
`(if ,condition
(progn ,@body)
,(let ((fn (intern (format "doom--delay-form-%s-h" (sxhash (cons condition body))))))
`(progn
(fset ',fn (lambda (&rest args)
(when ,(or condition t)
(remove-hook 'after-load-functions #',fn)
(unintern ',fn nil)
(ignore args)
,@body)))
(put ',fn 'permanent-local-hook t)
(add-hook 'after-load-functions #',fn)))))
(defmacro defer-feature! (feature &rest fns)
"Pretend FEATURE hasn't been loaded yet, until FEATURE-hook or FN runs.
Some packages (like `elisp-mode' and `lisp-mode') are loaded immediately at
startup, which will prematurely trigger `after!' (and `with-eval-after-load')
blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet,
then wait until FEATURE-hook (or MODE-hook, if FN is provided) is triggered to
reverse this and trigger `after!' blocks at a more reasonable time."
(let ((advice-fn (intern (format "doom--defer-feature-%s-a" feature))))
`(progn
(delq! ',feature features)
(defadvice! ,advice-fn (&rest _)
:before ',fns
;; Some plugins (like yasnippet) will invoke a fn early to parse
;; code, which would prematurely trigger this. In those cases, well
;; behaved plugins will use `delay-mode-hooks', which we can check for:
(unless delay-mode-hooks
;; ...Otherwise, announce to the world this package has been loaded,
;; so `after!' handlers can react.
(provide ',feature)
(dolist (fn ',fns)
(advice-remove fn #',advice-fn)))))))
;;; Hooks
(defvar doom--transient-counter 0)
(defmacro add-transient-hook! (hook-or-function &rest forms) (defmacro add-transient-hook! (hook-or-function &rest forms)
"Attaches a self-removing function to HOOK-OR-FUNCTION. "Attaches a self-removing function to HOOK-OR-FUNCTION.
@@ -193,9 +466,12 @@ HOOK-OR-FUNCTION can be a quoted hook or a sharp-quoted function (which will be
advised)." advised)."
(declare (indent 1)) (declare (indent 1))
(let ((append (if (eq (car forms) :after) (pop forms))) (let ((append (if (eq (car forms) :after) (pop forms)))
(fn (intern (format "doom--transient-%s-h" (sxhash hook-or-function))))) ;; Avoid `make-symbol' and `gensym' here because an interned symbol is
;; easier to debug in backtraces (and is visible to `describe-function')
(fn (intern (format "doom--transient-%d-h" (cl-incf doom--transient-counter)))))
`(let ((sym ,hook-or-function)) `(let ((sym ,hook-or-function))
(defun ,fn (&rest _) (defun ,fn (&rest _)
,(format "Transient hook for %S" (doom-unquote hook-or-function))
,@forms ,@forms
(let ((sym ,hook-or-function)) (let ((sym ,hook-or-function))
(cond ((functionp sym) (advice-remove sym #',fn)) (cond ((functionp sym) (advice-remove sym #',fn))
@@ -207,20 +483,29 @@ advised)."
(put ',fn 'permanent-local-hook t) (put ',fn 'permanent-local-hook t)
(add-hook sym #',fn ,append)))))) (add-hook sym #',fn ,append))))))
(defmacro add-hook-trigger! (hook-var &rest targets)
"TODO"
`(let ((fn (intern (format "%s-h" ,hook-var))))
(fset fn (lambda (&rest _) (run-hooks ,hook-var) (set ,hook-var nil)))
(put ,hook-var 'permanent-local t)
(dolist (on (list ,@targets))
(if (functionp on)
(advice-add on :before fn)
(add-hook on fn)))))
(defmacro add-hook! (hooks &rest rest) (defmacro add-hook! (hooks &rest rest)
"A convenience macro for adding N functions to M hooks. "A convenience macro for adding N functions to M hooks.
If N and M = 1, there's no benefit to using this macro over `add-hook'.
This macro accepts, in order: 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), 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 3. The function(s) to be added: this can be one function, a quoted list
modes, a quoted hook variable or a quoted list of hook variables. If thereof, a list of `defun's, or body forms (implicitly wrapped in a
unquoted, '-hook' will be appended to each symbol. lambda).
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).
\(fn HOOKS [:append :local] FUNCTIONS)" \(fn HOOKS [:append :local] FUNCTIONS)"
(declare (indent (lambda (indent-point state) (declare (indent (lambda (indent-point state)
@@ -301,106 +586,8 @@ If N and M = 1, there's no benefit to using this macro over `remove-hook'.
in (doom--setq-hook-fns hooks vars 'singles) in (doom--setq-hook-fns hooks vars 'singles)
collect `(remove-hook ',hook #',fn)))) collect `(remove-hook ',hook #',fn))))
(defmacro load! (filename &optional path noerror)
"Load a file relative to the current executing file (`load-file-name').
FILENAME is either a file path string or a form that should evaluate to such a
string at run time. PATH is where to look for the file (a string representing a
directory path). If omitted, the lookup is relative to either `load-file-name',
`byte-compile-current-file' or `buffer-file-name' (checked in that order).
If NOERROR is non-nil, don't throw an error if the file doesn't exist."
(let* ((path (or path
(dir!)
(error "Could not detect path to look for '%s' in"
filename)))
(file (if path
`(expand-file-name ,filename ,path)
filename)))
`(condition-case-unless-debug e
(let (file-name-handler-alist)
(load ,file ,noerror 'nomessage))
(doom-error (signal (car e) (cdr e)))
(error
(let* ((source (file-name-sans-extension ,file))
(err (cond ((not (featurep 'core))
(cons 'error (file-name-directory path)))
((file-in-directory-p source doom-core-dir)
(cons 'doom-error doom-core-dir))
((file-in-directory-p source doom-private-dir)
(cons 'doom-private-error doom-private-dir))
((cons 'doom-module-error doom-emacs-dir)))))
(signal (car err)
(list (file-relative-name
(concat source ".el")
(cdr err))
e)))))))
(defmacro defer-until! (condition &rest body)
"Run BODY when CONDITION is true (checks on `after-load-functions'). Meant to
serve as a predicated alternative to `after!'."
(declare (indent defun) (debug t))
`(if ,condition
(progn ,@body)
,(let ((fn (intern (format "doom--delay-form-%s-h" (sxhash (cons condition body))))))
`(progn
(fset ',fn (lambda (&rest args)
(when ,(or condition t)
(remove-hook 'after-load-functions #',fn)
(unintern ',fn nil)
(ignore args)
,@body)))
(put ',fn 'permanent-local-hook t)
(add-hook 'after-load-functions #',fn)))))
(defmacro defer-feature! (feature &optional fn)
"Pretend FEATURE hasn't been loaded yet, until FEATURE-hook or FN runs.
Some packages (like `elisp-mode' and `lisp-mode') are loaded immediately at
startup, which will prematurely trigger `after!' (and `with-eval-after-load')
blocks. To get around this we make Emacs believe FEATURE hasn't been loaded yet,
then wait until FEATURE-hook (or MODE-hook, if FN is provided) is triggered to
reverse this and trigger `after!' blocks at a more reasonable time."
(let ((advice-fn (intern (format "doom--defer-feature-%s-a" feature)))
(fn (or fn feature)))
`(progn
(setq features (delq ',feature features))
(advice-add #',fn :before #',advice-fn)
(defun ,advice-fn (&rest _)
;; Some plugins (like yasnippet) will invoke a fn early to parse
;; code, which would prematurely trigger this. In those cases, well
;; behaved plugins will use `delay-mode-hooks', which we can check for:
(when (and ,(intern (format "%s-hook" fn))
(not delay-mode-hooks))
;; ...Otherwise, announce to the world this package has been loaded,
;; so `after!' handlers can react.
(provide ',feature)
(advice-remove #',fn #',advice-fn))))))
(defmacro quiet! (&rest forms)
"Run FORMS without generating any output.
This silences calls to `message', `load-file', `write-region' and anything that
writes to `standard-output'."
`(cond (doom-debug-mode ,@forms)
((not doom-interactive-mode)
(let ((old-fn (symbol-function 'write-region)))
(cl-letf ((standard-output (lambda (&rest _)))
((symbol-function 'load-file) (lambda (file) (load file nil t)))
((symbol-function 'message) (lambda (&rest _)))
((symbol-function 'write-region)
(lambda (start end filename &optional append visit lockname mustbenew)
(unless visit (setq visit 'no-message))
(funcall old-fn start end filename append visit lockname mustbenew))))
,@forms)))
((let ((inhibit-message t)
(save-silently t))
(prog1 ,@forms (message ""))))))
;;
;;; Definers ;;; Definers
(defmacro defadvice! (symbol arglist &optional docstring &rest body) (defmacro defadvice! (symbol arglist &optional docstring &rest body)
"Define an advice called SYMBOL and add it to PLACES. "Define an advice called SYMBOL and add it to PLACES.
@@ -419,10 +606,63 @@ DOCSTRING and BODY are as in `defun'.
where-alist)) where-alist))
`(progn `(progn
(defun ,symbol ,arglist ,docstring ,@body) (defun ,symbol ,arglist ,docstring ,@body)
,(when where-alist (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 (targets (list ,@(nreverse where-alist)))
(dolist (target (cdr targets)) (dolist (target (cdr targets))
(advice-add target (car targets) #',symbol))))))) (advice-remove target #',symbol)))))
;;
;;; Backports
(when! (not EMACS27+)
;; DEPRECATED Backported from Emacs 27
(defmacro setq-local (&rest pairs)
"Make variables in PAIRS buffer-local and assign them the corresponding values.
PAIRS is a list of variable/value pairs. For each variable, make
it buffer-local and assign it the corresponding value. The
variables are literal symbols and should not be quoted.
The second VALUE is not computed until after the first VARIABLE
is set, and so on; each VALUE can use the new value of variables
set earlier in the setq-local. The return value of the
setq-local form is the value of the last VALUE.
\(fn [VARIABLE VALUE]...)"
(declare (debug setq))
(unless (zerop (mod (length pairs) 2))
(error "PAIRS must have an even number of variable/value members"))
(let ((expr nil))
(while pairs
(unless (symbolp (car pairs))
(error "Attempting to set a non-symbol: %s" (car pairs)))
;; Can't use backquote here, it's too early in the bootstrap.
(setq expr
(cons
(list 'set
(list 'make-local-variable (list 'quote (car pairs)))
(car (cdr pairs)))
expr))
(setq pairs (cdr (cdr pairs))))
(macroexp-progn (nreverse expr)))))
(provide 'core-lib) (provide 'core-lib)
;;; core-lib.el ends here ;;; core-lib.el ends here
+171 -212
View File
@@ -3,7 +3,7 @@
(defvar doom-init-modules-p nil (defvar doom-init-modules-p nil
"Non-nil if `doom-initialize-modules' has run.") "Non-nil if `doom-initialize-modules' has run.")
(defvar doom-modules () (defvar doom-modules (make-hash-table :test 'equal)
"A hash table of enabled modules. Set by `doom-initialize-modules'.") "A hash table of enabled modules. Set by `doom-initialize-modules'.")
(defvar doom-modules-dirs (defvar doom-modules-dirs
@@ -11,10 +11,23 @@
doom-modules-dir) doom-modules-dir)
"A list of module root directories. Order determines priority.") "A list of module root directories. Order determines priority.")
(defvar doom-module-init-file "init"
"The basename of init files for modules.
Init files are loaded early, just after Doom core, and before modules' config
files. They are always loaded, even in non-interactive sessions, and before
`doom-before-init-modules-hook'. Related to `doom-module-config-file'.")
(defvar doom-module-config-file "config"
"The basename of config files for modules.
Config files are loaded later, and almost always in interactive sessions. These
run before `doom-init-modules-hook'. Relevant to `doom-module-init-file'.")
(defconst doom-obsolete-modules (defconst doom-obsolete-modules
'((:feature (version-control (:emacs vc) (:ui vc-gutter)) '((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell)) (spellcheck (:checkers spell))
(syntax-checker (:tools flycheck)) (syntax-checker (:checkers syntax))
(evil (:editor evil)) (evil (:editor evil))
(snippets (:editor snippets)) (snippets (:editor snippets))
(file-templates (:editor file-templates)) (file-templates (:editor file-templates))
@@ -24,7 +37,9 @@
(debugger (:tools debugger))) (debugger (:tools debugger)))
(:tools (rotate-text (:editor rotate-text)) (:tools (rotate-text (:editor rotate-text))
(vterm (:term vterm)) (vterm (:term vterm))
(password-store (:tools pass))) (password-store (:tools pass))
(flycheck (:checkers syntax))
(flyspell (:checkers spell)))
(:emacs (electric-indent (:emacs electric)) (:emacs (electric-indent (:emacs electric))
(hideshow (:editor fold)) (hideshow (:editor fold))
(eshell (:term eshell)) (eshell (:term eshell))
@@ -34,13 +49,14 @@
(evil-goggles (:ui ophints)) (evil-goggles (:ui ophints))
(tabbar (:ui tabs))) (tabbar (:ui tabs)))
(:app (email (:email mu4e)) (:app (email (:email mu4e))
(notmuch (:email notmuch)))) (notmuch (:email notmuch)))
(:lang (perl (:lang raku))))
"A tree alist that maps deprecated modules to their replacement(s). "A tree alist that maps deprecated modules to their replacement(s).
Each entry is a three-level tree. For example: Each entry is a three-level tree. For example:
(:feature (version-control (:emacs vc) (:ui vc-gutter)) (:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell)) (spellcheck (:checkers spell))
(syntax-checker (:tools flycheck))) (syntax-checker (:tools flycheck)))
This marks :feature version-control, :feature spellcheck and :feature This marks :feature version-control, :feature spellcheck and :feature
@@ -48,7 +64,7 @@ syntax-checker modules obsolete. e.g. If :feature version-control is found in
your `doom!' block, a warning is emitted before replacing it with :emacs vc and your `doom!' block, a warning is emitted before replacing it with :emacs vc and
:ui vc-gutter.") :ui vc-gutter.")
(defvar doom-inhibit-module-warnings doom-interactive-mode (defvar doom-inhibit-module-warnings doom-interactive-p
"If non-nil, don't emit deprecated or missing module warnings at startup.") "If non-nil, don't emit deprecated or missing module warnings at startup.")
;;; Custom hooks ;;; Custom hooks
@@ -69,28 +85,39 @@ before the user's private module.")
;; ;;
;;; Bootstrap API ;;; Bootstrap API
(defun doom-initialize-modules (&optional force-p) (defun doom-initialize-core-modules ()
"Load Doom's core files for an interactive session."
(require 'core-keybinds)
(require 'core-ui)
(require 'core-projects)
(require 'core-editor))
(defun doom-module-loader (file)
"Return a closure that loads FILE from a module.
This closure takes two arguments: a cons cell containing (CATEGORY . MODULE)
symbols, and that module's plist."
(declare (pure t) (side-effect-free t))
(lambda (module plist)
(let ((doom--current-module module)
(doom--current-flags (plist-get plist :flags)))
(load! file (plist-get plist :path) t))))
(defun doom-initialize-modules (&optional force-p no-config-p)
"Loads the init.el in `doom-private-dir' and sets up hooks for a healthy "Loads the init.el in `doom-private-dir' and sets up hooks for a healthy
session of Dooming. Will noop if used more than once, unless FORCE-P is session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil." non-nil."
(when (or force-p (not doom-init-modules-p)) (when (or force-p (not doom-init-modules-p))
(setq doom-init-modules-p t (setq doom-init-modules-p t)
doom-modules nil) (unless no-config-p
(when (load! "init" doom-private-dir t) (doom-log "Initializing core modules")
(when doom-modules (doom-initialize-core-modules))
(maphash (lambda (key plist) (when-let (init-p (load! doom-module-init-file doom-private-dir t))
(let ((doom--current-module key) (doom-log "Initializing user config")
(doom--current-flags (plist-get plist :flags))) (maphash (doom-module-loader doom-module-init-file) doom-modules)
(load! "init" (plist-get plist :path) t)))
doom-modules))
(run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook) (run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
(when doom-interactive-mode (unless no-config-p
(when doom-modules (maphash (doom-module-loader doom-module-config-file) doom-modules)
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "config" (plist-get plist :path) t)))
doom-modules))
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook) (run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(load! "config" doom-private-dir t))))) (load! "config" doom-private-dir t)))))
@@ -101,11 +128,10 @@ non-nil."
(defun doom-module-p (category module &optional flag) (defun doom-module-p (category module &optional flag)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')." "Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(let ((plist (gethash (cons category module) doom-modules))) (when-let (plist (gethash (cons category module) doom-modules))
(and plist
(or (null flag) (or (null flag)
(memq flag (plist-get plist :flags))) (and (memq flag (plist-get plist :flags))
t))) t))))
(defun doom-module-get (category module &optional property) (defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set." "Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
@@ -120,15 +146,17 @@ non-nil."
of PROPERTY and VALUEs. of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])" \(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])"
(puthash (cons category module)
(if-let (old-plist (doom-module-get category module)) (if-let (old-plist (doom-module-get category module))
(progn (if (null plist)
(when plist old-plist
(when (cl-oddp (length plist)) (when (cl-oddp (length plist))
(signal 'wrong-number-of-arguments (list (length plist)))) (signal 'wrong-number-of-arguments (list (length plist))))
(while plist (while plist
(plist-put old-plist (pop plist) (pop plist)))) (plist-put old-plist (pop plist) (pop plist)))
(puthash (cons category module) old-plist doom-modules)) old-plist)
(puthash (cons category module) plist doom-modules))) plist)
doom-modules))
(defun doom-module-set (category module &rest plist) (defun doom-module-set (category module &rest plist)
"Enables a module by adding it to `doom-modules'. "Enables a module by adding it to `doom-modules'.
@@ -140,10 +168,8 @@ following properties:
:path [STRING] path to category root directory :path [STRING] path to category root directory
Example: Example:
(doom-module-set :lang 'haskell :flags '(+intero))" (doom-module-set :lang 'haskell :flags '(+dante))"
(puthash (cons category module) (puthash (cons category module) plist doom-modules))
plist
doom-modules))
(defun doom-module-path (category module &optional file) (defun doom-module-path (category module &optional file)
"Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and "Like `expand-file-name', but expands FILE relative to CATEGORY (keywordp) and
@@ -211,45 +237,108 @@ those directories. The first returned path is always `doom-private-dir'."
(declare (pure t) (side-effect-free t)) (declare (pure t) (side-effect-free t))
(append (list doom-private-dir) (append (list doom-private-dir)
(if module-dirs (if module-dirs
(mapcar (lambda (m) (doom-module-locate-path (car m) (cdr m)))
(delete-dups
(doom-files-in (if (listp module-dirs) (doom-files-in (if (listp module-dirs)
module-dirs module-dirs
doom-modules-dirs) doom-modules-dirs)
:map #'doom-module-from-path
:type 'dirs :type 'dirs
:mindepth 1 :mindepth 1
:depth 1) :depth 1)))
(cl-loop for plist being the hash-values of (doom-modules) (cl-loop for plist being the hash-values of doom-modules
collect (plist-get plist :path))) collect (plist-get plist :path)))
nil)) nil))
(defun doom-modules (&optional refresh-p) (defun doom-module-mplist-map (fn mplist)
"Apply FN to each module in MPLIST."
(let ((mplist (copy-sequence mplist))
(inhibit-message doom-inhibit-module-warnings)
obsolete
results
category m)
(while mplist
(setq m (pop mplist))
(cond ((keywordp m)
(setq category m
obsolete (assq m doom-obsolete-modules)))
((null category)
(error "No module category specified for %s" m))
((and (listp m) (keywordp (car m)))
(pcase (car m)
(:cond
(cl-loop for (cond . mods) in (cdr m)
if (eval cond t)
return (prependq! mplist mods)))
(:if (if (eval (cadr m) t)
(push (caddr m) mplist)
(prependq! mplist (cdddr m))))
(test (if (or (eval (cadr m) t)
(eq test :unless))
(prependq! mplist (cddr m))))))
((catch 'doom-modules
(let* ((module (if (listp m) (car m) m))
(flags (if (listp m) (cdr m))))
(when-let (new (assq module obsolete))
(let ((newkeys (cdr new)))
(if (null newkeys)
(message "WARNING %s module was removed" key)
(if (cdr newkeys)
(message "WARNING %s module was removed and split into the %s modules"
(list category module) (mapconcat #'prin1-to-string newkeys ", "))
(message "WARNING %s module was moved to %s"
(list category module) (car newkeys)))
(push category mplist)
(dolist (key newkeys)
(push (if flags
(nconc (cdr key) flags)
(cdr key))
mplist)
(push (car key) mplist))
(throw 'doom-modules t))))
(push (funcall fn category module
:flags (if (listp m) (cdr m))
:path (doom-module-locate-path category module))
results))))))
(unless doom-interactive-p
(setq doom-inhibit-module-warnings t))
(nreverse results)))
(defun doom-module-list (&optional all-p)
"Minimally initialize `doom-modules' (a hash table) and return it. "Minimally initialize `doom-modules' (a hash table) and return it.
This value is cached. If REFRESH-P, then don't use the cached value." This value is cached. If REFRESH-P, then don't use the cached value."
(or (unless refresh-p doom-modules) (if all-p
(let (doom-interactive-mode (cl-loop for path in (cdr (doom-module-load-path 'all))
doom-modules collect (doom-module-from-path path))
doom-init-modules-p) doom-modules))
(load! "init" doom-private-dir t)
(or doom-modules
(make-hash-table :test 'equal
:size 20
:rehash-threshold 1.0)))))
;; ;;
;;; Use-package modifications ;;; Use-package modifications
(eval-and-compile
(autoload 'use-package "use-package-core" nil nil t)
(setq use-package-compute-statistics doom-debug-mode
use-package-verbose doom-debug-mode
use-package-minimum-reported-time (if doom-debug-mode 0 0.1)
use-package-expand-minimally doom-interactive-mode))
(defvar doom--deferred-packages-alist '(t)) (defvar doom--deferred-packages-alist '(t))
(autoload 'use-package "use-package-core" nil nil t)
(setq use-package-compute-statistics doom-debug-p
use-package-verbose doom-debug-p
use-package-minimum-reported-time (if doom-debug-p 0 0.1)
use-package-expand-minimally doom-interactive-p)
;; A common mistake for new users is that they inadvertantly install their
;; packages with package.el, by copying over old `use-package' declarations with
;; an :ensure t property. Doom doesn't use package.el, so this will throw an
;; error that will confuse beginners, so we disable `:ensure'.
(setq use-package-ensure-function #'ignore)
;; ...On the other hand, if the user has loaded `package', then we should assume
;; they know what they're doing and restore the old behavior:
(add-transient-hook! 'package-initialize
(when (eq use-package-ensure-function #'ignore)
(setq use-package-ensure-function #'use-package-ensure-elpa)))
(with-eval-after-load 'use-package-core (with-eval-after-load 'use-package-core
;; Macros are already fontified, no need for this ;; `use-package' adds syntax highlighting for the `use-package' macro, but
;; Emacs 26+ already highlights macros, so it's redundant.
(font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords) (font-lock-remove-keywords 'emacs-lisp-mode use-package-font-lock-keywords)
;; We define :minor and :magic-minor from the `auto-minor-mode' package here ;; We define :minor and :magic-minor from the `auto-minor-mode' package here
@@ -266,6 +355,17 @@ This value is cached. If REFRESH-P, then don't use the cached value."
(defun use-package-handler/:magic-minor (name _ arg rest state) (defun use-package-handler/:magic-minor (name _ arg rest state)
(use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state)) (use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state))
;; HACK Fix `:load-path' so it resolves relative paths to the containing file,
;; rather than `user-emacs-directory'. This is a done as a convenience
;; for users, wanting to specify a local directory.
(defadvice! doom--resolve-load-path-from-containg-file-a (orig-fn label arg &optional recursed)
"Resolve :load-path from the current directory."
:around #'use-package-normalize-paths
;; `use-package-normalize-paths' resolves paths relative to
;; `user-emacs-directory', so we change that.
(let ((user-emacs-directory (if (stringp arg) (dir!))))
(funcall orig-fn label arg recursed)))
;; Adds two keywords to `use-package' to expand its lazy-loading capabilities: ;; Adds two keywords to `use-package' to expand its lazy-loading capabilities:
;; ;;
;; :after-call SYMBOL|LIST ;; :after-call SYMBOL|LIST
@@ -360,62 +460,16 @@ The overall load order of Doom is as follows:
Module load order is determined by your `doom!' block. See `doom-modules-dirs' 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 for a list of all recognized module trees. Order defines precedence (from most
to least)." to least)."
`(let ((modules `(unless doom-interactive-p
(doom-module-mplist-map
(lambda (category module &rest plist)
(if (plist-get plist :path)
(apply #'doom-module-set category module plist)
(message "WARNING Couldn't find the %s %s module" category module)))
,@(if (keywordp (car modules)) ,@(if (keywordp (car modules))
(list (list 'quote modules)) (list (list 'quote modules))
modules))) modules))
(unless doom-modules doom-modules))
(setq doom-modules
(make-hash-table :test 'equal
:size (if modules (length modules) 150)
:rehash-threshold 1.0)))
(let ((inhibit-message doom-inhibit-module-warnings)
obsolete category m)
(while modules
(setq m (pop modules))
(cond ((keywordp m)
(setq category m
obsolete (assq m doom-obsolete-modules)))
((not category)
(error "No module category specified for %s" m))
((and (listp m) (keywordp (car m)))
(pcase (car m)
(:cond
(cl-loop for (cond . mods) in (cdr m)
if (eval cond t)
return (prependq! modules mods)))
(:if (if (eval (cadr m) t)
(push (caddr m) modules)
(prependq! modules (cdddr m))))
(fn (if (or (eval (cadr m) t)
(eq fn :unless))
(prependq! modules (cddr m))))))
((catch 'doom-modules
(let* ((module (if (listp m) (car m) m))
(flags (if (listp m) (cdr m))))
(when-let (new (assq module obsolete))
(let ((newkeys (cdr new)))
(if (null newkeys)
(message "WARNING %s module was removed" key)
(if (cdr newkeys)
(message "WARNING %s module was removed and split into the %s modules"
(list category module) (mapconcat #'prin1-to-string newkeys ", "))
(message "WARNING %s module was moved to %s"
(list category module) (car newkeys)))
(push category modules)
(dolist (key newkeys)
(push (if flags
(nconc (cdr key) flags)
(cdr key))
modules)
(push (car key) modules))
(throw 'doom-modules t))))
(if-let (path (doom-module-locate-path category module))
(doom-module-set category module :flags flags :path path)
(message "WARNING Couldn't find the %s %s module" category module)))))))
(unless doom-interactive-mode
(setq doom-inhibit-module-warnings t))
doom-modules)))
(defvar doom-disabled-packages) (defvar doom-disabled-packages)
(defmacro use-package! (name &rest plist) (defmacro use-package! (name &rest plist)
@@ -481,39 +535,6 @@ WARNINGS:
(lambda () ,@body) (lambda () ,@body)
'append))) 'append)))
(defmacro require! (category module &rest flags)
"Loads the CATEGORY MODULE module with FLAGS.
CATEGORY is a keyword, MODULE is a symbol and FLAGS are symbols.
(require! :lang php +lsp)
This is for testing and internal use. This is not the correct way to enable a
module."
`(let ((doom-modules (or ,doom-modules (doom-modules)))
(module-path (doom-module-locate-path ,category ',module)))
(doom-module-set
,category ',module
(let ((plist (doom-module-get ,category ',module)))
,(when flags
`(plist-put plist :flags `,flags))
(unless (plist-member plist :path)
(plist-put plist :path ,(doom-module-locate-path category module)))
plist))
(if (directory-name-p module-path)
(condition-case-unless-debug ex
(let ((doom--current-module ',(cons category module))
(doom--current-flags ',flags))
(load! "init" module-path :noerror)
(load! "config" module-path :noerror))
('error
(lwarn 'doom-modules :error
"%s in '%s %s' -> %s"
(car ex) ,category ',module
(error-message-string ex))))
(warn 'doom-modules :warning "Couldn't find module '%s %s'"
,category ',module))))
(defmacro featurep! (category &optional module flag) (defmacro featurep! (category &optional module flag)
"Returns t if CATEGORY MODULE is enabled. "Returns t if CATEGORY MODULE is enabled.
@@ -522,82 +543,20 @@ If FLAG is provided, returns t if CATEGORY MODULE has FLAG enabled.
(featurep! :config default) (featurep! :config default)
Module FLAGs are set in your config's `doom!' block, typically in Module FLAGs are set in your config's `doom!' block, typically in
~/.emacs.d/init.el. Like so: ~/.doom.d/init.el. Like so:
:config (default +flag1 -flag2) :config (default +flag1 -flag2)
CATEGORY and MODULE can be omitted When this macro is used from inside a module 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))) (and (cond (flag (memq flag (doom-module-get category module :flags)))
(module (doom-module-p category module)) (module (doom-module-p category module))
(doom--current-flags (memq category doom--current-flags)) (doom--current-flags (memq category doom--current-flags))
((let ((module (doom-module-from-path))) ((if-let (module (doom-module-from-path))
(unless module (memq category (doom-module-get (car module) (cdr module) :flags))
(error "(featurep! %s %s %s) couldn't figure out what module it was called from (in %s)" (error "(featurep! %s %s %s) couldn't figure out what module it was called from (in %s)"
category module flag (file!))) category module flag (file!)))))
(memq category (doom-module-get (car module) (cdr module) :flags)))))
t)) 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!' was renamed to `use-package!'; use that instead.")
`(use-package! ,@args))
(defmacro def-package-hook! (&rest args)
(make-obsolete 'def-package-hook! 'use-package-hook! "2.0.9")
(message "`def-package-hook!' was renamed to `use-package-hook!'; use that instead.")
`(use-package-hook! ,@args))
(provide 'core-modules) (provide 'core-modules)
;;; core-modules.el ends here ;;; core-modules.el ends here
+433 -197
View File
@@ -1,27 +1,25 @@
;;; core/core-packages.el -*- lexical-binding: t; -*- ;;; core/core-packages.el -*- lexical-binding: t; -*-
;; Emacs package management is opinionated, and so is Doom. Doom uses `straight' ;; Emacs package management is opinionated, and so is Doom. Doom uses `straight'
;; to create a declarative, lazy-loaded and optionally rolling-release package ;; to create a declarative, lazy-loaded and (nominally) reproducible package
;; management system. We use `straight' over `package' because the latter is ;; management system. We use `straight' over `package' because the latter is
;; tempermental. ELPA sources suffer downtime occasionally, and often fail at ;; tempermental. ELPA sources suffer downtime occasionally and often fail to
;; building some packages when GNU Tar is unavailable (e.g. MacOS users start ;; build packages when GNU Tar is unavailable (e.g. MacOS users start with BSD
;; with BSD tar). There are also known gnutls errors in the current stable ;; tar). Known gnutls errors plague the current stable release of Emacs (26.x)
;; release of Emacs (26.x) which bork TLS handshakes with ELPA repos (mainly ;; which bork TLS handshakes with ELPA repos (mainly gnu.elpa.org). See
;; gnu.elpa.org). See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434. ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434.
;; ;;
;; What's worse, you can only get the latest version of packages through ELPA. ;; What's worse, you can only get the latest version of packages through ELPA.
;; In an ecosystem that is constantly changing, this is more frustrating than ;; In an ecosystem that is constantly changing, this is more frustrating than
;; convenient. Straight (and Doom) can do rolling release, but it is optional ;; convenient. Straight (and Doom) can do rolling release, but it is opt-in.
;; (and will eventually be opt-in).
;; ;;
;; ANyhow, interacting with this package management system is done through the ;; Interacting with this package management system is done through Doom's
;; bin/doom script included with Doom Emacs. You'll find more about it by ;; bin/doom script. Find out more about it by running 'doom help' (I highly
;; running 'doom help' (I highly recommend you add it to your PATH), but here ;; recommend you add the script to your PATH). Here are some highlights:
;; are the highlights:
;; ;;
;; + `bin/doom install`: a wizard that guides you through setting up Doom and ;; + `bin/doom install`: a wizard that guides you through setting up Doom and
;; your private config for the first time. ;; 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 ;; condition. It ensures all unneeded packages are removed, all needed ones
;; are installed, and all metadata associated with them is generated. ;; are installed, and all metadata associated with them is generated.
;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest ;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest
@@ -32,48 +30,31 @@
;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what ;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what
;; plugins to install and where from. ;; plugins to install and where from.
;; ;;
;; All that said, you can still use package.el's commands, but 'bin/doom ;; All that said, you can still use package.el's commands, but 'bin/doom sync'
;; refresh' will purge ELPA packages. ;; will purge ELPA packages.
(defvar doom-init-packages-p nil
"If non-nil, Doom's package management system has been initialized.")
(defvar doom-packages () (defvar doom-packages ()
"A list of enabled packages. Each element is a sublist, whose CAR is the "A list of enabled packages. Each element is a sublist, whose CAR is the
package's name as a symbol, and whose CDR is the plist supplied to its package's name as a symbol, and whose CDR is the plist supplied to its
`package!' declaration. Set by `doom-initialize-packages'.") `package!' declaration. Set by `doom-initialize-packages'.")
(defvar doom-core-packages '(straight use-package async gcmh)
"A list of packages that must be installed (and will be auto-installed if
missing) and shouldn't be deleted.")
(defvar doom-core-package-sources
'((org-elpa :local-repo nil)
(melpa
:type git :host github
:repo "melpa/melpa"
:no-build t)
(gnu-elpa-mirror
:type git :host github
:repo "emacs-straight/gnu-elpa-mirror"
:no-build t)
(emacsmirror-mirror
:type git :host github
:repo "emacs-straight/emacsmirror-mirror"
:no-build t))
"A list of recipes for straight's recipe repos.")
(defvar doom-disabled-packages () (defvar doom-disabled-packages ()
"A list of packages that should be ignored by `use-package!' and `after!'.") "A list of packages that should be ignored by `use-package!' and `after!'.")
(defvar doom-packages-file "packages"
"The basename of packages file for modules.
Package files are read whenever Doom's package manager wants a manifest of all
desired packages. They are rarely read in interactive sessions (unless the user
uses a straight or package.el command directly).")
;; ;;
;;; Package managers ;;; package.el
;; Ensure that, if we do need package.el, it is configured correctly. You really ;; 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. ;; shouldn't be using it, but it may be convenient for quick package testing.
(setq package--init-file-ensured t (setq package-enable-at-startup nil
package-enable-at-startup nil
package-user-dir (concat doom-local-dir "elpa/") package-user-dir (concat doom-local-dir "elpa/")
package-gnupghome-dir (expand-file-name "gpg" package-user-dir) package-gnupghome-dir (expand-file-name "gpg" package-user-dir)
;; I omit Marmalade because its packages are manually submitted rather ;; I omit Marmalade because its packages are manually submitted rather
@@ -84,175 +65,363 @@ missing) and shouldn't be deleted.")
("melpa" . ,(concat proto "://melpa.org/packages/")) ("melpa" . ,(concat proto "://melpa.org/packages/"))
("org" . ,(concat proto "://orgmode.org/elpa/"))))) ("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file' ;; package.el has no business modifying the user's init.el
(defadvice! doom--package-inhibit-custom-file-a (&optional value) (advice-add #'package--ensure-init-file :override #'ignore)
:override #'package--save-selected-packages
(if value (setq package-selected-packages value)))
;; Refresh package.el the first time you call `package-install' ;; Refresh package.el the first time you call `package-install', so it can still
;; be used (e.g. to temporarily test packages). Remember to run 'doom sync' to
;; purge them; they can conflict with packages installed via straight!
(add-transient-hook! 'package-install (package-refresh-contents)) (add-transient-hook! 'package-install (package-refresh-contents))
;;; straight
;;
;;; Straight
(setq straight-base-dir doom-local-dir (setq straight-base-dir doom-local-dir
straight-repository-branch "develop" straight-repository-branch "develop"
straight-cache-autoloads nil ; we already do this, and better. straight-cache-autoloads nil ; we already do this, and better.
;; Doom doesn't encourage you to modify packages in place. Disabling this ;; Doom doesn't encourage you to modify packages in place. Disabling this
;; makes 'doom refresh' instant (once everything set up), which is much ;; makes 'doom sync' instant (once everything set up), which is much nicer
;; nicer UX than the several seconds modification checks. ;; UX than the several seconds modification checks.
straight-check-for-modifications nil straight-check-for-modifications nil
;; We handle package.el ourselves (and a little more comprehensively) ;; We handle package.el ourselves (and a little more comprehensively)
straight-enable-package-integration nil straight-enable-package-integration nil
;; Before switching to straight, `doom-local-dir' would average out at ;; Before switching to straight, `doom-local-dir' would average out at
;; around 100mb with half Doom's modules at ~230 packages. Afterwards, at ;; around 100mb with half Doom's modules at ~230 packages. Afterwards, at
;; around 1gb. With shallow cloning, that is reduced to ~400mb. This ;; around 1gb. With shallow cloning, that is reduced to ~400mb. This has
;; imposes an issue with packages that require their git history for ;; no affect on packages that are pinned, however (run 'doom purge' to
;; certain things to work (like magit and org), but we can deal with that ;; compact those after-the-fact). Some packages break when shallow cloned
;; when we cross that bridge. ;; (like magit and org), but we'll deal with that elsewhere.
straight-vc-git-default-clone-depth 1 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 ;; Prefix declarations are unneeded bulk added to our autoloads file. Best
;; we just don't have to deal with them at all. ;; we 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 () (defadvice! doom--read-pinned-packages-a (orig-fn &rest args)
(mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist))) "Read `:pin's in `doom-packages' on top of straight's lockfiles."
(setq straight--transaction-alist nil)) :around #'straight--lockfile-read-all
(append (apply orig-fn args) ; lockfiles still take priority
;;; Getting straight to behave in batch mode (doom-package-pinned-list)))
(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))))))
;; ;;
;;; Bootstrapper ;;; Bootstrappers
(defun doom--ensure-straight (recipe pin)
(let ((repo-dir (doom-path straight-base-dir "straight/repos/straight.el"))
(repo-url (concat "http" (if gnutls-verify-error "s")
"://github.com/"
(or (plist-get recipe :repo) "raxod502/straight.el")))
(branch (or (plist-get recipe :branch) straight-repository-branch))
(call (if doom-debug-p #'doom-exec-process #'doom-call-process)))
(unless (file-directory-p repo-dir)
(message "Installing straight...")
(cond
((eq straight-vc-git-default-clone-depth 'full)
(funcall call "git" "clone" "--origin" "origin" repo-url repo-dir))
((null pin)
(funcall call "git" "clone" "--origin" "origin" repo-url repo-dir
"--depth" (number-to-string straight-vc-git-default-clone-depth)
"--branch" straight-repository-branch))
((integerp straight-vc-git-default-clone-depth)
(make-directory repo-dir t)
(let ((default-directory repo-dir))
(funcall call "git" "init")
(funcall call "git" "checkout" "-b" straight-repository-branch)
(funcall call "git" "remote" "add" "origin" repo-url)
(funcall call "git" "fetch" "origin" pin
"--depth" (number-to-string straight-vc-git-default-clone-depth))
(funcall call "git" "checkout" "--detach" pin)))))
(require 'straight (concat repo-dir "/straight.el"))
(doom-log "Initializing recipes")
(with-temp-buffer
(insert-file-contents (doom-path repo-dir "bootstrap.el"))
;; Don't install straight for us -- we've already done that -- only set
;; up its recipe repos for us.
(eval-region (search-forward "(require 'straight)")
(point-max)))))
(defun doom--ensure-core-packages (packages)
(doom-log "Installing core packages")
(dolist (package packages)
(let ((name (car package)))
(when-let (recipe (plist-get (cdr package) :recipe))
(straight-override-recipe (cons name recipe)))
(straight-use-package name))))
(defun doom-initialize-core-packages (&optional force-p)
"Ensure `straight' is installed and was compiled with this version of Emacs."
(when (or force-p (null (bound-and-true-p straight-recipe-repositories)))
(doom-log "Initializing straight")
(let ((packages (doom-package-list nil 'core)))
(cl-destructuring-bind (&key recipe pin &allow-other-keys)
(alist-get 'straight packages)
(doom--ensure-straight recipe pin))
(doom--ensure-core-packages packages))))
(defun doom-initialize-packages (&optional force-p) (defun doom-initialize-packages (&optional force-p)
"Ensures that Doom's package system and straight.el are initialized. "Process all packages, essential and otherwise, if they haven't already been.
If FORCE-P is non-nil, do it anyway. If FORCE-P is non-nil, do it anyway.
This ensure `doom-packages' is populated, if isn't aren't already. Use this This ensures `doom-packages' is populated and `straight' recipes are properly
before any of straight's or Doom's package management's API to ensure all the processed."
necessary package metadata is initialized and available for them." (doom-initialize-core-packages force-p)
(unless doom-init-packages-p
(setq force-p t))
(when (or force-p (not (bound-and-true-p package--initialized))) (when (or force-p (not (bound-and-true-p package--initialized)))
(doom-log "Initializing package.el") (doom-log "Initializing package.el")
(require 'package) (require 'package)
(package-initialize)) (package-initialize)
(when (or force-p (not doom-packages)) (unless package--initialized
(doom-log "Initializing straight") (error "Failed to initialize package.el")))
(setq doom-init-packages-p t) (when (or force-p (null doom-packages))
(unless (fboundp 'straight--reset-caches) (doom-log "Initializing straight.el")
(doom-ensure-straight) (or (setq doom-disabled-packages nil
(require 'straight))
(straight--reset-caches)
(setq straight-recipe-repositories 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))
(mapc #'straight-use-package doom-core-packages)
(doom-log "Initializing doom-packages")
(setq doom-disabled-packages nil
doom-packages (doom-package-list)) doom-packages (doom-package-list))
(cl-loop for (pkg . plist) in doom-packages (error "Failed to read any packages"))
if (plist-get plist :disable) (dolist (package doom-packages)
do (cl-pushnew pkg doom-disabled-packages) (cl-destructuring-bind
else if (not (plist-get plist :ignore)) (name &key recipe disable ignore &allow-other-keys) package
do (with-demoted-errors "Package error: %s" (unless ignore
(straight-register-package (if disable
(if-let (recipe (plist-get plist :recipe)) (cl-pushnew name doom-disabled-packages)
(cons pkg recipe) (when recipe
pkg)))) (straight-override-recipe (cons name recipe)))
(unless doom-interactive-mode (straight-register-package name)))))))
(add-hook 'kill-emacs-hook #'doom--finalize-straight))))
(defun doom-ensure-straight ()
"Ensure `straight' is installed and was compiled with this version of Emacs." ;;
(defvar bootstrap-version) ;;; Package management API
(let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of
;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d. (defun doom-package-get (package &optional prop nil-value)
(user-emacs-directory straight-base-dir) "Returns PACKAGE's `package!' recipe from `doom-packages'."
(bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el")) (let ((plist (cdr (assq package doom-packages))))
(bootstrap-version 5)) (if prop
(make-directory (doom-path straight-base-dir "straight/build") 'parents) (if (plist-member plist prop)
(unless (featurep 'straight) (plist-get plist prop)
(unless (or (require 'straight nil t) nil-value)
(file-readable-p bootstrap-file)) plist)))
(with-current-buffer
(url-retrieve-synchronously (defun doom-package-set (package prop value)
(format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el" "Set PROPERTY in PACKAGE's recipe to VALUE."
straight-repository-branch) (setf (alist-get package doom-packages)
'silent 'inhibit-cookies) (plist-put (alist-get package doom-packages)
(goto-char (point-max)) prop value)))
(eval-print-last-sexp)))
(load bootstrap-file nil t)))) (defun doom-package-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was registered with."
(let ((plist (gethash (symbol-name package) straight--recipe-cache)))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
(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)))
(defun doom-package-build-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was installed with."
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
(defun doom-package-build-time (package)
"TODO"
(car (gethash (symbol-name package) straight--build-cache)))
(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
(append deps (mapcan (lambda (dep) (doom-package-dependencies dep t t))
(copy-sequence deps)))
(copy-sequence deps))))
(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)
;; can't get dependencies for built-in packages
(unless (or (doom-package-build-recipe name)
noerror)
(error "Couldn't find %s, is it installed?" name))
(cl-loop for pkg in (hash-table-keys straight--build-cache)
for deps = (doom-package-dependencies pkg)
if (memq package deps)
collect pkg
and append (doom-package-depending-on pkg t)))
;;; Predicate functions
(defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in."
(eq (doom-package-build-recipe package :type)
'built-in))
(defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed."
(file-directory-p (straight--build-dir (symbol-name package))))
(defun doom-package-is-type-p (package type)
"TODO"
(memq type (doom-enlist (doom-package-get package :type))))
(defun doom-package-in-module-p (package category &optional module)
"Return non-nil if PACKAGE was installed by the user's private config."
(when-let (modules (doom-package-get package :modules))
(or (and (not module) (assq :private modules))
(member (cons category module) modules))))
(defun doom-package-backend (package)
"Return 'straight, 'builtin, 'elpa or 'other, depending on how PACKAGE is
installed."
(cond ((gethash (symbol-name package) straight--build-cache)
'straight)
((or (doom-package-built-in-p package)
(assq package package--builtins))
'builtin)
((assq package package-alist)
'elpa)
((locate-library (symbol-name package))
'other)))
(defun doom-package-changed-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it
was installed with."
(cl-check-type name symbol)
;; TODO
;; (when (doom-package-installed-p name)
;; (when-let* ((doom-recipe (assq name doom-packages))
;; (install-recipe (doom-package-recipe)))
;; (not (equal (cdr quelpa-recipe)
;; (cdr (plist-get (cdr doom-recipe) :recipe))))))
)
;;; Package getters
(defun doom--read-packages (file &optional noeval noerror)
(condition-case-unless-debug e
(with-temp-buffer ; prevent buffer-local state from propagating
(if (not noeval)
(load file noerror 'nomessage 'nosuffix)
(when (file-exists-p file)
(insert-file-contents file)
(let (emacs-lisp-mode) (emacs-lisp-mode))
;; Scrape `package!' blocks from FILE for a comprehensive listing of
;; packages used by this module.
(while (search-forward "(package!" nil t)
(let ((ppss (save-excursion (syntax-ppss))))
;; Don't collect packages in comments or strings
(unless (or (nth 3 ppss)
(nth 4 ppss))
(goto-char (match-beginning 0))
(cl-destructuring-bind (_ name . plist)
(read (current-buffer))
(push (cons
name (plist-put
plist :modules
(list (doom-module-from-path file))))
doom-packages))))))))
(error
(signal 'doom-package-error
(list (doom-module-from-path file)
file e)))))
(defun doom-package-list (&optional all-p core-only-p)
"Retrieve a list of explicitly declared packages from enabled modules.
If ALL-P, gather packages unconditionally across all modules, including disabled
ones."
(let ((packages-file (concat doom-packages-file ".el"))
doom-packages)
(doom--read-packages
(doom-path doom-core-dir packages-file) all-p 'noerror)
(unless core-only-p
(let ((private-packages (doom-path doom-private-dir packages-file))
(doom-modules (doom-module-list)))
(if all-p
(mapc #'doom--read-packages
(doom-files-in doom-modules-dir
:depth 2
:match "/packages\\.el$"))
;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten.
(doom--read-packages private-packages nil 'noerror)
(cl-loop for key being the hash-keys of doom-modules
for path = (doom-module-path (car key) (cdr key) packages-file)
for doom--current-module = key
do (doom--read-packages path nil 'noerror)))
(doom--read-packages private-packages all-p 'noerror)))
(cl-remove-if-not
(if core-only-p
(lambda (pkg) (eq (plist-get (cdr pkg) :type) 'core))
#'identity)
(nreverse doom-packages))))
(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)))))))
(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))))))
(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 &allow-other-keys)
recipe
(unless (or (null local-repo)
(eq type 'built-in))
(push recipe recipes))))
(nreverse recipes)))
(defun doom-package-state-list ()
"TODO"
(let (alist)
(dolist (recipe (hash-table-values straight--repo-cache) alist)
(cl-destructuring-bind (&key local-repo type &allow-other-keys)
recipe
(when (and local-repo (not (eq type 'built-in)))
(setf (alist-get local-repo alist nil nil #'equal)
(straight-vc-get-commit type local-repo)))))))
;; ;;
;;; Module package macros ;;; Module package macros
(cl-defmacro package! (cl-defmacro package!
(name &rest plist &key built-in recipe ignore _disable _freeze) (name &rest plist &key built-in recipe ignore _type _pin _disable)
"Declares a package and how to install it (if applicable). "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 This macro is declarative and does not load nor install packages. It is used to
@@ -263,67 +432,134 @@ Only use this macro in a module's packages.el file.
Accepts the following properties: Accepts the following properties:
:type core|local|built-in|virtual
Specifies what kind of package this is. Can be a symbol or a list thereof.
`core' = this is a protected package and cannot be disabled!
`local' = this package is being modified in-place. This package's repo is
unshallowed and will be skipped when you update packages.
`built-in' = this package is already built-in (otherwise, will be
installed)
`virtual' = this package is not tracked by Doom's package manager. It won't
be installed or uninstalled. Use this to pin 2nd order dependencies.
:recipe RECIPE :recipe RECIPE
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example); Specifies a straight.el recipe to allow you to acquire packages from external
for packages to be installed from external sources. sources. See https://github.com/raxod502/straight.el#the-recipe-format for
details on this recipe.
:disable BOOL :disable BOOL
Do not install or update this package AND disable all of its `use-package!' Do not install or update this package AND disable all of its `use-package!'
blocks. and `after!' blocks.
:ignore FORM :ignore FORM
Do not install this package. Do not install this package.
:freeze FORM :pin STR|nil
Do not update this package if FORM is non-nil. Pin this package to commit hash STR. Setting this to nil will unpin this
:built-in BOOL package if previously pinned.
Same as :ignore if the package is a built-in Emacs package. If set to :built-in BOOL|'prefer
'prefer, will use built-in package if it is present. 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 Returns t if package is successfully registered, and nil if it was disabled
elsewhere." elsewhere."
(declare (indent defun)) (declare (indent defun))
(when (and recipe (keywordp (car-safe recipe))) (when (and recipe (keywordp (car-safe recipe)))
(plist-put! plist :recipe `(quote ,recipe))) (plist-put! plist :recipe `(quote ,recipe)))
;; :built-in t is basically an alias for :ignore (locate-library NAME)
(when built-in (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))) (setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))
(plist-delete! plist :built-in) (plist-delete! plist :built-in)
(plist-put! plist :ignore built-in)) (plist-put! plist :ignore built-in))
`(let* ((name ',name) `(let* ((name ',name)
(plist (cdr (assq name doom-packages)))) (plist (cdr (assq name doom-packages))))
;; Record what module this declaration was found in
(let ((module-list (plist-get plist :modules)) (let ((module-list (plist-get plist :modules))
(module ',(doom-module-from-path))) (module ',(doom-module-from-path)))
(unless (member module module-list) (unless (member module module-list)
(plist-put! plist :modules (plist-put! plist :modules
(append module-list (append module-list
(list module) (list module)
(when (file-in-directory-p ,(dir!) doom-private-dir)
'((:private . modules)))
nil)))) nil))))
;; Merge given plist with pre-existing one
(doplist! ((prop val) (list ,@plist) plist) (doplist! ((prop val) (list ,@plist) plist)
(unless (null val) (unless (null val)
(plist-put! plist prop val))) (plist-put! plist prop val)))
;; Some basic key validation; throws an error on invalid properties
;; Some basic key validation; error if you're not using a valid key
(condition-case e (condition-case e
(when-let (recipe (plist-get plist :recipe))
(cl-destructuring-bind (cl-destructuring-bind
(&key _local-repo _files _flavor _no-build (&key local-repo _files _flavor
_no-build _no-byte-compile _no-autoloads
_type _repo _host _branch _remote _nonrecursive _fork _depth) _type _repo _host _branch _remote _nonrecursive _fork _depth)
(plist-get plist :recipe)) recipe
;; Expand :local-repo from current directory
(when local-repo
(plist-put!
plist :recipe
(plist-put recipe :local-repo
(let ((local-path (expand-file-name local-repo ,(dir!))))
(if (file-directory-p local-path)
local-path
local-repo)))))))
(error (error
(signal 'doom-package-error (signal 'doom-package-error
(cons ,(symbol-name name) (cons ,(symbol-name name)
(error-message-string e))))) (error-message-string e)))))
;; This is the only side-effect of this macro!
(setf (alist-get name doom-packages) plist) (setf (alist-get name doom-packages) plist)
(if (not (plist-get plist :disable)) t (unless (plist-get plist :disable)
(doom-log "Disabling package %S" name) (with-no-warnings
(cl-pushnew name doom-disabled-packages) (cons name plist)))))
nil)))
(defmacro disable-packages! (&rest packages) (defmacro disable-packages! (&rest packages)
"A convenience macro for disabling packages in bulk. "A convenience macro for disabling packages in bulk.
Only use this macro in a module's (or your private) packages.el file." Only use this macro in a module's (or your private) packages.el file."
(macroexp-progn (macroexp-progn
(cl-loop for p in packages (mapcar (lambda (p) `(package! ,p :disable t))
collect `(package! ,p :disable t)))) packages)))
(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))
(macroexp-progn
(mapcar
(lambda (target)
(when target
`(doom-package-set ',target :unpin t)))
(cl-loop for target in targets
if (or (keywordp target) (listp target))
append
(cl-loop with (category . modules) = (doom-enlist target)
for (name . plist) in doom-packages
for pkg-modules = (plist-get plist :modules)
if (and (assq category pkg-modules)
(or (null modules)
(cl-loop for module in modules
if (member (cons category module) pkg-modules)
return t))
name)
collect it)
else if (symbolp target)
collect target)))))
(provide 'core-packages) (provide 'core-packages)
;;; core-packages.el ends here ;;; core-packages.el ends here
+67 -44
View File
@@ -15,23 +15,18 @@ Emacs.")
"fd") "fd")
"name of `fd-find' executable binary") "name of `fd-find' executable binary")
(defvar doom-projectile-cache-timer-file (concat doom-cache-dir "projectile.timers")
"Where to save project file cache timers.")
;; ;;
;;; Packages ;;; Packages
(use-package! projectile (use-package! projectile
:after-call after-find-file dired-before-readin-hook minibuffer-setup-hook
:commands (projectile-project-root :commands (projectile-project-root
projectile-project-name projectile-project-name
projectile-project-p projectile-project-p
projectile-locate-dominating-file) projectile-locate-dominating-file)
:init :init
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache") (setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
projectile-enable-caching doom-interactive-mode projectile-enable-caching doom-interactive-p
projectile-files-cache-expire 86400 ; expire after a day
projectile-globally-ignored-files '(".DS_Store" "Icon projectile-globally-ignored-files '(".DS_Store" "Icon
" "TAGS") " "TAGS")
projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o") projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")
@@ -44,28 +39,47 @@ Emacs.")
:config :config
(projectile-mode +1) (projectile-mode +1)
;; Projectile runs four functions to determine the root (in this order):
;;
;; + `projectile-root-local' -> checks the `projectile-project-root' variable
;; for an explicit path.
;; + `projectile-root-bottom-up' -> searches from / to your current directory
;; for the paths listed in `projectile-project-root-files-bottom-up'. This
;; includes .git and .project
;; + `projectile-root-top-down' -> searches from the current directory down to
;; / the paths listed in `projectile-root-files', like package.json,
;; setup.py, or Cargo.toml
;; + `projectile-root-top-down-recurring' -> searches from the current
;; directory down to / for a directory that has one of
;; `projectile-project-root-files-top-down-recurring' but doesn't have a
;; parent directory with the same file.
;; ;;
;; In the interest of performance, we reduce the number of project root marker ;; In the interest of performance, we reduce the number of project root marker
;; files/directories projectile searches for when resolving the project root. ;; files/directories projectile searches for when resolving the project root.
(setq projectile-project-root-files-bottom-up (setq projectile-project-root-files-bottom-up
(append '(".projectile" ; projectile's root marker
".project" ; doom project marker ".project" ; doom project marker
".git") ; Git VCS root dir ".git") ; Git VCS root dir
(when (executable-find "hg") (when (executable-find "hg")
'(".hg")) ; Mercurial VCS root dir
(when (executable-find "fossil")
'(".fslckout" ; Fossil VCS root dir
'(".hg")) ; Mercurial VCS root dir '(".hg")) ; Mercurial VCS root dir
(when (executable-find "bzr") (when (executable-find "bzr")
'(".bzr")) ; Bazaar VCS root dir
(when (executable-find "darcs")
'(".bzr"))) ; Bazaar VCS root dir '(".bzr"))) ; Bazaar VCS root dir
;; This will be filled by other modules. We build this list manually so ;; 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 ;; projectile doesn't perform so many file checks every time it resolves
;; a project's root -- particularly when a file has no project. ;; a project's root -- particularly when a file has no project.
projectile-project-root-files '("TAGS") projectile-project-root-files '()
projectile-project-root-files-top-down-recurring '("Makefile")) projectile-project-root-files-top-down-recurring '("Makefile"))
(push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories) (push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories)
;; Override projectile's dirconfig file '.projectile' with doom's project marker '.project'.
(defadvice! doom--projectile-dirconfig-file-a ()
:override #'projectile-dirconfig-file
(cond
;; Prefers '.projectile' to maintain compatibility with existing projects.
((file-exists-p! (or ".projectile" ".project") (projectile-project-root)))
((expand-file-name ".project" (projectile-project-root)))))
;; Disable commands that won't work, as is, and that Doom already provides a ;; Disable commands that won't work, as is, and that Doom already provides a
;; better alternative for. ;; better alternative for.
@@ -88,7 +102,7 @@ b) represent blacklisted directories that are too big, change too often or are
b) represent blacklisted directories that are too big, change too often or are b) represent blacklisted directories that are too big, change too often or are
private. (see `doom-projectile-cache-blacklist'), private. (see `doom-projectile-cache-blacklist'),
c) are not valid projectile projects." c) are not valid projectile projects."
(when (and (bound-and-true-p projectile-projects-cache) (when (and (bound-and-true-p projectile-projects-cache)
doom-interactive-p) doom-interactive-p)
(cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist) (cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist)
for proot in (hash-table-keys projectile-projects-cache) for proot in (hash-table-keys projectile-projects-cache)
@@ -116,15 +130,34 @@ c) are not valid projectile projects."
(append projectile-project-root-files-bottom-up (append projectile-project-root-files-bottom-up
projectile-project-root-files) projectile-project-root-files)
projectile-project-root-files-bottom-up nil))) projectile-project-root-files-bottom-up nil)))
;; HACK Don't rely on VCS-specific commands to generate our file lists. That's
;; 7 commands to maintain, versus the more generic, reliable and
;; performant `fd' or `ripgrep'.
(defadvice! doom--only-use-generic-command-a (orig-fn vcs)
"Only use `projectile-generic-command' for indexing project files.
And if it's a function, evaluate it."
:around #'projectile-get-ext-command
(if (functionp projectile-generic-command)
(funcall projectile-generic-command vcs)
projectile-generic-command))
(cond (cond
;; If fd exists, use it for git and generic projects. fd is a rust program ;; If fd exists, use it for git and generic projects. fd is a rust program
;; that is significantly faster than git ls-files or find, and it respects ;; that is significantly faster than git ls-files or find, and it respects
;; .gitignore. This is recommended in the projectile docs. ;; .gitignore. This is recommended in the projectile docs.
((executable-find doom-projectile-fd-binary) ((executable-find doom-projectile-fd-binary)
(setq projectile-generic-command (setq projectile-generic-command
;; `projectile-generic-command' doesn't typically support a function.
;; My `doom--only-use-generic-command-a' advice allows this. I do it
;; this way so that future changes to
;; `projectile-globally-ignored-directories' are respected.
(lambda (_)
(concat (format "%s . -0 -H -E .git --color=never --type file --type symlink --follow" (concat (format "%s . -0 -H -E .git --color=never --type file --type symlink --follow"
doom-projectile-fd-binary) doom-projectile-fd-binary)
(cl-loop for dir in projectile-globally-ignored-directories
concat " -E "
concat (shell-quote-argument dir))
(if IS-WINDOWS " --path-separator=//"))) (if IS-WINDOWS " --path-separator=//")))
projectile-git-submodule-command nil projectile-git-submodule-command nil
;; ensure Windows users get fd's benefits ;; ensure Windows users get fd's benefits
@@ -132,29 +165,21 @@ c) are not valid projectile projects."
;; Otherwise, resort to ripgrep, which is also faster than find ;; Otherwise, resort to ripgrep, which is also faster than find
((executable-find "rg") ((executable-find "rg")
(setq projectile-generic-command (setq projectile-generic-command
(lambda (_)
(concat "rg -0 --files --follow --color=never --hidden" (concat "rg -0 --files --follow --color=never --hidden"
(cl-loop for dir in projectile-globally-ignored-directories (cl-loop for dir in projectile-globally-ignored-directories
concat (format " --glob '!%s'" dir))) concat " --glob "
concat (shell-quote-argument (concat "!" dir)))
(if IS-WINDOWS " --path-separator //"))) (if IS-WINDOWS " --path-separator //")))
projectile-git-submodule-command nil projectile-git-submodule-command nil
;; ensure Windows users get rg's benefits ;; ensure Windows users get rg's benefits
projectile-indexing-method 'alien)) projectile-indexing-method 'alien))
;; Fix breakage on windows in git projects with submodules, since Windows ;; Fix breakage on windows in git projects with submodules, since Windows
;; doesn't have tr ;; doesn't have tr
(IS-WINDOWS (IS-WINDOWS
(setq projectile-git-submodule-command nil))) (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) (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 "If projectile can't tell what kind of project you're in, it issues an error
@@ -169,12 +194,11 @@ the command instead."
;; Projectile root-searching functions can cause an infinite loop on TRAMP ;; Projectile root-searching functions can cause an infinite loop on TRAMP
;; connections, so disable them. ;; connections, so disable them.
;; TODO Is this still necessary? ;; TODO Is this still necessary?
(defadvice! doom--projectile-locate-dominating-file-a (file _name) (defadvice! doom--projectile-locate-dominating-file-a (file _name)
"Don't traverse the file system if on a remote connection." "Don't traverse the file system if on a remote connection."
:around #'projectile-locate-dominating-file :before-while #'projectile-locate-dominating-file
(when (and (stringp file) (and (stringp file)
(not (file-remote-p file nil t)))
(not (file-remote-p file nil t))))) (not (file-remote-p file nil t)))))
@@ -193,16 +217,15 @@ state are passed in.")
add-hooks add-hooks
on-load on-load
on-enter on-enter
on-exit) on-exit)
"Define a project minor-mode named NAME (a symbol) and declare where and how "Define a project minor mode named NAME and where/how it is activated.
it is activated. Project modes allow you to configure 'sub-modes' for
major-modes that are specific to a folder, project structure, framework or Project modes allow you to configure 'sub-modes' for major-modes that are
whatever arbitrary context you define. These project modes can have their own 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. snippets, etc.
This creates NAME-hook and NAME-map as well. 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 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. should be activated. If they are *all* true, NAME is activated.
+195 -106
View File
@@ -54,6 +54,9 @@ examples.
It is recommended you don't set specify a font-size, as to inherit `doom-font's It is recommended you don't set specify a font-size, as to inherit `doom-font's
size.") size.")
(defvar doom-unicode-extra-fonts nil
"Fonts to inject into the unicode charset before `doom-unicode-font'.")
;; ;;
;;; Custom hooks ;;; Custom hooks
@@ -85,45 +88,51 @@ size.")
(defvar doom--last-frame nil) (defvar doom--last-frame nil)
(defun doom-run-switch-window-hooks-h () (defun doom-run-switch-window-hooks-h ()
(let ((gc-cons-threshold most-positive-fixnum))
(unless (or doom-inhibit-switch-window-hooks (unless (or doom-inhibit-switch-window-hooks
(eq doom--last-window (selected-window)) (eq doom--last-window (selected-window))
(minibufferp)) (minibufferp))
(let ((doom-inhibit-switch-window-hooks t)) (let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-window-hooks t)
(inhibit-redisplay t))
(run-hooks 'doom-switch-window-hook) (run-hooks 'doom-switch-window-hook)
(setq doom--last-window (selected-window)))))) (setq doom--last-window (selected-window)))))
(defun doom-run-switch-frame-hooks-h (&rest _) (defun doom-run-switch-frame-hooks-h (&rest _)
(unless (or doom-inhibit-switch-frame-hooks (unless (or doom-inhibit-switch-frame-hooks
(eq doom--last-frame (selected-frame)) (eq doom--last-frame (selected-frame))
(frame-parameter nil 'parent-frame)) (frame-parameter nil 'parent-frame))
(let ((doom-inhibit-switch-frame-hooks t)) (let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-frame-hooks t))
(run-hooks 'doom-switch-frame-hook) (run-hooks 'doom-switch-frame-hook)
(setq doom--last-frame (selected-frame))))) (setq doom--last-frame (selected-frame)))))
(defun doom-run-switch-buffer-hooks-a (orig-fn buffer-or-name &rest args) (defun doom-run-switch-buffer-hooks-a (orig-fn buffer-or-name &rest args)
(let ((gc-cons-threshold most-positive-fixnum))
(if (or doom-inhibit-switch-buffer-hooks (if (or doom-inhibit-switch-buffer-hooks
(eq (current-buffer) (get-buffer buffer-or-name)) (and buffer-or-name
(eq (current-buffer)
(get-buffer buffer-or-name)))
(and (eq orig-fn #'switch-to-buffer) (car args))) (and (eq orig-fn #'switch-to-buffer) (car args)))
(apply orig-fn buffer-or-name args) (apply orig-fn buffer-or-name args)
(let ((doom-inhibit-switch-buffer-hooks t)) (let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-buffer-hooks t)
(inhibit-redisplay t))
(when-let (buffer (apply orig-fn buffer-or-name args)) (when-let (buffer (apply orig-fn buffer-or-name args))
(with-current-buffer (if (windowp buffer) (with-current-buffer (if (windowp buffer)
(window-buffer buffer) (window-buffer buffer)
buffer) buffer)
(run-hooks 'doom-switch-buffer-hook)) (run-hooks 'doom-switch-buffer-hook))
buffer))))) buffer))))
(defun doom-run-switch-to-next-prev-buffer-hooks-a (orig-fn &rest args) (defun doom-run-switch-to-next-prev-buffer-hooks-a (orig-fn &rest args)
(let ((gc-cons-threshold most-positive-fixnum))
(if doom-inhibit-switch-buffer-hooks (if doom-inhibit-switch-buffer-hooks
(apply orig-fn args) (apply orig-fn args)
(let ((doom-inhibit-switch-buffer-hooks t)) (let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-buffer-hooks t)
(inhibit-redisplay t))
(when-let (buffer (apply orig-fn args)) (when-let (buffer (apply orig-fn args))
(with-current-buffer buffer (with-current-buffer buffer
(run-hooks 'doom-switch-buffer-hook)) (run-hooks 'doom-switch-buffer-hook))
buffer))))) buffer))))
(defun doom-protect-fallback-buffer-h () (defun doom-protect-fallback-buffer-h ()
"Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'." "Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'."
@@ -135,10 +144,11 @@ size.")
e.g. If you indent with spaces by default, tabs will be highlighted. If you e.g. If you indent with spaces by default, tabs will be highlighted. If you
indent with tabs, spaces at BOL are highlighted. indent with tabs, spaces at BOL are highlighted.
Does nothing if `whitespace-mode' is already active or the current buffer is Does nothing if `whitespace-mode' or 'global-whitespace-mode' is already
read-only or not file-visiting." active or if the current buffer is read-only or not file-visiting."
(unless (or (eq major-mode 'fundamental-mode) (unless (or (eq major-mode 'fundamental-mode)
buffer-read-only buffer-read-only
(bound-and-true-p global-whitespace-mode)
(null buffer-file-name)) (null buffer-file-name))
(require 'whitespace) (require 'whitespace)
(set (make-local-variable 'whitespace-style) (set (make-local-variable 'whitespace-style)
@@ -156,6 +166,10 @@ read-only or not file-visiting."
;; Simpler confirmation prompt when killing Emacs ;; Simpler confirmation prompt when killing Emacs
(setq confirm-kill-emacs #'doom-quit-p) (setq confirm-kill-emacs #'doom-quit-p)
;; Don't prompt for confirmation when we create a new file or buffer (assume the
;; user knows what they're doing).
(setq confirm-nonexistent-file-or-buffer nil)
(setq uniquify-buffer-name-style 'forward (setq uniquify-buffer-name-style 'forward
;; no beeping or blinking please ;; no beeping or blinking please
ring-bell-function #'ignore ring-bell-function #'ignore
@@ -173,7 +187,12 @@ read-only or not file-visiting."
(setq hscroll-margin 2 (setq hscroll-margin 2
hscroll-step 1 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-margin 0
scroll-preserve-screen-position t scroll-preserve-screen-position t
;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll' ;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll'
@@ -195,12 +214,18 @@ read-only or not file-visiting."
;; ;;
;;; Cursor ;;; Cursor
;; Don't blink the cursor, it's too distracting. ;; The blinking cursor is distracting, but also interferes with cursor settings
;; in some minor modes that try to change it buffer-locally (like treemacs) and
;; can cause freezing for folks (esp on macOS) with customized & color cursors.
(blink-cursor-mode -1) (blink-cursor-mode -1)
;; Don't blink the paren matching the one at point, it's too distracting. ;; Don't blink the paren matching the one at point, it's too distracting.
(setq blink-matching-paren nil) (setq blink-matching-paren nil)
;; Some terminals offer two different cursors: a “visible” static cursor and a
;; “very visible” blinking one. By default, Emacs uses the very visible cursor
;; and switches to it when you start or resume Emacs. If `visible-cursor' is nil
;; when Emacs starts or resumes, it uses the normal cursor.
(setq visible-cursor nil) (setq visible-cursor nil)
;; Don't stretch the cursor to fit wide characters, it is disorienting, ;; Don't stretch the cursor to fit wide characters, it is disorienting,
@@ -214,8 +239,6 @@ read-only or not file-visiting."
;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers. ;; Make `next-buffer', `other-buffer', etc. ignore unreal buffers.
(push '(buffer-predicate . doom-buffer-frame-predicate) default-frame-alist) (push '(buffer-predicate . doom-buffer-frame-predicate) default-frame-alist)
(setq confirm-nonexistent-file-or-buffer t)
(defadvice! doom--switch-to-fallback-buffer-maybe-a (&rest _) (defadvice! doom--switch-to-fallback-buffer-maybe-a (&rest _)
"Switch to `doom-fallback-buffer' if on last real buffer. "Switch to `doom-fallback-buffer' if on last real buffer.
@@ -261,9 +284,6 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
(setq indicate-buffer-boundaries nil (setq indicate-buffer-boundaries nil
indicate-empty-lines nil) indicate-empty-lines nil)
;; remove continuation arrow on right fringe
(delq! 'continuation fringe-indicator-alist 'assq)
;; ;;
;;; Windows/frames ;;; Windows/frames
@@ -272,40 +292,53 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
(setq frame-title-format '("%b Doom Emacs") (setq frame-title-format '("%b Doom Emacs")
icon-title-format frame-title-format) 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 (setq window-resize-pixelwise t
frame-resize-pixelwise t) frame-resize-pixelwise t)
(unless EMACS27+ ; We already do this in early-init.el (unless (assq 'menu-bar-lines default-frame-alist)
;; Disable tool and scrollbars; Doom encourages keyboard-centric workflows, so ;; We do this in early-init.el too, but in case the user is on Emacs 26 we do
;; these are just clutter (the scrollbar also impacts Emacs' performance). ;; it here too: disable tool and scrollbars, as Doom encourages
(push '(menu-bar-lines . 0) default-frame-alist) ;; keyboard-centric workflows, so these are just clutter (the scrollbar also
(push '(tool-bar-lines . 0) default-frame-alist) ;; impacts performance).
(push '(vertical-scroll-bars) default-frame-alist)) (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 ;; These are disabled directly through their frame parameters, to avoid the
;; mismatching text color in the frame title) ;; extra work their minor modes do, but we have to unset these variables
(when IS-MAC ;; ourselves, otherwise users will have to cycle them twice to re-enable them.
(setq menu-bar-mode nil
tool-bar-mode nil
scroll-bar-mode nil)
(when! IS-MAC
;; Curse Lion and its sudden but inevitable fullscreen mode! ;; Curse Lion and its sudden but inevitable fullscreen mode!
;; NOTE Meaningless to railwaycat's emacs-mac build ;; NOTE Meaningless to railwaycat's emacs-mac build
(setq ns-use-native-fullscreen nil (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)
;; Sets ns-transparent-titlebar and ns-appearance frame parameters as is ;; Visit files opened outside of Emacs in existing frame, not a new one
;; appropriate for the loaded theme. (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) (and (or (daemonp)
(display-graphic-p)) (display-graphic-p))
(require 'ns-auto-titlebar nil t) (require 'ns-auto-titlebar nil t)
(ns-auto-titlebar-mode +1)) (ns-auto-titlebar-mode +1))
(add-hook! 'after-make-frame-functions ;; HACK On MacOS, disabling the menu bar makes MacOS treat Emacs as a
(defun doom-init-menu-bar-in-gui-frames-h (frame) ;; non-application window -- which means it doesn't automatically capture
"On MacOS, the menu bar isn't part of the frame. Disabling it makes MacOS ;; focus when it is started, among other things. We enable menu-bar-lines
treat Emacs as a non-application window." ;; 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) (when (display-graphic-p frame)
(set-frame-parameter frame 'menu-bar-lines 1))))) (set-frame-parameter frame 'menu-bar-lines 1))))))
;; The native border "consumes" a pixel of the fringe on righter-most splits, ;; The native border "consumes" a pixel of the fringe on righter-most splits,
;; `window-divider' does not. Available since Emacs 25.1. ;; `window-divider' does not. Available since Emacs 25.1.
@@ -314,18 +347,21 @@ treat Emacs as a non-application window."
window-divider-default-right-width 1) window-divider-default-right-width 1)
(add-hook 'doom-init-ui-hook #'window-divider-mode) (add-hook 'doom-init-ui-hook #'window-divider-mode)
;; Prompt the user for confirmation when deleting a non-empty frame ;; Prompt for confirmation when deleting a non-empty frame; a last line of
(global-set-key [remap delete-frame] #'doom/delete-frame) ;; defense against accidental loss of work.
(global-set-key [remap delete-frame] #'doom/delete-frame-with-prompt)
;; always avoid GUI ;; always avoid GUI
(setq use-dialog-box nil) (setq use-dialog-box nil)
;; Don't display floating tooltips; display their contents in the echo-area. ;; Don't display floating tooltips; display their contents in the echo-area,
(if (bound-and-true-p tooltip-mode) (tooltip-mode -1)) ;; because native tooltips are ugly.
;; native linux tooltips are ugly (when (bound-and-true-p tooltip-mode)
(tooltip-mode -1))
;; ...especially on linux
(when IS-LINUX (when IS-LINUX
(setq x-gtk-use-system-tooltips nil)) (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 (setq split-width-threshold 160
split-height-threshold nil) split-height-threshold nil)
@@ -334,11 +370,11 @@ treat Emacs as a non-application window."
;;; Minibuffer ;;; Minibuffer
;; Allow for minibuffer-ception. Sometimes we need another minibuffer command ;; 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) (setq enable-recursive-minibuffers t)
;; Show current key-sequence in minibuffer, like vim does. Any feedback after ;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any
;; typing is better UX than no feedback at all. ;; feedback after typing is better UX than no feedback at all.
(setq echo-keystrokes 0.02) (setq echo-keystrokes 0.02)
;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This ;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This
@@ -348,7 +384,7 @@ treat Emacs as a non-application window."
max-mini-window-height 0.15) max-mini-window-height 0.15)
;; Typing yes/no is obnoxious when y/n will do ;; Typing yes/no is obnoxious when y/n will do
(fset #'yes-or-no-p #'y-or-n-p) (advice-add #'yes-or-no-p :override #'y-or-n-p)
;; Try really hard to keep the cursor from getting stuck in the read-only prompt ;; Try really hard to keep the cursor from getting stuck in the read-only prompt
;; portion of the minibuffer. ;; portion of the minibuffer.
@@ -387,42 +423,36 @@ treat Emacs as a non-application window."
(set-window-configuration doom--ediff-saved-wconf))))) (set-window-configuration doom--ediff-saved-wconf)))))
(use-package! goto-addr
:hook (text-mode . goto-address-mode)
:hook (prog-mode . goto-address-prog-mode)
:config
(define-key goto-address-highlight-keymap (kbd "RET") #'goto-address-at-point))
(use-package! hl-line (use-package! hl-line
;; Highlights the current line ;; Highlights the current line
:hook ((prog-mode text-mode conf-mode) . hl-line-mode) :hook ((prog-mode text-mode conf-mode special-mode) . hl-line-mode)
:config :config
;; Not having to render the hl-line overlay in multiple buffers offers a tiny ;; Not having to render the hl-line overlay in multiple buffers offers a tiny
;; performance boost. I also don't need to see it in other buffers. ;; performance boost. I also don't need to see it in other buffers.
(setq hl-line-sticky-flag nil (setq hl-line-sticky-flag nil
global-hl-line-sticky-flag nil) global-hl-line-sticky-flag nil)
;; Disable `hl-line' in evil-visual mode (temporarily). `hl-line' can make the ;; Temporarily disable `hl-line' when selection is active, since it doesn't
;; selection region harder to see while in evil visual mode. ;; serve much purpose when the selection is so much more visible.
(after! evil (defvar doom--hl-line-mode nil)
(defvar doom-buffer-hl-line-mode nil)
(add-hook! 'evil-visual-state-entry-hook (add-hook! '(evil-visual-state-entry-hook activate-mark-hook)
(defun doom-disable-hl-line-h () (defun doom-disable-hl-line-h ()
(when hl-line-mode (when hl-line-mode
(setq-local doom-buffer-hl-line-mode t) (setq-local doom--hl-line-mode t)
(hl-line-mode -1)))) (hl-line-mode -1))))
(add-hook! 'evil-visual-state-exit-hook
(add-hook! '(evil-visual-state-exit-hook deactivate-mark-hook)
(defun doom-enable-hl-line-maybe-h () (defun doom-enable-hl-line-maybe-h ()
(when doom-buffer-hl-line-mode (when doom--hl-line-mode
(hl-line-mode +1)))))) (hl-line-mode +1)))))
(use-package! winner (use-package! winner
;; undo/redo changes to Emacs' window layout ;; undo/redo changes to Emacs' window layout
:after-call after-find-file doom-switch-window-hook
:preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself :preface (defvar winner-dont-bind-my-keys t) ; I'll bind keys myself
:config (winner-mode +1) :hook (doom-first-buffer . winner-mode)
:config
(appendq! winner-boring-buffers (appendq! winner-boring-buffers
'("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*" '("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*"
"*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*" "*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*"
@@ -431,13 +461,12 @@ treat Emacs as a non-application window."
(use-package! paren (use-package! paren
;; highlight matching delimiters ;; highlight matching delimiters
:after-call after-find-file doom-switch-buffer-hook :hook (doom-first-buffer . show-paren-mode)
:config :config
(setq show-paren-delay 0.1 (setq show-paren-delay 0.1
show-paren-highlight-openparen t show-paren-highlight-openparen t
show-paren-when-point-inside-paren t show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t) show-paren-when-point-in-periphery t))
(show-paren-mode +1))
;;;###package whitespace ;;;###package whitespace
@@ -461,13 +490,31 @@ treat Emacs as a non-application window."
all-the-icons-wicon all-the-icons-wicon
all-the-icons-material all-the-icons-material
all-the-icons-alltheicon) all-the-icons-alltheicon)
:init :preface
(setq doom-unicode-extra-fonts
(list "Weather Icons"
"github-octicons"
"FontAwesome"
"all-the-icons"
"file-icons"
"Material Icons"))
:config
(cond ((daemonp)
(defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args) (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." "Return a blank string in tty Emacs, which doesn't support multiple fonts."
:around #'all-the-icons-insert :around '(all-the-icons-octicon all-the-icons-material
(if (display-multi-font-p) 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) (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 ;;;###package hide-mode-line-mode
(add-hook! '(completion-list-mode-hook Man-mode-hook) (add-hook! '(completion-list-mode-hook Man-mode-hook)
@@ -486,10 +533,6 @@ treat Emacs as a non-application window."
;; languages like Lisp. ;; languages like Lisp.
(setq rainbow-delimiters-max-face-count 3) (setq rainbow-delimiters-max-face-count 3)
;;;###package pos-tip
(setq pos-tip-internal-border-width 6
pos-tip-border-width 1)
;; ;;
;;; Line numbers ;;; Line numbers
@@ -497,16 +540,16 @@ treat Emacs as a non-application window."
;; Explicitly define a width to reduce computation ;; Explicitly define a width to reduce computation
(setq-default display-line-numbers-width 3) (setq-default display-line-numbers-width 3)
;; Show absolute line numbers for narrowed regions ;; 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) (setq-default display-line-numbers-widen t)
;; Enable line numbers in most text-editing modes ;; 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) (add-hook! '(prog-mode-hook text-mode-hook conf-mode-hook)
#'display-line-numbers-mode) #'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 ;;; Theme & font
@@ -514,14 +557,18 @@ treat Emacs as a non-application window."
;; Underline looks a bit better when drawn lower ;; Underline looks a bit better when drawn lower
(setq x-underline-at-descent-line t) (setq x-underline-at-descent-line t)
;; DEPRECATED In Emacs 27
(defvar doom--prefer-theme-elc nil (defvar doom--prefer-theme-elc nil
"If non-nil, `load-theme' will prefer the compiled theme (unlike its default "If non-nil, `load-theme' will prefer the compiled theme (unlike its default
behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.") behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
(defun doom-init-fonts-h () (defun doom-init-fonts-h ()
"Loads `doom-font'." "Loads `doom-font'."
(cond (doom-font (cond
(doom-font
(cl-pushnew (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 (cons 'font
(cond ((stringp doom-font) doom-font) (cond ((stringp doom-font) doom-font)
((fontp doom-font) (font-xlfd-name doom-font)) ((fontp doom-font) (font-xlfd-name doom-font))
@@ -530,6 +577,8 @@ behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
default-frame-alist default-frame-alist
:key #'car :test #'eq)) :key #'car :test #'eq))
((display-graphic-p) ((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 (setq font-use-system-font t
doom-font (face-attribute 'default :font))))) doom-font (face-attribute 'default :font)))))
@@ -537,12 +586,15 @@ behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
"Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'." "Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'."
(condition-case e (condition-case e
(with-selected-frame (or frame (selected-frame)) (with-selected-frame (or frame (selected-frame))
(when doom-font
(set-face-attribute 'fixed-pitch nil :font doom-font))
(when doom-serif-font (when doom-serif-font
(set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font)) (set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font))
(when doom-variable-pitch-font (when doom-variable-pitch-font
(set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font)) (set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font))
(when (and doom-unicode-font (fboundp 'set-fontset-font)) (when (fboundp 'set-fontset-font)
(set-fontset-font t 'unicode doom-unicode-font nil 'prepend))) (dolist (font (append doom-unicode-extra-fonts (doom-enlist doom-unicode-font)))
(set-fontset-font t 'unicode font nil 'prepend))))
((debug error) ((debug error)
(if (string-prefix-p "Font not available: " (error-message-string e)) (if (string-prefix-p "Font not available: " (error-message-string e))
(lwarn 'doom-ui :warning (lwarn 'doom-ui :warning
@@ -554,29 +606,54 @@ behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
"Load the theme specified by `doom-theme' in FRAME." "Load the theme specified by `doom-theme' in FRAME."
(when (and doom-theme (not (memq doom-theme custom-enabled-themes))) (when (and doom-theme (not (memq doom-theme custom-enabled-themes)))
(with-selected-frame (or frame (selected-frame)) (with-selected-frame (or frame (selected-frame))
(let ((doom--prefer-theme-elc t)) (let ((doom--prefer-theme-elc t)) ; DEPRECATED in Emacs 27
(load-theme doom-theme t))))) (load-theme doom-theme t)))))
(defadvice! doom--run-load-theme-hooks-a (theme &optional _no-confirm no-enable) (defadvice! doom--load-theme-a (orig-fn theme &optional no-confirm no-enable)
"Set up `doom-load-theme-hook' to run after `load-theme' is called." "Run `doom-load-theme-hook' on `load-theme' and fix its issues.
:after-while #'load-theme
1. Disable previously enabled themes.
2. Don't let face-remapping screw up loading the new theme
(*cough*`mixed-pitch-mode').
3. Record the current theme in `doom-theme'."
:around #'load-theme
;; HACK Run `load-theme' from an estranged buffer, where we can be assured
;; that buffer-local face remaps (by `mixed-pitch-mode', for instance)
;; won't interfere with changing themes.
(with-temp-buffer
(when-let (result (funcall orig-fn theme no-confirm no-enable))
(unless no-enable (unless no-enable
(setq doom-theme theme (setq doom-theme theme
doom-init-theme-p t) doom-init-theme-p t)
(run-hooks 'doom-load-theme-hook))) ;; `load-theme' doesn't disable previously enabled themes, which seems
;; like what you'd want. You could always use `enable-theme' to activate
;; multiple themes instead.
(mapc #'disable-theme (remq theme custom-enabled-themes))
(run-hooks 'doom-load-theme-hook))
result)))
(when! (not EMACS27+)
;; DEPRECATED `doom--load-theme-a' handles this for us after the theme is
;; loaded, but this only works on Emacs 27+. Disabling old themes
;; must be done *before* the theme is loaded in Emacs 26.
(defadvice! doom--disable-previous-themes-a (theme &optional _no-confirm no-enable)
"Disable other themes when loading a new one."
:before #'load-theme
(unless no-enable
(mapc #'disable-theme custom-enabled-themes)))
;; DEPRECATED Not needed in Emacs 27
(defadvice! doom--prefer-compiled-theme-a (orig-fn &rest args) (defadvice! doom--prefer-compiled-theme-a (orig-fn &rest args)
"Make `load-theme' prioritize the byte-compiled theme for a moderate boost in "Have `load-theme' prioritize the byte-compiled theme.
startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil." This offers a moderate boost in startup (or theme switch) time, so long as
`doom--prefer-theme-elc' is non-nil."
:around #'load-theme :around #'load-theme
(if (or (null after-init-time) (if (or (null after-init-time)
doom--prefer-theme-elc) doom--prefer-theme-elc)
(cl-letf* ((old-locate-file (symbol-function 'locate-file)) (letf! (defun locate-file (filename path &optional _suffixes predicate)
((symbol-function 'locate-file) (funcall locate-file filename path '("c" "") predicate))
(lambda (filename path &optional _suffixes predicate)
(funcall old-locate-file filename path '("c" "") predicate))))
(apply orig-fn args)) (apply orig-fn args))
(apply orig-fn args))) (apply orig-fn args))))
;; ;;
@@ -590,9 +667,13 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil."
(add-hook 'after-change-major-mode-hook #'doom-highlight-non-default-indentation-h 'append) (add-hook 'after-change-major-mode-hook #'doom-highlight-non-default-indentation-h 'append)
;; Initialize custom switch-{buffer,window,frame} hooks: ;; Initialize custom switch-{buffer,window,frame} hooks:
;;
;; + `doom-switch-buffer-hook' ;; + `doom-switch-buffer-hook'
;; + `doom-switch-window-hook' ;; + `doom-switch-window-hook'
;; + `doom-switch-frame-hook' ;; + `doom-switch-frame-hook'
;;
;; These should be done as late as possible, as not to prematurely trigger
;; hooks during startup.
(add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h) (add-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
(add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h) (add-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(dolist (fn '(switch-to-next-buffer switch-to-prev-buffer)) (dolist (fn '(switch-to-next-buffer switch-to-prev-buffer))
@@ -617,19 +698,27 @@ startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil."
;;; Fixes/hacks ;;; Fixes/hacks
;; Doom doesn't support `customize' and it never will. It's a clumsy interface ;; Doom doesn't support `customize' and it never will. It's a clumsy interface
;; for something that should be configured from only one place ($DOOMDIR), so we ;; that sets variables at a time where it can be easily and unpredictably
;; disable them. ;; overwritten. Configure things from your $DOOMDIR instead.
(put 'customize 'disabled "Doom doesn't support `customize', configure Emacs from $DOOMDIR/config.el 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") (put 'customize-themes 'disabled "Set `doom-theme' or use `load-theme' in $DOOMDIR/config.el instead")
;; doesn't exist in terminal Emacs; we define it to prevent errors ;; Doesn't exist in terminal Emacs, so we define it to prevent void-function
;; errors emitted from packages that blindly try to use it.
(unless (fboundp 'define-fringe-bitmap) (unless (fboundp 'define-fringe-bitmap)
(fset 'define-fringe-bitmap #'ignore)) (fset 'define-fringe-bitmap #'ignore))
(after! whitespace (after! whitespace
(defun doom-disable-whitespace-mode-in-childframes-a (orig-fn) (defun doom-disable-whitespace-mode-in-childframes-a (orig-fn)
"`whitespace-mode' inundates child frames with whitspace markers, so disable "`whitespace-mode' inundates child frames with whitespace markers, so
it to fix all that visual noise." disable it to fix all that visual noise."
(unless (frame-parameter nil 'parent-frame) (unless (frame-parameter nil 'parent-frame)
(funcall orig-fn))) (funcall orig-fn)))
(add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a)) (add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a))
+202 -209
View File
@@ -1,41 +1,58 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*- ;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
(when (version< emacs-version "26.1") (eval-when-compile
(error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher" (when (< emacs-major-version 26)
emacs-version)) (error "Detected Emacs v%s. Doom only supports Emacs 26 and newer"
emacs-version)))
;;
;;; Variables
(defconst doom-version "2.0.9" (defconst doom-version "2.0.9"
"Current version of Doom Emacs.") "Current version of Doom Emacs.")
(defconst EMACS27+ (> emacs-major-version 26)) (defconst EMACS27+ (> emacs-major-version 26))
(defconst EMACS28+ (> emacs-major-version 27))
(defconst IS-MAC (eq system-type 'darwin)) (defconst IS-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux)) (defconst IS-LINUX (eq system-type 'gnu/linux))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos))) (defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix))) (defconst IS-BSD (or IS-MAC (eq system-type 'berkeley-unix)))
;; Unix tools look for HOME, but this is normally not defined on Windows.
(when (and IS-WINDOWS (null (getenv "HOME")))
(setenv "HOME" (getenv "USERPROFILE")))
;; Ensure `doom-core-dir' is in `load-path' ;; Ensure `doom-core-dir' is in `load-path'
(add-to-list 'load-path (file-name-directory load-file-name)) (add-to-list 'load-path (file-name-directory load-file-name))
(defvar doom--initial-load-path load-path) (defvar doom--initial-load-path load-path)
(defvar doom--initial-process-environment process-environment) (defvar doom--initial-process-environment process-environment)
(defvar doom--initial-exec-path exec-path) (defvar doom--initial-exec-path exec-path)
;; `file-name-handler-alist' is consulted on every `require', `load' and various
;; path/io functions. You get a minor speed up by nooping this. However, this
;; may cause problems on builds of Emacs where its site lisp files aren't
;; byte-compiled and we're forced to load the *.el.gz files (e.g. on Alpine)
(unless noninteractive
(defvar doom--initial-file-name-handler-alist file-name-handler-alist) (defvar doom--initial-file-name-handler-alist file-name-handler-alist)
;; 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) (setq file-name-handler-alist nil)
;; Restore `file-name-handler-alist', because it is needed for handling ;; Restore `file-name-handler-alist', because it is needed for handling
;; encrypted or compressed files, among other things. ;; encrypted or compressed files, among other things.
(defun doom-reset-file-handler-alist-h () (defun doom-reset-file-handler-alist-h ()
;; Re-add rather than `setq', because file-name-handler-alist may have
;; changed since startup, and we want to preserve those.
(dolist (handler file-name-handler-alist)
(add-to-list 'doom--initial-file-name-handler-alist handler))
(setq file-name-handler-alist doom--initial-file-name-handler-alist)) (setq file-name-handler-alist doom--initial-file-name-handler-alist))
(add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h) (add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h))
;; Load the bare necessities ;; Just the bare necessities
(require 'subr-x)
(require 'cl-lib)
(require 'core-lib) (require 'core-lib)
(autoload 'doom-initialize-packages "core-packages")
;; ;;
;;; Global variables ;;; Global variables
@@ -46,13 +63,13 @@
(defvar doom-init-time nil (defvar doom-init-time nil
"The time it took, in seconds, for Doom Emacs to initialize.") "The time it took, in seconds, for Doom Emacs to initialize.")
(defvar doom-debug-mode (or (getenv "DEBUG") init-file-debug) (defvar doom-debug-p (or (getenv "DEBUG") init-file-debug)
"If non-nil, Doom will log more. "If non-nil, Doom will log more.
Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the Use `doom-debug-mode' to toggle it. The --debug-init flag and setting the DEBUG
DEBUG envvar will enable this at startup.") envvar will enable this at startup.")
(defvar doom-interactive-mode (not noninteractive) (defvar doom-interactive-p (not noninteractive)
"If non-nil, Emacs is in interactive mode.") "If non-nil, Emacs is in interactive mode.")
;;; Directories/files ;;; Directories/files
@@ -110,14 +127,8 @@ whichever is found first. Must end in a slash.")
This file is responsible for informing Emacs where to find all of Doom's This file is responsible for informing Emacs where to find all of Doom's
autoloaded core functions (in core/autoload/*.el).") autoloaded core functions (in core/autoload/*.el).")
(defconst doom-package-autoload-file (concat doom-local-dir "autoloads.pkg.el")
"Where `doom-reload-package-autoloads' stores its package autoloads.
This file is compiled from the autoloads files of all installed packages
combined.")
(defconst doom-env-file (concat doom-local-dir "env") (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, 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 which is loaded at startup (if it exists). This is helpful if Emacs can't
@@ -127,7 +138,7 @@ users).")
;;; Custom error types ;;; Custom error types
(define-error 'doom-error "Error in Doom Emacs core") (define-error 'doom-error "Error in Doom Emacs core")
(define-error 'doom-hook-error "Error in a Doom startup hook" 'doom-error) (define-error 'doom-hook-error "Error in a Doom startup hook" 'doom-error)
(define-error 'doom-autoload-error "Error in an autoloads file" 'doom-error) (define-error 'doom-autoload-error "Error in Doom's autoloads file" 'doom-error)
(define-error 'doom-module-error "Error in a Doom module" 'doom-error) (define-error 'doom-module-error "Error in a Doom module" 'doom-error)
(define-error 'doom-private-error "Error in private config" 'doom-error) (define-error 'doom-private-error "Error in private config" 'doom-error)
(define-error 'doom-package-error "Error with packages" 'doom-error) (define-error 'doom-package-error "Error with packages" 'doom-error)
@@ -136,84 +147,99 @@ users).")
;; ;;
;;; Emacs core configuration ;;; Emacs core configuration
;; Reduce debug output, well, unless we've asked for it. ;; lo', longer logs ahoy, so to reliably locate lapses in doom's logic later
(setq debug-on-error doom-debug-mode (setq message-log-max 8192)
jka-compr-verbose doom-debug-mode)
;; UTF-8 as the default coding system ;; Reduce debug output, well, unless we've asked for it.
(setq debug-on-error doom-debug-p
jka-compr-verbose doom-debug-p)
;; Contrary to what many Emacs users have in their configs, you really don't
;; need more than this to make UTF-8 the default coding system:
(when (fboundp 'set-charset-priority) (when (fboundp 'set-charset-priority)
(set-charset-priority 'unicode)) ; pretty (set-charset-priority 'unicode)) ; pretty
(prefer-coding-system 'utf-8) ; pretty (prefer-coding-system 'utf-8) ; pretty
(setq locale-coding-system 'utf-8) ; please (setq locale-coding-system 'utf-8) ; please
;; Except for the clipboard on Windows, where its contents could be in an ;; The clipboard's on Windows could be in an encoding that's wider (or thinner)
;; encoding that's wider than utf-8, so we let Emacs/the OS decide what encoding ;; than utf-8, so let Emacs/the OS decide what encoding to use there.
;; to use.
(unless IS-WINDOWS (unless IS-WINDOWS
(setq selection-coding-system 'utf-8)) ; with sugar on top (setq selection-coding-system 'utf-8)) ; with sugar on top
;; Disable warnings from legacy advice system. They aren't useful, and we can't ;; Disable warnings from legacy advice system. They aren't useful, and what can
;; often do anything about them besides changing packages upstream ;; we do about them, besides changing packages upstream?
(setq ad-redefinition-action 'accept) (setq ad-redefinition-action 'accept)
;; Make apropos omnipotent. It's more useful this way. ;; Make apropos omnipotent. It's more useful this way.
(setq apropos-do-all t) (setq apropos-do-all t)
;; Don't make a second case-insensitive pass over `auto-mode-alist'. If it has ;; A second, case-insensitive pass over `auto-mode-alist' is time wasted, and
;; to, it's our (the user's) failure. One case for all! ;; indicates misconfiguration (or that the user needs to stop relying on case
;; insensitivity).
(setq auto-mode-case-fold nil) (setq auto-mode-case-fold nil)
;; Make all regexps case-sensitive by default. This favors correctness for ;; Less noise at startup. The dashboard/empty scratch buffer is good enough.
;; programmatical regexp searches and provides a slight performance benefit to
;; font-locking where the keywords don't let-bind `case-fold-search' themselves
;; and are already case-correct. This could break poorly written packages!
(setq-default case-fold-search nil)
;; Display the bare minimum at startup. We don't need all that noise. The
;; dashboard/empty scratch buffer is good enough.
(setq inhibit-startup-message t (setq inhibit-startup-message t
inhibit-startup-echo-area-message user-login-name inhibit-startup-echo-area-message user-login-name
inhibit-default-init t inhibit-default-init t
;; Avoid pulling in many packages by starting the scratch buffer in
;; `fundamental-mode', rather than, say, `org-mode' or `text-mode'.
initial-major-mode 'fundamental-mode initial-major-mode 'fundamental-mode
initial-scratch-message nil) initial-scratch-message nil)
(fset #'display-startup-echo-area-message #'ignore)
;; Get rid of "For information about GNU Emacs..." message at startup, unless
;; we're in a daemon session, where it'll say "Starting Emacs daemon." instead,
;; which isn't so bad.
(unless (daemonp)
(advice-add #'display-startup-echo-area-message :override #'ignore))
;; Emacs "updates" its ui more often than it needs to, so we slow it down ;; Emacs "updates" its ui more often than it needs to, so we slow it down
;; slightly, from 0.5s: ;; slightly from 0.5s:
(setq idle-update-delay 1) (setq idle-update-delay 1.0)
;; Emacs is a huge security vulnerability, what with all the dependencies it ;; Emacs is essentially one huge security vulnerability, what with all the
;; pulls in from all corners of the globe. Let's at least try to be more ;; dependencies it pulls in from all corners of the globe. Let's try to be at
;; discerning. ;; least a little more discerning.
(setq gnutls-verify-error (getenv "INSECURE") (setq gnutls-verify-error (not (getenv "INSECURE"))
gnutls-algorithm-priority
(when (boundp 'libgnutls-version)
(concat "SECURE128:+SECURE192:-VERS-ALL"
(if (and (not IS-WINDOWS)
(not (version< emacs-version "26.3"))
(>= libgnutls-version 30605))
":+VERS-TLS1.3")
":+VERS-TLS1.2"))
;; `gnutls-min-prime-bits' is set based on recommendations from
;; https://www.keylength.com/en/4/
gnutls-min-prime-bits 3072
tls-checktrust gnutls-verify-error tls-checktrust gnutls-verify-error
tls-program '("gnutls-cli --x509cafile %t -p %p %h" ;; Emacs is built with `gnutls' by default, so `tls-program' would not be
;; used in that case. Otherwise, people have reasons to not go with
;; `gnutls', we use `openssl' instead. For more details, see
;; https://redd.it/8sykl1
tls-program '("openssl s_client -connect %h:%p -CAfile %t -nbio -no_ssl3 -no_tls1 -no_tls1_1 -ign_eof"
"gnutls-cli -p %p --dh-bits=3072 --ocsp --x509cafile=%t \
--strict-tofu --priority='SECURE192:+SECURE128:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3' %h"
;; compatibility fallbacks ;; compatibility fallbacks
"gnutls-cli -p %p %h" "gnutls-cli -p %p %h"))
"openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof"))
;; Emacs stores authinfo in HOME and in plaintext. Let's not do that, mkay? This ;; Emacs stores `authinfo' in $HOME and in plain-text. Let's not do that, mkay?
;; file usually stores usernames, passwords, and other such treasures for the ;; This file stores usernames, passwords, and other such treasures for the
;; aspiring malicious third party. ;; aspiring malicious third party.
(setq auth-sources (list (expand-file-name "authinfo.gpg" doom-etc-dir) (setq auth-sources (list (concat doom-etc-dir "authinfo.gpg")
"~/.authinfo.gpg")) "~/.authinfo.gpg"))
;; Emacs on Windows frequently confuses HOME (C:\Users\<NAME>) and APPDATA, ;; Don't litter `doom-emacs-dir'. We don't use `no-littering' because it's a
;; causing `abbreviate-home-dir' to produce incorrect paths. ;; mote too opinionated for our needs.
(when IS-WINDOWS
(setq abbreviated-home-dir "\\`'"))
;; Don't litter `doom-emacs-dir'
(setq abbrev-file-name (concat doom-local-dir "abbrev.el") (setq abbrev-file-name (concat doom-local-dir "abbrev.el")
async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log")
bookmark-default-file (concat doom-etc-dir "bookmarks") bookmark-default-file (concat doom-etc-dir "bookmarks")
custom-file (concat doom-private-dir "init.el") custom-file (concat doom-private-dir "config.el")
custom-theme-directory (concat doom-private-dir "themes/") custom-theme-directory (concat doom-private-dir "themes/")
desktop-dirname (concat doom-etc-dir "desktop") desktop-dirname (concat doom-etc-dir "desktop")
desktop-base-file-name "autosave" desktop-base-file-name "autosave"
desktop-base-lock-name "autosave-lock" desktop-base-lock-name "autosave-lock"
pcache-directory (concat doom-cache-dir "pcache/") pcache-directory (concat doom-cache-dir "pcache/")
request-storage-directory (concat doom-cache-dir "request") request-storage-directory (concat doom-cache-dir "request")
server-auth-dir (concat doom-cache-dir "server/")
shared-game-score-directory (concat doom-etc-dir "shared-game-score/") shared-game-score-directory (concat doom-etc-dir "shared-game-score/")
tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/") tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/")
tramp-backup-directory-alist backup-directory-alist tramp-backup-directory-alist backup-directory-alist
@@ -227,13 +253,21 @@ users).")
:override #'emacs-session-filename :override #'emacs-session-filename
(concat doom-cache-dir "emacs-session." session-id)) (concat doom-cache-dir "emacs-session." session-id))
(defadvice! doom--save-enabled-commands-to-doomdir-a (orig-fn &rest args)
"When enabling a disabled command, the `put' call is written to
~/.emacs.d/init.el, which causes issues for Doom, so write it to the user's
config.el instead."
:around #'en/disable-command
(let ((user-init-file custom-file))
(apply orig-fn args)))
;; ;;
;;; Optimizations ;;; Optimizations
;; Disable bidirectional text rendering for a modest performance boost. Of ;; Disable bidirectional text rendering for a modest performance boost. I've set
;; course, this renders Emacs unable to detect/display right-to-left languages ;; this to `nil' in the past, but the `bidi-display-reordering's docs say that
;; (sorry!), but for us left-to-right language speakers/writers, it's a boon. ;; is an undefined state and suggest this to be just as good:
(setq-default bidi-display-reordering 'left-to-right (setq-default bidi-display-reordering 'left-to-right
bidi-paragraph-direction 'left-to-right) bidi-paragraph-direction 'left-to-right)
@@ -243,7 +277,8 @@ users).")
(setq highlight-nonselected-windows nil) (setq highlight-nonselected-windows nil)
;; More performant rapid scrolling over unfontified regions. May cause brief ;; More performant rapid scrolling over unfontified regions. May cause brief
;; spells of inaccurate fontification immediately after scrolling. ;; spells of inaccurate syntax highlighting right after scrolling, which should
;; quickly self-correct.
(setq fast-but-imprecise-scrolling t) (setq fast-but-imprecise-scrolling t)
;; Resizing the Emacs frame can be a terribly expensive part of changing the ;; Resizing the Emacs frame can be a terribly expensive part of changing the
@@ -254,30 +289,42 @@ users).")
;; Don't ping things that look like domain names. ;; Don't ping things that look like domain names.
(setq ffap-machine-p-known 'reject) (setq ffap-machine-p-known 'reject)
;; Font compacting can be terribly expensive, especially for rendering icon
;; fonts on Windows. Whether it has a notable affect on Linux and Mac hasn't
;; been determined, but we inhibit it there anyway.
(setq inhibit-compacting-font-caches t)
;; Performance on Windows is considerably worse than elsewhere, especially if ;; Performance on Windows is considerably worse than elsewhere, especially if
;; WSL is involved. We'll need everything we can get. ;; WSL is involved. We'll need everything we can get.
(when IS-WINDOWS (when IS-WINDOWS
;; Reduce the workload when doing file IO (setq w32-get-true-file-attributes nil ; slightly faster IO
(setq w32-get-true-file-attributes nil) w32-pipe-read-delay 0 ; faster ipc
w32-pipe-buffer-size (* 64 1024))) ; read more at a time (was 4K)
;; Font compacting can be terribly expensive, especially for rendering icon
;; fonts on Windows. Whether it has a noteable affect on Linux and Mac hasn't
;; been determined.
(setq inhibit-compacting-font-caches t))
;; Remove command line options that aren't relevant to our current OS; means ;; Remove command line options that aren't relevant to our current OS; means
;; slightly less to process at startup. ;; slightly less to process at startup.
(unless IS-MAC (setq command-line-ns-option-alist nil)) (unless IS-MAC (setq command-line-ns-option-alist nil))
(unless IS-LINUX (setq command-line-x-option-alist nil)) (unless IS-LINUX (setq command-line-x-option-alist nil))
;; 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)
;; Adopt a sneaky garbage collection strategy of waiting until idle time to ;; Adopt a sneaky garbage collection strategy of waiting until idle time to
;; collect; staving off the collector while the user is working. ;; collect; staving off the collector while the user is working.
(when doom-interactive-mode (setq gcmh-idle-delay 5
(add-transient-hook! 'pre-command-hook (gcmh-mode +1)) gcmh-high-cons-threshold (* 16 1024 1024) ; 16mb
(with-eval-after-load 'gcmh gcmh-verbose doom-debug-p)
(setq gcmh-idle-delay 10
gcmh-verbose doom-debug-mode) ;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some
(add-hook 'focus-out-hook #'gcmh-idle-garbage-collect))) ;; reason. Disabling it completely could have many side-effects, so we
;; defer it until later, at which time it (somehow) runs very quickly.
(unless (daemonp)
(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))))
;; ;;
@@ -290,18 +337,14 @@ users).")
"Run MODE-local-vars-hook after local variables are initialized." "Run MODE-local-vars-hook after local variables are initialized."
(run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode)) (run-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode))
#'doom-try-run-hook)) #'doom-try-run-hook))
(add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h)
;; If the user has disabled `enable-local-variables', then ;; If the user has disabled `enable-local-variables', then
;; `hack-local-variables-hook' is never triggered, so we trigger it at the end ;; `hack-local-variables-hook' is never triggered, so we trigger it at the end
;; of `after-change-major-mode-hook': ;; of `after-change-major-mode-hook':
(defun doom-run-local-var-hooks-if-necessary-h () (defun doom-run-local-var-hooks-maybe-h ()
"Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled." "Run `doom-run-local-var-hooks-h' if `enable-local-variables' is disabled."
(unless enable-local-variables (unless enable-local-variables
(doom-run-local-var-hooks-h))) (doom-run-local-var-hooks-h)))
(add-hook 'after-change-major-mode-hook
#'doom-run-local-var-hooks-if-necessary-h
'append)
;; ;;
@@ -326,21 +369,24 @@ If you want to disable incremental loading altogether, either remove
`doom-incremental-first-idle-timer' to nil. Incremental loading does not occur `doom-incremental-first-idle-timer' to nil. Incremental loading does not occur
in daemon sessions (they are loaded immediately at startup).") in daemon sessions (they are loaded immediately at startup).")
(defvar doom-incremental-first-idle-timer 2 (defvar doom-incremental-first-idle-timer 2.0
"How long (in idle seconds) until incremental loading starts. "How long (in idle seconds) until incremental loading starts.
Set this to nil to disable incremental loading.") Set this to nil to disable incremental loading.")
(defvar doom-incremental-idle-timer 1.5 (defvar doom-incremental-idle-timer 0.75
"How long (in idle seconds) in between incrementally loading packages.") "How long (in idle seconds) in between incrementally loading packages.")
(defvar doom-incremental-load-immediately (daemonp)
"If non-nil, load all incrementally deferred packages immediately at startup.")
(defun doom-load-packages-incrementally (packages &optional now) (defun doom-load-packages-incrementally (packages &optional now)
"Registers PACKAGES to be loaded incrementally. "Registers PACKAGES to be loaded incrementally.
If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer' If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer'
intervals." intervals."
(if (not now) (if (not now)
(nconc doom-incremental-packages packages) (appendq! doom-incremental-packages packages)
(while packages (while packages
(let ((req (pop packages))) (let ((req (pop packages)))
(unless (featurep req) (unless (featurep req)
@@ -357,7 +403,7 @@ intervals."
t) t)
(push req packages)) (push req packages))
((error debug) ((error debug)
(message "Failed to load '%s' package incrementally, because: %s" (message "Failed to load %S package incrementally, because: %s"
req e))) req e)))
(if (not packages) (if (not packages)
(doom-log "Finished incremental loading") (doom-log "Finished incremental loading")
@@ -370,33 +416,32 @@ intervals."
"Begin incrementally loading packages in `doom-incremental-packages'. "Begin incrementally loading packages in `doom-incremental-packages'.
If this is a daemon session, load them all immediately instead." If this is a daemon session, load them all immediately instead."
(if (daemonp) (if doom-incremental-load-immediately
(mapc #'require (cdr doom-incremental-packages)) (mapc #'require (cdr doom-incremental-packages))
(when (integerp doom-incremental-first-idle-timer) (when (numberp doom-incremental-first-idle-timer)
(run-with-idle-timer doom-incremental-first-idle-timer (run-with-idle-timer doom-incremental-first-idle-timer
nil #'doom-load-packages-incrementally nil #'doom-load-packages-incrementally
(cdr doom-incremental-packages) t)))) (cdr doom-incremental-packages) t))))
(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h)
;;
;;; Custom hooks
(defvar doom-first-input-hook nil
"Transient hooks run before the first user input.")
(defvar doom-first-file-hook nil
"Transient hooks run before the first interactively opened file.")
(defvar doom-first-buffer-hook nil
"Transient hooks run before the first interactively opened buffer.")
;; ;;
;;; Bootstrap helpers ;;; Bootstrap helpers
(defun doom-try-run-hook (hook)
"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
(funcall hook)
((debug error)
(signal 'doom-hook-error (list hook e))))
;; return nil so `run-hook-wrapped' won't short circuit
nil)
(defun doom-display-benchmark-h (&optional return-p) (defun doom-display-benchmark-h (&optional return-p)
"Display a benchmark, showing number of packages and modules, and how quickly "Display a benchmark including number of packages and modules loaded.
they were loaded at startup.
If RETURN-P, return the message as a string instead of displaying it." If RETURN-P, return the message as a string instead of displaying it."
(funcall (if return-p #'format #'message) (funcall (if return-p #'format #'message)
@@ -407,58 +452,13 @@ If RETURN-P, return the message as a string instead of displaying it."
(setq doom-init-time (setq doom-init-time
(float-time (time-subtract (current-time) before-init-time)))))) (float-time (time-subtract (current-time) before-init-time))))))
(defun doom-load-autoloads-file (file &optional noerror) (defun doom-initialize (&optional force-p)
"Tries to load FILE (an autoloads file).
Return t on success, nil otherwise (but logs a warning)."
(condition-case e
(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.
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 (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
(when (re-search-forward "^\\([^= ]+\\)=" nil t)
(line-beginning-position)))
(point-max))))
environment)))
(when environment
(setq process-environment
(append (nreverse environment) process-environment)
exec-path
(if (member "PATH" envvars)
(append (parse-colon-path (getenv "PATH"))
(list exec-directory))
exec-path)
shell-file-name
(if (member "SHELL" envvars)
(setq shell-file-name
(or (getenv "SHELL") shell-file-name))
shell-file-name))
envvars))))
(defun doom-initialize (&optional force-p noerror)
"Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil). "Bootstrap Doom, if it hasn't already (or if FORCE-P is non-nil).
The bootstrap process ensures that the essential directories exist, all core The bootstrap process ensures that everything Doom needs to run is set up;
packages are installed, `doom-autoload-file' and `doom-package-autoload-file' essential directories exist, core packages are installed, `doom-autoload-file'
exist and are loaded, and that `core-packages' is auto-loaded when `package' or is loaded (failing if it isn't), that all the needed hooks are in place, and
`straight' are. that `core-packages' will load when `package' or `straight' is used.
The overall load order of Doom is as follows: The overall load order of Doom is as follows:
@@ -470,7 +470,7 @@ The overall load order of Doom is as follows:
Module config.el files Module config.el files
~/.doom.d/config.el ~/.doom.d/config.el
`doom-init-modules-hook' `doom-init-modules-hook'
`after-init-hook' `doom-after-init-modules-hook' (`after-init-hook')
`emacs-startup-hook' `emacs-startup-hook'
`doom-init-ui-hook' `doom-init-ui-hook'
`window-setup-hook' `window-setup-hook'
@@ -480,74 +480,67 @@ for a list of all recognized module trees. Order defines precedence (from most
to least)." to least)."
(when (or force-p (not doom-init-p)) (when (or force-p (not doom-init-p))
(setq doom-init-p t) (setq doom-init-p t)
(doom-log "Initializing Doom")
;; Reset as much state as possible, so `doom-initialize' can be treated like ;; Reset as much state as possible, so `doom-initialize' can be treated like
;; a reset function. Particularly useful for reloading the config. ;; a reset function. e.g. when reloading the config.
(setq-default exec-path doom--initial-exec-path (setq-default exec-path doom--initial-exec-path
load-path doom--initial-load-path load-path doom--initial-load-path
process-environment doom--initial-process-environment) process-environment doom--initial-process-environment)
;; Load shell environment, optionally generated from 'doom env'. No need to ;; Doom caches a lot of information in `doom-autoload-file'. Module and
;; do so if we're in terminal Emacs, because Emacs will correctly inherit ;; package autoloads, autodefs like `set-company-backend!', and variables
;; like `doom-modules', `doom-disabled-packages', `load-path',
;; `auto-mode-alist', and `Info-directory-list'. etc. Compiling them into
;; one place is a big reduction in startup time.
(condition-case e
;; Avoid `file-name-sans-extension' for premature optimization reasons.
;; `string-remove-suffix' is cheaper because it performs no file sanity
;; checks; just plain ol' string manipulation.
(load (string-remove-suffix ".el" doom-autoload-file)
nil 'nomessage)
(file-missing
;; If the autoloads file fails to load then the user forgot to sync, or
;; aborted a doom command midway!
(signal 'doom-error
(list "Doom is in an incomplete state"
"run 'bin/doom sync' on the command line to repair it"))))
;; Load shell environment, optionally generated from 'doom env'. No need
;; to do so if we're in terminal Emacs, where Emacs correctly inherits
;; your shell environment there. ;; your shell environment there.
(when (and (or (display-graphic-p) (if (or (display-graphic-p)
(daemonp)) (daemonp))
(file-exists-p doom-env-file)) (doom-load-envvars-file doom-env-file 'noerror))
(doom-load-envvars-file doom-env-file))
;; Loads `use-package' and all the helper macros modules (and users) can use
;; to configure their packages.
(require 'core-modules) (require 'core-modules)
(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 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 noerror)))
(if (and core-autoloads-p pkg-autoloads-p (not force-p)) ;; There's a chance the user will want to use package.el or straight later
;; In case we want to use package.el or straight via M-x ;; on in this interactive session. If that's the case, make sure they're
(progn ;; properly initialized when they do.
(with-eval-after-load 'package (autoload 'doom-initialize-packages "core-packages")
(require 'core-packages)) (autoload 'doom-initialize-core-packages "core-packages")
(with-eval-after-load 'straight (with-eval-after-load 'package (require 'core-packages))
(require 'core-packages) (with-eval-after-load 'straight (doom-initialize-packages))
(doom-initialize-packages)))
;; Eagerly load these libraries because we may be in a session that hasn't been ;; Bootstrap the interactive session
;; fully initialized (e.g. where autoloads files haven't been generated or (add-hook! 'window-setup-hook
;; `load-path' populated). (add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h)
(mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix) (add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 'append)
(file-expand-wildcards (concat doom-core-dir "autoload/*.el"))) (add-hook 'doom-first-input-hook #'gcmh-mode)
(add-hook-trigger! 'doom-first-input-hook 'pre-command-hook)
(add-hook-trigger! 'doom-first-file-hook 'after-find-file 'dired-initial-position-hook)
(add-hook-trigger! 'doom-first-buffer-hook 'after-find-file 'doom-switch-buffer-hook))
(add-hook 'emacs-startup-hook #'doom-load-packages-incrementally-h)
(add-hook 'window-setup-hook #'doom-display-benchmark-h 'append)
(if doom-debug-p (doom-debug-mode +1))
;; Create all our core directories to quell file errors ;; Load core/core-*.el, the user's private init.el and their config.el
(dolist (dir (list doom-local-dir (doom-initialize-modules force-p))
doom-etc-dir
doom-cache-dir))
(unless (file-directory-p dir)
(make-directory dir 'parents)))
;; Ensure the package management system (and straight) are ready for doom-init-p)
;; action (and all core packages/repos are installed)
(require 'core-packages)
(doom-initialize-packages force-p))
(unless (or (and core-autoloads-p pkg-autoloads-p)
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"))))
t))
(defun doom-initialize-core ()
"Load Doom's core files for an interactive session."
(require 'core-keybinds)
(require 'core-ui)
(require 'core-projects)
(require 'core-editor))
(provide 'core) (provide 'core)
;;; core.el ends here ;;; core.el ends here
+47 -26
View File
@@ -2,39 +2,60 @@
;;; core/packages.el ;;; core/packages.el
;; core.el ;; core.el
(package! dotenv-mode) (package! auto-minor-mode :pin "17cfa1b54800fdef2975c0c0531dad34846a5065")
(package! auto-minor-mode) (package! gcmh :pin "b1bde5089169a74f62033d027e06e98cbeedd43f")
;; core-packages.el
(package! straight
:type 'core
:recipe `(:host github
:repo "raxod502/straight.el"
:branch ,straight-repository-branch
:local-repo "straight.el"
:files ("straight*.el")
:no-build t)
:pin "09cfa1b344cbeeea7da2e075df7c19262fb2b9e6")
;; core-modules.el
(package! use-package
:type 'core
:pin "d2640fec376a8458a669e7526e63e5870d875118")
;; core-ui.el ;; core-ui.el
(package! all-the-icons) (package! all-the-icons :pin "ed8e44de4fa601309d2bba902c3b37cb73e4daa0")
(package! hide-mode-line) (package! hide-mode-line :pin "88888825b5b27b300683e662fa3be88d954b1cea")
(package! highlight-numbers) (package! highlight-numbers :pin "8b4744c7f46c72b1d3d599d4fb75ef8183dee307")
(package! rainbow-delimiters) (package! rainbow-delimiters :pin "5125f4e47604ad36c3eb4706310fcafac729ca8c")
(package! restart-emacs) (package! restart-emacs :pin "9aa90d3df9e08bc420e1c9845ee3ff568e911bd9")
;; core-editor.el ;; core-editor.el
(package! better-jumper) (package! better-jumper :pin "6d240032ca213ccb3347e25f26c29b6822bf03a7")
(package! dtrt-indent) (package! dtrt-indent :pin "50c440c80e0d15303d8ab543bce4c56e9c2bf407")
(package! helpful) (package! helpful :pin "c0662aa07266fe204f4e6d72ccaa6af089400556")
(package! ns-auto-titlebar :ignore (not IS-MAC)) (when IS-MAC
(package! pcre2el) (package! ns-auto-titlebar :pin "1efc30d38509647b417f05587fd7003457719256"))
(package! smartparens) (package! pcre2el :pin "0b5b2a2c173aab3fd14aac6cf5e90ad3bf58fa7d")
(package! smartparens :pin "555626a43f9bb1985aa9a0eb675f2b88b29702c8")
(package! so-long (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 ;; 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 ;; I've created my own mirror for it because git.savannah.gnu.org runs
;; potato. ;; on a potato.
:recipe (:host github :repo "hlissner/emacs-so-long")) :recipe (:host github :repo "hlissner/emacs-so-long")
(package! undo-tree :pin "ed666b0716f60e8988c455804de24b55919e71ca")
;; Version 0.6.5 is on ELPA which lacks a fix we need, so we install 0.6.6 (package! ws-butler
;; from emacsmirror/undo-tree instead. ;; Use my fork of ws-butler, which has a few choice improvements and
:recipe (:host github :repo "emacsmirror/undo-tree")) ;; optimizations (the original has been abandoned).
(package! ws-butler) :recipe (:host github :repo "hlissner/ws-butler")
(package! xclip) :pin "2bb49d3ee7d2cba133bc7e9cdac416cd1c5e4fe0")
(unless IS-WINDOWS
(package! clipetty
:recipe (:host github :repo "spudlyo/clipetty")
:pin "01b39044b9b65fa4ea7d3166f8b1ffab6f740362"))
;; core-projects.el ;; core-projects.el
(package! projectile) (package! projectile :pin "7e552b6d876014ca5b4609318ca8a202b2a89014")
;; core-keybinds.el ;; core-keybinds.el
(package! general) (package! general :pin "a0b17d207badf462311b2eef7c065b884462cb7c")
(package! which-key) (package! which-key :pin "8b49ae978cceca65967f3544c236f32964ddbed0")
+12 -18
View File
@@ -1,25 +1,19 @@
Before you doom yourself, there are a few things you should know: But before you doom yourself, here are some things you should know:
1. Whenever you edit your doom! block in ~/.doom.d/init.el or modify your 1. Don't forget to run 'doom sync', then restart Emacs, after modifying
modules, run: ~/.doom.d/init.el or ~/.doom.d/packages.el.
bin/doom refresh This command ensures needed packages are installed, orphaned packages are
removed, and your autoloads/cache files are up to date. When in doubt, run
'doom sync'!
This will ensure all needed packages are installed, all orphaned packages are 2. If something goes wrong, run `doom doctor`. It diagnoses common issues with
removed, and your autoloads files are up to date. This is important! If you your environment and setup, and may offer clues about what is wrong.
forget to do this you will get errors!
2. If something inexplicably goes wrong, try `bin/doom doctor` 3. Use 'doom upgrade' to update Doom. Doing it any other way will require
additional steps. Run 'doom help upgrade' to understand those extra steps.
This will diagnose common issues with your environment and setup, and may 4. Access Doom's documentation from within Emacs via 'SPC h D' or 'C-h D' (or
give you clues about what is wrong. 'M-x doom/help')
3. Use `bin/doom upgrade` to update Doom. Doing it any other way may require
additional work. When in doubt, run `bin/doom refresh`.
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).
5. You can find Doom's documentation via `M-x doom/help` or `SPC h D`.
Have fun! Have fun!
-69
View File
@@ -1,69 +0,0 @@
;; -*- lexical-binding: t; no-byte-compile: t; -*-
;;; core/test/helpers.el
(eval-and-compile
(setq doom-interactive-mode 'test)
(doom-initialize 'force 'noerror)
(require 'buttercup)
(setq split-width-threshold 0
split-height-threshold 0
window-min-width 0
window-min-height 0))
;;
;;; Buttercup extensions
(buttercup-define-matcher-for-binary-function :to-equal-file file-equal-p)
(buttercup-define-matcher :to-expand-into (form expected)
(cl-destructuring-bind (form expected)
(mapcar #'funcall (list form expected))
(let ((expanded (macroexpand-1 form)))
(if (equal expanded expected)
(cons t (format "Expected `%S' to not expand to `%S'"
form expected))
(cons nil (format "Expected `%S' to not expand to `%S', but got `%S' instead"
form expected expanded))))))
(buttercup-define-matcher :to-output (form &optional expected-output)
(let ((expected-output (and (functionp expected-output)
(funcall expected-output)))
output)
(with-current-buffer (get-buffer "*Messages*")
(let ((standard-output (current-buffer))
(start (point)))
(let ((inhibit-message t))
(funcall form))
(setq output (buffer-substring-no-properties start (point-max)))
(with-silent-modifications (erase-buffer))))
(cond ((null expected-output)
(if (string-empty-p output)
(cons nil (format "Expected output %S, but got none"
expected-output))
(cons t (format "Expected no output, but got %S"
output))))
((not (equal expected-output output))
(cons nil (format "Expected output %S, but got %S instead"
expected-output output)))
((cons t (format "Expected to not get %S as output"
expected-output))))))
(buttercup-define-matcher :to-contain-items (items expected)
(cl-destructuring-bind (items expected)
(mapcar #'funcall (list items expected))
(if-let (missing (cl-set-difference expected items))
(cons nil (format "Expected list to contain %S, but it was missing %S"
expected missing))
(cons t (format "Expected list to not contain %S, but it did: %S"
expected items)))))
;;
;;; Helper macros
(defmacro insert!! (&rest text)
"Insert TEXT in buffer, then move cursor to last {0} marker."
`(progn
(insert ,@text)
(when (search-backward "{0}" nil t)
(replace-match "" t t))))
-17
View File
@@ -1,17 +0,0 @@
;;; core/test/init.el -*- lexical-binding: t; no-byte-compile: t; -*-
;; An init.el for our unit test suites. Do not use this!
(doom! :completion
company
:ui
doom-dashboard
popup
workspaces
:editor
evil
:tools
pass
:lang
org
web)
-4
View File
@@ -1,4 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/packages.el
(package! buttercup)
+3 -3
View File
@@ -3,9 +3,9 @@
(describe "core/autoload/format" (describe "core/autoload/format"
(describe "format!" (describe "format!"
:var (doom-format-backend) :var (doom-output-backend)
(before-all (before-all
(setq doom-format-backend 'ansi)) (setq doom-output-backend 'ansi))
(it "should be a drop-in replacement for `format'" (it "should be a drop-in replacement for `format'"
(expect (format! "Hello %s" "World") (expect (format! "Hello %s" "World")
@@ -16,7 +16,7 @@
:to-equal "Hello World")) :to-equal "Hello World"))
(it "supports text properties in interactive sessions" (it "supports text properties in interactive sessions"
(let ((doom-format-backend 'text-properties)) (let ((doom-output-backend 'text-properties))
(expect (get-text-property 0 'face (format! (red "Hello %s") "World")) (expect (get-text-property 0 'face (format! (red "Hello %s") "World"))
:to-equal (list :foreground (face-foreground 'term-color-red))))) :to-equal (list :foreground (face-foreground 'term-color-red)))))
-6
View File
@@ -101,12 +101,6 @@
(expect (appendq! list '(d e f)) (expect (appendq! list '(d e f))
:to-equal '(a b c 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!" (describe "delq!"
(it "delete's a symbol from a list" (it "delete's a symbol from a list"
(let ((list '(a b c))) (let ((list '(a b c)))
+3 -3
View File
@@ -2,9 +2,9 @@
;;; core/test/test-core.el ;;; core/test/test-core.el
(describe "core" (describe "core"
:var (doom-interactive-mode) :var (doom-interactive-p)
(before-each (before-each
(setq doom-interactive-mode nil)) (setq doom-interactive-p nil))
(describe "initialization" (describe "initialization"
(describe "doom-initialize" (describe "doom-initialize"
@@ -33,7 +33,7 @@
(expect 'doom-initialize-packages :to-have-been-called)) (expect 'doom-initialize-packages :to-have-been-called))
(it "doesn't initialize packages if core autoload file was loaded" (it "doesn't initialize packages if core autoload file was loaded"
(let ((doom-interactive-mode t)) (let ((doom-interactive-p t))
(spy-on 'doom-load-autoloads-file :and-return-value t) (spy-on 'doom-load-autoloads-file :and-return-value t)
(doom-initialize nil 'noerror) (doom-initialize nil 'noerror)
(expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file) (expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file)
+57 -38
View File
@@ -18,8 +18,8 @@ It is integrated into Helpful, in Doom.
- [[#disable-packages][disable-packages!]] - [[#disable-packages][disable-packages!]]
- [[#doom][doom!]] - [[#doom][doom!]]
- [[#file-exists-p][file-exists-p!]] - [[#file-exists-p][file-exists-p!]]
- [[#lambda][lambda!]] - [[#cmd][cmd!]]
- [[#lambda-1][lambda!!]] - [[#cmd-1][cmd!!]]
- [[#letenv][letenv!]] - [[#letenv][letenv!]]
- [[#load][load!]] - [[#load][load!]]
- [[#map][map!]] - [[#map][map!]]
@@ -28,11 +28,12 @@ It is integrated into Helpful, in Doom.
- [[#prependq][prependq!]] - [[#prependq][prependq!]]
- [[#quiet][quiet!]] - [[#quiet][quiet!]]
- [[#remove-hook][remove-hook!]] - [[#remove-hook][remove-hook!]]
- [[#setq][setq!]]
- [[#setq-hook][setq-hook!]] - [[#setq-hook][setq-hook!]]
- [[#unsetq-hook][unsetq-hook!]] - [[#unsetq-hook][unsetq-hook!]]
- [[#use-package][use-package!]] - [[#use-package][use-package!]]
- [[#interesting-snippets][Interesting snippets]] - [[#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]] - [[#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]] - [[#update-cursor-shape-under-terminal-emacs][Update cursor shape under terminal Emacs]]
@@ -237,38 +238,30 @@ It is integrated into Helpful, in Doom.
#+RESULTS: #+RESULTS:
: /home/hlissner/.emacs.d/LICENSE : /home/hlissner/.emacs.d/LICENSE
*** lambda! *** cmd!
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
(map! "C-j" (lambda! (newline) (indent-according-to-mode))) (map! "C-j" (cmd! (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 #+END_SRC
*** lambda!!
*** cmd!!
When ~newline~ is passed a numerical prefix argument (=C-u 5 M-x newline=), it 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 inserts N newlines. We can use ~cmd!!~ to easily create a keybinds that bakes in
in the prefix arg into the command call: the prefix arg into the command call:
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
(map! "C-j" (lambda!! #'newline 5)) (map! "C-j" (cmd!! #'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 #+END_SRC
Or to create aliases for functions that behave differently: Or to create aliases for functions that behave differently:
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
(fset 'insert-5-newlines (lambda!! #'newline 5)) (fset 'insert-5-newlines (cmd!! #'newline 5))
;; The equivalent of C-u M-x org-global-cycle, which resets the org document to ;; The equivalent of C-u M-x org-global-cycle, which resets the org document to
;; its startup visibility settings. ;; its startup visibility settings.
(fset 'org-reset-global-visibility (lambda!! #'org-global-cycle '(4)) (fset 'org-reset-global-visibility (cmd!! #'org-global-cycle '(4))
#+END_SRC #+END_SRC
*** letenv! *** letenv!
#+BEGIN_SRC elisp #+BEGIN_SRC elisp
(letenv! (("SHELL" "/bin/sh")) (letenv! (("SHELL" "/bin/sh"))
@@ -387,7 +380,7 @@ These are side-by-side comparisons, showing how to bind keys with and without
*** package! *** package!
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources ;; 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! evil)
(package! js2-mode) (package! js2-mode)
(package! rainbow-delimiters) (package! rainbow-delimiters)
@@ -404,15 +397,12 @@ 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: ;; you can tell the package manager not to clone the repo recursively:
(package! ansible :recipe (:nonrecursive t)) (package! ansible :recipe (:nonrecursive t))
;; To install a particular branch, commit or tag: ;; To pin a package to a specific commit:
(package! evil (package! evil :pin "e7bc39de2f9")
;; if :host and :fetcher aren't specified, the package manager will fall back ;; ...or branch:
;; to evil's default source provided by their (M)ELPA recipes:
:recipe (:commit "e7bc39de2f961505e8e112da8c1b315ae8afce52"))
(package! evil :recipe (:branch "stable")) (package! evil :recipe (:branch "stable"))
;; To unpin a pinned package:
(package! evil :recipe (:tag "1.2.9")) (package! evil :pin nil)
;; If you share your config between two computers, and don't want bin/doom ;; 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 ;; refresh to delete packages used only on one system, use :ignore
@@ -473,6 +463,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) ;; Removing arbitrary forms (must be exactly the same as the definition)
(remove-hook! (one-mode second-mode) (setq v 5) (setq a 2)) (remove-hook! (one-mode second-mode) (setq v 5) (setq a 2))
#+END_SRC #+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! *** setq-hook!
#+BEGIN_SRC elisp :eval no #+BEGIN_SRC elisp :eval no
;; Set multiple variables after a hook ;; Set multiple variables after a hook
@@ -515,21 +513,21 @@ These are side-by-side comparisons, showing how to bind keys with and without
:defer-incrementally t) :defer-incrementally t)
#+END_SRC #+END_SRC
* Interesting snippets * Interesting snippets
** Persist Emacs' initial frame size across sessions ** Center Emacs' initial frame with a fixed size
#+BEGIN_SRC elisp #+BEGIN_SRC elisp
(let ((display-height (display-pixel-height)) (let ((display-height (display-pixel-height))
(display-width (display-pixel-width))) (display-width (display-pixel-width)))
(add-to-list 'initial-frame-alist (pushnew! initial-frame-alist
`((left . ,(/ new-frame-width 2)) `(left . ,(/ display-width 2))
(top . ,(/ new-frame-height 2)) `(top . ,(/ display-height 2))
(width . ,(/ display-width 2)) `(width . ,display-width)
(height . ,(/ display-height 2))))) `(height . ,display-height)))
#+END_SRC #+END_SRC
** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions ** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions
#+BEGIN_SRC elisp #+BEGIN_SRC elisp
;; add to ~/.doom.d/config.el ;; add to ~/.doom.d/config.el
(when-let (dims (doom-cache-get 'last-frame-size)) (when-let (dims (doom-store-get 'last-frame-size))
(cl-destructuring-bind ((left . top) width height fullscreen) dims (cl-destructuring-bind ((left . top) width height fullscreen) dims
(setq initial-frame-alist (setq initial-frame-alist
(append initial-frame-alist (append initial-frame-alist
@@ -540,7 +538,7 @@ These are side-by-side comparisons, showing how to bind keys with and without
(fullscreen . ,fullscreen)))))) (fullscreen . ,fullscreen))))))
(defun save-frame-dimensions () (defun save-frame-dimensions ()
(doom-cache-set 'last-frame-size (doom-store-put 'last-frame-size
(list (frame-position) (list (frame-position)
(frame-width) (frame-width)
(frame-height) (frame-height)
@@ -567,3 +565,24 @@ than unwarranted characters.
Alternatively, an updated version exists at Alternatively, an updated version exists at
[[https://github.com/amosbird/evil-terminal-cursor-changer][amosbird/evil-terminal-cursor-changer]], with support for urxvt and tmux. [[https://github.com/amosbird/evil-terminal-cursor-changer][amosbird/evil-terminal-cursor-changer]], with support for urxvt and tmux.
** Create a paste-transient-state to cycle through kill ring on paste
Replaces the default evil-paste binding to paste then let you cycle through entries in your kill ring. Gives you more flexibility when copying to your clipboard, making edits, then deciding to paste after.
You will need to enable the `hydra` module first.
#+BEGIN_SRC elisp
(defhydra hydra-paste (:color red
:hint nil)
"\n[%s(length kill-ring-yank-pointer)/%s(length kill-ring)] \
[_C-j_/_C-k_] cycles through yanked text, [_p_/_P_] pastes the same text \
above or below. Anything else exits."
("C-j" evil-paste-pop)
("C-k" evil-paste-pop-next)
("p" evil-paste-after)
("P" evil-paste-before))
(map! :nv "p" #'hydra-paste/evil-paste-after
:nv "P" #'hydra-paste/evil-paste-before)
#+END_SRC
+27 -30
View File
@@ -1,14 +1,14 @@
#+TITLE: Contributing #+TITLE: Contributing
#+STARTUP: nofold #+STARTUP: nofold
I can't say Doom Emacs is a one man show anymore. It wouldn't have gotten this Doom Emacs is an active and ongoing project, maintained mostly by a single
far without the help of folks like you! Still, I struggle to maintain it all; person, but includes the efforts of 200 contributors and growing. There is no
especially the modules I do not use. If Doom has been useful to you, consider shortage of things that need doing; bugs that need stomping, features that need
pitching in and helping me out. 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 You are welcome to [[https://discord.gg/qvGgnVx][join us on our Discord server]], otherwise read on to learn how
documentation, or just spreading the good word. To ensure no toes get stepped on to contribute to our fine corner of the interwebs.
or wires crossed, this guide was created to help you help us.
* Table of Contents :TOC_3: * Table of Contents :TOC_3:
- [[#where-can-i-help][Where can I help?]] - [[#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-documentation][Contributing documentation]]
- [[#contributing-to-dooms-manual][Contributing to Doom's manual]] - [[#contributing-to-dooms-manual][Contributing to Doom's manual]]
- [[#contributing-module-documentation][Contributing module documentation]] - [[#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]] - [[#other-ways-to-support-doom-emacs][Other ways to support Doom Emacs]]
- [[#special-thanks][Special thanks]] - [[#special-thanks][Special thanks]]
* Where can I help? * 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, + 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 + 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. 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) + 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. 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]]. + 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 + 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. redirect your efforts.
+ The [[https://github.com/hlissner/doom-emacs/projects/2][plugins under review board]] lists third party plugins being considered (or + 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 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. cause) perhaps you can address them at the source.
* TODO Reporting issues * TODO Reporting issues
So you've found a problem. Before you fire off that bug report, there are a few You've found a problem and you're ready to fire off that bug report. Hold up!
things you should try first: 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 An effective bug report is informative. Please try to provide:
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 =<leader> 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 =<leader> 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.
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]]. + A backtrace of all mentioned errors.
Please make sure of the following before you submit: + 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 ** 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 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 Other ways to support Doom Emacs
* TODO Special thanks * TODO Special thanks
+474 -423
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+39 -209
View File
@@ -1,16 +1,20 @@
#+TITLE: Doom Emacs Documentation #+TITLE: Doom Emacs Documentation
#+STARTUP: nofold #+STARTUP: nofold
Doom Emacs is a configuration for [[https://www.gnu.org/software/emacs/][GNU Emacs]] written by a stubborn, Doom is a configuration framework for [[https://www.gnu.org/software/emacs/][GNU Emacs 26.3+]] tailored for Emacs
shell-dwelling, and melodramatic ex-vimmer. It is designed to be a foundation bankruptcy veterans who want less framework in their frameworks and the
for your own Emacs configuration or a resource for enthusiasts to learn more performance of a hand rolled config (or better). It can be a foundation for your
about our favorite OS. 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 #+begin_quote
Github fails to render org links to sub-sections, so it is recommended that you The documentation is designed to be viewed within Doom Emacs. Access it by
view the documentation from within Doom Emacs by pressing =<help> d h= (=<help>= pressing =SPC h d h= (or =C-h d h= for non-evil users), or search it with =SPC h
is =SPC h= for evil users and =C-h= for vanilla users) or searching it with d s= (or =C-h d s=).
=<help> d /=.
#+end_quote #+end_quote
* Table of Contents :TOC: * 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]] - [[#frequently-asked-questions][Frequently Asked Questions]]
- [[#contributing][Contributing]] - [[#contributing][Contributing]]
- [[#workflow-tips-tricks--tutorials][Workflow Tips, Tricks & Tutorials]] - [[#workflow-tips-tricks--tutorials][Workflow Tips, Tricks & Tutorials]]
- [[#module-appendix][Module Appendix]]
- [[#community-resources][Community Resources]] - [[#community-resources][Community Resources]]
- [[#asking-for-help][Asking for help]] - [[#asking-for-help][Asking for help]]
- [[#project-roadmap][Project roadmap]] - [[#project-roadmap][Project roadmap]]
- [[#tutorials--guides][Tutorials & guides]] - [[#tutorials--guides][Tutorials & guides]]
- [[#module-list][Module list]] - [[#projects-that-supportcomplement-doom][Projects that support/complement Doom]]
- [[#app][:app]] - [[#similar-projects][Similar projects]]
- [[#completion][:completion]]
- [[#config][:config]]
- [[#editor][:editor]]
- [[#emacs][:emacs]]
- [[#email][:email]]
- [[#input][:input]]
- [[#lang][:lang]]
- [[#term][:term]]
- [[#tools][:tools]]
- [[#ui][:ui]]
* TODO Release Notes * TODO Release Notes
* Documentation * Documentation
** [[file:getting_started.org][Getting Started]] ** [[file:getting_started.org][Getting Started]]
- [[file:getting_started.org::*Install][Install]] - How to install Emacs, Doom and its plugins - [[file:getting_started.org::*Install][Install]]
- [[file:getting_started.org::*Update][Update]] - Keep Doom and its packages up-to-date - [[file:getting_started.org::*Update & Rollback][Update & Rollback]]
- [[file:getting_started.org::*Customize][Customize]] - A primer on customizing and reconfiguring Doom - [[file:getting_started.org::*Configure][Configure]]
- [[file:getting_started.org::*Troubleshoot][Troubleshoot]] - How to debug Emacs & Doom, find help or look up documentation - [[file:getting_started.org::*Migrate][Migrate]]
- [[file:getting_started.org::*Troubleshoot][Troubleshoot]]
** [[file:faq.org][Frequently Asked Questions]] ** [[file:faq.org][Frequently Asked Questions]]
- [[file:faq.org::*General][General]] - [[file:faq.org::*General][General]]
- [[file:faq.org::*Configuration][Configuration]] - [[file:faq.org::*Configuration][Configuration]]
- [[file:faq.org::*Package Management][Package Management]]
- [[file:faq.org::*Defaults][Defaults]] - [[file:faq.org::*Defaults][Defaults]]
- [[file:faq.org::Common Issues][Common Issues]] - [[file:faq.org::Common Issues][Common Issues]]
- [[file:faq.org::Contributing][Contributing]] - [[file:faq.org::Contributing][Contributing]]
** TODO [[file:contributing.org][Contributing]] ** TODO [[file:contributing.org][Contributing]]
- [[file:contributing.org::*Where can I help?][Where to get help]] - [[file:contributing.org::*Where can I help?][Where to get help?]]
- Writing an effective bug report - Reporting issues
- Suggesting features, keybinds or enhancements - Suggesting features, keybinds and enhancements
- Contributing code - Contributing code or documentation
- Contributing documentation
- Other ways to support Doom Emacs - Other ways to support Doom Emacs
- Special thanks - Special thanks
** TODO [[file:workflow.org][Workflow Tips, Tricks & Tutorials]] ** TODO [[file:workflow.org][Workflow Tips, Tricks & Tutorials]]
** [[file:modules.org][Module Appendix]]
* Community Resources * Community Resources
** Asking for help ** 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]] - [[https://github.com/hlissner/doom-emacs/issues][Our issue tracker]]
** Project roadmap ** Project roadmap
@@ -79,11 +77,12 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
** Tutorials & guides ** Tutorials & guides
+ *Doom Emacs* + *Doom Emacs*
- (videos) [[https://www.youtube.com/playlist?list=PLyy8KUDC8P7X6YkegqrnEnymzMWCNB4bN][Doom Emacs Tutorials]] by [[https://www.youtube.com/channel/UCVls1GmFKf6WlTraIb_IaJg][DistroTube]]
- (videos) [[https://www.youtube.com/playlist?list=PLhXZp00uXBk4np17N39WvB80zgxlZfVwj][DoomCasts]] by @zaiste
- [[https://noelwelsh.com/posts/2019-01-10-doom-emacs.html][Noel's crash course on Doom Emacs]] - [[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/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://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)]] - (video) [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]]
- [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]]
+ *Emacs & Emacs Lisp* + *Emacs & Emacs Lisp*
- [[https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html][The Official Emacs manual]] - [[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 - A variety of Emacs resources - https://github.com/ema2159/awesome-emacs
@@ -92,185 +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 - http://steve-yegge.blogspot.com/2008/01/emergency-elisp.html
- Workflows for customizing Emacs and its packages (and its C/C++ modes): - Workflows for customizing Emacs and its packages (and its C/C++ modes):
- https://david.rothlis.net/emacs/customize_c.html - https://david.rothlis.net/emacs/customize_c.html
+ *Tools in Emacs* - *Tools in Emacs*
- [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]] - [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]]
+ *Vim & Evil* + *Vim & Evil*
- [[https://gist.github.com/dmsul/8bb08c686b70d5a68da0e2cb81cd857f][A crash course on modal editing and Ex commands]] - [[https://gist.github.com/dmsul/8bb08c686b70d5a68da0e2cb81cd857f][A crash course on modal editing and Ex commands]]
* Module list ** Projects that support/complement Doom
** :app + [[https://github.com/plexus/chemacs][plexus/chemacs]]
Application modules are complex and opinionated modules that transform Emacs + [[https://github.com/r-darwish/topgrade][r-darwish/topgrade]]
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 ** Similar projects
+ [[file:../modules/app/irc/README.org][irc]] - how neckbeards socialize + [[https://github.com/purcell/emacs.d][purcell/emacs.d]]
+ rss =+org= - an RSS client in Emacs + [[https://github.com/seagle0128/.emacs.d][seagle0128/.emacs.d]]
+ [[file:../modules/app/twitter/README.org][twitter]] - A twitter client for Emacs + [[https://github.com/syl20bnr/spacemacs][syl20bnr/spacemacs]]
+ [[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
+ [[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
+ 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]] =+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]] - 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]] =+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
+ [[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
+18 -2
View File
@@ -1,7 +1,15 @@
#+TITLE: Workflow tips, tricks & tutorials #+TITLE: Getting to know Doom Emacs
#+STARTUP: nofold #+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: * Table of Contents :TOC:
- [[#day-1-in-doom-emacs][Day 1 in Doom Emacs]] - [[#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]] - [[#pipe-text-through-ex-commands-and-programs][Pipe text through ex commands and programs]]
- [[#transposingswapping-text][Transposing/swapping text]] - [[#transposingswapping-text][Transposing/swapping text]]
- [[#managing-your-projects][Managing your projects]] - [[#managing-your-projects][Managing your projects]]
- [[#reconfiguring-emacs-on-a-per-project-basis][Reconfiguring Emacs on a per-project basis]]
- [[#search--replace][Search & replace]] - [[#search--replace][Search & replace]]
- [[#project-wide-text-search][Project-wide text search]] - [[#project-wide-text-search][Project-wide text search]]
- [[#search--replace-1][Search & replace]] - [[#search--replace-1][Search & replace]]
@@ -32,6 +41,8 @@ This page is a WIP.
- [[#using-emacs-for][Using Emacs for...]] - [[#using-emacs-for][Using Emacs for...]]
- [[#writing-fiction][Writing fiction]] - [[#writing-fiction][Writing fiction]]
- [[#writing-papers][Writing papers]] - [[#writing-papers][Writing papers]]
- [[#note-keeping][Note-keeping]]
- [[#a-personal-organizer][A Personal Organizer]]
- [[#composing-music][Composing music]] - [[#composing-music][Composing music]]
- [[#game-development][Game development]] - [[#game-development][Game development]]
- [[#web-development][Web development]] - [[#web-development][Web development]]
@@ -48,6 +59,9 @@ This page is a WIP.
** TODO Pipe text through ex commands and programs ** TODO Pipe text through ex commands and programs
** TODO Transposing/swapping text ** TODO Transposing/swapping text
* TODO Managing your projects * TODO Managing your projects
** TODO Reconfiguring Emacs on a per-project basis
*** TODO .dir-locals.el
*** TODO editorconfig
* TODO Search & replace * TODO Search & replace
** TODO Project-wide text search ** TODO Project-wide text search
** TODO Search & replace ** TODO Search & replace
@@ -66,6 +80,8 @@ This page is a WIP.
* TODO Using Emacs for... * TODO Using Emacs for...
** TODO Writing fiction ** TODO Writing fiction
** TODO Writing papers ** TODO Writing papers
** TODO Note-keeping
** TODO A Personal Organizer
** TODO Composing music ** TODO Composing music
** TODO Game development ** TODO Game development
** TODO Web development ** TODO Web development
+1
View File
@@ -10,6 +10,7 @@
;; loaded, but after `early-init-file'. Doom handles package initialization, so ;; loaded, but after `early-init-file'. Doom handles package initialization, so
;; we must prevent Emacs from doing it early! ;; we must prevent Emacs from doing it early!
(setq package-enable-at-startup nil) (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. ;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
(push '(menu-bar-lines . 0) default-frame-alist) (push '(menu-bar-lines . 0) default-frame-alist)
+2 -9
View File
@@ -28,9 +28,8 @@
;;; License: MIT ;;; License: MIT
;; A big contributor to startup times is garbage collection. We up the gc ;; 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 ;; threshold to temporarily prevent it from running, then reset it later by
;; `doom-restore-garbage-collection-h'. Not resetting it will cause ;; enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
;; stuttering/freezes.
(setq gc-cons-threshold most-positive-fixnum) (setq gc-cons-threshold most-positive-fixnum)
;; In noninteractive sessions, prioritize non-byte-compiled source files to ;; In noninteractive sessions, prioritize non-byte-compiled source files to
@@ -48,9 +47,3 @@
;; And let 'er rip! ;; And let 'er rip!
(doom-initialize) (doom-initialize)
(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)))
+44 -36
View File
@@ -1,12 +1,18 @@
;;; init.el -*- lexical-binding: t; -*- ;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install' ;; This file controls what Doom modules are enabled and what order they load
;; will do this for you). The `doom!' block below controls what modules are ;; in. Remember to run 'doom sync' after modifying it!
;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; 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 a "Module Index" link where you'll find
;; a comprehensive list 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 c 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 ;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; found in modules/README.org. ;; directory (for easy access to its source code).
(doom! :input (doom! :input
;;chinese ;;chinese
@@ -27,21 +33,21 @@
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;hydra ;;hydra
;;indent-guides ; highlighted indent columns ;;indent-guides ; highlighted indent columns
;;minimap ; show a map of the code on the side
modeline ; snazzy, Atom-inspired modeline, plus API modeline ; snazzy, Atom-inspired modeline, plus API
nav-flash ; blink the current line after jumping ;;nav-flash ; blink cursor line after big motions
;;neotree ; a project drawer, like NERDTree for vim ;;neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on ophints ; highlight the region an operation acts on
(popup ; tame sudden yet inevitable temporary windows (popup +defaults) ; tame sudden yet inevitable temporary windows
+all ; catch all popups that start with an asterix ;;pretty-code ; ligatures or substitute text with pretty symbols
+defaults) ; default popup rules
;;pretty-code ; replace bits of code with pretty symbols
;;tabs ; an tab bar for Emacs ;;tabs ; an tab bar for Emacs
;;treemacs ; a project drawer, like neotree but cooler ;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages ;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe vc-gutter ; vcs diff in the fringe
vi-tilde-fringe ; fringe tildes to mark beyond EOB vi-tilde-fringe ; fringe tildes to mark beyond EOB
window-select ; visually switch windows ;;window-select ; visually switch windows
workspaces ; tab emulation, persistence & separate workspaces workspaces ; tab emulation, persistence & separate workspaces
;;zen ; distraction-free coding or writing
:editor :editor
(evil +everywhere); come to the dark side, we have cookies (evil +everywhere); come to the dark side, we have cookies
@@ -50,24 +56,30 @@
;;(format +onsave) ; automated prettiness ;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys ;;god ; run Emacs commands without modifier keys
;;lispy ; vim for lisp, for people who don't like vim ;;lispy ; vim for lisp, for people who don't like vim
multiple-cursors ; editing in many places at once ;;multiple-cursors ; editing in many places at once
;;objed ; text object editing for the innocent ;;objed ; text object editing for the innocent
;;parinfer ; turn lisp into python, sort of ;;parinfer ; turn lisp into python, sort of
rotate-text ; cycle region at point between text candidates ;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to snippets ; my elves. They type so I don't have to
;;word-wrap ; soft wrapping with language-aware indent ;;word-wrap ; soft wrapping with language-aware indent
:emacs :emacs
dired ; making dired pretty [functional] dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent electric ; smarter, keyword-based electric-indent
ibuffer ; interactive buffer management ;;ibuffer ; interactive buffer management
undo ; persistent, smarter undo for your inevitable mistakes
vc ; version-control and Emacs, sitting in a tree vc ; version-control and Emacs, sitting in a tree
:term :term
;;eshell ; a consistent, cross-platform shell (WIP) ;;eshell ; the elisp shell that works everywhere
;;shell ; a terminal REPL for Emacs ;;shell ; simple shell REPL for Emacs
;;term ; terminals in Emacs ;;term ; basic terminal emulator for Emacs
;;vterm ; another terminals in Emacs ;;vterm ; the best terminal emulation 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 :tools
;;ansible ;;ansible
@@ -77,11 +89,8 @@
;;editorconfig ; let someone else argue about tabs vs spaces ;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs ;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls) (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 ;;gist ; interacting with github gists
(lookup ; helps you navigate your code and documentation lookup ; navigate your code and its documentation
+docsets) ; ...or in Dash docsets locally
;;lsp ;;lsp
;;macos ; MacOS-specific commands ;;macos ; MacOS-specific commands
magit ; a git porcelain for Emacs magit ; a git porcelain for Emacs
@@ -90,20 +99,21 @@
;;pdf ; pdf enhancements ;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders ;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings ;;rgb ; creating color strings
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code ;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux ;;tmux ; an API for interacting with tmux
;;upload ; map local to remote projects via ssh/ftp ;;upload ; map local to remote projects via ssh/ftp
:lang :lang
;;agda ; types of types of types of types... ;;agda ; types of types of types of types...
;;assembly ; assembly for fun or debugging
;;cc ; C/C++/Obj-C madness ;;cc ; C/C++/Obj-C madness
;;clojure ; java with a lisp ;;clojure ; java with a lisp
;;common-lisp ; if you've seen one lisp, you've seen them all ;;common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs ;;coq ; proofs-as-programs
;;crystal ; ruby at the speed of c ;;crystal ; ruby at the speed of c
;;csharp ; unity, .NET, and mono shenanigans ;;csharp ; unity, .NET, and mono shenanigans
data ; config/data formats ;;data ; config/data formats
;;(dart +flutter) ; paint ui and not much else
;;elixir ; erlang done right ;;elixir ; erlang done right
;;elm ; care for a cup of TEA? ;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses emacs-lisp ; drown in parentheses
@@ -111,10 +121,13 @@
;;ess ; emacs speaks statistics ;;ess ; emacs speaks statistics
;;faust ; dsp, but you get to keep your soul ;;faust ; dsp, but you get to keep your soul
;;fsharp ; ML stands for Microsoft's Language ;;fsharp ; ML stands for Microsoft's Language
;;go ; the hipster dialect ;;fstar ; (dependent) types and (monadic) effects and Z3
;;gdscript ; the language you waited for
;;(go +lsp) ; the hipster dialect
;;(haskell +dante) ; 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 ;;hy ; readability of scheme w/ speed of python
;;idris ; ;;idris ;
;;json ; At least it ain't XML
;;(java +meghanada) ; the poster child for carpal tunnel syndrome ;;(java +meghanada) ; the poster child for carpal tunnel syndrome
;;javascript ; all(hope(abandon(ye(who(enter(here)))))) ;;javascript ; all(hope(abandon(ye(who(enter(here))))))
;;julia ; a better, faster MATLAB ;;julia ; a better, faster MATLAB
@@ -128,31 +141,27 @@
;;nim ; python + lisp at the speed of c ;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!" ;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel ;;ocaml ; an objective camel
(org ; organize your plain life in plain text 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
;;+pomodoro ; be fruitful with the tomato technique
+present) ; using org-mode for presentations
;;perl ; write code no one else can comprehend
;;php ; perl's insecure younger brother ;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more ;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional ;;purescript ; javascript, but functional
;;python ; beautiful is better than ugly ;;python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever ;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs ;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client ;;rest ; Emacs as a REST client
;;rst ; ReST in peace ;;rst ; ReST in peace
;;ruby ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"} ;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap() ;;rust ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
;;scala ; java, but good ;;scala ; java, but good
;;scheme ; a fully conniving family of lisps ;;scheme ; a fully conniving family of lisps
sh ; she sells {ba,z,fi}sh shells on the C xor sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No. ;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables? ;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance. ;;terra ; Earth and Moon in alignment for performance.
;;web ; the tubes ;;web ; the tubes
;;yaml ; JSON, but readable
:email :email
;;(mu4e +gmail) ;;(mu4e +gmail)
@@ -164,7 +173,6 @@
;;irc ; how neckbeards socialize ;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader ;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought ;;twitter ; twitter client https://twitter.com/vnought
;;write ; emacs for writers (fiction, notes, papers, etc.)
:config :config
;;literate ;;literate
+1 -1
View File
@@ -62,5 +62,5 @@ To control what org files ~clfw:org-create-source~ will use, ~let~-bind
The [[https://github.com/kiwanami/emacs-calfw][kiwanami/emacs-calfw]] project readme contains more examples. The [[https://github.com/kiwanami/emacs-calfw][kiwanami/emacs-calfw]] project readme contains more examples.
** Synchronizing Org and Google Calendar ** Synchronizing Org and Google Calendar
The [[https://github.com/myuhe/org-gcal.el][myuhe/org-gcal.el]] project README contains more detailed instructions on how The [[https://github.com/kidd/org-gcal.el][kidd/org-gcal.el]] project README contains more detailed instructions on how
to link your calendar with Google calendars. to link your calendar with Google calendars.
+3 -3
View File
@@ -3,9 +3,9 @@
(defvar +calendar--wconf nil) (defvar +calendar--wconf nil)
(defun +calendar--init () (defun +calendar--init ()
(if-let* ((win (cl-loop for win in (doom-visible-windows) (if-let (win (cl-find-if (lambda (b) (string-match-p "^\\*cfw:" (buffer-name b)))
if (string-match-p "^\\*cfw:" (buffer-name (window-buffer win))) (doom-visible-windows)
return win))) :key #'window-buffer))
(select-window win) (select-window win)
(call-interactively +calendar-open-function))) (call-interactively +calendar-open-function)))
+3 -3
View File
@@ -42,10 +42,10 @@
org-gcal-fetch org-gcal-fetch
org-gcal-post-at-point org-gcal-post-at-point
org-gcal-delete-at-point) org-gcal-delete-at-point)
:init
(defvar org-gcal-dir (concat doom-cache-dir "org-gcal/"))
(defvar org-gcal-token-file (concat org-gcal-dir "token.gpg"))
:config :config
;; hack to avoid the deferred.el error ;; hack to avoid the deferred.el error
(defun org-gcal--notify (title mes) (defun org-gcal--notify (title mes)
(message "org-gcal::%s - %s" title mes))) (message "org-gcal::%s - %s" title mes)))
;; (use-package! alert)
+3 -3
View File
@@ -1,6 +1,6 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; app/calendar/packages.el ;;; app/calendar/packages.el
(package! calfw) (package! calfw :pin "03abce97620a4a7f7ec5f911e669da9031ab9088")
(package! calfw-org) (package! calfw-org :pin "03abce97620a4a7f7ec5f911e669da9031ab9088")
(package! org-gcal) (package! org-gcal :pin "744505832b34e07b44a5d97d8720b2d7492d7fc9")
+35 -12
View File
@@ -7,8 +7,11 @@
- [[#description][Description]] - [[#description][Description]]
- [[#module-flags][Module Flags]] - [[#module-flags][Module Flags]]
- [[#plugins][Plugins]] - [[#plugins][Plugins]]
- [[#dependencies][Dependencies]]
- [[#prerequisites][Prerequisites]] - [[#prerequisites][Prerequisites]]
- [[#macos][macOS]]
- [[#debian--ubuntu][Debian / Ubuntu]]
- [[#arch-linux][Arch Linux]]
- [[#nixos][NixOS]]
- [[#features][Features]] - [[#features][Features]]
- [[#an-irc-client-in-emacs][An IRC Client in Emacs]] - [[#an-irc-client-in-emacs][An IRC Client in Emacs]]
- [[#configuration][Configuration]] - [[#configuration][Configuration]]
@@ -17,7 +20,7 @@
- [[#troubleshooting][Troubleshooting]] - [[#troubleshooting][Troubleshooting]]
* Description * 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 ** Module Flags
This module provides no flags. This module provides no flags.
@@ -26,11 +29,27 @@ This module provides no flags.
+ [[https://github.com/jorgenschaefer/circe][circe]] + [[https://github.com/jorgenschaefer/circe][circe]]
+ [[https://github.com/eqyiel/circe-notifications][circe-notifications]] + [[https://github.com/eqyiel/circe-notifications][circe-notifications]]
* Dependencies
This module requires =gnutls-cli= or =openssl= for secure connections.
* Prerequisites * 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 * Features
** An IRC Client in Emacs ** 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 | | ~circe-reconnect~ | =SPC m R= | Reconnect the current server |
* Configuration * 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~. takes the same arguments as ~circe-network-options~.
#+BEGIN_SRC emacs-lisp :tangle no #+BEGIN_SRC emacs-lisp :tangle no
;; if you omit =:host=, ~SERVER~ will be used instead.
(after! circe
(set-irc-server! "chat.freenode.net" (set-irc-server! "chat.freenode.net"
`(:tls t `(:tls t
:port 6697
:nick "doom" :nick "doom"
:sasl-username "myusername" :sasl-username "myusername"
:sasl-password "mypassword" :sasl-password "mypassword"
:channels ("#emacs"))) :channels ("#emacs"))))
#+END_SRC #+END_SRC
*It is a obviously a bad idea to store auth-details in plaintext,* so here are However, *it is a obviously a bad idea to store your password in plaintext,* so
some ways to avoid that: here are ways to avoid that:
** Pass: the unix password manager ** 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 [[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 #+BEGIN_SRC emacs-lisp :tangle no
(set-irc-server! "chat.freenode.net" (set-irc-server! "chat.freenode.net"
`(:tls t `(:tls t
:port 6697
:nick "doom" :nick "doom"
:sasl-username ,(+pass-get-user "irc/freenode.net") :sasl-username ,(+pass-get-user "irc/freenode.net")
:sasl-password ,(+pass-get-secret "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 listed in =+pass-user-fields= (by default =login=, =user==, =username== and
=email=)=). An example configuration looks like =email=)=). An example configuration looks like
#+BEGIN_SRC txt :tangle no #+begin_example
mysecretpassword mysecretpassword
username: myusername username: myusername
#+END_SRC #+end_example
** Emacs' auth-source API ** 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 ~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
+20 -22
View File
@@ -3,15 +3,18 @@
(defvar +irc--workspace-name "*IRC*") (defvar +irc--workspace-name "*IRC*")
(defun +irc-setup-wconf (&optional inhibit-workspace) (defun +irc-setup-wconf (&optional inhibit-workspace)
(unless inhibit-workspace (when (and (featurep! :ui workspaces)
(+workspace-switch +irc--workspace-name t)) (not inhibit-workspace))
(+workspace-switch +irc--workspace-name 'auto-create))
(let ((buffers (doom-buffers-in-mode 'circe-mode nil t))) (let ((buffers (doom-buffers-in-mode 'circe-mode nil t)))
(if (not (member (window-buffer) buffers))
(if buffers (if buffers
(ignore (switch-to-buffer (car buffers))) (ignore (switch-to-buffer (car buffers)))
(require 'circe) (require 'circe)
(delete-other-windows) (delete-other-windows)
(switch-to-buffer (doom-fallback-buffer)) (switch-to-buffer (doom-fallback-buffer))
t))) t)
(user-error "IRC buffer is already active and selected"))))
;;;###autoload ;;;###autoload
(defun =irc (&optional inhibit-workspace) (defun =irc (&optional inhibit-workspace)
@@ -20,17 +23,12 @@
If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new
workspace for it." workspace for it."
(interactive "P") (interactive "P")
(cond ((and (featurep! :ui workspaces) (+irc-setup-wconf inhibit-workspace)
(+workspace-exists-p +irc--workspace-name)) (cond ((doom-buffers-in-mode 'circe-mode (doom-buffer-list) t)
(+workspace-switch +irc--workspace-name)) (message "Circe buffers are already open"))
((not (+irc-setup-wconf inhibit-workspace)) (circe-network-options
(user-error "Couldn't start up a workspace for IRC"))) (mapc #'circe (mapcar #'car circe-network-options)))
(if (doom-buffers-in-mode 'circe-mode (buffer-list) t) ((call-interactively #'circe))))
(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))))
;;;###autoload ;;;###autoload
(defun +irc/connect (&optional inhibit-workspace) (defun +irc/connect (&optional inhibit-workspace)
@@ -52,16 +50,17 @@ workspace for it."
(defun +irc/quit () (defun +irc/quit ()
"Kill current circe session and workgroup." "Kill current circe session and workgroup."
(interactive) (interactive)
(if (y-or-n-p "Really kill IRC session?") (unless (y-or-n-p "Really kill IRC session?")
(user-error "Aborted"))
(let (circe-channel-killed-confirmation (let (circe-channel-killed-confirmation
circe-server-killed-confirmation) circe-server-killed-confirmation)
(when +irc--defer-timer (when +irc--defer-timer
(cancel-timer +irc--defer-timer)) (cancel-timer +irc--defer-timer))
(disable-circe-notifications) (disable-circe-notifications)
(mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t)) (mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t))
(when (featurep! :ui workspaces)
(when (equal (+workspace-current-name) +irc--workspace-name) (when (equal (+workspace-current-name) +irc--workspace-name)
(+workspace/delete +irc--workspace-name))) (+workspace/delete +irc--workspace-name)))))
(message "Aborted")))
;;;###autoload ;;;###autoload
(defun +irc/ivy-jump-to-channel (&optional this-server) (defun +irc/ivy-jump-to-channel (&optional this-server)
@@ -93,7 +92,7 @@ argument) is non-nil only show channels in current server."
;;;###autoload ;;;###autoload
(defun +irc/tracking-next-buffer () (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) (interactive)
(when (derived-mode-p 'circe-mode) (when (derived-mode-p 'circe-mode)
(tracking-next-buffer))) (tracking-next-buffer)))
@@ -106,13 +105,12 @@ argument) is non-nil only show channels in current server."
(defun +circe-buffer-p (buf) (defun +circe-buffer-p (buf)
"Return non-nil if BUF is a `circe-mode' buffer." "Return non-nil if BUF is a `circe-mode' buffer."
(with-current-buffer buf (with-current-buffer buf
(and (derived-mode-p 'circe-mode) (derived-mode-p 'circe-mode)))
(eq (safe-persp-name (get-current-persp))
+irc--workspace-name))))
;;;###autoload ;;;###autoload
(defun +irc--add-circe-buffer-to-persp-h () (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)) (let ((persp (get-current-persp))
(buf (current-buffer))) (buf (current-buffer)))
;; Add a new circe buffer to irc workspace when we're in another workspace ;; Add a new circe buffer to irc workspace when we're in another workspace
+10 -2
View File
@@ -1,9 +1,17 @@
;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*- ;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*-
;;;###autodef ;;;###autodef
(defun set-irc-server! (server letvars) (defun set-irc-server! (server plist)
"Registers an irc SERVER for circe. "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." See `circe-network-options' for details."
(after! circe (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)))
+21 -2
View File
@@ -48,7 +48,6 @@ playback.")
(use-package! circe (use-package! circe
:commands circe circe-server-buffers :commands circe circe-server-buffers
:init (setq circe-network-defaults nil)
:config :config
(setq circe-default-quit-message nil (setq circe-default-quit-message nil
circe-default-part-message nil circe-default-part-message nil
@@ -93,6 +92,26 @@ playback.")
(add-hook 'doom-real-buffer-functions #'+circe-buffer-p) (add-hook 'doom-real-buffer-functions #'+circe-buffer-p)
(add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode) (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 #'+irc--add-circe-buffer-to-persp-h)
(add-hook 'circe-mode-hook #'turn-off-smartparens-mode)
;; HACK Fix #1862: circe hangs on TLS connections when using OpenSSL versions
;; > 1.1.0, where tls.el does not correctly determine the end of the info
;; block. This fixes proposed in jorgenschaefer/circe#340
(setq-hook! 'circe-mode-hook
tls-end-of-info
(concat "\\("
;; `openssl s_client' regexp. See ssl/ssl_txt.c lines 219-220.
;; According to apps/s_client.c line 1515 `---' is always the last
;; line that is printed by s_client before the real data.
"^ Verify return code: .+\n\\(\\|^ Extended master secret: .+\n\\)\\(\\|^ Max Early Data: .+\n\\)---\n\\|"
;; `gnutls' regexp. See src/cli.c lines 721-.
"^- Simple Client Mode:\n"
"\\(\n\\|" ; ignore blank lines
;; According to GnuTLS v2.1.5 src/cli.c lines 640-650 and 705-715 in
;; `main' the handshake will start after this message. If the
;; handshake fails, the programs will abort.
"^\\*\\*\\* Starting TLS handshake\n\\)*"
"\\)"))
(defadvice! +irc--circe-run-disconnect-hook-a (&rest _) (defadvice! +irc--circe-run-disconnect-hook-a (&rest _)
"Runs `+irc-disconnect-hook' after circe disconnects." "Runs `+irc-disconnect-hook' after circe disconnects."
@@ -175,7 +194,7 @@ playback.")
(define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line) (define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line)
(setq lui-fill-type nil) (setq lui-fill-type nil)
(when (featurep! :tools flyspell) (when (featurep! :checkers spell)
(setq lui-flyspell-p t)) (setq lui-flyspell-p t))
(after! evil (after! evil
+2 -2
View File
@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; app/irc/packages.el ;;; app/irc/packages.el
(package! circe) (package! circe :pin "e5bf5f89741a9c43aa406491e94dd8d58c302fb4")
(package! circe-notifications) (package! circe-notifications :pin "291149ac12877bbd062da993479d3533a26862b0")
@@ -1,14 +0,0 @@
;;; app/regex/autoload/export.el
;;;###autoload
(defun +regex/export () (interactive)) ; TODO +regex/export
;;
(defun +regex-export-python ()) ; import (re|regex)
(defun +regex-export-php ()) ; preg_(match(_all)?|replace)
(defun +regex-export-ruby ()) ; %r[.+]
(defun +regex-export-js ()) ; /.+/
@@ -1,272 +0,0 @@
;;; app/regex/autoload/regex.el
(defvar +regex--text-buffer nil)
(defvar +regex--text-replace-buffer nil)
(defvar +regex--expr-buffer nil)
(defvar +regex--expr-replace-buffer nil)
(defvar +regex--groups-buffer nil)
(defvar +regex--replace-buffer nil)
;;
(defface +regex-match-0-face
`((t (:foreground "Black" :background ,(doom-color 'magenta) :bold t)))
"TODO"
:group 'faces)
(defface +regex-match-1-face
`((t (:foreground "Black" :background ,(doom-color 'blue) :bold t)))
"TODO"
:group 'faces)
(defface +regex-match-2-face
`((t (:foreground "Black" :background ,(doom-color 'green) :bold t)))
"TODO"
:group 'faces)
(defvar +regex-faces
'(+regex-match-0-face +regex-match-1-face +regex-match-2-face)
"TODO")
;;
(defvar +regex-mode-map
(let ((map (make-sparse-keymap)))
(define-key map "\C-c\C-c" #'+regex-update-buffers)
(define-key map "\C-c\C-r" #'=regex/replace)
(define-key map "\C-c\C-k" #'+regex/quit)
(define-key map [remap kill-current-buffer] #'+regex/quit)
(define-key map [remap kill-buffer] #'+regex/quit)
map)
"TODO")
;;;###autoload
(define-minor-mode +regex-mode
"TODO"
:init-value nil
:global nil
:lighter ""
:keymap +regex-mode-map
(if +regex-mode
(add-hook 'after-change-functions #'+regex-update-buffers nil t)
(remove-hook 'after-change-functions #'+regex-update-buffers t)))
;;;###autoload
(defun =regex (&optional dummy-text)
"Start the Regex IDE."
(interactive "P")
(unless (buffer-live-p +regex--expr-buffer)
(condition-case ex
(progn
(setq +regex--expr-buffer (get-buffer-create "*doom-regex*")
+regex--text-buffer (if dummy-text (get-buffer-create "*doom-regex-text*") (current-buffer))
+regex--groups-buffer (get-buffer-create "*doom-regex-groups*"))
(when dummy-text
(+workspace-switch +regex-workspace-name t)
(switch-to-buffer +regex--text-buffer)
(with-current-buffer +regex--text-buffer
(insert +regex-dummy-text)))
(pop-to-buffer +regex--groups-buffer)
(pop-to-buffer +regex--expr-buffer)
(with-current-buffer +regex--expr-buffer
(conf-mode)
(rainbow-delimiters-mode +1)
(doom/toggle-line-numbers +1)
(setq-local require-final-newline nil)
(+regex-mode +1)
(text-scale-set 3)))
('error
(+regex/quit)
(error "Failed to open the Regexp IDE: %s" ex)))))
;;;###autoload
(defun =regex/replace ()
(interactive)
(unless (buffer-live-p +regex--replace-buffer)
(let (text)
(=regex t)
(with-selected-window (get-buffer-window +regex--text-buffer)
(setq text (buffer-string))
(select-window (split-window-right))
(switch-to-buffer (get-buffer-create "*doom-regex-text-repl*"))
(erase-buffer)
(insert text)
(read-only-mode +1)
(setq +regex--text-replace-buffer (current-buffer)))
(with-current-buffer +regex--expr-buffer
(select-window (split-window-right))
(switch-to-buffer (get-buffer-create "*doom-regex-repl*"))
(conf-mode)
(rainbow-delimiters-mode +1)
(doom/toggle-line-numbers -1)
(setq-local require-final-newline nil)
(setq mode-line-format nil
+regex--expr-replace-buffer (current-buffer))
(+regex-mode +1)
(text-scale-set 3)))))
;;;###autoload
(defun +regex/quit ()
"TODO"
(interactive)
(when (and +regex--text-buffer (buffer-live-p +regex--text-buffer))
(with-current-buffer +regex--text-buffer
(+regex-mode -1)
(remove-overlays nil nil 'category '+regex))
(when (equal (buffer-name +regex--text-buffer) "*doom-regex-text*")
(kill-buffer +regex--text-buffer)))
(when (equal (+workspace-current-name) +regex-workspace-name)
(+workspace/delete +regex-workspace-name))
(mapc (lambda (bufname)
(let ((buf (symbol-value bufname)))
(when (and buf (buffer-live-p buf))
(kill-buffer buf)
(set bufname nil))))
(list '+regex--text-replace-buffer
'+regex--expr-replace-buffer
'+regex--expr-buffer
'+regex--groups-buffer
'+regex--replace-buffer)))
(defun +regex--expr ()
(when (buffer-live-p +regex--expr-buffer)
(with-current-buffer +regex--expr-buffer
(string-trim (buffer-string)))))
(defun +regex--expr-replace ()
(when (buffer-live-p +regex--expr-replace-buffer)
(with-current-buffer +regex--expr-replace-buffer
(string-trim (buffer-string)))))
;;;###autoload
(defun +regex-update-buffers (&optional beg end len)
(interactive)
(let* ((inhibit-read-only t)
(regex (or (+regex--expr) ""))
(replace (or (+regex--expr-replace) ""))
(str (or (with-current-buffer +regex--text-buffer (buffer-string)) "")))
(with-current-buffer +regex--groups-buffer
(erase-buffer))
(with-current-buffer +regex--text-buffer
(remove-overlays nil nil 'category '+regex)
(when (> (length regex) 0)
(save-excursion
(goto-char (point-min))
(pcase +regex-default-backend
('emacs (+regex-backend-emacs regex replace str))
('perl (+regex-backend-perl regex replace str))))))
(with-current-buffer +regex--groups-buffer
(goto-char (point-min)))))
;; --- backends ---------------------------
(defun +regex--render-perl (regex text)
"From <https://github.com/jwiegley/regex-tool>"
(with-temp-buffer
(insert (format "@lines = <DATA>;
$line = join(\"\", @lines);
print \"(\";
while ($line =~ m/%s/gm) {
print \"(\", length($`), \" \", length($&), \" \";
for $i (1 .. 20) {
if ($$i) {
my $group = $$i;
$group =~ s/([\\\\\"])/\\\\\\1/g;
print \"(\", $i, \" . \\\"\", $group, \"\\\") \";
}
}
print \")\";
}
print \")\";
__DATA__
%s" (replace-regexp-in-string "/" "\\/" regex nil t) text))
(call-process-region (point-min) (point-max) "perl" t t)
(goto-char (point-min))
(read (current-buffer))))
(defun +regex--replace-perl (regex replace text)
(unless (or (string-empty-p regex)
(string-empty-p replace)
(string-empty-p text))
(with-temp-buffer
(insert (format "@lines = <DATA>;
$line = join(\"\", @lines);
$line =~ s/%s/%s/gm;
print $line;
__DATA__
%s" (replace-regexp-in-string "/" "\\/" regex nil t) (replace-regexp-in-string "/" "\\/" replace nil t) text))
(call-process-region (point-min) (point-max) "perl" t t)
(buffer-string))))
;;;###autoload
(defun +regex-backend-perl (regex replace str)
"TODO"
(cl-assert (stringp regex))
(cl-assert (stringp replace))
(cl-assert (stringp str))
(let ((i 0)
(results (+regex--render-perl regex str)))
(when (buffer-live-p +regex--text-replace-buffer)
(let ((replace (+regex--replace-perl regex replace str)))
(with-current-buffer +regex--text-replace-buffer
(erase-buffer)
(insert
(if (and (listp results)
replace
(not (string-empty-p replace)))
replace
str)))))
(dolist (result (if (listp results) results))
(let* ((offset (nth 0 result))
(length (nth 1 result))
(matches (nthcdr 2 result))
(ov (make-overlay (1+ offset) (+ offset length 1))))
(overlay-put ov 'face (nth (mod i 3) +regex-faces))
(overlay-put ov 'category '+regex)
(cl-incf i)
(let* ((match-zero (buffer-substring (1+ offset) (+ offset length 1)))
(line (format "Match: %s\n" (propertize match-zero 'face 'font-lock-string-face))))
(with-current-buffer +regex--groups-buffer
(insert line)))
(dolist (match matches)
(with-current-buffer +regex--groups-buffer
(goto-char (point-max))
(insert (format "Group %d: %s\n"
(propertize (car match) 'face 'font-lock-constant-face)
(propertize (cdr match) 'face 'font-lock-string-face)))))
(with-current-buffer +regex--groups-buffer
(insert ?\n))))))
;;;###autoload
(defun +regex-backend-emacs (regex replace str)
"TODO"
(cl-assert (stringp regex))
(cl-assert (stringp replace))
(cl-assert (stringp str))
(let ((i 0)
pos)
(when (buffer-live-p +regex--text-replace-buffer)
(with-current-buffer +regex--text-replace-buffer
(erase-buffer)
(insert str)
(when (and (listp results) (string-empty-p replace))
(replace-regexp regex replace))))
(while (and (setq pos (point))
(re-search-forward regex nil t))
(if (= (point) pos)
(forward-char 1)
(let ((ov (make-overlay (match-beginning 0) (match-end 0))))
(overlay-put ov 'face (nth (mod i 3) +regex-faces))
(overlay-put ov 'category '+regex))
(cl-incf i)
(dotimes (i 10)
(when-let (text (match-string i))
(save-match-data
(with-current-buffer +regex--groups-buffer
(goto-char (point-max))
(insert
(format "Group %d: %s\n"
(propertize i 'face 'font-lock-constant-face)
(propertize text 'face 'font-lock-string-face)))))))
(with-current-buffer +regex--groups-buffer
(insert ?\n))))))
-51
View File
@@ -1,51 +0,0 @@
;;; app/regex/config.el
;; Often, I find myself writing regular expressions that could terrify seasoned
;; programmers (or little children). To hone my regex fu, I need a regex
;; playground. Sure, there's regexr.com, but don't be silly, that's not Emacs.
;;
;; Sadly, the Emacs' regex syntax is niche and lacks support for a few
;; questionably useful features, like lookaround assertions, conditionals, case
;; modifiers or backreferences, among others. No, I want PCRE. I am going to
;; have my cake and eat it too, damn it!
;;
;; Workflow:
;; + Invoke `=regex' (if opened with C-u, opens in separate workspace with a
;; dummy text buffer).
;; + A regex window will popup up. Any matches will be highlighted in the
;; original buffer.
;; + C-c C-k to close it
;; + TODO C-c C-e to export to various langauges
;;
;; WARNING: THIS IS A WORK IN PROGRESS
(defvar +regex-workspace-name "*regex*"
"TODO")
(defvar +regex-default-backend 'perl
"The backend used to process regular expressions.
The `emacs' backend handles regular expressions directly.
The `perl' backend talks to a perl subprocess to do the handling.")
(defvar +regex-dummy-text
"Welcome to DOOM Emacs, proudly hosted by the demons of hell!
Edit the Expression & Text to see matches. Roll over matches or the expression
for details. Undo mistakes with ctrl-z. Save Favorites & Share expressions with
friends or the Community. Explore your results with Tools. A full Reference &
Help is available in the Library, or watch the video Tutorial.
Sample text for testing:
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789 _+-.,!@#$%^&*();\/|<>\"'
12345 -98.7 3.141 .6180 9,000 +42
555.123.4567 +1-(800)-555-2468
foo@demo.net bar.ba@test.co.uk
www.demo.com http://foo.co.uk/
http://regexr.com/foo.html?q=bar
https://mediatemple.net"
"TODO")
(set-popup-rules!
'(("^\\*doom-regex\\*$" :size 4 :quit nil)
("^\\*doom-regex-groups" :side 'left :size 28 :select nil :quit nil)))
+3 -4
View File
@@ -70,7 +70,7 @@
(let ((search-buffers (doom-buffers-in-mode 'elfeed-search-mode)) (let ((search-buffers (doom-buffers-in-mode 'elfeed-search-mode))
(show-buffers (doom-buffers-in-mode 'elfeed-show-mode)) (show-buffers (doom-buffers-in-mode 'elfeed-show-mode))
kill-buffer-query-functions) kill-buffer-query-functions)
(dolist (file +rss-elfeed-files) (dolist (file (bound-and-true-p rmh-elfeed-org-files))
(when-let (buf (get-file-buffer (expand-file-name file org-directory))) (when-let (buf (get-file-buffer (expand-file-name file org-directory)))
(kill-buffer buf))) (kill-buffer buf)))
(dolist (b search-buffers) (dolist (b search-buffers)
@@ -101,10 +101,9 @@
;;;###autoload ;;;###autoload
(defun +rss-put-sliced-image-fn (spec alt &optional flags) (defun +rss-put-sliced-image-fn (spec alt &optional flags)
"TODO" "TODO"
(cl-letf (((symbol-function #'insert-image) (letf! (defun insert-image (image &optional alt _area _slice)
(lambda (image &optional alt _area _slice)
(let ((height (cdr (image-size image t)))) (let ((height (cdr (image-size image t))))
(insert-sliced-image image alt nil (max 1 (/ height 20.0)) 1))))) (insert-sliced-image image alt nil (max 1 (/ height 20.0)) 1)))
(shr-put-image spec alt flags))) (shr-put-image spec alt flags)))
;;;###autoload ;;;###autoload
+9 -9
View File
@@ -4,10 +4,6 @@
;; by apps Reeder and Readkit. It can be invoked via `=rss'. Otherwise, if you ;; 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'. ;; 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 (defvar +rss-split-direction 'below
"What direction to pop up the entry buffer in elfeed.") "What direction to pop up the entry buffer in elfeed.")
@@ -21,10 +17,11 @@ easier to scroll through.")
(use-package! elfeed (use-package! elfeed
:commands elfeed :commands elfeed
:init
(setq elfeed-db-directory (concat doom-local-dir "elfeed/db/")
elfeed-enclosure-default-dir (concat doom-local-dir "elfeed/enclosures/"))
:config :config
(setq elfeed-search-filter "@2-week-ago " (setq elfeed-search-filter "@2-week-ago "
elfeed-db-directory (concat doom-local-dir "elfeed/db/")
elfeed-enclosure-default-dir (concat doom-local-dir "elfeed/enclosures/")
elfeed-show-entry-switch #'pop-to-buffer elfeed-show-entry-switch #'pop-to-buffer
elfeed-show-entry-delete #'+rss/delete-pane elfeed-show-entry-delete #'+rss/delete-pane
shr-max-image-proportion 0.8) shr-max-image-proportion 0.8)
@@ -67,8 +64,11 @@ easier to scroll through.")
(use-package! elfeed-org (use-package! elfeed-org
:when (featurep! +org) :when (featurep! +org)
:after elfeed :after elfeed
:preface
(setq rmh-elfeed-org-files (list "elfeed.org"))
:config :config
(let ((default-directory org-directory)) (and (let ((default-directory org-directory))
(setq rmh-elfeed-org-files (setq rmh-elfeed-org-files
(mapcar #'expand-file-name +rss-elfeed-files))) (cl-remove-if-not
(elfeed-org)) #'file-exists-p (mapcar #'expand-file-name rmh-elfeed-org-files))))
(elfeed-org)))
+2 -2
View File
@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; app/rss/packages.el ;;; app/rss/packages.el
(package! elfeed) (package! elfeed :pin "d0405e63863e54a01200740a6717ac875eceabc1")
(package! elfeed-org) (package! elfeed-org :pin "77b6bbf222487809813de260447d31c4c59902c9")
+1
View File
@@ -83,6 +83,7 @@ that works with the feature/popup module."
"Open a visible link, username or hashtag in a `twittering-mode' buffer." "Open a visible link, username or hashtag in a `twittering-mode' buffer."
(interactive) (interactive)
(require 'avy) (require 'avy)
;; REVIEW Is this necessary anymore with `link-hint'
(let ((pt (avy-with +twitter/ace-link (let ((pt (avy-with +twitter/ace-link
(avy--process (avy--process
(+twitter--collect-links) (+twitter--collect-links)
+2 -2
View File
@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; app/twitter/packages.el ;;; app/twitter/packages.el
(package! twittering-mode) (package! twittering-mode :pin "114891e8fdb4f06b1326a6cf795e49c205cf9e29")
(package! avy) (package! avy :pin "509471bad0e8094b8639729ec39ca141fae7d4bd")
-131
View File
@@ -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~
-43
View File
@@ -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-h ()
"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-h ()
(display-line-numbers-mode (if +write-mode +1 -1)))
;;;###autoload
(defun +write-init-mixed-pitch-h ()
(mixed-pitch-mode (if +write-mode +1 -1)))
;;;###autoload
(defun +write-init-visual-fill-column-h ()
(visual-fill-column-mode (if +write-mode +1 -1)))
;;;###autoload
(add-hook! '+write-mode-hook
#'(flyspell-mode
visual-line-mode
+write-init-mixed-pitch-h
+write-init-visual-fill-column-h
+write-init-line-numbers-h
+write-init-org-mode-h))
-56
View File
@@ -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))))
-7
View File
@@ -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")))
-12
View File
@@ -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)
@@ -116,7 +116,7 @@ There is no guarantee your language mode will have completion support.
Some, like ~lua-mode~, don't have completion support in Emacs at all. Others may Some, like ~lua-mode~, don't have completion support in Emacs at all. Others may
requires additional setup to get code completion working. For instance, requires additional setup to get code completion working. For instance,
~go-mode~ requires ~guru~ to be installed on your system, and ~enh-ruby-mode~ ~go-mode~ requires ~guru~ to be installed on your system, and ~ruby-mode~
requires that you have a Robe server running (~M-x robe-start~). requires that you have a Robe server running (~M-x robe-start~).
Check the relevant module's documentation for this kind of information. Check the relevant module's documentation for this kind of information.
@@ -67,11 +67,10 @@ Examples:
;;;###autoload ;;;###autoload
(defun +company-init-backends-h () (defun +company-init-backends-h ()
"Set `company-backends' for the current buffer." "Set `company-backends' for the current buffer."
(if (not company-mode) (or (memq major-mode '(fundamental-mode special-mode))
(remove-hook 'change-major-mode-after-body-hook #'+company-init-backends-h 'local) buffer-read-only
(unless (eq major-mode 'fundamental-mode) (doom-temp-buffer-p (or (buffer-base-buffer) (current-buffer)))
(setq-local company-backends (+company--backends))) (setq-local company-backends (+company--backends))))
(add-hook 'change-major-mode-after-body-hook #'+company-init-backends-h nil 'local)))
(put '+company-init-backends-h 'permanent-local-hook t) (put '+company-init-backends-h 'permanent-local-hook t)
+80 -54
View File
@@ -2,25 +2,49 @@
(use-package! company (use-package! company
:commands company-complete-common company-manual-begin company-grab-line :commands company-complete-common company-manual-begin company-grab-line
:after-call pre-command-hook after-find-file :hook (doom-first-input . global-company-mode)
:init :init
(setq company-minimum-prefix-length 2 (setq company-idle-delay 0.25
company-minimum-prefix-length 2
company-tooltip-limit 14 company-tooltip-limit 14
company-dabbrev-downcase nil
company-dabbrev-ignore-case nil
company-dabbrev-code-other-buffers t
company-tooltip-align-annotations t company-tooltip-align-annotations t
company-require-match 'never company-require-match 'never
company-global-modes company-global-modes
'(not erc-mode message-mode help-mode gud-mode eshell-mode) '(not erc-mode message-mode help-mode gud-mode)
company-frontends '(company-pseudo-tooltip-frontend
company-echo-metadata-frontend)
;; Buffer-local backends will be computed when loading a major mode, so
;; only specify a global default here.
company-backends '(company-capf) company-backends '(company-capf)
company-frontends
'(company-pseudo-tooltip-frontend ;; Company overrides `company-active-map' based on
company-echo-metadata-frontend)) ;; `company-auto-complete-chars'; no magic please!
company-auto-complete-chars nil
;; Only search the current buffer for `company-dabbrev' (a backend that
;; suggests text your open buffers). This prevents Company from causing
;; lag once you have a lot of buffers open.
company-dabbrev-other-buffers nil
;; Make `company-dabbrev' fully case-sensitive, to improve UX with
;; domain-specific words with particular casing.
company-dabbrev-ignore-case nil
company-dabbrev-downcase nil)
:config :config
(when (featurep! :editor evil) (when (featurep! :editor evil)
(add-hook 'company-mode-hook #'evil-normalize-keymaps) (add-hook 'company-mode-hook #'evil-normalize-keymaps)
(unless (featurep! +childframe)
;; Don't persist company popups when switching back to normal mode.
;; `company-box' aborts on mode switch so it doesn't need this.
(add-hook! 'evil-normal-state-entry-hook
(defun +company-abort-h ()
;; HACK `company-abort' doesn't no-op if company isn't active; causing
;; unwanted side-effects, like the suppression of messages in the
;; echo-area.
;; REVIEW Revisit this to refactor; shouldn't be necessary!
(when company-candidates
(company-abort)))))
;; Allow users to switch between backends on the fly. E.g. C-x C-s followed ;; 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 ;; by C-x C-n, will switch from `company-yasnippet' to
;; `company-dabbrev-code'. ;; `company-dabbrev-code'.
@@ -28,8 +52,7 @@
:before #'company-begin-backend :before #'company-begin-backend
(company-abort))) (company-abort)))
(add-hook 'company-mode-hook #'+company-init-backends-h) (add-hook 'after-change-major-mode-hook #'+company-init-backends-h 'append))
(global-company-mode +1))
(use-package! company-tng (use-package! company-tng
@@ -49,8 +72,7 @@
;; Packages ;; Packages
(after! company-files (after! company-files
(pushnew! company-files--regexps (add-to-list 'company-files--regexps "file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))
"file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))
(use-package! company-prescient (use-package! company-prescient
@@ -70,47 +92,44 @@
company-box-max-candidates 50 company-box-max-candidates 50
company-box-icons-alist 'company-box-icons-all-the-icons company-box-icons-alist 'company-box-icons-all-the-icons
company-box-icons-functions company-box-icons-functions
'(+company-box-icons--yasnippet-fn (cons #'+company-box-icons--elisp-fn
company-box-icons--lsp (delq 'company-box-icons--elisp
+company-box-icons--elisp-fn company-box-icons-functions))
company-box-icons--acphp)
company-box-icons-all-the-icons company-box-icons-all-the-icons
`((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple)) (let ((all-the-icons-scale-factor 0.8))
(Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green)) `((Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple))
(Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) (Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green))
(Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) (Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) (Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) (Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue)) (Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) (Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue))
(Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red)) (Class . ,(all-the-icons-material "class" :face 'all-the-icons-red))
(Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red)) (Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red))
(Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red)) (Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red))
(Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red)) (Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red))
(Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red)) (Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red))
(Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red)) (Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red))
(Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red)) (Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red))
(Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red)) (Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red))
(Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red)) (Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red))
(File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red)) (Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red))
(Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red)) (File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red))
(Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red)) (Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red))
(EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red)) (Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red))
(Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red)) (EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red))
(Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red)) (Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red))
(Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red)) (Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red))
(Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red)) (Event . ,(all-the-icons-material "event" :face 'all-the-icons-red))
(TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red)) (Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red))
;; (Template . ,(company-box-icons-image "Template.png")))) (TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red))
(Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green)) (Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green))
(ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red)) (ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue)) (ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue))
(ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange)) (ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange))
(ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink)))) (ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink)))))
(defun +company-box-icons--yasnippet-fn (candidate) (delq! 'company-echo-metadata-frontend company-frontends)
(when (get-text-property 0 'yas-annotation candidate)
'Yasnippet))
(defun +company-box-icons--elisp-fn (candidate) (defun +company-box-icons--elisp-fn (candidate)
(when (derived-mode-p 'emacs-lisp-mode) (when (derived-mode-p 'emacs-lisp-mode)
@@ -118,7 +137,14 @@
(cond ((fboundp sym) 'ElispFunction) (cond ((fboundp sym) 'ElispFunction)
((boundp sym) 'ElispVariable) ((boundp sym) 'ElispVariable)
((featurep sym) 'ElispFeature) ((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
(letf! ((#'display-buffer-in-side-window #'ignore))
(apply orig-fn args))))
(use-package! company-dict (use-package! company-dict
@@ -1,8 +1,8 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; completion/company/packages.el ;;; completion/company/packages.el
(package! company) (package! company :pin "6333fc4ebbbf4d28e834de8715561e984f149ecb")
(package! company-dict) (package! company-dict :pin "cd7b8394f6014c57897f65d335d6b2bd65dab1f4")
(package! company-prescient) (package! company-prescient :pin "0f4a89bdec61395138d968a38d375e63ccfbed63")
(when (featurep! +childframe) (when (featurep! +childframe)
(package! company-box)) (package! company-box :pin "3814fcb14e92f4b85b19e664e216a7c8d5c7144d"))
@@ -38,7 +38,7 @@ workspace."
;;; Project search ;;; Project search
;;;###autoload ;;;###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. "Conduct a file search using ripgrep.
:query STRING :query STRING
@@ -52,10 +52,13 @@ workspace."
(unless (executable-find "rg") (unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH")) (user-error "Couldn't find ripgrep in your PATH"))
(require 'helm-rg) (require 'helm-rg)
(let ((helm-rg-default-directory (or in (doom-project-root) default-directory)) (let ((this-command 'helm-rg)
(helm-rg-default-directory (or in (doom-project-root) default-directory))
(helm-rg-default-extra-args (helm-rg-default-extra-args
(delq nil (list (when all-files "-z -uu") (delq nil (append (list (when all-files "-z -uu")
(unless recursive "--maxdepth 1"))))) (unless recursive "--maxdepth 1"))
args))))
(setq deactivate-mark t)
(helm-rg (or query (helm-rg (or query
(when (use-region-p) (when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
@@ -23,6 +23,7 @@ bottom, which is easier on the eyes on big displays."
(setq +helm--posframe-buffer buffer) (setq +helm--posframe-buffer buffer)
:position (point) :position (point)
:poshandler +helm-posframe-handler :poshandler +helm-posframe-handler
:respect-header-line helm-echo-input-in-header-line
:width :width
(max (cl-typecase .width (max (cl-typecase .width
(integer .width) (integer .width)
+23 -22
View File
@@ -23,8 +23,7 @@ be negative.")
;;; Packages ;;; Packages
(use-package! helm-mode (use-package! helm-mode
:defer t :hook (doom-first-input . helm-mode)
:after-call pre-command-hook
:init :init
(map! [remap apropos] #'helm-apropos (map! [remap apropos] #'helm-apropos
[remap find-library] #'helm-locate-library [remap find-library] #'helm-locate-library
@@ -43,7 +42,6 @@ be negative.")
[remap recentf-open-files] #'helm-recentf [remap recentf-open-files] #'helm-recentf
[remap yank-pop] #'helm-show-kill-ring) [remap yank-pop] #'helm-show-kill-ring)
:config :config
(helm-mode +1)
;; helm is too heavy for `find-file-at-point' ;; helm is too heavy for `find-file-at-point'
(add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil))) (add-to-list 'helm-completing-read-handlers-alist (cons #'find-file-at-point nil)))
@@ -57,8 +55,6 @@ be negative.")
helm-mode-line-string nil helm-mode-line-string nil
helm-ff-auto-update-initial-value nil helm-ff-auto-update-initial-value nil
helm-find-files-doc-header 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 ;; Default helm window sizes
helm-display-buffer-default-width nil helm-display-buffer-default-width nil
helm-display-buffer-default-height 0.25 helm-display-buffer-default-height 0.25
@@ -73,38 +69,37 @@ be negative.")
:init :init
(when (featurep! +childframe) (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)) (setq helm-display-function #'+helm-posframe-display-fn))
(let ((fuzzy (featurep! +fuzzy))) (let ((fuzzy (featurep! +fuzzy)))
(setq helm-M-x-fuzzy-match fuzzy (setq helm-apropos-fuzzy-match fuzzy
helm-apropos-fuzzy-match fuzzy
helm-apropos-fuzzy-match fuzzy
helm-bookmark-show-location fuzzy helm-bookmark-show-location fuzzy
helm-buffers-fuzzy-matching fuzzy helm-buffers-fuzzy-matching fuzzy
helm-completion-in-region-fuzzy-match fuzzy
helm-completion-in-region-fuzzy-match fuzzy
helm-ff-fuzzy-matching fuzzy helm-ff-fuzzy-matching fuzzy
helm-file-cache-fuzzy-match fuzzy helm-file-cache-fuzzy-match fuzzy
helm-flx-for-helm-locate fuzzy helm-flx-for-helm-locate fuzzy
helm-imenu-fuzzy-match fuzzy helm-imenu-fuzzy-match fuzzy
helm-lisp-fuzzy-completion fuzzy helm-lisp-fuzzy-completion fuzzy
helm-locate-fuzzy-match fuzzy helm-locate-fuzzy-match fuzzy
helm-mode-fuzzy-match fuzzy
helm-projectile-fuzzy-match fuzzy helm-projectile-fuzzy-match fuzzy
helm-recentf-fuzzy-match fuzzy helm-recentf-fuzzy-match fuzzy
helm-semantic-fuzzy-match fuzzy)) helm-semantic-fuzzy-match fuzzy)
;; Make sure that we have helm-multi-matching or fuzzy matching,
;; (as prescribed by the fuzzy flag) also in the following cases:
;; - helmized commands that use `completion-at-point' and similar functions
;; - native commands that fall back to `completion-styles' like `helm-M-x'
(push (if EMACS27+
(if fuzzy 'flex 'helm)
(if fuzzy 'helm-flex 'helm))
completion-styles))
:config :config
(set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil) (set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil)
;; HACK Doom doesn't support these commands, which invite the user to install ;; Hide the modeline in helm windows as it serves little purpose.
;; the package via ELPA. Force them to use +helm/* instead, because they work
;; out of the box.
(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 _) (defun +helm--hide-mode-line (&rest _)
(with-current-buffer (helm-buffer-get) (with-current-buffer (helm-buffer-get)
(unless helm-mode-line-string (unless helm-mode-line-string
@@ -113,11 +108,13 @@ be negative.")
(advice-add #'helm-display-mode-line :override #'+helm--hide-mode-line) (advice-add #'helm-display-mode-line :override #'+helm--hide-mode-line)
(advice-add #'helm-ag-show-status-default-mode-line :override #'ignore) (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 ;; Use helpful instead of describe-* to display documentation
(dolist (fn '(helm-describe-variable helm-describe-function)) (dolist (fn '(helm-describe-variable helm-describe-function))
(advice-add fn :around #'doom-use-helpful-a))) (advice-add fn :around #'doom-use-helpful-a)))
(use-package! helm-flx (use-package! helm-flx
:when (featurep! +fuzzy) :when (featurep! +fuzzy)
:hook (helm-mode . helm-flx-mode) :hook (helm-mode . helm-flx-mode)
@@ -131,7 +128,7 @@ be negative.")
"C-c C-e" #'helm-rg--bounce) "C-c C-e" #'helm-rg--bounce)
(map! :map helm-rg--bounce-mode-map (map! :map helm-rg--bounce-mode-map
"q" #'kill-current-buffer "q" #'kill-current-buffer
"C-c C-c" (λ! (helm-rg--bounce-dump) (kill-current-buffer)) "C-c C-c" (cmd! (helm-rg--bounce-dump) (kill-current-buffer))
"C-x C-c" #'helm-rg--bounce-dump-current-file "C-x C-c" #'helm-rg--bounce-dump-current-file
"C-c C-k" #'kill-current-buffer)) "C-c C-k" #'kill-current-buffer))
@@ -183,3 +180,7 @@ be negative.")
(lambda (buf &optional _resume) (pop-to-buffer buf))) (lambda (buf &optional _resume) (pop-to-buffer buf)))
(global-set-key [remap swiper] #'swiper-helm) (global-set-key [remap swiper] #'swiper-helm)
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode nil #'eq)) (add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode nil #'eq))
(use-package! helm-descbinds
:hook (helm-mode . helm-descbinds-mode))
+13 -10
View File
@@ -1,16 +1,19 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; completion/helm/packages.el ;;; completion/helm/packages.el
(package! helm) (package! helm :pin "b6db9fb47a8900704394e63b795f4a54cb4701a8")
(package! helm-rg) (package! helm-rg :pin "785a80fe5cc87e27c5ea3d00a70049028d9e2847")
(package! helm-c-yasnippet) (package! helm-c-yasnippet :pin "65ca732b510bfc31636708aebcfe4d2d845b59b0")
(package! helm-company) (package! helm-company :pin "6eb5c2d730a60e394e005b47c1db018697094dde")
(package! helm-describe-modes :recipe (:host github :repo "emacs-helm/helm-describe-modes")) (package! helm-describe-modes
(package! helm-projectile) :recipe (:host github :repo "emacs-helm/helm-describe-modes")
(package! swiper-helm) :pin "11fb36af119b784539d31c6160002de1957408aa")
(package! helm-projectile :pin "5328b74dddcee8d1913803ca8167868831a07463")
(package! swiper-helm :pin "93fb6db87bc6a5967898b5fd3286954cc72a0008")
(when (featurep! +fuzzy) (when (featurep! +fuzzy)
(package! helm-flx)) (package! helm-flx :pin "6640fac5cb16bee73c95b8ed1248a4e5e113690e"))
(when (featurep! +childframe) (when (featurep! +childframe)
(package! posframe)) (package! posframe :pin "093b29a53cbeda6d637ccc9ef4dfc47123e79b9e"))
(when (featurep! :lang org) (when (featurep! :lang org)
(package! helm-org)) (package! helm-org :pin "b7a18dfc17e8b933956d61d68c435eee03a96c24"))
(package! helm-descbinds :pin "b72515982396b6e336ad7beb6767e95a80fca192")
+49 -46
View File
@@ -1,59 +1,62 @@
;;; completion/ido/config.el -*- lexical-binding: t; -*- ;;; completion/ido/config.el -*- lexical-binding: t; -*-
(defun +ido-init-h () (defvar ido-mode-hook nil
"List of hooks to run when `ido-mode' is activated.")
;;
;;; Packages
(use-package! ido
:hook (doom-first-input . ido-mode)
:hook (ido-mode . ido-everywhere)
:hook (ido-mode . ido-ubiquitous-mode)
:preface
;; HACK `ido' is a really old package. It defines `ido-mode' manually and
;; doesn't define a hook, so we define one for it, so we can use it!
(defadvice! +ido-run-hooks-a (&rest _)
:after #'ido-mode
(run-hooks 'ido-mode-hook))
:init
(setq ido-save-directory-list-file (concat doom-cache-dir "ido.last"))
:config
(pushnew! ido-ignore-files "\\`.DS_Store$" "Icon\\?$")
(setq ido-ignore-buffers (setq ido-ignore-buffers
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer" '("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*[Hh]elp" "^\\*Buffer"
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" "^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" "_region_"
"_region_" " output\\*$" "^TAGS$" "^\*Ido") " output\\*$" "^TAGS$" "^\*Ido")
ido-use-faces nil ido-auto-merge-work-directories-length -1
ido-confirm-unique-completion t ido-confirm-unique-completion t
ido-case-fold t ido-case-fold t
ido-enable-tramp-completion nil
ido-enable-flex-matching t
ido-create-new-buffer 'always ido-create-new-buffer 'always
ido-enable-tramp-completion t ido-enable-flex-matching t)
ido-enable-last-directory-history t
ido-save-directory-list-file (concat doom-cache-dir "ido.last"))
(unless (member "\\`.DS_Store$" ido-ignore-files) (map! :map (ido-common-completion-map ido-file-completion-map)
(push "\\`.DS_Store$" ido-ignore-files) "C-w" #'ido-delete-backward-word-updir
(push "Icon\\?$" ido-ignore-files)) :map (ido-common-completion-map ido-file-dir-completion-map)
"C-n" #'ido-next-match
(define-key! (ido-common-completion-map ido-completion-map ido-file-completion-map) "C-p" #'ido-prev-match
"\C-n" #'ido-next-match [down] #'ido-next-match
"\C-p" #'ido-prev-match [up] #'ido-prev-match
"\C-w" #'ido-delete-backward-word-updir :map ido-file-completion-map
;; Go to $HOME with ~ ;; Go to $HOME with ~
"~" (λ! (if (looking-back "/" (point-min)) "~" (cmd! (if (looking-back "/" (point-min))
(insert "~/") (insert "~/")
(call-interactively #'self-insert-command)))) (call-interactively #'self-insert-command)))))
(defadvice! +ido--sort-mtime-a ()
"Sort ido filelist by mtime instead of alphabetically."
:override #'ido-sort-mtime
(setq ido-temp-list
(sort ido-temp-list
(lambda (a b)
(time-less-p
(sixth (file-attributes (concat ido-current-directory b)))
(sixth (file-attributes (concat ido-current-directory a)))))))
(ido-to-end ;; move . files to end (again)
(cl-loop for x in ido-temp-list
if (char-equal (string-to-char x) ?.)
collect x)))
(add-hook! '(ido-make-file-list-hook ido-make-dir-list-hook)
#'ido-sort-mtime)
;; (use-package! ido-vertical-mode
(ido-mode 1) :hook (ido-mode . ido-vertical-mode)
(ido-everywhere 1) :config (setq ido-vertical-show-count t))
(ido-ubiquitous-mode 1)
(ido-vertical-mode 1)
(flx-ido-mode +1)
(crm-custom-mode +1)
;;
(remove-hook 'ido-setup-hook #'+ido-init-h))
;; (use-package! ido-sort-mtime
(add-hook 'ido-setup-hook #'+ido-init-h) :hook (ido-mode . ido-sort-mtime-mode))
(use-package! crm-custom
:hook (ido-mode . crm-custom-mode))
(use-package! flx-ido
:hook (ido-mode . flx-ido-mode))
+5 -4
View File
@@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; completion/ido/packages.el ;;; completion/ido/packages.el
(package! flx-ido) (package! flx-ido :pin "17f5c9cb2af18aa6f52910ff4a5a63591261ced5")
(package! ido-completing-read+) (package! ido-completing-read+ :pin "98d3a6e56b1d3652da7b47f49f76d77f82ea80ba")
(package! ido-vertical-mode) (package! ido-sort-mtime :pin "f638ff0c922af862f5211779f2311a27fde428eb")
(package! crm-custom) (package! ido-vertical-mode :pin "16c4c1a112796ee0bcf401ea39d3e2643a89feaf")
(package! crm-custom :pin "f1aaccf64306a5f99d9bf7ba815d7ea41c15518d")
+1 -1
View File
@@ -136,7 +136,7 @@ These keybindings are available while a search is active:
| =C-c C-o= | Open a buffer with your search results | | =C-c C-o= | Open a buffer with your search results |
| =C-c C-e= | Open a writable buffer of your search results | | =C-c C-e= | Open a writable buffer of your search results |
| =C-SPC= | Preview the current candidate | | =C-SPC= | Preview the current candidate |
| =M-RET= | Open the selected candidate in other-window | | =C-RET= | Open the selected candidate in other-window |
Changes to the resulting wgrep buffer (opened by =C-c C-e=) can be committed Changes to the resulting wgrep buffer (opened by =C-c C-e=) can be committed
with =C-c C-c= and aborted with =C-c C-k= (alternatively =ZZ= and =ZQ=, for evil with =C-c C-c= and aborted with =C-c C-k= (alternatively =ZZ= and =ZQ=, for evil
+22 -29
View File
@@ -13,16 +13,6 @@
(and (not (eq buffer (current-buffer))) (and (not (eq buffer (current-buffer)))
(+workspace-contains-buffer-p buffer)))) (+workspace-contains-buffer-p buffer))))
;;;###autoload
(defun +ivy-standard-search (str)
"TODO"
(funcall +ivy-standard-search-fn str))
;;;###autoload
(defun +ivy-alternative-search (str)
"TODO"
(funcall +ivy-alternative-search-fn str))
;;;###autoload ;;;###autoload
(defun +ivy-rich-buffer-name (candidate) (defun +ivy-rich-buffer-name (candidate)
"Display the buffer name. "Display the buffer name.
@@ -188,7 +178,7 @@ If ARG (universal argument), open selection in other-window."
(user-error "%S doesn't support wgrep" caller))))) (user-error "%S doesn't support wgrep" caller)))))
;;;###autoload ;;;###autoload
(defun +ivy-yas-prompt (prompt choices &optional display-fn) (defun +ivy-yas-prompt-fn (prompt choices &optional display-fn)
(yas-completing-prompt prompt choices display-fn #'ivy-completing-read)) (yas-completing-prompt prompt choices display-fn #'ivy-completing-read))
;;;###autoload ;;;###autoload
@@ -217,14 +207,19 @@ If ARG (universal argument), open selection in other-window."
;;;###autoload ;;;###autoload
(defun +ivy/projectile-find-file () (defun +ivy/projectile-find-file ()
"A more sensible `counsel-projectile-find-file', which will revert to "A more sensible `counsel-projectile-find-file', which will revert to
`counsel-find-file' if invoked from $HOME, `counsel-file-jump' if invoked from a `counsel-find-file' if invoked from $HOME or /, `counsel-file-jump' if invoked
non-project, `projectile-find-file' if in a big project (more than from a non-project, `projectile-find-file' if in a big project (more than
`ivy-sort-max-size' files), or `counsel-projectile-find-file' otherwise. `ivy-sort-max-size' files), or `counsel-projectile-find-file' otherwise.
The point of this is to avoid Emacs locking up indexing massive file trees." The point of this is to avoid Emacs locking up indexing massive file trees."
(interactive) (interactive)
;; 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 (call-interactively
(cond ((or (file-equal-p default-directory "~") (cond ((or (file-equal-p default-directory "~")
(file-equal-p default-directory "/")
(when-let (proot (doom-project-root)) (when-let (proot (doom-project-root))
(file-equal-p proot "~"))) (file-equal-p proot "~")))
#'counsel-find-file) #'counsel-find-file)
@@ -235,10 +230,10 @@ The point of this is to avoid Emacs locking up indexing massive file trees."
#'counsel-projectile-find-file #'counsel-projectile-find-file
#'projectile-find-file))) #'projectile-find-file)))
(#'counsel-file-jump)))) (#'counsel-file-jump)))))
;;;###autoload ;;;###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. "Conduct a file search using ripgrep.
:query STRING :query STRING
@@ -252,36 +247,34 @@ The point of this is to avoid Emacs locking up indexing massive file trees."
(unless (executable-find "rg") (unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH")) (user-error "Couldn't find ripgrep in your PATH"))
(require 'counsel) (require 'counsel)
(let* ((ivy-more-chars-alist '((t . 1))) (let* ((this-command 'counsel-rg)
(project-root (or (doom-project-root) default-directory)) (project-root (or (doom-project-root) default-directory))
(directory (or in project-root)) (directory (or in project-root))
(default-directory directory)
(args (concat (if all-files " -uu") (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 (counsel-rg
(or (if query query) (or query
(when (use-region-p) (when (doom-region-active-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning))) (replace-regexp-in-string
(end (or (bound-and-true-p evil-visual-end) (region-end)))) "[! |]" (lambda (substr)
(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 " ") (cond ((and (string= substr " ")
(not (featurep! +fuzzy))) (not (featurep! +fuzzy)))
" ") " ")
((string= substr "|") ((string= substr "|")
"\\\\\\\\|") "\\\\\\\\|")
((concat "\\\\" substr)))) ((concat "\\\\" substr))))
(rxt-quote-pcre query))))))) (rxt-quote-pcre (doom-thing-at-point-or-region)))))
directory args directory args
(format "rg%s %s" (or prompt
(format "rg%s [%s]: "
args args
(cond ((equal directory default-directory) (cond ((equal directory default-directory)
"./") "./")
((equal directory project-root) ((equal directory project-root)
(projectile-project-name)) (projectile-project-name))
((file-relative-name directory project-root))))))) ((file-relative-name directory project-root))))))))
;;;###autoload ;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory) (defun +ivy/project-search (&optional arg initial-query directory)
+147 -100
View File
@@ -14,33 +14,31 @@ When 'everything, also preview virtual buffers")
"A plist mapping ivy/counsel commands to commands that generate an editable "A plist mapping ivy/counsel commands to commands that generate an editable
results buffer.") 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.
This uses the standard search algorithm ivy uses (or a variant of it).")
(defvar +ivy-alternative-search-fn
(cond ((featurep! +prescient) #'ivy-prescient-re-builder)
((featurep! +fuzzy) #'ivy--regex-fuzzy)
;; Ignore order for non-fuzzy searches by default
(#'ivy--regex-ignore-order))
"Function to use for fuzzy search commands.
This uses a search algorithm other than ivy's default.")
;; ;;
;;; Packages ;;; Packages
(use-package! ivy (use-package! ivy
:after-call pre-command-hook :hook (doom-first-input . ivy-mode)
:init :init
(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 (setq ivy-re-builders-alist
`((counsel-rg . +ivy-standard-search) `((counsel-rg . ,standard-search-fn)
(swiper . +ivy-standard-search) (swiper . ,standard-search-fn)
(swiper-isearch . +ivy-standard-search) (swiper-isearch . ,standard-search-fn)
(t . +ivy-alternative-search))) (t . ,alt-search-fn))
ivy-more-chars-alist
'((counsel-rg . 1)
(counsel-search . 2)
(t . 3))))
(define-key! (define-key!
[remap switch-to-buffer] #'+ivy/switch-buffer [remap switch-to-buffer] #'+ivy/switch-buffer
@@ -90,7 +88,7 @@ This uses a search algorithm other than ivy's default.")
(setq +ivy--origin nil))) (setq +ivy--origin nil)))
(after! yasnippet (after! yasnippet
(add-hook 'yas-prompt-functions #'+ivy-yas-prompt)) (add-hook 'yas-prompt-functions #'+ivy-yas-prompt-fn))
(defadvice! +ivy--inhibit-completion-in-region-a (orig-fn &rest args) (defadvice! +ivy--inhibit-completion-in-region-a (orig-fn &rest args)
"`ivy-completion-in-region' struggles with completing certain "`ivy-completion-in-region' struggles with completing certain
@@ -99,19 +97,11 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(let ((completion-in-region-function #'completion--in-region)) (let ((completion-in-region-function #'completion--in-region))
(apply orig-fn args))) (apply orig-fn args)))
(define-key ivy-minibuffer-map (kbd "C-c C-e") #'+ivy/woccur)
(ivy-mode +1)
(use-package! ivy-hydra
:commands (ivy-dispatching-done ivy--matcher-desc ivy-hydra/body)
:init
(define-key! ivy-minibuffer-map (define-key! ivy-minibuffer-map
[remap doom/delete-backward-word] #'ivy-backward-kill-word
"C-c C-e" #'+ivy/woccur
"C-o" #'ivy-dispatching-done "C-o" #'ivy-dispatching-done
"M-o" #'hydra-ivy/body) "M-o" #'hydra-ivy/body))
:config
;; ivy-hydra rebinds this, so we have to do so again
(define-key ivy-minibuffer-map (kbd "M-o") #'hydra-ivy/body)))
(use-package! ivy-rich (use-package! ivy-rich
@@ -122,15 +112,27 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(when (featurep! +icons) (when (featurep! +icons)
(cl-pushnew '(+ivy-rich-buffer-icon) (cl-pushnew '(+ivy-rich-buffer-icon)
(cadr (plist-get ivy-rich-display-transformers-list (cadr (plist-get ivy-rich-display-transformers-list
'ivy-switch-buffer)))) 'ivy-switch-buffer))
:test #'equal))
;; Include variable value in `counsel-describe-variable' ;; Enahnce the appearance of a couple counsel commands
(plist-put! ivy-rich-display-transformers-list (plist-put! ivy-rich-display-transformers-list
'counsel-describe-variable 'counsel-describe-variable
'(:columns '(:columns
((counsel-describe-variable-transformer (:width 40)) ; the original transformer ((counsel-describe-variable-transformer (:width 40)) ; the original transformer
(+ivy-rich-describe-variable-transformer (:width 50)) (+ivy-rich-describe-variable-transformer (:width 50)) ; display variable value
(ivy-rich-counsel-variable-docstring (:face font-lock-doc-face))))) (ivy-rich-counsel-variable-docstring (:face font-lock-doc-face))))
'counsel-M-x
'(:columns
((counsel-M-x-transformer (:width 60))
(ivy-rich-counsel-function-docstring (:face font-lock-doc-face))))
;; Apply switch buffer transformers to `counsel-projectile-switch-to-buffer' as well
'counsel-projectile-switch-to-buffer
(plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)
'counsel-bookmark
'(:columns
((ivy-rich-candidate (:width 0.5))
(ivy-rich-bookmark-filename (:width 60)))))
;; Remove built-in coloring of buffer list; we do our own ;; Remove built-in coloring of buffer list; we do our own
(setq ivy-switch-buffer-faces-alist nil) (setq ivy-switch-buffer-faces-alist nil)
@@ -138,15 +140,9 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
;; Highlight buffers differently based on whether they're in the same project ;; Highlight buffers differently based on whether they're in the same project
;; as the current project or not. ;; as the current project or not.
(let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer)) (when-let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer))
(switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns)))) (switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns))))
(when switch-buffer-alist (setcar switch-buffer-alist '+ivy-rich-buffer-name))
(setcar switch-buffer-alist '+ivy-rich-buffer-name)))
;; Apply switch buffer transformers to `counsel-projectile-switch-to-buffer' as well
(plist-put! ivy-rich-display-transformers-list
'counsel-projectile-switch-to-buffer
(plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer))
(ivy-rich-mode +1)) (ivy-rich-mode +1))
@@ -161,7 +157,8 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(all-the-icons-ivy-setup) (all-the-icons-ivy-setup)
(after! counsel-projectile (after! counsel-projectile
(let ((all-the-icons-ivy-file-commands '(counsel-projectile (let ((all-the-icons-ivy-file-commands
'(counsel-projectile
counsel-projectile-find-file counsel-projectile-find-file
counsel-projectile-find-dir))) counsel-projectile-find-dir)))
(all-the-icons-ivy-setup)))) (all-the-icons-ivy-setup))))
@@ -173,62 +170,69 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(define-key! (define-key!
[remap apropos] #'counsel-apropos [remap apropos] #'counsel-apropos
[remap bookmark-jump] #'counsel-bookmark [remap bookmark-jump] #'counsel-bookmark
[remap compile] #'+ivy/compile
[remap describe-bindings] #'counsel-descbinds
[remap describe-face] #'counsel-faces [remap describe-face] #'counsel-faces
[remap describe-function] #'counsel-describe-function [remap describe-function] #'counsel-describe-function
[remap describe-variable] #'counsel-describe-variable [remap describe-variable] #'counsel-describe-variable
[remap describe-bindings] #'counsel-descbinds [remap evil-ex-registers] #'counsel-evil-registers
[remap set-variable] #'counsel-set-variable [remap evil-show-marks] #'counsel-mark-ring
[remap execute-extended-command] #'counsel-M-x [remap execute-extended-command] #'counsel-M-x
[remap find-file] #'counsel-find-file [remap find-file] #'counsel-find-file
[remap find-library] #'counsel-find-library [remap find-library] #'counsel-find-library
[remap info-lookup-symbol] #'counsel-info-lookup-symbol
[remap imenu] #'counsel-imenu [remap imenu] #'counsel-imenu
[remap recentf-open-files] #'counsel-recentf [remap info-lookup-symbol] #'counsel-info-lookup-symbol
[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 load-theme] #'counsel-load-theme [remap load-theme] #'counsel-load-theme
[remap locate] #'counsel-locate [remap locate] #'counsel-locate
[remap org-goto] #'counsel-org-goto
[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 unicode-chars-list-chars] #'counsel-unicode-char
[remap compile] #'+ivy/compile [remap yank-pop] #'counsel-yank-pop)
[remap projectile-compile-project] #'+ivy/project-compile)
:config :config
(set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil) (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 ;; Don't use ^ as initial input. Set this here because `counsel' defines more
;; of its own, on top of the defaults. ;; of its own, on top of the defaults.
(setq ivy-initial-inputs-alist nil) (setq ivy-initial-inputs-alist nil)
;; REVIEW Move this somewhere else and perhaps generalize this so both ;; REVIEW Counsel allows `counsel-rg-base-command' to be a string or list.
;; ivy/helm users can enjoy it. ;; This backwards compatibility complicates things for Doom. Simpler to
(defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args) ;; just force it to always be a list.
"Change `counsel-file-jump' to use fd or ripgrep, if they are available." (when (stringp counsel-rg-base-command)
:override #'counsel--find-return-list (setq counsel-rg-base-command (split-string counsel-rg-base-command)))
(cl-destructuring-bind (find-program . args)
(cond ((executable-find "fd") ;; REVIEW Fix #3215: prevents mingw on Windows throwing an error trying to
(cons "fd" (list "-t" "f" "-E" ".git"))) ;; expand / to an absolute path. Remove this when it is fixed upstream
((executable-find "rg") ;; in counsel.
(cons "rg" (list "--files" "--hidden" "--no-messages"))) (when (and (memq system-type '(windows-nt ms-dos))
((cons find-program args))) (listp counsel-rg-base-command)
(unless (listp args) (member "--path-separator" counsel-rg-base-command))
(user-error "`counsel-file-jump-args' is a list now, please customize accordingly.")) (setf (cadr (member "--path-separator" counsel-rg-base-command))
(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' ;; Integrate with `helpful'
(setq counsel-describe-function-function #'helpful-callable (setq counsel-describe-function-function #'helpful-callable
counsel-describe-variable-function #'helpful-variable) 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)
(add-hook 'counsel-grep-post-action-hook #'recenter)
(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 ;; Make `counsel-compile' projectile-aware (if you prefer it over
;; `+ivy/compile' and `+ivy/project-compile') ;; `+ivy/compile' and `+ivy/project-compile')
(add-to-list 'counsel-compile-root-functions #'projectile-project-root) (add-to-list 'counsel-compile-root-functions #'projectile-project-root)
@@ -236,33 +240,69 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
;; Persist `counsel-compile' history ;; Persist `counsel-compile' history
(add-to-list 'savehist-additional-variables 'counsel-compile-history)) (add-to-list 'savehist-additional-variables 'counsel-compile-history))
;; Use spotlight on mac for `counsel-locate' by default, since it doesn't need ;; `counsel-imenu' -- no sorting for imenu. Sort it by appearance in page.
;; any additional setup. (add-to-list 'ivy-sort-functions-alist '(counsel-imenu))
;; `counsel-locate'
(when IS-MAC (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)) (setq counsel-locate-cmd #'counsel-locate-cmd-mdfind))
;; `swiper'
;; Don't mess with font-locking on the dashboard; it causes breakages ;; Don't mess with font-locking on the dashboard; it causes breakages
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode) (add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode)
;; Record in jumplist when opening files via counsel-{ag,rg,pt,git-grep} ;; `counsel-find-file'
(add-hook 'counsel-grep-post-action-hook #'better-jumper-set-jump)
;; Configure `counsel-find-file'
(setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)") (setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)")
(dolist (fn '(counsel-rg counsel-find-file))
(ivy-add-actions (ivy-add-actions
'counsel-find-file fn '(("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory))))
'(("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory))))
"insert relative path") "insert relative path")
("P" (lambda (path) (with-ivy-window (insert path))) ("P" (lambda (path) (with-ivy-window (insert path)))
"insert absolute path") "insert absolute path")
("l" (lambda (path) (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory))))) ("l" (lambda (path) (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory)))))
"insert relative org-link") "insert relative org-link")
("L" (lambda (path) (with-ivy-window (insert (format "[[%s]]" path)))) ("L" (lambda (path) (with-ivy-window (insert (format "[[%s]]" path))))
"Insert absolute org-link"))) "Insert absolute org-link"))))
(ivy-add-actions (ivy-add-actions 'counsel-file-jump (plist-get ivy--actions-list 'counsel-find-file))
'counsel-ag ; also applies to `counsel-rg'
'(("O" +ivy-git-grep-other-window-action "open in other window")))) ;; `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)
(append (list doom-projectile-fd-binary
"--color=never" "-E" ".git"
"--type" "file" "--type" "symlink" "--follow")
(if IS-WINDOWS '("--path-separator=/"))))
((executable-find "rg")
(append (list "rg" "--files" "--follow" "--color=never" "--hidden" "--no-messages")
(cl-loop for dir in projectile-globally-ignored-directories
collect "--glob"
collect (concat "!" dir))
(if IS-WINDOWS (list "--path-separator" "/"))))
((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 (use-package! counsel-projectile
@@ -284,7 +324,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
#'+ivy/projectile-find-file) #'+ivy/projectile-find-file)
;; no highlighting visited files; slows down the filtering ;; 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)
(when (featurep! +prescient)
(setq counsel-projectile-sort-files t)))
(use-package! wgrep (use-package! wgrep
@@ -311,7 +354,9 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
;; posframe. ;; posframe.
(dolist (fn '(swiper counsel-rg counsel-grep counsel-git-grep)) (dolist (fn '(swiper counsel-rg counsel-grep counsel-git-grep))
(setf (alist-get fn ivy-posframe-display-functions-alist) (setf (alist-get fn ivy-posframe-display-functions-alist)
#'ivy-display-function-fallback))) #'ivy-display-function-fallback))
(add-hook 'doom-reload-hook #'posframe-delete-all))
(use-package! flx (use-package! flx
@@ -322,24 +367,26 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! ivy-prescient (use-package! ivy-prescient
:hook (ivy-mode . ivy-prescient-mode)
:when (featurep! +prescient) :when (featurep! +prescient)
:hook (ivy-mode . ivy-prescient-mode)
:hook (ivy-prescient-mode . prescient-persist-mode)
:init :init
(setq prescient-filter-method (setq prescient-filter-method
(if (featurep! +fuzzy) (if (featurep! +fuzzy)
'(literal regexp initialism fuzzy) '(literal regexp initialism fuzzy)
'(literal regexp initialism)) '(literal regexp initialism)))
ivy-prescient-enable-filtering nil ; we do this ourselves
ivy-prescient-retain-classic-highlighting t)
:config :config
(setq ivy-prescient-sort-commands
'(:not swiper swiper-isearch ivy-switch-buffer counsel-grep
counsel-git-grep counsel-ag counsel-rg counsel-imenu
counsel-yank-pop counsel-recentf counsel-buffer-or-recentf)
ivy-prescient-retain-classic-highlighting t)
(defun +ivy-prescient-non-fuzzy (str) (defun +ivy-prescient-non-fuzzy (str)
(let ((prescient-filter-method '(literal regexp))) (let ((prescient-filter-method '(literal regexp)))
(ivy-prescient-re-builder str))) (ivy-prescient-re-builder str)))
;; NOTE prescient config duplicated with `company' ;; NOTE prescient config duplicated with `company'
(setq prescient-save-file (concat doom-cache-dir "prescient-save.el")) (setq prescient-save-file (concat doom-cache-dir "prescient-save.el")))
(prescient-persist-mode +1))
;;;###package swiper ;;;###package swiper
+11 -10
View File
@@ -1,22 +1,23 @@
;; -*- no-byte-compile: t; -*- ;; -*- no-byte-compile: t; -*-
;;; completion/ivy/packages.el ;;; completion/ivy/packages.el
(package! amx) (package! swiper :pin "544e7de63a4543a74596c5d95efa0bb9da25791e")
(package! ivy) (package! ivy)
(package! counsel)
(package! counsel-projectile)
(package! swiper)
(package! ivy-hydra) (package! ivy-hydra)
(package! ivy-rich) (package! counsel)
(package! wgrep)
(package! amx :pin "7fb7b874291e0cdeb1f0acb18564a686ec86788d")
(package! counsel-projectile :pin "77392cbbc42e98fc137b43f1db1b111ba6e2dd75")
(package! ivy-rich :pin "10970130b41c6ef9570893cdab8dfbe720e2b1a9")
(package! wgrep :pin "f0ef9bfa44db503cdb2f83fcfbd2fa4e2382ef1f")
(if (featurep! +prescient) (if (featurep! +prescient)
(package! ivy-prescient) (package! ivy-prescient :pin "3ab7605d997fb8337bf5ded2ad960b98ac0e1fd7")
(when (featurep! +fuzzy) (when (featurep! +fuzzy)
(package! flx))) (package! flx :pin "17f5c9cb2af18aa6f52910ff4a5a63591261ced5")))
(when (featurep! +childframe) (when (featurep! +childframe)
(package! ivy-posframe)) (package! ivy-posframe :pin "44749562a9e68bd43ccaa225b31311476fab1251"))
(when (featurep! +icons) (when (featurep! +icons)
(package! all-the-icons-ivy)) (package! all-the-icons-ivy :pin "a70cbfa1effe36efc946a823a580cec686d5e88d"))
@@ -25,76 +25,231 @@
(:prefix ("l" . "<localleader>")) ; bound locally (:prefix ("l" . "<localleader>")) ; bound locally
(:prefix ("!" . "checkers")) ; bound by flycheck (:prefix ("!" . "checkers")) ; bound by flycheck
;;; <leader> 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! :checkers syntax)
:desc "List errors" "x" #'flycheck-list-errors)
(:when (and (featurep! :tools lsp) (not (featurep! :tools lsp +eglot)))
:desc "LSP Code actions" "a" #'lsp-execute-code-action
:desc "LSP Organize imports" "i" #'lsp-organize-imports
:desc "LSP Rename" "r" #'lsp-rename
(:after lsp-mode
:desc "LSP" "l" lsp-command-map)
(: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))
(:when (featurep! :tools lsp +eglot)
:desc "LSP Execute code action" "a" #'eglot-code-actions
:desc "LSP Format buffer/region" "F" #'eglot-format
:desc "LSP Rename" "r" #'eglot-rename
:desc "LSP Find declaration" "j" #'eglot-find-declaration
:desc "LSP Find implementation" "J" #'eglot-find-implementation))
;;; <leader> f --- file ;;; <leader> f --- file
(:prefix-map ("f" . "file") (:prefix-map ("f" . "file")
:desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig (:when (featurep! :tools editorconfig)
:desc "Find other file" "a" #'projectile-find-other-file :desc "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig)
: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 "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 "Find file in emacs.d" "e" #'+default/find-in-emacsd
:desc "Browse emacs.d" "E" #'+default/browse-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" "f" #'find-file
:desc "Find file in other project" "F" #'doom/browse-in-other-project :desc "Find file from here" "F" #'+default/find-file-under-here
:desc "Delete this file" "K" #'doom/delete-this-file :desc "Locate file" "l" #'locate
:desc "Move this file" "m" #'doom/move-this-file :desc "Rename/move this file" "m" #'doom/move-this-file
:desc "Find file in project" "p" #'projectile-find-file :desc "Find file in private config" "p" #'doom/open-private-config
:desc "Find file in other project" "P" #'doom/find-file-in-other-project :desc "Browse private config" "P" #'doom/open-private-config
:desc "Recent files" "r" #'recentf-open-files :desc "Recent files" "r" #'recentf-open-files
:desc "Recent project files" "R" #'projectile-recentf :desc "Recent project files" "R" #'projectile-recentf
:desc "Sudo this file" "s" #'doom/sudo-this-file :desc "Sudo this file" "u" #'doom/sudo-this-file
:desc "Sudo find file" "S" #'doom/sudo-find-file :desc "Sudo find file" "U" #'doom/sudo-find-file
:desc "Yank filename" "y" #'+default/yank-buffer-filename :desc "Yank filename" "y" #'+default/yank-buffer-filename
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer :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)
;;; <leader> g --- lookup ;;; <leader> r --- remote
(:when (featurep! :tools lookup) (:when (featurep! :tools upload)
(:prefix-map ("g" . "lookup") (:prefix-map ("r" . "remote")
"k" #'+lookup/documentation :desc "Browse remote" "b" #'ssh-deploy-browse-remote-base-handler
"d" #'+lookup/definition :desc "Browse relative" "B" #'ssh-deploy-browse-remote-handler
"D" #'+lookup/references :desc "Download remote" "d" #'ssh-deploy-download-handler
"f" #'+lookup/file :desc "Delete local & remote" "D" #'ssh-deploy-delete-handler
"o" #'+lookup/online-select :desc "Eshell base terminal" "e" #'ssh-deploy-remote-terminal-eshell-base-handler
"i" #'+lookup/in-docsets :desc "Eshell relative terminal" "E" #'ssh-deploy-remote-terminal-eshell-handler
"I" #'+lookup/in-all-docsets)) :desc "Move/rename local & remote" "m" #'ssh-deploy-rename-handler
:desc "Open this file on remote" "o" #'ssh-deploy-open-remote-file-handler
:desc "Run deploy script" "s" #'ssh-deploy-run-deploy-script-handler
:desc "Upload local" "u" #'ssh-deploy-upload-handler
:desc "Upload local (force)" "U" #'ssh-deploy-upload-handler-forced
:desc "Diff local & remote" "x" #'ssh-deploy-diff-handler
:desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler
:desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler))
;;; <leader> o --- org ;;; <leader> s --- search
"o" nil ; we need to unbind it first as Org claims this (:prefix-map ("s" . "search")
(:prefix-map ("o". "org") :desc "Search project for symbol" "." #'+default/search-project-for-symbol-at-point
:desc "Do what I mean" "o" #'+org/dwim-at-point :desc "Search buffer" "b" #'swiper
:desc "Display inline images" "i" #'org-display-inline-images :desc "Search current directory" "d" #'+default/search-cwd
:desc "Search other directory" "D" #'+default/search-other-cwd
:desc "Locate file" "f" #'+lookup/file
: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 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 "Search buffer" "s" #'swiper-isearch
:desc "Search buffer for thing at point" "S" #'swiper-isearch-thing-at-point
:desc "Dictionary" "t" #'+lookup/dictionary-definition
:desc "Thesaurus" "T" #'+lookup/synonyms)
;;; <leader> i --- insert
(:prefix-map ("i" . "insert")
:desc "Current file name" "f" #'+default/insert-file-path
:desc "Current file path" "F" (cmd!! #'+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)
;;; <leader> n --- notes
(:prefix-map ("n" . "notes")
:desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point :desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point
(:prefix ("a" . "org agenda") :desc "Org agenda" "a" #'org-agenda
:desc "Agenda" "a" #'org-agenda (:when (featurep! :tools biblio)
:desc "Bibliographic entries" "b"
(cond ((featurep! :completion ivy) #'ivy-bibtex)
((featurep! :completion helm) #'helm-bibtex)))
:desc "Toggle org-clock" "c" #'+org/toggle-clock
:desc "Cancel org-clock" "C" #'org-clock-cancel
:desc "Open deft" "d" #'deft
(:when (featurep! :lang org +noter)
:desc "Org noter" "e" #'org-noter)
: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 "Todo list" "t" #'org-todo-list
:desc "Tags view" "m" #'org-tags-view :desc "Search notes" "s" #'+default/org-notes-search
:desc "View search" "v" #'org-search-view) :desc "Search org agenda headlines" "S" #'+default/org-notes-headlines
:desc "Browse notes" "f" #'+default/browse-notes :desc "View search" "v" #'org-search-view
:desc "Search org-directory" "s" #'+default/org-notes-search :desc "Org export to clipboard" "y" #'+org/export-to-clipboard
:desc "Switch org buffers" "b" #'org-switchb :desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text
:desc "Capture" "c" #'org-capture (:when (featurep! :lang org +journal)
:desc "Goto capture" "C" #'org-capture-goto-target (:prefix ("j" . "journal")
:desc "Link store" "l" #'org-store-link :desc "New Entry" "j" #'org-journal-new-entry
:desc "Sync org caldav" "S" #'org-caldav-sync) :desc "Search Forever" "s" #'org-journal-search-forever))
(:when (featurep! :lang org +roam)
(:prefix ("r" . "roam")
:desc "Switch to buffer" "b" #'org-roam-switch-to-buffer
:desc "Org Roam Capture" "c" #'org-roam-capture
:desc "Find file" "f" #'org-roam-find-file
:desc "Show graph" "g" #'org-roam-graph
:desc "Insert" "i" #'org-roam-insert
:desc "Org Roam" "r" #'org-roam
(:prefix ("d" . "by date")
:desc "Arbitrary date" "d" #'org-roam-dailies-date
:desc "Today" "t" #'org-roam-dailies-today
:desc "Tomorrow" "m" #'org-roam-dailies-tomorrow
:desc "Yesterday" "y" #'org-roam-dailies-yesterday))))
;;; <leader> 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
:desc "Open in iTerm" "i" #'+macos/open-in-iterm)
(:when (featurep! :tools docker)
:desc "Docker" "D" #'docker)
(:when (featurep! :email mu4e)
:desc "mu4e" "m" #'=mu4e)
(:when (featurep! :email notmuch)
:desc "notmuch" "m" #'=notmuch)
(:when (featurep! :email wanderlust)
:desc "wanderlust" "m" #'=wanderlust))
;;; <leader> p --- project ;;; <leader> p --- project
(:prefix ("p" . "project") (:prefix ("p" . "project")
:desc "Search project for symbol" "." #'+default/search-project-for-symbol-at-point
:desc "Find file in other project" "F" #'doom/find-file-in-other-project :desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Search project" "s" #'+default/search-project :desc "Search project" "s" #'+default/search-project
:desc "List project tasks" "t" #'magit-todos-list :desc "List project todos" "t" #'magit-todos-list
:desc "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer :desc "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer :desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
(:when (and (featurep! :tools taskrunner)
(or (featurep! :completion ivy)
(featurep! :completion helm)))
:desc "List project tasks" "z" #'+taskrunner/project-tasks)
;; later expanded by projectile ;; later expanded by projectile
(:prefix ("4" . "in other window")) (:prefix ("4" . "in other window"))
(:prefix ("5" . "in other frame"))) (:prefix ("5" . "in other frame")))
;;; <leader> q --- quit/restart ;;; <leader> q --- quit/restart
(:prefix-map ("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 "Quit Emacs" "q" #'kill-emacs
:desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal :desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal
(:when (featurep! :ui workspaces) :desc "Quick save current session" "s" #'doom/quicksave-session
:desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit) :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 & restore Emacs" "r" #'doom/restart-and-restore
:desc "Restart Emacs" "R" #'doom/restart) :desc "Restart Emacs" "R" #'doom/restart)
@@ -107,21 +262,34 @@
:desc "Create Temp Template" "c" #'aya-create :desc "Create Temp Template" "c" #'aya-create
:desc "Use Temp Template" "e" #'aya-expand) :desc "Use Temp Template" "e" #'aya-expand)
(:prefix-map ("t" . "terminal") ;;; <leader> t --- toggle
(:when (featurep! :term term) (:prefix-map ("t" . "toggle")
:desc "Toggle term popup" "t" #'+term/toggle :desc "Big mode" "b" #'doom-big-font-mode
:desc "Open term here" "T" #'+term/here) :desc "Flymake" "f" #'flymake-mode
(:when (featurep! :term vterm) :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Toggle vterm popup" "t" #'+vterm/toggle :desc "Indent style" "I" #'doom/toggle-indent-style
:desc "Open vterm here" "T" #'+vterm/here) :desc "Line numbers" "l" #'doom/toggle-line-numbers
(:when (featurep! :term eshell) :desc "Word-wrap mode" "w" #'+word-wrap-mode
:desc "Toggle eshell popup" "t" #'+eshell/toggle (:when (featurep! :checkers syntax)
:desc "Open eshell here" "T" #'+eshell/here)) :desc "Flycheck" "f" #'flycheck-mode)
(:when (featurep! :ui indent-guides)
:desc "Indent guides" "i" #'highlight-indent-guides-mode)
(:when (featurep! :ui minimap)
:desc "Minimap mode" "m" #'minimap-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! :checkers spell)
: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))
;;; <leader> v --- versioning ;;; <leader> v --- versioning
(:prefix-map ("v" . "versioning") (:prefix-map ("v" . "versioning")
:desc "Git revert file" "R" #'vc-revert :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 remote" "y" #'browse-at-remote-kill
:desc "Kill link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage :desc "Kill link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage
(:when (featurep! :ui vc-gutter) (:when (featurep! :ui vc-gutter)
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk :desc "Git revert hunk" "r" #'git-gutter:revert-hunk
@@ -133,6 +301,7 @@
:desc "Magit dispatch" "/" #'magit-dispatch :desc "Magit dispatch" "/" #'magit-dispatch
:desc "Forge dispatch" "'" #'forge-dispatch :desc "Forge dispatch" "'" #'forge-dispatch
:desc "Magit status" "g" #'magit-status :desc "Magit status" "g" #'magit-status
:desc "Magit status here" "G" #'magit-status-here
:desc "Magit file delete" "x" #'magit-file-delete :desc "Magit file delete" "x" #'magit-file-delete
:desc "Magit blame" "B" #'magit-blame-addition :desc "Magit blame" "B" #'magit-blame-addition
:desc "Magit clone" "C" #'magit-clone :desc "Magit clone" "C" #'magit-clone
@@ -147,7 +316,7 @@
:desc "Find issue" "i" #'forge-visit-issue :desc "Find issue" "i" #'forge-visit-issue
:desc "Find pull request" "p" #'forge-visit-pullreq) :desc "Find pull request" "p" #'forge-visit-pullreq)
(:prefix ("o" . "open in browser") (:prefix ("o" . "open in browser")
:desc "Browse file or region" "." #'+vc/browse-at-remote-file-or-region :desc "Browse file or region" "." #'browse-at-remote
:desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage :desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage
:desc "Browse remote" "r" #'forge-browse-remote :desc "Browse remote" "r" #'forge-browse-remote
:desc "Browse commit" "c" #'forge-browse-commit :desc "Browse commit" "c" #'forge-browse-commit
@@ -157,7 +326,7 @@
:desc "Browse pull requests" "P" #'forge-browse-pullreqs) :desc "Browse pull requests" "P" #'forge-browse-pullreqs)
(:prefix ("l" . "list") (:prefix ("l" . "list")
(:when (featurep! :tools gist) (:when (featurep! :tools gist)
:desc "List gists" "g" #'+gist:list) :desc "List gists" "g" #'gist-list)
:desc "List repositories" "r" #'magit-list-repositories :desc "List repositories" "r" #'magit-list-repositories
:desc "List submodules" "s" #'magit-list-submodules :desc "List submodules" "s" #'magit-list-submodules
:desc "List issues" "i" #'forge-list-issues :desc "List issues" "i" #'forge-list-issues
@@ -201,7 +370,7 @@
;;; <leader> m --- multiple cursors ;;; <leader> m --- multiple cursors
(:when (featurep! :editor multiple-cursors) (:when (featurep! :editor multiple-cursors)
(:prefix-map ("m" . "multiple cursors") (:prefix-map ("m" . "multiple-cursors")
:desc "Edit lines" "l" #'mc/edit-lines :desc "Edit lines" "l" #'mc/edit-lines
:desc "Mark next" "n" #'mc/mark-next-like-this :desc "Mark next" "n" #'mc/mark-next-like-this
:desc "Unmark next" "N" #'mc/unmark-next-like-this :desc "Unmark next" "N" #'mc/unmark-next-like-this
@@ -231,7 +400,7 @@
:desc "Reconnect all" "r" #'circe-reconnect-all :desc "Reconnect all" "r" #'circe-reconnect-all
:desc "Send message" "s" #'+irc/send-message :desc "Send message" "s" #'+irc/send-message
(:when (featurep! :completion ivy) (:when (featurep! :completion ivy)
:desc "Jump to channel" "j" #'irc/ivy-jump-to-channel))) :desc "Jump to channel" "j" #'+irc/ivy-jump-to-channel)))
;;; <leader> T --- twitter ;;; <leader> T --- twitter
(:when (featurep! :app twitter) (:when (featurep! :app twitter)
@@ -250,7 +419,7 @@
;;; Text scaling ;;; Text scaling
[C-mouse-4] #'text-scale-increase [C-mouse-4] #'text-scale-increase
[C-mouse-5] #'text-scale-decrease [C-mouse-5] #'text-scale-decrease
[C-down-mouse-2] (λ! (text-scale-set 0)) [C-down-mouse-2] (cmd! (text-scale-set 0))
"M-+" #'doom/reset-font-size "M-+" #'doom/reset-font-size
"M-=" #'doom/increase-font-size "M-=" #'doom/increase-font-size
"M--" #'doom/decrease-font-size "M--" #'doom/decrease-font-size
@@ -272,12 +441,15 @@
"M-SPC" #'objed-activate) "M-SPC" #'objed-activate)
;;; buffer management ;;; buffer management
"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" #'persp-switch-to-buffer
(:when (featurep! :completion ivy)
"C-x 4 b" #'+ivy/switch-workspace-buffer-other-window)
"C-x C-b" #'ibuffer-list-buffers
"C-x B" #'switch-to-buffer "C-x B" #'switch-to-buffer
"C-x 4 B" #'switch-to-buffer-other-window "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
"C-x K" #'doom/kill-this-buffer-in-all-windows "C-x K" #'doom/kill-this-buffer-in-all-windows
;;; company-mode ;;; company-mode
@@ -291,14 +463,14 @@
"C-S-h" #'company-show-doc-buffer "C-S-h" #'company-show-doc-buffer
"C-s" #'company-search-candidates "C-s" #'company-search-candidates
"M-s" #'company-filter-candidates "M-s" #'company-filter-candidates
"<C-tab>" #'company-complete-common-or-cycle [C-tab] #'company-complete-common-or-cycle
[tab] #'company-complete-common-or-cycle [tab] #'company-complete-common-or-cycle
[backtab] #'company-select-previous [backtab] #'company-select-previous
"C-RET" #'counsel-company "C-RET" #'counsel-company
:map company-search-map :map company-search-map
"C-n" #'company-search-repeat-forward "C-n" #'company-search-repeat-forward
"C-p" #'company-search-repeat-backward "C-p" #'company-search-repeat-backward
"C-s" (λ! (company-search-abort) (company-filter-candidates))) "C-s" (cmd! (company-search-abort) (company-filter-candidates)))
;;; ein notebooks ;;; ein notebooks
(:after ein:notebook-multilang (:after ein:notebook-multilang
@@ -319,22 +491,22 @@
;;; help and info ;;; help and info
(:after help-mode (:after help-mode
:map help-mode-map :map help-mode-map
"o" #'ace-link-help "o" #'link-hint-open-link
">" #'help-go-forward ">" #'help-go-forward
"<" #'help-go-back "<" #'help-go-back
"n" #'forward-button "n" #'forward-button
"p" #'backward-button) "p" #'backward-button)
(:after helpful (:after helpful
:map helpful-mode-map :map helpful-mode-map
"o" #'ace-link-help) "o" #'link-hint-open-link)
(:after apropos (:after apropos
:map apropos-mode-map :map apropos-mode-map
"o" #'ace-link-help "o" #'link-hint-open-link
"n" #'forward-button "n" #'forward-button
"p" #'backward-button) "p" #'backward-button)
(:after info (:after info
:map Info-mode-map :map Info-mode-map
"o" #'ace-link-info) "o" #'link-hint-open-link)
;;; ivy & counsel ;;; ivy & counsel
(:when (featurep! :completion ivy) (:when (featurep! :completion ivy)
@@ -377,10 +549,6 @@
"C-`" #'+popup/toggle "C-`" #'+popup/toggle
"C-~" #'+popup/raise) "C-~" #'+popup/raise)
;;; repl
"C-c C-z" #'+eval/open-repl-other-window
"C-c C-S-z" #'+eval/open-repl-same-window
;;; smartparens ;;; smartparens
(:after smartparens (:after smartparens
:map smartparens-mode-map :map smartparens-mode-map
@@ -399,14 +567,4 @@
;;; treemacs ;;; treemacs
(:when (featurep! :ui treemacs) (:when (featurep! :ui treemacs)
"<f9>" #'+treemacs/toggle "<f9>" #'+treemacs/toggle
"<C-f9>" #'+treemacs/find-file) "<C-f9>" #'treemacs-find-file))
;;; yasnippet
(:after yasnippet
:map yas-keymap ; keymap while editing an inserted snippet
"C-e" #'+snippets/goto-end-of-field
"C-a" #'+snippets/goto-start-of-field
"<S-tab>" #'yas-prev-field
"<M-backspace>" #'+snippets/delete-to-start-of-field
[backspace] #'+snippets/delete-backward-char
[delete] #'+snippets/delete-forward-char-or-field))
+136 -76
View File
@@ -1,38 +1,34 @@
;;; config/default/+bindings.el -*- lexical-binding: t; -*- ;;; config/default/+bindings.el -*- lexical-binding: t; -*-
(when (featurep! :editor evil +everywhere) (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. ;; 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 ;; Minibuffer
(define-key! evil-ex-completion-map (map! :map (evil-ex-completion-map evil-ex-search-keymap)
"C-a" #'move-beginning-of-line "C-a" #'evil-beginning-of-line
"C-b" #'backward-word "C-b" #'evil-backward-char
"C-s" (if (featurep! :completion ivy) "C-f" #'evil-forward-char
#'counsel-minibuffer-history :gi "C-j" #'next-complete-history-element
#'helm-minibuffer-history)) :gi "C-k" #'previous-complete-history-element)
(define-key! :keymaps +default-minibuffer-maps (define-key! :keymaps +default-minibuffer-maps
[escape] #'abort-recursive-edit [escape] #'abort-recursive-edit
"C-a" #'move-beginning-of-line "C-a" #'move-beginning-of-line
"C-b" #'backward-word
"C-f" #'forward-word
"C-r" #'evil-paste-from-register "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-v" #'yank
"C-w" #'backward-kill-word "C-w" #'doom/delete-backward-word
"C-z" (λ! (ignore-errors (call-interactively #'undo))) "C-z" (cmd! (ignore-errors (call-interactively #'undo))))
;; Scrolling lines
(define-key! :keymaps +default-minibuffer-maps
"C-j" #'next-line "C-j" #'next-line
"C-k" #'previous-line "C-k" #'previous-line
"C-S-j" #'scroll-up-command "C-S-j" #'scroll-up-command
"C-S-k" #'scroll-down-command) "C-S-k" #'scroll-down-command)
;; For folks with `evil-collection-setup-minibuffer' enabled
(define-key! :states 'insert :keymaps +default-minibuffer-maps
"C-j" #'next-line
"C-k" #'previous-line)
(define-key! read-expression-map (define-key! read-expression-map
"C-j" #'next-line-or-history-element "C-j" #'next-line-or-history-element
"C-k" #'previous-line-or-history-element)) "C-k" #'previous-line-or-history-element))
@@ -42,40 +38,30 @@
;;; Global keybindings ;;; Global keybindings
;; Smart tab, these will only work in GUI Emacs ;; Smart tab, these will only work in GUI Emacs
(map! :i [tab] (general-predicate-dispatch nil ; fall back to nearest keymap (map! :i [tab] (cmds! (and (featurep! :editor snippets)
(and (featurep! :editor snippets)
(bound-and-true-p yas-minor-mode) (bound-and-true-p yas-minor-mode)
(yas-maybe-expand-abbrev-key-filter 'yas-expand)) (yas-maybe-expand-abbrev-key-filter 'yas-expand))
#'yas-expand #'yas-expand
(and (featurep! :completion company +tng) (and (featurep! :completion company +tng)
(+company-has-completion-p)) (+company-has-completion-p))
#'+company/complete) #'+company/complete)
:n [tab] (general-predicate-dispatch nil :v [tab] (cmds! (and (bound-and-true-p yas-minor-mode)
(and (featurep! :editor fold)
(save-excursion (end-of-line) (invisible-p (point))))
#'+fold/toggle
(fboundp 'evil-jump-item)
#'evil-jump-item)
:v [tab] (general-predicate-dispatch nil
(and (bound-and-true-p yas-minor-mode)
(or (eq evil-visual-selection 'line) (or (eq evil-visual-selection 'line)
(not (memq (char-after) (list ?\( ?\[ ?\{ ?\} ?\] ?\)))))) (not (memq (char-after) (list ?\( ?\[ ?\{ ?\} ?\] ?\))))))
#'yas-insert-snippet #'yas-insert-snippet)
(fboundp 'evil-jump-item)
#'evil-jump-item)
;; Smarter newlines ;; Smarter newlines
:i [remap newline] #'newline-and-indent ; auto-indent on newline :i [remap newline] #'newline-and-indent ; auto-indent on newline
:i "C-j" #'+default/newline ; default behavior :i "C-j" #'+default/newline ; default behavior
(:after help :map help-mode-map (:after help :map help-mode-map
:n "o" #'ace-link-help) :n "o" #'link-hint-open-link)
(:after helpful :map helpful-mode-map (:after helpful :map helpful-mode-map
:n "o" #'ace-link-help) :n "o" #'link-hint-open-link)
(:after info :map Info-mode-map (:after info :map Info-mode-map
:n "o" #'ace-link-info) :n "o" #'link-hint-open-link)
(:after apropos :map apropos-mode-map (: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 [tab] #'forward-button :n [tab] #'forward-button
:n [backtab] #'backward-button) :n [backtab] #'backward-button)
@@ -84,9 +70,8 @@
(:after man :map Man-mode-map (:after man :map Man-mode-map
:n "q" #'kill-current-buffer) :n "q" #'kill-current-buffer)
:m "gs" #'+evil/easymotion ; lazy-load `evil-easymotion' (:after (evil-org evil-easymotion)
(:after org :map evil-org-mode-map
:map org-mode-map
:m "gsh" #'+org/goto-visible) :m "gsh" #'+org/goto-visible)
(:when (featurep! :editor multiple-cursors) (:when (featurep! :editor multiple-cursors)
@@ -102,8 +87,8 @@
:nv "P" #'evil-mc-make-and-goto-first-cursor :nv "P" #'evil-mc-make-and-goto-first-cursor
:nv "q" #'evil-mc-undo-all-cursors :nv "q" #'evil-mc-undo-all-cursors
:nv "t" #'+multiple-cursors/evil-mc-toggle-cursors :nv "t" #'+multiple-cursors/evil-mc-toggle-cursors
:nv "u" #'evil-mc-undo-last-added-cursor :nv "u" #'+multiple-cursors/evil-mc-undo-cursor
:nv "z" #'+multiple-cursors/evil-mc-make-cursor-here :nv "z" #'+multiple-cursors/evil-mc-toggle-cursor-here
:v "I" #'evil-mc-make-cursor-in-visual-selection-beg :v "I" #'evil-mc-make-cursor-in-visual-selection-beg
:v "A" #'evil-mc-make-cursor-in-visual-selection-end) :v "A" #'evil-mc-make-cursor-in-visual-selection-end)
@@ -148,8 +133,8 @@
"C-p" #'company-select-previous-or-abort "C-p" #'company-select-previous-or-abort
"C-j" #'company-select-next-or-abort "C-j" #'company-select-next-or-abort
"C-k" #'company-select-previous-or-abort "C-k" #'company-select-previous-or-abort
"C-s" (λ! (company-search-abort) (company-filter-candidates)) "C-s" (cmd! (company-search-abort) (company-filter-candidates))
"ESC" #'company-search-abort)) [escape] #'company-search-abort))
;; TAB auto-completion in term buffers ;; TAB auto-completion in term buffers
(:after comint :map comint-mode-map (:after comint :map comint-mode-map
"TAB" #'company-complete "TAB" #'company-complete
@@ -169,15 +154,18 @@
(:when (featurep! :completion helm) (:when (featurep! :completion helm)
(:after helm :map helm-map (:after helm :map helm-map
[remap next-line] #'helm-next-line
[remap previous-line] #'helm-previous-line
[left] #'left-char [left] #'left-char
[right] #'right-char [right] #'right-char
"C-S-f" #'helm-previous-page "C-S-f" #'helm-previous-page
"C-S-n" #'helm-next-source "C-S-n" #'helm-next-source
"C-S-p" #'helm-previous-source "C-S-p" #'helm-previous-source
"C-S-j" #'helm-next-source (:when (featurep! :editor evil +everywhere)
"C-S-k" #'helm-previous-source
"C-j" #'helm-next-line "C-j" #'helm-next-line
"C-k" #'helm-previous-line "C-k" #'helm-previous-line
"C-S-j" #'helm-next-source
"C-S-k" #'helm-previous-source)
"C-u" #'helm-delete-minibuffer-contents "C-u" #'helm-delete-minibuffer-contents
"C-s" #'helm-minibuffer-history "C-s" #'helm-minibuffer-history
;; Swap TAB and C-z ;; Swap TAB and C-z
@@ -203,9 +191,9 @@
;;; :ui ;;; :ui
(map! (:when (featurep! :ui popup) (map! (:when (featurep! :ui popup)
:n "C-`" #'+popup/toggle "C-`" #'+popup/toggle
:n "C-~" #'+popup/raise "C-~" #'+popup/raise
:g "C-x p" #'+popup/other) "C-x p" #'+popup/other)
(:when (featurep! :ui workspaces) (:when (featurep! :ui workspaces)
:n "C-t" #'+workspace/new :n "C-t" #'+workspace/new
@@ -274,7 +262,6 @@
:desc "M-x" ":" #'execute-extended-command :desc "M-x" ":" #'execute-extended-command
:desc "Pop up scratch buffer" "x" #'doom/open-scratch-buffer :desc "Pop up scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Org Capture" "X" #'org-capture :desc "Org Capture" "X" #'org-capture
;; C-u is used by evil ;; C-u is used by evil
:desc "Universal argument" "u" #'universal-argument :desc "Universal argument" "u" #'universal-argument
:desc "window" "w" evil-window-map :desc "window" "w" evil-window-map
@@ -283,12 +270,10 @@
(:when (featurep! :ui popup) (:when (featurep! :ui popup)
:desc "Toggle last popup" "~" #'+popup/toggle) :desc "Toggle last popup" "~" #'+popup/toggle)
:desc "Find file" "." #'find-file :desc "Find file" "." #'find-file
:desc "Switch buffer" "," #'switch-to-buffer :desc "Switch buffer" "," #'switch-to-buffer
(:when (featurep! :ui workspaces) (:when (featurep! :ui workspaces)
:desc "Switch workspace buffer" "," #'persp-switch-to-buffer :desc "Switch workspace buffer" "," #'persp-switch-to-buffer
:desc "Switch buffer" "<" #'switch-to-buffer) :desc "Switch buffer" "<" #'switch-to-buffer)
:desc "Switch to last buffer" "`" #'evil-switch-to-windows-last-buffer :desc "Switch to last buffer" "`" #'evil-switch-to-windows-last-buffer
:desc "Resume last search" "'" :desc "Resume last search" "'"
(cond ((featurep! :completion ivy) #'ivy-resume) (cond ((featurep! :completion ivy) #'ivy-resume)
@@ -340,6 +325,8 @@
:desc "Kill buffer" "k" #'kill-current-buffer :desc "Kill buffer" "k" #'kill-current-buffer
:desc "Kill all buffers" "K" #'doom/kill-all-buffers :desc "Kill all buffers" "K" #'doom/kill-all-buffers
:desc "Switch to last buffer" "l" #'evil-switch-to-windows-last-buffer :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 "Next buffer" "n" #'next-buffer
:desc "New empty buffer" "N" #'evil-buffer-new :desc "New empty buffer" "N" #'evil-buffer-new
:desc "Kill other buffers" "O" #'doom/kill-other-buffers :desc "Kill other buffers" "O" #'doom/kill-other-buffers
@@ -347,6 +334,7 @@
:desc "Revert buffer" "r" #'revert-buffer :desc "Revert buffer" "r" #'revert-buffer
:desc "Save buffer" "s" #'basic-save-buffer :desc "Save buffer" "s" #'basic-save-buffer
:desc "Save all buffers" "S" #'evil-write-all :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 "Pop up scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer :desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer
:desc "Bury buffer" "z" #'bury-buffer :desc "Bury buffer" "z" #'bury-buffer
@@ -354,6 +342,24 @@
;;; <leader> c --- code ;;; <leader> c --- code
(:prefix-map ("c" . "code") (:prefix-map ("c" . "code")
(:unless (featurep! :tools lsp +eglot)
:desc "LSP Execute code action" "a" #'lsp-execute-code-action
: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 "LSP Rename" "r" #'lsp-rename
(:after lsp-mode
:desc "LSP" "l" lsp-command-map))
(:when (featurep! :tools lsp +eglot)
:desc "LSP Execute code action" "a" #'eglot-code-actions
:desc "LSP Format buffer/region" "F" #'eglot-format
:desc "LSP Rename" "r" #'eglot-rename
:desc "LSP Find declaration" "j" #'eglot-find-declaration
:desc "LSP Find implementation" "J" #'eglot-find-implementation)
:desc "Compile" "c" #'compile :desc "Compile" "c" #'compile
:desc "Recompile" "C" #'recompile :desc "Recompile" "C" #'recompile
:desc "Jump to definition" "d" #'+lookup/definition :desc "Jump to definition" "d" #'+lookup/definition
@@ -361,15 +367,12 @@
:desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region :desc "Evaluate buffer/region" "e" #'+eval/buffer-or-region
:desc "Evaluate & replace region" "E" #'+eval:replace-region :desc "Evaluate & replace region" "E" #'+eval:replace-region
:desc "Format buffer/region" "f" #'+format/region-or-buffer :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 "Jump to documentation" "k" #'+lookup/documentation
:desc "LSP Rename" "r" #'lsp-rename
:desc "Send to repl" "s" #'+eval/send-region-to-repl :desc "Send to repl" "s" #'+eval/send-region-to-repl
:desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace :desc "Delete trailing whitespace" "w" #'delete-trailing-whitespace
:desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines :desc "Delete trailing newlines" "W" #'doom/delete-trailing-newlines
:desc "List errors" "x" #'flymake-show-diagnostics-buffer :desc "List errors" "x" #'flymake-show-diagnostics-buffer
(:when (featurep! :tools flycheck) (:when (featurep! :checkers syntax)
:desc "List errors" "x" #'flycheck-list-errors)) :desc "List errors" "x" #'flycheck-list-errors))
;;; <leader> f --- file ;;; <leader> f --- file
@@ -393,13 +396,17 @@
:desc "Sudo this file" "U" #'doom/sudo-this-file :desc "Sudo this file" "U" #'doom/sudo-this-file
:desc "Yank filename" "y" #'+default/yank-buffer-filename) :desc "Yank filename" "y" #'+default/yank-buffer-filename)
;;; <leader> g --- git ;;; <leader> g --- git/version control
(:prefix-map ("g" . "git") (:prefix-map ("g" . "git")
:desc "Git revert file" "R" #'vc-revert :desc "Revert file" "R" #'vc-revert
:desc "Copy link to remote" "y" #'+vc/browse-at-remote-kill-file-or-region :desc "Copy link to remote" "y" #'browse-at-remote-kill
:desc "Copy link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage :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) (:when (featurep! :ui vc-gutter)
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk (:when (featurep! :ui hydra)
:desc "VCGutter" "." #'+vc/gutter-hydra/body)
:desc "Revert hunk" "r" #'git-gutter:revert-hunk
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk :desc "Git stage hunk" "s" #'git-gutter:stage-hunk
:desc "Git time machine" "t" #'git-timemachine-toggle :desc "Git time machine" "t" #'git-timemachine-toggle
:desc "Jump to next hunk" "]" #'git-gutter:next-hunk :desc "Jump to next hunk" "]" #'git-gutter:next-hunk
@@ -409,6 +416,7 @@
:desc "Forge dispatch" "'" #'forge-dispatch :desc "Forge dispatch" "'" #'forge-dispatch
:desc "Magit switch branch" "b" #'magit-branch-checkout :desc "Magit switch branch" "b" #'magit-branch-checkout
:desc "Magit status" "g" #'magit-status :desc "Magit status" "g" #'magit-status
:desc "Magit status here" "G" #'magit-status-here
:desc "Magit file delete" "D" #'magit-file-delete :desc "Magit file delete" "D" #'magit-file-delete
:desc "Magit blame" "B" #'magit-blame-addition :desc "Magit blame" "B" #'magit-blame-addition
:desc "Magit clone" "C" #'magit-clone :desc "Magit clone" "C" #'magit-clone
@@ -423,7 +431,7 @@
:desc "Find issue" "i" #'forge-visit-issue :desc "Find issue" "i" #'forge-visit-issue
:desc "Find pull request" "p" #'forge-visit-pullreq) :desc "Find pull request" "p" #'forge-visit-pullreq)
(:prefix ("o" . "open in browser") (:prefix ("o" . "open in browser")
:desc "Browse file or region" "o" #'+vc/browse-at-remote-file-or-region :desc "Browse file or region" "o" #'browse-at-remote
:desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage :desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage
:desc "Browse remote" "r" #'forge-browse-remote :desc "Browse remote" "r" #'forge-browse-remote
:desc "Browse commit" "c" #'forge-browse-commit :desc "Browse commit" "c" #'forge-browse-commit
@@ -451,8 +459,8 @@
;;; <leader> i --- insert ;;; <leader> i --- insert
(:prefix-map ("i" . "insert") (:prefix-map ("i" . "insert")
:desc "Current file name" "f" #'+default/insert-file-path :desc "Current file name" "f" #'+default/insert-file-path
:desc "Current file path" "F" (λ!! #'+default/insert-file-path t) :desc "Current file path" "F" (cmd!! #'+default/insert-file-path t)
:desc "Evil ex path" "p" (λ! (evil-ex "R!echo ")) :desc "Evil ex path" "p" (cmd! (evil-ex "R!echo "))
:desc "From evil register" "r" #'evil-ex-registers :desc "From evil register" "r" #'evil-ex-registers
:desc "Snippet" "s" #'yas-insert-snippet :desc "Snippet" "s" #'yas-insert-snippet
:desc "Unicode" "u" #'unicode-chars-list-chars :desc "Unicode" "u" #'unicode-chars-list-chars
@@ -462,14 +470,23 @@
(:prefix-map ("n" . "notes") (:prefix-map ("n" . "notes")
:desc "Search notes for symbol" "*" #'+default/search-notes-for-symbol-at-point :desc "Search notes for symbol" "*" #'+default/search-notes-for-symbol-at-point
:desc "Org agenda" "a" #'org-agenda :desc "Org agenda" "a" #'org-agenda
(:when (featurep! :tools biblio)
:desc "Bibliographic entries" "b"
(cond ((featurep! :completion ivy) #'ivy-bibtex)
((featurep! :completion helm) #'helm-bibtex)))
:desc "Toggle org-clock" "c" #'+org/toggle-clock :desc "Toggle org-clock" "c" #'+org/toggle-clock
:desc "Cancel org-clock" "C" #'org-clock-cancel :desc "Cancel org-clock" "C" #'org-clock-cancel
:desc "Open deft" "d" #'deft :desc "Open deft" "d" #'deft
(:when (featurep! :lang org +noter)
:desc "Org noter" "e" #'org-noter)
:desc "Find file in notes" "f" #'+default/find-in-notes :desc "Find file in notes" "f" #'+default/find-in-notes
:desc "Browse notes" "F" #'+default/browse-notes :desc "Browse notes" "F" #'+default/browse-notes
:desc "Org store link" "l" #'org-store-link :desc "Org store link" "l" #'org-store-link
:desc "Tags search" "m" #'org-tags-view :desc "Tags search" "m" #'org-tags-view
:desc "Org capture" "n" #'org-capture :desc "Org capture" "n" #'org-capture
:desc "Goto capture" "N" #'org-capture-goto-target
:desc "Active org-clock" "o" #'org-clock-goto :desc "Active org-clock" "o" #'org-clock-goto
:desc "Todo list" "t" #'org-todo-list :desc "Todo list" "t" #'org-todo-list
:desc "Search notes" "s" #'+default/org-notes-search :desc "Search notes" "s" #'+default/org-notes-search
@@ -478,6 +495,20 @@
:desc "Org export to clipboard" "y" #'+org/export-to-clipboard :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 :desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text
(:when (featurep! :lang org +roam)
(:prefix ("r" . "roam")
:desc "Switch to buffer" "b" #'org-roam-switch-to-buffer
:desc "Org Roam Capture" "c" #'org-roam-capture
:desc "Find file" "f" #'org-roam-find-file
:desc "Show graph" "g" #'org-roam-graph
:desc "Insert" "i" #'org-roam-insert
:desc "Org Roam" "r" #'org-roam
(:prefix ("d" . "by date")
:desc "Arbitrary date" "d" #'org-roam-dailies-date
:desc "Today" "t" #'org-roam-dailies-today
:desc "Tomorrow" "m" #'org-roam-dailies-tomorrow
:desc "Yesterday" "y" #'org-roam-dailies-yesterday)))
(:when (featurep! :lang org +journal) (:when (featurep! :lang org +journal)
(:prefix ("j" . "journal") (:prefix ("j" . "journal")
:desc "New Entry" "j" #'org-journal-new-entry :desc "New Entry" "j" #'org-journal-new-entry
@@ -502,7 +533,7 @@
:desc "Find file in project sidebar" "P" #'+neotree/find-this-file) :desc "Find file in project sidebar" "P" #'+neotree/find-this-file)
(:when (featurep! :ui treemacs) (:when (featurep! :ui treemacs)
:desc "Project sidebar" "p" #'+treemacs/toggle :desc "Project sidebar" "p" #'+treemacs/toggle
:desc "Find file in project sidebar" "P" #'+treemacs/find-file) :desc "Find file in project sidebar" "P" #'treemacs-find-file)
(:when (featurep! :term shell) (:when (featurep! :term shell)
:desc "Toggle shell popup" "t" #'+shell/toggle :desc "Toggle shell popup" "t" #'+shell/toggle
:desc "Open shell here" "T" #'+shell/here) :desc "Open shell here" "T" #'+shell/here)
@@ -521,9 +552,16 @@
:desc "Send to Transmit" "u" #'+macos/send-to-transmit :desc "Send to Transmit" "u" #'+macos/send-to-transmit
:desc "Send project to Transmit" "U" #'+macos/send-project-to-transmit :desc "Send project to Transmit" "U" #'+macos/send-project-to-transmit
:desc "Send to Launchbar" "l" #'+macos/send-to-launchbar :desc "Send to Launchbar" "l" #'+macos/send-to-launchbar
:desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar) :desc "Send project to Launchbar" "L" #'+macos/send-project-to-launchbar
:desc "Open in iTerm" "i" #'+macos/open-in-iterm)
(:when (featurep! :tools docker) (:when (featurep! :tools docker)
:desc "Docker" "D" #'docker)) :desc "Docker" "D" #'docker)
(:when (featurep! :email mu4e)
:desc "mu4e" "m" #'=mu4e)
(:when (featurep! :email notmuch)
:desc "notmuch" "m" #'=notmuch)
(:when (featurep! :email wanderlust)
:desc "wanderlust" "m" #'=wanderlust))
;;; <leader> p --- project ;;; <leader> p --- project
(:prefix-map ("p" . "project") (:prefix-map ("p" . "project")
@@ -535,6 +573,7 @@
:desc "Compile in project" "c" #'projectile-compile-project :desc "Compile in project" "c" #'projectile-compile-project
:desc "Repeat last command" "C" #'projectile-repeat-last-command :desc "Repeat last command" "C" #'projectile-repeat-last-command
:desc "Remove known project" "d" #'projectile-remove-known-project :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 "Edit project .dir-locals" "e" #'projectile-edit-dir-locals
:desc "Find file in project" "f" #'projectile-find-file :desc "Find file in project" "f" #'projectile-find-file
:desc "Find file in other project" "F" #'doom/find-file-in-other-project :desc "Find file in other project" "F" #'doom/find-file-in-other-project
@@ -546,10 +585,14 @@
:desc "Find recent project files" "r" #'projectile-recentf :desc "Find recent project files" "r" #'projectile-recentf
:desc "Run project" "R" #'projectile-run-project :desc "Run project" "R" #'projectile-run-project
:desc "Save project files" "s" #'projectile-save-project-buffers :desc "Save project files" "s" #'projectile-save-project-buffers
:desc "List project todos" "t" #'magit-todos-list
:desc "Test project" "T" #'projectile-test-project
:desc "Pop up scratch buffer" "x" #'doom/open-project-scratch-buffer :desc "Pop up scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to scratch buffer" "X" #'doom/switch-to-project-scratch-buffer :desc "Switch to scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
:desc "List project tasks" "t" #'magit-todos-list (:when (and (featurep! :tools taskrunner)
:desc "Test project" "T" #'projectile-test-project) (or (featurep! :completion ivy)
(featurep! :completion helm)))
:desc "List project tasks" "z" #'+taskrunner/project-tasks))
;;; <leader> q --- quit/session ;;; <leader> q --- quit/session
(:prefix-map ("q" . "quit/session") (:prefix-map ("q" . "quit/session")
@@ -569,10 +612,18 @@
;;; <leader> r --- remote ;;; <leader> r --- remote
(:when (featurep! :tools upload) (:when (featurep! :tools upload)
(:prefix-map ("r" . "remote") (:prefix-map ("r" . "remote")
:desc "Browse remote" "b" #'ssh-deploy-browse-remote-base-handler
:desc "Browse relative" "B" #'ssh-deploy-browse-remote-handler
:desc "Download remote" "d" #'ssh-deploy-download-handler
:desc "Delete local & remote" "D" #'ssh-deploy-delete-handler
:desc "Eshell base terminal" "e" #'ssh-deploy-remote-terminal-eshell-base-handler
:desc "Eshell relative terminal" "E" #'ssh-deploy-remote-terminal-eshell-handler
:desc "Move/rename local & remote" "m" #'ssh-deploy-rename-handler
:desc "Open this file on remote" "o" #'ssh-deploy-open-remote-file-handler
:desc "Run deploy script" "s" #'ssh-deploy-run-deploy-script-handler
:desc "Upload local" "u" #'ssh-deploy-upload-handler :desc "Upload local" "u" #'ssh-deploy-upload-handler
:desc "Upload local (force)" "U" #'ssh-deploy-upload-handler-forced :desc "Upload local (force)" "U" #'ssh-deploy-upload-handler-forced
:desc "Download remote" "d" #'ssh-deploy-download-handler :desc "Diff local & remote" "x" #'ssh-deploy-diff-handler
:desc "Diff local & remote" "D" #'ssh-deploy-diff-handler
:desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler :desc "Browse remote files" "." #'ssh-deploy-browse-remote-handler
:desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler)) :desc "Detect remote changes" ">" #'ssh-deploy-remote-changes-handler))
@@ -583,24 +634,27 @@
:desc "Search other directory" "D" #'+default/search-other-cwd :desc "Search other directory" "D" #'+default/search-other-cwd
:desc "Locate file" "f" #'locate :desc "Locate file" "f" #'locate
:desc "Jump to symbol" "i" #'imenu :desc "Jump to symbol" "i" #'imenu
:desc "Jump to visible link" "l" #'ace-link :desc "Jump to visible link" "l" #'link-hint-open-link
:desc "Jump to link" "L" #'ffap-menu :desc "Jump to link" "L" #'ffap-menu
:desc "Jump list" "j" #'evil-show-jumps :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" "o" #'+lookup/online
:desc "Look up online (w/ prompt)" "O" #'+lookup/online-select :desc "Look up online (w/ prompt)" "O" #'+lookup/online-select
:desc "Look up in local docsets" "k" #'+lookup/in-docsets :desc "Look up in local docsets" "k" #'+lookup/in-docsets
:desc "Look up in all docsets" "K" #'+lookup/in-all-docsets :desc "Look up in all docsets" "K" #'+lookup/in-all-docsets
:desc "Search project" "p" #'+default/search-project :desc "Search project" "p" #'+default/search-project
:desc "Search other project" "P" #'+default/search-other-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" "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)
;;; <leader> t --- toggle ;;; <leader> t --- toggle
(:prefix-map ("t" . "toggle") (:prefix-map ("t" . "toggle")
:desc "Big mode" "b" #'doom-big-font-mode :desc "Big mode" "b" #'doom-big-font-mode
:desc "Flymake" "f" #'flymake-mode :desc "Flymake" "f" #'flymake-mode
(:when (featurep! :tools flycheck) (:when (featurep! :checkers syntax)
:desc "Flycheck" "f" #'flycheck-mode) :desc "Flycheck" "f" #'flycheck-mode)
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen :desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Evil goggles" "g" #'evil-goggles-mode :desc "Evil goggles" "g" #'evil-goggles-mode
@@ -608,14 +662,20 @@
:desc "Indent guides" "i" #'highlight-indent-guides-mode) :desc "Indent guides" "i" #'highlight-indent-guides-mode)
:desc "Indent style" "I" #'doom/toggle-indent-style :desc "Indent style" "I" #'doom/toggle-indent-style
:desc "Line numbers" "l" #'doom/toggle-line-numbers :desc "Line numbers" "l" #'doom/toggle-line-numbers
(:when (featurep! :ui minimap)
:desc "Minimap" "m" #'minimap-mode)
(:when (featurep! :lang org +present) (: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 :desc "Read-only mode" "r" #'read-only-mode
(:when (featurep! :tools flyspell) (:when (featurep! :checkers spell)
:desc "Flyspell" "s" #'flyspell-mode) :desc "Flyspell" "s" #'flyspell-mode)
(:when (featurep! :lang org +pomodoro) (:when (featurep! :lang org +pomodoro)
:desc "Pomodoro timer" "t" #'org-pomodoro) :desc "Pomodoro timer" "t" #'org-pomodoro)
:desc "Word-wrap mode" "w" #'+word-wrap-mode)) :desc "Soft line wrapping" "w" #'visual-line-mode
(:when (featurep! :editor word-wrap)
:desc "Soft line wrapping" "w" #'+word-wrap-mode)
(:when (featurep! :ui zen)
:desc "Zen mode" "z" #'writeroom-mode)))
(after! which-key (after! which-key
(let ((prefix-re (regexp-opt (list doom-leader-key doom-leader-alt-key)))) (let ((prefix-re (regexp-opt (list doom-leader-key doom-leader-alt-key))))
@@ -1,39 +1,5 @@
;; config/default/autoload/default.el -*- lexical-binding: t; -*- ;; 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 ;;;###autoload
(defun +default/compile (arg) (defun +default/compile (arg)
"Runs `compile' from the root of the current project. "Runs `compile' from the root of the current project.
@@ -59,9 +25,6 @@ If ARG (universal argument), runs `compile' from the current directory."
#'man #'man
#'woman))) #'woman)))
;;;###autoload
(defalias '+default/newline #'newline)
;;;###autoload ;;;###autoload
(defun +default/new-buffer () (defun +default/new-buffer ()
"TODO" "TODO"
@@ -73,275 +36,11 @@ If ARG (universal argument), runs `compile' from the current directory."
(with-current-buffer buffer (with-current-buffer buffer
(funcall (default-value 'major-mode)))))) (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)
#'lsp-format-region
#'lsp-format-buffer)))
;;;###autoload ;;;###autoload
(defun +default/restart-server () (defun +default/restart-server ()
"Restart the Emacs server." "Restart the Emacs server."
(interactive) (interactive)
(server-force-delete) (server-force-delete)
(while (server-running-p) (while (server-running-p)
(sit-for 1)) (sleep-for 1))
(server-start)) (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)))))
+104 -67
View File
@@ -1,13 +1,16 @@
;;; config/default/config.el -*- lexical-binding: t; -*- ;;; config/default/config.el -*- lexical-binding: t; -*-
(defvar +default-want-RET-continue-comments t
"If non-nil, RET will continue commented lines.")
(defvar +default-minibuffer-maps (defvar +default-minibuffer-maps
`(minibuffer-local-map (append '(minibuffer-local-map
minibuffer-local-ns-map minibuffer-local-ns-map
minibuffer-local-completion-map minibuffer-local-completion-map
minibuffer-local-must-match-map minibuffer-local-must-match-map
minibuffer-local-isearch-map minibuffer-local-isearch-map
read-expression-map read-expression-map)
,@(cond ((featurep! :completion ivy) (cond ((featurep! :completion ivy)
'(ivy-minibuffer-map '(ivy-minibuffer-map
ivy-switch-buffer-map)) ivy-switch-buffer-map))
((featurep! :completion helm) ((featurep! :completion helm)
@@ -23,20 +26,32 @@
;;;###package avy ;;;###package avy
(setq avy-all-windows nil (setq avy-all-windows nil
avy-all-windows-alt t 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 (after! epa
(setq epa-file-encrypt-to ;; With GPG 2.1+, this forces gpg-agent to use the Emacs minibuffer to prompt
(or epa-file-encrypt-to ;; for the key passphrase.
;; Collect all public key IDs with your username (set (if EMACS27+
'epg-pinentry-mode
'epa-pinentry-mode) ; DEPRECATED `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) (unless (string-empty-p user-full-name)
(cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) 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))))) collect (epg-sub-key-id (car (epg-key-sub-key-list key)))))
user-mail-address) user-mail-address))
;; With GPG 2.1, this forces gpg-agent to use the Emacs minibuffer to ;; And suppress prompts if epa-file-encrypt-to has a default value (without
;; prompt for the key passphrase. ;; overwriting file-local values).
epa-pinentry-mode 'loopback)) (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 (use-package! drag-stuff
@@ -62,11 +77,6 @@
;; or specific :post-handlers with: ;; or specific :post-handlers with:
;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC"))) ;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC")))
(after! smartparens (after! smartparens
;; Smartparens is broken in `cc-mode' as of Emacs 27. See
;; <https://github.com/Fuco1/smartparens/issues/963>.
(unless EMACS27+
(pushnew! sp--special-self-insert-commands 'c-electric-paren 'c-electric-brace))
;; Smartparens' navigation feature is neat, but does not justify how ;; 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 ;; 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! ;; be reactivated for non-evil users though. Needs more testing!
@@ -76,7 +86,7 @@
sp-navigate-consider-sgml-tags nil))) sp-navigate-consider-sgml-tags nil)))
;; Autopair quotes more conservatively; if I'm next to a word/before another ;; 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 (let ((unless-list '(sp-point-before-word-p
sp-point-after-word-p sp-point-after-word-p
sp-point-before-same-p))) sp-point-before-same-p)))
@@ -97,10 +107,13 @@
(sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p)) (sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p))
;; Major-mode specific fixes ;; Major-mode specific fixes
(sp-local-pair '(ruby-mode enh-ruby-mode) "{" "}" (sp-local-pair 'ruby-mode "{" "}"
:pre-handlers '(:rem sp-ruby-pre-handler) :pre-handlers '(:rem sp-ruby-pre-handler)
:post-handlers '(:rem sp-ruby-post-handler)) :post-handlers '(:rem sp-ruby-post-handler))
;; Don't eagerly escape Swift style string interpolation
(sp-local-pair 'swift-mode "\\(" ")" :when '(sp-in-string-p))
;; Don't do square-bracket space-expansion where it doesn't make sense to ;; Don't do square-bracket space-expansion where it doesn't make sense to
(sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode) (sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode)
"[" nil :post-handlers '(:rem ("| " "SPC"))) "[" nil :post-handlers '(:rem ("| " "SPC")))
@@ -114,9 +127,7 @@
;; Disable electric keys in C modes because it interferes with smartparens ;; Disable electric keys in C modes because it interferes with smartparens
;; and custom bindings. We'll do it ourselves (mostly). ;; and custom bindings. We'll do it ourselves (mostly).
(after! cc-mode (after! cc-mode
(c-toggle-electric-state -1) (setq-default c-electric-flag nil)
(c-toggle-auto-newline -1)
(setq c-electric-flag nil)
(dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177")) (dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177"))
(define-key c-mode-base-map key nil)) (define-key c-mode-base-map key nil))
@@ -176,7 +187,7 @@
(sp-local-pair "(*" "*)" :actions nil) (sp-local-pair "(*" "*)" :actions nil)
(sp-local-pair "(*" "*" (sp-local-pair "(*" "*"
:actions '(insert) :actions '(insert)
:post-handlers '(("| " "SPC") ("|\n[i]*)[d-2]" "RET"))))) :post-handlers '(("| " "SPC") ("|[i]*)[d-2]" "RET")))))
(after! smartparens-markdown (after! smartparens-markdown
(sp-with-modes '(markdown-mode gfm-mode) (sp-with-modes '(markdown-mode gfm-mode)
@@ -196,32 +207,52 @@
;; This keybind allows * to skip over **. ;; This keybind allows * to skip over **.
(map! :map markdown-mode-map (map! :map markdown-mode-map
:ig "*" (λ! (if (looking-at-p "\\*\\* *$") :ig "*" (general-predicate-dispatch nil
(forward-char 2) (looking-at-p "\\*\\* *")
(call-interactively 'self-insert-command))))) (cmd! (forward-char 2)))))))
;; Highjacks backspace to:
;; a) balance spaces inside brackets/parentheses ( | ) -> (|)
;; b) delete up to nearest column multiple of `tab-width' at a time
;; c) close empty multiline brace blocks in one step:
;; {
;; |
;; }
;; becomes {|}
;; d) refresh smartparens' :post-handlers, so SPC and RET expansions work
;; even after a backspace.
;; e) properly delete smartparen pairs when they are encountered, without
;; the need for strict mode.
;; f) do none of this when inside a string
(advice-add #'delete-backward-char :override #'+default--delete-backward-char-a))
;; Makes `newline-and-indent' continue comments (and more reliably)
(advice-add #'newline-and-indent :override #'+default--newline-indent-and-continue-comments-a))
;; ;;
;;; Keybinding fixes ;;; Keybinding fixes
;; Highjacks backspace to delete up to nearest column multiple of `tab-width' at
;; a time. If you have smartparens enabled, it will also:
;; a) balance spaces inside brackets/parentheses ( | ) -> (|)
;; b) close empty multiline brace blocks in one step:
;; {
;; |
;; }
;; becomes {|}
;; c) refresh smartparens' :post-handlers, so SPC and RET expansions work even
;; after a backspace.
;; d) properly delete smartparen pairs when they are encountered, without the
;; need for strict mode.
;; e) do none of this when inside a string
(advice-add #'delete-backward-char :override #'+default--delete-backward-char-a)
;; HACK Makes `newline-and-indent' continue comments (and more reliably).
;; Consults `doom-point-in-comment-functions' to detect a commented region
;; and uses that mode's `comment-line-break-function' to continue comments.
;; If neither exists, it will fall back to the normal behavior of
;; `newline-and-indent'.
;;
;; We use an advice here instead of a remapping because many modes define
;; and remap to their own newline-and-indent commands, and tackling all
;; those cases was judged to be more work than dealing with the edge cases
;; on a case by case basis.
(defadvice! +default--newline-indent-and-continue-comments-a (&rest _)
"A replacement for `newline-and-indent'.
Continues comments if executed from a commented line. Consults
`doom-point-in-comment-functions' to determine if in a comment."
:before-until #'newline-and-indent
(interactive "*")
(when (and +default-want-RET-continue-comments
(doom-point-in-comment-p)
(fboundp comment-line-break-function))
(funcall comment-line-break-function nil)
t))
;; This section is dedicated to "fixing" certain keys so that they behave ;; This section is dedicated to "fixing" certain keys so that they behave
;; sensibly (and consistently with similar contexts). ;; sensibly (and consistently with similar contexts).
@@ -252,6 +283,7 @@
"s-c" (if (featurep 'evil) #'evil-yank #'copy-region-as-kill) "s-c" (if (featurep 'evil) #'evil-yank #'copy-region-as-kill)
"s-v" #'yank "s-v" #'yank
"s-s" #'save-buffer "s-s" #'save-buffer
"s-x" #'execute-extended-command
:v "s-x" #'kill-region :v "s-x" #'kill-region
;; Buffer-local font scaling ;; Buffer-local font scaling
"s-+" #'doom/reset-font-size "s-+" #'doom/reset-font-size
@@ -259,7 +291,7 @@
"s--" #'doom/decrease-font-size "s--" #'doom/decrease-font-size
;; Conventional text-editing keys & motions ;; Conventional text-editing keys & motions
"s-a" #'mark-whole-buffer "s-a" #'mark-whole-buffer
"s-/" (λ! (save-excursion (comment-line 1))) "s-/" (cmd! (save-excursion (comment-line 1)))
:n "s-/" #'evilnc-comment-or-uncomment-lines :n "s-/" #'evilnc-comment-or-uncomment-lines
:v "s-/" #'evilnc-comment-operator :v "s-/" #'evilnc-comment-operator
:gi [s-backspace] #'doom/backward-kill-to-bol-and-indent :gi [s-backspace] #'doom/backward-kill-to-bol-and-indent
@@ -278,6 +310,7 @@
(define-key! help-map (define-key! help-map
;; new keybinds ;; new keybinds
"'" #'describe-char "'" #'describe-char
"u" #'doom/help-autodefs
"E" #'doom/sandbox "E" #'doom/sandbox
"M" #'doom/describe-active-minor-mode "M" #'doom/describe-active-minor-mode
"O" #'+lookup/online "O" #'+lookup/online
@@ -313,25 +346,27 @@
;; replaces `apropos-documentation' b/c `apropos' covers this ;; replaces `apropos-documentation' b/c `apropos' covers this
"d" nil "d" nil
"da" #'doom/help-autodefs
"db" #'doom/report-bug "db" #'doom/report-bug
"dd" #'doom/toggle-debug-mode "dc" #'doom/goto-private-config-file
"dC" #'doom/goto-private-init-file
"dd" #'doom-debug-mode
"df" #'doom/help-faq "df" #'doom/help-faq
"dh" #'doom/help "dh" #'doom/help
"dk" #'doom/goto-packages-file
"dl" #'doom/help-search-load-path "dl" #'doom/help-search-load-path
"dL" #'doom/help-search-loaded-files
"dm" #'doom/help-modules "dm" #'doom/help-modules
"dn" #'doom/help-news "dn" #'doom/help-news
"dN" #'doom/help-news-search "dN" #'doom/help-search-news
"di" #'doom/goto-doomblock "dpc" #'doom/help-package-config
"dp" #'doom/help-packages "dpd" #'doom/goto-private-packages-file
"dP" #'doom/help-package-homepage "dph" #'doom/help-package-homepage
"dc" #'doom/goto-config-file "dpp" #'doom/help-packages
"dC" #'doom/help-package-config "ds" #'doom/help-search-headings
"ds" #'doom/help-search "dS" #'doom/help-search
"dx" #'doom/sandbox
"dt" #'doom/toggle-profiler "dt" #'doom/toggle-profiler
"du" #'doom/help-autodefs
"dv" #'doom/version "dv" #'doom/version
"dx" #'doom/sandbox
;; replaces `apropos-command' ;; replaces `apropos-command'
"a" #'apropos "a" #'apropos
@@ -348,7 +383,7 @@
"t" #'load-theme "t" #'load-theme
;; replaces `finder-by-keyword' b/c not useful ;; replaces `finder-by-keyword' b/c not useful
"p" #'doom/help-packages "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) "P" #'find-library)
(after! which-key (after! which-key
@@ -372,10 +407,15 @@
;; A Doom convention where C-s on popups and interactive searches will invoke ;; A Doom convention where C-s on popups and interactive searches will invoke
;; ivy/helm for their superior filtering. ;; ivy/helm for their superior filtering.
(define-key! :keymaps +default-minibuffer-maps (when-let (command (cond ((featurep! :completion ivy)
"C-s" (if (featurep! :completion ivy) #'counsel-minibuffer-history)
#'counsel-minibuffer-history ((featurep! :completion helm)
#'helm-minibuffer-history)) #'helm-minibuffer-history)))
(define-key!
:keymaps (append +default-minibuffer-maps
(when (featurep! :editor evil +everywhere)
'(evil-ex-completion-map)))
"C-s" command))
;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation. ;; Smarter C-a/C-e for both Emacs and Evil. C-a will jump to indentation.
;; Pressing it again will send you to the true bol. Same goes for C-e, except ;; Pressing it again will send you to the true bol. Same goes for C-e, except
@@ -387,14 +427,11 @@
;; which ctrl+RET will add a new "item" below the current one and ;; 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 ;; cmd+RET (Mac) / meta+RET (elsewhere) will add a new, blank line below
;; the current one. ;; the current one.
:gni [C-return] #'+default/newline-below :gn [C-return] #'+default/newline-below
:gni [C-S-return] #'+default/newline-above :gn [C-S-return] #'+default/newline-above
(:when IS-MAC (:when IS-MAC
:gni [s-return] #'+default/newline-below :gn [s-return] #'+default/newline-below
:gni [S-s-return] #'+default/newline-above) :gn [S-s-return] #'+default/newline-above)))
(:unless IS-MAC
:gni [M-return] #'+default/newline-below
:gni [M-S-return] #'+default/newline-above)))
;; ;;

Some files were not shown because too many files have changed in this diff Show More