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 13527 additions and 12455 deletions

19
.bashrc
View File

@@ -6,10 +6,12 @@
#
# My bash config. Not much to see here. Some pretty standard stuff.
PATH="$HOME/.local/bin${PATH:+:${PATH}}"
EDITOR="nvim"
export TERM="st-256color"
export HISTCONTROL=ignoredups:erasedups # no duplicate entries
PATH="$HOME/.local/bin${PATH:+:${PATH}}" # adding .local/bin to $PATH
export TERM="xterm-256color" # getting proper colors
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
@@ -150,8 +152,15 @@ alias sudo='sudo '
alias ..='cd ..'
alias ...='cd ../..'
# vim
# vim and emacs
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
alias br='br -dhp'

View File

@@ -1,28 +1,64 @@
;;; .doom.d/config.el -*- lexical-binding: t; -*-
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;; Place your private configuration here!
;; ____ _____
;; | _ \_ _| Derek Taylor (DistroTube)
;; | | | || | http://www.youtube.com/c/DistroTube
;; | |_| || | http://www.gitlab.com/dwt1/
;; |____/ |_|
;;
;; A customized config for Doom Emacs (https://github.com/hlissner/doom-emacs)
;; Modified by Derek Taylor (http://www.gitlab.com/dwt1/)
;; Place your private configuration here! Remember, you do not need to run 'doom
;; sync' after modifying this file!
;; Setting the font.
(setq doom-font (font-spec :family "Mononoki Nerd Font Mono" :size 15))
;;(setq doom-font (font-spec :family "SauceCodePro Nerd Font" :size 15))
;; Setting the theme
;; 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:
;;
;; + `doom-font'
;; + `doom-variable-pitch-font'
;; + `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;;
;; They all accept either a font-spec, font string ("Input Mono-12"), or xlfd
;; font string. You generally only need these two:
(setq doom-font (font-spec :family "Mononoki Nerd Font" :size 15)
doom-variable-pitch-font (font-spec :family "Mononoki Nerd Font" :size 15))
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-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.
(setq neo-window-fixed-size nil)
;; Sets binding to toggle line wrap on/off.
(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.
;; (set-frame-parameter (selected-frame) '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-responsive 'top
;; highlight-indent-guides-auto-enabled nil))
(use-package emms
:ensure t
:config
(require 'emms-setup)
(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-mode-line 1)
(emms-playing-time 1))
(require 'emms-setup)
(emms-standard)
(emms-default-players)
;; Setting up mu4e which is an email client that works within emacs.
;; You must install mu4e and mbsync through your Linux distribution's
;; package manager.
;; Adding path to mu4e
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(require 'mu4e)
(setq mu4e-get-mail-command "mbsync -c ~/.emacs.d/mu4e/.mbsyncrc -a"
mu4e-update-interval 300)
@@ -71,5 +126,38 @@
smtpmail-default-smtp-server "smtp.1and1.com"
smtpmail-smtp-server "smtp.1and1.com"
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.
)

View File

@@ -1,14 +1,20 @@
;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install'
;; will do this for you). The `doom!' block below controls what modules are
;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; after modifying it.
;;
;; More information about these modules (and what flags they support) can be
;; found in modules/README.org.
;; This file controls what Doom modules are enabled and what order they load
;; in. Remember to run 'doom sync' after modifying it!
;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
;; documentation. There you'll find 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).
;;
;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; directory (for easy access to its source code).
;; Adding path to mu4e
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
(doom! :input
@@ -29,22 +35,22 @@
;;fill-column ; a `fill-column' indicator
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;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
nav-flash ; blink the current line after jumping
;;nav-flash ; blink cursor line after big motions
neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on
(popup ; tame sudden yet inevitable temporary windows
+all ; catch all popups that start with an asterix
+defaults) ; default popup rules
;;pretty-code ; replace bits of code with pretty symbols
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;pretty-code ; ligatures or substitute text with pretty symbols
;;tabs ; an tab bar for Emacs
;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe
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
;;zen ; distraction-free coding or writing
:editor
(evil +everywhere); come to the dark side, we have cookies
@@ -53,24 +59,30 @@
;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;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
;;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
;;word-wrap ; soft wrapping with language-aware indent
:emacs
dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent
ibuffer ; interactive buffer management
vc ; version-control and Emacs, sitting in a tree
dired ; making dired pretty [functional]
electric ; smarter, keyword-based electric-indent
;;ibuffer ; interactive buffer management
undo ; persistent, smarter undo for your inevitable mistakes
vc ; version-control and Emacs, sitting in a tree
:term
eshell ; a consistent, cross-platform shell (WIP)
;;shell ; a terminal REPL for Emacs
;;term ; terminals in Emacs
vterm ; another terminals in Emacs
eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for 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
;;ansible
@@ -80,11 +92,8 @@
;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls)
flycheck ; tasing you for every semicolon you forget
;;flyspell ; tasing you for misspelling mispelling
;;gist ; interacting with github gists
(lookup ; helps you navigate your code and documentation
+docsets) ; ...or in Dash docsets locally
lookup ; navigate your code and its documentation
;;lsp
;;macos ; MacOS-specific commands
magit ; a git porcelain for Emacs
@@ -93,69 +102,69 @@
;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
;;upload ; map local to remote projects via ssh/ftp
:lang
;;agda ; types of types of types of types...
;;assembly ; assembly for fun or debugging
;;cc ; C/C++/Obj-C madness
;;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
;;crystal ; ruby at the speed of c
;;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
;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses
emacs-lisp ; drown in parentheses
;;erlang ; an elegant language for a more civilized age
;;ess ; emacs speaks statistics
;;faust ; dsp, but you get to keep your soul
;;fsharp ; ML stands for Microsoft's Language
;;go ; the hipster dialect
(haskell +dante) ; a language that's lazier than I am
;;fsharp ; ML stands for Microsoft's Language
;;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
;;hy ; readability of scheme w/ speed of python
;;idris ;
json ; At least it ain't XML
;;(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
;;kotlin ; a better, slicker Java(Script)
latex ; writing papers in Emacs has never been so fun
latex ; writing papers in Emacs has never been so fun
;;lean
;;factor
;;ledger ; an accounting system in Emacs
lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
lua ; one-based indices? one-based indices
markdown ; writing docs for people to ignore
;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(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
org ; organize your plain life in plain text
php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional
python ; beautiful is better than ugly
python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client
;;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()
;;scala ; java, but good
;;scheme ; a fully conniving family of lisps
sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
web ; the tubes
web ; the tubes
yaml ; JSON, but readable
:email
;;(mu4e +gmail)
@@ -167,20 +176,7 @@
;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
;;write ; emacs for writers (fiction, notes, papers, etc.)
:config
;;literate
(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.
)

View File

@@ -1,10 +1,53 @@
;; -*- no-byte-compile: t; -*-
;;; .doom.d/packages.el
;;; $DOOMDIR/packages.el
;;; Examples:
;; (package! some-package)
;; (package! another-package :recipe (:host github :repo "username/repo"))
;; (package! builtin-package :disable t)
;; To install a package with Doom you must declare them here and run 'doom sync'
;; on the command line, then restart Emacs for the changes to take effect -- or
;; use 'M-x doom/reload'.
;; 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! elfeed)
(package! peep-dired)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016-2019 Henrik Lissner.
Copyright (c) 2016-2020 Henrik Lissner.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,178 +1,254 @@
<a href="http://doomemacs.org">
<img src="https://img.shields.io/github/tag/hlissner/doom-emacs.svg?label=release&color=orange" alt="Made with Doom Emacs">
</a>
<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+">
</a>
<a href="https://github.com/hlissner/doom-emacs/actions">
<img src="https://github.com/hlissner/doom-emacs/workflows/CI/badge.svg" alt="Build status: develop">
</a>
<a href="https://discord.gg/bcZ6P3y">
<img src="https://img.shields.io/badge/Discord-blue.svg?logo=discord&label=join" alt="Join our discord server" align="right">
</a>
<br><br>
<div align="center">
# Doom Emacs
[Install](#install) • [Documentation] • [FAQ] • [Screenshots] • [Contribute](#contribute)
![Made with Doom Emacs](https://img.shields.io/github/tag/hlissner/doom-emacs.svg?style=flat-square&label=release&color=58839b)
![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)
![Latest commit](https://img.shields.io/github/last-commit/hlissner/doom-emacs/develop?style=flat-square)
![Build status: develop](https://img.shields.io/github/workflow/status/hlissner/doom-emacs/CI/develop?style=flat-square)
[![Discord Server](https://img.shields.io/discord/406534637242810369?color=blue&label=Discord%20Chat&logo=discord&logoColor=white&style=flat-square)][Discord]
![Doom Emacs Screenshot](https://raw.githubusercontent.com/hlissner/doom-emacs/screenshots/main.png)
<p align="center">
<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>
</div>
---
**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
```
**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)
- [Doom's mantras](#dooms-mantras)
- [Features](#features)
- [Getting Help](#getting-help)
- [Community](#community)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
It's a good idea to add `~/.emacs.d/bin` to your `PATH`! Other `bin/doom`
commands you should know about:
# 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
vimmer -- envious of the features of modern text editors -- spirals into despair
before succumbing to the [dark side][url:evil-mode]. This is his config.
# Roadmap
Doom is an active and ongoing project. To make that development more
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/)
designed to make Emacs faster and easier to customize. It can serve as framework
for your own configuration or a resource for fellow Emacs enthusiasts who want
to learn more about our favorite OS.
+ [Development Roadmap](https://github.com/hlissner/doom-emacs/projects/3):
roughly outlines our goals between release milestones and their progress.
+ [Plugins under review](https://github.com/hlissner/doom-emacs/projects/2):
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
# Getting help
Emacs is no journey of a mere thousand miles. You _will_ run into problems and
mysterious errors. When you do, here are some places you can look for help:
- **Gotta go fast.** Startup and run-time performance are high priorities.
Expensive functionality (built-in or in plugins) is modified and optimized
toward this end, otherwise, they must be opt-in.
- **Close to metal.** There's less between you and vanilla Emacs, by design.
There's less to grok. Modules should be syntactically sweet and backend logic
explicit and abstraction-light. The code itself ought to be designed as if
grokking it were part of the user experience; and it is!
- **Opinionated, but not stubborn.** Doom is a bundle of reasonable defaults
and curated opinions, but you aren't stuck with it. Use as little or as much
of it as you like. Use it as-is as a complete Emacs distribution; disable
everything and use it as a baseline for your own; or anywhere in between.
- **Your system, your rules.** There are more ways to set up your programming
environment than there are dislikes on Youtube Rewind '18, so Doom and its
plugins promise not to *automatically* (and definitely not *silently*) install
system dependencies. This means fonts, packages and programs. `doom doctor`
will tell you what's missing though!
+ [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.
## Features
- 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!
# 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]
# Getting Help
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!
## Community
+ 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.
[![](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)
We have [a Discord server][url:discord]! Hop on and say hi!
## Troubleshooting
[contribute]: docs/contributing.org
[discord]: https://discord.gg/qvGgnVx
[documentation]: docs/index.org
[faq]: docs/faq.org
[getting-started]: docs/getting_started.org
[install]: docs/getting_started.org#install
[backtrace]: docs/getting_started.org#how-to-extract-a-backtrace-from-an-error
[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
Encountered strange behavior or an error? Here are some things to try before you
shoot off that bug report:
[bindings]: modules/config/default/+evil-bindings.el
[editorconfig]: http://editorconfig.org/
[evil-mode]: https://github.com/emacs-evil/evil
[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
- Run `bin/doom refresh`. This ensures Doom is properly set up and its autoloads
files are up-to-date.
- If you have byte-compiled your config (with `bin/doom compile`), see if
`bin/doom clean` makes the issue go away. Never debug issues with a
byte-compiled config, it will make your job harder.
- Run `bin/doom doctor` to detect common issues in your development environment.
- Search Doom's issue tracker for mention of any error messages you've received.
- [Visit our FAQ][docs:faq] to see if your issue is listed.
If all else fails, [file that bug report][github:new-issue]! Please include the
behavior you've observed, the behavior you expected, and any error messages or
warnings logged to the \*Messages\* buffer (can be opened with <kbd>SPC h
e</kbd> or `M-x view-echo-area-messages`).
It's a great help if you included a backtrace with errors, i.e. `M-x
toggle-debug-on-error` then recreating the error(s).
## 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
[liberapay]: https://liberapay.com/hlissner/donate
[paypal]: https://paypal.me/henriklissner/10

View File

@@ -1,30 +1,45 @@
#!/usr/bin/env sh
:; ( 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; }
:; 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
:; DOOMBASE="$(dirname "$0")/.."
:; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; }
:; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; }
:; exec $EMACS --script "$0" -- "$@"
:; exit 0
:; _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
:; _DOOMBASE="${EMACSDIR:-$(dirname "$0")/..}"
:; _DOOMPOST="$_DOOMBASE/.local/.doom.sh"
:; rm -f "$_DOOMPOST"
:; $EMACS --no-site-file --script "$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)))
(emacsdir (getenv "EMACSDIR"))
(user-emacs-directory (or emacsdir (expand-file-name "../" loaddir)))
(load-prefer-newer t))
(user-emacs-directory
(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)
(defcli! :main
((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")
(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)
"A command line interface for managing Doom Emacs.
@@ -33,74 +48,80 @@ Includes package management, diagnostics, unit tests, and byte-compilation.
This tool also makes it trivial to launch Emacs out of a different folder or
with a different private module."
: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
(setenv "DEBUG" "1")
(setq doom-debug-mode t)
(setq doom-debug-p t)
(print! (info "Debug mode on")))
(when yes-p
(setenv "YES" "1")
(setq doom-auto-accept t)
(print! (info "Auto-yes on")))
(when help-p
(push command args)
(when command
(push command args))
(setq command "help"))
;; Reload core in case any of the directories were changed.
(when (or emacsdir doomdir localdir)
(load! "core/core.el" user-emacs-directory))
(when (equal (user-real-uid) 0)
(print!
(concat "WARNING: This script is running as root. This likely wasn't intentional, and\n"
"is unnecessary to use this script. This will cause file permissions errors\n"
"later if you use this Doom installation on a non-root account.\n"))
(unless (or doom-auto-accept (y-or-n-p "Continue anyway?"))
(user-error "Aborted")))
(cond ((not noninteractive)
(print! "Doom launched out of %s (test mode)" (path user-emacs-directory))
(load! "init.el" user-emacs-directory)
(doom-run-all-startup-hooks-h))
;; Load any the user's private init.el or any cli.el files in modules. This
;; gives the user (and modules) an opportunity to define their own CLI
;; commands, or to customize the CLI to better suit them.
(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)
(doom-cli-execute "help"))
((condition-case e
(let ((start-time (current-time)))
(and (doom-cli-execute command args)
(print! (success "Finished! (%.4fs)")
(float-time
(time-subtract (current-time)
start-time)))))
(user-error
(print! (error "%s\n") (error-message-string e))
(print! (yellow "See 'doom help %s' for documentation on this command.") (car args))
(error "")) ; Ensure non-zero exit code
((debug error)
(print! (error "There was an unexpected error:"))
(print-group!
(print! "%s %s" (bold "Type:") (car e))
(print! (bold "Message:"))
(print-group!
(print! "%s" (get (car e) 'error-message)))
(print! (bold "Data:"))
(print-group!
(if (cdr e)
(dolist (item (cdr e))
(print! "%S" item))
(print! "n/a"))))
(unless debug-on-error
(terpri)
(print!
(concat "Run the command again with the -d (or --debug) switch to enable debug\n"
"mode and (hopefully) generate a backtrace from this error:\n"
"\n %s\n\n"
"If you file a bug report, please include it!")
(string-join (append (list (file-name-nondirectory load-file-name) "-d" command)
args)
" "))
(error ""))))))) ; Ensure non-zero exit code
(let (print-level print-gensym print-length)
(condition-case e
(if (null command)
(doom-cli-execute "help")
(let ((start-time (current-time)))
(and (doom-cli-execute command args)
(progn (run-hooks 'doom-cli-post-hook) t)
(print! (success "Finished! (%.4fs)")
(float-time
(time-subtract (current-time)
start-time))))))
(user-error
(print! (error "%s\n") (error-message-string e))
(print! (yellow "See 'doom help %s' for documentation on this command.") (car args))
(error "")) ; Ensure non-zero exit code
((debug error)
(print! (error "There was an unexpected error:"))
(print-group!
(print! "%s %s" (bold "Type:") (car e))
(print! (bold "Message:"))
(print-group!
(print! "%s" (get (car e) 'error-message)))
(print! (bold "Data:"))
(print-group!
(if (cdr e)
(dolist (item (cdr e))
(print! "%S" item))
(print! "n/a")))
(when (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
(terpri)
(print!
(concat "Run the command again with the -d (or --debug) switch to enable debug\n"
"mode and (hopefully) generate a backtrace from this error:\n"
"\n %s\n\n"
"If you file a bug report, please include it!")
(string-join (append (list (file-name-nondirectory load-file-name) "-d" command)
args)
" "))
;; Ensure non-zero exit code
(error ""))))))
(doom-cli-execute :main (cdr (member "--" argv)))
(setq argv nil))

View File

@@ -14,7 +14,7 @@ cleanup() {
}
# 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
trap cleanup EXIT INT TERM
daemon=1
@@ -22,8 +22,8 @@ fi
# org-capture key mapped to argument flags
# keys=$(emacsclient -e "(+org-capture-available-keys)" | cut -d '"' -f2)
while getopts hk opt; do
key="\"$opt\""
while getopts "hk:" opt; do
key="\"$OPTARG\""
break
done
shift $((OPTIND-1))
@@ -32,7 +32,7 @@ shift $((OPTIND-1))
if [ $daemon ]; then
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})"
else
# Non-daemon servers flicker a lot if frames are created from terminal, so we

View File

@@ -36,7 +36,7 @@ it if it doesn't exist).")
;;
;; Functions
;;; Functions
;;;###autoload
(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)
(signal 'wrong-type-argument (list '(bufferp stringp) 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)
(not (doom-temp-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
(defun doom-set-buffer-real (buffer flag)
"Forcibly mark BUFFER as FLAG (non-nil = real)."
"Forcibly mark BUFFER as FLAG (non-nil = real).
See `doom-real-buffer-p' for an explanation for real buffers."
(with-current-buffer buffer
(setq doom-real-buffer-p flag)))
@@ -251,7 +255,9 @@ regex PATTERN. Returns the number of killed buffers."
;;;###autoload
(defun doom-mark-buffer-as-real-h ()
"Hook function that marks the current buffer as real."
"Hook function that marks the current buffer as real.
See `doom-real-buffer-p' for an explanation for real buffers."
(doom-set-buffer-real (current-buffer) t))
@@ -272,6 +278,12 @@ If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)."
(set-buffer-modified-p nil)))
(doom-kill-buffer-fixup-windows buffer))
(defun doom--message-or-count (interactive message count)
(if interactive
(message message count)
count))
;;;###autoload
(defun doom/kill-all-buffers (&optional buffer-list interactive)
"Kill all buffers and closes their windows.
@@ -286,14 +298,14 @@ belong to the current project."
(if (null buffer-list)
(message "No buffers to kill")
(save-some-buffers)
(delete-other-windows)
(when (memq (current-buffer) buffer-list)
(switch-to-buffer (doom-fallback-buffer)))
(mapc #'doom-kill-buffer-and-windows buffer-list)
(delete-other-windows)
(when interactive
(message "Killed %s buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))))
(mapc #'kill-buffer buffer-list)
(doom--message-or-count
interactive "Killed %d buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list))))))
;;;###autoload
(defun doom/kill-other-buffers (&optional buffer-list interactive)
@@ -308,10 +320,10 @@ project."
(doom-buffer-list)))
t))
(mapc #'doom-kill-buffer-and-windows buffer-list)
(when interactive
(message "Killed %s buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list))))))
(doom--message-or-count
interactive "Killed %d other buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))
;;;###autoload
(defun doom/kill-matching-buffers (pattern &optional buffer-list interactive)
@@ -341,10 +353,10 @@ current project."
(if current-prefix-arg (doom-project-buffer-list)))
t))
(mapc #'kill-buffer buffer-list)
(when interactive
(message "Killed %s buried buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list))))))
(doom--message-or-count
interactive "Killed %d buried buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))
;;;###autoload
(defun doom/kill-project-buffers (project &optional interactive)
@@ -362,9 +374,9 @@ current project."
nil)
t))
(when project
(let ((buffers (doom-project-buffer-list project)))
(doom-kill-buffers-fixup-windows buffers)
(when interactive
(message "Killed %d buffer(s)"
(- (length buffers)
(length (cl-remove-if-not #'buffer-live-p buffers))))))))
(let ((buffer-list (doom-project-buffer-list project)))
(doom-kill-buffers-fixup-windows buffer-list)
(doom--message-or-count
interactive "Killed %d project buffers"
(- (length buffer-list)
(length (cl-remove-if-not #'buffer-live-p buffer-list)))))))

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)))

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)))))

View File

@@ -26,8 +26,9 @@
(doom-project-find-file doom-private-dir))
;;;###autoload
(defun doom/goto-doomblock ()
"Open your private init.el and go to your `doom!' block."
(defun doom/goto-private-init-file ()
"Open your private init.el file.
And jumps to your `doom!' block."
(interactive)
(find-file (expand-file-name "init.el" doom-private-dir))
(goto-char
@@ -37,13 +38,13 @@
(point))))
;;;###autoload
(defun doom/goto-config-file ()
(defun doom/goto-private-config-file ()
"Open your private config.el file."
(interactive)
(find-file (expand-file-name "config.el" doom-private-dir)))
;;;###autoload
(defun doom/goto-packages-file ()
(defun doom/goto-private-packages-file ()
"Open your private packages.el file."
(interactive)
(find-file (expand-file-name "packages.el" doom-private-dir)))
@@ -52,42 +53,48 @@
;;
;;; Managements
(cl-defmacro doom--compile (command &key on-success on-failure)
(declare (indent defun))
`(with-current-buffer (compile ,command)
(add-hook
'compilation-finish-functions
(lambda (_buf status)
(if (equal status "finished\n")
,on-success
,on-failure))
nil 'local)))
(defmacro doom--if-compile (command on-success &optional on-failure)
(declare (indent 2))
(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
'compilation-finish-functions
(lambda (_buf status)
(if (equal status "finished\n")
(progn
(delete-window ,windowsym)
,on-success)
,on-failure))
nil 'local)))))
;;;###autoload
(defun doom/reload ()
"Reloads your private config.
This is experimental! It will try to do as `bin/doom refresh' does, but from
within this Emacs session. i.e. it reload autoloads files (if necessary),
reloads your package list, and lastly, reloads your private config.el.
This is experimental! It will try to do as `bin/doom sync' does, but from within
this Emacs session. i.e. it reload autoloads files (if necessary), reloads your
package list, and lastly, reloads your private config.el.
Runs `doom-reload-hook' afterwards."
(interactive)
(require 'core-cli)
(when (and IS-WINDOWS (file-exists-p doom-env-file))
(warn "Can't regenerate envvar file from within Emacs. Run 'doom env' from the console"))
(doom--compile (format "%s refresh -e" doom-bin)
:on-success
(let ((doom-reloading-p t))
(doom-initialize 'force)
(with-demoted-errors "PRIVATE CONFIG ERROR: %s"
(general-auto-unbind-keys)
(unwind-protect
(doom-initialize-modules 'force)
(general-auto-unbind-keys t)))
(run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)
(print! (success "Config successfully reloaded!")))
:on-failure
(message "Can't regenerate envvar file from within Emacs. Run 'doom env' from the console"))
;; In case doom/reload is run before incrementally loaded packages are loaded,
;; 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))
(doom-initialize 'force)
(with-demoted-errors "PRIVATE CONFIG ERROR: %s"
(general-auto-unbind-keys)
(unwind-protect
(doom-initialize-modules 'force)
(general-auto-unbind-keys t)))
(run-hook-wrapped 'doom-reload-hook #'doom-try-run-hook)
(message "Config successfully reloaded!"))
(user-error "Failed to reload your config")))
;;;###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
remove orphaned ones. It also doesn't reload your private config.
It is useful to only pull in changes performed by 'doom refresh' on the command
It is useful to only pull in changes performed by 'doom sync' on the command
line."
(interactive)
(require 'core-cli)
(require 'core-packages)
(doom-initialize-packages)
(doom-cli-reload-autoloads nil 'force))
(doom-autoloads-reload))
;;;###autoload
(defun doom/reload-env (&optional arg)
@@ -118,25 +125,22 @@ imported into Emacs."
(interactive "P")
(when IS-WINDOWS
(user-error "Cannot reload envvar file from within Emacs on Windows, run it from cmd.exe"))
(doom--compile
(format "%s -ic '%s env%s'"
(string-trim
(shell-command-to-string
(format "getent passwd %S | cut -d: -f7"
(user-login-name))))
doom-bin (if arg " -c" ""))
:on-success
(let ((doom-reloading-p t))
(unless arg
(doom-load-envvars-file doom-env-file)))
:on-failure
(doom--if-compile
(format "%s -ic '%s env%s'"
(string-trim
(shell-command-to-string
(format "getent passwd %S | cut -d: -f7"
(user-login-name))))
doom-bin (if arg " -c" ""))
(let ((doom-reloading-p t))
(unless arg
(doom-load-envvars-file doom-env-file)))
(error "Failed to generate env file")))
;;;###autoload
(defun doom/upgrade ()
"Run 'doom upgrade' then prompt to restart Emacs."
(interactive)
(doom--compile (format "%s upgrade" doom-bin)
:on-success
(when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?")
(doom/restart-and-restore))))
(doom--if-compile (format "%s -y upgrade" doom-bin)
(when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?")
(doom/restart-and-restore))))

View File

@@ -1,5 +1,37 @@
;;; 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
(defun doom-run-all-startup-hooks-h ()
"Run all startup Emacs hooks. Meant to be executed after starting Emacs with
@@ -17,36 +49,44 @@
;;
;;; Helpers
(defun doom-template-insert (template)
"TODO"
(let ((file (expand-file-name (format "templates/%s" template) doom-core-dir)))
(when (file-exists-p file)
(insert-file-contents file))))
(defsubst doom--collect-forms-in (file form)
(when (file-readable-p file)
(let (forms)
(with-temp-buffer
(insert-file-contents file)
(let (emacs-lisp-mode) (emacs-lisp-mode))
(while (re-search-forward (format "(%s " (regexp-quote form)) nil t)
(let ((ppss (syntax-ppss)))
(unless (or (nth 4 ppss)
(nth 3 ppss))
(save-excursion
(goto-char (match-beginning 0))
(push (sexp-at-point) forms)))))
(nreverse forms)))))
;;;###autoload
(defun doom-info ()
"Returns diagnostic information about the current Emacs session in markdown,
ready to be pasted in a bug report on github."
(require 'vc-git)
(require 'core-packages)
(let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules)))
(cl-letf
(((symbol-function 'sh)
(lambda (&rest args)
(cdr (apply #'doom-call-process args)))))
(doom-modules (doom-module-list)))
(letf! (defun sh (&rest args) (cdr (apply #'doom-call-process args)))
`((emacs
(version . ,emacs-version)
(features ,@system-configuration-features)
(build . ,(format-time-string "%b %d, %Y" emacs-build-time))
(buildopts ,system-configuration-options)
(windowsys . ,(if noninteractive 'batch window-system))
(windowsys . ,(if doom-interactive-p window-system 'batch))
(daemonp . ,(cond ((daemonp) 'daemon)
((and (require 'server)
(server-running-p))
'server-running))))
(doom
(version . ,doom-version)
(build . ,(sh "git" "log" "-1" "--format=%D %h %ci")))
(build . ,(sh "git" "log" "-1" "--format=%D %h %ci"))
(dir . ,(abbreviate-file-name (file-truename doom-private-dir))))
(system
(type . ,system-type)
(config . ,system-configuration)
@@ -79,14 +119,19 @@ ready to be pasted in a bug report on github."
'("n/a")))
(packages
,@(or (condition-case e
(cl-loop for (name . plist) in (doom-package-list)
if (cl-find :private (plist-get plist :modules)
:key #'car)
collect
(if-let (splist (doom-plist-delete (copy-sequence plist)
:modules))
(prin1-to-string (cons name splist))
name))
(mapcar
#'cdr (doom--collect-forms-in
(doom-path doom-private-dir "packages.el")
"package!"))
(error (format "<%S>" e)))
'("n/a")))
(unpin
,@(or (condition-case e
(mapcan #'identity
(mapcar
#'cdr (doom--collect-forms-in
(doom-path doom-private-dir "packages.el")
"unpin!")))
(error (format "<%S>" e)))
'("n/a")))
(elpa
@@ -109,13 +154,13 @@ branch and commit."
(interactive)
(require 'vc-git)
(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
(or (vc-git-working-revision doom-core-dir)
"n/a")
emacs-version
(or (vc-git--symbolic-ref doom-core-dir)
"n/a")
(or (vc-git-working-revision doom-core-dir)
"n/a")
(or (cdr (doom-call-process "git" "log" "-1" "--format=%ci"))
"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*"))
(info (doom-info)))
(with-current-buffer buffer
(unless (or noninteractive
(eq major-mode 'markdown-mode)
(not (fboundp 'markdown-mode)))
(markdown-mode))
(or (not doom-interactive-p)
(eq major-mode 'markdown-mode)
(not (fboundp 'markdown-mode))
(markdown-mode))
(erase-buffer)
(if raw
(progn
(save-excursion
(pp info (current-buffer)))
(when (search-forward "(modules " nil t)
(goto-char (match-beginning 0))
(cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'sexp)
(let ((sexp (prin1-to-string (sexp-at-point))))
(delete-region beg end)
(insert sexp)))))
(dolist (sym '(modules packages))
(when (re-search-forward (format "^ *\\((%s\\)" sym) nil t)
(goto-char (match-beginning 1))
(cl-destructuring-bind (beg . end)
(bounds-of-thing-at-point 'sexp)
(let ((sexp (prin1-to-string (sexp-at-point))))
(delete-region beg end)
(insert sexp))))))
(insert "<details>\n\n```\n")
(dolist (group info)
(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")
((car spec) (cdr spec)))))
(insert "```\n</details>"))
(if noninteractive
(if (not doom-interactive-p)
(print! (buffer-string))
(switch-to-buffer buffer)
(kill-new (buffer-string))
@@ -161,15 +207,45 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
;;;###autoload
(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))
(interactive)
(unless (string-match-p "\\_<GNUTLS\\_>" system-configuration-features)
(warn "gnutls support isn't built into Emacs, there may be problems"))
(if-let* ((bad-hosts
(cl-loop for bad
in '("https://wrong.host.badssl.com/"
"https://self-signed.badssl.com/")
in '("https://expired.badssl.com/"
"https://wrong.host.badssl.com/"
"https://self-signed.badssl.com/"
"https://untrusted-root.badssl.com/"
;; "https://revoked.badssl.com/"
;; "https://pinning-test.badssl.com/"
"https://sha1-intermediate.badssl.com/"
"https://rc4-md5.badssl.com/"
"https://rc4.badssl.com/"
"https://3des.badssl.com/"
"https://null.badssl.com/"
"https://sha1-intermediate.badssl.com/"
;; "https://client-cert-missing.badssl.com/"
"https://dh480.badssl.com/"
"https://dh512.badssl.com/"
"https://dh-small-subgroup.badssl.com/"
"https://dh-composite.badssl.com/"
"https://invalid-expected-sct.badssl.com/"
;; "https://no-sct.badssl.com/"
;; "https://mixed-script.badssl.com/"
;; "https://very.badssl.com/"
"https://subdomain.preloaded-hsts.badssl.com/"
"https://superfish.badssl.com/"
"https://edellroot.badssl.com/"
"https://dsdtestprovider.badssl.com/"
"https://preact-cli.badssl.com/"
"https://webpack-dev-server.badssl.com/"
"https://captive-portal.badssl.com/"
"https://mitm-software.badssl.com/"
"https://sha1-2016.badssl.com/"
"https://sha1-2017.badssl.com/")
if (condition-case _e
(url-retrieve-synchronously bad)
(error nil))
@@ -193,48 +269,59 @@ markdown and copies it to your clipboard, ready to be pasted into bug reports!"
(file (make-temp-file "doom-sandbox-")))
(require 'package)
(with-temp-file file
(insert
(prin1-to-string
(macroexp-progn
(append `((setq noninteractive nil
doom-debug-mode t
load-path ',load-path
package--init-file-ensured t
package-user-dir ,package-user-dir
package-archives ',package-archives
user-emacs-directory ,doom-emacs-dir)
(with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't
;; corrrectly initialized
(setq-default buffer-undo-tree (make-undo-tree))))
(pcase mode
(`vanilla-doom+ ; Doom core + modules - private config
`((load-file ,(expand-file-name "core.el" doom-core-dir))
(doom-initialize)
(doom-initialize-core)
(add-hook 'window-setup-hook #'doom-display-benchmark-h)
(setq doom-modules ',doom-modules)
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "init" (doom-module-locate-path (car key) (cdr key)) t)))
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)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(doom-run-all-startup-hooks-h)))
(`vanilla-doom ; only Doom core
`((load-file ,(expand-file-name "core.el" doom-core-dir))
(doom-initialize)
(doom-initialize-core)
(doom-run-all-startup-hooks-h)))
(`vanilla ; nothing loaded
`((package-initialize)))))))
"\n(unwind-protect (progn\n" contents "\n)\n"
(format "(delete-file %S))" file)))
(prin1 `(progn
(setq noninteractive nil
process-environment ',doom--initial-process-environment
exec-path ',doom--initial-exec-path
init-file-debug t
load-path ',load-path
package--init-file-ensured t
package-user-dir ,package-user-dir
package-archives ',package-archives
user-emacs-directory ,doom-emacs-dir)
(with-eval-after-load 'undo-tree
;; undo-tree throws errors because `buffer-undo-tree' isn't
;; correctly initialized
(setq-default buffer-undo-tree (make-undo-tree)))
(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
`(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-core-modules))
(setq doom-modules ',doom-modules)
(maphash (lambda (key plist)
(doom-module-put
(car key) (cdr key)
:path (doom-module-locate-path (car key) (cdr key))))
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)
(doom-run-all-startup-hooks-h)))
(`vanilla-doom ; only Doom core
`(progn
(load-file ,(expand-file-name "core.el" doom-core-dir))
(let ((doom-init-modules-p t))
(doom-initialize)
(doom-initialize-core-modules))
(--run--)
(doom-run-all-startup-hooks-h)))
(`vanilla ; nothing loaded
`(progn
(package-initialize)
(--run--))))
(current-buffer)))
(let ((args (if (eq mode 'doom)
(list "-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)
(signal (car e) (cdr e)))))))
(fset 'doom--run-vanilla-emacs (lambda! (doom--run-sandbox 'vanilla)))
(fset 'doom--run-vanilla-doom (lambda! (doom--run-sandbox 'vanilla-doom)))
(fset 'doom--run-vanilla-doom+ (lambda! (doom--run-sandbox 'vanilla-doom+)))
(fset 'doom--run-full-doom (lambda! (doom--run-sandbox 'doom)))
(fset 'doom--run-vanilla-emacs (cmd! (doom--run-sandbox 'vanilla)))
(fset 'doom--run-vanilla-doom (cmd! (doom--run-sandbox 'vanilla-doom)))
(fset 'doom--run-vanilla-doom+ (cmd! (doom--run-sandbox 'vanilla-doom+)))
(fset 'doom--run-full-doom (cmd! (doom--run-sandbox 'doom)))
(defvar doom-sandbox-emacs-lisp-mode-map
(let ((map (make-sparse-keymap)))
@@ -293,7 +380,7 @@ to reproduce bugs and determine if Doom is to blame."
(doom-sandbox-emacs-lisp-mode)
(setq-local default-directory doom-emacs-dir)
(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))))
(erase-buffer)
(insert contents "\n")))
@@ -328,17 +415,3 @@ will be automatically appended to the result."
(profiler-report)
(profiler-stop))
(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"))))

View File

@@ -22,23 +22,20 @@ Returns (approximately):
This is used by `file-exists-p!' and `project-file-exists-p!'."
(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)
(memq (car spec) '(or and)))
(cons (car spec)
(mapcar (doom-rpartial #'doom--resolve-path-forms directory)
(cdr spec)))
(let ((filevar (make-symbol "file")))
`(let* ((file-name-handler-alist nil)
(,filevar ,spec))
(and (stringp ,filevar)
,(if directory
`(let ((default-directory ,directory))
(,exists-fn ,filevar))
(list exists-fn filevar))
,filevar))))))
(if (and (listp spec)
(memq (car spec) '(or and)))
(cons (car spec)
(mapcar (doom-rpartial #'doom--resolve-path-forms directory)
(cdr spec)))
(let ((filevar (make-symbol "file")))
`(let* ((file-name-handler-alist nil)
(,filevar ,spec))
(and (stringp ,filevar)
,(if directory
`(let ((default-directory ,directory))
(file-exists-p ,filevar))
`(file-exists-p ,filevar))
,filevar)))))
(defun doom--path (&rest segments)
(let (file-name-handler-alist)
@@ -108,26 +105,26 @@ be relative to it.
The search recurses up to DEPTH and no further. DEPTH is an integer.
MATCH is a string regexp. Only entries that match it will be included."
(let (file-name-handler-alist
result)
(let (result file-name-handler-alist)
(dolist (file (mapcan (doom-rpartial #'doom-glob "*") (doom-enlist paths)))
(cond ((file-directory-p file)
(nconcq! result
(and (memq type '(t dirs))
(string-match-p match file)
(not (and filter (funcall filter file)))
(not (and (file-symlink-p file)
(not follow-symlinks)))
(<= mindepth 0)
(list (cond (map (funcall map file))
(relative-to (file-relative-name file relative-to))
(file))))
(and (>= depth 1)
(apply #'doom-files-in file
(append (list :mindepth (1- mindepth)
:depth (1- depth)
:relative-to relative-to)
rest)))))
(appendq!
result
(and (memq type '(t dirs))
(string-match-p match file)
(not (and filter (funcall filter file)))
(not (and (file-symlink-p file)
(not follow-symlinks)))
(<= mindepth 0)
(list (cond (map (funcall map file))
(relative-to (file-relative-name file relative-to))
(file))))
(and (>= depth 1)
(apply #'doom-files-in file
(append (list :mindepth (1- mindepth)
:depth (1- depth)
:relative-to relative-to)
rest)))))
((and (memq type '(t files))
(string-match-p match file)
(not (and filter (funcall filter file)))
@@ -203,53 +200,29 @@ single file or nested compound statement of `and' and `or' statements."
;;
;;; Helpers
(defun doom--forget-file (old-path &optional new-path)
"Ensure `recentf', `projectile' and `save-place' forget OLD-PATH."
(when (bound-and-true-p recentf-mode)
(when new-path
(recentf-add-file new-path))
(recentf-remove-if-non-kept old-path))
(when (and (bound-and-true-p projectile-mode)
(doom-project-p)
(projectile-file-cached-p old-path (doom-project-root)))
(projectile-purge-file-from-cache old-path))
(when (bound-and-true-p save-place-mode)
(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))))
(defun doom--update-files (&rest files)
"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)
(recentf-remove-if-non-kept file))
(when (and (bound-and-true-p projectile-mode)
(doom-project-p)
(projectile-file-cached-p file (doom-project-root)))
(projectile-purge-file-from-cache file))))
(dolist (default-directory toplevels)
(magit-refresh))
(when (bound-and-true-p save-place-mode)
(save-place-forget-unreadable-files))))
;;
@@ -257,68 +230,69 @@ single file or nested compound statement of `and' and `or' statements."
;;;###autoload
(defun doom/delete-this-file (&optional path force-p)
"Delete FILENAME (defaults to the file associated with current buffer) and
kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)."
"Delete PATH, kill its buffers and expunge it from vc/magit cache.
If PATH is not specified, default to the current buffer's file.
If FORCE-P, delete without confirmation."
(interactive
(list (file-truename (buffer-file-name))
(list (buffer-file-name (buffer-base-buffer))
current-prefix-arg))
(let* ((fbase (file-name-sans-extension (file-name-nondirectory path)))
(buf (current-buffer)))
(cond ((not (file-exists-p path))
(error "File doesn't exist: %s" path))
((not (or force-p (y-or-n-p (format "Really delete %s?" fbase))))
(message "Aborted")
nil)
((unwind-protect
(progn (delete-file path) t)
(let ((short-path (file-relative-name path (doom-project-root))))
(if (file-exists-p path)
(error "Failed to delete %s" short-path)
;; Ensures that windows displaying this buffer will be switched
;; to real buffers (`doom-real-buffer-p')
(doom/kill-this-buffer-in-all-windows buf t)
(doom--forget-file path)
(doom--update-file path)
(message "Successfully deleted %s" short-path))))))))
(let* ((path (or path (buffer-file-name (buffer-base-buffer))))
(short-path (abbreviate-file-name 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))
(unless (or force-p (y-or-n-p (format "Really delete %S?" short-path)))
(user-error "Aborted"))
(let ((buf (current-buffer)))
(unwind-protect
(progn (delete-file path) t)
(if (file-exists-p path)
(error "Failed to delete %S" short-path)
;; Ensures that windows displaying this buffer will be switched to
;; real buffers (`doom-real-buffer-p')
(doom/kill-this-buffer-in-all-windows buf t)
(doom--update-files path)
(message "Deleted %S" short-path))))))
;;;###autoload
(defun doom/copy-this-file (new-path &optional force-p)
"Copy current buffer's file to NEW-PATH. If FORCE-P, overwrite the destination
file if it exists, without confirmation."
"Copy current buffer's file to NEW-PATH.
If FORCE-P, overwrite the destination file if it exists, without confirmation."
(interactive
(list (read-file-name "Copy file to: ")
current-prefix-arg))
(pcase (catch 'status
(when-let (dest (doom--copy-file (buffer-file-name) new-path force-p))
(doom--update-file new-path)
(message "File successfully copied to %s" dest)))
(`overwrite-self (error "Cannot overwrite self"))
(`aborted (message "Aborted"))
(_ t)))
(unless (and buffer-file-name (file-exists-p 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)))
(make-directory (file-name-directory new-path) 't)
(copy-file old-path new-path (or force-p 1))
(doom--update-files old-path new-path)
(message "File copied to %S" (abbreviate-file-name new-path))))
;;;###autoload
(defun doom/move-this-file (new-path &optional force-p)
"Move current buffer's file to NEW-PATH. If FORCE-P, overwrite the destination
file if it exists, without confirmation."
"Move current buffer's file to NEW-PATH.
If FORCE-P, overwrite the destination file if it exists, without confirmation."
(interactive
(list (read-file-name "Move file to: ")
current-prefix-arg))
(pcase (catch 'status
(let ((old-path (buffer-file-name))
(new-path (expand-file-name new-path)))
(when-let (dest (doom--copy-file old-path new-path force-p))
(when (file-exists-p old-path)
(delete-file old-path))
(kill-current-buffer)
(doom--forget-file old-path new-path)
(doom--update-file new-path)
(find-file new-path)
(message "File successfully moved to %s" dest))))
(`overwrite-self (error "Cannot overwrite self"))
(`aborted (message "Aborted"))
(_ t)))
(unless (and buffer-file-name (file-exists-p 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)))
(make-directory (file-name-directory new-path) 't)
(rename-file old-path new-path (or force-p 1))
(set-visited-file-name new-path t t)
(doom--update-files old-path new-path)
(message "File moved to %S" (abbreviate-file-name new-path))))
(defun doom--sudo-file (file)
(defun doom--sudo-file-path (file)
(let ((host (or (file-remote-p file 'host) "localhost")))
(concat "/" (when (file-remote-p file)
(concat (file-remote-p file 'method) ":"
@@ -334,10 +308,32 @@ file if it exists, without confirmation."
(defun doom/sudo-find-file (file)
"Open FILE as root."
(interactive "FOpen file as root: ")
(find-file (doom--sudo-file file)))
(find-file (doom--sudo-file-path file)))
;;;###autoload
(defun doom/sudo-this-file ()
"Open the current file as root."
(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))))

View File

@@ -12,7 +12,7 @@ scaled up by `doom-big-font-increment'. See `doom-font' for details on
acceptable values for this variable.")
;;;###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
`doom-big-font-mode' is enabled and `doom-big-font' is nil.")
@@ -20,56 +20,47 @@ acceptable values for this variable.")
;;
;;; Library
(defun doom--font-name (fontname frame)
(defun doom--font-name (fontname)
(when (query-fontset fontname)
(when-let (ascii (assq 'ascii (aref (fontset-info fontname frame) 2)))
(when-let (ascii (assq 'ascii (aref (fontset-info fontname) 2)))
(setq fontname (nth 2 ascii))))
(or (x-decompose-font-name fontname)
(error "Cannot decompose font name")))
(defun doom--frame-list (&optional frame)
"Return a list consisting of FRAME and all of FRAME's child frames."
(let ((frame (or frame (selected-frame))))
(cons (selected-frame)
(cl-loop for fr in (frame-list)
if (eq (frame-parameter fr 'parent-frame) frame)
collect fr))))
(defvar doom--font-scale nil)
;;;###autoload
(defun doom-adjust-font-size (increment &optional frame)
(defun doom-adjust-font-size (increment)
"Increase size of font in FRAME by INCREMENT.
FRAME parameter defaults to current frame."
(if (null increment)
(let ((frames (doom--frame-list frame)))
(dolist (frame frames)
(when (frame-parameter frame 'font-scale)
(set-frame-parameter frame 'font-scale nil)))
(set-frame-font doom-font 'keep-size frames)
(and frames t))
(let (success)
(dolist (frame (doom--frame-list frame))
(let* ((font (frame-parameter frame 'font))
(font (doom--font-name font frame))
(increment (* increment doom-font-increment))
(zoom-factor (or (frame-parameter frame 'font-scale) 0)))
(let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum))
increment)))
(unless (> new-size 0)
(error "Font is too small at %d" new-size))
(aset font xlfd-regexp-pixelsize-subnum (number-to-string new-size)))
;; Set point size & width to "*", so frame width will adjust to new font size
(aset font xlfd-regexp-pointsize-subnum "*")
(aset font xlfd-regexp-avgwidth-subnum "*")
(setq font (x-compose-font-name font))
(unless (x-list-fonts font)
(error "Cannot change font size"))
(set-frame-parameter frame 'font font)
(set-frame-parameter frame 'font-scale (+ zoom-factor increment))
(setq success t)))
(when success
;; Unlike `set-frame-font', `set-frame-parameter' won't trigger this
(run-hooks 'after-setting-font-hook)
t))))
(progn
(set-frame-font doom-font 'keep-size t)
(setf (alist-get 'font default-frame-alist)
(cond ((stringp doom-font) doom-font)
((fontp doom-font) (font-xlfd-name doom-font))
((signal 'wrong-type-argument (list '(fontp stringp)
doom-font)))))
t)
(let* ((font (frame-parameter nil 'font))
(font (doom--font-name font))
(increment (* increment doom-font-increment))
(zoom-factor (or doom--font-scale 0)))
(let ((new-size (+ (string-to-number (aref font xlfd-regexp-pixelsize-subnum))
increment)))
(unless (> new-size 0)
(error "Font is too small at %d" new-size))
(aset font xlfd-regexp-pixelsize-subnum (number-to-string new-size)))
;; Set point size & width to "*", so frame width will adjust to new font size
(aset font xlfd-regexp-pointsize-subnum "*")
(aset font xlfd-regexp-avgwidth-subnum "*")
(setq font (x-compose-font-name font))
(unless (x-list-fonts font)
(error "Cannot change font size"))
(set-frame-font font 'keep-size t)
(setf (alist-get 'font default-frame-alist) font)
(setq doom--font-scale (+ zoom-factor increment))
;; Unlike `set-frame-font', `set-frame-parameter' won't trigger this
(run-hooks 'after-setting-font-hook))))
;;
@@ -127,8 +118,13 @@ This uses `doom/increase-font-size' under the hood, and enlargens the font by
(unless doom-font
(user-error "`doom-font' must be set to a valid font"))
(if doom-big-font
(set-frame-font (if doom-big-font-mode doom-big-font doom-font)
'keep-size (doom--frame-list))
(let ((font (if doom-big-font-mode doom-big-font doom-font)))
(set-frame-font font 'keep-size t)
(setf (alist-get 'font default-frame-alist)
(cond ((stringp doom-font) font)
((fontp font) (font-xlfd-name font))
((signal 'wrong-type-argument (list '(fontp stringp)
font))))))
(doom-adjust-font-size
(and doom-big-font-mode
(integerp doom-big-font-increment)

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)))

View File

@@ -3,9 +3,6 @@
(defvar doom--help-major-mode-module-alist
'((dockerfile-mode :tools docker)
(agda2-mode :lang agda)
(haxor-mode :lang assembly)
(mips-mode :lang assembly)
(nasm-mode :lang assembly)
(c-mode :lang cc)
(c++-mode :lang cc)
(objc++-mode :lang cc)
@@ -34,6 +31,7 @@
(js2-mode :lang javascript)
(rjsx-mode :lang javascript)
(typescript-mode :lang javascript)
(typescript-tsx-mode :lang javascript)
(coffee-mode :lang javascript)
(julia-mode :lang julia)
(kotlin-mode :lang kotlin)
@@ -41,13 +39,14 @@
(LaTeX-mode :lang latex)
(ledger-mode :lang ledger)
(lua-mode :lang lua)
(moonscript-mode :lang lua)
(markdown-mode :lang markdown)
(gfm-mode :lang markdown)
(nim-mode :lang nim)
(nix-mode :lang nix)
(taureg-mode :lang ocaml)
(org-mode :lang org)
(perl-mode :lang perl)
(raku-mode :lang raku)
(php-mode :lang php)
(hack-mode :lang php)
(plantuml-mode :lang plantuml)
@@ -55,9 +54,10 @@
(python-mode :lang python)
(restclient-mode :lang rest)
(ruby-mode :lang ruby)
(enh-ruby-mode :lang ruby)
(rust-mode :lang rust)
(rustic-mode :lang rust)
(scala-mode :lang scala)
(scheme-mode :lang scheme)
(sh-mode :lang sh)
(swift-mode :lang swift)
(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
selection of all minor-modes, active or not."
(interactive
(list (completing-read "Minor mode: " (doom-active-minor-modes))))
(list (completing-read "Describe active mode: " (doom-active-minor-modes))))
(let ((symbol
(cond ((stringp mode) (intern mode))
((symbolp mode) mode)
@@ -105,21 +105,6 @@ selection of all minor-modes, active or not."
(helpful-function symbol)
(helpful-variable symbol))))
;;;###autoload
(defun doom/describe-symbol (symbol)
"Show help for SYMBOL, a variable, function or macro."
(interactive
(list (helpful--read-symbol "Symbol: " #'helpful--bound-p)))
(let* ((sym (intern-soft symbol))
(bound (boundp sym))
(fbound (fboundp sym)))
(cond ((and sym bound (not fbound))
(helpful-variable sym))
((and sym fbound (not bound))
(helpful-callable sym))
((apropos (format "^%s\$" symbol)))
((apropos (format "%s" symbol))))))
;;
;;; Documentation commands
@@ -130,7 +115,8 @@ selection of all minor-modes, active or not."
(require 'org)
(let* ((default-directory doom-docs-dir)
(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...")
(unwind-protect
(delq
@@ -150,7 +136,8 @@ selection of all minor-modes, active or not."
(list (or (+org-get-global-property "TITLE")
(file-relative-name (buffer-file-name)))))
path
(list (replace-regexp-in-string org-link-any-re "\\4" text)))
(when text
(list (replace-regexp-in-string org-link-any-re "\\4" text))))
" > ")
tags)
" ")
@@ -196,7 +183,7 @@ selection of all minor-modes, active or not."
(find-file (expand-file-name "index.org" doom-docs-dir)))
;;;###autoload
(defun doom/help-search (&optional initial-input)
(defun doom/help-search-headings (&optional initial-input)
"Search Doom's documentation and jump to a headline."
(interactive)
(doom-completing-read-org-headings
@@ -213,7 +200,26 @@ selection of all minor-modes, active or not."
(doom--help-modules-list))))
;;;###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."
(interactive)
(doom-completing-read-org-headings
@@ -316,13 +322,7 @@ without needing to check if they are available."
readme-path)))
(defun doom--help-current-module-str ()
(cond ((and buffer-file-name
(eq major-mode 'emacs-lisp-mode)
(file-in-directory-p buffer-file-name doom-private-dir)
(save-excursion (goto-char (point-min))
(re-search-forward "^\\s-*(doom! " nil t))
(thing-at-point 'sexp t)))
((save-excursion
(cond ((save-excursion
(require 'smartparens)
(ignore-errors
(sp-beginning-of-sexp)
@@ -341,11 +341,14 @@ without needing to check if they are available."
(symbol-name (cadr mod)))))))
;;;###autoload
(defun doom/help-modules (category module)
(defun doom/help-modules (category module &optional visit-dir)
"Open the documentation for a Doom module.
CATEGORY is a keyword and MODULE is a symbol. e.g. :editor and 'evil.
If VISIT-DIR is non-nil, visit the module's directory rather than its
documentation.
Automatically selects a) the module at point (in private init files), b) the
module derived from a `featurep!' or `require!' call, c) the module that the
current file is in, or d) the module associated with the current major mode (see
@@ -354,7 +357,8 @@ current file is in, or d) the module associated with the current major mode (see
(mapcar #'intern
(split-string
(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))
" " t)))
(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)
(or (assoc (format "%s %s" category module) (doom--help-modules-list))
(user-error "'%s %s' is not a valid module" category module))
(setq module-string (substring-no-properties module-string))
(unless (file-readable-p path)
(error "Can't find or read %S module at %S" module-string path))
(if (not (file-directory-p path))
(find-file path)
(if (y-or-n-p (format "The %S module has no README file. Explore its directory?"
module-string))
(doom-project-browse path)
(user-error "Aborted module lookup")))))
(cond ((not (file-directory-p path))
(if visit-dir
(doom-project-browse (file-name-directory path))
(find-file path)))
(visit-dir
(doom-project-browse path))
((y-or-n-p (format "The %S module has no README file. Explore its directory?"
module-string))
(doom-project-browse (file-name-directory path)))
((user-error "Aborted module lookup")))))
;;
@@ -419,8 +428,8 @@ If prefix arg is present, refresh the cache."
(let ((guess (or (function-called-at-point)
(symbol-at-point))))
(require 'finder-inf nil t)
(require 'core-packages)
(doom-initialize-packages)
(require 'package)
(require 'straight)
(let ((packages (delete-dups
(append (mapcar #'car package-alist)
(mapcar #'car package--builtins)
@@ -432,9 +441,9 @@ If prefix arg is present, refresh the cache."
(list
(intern
(completing-read (if guess
(format "Select package to search for (default %s): "
(format "Select Doom package to search for (default %s): "
guess)
"Describe package: ")
"Describe Doom package: ")
packages nil t nil nil
(if guess (symbol-name guess))))))))
(require 'core-packages)
@@ -462,27 +471,44 @@ If prefix arg is present, refresh the cache."
(insert (symbol-name package) "\n")
(package--print-help-section "Source")
(insert (or (pcase (doom-package-backend package)
(`straight
(format! "Straight (%s)\n%s"
(let ((default-directory (straight--build-dir (symbol-name package))))
(cdr
(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")
(pcase (doom-package-backend package)
(`straight
(insert "Straight\n")
(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
(doom-call-process "git" "log" "-1" "--format=%D %h %ci")))
"\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")
(insert "Declared by the following Doom modules:\n")
(dolist (m (doom-package-get package :modules))
(dolist (m modules)
(insert indent)
(doom--help-package-insert-button
(format "%s %s" (car m) (or (cdr m) ""))
@@ -512,7 +538,7 @@ If prefix arg is present, refresh the cache."
(insert "\n\n")))))
(defvar doom--package-cache nil)
(defun doom--package-list ()
(defun doom--package-list (&optional prompt)
(let* ((guess (or (function-called-at-point)
(symbol-at-point))))
(require 'finder-inf nil t)
@@ -528,10 +554,11 @@ If prefix arg is present, refresh the cache."
(setq doom--package-cache packages)
(unless (memq guess packages)
(setq guess nil))
(intern (completing-read (if guess
(format "Select package to search for (default %s): "
guess)
"Describe package: ")
(intern (completing-read (or prompt
(if guess
(format "Select package to search for (default %s): "
guess)
"Describe package: "))
packages nil t nil nil
(if guess (symbol-name guess)))))))
@@ -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
config blocks in your private config."
(interactive (list (doom--package-list)))
(interactive (list (doom--package-list "Find package config: ")))
(cl-destructuring-bind (file line _match)
(split-string
(completing-read
@@ -592,55 +619,48 @@ config blocks in your private config."
(recenter)))
;;;###autoload
(defun doom/help-package-homepage (package)
"Open PACKAGE's repo or homepage in your browser."
(interactive (list (doom--package-list)))
(browse-url (doom--package-url package)))
(defalias 'doom/help-package-homepage #'straight-visit-package-website)
(defun doom--help-search-prompt (prompt)
(let ((query (doom-thing-at-point-or-region)))
(if (featurep 'counsel)
query
(read-string prompt query 'git-grep query))))
(defvar counsel-rg-base-command)
(defun doom--help-search (dirs query prompt)
;; REVIEW Replace with deadgrep
(unless (executable-find "rg")
(user-error "Can't find ripgrep on your system"))
(if (fboundp 'counsel-rg)
(let ((counsel-rg-base-command (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
(defun doom/help-search-load-path (query)
"Perform a text search on your `load-path'.
Uses the symbol at point or the current selection, if available."
(interactive
(let ((query
;; TODO Generalize this later; into something the lookup module and
;; project search commands could as well
(if (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))
(or (symbol-name (symbol-at-point)) ""))))
(list (read-string
(format "Search load-path (default: %s): " query)
nil 'git-grep query))))
;; REVIEW Replace with deadgrep
(grep-find
(mapconcat
#'shell-quote-argument
(append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
(cl-remove-if-not #'file-directory-p load-path))
" ")))
(list (doom--help-search-prompt "Search load-path: ")))
(doom--help-search (cl-remove-if-not #'file-directory-p load-path)
query "Search load-path: "))
;; TODO factor our the duplicate code between this and the above
;;;###autoload
(defun doom/help-search-loaded-files (query)
"Perform a text search on your `load-path'.
Uses the symbol at point or the current selection, if available."
(interactive
(let ((query
;; TODO Generalize this later; into something the lookup module and
;; project search commands could as well.
(if (use-region-p)
(buffer-substring-no-properties (region-beginning) (region-end))
(or (symbol-name (symbol-at-point)) ""))))
(list (read-string
(format "Search load-path (default: %s): " query)
nil 'git-grep query))))
(unless (executable-find "rg")
(user-error "Can't find ripgrep on your system"))
(require 'elisp-refs)
;; REVIEW Replace with deadgrep
(grep-find
(mapconcat
#'shell-quote-argument
(append (list "rg" "-L" "--search-zip" "--no-heading" "--color=never" query)
(cl-remove-if-not #'file-directory-p (elisp-refs--loaded-paths)))
" ")))
(list (doom--help-search-prompt "Search loaded files: ")))
(doom--help-search
(cl-loop for (file . _) in (cl-remove-if-not #'stringp load-history :key #'car)
for filebase = (file-name-sans-extension file)
if (file-exists-p! (format "%s.el" filebase))
collect it)
query "Search loaded files: "))

View File

@@ -1,198 +1,5 @@
;;; 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
(defun doom/reload-packages ()
"Reload `doom-packages', `package' and `quelpa'."
@@ -201,3 +8,190 @@ ones."
(message "Reloading packages")
(doom-initialize-packages t)
(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))

View File

@@ -35,24 +35,6 @@ list, the pair is destructured into (CAR . CDR)."
"Delete PROP from PLIST in-place."
`(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
@@ -66,7 +48,7 @@ BODY."
;;;###autoload
(defun doom-plist-merge (from-plist to-plist)
"Destructively merge FROM-PLIST onto TO-PLIST"
"Non-destructively merge FROM-PLIST onto TO-PLIST"
(let ((plist (copy-sequence from-plist)))
(while plist
(plist-put! to-plist (pop plist) (pop plist)))
@@ -83,11 +65,11 @@ BODY."
p))
;;;###autoload
(defun doom-plist-delete (plist prop)
"Delete PROP from a copy of PLIST."
(defun doom-plist-delete (plist &rest props)
"Delete PROPS from a copy of PLIST."
(let (p)
(while plist
(if (not (eq prop (car plist)))
(if (not (memq (car plist) props))
(plist-put! p (car plist) (nth 1 plist)))
(setq plist (cddr plist)))
p))

View File

@@ -1,6 +1,8 @@
;;; core/autoload/projects.el -*- lexical-binding: t; -*-
(defvar projectile-project-root nil)
(defvar projectile-enable-caching)
(defvar projectile-require-project-root)
;;;###autoload (autoload 'projectile-relevant-known-projects "projectile")
@@ -39,8 +41,7 @@ they are absolute."
"Preforms `projectile-find-file' in a known project of your choosing."
(interactive
(list
(completing-read "Find file in project: " (projectile-relevant-known-projects)
nil nil nil nil (doom-project-root))))
(completing-read "Find file in project: " (projectile-relevant-known-projects))))
(unless (file-directory-p project-root)
(error "Project directory '%s' doesn't exist" 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."
(interactive
(list
(completing-read "Browse in project: " (projectile-relevant-known-projects)
nil nil nil nil (doom-project-root))))
(completing-read "Browse in project: " (projectile-relevant-known-projects))))
(unless (file-directory-p project-root)
(error "Project directory '%s' doesn't exist" 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)
(error "Directory %S isn't readable" dir))
(let* ((default-directory (file-truename (expand-file-name dir)))
(project-root (doom-project-root default-directory))
(projectile-project-root default-directory)
(projectile-project-root (doom-project-root default-directory))
(projectile-enable-caching projectile-enable-caching))
(cond ((and project-root (file-equal-p project-root projectile-project-root))
(unless (doom-project-p projectile-project-root)
(cond ((and projectile-project-root (file-equal-p projectile-project-root default-directory))
(unless (doom-project-p default-directory)
;; Disable caching if this is not a real project; caching
;; non-projects easily has the potential to inflate the projectile
;; cache beyond reason.

View File

@@ -8,9 +8,11 @@ Will be saved in `doom-scratch-dir'.")
(defvar doom-scratch-dir (concat doom-etc-dir "scratch")
"Where to save persistent scratch buffers.")
(defvar doom-scratch-buffer-major-mode nil
"What major mode to use in scratch buffers. This can be one of the
following:
(defvar doom-scratch-initial-major-mode nil
"What major mode to start fresh scratch buffers in.
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.
nil Uses `fundamental-mode'
@@ -21,43 +23,56 @@ following:
(defvar doom-scratch-current-project nil
"The name of the project associated with the current scratch buffer.")
(put 'doom-scratch-current-project 'permanent-local t)
(defvar doom-scratch-buffer-hook ()
"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
(or name
(or project-name
doom-scratch-default-file))
(let ((scratch-file
(expand-file-name doom-scratch-current-project
(let ((smart-scratch-file
(expand-file-name (concat doom-scratch-current-project ".el")
doom-scratch-dir)))
(make-directory doom-scratch-dir t)
(when (file-readable-p scratch-file)
(erase-buffer)
(insert-file-contents scratch-file)
(set-auto-mode)
t)))
(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)
(funcall mode)
(insert content)
(goto-char point)
t))))
;;;###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."
(with-current-buffer
(get-buffer-create (if project-name
(format "*doom:scratch (%s)*" project-name)
"*doom:scratch*"))
(setq default-directory directory)
(unless doom-scratch-current-project
(doom--load-persistent-scratch-buffer project-name)
(when (and (eq major-mode 'fundamental-mode)
(functionp mode))
(funcall mode)))
(cl-pushnew (current-buffer) doom-scratch-buffers)
(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)
(current-buffer)))
(let* ((buffer-name (if project-name
(format "*doom:scratch (%s)*" project-name)
"*doom:scratch*"))
(buffer (get-buffer buffer-name)))
(with-current-buffer
(or buffer (get-buffer-create buffer-name))
(setq default-directory directory)
(setq-local so-long--inhibited t)
(if dont-restore-p
(erase-buffer)
(unless buffer
(doom--load-persistent-scratch-buffer project-name)
(when (and (eq major-mode 'fundamental-mode)
(functionp mode))
(funcall mode))))
(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)
(run-hooks 'doom-scratch-buffer-created-hook)
(current-buffer))))
;;
@@ -66,11 +81,18 @@ following:
;;;###autoload
(defun doom-persist-scratch-buffer-h ()
"Save the current buffer to `doom-scratch-dir'."
(write-region
(point-min) (point-max)
(expand-file-name (or doom-scratch-current-project
doom-scratch-default-file)
doom-scratch-dir)))
(let ((content (buffer-substring-no-properties (point-min) (point-max)))
(point (point))
(mode major-mode))
(with-temp-file
(expand-file-name (concat (or doom-scratch-current-project
doom-scratch-default-file)
".el")
doom-scratch-dir)
(prin1 (list content
point
mode)
(current-buffer)))))
;;;###autoload
(defun doom-persist-scratch-buffers-h ()
@@ -89,62 +111,66 @@ following:
(remove-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h)))
;;;###autoload
(unless noninteractive
(when doom-interactive-p
(add-hook 'kill-emacs-hook #'doom-persist-scratch-buffers-h))
;;
;;; Commands
(defvar projectile-enable-caching)
;;;###autoload
(defun doom/open-scratch-buffer (&optional arg project-p)
"Opens the (persistent) scratch buffer in a popup.
(defun doom/open-scratch-buffer (&optional arg project-p same-window-p)
"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
current project."
(interactive "P")
(let (projectile-enable-caching)
(funcall
(if arg
(if same-window-p
#'switch-to-buffer
#'pop-to-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
(derived-mode-p 'special-mode)
(string-match-p "^ ?\\*" (buffer-name)))
major-mode))
((null doom-scratch-buffer-major-mode)
((null doom-scratch-initial-major-mode)
nil)
((symbolp doom-scratch-buffer-major-mode)
doom-scratch-buffer-major-mode))
((symbolp doom-scratch-initial-major-mode)
doom-scratch-initial-major-mode))
default-directory
(when project-p
(doom-project-name))))))
;;;###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.
If passed the prefix arg, open project scratch buffer."
If passed the prefix ARG, do not restore the last scratch buffer."
(interactive "P")
(doom/open-scratch-buffer t project-p))
(doom/open-scratch-buffer arg project-p 'same-window))
;;;###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.
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")
(doom/open-scratch-buffer current-window 'project))
(doom/open-scratch-buffer arg 'project same-window-p))
;;;###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
window."
(interactive)
(doom/open-project-scratch-buffer t))
window.
If passed the prefix ARG, do not restore the last scratch buffer."
(interactive "P")
(doom/open-project-scratch-buffer arg 'same-window))
;;;###autoload
(defun doom/revert-scratch-buffer ()

View File

@@ -47,10 +47,16 @@
"TODO"
(setq file (expand-file-name (or file (doom-session-file))))
(message "Attempting to load %s" file)
(cond ((require 'persp-mode nil t)
(cond ((not (file-readable-p file))
(message "No session file at %S to read from" file))
((require 'persp-mode nil t)
(unless persp-mode
(persp-mode +1))
(persp-load-state-from-file file))
(let ((allowed (persp-list-persp-names-in-file file)))
(cl-loop for name being the hash-keys of *persp-hash*
unless (member name allowed)
do (persp-kill name))
(persp-load-state-from-file file)))
((and (require 'frameset nil t)
(require 'restart-emacs nil t))
(restart-emacs--restore-frames-using-desktop file))
@@ -65,6 +71,9 @@
"TODO"
(add-hook 'window-setup-hook #'doom-load-session 'append))
;;;###autoload
(add-to-list 'command-switch-alist (cons "--restore" #'doom-restore-session-handler))
;;
;;; Commands
@@ -124,5 +133,11 @@
(interactive "P")
(setq doom-autosave-session nil)
(doom/quicksave-session)
(restart-emacs
(delq nil (list (if debug "--debug-init") "--restore"))))
(save-some-buffers nil t)
(letf! ((#'save-buffers-kill-emacs #'kill-emacs)
(confirm-kill-emacs))
(restart-emacs
(append (if debug (list "--debug-init"))
(when (boundp 'chemacs-current-emacs-profile)
(list "--with-profile" chemacs-current-emacs-profile))
(list "--restore")))))

View File

@@ -1,5 +1,17 @@
;;; core/autoload/text.el -*- lexical-binding: t; -*-
(defvar doom-point-in-comment-functions ()
"List of functions to run to determine if point is in a comment.
Each function takes one argument: the position of the point. Stops on the first
function to return non-nil. Used by `doom-point-in-comment-p'.")
(defvar doom-point-in-string-functions ()
"List of functions to run to determine if point is in a string.
Each function takes one argument: the position of the point. Stops on the first
function to return non-nil. Used by `doom-point-in-string-p'.")
;;;###autoload
(defun doom-surrounded-p (pair &optional inline balanced)
"Returns t if point is surrounded by a brace delimiter: {[(
@@ -28,31 +40,18 @@ lines, above and below, with only whitespace in between."
;;;###autoload
(defun doom-point-in-comment-p (&optional pos)
"Return non-nil if POS is in a comment.
POS defaults to the current position."
;; REVIEW Should we cache `syntax-ppss'?
(let* ((pos (or pos (point)))
(ppss (syntax-ppss pos)))
(or (nth 4 ppss)
(nth 8 ppss)
(and (< pos (point-max))
(memq (char-syntax (char-after pos)) '(?< ?>))
(not (eq (char-after pos) ?\n)))
(when-let (s (car (syntax-after pos)))
(or (and (/= 0 (logand (lsh 1 16) s))
(nth 4 (doom-syntax-ppss (+ pos 2))))
(and (/= 0 (logand (lsh 1 17) s))
(nth 4 (doom-syntax-ppss (+ pos 1))))
(and (/= 0 (logand (lsh 1 18) s))
(nth 4 (doom-syntax-ppss (- pos 1))))
(and (/= 0 (logand (lsh 1 19) s))
(nth 4 (doom-syntax-ppss (- pos 2)))))))))
(let ((pos (or pos (point))))
(or (run-hook-with-args-until-success 'doom-point-in-comment-functions pos)
(sp-point-in-comment pos))))
;;;###autoload
(defun doom-point-in-string-p (&optional pos)
"Return non-nil if POS is in a string."
;; REVIEW Should we cache `syntax-ppss'?
(nth 3 (syntax-ppss pos)))
(let ((pos (or pos (point))))
(or (run-hook-with-args-until-success 'doom-point-in-string-functions pos)
(sp-point-in-string pos))))
;;;###autoload
(defun doom-point-in-string-or-comment-p (&optional pos)
@@ -60,74 +59,157 @@ POS defaults to the current position."
(or (doom-point-in-string-p pos)
(doom-point-in-comment-p pos)))
;;;###autoload
(defun doom-region-active-p ()
"Return non-nil if selection is active.
Detects evil visual mode as well."
(declare (side-effect-free t))
(or (use-region-p)
(and (bound-and-true-p evil-local-mode)
(evil-visual-state-p))))
;;;###autoload
(defun doom-region-beginning ()
"Return beginning position of selection.
Uses `evil-visual-beginning' if available."
(declare (side-effect-free t))
(if (bound-and-true-p evil-local-mode)
evil-visual-beginning
(region-beginning)))
;;;###autoload
(defun doom-region-end ()
"Return end position of selection.
Uses `evil-visual-end' if available."
(declare (side-effect-free t))
(if (bound-and-true-p evil-local-mode)
evil-visual-end
(region-end)))
;;;###autoload
(defun doom-thing-at-point-or-region (&optional thing prompt)
"Grab the current selection, THING at point, or xref identifier at point.
Returns THING if it is a string. Otherwise, if nothing is found at point and
PROMPT is non-nil, prompt for a string (if PROMPT is a string it'll be used as
the prompting string). Returns nil if all else fails.
NOTE: Don't use THING for grabbing symbol-at-point. The xref fallback is smarter
in some cases."
(declare (side-effect-free t))
(cond ((stringp thing)
thing)
((doom-region-active-p)
(buffer-substring-no-properties
(doom-region-beginning)
(doom-region-end)))
(thing
(thing-at-point thing t))
((require 'xref nil t)
;; A little smarter than using `symbol-at-point', though in most cases,
;; xref ends up using `symbol-at-point' anyway.
(xref-backend-identifier-at-point (xref-find-backend)))
(prompt
(read-string (if (stringp prompt) prompt "")))))
;;
;;; Commands
(defvar doom--last-backward-pt most-positive-fixnum)
(defun doom--bol-bot-eot-eol (&optional pos)
(save-mark-and-excursion
(when pos
(goto-char pos))
(let* ((bol (if visual-line-mode
(save-excursion
(beginning-of-visual-line)
(point))
(line-beginning-position)))
(bot (save-excursion
(goto-char bol)
(skip-chars-forward " \t\r")
(point)))
(eol (if visual-line-mode
(save-excursion (end-of-visual-line) (point))
(line-end-position)))
(eot (or (save-excursion
(if (not comment-use-syntax)
(progn
(goto-char bol)
(when (re-search-forward comment-start-skip eol t)
(or (match-end 1) (match-beginning 0))))
(goto-char eol)
(while (and (doom-point-in-comment-p)
(> (point) bol))
(backward-char))
(skip-chars-backward " " bol)
(unless (or (eq (char-after) 32) (eolp))
(forward-char))
(point)))
eol)))
(list bol bot eot eol))))
(defvar doom--last-backward-pt nil)
;;;###autoload
(defun doom/backward-to-bol-or-indent ()
(defun doom/backward-to-bol-or-indent (&optional point)
"Jump between the indentation column (first non-whitespace character) and the
beginning of the line. The opposite of
`doom/forward-to-last-non-comment-or-eol'."
(interactive)
(let ((pt (point)))
(cl-destructuring-bind (bol . bot)
(save-excursion
(beginning-of-visual-line)
(cons (point)
(progn (skip-chars-forward " \t\r")
(point))))
(interactive "^d")
(let ((pt (or point (point))))
(cl-destructuring-bind (bol bot _eot _eol)
(doom--bol-bot-eot-eol pt)
(cond ((> pt bot)
(goto-char bot))
((= pt bol)
(goto-char (min doom--last-backward-pt bot))
(setq doom--last-backward-pt most-positive-fixnum))
(or (and doom--last-backward-pt
(= (line-number-at-pos doom--last-backward-pt)
(line-number-at-pos pt)))
(setq doom--last-backward-pt nil))
(goto-char (or doom--last-backward-pt bot))
(setq doom--last-backward-pt nil))
((<= pt bot)
(setq doom--last-backward-pt pt)
(goto-char bol))))))
(defvar doom--last-forward-pt -1)
(defvar doom--last-forward-pt nil)
;;;###autoload
(defun doom/forward-to-last-non-comment-or-eol ()
(defun doom/forward-to-last-non-comment-or-eol (&optional point)
"Jumps between the last non-blank, non-comment character in the line and the
true end of the line. The opposite of `doom/backward-to-bol-or-indent'."
(interactive "^d")
(let ((pt (or point (point))))
(cl-destructuring-bind (_bol _bot eot eol)
(doom--bol-bot-eot-eol pt)
(cond ((< pt eot)
(goto-char eot))
((= pt eol)
(goto-char (or doom--last-forward-pt eot))
(setq doom--last-forward-pt nil))
((>= pt eot)
(setq doom--last-backward-pt pt)
(goto-char eol))))))
;;;###autoload
(defun doom/backward-kill-to-bol-and-indent ()
"Kill line to the first non-blank character. If invoked again afterwards, kill
line to beginning of line. Same as `evil-delete-back-to-indentation'."
(interactive)
(let ((eol (if (not visual-line-mode)
(line-end-position)
(save-excursion (end-of-visual-line) (point)))))
(if (or (and (< (point) eol)
(sp-point-in-comment))
(not (sp-point-in-comment eol)))
(if (= (point) eol)
(progn
(goto-char doom--last-forward-pt)
(setq doom--last-forward-pt -1))
(setq doom--last-forward-pt (point))
(goto-char eol))
(let* ((bol (save-excursion (beginning-of-visual-line) (point)))
(boc (or (save-excursion
(if (not comment-use-syntax)
(progn
(goto-char bol)
(when (re-search-forward comment-start-skip eol t)
(or (match-end 1) (match-beginning 0))))
(goto-char eol)
(while (and (sp-point-in-comment)
(> (point) bol))
(backward-char))
(skip-chars-backward " " bol)
(point)))
eol)))
(when (> doom--last-forward-pt boc)
(setq boc doom--last-forward-pt))
(if (or (= eol (point))
(> boc (point)))
(progn
(goto-char boc)
(setq doom--last-forward-pt -1))
(setq doom--last-forward-pt (point))
(goto-char eol))))))
(let ((empty-line-p (save-excursion (beginning-of-line)
(looking-at-p "[ \t]*$"))))
(funcall (if (fboundp 'evil-delete)
#'evil-delete
#'delete-region)
(point-at-bol) (point))
(unless empty-line-p
(indent-according-to-mode))))
;;;###autoload
(defun doom/delete-backward-word (arg)
"Like `backward-kill-word', but doesn't affect the kill-ring."
(interactive "p")
(let (kill-ring)
(backward-kill-word arg)))
;;;###autoload
(defun doom/dumb-indent ()
@@ -155,20 +237,6 @@ true end of the line. The opposite of `doom/backward-to-bol-or-indent'."
tab-width
(- tab-width movement)))))))))
;;;###autoload
(defun doom/backward-kill-to-bol-and-indent ()
"Kill line to the first non-blank character. If invoked again
afterwards, kill line to beginning of line."
(interactive)
(let ((empty-line-p (save-excursion (beginning-of-line)
(looking-at-p "[ \t]*$"))))
(funcall (if (fboundp 'evil-delete)
#'evil-delete
#'delete-region)
(point-at-bol) (point))
(unless empty-line-p
(indent-according-to-mode))))
;;;###autoload
(defun doom/retab (arg &optional beg end)
"Converts tabs-to-spaces or spaces-to-tabs within BEG and END (defaults to
@@ -192,15 +260,9 @@ opposite indentation style."
Respects `require-final-newline'."
(interactive)
(goto-char (point-max))
(skip-chars-backward " \t\n\v")
(when (looking-at "\n\\(\n\\|\\'\\)")
(forward-char 1))
(when require-final-newline
(unless (bolp)
(insert "\n")))
(when (looking-at "\n+")
(replace-match "")))
(save-excursion
(goto-char (point-max))
(delete-blank-lines)))
;;;###autoload
(defun doom/dos2unix ()

View File

@@ -9,6 +9,13 @@
`((,(car spec) ((t ,(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
(defmacro custom-theme-set-faces! (theme &rest specs)
"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
all themes. It will apply to all themes once they are loaded."
(declare (indent defun))
`(let ((fn (gensym "doom--customize-themes-h-")))
(fset
fn (lambda ()
(let (custom--inhibit-theme-enable)
(dolist (theme (doom-enlist (or ,theme 'user)))
(when (or (eq theme 'user)
(custom-theme-enabled-p theme))
(apply #'custom-theme-set-faces theme
(mapcan #'doom--custom-theme-set-face
(list ,@specs))))))))
(when (or doom-init-theme-p (null doom-theme))
(funcall fn))
(add-hook 'doom-load-theme-hook fn 'append)))
(let ((fn (gensym "doom--customize-themes-h-")))
`(progn
(defun ,fn ()
(let (custom--inhibit-theme-enable)
(dolist (theme (doom-enlist (or ,theme 'user)))
(when (or (eq theme 'user)
(custom-theme-enabled-p theme))
(apply #'custom-theme-set-faces theme
(mapcan #'doom--custom-theme-set-face
(list ,@specs)))))))
(when (or doom-init-theme-p (null doom-theme))
(funcall #',fn))
(add-hook 'doom-customize-theme-hook #',fn 'append))))
;;;###autoload
(defmacro custom-set-faces! (&rest specs)
"Apply a list of face SPECS as user customizations.
This is a drop-in replacement for `custom-set-face' that allows for a simplified
face format."
This is a convenience macro alternative to `custom-set-face' which allows for a
simplified face format, and takes care of load order issues, so you can use
doom-themes' API without worry."
(declare (indent defun))
`(custom-theme-set-faces! 'user ,@specs))

View File

@@ -1,7 +1,7 @@
;;; core/autoload/ui.el -*- lexical-binding: t; -*-
;;
;; Public library
;;; Public library
;;;###autoload
(defun doom-resize-window (window new-size &optional horizontal force-p)
@@ -24,13 +24,23 @@ are open."
;;
;; Advice
;;; Advice
;;;###autoload
(defun doom-recenter-a (&rest _)
"Generic advisor for recentering window (typically :after other functions)."
"Generic advice for recentering window (typically :after other functions)."
(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
(defun doom-shut-up-a (orig-fn &rest args)
"Generic advisor for silencing noisy functions.
@@ -43,7 +53,7 @@ In tty Emacs, messages suppressed completely."
;;
;; Hooks
;;; Hooks
;;;###autoload
(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."
(setq-local show-paren-mode nil))
;;;###autoload
(defun doom-enable-line-numbers-h ()
(display-line-numbers-mode +1))
;;;###autoload
(defun doom-disable-line-numbers-h ()
(display-line-numbers-mode -1))
;;
;; Commands
;;; Commands
;;;###autoload
(defun doom/toggle-line-numbers ()
@@ -87,7 +105,7 @@ See `display-line-numbers' for what these values mean."
(_ (symbol-name next))))))
;;;###autoload
(defun doom/delete-frame ()
(defun doom/delete-frame-with-prompt ()
"Delete the current frame, but ask for confirmation if it isn't empty."
(interactive)
(if (cdr (frame-list))
@@ -95,6 +113,7 @@ See `display-line-numbers' for what these values mean."
(delete-frame))
(save-buffers-kill-emacs)))
(defvar doom--maximize-last-wconf nil)
;;;###autoload
(defun doom/window-maximize-buffer ()
"Close other windows to focus on this one. Activate again to undo this. If the
@@ -102,41 +121,40 @@ window changes before then, the undo expires.
Alternatively, use `doom/window-enlargen'."
(interactive)
(if (and (one-window-p)
(assq ?_ register-alist))
(jump-to-register ?_)
(when (and (bound-and-true-p +popup-mode)
(+popup-window-p))
(user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead"))
(window-configuration-to-register ?_)
(delete-other-windows)))
(setq doom--maximize-last-wconf
(if (and (null (cdr (cl-remove-if #'window-dedicated-p (window-list))))
doom--maximize-last-wconf)
(ignore (set-window-configuration doom--maximize-last-wconf))
(when (and (bound-and-true-p +popup-mode)
(+popup-window-p))
(user-error "Cannot maximize a popup, use `+popup/raise' first or use `doom/window-enlargen' instead"))
(prog1 (current-window-configuration)
(delete-other-windows)))))
(defvar doom--window-enlargened nil)
(defvar doom--enlargen-last-wconf nil)
;;;###autoload
(defun doom/window-enlargen ()
"Enlargen the current window to focus on this one. Does not close other
windows (unlike `doom/window-maximize-buffer') Activate again to undo."
windows (unlike `doom/window-maximize-buffer'). Activate again to undo."
(interactive)
(setq doom--window-enlargened
(if (and doom--window-enlargened
(assq ?_ register-alist))
(ignore (ignore-errors (jump-to-register ?_)))
(window-configuration-to-register ?_)
(let* ((window (selected-window))
(dedicated-p (window-dedicated-p window))
(preserved-p (window-parameter window 'window-preserved-size))
(ignore-window-parameters t))
(unwind-protect
(progn
(when dedicated-p
(set-window-dedicated-p window nil))
(when preserved-p
(set-window-parameter window 'window-preserved-size nil))
(maximize-window window))
(set-window-dedicated-p window dedicated-p)
(when preserved-p
(set-window-parameter window 'window-preserved-size preserved-p)))
t))))
(setq doom--enlargen-last-wconf
(if doom--enlargen-last-wconf
(ignore (set-window-configuration doom--enlargen-last-wconf))
(prog1 (current-window-configuration)
(let* ((window (selected-window))
(dedicated-p (window-dedicated-p window))
(preserved-p (window-parameter window 'window-preserved-size))
(ignore-window-parameters t))
(unwind-protect
(progn
(when dedicated-p
(set-window-dedicated-p window nil))
(when preserved-p
(set-window-parameter window 'window-preserved-size nil))
(maximize-window window))
(set-window-dedicated-p window dedicated-p)
(when preserved-p
(set-window-parameter window 'window-preserved-size preserved-p))))))))
;;;###autoload
(defun doom/window-maximize-horizontally ()

View File

@@ -1,407 +1,225 @@
;;; core/cli/autoloads.el -*- lexical-binding: t; -*-
(defvar doom-autoload-excluded-packages '("gh")
"Packages that have silly or destructive autoload files that try to load
(defvar doom-autoloads-excluded-packages '("gh")
"What packages whose autoloads file we won't index.
These packages have silly or destructive autoload files that try to load
everyone in the universe and their dog, causing errors that make babies cry. No
one wants that.")
;; externs
(defvar doom-autoloads-cached-vars
'(doom-modules
doom-disabled-packages
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.")
;;
;;; Library
(defun doom-autoloads-reload (&optional file)
"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)
(doom-initialize-packages)
(and (print! (start "Generating autoloads file..."))
(doom-autoloads--write
file
`((unless (equal emacs-major-version ,emacs-major-version)
(signal 'doom-error
(list "The installed version of Emacs has changed since last 'doom sync' ran"
"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-autoloads--write (file &rest forms)
(make-directory (file-name-directory file) 'parents)
(condition-case-unless-debug e
(with-temp-file file
(setq-local coding-system-for-write 'utf-8)
(let ((standard-output (current-buffer))
(print-quoted t)
(print-level nil)
(print-length nil))
(insert ";; -*- lexical-binding: t; coding: utf-8; -*-\n"
";; This file is autogenerated by 'doom sync', DO NOT EDIT IT!!\n")
(dolist (form (delq nil forms))
(mapc #'prin1 form))
t))
(error (delete-file file)
(signal 'doom-autoload-error (list file e)))))
(defun doom-autoloads--compile-file (file)
(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)))))
(defun doom-autoloads--cleanup-form (form &optional expand)
(let ((func (car-safe form)))
(cond ((memq func '(provide custom-autoload))
nil)
((and (eq func 'add-to-list)
(memq (doom-unquote (cadr form))
doom-autoloads-cached-vars))
nil)
((not (eq func 'autoload))
form)
((and expand (not (file-name-absolute-p (nth 2 form))))
(defvar doom--autoloads-path-cache nil)
(setf (nth 2 form)
(let ((path (nth 2 form)))
(or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (locate-library path))
(libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache)
libpath)
path)))
form)
(form))))
(defun doom-autoloads--scan-autodefs (file buffer module &optional module-enabled-p)
(with-temp-buffer
(insert-file-contents file)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((standard-output buffer)
(form (read (current-buffer)))
(altform (match-string 1))
(definer (car-safe form))
(symbol (doom-unquote (cadr form))))
(cond ((and (not module-enabled-p) altform)
(print (read altform)))
((memq definer '(defun defmacro cl-defun cl-defmacro))
(if module-enabled-p
(print (make-autoload form file))
(cl-destructuring-bind (_ _ arglist &rest body) form
(print
(if altform
(read altform)
(append
(list (pcase definer
(`defun 'defmacro)
(`cl-defun `cl-defmacro)
(_ type))
symbol arglist
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
module
(if (stringp (car body))
(pop body)
"No documentation.")))
(cl-loop for arg in arglist
if (and (symbolp arg)
(not (keywordp arg))
(not (memq arg cl--lambda-list-keywords)))
collect arg into syms
else if (listp arg)
collect (car arg) into syms
finally return (if syms `((ignore ,@syms)))))))))
(print `(put ',symbol 'doom-module ',module)))
((eq definer 'defalias)
(cl-destructuring-bind (_ _ target &optional docstring) form
(unless module-enabled-p
(setq target #'ignore
docstring
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
module docstring)))
(print `(put ',symbol 'doom-module ',module))
(print `(defalias ',symbol #',(doom-unquote target) ,docstring))))
(module-enabled-p (print form)))))))
(defvar autoload-timestamps)
(defvar generated-autoload-load-name)
(defvar generated-autoload-file)
;;
;;; Helpers
(defun doom--cli-delete-autoloads-file (file)
"Delete FILE (an autoloads file) and accompanying *.elc file, if any."
(cl-check-type file string)
(when (file-exists-p file)
(when-let (buf (find-buffer-visiting file))
(with-current-buffer buf
(set-buffer-modified-p nil))
(kill-buffer buf))
(delete-file file)
(ignore-errors (delete-file (byte-compile-dest-file file)))
t))
(defun doom--cli-warn-refresh-session-h ()
(message "Restart or reload Doom Emacs for changes to take effect:\n")
(message " M-x doom/restart-and-restore")
(message " M-x doom/restart")
(message " M-x doom/reload"))
(defun doom--cli-byte-compile-file (file)
(let ((byte-compile-warnings (if doom-debug-mode byte-compile-warnings))
(byte-compile-dynamic t)
(byte-compile-dynamic-docstrings t))
(condition-case-unless-debug e
(when (byte-compile-file file)
(unless doom-interactive-mode
(add-hook 'doom-cli-post-success-execute-hook #'doom--cli-warn-refresh-session-h))
(let (noninteractive)
(load file 'noerror 'nomessage 'nosuffix)))
((debug error)
(let ((backup-file (concat file ".bk")))
(print! (warn "Copied backup to %s") (relpath backup-file))
(copy-file file backup-file 'overwrite))
(doom--cli-delete-autoloads-file file)
(signal 'doom-autoload-error (list file e))))))
(defun doom-cli-reload-autoloads (&optional file force-p)
"Reloads FILE (an autoload file), if it needs reloading.
FILE should be one of `doom-autoload-file' or `doom-package-autoload-file'. If
it is nil, it will try to reload both. If FORCE-P (universal argument) do it
even if it doesn't need reloading!"
(or (null file)
(stringp file)
(signal 'wrong-type-argument (list 'stringp file)))
(if (stringp file)
(cond ((file-equal-p file doom-autoload-file)
(doom-cli-reload-core-autoloads force-p))
((file-equal-p file doom-package-autoload-file)
(doom-cli-reload-package-autoloads force-p))
((error "Invalid autoloads file: %s" file)))
(doom-cli-reload-core-autoloads force-p)
(doom-cli-reload-package-autoloads force-p)))
;;
;;; 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)
(while (re-search-forward "^\\s-*(\\(?:custom-\\)?autoload\\s-+'[^ ]+\\s-+\"\\([^\"]*\\)\"" nil t)
(let ((path (match-string 1)))
(replace-match
(or (cdr (assoc path doom--autoloads-path-cache))
(when-let* ((libpath (or (and allow-internal-paths
(locate-library path nil (cons doom-emacs-dir doom-modules-dirs)))
(locate-library path)))
(libpath (file-name-sans-extension libpath))
(libpath (abbreviate-file-name libpath)))
(push (cons path libpath) doom--autoloads-path-cache)
libpath)
path)
t t nil 1)))))
(defun doom--cli-generate-autodefs-1 (path &optional member-p)
(let (forms)
(while (re-search-forward "^;;;###autodef *\\([^\n]+\\)?\n" nil t)
(let* ((sexp (sexp-at-point))
(alt-sexp (match-string 1))
(type (car sexp))
(name (doom-unquote (cadr sexp)))
(origin (doom-module-from-path path)))
(cond
((and (not member-p)
alt-sexp)
(push (read alt-sexp) forms))
((memq type '(defun defmacro cl-defun cl-defmacro))
(cl-destructuring-bind (_ _name arglist &rest body) sexp
(appendq!
forms
(list (if member-p
(make-autoload sexp path)
(let ((docstring
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin
(if (stringp (car body))
(pop body)
"No documentation."))))
(condition-case-unless-debug e
(if alt-sexp
(read alt-sexp)
(append
(list (pcase type
(`defun 'defmacro)
(`cl-defun `cl-defmacro)
(_ type))
name arglist docstring)
(cl-loop for arg in arglist
if (and (symbolp arg)
(not (keywordp arg))
(not (memq arg cl--lambda-list-keywords)))
collect arg into syms
else if (listp arg)
collect (car arg) into syms
finally return (if syms `((ignore ,@syms))))))
('error
(print! "- Ignoring autodef %s (%s)" name e)
nil))))
`(put ',name 'doom-module ',origin)))))
((eq type 'defalias)
(cl-destructuring-bind (_type name target &optional docstring) sexp
(let ((name (doom-unquote name))
(target (doom-unquote target)))
(unless member-p
(setq target #'ignore
docstring
(format "THIS FUNCTION DOES NOTHING BECAUSE %s IS DISABLED\n\n%s"
origin docstring)))
(appendq! forms `((put ',name 'doom-module ',origin)
(defalias ',name #',target ,docstring))))))
(member-p (push sexp forms)))))
forms))
(defun doom--cli-generate-autodefs (targets enabled-targets)
(goto-char (point-max))
(search-backward ";;;***" nil t)
(save-excursion (insert "\n"))
(dolist (path targets)
(insert
(with-temp-buffer
(insert-file-contents path)
(if-let (forms (doom--cli-generate-autodefs-1 path (member path enabled-targets)))
(concat (mapconcat #'prin1-to-string (nreverse forms) "\n")
"\n")
"")))))
(defun doom--cli-cleanup-autoloads ()
(goto-char (point-min))
(when (re-search-forward "^;;\\(;[^\n]*\\| no-byte-compile: t\\)\n" nil t)
(replace-match "" t t)))
(defun doom-cli-reload-core-autoloads (&optional force-p)
"Refreshes `doom-autoload-file', if necessary (or if FORCE-P is non-nil).
It scans and reads autoload cookies (;;;###autoload) in core/autoload/*.el,
modules/*/*/autoload.el and modules/*/*/autoload/*.el, and generates
`doom-autoload-file'.
Run this whenever your `doom!' block, or a module autoload file, is modified."
(require 'autoload)
(let* ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))
(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'.
;; 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)
case-fold-search ; reduce magic
autoload-timestamps ; reduce noise in generated files
;; Needed for `autoload-generate-file-autoloads'
(generated-autoload-load-name (file-name-sans-extension file))
(target-buffer (current-buffer))
(module (doom-module-from-path file))
(module-enabled-p (and (or (memq (car module) '(:core :private))
(doom-module-p (car module) (cdr module)))
(doom-file-cookie-p file "if" t))))
(save-excursion
(when module-enabled-p
(quiet! (autoload-generate-file-autoloads file target-buffer)))
(doom-autoloads--scan-autodefs
file target-buffer module module-enabled-p))))
;; Where we'll store the files we'll scan for autoloads. This should
;; contain *all* autoload files, even in disabled modules, so we can
;; scan those for autodefs. We start with the core libraries.
(targets (doom-glob doom-core-dir "autoload/*.el"))
;; A subset of `targets' in enabled modules
(active-targets (copy-sequence targets)))
(dolist (path (doom-module-load-path 'all-p))
(when-let* ((files (cons (doom-glob path "autoload.el")
(doom-files-in (doom-path path "autoload")
:match "\\.el$")))
(files (delq nil files)))
(appendq! targets files)
(when (or (doom-module-from-path path 'enabled-only)
(file-equal-p path doom-private-dir))
(appendq! active-targets files))))
(print! (start "Checking core autoloads file"))
(print-group!
(if (and (not force-p)
(file-exists-p doom-autoload-file)
(not (file-newer-than-file-p doom-emacs-dir doom-autoload-file))
(not (cl-loop for dir
in (append (doom-glob doom-private-dir "init.el*")
targets)
if (file-newer-than-file-p dir doom-autoload-file)
return t)))
(ignore
(print! (success "Skipping core autoloads, they are up-to-date"))
(doom-load-autoloads-file doom-autoload-file))
(if (doom--cli-delete-autoloads-file doom-autoload-file)
(print! (success "Deleted old %s") (filename doom-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
(print! (start "Regenerating core autoloads file"))
(print-group!
(with-temp-file doom-autoload-file
(doom--cli-generate-header 'doom-cli-reload-core-autoloads)
(save-excursion
(doom--cli-generate-autoloads active-targets)
(print! (success "Generated new autoloads.el")))
;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path'
(save-excursion
(doom--cli-expand-autoload-paths 'allow-internal-paths)
(print! (success "Expanded module autoload paths")))
;; Generates stub definitions for functions/macros defined in disabled
;; modules, so that you will never get a void-function when you use
;; them.
(save-excursion
(doom--cli-generate-autodefs targets (reverse active-targets))
(print! (success "Generated autodefs")))
;; Remove byte-compile-inhibiting file variables so we can byte-compile
;; the file, and autoload comments.
(doom--cli-cleanup-autoloads)
(print! (success "Cleaned up autoloads"))))
;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts)
(print! "> Byte-compiling %s..." (relpath doom-autoload-file))
(when (doom--cli-byte-compile-file doom-autoload-file)
(print-group!
(print! (success "Compiled %s") (relpath doom-autoload-file)))))
t)))
;;
;;; Package autoloads
(defun doom--generate-package-autoloads ()
"Concatenates package autoload files, let-binds `load-file-name' around
them,and remove unnecessary `provide' statements or blank links."
(dolist (pkg (hash-table-keys straight--build-cache))
(unless (member pkg doom-autoload-excluded-packages)
(let ((file (straight--autoloads-file pkg)))
(when (file-exists-p file)
(insert-file-contents file)
(save-excursion
(while (re-search-forward "\\(?:\\_<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."
(defun doom-autoloads--scan (files &optional literal)
(require 'autoload)
(print! (start "Checking package autoloads file"))
(print-group!
(if (and (not force-p)
(file-exists-p doom-package-autoload-file)
(not (file-newer-than-file-p package-user-dir doom-package-autoload-file))
(not (cl-loop for dir in (straight--directory-files (straight--build-dir))
if (cl-find-if
(lambda (dir)
(file-newer-than-file-p dir doom-package-autoload-file))
(doom-glob (straight--build-dir dir) "*.el"))
return t))
(not (cl-loop with doom-modules = (doom-modules)
for key being the hash-keys of doom-modules
for path = (doom-module-path (car key) (cdr key) "packages.el")
if (file-newer-than-file-p path doom-package-autoload-file)
return t)))
(ignore
(print! (success "Skipping package autoloads, they are up-to-date"))
(doom-load-autoloads-file doom-package-autoload-file))
(let (;; The following bindings are in `package-generate-autoloads'.
;; Presumably for a good reason, so I just copied them
(backup-inhibited t)
(version-control 'never)
(case-fold-search nil) ; reduce magic
(autoload-timestamps nil))
(if (doom--cli-delete-autoloads-file doom-package-autoload-file)
(print! (success "Deleted old %s") (filename doom-package-autoload-file))
(make-directory (file-name-directory doom-autoload-file) t))
(print! (start "Regenerating package autoloads file"))
(print-group!
(with-temp-file doom-package-autoload-file
(doom--cli-generate-header 'doom-cli-reload-package-autoloads)
(save-excursion
;; Cache important and expensive-to-initialize state here.
(doom--generate-var-cache)
(print! (success "Cached package state"))
;; Concatenate the autoloads of all installed packages.
(doom--generate-package-autoloads)
(print! (success "Package autoloads included")))
;; Replace autoload paths (only for module autoloads) with absolute
;; paths for faster resolution during load and simpler `load-path'
(save-excursion
(doom--cli-expand-autoload-paths)
(print! (success "Expanded module autoload paths")))
;; Remove `load-path' and `auto-mode-alist' modifications (most of them,
;; at least); they are cached later, so all those membership checks are
;; unnecessary overhead.
(doom--cleanup-package-autoloads)
(print! (success "Removed load-path/auto-mode-alist entries"))))
;; Byte compile it to give the file a chance to reveal errors (and buy us a
;; few marginal performance boosts)
(print! (start "Byte-compiling %s...") (relpath doom-package-autoload-file))
(when (doom--cli-byte-compile-file doom-package-autoload-file)
(print-group!
(print! (success "Compiled %s") (relpath doom-package-autoload-file))))))
t))
(let (autoloads)
(dolist (file
(seq-filter #'file-readable-p files)
(nreverse (delq nil autoloads)))
(with-temp-buffer
(print! (debug "- Scanning %s") (relpath file doom-emacs-dir))
(if literal
(insert-file-contents file)
(doom-autoloads--scan-file file))
(save-excursion
(let ((filestr (prin1-to-string file)))
(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 with the
;; file they came from.
(let ((ppss (save-excursion (syntax-ppss))))
(or (nth 3 ppss)
(nth 4 ppss)
(replace-match filestr t t))))))
(let ((load-file-name file)
(load-path
(append (list doom-private-dir)
doom-modules-dirs
load-path)))
(condition-case _
(while t
(push (doom-autoloads--cleanup-form (read (current-buffer))
(not literal))
autoloads))
(end-of-file)))))))

View File

@@ -2,7 +2,9 @@
(defcli! (compile c)
((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.
compile [TARGETS...]
@@ -11,8 +13,23 @@
Accepts :core and :private as special arguments, which target Doom's core files
and your private config files, respectively. To recompile your packages, use
'doom rebuild' instead."
(doom-cli-byte-compile targets recompile-p))
'doom build' instead."
(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 ()
"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)
(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)
(not (equal (file-name-extension path) "el"))
(member filename (list "packages.el" "doctor.el")))))
(string-prefix-p "flycheck_" filename)
(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.
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
byte-compiled. Otherwise, all enabled modules are byte-compiled, including Doom
core. It always ignores unit tests and files with `no-byte-compile' enabled.
@@ -47,138 +68,109 @@ Use `doom-clean-byte-compiled-files' or `make clean' to reverse
byte-compilation.
If RECOMPILE-P is non-nil, only recompile out-of-date files."
(let ((default-directory doom-emacs-dir)
(doom-modules (doom-modules))
(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
(auto-mode-alist auto-mode-alist)
(noninteractive t)
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* ((default-directory doom-emacs-dir)
(targets (nreverse (delete-dups targets)))
;; In case it is changed during compile-time
(auto-mode-alist auto-mode-alist)
kill-emacs-hook kill-buffer-query-functions)
(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
;; fully loaded. Which usually aren't so in an noninteractive session.
(let ((doom-interactive-mode 'byte-compile))
(doom-initialize)
(doom-initialize-packages)
(doom-initialize-core))
(let ((load-prefer-newer t)
(noninteractive t)
doom-interactive-p)
(doom-initialize 'force)
(quiet! (doom-initialize-packages))))
;;
(unless target-dirs
(push (doom-glob doom-emacs-dir "init.el") targets)
;; If no targets were supplied, then we use your module list.
(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))))))
(if (null targets)
(print! (info "No targets to %scompile" (if recompile-p "re" "")))
(print! (start "%scompiling your config...")
(if recompile-p "Re" "Byte-"))
;; Assemble el files we want to compile; taking into account that MODULES
;; may be a list of MODULE/SUBMODULE strings from the command line.
(appendq! targets
(doom-files-in target-dirs
:match "\\.el$"
:filter #'doom--byte-compile-ignore-file-p)))
(dolist (dir
(cl-remove-if-not #'file-directory-p targets)
(setq targets (cl-remove-if #'file-directory-p targets)))
(prependq! targets
(doom-files-in
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!
(require 'use-package)
(condition-case e
(let ((total-ok 0)
(total-fail 0)
(total-noop 0)
(use-package-defaults use-package-defaults)
(use-package-expand-minimally t)
kill-emacs-hook kill-buffer-query-functions)
;; Prevent packages from being loaded at compile time if they
;; don't meet their own predicates.
(push (list :no-require t
(lambda (_name args)
(or (when-let (pred (or (plist-get args :if)
(plist-get args :when)))
(not (eval pred t)))
(when-let (pred (plist-get args :unless))
(eval pred t)))))
use-package-defaults)
(unless recompile-p
(doom-clean-byte-compiled-files))
(dolist (target (delete-dups (delq nil targets)))
(cl-incf
(if (not (or (not recompile-p)
(let ((elc-file (byte-compile-dest-file target)))
(and (file-exists-p elc-file)
(file-newer-than-file-p target elc-file)))))
total-noop
(pcase (if (doom-file-cookie-p target "if" t)
(byte-compile-file target)
'no-byte-compile)
(`no-byte-compile
(print! (info "Ignored %s") (relpath target))
total-noop)
(`nil
(print! (error "Failed to compile %s") (relpath target))
total-fail)
(_
(print! (success "Compiled %s") (relpath target))
(load target t t)
total-ok)))))
(print! (class (if (= total-fail 0) 'success 'error)
"%s %d/%d file(s) (%d ignored)")
(if recompile-p "Recompiled" "Compiled")
total-ok (- (length targets) total-noop)
total-noop)
t)
((debug error)
(print! (error "\nThere were breaking errors.\n\n%s")
"Reverting changes...")
(signal 'doom-error (list 'byte-compile e)))))))
(print-group!
(require 'use-package)
(condition-case-unless-debug e
(let* ((total-ok 0)
(total-fail 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-expand-minimally t)
(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
;; don't meet their own predicates.
(push (list :no-require t
(lambda (_name args)
(or (when-let (pred (or (plist-get args :if)
(plist-get args :when)))
(not (eval pred t)))
(when-let (pred (plist-get args :unless))
(eval pred t)))))
use-package-defaults)
(dolist (module-files modules)
(cl-incf i)
(dolist (target (cdr module-files))
(let ((elc-file (byte-compile-dest-file target)))
(cl-incf
(if (and recompile-p (not (file-newer-than-file-p target elc-file)))
total-noop
(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)
(quiet! (byte-compile-file target))))
(`no-byte-compile
(print! (debug "(% 3d/%d) Ignored %s")
i total-modules (relpath target))
total-noop)
(`nil
(print! (error "(% 3d/%d) Failed to compile %s")
i total-modules (relpath target))
total-fail)
(_ total-ok)))))))
(print! (class (if (= total-fail 0) 'success 'warn)
"%s %d/%d file(s) (%d ignored)")
(if recompile-p "Recompiled" "Byte-compiled")
total-ok total-files
total-noop)
(= total-fail 0))
((debug error)
(print! (error "There were breaking errors.\n\n%s")
"Reverting changes...")
(signal 'doom-error (list 'byte-compile e))))))))
(defun doom-clean-byte-compiled-files ()
"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-group!
(cl-loop with default-directory = doom-emacs-dir
with success = nil
with success = 0
with esc = (if doom-debug-p "" "\033[1A")
for path
in (append (doom-glob doom-emacs-dir "*.elc")
(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))
if (file-exists-p path)
do (delete-file path)
and do (print! (success "Deleted %s") (relpath path))
and do (setq success t)
and do (print! (success "\033[KDeleted %s%s") (relpath path) esc)
and do (cl-incf success)
finally do
(print! (if success
(success "All elc files deleted")
(info "No elc files to clean"))))
(print! (if (> success 0)
(success "\033[K%d elc files deleted" success)
(info "\033[KNo elc files to clean"))))
t))

View File

@@ -1,10 +1,12 @@
;;; core/cli/debug.el -*- lexical-binding: t; -*-
(load! "autoload/debug" doom-core-dir)
;;
;;; Commands
(defcli! info
((format ["--json" "--md" "--lisp"] "What format to dump info into"))
((format ["--json" "--md" "--lisp"] "What format to dump info into"))
"Output system info in markdown for bug reports."
(pcase format
("--json"
@@ -24,6 +26,5 @@
(defcli! (version v) ()
"Show version information for Doom & Emacs."
:bare t
(doom/version)
nil)

View File

@@ -48,8 +48,8 @@ in."
;; REVIEW Refactor me
(print! (start "Checking your Emacs version..."))
(when EMACS27+
(warn! "Emacs %s detected. Emacs HEAD is unstable and may cause errors."
(when EMACS28+
(warn! "Emacs %s detected. Doom doesn't support Emacs 28/HEAD. It is unstable and may cause errors."
emacs-version))
(print! (start "Checking for Emacs config conflicts..."))
@@ -79,9 +79,8 @@ in."
(print! (start "Checking Doom Emacs..."))
(condition-case-unless-debug ex
(print-group!
(let ((doom-interactive-mode 'doctor))
(let ((doom-interactive-p 'doctor))
(doom-initialize 'force)
(doom-initialize-core)
(doom-initialize-modules))
(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 (> size 1048576) ; larger than 1mb
(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)"))))
(unless (executable-find "rg")
(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")
(unless (executable-find "rg")
(warn! "Couldn't find the `rg' binary either; project file searches will be even slower")))
(warn! "Couldn't find the `fd' binary; project file searches will be slightly slower"))
(require 'projectile)
(when (projectile-project-root "~")
@@ -123,11 +123,8 @@ in."
"both is rarely intentional; you should one or the other."))
;; Check for fonts
(if (not (fboundp 'find-font))
(progn
(warn! "Warning: unable to detect font")
(explain! "The `find-font' function is missing. This could indicate the incorrect "
"version of Emacs is being used!"))
(if (not (executable-find "fc-list"))
(warn! "Warning: unable to detect fonts because fontconfig isn't installed")
;; all-the-icons fonts
(when (and (pcase system-type
(`gnu/linux (concat (or (getenv "XDG_DATA_HOME")
@@ -136,14 +133,21 @@ in."
(`darwin "~/Library/Fonts/"))
(require 'all-the-icons nil t))
(with-temp-buffer
(insert (cdr (doom-call-process "fc-list")))
(dolist (font all-the-icons-font-names)
(if (save-excursion (re-search-backward font nil t))
(success! "Found font %s" 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"
"This could also mean you've installed them in non-standard locations, in which "
"case feel free to ignore this warning.")))))))
(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)
(if (save-excursion (re-search-backward font nil t))
(success! "Found font %s" font)
(print! (warn "Warning: couldn't find %S font") font)))
(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 "
"case feel free to ignore this warning.")))))))))
(print! (start "Checking for stale elc files in your DOOMDIR..."))
(when (file-directory-p doom-private-dir)
@@ -161,15 +165,17 @@ in."
(condition-case-unless-debug ex
(let ((doctor-file (doom-module-path (car key) (cdr key) "doctor.el"))
(packages-file (doom-module-path (car key) (cdr key) "packages.el")))
(cl-loop for name in (let (doom-packages
(cl-loop with doom-output-indent = 6
for name in (let (doom-packages
doom-disabled-packages)
(load packages-file 'noerror 'nomessage)
(mapcar #'car doom-packages))
unless (or (doom-package-get name :disable)
(eval (doom-package-get name :ignore))
(plist-member (doom-package-get name :recipe) :local-repo)
(doom-package-built-in-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))
(load doctor-file 'noerror 'nomessage)))
(file-missing (error! "%s" (error-message-string ex)))

View File

@@ -1,26 +1,28 @@
;;; core/cli/env.el -*- lexical-binding: t; -*-
(defcli! env
((clear-p ["-c" "--clear"] "Clear and delete your envvar file")
(outputfile ["-o" PATH]
"Generate the envvar file at PATH. Note that envvar files that aren't in
`doom-env-file' won't be loaded automatically at startup. You will need to
load them manually from your private config with the `doom-load-envvars-file'
((allow ["-a" "--allow" regexp] "An envvar whitelist regexp")
(reject ["-r" "--reject" regexp] "An envvar blacklist regexp")
(clear-p ["-c" "--clear"] "Clear and delete your envvar file")
(outputfile ["-o" path]
"Generate the envvar file at PATH. Envvar files that aren't in
`doom-env-file' won't be loaded automatically at startup. You will need to load
them manually from your private config with the `doom-load-envvars-file'
function."))
"Creates or regenerates your envvars file.
The envvars file is created by scraping your (interactive) shell environment
into newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env'
(or '$SHELL -c set' on windows). Doom loads this file at startup (if it exists)
to ensure Emacs mirrors your shell environment (particularly to ensure PATH and
The envvars file is created by scraping the current shell environment into
newline-delimited KEY=VALUE pairs. Typically by running '$SHELL -ic env' (or
'$SHELL -c set' on windows). Doom loads this file at startup (if it exists) to
ensure Emacs mirrors your shell environment (particularly to ensure PATH and
SHELL are correctly set).
This is useful in cases where you cannot guarantee that Emacs (or the daemon)
will be launched from the correct environment (e.g. on MacOS or through certain
app launchers on Linux).
This file is automatically regenerated when you run this command or 'doom
refresh'. However, 'doom refresh' will only regenerate this file if it exists.
This file is automatically regenerated when you run this command or 'doom sync'.
However, 'doom sync' will only regenerate this file if it exists.
Why this over exec-path-from-shell?
@@ -36,96 +38,91 @@ Why this over exec-path-from-shell?
I'd rather it inherit your shell environment /correctly/ (and /completely/)
or not at all. It frontloads the debugging process rather than hiding it
until it you least want to deal with it."
until you least want to deal with it."
(let ((env-file (expand-file-name (or outputfile doom-env-file))))
(cond (clear-p
(unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared"
(path env-file)))
(delete-file env-file)
(print! (success "Successfully deleted %S")
(path env-file)))
(args
(user-error "I don't understand 'doom env %s'"
(string-join args " ")))
((doom-cli-reload-env-file 'force env-file)))))
(if (null clear-p)
(doom-cli-reload-env-file
'force env-file (list allow) (list reject))
(unless (file-exists-p env-file)
(user-error! "%S does not exist to be cleared"
(path env-file)))
(delete-file env-file)
(print! (success "Successfully deleted %S")
(path env-file)))))
;;
;; Helpers
(defvar doom-env-ignored-vars
(defvar doom-env-blacklist
'("^DBUS_SESSION_BUS_ADDRESS$"
"^GPG_AGENT_INFO$"
"^GPG_TTY$"
"^HOME$"
"^PS1$"
"^PWD$"
"^R?PROMPT$"
"^SSH_AGENT_PID$"
"^SSH_AUTH_SOCK$"
"^TERM$"
"^GPG_AGENT_INFO$" "^\\(SSH\\|GPG\\)_TTY$"
"^SSH_\\(AUTH_SOCK\\|AGENT_PID\\)$"
"^HOME$" "^PWD$" "^PS1$" "^R?PROMPT$" "^TERM$"
;; Doom envvars
"^DEBUG$"
"^INSECURE$"
"^YES$"
"^__")
"^DEBUG$" "^INSECURE$" "^YES$" "^__")
"Environment variables to not save in `doom-env-file'.
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)
(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).
This scrapes the variables from your shell environment by running
`doom-env-executable' through `shell-file-name' with `doom-env-switches'. By
default, on Linux, this is '$SHELL -ic /usr/bin/env'. Variables in
`doom-env-ignored-vars' are removed."
(let ((env-file (if env-file
(expand-file-name env-file)
doom-env-file)))
(let ((env-file (if env-file (expand-file-name env-file) doom-env-file))
(process-environment doom--initial-process-environment))
(when (or force-p (not (file-exists-p env-file)))
(with-temp-file env-file
(setq-local coding-system-for-write 'utf-8)
(print! (start "%s envvars file at %S")
(if (file-exists-p env-file)
"Regenerating"
"Generating")
(path env-file))
(let ((process-environment doom--initial-process-environment))
(print! (info "Scraping shell environment"))
(print-group!
(when doom-interactive-mode
(user-error "'doom env' must be run on the command line, not an interactive session"))
(goto-char (point-min))
(insert
(concat
"# -*- mode: dotenv -*-\n"
(format "# Generated from a %s shell environent\n" shell-file-name)
"# ---------------------------------------------------------------------------\n"
"# This file was auto-generated by `doom env'. It contains a list of environment\n"
"# variables scraped from your default shell (excluding variables blacklisted\n"
"# in doom-env-ignored-vars).\n"
"#\n"
(if (file-equal-p env-file doom-env-file)
(concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n"
"# run 'doom refresh'. To create a safe-to-edit envvar file use:\n#\n"
"# doom env -o ~/.doom.d/myenv\n#\n"
"# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n")
(concat "# This file is safe to edit by hand, but needs to be loaded manually with:\n#\n"
"# (doom-load-envvars-file \"path/to/this/file\")\n#\n"
"# Use 'doom env -o path/to/this/file' to regenerate it."))
"# ---------------------------------------------------------------------------\n\n"))
;; We assume that this noninteractive session was spawned from the
;; user's interactive shell, therefore we just dump
;; `process-environment' to a file.
(dolist (env process-environment)
(if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "=")))
doom-env-ignored-vars)
(print! (info "Ignoring %s") env)
(insert env "\n")))
(print! (success "Successfully generated %S")
(path env-file))
t))))))
(print-group!
(when doom-interactive-p
(user-error "'doom env' must be run on the command line, not an interactive session"))
(goto-char (point-min))
(insert
(concat
"# -*- mode: sh -*-\n"
"# ---------------------------------------------------------------------------\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"
"# in doom-env-ignored-vars).\n"
"#\n"
(if (file-equal-p env-file doom-env-file)
(concat "# It is NOT safe to edit this file. Changes will be overwritten next time you\n"
"# run 'doom sync'. To create a safe-to-edit envvar file use:\n#\n"
"# doom env -o ~/.doom.d/myenv\n#\n"
"# And load it with (doom-load-envvars-file \"~/.doom.d/myenv\").\n")
(concat "# This file is safe to edit by hand, but 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"
"# Use 'doom env -o path/to/this/file' to regenerate it."))
"# ---------------------------------------------------------------------------\n\0\n"))
;; We assume that this noninteractive session was spawned from the
;; user's interactive shell, therefore we just dump
;; `process-environment' to a file.
(dolist (env process-environment)
(if (cl-find-if (doom-rpartial #'string-match-p (car (split-string env "=")))
(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 "Whitelisted %s") env)
(insert env "\0\n"))
(insert env "\0\n")))
(print! (success "Successfully generated %S")
(path env-file))
t)))))

View File

@@ -4,8 +4,7 @@
((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')")
(noinstall-p ["--no-install"] "Don't auto-install packages")
(nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts")
&rest _args)
(nofonts-p ["--no-fonts"] "Don't install (or prompt to install) all-the-icons fonts"))
"Installs and sets up Doom Emacs for the first time.
This command does the following:
@@ -24,7 +23,6 @@ DOOMDIR environment variable. e.g.
doom -p ~/.config/doom install
DOOMDIR=~/.config/doom doom install"
:bare t
(print! (green "Installing Doom Emacs!\n"))
(let ((default-directory (doom-path "~")))
;; Create `doom-private-dir'
@@ -37,34 +35,20 @@ DOOMDIR environment variable. e.g.
;; Create init.el, config.el & packages.el
(mapc (lambda (file)
(cl-destructuring-bind (filename . fn) file
(cl-destructuring-bind (filename . template) file
(if (file-exists-p! filename doom-private-dir)
(print! (warn "%s already exists, skipping") filename)
(print! (info "Creating %s%s") (relpath doom-private-dir) filename)
(with-temp-file (doom-path doom-private-dir filename)
(funcall fn))
(insert-file-contents template))
(print! (success "Done!")))))
'(("init.el" .
(lambda ()
(insert-file-contents (doom-path doom-emacs-dir "init.example.el"))))
("config.el" .
(lambda ()
(insert! ";;; %sconfig.el -*- lexical-binding: t; -*-\n\n"
";; Place your private configuration here\n"
((relpath doom-private-dir)))))
("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))))))))
`(("init.el" . ,(doom-path doom-emacs-dir "init.example.el"))
("config.el" . ,(doom-path doom-core-dir "templates/config.example.el"))
("packages.el" . ,(doom-path doom-core-dir "templates/packages.example.el")))))
;; In case no init.el was present the first time `doom-initialize-modules' was
;; called in core.el (e.g. on first install)
(doom-initialize 'force 'noerror)
(doom-initialize-modules)
(doom-initialize-modules 'force 'no-config)
;; Ask if user would like an envvar file generated
(if noenv-p
@@ -72,7 +56,7 @@ DOOMDIR environment variable. e.g.
(if (file-exists-p doom-env-file)
(print! (info "Envvar file already exists, skipping"))
(when (or doom-auto-accept
(y-or-n-p "Generate an env file? (see `doom help env` for details)"))
(y-or-n-p "Generate an envvar file? (see `doom help env` for details)"))
(doom-cli-reload-env-file 'force-p))))
;; Install Doom packages
@@ -82,21 +66,29 @@ DOOMDIR environment variable. e.g.
(doom-cli-packages-install))
(print! "Regenerating autoloads files")
(doom-cli-reload-autoloads nil 'force-p)
(doom-autoloads-reload)
(if nofonts-p
(print! (warn "Not installing fonts, as requested"))
(when (or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns)
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
(cond (nofonts-p)
(IS-WINDOWS
(print! (warn "Doom cannot install all-the-icons' fonts on Windows!\n"))
(print-group!
(print!
(concat "You'll have to do so manually:\n\n"
" 1. Launch Doom Emacs\n"
" 2. Execute 'M-x all-the-icons-install-fonts' to download the fonts\n"
" 3. Open the download location in windows explorer\n"
" 4. Open each font file to install them"))))
((or doom-auto-accept
(y-or-n-p "Download and install all-the-icon's fonts?"))
(require 'all-the-icons)
(let ((window-system (cond (IS-MAC 'ns)
(IS-LINUX 'x))))
(all-the-icons-install-fonts 'yes))))
(when (file-exists-p "~/.emacs")
(print! (warn "A ~/.emacs file was detected. This conflicts with Doom and should be deleted!")))
(print! (success "\nFinished! Doom is ready to go!\n"))
(with-temp-buffer
(doom-template-insert "QUICKSTART_INTRO")
(print! (buffer-string)))))
(insert-file-contents (doom-glob doom-core-dir "templates/QUICKSTART_INTRO"))
(print! "%s" (buffer-string)))))

View File

@@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*-
;;; core/cli/packages.el
(defcli! (update u) ()
(defcli! (update u)
((discard-p ["--discard"] "All local changes to packages are discarded"))
"Updates packages.
This works by fetching all installed package repos and checking the distance
@@ -10,10 +11,10 @@ between HEAD and FETCH_HEAD. This can take a while.
This excludes packages whose `package!' declaration contains a non-nil :freeze
or :ignore property."
(straight-check-all)
(doom-cli-reload-core-autoloads)
(when (doom-cli-packages-update)
(doom-cli-reload-package-autoloads 'force-p))
t)
(let ((doom-auto-discard discard-p))
(when (doom-cli-packages-update)
(doom-autoloads-reload))
t))
(defcli! (build b)
((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
Emacs (as byte-code is generally not forward-compatible)."
(when (doom-cli-packages-build (not rebuild-p))
(doom-cli-reload-package-autoloads 'force-p))
(doom-autoloads-reload))
t)
(defcli! (purge p)
@@ -46,7 +47,7 @@ list remains lean."
(not norepos-p)
(not nobuilds-p)
regraft-p)
(doom-cli-reload-package-autoloads 'force-p))
(doom-autoloads-reload))
t)
;; (defcli! rollback () ; TODO doom rollback
@@ -57,144 +58,248 @@ list remains lean."
;;
;;; Library
(defun doom--same-commit-p (abbrev-ref ref)
(and (stringp abbrev-ref)
(stringp ref)
(string-match-p (concat "^" (regexp-quote abbrev-ref))
ref)))
(defun doom--abbrev-commit (commit &optional full)
(if full commit (substring commit 0 7)))
(defun doom--commit-log-between (start-ref end-ref)
(and (straight--call
"git" "log" "--oneline" "--no-merges"
"-n" "25" end-ref (concat "^" (regexp-quote start-ref)))
(straight--process-get-output)))
(defun doom--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 ()
"Installs missing packages.
This function will install any primary package (i.e. a package with a `package!'
declaration) or dependency thereof that hasn't already been."
(print! (start "Installing & building packages..."))
(print-group!
(let ((n 0))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight--with-plist (gethash package straight--recipe-cache)
(local-repo)
(let ((existed-p (file-directory-p (straight--repos-dir package))))
(condition-case-unless-debug e
(and (straight-use-package (intern package) nil nil (make-string (1- (or doom-format-indent 1)) 32))
(not existed-p)
(file-directory-p (straight--repos-dir package))
(cl-incf n))
(error
(signal 'doom-package-error
(list e (straight--process-get-output))))))))
(if (= n 0)
(ignore (print! (success "No packages need to be installed")))
(print! (success "Installed & built %d packages") n)
t))))
(doom-initialize-packages)
(print! (start "Installing packages..."))
(let ((pinned (doom-package-pinned-list)))
(print-group!
(if-let (built
(doom--with-package-recipes (doom-package-recipe-list)
(recipe package type local-repo)
(unless (file-directory-p (straight--repos-dir local-repo))
(doom--cli-recipes-update))
(condition-case-unless-debug e
(let ((straight-use-package-pre-build-functions
(cons (lambda (pkg &rest _)
(when-let (commit (cdr (assoc pkg pinned)))
(print! (info "Checked out %s") commit)))
straight-use-package-pre-build-functions)))
(straight-use-package (intern package)))
(error
(signal 'doom-package-error (list package e))))))
(print! (success "Installed %d packages")
(length built))
(print! (info "No packages need to be installed"))
nil))))
(defun doom-cli-packages-build (&optional force-p)
"(Re)build all packages."
(doom-initialize-packages)
(print! (start "(Re)building %spackages...") (if force-p "all " ""))
(print-group!
(let ((n 0))
(if force-p
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(dolist (package (hash-table-keys straight--recipe-cache))
(straight-use-package
(intern package) nil (lambda (_) (cl-incf n) nil)
(make-string (1- (or doom-format-indent 1)) 32))))
(dolist (recipe (hash-table-values straight--recipe-cache))
(straight--with-plist recipe (package local-repo no-build)
(unless (or no-build (null local-repo))
;; REVIEW We do these modification checks manually because
;; Straight's checks seem to miss stale elc files. Need
;; more tests to confirm this.
(when (or (ignore-errors
(gethash package straight--packages-to-rebuild))
(gethash package straight--cached-package-modifications)
(not (file-directory-p (straight--build-dir package)))
(cl-loop for file
in (doom-files-in (straight--build-dir package)
:match "\\.el$"
:full t)
for elc-file = (byte-compile-dest-file file)
if (and (file-exists-p elc-file)
(file-newer-than-file-p file elc-file))
return t))
(let ((straight-use-package-pre-build-functions
straight-use-package-pre-build-functions))
(add-hook 'straight-use-package-pre-build-functions
(lambda (&rest _) (cl-incf n)))
(let ((straight--packages-to-rebuild :all)
(straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(straight-use-package
(intern package) nil nil
(make-string (or doom-format-indent 0) 32)))
(straight--byte-compile-package recipe)
(dolist (dep (straight--get-dependencies package))
(when-let (recipe (gethash dep straight--recipe-cache))
(straight--byte-compile-package recipe)))))))))
(if (= n 0)
(ignore (print! (success "No packages need rebuilding")))
(doom--finalize-straight)
(print! (success "Rebuilt %d package(s)" n))
t))))
(let ((straight-check-for-modifications
(when (file-directory-p (straight--modified-dir))
'(find-when-checking)))
(straight--allow-find
(and straight-check-for-modifications
(executable-find straight-find-executable)
t))
(straight--packages-not-to-rebuild
(or straight--packages-not-to-rebuild (make-hash-table :test #'equal)))
(straight--packages-to-rebuild
(or (if force-p :all straight--packages-to-rebuild)
(make-hash-table :test #'equal)))
(recipes (doom-package-recipe-list)))
(unless force-p
(straight--make-build-cache-available))
(if-let (built
(doom--with-package-recipes recipes (package local-repo recipe)
(unless force-p
;; Ensure packages with outdated files/bytecode are rebuilt
(let ((build-dir (straight--build-dir package))
(repo-dir (straight--repos-dir local-repo)))
(and (or (file-newer-than-file-p repo-dir build-dir)
(file-exists-p (straight--modified-dir (or local-repo package)))
;; Doesn't make sense to compare el and elc files
;; when the former isn't a symlink to their source.
(when straight-use-symlinks
(cl-loop for file
in (doom-files-in build-dir :match "\\.el$" :full t)
for elc-file = (byte-compile-dest-file file)
if (and (file-exists-p elc-file)
(file-newer-than-file-p file elc-file))
return t)))
(not (plist-get recipe :no-build))
(puthash package t straight--packages-to-rebuild))))
(straight-use-package (intern package))))
(print! (success "Rebuilt %d package(s)") (length built))
(print! (success "No packages need rebuilding"))
nil))))
(defun doom-cli-packages-update ()
"Updates packages."
(print! (start "Updating packages (this may take a while)..."))
;; TODO Refactor me
(let ((straight--repos-dir (straight--repos-dir))
(straight--packages-to-rebuild (make-hash-table :test #'equal))
(total (hash-table-count straight--repo-cache))
(i 1)
errors)
(print-group!
(dolist (recipe (hash-table-values straight--repo-cache))
(straight--with-plist recipe (package type local-repo)
(doom-initialize-packages)
(doom--barf-if-incomplete-packages)
(let* ((repo-dir (straight--repos-dir))
(pinned (doom-package-pinned-list))
(recipes (doom-package-recipe-list))
(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)
(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!
(unless (straight--repository-is-available-p recipe)
(print! (error "(%d/%d) Couldn't find local repo for %s") i total package)
(cl-return))
(when (gethash local-repo repos-to-rebuild)
(puthash package t packages-to-rebuild)
(print! (success "(%d/%d) %s was updated indirectly (with %s)") i total package local-repo)
(cl-return))
(let ((default-directory (straight--repos-dir local-repo)))
(unless (file-in-directory-p default-directory repo-dir)
(print! (warn "(%d/%d) Skipping %s because it is local") i total package)
(cl-return))
(when (eq type 'git)
(unless (file-exists-p ".git")
(error "%S is not a valid repository" package)))
(condition-case-unless-debug e
(let ((default-directory (straight--repos-dir local-repo)))
(if (not (file-in-directory-p default-directory straight--repos-dir))
(print! (warn "[%d/%d] Skipping %s because it is local")
i total package)
(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)))
(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)
(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)
(straight--call "git" "log" "--oneline" newcommit (concat "^" commit))
(print-group!
(print! "%s" (straight--process-get-output))))))
(print! (success "(%d/%d) %s updated (%s -> %s)") i total package
(substring commit 0 7)
(substring newcommit 0 7))))))))
(cl-incf i))
(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!
(straight-use-package (intern package) nil 'no-build))
(prog1 (file-directory-p repo)
(or (not (eq type 'git))
(setq output (doom--commit-log-between ref target-ref)))))))
(progn
(print! (warn "\033[K(%d/%d) Failed to fetch %s")
i total local-repo)
(unless (string-empty-p output)
(print-group! (print! (info "%s" output))))
(cl-return)))
(puthash local-repo t repos-to-rebuild)
(puthash package t packages-to-rebuild)
(unless (string-empty-p output)
(print! (start "\033[K(%d/%d) Updating %s...") i total local-repo)
(print-group! (print! "%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
(signal 'user-error (error-message-string e)))
(error
(print! (warn "(%d/%d) Encountered error with %s" i total package))
(print-group!
(print! (error "%s" e))
(print-group! (print! (info "%s" (straight--process-get-output)))))
(push package errors)))))
(signal 'doom-package-error (list package e)))))))
(print-group!
(princ "\033[K")
(when errors
(print! (error "There were %d errors, the offending packages are: %s")
(length errors) (string-join errors ", ")))
(if (hash-table-empty-p straight--packages-to-rebuild)
(ignore
(print! (success "All %d packages are up-to-date")
(hash-table-count straight--repo-cache)))
(let ((count (hash-table-count straight--packages-to-rebuild))
(packages (hash-table-keys straight--packages-to-rebuild)))
(sort packages #'string-lessp)
(doom--finalize-straight)
(doom-cli-packages-build)
(print! (success "Updated %d package(s)") count))
(if (hash-table-empty-p packages-to-rebuild)
(ignore (print! (success "All %d packages are up-to-date") total))
(straight--transaction-finalize)
(let ((default-directory (straight--build-dir)))
(mapc (doom-rpartial #'delete-directory 'recursive)
(hash-table-keys packages-to-rebuild)))
(print! (success "Updated %d package(s)")
(hash-table-count packages-to-rebuild))
(doom-cli-packages-build)
t))))
@@ -209,30 +314,38 @@ declaration) or dependency thereof that hasn't already been."
(defun doom--cli-packages-purge-builds (builds)
(if (not builds)
(progn (print! (info "No builds to purge"))
0)
(length
(delq nil (mapcar #'doom--cli-packages-purge-build builds)))))
(prog1 0
(print! (info "No builds to purge")))
(print! (start "Purging straight builds..." (length builds)))
(print-group!
(length
(delq nil (mapcar #'doom--cli-packages-purge-build builds))))))
(defun doom--cli-packages-regraft-repo (repo)
(cl-defun doom--cli-packages-regraft-repo (repo)
(let ((default-directory (straight--repos-dir repo)))
(if (not (file-directory-p ".git"))
(ignore (print! (warn "\033[Krepos/%s is not a git repo, skipping" repo)))
(let ((before-size (doom-directory-size default-directory)))
(straight--call "git" "reset" "--hard")
(straight--call "git" "clean" "-ffd")
(if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
(print! (info "\033[Krepos/%s is already compact\033[1A" repo))
(straight--call "git" "gc")
(print! (success "\033[KRegrafted repos/%s (from %0.1fKB to %0.1fKB)")
repo before-size (doom-directory-size default-directory))
(print-group! (print! "%s" (straight--process-get-output)))))
(unless (file-directory-p ".git")
(print! (warn "\033[Krepos/%s is not a git repo, skipping" repo))
(cl-return))
(unless (file-in-directory-p default-directory straight-base-dir)
(print! (warn "\033[KSkipping repos/%s because it is local" repo))
(cl-return))
(let ((before-size (doom-directory-size default-directory)))
(straight--call "git" "reset" "--hard")
(straight--call "git" "clean" "-ffd")
(if (not (car (straight--call "git" "replace" "--graft" "HEAD")))
(print! (info "\033[Krepos/%s is already compact\033[1A" repo))
(straight--call "git" "reflog" "expire" "--expire=all" "--all")
(straight--call "git" "gc" "--prune=now")
(print! (success "\033[KRegrafted repos/%s (from %0.1fKB to %0.1fKB)")
repo before-size (doom-directory-size default-directory))
(print-group! (print! "%s" (straight--process-get-output))))
t)))
(defun doom--cli-packages-regraft-repos (repos)
(if (not repos)
(progn (print! (info "No repos to regraft"))
0)
(prog1 0
(print! (info "No repos to regraft")))
(print! (start "Regrafting %d repos..." (length repos)))
(let ((before-size (doom-directory-size (straight--repos-dir))))
(print-group!
(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)
(let ((repo-dir (straight--repos-dir repo)))
(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)
(ignore (print! (error "Failed to purge 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)
(if (not repos)
(progn (print! (info "No repos to purge"))
0)
(length
(delq nil (mapcar #'doom--cli-packages-purge-repo repos)))))
(prog1 0
(print! (info "No repos to purge")))
(print! (start "Purging straight repositories..."))
(print-group!
(length
(delq nil (mapcar #'doom--cli-packages-purge-repo repos))))))
(defun doom--cli-packages-purge-elpa ()
(unless (bound-and-true-p package--initialized)
(package-initialize))
(let ((packages (cl-loop for (package desc) in package-alist
for dir = (package-desc-dir desc)
if (file-in-directory-p dir package-user-dir)
collect (cons package dir))))
(if (not package-alist)
(progn (print! (info "No ELPA packages to purge"))
0)
(mapc (doom-rpartial #'delete-directory 'recursive)
(mapcar #'cdr packages))
(length packages))))
(require 'core-packages)
(let ((dirs (doom-files-in package-user-dir :type t :depth 0)))
(if (not dirs)
(prog1 0
(print! (info "No ELPA packages to purge")))
(print! (start "Purging ELPA packages..."))
(dolist (path dirs (length dirs))
(condition-case e
(print-group!
(if (file-directory-p path)
(delete-directory path 'recursive)
(delete-file path))
(print! (success "Deleted %s") (filename path)))
(error
(print! (error "Failed to delete %s because: %s")
(filename path)
e)))))))
(defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p)
"Auto-removes orphaned packages and repos.
@@ -282,36 +401,35 @@ a `package!' declaration) or isn't depended on by another primary package.
If BUILDS-P, include straight package builds.
If REPOS-P, include straight repos.
If ELPA-P, include packages installed with package.el (M-x package-install)."
(print! (start "Searching for orphaned packages to purge (for the emperor)..."))
(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)
(let ((rdirs (straight--directory-files (straight--repos-dir) nil nil 'sort))
(bdirs (straight--directory-files (straight--build-dir) nil nil 'sort)))
(list (cl-remove-if (doom-rpartial #'gethash straight--profile-cache)
bdirs)
(cl-remove-if (doom-rpartial #'straight--checkhash straight--repo-cache)
rdirs)
(cl-remove-if-not (doom-rpartial #'straight--checkhash straight--repo-cache)
rdirs)))
(let (success)
(print-group!
(if (not builds-p)
(print! (info "Skipping builds"))
(and (/= 0 (doom--cli-packages-purge-builds builds-to-purge))
(setq success t)
(straight-prune-build-cache)))
(if (not elpa-p)
(print! (info "Skipping elpa packages"))
(and (/= 0 (doom--cli-packages-purge-elpa))
(setq success t)))
(if (not repos-p)
(print! (info "Skipping repos"))
(and (/= 0 (doom--cli-packages-purge-repos repos-to-purge))
(setq success t)))
(if (not regraft-repos-p)
(print! (info "Skipping regrafting"))
(print! (start "Regrafting %d repos..." (length repos-to-regraft)))
(and (doom--cli-packages-regraft-repos repos-to-regraft)
(setq success t)))
(when success
(doom--finalize-straight)
t)))))
(let ((rdirs
(and (or repos-p regraft-repos-p)
(straight--directory-files (straight--repos-dir) nil nil 'sort))))
(list (when builds-p
(seq-remove (doom-rpartial #'gethash straight--profile-cache)
(straight--directory-files (straight--build-dir) nil nil 'sort)))
(when repos-p
(seq-remove (doom-rpartial #'straight--checkhash straight--repo-cache)
rdirs))
(when regraft-repos-p
(seq-filter (doom-rpartial #'straight--checkhash straight--repo-cache)
rdirs))))
(print-group!
(delq
nil (list
(if (not builds-p)
(ignore (print! (info "Skipping builds")))
(and (/= 0 (doom--cli-packages-purge-builds builds-to-purge))
(straight-prune-build-cache)))
(if (not elpa-p)
(ignore (print! (info "Skipping elpa packages")))
(/= 0 (doom--cli-packages-purge-elpa)))
(if (not repos-p)
(ignore (print! (info "Skipping repos")))
(/= 0 (doom--cli-packages-purge-repos repos-to-purge)))
(if (not regraft-repos-p)
(ignore (print! (info "Skipping regrafting")))
(doom--cli-packages-regraft-repos repos-to-regraft)))))))

View File

@@ -50,9 +50,9 @@
(require 'core-cli)
(doom-initialize 'force 'noerror)
(doom-initialize-modules)
(doom-cli-reload-core-autoloads 'force)
(doom-cli-reload-core-autoloads)
(when (doom-cli-packages-install)
(doom-cli-reload-package-autoloads 'force)))))
(doom-cli-reload-package-autoloads)))))
(unless (zerop status)
(error "Failed to bootstrap unit tests"))))
(with-temp-buffer

View File

@@ -1,7 +1,8 @@
;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
(defcli! (upgrade up)
((force-p ["-f" "--force"]))
((force-p ["-f" "--force"] "Discard local changes to Doom and packages, and upgrade anyway")
(packages-only-p ["-p" "--packages"] "Only upgrade packages, not Doom"))
"Updates Doom and packages.
This requires that ~/.emacs.d is a git repo, and is the equivalent of the
@@ -10,18 +11,22 @@ following shell commands:
cd ~/.emacs.d
git pull --rebase
bin/doom clean
bin/doom refresh
bin/doom sync
bin/doom update"
:bare t
(if (delq
nil (list
(doom-cli-upgrade doom-auto-accept force-p)
(doom-cli-execute "refresh")
(when (doom-cli-packages-update)
(doom-cli-reload-package-autoloads 'force)
t)))
(print! (success "Done! Restart Emacs for changes to take effect."))
(print! "Nothing to do. Doom is up-to-date!")))
(let ((doom-auto-discard force-p))
(cond
(packages-only-p
(doom-cli-execute "sync" '("-u"))
(print! (success "Finished upgrading Doom Emacs")))
((doom-cli-upgrade doom-auto-accept doom-auto-discard)
;; Reload Doom's CLI & libraries, in case there were any upstream changes.
;; 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)
(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)))
(unless branch
(error! (if (file-exists-p! ".git" doom-emacs-dir)
@@ -103,11 +114,10 @@ following shell commands:
(print! (start "Upgrading Doom Emacs..."))
(print-group!
(doom-clean-byte-compiled-files)
(if (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote)))
(equal (vc-git--rev-parse "HEAD") new-rev))
(print! (info "%s") (cdr result))
(unless (and (zerop (car (doom-call-process "git" "reset" "--hard" target-remote)))
(equal (vc-git--rev-parse "HEAD") new-rev))
(error "Failed to check out %s" (substring new-rev 0 10)))
(print! (success "Finished upgrading Doom Emacs")))
t)))))
(print! (info "%s") (cdr result))
t))))))
(ignore-errors
(doom-call-process "git" "remote" "remove" doom-repo-remote))))))

View File

@@ -2,14 +2,21 @@
(require 'seq)
;; Eagerly load these libraries because we may be in a session that hasn't been
;; fully initialized (e.g. where autoloads files haven't been generated or
;; `load-path' populated).
(load! "autoload/cli")
(load! "autoload/debug")
(load! "autoload/files")
(load! "autoload/format")
(load! "autoload/process")
(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
`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-groups (make-hash-table :test 'equal))
(defvar doom--cli-group nil)
@@ -63,9 +79,10 @@ commands like `doom-cli-packages-install', `doom-cli-packages-update' and
(defun doom--cli-process (cli args)
(let* ((args (copy-sequence args))
(arglist (copy-sequence (doom-cli-arglist cli)))
(expected (or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords)
arglist)
(length arglist)))
(expected
(or (cl-position-if (doom-rpartial #'memq cl--lambda-list-keywords)
arglist)
(length arglist)))
(got 0)
restvar
rest
@@ -146,9 +163,27 @@ Executes a cli defined with `defcli!' with the name or alias specified by
COMMAND, and passes ARGS to it."
(if-let (cli (doom-cli-get command))
(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)))
(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)
"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)
unless (memq optsym cl--lambda-list-keywords)
collect (list optsym `(cdr (assq ',optsym --alist--))))
,@(unless (plist-get plist :bare)
'((unless doom-init-p
(doom-initialize 'force 'noerror)
(doom-initialize-modules))))
,@body)))
doom--cli-commands)
(when aliases
@@ -213,19 +244,142 @@ BODY will be run when this dispatcher is called."
,@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
(load! "cli/help")
(load! "cli/install")
(defcligroup! "Maintenance"
"For managing your config and packages"
(defcli! (refresh re sync)
((if-necessary-p ["-n" "--if-necessary"] "Only regenerate autoloads files if necessary")
(inhibit-envvar-p ["-e"] "Don't regenerate the envvar file")
(prune-p ["-p" "--prune"] "Purge orphaned packages & regraft repos"))
"Ensure Doom is properly set up.
(defcli! (refresh re) ()
"Deprecated for 'doom sync'"
:hidden t
(user-error "'doom refresh' has been replaced with 'doom sync'. Use that instead"))
(defcli! (sync s)
((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
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
installed, autoloads files are up-to-date and no byte-compiled files have gone
stale."
:bare t
(print! (start "Initiating a refresh of Doom Emacs..."))
(print-group!
(let (success)
(when (and (not inhibit-envvar-p)
(file-exists-p doom-env-file))
(doom-cli-reload-env-file 'force))
(print! (start "Synchronizing your config with Doom Emacs..."))
(print-group!
(delete-file doom-autoload-file)
(when (and (not inhibit-envvar-p)
(file-exists-p doom-env-file))
(doom-cli-reload-env-file 'force))
(run-hooks 'doom-sync-pre-hook)
(doom-cli-packages-install)
(doom-cli-packages-build)
(when update-p
(doom-cli-packages-update))
(doom-cli-packages-purge prune-p 'builds-p prune-p prune-p)
(run-hooks 'doom-sync-post-hook)
(when (doom-autoloads-reload)
(print! (info "Restart Emacs or use 'M-x doom/reload' for changes to take effect")))
t))
;; Ensures that no pre-existing state pollutes the generation of the new
;; autoloads files.
(mapc #'doom--cli-delete-autoloads-file
(list doom-autoload-file
doom-package-autoload-file))
(doom-initialize 'force 'noerror)
(doom-initialize-modules)
(doom-cli-reload-core-autoloads (not if-necessary-p))
(unwind-protect
(progn
(and (doom-cli-packages-install)
(setq success t))
(and (doom-cli-packages-build)
(setq success t))
(and (doom-cli-packages-purge 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/upgrade")
(load! "cli/packages")
(load! "cli/autoloads"))
(load! "cli/env")
(load! "cli/upgrade")
(load! "cli/packages")
(load! "cli/autoloads")
(defcligroup! "Diagnostics"
"For troubleshooting and diagnostics"
(load! "cli/doctor")
(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"
"For compiling Doom and your config"
(load! "cli/byte-compile"))
(defcligroup! "Utilities"
"Conveniences for interacting with Doom externally"
(defcli! run ()
(defcli! run (&rest args)
"Run Doom Emacs from bin/doom's parent directory.
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
additional overhead by being started this way. For the best performance, it is
best to run Doom out of ~/.emacs.d and ~/.doom.d.")
;; (load! "cli/batch")
;; (load! "cli/org")
)
best to run Doom out of ~/.emacs.d and ~/.doom.d."
(apply #'doom-cli-execute-after invocation-name args)
nil))
(provide 'core-cli)
;;; core-cli.el ends here

View File

@@ -12,8 +12,8 @@ successfully sets indent_style/indent_size.")
(defvar-local doom-large-file-p nil)
(put 'doom-large-file-p 'permanent-local t)
(defvar doom-large-file-size-alist '((t . 1.0))
"An alist mapping major mode to filesize thresholds.
(defvar doom-large-file-size-alist '(("." . 1.0))
"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
performs emergency optimizations to prevent Emacs from hanging, crashing or
@@ -42,15 +42,17 @@ possible."
(if (setq doom-large-file-p
(and buffer-file-name
(not doom-large-file-p)
(file-readable-p buffer-file-name)
(> (nth 7 (file-attributes buffer-file-name))
(* 1024 1024
(cdr (or (assq major-mode doom-large-file-size-alist)
(assq 't doom-large-file-size-alist)))))))
(file-exists-p buffer-file-name)
(ignore-errors
(> (nth 7 (file-attributes buffer-file-name))
(* 1024 1024
(assoc-default buffer-file-name doom-large-file-size-alist
#'string-match-p))))))
(prog1 (apply orig-fn args)
(if (memq major-mode doom-large-file-excluded-modes)
(setq doom-large-file-p nil)
(so-long-minor-mode +1)
(when (fboundp 'so-long-minor-mode) ; in case the user disabled it
(so-long-minor-mode +1))
(message "Large file detected! Cutting a few corners to improve performance...")))
(apply orig-fn args)))
@@ -77,68 +79,94 @@ possible."
t))))))
;; 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
;; git and our own good fortune instead. Fingers crossed!
;; copies of potentially sensitive material floating around or polluting our
;; filesystem. We rely on git and our own good fortune instead. Fingers crossed!
(setq auto-save-default nil
create-lockfiles nil
make-backup-files nil
;; But have a place to store them in case we do use them...
auto-save-list-file-name (concat doom-cache-dir "autosave")
backup-directory-alist `(("." . ,(concat doom-cache-dir "backup/"))))
;; auto-save-list-file-name (concat doom-cache-dir "autosave")
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
(defun doom-guess-mode-h ()
"Guess mode when saving a file in `fundamental-mode'."
(and (eq major-mode 'fundamental-mode)
(buffer-file-name (buffer-base-buffer))
(eq (current-buffer) (window-buffer (selected-window))) ; only visible buffers
(set-auto-mode))))
(when (eq major-mode 'fundamental-mode)
(let ((buffer (or (buffer-base-buffer) (current-buffer))))
(and (buffer-file-name buffer)
(eq buffer (window-buffer (selected-window))) ; only visible buffers
(set-auto-mode))))))
;;
;;; Formatting
;; Indentation
(setq-default tab-width 4
tab-always-indent t
indent-tabs-mode nil
fill-column 80)
;; Favor spaces over tabs. Pls dun h8, but I think spaces (and 4 of them) is a
;; more consistent default than 8-space tabs. It can be changed on a per-mode
;; basis anyway (and is, where tabs are the canonical style, like go-mode).
(setq-default indent-tabs-mode nil
tab-width 4)
;; Word wrapping
(setq-default word-wrap t
truncate-lines t
truncate-partial-width-windows nil)
;; Make `tabify' and `untabify' only affect indentation. Not tabs/spaces in the
;; middle of a line.
(setq tabify-regexp "^\t* [ \t]+")
(setq sentence-end-double-space nil
delete-trailing-lines nil
require-final-newline t
tabify-regexp "^\t* [ \t]+") ; for :retab
;; An archaic default in the age of widescreen 4k displays? I disagree. We still
;; frequently split our terminals and editor frames, or have them side-by-side,
;; using up more of that newly available horizontal real-estate.
(setq-default fill-column 80)
;; Favor hard-wrapping in text modes
(add-hook 'text-mode-hook #'auto-fill-mode)
;; Continue wrapped words at whitespace, rather than in the middle of a word.
(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
;; Eliminate duplicates in the kill ring. That is, if you kill the same thing
;; twice, you won't have to use M-y twice to get past it to older entries in the
;; kill ring.
;; Cull duplicates in the kill ring to reduce bloat and make the kill ring
;; easier to peruse (with `counsel-yank-pop' or `helm-show-kill-ring'.
(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))
;; Save clipboard contents into kill-ring before replacing them
(setq save-interprogram-paste-before-kill t)
;; Fixes the clipboard in tty Emacs by piping clipboard I/O through xclip, xsel,
;; pb{copy,paste}, wl-copy, termux-clipboard-get, or getclip (cygwin).
(add-hook! 'tty-setup-hook
(defun doom-init-clipboard-in-tty-emacs-h ()
(and (not (getenv "SSH_CONNECTION"))
(require 'xclip nil t)
(xclip-mode +1))))
;; pb{copy,paste}, wl-copy, termux-clipboard-get, or getclip (cygwin); depending
;; on what is available.
(unless IS-WINDOWS
(add-hook! 'tty-setup-hook
(defun doom-init-clipboard-in-tty-emacs-h ()
(and (require 'clipetty nil t)
(global-clipetty-mode +1)))))
;;
@@ -146,6 +174,8 @@ possible."
(push '("/LICENSE\\'" . 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.
revert-without-query (list "."))
;; Instead of using `auto-revert-mode' or `global-auto-revert-mode', we employ
;; lazy auto reverting on `focus-in-hook' and `doom-switch-buffer-hook'.
;; Instead of `auto-revert-mode' or `global-auto-revert-mode', we lazily auto
;; 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
;; grind Emacs to a halt if you do expensive IO (outside of Emacs) on the
;; files you have open (like compression). We only really need to revert
;; changes when we switch to a buffer or when we focus the Emacs frame.
;; Autorevert normally abuses the heck out of inotify handles which can grind
;; Emacs to a halt if you do expensive IO (outside of Emacs) on the files you
;; have open (like compression). The only alternative is aggressive polling,
;; which is unreliable and expensive with a lot of buffers open.
(defun doom-auto-revert-buffer-h ()
"Auto revert current buffer, if necessary."
(unless (or auto-revert-mode (active-minibuffer-window))
@@ -186,7 +216,7 @@ possible."
(use-package! recentf
;; Keep track of recently opened files
:defer-incrementally easymenu tree-widget timer
:after-call after-find-file
:hook (doom-first-file . recentf-mode)
:commands recentf-open-files
:config
(defun doom--recent-file-truename (file)
@@ -194,9 +224,11 @@ possible."
(not (file-remote-p file)))
(file-truename file)
file))
(setq recentf-filename-handlers '(doom--recent-file-truename abbreviate-file-name))
(setq recentf-save-file (concat doom-cache-dir "recentf")
(setq recentf-filename-handlers
'(substring-no-properties ; strip out lingering text properties
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-max-menu-items 0
recentf-max-saved-items 200)
@@ -214,22 +246,20 @@ possible."
"Add dired directory to recentf file list."
(recentf-add-file default-directory)))
(when doom-interactive-mode
(add-hook 'kill-emacs-hook #'recentf-cleanup)
(quiet! (recentf-mode +1))))
(add-hook 'kill-emacs-hook #'recentf-cleanup)
(advice-add #'recentf-load-list :around #'doom-shut-up-a))
(use-package! savehist
;; persist variables across sessions
:defer-incrementally custom
:after-call post-command-hook
:hook (doom-first-input . savehist-mode)
:init
(setq savehist-file (concat doom-cache-dir "savehist"))
:config
(setq savehist-file (concat doom-cache-dir "savehist")
savehist-save-minibuffer-history t
(setq savehist-save-minibuffer-history t
savehist-autosave-interval nil ; save on kill only
savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
(savehist-mode +1)
(add-hook! 'kill-emacs-hook
(defun doom-unpropertize-kill-ring-h ()
"Remove text properties from `kill-ring' for a smaller savehist file."
@@ -241,31 +271,33 @@ possible."
(use-package! saveplace
;; persistent point location in buffers
:after-call after-find-file dired-initial-position-hook
:config
:hook (doom-first-file . save-place-mode)
:init
(setq save-place-file (concat doom-cache-dir "saveplace")
save-place-limit 100)
:config
(defadvice! doom--recenter-on-load-saveplace-a (&rest _)
"Recenter on cursor when loading a saved place."
:after-while #'save-place-find-file-hook
(if buffer-file-name (ignore-errors (recenter))))
(defadvice! doom--inhibit-saveplace-in-long-files-a (orig-fn &rest args)
:around #'save-place-to-alist
(unless doom-large-file-p
(apply orig-fn args)))
(defadvice! doom--dont-prettify-saveplace-cache-a (orig-fn)
"`save-place-alist-to-file' uses `pp' to prettify the contents of its cache.
`pp' can be expensive for longer lists, and there's no reason to prettify cache
files, so we replace calls to `pp' with the much faster `prin1'."
:around #'save-place-alist-to-file
(cl-letf (((symbol-function #'pp)
(symbol-function #'prin1)))
(funcall orig-fn)))
(save-place-mode +1))
(letf! ((#'pp #'prin1)) (funcall orig-fn))))
(use-package! server
:when (display-graphic-p)
:after-call pre-command-hook after-find-file focus-out-hook
:defer 1
:init
(when-let (name (getenv "EMACS_SERVER_NAME"))
(setq server-name name))
@@ -278,7 +310,8 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;;; Packages
(use-package! better-jumper
:after-call pre-command-hook
:hook (doom-first-input . better-jumper-mode)
:hook (better-jumper-post-jump . recenter)
:preface
;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at
;; 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 xref-pop-marker-stack] #'better-jumper-jump-backward)
:config
(better-jumper-mode +1)
(add-hook 'better-jumper-post-jump-hook #'recenter)
(defadvice! doom-set-jump-a (orig-fn &rest args)
(defun doom-set-jump-a (orig-fn &rest args)
"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)))
(let ((evil--jumps-jumping t)
(better-jumper--jumping t))
(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."
(let ((origin (point-marker))
(result
@@ -329,20 +359,19 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(use-package! dtrt-indent
;; Automatic detection of indent settings
:when doom-interactive-mode
:defer t
:init
(add-hook! '(change-major-mode-after-body-hook read-only-mode-hook)
(defun doom-detect-indentation-h ()
(unless (or (not after-init-time)
doom-inhibit-indent-detection
doom-large-file-p
(memq major-mode doom-detect-indentation-excluded-modes)
(member (substring (buffer-name) 0 1) '(" " "*")))
;; Don't display messages in the echo area, but still log them
(let ((inhibit-message (not doom-debug-mode)))
(dtrt-indent-mode +1)))))
:when doom-interactive-p
:hook ((change-major-mode-after-body read-only-mode) . doom-detect-indentation-h)
:config
(defun doom-detect-indentation-h ()
(unless (or (not after-init-time)
doom-inhibit-indent-detection
doom-large-file-p
(memq major-mode doom-detect-indentation-excluded-modes)
(member (substring (buffer-name) 0 1) '(" " "*")))
;; Don't display messages in the echo area, but still log them
(let ((inhibit-message (not doom-debug-p)))
(dtrt-indent-mode +1))))
;; Enable dtrt-indent even in smie modes so that it can update `tab-width',
;; `standard-indent' and `evil-shift-width' there as well.
(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."
:around #'dtrt-indent-mode
(let ((dtrt-indent-run-after-smie dtrt-indent-run-after-smie))
(cl-letf* ((old-smie-config-guess (symbol-function 'smie-config-guess))
(old-smie-config--guess (symbol-function 'symbol-config--guess))
((symbol-function 'symbol-config--guess)
(lambda (beg end)
(funcall old-smie-config--guess beg (min end 10000))))
((symbol-function 'smie-config-guess)
(lambda ()
(condition-case e (funcall old-smie-config-guess)
(error (setq dtrt-indent-run-after-smie t)
(message "[WARNING] Indent detection: %s"
(error-message-string e))
(message "")))))) ; warn silently
(letf! ((defun symbol-config--guess (beg end)
(funcall symbol-config--guess beg (min end 10000)))
(defun smie-config-guess ()
(condition-case e (funcall smie-config-guess)
(error (setq dtrt-indent-run-after-smie t)
(message "[WARNING] Indent detection: %s"
(error-message-string e))
(message ""))))) ; warn silently
(funcall orig-fn arg)))))
@@ -377,17 +402,16 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;; a better *help* buffer
:commands helpful--read-symbol
:init
(define-key!
[remap describe-function] #'helpful-callable
[remap describe-command] #'helpful-command
[remap describe-variable] #'helpful-variable
[remap describe-key] #'helpful-key
[remap describe-symbol] #'doom/describe-symbol)
(global-set-key [remap describe-function] #'helpful-callable)
(global-set-key [remap describe-command] #'helpful-command)
(global-set-key [remap describe-variable] #'helpful-variable)
(global-set-key [remap describe-key] #'helpful-key)
(global-set-key [remap describe-symbol] #'helpful-symbol)
(defun doom-use-helpful-a (orig-fn &rest args)
"Force ORIG-FN to use helpful instead of the old describe-* commands."
(cl-letf (((symbol-function #'describe-function) #'helpful-function)
((symbol-function #'describe-variable) #'helpful-variable))
(letf! ((#'describe-function #'helpful-function)
(#'describe-variable #'helpful-variable))
(apply orig-fn args)))
(after! apropos
@@ -411,30 +435,33 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(use-package! smartparens
;; Auto-close delimiters and blocks as you type. It's more powerful than that,
;; but that is all Doom uses it for.
:after-call doom-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
: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
(require 'smartparens-config)
;; Overlays are too distracting and not terribly helpful. show-parens does
;; this for us already, so...
;; this for us already (and is faster), so...
(setq sp-highlight-pair-overlay nil
sp-highlight-wrap-overlay nil
sp-highlight-wrap-tag-overlay nil)
;; But if someone does want overlays enabled, evil users will be stricken with
;; an off-by-one issue where smartparens assumes you're outside the pair when
;; you're really at the last character in insert mode. We must correct this
;; vile injustice.
(setq sp-show-pair-from-inside t)
;; ...and stay highlighted until we've truly escaped the pair!
(setq sp-cancel-autoskip-on-backward-movement nil)
(with-eval-after-load 'evil
;; But if someone does want overlays enabled, evil users will be stricken
;; with an off-by-one issue where smartparens assumes you're outside the
;; pair when you're really at the last character in insert mode. We must
;; correct this vile injustice.
(setq sp-show-pair-from-inside t)
;; ...and stay highlighted until we've truly escaped the pair!
(setq sp-cancel-autoskip-on-backward-movement nil))
;; The default is 100, because smartparen's scans are relatively expensive
;; (especially with large pair lists for somoe modes), we halve it, as a
;; (especially with large pair lists for some modes), we reduce it, as a
;; better compromise between performance and accuracy.
(setq sp-max-prefix-length 50)
;; This speeds up smartparens. No pair has any business being longer than 4
;; characters; if they must, the modes that need it set it buffer-locally.
(setq sp-max-prefix-length 25)
;; No pair has any business being longer than 4 characters; if they must, set
;; it buffer-locally. It's less work for smartparens.
(setq sp-max-pair-length 4)
;; This isn't always smart enough to determine when we're in a string or not.
;; See https://github.com/Fuco1/smartparens/issues/783.
@@ -442,14 +469,15 @@ files, so we replace calls to `pp' with the much faster `prin1'."
;; Silence some harmless but annoying echo-area spam
(dolist (key '(:unmatched-expression :no-matching-tag))
(setf (cdr (assq key sp-message-alist)) nil))
(setf (alist-get key sp-message-alist) nil))
(add-hook! 'minibuffer-setup-hook
(defun doom-init-smartparens-in-minibuffer-maybe-h ()
"Enable `smartparens-mode' in the minibuffer, during `eval-expression' or
`evil-ex'."
(when (memq this-command '(eval-expression evil-ex))
(smartparens-mode))))
"Enable `smartparens-mode' in the minibuffer, during `eval-expression',
`pp-eval-expression' or `evil-ex'."
(and (memq this-command '(eval-expression pp-eval-expression evil-ex))
smartparens-global-mode
(smartparens-mode))))
;; You're likely writing lisp in the minibuffer, therefore, disable these
;; quote pairs, which lisps doesn't use for strings:
@@ -467,16 +495,13 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(defun doom-disable-smartparens-mode-maybe-h ()
(when smartparens-mode
(setq-local doom-buffer-smartparens-mode t)
(turn-off-smartparens-mode))))
(smartparens-global-mode +1))
(turn-off-smartparens-mode)))))
(use-package! so-long
:after-call after-find-file
:hook (doom-first-file . global-so-long-mode)
:config
(when doom-interactive-mode
(global-so-long-mode +1))
(setq so-long-threshold 400) ; reduce false positives w/ larger threshold
;; 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
;; 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)
;; ...but at least reduce the level of syntax highlighting
(add-to-list 'so-long-variable-overrides '(font-lock-maximum-decoration . 1))
;; ...and insist that save-place not operate in large/long files
(add-to-list 'so-long-variable-overrides '(save-place-alist . nil))
;; Text files could possibly be too long too
(add-to-list 'so-long-target-modes 'text-mode)
;; But disable everything else that may be unnecessary/expensive for large
@@ -493,6 +520,7 @@ files, so we replace calls to `pp' with the much faster `prin1'."
(appendq! so-long-minor-modes
'(flycheck-mode
flyspell-mode
spell-fu-mode
eldoc-mode
smartparens-mode
highlight-numbers-mode
@@ -502,60 +530,28 @@ files, so we replace calls to `pp' with the much faster `prin1'."
undo-tree-mode
highlight-indent-guides-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 ()
(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)))
(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
;; a less intrusive `delete-trailing-whitespaces' on save
:after-call after-find-file
:config
(appendq! ws-butler-global-exempt-modes
'(special-mode comint-mode term-mode eshell-mode))
(ws-butler-global-mode))
:hook (doom-first-buffer . ws-butler-global-mode))
(provide 'core-editor)
;;; core-editor.el ends here

View File

@@ -26,9 +26,12 @@ and Emacs states, and for non-evil users.")
;;
;;; Keybind settings
(when IS-MAC
(setq mac-command-modifier 'super
mac-option-modifier 'meta))
(cond (IS-MAC
(setq mac-command-modifier 'super
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
;; Convenience aliases
(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
;; 20-30% more startup time, so we reimplement it ourselves.
@@ -108,8 +112,8 @@ all hooks after it are ignored.")
(general--concat t doom-leader-key ,key)
,desc))))))))
(macroexp-progn
(cons `(after! which-key ,@(nreverse wkforms))
(nreverse forms)))))
(append (and wkforms `((after! which-key ,@(nreverse wkforms))))
(nreverse forms)))))
(defmacro define-leader-key! (&rest args)
"Define <leader> keys.
@@ -175,8 +179,7 @@ localleader prefix."
;;; Packages
(use-package! which-key
:defer 1
:after-call pre-command-hook
:hook (doom-first-input . which-key-mode)
:init
(setq which-key-sort-order #'which-key-prefix-then-key-order
which-key-sort-uppercase-first nil
@@ -191,9 +194,7 @@ localleader prefix."
(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-localleader-key "<localleader>")
(which-key-mode +1))
(which-key-add-key-based-replacements doom-localleader-key "<localleader>"))
;;
@@ -215,21 +216,11 @@ localleader prefix."
For example, :nvi will map to (list 'normal 'visual 'insert). See
`doom-evil-state-alist' to customize this."
(cl-loop for l across (substring (symbol-name keyword) 1)
if (cdr (assq l doom-evil-state-alist)) collect it
(cl-loop for l across (doom-keyword-name keyword)
if (assq l doom-evil-state-alist) collect (cdr it)
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
(defvar doom--map-forms nil)
(defvar doom--map-fn nil)

View File

@@ -1,8 +1,5 @@
;;; core-lib.el -*- lexical-binding: t; -*-
(require 'cl-lib)
(require 'subr-x)
;;
;;; Helpers
@@ -69,10 +66,10 @@ list is returned as-is."
(substring (symbol-name keyword) 1))
(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*.
Accepts the same arguments as `message'."
`(when doom-debug-mode
`(when doom-debug-p
(let ((inhibit-message (active-minibuffer-window)))
(message
,(concat (propertize "DOOM " 'face 'font-lock-comment-face)
@@ -85,6 +82,53 @@ Accepts the same arguments as `message'."
format-string)
,@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)
(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
function which does the same as FUN, except that the last N arguments are fixed
at the values with which this function was called."
(declare (side-effect-free t))
(lambda (&rest pre-args)
(apply fn (append pre-args args))))
@@ -100,23 +145,10 @@ at the values with which this function was called."
;;
;;; Sugars
(defmacro λ! (&rest body)
"Expands to (lambda () (interactive) ,@body).
A factory for quickly producing interaction commands, particularly for keybinds
or aliases."
(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 dir! ()
"Returns the directory of the emacs lisp file this macro is called from."
(when-let (path (file!))
(directory-file-name (file-name-directory path))))
(defun file! ()
"Return the emacs lisp file this macro is called from."
@@ -127,18 +159,152 @@ aliases."
(buffer-file-name)
((error "Cannot get this file-path"))))
(defun dir! ()
"Returns the directory of the emacs lisp file this macro is called from."
(when-let (path (file!))
(directory-file-name (file-name-directory path))))
(defmacro letenv! (envvars &rest body)
"Lexically bind ENVVARS in BODY, like `let' but for `process-environment'."
(declare (indent 1))
`(let ((process-environment (copy-sequence process-environment)))
(dolist (var (list ,@(cl-loop for (var val) in envvars
collect `(cons ,var ,val))))
(setenv (car var) (cdr var)))
,@body))
(defmacro 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)
"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
(cl-loop for (var val) on settings by 'cddr
collect `(funcall (or (get ',var 'custom-set) #'set)
',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)
"Push VALUES sequentially into PLACE, if they aren't already present.
This is a variadic `cl-pushnew'."
@@ -150,31 +316,8 @@ This is a variadic `cl-pushnew'."
"Prepend LISTS to SYM in place."
`(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)
"Add DIRS to `load-path', relative to the current file.
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))
(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)
"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)."
(declare (indent 1))
(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))
(defun ,fn (&rest _)
,(format "Transient hook for %S" (doom-unquote hook-or-function))
,@forms
(let ((sym ,hook-or-function))
(cond ((functionp sym) (advice-remove sym #',fn))
@@ -207,20 +483,29 @@ advised)."
(put ',fn 'permanent-local-hook t)
(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)
"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:
1. Optional properties :local and/or :append, which will make the hook
1. The mode(s) or hook(s) to add to. This is either an unquoted mode, an
unquoted list of modes, a quoted hook variable or a quoted list of hook
variables.
2. Optional properties :local and/or :append, which will make the hook
buffer-local or append to the list of hooks (respectively),
2. The hook(s) to be added to: either an unquoted mode, an unquoted list of
modes, a quoted hook variable or a quoted list of hook variables. If
unquoted, '-hook' will be appended to each symbol.
3. The function(s) to be added: this can be one function, a list thereof, a
list of `defun's, or body forms (implicitly wrapped in a closure).
3. The function(s) to be added: this can be one function, a quoted list
thereof, a list of `defun's, or body forms (implicitly wrapped in a
lambda).
\(fn HOOKS [:append :local] FUNCTIONS)"
(declare (indent (lambda (indent-point state)
@@ -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)
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
(defmacro defadvice! (symbol arglist &optional docstring &rest body)
"Define an advice called SYMBOL and add it to PLACES.
@@ -419,10 +606,63 @@ DOCSTRING and BODY are as in `defun'.
where-alist))
`(progn
(defun ,symbol ,arglist ,docstring ,@body)
,(when where-alist
`(dolist (targets (list ,@(nreverse where-alist)))
(dolist (target (cdr targets))
(advice-add target (car targets) #',symbol)))))))
(dolist (targets (list ,@(nreverse where-alist)))
(dolist (target (cdr targets))
(advice-add target (car targets) #',symbol))))))
(defmacro undefadvice! (symbol _arglist &optional docstring &rest body)
"Undefine an advice called SYMBOL.
This has the same signature as `defadvice!' an exists as an easy undefiner when
testing advice (when combined with `rotate-text').
\(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)"
(declare (doc-string 3) (indent defun))
(let (where-alist)
(unless (stringp docstring)
(push docstring body))
(while (keywordp (car body))
(push `(cons ,(pop body) (doom-enlist ,(pop body)))
where-alist))
`(dolist (targets (list ,@(nreverse where-alist)))
(dolist (target (cdr targets))
(advice-remove target #',symbol)))))
;;
;;; 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)
;;; core-lib.el ends here

View File

@@ -3,7 +3,7 @@
(defvar doom-init-modules-p nil
"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'.")
(defvar doom-modules-dirs
@@ -11,36 +11,52 @@
doom-modules-dir)
"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
'((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell))
(syntax-checker (:tools flycheck))
(evil (:editor evil))
(snippets (:editor snippets))
(file-templates (:editor file-templates))
(workspaces (:ui workspaces))
(eval (:tools eval))
(lookup (:tools lookup))
(debugger (:tools debugger)))
(:tools (rotate-text (:editor rotate-text))
(vterm (:term vterm))
(password-store (:tools pass)))
(:emacs (electric-indent (:emacs electric))
(hideshow (:editor fold))
(eshell (:term eshell))
(term (:term term)))
(:ui (doom-modeline (:ui modeline))
(fci (:ui fill-column))
(evil-goggles (:ui ophints))
(tabbar (:ui tabs)))
(:app (email (:email mu4e))
(notmuch (:email notmuch))))
'((:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:checkers spell))
(syntax-checker (:checkers syntax))
(evil (:editor evil))
(snippets (:editor snippets))
(file-templates (:editor file-templates))
(workspaces (:ui workspaces))
(eval (:tools eval))
(lookup (:tools lookup))
(debugger (:tools debugger)))
(:tools (rotate-text (:editor rotate-text))
(vterm (:term vterm))
(password-store (:tools pass))
(flycheck (:checkers syntax))
(flyspell (:checkers spell)))
(:emacs (electric-indent (:emacs electric))
(hideshow (:editor fold))
(eshell (:term eshell))
(term (:term term)))
(:ui (doom-modeline (:ui modeline))
(fci (:ui fill-column))
(evil-goggles (:ui ophints))
(tabbar (:ui tabs)))
(:app (email (:email mu4e))
(notmuch (:email notmuch)))
(:lang (perl (:lang raku))))
"A tree alist that maps deprecated modules to their replacement(s).
Each entry is a three-level tree. For example:
(:feature (version-control (:emacs vc) (:ui vc-gutter))
(spellcheck (:tools flyspell))
(spellcheck (:checkers spell))
(syntax-checker (:tools flycheck)))
This marks :feature version-control, :feature spellcheck and :feature
@@ -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
: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.")
;;; Custom hooks
@@ -69,28 +85,39 @@ before the user's private module.")
;;
;;; 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
session of Dooming. Will noop if used more than once, unless FORCE-P is
non-nil."
(when (or force-p (not doom-init-modules-p))
(setq doom-init-modules-p t
doom-modules nil)
(when (load! "init" doom-private-dir t)
(when doom-modules
(maphash (lambda (key plist)
(let ((doom--current-module key)
(doom--current-flags (plist-get plist :flags)))
(load! "init" (plist-get plist :path) t)))
doom-modules))
(setq doom-init-modules-p t)
(unless no-config-p
(doom-log "Initializing core modules")
(doom-initialize-core-modules))
(when-let (init-p (load! doom-module-init-file doom-private-dir t))
(doom-log "Initializing user config")
(maphash (doom-module-loader doom-module-init-file) doom-modules)
(run-hook-wrapped 'doom-before-init-modules-hook #'doom-try-run-hook)
(when doom-interactive-mode
(when 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))
(unless no-config-p
(maphash (doom-module-loader doom-module-config-file) doom-modules)
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
(load! "config" doom-private-dir t)))))
@@ -101,11 +128,10 @@ non-nil."
(defun doom-module-p (category module &optional flag)
"Returns t if CATEGORY MODULE is enabled (ie. present in `doom-modules')."
(declare (pure t) (side-effect-free t))
(let ((plist (gethash (cons category module) doom-modules)))
(and plist
(or (null flag)
(memq flag (plist-get plist :flags)))
t)))
(when-let (plist (gethash (cons category module) doom-modules))
(or (null flag)
(and (memq flag (plist-get plist :flags))
t))))
(defun doom-module-get (category module &optional property)
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
@@ -120,15 +146,17 @@ non-nil."
of PROPERTY and VALUEs.
\(fn CATEGORY MODULE PROPERTY VALUE &rest [PROPERTY VALUE [...]])"
(if-let (old-plist (doom-module-get category module))
(progn
(when plist
(when (cl-oddp (length plist))
(signal 'wrong-number-of-arguments (list (length plist))))
(while plist
(plist-put old-plist (pop plist) (pop plist))))
(puthash (cons category module) old-plist doom-modules))
(puthash (cons category module) plist doom-modules)))
(puthash (cons category module)
(if-let (old-plist (doom-module-get category module))
(if (null plist)
old-plist
(when (cl-oddp (length plist))
(signal 'wrong-number-of-arguments (list (length plist))))
(while plist
(plist-put old-plist (pop plist) (pop plist)))
old-plist)
plist)
doom-modules))
(defun doom-module-set (category module &rest plist)
"Enables a module by adding it to `doom-modules'.
@@ -140,10 +168,8 @@ following properties:
:path [STRING] path to category root directory
Example:
(doom-module-set :lang 'haskell :flags '(+intero))"
(puthash (cons category module)
plist
doom-modules))
(doom-module-set :lang 'haskell :flags '(+dante))"
(puthash (cons category module) plist doom-modules))
(defun doom-module-path (category module &optional file)
"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))
(append (list doom-private-dir)
(if module-dirs
(doom-files-in (if (listp module-dirs)
module-dirs
doom-modules-dirs)
:type 'dirs
:mindepth 1
:depth 1)
(cl-loop for plist being the hash-values of (doom-modules)
(mapcar (lambda (m) (doom-module-locate-path (car m) (cdr m)))
(delete-dups
(doom-files-in (if (listp module-dirs)
module-dirs
doom-modules-dirs)
:map #'doom-module-from-path
:type 'dirs
:mindepth 1
:depth 1)))
(cl-loop for plist being the hash-values of doom-modules
collect (plist-get plist :path)))
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.
This value is cached. If REFRESH-P, then don't use the cached value."
(or (unless refresh-p doom-modules)
(let (doom-interactive-mode
doom-modules
doom-init-modules-p)
(load! "init" doom-private-dir t)
(or doom-modules
(make-hash-table :test 'equal
:size 20
:rehash-threshold 1.0)))))
(if all-p
(cl-loop for path in (cdr (doom-module-load-path 'all))
collect (doom-module-from-path path))
doom-modules))
;;
;;; 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))
(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
;; 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)
;; 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)
(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:
;;
;; :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'
for a list of all recognized module trees. Order defines precedence (from most
to least)."
`(let ((modules
,@(if (keywordp (car modules))
(list (list 'quote modules))
modules)))
(unless 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)))
`(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))
(list (list 'quote modules))
modules))
doom-modules))
(defvar doom-disabled-packages)
(defmacro use-package! (name &rest plist)
@@ -481,39 +535,6 @@ WARNINGS:
(lambda () ,@body)
'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)
"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)
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)
CATEGORY and MODULE can be omitted When this macro is used from inside a module
(except your DOOMDIR, which is a special moduel). e.g. (featurep! +flag)"
(except your DOOMDIR, which is a special module). e.g. (featurep! +flag)"
(and (cond (flag (memq flag (doom-module-get category module :flags)))
(module (doom-module-p category module))
(doom--current-flags (memq category doom--current-flags))
((let ((module (doom-module-from-path)))
(unless module
(error "(featurep! %s %s %s) couldn't figure out what module it was called from (in %s)"
category module flag (file!)))
(memq category (doom-module-get (car module) (cdr module) :flags)))))
((if-let (module (doom-module-from-path))
(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)"
category module flag (file!)))))
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)
;;; core-modules.el ends here

View File

@@ -1,27 +1,25 @@
;;; core/core-packages.el -*- lexical-binding: t; -*-
;; 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
;; tempermental. ELPA sources suffer downtime occasionally, and often fail at
;; building some packages when GNU Tar is unavailable (e.g. MacOS users start
;; with BSD tar). There are also known gnutls errors in the current stable
;; release of Emacs (26.x) which bork TLS handshakes with ELPA repos (mainly
;; gnu.elpa.org). See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434.
;; tempermental. ELPA sources suffer downtime occasionally and often fail to
;; build packages when GNU Tar is unavailable (e.g. MacOS users start with BSD
;; tar). Known gnutls errors plague the current stable release of Emacs (26.x)
;; which bork TLS handshakes with ELPA repos (mainly gnu.elpa.org). See
;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3434.
;;
;; 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
;; convenient. Straight (and Doom) can do rolling release, but it is optional
;; (and will eventually be opt-in).
;; convenient. Straight (and Doom) can do rolling release, but it is opt-in.
;;
;; ANyhow, interacting with this package management system is done through the
;; bin/doom script included with Doom Emacs. You'll find more about it by
;; running 'doom help' (I highly recommend you add it to your PATH), but here
;; are the highlights:
;; Interacting with this package management system is done through Doom's
;; bin/doom script. Find out more about it by running 'doom help' (I highly
;; recommend you add the script to your PATH). Here are some highlights:
;;
;; + `bin/doom install`: a wizard that guides you through setting up Doom and
;; your private config for the first time.
;; + `bin/doom refresh`: your go-to command for making sure Doom is in optimal
;; + `bin/doom sync`: your go-to command for making sure Doom is in optimal
;; condition. It ensures all unneeded packages are removed, all needed ones
;; are installed, and all metadata associated with them is generated.
;; + `bin/doom upgrade`: upgrades Doom Emacs and your packages to the latest
@@ -32,48 +30,31 @@
;; `doom-core-dir'. These contain `package!' declarations that tell DOOM what
;; plugins to install and where from.
;;
;; All that said, you can still use package.el's commands, but 'bin/doom
;; refresh' will purge ELPA packages.
(defvar doom-init-packages-p nil
"If non-nil, Doom's package management system has been initialized.")
;; All that said, you can still use package.el's commands, but 'bin/doom sync'
;; will purge ELPA packages.
(defvar doom-packages ()
"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!' 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 ()
"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
;; shouldn't be using it, but it may be convenient for quick package testing.
(setq package--init-file-ensured t
package-enable-at-startup nil
(setq package-enable-at-startup nil
package-user-dir (concat doom-local-dir "elpa/")
package-gnupghome-dir (expand-file-name "gpg" package-user-dir)
;; I omit Marmalade because its packages are manually submitted rather
@@ -84,175 +65,363 @@ missing) and shouldn't be deleted.")
("melpa" . ,(concat proto "://melpa.org/packages/"))
("org" . ,(concat proto "://orgmode.org/elpa/")))))
;; Don't save `package-selected-packages' to `custom-file'
(defadvice! doom--package-inhibit-custom-file-a (&optional value)
:override #'package--save-selected-packages
(if value (setq package-selected-packages value)))
;; package.el has no business modifying the user's init.el
(advice-add #'package--ensure-init-file :override #'ignore)
;; 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))
;;; straight
;;
;;; Straight
(setq straight-base-dir doom-local-dir
straight-repository-branch "develop"
straight-cache-autoloads nil ; we already do this, and better.
;; Doom doesn't encourage you to modify packages in place. Disabling this
;; makes 'doom refresh' instant (once everything set up), which is much
;; nicer UX than the several seconds modification checks.
;; makes 'doom sync' instant (once everything set up), which is much nicer
;; UX than the several seconds modification checks.
straight-check-for-modifications nil
;; We handle package.el ourselves (and a little more comprehensively)
straight-enable-package-integration nil
;; Before switching to straight, `doom-local-dir' would average out at
;; around 100mb with half Doom's modules at ~230 packages. Afterwards, at
;; around 1gb. With shallow cloning, that is reduced to ~400mb. This
;; imposes an issue with packages that require their git history for
;; certain things to work (like magit and org), but we can deal with that
;; when we cross that bridge.
;; around 1gb. With shallow cloning, that is reduced to ~400mb. This has
;; no affect on packages that are pinned, however (run 'doom purge' to
;; compact those after-the-fact). Some packages break when shallow cloned
;; (like magit and org), but we'll deal with that elsewhere.
straight-vc-git-default-clone-depth 1
;; Straight's own emacsmirror mirror is a little smaller and faster.
straight-recipes-emacsmirror-use-mirror t
;; Prefix declarations are unneeded bulk added to our autoloads file. Best
;; we just don't have to deal with them at all.
autoload-compute-prefixes nil)
;; we don't have to deal with them at all.
autoload-compute-prefixes nil
;; We handle it ourselves
straight-fix-org nil)
(defun doom--finalize-straight ()
(mapc #'funcall (delq nil (mapcar #'cdr straight--transaction-alist)))
(setq straight--transaction-alist nil))
;;; Getting straight to behave in batch mode
(when noninteractive
;; HACK Remove dired & magit options from prompt, since they're inaccessible
;; in noninteractive sessions.
(advice-add #'straight-vc-git--popup-raw :override #'straight--popup-raw))
;; HACK Replace GUI popup prompts (which hang indefinitely in tty Emacs) with
;; simple prompts.
(defadvice! doom--straight-fallback-to-y-or-n-prompt-a (orig-fn &optional prompt)
:around #'straight-are-you-sure
(if noninteractive
(y-or-n-p (format! "%s" (or prompt "")))
(funcall orig-fn prompt)))
(defadvice! doom--straight-fallback-to-tty-prompt-a (orig-fn prompt actions)
"Modifies straight to prompt on the terminal when in noninteractive sessions."
:around #'straight--popup-raw
(if (not noninteractive)
(funcall orig-fn prompt actions)
;; We can't intercept C-g, so no point displaying any options for this key
;; Just use C-c
(delq! "C-g" actions 'assoc)
;; HACK These are associated with opening dired or magit, which isn't
;; possible in tty Emacs, so...
(delq! "e" actions 'assoc)
(delq! "g" actions 'assoc)
(let ((options (list (lambda ()
(let ((doom-format-indent 0))
(terpri)
(print! (error "Aborted")))
(kill-emacs)))))
(print! (start "%s") (red prompt))
(terpri)
(print-group!
(print-group!
(print! " 1) Abort")
(dolist (action actions)
(cl-destructuring-bind (_key desc func) action
(when desc
(push func options)
(print! "%2s) %s" (length options) desc)))))
(terpri)
(let ((options (nreverse options))
answer fn)
(while
(not
(setq
fn (ignore-errors
(nth (1- (setq answer
(read-number
(format! "How to proceed? (%s) "
(mapconcat #'number-to-string
(number-sequence 1 (length options))
", ")))))
options))))
(print! (warn "%s is not a valid answer, try again.") answer))
(funcall fn))))))
(defadvice! doom--read-pinned-packages-a (orig-fn &rest args)
"Read `:pin's in `doom-packages' on top of straight's lockfiles."
:around #'straight--lockfile-read-all
(append (apply orig-fn args) ; lockfiles still take priority
(doom-package-pinned-list)))
;;
;;; 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)
"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.
This ensure `doom-packages' is populated, if isn't aren't already. Use this
before any of straight's or Doom's package management's API to ensure all the
necessary package metadata is initialized and available for them."
(unless doom-init-packages-p
(setq force-p t))
This ensures `doom-packages' is populated and `straight' recipes are properly
processed."
(doom-initialize-core-packages force-p)
(when (or force-p (not (bound-and-true-p package--initialized)))
(doom-log "Initializing package.el")
(require 'package)
(package-initialize))
(when (or force-p (not doom-packages))
(doom-log "Initializing straight")
(setq doom-init-packages-p t)
(unless (fboundp 'straight--reset-caches)
(doom-ensure-straight)
(require 'straight))
(straight--reset-caches)
(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))
(cl-loop for (pkg . plist) in doom-packages
if (plist-get plist :disable)
do (cl-pushnew pkg doom-disabled-packages)
else if (not (plist-get plist :ignore))
do (with-demoted-errors "Package error: %s"
(straight-register-package
(if-let (recipe (plist-get plist :recipe))
(cons pkg recipe)
pkg))))
(unless doom-interactive-mode
(add-hook 'kill-emacs-hook #'doom--finalize-straight))))
(package-initialize)
(unless package--initialized
(error "Failed to initialize package.el")))
(when (or force-p (null doom-packages))
(doom-log "Initializing straight.el")
(or (setq doom-disabled-packages nil
doom-packages (doom-package-list))
(error "Failed to read any packages"))
(dolist (package doom-packages)
(cl-destructuring-bind
(name &key recipe disable ignore &allow-other-keys) package
(unless ignore
(if disable
(cl-pushnew name doom-disabled-packages)
(when recipe
(straight-override-recipe (cons name recipe)))
(straight-register-package name)))))))
(defun doom-ensure-straight ()
"Ensure `straight' is installed and was compiled with this version of Emacs."
(defvar bootstrap-version)
(let* (;; Force straight to install into ~/.emacs.d/.local/straight instead of
;; ~/.emacs.d/straight by pretending `doom-local-dir' is our .emacs.d.
(user-emacs-directory straight-base-dir)
(bootstrap-file (doom-path straight-base-dir "straight/repos/straight.el/straight.el"))
(bootstrap-version 5))
(make-directory (doom-path straight-base-dir "straight/build") 'parents)
(unless (featurep 'straight)
(unless (or (require 'straight nil t)
(file-readable-p bootstrap-file))
(with-current-buffer
(url-retrieve-synchronously
(format "https://raw.githubusercontent.com/raxod502/straight.el/%s/install.el"
straight-repository-branch)
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil t))))
;;
;;; Package management API
(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)))
(defun doom-package-set (package prop value)
"Set PROPERTY in PACKAGE's recipe to VALUE."
(setf (alist-get package doom-packages)
(plist-put (alist-get package doom-packages)
prop value)))
(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
(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).
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:
: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
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
for packages to be installed from external sources.
Specifies a straight.el recipe to allow you to acquire packages from external
sources. See https://github.com/raxod502/straight.el#the-recipe-format for
details on this recipe.
:disable BOOL
Do not install or update this package AND disable all of its `use-package!'
blocks.
and `after!' blocks.
:ignore FORM
Do not install this package.
:freeze FORM
Do not update this package if FORM is non-nil.
:built-in BOOL
Same as :ignore if the package is a built-in Emacs package. If set to
'prefer, will use built-in package if it is present.
:pin STR|nil
Pin this package to commit hash STR. Setting this to nil will unpin this
package if previously pinned.
:built-in BOOL|'prefer
Same as :ignore if the package is a built-in Emacs package. This is more to
inform help commands like `doom/help-packages' that this is a built-in
package. If set to 'prefer, the package will not be installed if it is
already provided by Emacs.
Returns t if package is successfully registered, and nil if it was disabled
elsewhere."
(declare (indent defun))
(when (and recipe (keywordp (car-safe recipe)))
(plist-put! plist :recipe `(quote ,recipe)))
;; :built-in t is basically an alias for :ignore (locate-library NAME)
(when built-in
(when (and (not ignore) (equal built-in '(quote prefer)))
(when (and (not ignore)
(equal built-in '(quote prefer)))
(setq built-in `(locate-library ,(symbol-name name) nil doom--initial-load-path)))
(plist-delete! plist :built-in)
(plist-put! plist :ignore built-in))
`(let* ((name ',name)
(plist (cdr (assq name doom-packages))))
;; Record what module this declaration was found in
(let ((module-list (plist-get plist :modules))
(module ',(doom-module-from-path)))
(unless (member module module-list)
(plist-put! plist :modules
(append module-list
(list module)
(when (file-in-directory-p ,(dir!) doom-private-dir)
'((:private . modules)))
nil))))
;; Merge given plist with pre-existing one
(doplist! ((prop val) (list ,@plist) plist)
(unless (null val)
(plist-put! plist prop val)))
;; Some basic key validation; error if you're not using a valid key
;; Some basic key validation; throws an error on invalid properties
(condition-case e
(cl-destructuring-bind
(&key _local-repo _files _flavor _no-build
_type _repo _host _branch _remote _nonrecursive _fork _depth)
(plist-get plist :recipe))
(when-let (recipe (plist-get plist :recipe))
(cl-destructuring-bind
(&key local-repo _files _flavor
_no-build _no-byte-compile _no-autoloads
_type _repo _host _branch _remote _nonrecursive _fork _depth)
recipe
;; Expand :local-repo from current directory
(when local-repo
(plist-put!
plist :recipe
(plist-put recipe :local-repo
(let ((local-path (expand-file-name local-repo ,(dir!))))
(if (file-directory-p local-path)
local-path
local-repo)))))))
(error
(signal 'doom-package-error
(cons ,(symbol-name name)
(error-message-string e)))))
;; This is the only side-effect of this macro!
(setf (alist-get name doom-packages) plist)
(if (not (plist-get plist :disable)) t
(doom-log "Disabling package %S" name)
(cl-pushnew name doom-disabled-packages)
nil)))
(unless (plist-get plist :disable)
(with-no-warnings
(cons name plist)))))
(defmacro disable-packages! (&rest packages)
"A convenience macro for disabling packages in bulk.
Only use this macro in a module's (or your private) packages.el file."
(macroexp-progn
(cl-loop for p in packages
collect `(package! ,p :disable t))))
(mapcar (lambda (p) `(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)
;;; core-packages.el ends here

View File

@@ -15,23 +15,18 @@ Emacs.")
"fd")
"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
(use-package! projectile
:after-call after-find-file dired-before-readin-hook minibuffer-setup-hook
:commands (projectile-project-root
projectile-project-name
projectile-project-p
projectile-locate-dominating-file)
:init
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
projectile-enable-caching doom-interactive-mode
projectile-files-cache-expire 86400 ; expire after a day
projectile-enable-caching doom-interactive-p
projectile-globally-ignored-files '(".DS_Store" "Icon
" "TAGS")
projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")
@@ -44,28 +39,47 @@ Emacs.")
:config
(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
;; 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
".git") ; Git VCS root dir
(when (executable-find "hg")
'(".hg")) ; Mercurial VCS root dir
(when (executable-find "fossil")
'(".fslckout" ; Fossil VCS root dir
'(".hg")) ; Mercurial VCS root dir
(when (executable-find "bzr")
'(".bzr")) ; Bazaar VCS root dir
(when (executable-find "darcs")
(when (executable-find "bzr")
'(".bzr"))) ; Bazaar VCS root dir
;; This will be filled by other modules. We build this list manually so
;; projectile doesn't perform so many file checks every time it resolves
;; a project's root -- particularly when a file has no project.
projectile-project-root-files '("TAGS")
;; a project's root -- particularly when a file has no project.
projectile-project-root-files '()
projectile-project-root-files-top-down-recurring '("Makefile"))
(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
;; 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
private. (see `doom-projectile-cache-blacklist'),
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)
(cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist)
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
projectile-project-root-files)
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
;; 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
;; .gitignore. This is recommended in the projectile docs.
((executable-find doom-projectile-fd-binary)
(setq projectile-generic-command
(format "%s . --color=never --type f -0 -H -E .git"
doom-projectile-fd-binary)
(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"
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=//")))
projectile-git-submodule-command nil
;; 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
((executable-find "rg")
(setq projectile-generic-command
(concat "rg -0 --files --color=never --hidden"
(cl-loop for dir in projectile-globally-ignored-directories
concat (format " --glob '!%s'" dir)))
(setq projectile-generic-command
(lambda (_)
(concat "rg -0 --files --follow --color=never --hidden"
(cl-loop for dir in projectile-globally-ignored-directories
concat " --glob "
concat (shell-quote-argument (concat "!" dir)))
(if IS-WINDOWS " --path-separator //")))
projectile-git-submodule-command nil
;; ensure Windows users get rg's benefits
projectile-indexing-method 'alien))
;; Fix breakage on windows in git projects with submodules, since Windows
;; doesn't have tr
;; doesn't have tr
(IS-WINDOWS
(setq projectile-git-submodule-command nil)))
(defadvice! doom--projectile-cache-timers-a ()
"Persist `projectile-projects-cache-time' across sessions, so that
`projectile-files-cache-expire' checks won't reset when restarting Emacs."
:before #'projectile-serialize-cache
(projectile-serialize projectile-projects-cache-time doom-projectile-cache-timer-file))
;; Restore it
(when (file-readable-p doom-projectile-cache-timer-file)
(setq projectile-projects-cache-time
(projectile-unserialize doom-projectile-cache-timer-file)))
(defadvice! doom--projectile-default-generic-command-a (orig-fn &rest args)
"If projectile can't tell what kind of project you're in, it issues an error
@@ -169,12 +194,11 @@ the command instead."
;; Projectile root-searching functions can cause an infinite loop on TRAMP
;; connections, so disable them.
;; TODO Is this still necessary?
;; TODO Is this still necessary?
(defadvice! doom--projectile-locate-dominating-file-a (file _name)
"Don't traverse the file system if on a remote connection."
:around #'projectile-locate-dominating-file
(when (and (stringp file)
(not (file-remote-p file nil t)))
"Don't traverse the file system if on a remote connection."
:before-while #'projectile-locate-dominating-file
(and (stringp file)
(not (file-remote-p file nil t)))))
@@ -193,16 +217,15 @@ state are passed in.")
add-hooks
on-load
on-enter
on-exit)
"Define a project minor-mode named NAME (a symbol) and declare where and how
it is activated. Project modes allow you to configure 'sub-modes' for
major-modes that are specific to a folder, project structure, framework or
whatever arbitrary context you define. These project modes can have their own
on-exit)
"Define a project minor mode named NAME and where/how it is activated.
Project modes allow you to configure 'sub-modes' for major-modes that are
specific to a folder, project structure, framework or whatever arbitrary context
you define. These project modes can have their own settings, keymaps, hooks,
snippets, etc.
This creates NAME-hook and NAME-map as well.
A project can be enabled through .dir-locals.el too, by setting `doom-project'.
PLIST may contain any of these properties, which are all checked to see if NAME
should be activated. If they are *all* true, NAME is activated.

View File

@@ -54,6 +54,9 @@ examples.
It is recommended you don't set specify a font-size, as to inherit `doom-font's
size.")
(defvar doom-unicode-extra-fonts nil
"Fonts to inject into the unicode charset before `doom-unicode-font'.")
;;
;;; Custom hooks
@@ -85,45 +88,51 @@ size.")
(defvar doom--last-frame nil)
(defun doom-run-switch-window-hooks-h ()
(let ((gc-cons-threshold most-positive-fixnum))
(unless (or doom-inhibit-switch-window-hooks
(eq doom--last-window (selected-window))
(minibufferp))
(let ((doom-inhibit-switch-window-hooks t))
(run-hooks 'doom-switch-window-hook)
(setq doom--last-window (selected-window))))))
(unless (or doom-inhibit-switch-window-hooks
(eq doom--last-window (selected-window))
(minibufferp))
(let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-window-hooks t)
(inhibit-redisplay t))
(run-hooks 'doom-switch-window-hook)
(setq doom--last-window (selected-window)))))
(defun doom-run-switch-frame-hooks-h (&rest _)
(unless (or doom-inhibit-switch-frame-hooks
(eq doom--last-frame (selected-frame))
(frame-parameter nil 'parent-frame))
(let ((doom-inhibit-switch-frame-hooks t))
(let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-frame-hooks t))
(run-hooks 'doom-switch-frame-hook)
(setq doom--last-frame (selected-frame)))))
(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
(eq (current-buffer) (get-buffer buffer-or-name))
(and (eq orig-fn #'switch-to-buffer) (car args)))
(apply orig-fn buffer-or-name args)
(let ((doom-inhibit-switch-buffer-hooks t))
(when-let (buffer (apply orig-fn buffer-or-name args))
(with-current-buffer (if (windowp buffer)
(window-buffer buffer)
buffer)
(run-hooks 'doom-switch-buffer-hook))
buffer)))))
(if (or doom-inhibit-switch-buffer-hooks
(and buffer-or-name
(eq (current-buffer)
(get-buffer buffer-or-name)))
(and (eq orig-fn #'switch-to-buffer) (car args)))
(apply orig-fn buffer-or-name args)
(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))
(with-current-buffer (if (windowp buffer)
(window-buffer buffer)
buffer)
(run-hooks 'doom-switch-buffer-hook))
buffer))))
(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
(apply orig-fn args)
(let ((doom-inhibit-switch-buffer-hooks t))
(when-let (buffer (apply orig-fn args))
(with-current-buffer buffer
(run-hooks 'doom-switch-buffer-hook))
buffer)))))
(if doom-inhibit-switch-buffer-hooks
(apply orig-fn args)
(let ((gc-cons-threshold most-positive-fixnum)
(doom-inhibit-switch-buffer-hooks t)
(inhibit-redisplay t))
(when-let (buffer (apply orig-fn args))
(with-current-buffer buffer
(run-hooks 'doom-switch-buffer-hook))
buffer))))
(defun doom-protect-fallback-buffer-h ()
"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
indent with tabs, spaces at BOL are highlighted.
Does nothing if `whitespace-mode' is already active or the current buffer is
read-only or not file-visiting."
Does nothing if `whitespace-mode' or 'global-whitespace-mode' is already
active or if the current buffer is read-only or not file-visiting."
(unless (or (eq major-mode 'fundamental-mode)
buffer-read-only
(bound-and-true-p global-whitespace-mode)
(null buffer-file-name))
(require 'whitespace)
(set (make-local-variable 'whitespace-style)
@@ -156,6 +166,10 @@ read-only or not file-visiting."
;; Simpler confirmation prompt when killing Emacs
(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
;; no beeping or blinking please
ring-bell-function #'ignore
@@ -173,7 +187,12 @@ read-only or not file-visiting."
(setq hscroll-margin 2
hscroll-step 1
scroll-conservatively 10
;; Emacs spends too much effort recentering the screen if you scroll the
;; cursor more than N lines past window edges (where N is the settings of
;; `scroll-conservatively'). This is especially slow in larger files
;; during large-scale scrolling commands. If kept over 100, the window is
;; never automatically recentered.
scroll-conservatively 101
scroll-margin 0
scroll-preserve-screen-position t
;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll'
@@ -195,12 +214,18 @@ read-only or not file-visiting."
;;
;;; 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)
;; Don't blink the paren matching the one at point, it's too distracting.
(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)
;; 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.
(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 _)
"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
indicate-empty-lines nil)
;; remove continuation arrow on right fringe
(delq! 'continuation fringe-indicator-alist 'assq)
;;
;;; Windows/frames
@@ -272,40 +292,53 @@ windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
(setq frame-title-format '("%b Doom Emacs")
icon-title-format frame-title-format)
;; Don't resize emacs in steps, it looks weird.
;; Don't resize windows & frames in steps; it's prohibitive to prevent the user
;; from resizing it to exact dimensions, and looks weird.
(setq window-resize-pixelwise t
frame-resize-pixelwise t)
(unless EMACS27+ ; We already do this in early-init.el
;; Disable tool and scrollbars; Doom encourages keyboard-centric workflows, so
;; these are just clutter (the scrollbar also impacts Emacs' performance).
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist))
(unless (assq 'menu-bar-lines default-frame-alist)
;; We do this in early-init.el too, but in case the user is on Emacs 26 we do
;; it here too: disable tool and scrollbars, as Doom encourages
;; keyboard-centric workflows, so these are just clutter (the scrollbar also
;; impacts performance).
(add-to-list 'default-frame-alist '(menu-bar-lines . 0))
(add-to-list 'default-frame-alist '(tool-bar-lines . 0))
(add-to-list 'default-frame-alist '(vertical-scroll-bars)))
;; Sets `ns-appearance' and `ns-transparent-titlebar' on GUI frames (and fixes
;; mismatching text color in the frame title)
(when IS-MAC
;; These are disabled directly through their frame parameters, to avoid the
;; extra work their minor modes do, but we have to unset these variables
;; 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!
;; NOTE Meaningless to railwaycat's emacs-mac build
(setq ns-use-native-fullscreen nil
;; Visit files opened outside of Emacs in existing frame, rather than a
;; new one
ns-pop-up-frames nil)
(setq ns-use-native-fullscreen nil)
;; Sets ns-transparent-titlebar and ns-appearance frame parameters as is
;; appropriate for the loaded theme.
;; Visit files opened outside of Emacs in existing frame, not a new one
(setq ns-pop-up-frames nil)
;; Sets `ns-transparent-titlebar' and `ns-appearance' frame parameters so
;; window borders will match the enabled theme.
(and (or (daemonp)
(display-graphic-p))
(require 'ns-auto-titlebar nil t)
(ns-auto-titlebar-mode +1))
(add-hook! 'after-make-frame-functions
(defun doom-init-menu-bar-in-gui-frames-h (frame)
"On MacOS, the menu bar isn't part of the frame. Disabling it makes MacOS
treat Emacs as a non-application window."
(when (display-graphic-p frame)
(set-frame-parameter frame 'menu-bar-lines 1)))))
;; HACK On MacOS, disabling the menu bar makes MacOS treat Emacs as a
;; non-application window -- which means it doesn't automatically capture
;; focus when it is started, among other things. We enable menu-bar-lines
;; there, but we still want it disabled in terminal frames because there
;; it activates an ugly menu bar.
(add-hook! '(window-setup-hook after-make-frame-functions)
(defun doom-init-menu-bar-in-gui-frames-h (&optional frame)
"Re-enable menu-bar-lines in GUI frames."
(when-let (frame (or frame (selected-frame)))
(when (display-graphic-p frame)
(set-frame-parameter frame 'menu-bar-lines 1))))))
;; The native border "consumes" a pixel of the fringe on righter-most splits,
;; `window-divider' does not. Available since Emacs 25.1.
@@ -314,18 +347,21 @@ treat Emacs as a non-application window."
window-divider-default-right-width 1)
(add-hook 'doom-init-ui-hook #'window-divider-mode)
;; Prompt the user for confirmation when deleting a non-empty frame
(global-set-key [remap delete-frame] #'doom/delete-frame)
;; Prompt for confirmation when deleting a non-empty frame; a last line of
;; defense against accidental loss of work.
(global-set-key [remap delete-frame] #'doom/delete-frame-with-prompt)
;; always avoid GUI
(setq use-dialog-box nil)
;; Don't display floating tooltips; display their contents in the echo-area.
(if (bound-and-true-p tooltip-mode) (tooltip-mode -1))
;; native linux tooltips are ugly
;; Don't display floating tooltips; display their contents in the echo-area,
;; because native tooltips are ugly.
(when (bound-and-true-p tooltip-mode)
(tooltip-mode -1))
;; ...especially on linux
(when IS-LINUX
(setq x-gtk-use-system-tooltips nil))
;; Favor vertical splits over horizontal ones
;; Favor vertical splits over horizontal ones. Screens are usually wide.
(setq split-width-threshold 160
split-height-threshold nil)
@@ -334,11 +370,11 @@ treat Emacs as a non-application window."
;;; Minibuffer
;; Allow for minibuffer-ception. Sometimes we need another minibuffer command
;; _while_ we're in the minibuffer.
;; while we're in the minibuffer.
(setq enable-recursive-minibuffers t)
;; Show current key-sequence in minibuffer, like vim does. Any feedback after
;; typing is better UX than no feedback at all.
;; Show current key-sequence in minibuffer ala 'set showcmd' in vim. Any
;; feedback after typing is better UX than no feedback at all.
(setq echo-keystrokes 0.02)
;; 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)
;; 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
;; portion of the minibuffer.
@@ -387,42 +423,36 @@ treat Emacs as a non-application window."
(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
;; 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
;; 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.
(setq hl-line-sticky-flag nil
global-hl-line-sticky-flag nil)
;; Disable `hl-line' in evil-visual mode (temporarily). `hl-line' can make the
;; selection region harder to see while in evil visual mode.
(after! evil
(defvar doom-buffer-hl-line-mode nil)
(add-hook! 'evil-visual-state-entry-hook
(defun doom-disable-hl-line-h ()
(when hl-line-mode
(setq-local doom-buffer-hl-line-mode t)
(hl-line-mode -1))))
(add-hook! 'evil-visual-state-exit-hook
(defun doom-enable-hl-line-maybe-h ()
(when doom-buffer-hl-line-mode
(hl-line-mode +1))))))
;; Temporarily disable `hl-line' when selection is active, since it doesn't
;; serve much purpose when the selection is so much more visible.
(defvar doom--hl-line-mode nil)
(add-hook! '(evil-visual-state-entry-hook activate-mark-hook)
(defun doom-disable-hl-line-h ()
(when hl-line-mode
(setq-local doom--hl-line-mode t)
(hl-line-mode -1))))
(add-hook! '(evil-visual-state-exit-hook deactivate-mark-hook)
(defun doom-enable-hl-line-maybe-h ()
(when doom--hl-line-mode
(hl-line-mode +1)))))
(use-package! winner
;; 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
:config (winner-mode +1)
:hook (doom-first-buffer . winner-mode)
:config
(appendq! winner-boring-buffers
'("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*"
"*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*"
@@ -431,13 +461,12 @@ treat Emacs as a non-application window."
(use-package! paren
;; highlight matching delimiters
:after-call after-find-file doom-switch-buffer-hook
:hook (doom-first-buffer . show-paren-mode)
:config
(setq show-paren-delay 0.1
show-paren-highlight-openparen t
show-paren-when-point-inside-paren t
show-paren-when-point-in-periphery t)
(show-paren-mode +1))
show-paren-when-point-in-periphery t))
;;;###package whitespace
@@ -461,13 +490,31 @@ treat Emacs as a non-application window."
all-the-icons-wicon
all-the-icons-material
all-the-icons-alltheicon)
:init
(defadvice! doom--disable-all-the-icons-in-tty-a (orig-fn &rest args)
"Return a blank string in tty Emacs, which doesn't support multiple fonts."
:around #'all-the-icons-insert
(if (display-multi-font-p)
(apply orig-fn args)
"")))
: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)
"Return a blank string in tty Emacs, which doesn't support multiple fonts."
:around '(all-the-icons-octicon all-the-icons-material
all-the-icons-faicon all-the-icons-fileicon
all-the-icons-wicon all-the-icons-alltheicon)
(if (or (not after-init-time) (display-multi-font-p))
(apply orig-fn args)
"")))
((not (display-graphic-p))
(defadvice! doom--disable-all-the-icons-in-tty-a (&rest _)
"Return a blank string for tty users."
:override '(all-the-icons-octicon all-the-icons-material
all-the-icons-faicon all-the-icons-fileicon
all-the-icons-wicon all-the-icons-alltheicon)
""))))
;;;###package hide-mode-line-mode
(add-hook! '(completion-list-mode-hook Man-mode-hook)
@@ -486,10 +533,6 @@ treat Emacs as a non-application window."
;; languages like Lisp.
(setq rainbow-delimiters-max-face-count 3)
;;;###package pos-tip
(setq pos-tip-internal-border-width 6
pos-tip-border-width 1)
;;
;;; Line numbers
@@ -497,16 +540,16 @@ treat Emacs as a non-application window."
;; Explicitly define a width to reduce computation
(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)
;; 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)
#'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
@@ -514,35 +557,44 @@ treat Emacs as a non-application window."
;; Underline looks a bit better when drawn lower
(setq x-underline-at-descent-line t)
;; DEPRECATED In Emacs 27
(defvar doom--prefer-theme-elc nil
"If non-nil, `load-theme' will prefer the compiled theme (unlike its default
behavior). Do not set this directly, this is let-bound in `doom-init-theme-h'.")
(defun doom-init-fonts-h ()
"Loads `doom-font'."
(cond (doom-font
(cl-pushnew
(cons 'font
(cond ((stringp doom-font) doom-font)
((fontp doom-font) (font-xlfd-name doom-font))
((signal 'wrong-type-argument (list '(fontp stringp)
doom-font)))))
default-frame-alist
:key #'car :test #'eq))
((display-graphic-p)
(setq font-use-system-font t
doom-font (face-attribute 'default :font)))))
(cond
(doom-font
(cl-pushnew
;; Avoiding `set-frame-font' because it does a lot of extra, expensive
;; work we can avoid by setting the font frame parameter instead.
(cons 'font
(cond ((stringp doom-font) doom-font)
((fontp doom-font) (font-xlfd-name doom-font))
((signal 'wrong-type-argument (list '(fontp stringp)
doom-font)))))
default-frame-alist
:key #'car :test #'eq))
((display-graphic-p)
;; We try our best to record your system font, so `doom-big-font-mode'
;; can still use it to compute a larger font size with.
(setq font-use-system-font t
doom-font (face-attribute 'default :font)))))
(defun doom-init-extra-fonts-h (&optional frame)
"Loads `doom-variable-pitch-font',`doom-serif-font' and `doom-unicode-font'."
(condition-case e
(with-selected-frame (or frame (selected-frame))
(when doom-font
(set-face-attribute 'fixed-pitch nil :font doom-font))
(when doom-serif-font
(set-face-attribute 'fixed-pitch-serif nil :font doom-serif-font))
(when doom-variable-pitch-font
(set-face-attribute 'variable-pitch nil :font doom-variable-pitch-font))
(when (and doom-unicode-font (fboundp 'set-fontset-font))
(set-fontset-font t 'unicode doom-unicode-font nil 'prepend)))
(when (fboundp 'set-fontset-font)
(dolist (font (append doom-unicode-extra-fonts (doom-enlist doom-unicode-font)))
(set-fontset-font t 'unicode font nil 'prepend))))
((debug error)
(if (string-prefix-p "Font not available: " (error-message-string e))
(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."
(when (and doom-theme (not (memq doom-theme custom-enabled-themes)))
(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)))))
(defadvice! doom--run-load-theme-hooks-a (theme &optional _no-confirm no-enable)
"Set up `doom-load-theme-hook' to run after `load-theme' is called."
:after-while #'load-theme
(unless no-enable
(setq doom-theme theme
doom-init-theme-p t)
(run-hooks 'doom-load-theme-hook)))
(defadvice! doom--load-theme-a (orig-fn theme &optional no-confirm no-enable)
"Run `doom-load-theme-hook' on `load-theme' and fix its issues.
(defadvice! doom--prefer-compiled-theme-a (orig-fn &rest args)
"Make `load-theme' prioritize the byte-compiled theme for a moderate boost in
startup (or theme switch) time, so long as `doom--prefer-theme-elc' is non-nil."
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
(if (or (null after-init-time)
doom--prefer-theme-elc)
(cl-letf* ((old-locate-file (symbol-function 'locate-file))
((symbol-function 'locate-file)
(lambda (filename path &optional _suffixes predicate)
(funcall old-locate-file filename path '("c" "") predicate))))
(apply orig-fn args))
(apply orig-fn args)))
;; 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
(setq doom-theme theme
doom-init-theme-p t)
;; `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)
"Have `load-theme' prioritize the byte-compiled theme.
This offers a moderate boost in startup (or theme switch) time, so long as
`doom--prefer-theme-elc' is non-nil."
:around #'load-theme
(if (or (null after-init-time)
doom--prefer-theme-elc)
(letf! (defun locate-file (filename path &optional _suffixes predicate)
(funcall locate-file filename path '("c" "") predicate))
(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)
;; Initialize custom switch-{buffer,window,frame} hooks:
;;
;; + `doom-switch-buffer-hook'
;; + `doom-switch-window-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 'focus-in-hook #'doom-run-switch-frame-hooks-h)
(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
;; 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
;; disable them.
(put 'customize 'disabled "Doom doesn't support `customize', configure Emacs from $DOOMDIR/config.el instead")
;; that sets variables at a time where it can be easily and unpredictably
;; overwritten. Configure things from your $DOOMDIR instead.
(dolist (sym '(customize-option customize-browse customize-group customize-face
customize-rogue customize-saved customize-apropos
customize-changed customize-unsaved customize-variable
customize-set-value customize-customized customize-set-variable
customize-apropos-faces customize-save-variable
customize-apropos-groups customize-apropos-options
customize-changed-options customize-save-customized))
(put sym 'disabled "Doom doesn't support `customize', configure Emacs from $DOOMDIR/config.el instead"))
(put 'customize-themes 'disabled "Set `doom-theme' or use `load-theme' in $DOOMDIR/config.el instead")
;; doesn't exist in terminal Emacs; 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)
(fset 'define-fringe-bitmap #'ignore))
(after! whitespace
(defun doom-disable-whitespace-mode-in-childframes-a (orig-fn)
"`whitespace-mode' inundates child frames with whitspace markers, so disable
it to fix all that visual noise."
"`whitespace-mode' inundates child frames with whitespace markers, so
disable it to fix all that visual noise."
(unless (frame-parameter nil 'parent-frame)
(funcall orig-fn)))
(add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a))

View File

@@ -1,41 +1,58 @@
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
(when (version< emacs-version "26.1")
(error "Detected Emacs %s. Doom only supports Emacs 26.1 and higher"
emacs-version))
(eval-when-compile
(when (< emacs-major-version 26)
(error "Detected Emacs v%s. Doom only supports Emacs 26 and newer"
emacs-version)))
;;
;;; Variables
(defconst doom-version "2.0.9"
"Current version of Doom Emacs.")
(defconst EMACS27+ (> emacs-major-version 26))
(defconst EMACS28+ (> emacs-major-version 27))
(defconst IS-MAC (eq system-type 'darwin))
(defconst IS-LINUX (eq system-type 'gnu/linux))
(defconst IS-WINDOWS (memq system-type '(cygwin windows-nt ms-dos)))
(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'
(add-to-list 'load-path (file-name-directory load-file-name))
(defvar doom--initial-load-path load-path)
(defvar doom--initial-process-environment process-environment)
(defvar doom--initial-exec-path exec-path)
(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)
;; `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)
;; Restore `file-name-handler-alist', because it is needed for handling
;; encrypted or compressed files, among other things.
(defun doom-reset-file-handler-alist-h ()
(setq file-name-handler-alist doom--initial-file-name-handler-alist))
(add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h)
(setq file-name-handler-alist nil)
;; Restore `file-name-handler-alist', because it is needed for handling
;; encrypted or compressed files, among other things.
(defun doom-reset-file-handler-alist-h ()
;; 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))
(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)
(autoload 'doom-initialize-packages "core-packages")
;;
;;; Global variables
@@ -46,13 +63,13 @@
(defvar doom-init-time nil
"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.
Use `doom/toggle-debug-mode' to toggle it. The --debug-init flag and setting the
DEBUG envvar will enable this at startup.")
Use `doom-debug-mode' to toggle it. The --debug-init flag and setting the DEBUG
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.")
;;; 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
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")
"The location of your envvar file, generated by `doom env refresh`.
"The location of your envvar file, generated by `doom env`.
This file contains environment variables scraped from your shell environment,
which is loaded at startup (if it exists). This is helpful if Emacs can't
@@ -127,7 +138,7 @@ users).")
;;; Custom error types
(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-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-private-error "Error in private config" 'doom-error)
(define-error 'doom-package-error "Error with packages" 'doom-error)
@@ -136,84 +147,99 @@ users).")
;;
;;; Emacs core configuration
;; Reduce debug output, well, unless we've asked for it.
(setq debug-on-error doom-debug-mode
jka-compr-verbose doom-debug-mode)
;; lo', longer logs ahoy, so to reliably locate lapses in doom's logic later
(setq message-log-max 8192)
;; 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)
(set-charset-priority 'unicode)) ; pretty
(prefer-coding-system 'utf-8) ; pretty
(setq locale-coding-system 'utf-8) ; please
;; Except for the clipboard on Windows, where its contents could be in an
;; encoding that's wider than utf-8, so we let Emacs/the OS decide what encoding
;; to use.
;; The clipboard's on Windows could be in an encoding that's wider (or thinner)
;; than utf-8, so let Emacs/the OS decide what encoding to use there.
(unless IS-WINDOWS
(setq selection-coding-system 'utf-8)) ; with sugar on top
;; Disable warnings from legacy advice system. They aren't useful, and we can't
;; often do anything about them besides changing packages upstream
;; Disable warnings from legacy advice system. They aren't useful, and what can
;; we do about them, besides changing packages upstream?
(setq ad-redefinition-action 'accept)
;; Make apropos omnipotent. It's more useful this way.
(setq apropos-do-all t)
;; Don't make a second case-insensitive pass over `auto-mode-alist'. If it has
;; to, it's our (the user's) failure. One case for all!
;; A second, case-insensitive pass over `auto-mode-alist' is time wasted, and
;; indicates misconfiguration (or that the user needs to stop relying on case
;; insensitivity).
(setq auto-mode-case-fold nil)
;; Make all regexps case-sensitive by default. This favors correctness for
;; 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.
;; Less noise at startup. The dashboard/empty scratch buffer is good enough.
(setq inhibit-startup-message t
inhibit-startup-echo-area-message user-login-name
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-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
;; slightly, from 0.5s:
(setq idle-update-delay 1)
;; slightly from 0.5s:
(setq idle-update-delay 1.0)
;; Emacs is a huge security vulnerability, what with all the dependencies it
;; pulls in from all corners of the globe. Let's at least try to be more
;; discerning.
(setq gnutls-verify-error (getenv "INSECURE")
;; Emacs is essentially one huge security vulnerability, what with all the
;; dependencies it pulls in from all corners of the globe. Let's try to be at
;; least a little more discerning.
(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-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
"gnutls-cli -p %p %h"
"openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof"))
"gnutls-cli -p %p %h"))
;; Emacs stores authinfo in HOME and in plaintext. Let's not do that, mkay? This
;; file usually stores usernames, passwords, and other such treasures for the
;; Emacs stores `authinfo' in $HOME and in plain-text. Let's not do that, mkay?
;; This file stores usernames, passwords, and other such treasures for the
;; 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"))
;; Emacs on Windows frequently confuses HOME (C:\Users\<NAME>) and APPDATA,
;; causing `abbreviate-home-dir' to produce incorrect paths.
(when IS-WINDOWS
(setq abbreviated-home-dir "\\`'"))
;; Don't litter `doom-emacs-dir'
;; Don't litter `doom-emacs-dir'. We don't use `no-littering' because it's a
;; mote too opinionated for our needs.
(setq abbrev-file-name (concat doom-local-dir "abbrev.el")
async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log")
bookmark-default-file (concat doom-etc-dir "bookmarks")
custom-file (concat doom-private-dir "init.el")
custom-file (concat doom-private-dir "config.el")
custom-theme-directory (concat doom-private-dir "themes/")
desktop-dirname (concat doom-etc-dir "desktop")
desktop-base-file-name "autosave"
desktop-base-lock-name "autosave-lock"
pcache-directory (concat doom-cache-dir "pcache/")
request-storage-directory (concat doom-cache-dir "request")
server-auth-dir (concat doom-cache-dir "server/")
shared-game-score-directory (concat doom-etc-dir "shared-game-score/")
tramp-auto-save-directory (concat doom-cache-dir "tramp-auto-save/")
tramp-backup-directory-alist backup-directory-alist
@@ -227,13 +253,21 @@ users).")
:override #'emacs-session-filename
(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
;; Disable bidirectional text rendering for a modest performance boost. Of
;; course, this renders Emacs unable to detect/display right-to-left languages
;; (sorry!), but for us left-to-right language speakers/writers, it's a boon.
;; Disable bidirectional text rendering for a modest performance boost. I've set
;; this to `nil' in the past, but the `bidi-display-reordering's docs say that
;; is an undefined state and suggest this to be just as good:
(setq-default bidi-display-reordering 'left-to-right
bidi-paragraph-direction 'left-to-right)
@@ -243,7 +277,8 @@ users).")
(setq highlight-nonselected-windows nil)
;; 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)
;; 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.
(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
;; WSL is involved. We'll need everything we can get.
(when IS-WINDOWS
;; Reduce the workload when doing file IO
(setq w32-get-true-file-attributes nil)
;; 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))
(setq w32-get-true-file-attributes nil ; slightly faster IO
w32-pipe-read-delay 0 ; faster ipc
w32-pipe-buffer-size (* 64 1024))) ; read more at a time (was 4K)
;; Remove command line options that aren't relevant to our current OS; means
;; slightly less to process at startup.
(unless IS-MAC (setq command-line-ns-option-alist nil))
(unless IS-LINUX (setq command-line-x-option-alist nil))
;; 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
;; collect; staving off the collector while the user is working.
(when doom-interactive-mode
(add-transient-hook! 'pre-command-hook (gcmh-mode +1))
(with-eval-after-load 'gcmh
(setq gcmh-idle-delay 10
gcmh-verbose doom-debug-mode)
(add-hook 'focus-out-hook #'gcmh-idle-garbage-collect)))
(setq gcmh-idle-delay 5
gcmh-high-cons-threshold (* 16 1024 1024) ; 16mb
gcmh-verbose doom-debug-p)
;; HACK `tty-run-terminal-initialization' is *tremendously* slow for some
;; reason. Disabling it completely could have many side-effects, so we
;; defer it until later, 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-hook-wrapped (intern-soft (format "%s-local-vars-hook" major-mode))
#'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
;; `hack-local-variables-hook' is never triggered, so we trigger it at the end
;; 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."
(unless enable-local-variables
(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
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.
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.")
(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)
"Registers PACKAGES to be loaded incrementally.
If NOW is non-nil, load PACKAGES incrementally, in `doom-incremental-idle-timer'
intervals."
(if (not now)
(nconc doom-incremental-packages packages)
(appendq! doom-incremental-packages packages)
(while packages
(let ((req (pop packages)))
(unless (featurep req)
@@ -357,7 +403,7 @@ intervals."
t)
(push req packages))
((error debug)
(message "Failed to load '%s' package incrementally, because: %s"
(message "Failed to load %S package incrementally, because: %s"
req e)))
(if (not packages)
(doom-log "Finished incremental loading")
@@ -370,33 +416,32 @@ intervals."
"Begin incrementally loading packages in `doom-incremental-packages'.
If this is a daemon session, load them all immediately instead."
(if (daemonp)
(if doom-incremental-load-immediately
(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
nil #'doom-load-packages-incrementally
(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
(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)
"Display a benchmark, showing number of packages and modules, and how quickly
they were loaded at startup.
"Display a benchmark including number of packages and modules loaded.
If RETURN-P, return the message as a string instead of displaying it."
(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
(float-time (time-subtract (current-time) before-init-time))))))
(defun doom-load-autoloads-file (file &optional noerror)
"Tries to load FILE (an autoloads file).
Return t on success, nil otherwise (but logs a warning)."
(condition-case e
(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)
(defun doom-initialize (&optional force-p)
"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
packages are installed, `doom-autoload-file' and `doom-package-autoload-file'
exist and are loaded, and that `core-packages' is auto-loaded when `package' or
`straight' are.
The bootstrap process ensures that everything Doom needs to run is set up;
essential directories exist, core packages are installed, `doom-autoload-file'
is loaded (failing if it isn't), that all the needed hooks are in place, and
that `core-packages' will load when `package' or `straight' is used.
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
~/.doom.d/config.el
`doom-init-modules-hook'
`after-init-hook'
`doom-after-init-modules-hook' (`after-init-hook')
`emacs-startup-hook'
`doom-init-ui-hook'
`window-setup-hook'
@@ -480,74 +480,67 @@ for a list of all recognized module trees. Order defines precedence (from most
to least)."
(when (or force-p (not doom-init-p))
(setq doom-init-p t)
(doom-log "Initializing Doom")
;; 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
load-path doom--initial-load-path
process-environment doom--initial-process-environment)
;; Load shell environment, optionally generated from 'doom env'. No need to
;; do so if we're in terminal Emacs, because Emacs will correctly inherit
;; Doom caches a lot of information in `doom-autoload-file'. Module and
;; 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.
(when (and (or (display-graphic-p)
(daemonp))
(file-exists-p doom-env-file))
(doom-load-envvars-file doom-env-file))
(if (or (display-graphic-p)
(daemonp))
(doom-load-envvars-file doom-env-file 'noerror))
;; Loads `use-package' and all the helper macros modules (and users) can use
;; to configure their packages.
(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))
;; In case we want to use package.el or straight via M-x
(progn
(with-eval-after-load 'package
(require 'core-packages))
(with-eval-after-load 'straight
(require 'core-packages)
(doom-initialize-packages)))
;; There's a chance the user will want to use package.el or straight later
;; on in this interactive session. If that's the case, make sure they're
;; properly initialized when they do.
(autoload 'doom-initialize-packages "core-packages")
(autoload 'doom-initialize-core-packages "core-packages")
(with-eval-after-load 'package (require 'core-packages))
(with-eval-after-load 'straight (doom-initialize-packages))
;; Eagerly load these libraries because we may be in a session that hasn't been
;; fully initialized (e.g. where autoloads files haven't been generated or
;; `load-path' populated).
(mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix)
(file-expand-wildcards (concat doom-core-dir "autoload/*.el")))
;; Bootstrap the interactive session
(add-hook! 'window-setup-hook
(add-hook 'hack-local-variables-hook #'doom-run-local-var-hooks-h)
(add-hook 'after-change-major-mode-hook #'doom-run-local-var-hooks-maybe-h 'append)
(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
(dolist (dir (list doom-local-dir
doom-etc-dir
doom-cache-dir))
(unless (file-directory-p dir)
(make-directory dir 'parents)))
;; Load core/core-*.el, the user's private init.el and their config.el
(doom-initialize-modules force-p))
;; Ensure the package management system (and straight) are ready for
;; 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))
doom-init-p)
(provide 'core)
;;; core.el ends here

View File

@@ -2,39 +2,60 @@
;;; core/packages.el
;; core.el
(package! dotenv-mode)
(package! auto-minor-mode)
(package! auto-minor-mode :pin "17cfa1b54800fdef2975c0c0531dad34846a5065")
(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
(package! all-the-icons)
(package! hide-mode-line)
(package! highlight-numbers)
(package! rainbow-delimiters)
(package! restart-emacs)
(package! all-the-icons :pin "ed8e44de4fa601309d2bba902c3b37cb73e4daa0")
(package! hide-mode-line :pin "88888825b5b27b300683e662fa3be88d954b1cea")
(package! highlight-numbers :pin "8b4744c7f46c72b1d3d599d4fb75ef8183dee307")
(package! rainbow-delimiters :pin "5125f4e47604ad36c3eb4706310fcafac729ca8c")
(package! restart-emacs :pin "9aa90d3df9e08bc420e1c9845ee3ff568e911bd9")
;; core-editor.el
(package! better-jumper)
(package! dtrt-indent)
(package! helpful)
(package! ns-auto-titlebar :ignore (not IS-MAC))
(package! pcre2el)
(package! smartparens)
(package! better-jumper :pin "6d240032ca213ccb3347e25f26c29b6822bf03a7")
(package! dtrt-indent :pin "50c440c80e0d15303d8ab543bce4c56e9c2bf407")
(package! helpful :pin "c0662aa07266fe204f4e6d72ccaa6af089400556")
(when IS-MAC
(package! ns-auto-titlebar :pin "1efc30d38509647b417f05587fd7003457719256"))
(package! pcre2el :pin "0b5b2a2c173aab3fd14aac6cf5e90ad3bf58fa7d")
(package! smartparens :pin "555626a43f9bb1985aa9a0eb675f2b88b29702c8")
(package! so-long
:built-in 'prefer
:built-in 'prefer ; included in Emacs 27+
;; REVIEW so-long is slated to be published to ELPA eventually, but until then
;; I've created my own mirror for it because git.savannah.gnu.org runs on a
;; potato.
:recipe (:host github :repo "hlissner/emacs-so-long"))
(package! undo-tree
;; Version 0.6.5 is on ELPA which lacks a fix we need, so we install 0.6.6
;; from emacsmirror/undo-tree instead.
:recipe (:host github :repo "emacsmirror/undo-tree"))
(package! ws-butler)
(package! xclip)
;; I've created my own mirror for it because git.savannah.gnu.org runs
;; on a potato.
:recipe (:host github :repo "hlissner/emacs-so-long")
:pin "ed666b0716f60e8988c455804de24b55919e71ca")
(package! ws-butler
;; Use my fork of ws-butler, which has a few choice improvements and
;; optimizations (the original has been abandoned).
:recipe (:host github :repo "hlissner/ws-butler")
:pin "2bb49d3ee7d2cba133bc7e9cdac416cd1c5e4fe0")
(unless IS-WINDOWS
(package! clipetty
:recipe (:host github :repo "spudlyo/clipetty")
:pin "01b39044b9b65fa4ea7d3166f8b1ffab6f740362"))
;; core-projects.el
(package! projectile)
(package! projectile :pin "7e552b6d876014ca5b4609318ca8a202b2a89014")
;; core-keybinds.el
(package! general)
(package! which-key)
(package! general :pin "a0b17d207badf462311b2eef7c065b884462cb7c")
(package! which-key :pin "8b49ae978cceca65967f3544c236f32964ddbed0")

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
modules, run:
1. Don't forget to run 'doom sync', then restart Emacs, after modifying
~/.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
removed, and your autoloads files are up to date. This is important! If you
forget to do this you will get errors!
2. If something goes wrong, run `doom doctor`. It diagnoses common issues with
your environment and setup, and may offer clues about what is wrong.
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
give you clues about what is wrong.
3. Use `bin/doom upgrade` to update Doom. Doing it any other way may require
additional work. When in doubt, run `bin/doom refresh`.
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`.
4. Access Doom's documentation from within Emacs via 'SPC h D' or 'C-h D' (or
'M-x doom/help')
Have fun!

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))))

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)

View File

@@ -1,4 +0,0 @@
;; -*- no-byte-compile: t; -*-
;;; core/test/packages.el
(package! buttercup)

View File

@@ -3,9 +3,9 @@
(describe "core/autoload/format"
(describe "format!"
:var (doom-format-backend)
:var (doom-output-backend)
(before-all
(setq doom-format-backend 'ansi))
(setq doom-output-backend 'ansi))
(it "should be a drop-in replacement for `format'"
(expect (format! "Hello %s" "World")
@@ -16,7 +16,7 @@
:to-equal "Hello World"))
(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"))
:to-equal (list :foreground (face-foreground 'term-color-red)))))

View File

@@ -101,12 +101,6 @@
(expect (appendq! list '(d e f))
:to-equal '(a b c d e f)))))
(describe "nconcq!"
(it "nconc's a list to a list symbol"
(let ((list '(a b c)))
(expect (nconcq! list '(d e f))
:to-equal '(a b c d e f)))))
(describe "delq!"
(it "delete's a symbol from a list"
(let ((list '(a b c)))

View File

@@ -2,9 +2,9 @@
;;; core/test/test-core.el
(describe "core"
:var (doom-interactive-mode)
:var (doom-interactive-p)
(before-each
(setq doom-interactive-mode nil))
(setq doom-interactive-p nil))
(describe "initialization"
(describe "doom-initialize"
@@ -33,7 +33,7 @@
(expect 'doom-initialize-packages :to-have-been-called))
(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)
(doom-initialize nil 'noerror)
(expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file)

View File

@@ -18,8 +18,8 @@ It is integrated into Helpful, in Doom.
- [[#disable-packages][disable-packages!]]
- [[#doom][doom!]]
- [[#file-exists-p][file-exists-p!]]
- [[#lambda][lambda!]]
- [[#lambda-1][lambda!!]]
- [[#cmd][cmd!]]
- [[#cmd-1][cmd!!]]
- [[#letenv][letenv!]]
- [[#load][load!]]
- [[#map][map!]]
@@ -28,11 +28,12 @@ It is integrated into Helpful, in Doom.
- [[#prependq][prependq!]]
- [[#quiet][quiet!]]
- [[#remove-hook][remove-hook!]]
- [[#setq][setq!]]
- [[#setq-hook][setq-hook!]]
- [[#unsetq-hook][unsetq-hook!]]
- [[#use-package][use-package!]]
- [[#interesting-snippets][Interesting snippets]]
- [[#persist-emacs-initial-frame-size-across-sessions][Persist Emacs' initial frame size across sessions]]
- [[#center-emacs-initial-frame-with-a-fixed-size][Center Emacs' initial frame with a fixed size]]
- [[#persist-emacs-initial-frame-position-dimensions-andor-full-screen-state-across-sessions][Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions]]
- [[#update-cursor-shape-under-terminal-emacs][Update cursor shape under terminal Emacs]]
@@ -237,38 +238,30 @@ It is integrated into Helpful, in Doom.
#+RESULTS:
: /home/hlissner/.emacs.d/LICENSE
*** lambda!
*** cmd!
#+BEGIN_SRC elisp :eval no
(map! "C-j" (lambda! (newline) (indent-according-to-mode)))
;; The `λ!' short-form alias exists. If you have the snippets module enabled and
;; Doom's default snippets, the 'lam' snippet will expand into 'λ!'. Otherwise,
;; you can use `lambda!'.
(map! "C-j" (λ! (newline) (indent-according-to-mode)))
(map! "C-j" (cmd! (newline) (indent-according-to-mode)))
#+END_SRC
*** lambda!!
*** cmd!!
When ~newline~ is passed a numerical prefix argument (=C-u 5 M-x newline=), it
inserts N newlines. We can use ~lambda!!~ to easily create a keybinds that bakes
in the prefix arg into the command call:
inserts N newlines. We can use ~cmd!!~ to easily create a keybinds that bakes in
the prefix arg into the command call:
#+BEGIN_SRC elisp :eval no
(map! "C-j" (lambda!! #'newline 5))
;; The `λ!!' short-form alias exists. If you have the snippets module enabled
;; and Doom's default snippets, a 'lam' snippet is available to expand into
;; 'λ!'. Otherwise, you can use `lambda!!'.
(map! "C-j" (λ!! #'newline 5))
(map! "C-j" (cmd!! #'newline 5))
#+END_SRC
Or to create aliases for functions that behave differently:
#+BEGIN_SRC elisp :eval no
(fset 'insert-5-newlines (lambda!! #'newline 5))
(fset 'insert-5-newlines (cmd!! #'newline 5))
;; The equivalent of C-u M-x org-global-cycle, which resets the org document to
;; its startup visibility settings.
(fset 'org-reset-global-visibility (lambda!! #'org-global-cycle '(4))
(fset 'org-reset-global-visibility (cmd!! #'org-global-cycle '(4))
#+END_SRC
*** letenv!
#+BEGIN_SRC elisp
(letenv! (("SHELL" "/bin/sh"))
@@ -387,7 +380,7 @@ These are side-by-side comparisons, showing how to bind keys with and without
*** package!
#+BEGIN_SRC elisp :eval no
;; To install a package that can be found on ELPA or any of the sources
;; specified in `doom-core-package-sources':
;; specified in `straight-recipe-repositories':
(package! evil)
(package! js2-mode)
(package! rainbow-delimiters)
@@ -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:
(package! ansible :recipe (:nonrecursive t))
;; To install a particular branch, commit or tag:
(package! evil
;; if :host and :fetcher aren't specified, the package manager will fall back
;; to evil's default source provided by their (M)ELPA recipes:
:recipe (:commit "e7bc39de2f961505e8e112da8c1b315ae8afce52"))
;; To pin a package to a specific commit:
(package! evil :pin "e7bc39de2f9")
;; ...or branch:
(package! evil :recipe (:branch "stable"))
(package! evil :recipe (:tag "1.2.9"))
;; To unpin a pinned package:
(package! evil :pin nil)
;; If you share your config between two computers, and don't want bin/doom
;; refresh to delete packages used only on one system, use :ignore
@@ -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)
(remove-hook! (one-mode second-mode) (setq v 5) (setq a 2))
#+END_SRC
*** setq!
#+BEGIN_SRC elisp
;; Each of these have a setter associated with them, which must be triggered in
;; order for their new values to have an effect.
(setq! evil-want-Y-yank-to-eol nil
evil-want-C-u-scroll nil
evil-want-C-d-scroll nil)
#+END_SRC
*** setq-hook!
#+BEGIN_SRC elisp :eval no
;; Set multiple variables after a hook
@@ -515,21 +513,21 @@ These are side-by-side comparisons, showing how to bind keys with and without
:defer-incrementally t)
#+END_SRC
* Interesting snippets
** Persist Emacs' initial frame size across sessions
** Center Emacs' initial frame with a fixed size
#+BEGIN_SRC elisp
(let ((display-height (display-pixel-height))
(display-width (display-pixel-width)))
(add-to-list 'initial-frame-alist
`((left . ,(/ new-frame-width 2))
(top . ,(/ new-frame-height 2))
(width . ,(/ display-width 2))
(height . ,(/ display-height 2)))))
(pushnew! initial-frame-alist
`(left . ,(/ display-width 2))
`(top . ,(/ display-height 2))
`(width . ,display-width)
`(height . ,display-height)))
#+END_SRC
** Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions
#+BEGIN_SRC elisp
;; 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
(setq 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))))))
(defun save-frame-dimensions ()
(doom-cache-set 'last-frame-size
(doom-store-put 'last-frame-size
(list (frame-position)
(frame-width)
(frame-height)
@@ -567,3 +565,24 @@ than unwarranted characters.
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.
** 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

View File

@@ -1,14 +1,14 @@
#+TITLE: Contributing
#+STARTUP: nofold
I can't say Doom Emacs is a one man show anymore. It wouldn't have gotten this
far without the help of folks like you! Still, I struggle to maintain it all;
especially the modules I do not use. If Doom has been useful to you, consider
pitching in and helping me out.
Doom Emacs is an active and ongoing project, maintained mostly by a single
person, but includes the efforts of 200 contributors and growing. There is no
shortage of things that need doing; bugs that need stomping, features that need
implementing, and documentation that needs documenting. If Doom's been useful to
you, convert some caffiene into code; it'd be a huge help!
Help can range from reporting bugs, proposing enhancements, submitting code and
documentation, or just spreading the good word. To ensure no toes get stepped on
or wires crossed, this guide was created to help you help us.
You are welcome to [[https://discord.gg/qvGgnVx][join us on our Discord server]], otherwise read on to learn how
to contribute to our fine corner of the interwebs.
* Table of Contents :TOC_3:
- [[#where-can-i-help][Where can I help?]]
@@ -32,19 +32,20 @@ or wires crossed, this guide was created to help you help us.
- [[#contributing-documentation][Contributing documentation]]
- [[#contributing-to-dooms-manual][Contributing to Doom's manual]]
- [[#contributing-module-documentation][Contributing module documentation]]
- [[#help-keep-packages-up-to-date][Help keep packages up-to-date!]]
- [[#other-ways-to-support-doom-emacs][Other ways to support Doom Emacs]]
- [[#special-thanks][Special thanks]]
* Where can I help?
+ Our [[https://github.com/hlissner/doom-emacs/issues][issue tracker]] has many issues. If you find one that you have an answer to,
please don't hold back!
it would be a huge help!
+ Look for issues tagged [[https://github.com/hlissner/doom-emacs/labels/good%20first%20issue][good first issue]]. These were judged to have a low
barrier of entry.
+ Look for issues tagged [[https://github.com/hlissner/doom-emacs/labels/help%20wanted][help wanted]]. These tend to be a little (or a lot)
harder, and are issues outside my own expertise.
+ If you've encountered a bug, [[https://github.com/hlissner/doom-emacs/issues/new/choose][file a bug report]].
+ The [[https://github.com/hlissner/doom-emacs/projects/3][development roadmap board]] is a rough timeline of what is being worked on
and when. It will give you some idea of what will change and where you can
and when. It will give you an idea of what will change and where you can
redirect your efforts.
+ The [[https://github.com/hlissner/doom-emacs/projects/2][plugins under review board]] lists third party plugins being considered (or
rejected) for inclusion in Doom Emacs. Approved and unclaimed packages are
@@ -54,30 +55,21 @@ or wires crossed, this guide was created to help you help us.
cause) perhaps you can address them at the source.
* TODO Reporting issues
So you've found a problem. Before you fire off that bug report, there are a few
things you should try first:
You've found a problem and you're ready to fire off that bug report. Hold up!
Before you do that, [[file:getting_started.org::*Troubleshoot][have a look at our Troubleshooting guide]]. If none of these
suggestions pan out, /then/ it is time to file a bug report.
+ Make sure your configuration (or Doom Emacs) is *not* byte-compiled. Run ~doom
clean~ to ensure it isn't. *Byte-compilation interferes with debugging!*
+ Run ~bin/doom refresh~ to ensure all plugins are installed and autoload files
generated.
+ Run ~bin/doom doctor~ to diagnose common issues with your system.
+ Check [[file:faq.org::*Common%20Issues][Common Issues]] in the FAQ to see if yours is a known issue.
+ If you happen to know what module(s) are relevant to your issue, check their
documentation (press =<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.
An effective bug report is informative. Please try to provide:
If these suggestions haven't worked for you, it's time [[https://github.com/hlissner/doom-emacs/issues/new/choose][to write a bug report]].
Please make sure of the following before you submit:
+ A backtrace of all mentioned errors.
+ A step-by-step reproduction of the issue.
+ Information about your Doom config and system environment.
+ Screenshots/casts of the issue (if possible).
** TODO Collect backtraces of any error messages
This section will show you how to collect this information.
** Acquire a backtrace from errors
See "[[file:getting_started.org::*How to extract a backtrace from an error][How to extract a backtrace from an error]]" in the [[file:getting_started.org][Getting Started]] guide.
** TODO Create a step-by-step reproduction guide
@@ -165,6 +157,11 @@ contact via our [[https://discord.gg/bcZ6P3y][Discord server]] or [[mailto:henri
** TODO Contributing module documentation
* TODO Help keep packages up-to-date!
Doom pins all its packages to reduce the likelihood of upstream breakage leaking
into Doom Emacs. However, we may miss when a package releases hotfixes for
critical issues. Let us know or PR a bump to our pinned packages.
* TODO Other ways to support Doom Emacs
* TODO Special thanks

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,20 @@
#+TITLE: Doom Emacs Documentation
#+STARTUP: nofold
Doom Emacs is a configuration for [[https://www.gnu.org/software/emacs/][GNU Emacs]] written by a stubborn,
shell-dwelling, and melodramatic ex-vimmer. It is designed to be a foundation
for your own Emacs configuration or a resource for enthusiasts to learn more
about our favorite OS.
Doom is a configuration framework for [[https://www.gnu.org/software/emacs/][GNU Emacs 26.3+]] tailored for Emacs
bankruptcy veterans who want less framework in their frameworks and the
performance of a hand rolled config (or better). It can be a foundation for your
own config or a resource for Emacs enthusiasts to learn more about our favorite
OS.
Doom is an opinionated collection of reasonable (and optional) defaults with a
focus on performance (both runtime and startup) and on abstraction-light,
readable code design, so that there is less between you and Emacs.
#+begin_quote
Github fails to render org links to sub-sections, so it is recommended that you
view the documentation from within Doom Emacs by pressing =<help> d h= (=<help>=
is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
=<help> d /=.
The documentation is designed to be viewed within Doom Emacs. Access it by
pressing =SPC h d h= (or =C-h d h= for non-evil users), or search it with =SPC h
d s= (or =C-h d s=).
#+end_quote
* Table of Contents :TOC:
@@ -20,53 +24,47 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
- [[#frequently-asked-questions][Frequently Asked Questions]]
- [[#contributing][Contributing]]
- [[#workflow-tips-tricks--tutorials][Workflow Tips, Tricks & Tutorials]]
- [[#module-appendix][Module Appendix]]
- [[#community-resources][Community Resources]]
- [[#asking-for-help][Asking for help]]
- [[#project-roadmap][Project roadmap]]
- [[#tutorials--guides][Tutorials & guides]]
- [[#module-list][Module list]]
- [[#app][:app]]
- [[#completion][:completion]]
- [[#config][:config]]
- [[#editor][:editor]]
- [[#emacs][:emacs]]
- [[#email][:email]]
- [[#input][:input]]
- [[#lang][:lang]]
- [[#term][:term]]
- [[#tools][:tools]]
- [[#ui][:ui]]
- [[#projects-that-supportcomplement-doom][Projects that support/complement Doom]]
- [[#similar-projects][Similar projects]]
* TODO Release Notes
* Documentation
** [[file:getting_started.org][Getting Started]]
- [[file:getting_started.org::*Install][Install]] - How to install Emacs, Doom and its plugins
- [[file:getting_started.org::*Update][Update]] - Keep Doom and its packages up-to-date
- [[file:getting_started.org::*Customize][Customize]] - A primer on customizing and reconfiguring Doom
- [[file:getting_started.org::*Troubleshoot][Troubleshoot]] - How to debug Emacs & Doom, find help or look up documentation
- [[file:getting_started.org::*Install][Install]]
- [[file:getting_started.org::*Update & Rollback][Update & Rollback]]
- [[file:getting_started.org::*Configure][Configure]]
- [[file:getting_started.org::*Migrate][Migrate]]
- [[file:getting_started.org::*Troubleshoot][Troubleshoot]]
** [[file:faq.org][Frequently Asked Questions]]
- [[file:faq.org::*General][General]]
- [[file:faq.org::*Configuration][Configuration]]
- [[file:faq.org::*Package Management][Package Management]]
- [[file:faq.org::*Defaults][Defaults]]
- [[file:faq.org::Common Issues][Common Issues]]
- [[file:faq.org::Contributing][Contributing]]
** TODO [[file:contributing.org][Contributing]]
- [[file:contributing.org::*Where can I help?][Where to get help]]
- Writing an effective bug report
- Suggesting features, keybinds or enhancements
- Contributing code
- Contributing documentation
- [[file:contributing.org::*Where can I help?][Where to get help?]]
- Reporting issues
- Suggesting features, keybinds and enhancements
- Contributing code or documentation
- Other ways to support Doom Emacs
- Special thanks
** TODO [[file:workflow.org][Workflow Tips, Tricks & Tutorials]]
** [[file:modules.org][Module Appendix]]
* Community Resources
** Asking for help
- [[https://discord.gg/bcZ6P3y][Our Discord server]]
- [[https://discord.gg/qvGgnVx][Our Discord server]]
- [[https://github.com/hlissner/doom-emacs/issues][Our issue tracker]]
** Project roadmap
@@ -79,11 +77,12 @@ is =SPC h= for evil users and =C-h= for vanilla users) or searching it with
** Tutorials & guides
+ *Doom Emacs*
- (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://medium.com/@aria_39488/getting-started-with-doom-emacs-a-great-transition-from-vim-to-emacs-9bab8e0d8458][Getting Started with Doom Emacs -- a great transition from Vim to Emacs]]
- [[https://medium.com/@aria_39488/the-niceties-of-evil-in-doom-emacs-cabb46a9446b][The Niceties of evil in Doom Emacs]]
- [[https://www.youtube.com/playlist?list=PLhXZp00uXBk4np17N39WvB80zgxlZfVwj][DoomCasts (youtube series)]]
- [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]]
- (video) [[https://www.youtube.com/watch?v=GK3fij-D1G8][Org-mode, literate programming in (Doom) Emacs]]
+ *Emacs & Emacs Lisp*
- [[https://www.gnu.org/software/emacs/manual/html_node/elisp/index.html][The Official Emacs manual]]
- A variety of Emacs resources - https://github.com/ema2159/awesome-emacs
@@ -92,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
- Workflows for customizing Emacs and its packages (and its C/C++ modes):
- https://david.rothlis.net/emacs/customize_c.html
+ *Tools in Emacs*
- [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]]
- *Tools in Emacs*
- [[https://www.emacswiki.org/emacs/Calc_Tutorials_by_Andrew_Hyatt][How to use M-x calc]]
+ *Vim & Evil*
- [[https://gist.github.com/dmsul/8bb08c686b70d5a68da0e2cb81cd857f][A crash course on modal editing and Ex commands]]
* Module list
** :app
Application modules are complex and opinionated modules that transform Emacs
toward a specific purpose. They may have additional dependencies and should be
loaded last, before =:config= modules.
** Projects that support/complement Doom
+ [[https://github.com/plexus/chemacs][plexus/chemacs]]
+ [[https://github.com/r-darwish/topgrade][r-darwish/topgrade]]
+ [[file:../modules/app/calendar/README.org][calendar]] - TODO
+ [[file:../modules/app/irc/README.org][irc]] - how neckbeards socialize
+ rss =+org= - an RSS client in Emacs
+ [[file:../modules/app/twitter/README.org][twitter]] - A twitter client for Emacs
+ [[file:../modules/app/write/README.org][write]] =+wordnut +langtool= - Transforms emacs into an IDE for writers, and for
writing fiction, notes, papers and so on.
** :completion
Modules that provide new interfaces or frameworks for completion, including code
completion.
+ [[file:../modules/completion/company/README.org][company]] =+childframe +tng= - The ultimate code completion backend
+ helm =+fuzzy +childframe= - *Another* search engine for love and life
+ ido - The /other/ *other* search engine for love and life
+ [[file:../modules/completion/ivy/README.org][ivy]] =+fuzzy +prescient +childframe= - /The/ search engine for love and life
** :config
Modules that configure Emacs one way or another, or focus on making it easier
for you to customize it yourself. It is best to load these last.
+ literate - For users with literate configs. This will tangle+compile a
config.org in your ~doom-private-dir~ when it changes.
+ [[file:../modules/config/default/README.org][default]] =+bindings +smartparens= - The default module sets reasonable defaults
for Emacs. It also provides a Spacemacs-inspired keybinding scheme and a
smartparens config. Use it as a reference for your own modules.
** :editor
Modules that affect and augment your ability to manipulate or insert text.
+ [[file:../modules/editor/evil/README.org][evil]] =+everywhere= - transforms Emacs into Vim
+ [[file:../modules/editor/file-templates/README.org][file-templates]] - Auto-inserted templates in blank new files
+ [[file:../modules/editor/fold/README.org][fold]] - universal code folding
+ format =+onsave= - TODO
+ god - run Emacs commands without modifier keys
+ [[file:../modules/editor/lispy/README.org][lispy]] - TODO
+ multiple-cursors - TODO
+ [[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
** Similar projects
+ [[https://github.com/purcell/emacs.d][purcell/emacs.d]]
+ [[https://github.com/seagle0128/.emacs.d][seagle0128/.emacs.d]]
+ [[https://github.com/syl20bnr/spacemacs][syl20bnr/spacemacs]]

View File

@@ -1,7 +1,15 @@
#+TITLE: Workflow tips, tricks & tutorials
#+TITLE: Getting to know Doom Emacs
#+STARTUP: nofold
This page is a WIP.
Once you've installed Doom and launched it, the next step of your masochistic
journey is to master it. This guide will walk you through many Doom-centric
workflows you'll commonly find in a text editor (and beyond). It isn't
exhaustive because I don't have enough lives to make it so.
#+begin_quote
If you feel like we've missed something, don't hesitate to let us know! You're
welcome to [[https://discord.gg/qvGgnVx][join us on our Discord server]].
#+end_quote
* Table of Contents :TOC:
- [[#day-1-in-doom-emacs][Day 1 in Doom Emacs]]
@@ -14,6 +22,7 @@ This page is a WIP.
- [[#pipe-text-through-ex-commands-and-programs][Pipe text through ex commands and programs]]
- [[#transposingswapping-text][Transposing/swapping text]]
- [[#managing-your-projects][Managing your projects]]
- [[#reconfiguring-emacs-on-a-per-project-basis][Reconfiguring Emacs on a per-project basis]]
- [[#search--replace][Search & replace]]
- [[#project-wide-text-search][Project-wide text search]]
- [[#search--replace-1][Search & replace]]
@@ -32,6 +41,8 @@ This page is a WIP.
- [[#using-emacs-for][Using Emacs for...]]
- [[#writing-fiction][Writing fiction]]
- [[#writing-papers][Writing papers]]
- [[#note-keeping][Note-keeping]]
- [[#a-personal-organizer][A Personal Organizer]]
- [[#composing-music][Composing music]]
- [[#game-development][Game development]]
- [[#web-development][Web development]]
@@ -48,6 +59,9 @@ This page is a WIP.
** TODO Pipe text through ex commands and programs
** TODO Transposing/swapping text
* TODO Managing your projects
** TODO Reconfiguring Emacs on a per-project basis
*** TODO .dir-locals.el
*** TODO editorconfig
* TODO Search & replace
** TODO Project-wide text search
** TODO Search & replace
@@ -66,6 +80,8 @@ This page is a WIP.
* TODO Using Emacs for...
** TODO Writing fiction
** TODO Writing papers
** TODO Note-keeping
** TODO A Personal Organizer
** TODO Composing music
** TODO Game development
** TODO Web development

View File

@@ -10,6 +10,7 @@
;; loaded, but after `early-init-file'. Doom handles package initialization, so
;; we must prevent Emacs from doing it early!
(setq package-enable-at-startup nil)
(advice-add #'package--ensure-init-file :override #'ignore)
;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
(push '(menu-bar-lines . 0) default-frame-alist)

View File

@@ -28,9 +28,8 @@
;;; License: MIT
;; A big contributor to startup times is garbage collection. We up the gc
;; threshold to temporarily prevent it from running, then reset it later with
;; `doom-restore-garbage-collection-h'. Not resetting it will cause
;; stuttering/freezes.
;; threshold to temporarily prevent it from running, then reset it later by
;; enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
(setq gc-cons-threshold most-positive-fixnum)
;; In noninteractive sessions, prioritize non-byte-compiled source files to
@@ -48,9 +47,3 @@
;; And let 'er rip!
(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)))

View File

@@ -1,12 +1,18 @@
;;; init.el -*- lexical-binding: t; -*-
;; Copy this file to ~/.doom.d/init.el or ~/.config/doom/init.el ('doom install'
;; will do this for you). The `doom!' block below controls what modules are
;; enabled and in what order they will be loaded. Remember to run 'doom refresh'
;; after modifying it.
;; This file controls what Doom modules are enabled and what order they load
;; in. Remember to run 'doom sync' after modifying it!
;; NOTE Press 'SPC h d h' (or 'C-h d h' for non-vim users) to access Doom's
;; documentation. There you'll find 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
;; found in modules/README.org.
;; Alternatively, press 'gd' (or 'C-c c d') on a module to browse its
;; directory (for easy access to its source code).
(doom! :input
;;chinese
@@ -27,21 +33,21 @@
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
;;hydra
;;indent-guides ; highlighted indent columns
;;minimap ; show a map of the code on the side
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
ophints ; highlight the region an operation acts on
(popup ; tame sudden yet inevitable temporary windows
+all ; catch all popups that start with an asterix
+defaults) ; default popup rules
;;pretty-code ; replace bits of code with pretty symbols
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;pretty-code ; ligatures or substitute text with pretty symbols
;;tabs ; an tab bar for Emacs
;;treemacs ; a project drawer, like neotree but cooler
;;unicode ; extended unicode support for various languages
vc-gutter ; vcs diff in the fringe
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
;;zen ; distraction-free coding or writing
:editor
(evil +everywhere); come to the dark side, we have cookies
@@ -50,24 +56,30 @@
;;(format +onsave) ; automated prettiness
;;god ; run Emacs commands without modifier keys
;;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
;;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
;;word-wrap ; soft wrapping with language-aware indent
:emacs
dired ; making dired pretty [functional]
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
:term
;;eshell ; a consistent, cross-platform shell (WIP)
;;shell ; a terminal REPL for Emacs
;;term ; terminals in Emacs
;;vterm ; another terminals in Emacs
;;eshell ; the elisp shell that works everywhere
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for 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
;;ansible
@@ -77,11 +89,8 @@
;;editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
(eval +overlay) ; run code, run (also, repls)
flycheck ; tasing you for every semicolon you forget
;;flyspell ; tasing you for misspelling mispelling
;;gist ; interacting with github gists
(lookup ; helps you navigate your code and documentation
+docsets) ; ...or in Dash docsets locally
lookup ; navigate your code and its documentation
;;lsp
;;macos ; MacOS-specific commands
magit ; a git porcelain for Emacs
@@ -90,20 +99,21 @@
;;pdf ; pdf enhancements
;;prodigy ; FIXME managing external services & code builders
;;rgb ; creating color strings
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
;;upload ; map local to remote projects via ssh/ftp
:lang
;;agda ; types of types of types of types...
;;assembly ; assembly for fun or debugging
;;cc ; C/C++/Obj-C madness
;;clojure ; java with a lisp
;;common-lisp ; if you've seen one lisp, you've seen them all
;;coq ; proofs-as-programs
;;crystal ; ruby at the speed of c
;;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
;;elm ; care for a cup of TEA?
emacs-lisp ; drown in parentheses
@@ -111,10 +121,13 @@
;;ess ; emacs speaks statistics
;;faust ; dsp, but you get to keep your soul
;;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
;;hy ; readability of scheme w/ speed of python
;;idris ;
;;json ; At least it ain't XML
;;(java +meghanada) ; the poster child for carpal tunnel syndrome
;;javascript ; all(hope(abandon(ye(who(enter(here))))))
;;julia ; a better, faster MATLAB
@@ -128,31 +141,27 @@
;;nim ; python + lisp at the speed of c
;;nix ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(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
org ; organize your plain life in plain text
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional
;;python ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
;;rest ; Emacs as a REST client
;;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()
;;scala ; java, but good
;;scheme ; a fully conniving family of lisps
sh ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
;;web ; the tubes
;;yaml ; JSON, but readable
:email
;;(mu4e +gmail)
@@ -164,7 +173,6 @@
;;irc ; how neckbeards socialize
;;(rss +org) ; emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
;;write ; emacs for writers (fiction, notes, papers, etc.)
:config
;;literate

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.
** 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.

View File

@@ -3,9 +3,9 @@
(defvar +calendar--wconf nil)
(defun +calendar--init ()
(if-let* ((win (cl-loop for win in (doom-visible-windows)
if (string-match-p "^\\*cfw:" (buffer-name (window-buffer win)))
return win)))
(if-let (win (cl-find-if (lambda (b) (string-match-p "^\\*cfw:" (buffer-name b)))
(doom-visible-windows)
:key #'window-buffer))
(select-window win)
(call-interactively +calendar-open-function)))

View File

@@ -42,10 +42,10 @@
org-gcal-fetch
org-gcal-post-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
;; hack to avoid the deferred.el error
(defun org-gcal--notify (title mes)
(message "org-gcal::%s - %s" title mes)))
;; (use-package! alert)

View File

@@ -1,6 +1,6 @@
;; -*- no-byte-compile: t; -*-
;;; app/calendar/packages.el
(package! calfw)
(package! calfw-org)
(package! org-gcal)
(package! calfw :pin "03abce97620a4a7f7ec5f911e669da9031ab9088")
(package! calfw-org :pin "03abce97620a4a7f7ec5f911e669da9031ab9088")
(package! org-gcal :pin "744505832b34e07b44a5d97d8720b2d7492d7fc9")

View File

@@ -7,8 +7,11 @@
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#dependencies][Dependencies]]
- [[#prerequisites][Prerequisites]]
- [[#macos][macOS]]
- [[#debian--ubuntu][Debian / Ubuntu]]
- [[#arch-linux][Arch Linux]]
- [[#nixos][NixOS]]
- [[#features][Features]]
- [[#an-irc-client-in-emacs][An IRC Client in Emacs]]
- [[#configuration][Configuration]]
@@ -17,7 +20,7 @@
- [[#troubleshooting][Troubleshooting]]
* Description
This module turns adds an IRC client to Emacs with OS notifications.
This module turns Emacs into an IRC client, capable of OS notifications.
** Module Flags
This module provides no flags.
@@ -26,11 +29,27 @@ This module provides no flags.
+ [[https://github.com/jorgenschaefer/circe][circe]]
+ [[https://github.com/eqyiel/circe-notifications][circe-notifications]]
* Dependencies
This module requires =gnutls-cli= or =openssl= for secure connections.
* Prerequisites
This module has no direct prerequisites.
This module requires =gnutls= for secure IRC connections to work.
** macOS
#+BEGIN_SRC sh
brew install gnutls
#+END_SRC
** Debian / Ubuntu
#+BEGIN_SRC sh
apt install gnutls-bin
#+END_SRC
** Arch Linux
#+BEGIN_SRC sh
pacman -S gnutls
#+END_SRC
** NixOS
#+BEGIN_SRC nix
environment.systemPackages = [ pkgs.gnutls ];
#+END_SRC
* Features
** An IRC Client in Emacs
@@ -54,20 +73,23 @@ When in a circe buffer these keybindings will be available.
| ~circe-reconnect~ | =SPC m R= | Reconnect the current server |
* Configuration
Use ~set-irc-server!~ to configure IRC servers. Its second argument (a plist)
Use ~set-irc-server! SERVER PLIST~ to configure IRC servers. Its second argument (a plist)
takes the same arguments as ~circe-network-options~.
#+BEGIN_SRC emacs-lisp :tangle no
(set-irc-server! "chat.freenode.net"
`(:tls t
:nick "doom"
:sasl-username "myusername"
:sasl-password "mypassword"
:channels ("#emacs")))
;; if you omit =:host=, ~SERVER~ will be used instead.
(after! circe
(set-irc-server! "chat.freenode.net"
`(:tls t
:port 6697
:nick "doom"
:sasl-username "myusername"
:sasl-password "mypassword"
:channels ("#emacs"))))
#+END_SRC
*It is a obviously a bad idea to store auth-details in plaintext,* so here are
some ways to avoid that:
However, *it is a obviously a bad idea to store your password in plaintext,* so
here are ways to avoid that:
** Pass: the unix password manager
[[https://www.passwordstore.org/][Pass]] is my tool of choice. I use it to manage my passwords. If you activate the
@@ -80,6 +102,7 @@ password store.
#+BEGIN_SRC emacs-lisp :tangle no
(set-irc-server! "chat.freenode.net"
`(:tls t
:port 6697
:nick "doom"
:sasl-username ,(+pass-get-user "irc/freenode.net")
:sasl-password ,(+pass-get-secret "irc/freenode.net")
@@ -105,10 +128,10 @@ Note that =+pass-get-user= tries to find your username by looking for the fields
listed in =+pass-user-fields= (by default =login=, =user==, =username== and
=email=)=). An example configuration looks like
#+BEGIN_SRC txt :tangle no
#+begin_example
mysecretpassword
username: myusername
#+END_SRC
#+end_example
** Emacs' auth-source API
~auth-source~ is built into Emacs. As suggested [[https://github.com/jorgenschaefer/circe/wiki/Configuration#safer-password-management][in the circe wiki]], you can store

View File

@@ -3,15 +3,18 @@
(defvar +irc--workspace-name "*IRC*")
(defun +irc-setup-wconf (&optional inhibit-workspace)
(unless inhibit-workspace
(+workspace-switch +irc--workspace-name t))
(when (and (featurep! :ui workspaces)
(not inhibit-workspace))
(+workspace-switch +irc--workspace-name 'auto-create))
(let ((buffers (doom-buffers-in-mode 'circe-mode nil t)))
(if buffers
(ignore (switch-to-buffer (car buffers)))
(require 'circe)
(delete-other-windows)
(switch-to-buffer (doom-fallback-buffer))
t)))
(if (not (member (window-buffer) buffers))
(if buffers
(ignore (switch-to-buffer (car buffers)))
(require 'circe)
(delete-other-windows)
(switch-to-buffer (doom-fallback-buffer))
t)
(user-error "IRC buffer is already active and selected"))))
;;;###autoload
(defun =irc (&optional inhibit-workspace)
@@ -20,17 +23,12 @@
If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new
workspace for it."
(interactive "P")
(cond ((and (featurep! :ui workspaces)
(+workspace-exists-p +irc--workspace-name))
(+workspace-switch +irc--workspace-name))
((not (+irc-setup-wconf inhibit-workspace))
(user-error "Couldn't start up a workspace for IRC")))
(if (doom-buffers-in-mode 'circe-mode (buffer-list) t)
(message "Circe buffers are already open")
(if circe-network-options
(cl-loop for network in circe-network-options
collect (circe (car network)))
(call-interactively #'circe))))
(+irc-setup-wconf inhibit-workspace)
(cond ((doom-buffers-in-mode 'circe-mode (doom-buffer-list) t)
(message "Circe buffers are already open"))
(circe-network-options
(mapc #'circe (mapcar #'car circe-network-options)))
((call-interactively #'circe))))
;;;###autoload
(defun +irc/connect (&optional inhibit-workspace)
@@ -52,16 +50,17 @@ workspace for it."
(defun +irc/quit ()
"Kill current circe session and workgroup."
(interactive)
(if (y-or-n-p "Really kill IRC session?")
(let (circe-channel-killed-confirmation
circe-server-killed-confirmation)
(when +irc--defer-timer
(cancel-timer +irc--defer-timer))
(disable-circe-notifications)
(mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t))
(when (equal (+workspace-current-name) +irc--workspace-name)
(+workspace/delete +irc--workspace-name)))
(message "Aborted")))
(unless (y-or-n-p "Really kill IRC session?")
(user-error "Aborted"))
(let (circe-channel-killed-confirmation
circe-server-killed-confirmation)
(when +irc--defer-timer
(cancel-timer +irc--defer-timer))
(disable-circe-notifications)
(mapc #'kill-buffer (doom-buffers-in-mode 'circe-mode (buffer-list) t))
(when (featurep! :ui workspaces)
(when (equal (+workspace-current-name) +irc--workspace-name)
(+workspace/delete +irc--workspace-name)))))
;;;###autoload
(defun +irc/ivy-jump-to-channel (&optional this-server)
@@ -93,7 +92,7 @@ argument) is non-nil only show channels in current server."
;;;###autoload
(defun +irc/tracking-next-buffer ()
"Dissables switching to an unread buffer unless in the irc workspace."
"Disables switching to an unread buffer unless in the irc workspace."
(interactive)
(when (derived-mode-p 'circe-mode)
(tracking-next-buffer)))
@@ -106,13 +105,12 @@ argument) is non-nil only show channels in current server."
(defun +circe-buffer-p (buf)
"Return non-nil if BUF is a `circe-mode' buffer."
(with-current-buffer buf
(and (derived-mode-p 'circe-mode)
(eq (safe-persp-name (get-current-persp))
+irc--workspace-name))))
(derived-mode-p 'circe-mode)))
;;;###autoload
(defun +irc--add-circe-buffer-to-persp-h ()
(when (bound-and-true-p persp-mode)
(when (and (bound-and-true-p persp-mode)
(+workspace-exists-p +irc--workspace-name))
(let ((persp (get-current-persp))
(buf (current-buffer)))
;; Add a new circe buffer to irc workspace when we're in another workspace

View File

@@ -1,9 +1,17 @@
;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*-
;;;###autodef
(defun set-irc-server! (server letvars)
(defun set-irc-server! (server plist)
"Registers an irc SERVER for circe.
SERVER can either be a name for the network (in which case you must specify a
:host), or it may be the hostname itself, in which case it will be used as the
:host.
See `circe-network-options' for details."
(after! circe
(push (cons server letvars) circe-network-options)))
(unless (plist-member plist :host)
(plist-put! plist :host server))
(setf (alist-get server circe-network-options
nil nil #'equal)
plist)))

View File

@@ -48,7 +48,6 @@ playback.")
(use-package! circe
:commands circe circe-server-buffers
:init (setq circe-network-defaults nil)
:config
(setq circe-default-quit-message nil
circe-default-part-message nil
@@ -93,6 +92,26 @@ playback.")
(add-hook 'doom-real-buffer-functions #'+circe-buffer-p)
(add-hook 'circe-channel-mode-hook #'turn-on-visual-line-mode)
(add-hook 'circe-mode-hook #'+irc--add-circe-buffer-to-persp-h)
(add-hook 'circe-mode-hook #'turn-off-smartparens-mode)
;; 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 _)
"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)
(setq lui-fill-type nil)
(when (featurep! :tools flyspell)
(when (featurep! :checkers spell)
(setq lui-flyspell-p t))
(after! evil

View File

@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; app/irc/packages.el
(package! circe)
(package! circe-notifications)
(package! circe :pin "e5bf5f89741a9c43aa406491e94dd8d58c302fb4")
(package! circe-notifications :pin "291149ac12877bbd062da993479d3533a26862b0")

View File

@@ -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 ()) ; /.+/

View File

@@ -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))))))

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)))

View File

@@ -70,7 +70,7 @@
(let ((search-buffers (doom-buffers-in-mode 'elfeed-search-mode))
(show-buffers (doom-buffers-in-mode 'elfeed-show-mode))
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)))
(kill-buffer buf)))
(dolist (b search-buffers)
@@ -101,10 +101,9 @@
;;;###autoload
(defun +rss-put-sliced-image-fn (spec alt &optional flags)
"TODO"
(cl-letf (((symbol-function #'insert-image)
(lambda (image &optional alt _area _slice)
(let ((height (cdr (image-size image t))))
(insert-sliced-image image alt nil (max 1 (/ height 20.0)) 1)))))
(letf! (defun insert-image (image &optional alt _area _slice)
(let ((height (cdr (image-size image t))))
(insert-sliced-image image alt nil (max 1 (/ height 20.0)) 1)))
(shr-put-image spec alt flags)))
;;;###autoload

View File

@@ -4,10 +4,6 @@
;; by apps Reeder and Readkit. It can be invoked via `=rss'. Otherwise, if you
;; don't care for the UI you can invoke elfeed directly with `elfeed'.
(defvar +rss-elfeed-files (list "elfeed.org")
"Where to look for elfeed.org files, relative to `org-directory'. Can be
absolute paths.")
(defvar +rss-split-direction 'below
"What direction to pop up the entry buffer in elfeed.")
@@ -21,10 +17,11 @@ easier to scroll through.")
(use-package! 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
(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-delete #'+rss/delete-pane
shr-max-image-proportion 0.8)
@@ -67,8 +64,11 @@ easier to scroll through.")
(use-package! elfeed-org
:when (featurep! +org)
:after elfeed
:preface
(setq rmh-elfeed-org-files (list "elfeed.org"))
:config
(let ((default-directory org-directory))
(setq rmh-elfeed-org-files
(mapcar #'expand-file-name +rss-elfeed-files)))
(elfeed-org))
(and (let ((default-directory org-directory))
(setq rmh-elfeed-org-files
(cl-remove-if-not
#'file-exists-p (mapcar #'expand-file-name rmh-elfeed-org-files))))
(elfeed-org)))

View File

@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; app/rss/packages.el
(package! elfeed)
(package! elfeed-org)
(package! elfeed :pin "d0405e63863e54a01200740a6717ac875eceabc1")
(package! elfeed-org :pin "77b6bbf222487809813de260447d31c4c59902c9")

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."
(interactive)
(require 'avy)
;; REVIEW Is this necessary anymore with `link-hint'
(let ((pt (avy-with +twitter/ace-link
(avy--process
(+twitter--collect-links)

View File

@@ -1,5 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; app/twitter/packages.el
(package! twittering-mode)
(package! avy)
(package! twittering-mode :pin "114891e8fdb4f06b1326a6cf795e49c205cf9e29")
(package! avy :pin "509471bad0e8094b8639729ec39ca141fae7d4bd")

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~

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))

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))))

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")))

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)

View File

@@ -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
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~).
Check the relevant module's documentation for this kind of information.

View File

@@ -67,11 +67,10 @@ Examples:
;;;###autoload
(defun +company-init-backends-h ()
"Set `company-backends' for the current buffer."
(if (not company-mode)
(remove-hook 'change-major-mode-after-body-hook #'+company-init-backends-h 'local)
(unless (eq major-mode 'fundamental-mode)
(setq-local company-backends (+company--backends)))
(add-hook 'change-major-mode-after-body-hook #'+company-init-backends-h nil 'local)))
(or (memq major-mode '(fundamental-mode special-mode))
buffer-read-only
(doom-temp-buffer-p (or (buffer-base-buffer) (current-buffer)))
(setq-local company-backends (+company--backends))))
(put '+company-init-backends-h 'permanent-local-hook t)

View File

@@ -2,25 +2,49 @@
(use-package! company
: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
(setq company-minimum-prefix-length 2
(setq company-idle-delay 0.25
company-minimum-prefix-length 2
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-require-match 'never
company-global-modes
'(not erc-mode message-mode help-mode gud-mode eshell-mode)
company-backends '(company-capf)
company-frontends
'(company-pseudo-tooltip-frontend
company-echo-metadata-frontend))
'(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 overrides `company-active-map' based on
;; `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
(when (featurep! :editor evil)
(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
;; by C-x C-n, will switch from `company-yasnippet' to
;; `company-dabbrev-code'.
@@ -28,8 +52,7 @@
:before #'company-begin-backend
(company-abort)))
(add-hook 'company-mode-hook #'+company-init-backends-h)
(global-company-mode +1))
(add-hook 'after-change-major-mode-hook #'+company-init-backends-h 'append))
(use-package! company-tng
@@ -49,8 +72,7 @@
;; Packages
(after! company-files
(pushnew! company-files--regexps
"file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))
(add-to-list 'company-files--regexps "file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))
(use-package! company-prescient
@@ -70,47 +92,44 @@
company-box-max-candidates 50
company-box-icons-alist 'company-box-icons-all-the-icons
company-box-icons-functions
'(+company-box-icons--yasnippet-fn
company-box-icons--lsp
+company-box-icons--elisp-fn
company-box-icons--acphp)
(cons #'+company-box-icons--elisp-fn
(delq 'company-box-icons--elisp
company-box-icons-functions))
company-box-icons-all-the-icons
`((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple))
(Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green))
(Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
(Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
(Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
(Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
(Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue))
(Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
(Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red))
(Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red))
(Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red))
(Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red))
(Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red))
(Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red))
(Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red))
(Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red))
(Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red))
(File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red))
(Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red))
(Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red))
(EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red))
(Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red))
(Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red))
(Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red))
(Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red))
(TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
;; (Template . ,(company-box-icons-image "Template.png"))))
(Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green))
(ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
(ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue))
(ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange))
(ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink))))
(let ((all-the-icons-scale-factor 0.8))
`((Unknown . ,(all-the-icons-material "find_in_page" :face 'all-the-icons-purple))
(Text . ,(all-the-icons-material "text_fields" :face 'all-the-icons-green))
(Method . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Function . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Constructor . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Field . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(Variable . ,(all-the-icons-material "adjust" :face 'all-the-icons-blue))
(Class . ,(all-the-icons-material "class" :face 'all-the-icons-red))
(Interface . ,(all-the-icons-material "settings_input_component" :face 'all-the-icons-red))
(Module . ,(all-the-icons-material "view_module" :face 'all-the-icons-red))
(Property . ,(all-the-icons-material "settings" :face 'all-the-icons-red))
(Unit . ,(all-the-icons-material "straighten" :face 'all-the-icons-red))
(Value . ,(all-the-icons-material "filter_1" :face 'all-the-icons-red))
(Enum . ,(all-the-icons-material "plus_one" :face 'all-the-icons-red))
(Keyword . ,(all-the-icons-material "filter_center_focus" :face 'all-the-icons-red))
(Snippet . ,(all-the-icons-material "short_text" :face 'all-the-icons-red))
(Color . ,(all-the-icons-material "color_lens" :face 'all-the-icons-red))
(File . ,(all-the-icons-material "insert_drive_file" :face 'all-the-icons-red))
(Reference . ,(all-the-icons-material "collections_bookmark" :face 'all-the-icons-red))
(Folder . ,(all-the-icons-material "folder" :face 'all-the-icons-red))
(EnumMember . ,(all-the-icons-material "people" :face 'all-the-icons-red))
(Constant . ,(all-the-icons-material "pause_circle_filled" :face 'all-the-icons-red))
(Struct . ,(all-the-icons-material "streetview" :face 'all-the-icons-red))
(Event . ,(all-the-icons-material "event" :face 'all-the-icons-red))
(Operator . ,(all-the-icons-material "control_point" :face 'all-the-icons-red))
(TypeParameter . ,(all-the-icons-material "class" :face 'all-the-icons-red))
(Template . ,(all-the-icons-material "short_text" :face 'all-the-icons-green))
(ElispFunction . ,(all-the-icons-material "functions" :face 'all-the-icons-red))
(ElispVariable . ,(all-the-icons-material "check_circle" :face 'all-the-icons-blue))
(ElispFeature . ,(all-the-icons-material "stars" :face 'all-the-icons-orange))
(ElispFace . ,(all-the-icons-material "format_paint" :face 'all-the-icons-pink)))))
(defun +company-box-icons--yasnippet-fn (candidate)
(when (get-text-property 0 'yas-annotation candidate)
'Yasnippet))
(delq! 'company-echo-metadata-frontend company-frontends)
(defun +company-box-icons--elisp-fn (candidate)
(when (derived-mode-p 'emacs-lisp-mode)
@@ -118,7 +137,14 @@
(cond ((fboundp sym) 'ElispFunction)
((boundp sym) 'ElispVariable)
((featurep sym) 'ElispFeature)
((facep sym) 'ElispFace))))))
((facep sym) 'ElispFace)))))
(defadvice! +company-remove-scrollbar-a (orig-fn &rest args)
"This disables the company-box scrollbar, because:
https://github.com/sebastiencs/company-box/issues/44"
:around #'company-box--update-scrollbar
(letf! ((#'display-buffer-in-side-window #'ignore))
(apply orig-fn args))))
(use-package! company-dict

View File

@@ -1,8 +1,8 @@
;; -*- no-byte-compile: t; -*-
;;; completion/company/packages.el
(package! company)
(package! company-dict)
(package! company-prescient)
(package! company :pin "6333fc4ebbbf4d28e834de8715561e984f149ecb")
(package! company-dict :pin "cd7b8394f6014c57897f65d335d6b2bd65dab1f4")
(package! company-prescient :pin "0f4a89bdec61395138d968a38d375e63ccfbed63")
(when (featurep! +childframe)
(package! company-box))
(package! company-box :pin "3814fcb14e92f4b85b19e664e216a7c8d5c7144d"))

View File

@@ -38,7 +38,7 @@ workspace."
;;; Project search
;;;###autoload
(cl-defun +helm-file-search (&key query in all-files (recursive t))
(cl-defun +helm-file-search (&key query in all-files (recursive t) _prompt args)
"Conduct a file search using ripgrep.
:query STRING
@@ -52,10 +52,13 @@ workspace."
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(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
(delq nil (list (when all-files "-z -uu")
(unless recursive "--maxdepth 1")))))
(delq nil (append (list (when all-files "-z -uu")
(unless recursive "--maxdepth 1"))
args))))
(setq deactivate-mark t)
(helm-rg (or query
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))

View File

@@ -23,6 +23,7 @@ bottom, which is easier on the eyes on big displays."
(setq +helm--posframe-buffer buffer)
:position (point)
:poshandler +helm-posframe-handler
:respect-header-line helm-echo-input-in-header-line
:width
(max (cl-typecase .width
(integer .width)

View File

@@ -23,8 +23,7 @@ be negative.")
;;; Packages
(use-package! helm-mode
:defer t
:after-call pre-command-hook
:hook (doom-first-input . helm-mode)
:init
(map! [remap apropos] #'helm-apropos
[remap find-library] #'helm-locate-library
@@ -43,7 +42,6 @@ be negative.")
[remap recentf-open-files] #'helm-recentf
[remap yank-pop] #'helm-show-kill-ring)
:config
(helm-mode +1)
;; helm is too heavy for `find-file-at-point'
(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-ff-auto-update-initial-value nil
helm-find-files-doc-header nil
;; Don't override evil-ex's completion
helm-mode-handle-completion-in-region nil
;; Default helm window sizes
helm-display-buffer-default-width nil
helm-display-buffer-default-height 0.25
@@ -73,38 +69,37 @@ be negative.")
:init
(when (featurep! +childframe)
;; If this is set to 'iconify-top-level then Emacs will be minimized upon
;; helm completion.
(setq iconify-child-frame 'make-invisible)
(setq helm-display-function #'+helm-posframe-display-fn))
(let ((fuzzy (featurep! +fuzzy)))
(setq helm-M-x-fuzzy-match fuzzy
helm-apropos-fuzzy-match fuzzy
helm-apropos-fuzzy-match fuzzy
(setq helm-apropos-fuzzy-match fuzzy
helm-bookmark-show-location 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-file-cache-fuzzy-match fuzzy
helm-flx-for-helm-locate fuzzy
helm-imenu-fuzzy-match fuzzy
helm-lisp-fuzzy-completion fuzzy
helm-locate-fuzzy-match fuzzy
helm-mode-fuzzy-match fuzzy
helm-projectile-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
(set-popup-rule! "^\\*helm" :vslot -100 :size 0.22 :ttl nil)
;; HACK Doom doesn't support these commands, which invite the user to install
;; the package via ELPA. Force them to use +helm/* instead, because they work
;; out of the box.
(advice-add #'helm-projectile-rg :override #'+helm/project-search)
(advice-add #'helm-projectile-ag :override #'+helm/project-search)
(advice-add #'helm-projectile-grep :override #'+helm/project-search)
;; Hide the modeline
;; Hide the modeline in helm windows as it serves little purpose.
(defun +helm--hide-mode-line (&rest _)
(with-current-buffer (helm-buffer-get)
(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-ag-show-status-default-mode-line :override #'ignore)
;; Hide minibuffer if `helm-echo-input-in-header-line'
(add-hook 'helm-minibuffer-set-up-hook #'helm-hide-minibuffer-maybe)
;; Use helpful instead of describe-* to display documentation
(dolist (fn '(helm-describe-variable helm-describe-function))
(advice-add fn :around #'doom-use-helpful-a)))
(use-package! helm-flx
:when (featurep! +fuzzy)
:hook (helm-mode . helm-flx-mode)
@@ -131,7 +128,7 @@ be negative.")
"C-c C-e" #'helm-rg--bounce)
(map! :map helm-rg--bounce-mode-map
"q" #'kill-current-buffer
"C-c C-c" (λ! (helm-rg--bounce-dump) (kill-current-buffer))
"C-c C-c" (cmd! (helm-rg--bounce-dump) (kill-current-buffer))
"C-x C-c" #'helm-rg--bounce-dump-current-file
"C-c C-k" #'kill-current-buffer))
@@ -183,3 +180,7 @@ be negative.")
(lambda (buf &optional _resume) (pop-to-buffer buf)))
(global-set-key [remap swiper] #'swiper-helm)
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode nil #'eq))
(use-package! helm-descbinds
:hook (helm-mode . helm-descbinds-mode))

View File

@@ -1,16 +1,19 @@
;; -*- no-byte-compile: t; -*-
;;; completion/helm/packages.el
(package! helm)
(package! helm-rg)
(package! helm-c-yasnippet)
(package! helm-company)
(package! helm-describe-modes :recipe (:host github :repo "emacs-helm/helm-describe-modes"))
(package! helm-projectile)
(package! swiper-helm)
(package! helm :pin "b6db9fb47a8900704394e63b795f4a54cb4701a8")
(package! helm-rg :pin "785a80fe5cc87e27c5ea3d00a70049028d9e2847")
(package! helm-c-yasnippet :pin "65ca732b510bfc31636708aebcfe4d2d845b59b0")
(package! helm-company :pin "6eb5c2d730a60e394e005b47c1db018697094dde")
(package! helm-describe-modes
:recipe (:host github :repo "emacs-helm/helm-describe-modes")
:pin "11fb36af119b784539d31c6160002de1957408aa")
(package! helm-projectile :pin "5328b74dddcee8d1913803ca8167868831a07463")
(package! swiper-helm :pin "93fb6db87bc6a5967898b5fd3286954cc72a0008")
(when (featurep! +fuzzy)
(package! helm-flx))
(package! helm-flx :pin "6640fac5cb16bee73c95b8ed1248a4e5e113690e"))
(when (featurep! +childframe)
(package! posframe))
(package! posframe :pin "093b29a53cbeda6d637ccc9ef4dfc47123e79b9e"))
(when (featurep! :lang org)
(package! helm-org))
(package! helm-org :pin "b7a18dfc17e8b933956d61d68c435eee03a96c24"))
(package! helm-descbinds :pin "b72515982396b6e336ad7beb6767e95a80fca192")

View File

@@ -1,59 +1,62 @@
;;; completion/ido/config.el -*- lexical-binding: t; -*-
(defun +ido-init-h ()
(setq ido-ignore-buffers
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer"
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-"
"_region_" " output\\*$" "^TAGS$" "^\*Ido")
ido-use-faces nil
ido-confirm-unique-completion t
ido-case-fold t
ido-enable-tramp-completion nil
ido-enable-flex-matching t
ido-create-new-buffer 'always
ido-enable-tramp-completion t
ido-enable-last-directory-history t
ido-save-directory-list-file (concat doom-cache-dir "ido.last"))
(defvar ido-mode-hook nil
"List of hooks to run when `ido-mode' is activated.")
(unless (member "\\`.DS_Store$" ido-ignore-files)
(push "\\`.DS_Store$" ido-ignore-files)
(push "Icon\\?$" ido-ignore-files))
(define-key! (ido-common-completion-map ido-completion-map ido-file-completion-map)
"\C-n" #'ido-next-match
"\C-p" #'ido-prev-match
"\C-w" #'ido-delete-backward-word-updir
;; Go to $HOME with ~
"~" (λ! (if (looking-back "/" (point-min))
(insert "~/")
(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)
;;
(ido-mode 1)
(ido-everywhere 1)
(ido-ubiquitous-mode 1)
(ido-vertical-mode 1)
(flx-ido-mode +1)
(crm-custom-mode +1)
;;
(remove-hook 'ido-setup-hook #'+ido-init-h))
;;
(add-hook 'ido-setup-hook #'+ido-init-h)
;;; 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
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*[Hh]elp" "^\\*Buffer"
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-" "_region_"
" output\\*$" "^TAGS$" "^\*Ido")
ido-auto-merge-work-directories-length -1
ido-confirm-unique-completion t
ido-case-fold t
ido-create-new-buffer 'always
ido-enable-flex-matching t)
(map! :map (ido-common-completion-map ido-file-completion-map)
"C-w" #'ido-delete-backward-word-updir
:map (ido-common-completion-map ido-file-dir-completion-map)
"C-n" #'ido-next-match
"C-p" #'ido-prev-match
[down] #'ido-next-match
[up] #'ido-prev-match
:map ido-file-completion-map
;; Go to $HOME with ~
"~" (cmd! (if (looking-back "/" (point-min))
(insert "~/")
(call-interactively #'self-insert-command)))))
(use-package! ido-vertical-mode
:hook (ido-mode . ido-vertical-mode)
:config (setq ido-vertical-show-count t))
(use-package! ido-sort-mtime
: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))

View File

@@ -1,7 +1,8 @@
;; -*- no-byte-compile: t; -*-
;;; completion/ido/packages.el
(package! flx-ido)
(package! ido-completing-read+)
(package! ido-vertical-mode)
(package! crm-custom)
(package! flx-ido :pin "17f5c9cb2af18aa6f52910ff4a5a63591261ced5")
(package! ido-completing-read+ :pin "98d3a6e56b1d3652da7b47f49f76d77f82ea80ba")
(package! ido-sort-mtime :pin "f638ff0c922af862f5211779f2311a27fde428eb")
(package! ido-vertical-mode :pin "16c4c1a112796ee0bcf401ea39d3e2643a89feaf")
(package! crm-custom :pin "f1aaccf64306a5f99d9bf7ba815d7ea41c15518d")

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-e= | Open a writable buffer of your search results |
| =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
with =C-c C-c= and aborted with =C-c C-k= (alternatively =ZZ= and =ZQ=, for evil

View File

@@ -13,16 +13,6 @@
(and (not (eq buffer (current-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
(defun +ivy-rich-buffer-name (candidate)
"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)))))
;;;###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))
;;;###autoload
@@ -217,28 +207,33 @@ If ARG (universal argument), open selection in other-window."
;;;###autoload
(defun +ivy/projectile-find-file ()
"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
non-project, `projectile-find-file' if in a big project (more than
`counsel-find-file' if invoked from $HOME or /, `counsel-file-jump' if invoked
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.
The point of this is to avoid Emacs locking up indexing massive file trees."
(interactive)
(call-interactively
(cond ((or (file-equal-p default-directory "~")
(when-let (proot (doom-project-root))
(file-equal-p proot "~")))
#'counsel-find-file)
;; Spoof the command so that ivy/counsel will display the (well fleshed-out)
;; actions list for `counsel-find-file' on C-o. The actions list for the other
;; commands aren't as well configured or are empty.
(let ((this-command 'counsel-find-file))
(call-interactively
(cond ((or (file-equal-p default-directory "~")
(file-equal-p default-directory "/")
(when-let (proot (doom-project-root))
(file-equal-p proot "~")))
#'counsel-find-file)
((doom-project-p)
(let ((files (projectile-current-project-files)))
(if (<= (length files) ivy-sort-max-size)
#'counsel-projectile-find-file
#'projectile-find-file)))
((doom-project-p)
(let ((files (projectile-current-project-files)))
(if (<= (length files) ivy-sort-max-size)
#'counsel-projectile-find-file
#'projectile-find-file)))
(#'counsel-file-jump))))
(#'counsel-file-jump)))))
;;;###autoload
(cl-defun +ivy-file-search (&key query in all-files (recursive t))
(cl-defun +ivy-file-search (&key query in all-files (recursive t) prompt args)
"Conduct a file search using ripgrep.
:query STRING
@@ -252,36 +247,34 @@ The point of this is to avoid Emacs locking up indexing massive file trees."
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(require 'counsel)
(let* ((ivy-more-chars-alist '((t . 1)))
(let* ((this-command 'counsel-rg)
(project-root (or (doom-project-root) default-directory))
(directory (or in project-root))
(default-directory directory)
(args (concat (if all-files " -uu")
(unless recursive " --maxdepth 1"))))
(unless recursive " --maxdepth 1")
" " (mapconcat #'shell-quote-argument args " "))))
(setq deactivate-mark t)
(counsel-rg
(or (if query query)
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
(end (or (bound-and-true-p evil-visual-end) (region-end))))
(when (> (abs (- end beg)) 1)
(let ((query (buffer-substring-no-properties beg end)))
;; Escape characters that are special to ivy searches
(replace-regexp-in-string "[! |]" (lambda (substr)
(cond ((and (string= substr " ")
(not (featurep! +fuzzy)))
" ")
((string= substr "|")
"\\\\\\\\|")
((concat "\\\\" substr))))
(rxt-quote-pcre query)))))))
(or query
(when (doom-region-active-p)
(replace-regexp-in-string
"[! |]" (lambda (substr)
(cond ((and (string= substr " ")
(not (featurep! +fuzzy)))
" ")
((string= substr "|")
"\\\\\\\\|")
((concat "\\\\" substr))))
(rxt-quote-pcre (doom-thing-at-point-or-region)))))
directory args
(format "rg%s %s"
args
(cond ((equal directory default-directory)
"./")
((equal directory project-root)
(projectile-project-name))
((file-relative-name directory project-root)))))))
(or prompt
(format "rg%s [%s]: "
args
(cond ((equal directory default-directory)
"./")
((equal directory project-root)
(projectile-project-name))
((file-relative-name directory project-root))))))))
;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory)

View File

@@ -14,33 +14,31 @@ When 'everything, also preview virtual buffers")
"A plist mapping ivy/counsel commands to commands that generate an editable
results buffer.")
(defvar +ivy-standard-search-fn
(if (featurep! +prescient)
#'+ivy-prescient-non-fuzzy
#'ivy--regex-plus)
"Function to use for non-fuzzy search commands.
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
(use-package! ivy
:after-call pre-command-hook
:hook (doom-first-input . ivy-mode)
:init
(setq ivy-re-builders-alist
`((counsel-rg . +ivy-standard-search)
(swiper . +ivy-standard-search)
(swiper-isearch . +ivy-standard-search)
(t . +ivy-alternative-search)))
(let ((standard-search-fn
(if (featurep! +prescient)
#'+ivy-prescient-non-fuzzy
#'ivy--regex-plus))
(alt-search-fn
(if (featurep! +fuzzy)
#'ivy--regex-fuzzy
;; Ignore order for non-fuzzy searches by default
#'ivy--regex-ignore-order)))
(setq ivy-re-builders-alist
`((counsel-rg . ,standard-search-fn)
(swiper . ,standard-search-fn)
(swiper-isearch . ,standard-search-fn)
(t . ,alt-search-fn))
ivy-more-chars-alist
'((counsel-rg . 1)
(counsel-search . 2)
(t . 3))))
(define-key!
[remap switch-to-buffer] #'+ivy/switch-buffer
@@ -90,7 +88,7 @@ This uses a search algorithm other than ivy's default.")
(setq +ivy--origin nil)))
(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)
"`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))
(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
"C-o" #'ivy-dispatching-done
"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)))
(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
"M-o" #'hydra-ivy/body))
(use-package! ivy-rich
@@ -122,15 +112,27 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(when (featurep! +icons)
(cl-pushnew '(+ivy-rich-buffer-icon)
(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
'counsel-describe-variable
'(:columns
((counsel-describe-variable-transformer (:width 40)) ; the original transformer
(+ivy-rich-describe-variable-transformer (:width 50))
(ivy-rich-counsel-variable-docstring (:face font-lock-doc-face)))))
(+ivy-rich-describe-variable-transformer (:width 50)) ; display variable value
(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
(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
;; as the current project or not.
(let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer))
(switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns))))
(when switch-buffer-alist
(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))
(when-let* ((plist (plist-get ivy-rich-display-transformers-list 'ivy-switch-buffer))
(switch-buffer-alist (assq 'ivy-rich-candidate (plist-get plist :columns))))
(setcar switch-buffer-alist '+ivy-rich-buffer-name))
(ivy-rich-mode +1))
@@ -161,9 +157,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(all-the-icons-ivy-setup)
(after! counsel-projectile
(let ((all-the-icons-ivy-file-commands '(counsel-projectile
counsel-projectile-find-file
counsel-projectile-find-dir)))
(let ((all-the-icons-ivy-file-commands
'(counsel-projectile
counsel-projectile-find-file
counsel-projectile-find-dir)))
(all-the-icons-ivy-setup))))
@@ -173,62 +170,69 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(define-key!
[remap apropos] #'counsel-apropos
[remap bookmark-jump] #'counsel-bookmark
[remap compile] #'+ivy/compile
[remap describe-bindings] #'counsel-descbinds
[remap describe-face] #'counsel-faces
[remap describe-function] #'counsel-describe-function
[remap describe-variable] #'counsel-describe-variable
[remap describe-bindings] #'counsel-descbinds
[remap set-variable] #'counsel-set-variable
[remap evil-ex-registers] #'counsel-evil-registers
[remap evil-show-marks] #'counsel-mark-ring
[remap execute-extended-command] #'counsel-M-x
[remap find-file] #'counsel-find-file
[remap find-library] #'counsel-find-library
[remap info-lookup-symbol] #'counsel-info-lookup-symbol
[remap imenu] #'counsel-imenu
[remap recentf-open-files] #'counsel-recentf
[remap swiper] #'counsel-grep-or-swiper
[remap evil-ex-registers] #'counsel-evil-registers
[remap evil-show-marks] #'counsel-mark-ring
[remap yank-pop] #'counsel-yank-pop
[remap info-lookup-symbol] #'counsel-info-lookup-symbol
[remap load-theme] #'counsel-load-theme
[remap locate] #'counsel-locate
[remap org-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 compile] #'+ivy/compile
[remap projectile-compile-project] #'+ivy/project-compile)
[remap yank-pop] #'counsel-yank-pop)
:config
(set-popup-rule! "^\\*ivy-occur" :size 0.35 :ttl 0 :quit nil)
;; HACK Fix an issue where `counsel-projectile-find-file-action' would try to
;; open a candidate in an occur buffer relative to the wrong buffer,
;; causing it to fail to find the file we want.
(defadvice! +ivy--run-from-ivy-directory-a (orig-fn &rest args)
:around #'counsel-projectile-find-file-action
(let ((default-directory (ivy-state-directory ivy-last)))
(apply orig-fn args)))
;; Don't use ^ as initial input. Set this here because `counsel' defines more
;; of its own, on top of the defaults.
(setq ivy-initial-inputs-alist nil)
;; REVIEW Move this somewhere else and perhaps generalize this so both
;; ivy/helm users can enjoy it.
(defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args)
"Change `counsel-file-jump' to use fd or ripgrep, if they are available."
:override #'counsel--find-return-list
(cl-destructuring-bind (find-program . args)
(cond ((executable-find "fd")
(cons "fd" (list "-t" "f" "-E" ".git")))
((executable-find "rg")
(cons "rg" (list "--files" "--hidden" "--no-messages")))
((cons find-program args)))
(unless (listp args)
(user-error "`counsel-file-jump-args' is a list now, please customize accordingly."))
(counsel--call
(cons find-program args)
(lambda ()
(goto-char (point-min))
(let ((offset (if (member find-program '("fd" "rg")) 0 2))
files)
(while (< (point) (point-max))
(push (buffer-substring
(+ offset (line-beginning-position)) (line-end-position)) files)
(forward-line 1))
(nreverse files))))))
;; REVIEW Counsel allows `counsel-rg-base-command' to be a string or list.
;; This backwards compatibility complicates things for Doom. Simpler to
;; just force it to always be a list.
(when (stringp counsel-rg-base-command)
(setq counsel-rg-base-command (split-string counsel-rg-base-command)))
;; REVIEW Fix #3215: prevents mingw on Windows throwing an error trying to
;; expand / to an absolute path. Remove this when it is fixed upstream
;; in counsel.
(when (and (memq system-type '(windows-nt ms-dos))
(listp counsel-rg-base-command)
(member "--path-separator" counsel-rg-base-command))
(setf (cadr (member "--path-separator" counsel-rg-base-command))
"//"))
;; Integrate with `helpful'
(setq counsel-describe-function-function #'helpful-callable
counsel-describe-variable-function #'helpful-variable)
;; Record in jumplist when opening files via counsel-{ag,rg,pt,git-grep}
(add-hook 'counsel-grep-post-action-hook #'better-jumper-set-jump)
(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
;; `+ivy/compile' and `+ivy/project-compile')
(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
(add-to-list 'savehist-additional-variables 'counsel-compile-history))
;; Use spotlight on mac for `counsel-locate' by default, since it doesn't need
;; any additional setup.
;; `counsel-imenu' -- no sorting for imenu. Sort it by appearance in page.
(add-to-list 'ivy-sort-functions-alist '(counsel-imenu))
;; `counsel-locate'
(when IS-MAC
;; Use spotlight on mac by default since it doesn't need any additional setup
(setq counsel-locate-cmd #'counsel-locate-cmd-mdfind))
;; `swiper'
;; Don't mess with font-locking on the dashboard; it causes breakages
(add-to-list 'swiper-font-lock-exclude #'+doom-dashboard-mode)
;; Record in jumplist when opening files via counsel-{ag,rg,pt,git-grep}
(add-hook 'counsel-grep-post-action-hook #'better-jumper-set-jump)
;; Configure `counsel-find-file'
;; `counsel-find-file'
(setq counsel-find-file-ignore-regexp "\\(?:^[#.]\\)\\|\\(?:[#~]$\\)\\|\\(?:^Icon?\\)")
(ivy-add-actions
'counsel-find-file
'(("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory))))
"insert relative path")
("P" (lambda (path) (with-ivy-window (insert path)))
"insert absolute path")
("l" (lambda (path) (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory)))))
"insert relative org-link")
("L" (lambda (path) (with-ivy-window (insert (format "[[%s]]" path))))
"Insert absolute org-link")))
(dolist (fn '(counsel-rg counsel-find-file))
(ivy-add-actions
fn '(("p" (lambda (path) (with-ivy-window (insert (file-relative-name path default-directory))))
"insert relative path")
("P" (lambda (path) (with-ivy-window (insert path)))
"insert absolute path")
("l" (lambda (path) (with-ivy-window (insert (format "[[./%s]]" (file-relative-name path default-directory)))))
"insert relative org-link")
("L" (lambda (path) (with-ivy-window (insert (format "[[%s]]" path))))
"Insert absolute org-link"))))
(ivy-add-actions
'counsel-ag ; also applies to `counsel-rg'
'(("O" +ivy-git-grep-other-window-action "open in other window"))))
(ivy-add-actions 'counsel-file-jump (plist-get ivy--actions-list 'counsel-find-file))
;; `counsel-search': use normal page for displaying results, so that we see
;; custom ddg themes (if one is set).
(setf (nth 1 (alist-get 'ddg counsel-search-engines-alist))
"https://duckduckgo.com/?q=")
;; REVIEW Move this somewhere else and perhaps generalize this so both
;; ivy/helm users can enjoy it.
(defadvice! +ivy--counsel-file-jump-use-fd-rg-a (args)
"Change `counsel-file-jump' to use fd or ripgrep, if they are available."
:override #'counsel--find-return-list
(cl-destructuring-bind (find-program . args)
(cond ((executable-find doom-projectile-fd-binary)
(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
@@ -284,7 +324,10 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
#'+ivy/projectile-find-file)
;; no highlighting visited files; slows down the filtering
(ivy-set-display-transformer #'counsel-projectile-find-file nil))
(ivy-set-display-transformer #'counsel-projectile-find-file nil)
(when (featurep! +prescient)
(setq counsel-projectile-sort-files t)))
(use-package! wgrep
@@ -311,7 +354,9 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
;; posframe.
(dolist (fn '(swiper counsel-rg counsel-grep counsel-git-grep))
(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
@@ -322,24 +367,26 @@ evil-ex-specific constructs, so we disable it solely in evil-ex."
(use-package! ivy-prescient
:hook (ivy-mode . ivy-prescient-mode)
:when (featurep! +prescient)
:hook (ivy-mode . ivy-prescient-mode)
:hook (ivy-prescient-mode . prescient-persist-mode)
:init
(setq prescient-filter-method
(if (featurep! +fuzzy)
'(literal regexp initialism fuzzy)
'(literal regexp initialism))
ivy-prescient-enable-filtering nil ; we do this ourselves
ivy-prescient-retain-classic-highlighting t)
'(literal regexp initialism)))
: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)
(let ((prescient-filter-method '(literal regexp)))
(ivy-prescient-re-builder str)))
;; NOTE prescient config duplicated with `company'
(setq prescient-save-file (concat doom-cache-dir "prescient-save.el"))
(prescient-persist-mode +1))
(setq prescient-save-file (concat doom-cache-dir "prescient-save.el")))
;;;###package swiper

View File

@@ -1,22 +1,23 @@
;; -*- no-byte-compile: t; -*-
;;; completion/ivy/packages.el
(package! amx)
(package! swiper :pin "544e7de63a4543a74596c5d95efa0bb9da25791e")
(package! ivy)
(package! counsel)
(package! counsel-projectile)
(package! swiper)
(package! ivy-hydra)
(package! ivy-rich)
(package! wgrep)
(package! counsel)
(package! amx :pin "7fb7b874291e0cdeb1f0acb18564a686ec86788d")
(package! counsel-projectile :pin "77392cbbc42e98fc137b43f1db1b111ba6e2dd75")
(package! ivy-rich :pin "10970130b41c6ef9570893cdab8dfbe720e2b1a9")
(package! wgrep :pin "f0ef9bfa44db503cdb2f83fcfbd2fa4e2382ef1f")
(if (featurep! +prescient)
(package! ivy-prescient)
(package! ivy-prescient :pin "3ab7605d997fb8337bf5ded2ad960b98ac0e1fd7")
(when (featurep! +fuzzy)
(package! flx)))
(package! flx :pin "17f5c9cb2af18aa6f52910ff4a5a63591261ced5")))
(when (featurep! +childframe)
(package! ivy-posframe))
(package! ivy-posframe :pin "44749562a9e68bd43ccaa225b31311476fab1251"))
(when (featurep! +icons)
(package! all-the-icons-ivy))
(package! all-the-icons-ivy :pin "a70cbfa1effe36efc946a823a580cec686d5e88d"))

View File

@@ -25,221 +25,390 @@
(:prefix ("l" . "<localleader>")) ; bound locally
(: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
(:prefix-map ("f" . "file")
:desc "Open project editorconfig" "." #'editorconfig-find-current-editorconfig
:desc "Find other file" "a" #'projectile-find-other-file
:desc "Browse private config" "c" #'doom/open-private-config
:desc "Find file in private config" "C" #'doom/find-file-in-private-config
:desc "Find directory" "d" #'dired
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
:desc "Find file from here" "f" (if (fboundp 'counsel-file-jump) #'counsel-file-jump #'find-file)
:desc "Find file in other project" "F" #'doom/browse-in-other-project
:desc "Delete this file" "K" #'doom/delete-this-file
:desc "Move this file" "m" #'doom/move-this-file
:desc "Find file in project" "p" #'projectile-find-file
:desc "Find file in other project" "P" #'doom/find-file-in-other-project
:desc "Recent files" "r" #'recentf-open-files
:desc "Recent project files" "R" #'projectile-recentf
:desc "Sudo this file" "s" #'doom/sudo-this-file
:desc "Sudo find file" "S" #'doom/sudo-find-file
:desc "Yank filename" "y" #'+default/yank-buffer-filename
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Open project scratch buffer" "X" #'doom/switch-to-scratch-buffer)
(:when (featurep! :tools editorconfig)
:desc "Open project editorconfig" "c" #'editorconfig-find-current-editorconfig)
:desc "Copy this file" "C" #'doom/copy-this-file
:desc "Find directory" "d" #'dired
:desc "Delete this file" "D" #'doom/delete-this-file
:desc "Find file in emacs.d" "e" #'+default/find-in-emacsd
:desc "Browse emacs.d" "E" #'+default/browse-emacsd
:desc "Find file" "f" #'find-file
:desc "Find file from here" "F" #'+default/find-file-under-here
:desc "Locate file" "l" #'locate
:desc "Rename/move this file" "m" #'doom/move-this-file
:desc "Find file in private config" "p" #'doom/open-private-config
:desc "Browse private config" "P" #'doom/open-private-config
:desc "Recent files" "r" #'recentf-open-files
:desc "Recent project files" "R" #'projectile-recentf
:desc "Sudo this file" "u" #'doom/sudo-this-file
:desc "Sudo find file" "U" #'doom/sudo-find-file
:desc "Yank filename" "y" #'+default/yank-buffer-filename
:desc "Open scratch buffer" "x" #'doom/open-scratch-buffer
:desc "Switch to scratch buffer" "X" #'doom/switch-to-scratch-buffer)
;;; <leader> g --- lookup
(:when (featurep! :tools lookup)
(:prefix-map ("g" . "lookup")
"k" #'+lookup/documentation
"d" #'+lookup/definition
"D" #'+lookup/references
"f" #'+lookup/file
"o" #'+lookup/online-select
"i" #'+lookup/in-docsets
"I" #'+lookup/in-all-docsets))
;;; <leader> r --- remote
(:when (featurep! :tools upload)
(: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 (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> s --- search
(:prefix-map ("s" . "search")
:desc "Search project for symbol" "." #'+default/search-project-for-symbol-at-point
:desc "Search buffer" "b" #'swiper
:desc "Search current directory" "d" #'+default/search-cwd
:desc "Search other directory" "D" #'+default/search-other-cwd
:desc "Locate file" "f" #'+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 "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 "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 "Search notes" "s" #'+default/org-notes-search
:desc "Search org agenda headlines" "S" #'+default/org-notes-headlines
:desc "View search" "v" #'org-search-view
:desc "Org export to clipboard" "y" #'+org/export-to-clipboard
:desc "Org export to clipboard as RTF" "Y" #'+org/export-to-clipboard-as-rich-text
(:when (featurep! :lang org +journal)
(:prefix ("j" . "journal")
:desc "New Entry" "j" #'org-journal-new-entry
:desc "Search Forever" "s" #'org-journal-search-forever))
(: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> o --- org
"o" nil ; we need to unbind it first as Org claims this
(:prefix-map ("o". "org")
:desc "Do what I mean" "o" #'+org/dwim-at-point
:desc "Display inline images" "i" #'org-display-inline-images
:desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point
(:prefix ("a" . "org agenda")
:desc "Agenda" "a" #'org-agenda
:desc "Todo list" "t" #'org-todo-list
:desc "Tags view" "m" #'org-tags-view
:desc "View search" "v" #'org-search-view)
:desc "Browse notes" "f" #'+default/browse-notes
:desc "Search org-directory" "s" #'+default/org-notes-search
:desc "Switch org buffers" "b" #'org-switchb
:desc "Capture" "c" #'org-capture
:desc "Goto capture" "C" #'org-capture-goto-target
:desc "Link store" "l" #'org-store-link
:desc "Sync org caldav" "S" #'org-caldav-sync)
;;; <leader> p --- project
(:prefix ("p" . "project")
:desc "Find file in other project" "F" #'doom/find-file-in-other-project
:desc "Search project" "s" #'+default/search-project
:desc "List project tasks" "t" #'magit-todos-list
:desc "Open project scratch buffer" "x" #'doom/open-project-scratch-buffer
:desc "Switch to project scratch buffer" "X" #'doom/switch-to-project-scratch-buffer
;; later expanded by projectile
(:prefix ("4" . "in other window"))
(:prefix ("5" . "in other frame")))
: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 "Search project" "s" #'+default/search-project
:desc "List project todos" "t" #'magit-todos-list
:desc "Open project scratch buffer" "x" #'doom/open-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
(:prefix ("4" . "in other window"))
(:prefix ("5" . "in other frame")))
;;; <leader> q --- quit/restart
(:prefix-map ("q" . "quit/restart")
:desc "Quit Emacs" "q" #'kill-emacs
:desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal
(:when (featurep! :ui workspaces)
:desc "Quit Emacs & forget session" "X" #'+workspace/kill-session-and-quit)
:desc "Restart & restore Emacs" "r" #'doom/restart-and-restore
:desc "Restart Emacs" "R" #'doom/restart)
:desc "Restart emacs server" "d" #'+default/restart-server
:desc "Delete frame" "f" #'delete-frame
:desc "Clear current frame" "F" #'doom/kill-all-buffers
:desc "Kill Emacs (and daemon)" "K" #'save-buffers-kill-emacs
:desc "Quit Emacs" "q" #'kill-emacs
:desc "Save and quit Emacs" "Q" #'save-buffers-kill-terminal
:desc "Quick save current session" "s" #'doom/quicksave-session
:desc "Restore last session" "l" #'doom/quickload-session
:desc "Save session to file" "S" #'doom/save-session
:desc "Restore session from file" "L" #'doom/load-session
:desc "Restart & restore Emacs" "r" #'doom/restart-and-restore
:desc "Restart Emacs" "R" #'doom/restart)
;;; <leader> & --- snippets
(:prefix-map ("&" . "snippets")
:desc "New snippet" "n" #'yas-new-snippet
:desc "Insert snippet" "i" #'yas-insert-snippet
:desc "Find global snippet" "/" #'yas-visit-snippet-file
:desc "Reload snippets" "r" #'yas-reload-all
:desc "Create Temp Template" "c" #'aya-create
:desc "Use Temp Template" "e" #'aya-expand)
:desc "New snippet" "n" #'yas-new-snippet
:desc "Insert snippet" "i" #'yas-insert-snippet
:desc "Find global snippet" "/" #'yas-visit-snippet-file
:desc "Reload snippets" "r" #'yas-reload-all
:desc "Create Temp Template" "c" #'aya-create
:desc "Use Temp Template" "e" #'aya-expand)
(:prefix-map ("t" . "terminal")
(:when (featurep! :term term)
:desc "Toggle term popup" "t" #'+term/toggle
:desc "Open term here" "T" #'+term/here)
(:when (featurep! :term vterm)
:desc "Toggle vterm popup" "t" #'+vterm/toggle
:desc "Open vterm here" "T" #'+vterm/here)
(:when (featurep! :term eshell)
:desc "Toggle eshell popup" "t" #'+eshell/toggle
:desc "Open eshell here" "T" #'+eshell/here))
;;; <leader> t --- toggle
(:prefix-map ("t" . "toggle")
:desc "Big mode" "b" #'doom-big-font-mode
:desc "Flymake" "f" #'flymake-mode
:desc "Frame fullscreen" "F" #'toggle-frame-fullscreen
:desc "Indent style" "I" #'doom/toggle-indent-style
:desc "Line numbers" "l" #'doom/toggle-line-numbers
:desc "Word-wrap mode" "w" #'+word-wrap-mode
(:when (featurep! :checkers syntax)
: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
(:prefix-map ("v" . "versioning")
:desc "Git revert file" "R" #'vc-revert
:desc "Kill link to remote" "y" #'+vc/browse-at-remote-kill-file-or-region
:desc "Kill link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage
(:when (featurep! :ui vc-gutter)
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk
:desc "Git time machine" "t" #'git-timemachine-toggle
:desc "Jump to next hunk" "n" #'git-gutter:next-hunk
:desc "Jump to previous hunk" "p" #'git-gutter:previous-hunk)
(:when (featurep! :tools magit)
:desc "Magit dispatch" "/" #'magit-dispatch
:desc "Forge dispatch" "'" #'forge-dispatch
:desc "Magit status" "g" #'magit-status
:desc "Magit file delete" "x" #'magit-file-delete
:desc "Magit blame" "B" #'magit-blame-addition
:desc "Magit clone" "C" #'magit-clone
:desc "Magit fetch" "F" #'magit-fetch
:desc "Magit buffer log" "L" #'magit-log
:desc "Git stage file" "S" #'magit-stage-file
:desc "Git unstage file" "U" #'magit-unstage-file
(:prefix ("f" . "find")
:desc "Find file" "f" #'magit-find-file
:desc "Find gitconfig file" "g" #'magit-find-git-config-file
:desc "Find commit" "c" #'magit-show-commit
:desc "Find issue" "i" #'forge-visit-issue
:desc "Find pull request" "p" #'forge-visit-pullreq)
(:prefix ("o" . "open in browser")
:desc "Browse file or region" "." #'+vc/browse-at-remote-file-or-region
:desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage
:desc "Browse remote" "r" #'forge-browse-remote
:desc "Browse commit" "c" #'forge-browse-commit
:desc "Browse an issue" "i" #'forge-browse-issue
:desc "Browse a pull request" "p" #'forge-browse-pullreq
:desc "Browse issues" "I" #'forge-browse-issues
:desc "Browse pull requests" "P" #'forge-browse-pullreqs)
(:prefix ("l" . "list")
(:when (featurep! :tools gist)
:desc "List gists" "g" #'+gist:list)
:desc "List repositories" "r" #'magit-list-repositories
:desc "List submodules" "s" #'magit-list-submodules
:desc "List issues" "i" #'forge-list-issues
:desc "List pull requests" "p" #'forge-list-pullreqs
:desc "List notifications" "n" #'forge-list-notifications)
(:prefix ("c" . "create")
:desc "Initialize repo" "r" #'magit-init
:desc "Clone repo" "R" #'magit-clone
:desc "Commit" "c" #'magit-commit-create
:desc "Fixup" "f" #'magit-commit-fixup
:desc "Issue" "i" #'forge-create-issue
:desc "Pull request" "p" #'forge-create-pullreq)))
:desc "Git revert file" "R" #'vc-revert
:desc "Kill link to remote" "y" #'browse-at-remote-kill
:desc "Kill link to homepage" "Y" #'+vc/browse-at-remote-kill-homepage
(:when (featurep! :ui vc-gutter)
:desc "Git revert hunk" "r" #'git-gutter:revert-hunk
:desc "Git stage hunk" "s" #'git-gutter:stage-hunk
:desc "Git time machine" "t" #'git-timemachine-toggle
:desc "Jump to next hunk" "n" #'git-gutter:next-hunk
:desc "Jump to previous hunk" "p" #'git-gutter:previous-hunk)
(:when (featurep! :tools magit)
:desc "Magit dispatch" "/" #'magit-dispatch
:desc "Forge dispatch" "'" #'forge-dispatch
:desc "Magit status" "g" #'magit-status
:desc "Magit status here" "G" #'magit-status-here
:desc "Magit file delete" "x" #'magit-file-delete
:desc "Magit blame" "B" #'magit-blame-addition
:desc "Magit clone" "C" #'magit-clone
:desc "Magit fetch" "F" #'magit-fetch
:desc "Magit buffer log" "L" #'magit-log
:desc "Git stage file" "S" #'magit-stage-file
:desc "Git unstage file" "U" #'magit-unstage-file
(:prefix ("f" . "find")
:desc "Find file" "f" #'magit-find-file
:desc "Find gitconfig file" "g" #'magit-find-git-config-file
:desc "Find commit" "c" #'magit-show-commit
:desc "Find issue" "i" #'forge-visit-issue
:desc "Find pull request" "p" #'forge-visit-pullreq)
(:prefix ("o" . "open in browser")
:desc "Browse file or region" "." #'browse-at-remote
:desc "Browse homepage" "h" #'+vc/browse-at-remote-homepage
:desc "Browse remote" "r" #'forge-browse-remote
:desc "Browse commit" "c" #'forge-browse-commit
:desc "Browse an issue" "i" #'forge-browse-issue
:desc "Browse a pull request" "p" #'forge-browse-pullreq
:desc "Browse issues" "I" #'forge-browse-issues
:desc "Browse pull requests" "P" #'forge-browse-pullreqs)
(:prefix ("l" . "list")
(:when (featurep! :tools gist)
:desc "List gists" "g" #'gist-list)
:desc "List repositories" "r" #'magit-list-repositories
:desc "List submodules" "s" #'magit-list-submodules
:desc "List issues" "i" #'forge-list-issues
:desc "List pull requests" "p" #'forge-list-pullreqs
:desc "List notifications" "n" #'forge-list-notifications)
(:prefix ("c" . "create")
:desc "Initialize repo" "r" #'magit-init
:desc "Clone repo" "R" #'magit-clone
:desc "Commit" "c" #'magit-commit-create
:desc "Fixup" "f" #'magit-commit-fixup
:desc "Issue" "i" #'forge-create-issue
:desc "Pull request" "p" #'forge-create-pullreq)))
;;; <leader> w --- workspaces/windows
(:prefix-map ("w" . "workspaces/windows")
:desc "Autosave session" "a" #'doom/quicksave-session
:desc "Display workspaces" "d" #'+workspace/display
:desc "Rename workspace" "r" #'+workspace/rename
:desc "Create workspace" "c" #'+workspace/new
:desc "Delete workspace" "k" #'+workspace/delete
:desc "Save session" "s" #'doom/save-session
:desc "Save workspace" "S" #'+workspace/save
:desc "Load session" "l" #'doom/load-session
:desc "Load last autosaved session" "L" #'doom/quickload-session
:desc "Switch to other workspace" "o" #'+workspace/other
:desc "Undo window config" "u" #'winner-undo
:desc "Redo window config" "U" #'winner-redo
:desc "Switch to left workspace" "p" #'+workspace/switch-left
:desc "Switch to right workspace" "n" #'+workspace/switch-right
:desc "Switch to" "w" #'+workspace/switch-to
:desc "Switch to workspace 1" "1" #'+workspace/switch-to-0
:desc "Switch to workspace 2" "2" #'+workspace/switch-to-1
:desc "Switch to workspace 3" "3" #'+workspace/switch-to-2
:desc "Switch to workspace 4" "4" #'+workspace/switch-to-3
:desc "Switch to workspace 5" "5" #'+workspace/switch-to-4
:desc "Switch to workspace 6" "6" #'+workspace/switch-to-5
:desc "Switch to workspace 7" "7" #'+workspace/switch-to-6
:desc "Switch to workspace 8" "8" #'+workspace/switch-to-7
:desc "Switch to workspace 9" "9" #'+workspace/switch-to-8
:desc "Switch to last workspace" "0" #'+workspace/switch-to-final)
:desc "Autosave session" "a" #'doom/quicksave-session
:desc "Display workspaces" "d" #'+workspace/display
:desc "Rename workspace" "r" #'+workspace/rename
:desc "Create workspace" "c" #'+workspace/new
:desc "Delete workspace" "k" #'+workspace/delete
:desc "Save session" "s" #'doom/save-session
:desc "Save workspace" "S" #'+workspace/save
:desc "Load session" "l" #'doom/load-session
:desc "Load last autosaved session" "L" #'doom/quickload-session
:desc "Switch to other workspace" "o" #'+workspace/other
:desc "Undo window config" "u" #'winner-undo
:desc "Redo window config" "U" #'winner-redo
:desc "Switch to left workspace" "p" #'+workspace/switch-left
:desc "Switch to right workspace" "n" #'+workspace/switch-right
:desc "Switch to" "w" #'+workspace/switch-to
:desc "Switch to workspace 1" "1" #'+workspace/switch-to-0
:desc "Switch to workspace 2" "2" #'+workspace/switch-to-1
:desc "Switch to workspace 3" "3" #'+workspace/switch-to-2
:desc "Switch to workspace 4" "4" #'+workspace/switch-to-3
:desc "Switch to workspace 5" "5" #'+workspace/switch-to-4
:desc "Switch to workspace 6" "6" #'+workspace/switch-to-5
:desc "Switch to workspace 7" "7" #'+workspace/switch-to-6
:desc "Switch to workspace 8" "8" #'+workspace/switch-to-7
:desc "Switch to workspace 9" "9" #'+workspace/switch-to-8
:desc "Switch to last workspace" "0" #'+workspace/switch-to-final)
;;; <leader> m --- multiple cursors
(:when (featurep! :editor multiple-cursors)
(:prefix-map ("m" . "multiple cursors")
:desc "Edit lines" "l" #'mc/edit-lines
:desc "Mark next" "n" #'mc/mark-next-like-this
:desc "Unmark next" "N" #'mc/unmark-next-like-this
:desc "Mark previous" "p" #'mc/mark-previous-like-this
:desc "Unmark previous" "P" #'mc/unmark-previous-like-this
:desc "Mark all" "t" #'mc/mark-all-like-this
:desc "Mark all DWIM" "m" #'mc/mark-all-like-this-dwim
:desc "Edit line endings" "e" #'mc/edit-ends-of-lines
:desc "Edit line starts" "a" #'mc/edit-beginnings-of-lines
:desc "Mark tag" "s" #'mc/mark-sgml-tag-pair
:desc "Mark in defun" "d" #'mc/mark-all-like-this-in-defun
:desc "Add cursor w/mouse" "<mouse-1>" #'mc/add-cursor-on-click))
(:prefix-map ("m" . "multiple-cursors")
:desc "Edit lines" "l" #'mc/edit-lines
:desc "Mark next" "n" #'mc/mark-next-like-this
:desc "Unmark next" "N" #'mc/unmark-next-like-this
:desc "Mark previous" "p" #'mc/mark-previous-like-this
:desc "Unmark previous" "P" #'mc/unmark-previous-like-this
:desc "Mark all" "t" #'mc/mark-all-like-this
:desc "Mark all DWIM" "m" #'mc/mark-all-like-this-dwim
:desc "Edit line endings" "e" #'mc/edit-ends-of-lines
:desc "Edit line starts" "a" #'mc/edit-beginnings-of-lines
:desc "Mark tag" "s" #'mc/mark-sgml-tag-pair
:desc "Mark in defun" "d" #'mc/mark-all-like-this-in-defun
:desc "Add cursor w/mouse" "<mouse-1>" #'mc/add-cursor-on-click))
;; APPs
;;; <leader> M --- mu4e
(:when (featurep! :email mu4e)
(:prefix-map ("M" . "mu4e")
:desc "Open email app" "M" #'=mu4e
:desc "Compose email" "c" #'+mu4e/compose))
(:prefix-map ("M" . "mu4e")
:desc "Open email app" "M" #'=mu4e
:desc "Compose email" "c" #'+mu4e/compose))
;;; <leader> I --- IRC
(:when (featurep! :app irc)
(:prefix-map ("I" . "irc")
:desc "Open irc app" "I" #'=irc
:desc "Next unread buffer" "a" #'tracking-next-buffer
:desc "Quit irc" "q" #'+irc/quit
:desc "Reconnect all" "r" #'circe-reconnect-all
:desc "Send message" "s" #'+irc/send-message
(:when (featurep! :completion ivy)
:desc "Jump to channel" "j" #'irc/ivy-jump-to-channel)))
(:prefix-map ("I" . "irc")
:desc "Open irc app" "I" #'=irc
:desc "Next unread buffer" "a" #'tracking-next-buffer
:desc "Quit irc" "q" #'+irc/quit
:desc "Reconnect all" "r" #'circe-reconnect-all
:desc "Send message" "s" #'+irc/send-message
(:when (featurep! :completion ivy)
:desc "Jump to channel" "j" #'+irc/ivy-jump-to-channel)))
;;; <leader> T --- twitter
(:when (featurep! :app twitter)
(:prefix-map ("T" . "twitter")
:desc "Open twitter app" "T" #'=twitter
:desc "Quit twitter" "q" #'+twitter/quit
:desc "Rerender twits" "r" #'+twitter/rerender-all
:desc "Ace link" "l" #'+twitter/ace-link)))
(:prefix-map ("T" . "twitter")
:desc "Open twitter app" "T" #'=twitter
:desc "Quit twitter" "q" #'+twitter/quit
:desc "Rerender twits" "r" #'+twitter/rerender-all
:desc "Ace link" "l" #'+twitter/ace-link)))
;;
@@ -250,7 +419,7 @@
;;; Text scaling
[C-mouse-4] #'text-scale-increase
[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/increase-font-size
"M--" #'doom/decrease-font-size
@@ -272,12 +441,15 @@
"M-SPC" #'objed-activate)
;;; buffer management
"C-x b" #'persp-switch-to-buffer
(:when (featurep! :completion ivy)
"C-x 4 b" #'+ivy/switch-workspace-buffer-other-window)
"C-x C-b" #'ibuffer-list-buffers
"C-x B" #'switch-to-buffer
"C-x 4 B" #'switch-to-buffer-other-window
"C-x b" #'switch-to-buffer
"C-x 4 b" #'switch-to-buffer-other-window
(:when (featurep! :ui workspaces)
"C-x b" #'persp-switch-to-buffer
"C-x B" #'switch-to-buffer
"C-x 4 B" #'switch-to-buffer-other-window
(:when (featurep! :completion ivy)
"C-x 4 b" #'+ivy/switch-workspace-buffer-other-window))
"C-x C-b" #'ibuffer
"C-x K" #'doom/kill-this-buffer-in-all-windows
;;; company-mode
@@ -291,14 +463,14 @@
"C-S-h" #'company-show-doc-buffer
"C-s" #'company-search-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
[backtab] #'company-select-previous
"C-RET" #'counsel-company
:map company-search-map
"C-n" #'company-search-repeat-forward
"C-p" #'company-search-repeat-backward
"C-s" (λ! (company-search-abort) (company-filter-candidates)))
"C-s" (cmd! (company-search-abort) (company-filter-candidates)))
;;; ein notebooks
(:after ein:notebook-multilang
@@ -319,22 +491,22 @@
;;; help and info
(:after help-mode
:map help-mode-map
"o" #'ace-link-help
"o" #'link-hint-open-link
">" #'help-go-forward
"<" #'help-go-back
"n" #'forward-button
"p" #'backward-button)
(:after helpful
:map helpful-mode-map
"o" #'ace-link-help)
"o" #'link-hint-open-link)
(:after apropos
:map apropos-mode-map
"o" #'ace-link-help
"o" #'link-hint-open-link
"n" #'forward-button
"p" #'backward-button)
(:after info
:map Info-mode-map
"o" #'ace-link-info)
"o" #'link-hint-open-link)
;;; ivy & counsel
(:when (featurep! :completion ivy)
@@ -377,10 +549,6 @@
"C-`" #'+popup/toggle
"C-~" #'+popup/raise)
;;; repl
"C-c C-z" #'+eval/open-repl-other-window
"C-c C-S-z" #'+eval/open-repl-same-window
;;; smartparens
(:after smartparens
:map smartparens-mode-map
@@ -399,14 +567,4 @@
;;; treemacs
(:when (featurep! :ui treemacs)
"<f9>" #'+treemacs/toggle
"<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))
"<C-f9>" #'treemacs-find-file))

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,5 @@
;; config/default/autoload/default.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +default/yank-buffer-filename ()
"Copy the current buffer's path to the kill ring."
(interactive)
(if-let* ((filename (or buffer-file-name (bound-and-true-p list-buffers-directory))))
(message (kill-new (abbreviate-file-name filename)))
(error "Couldn't find filename in current buffer")))
;;;###autoload
(defun +default/browse-project ()
(interactive) (doom-project-browse (doom-project-root)))
;; NOTE No need for find-in-project, use `projectile-find-file'
;;;###autoload
(defun +default/browse-templates ()
(interactive) (doom-project-browse +file-templates-dir))
;;;###autoload
(defun +default/find-in-templates ()
(interactive) (doom-project-find-file +file-templates-dir))
;;;###autoload
(defun +default/browse-emacsd ()
(interactive) (doom-project-browse doom-emacs-dir))
;;;###autoload
(defun +default/find-in-emacsd ()
(interactive) (doom-project-find-file doom-emacs-dir))
;;;###autoload
(defun +default/browse-notes ()
(interactive) (doom-project-browse org-directory))
;;;###autoload
(defun +default/find-in-notes ()
(interactive) (doom-project-find-file org-directory))
;;;###autoload
(defun +default/compile (arg)
"Runs `compile' from the root of the current project.
@@ -59,9 +25,6 @@ If ARG (universal argument), runs `compile' from the current directory."
#'man
#'woman)))
;;;###autoload
(defalias '+default/newline #'newline)
;;;###autoload
(defun +default/new-buffer ()
"TODO"
@@ -73,275 +36,11 @@ If ARG (universal argument), runs `compile' from the current directory."
(with-current-buffer buffer
(funcall (default-value 'major-mode))))))
;;;###autoload
(defun +default/newline-above ()
"Insert an indented new line before the current one."
(interactive)
(if (featurep 'evil)
(call-interactively 'evil-open-above)
(beginning-of-line)
(save-excursion (newline))
(indent-according-to-mode)))
;;;###autoload
(defun +default/newline-below ()
"Insert an indented new line after the current one."
(interactive)
(if (featurep 'evil)
(call-interactively 'evil-open-below)
(end-of-line)
(newline-and-indent)))
;;;###autoload
(defun +default/yank-pop ()
"Interactively select what text to insert from the kill ring."
(interactive)
(call-interactively
(cond ((fboundp 'counsel-yank-pop) #'counsel-yank-pop)
((fboundp 'helm-show-kill-ring) #'helm-show-kill-ring)
((error "No kill-ring search backend available. Enable ivy or helm!")))))
;;;###autoload
(defun +default--newline-indent-and-continue-comments-a ()
"A replacement for `newline-and-indent'.
Continues comments if executed from a commented line, with special support for
languages with weak native comment continuation support (like C-family
languages)."
(interactive)
(if (and (sp-point-in-comment)
comment-line-break-function)
(funcall comment-line-break-function nil)
(delete-horizontal-space t)
(newline nil t)
(indent-according-to-mode)))
(defun doom--backward-delete-whitespace-to-column ()
"Delete back to the previous column of whitespace, or as much whitespace as
possible, or just one char if that's not possible."
(interactive)
(let* ((context (ignore-errors (sp-get-thing)))
(op (plist-get context :op))
(cl (plist-get context :cl))
open-len close-len)
(cond ;; When in strings (sp acts weird with quotes; this is the fix)
;; Also, skip closing delimiters
((and op cl
(string= op cl)
(and (string= (char-to-string (or (char-before) 0)) op)
(setq open-len (length op)))
(and (string= (char-to-string (or (char-after) 0)) cl)
(setq close-len (length cl))))
(delete-char (- open-len))
(delete-char close-len))
;; Delete up to the nearest tab column IF only whitespace between
;; point and bol.
((and (not indent-tabs-mode)
(not (bolp))
(not (sp-point-in-string))
(save-excursion (>= (- (skip-chars-backward " \t")) tab-width)))
(let ((movement (% (current-column) tab-width)))
(when (= movement 0)
(setq movement tab-width))
(delete-char (- movement)))
(unless (memq (char-before) (list ?\n ?\ ))
(insert " ")))
;; Otherwise do a regular delete
((delete-char -1)))))
;;;###autoload
(defun +default--delete-backward-char-a (n &optional killflag)
"Same as `delete-backward-char', but preforms these additional checks:
+ If point is surrounded by (balanced) whitespace and a brace delimiter ({} []
()), delete a space on either side of the cursor.
+ If point is at BOL and surrounded by braces on adjacent lines, collapse
newlines:
{
|
} => {|}
+ Otherwise, resort to `doom--backward-delete-whitespace-to-column'.
+ Resorts to `delete-char' if n > 1"
(interactive "p\nP")
(or (integerp n)
(signal 'wrong-type-argument (list 'integerp n)))
(cond ((and (use-region-p)
delete-active-region
(= n 1))
;; If a region is active, kill or delete it.
(if (eq delete-active-region 'kill)
(kill-region (region-beginning) (region-end) 'region)
(funcall region-extract-function 'delete-only)))
;; In Overwrite mode, maybe untabify while deleting
((null (or (null overwrite-mode)
(<= n 0)
(memq (char-before) '(?\t ?\n))
(eobp)
(eq (char-after) ?\n)))
(let ((ocol (current-column)))
(delete-char (- n) killflag)
(save-excursion
(insert-char ?\s (- ocol (current-column)) nil))))
;;
((and (= n 1) (bound-and-true-p smartparens-mode))
(cond ((and (memq (char-before) (list ?\ ?\t))
(save-excursion
(and (/= (skip-chars-backward " \t" (line-beginning-position)) 0)
(bolp))))
(doom--backward-delete-whitespace-to-column))
((let* ((pair (ignore-errors (sp-get-thing)))
(op (plist-get pair :op))
(cl (plist-get pair :cl))
(beg (plist-get pair :beg))
(end (plist-get pair :end)))
(cond ((and end beg (= end (+ beg (length op) (length cl))))
(sp-backward-delete-char 1))
((doom-surrounded-p pair 'inline 'balanced)
(delete-char -1 killflag)
(delete-char 1)
(when (= (point) (+ (length cl) beg))
(sp-backward-delete-char 1)
(sp-insert-pair op)))
((and (bolp) (doom-surrounded-p pair nil 'balanced))
(delete-region beg end)
(sp-insert-pair op)
t)
((run-hook-with-args-until-success 'doom-delete-backward-functions))
((doom--backward-delete-whitespace-to-column)))))))
;; Otherwise, do simple deletion.
((delete-char (- n) killflag))))
;;;###autoload
(defun +default/search-cwd (&optional arg)
"Conduct a text search in files under the current folder.
If prefix ARG is set, prompt for a directory to search from."
(interactive "P")
(let ((default-directory
(if arg
(read-directory-name "Search directory: ")
default-directory)))
(call-interactively
(cond ((featurep! :completion ivy) #'+ivy/project-search-from-cwd)
((featurep! :completion helm) #'+helm/project-search-from-cwd)
(#'rgrep)))))
;;;###autoload
(defun +default/search-other-cwd ()
"Conduct a text search in another directory."
(interactive)
(+default/search-cwd 'other))
;;;###autoload
(defun +default/search-project (&optional arg)
"Conduct a text search in the current project root.
If prefix ARG is set, prompt for a known project to search from."
(interactive "P")
(let ((default-directory
(if arg
(if-let (projects (projectile-relevant-known-projects))
(completing-read "Search project: " projects
nil t nil nil (doom-project-root))
(user-error "There are no known projects"))
default-directory)))
(call-interactively
(cond ((featurep! :completion ivy) #'+ivy/project-search)
((featurep! :completion helm) #'+helm/project-search)
(#'projectile-grep)))))
;;;###autoload
(defun +default/search-other-project ()
"Conduct a text search in a known project."
(interactive)
(+default/search-project 'other))
;;;###autoload
(defun +default/search-project-for-symbol-at-point (&optional arg symbol)
"Conduct a text search in the current project for symbol at point.
If prefix ARG is set, prompt for a known project to search from."
(interactive
(list current-prefix-arg
(or (and (use-region-p)
(rxt-quote-pcre
(buffer-substring-no-properties (region-beginning)
(region-end))))
(rxt-quote-pcre (thing-at-point 'symbol t))
"")))
(let ((default-directory
(if arg
(if-let (projects (projectile-relevant-known-projects))
(completing-read "Switch to project: " projects
nil t nil nil (doom-project-root))
(user-error "There are no known projects"))
default-directory)))
(cond ((featurep! :completion ivy)
(+ivy/project-search nil symbol))
((featurep! :completion helm)
(+helm/project-search nil symbol))
((rgrep (regexp-quote symbol))))))
;;;###autoload
(defun +default/search-notes-for-symbol-at-point (&optional symbol)
"Conduct a text search in the current project for symbol at point. If prefix
ARG is set, prompt for a known project to search from."
(interactive
(list (rxt-quote-pcre (or (thing-at-point 'symbol t) ""))))
(require 'org)
(let ((default-directory org-directory))
(+default/search-project-for-symbol-at-point
nil symbol)))
;;;###autoload
(defun +default/org-notes-search ()
"Perform a text search on `org-directory'."
(interactive)
(require 'org)
(let ((default-directory org-directory))
(+default/search-project-for-symbol-at-point nil "")))
;;;###autoload
(defun +default/org-notes-headlines ()
"Jump to an Org headline in `org-agenda-files'."
(interactive)
(doom-completing-read-org-headings
"Jump to org headline: " org-agenda-files 3 t))
;;;###autoload
(defun +default/lsp-format-region-or-buffer ()
"Format the buffer (or selection) with LSP."
(interactive)
(unless (bound-and-true-p lsp-mode)
(user-error "Not in an LSP buffer"))
(call-interactively
(if (use-region-p)
#'lsp-format-region
#'lsp-format-buffer)))
;;;###autoload
(defun +default/restart-server ()
"Restart the Emacs server."
(interactive)
(server-force-delete)
(while (server-running-p)
(sit-for 1))
(sleep-for 1))
(server-start))
;;;###autoload
(defun +default/find-file-under-here ()
"Perform a recursive file search from the current directory."
(interactive)
(if (featurep! :completion ivy)
(call-interactively #'counsel-file-jump)
(λ! (doom-project-find-file default-directory))))
;;;###autoload
(defun +default/insert-file-path (arg)
"Insert the file name (absolute path if prefix ARG).
If `buffer-file-name' isn't set, uses `default-directory'."
(interactive "P")
(let ((path (or buffer-file-name default-directory)))
(insert
(if arg
(abbreviate-file-name path)
(file-name-nondirectory path)))))

View File

@@ -1,19 +1,22 @@
;;; 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
`(minibuffer-local-map
minibuffer-local-ns-map
minibuffer-local-completion-map
minibuffer-local-must-match-map
minibuffer-local-isearch-map
read-expression-map
,@(cond ((featurep! :completion ivy)
'(ivy-minibuffer-map
ivy-switch-buffer-map))
((featurep! :completion helm)
'(helm-map
helm-ag-map
helm-read-file-map))))
(append '(minibuffer-local-map
minibuffer-local-ns-map
minibuffer-local-completion-map
minibuffer-local-must-match-map
minibuffer-local-isearch-map
read-expression-map)
(cond ((featurep! :completion ivy)
'(ivy-minibuffer-map
ivy-switch-buffer-map))
((featurep! :completion helm)
'(helm-map
helm-ag-map
helm-read-file-map))))
"A list of all the keymaps used for the minibuffer.")
@@ -23,20 +26,32 @@
;;;###package avy
(setq avy-all-windows nil
avy-all-windows-alt t
avy-background t)
avy-background t
;; the unpredictability of this (when enabled) makes it a poor default
avy-single-candidate-jump nil)
(after! epa
(setq epa-file-encrypt-to
(or epa-file-encrypt-to
;; Collect all public key IDs with your username
(unless (string-empty-p user-full-name)
(cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) user-full-name))
collect (epg-sub-key-id (car (epg-key-sub-key-list key)))))
user-mail-address)
;; With GPG 2.1, this forces gpg-agent to use the Emacs minibuffer to
;; prompt for the key passphrase.
epa-pinentry-mode 'loopback))
;; With GPG 2.1+, this forces gpg-agent to use the Emacs minibuffer to prompt
;; for the key passphrase.
(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)
(cl-loop for key in (ignore-errors (epg-list-keys (epg-make-context) user-full-name))
collect (epg-sub-key-id (car (epg-key-sub-key-list key)))))
user-mail-address))
;; And suppress prompts if epa-file-encrypt-to has a default value (without
;; overwriting file-local values).
(defadvice! +default--dont-prompt-for-keys-a (&rest _)
:before #'epa-file-write-region
(unless (local-variable-p 'epa-file-encrypt-to)
(setq-local epa-file-encrypt-to (default-value 'epa-file-encrypt-to)))))
(use-package! drag-stuff
@@ -62,11 +77,6 @@
;; or specific :post-handlers with:
;; (sp-pair "{" nil :post-handlers '(:rem ("| " "SPC")))
(after! smartparens
;; Smartparens is broken in `cc-mode' as of Emacs 27. See
;; <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
;; expensive it is. It's also less useful for evil users. This may need to
;; be reactivated for non-evil users though. Needs more testing!
@@ -76,7 +86,7 @@
sp-navigate-consider-sgml-tags nil)))
;; Autopair quotes more conservatively; if I'm next to a word/before another
;; quote, I likely don't want to open a new pair.
;; quote, I don't want to open a new pair or it would unbalance them.
(let ((unless-list '(sp-point-before-word-p
sp-point-after-word-p
sp-point-before-same-p)))
@@ -97,10 +107,13 @@
(sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p))
;; Major-mode specific fixes
(sp-local-pair '(ruby-mode enh-ruby-mode) "{" "}"
(sp-local-pair 'ruby-mode "{" "}"
:pre-handlers '(:rem sp-ruby-pre-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
(sp-local-pair '(emacs-lisp-mode org-mode markdown-mode gfm-mode)
"[" nil :post-handlers '(:rem ("| " "SPC")))
@@ -114,9 +127,7 @@
;; Disable electric keys in C modes because it interferes with smartparens
;; and custom bindings. We'll do it ourselves (mostly).
(after! cc-mode
(c-toggle-electric-state -1)
(c-toggle-auto-newline -1)
(setq c-electric-flag nil)
(setq-default c-electric-flag nil)
(dolist (key '("#" "{" "}" "/" "*" ";" "," ":" "(" ")" "\177"))
(define-key c-mode-base-map key nil))
@@ -176,7 +187,7 @@
(sp-local-pair "(*" "*)" :actions nil)
(sp-local-pair "(*" "*"
:actions '(insert)
:post-handlers '(("| " "SPC") ("|\n[i]*)[d-2]" "RET")))))
:post-handlers '(("| " "SPC") ("|[i]*)[d-2]" "RET")))))
(after! smartparens-markdown
(sp-with-modes '(markdown-mode gfm-mode)
@@ -196,32 +207,52 @@
;; This keybind allows * to skip over **.
(map! :map markdown-mode-map
:ig "*" (λ! (if (looking-at-p "\\*\\* *$")
(forward-char 2)
(call-interactively 'self-insert-command)))))
;; 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))
:ig "*" (general-predicate-dispatch nil
(looking-at-p "\\*\\* *")
(cmd! (forward-char 2)))))))
;;
;;; 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
;; sensibly (and consistently with similar contexts).
@@ -252,6 +283,7 @@
"s-c" (if (featurep 'evil) #'evil-yank #'copy-region-as-kill)
"s-v" #'yank
"s-s" #'save-buffer
"s-x" #'execute-extended-command
:v "s-x" #'kill-region
;; Buffer-local font scaling
"s-+" #'doom/reset-font-size
@@ -259,7 +291,7 @@
"s--" #'doom/decrease-font-size
;; Conventional text-editing keys & motions
"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
:v "s-/" #'evilnc-comment-operator
:gi [s-backspace] #'doom/backward-kill-to-bol-and-indent
@@ -278,6 +310,7 @@
(define-key! help-map
;; new keybinds
"'" #'describe-char
"u" #'doom/help-autodefs
"E" #'doom/sandbox
"M" #'doom/describe-active-minor-mode
"O" #'+lookup/online
@@ -313,25 +346,27 @@
;; replaces `apropos-documentation' b/c `apropos' covers this
"d" nil
"da" #'doom/help-autodefs
"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
"dh" #'doom/help
"dk" #'doom/goto-packages-file
"dl" #'doom/help-search-load-path
"dL" #'doom/help-search-loaded-files
"dm" #'doom/help-modules
"dn" #'doom/help-news
"dN" #'doom/help-news-search
"di" #'doom/goto-doomblock
"dp" #'doom/help-packages
"dP" #'doom/help-package-homepage
"dc" #'doom/goto-config-file
"dC" #'doom/help-package-config
"ds" #'doom/help-search
"dx" #'doom/sandbox
"dN" #'doom/help-search-news
"dpc" #'doom/help-package-config
"dpd" #'doom/goto-private-packages-file
"dph" #'doom/help-package-homepage
"dpp" #'doom/help-packages
"ds" #'doom/help-search-headings
"dS" #'doom/help-search
"dt" #'doom/toggle-profiler
"du" #'doom/help-autodefs
"dv" #'doom/version
"dx" #'doom/sandbox
;; replaces `apropos-command'
"a" #'apropos
@@ -348,7 +383,7 @@
"t" #'load-theme
;; replaces `finder-by-keyword' b/c not useful
"p" #'doom/help-packages
;; replaces `describe-package' b/c redundant w/ `doom/describe-package'
;; replaces `describe-package' b/c redundant w/ `doom/help-packages'
"P" #'find-library)
(after! which-key
@@ -372,10 +407,15 @@
;; A Doom convention where C-s on popups and interactive searches will invoke
;; ivy/helm for their superior filtering.
(define-key! :keymaps +default-minibuffer-maps
"C-s" (if (featurep! :completion ivy)
#'counsel-minibuffer-history
#'helm-minibuffer-history))
(when-let (command (cond ((featurep! :completion ivy)
#'counsel-minibuffer-history)
((featurep! :completion helm)
#'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.
;; 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
;; cmd+RET (Mac) / meta+RET (elsewhere) will add a new, blank line below
;; the current one.
:gni [C-return] #'+default/newline-below
:gni [C-S-return] #'+default/newline-above
:gn [C-return] #'+default/newline-below
:gn [C-S-return] #'+default/newline-above
(:when IS-MAC
:gni [s-return] #'+default/newline-below
:gni [S-s-return] #'+default/newline-above)
(:unless IS-MAC
:gni [M-return] #'+default/newline-below
:gni [M-S-return] #'+default/newline-above)))
:gn [s-return] #'+default/newline-below
:gn [S-s-return] #'+default/newline-above)))
;;

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