mirror of
https://gitlab.com/dwt1/dotfiles.git
synced 2026-04-14 12:48:29 +10:00
Moving to Doom Emacs!
This commit is contained in:
22
.emacs.d/LICENSE
Normal file
22
.emacs.d/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2019 Henrik Lissner.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
178
.emacs.d/README.md
Normal file
178
.emacs.d/README.md
Normal file
@@ -0,0 +1,178 @@
|
||||
<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>
|
||||
|
||||

|
||||
|
||||
<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>
|
||||
|
||||
---
|
||||
|
||||
**Quick start**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d
|
||||
~/.emacs.d/bin/doom install
|
||||
```
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [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)
|
||||
|
||||
# What is Doom 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.
|
||||
|
||||
Doom is a configuration for [GNU Emacs](https://www.gnu.org/software/emacs/)
|
||||
designed to make Emacs faster and easier to customize. It can serve as framework
|
||||
for your own configuration or a resource for fellow Emacs enthusiasts who want
|
||||
to learn more about our favorite OS.
|
||||
|
||||
## Doom's mantras
|
||||
|
||||
- **Gotta go fast.** Startup and run-time performance are high priorities.
|
||||
Expensive functionality (built-in or in plugins) is modified and optimized
|
||||
toward this end, otherwise, they must be opt-in.
|
||||
- **Close to metal.** There's less between you and vanilla Emacs, by design.
|
||||
There's less to grok. Modules should be syntactically sweet and backend logic
|
||||
explicit and abstraction-light. The code itself ought to be designed as if
|
||||
grokking it were part of the user experience; and it is!
|
||||
- **Opinionated, but not stubborn.** Doom is a bundle of reasonable defaults
|
||||
and curated opinions, but you aren't stuck with it. Use as little or as much
|
||||
of it as you like. Use it as-is as a complete Emacs distribution; disable
|
||||
everything and use it as a baseline for your own; or anywhere in between.
|
||||
- **Your system, your rules.** There are more ways to set up your programming
|
||||
environment than there are dislikes on Youtube Rewind '18, so Doom and its
|
||||
plugins promise not to *automatically* (and definitely not *silently*) install
|
||||
system dependencies. This means fonts, packages and programs. `doom doctor`
|
||||
will tell you what's missing though!
|
||||
|
||||
## 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!
|
||||
|
||||
# Getting Help
|
||||
|
||||
## Community
|
||||
|
||||
We have [a Discord server][url:discord]! Hop on and say hi!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Encountered strange behavior or an error? Here are some things to try before you
|
||||
shoot off that bug report:
|
||||
|
||||
- Run `bin/doom refresh`. This ensures Doom is properly set up and its autoloads
|
||||
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
|
||||
106
.emacs.d/bin/doom
Executable file
106
.emacs.d/bin/doom
Executable file
@@ -0,0 +1,106 @@
|
||||
#!/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
|
||||
|
||||
(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))
|
||||
|
||||
(push (expand-file-name "core" user-emacs-directory) load-path)
|
||||
(require 'core)
|
||||
(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)")
|
||||
(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.
|
||||
|
||||
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)
|
||||
(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)
|
||||
(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))
|
||||
|
||||
(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))
|
||||
|
||||
((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
|
||||
|
||||
(doom-cli-execute :main (cdr (member "--" argv)))
|
||||
(setq argv nil))
|
||||
25
.emacs.d/bin/doom.cmd
Normal file
25
.emacs.d/bin/doom.cmd
Normal file
@@ -0,0 +1,25 @@
|
||||
:: Forward the ./doom script to Emacs
|
||||
|
||||
@ECHO OFF
|
||||
SETLOCAL ENABLEDELAYEDEXPANSION
|
||||
|
||||
PUSHD "%~dp0" >NUL
|
||||
|
||||
SET args=
|
||||
SET command=%1
|
||||
|
||||
:LOOP
|
||||
SHIFT /1
|
||||
IF NOT [%1]==[] (
|
||||
SET args=%args% %1
|
||||
GOTO :LOOP
|
||||
)
|
||||
|
||||
IF [%command%]==[run] (
|
||||
start runemacs -Q %args% -l ..\init.el -f "doom-run-all-startup-hooks-h"
|
||||
) ELSE (
|
||||
emacs --quick --script .\doom -- %*
|
||||
)
|
||||
|
||||
POPD >NUL
|
||||
ECHO ON
|
||||
42
.emacs.d/bin/org-capture
Executable file
42
.emacs.d/bin/org-capture
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# Open an org-capture popup frame from the shell. This opens a temporary emacs
|
||||
# daemon if emacs isn't already running.
|
||||
#
|
||||
# Usage: org-capture [-k KEY] [MESSAGE]
|
||||
# Examples:
|
||||
# org-capture -k n "To the mind that is still, the whole universe surrenders."
|
||||
|
||||
set -e
|
||||
|
||||
cleanup() {
|
||||
emacsclient --eval '(let (kill-emacs-hook) (kill-emacs))'
|
||||
}
|
||||
|
||||
# If emacs isn't running, we start a temporary daemon, solely for this window.
|
||||
if ! emacsclient -e nil; then
|
||||
emacs --daemon
|
||||
trap cleanup EXIT INT TERM
|
||||
daemon=1
|
||||
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\""
|
||||
break
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
[ -t 0 ] && str="$*" || str=$(cat)
|
||||
|
||||
if [ $daemon ]; then
|
||||
emacsclient -a "" \
|
||||
-c -F '((name . "org-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
|
||||
# do it internally instead.
|
||||
emacsclient -a "" \
|
||||
-e "(+org-capture/open-frame \"$str\" ${key:-nil})"
|
||||
fi
|
||||
144
.emacs.d/bin/org-tangle
Executable file
144
.emacs.d/bin/org-tangle
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env sh
|
||||
":"; exec emacs --quick --script "$0" -- "$@" # -*- mode: emacs-lisp; lexical-binding: t; -*-
|
||||
;;; bin/org-tangle
|
||||
|
||||
;; Tangles source blocks from org files. Debug/info messages are directed to
|
||||
;; stderr and can be ignored.
|
||||
;;
|
||||
;; -l/--lang LANG
|
||||
;; Only include blocks in the specified language (e.g. emacs-lisp).
|
||||
;; -a/--all
|
||||
;; Tangle all blocks by default (unless it has :tangle nil set or a
|
||||
;; :notangle: tag)
|
||||
;; -t/--tag TAG
|
||||
;; --and TAG
|
||||
;; --or TAG
|
||||
;; Only include blocks in trees that have these tags. Combine multiple --and
|
||||
;; and --or's, or just use --tag (implicit --and).
|
||||
;; -p/--print
|
||||
;; Prints tangled code to stdout instead of to files
|
||||
;;
|
||||
;; Usage: org-tangle [[-l|--lang] LANG] some-file.org another.org
|
||||
;; Examples:
|
||||
;; org-tangle -l sh modules/some/module/README.org > install_module.sh
|
||||
;; org-tangle -l sh modules/lang/go/README.org | sh
|
||||
;; org-tangle --and tagA --and tagB my/literate/config.org
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'ob-tangle)
|
||||
|
||||
(defun usage ()
|
||||
(with-temp-buffer
|
||||
(insert (format "%s %s [OPTIONS] [TARGETS...]\n"
|
||||
"[1mUsage:[0m"
|
||||
(file-name-nondirectory load-file-name))
|
||||
"\n"
|
||||
"A command line interface for tangling org-mode files. TARGETS can be\n"
|
||||
"files or folders (which are searched for org files recursively).\n"
|
||||
"\n"
|
||||
"This is useful for literate configs that rely on command line\n"
|
||||
"workflows to build it.\n"
|
||||
"\n"
|
||||
"[1mExample:[0m\n"
|
||||
" org-tangle some-file.org\n"
|
||||
" org-tangle literate/config/\n"
|
||||
" org-tangle -p -l sh scripts.org > do_something.sh\n"
|
||||
" org-tangle -p -l python -t tagA -t tagB file.org | python\n"
|
||||
"\n"
|
||||
"[1mOptions:[0m\n"
|
||||
" -a --all\t\tTangle all blocks by default\n"
|
||||
" -l --lang LANG\tOnly tangle blocks written in LANG\n"
|
||||
" -p --print\t\tPrint tangled output to stdout than to files\n"
|
||||
" -t --tag TAG\n"
|
||||
" --and TAG\n"
|
||||
" --or TAG\n"
|
||||
" Lets you tangle org blocks by tag. You may have more than one\n"
|
||||
" of these options.\n")
|
||||
(princ (buffer-string))))
|
||||
|
||||
(defun *org-babel-tangle (orig-fn &rest args)
|
||||
"Don't write tangled blocks to files, print them to stdout."
|
||||
(cl-letf (((symbol-function 'write-region)
|
||||
(lambda (start end filename &optional append visit lockname mustbenew)
|
||||
(princ (buffer-string)))))
|
||||
(apply orig-fn args)))
|
||||
|
||||
(defun *org-babel-tangle-collect-blocks (&optional language tangle-file)
|
||||
"Like `org-babel-tangle-collect-blocks', but will ignore blocks that are in
|
||||
trees with the :notangle: tag."
|
||||
(let ((counter 0) last-heading-pos blocks)
|
||||
(org-babel-map-src-blocks (buffer-file-name)
|
||||
(let ((current-heading-pos
|
||||
(org-with-wide-buffer
|
||||
(org-with-limited-levels (outline-previous-heading)))))
|
||||
(if (eq last-heading-pos current-heading-pos) (cl-incf counter)
|
||||
(setq counter 1)
|
||||
(setq last-heading-pos current-heading-pos)))
|
||||
(unless (org-in-commented-heading-p)
|
||||
(require 'org)
|
||||
(let* ((tags (org-get-tags-at))
|
||||
(info (org-babel-get-src-block-info 'light))
|
||||
(src-lang (nth 0 info))
|
||||
(src-tfile (cdr (assq :tangle (nth 2 info)))))
|
||||
(cond ((member "notangle" tags))
|
||||
|
||||
((and (or or-tags and-tags)
|
||||
(or (not and-tags)
|
||||
(let ((a (cl-intersection and-tags tags :test #'string=))
|
||||
(b and-tags))
|
||||
(not (or (cl-set-difference a b :test #'equal)
|
||||
(cl-set-difference b a :test #'equal)))))
|
||||
(or (not or-tags)
|
||||
(cl-intersection or-tags tags :test #'string=))
|
||||
t))
|
||||
|
||||
((or (not (or all-blocks src-tfile))
|
||||
(string= src-tfile "no") ; tangle blocks by default
|
||||
(and tangle-file (not (equal tangle-file src-tfile)))
|
||||
(and language (not (string= language src-lang)))))
|
||||
|
||||
;; Add the spec for this block to blocks under its language.
|
||||
((let ((by-lang (assoc src-lang blocks))
|
||||
(block (org-babel-tangle-single-block counter)))
|
||||
(if by-lang
|
||||
(setcdr by-lang (cons block (cdr by-lang)))
|
||||
(push (cons src-lang (list block)) blocks))))))))
|
||||
;; Ensure blocks are in the correct order.
|
||||
(mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) blocks)))
|
||||
(advice-add #'org-babel-tangle-collect-blocks
|
||||
:override #'*org-babel-tangle-collect-blocks)
|
||||
|
||||
(defvar all-blocks nil)
|
||||
(defvar and-tags nil)
|
||||
(defvar or-tags nil)
|
||||
(let (lang srcs and-tags or-tags)
|
||||
(pop argv)
|
||||
(while argv
|
||||
(let ((arg (pop argv)))
|
||||
(pcase arg
|
||||
((or "-h" "--help")
|
||||
(usage)
|
||||
(error ""))
|
||||
((or "-a" "--all")
|
||||
(setq all-blocks t))
|
||||
((or "-l" "--lang")
|
||||
(setq lang (pop argv)))
|
||||
((or "-p" "--print")
|
||||
(advice-add #'org-babel-tangle :around #'*org-babel-tangle))
|
||||
((or "-t" "--tag" "--and")
|
||||
(push (pop argv) and-tags))
|
||||
("--or"
|
||||
(push (pop argv) or-tags))
|
||||
((guard (string-match-p "^--lang=" arg))
|
||||
(setq lang (cadr (split-string arg "=" t t))))
|
||||
((guard (file-directory-p arg))
|
||||
(setq srcs
|
||||
(append (directory-files-recursively arg "\\.org$")
|
||||
srcs)))
|
||||
((guard (file-exists-p arg))
|
||||
(push arg srcs))
|
||||
(_ (error "Unknown option or file: %s" arg)))))
|
||||
|
||||
(dolist (file srcs)
|
||||
(org-babel-tangle-file file nil lang))
|
||||
(kill-emacs 0))
|
||||
370
.emacs.d/core/autoload/buffers.el
Normal file
370
.emacs.d/core/autoload/buffers.el
Normal file
@@ -0,0 +1,370 @@
|
||||
;;; core/autoload/buffers.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-real-buffer-functions
|
||||
'(doom-dired-buffer-p)
|
||||
"A list of predicate functions run to determine if a buffer is real, unlike
|
||||
`doom-unreal-buffer-functions'. They are passed one argument: the buffer to be
|
||||
tested.
|
||||
|
||||
Should any of its function returns non-nil, the rest of the functions are
|
||||
ignored and the buffer is considered real.
|
||||
|
||||
See `doom-real-buffer-p' for more information.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-unreal-buffer-functions
|
||||
'(minibufferp doom-special-buffer-p doom-non-file-visiting-buffer-p)
|
||||
"A list of predicate functions run to determine if a buffer is *not* real,
|
||||
unlike `doom-real-buffer-functions'. They are passed one argument: the buffer to
|
||||
be tested.
|
||||
|
||||
Should any of these functions return non-nil, the rest of the functions are
|
||||
ignored and the buffer is considered unreal.
|
||||
|
||||
See `doom-real-buffer-p' for more information.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar-local doom-real-buffer-p nil
|
||||
"If non-nil, this buffer should be considered real no matter what. See
|
||||
`doom-real-buffer-p' for more information.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-fallback-buffer-name "*scratch*"
|
||||
"The name of the buffer to fall back to if no other buffers exist (will create
|
||||
it if it doesn't exist).")
|
||||
|
||||
|
||||
;;
|
||||
;; Functions
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-buffer-frame-predicate (buf)
|
||||
"To be used as the default frame buffer-predicate parameter. Returns nil if
|
||||
BUF should be skipped over by functions like `next-buffer' and `other-buffer'."
|
||||
(or (doom-real-buffer-p buf)
|
||||
(eq buf (doom-fallback-buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-fallback-buffer ()
|
||||
"Returns the fallback buffer, creating it if necessary. By default this is the
|
||||
scratch buffer. See `doom-fallback-buffer-name' to change this."
|
||||
(let (buffer-list-update-hook)
|
||||
(get-buffer-create doom-fallback-buffer-name)))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'doom-buffer-list #'buffer-list)
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-buffer-list (&optional project)
|
||||
"Return a list of buffers belonging to the specified PROJECT.
|
||||
|
||||
If PROJECT is nil, default to the current project.
|
||||
|
||||
If no project is active, return all buffers."
|
||||
(let ((buffers (doom-buffer-list)))
|
||||
(if-let* ((project-root
|
||||
(if project (expand-file-name project)
|
||||
(doom-project-root))))
|
||||
(cl-loop for buf in buffers
|
||||
if (projectile-project-buffer-p buf project-root)
|
||||
collect buf)
|
||||
buffers)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-open-projects ()
|
||||
"Return a list of projects with open buffers."
|
||||
(cl-loop with projects = (make-hash-table :test 'equal :size 8)
|
||||
for buffer in (doom-buffer-list)
|
||||
if (buffer-live-p buffer)
|
||||
if (doom-real-buffer-p buffer)
|
||||
if (with-current-buffer buffer (doom-project-root))
|
||||
do (puthash (abbreviate-file-name it) t projects)
|
||||
finally return (hash-table-keys projects)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-dired-buffer-p (buf)
|
||||
"Returns non-nil if BUF is a dired buffer."
|
||||
(with-current-buffer buf (derived-mode-p 'dired-mode)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-special-buffer-p (buf)
|
||||
"Returns non-nil if BUF's name starts and ends with an *."
|
||||
(equal (substring (buffer-name buf) 0 1) "*"))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-temp-buffer-p (buf)
|
||||
"Returns non-nil if BUF is temporary."
|
||||
(equal (substring (buffer-name buf) 0 1) " "))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-visible-buffer-p (buf)
|
||||
"Return non-nil if BUF is visible."
|
||||
(get-buffer-window buf))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-buried-buffer-p (buf)
|
||||
"Return non-nil if BUF is not visible."
|
||||
(not (doom-visible-buffer-p buf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-non-file-visiting-buffer-p (buf)
|
||||
"Returns non-nil if BUF does not have a value for `buffer-file-name'."
|
||||
(not (buffer-file-name buf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-real-buffer-list (&optional buffer-list)
|
||||
"Return a list of buffers that satify `doom-real-buffer-p'."
|
||||
(cl-remove-if-not #'doom-real-buffer-p (or buffer-list (doom-buffer-list))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-real-buffer-p (buffer-or-name)
|
||||
"Returns t if BUFFER-OR-NAME is a 'real' buffer.
|
||||
|
||||
A real buffer is a useful buffer; a first class citizen in Doom. Real ones
|
||||
should get special treatment, because we will be spending most of our time in
|
||||
them. Unreal ones should be low-profile and easy to cast aside, so we can focus
|
||||
on real ones.
|
||||
|
||||
The exact criteria for a real buffer is:
|
||||
|
||||
1. A non-nil value for the buffer-local value of the `doom-real-buffer-p'
|
||||
variable OR
|
||||
2. Any function in `doom-real-buffer-functions' returns non-nil OR
|
||||
3. None of the functions in `doom-unreal-buffer-functions' must return
|
||||
non-nil.
|
||||
|
||||
If BUFFER-OR-NAME is omitted or nil, the current buffer is tested."
|
||||
(or (bufferp buffer-or-name)
|
||||
(stringp buffer-or-name)
|
||||
(signal 'wrong-type-argument (list '(bufferp stringp) buffer-or-name)))
|
||||
(when-let (buf (get-buffer buffer-or-name))
|
||||
(and (buffer-live-p buf)
|
||||
(not (doom-temp-buffer-p buf))
|
||||
(or (buffer-local-value 'doom-real-buffer-p buf)
|
||||
(run-hook-with-args-until-success 'doom-real-buffer-functions buf)
|
||||
(not (run-hook-with-args-until-success 'doom-unreal-buffer-functions buf))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-unreal-buffer-p (buffer-or-name)
|
||||
"Return t if BUFFER-OR-NAME is an 'unreal' buffer.
|
||||
|
||||
See `doom-real-buffer-p' for details on what that means."
|
||||
(not (doom-real-buffer-p buffer-or-name)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-buffers-in-mode (modes &optional buffer-list derived-p)
|
||||
"Return a list of buffers whose `major-mode' is `eq' to MODE(S).
|
||||
|
||||
If DERIVED-P, test with `derived-mode-p', otherwise use `eq'."
|
||||
(let ((modes (doom-enlist modes)))
|
||||
(cl-remove-if-not (if derived-p
|
||||
(lambda (buf)
|
||||
(with-current-buffer buf
|
||||
(apply #'derived-mode-p modes)))
|
||||
(lambda (buf)
|
||||
(memq (buffer-local-value 'major-mode buf) modes)))
|
||||
(or buffer-list (doom-buffer-list)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-visible-windows (&optional window-list)
|
||||
"Return a list of the visible, non-popup (dedicated) windows."
|
||||
(cl-loop for window in (or window-list (window-list))
|
||||
when (or (window-parameter window 'visible)
|
||||
(not (window-dedicated-p window)))
|
||||
collect window))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-visible-buffers (&optional buffer-list)
|
||||
"Return a list of visible buffers (i.e. not buried)."
|
||||
(if buffer-list
|
||||
(cl-remove-if-not #'get-buffer-window buffer-list)
|
||||
(delete-dups (mapcar #'window-buffer (window-list)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-buried-buffers (&optional buffer-list)
|
||||
"Get a list of buffers that are buried."
|
||||
(cl-remove-if #'get-buffer-window (or buffer-list (doom-buffer-list))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-matching-buffers (pattern &optional buffer-list)
|
||||
"Get a list of all buffers that match the regex PATTERN."
|
||||
(cl-loop for buf in (or buffer-list (doom-buffer-list))
|
||||
when (string-match-p pattern (buffer-name buf))
|
||||
collect buf))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-set-buffer-real (buffer flag)
|
||||
"Forcibly mark BUFFER as FLAG (non-nil = real)."
|
||||
(with-current-buffer buffer
|
||||
(setq doom-real-buffer-p flag)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-kill-buffer-and-windows (buffer)
|
||||
"Kill the buffer and delete all the windows it's displayed in."
|
||||
(dolist (window (get-buffer-window-list buffer))
|
||||
(unless (one-window-p t)
|
||||
(delete-window window)))
|
||||
(kill-buffer buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-fixup-windows (windows)
|
||||
"Ensure that each of WINDOWS is showing a real buffer or the fallback buffer."
|
||||
(dolist (window windows)
|
||||
(with-selected-window window
|
||||
(when (doom-unreal-buffer-p (window-buffer))
|
||||
(previous-buffer)
|
||||
(when (doom-unreal-buffer-p (window-buffer))
|
||||
(switch-to-buffer (doom-fallback-buffer)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-kill-buffer-fixup-windows (buffer)
|
||||
"Kill the BUFFER and ensure all the windows it was displayed in have switched
|
||||
to a real buffer or the fallback buffer."
|
||||
(let ((windows (get-buffer-window-list buffer)))
|
||||
(kill-buffer buffer)
|
||||
(doom-fixup-windows (cl-remove-if-not #'window-live-p windows))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-kill-buffers-fixup-windows (buffers)
|
||||
"Kill the BUFFERS and ensure all the windows they were displayed in have
|
||||
switched to a real buffer or the fallback buffer."
|
||||
(let ((seen-windows (make-hash-table :test 'eq :size 8)))
|
||||
(dolist (buffer buffers)
|
||||
(let ((windows (get-buffer-window-list buffer)))
|
||||
(kill-buffer buffer)
|
||||
(dolist (window (cl-remove-if-not #'window-live-p windows))
|
||||
(puthash window t seen-windows))))
|
||||
(doom-fixup-windows (hash-table-keys seen-windows))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-kill-matching-buffers (pattern &optional buffer-list)
|
||||
"Kill all buffers (in current workspace OR in BUFFER-LIST) that match the
|
||||
regex PATTERN. Returns the number of killed buffers."
|
||||
(let ((buffers (doom-matching-buffers pattern buffer-list)))
|
||||
(dolist (buf buffers (length buffers))
|
||||
(kill-buffer buf))))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-mark-buffer-as-real-h ()
|
||||
"Hook function that marks the current buffer as real."
|
||||
(doom-set-buffer-real (current-buffer) t))
|
||||
|
||||
|
||||
;;
|
||||
;; Interactive commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-this-buffer-in-all-windows (buffer &optional dont-save)
|
||||
"Kill BUFFER globally and ensure all windows previously showing this buffer
|
||||
have switched to a real buffer or the fallback buffer.
|
||||
|
||||
If DONT-SAVE, don't prompt to save modified buffers (discarding their changes)."
|
||||
(interactive
|
||||
(list (current-buffer) current-prefix-arg))
|
||||
(cl-assert (bufferp buffer) t)
|
||||
(when (and (buffer-modified-p buffer) dont-save)
|
||||
(with-current-buffer buffer
|
||||
(set-buffer-modified-p nil)))
|
||||
(doom-kill-buffer-fixup-windows buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-all-buffers (&optional buffer-list interactive)
|
||||
"Kill all buffers and closes their windows.
|
||||
|
||||
If the prefix arg is passed, doesn't close windows and only kill buffers that
|
||||
belong to the current project."
|
||||
(interactive
|
||||
(list (if current-prefix-arg
|
||||
(doom-project-buffer-list)
|
||||
(doom-buffer-list))
|
||||
t))
|
||||
(if (null buffer-list)
|
||||
(message "No buffers to kill")
|
||||
(save-some-buffers)
|
||||
(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)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-other-buffers (&optional buffer-list interactive)
|
||||
"Kill all other buffers (besides the current one).
|
||||
|
||||
If the prefix arg is passed, kill only buffers that belong to the current
|
||||
project."
|
||||
(interactive
|
||||
(list (delq (current-buffer)
|
||||
(if current-prefix-arg
|
||||
(doom-project-buffer-list)
|
||||
(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))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-matching-buffers (pattern &optional buffer-list interactive)
|
||||
"Kill buffers that match PATTERN in BUFFER-LIST.
|
||||
|
||||
If the prefix arg is passed, only kill matching buffers in the current project."
|
||||
(interactive
|
||||
(list (read-regexp "Buffer pattern: ")
|
||||
(if current-prefix-arg
|
||||
(doom-project-buffer-list)
|
||||
(doom-buffer-list))
|
||||
t))
|
||||
(doom-kill-matching-buffers pattern buffer-list)
|
||||
(when interactive
|
||||
(message "Killed %d buffer(s)"
|
||||
(- (length buffer-list)
|
||||
(length (cl-remove-if-not #'buffer-live-p buffer-list))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-buried-buffers (&optional buffer-list interactive)
|
||||
"Kill buffers that are buried.
|
||||
|
||||
If PROJECT-P (universal argument), only kill buried buffers belonging to the
|
||||
current project."
|
||||
(interactive
|
||||
(list (doom-buried-buffers
|
||||
(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))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/kill-project-buffers (project &optional interactive)
|
||||
"Kill buffers for the specified PROJECT."
|
||||
(interactive
|
||||
(list (if-let (open-projects (doom-open-projects))
|
||||
(completing-read
|
||||
"Kill buffers for project: " open-projects
|
||||
nil t nil nil
|
||||
(if-let* ((project-root (doom-project-root))
|
||||
(project-root (abbreviate-file-name project-root))
|
||||
((member project-root open-projects)))
|
||||
project-root))
|
||||
(message "No projects are open!")
|
||||
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))))))))
|
||||
95
.emacs.d/core/autoload/cache.el
Normal file
95
.emacs.d/core/autoload/cache.el
Normal file
@@ -0,0 +1,95 @@
|
||||
;;; core/autoload/cache.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This little library thinly wraps around persistent-soft (which is a pcache
|
||||
;; wrapper, how about that). It has three purposes:
|
||||
;;
|
||||
;; + To encapsulate the cache backend (persistent-soft/pcache in this case), in
|
||||
;; case it needs to change.
|
||||
;; + To provide `doom-cache-persist': a mechanism for easily persisting
|
||||
;; variables across Emacs sessions.
|
||||
;; + To lazy-load persistent-soft until it is really needed.
|
||||
;;
|
||||
;; Like persistent-soft, caches assume a 2-tier structure, where all caches are
|
||||
;; namespaced by location.
|
||||
|
||||
(defvar doom-cache-alists '(t)
|
||||
"An alist of alists, containing lists of variables for the doom cache library
|
||||
to persist across Emacs sessions.")
|
||||
|
||||
(defvar doom-cache-location 'doom
|
||||
"The default location for cache files. This symbol is translated into a file
|
||||
name under `pcache-directory' (by default a subdirectory under
|
||||
`doom-cache-dir'). One file may contain multiple cache entries.")
|
||||
|
||||
(defun doom-save-persistent-cache-h ()
|
||||
"Hook to run when an Emacs session is killed. Saves all persisted variables
|
||||
listed in `doom-cache-alists' to files."
|
||||
(dolist (alist (butlast doom-cache-alists 1))
|
||||
(cl-loop with key = (car alist)
|
||||
for var in (cdr alist)
|
||||
if (symbol-value var)
|
||||
do (doom-cache-set var it nil key))))
|
||||
(add-hook 'kill-emacs-hook #'doom-save-persistent-cache-h)
|
||||
|
||||
|
||||
;;
|
||||
;; Library
|
||||
|
||||
;;;###autoload
|
||||
(defmacro with-cache! (location &rest body)
|
||||
"Runs BODY with a different default `doom-cache-location'."
|
||||
(declare (indent defun))
|
||||
`(let ((doom-cache-location ',location))
|
||||
,@body))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-persist (location variables)
|
||||
"Persist VARIABLES (list of symbols) in LOCATION (symbol).
|
||||
|
||||
This populates these variables with cached values, if one exists, and saves them
|
||||
to file when Emacs quits.
|
||||
|
||||
Warning: this is incompatible with buffer-local variables."
|
||||
(dolist (var variables)
|
||||
(when (doom-cache-exists var location)
|
||||
(set var (doom-cache-get var location))))
|
||||
(setf (alist-get location doom-cache-alists)
|
||||
(append variables (cdr (assq location doom-cache-alists)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-desist (location &optional variables)
|
||||
"Unregisters VARIABLES (list of symbols) in LOCATION (symbol) from
|
||||
`doom-cache-alists', thus preventing them from being saved between sessions.
|
||||
Does not affect the actual variables themselves or their values."
|
||||
(if variables
|
||||
(setf (alist-get location doom-cache-alists)
|
||||
(cl-set-difference (cdr (assq location doom-cache-alists))
|
||||
variables))
|
||||
(delq (assq location doom-cache-alists)
|
||||
doom-cache-alists)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-get (key &optional location)
|
||||
"Retrieve KEY from LOCATION (defaults to `doom-cache-location'), if it exists
|
||||
and hasn't expired."
|
||||
(persistent-soft-fetch
|
||||
key (symbol-name (or location doom-cache-location))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-set (key value &optional ttl location)
|
||||
"Set KEY to VALUE in the cache. TTL is the time (in seconds) until this cache
|
||||
entry expires. LOCATION is the super-key to store this cache item under; the
|
||||
default is `doom-cache-location'. "
|
||||
(persistent-soft-store
|
||||
key value
|
||||
(symbol-name (or location doom-cache-location)) ttl))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-exists (key &optional location)
|
||||
"Returns t if KEY exists at LOCATION (defaults to `doom-cache-location')."
|
||||
(persistent-soft-exists-p key (or location doom-cache-location)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-cache-clear (&optional location)
|
||||
"Clear a cache LOCATION (defaults to `doom-cache-location')."
|
||||
(persistent-soft-flush (or location doom-cache-location)))
|
||||
45
.emacs.d/core/autoload/cli.el
Normal file
45
.emacs.d/core/autoload/cli.el
Normal file
@@ -0,0 +1,45 @@
|
||||
;;; 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)))))
|
||||
142
.emacs.d/core/autoload/config.el
Normal file
142
.emacs.d/core/autoload/config.el
Normal file
@@ -0,0 +1,142 @@
|
||||
;;; core/autoload/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-bin-dir (concat doom-emacs-dir "bin/"))
|
||||
(defvar doom-bin (concat doom-bin-dir "doom"))
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-reload-hook nil
|
||||
"A list of hooks to run when `doom/reload' is called.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-reloading-p nil
|
||||
"TODO")
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/open-private-config ()
|
||||
"Browse your `doom-private-dir'."
|
||||
(interactive)
|
||||
(unless (file-directory-p doom-private-dir)
|
||||
(make-directory doom-private-dir t))
|
||||
(doom-project-browse doom-private-dir))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/find-file-in-private-config ()
|
||||
"Search for a file in `doom-private-dir'."
|
||||
(interactive)
|
||||
(doom-project-find-file doom-private-dir))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/goto-doomblock ()
|
||||
"Open your private init.el and go to your `doom!' block."
|
||||
(interactive)
|
||||
(find-file (expand-file-name "init.el" doom-private-dir))
|
||||
(goto-char
|
||||
(or (save-excursion
|
||||
(goto-char (point-min))
|
||||
(search-forward "(doom!" nil t))
|
||||
(point))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/goto-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 ()
|
||||
"Open your private packages.el file."
|
||||
(interactive)
|
||||
(find-file (expand-file-name "packages.el" doom-private-dir)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Managements
|
||||
|
||||
(cl-defmacro doom--compile (command &key on-success on-failure)
|
||||
(declare (indent defun))
|
||||
`(with-current-buffer (compile ,command)
|
||||
(add-hook
|
||||
'compilation-finish-functions
|
||||
(lambda (_buf status)
|
||||
(if (equal status "finished\n")
|
||||
,on-success
|
||||
,on-failure))
|
||||
nil 'local)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/reload ()
|
||||
"Reloads your private config.
|
||||
|
||||
This is experimental! It will try to do as `bin/doom refresh' does, but from
|
||||
within this Emacs session. i.e. it reload autoloads files (if necessary),
|
||||
reloads your package list, and lastly, reloads your private config.el.
|
||||
|
||||
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
|
||||
(user-error "Failed to reload your config")))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/reload-autoloads ()
|
||||
"Reload only `doom-autoload-file' and `doom-package-autoload-file'.
|
||||
|
||||
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
|
||||
line."
|
||||
(interactive)
|
||||
(require 'core-cli)
|
||||
(require 'core-packages)
|
||||
(doom-initialize-packages)
|
||||
(doom-cli-reload-autoloads nil 'force))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/reload-env (&optional arg)
|
||||
"Regenerates and/or reloads your envvar file.
|
||||
|
||||
If passed the prefix ARG, clear the envvar file. Uses the same mechanism as
|
||||
'bin/doom env'.
|
||||
|
||||
An envvar file contains a snapshot of your shell environment, which can be
|
||||
imported into Emacs."
|
||||
(interactive "P")
|
||||
(when IS-WINDOWS
|
||||
(user-error "Cannot reload envvar file from within Emacs on Windows, run it from cmd.exe"))
|
||||
(doom--compile
|
||||
(format "%s -ic '%s env%s'"
|
||||
(string-trim
|
||||
(shell-command-to-string
|
||||
(format "getent passwd %S | cut -d: -f7"
|
||||
(user-login-name))))
|
||||
doom-bin (if arg " -c" ""))
|
||||
:on-success
|
||||
(let ((doom-reloading-p t))
|
||||
(unless arg
|
||||
(doom-load-envvars-file doom-env-file)))
|
||||
:on-failure
|
||||
(error "Failed to generate env file")))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/upgrade ()
|
||||
"Run 'doom upgrade' then prompt to restart Emacs."
|
||||
(interactive)
|
||||
(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))))
|
||||
344
.emacs.d/core/autoload/debug.el
Normal file
344
.emacs.d/core/autoload/debug.el
Normal file
@@ -0,0 +1,344 @@
|
||||
;;; core/autoload/debug.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-run-all-startup-hooks-h ()
|
||||
"Run all startup Emacs hooks. Meant to be executed after starting Emacs with
|
||||
-q or -Q, for example:
|
||||
|
||||
emacs -Q -l init.el -f doom-run-all-startup-hooks-h"
|
||||
(run-hook-wrapped 'after-init-hook #'doom-try-run-hook)
|
||||
(setq after-init-time (current-time))
|
||||
(mapc (doom-rpartial #'run-hook-wrapped #'doom-try-run-hook)
|
||||
(list 'delayed-warnings-hook
|
||||
'emacs-startup-hook 'tty-setup-hook
|
||||
'window-setup-hook)))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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))))
|
||||
|
||||
;;;###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)
|
||||
(let ((default-directory doom-emacs-dir)
|
||||
(doom-modules (doom-modules)))
|
||||
(cl-letf
|
||||
(((symbol-function 'sh)
|
||||
(lambda (&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))
|
||||
(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")))
|
||||
(system
|
||||
(type . ,system-type)
|
||||
(config . ,system-configuration)
|
||||
(shell . ,shell-file-name)
|
||||
(uname . ,(if IS-WINDOWS
|
||||
"n/a"
|
||||
(sh "uname" "-msrv")))
|
||||
(path . ,(mapcar #'abbreviate-file-name exec-path)))
|
||||
(config
|
||||
(envfile
|
||||
. ,(cond ((file-exists-p doom-env-file) 'envvar-file)
|
||||
((featurep 'exec-path-from-shell) 'exec-path-from-shell)))
|
||||
(elc-files
|
||||
. ,(length (doom-files-in `(,@doom-modules-dirs
|
||||
,doom-core-dir
|
||||
,doom-private-dir)
|
||||
:type 'files :match "\\.elc$")))
|
||||
(modules
|
||||
,@(or (cl-loop with cat = nil
|
||||
for key being the hash-keys of doom-modules
|
||||
if (or (not cat)
|
||||
(not (eq cat (car key))))
|
||||
do (setq cat (car key))
|
||||
and collect cat
|
||||
collect
|
||||
(let ((flags (doom-module-get cat (cdr key) :flags)))
|
||||
(if flags
|
||||
`(,(cdr key) ,@flags)
|
||||
(cdr key))))
|
||||
'("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))
|
||||
(error (format "<%S>" e)))
|
||||
'("n/a")))
|
||||
(elpa
|
||||
,@(or (condition-case e
|
||||
(progn
|
||||
(package-initialize)
|
||||
(cl-loop for (name . _) in package-alist
|
||||
collect (format "%s" name)))
|
||||
(error (format "<%S>" e)))
|
||||
'("n/a"))))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/version ()
|
||||
"Display the current version of Doom & Emacs, including the current Doom
|
||||
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"
|
||||
doom-version
|
||||
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"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/info (&optional raw)
|
||||
"Collects some debug information about your Emacs session, formats it into
|
||||
markdown and copies it to your clipboard, ready to be pasted into bug reports!"
|
||||
(interactive "P")
|
||||
(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))
|
||||
(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)))))
|
||||
(insert "<details>\n\n```\n")
|
||||
(dolist (group info)
|
||||
(insert! "%-8s%-10s %s\n"
|
||||
((car group)
|
||||
(caadr group)
|
||||
(cdadr group)))
|
||||
(dolist (spec (cddr group))
|
||||
(insert! (indent 8 "%-10s %s\n")
|
||||
((car spec) (cdr spec)))))
|
||||
(insert "```\n</details>"))
|
||||
(if noninteractive
|
||||
(print! (buffer-string))
|
||||
(switch-to-buffer buffer)
|
||||
(kill-new (buffer-string))
|
||||
(print! (green "Copied markdown to clipboard"))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/am-i-secure ()
|
||||
"Test to see if your root certificates are securely configured in emacs."
|
||||
(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/")
|
||||
if (condition-case _e
|
||||
(url-retrieve-synchronously bad)
|
||||
(error nil))
|
||||
collect bad)))
|
||||
(error "tls seems to be misconfigured (it got %s)."
|
||||
bad-hosts)
|
||||
(url-retrieve "https://badssl.com"
|
||||
(lambda (status)
|
||||
(if (or (not status) (plist-member status :error))
|
||||
(warn "Something went wrong.\n\n%s" (pp-to-string status))
|
||||
(message "Your trust roots are set up properly.\n\n%s" (pp-to-string status))
|
||||
t)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Vanilla sandbox
|
||||
|
||||
(defun doom--run-sandbox (&optional mode)
|
||||
(interactive)
|
||||
(let ((contents (buffer-string))
|
||||
(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)))
|
||||
(let ((args (if (eq mode 'doom)
|
||||
(list "-l" file)
|
||||
(list "-Q" "-l" file))))
|
||||
(require 'restart-emacs)
|
||||
(condition-case e
|
||||
(cond ((display-graphic-p)
|
||||
(if (memq system-type '(windows-nt ms-dos))
|
||||
(restart-emacs--start-gui-on-windows args)
|
||||
(restart-emacs--start-gui-using-sh args)))
|
||||
((memq system-type '(windows-nt ms-dos))
|
||||
(user-error "Cannot start another Emacs from Windows shell."))
|
||||
((suspend-emacs
|
||||
(format "%s %s -nw; fg"
|
||||
(shell-quote-argument (restart-emacs--get-emacs-binary))
|
||||
(mapconcat #'shell-quote-argument args " ")))))
|
||||
(error
|
||||
(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)))
|
||||
|
||||
(defvar doom-sandbox-emacs-lisp-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "C-c C-c") #'doom--run-vanilla-emacs)
|
||||
(define-key map (kbd "C-c C-d") #'doom--run-vanilla-doom)
|
||||
(define-key map (kbd "C-c C-p") #'doom--run-vanilla-doom+)
|
||||
(define-key map (kbd "C-c C-f") #'doom--run-full-doom)
|
||||
(define-key map (kbd "C-c C-k") #'kill-current-buffer)
|
||||
map))
|
||||
|
||||
(define-derived-mode doom-sandbox-emacs-lisp-mode emacs-lisp-mode "Sandbox Elisp"
|
||||
"TODO")
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/sandbox ()
|
||||
"Open the Emacs Lisp sandbox.
|
||||
|
||||
This is a test bed for running Emacs Lisp in another instance of Emacs with
|
||||
varying amounts of Doom loaded, including:
|
||||
|
||||
a) vanilla Emacs (nothing loaded),
|
||||
b) vanilla Doom (only Doom core),
|
||||
c) Doom + modules - your private config or
|
||||
c) Doom + modules + your private config (a complete Doom session)
|
||||
|
||||
This is done without sacrificing access to installed packages. Use the sandbox
|
||||
to reproduce bugs and determine if Doom is to blame."
|
||||
(interactive)
|
||||
(let* ((buffer-name "*doom:sandbox*")
|
||||
(exists (get-buffer buffer-name))
|
||||
(buf (get-buffer-create buffer-name)))
|
||||
(with-current-buffer buf
|
||||
(doom-sandbox-emacs-lisp-mode)
|
||||
(setq-local default-directory doom-emacs-dir)
|
||||
(unless (buffer-live-p exists)
|
||||
(doom-template-insert "VANILLA_SANDBOX")
|
||||
(let ((contents (substitute-command-keys (buffer-string))))
|
||||
(erase-buffer)
|
||||
(insert contents "\n")))
|
||||
(goto-char (point-max)))
|
||||
(pop-to-buffer buf)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Reporting bugs
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/report-bug ()
|
||||
"Open a markdown buffer destinated to populate the New Issue page on Doom
|
||||
Emacs' issue tracker.
|
||||
|
||||
If called when a backtrace buffer is present, it and the output of `doom-info'
|
||||
will be automatically appended to the result."
|
||||
(interactive)
|
||||
(browse-url "https://github.com/hlissner/doom-emacs/issues/new/choose"))
|
||||
|
||||
|
||||
;;
|
||||
;;; Profiling
|
||||
|
||||
(defvar doom--profiler nil)
|
||||
;;;###autoload
|
||||
(defun doom/toggle-profiler ()
|
||||
"Toggle the Emacs profiler. Run it again to see the profiling report."
|
||||
(interactive)
|
||||
(if (not doom--profiler)
|
||||
(profiler-start 'cpu+mem)
|
||||
(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"))))
|
||||
343
.emacs.d/core/autoload/files.el
Normal file
343
.emacs.d/core/autoload/files.el
Normal file
@@ -0,0 +1,343 @@
|
||||
;;; core/autoload/files.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun doom--resolve-path-forms (spec &optional directory)
|
||||
"Converts a simple nested series of or/and forms into a series of
|
||||
`file-exists-p' checks.
|
||||
|
||||
For example
|
||||
|
||||
(doom--resolve-path-forms
|
||||
'(or A (and B C))
|
||||
\"~\")
|
||||
|
||||
Returns (approximately):
|
||||
|
||||
'(let* ((_directory \"~\")
|
||||
(A (expand-file-name A _directory))
|
||||
(B (expand-file-name B _directory))
|
||||
(C (expand-file-name C _directory)))
|
||||
(or (and (file-exists-p A) A)
|
||||
(and (if (file-exists-p B) B)
|
||||
(if (file-exists-p C) C))))
|
||||
|
||||
This is used by `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))))))
|
||||
|
||||
(defun doom--path (&rest segments)
|
||||
(let (file-name-handler-alist)
|
||||
(let ((dir (pop segments)))
|
||||
(unless segments
|
||||
(setq dir (expand-file-name dir)))
|
||||
(while segments
|
||||
(setq dir (expand-file-name (car segments) dir)
|
||||
segments (cdr segments)))
|
||||
dir)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-glob (&rest segments)
|
||||
"Construct a path from SEGMENTS and expand glob patterns.
|
||||
Returns nil if the path doesn't exist."
|
||||
(let* (case-fold-search
|
||||
file-name-handler-alist
|
||||
(dir (apply #'doom--path segments)))
|
||||
(if (string-match-p "[[*?]" dir)
|
||||
(file-expand-wildcards dir t)
|
||||
(if (file-exists-p dir)
|
||||
dir))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-path (&rest segments)
|
||||
"Constructs a file path from SEGMENTS."
|
||||
(if segments
|
||||
(apply #'doom--path segments)
|
||||
(file!)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-dir (&rest segments)
|
||||
"Constructs a path from SEGMENTS.
|
||||
See `doom-path'."
|
||||
(when-let (path (apply #'doom-path segments))
|
||||
(directory-file-name (file-name-directory path))))
|
||||
|
||||
;;;###autoload
|
||||
(cl-defun doom-files-in
|
||||
(paths &rest rest
|
||||
&key
|
||||
filter
|
||||
map
|
||||
(full t)
|
||||
(follow-symlinks t)
|
||||
(type 'files)
|
||||
(relative-to (unless full default-directory))
|
||||
(depth 99999)
|
||||
(mindepth 0)
|
||||
(match "/[^._][^/]+"))
|
||||
"Return a list of files/directories in PATHS (one string or a list of them).
|
||||
|
||||
FILTER is a function or symbol that takes one argument (the path). If it returns
|
||||
non-nil, the entry will be excluded.
|
||||
|
||||
MAP is a function or symbol which will be used to transform each entry in the
|
||||
results.
|
||||
|
||||
TYPE determines what kind of path will be included in the results. This can be t
|
||||
(files and folders), 'files or 'dirs.
|
||||
|
||||
By default, this function returns paths relative to PATH-OR-PATHS if it is a
|
||||
single path. If it a list of paths, this function returns absolute paths.
|
||||
Otherwise, by setting RELATIVE-TO to a path, the results will be transformed to
|
||||
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)
|
||||
(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)))))
|
||||
((and (memq type '(t files))
|
||||
(string-match-p match file)
|
||||
(not (and filter (funcall filter file)))
|
||||
(<= mindepth 0))
|
||||
(push (if relative-to
|
||||
(file-relative-name file relative-to)
|
||||
file)
|
||||
result))))
|
||||
result))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-file-cookie-p (file &optional cookie null-value)
|
||||
"Returns the evaluated result of FORM in a ;;;###COOKIE FORM at the top of
|
||||
FILE.
|
||||
|
||||
If COOKIE doesn't exist, return NULL-VALUE."
|
||||
(unless (file-exists-p file)
|
||||
(signal 'file-missing file))
|
||||
(unless (file-readable-p file)
|
||||
(error "%S is unreadable" file))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file nil 0 256)
|
||||
(if (re-search-forward (format "^;;;###%s " (regexp-quote (or cookie "if")))
|
||||
nil t)
|
||||
(let ((load-file-name file))
|
||||
(eval (sexp-at-point) t))
|
||||
null-value)))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro file-exists-p! (files &optional directory)
|
||||
"Returns non-nil if the FILES in DIRECTORY all exist.
|
||||
|
||||
DIRECTORY is a path; defaults to `default-directory'.
|
||||
|
||||
Returns the last file found to meet the rules set by FILES, which can be a
|
||||
single file or nested compound statement of `and' and `or' statements."
|
||||
`(let ((p ,(doom--resolve-path-forms files directory)))
|
||||
(and p (expand-file-name p ,directory))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-file-size (file &optional dir)
|
||||
"Returns the size of FILE (in DIR) in bytes."
|
||||
(let ((file (expand-file-name file dir)))
|
||||
(unless (file-exists-p file)
|
||||
(error "Couldn't find file %S" file))
|
||||
(unless (file-readable-p file)
|
||||
(error "File %S is unreadable; can't acquire its filesize"
|
||||
file))
|
||||
(nth 7 (file-attributes file))))
|
||||
|
||||
(defvar w32-get-true-file-attributes)
|
||||
;;;###autoload
|
||||
(defun doom-directory-size (dir)
|
||||
"Returns the size of FILE (in DIR) in kilobytes."
|
||||
(unless (file-directory-p dir)
|
||||
(error "Directory %S does not exist" dir))
|
||||
(if (executable-find "du")
|
||||
(/ (string-to-number (cdr (doom-call-process "du" "-sb" dir)))
|
||||
1024.0)
|
||||
;; REVIEW This is slow and terribly inaccurate, but it's something
|
||||
(let ((w32-get-true-file-attributes t)
|
||||
(file-name-handler-alist dir)
|
||||
(max-lisp-eval-depth 5000)
|
||||
(sum 0.0))
|
||||
(dolist (attrs (directory-files-and-attributes dir nil nil t) sum)
|
||||
(unless (member (car attrs) '("." ".."))
|
||||
(cl-incf
|
||||
sum (if (eq (nth 1 attrs) t) ; is directory
|
||||
(doom-directory-size (expand-file-name (car attrs) dir))
|
||||
(/ (nth 8 attrs) 1024.0))))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###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)."
|
||||
(interactive
|
||||
(list (file-truename (buffer-file-name))
|
||||
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))))))))
|
||||
|
||||
;;;###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."
|
||||
(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)))
|
||||
|
||||
;;;###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."
|
||||
(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)))
|
||||
|
||||
(defun doom--sudo-file (file)
|
||||
(let ((host (or (file-remote-p file 'host) "localhost")))
|
||||
(concat "/" (when (file-remote-p file)
|
||||
(concat (file-remote-p file 'method) ":"
|
||||
(if-let (user (file-remote-p file 'user))
|
||||
(concat user "@" host)
|
||||
host)
|
||||
"|"))
|
||||
"sudo:root@" host
|
||||
":" (or (file-remote-p file 'localname)
|
||||
file))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/sudo-find-file (file)
|
||||
"Open FILE as root."
|
||||
(interactive "FOpen file as root: ")
|
||||
(find-file (doom--sudo-file file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/sudo-this-file ()
|
||||
"Open the current file as root."
|
||||
(interactive)
|
||||
(find-alternate-file (doom--sudo-file buffer-file-name)))
|
||||
136
.emacs.d/core/autoload/fonts.el
Normal file
136
.emacs.d/core/autoload/fonts.el
Normal file
@@ -0,0 +1,136 @@
|
||||
;;; core/autoload/fonts.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-font-increment 2
|
||||
"How many steps to increase the font size each time `doom/increase-font-size'
|
||||
or `doom/decrease-font-size' are invoked.")
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-big-font nil
|
||||
"The font to use for `doom-big-font-mode'. If nil, `doom-font' will be used,
|
||||
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
|
||||
"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.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(defun doom--font-name (fontname frame)
|
||||
(when (query-fontset fontname)
|
||||
(when-let (ascii (assq 'ascii (aref (fontset-info fontname frame) 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))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-adjust-font-size (increment &optional frame)
|
||||
"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))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/reload-font ()
|
||||
"Reload your fonts, if they're set.
|
||||
See `doom-init-fonts-h'."
|
||||
(interactive)
|
||||
(when doom-font
|
||||
(set-frame-font doom-font t))
|
||||
(doom-init-fonts-h)
|
||||
(mapc #'doom-init-extra-fonts-h (frame-list)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/increase-font-size (count)
|
||||
"Enlargens the font size across the current and child frames."
|
||||
(interactive "p")
|
||||
(doom-adjust-font-size count))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/decrease-font-size (count)
|
||||
"Shrinks the font size across the current and child frames."
|
||||
(interactive "p")
|
||||
(doom-adjust-font-size (- count)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/reset-font-size ()
|
||||
"Reset font size and `text-scale'.
|
||||
|
||||
Assuming it has been adjusted via `doom/increase-font-size' and
|
||||
`doom/decrease-font-size', or `text-scale-*' commands."
|
||||
(interactive)
|
||||
(let (success)
|
||||
(when (and (boundp 'text-scale-mode-amount)
|
||||
(/= text-scale-mode-amount 0))
|
||||
(text-scale-set 0)
|
||||
(setq success t))
|
||||
(when (doom-adjust-font-size nil)
|
||||
(setq success t))
|
||||
(unless success
|
||||
(user-error "The font hasn't been resized"))))
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode doom-big-font-mode
|
||||
"A global mode that resizes the font, for streams, screen-sharing and
|
||||
presentations.
|
||||
|
||||
This uses `doom/increase-font-size' under the hood, and enlargens the font by
|
||||
`doom-big-font-increment'."
|
||||
:init-value nil
|
||||
:lighter " BIG"
|
||||
:global t
|
||||
(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))
|
||||
(doom-adjust-font-size
|
||||
(and doom-big-font-mode
|
||||
(integerp doom-big-font-increment)
|
||||
(/= doom-big-font-increment 0)
|
||||
doom-big-font-increment))))
|
||||
243
.emacs.d/core/autoload/format.el
Normal file
243
.emacs.d/core/autoload/format.el
Normal file
@@ -0,0 +1,243 @@
|
||||
;;; 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)))
|
||||
646
.emacs.d/core/autoload/help.el
Normal file
646
.emacs.d/core/autoload/help.el
Normal file
@@ -0,0 +1,646 @@
|
||||
;;; core/autoload/help.el -*- lexical-binding: t; -*-
|
||||
|
||||
(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)
|
||||
(crystal-mode :lang crystal)
|
||||
(lisp-mode :lang common-lisp)
|
||||
(csharp-mode :lang csharp)
|
||||
(clojure-mode :lang clojure)
|
||||
(clojurescript-mode :lang clojure)
|
||||
(graphql-mode :lang data)
|
||||
(toml-mode :lang data)
|
||||
(json-mode :lang data)
|
||||
(yaml-mode :lang data)
|
||||
(csv-mode :lang data)
|
||||
(dhall-mode :lang data)
|
||||
(erlang-mode :lang erlang)
|
||||
(elixir-mode :lang elixir)
|
||||
(elm-mode :lang elm)
|
||||
(emacs-lisp-mode :lang emacs-lisp)
|
||||
(ess-r-mode :lang ess)
|
||||
(ess-julia-mode :lang ess)
|
||||
(go-mode :lang go)
|
||||
(haskell-mode :lang haskell)
|
||||
(hy-mode :lang hy)
|
||||
(idris-mode :lang idris)
|
||||
(java-mode :lang java)
|
||||
(js2-mode :lang javascript)
|
||||
(rjsx-mode :lang javascript)
|
||||
(typescript-mode :lang javascript)
|
||||
(coffee-mode :lang javascript)
|
||||
(julia-mode :lang julia)
|
||||
(kotlin-mode :lang kotlin)
|
||||
(latex-mode :lang latex)
|
||||
(LaTeX-mode :lang latex)
|
||||
(ledger-mode :lang ledger)
|
||||
(lua-mode :lang lua)
|
||||
(markdown-mode :lang markdown)
|
||||
(gfm-mode :lang markdown)
|
||||
(nim-mode :lang nim)
|
||||
(nix-mode :lang nix)
|
||||
(taureg-mode :lang ocaml)
|
||||
(org-mode :lang org)
|
||||
(perl-mode :lang perl)
|
||||
(php-mode :lang php)
|
||||
(hack-mode :lang php)
|
||||
(plantuml-mode :lang plantuml)
|
||||
(purescript-mode :lang purescript)
|
||||
(python-mode :lang python)
|
||||
(restclient-mode :lang rest)
|
||||
(ruby-mode :lang ruby)
|
||||
(enh-ruby-mode :lang ruby)
|
||||
(rust-mode :lang rust)
|
||||
(scala-mode :lang scala)
|
||||
(sh-mode :lang sh)
|
||||
(swift-mode :lang swift)
|
||||
(web-mode :lang web)
|
||||
(css-mode :lang web)
|
||||
(scss-mode :lang web)
|
||||
(sass-mode :lang web)
|
||||
(less-css-mode :lang web)
|
||||
(stylus-mode :lang web)
|
||||
(terra-mode :lang terra))
|
||||
"An alist mapping major modes to Doom modules.
|
||||
|
||||
This is used by `doom/help-modules' to auto-select the module corresponding to
|
||||
the current major-modea.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Helpers
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-active-minor-modes ()
|
||||
"Return a list of active minor-mode symbols."
|
||||
(cl-loop for mode in minor-mode-list
|
||||
if (and (boundp mode) (symbol-value mode))
|
||||
collect mode))
|
||||
|
||||
|
||||
;;
|
||||
;;; Custom describe commands
|
||||
|
||||
;;;###autoload (defalias 'doom/describe-autodefs #'doom/help-autodefs)
|
||||
;;;###autoload (defalias 'doom/describe-module #'doom/help-modules)
|
||||
;;;###autoload (defalias 'doom/describe-package #'doom/help-packages)
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/describe-active-minor-mode (mode)
|
||||
"Get information on an active minor mode. Use `describe-minor-mode' for a
|
||||
selection of all minor-modes, active or not."
|
||||
(interactive
|
||||
(list (completing-read "Minor mode: " (doom-active-minor-modes))))
|
||||
(let ((symbol
|
||||
(cond ((stringp mode) (intern mode))
|
||||
((symbolp mode) mode)
|
||||
((error "Expected a symbol/string, got a %s" (type-of mode))))))
|
||||
(if (fboundp symbol)
|
||||
(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
|
||||
|
||||
(defvar org-agenda-files)
|
||||
(defun doom--org-headings (files &optional depth include-files)
|
||||
"TODO"
|
||||
(require 'org)
|
||||
(let* ((default-directory doom-docs-dir)
|
||||
(org-agenda-files (mapcar #'expand-file-name (doom-enlist files)))
|
||||
(depth (if (integerp depth) depth)))
|
||||
(message "Loading search results...")
|
||||
(unwind-protect
|
||||
(delq
|
||||
nil
|
||||
(org-map-entries
|
||||
(lambda ()
|
||||
(cl-destructuring-bind (level _reduced-level _todo _priority text tags)
|
||||
(org-heading-components)
|
||||
(when (and (or (null depth)
|
||||
(<= level depth))
|
||||
(or (null tags)
|
||||
(not (string-match-p ":TOC" tags))))
|
||||
(let ((path (org-get-outline-path)))
|
||||
(list (string-join
|
||||
(list (string-join
|
||||
(append (when include-files
|
||||
(list (or (+org-get-global-property "TITLE")
|
||||
(file-relative-name (buffer-file-name)))))
|
||||
path
|
||||
(list (replace-regexp-in-string org-link-any-re "\\4" text)))
|
||||
" > ")
|
||||
tags)
|
||||
" ")
|
||||
(buffer-file-name)
|
||||
(point))))))
|
||||
t 'agenda))
|
||||
(mapc #'kill-buffer org-agenda-new-buffers)
|
||||
(setq org-agenda-new-buffers nil))))
|
||||
|
||||
(defvar ivy-sort-functions-alist)
|
||||
;;;###autoload
|
||||
(defun doom-completing-read-org-headings (prompt files &optional depth include-files initial-input extra-candidates)
|
||||
"TODO"
|
||||
(let ((alist
|
||||
(append (doom--org-headings files depth include-files)
|
||||
extra-candidates))
|
||||
ivy-sort-functions-alist)
|
||||
(if-let (result (completing-read prompt alist nil nil initial-input))
|
||||
(cl-destructuring-bind (file &optional location)
|
||||
(cdr (assoc result alist))
|
||||
(find-file file)
|
||||
(cond ((functionp location)
|
||||
(funcall location))
|
||||
(location
|
||||
(goto-char location)))
|
||||
(ignore-errors
|
||||
(when (outline-invisible-p)
|
||||
(save-excursion
|
||||
(outline-previous-visible-heading 1)
|
||||
(org-show-subtree)))))
|
||||
(user-error "Aborted"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/homepage ()
|
||||
"Open the doom emacs homepage in the browser."
|
||||
(interactive)
|
||||
(browse-url "https://doomemacs.org"))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help ()
|
||||
"Open Doom's user manual."
|
||||
(interactive)
|
||||
(find-file (expand-file-name "index.org" doom-docs-dir)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-search (&optional initial-input)
|
||||
"Search Doom's documentation and jump to a headline."
|
||||
(interactive)
|
||||
(doom-completing-read-org-headings
|
||||
"Find in Doom help: "
|
||||
(list "getting_started.org"
|
||||
"contributing.org"
|
||||
"troubleshooting.org"
|
||||
"tutorials.org"
|
||||
"faq.org")
|
||||
2 t initial-input
|
||||
(mapcar (lambda (x)
|
||||
(setcar x (concat "Doom Modules > " (car x)))
|
||||
x)
|
||||
(doom--help-modules-list))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-news-search (&optional initial-input)
|
||||
"Search headlines in Doom's newsletters."
|
||||
(interactive)
|
||||
(doom-completing-read-org-headings
|
||||
"Find in News: "
|
||||
(nreverse (doom-files-in (expand-file-name "news" doom-docs-dir)
|
||||
:match "/[0-9]"
|
||||
:relative-to doom-docs-dir))
|
||||
nil t initial-input))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-faq (&optional initial-input)
|
||||
"Search Doom's FAQ and jump to a question."
|
||||
(interactive)
|
||||
(doom-completing-read-org-headings
|
||||
"Find in FAQ: " (list "faq.org")
|
||||
2 nil initial-input))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-news ()
|
||||
"Open a Doom newsletter.
|
||||
The latest newsletter will be selected by default."
|
||||
(interactive)
|
||||
(let* ((default-directory (expand-file-name "news/" doom-docs-dir))
|
||||
(news-files (doom-files-in default-directory)))
|
||||
(find-file
|
||||
(read-file-name (format "Open Doom newsletter (current: v%s): "
|
||||
doom-version)
|
||||
default-directory
|
||||
(if (member doom-version news-files)
|
||||
doom-version
|
||||
(concat (mapconcat #'number-to-string
|
||||
(nbutlast (version-to-list doom-version) 1)
|
||||
".")
|
||||
".x"))
|
||||
t doom-version))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-autodefs (autodef)
|
||||
"Open documentation for an autodef.
|
||||
|
||||
An autodef is a Doom concept. It is a function or macro that is always defined,
|
||||
whether or not its containing module is disabled (in which case it will safely
|
||||
no-op without evaluating its arguments). This syntactic sugar lets you use them
|
||||
without needing to check if they are available."
|
||||
(interactive
|
||||
(let* ((settings
|
||||
(cl-loop with case-fold-search = nil
|
||||
for sym being the symbols of obarray
|
||||
for sym-name = (symbol-name sym)
|
||||
if (and (or (functionp sym)
|
||||
(macrop sym))
|
||||
(string-match-p "[a-z]!$" sym-name))
|
||||
collect sym))
|
||||
(sym (symbol-at-point))
|
||||
(autodef
|
||||
(completing-read
|
||||
"Describe setter: "
|
||||
;; TODO Could be cleaner (refactor me!)
|
||||
(cl-loop with maxwidth = (apply #'max (mapcar #'length (mapcar #'symbol-name settings)))
|
||||
for def in (sort settings #'string-lessp)
|
||||
if (get def 'doom-module)
|
||||
collect
|
||||
(format (format "%%-%ds%%s" (+ maxwidth 4))
|
||||
def (propertize (format "%s %s" (car it) (cdr it))
|
||||
'face 'font-lock-comment-face))
|
||||
else if (and (string-match-p "^set-.+!$" (symbol-name def))
|
||||
(symbol-file def)
|
||||
(file-in-directory-p (symbol-file def) doom-core-dir))
|
||||
collect
|
||||
(format (format "%%-%ds%%s" (+ maxwidth 4))
|
||||
def (propertize (format "core/%s.el" (file-name-sans-extension (file-relative-name (symbol-file def) doom-core-dir)))
|
||||
'face 'font-lock-comment-face)))
|
||||
nil t
|
||||
(when (and (symbolp sym)
|
||||
(string-match-p "!$" (symbol-name sym)))
|
||||
(symbol-name sym)))))
|
||||
(list (and autodef (car (split-string autodef " "))))))
|
||||
(or (stringp autodef)
|
||||
(functionp autodef)
|
||||
(signal 'wrong-type-argument (list '(stringp functionp) autodef)))
|
||||
(let ((fn (if (functionp autodef)
|
||||
autodef
|
||||
(intern-soft autodef))))
|
||||
(or (fboundp fn)
|
||||
(error "'%s' is not a valid DOOM autodef" autodef))
|
||||
(if (fboundp 'helpful-callable)
|
||||
(helpful-callable fn)
|
||||
(describe-function fn))))
|
||||
|
||||
(defun doom--help-modules-list ()
|
||||
(cl-loop for path in (cdr (doom-module-load-path 'all))
|
||||
for (cat . mod) = (doom-module-from-path path)
|
||||
for readme-path = (or (doom-module-locate-path cat mod "README.org")
|
||||
(doom-module-locate-path cat mod))
|
||||
for format = (format "%s %s" cat mod)
|
||||
if (doom-module-p cat mod)
|
||||
collect (list format readme-path)
|
||||
else if (and cat mod)
|
||||
collect (list (propertize format 'face 'font-lock-comment-face)
|
||||
readme-path)))
|
||||
|
||||
(defun doom--help-current-module-str ()
|
||||
(cond ((and buffer-file-name
|
||||
(eq major-mode 'emacs-lisp-mode)
|
||||
(file-in-directory-p buffer-file-name doom-private-dir)
|
||||
(save-excursion (goto-char (point-min))
|
||||
(re-search-forward "^\\s-*(doom! " nil t))
|
||||
(thing-at-point 'sexp t)))
|
||||
((save-excursion
|
||||
(require 'smartparens)
|
||||
(ignore-errors
|
||||
(sp-beginning-of-sexp)
|
||||
(unless (eq (char-after) ?\()
|
||||
(backward-char))
|
||||
(let ((sexp (sexp-at-point)))
|
||||
(when (memq (car-safe sexp) '(featurep! require!))
|
||||
(format "%s %s" (nth 1 sexp) (nth 2 sexp)))))))
|
||||
((when buffer-file-name
|
||||
(when-let (mod (doom-module-from-path buffer-file-name))
|
||||
(unless (memq (car mod) '(:core :private))
|
||||
(format "%s %s" (car mod) (cdr mod))))))
|
||||
((when-let (mod (cdr (assq major-mode doom--help-major-mode-module-alist)))
|
||||
(format "%s %s"
|
||||
(symbol-name (car mod))
|
||||
(symbol-name (cadr mod)))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-modules (category module)
|
||||
"Open the documentation for a Doom module.
|
||||
|
||||
CATEGORY is a keyword and MODULE is a symbol. e.g. :editor and 'evil.
|
||||
|
||||
Automatically selects a) the module at point (in private init files), b) the
|
||||
module derived from a `featurep!' or `require!' call, c) the module that the
|
||||
current file is in, or d) the module associated with the current major mode (see
|
||||
`doom--help-major-mode-module-alist')."
|
||||
(interactive
|
||||
(mapcar #'intern
|
||||
(split-string
|
||||
(completing-read "Describe module: "
|
||||
(doom--help-modules-list) nil t nil nil
|
||||
(doom--help-current-module-str))
|
||||
" " t)))
|
||||
(cl-check-type category symbol)
|
||||
(cl-check-type module symbol)
|
||||
(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))
|
||||
(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")))))
|
||||
|
||||
|
||||
;;
|
||||
;;; `doom/help-packages'
|
||||
|
||||
(defun doom--help-package-insert-button (label path &optional regexp)
|
||||
(declare (indent defun))
|
||||
(insert-text-button
|
||||
(string-trim label)
|
||||
'face 'link
|
||||
'follow-link t
|
||||
'action
|
||||
`(lambda (_)
|
||||
(unless (file-exists-p ,path)
|
||||
(user-error "Module doesn't exist"))
|
||||
(when (window-dedicated-p)
|
||||
(other-window 1))
|
||||
(let ((buffer (find-file ,path)))
|
||||
(when ,(stringp regexp)
|
||||
(with-current-buffer buffer
|
||||
(goto-char (point-min))
|
||||
(if (re-search-forward ,regexp nil t)
|
||||
(recenter)
|
||||
(message "Couldn't find the config block"))))))))
|
||||
|
||||
(defun doom--help-package-configs (package)
|
||||
(let ((default-directory doom-emacs-dir))
|
||||
;; TODO Use ripgrep instead
|
||||
(split-string
|
||||
(cdr (doom-call-process
|
||||
"git" "grep" "--no-break" "--no-heading" "--line-number"
|
||||
(format "%s %s\\($\\| \\)"
|
||||
"\\(^;;;###package\\|(after!\\|(use-package!\\)"
|
||||
package)
|
||||
":(exclude)*.org"))
|
||||
"\n" t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-packages (package)
|
||||
"Like `describe-package', but for packages installed by Doom modules.
|
||||
|
||||
Only shows installed packages. Includes information about where packages are
|
||||
defined and configured.
|
||||
|
||||
If prefix arg is present, refresh the cache."
|
||||
(interactive
|
||||
(let ((guess (or (function-called-at-point)
|
||||
(symbol-at-point))))
|
||||
(require 'finder-inf nil t)
|
||||
(require 'core-packages)
|
||||
(doom-initialize-packages)
|
||||
(let ((packages (delete-dups
|
||||
(append (mapcar #'car package-alist)
|
||||
(mapcar #'car package--builtins)
|
||||
(mapcar #'intern (hash-table-keys straight--build-cache))
|
||||
(mapcar #'car (doom-package-list 'all))
|
||||
nil))))
|
||||
(unless (memq guess packages)
|
||||
(setq guess nil))
|
||||
(list
|
||||
(intern
|
||||
(completing-read (if guess
|
||||
(format "Select package to search for (default %s): "
|
||||
guess)
|
||||
"Describe package: ")
|
||||
packages nil t nil nil
|
||||
(if guess (symbol-name guess))))))))
|
||||
(require 'core-packages)
|
||||
(doom-initialize-packages)
|
||||
(if (or (package-desc-p package)
|
||||
(and (symbolp package)
|
||||
(or (assq package package-alist)
|
||||
(assq package package--builtins))))
|
||||
(describe-package package)
|
||||
(help-setup-xref (list #'doom/help-packages package)
|
||||
(called-interactively-p 'interactive))
|
||||
(with-help-window (help-buffer)))
|
||||
(save-excursion
|
||||
(with-current-buffer (help-buffer)
|
||||
(let ((inhibit-read-only t)
|
||||
(indent (make-string 13 ? )))
|
||||
(goto-char (point-max))
|
||||
(if (re-search-forward "^ *Status: " nil t)
|
||||
(progn
|
||||
(end-of-line)
|
||||
(insert "\n"))
|
||||
(search-forward "\n\n" nil t))
|
||||
|
||||
(package--print-help-section "Package")
|
||||
(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")
|
||||
|
||||
(when (gethash (symbol-name package) straight--build-cache)
|
||||
(package--print-help-section "Modules")
|
||||
(insert "Declared by the following Doom modules:\n")
|
||||
(dolist (m (doom-package-get package :modules))
|
||||
(insert indent)
|
||||
(doom--help-package-insert-button
|
||||
(format "%s %s" (car m) (or (cdr m) ""))
|
||||
(pcase (car m)
|
||||
(:core doom-core-dir)
|
||||
(:private doom-private-dir)
|
||||
(category (doom-module-path category (cdr m)))))
|
||||
(insert "\n")))
|
||||
|
||||
(package--print-help-section "Configs")
|
||||
(insert "This package is configured in the following locations:")
|
||||
(dolist (location (doom--help-package-configs package))
|
||||
(insert "\n" indent)
|
||||
(insert-text-button
|
||||
location
|
||||
'face 'link
|
||||
'follow-link t
|
||||
'action
|
||||
`(lambda (_)
|
||||
(cl-destructuring-bind (file line _match)
|
||||
',(split-string location ":")
|
||||
(find-file (expand-file-name file doom-emacs-dir))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- (string-to-number line)))
|
||||
(recenter)))))
|
||||
|
||||
(insert "\n\n")))))
|
||||
|
||||
(defvar doom--package-cache nil)
|
||||
(defun doom--package-list ()
|
||||
(let* ((guess (or (function-called-at-point)
|
||||
(symbol-at-point))))
|
||||
(require 'finder-inf nil t)
|
||||
(unless package--initialized
|
||||
(package-initialize t))
|
||||
(let ((packages (or doom--package-cache
|
||||
(progn
|
||||
(message "Reading packages...")
|
||||
(cl-delete-duplicates
|
||||
(append (mapcar 'car package-alist)
|
||||
(mapcar 'car package--builtins)
|
||||
(mapcar 'car package-archive-contents)))))))
|
||||
(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: ")
|
||||
packages nil t nil nil
|
||||
(if guess (symbol-name guess)))))))
|
||||
|
||||
(defun doom--package-url (package)
|
||||
(cond ((assq package package--builtins)
|
||||
(user-error "Package is built into Emacs and cannot be looked up"))
|
||||
((when-let (location (locate-library (symbol-name package)))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (concat (file-name-sans-extension location) ".el")
|
||||
nil 0 4096)
|
||||
(let ((case-fold-search t))
|
||||
(when (re-search-forward " \\(?:URL\\|homepage\\|Website\\): \\(http[^\n]+\\)\n" nil t)
|
||||
(match-string-no-properties 1))))))
|
||||
((and (ignore-errors (eq (doom-package-backend package) 'quelpa))
|
||||
(let* ((plist (cdr (doom-package-prop package :recipe)))
|
||||
(fetcher (plist-get plist :fetcher)))
|
||||
(pcase fetcher
|
||||
(`git (plist-get plist :url))
|
||||
(`github (format "https://github.com/%s.git" (plist-get plist :repo)))
|
||||
(`gitlab (format "https://gitlab.com/%s.git" (plist-get plist :repo)))
|
||||
(`bitbucket (format "https://bitbucket.com/%s" (plist-get plist :repo)))
|
||||
(`wiki (format "https://www.emacswiki.org/emacs/download/%s"
|
||||
(or (car-safe (doom-enlist (plist-get plist :files)))
|
||||
(format "%s.el" package))))
|
||||
(_ (plist-get plist :url))))))
|
||||
((and (require 'package nil t)
|
||||
(or package-archive-contents (doom-refresh-packages-maybe))
|
||||
(pcase (package-desc-archive (cadr (assq package package-archive-contents)))
|
||||
("org" "https://orgmode.org")
|
||||
((or "melpa" "melpa-mirror")
|
||||
(format "https://melpa.org/#/%s" package))
|
||||
("gnu"
|
||||
(format "https://elpa.gnu.org/packages/%s.html" package))
|
||||
(archive
|
||||
(if-let (src (cdr (assoc package package-archives)))
|
||||
(format "%s" src)
|
||||
(user-error "%S isn't installed through any known source (%s)"
|
||||
package archive))))))
|
||||
((user-error "Cannot find the homepage for %S" package))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/help-package-config (package)
|
||||
"Jump to any `use-package!', `after!' or ;;;###package block for PACKAGE.
|
||||
|
||||
This only searches `doom-emacs-dir' (typically ~/.emacs.d) and does not include
|
||||
config blocks in your private config."
|
||||
(interactive (list (doom--package-list)))
|
||||
(cl-destructuring-bind (file line _match)
|
||||
(split-string
|
||||
(completing-read
|
||||
"Jump to config: "
|
||||
(or (doom--help-package-configs package)
|
||||
(user-error "This package isn't configured by you or Doom")))
|
||||
":")
|
||||
(find-file (expand-file-name file doom-emacs-dir))
|
||||
(goto-char (point-min))
|
||||
(forward-line (1- line))
|
||||
(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)))
|
||||
|
||||
;;;###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))
|
||||
" ")))
|
||||
|
||||
;; 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)))
|
||||
" ")))
|
||||
203
.emacs.d/core/autoload/packages.el
Normal file
203
.emacs.d/core/autoload/packages.el
Normal file
@@ -0,0 +1,203 @@
|
||||
;;; 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'."
|
||||
(interactive)
|
||||
;; HACK straight.el must be loaded for this to work
|
||||
(message "Reloading packages")
|
||||
(doom-initialize-packages t)
|
||||
(message "Reloading packages...DONE"))
|
||||
93
.emacs.d/core/autoload/plist.el
Normal file
93
.emacs.d/core/autoload/plist.el
Normal file
@@ -0,0 +1,93 @@
|
||||
;;; core/autoload/plist.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;;; Macros
|
||||
|
||||
;;;###autoload
|
||||
(cl-defmacro doplist! ((arglist plist &optional retval) &rest body)
|
||||
"Loop over a PLIST's (property value) pairs then return RETVAL.
|
||||
|
||||
Evaluate BODY with either ARGLIST bound to (cons PROP VAL) or, if ARGLIST is a
|
||||
list, the pair is destructured into (CAR . CDR)."
|
||||
(declare (indent 1))
|
||||
(let ((plist-var (make-symbol "plist")))
|
||||
`(let ((,plist-var (copy-sequence ,plist)))
|
||||
(while ,plist-var
|
||||
(let ,(if (listp arglist)
|
||||
`((,(pop arglist) (pop ,plist-var))
|
||||
(,(pop arglist) (pop ,plist-var)))
|
||||
`((,arglist (cons (pop ,plist-var)
|
||||
(pop ,plist-var)))))
|
||||
,@body))
|
||||
,retval)))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro plist-put! (plist &rest rest)
|
||||
"Set each PROP VALUE pair in REST to PLIST in-place."
|
||||
`(cl-loop for (prop value)
|
||||
on (list ,@rest) by #'cddr
|
||||
do ,(if (symbolp plist)
|
||||
`(setq ,plist (plist-put ,plist prop value))
|
||||
`(plist-put ,plist prop value))))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro plist-delete! (plist prop)
|
||||
"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
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-plist-get (plist prop &optional nil-value)
|
||||
"Return PROP in PLIST, if it exists. Otherwise NIL-VALUE."
|
||||
(if-let (val (plist-member plist prop))
|
||||
(cadr val)
|
||||
nil-value))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-plist-merge (from-plist to-plist)
|
||||
"Destructively merge FROM-PLIST onto TO-PLIST"
|
||||
(let ((plist (copy-sequence from-plist)))
|
||||
(while plist
|
||||
(plist-put! to-plist (pop plist) (pop plist)))
|
||||
to-plist))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-plist-delete-nil (plist)
|
||||
"Delete `nil' properties from a copy of PLIST."
|
||||
(let (p)
|
||||
(while plist
|
||||
(if (car plist)
|
||||
(plist-put! p (car plist) (nth 1 plist)))
|
||||
(setq plist (cddr plist)))
|
||||
p))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-plist-delete (plist prop)
|
||||
"Delete PROP from a copy of PLIST."
|
||||
(let (p)
|
||||
(while plist
|
||||
(if (not (eq prop (car plist)))
|
||||
(plist-put! p (car plist) (nth 1 plist)))
|
||||
(setq plist (cddr plist)))
|
||||
p))
|
||||
135
.emacs.d/core/autoload/projects.el
Normal file
135
.emacs.d/core/autoload/projects.el
Normal file
@@ -0,0 +1,135 @@
|
||||
;;; core/autoload/projects.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar projectile-project-root nil)
|
||||
|
||||
;;;###autoload (autoload 'projectile-relevant-known-projects "projectile")
|
||||
|
||||
;;;###autodef
|
||||
(cl-defun set-project-type! (name &key predicate compile run test configure dir)
|
||||
"Add a project type to `projectile-project-type'."
|
||||
(declare (indent 1))
|
||||
(after! projectile
|
||||
(add-to-list 'projectile-project-types
|
||||
(list name
|
||||
'marker-files predicate
|
||||
'compilation-dir dir
|
||||
'configure-command configure
|
||||
'compile-command compile
|
||||
'test-command test
|
||||
'run-command run))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Macros
|
||||
|
||||
;;;###autoload
|
||||
(defmacro project-file-exists-p! (files)
|
||||
"Checks if the project has the specified FILES.
|
||||
Paths are relative to the project root, unless they start with ./ or ../ (in
|
||||
which case they're relative to `default-directory'). If they start with a slash,
|
||||
they are absolute."
|
||||
`(file-exists-p! ,files (doom-project-root)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/find-file-in-other-project (project-root)
|
||||
"Preforms `projectile-find-file' in a known project of your choosing."
|
||||
(interactive
|
||||
(list
|
||||
(completing-read "Find file in project: " (projectile-relevant-known-projects)
|
||||
nil nil nil nil (doom-project-root))))
|
||||
(unless (file-directory-p project-root)
|
||||
(error "Project directory '%s' doesn't exist" project-root))
|
||||
(doom-project-find-file project-root))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/browse-in-other-project (project-root)
|
||||
"Preforms `find-file' in a known project of your choosing."
|
||||
(interactive
|
||||
(list
|
||||
(completing-read "Browse in project: " (projectile-relevant-known-projects)
|
||||
nil nil nil nil (doom-project-root))))
|
||||
(unless (file-directory-p project-root)
|
||||
(error "Project directory '%s' doesn't exist" project-root))
|
||||
(doom-project-browse project-root))
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-p (&optional dir)
|
||||
"Return t if DIR (defaults to `default-directory') is a valid project."
|
||||
(and (doom-project-root dir)
|
||||
t))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-root (&optional dir)
|
||||
"Return the project root of DIR (defaults to `default-directory').
|
||||
Returns nil if not in a project."
|
||||
(let ((projectile-project-root (unless dir projectile-project-root))
|
||||
projectile-require-project-root)
|
||||
(projectile-project-root dir)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-name (&optional dir)
|
||||
"Return the name of the current project.
|
||||
|
||||
Returns '-' if not in a valid project."
|
||||
(if-let (project-root (or (doom-project-root dir)
|
||||
(if dir (expand-file-name dir))))
|
||||
(funcall projectile-project-name-function project-root)
|
||||
"-"))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-expand (name &optional dir)
|
||||
"Expand NAME to project root."
|
||||
(expand-file-name name (doom-project-root dir)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-find-file (dir)
|
||||
"Jump to a file in DIR (searched recursively).
|
||||
|
||||
If DIR is not a project, it will be indexed (but not cached)."
|
||||
(unless (file-directory-p dir)
|
||||
(error "Directory %S does not exist" dir))
|
||||
(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-enable-caching projectile-enable-caching))
|
||||
(cond ((and project-root (file-equal-p project-root projectile-project-root))
|
||||
(unless (doom-project-p projectile-project-root)
|
||||
;; Disable caching if this is not a real project; caching
|
||||
;; non-projects easily has the potential to inflate the projectile
|
||||
;; cache beyond reason.
|
||||
(setq projectile-enable-caching nil))
|
||||
(call-interactively
|
||||
;; Intentionally avoid `helm-projectile-find-file', because it runs
|
||||
;; asynchronously, and thus doesn't see the lexical
|
||||
;; `default-directory'
|
||||
(if (doom-module-p :completion 'ivy)
|
||||
#'counsel-projectile-find-file
|
||||
#'projectile-find-file)))
|
||||
((fboundp 'counsel-file-jump) ; ivy only
|
||||
(call-interactively #'counsel-file-jump))
|
||||
((project-current)
|
||||
(project-find-file-in nil (list default-directory) nil))
|
||||
((fboundp 'helm-find-files)
|
||||
(call-interactively #'helm-find-files))
|
||||
((call-interactively #'find-file)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-project-browse (dir)
|
||||
"Traverse a file structure starting linearly from DIR."
|
||||
(let ((default-directory (file-truename (expand-file-name dir))))
|
||||
(call-interactively
|
||||
(cond ((doom-module-p :completion 'ivy)
|
||||
#'counsel-find-file)
|
||||
((doom-module-p :completion 'helm)
|
||||
#'helm-find-files)
|
||||
(#'find-file)))))
|
||||
173
.emacs.d/core/autoload/scratch.el
Normal file
173
.emacs.d/core/autoload/scratch.el
Normal file
@@ -0,0 +1,173 @@
|
||||
;;; core/autoload/scratch.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-scratch-default-file "__default"
|
||||
"The default file name for a project-less scratch buffer.
|
||||
|
||||
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:
|
||||
|
||||
t Inherits the major mode of the last buffer you had selected.
|
||||
nil Uses `fundamental-mode'
|
||||
MAJOR-MODE Any major mode symbol")
|
||||
|
||||
(defvar doom-scratch-buffers nil
|
||||
"A list of active scratch buffers.")
|
||||
|
||||
(defvar doom-scratch-current-project nil
|
||||
"The name of the project associated with the current scratch buffer.")
|
||||
|
||||
(defvar doom-scratch-buffer-hook ()
|
||||
"The hooks to run after a scratch buffer is created.")
|
||||
|
||||
|
||||
(defun doom--load-persistent-scratch-buffer (name)
|
||||
(setq-local doom-scratch-current-project
|
||||
(or name
|
||||
doom-scratch-default-file))
|
||||
(let ((scratch-file
|
||||
(expand-file-name doom-scratch-current-project
|
||||
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)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-scratch-buffer (&optional 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)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Persistent scratch buffer
|
||||
|
||||
;;;###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)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-persist-scratch-buffers-h ()
|
||||
"Save all scratch buffers to `doom-scratch-dir'."
|
||||
(setq doom-scratch-buffers
|
||||
(cl-delete-if-not #'buffer-live-p doom-scratch-buffers))
|
||||
(dolist (buffer doom-scratch-buffers)
|
||||
(with-current-buffer buffer
|
||||
(doom-persist-scratch-buffer-h))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-persist-scratch-buffers-after-switch-h ()
|
||||
"Kill scratch buffers when they are no longer visible, saving them to disk."
|
||||
(unless (cl-some #'get-buffer-window doom-scratch-buffers)
|
||||
(mapc #'kill-buffer doom-scratch-buffers)
|
||||
(remove-hook 'doom-switch-buffer-hook #'doom-persist-scratch-buffers-after-switch-h)))
|
||||
|
||||
;;;###autoload
|
||||
(unless noninteractive
|
||||
(add-hook 'kill-emacs-hook #'doom-persist-scratch-buffers-h))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/open-scratch-buffer (&optional arg project-p)
|
||||
"Opens the (persistent) scratch buffer in a popup.
|
||||
|
||||
If passed the prefix ARG, switch to it in the current window.
|
||||
If PROJECT-P is non-nil, open a persistent scratch buffer associated with the
|
||||
current project."
|
||||
(interactive "P")
|
||||
(let (projectile-enable-caching)
|
||||
(funcall
|
||||
(if arg
|
||||
#'switch-to-buffer
|
||||
#'pop-to-buffer)
|
||||
(doom-scratch-buffer
|
||||
(cond ((eq doom-scratch-buffer-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)
|
||||
nil)
|
||||
((symbolp doom-scratch-buffer-major-mode)
|
||||
doom-scratch-buffer-major-mode))
|
||||
default-directory
|
||||
(when project-p
|
||||
(doom-project-name))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/switch-to-scratch-buffer (&optional project-p)
|
||||
"Like `doom/open-scratch-buffer', but switches to it in the current window.
|
||||
|
||||
If passed the prefix arg, open project scratch buffer."
|
||||
(interactive "P")
|
||||
(doom/open-scratch-buffer t project-p))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/open-project-scratch-buffer (&optional current-window)
|
||||
"Opens the (persistent) project scratch buffer in a popup.
|
||||
|
||||
If passed the prefix arg, switch to it in the current window."
|
||||
(interactive "P")
|
||||
(doom/open-scratch-buffer current-window 'project))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/switch-to-project-scratch-buffer ()
|
||||
"Like `doom/open-project-scratch-buffer', but switches to it in the current
|
||||
window."
|
||||
(interactive)
|
||||
(doom/open-project-scratch-buffer t))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/revert-scratch-buffer ()
|
||||
"Revert scratch buffer to last persistent state."
|
||||
(interactive)
|
||||
(unless (string-match-p "^\\*doom:scratch" (buffer-name))
|
||||
(user-error "Not in a scratch buffer"))
|
||||
(when (doom--load-persistent-scratch-buffer doom-scratch-current-project)
|
||||
(message "Reloaded scratch buffer")))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/delete-persistent-scratch-file (&optional arg)
|
||||
"Deletes a scratch buffer file in `doom-scratch-dir'.
|
||||
|
||||
If prefix ARG, delete all persistent scratches."
|
||||
(interactive)
|
||||
(if arg
|
||||
(progn
|
||||
(delete-directory doom-scratch-dir t)
|
||||
(message "Cleared %S" (abbreviate-file-name doom-scratch-dir)))
|
||||
(make-directory doom-scratch-dir t)
|
||||
(let ((file (read-file-name "Delete scratch file > " doom-scratch-dir "scratch")))
|
||||
(if (not (file-exists-p file))
|
||||
(message "%S does not exist" (abbreviate-file-name file))
|
||||
(delete-file file)
|
||||
(message "Successfully deleted %S" (abbreviate-file-name file))))))
|
||||
128
.emacs.d/core/autoload/sessions.el
Normal file
128
.emacs.d/core/autoload/sessions.el
Normal file
@@ -0,0 +1,128 @@
|
||||
;;; core/autoload/sessions.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar desktop-base-file-name)
|
||||
(defvar desktop-dirname)
|
||||
(defvar desktop-restore-eager)
|
||||
(defvar desktop-file-modtime)
|
||||
|
||||
|
||||
;;
|
||||
;;; Helpers
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-session-file (&optional name)
|
||||
"TODO"
|
||||
(cond ((require 'persp-mode nil t)
|
||||
(expand-file-name (or name persp-auto-save-fname) persp-save-dir))
|
||||
((require 'desktop nil t)
|
||||
(if name
|
||||
(expand-file-name name (file-name-directory (desktop-full-file-name)))
|
||||
(desktop-full-file-name)))
|
||||
((error "No session backend available"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-save-session (&optional file)
|
||||
"TODO"
|
||||
(setq file (expand-file-name (or file (doom-session-file))))
|
||||
(cond ((require 'persp-mode nil t)
|
||||
(unless persp-mode (persp-mode +1))
|
||||
(setq persp-auto-save-opt 0)
|
||||
(persp-save-state-to-file file))
|
||||
((and (require 'frameset nil t)
|
||||
(require 'restart-emacs nil t))
|
||||
(let ((frameset-filter-alist (append '((client . restart-emacs--record-tty-file))
|
||||
frameset-filter-alist))
|
||||
(desktop-base-file-name (file-name-nondirectory file))
|
||||
(desktop-dirname (file-name-directory file))
|
||||
(desktop-restore-eager t)
|
||||
desktop-file-modtime)
|
||||
(make-directory desktop-dirname t)
|
||||
;; Prevents confirmation prompts
|
||||
(let ((desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
|
||||
(desktop-save desktop-dirname t))))
|
||||
((error "No session backend to save session with"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-load-session (&optional file)
|
||||
"TODO"
|
||||
(setq file (expand-file-name (or file (doom-session-file))))
|
||||
(message "Attempting to load %s" file)
|
||||
(cond ((require 'persp-mode nil t)
|
||||
(unless persp-mode
|
||||
(persp-mode +1))
|
||||
(persp-load-state-from-file file))
|
||||
((and (require 'frameset nil t)
|
||||
(require 'restart-emacs nil t))
|
||||
(restart-emacs--restore-frames-using-desktop file))
|
||||
((error "No session backend to load session with"))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Command line switch
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-restore-session-handler (&rest _)
|
||||
"TODO"
|
||||
(add-hook 'window-setup-hook #'doom-load-session 'append))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/quickload-session ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(message "Restoring session...")
|
||||
(doom-load-session)
|
||||
(message "Session restored. Welcome back."))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/quicksave-session ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(message "Saving session")
|
||||
(doom-save-session)
|
||||
(message "Saving session...DONE"))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/load-session (file)
|
||||
"TODO"
|
||||
(interactive
|
||||
(let ((session-file (doom-session-file)))
|
||||
(list (or (read-file-name "Session to restore: "
|
||||
(file-name-directory session-file)
|
||||
(file-name-nondirectory session-file)
|
||||
t)
|
||||
(user-error "No session selected. Aborting")))))
|
||||
(unless file
|
||||
(error "No session file selected"))
|
||||
(message "Loading '%s' session" file)
|
||||
(doom-load-session file)
|
||||
(message "Session restored. Welcome back."))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/save-session (file)
|
||||
"TODO"
|
||||
(interactive
|
||||
(let ((session-file (doom-session-file)))
|
||||
(list (or (read-file-name "Save session to: "
|
||||
(file-name-directory session-file)
|
||||
(file-name-nondirectory session-file))
|
||||
(user-error "No session selected. Aborting")))))
|
||||
(unless file
|
||||
(error "No session file selected"))
|
||||
(message "Saving '%s' session" file)
|
||||
(doom-save-session file))
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'doom/restart #'restart-emacs)
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/restart-and-restore (&optional debug)
|
||||
"TODO"
|
||||
(interactive "P")
|
||||
(setq doom-autosave-session nil)
|
||||
(doom/quicksave-session)
|
||||
(restart-emacs
|
||||
(delq nil (list (if debug "--debug-init") "--restore"))))
|
||||
274
.emacs.d/core/autoload/text.el
Normal file
274
.emacs.d/core/autoload/text.el
Normal file
@@ -0,0 +1,274 @@
|
||||
;;; core/autoload/text.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-surrounded-p (pair &optional inline balanced)
|
||||
"Returns t if point is surrounded by a brace delimiter: {[(
|
||||
|
||||
If INLINE is non-nil, only returns t if braces are on the same line, and
|
||||
whitespace is balanced on either side of the cursor.
|
||||
|
||||
If INLINE is nil, returns t if the opening and closing braces are on adjacent
|
||||
lines, above and below, with only whitespace in between."
|
||||
(when pair
|
||||
(let ((beg (plist-get pair :beg))
|
||||
(end (plist-get pair :end))
|
||||
(pt (point)))
|
||||
(when (and (> pt beg) (< pt end))
|
||||
(when-let* ((cl (plist-get pair :cl))
|
||||
(op (plist-get pair :op)))
|
||||
(and (not (string= op ""))
|
||||
(not (string= cl ""))
|
||||
(let ((nbeg (+ (length op) beg))
|
||||
(nend (- end (length cl))))
|
||||
(let ((content (buffer-substring-no-properties nbeg nend)))
|
||||
(and (string-match-p (format "[ %s]*" (if inline "" "\n")) content)
|
||||
(or (not balanced)
|
||||
(= (- pt nbeg) (- nend pt))))))))))))
|
||||
|
||||
;;;###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)))))))))
|
||||
|
||||
;;;###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)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-point-in-string-or-comment-p (&optional pos)
|
||||
"Return non-nil if POS is in a string or comment."
|
||||
(or (doom-point-in-string-p pos)
|
||||
(doom-point-in-comment-p pos)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
(defvar doom--last-backward-pt most-positive-fixnum)
|
||||
;;;###autoload
|
||||
(defun doom/backward-to-bol-or-indent ()
|
||||
"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))))
|
||||
(cond ((> pt bot)
|
||||
(goto-char bot))
|
||||
((= pt bol)
|
||||
(goto-char (min doom--last-backward-pt bot))
|
||||
(setq doom--last-backward-pt most-positive-fixnum))
|
||||
((<= pt bot)
|
||||
(setq doom--last-backward-pt pt)
|
||||
(goto-char bol))))))
|
||||
|
||||
(defvar doom--last-forward-pt -1)
|
||||
;;;###autoload
|
||||
(defun doom/forward-to-last-non-comment-or-eol ()
|
||||
"Jumps between the last non-blank, non-comment character in the line and the
|
||||
true end of the line. The opposite of `doom/backward-to-bol-or-indent'."
|
||||
(interactive)
|
||||
(let ((eol (if (not visual-line-mode)
|
||||
(line-end-position)
|
||||
(save-excursion (end-of-visual-line) (point)))))
|
||||
(if (or (and (< (point) eol)
|
||||
(sp-point-in-comment))
|
||||
(not (sp-point-in-comment eol)))
|
||||
(if (= (point) eol)
|
||||
(progn
|
||||
(goto-char doom--last-forward-pt)
|
||||
(setq doom--last-forward-pt -1))
|
||||
(setq doom--last-forward-pt (point))
|
||||
(goto-char eol))
|
||||
(let* ((bol (save-excursion (beginning-of-visual-line) (point)))
|
||||
(boc (or (save-excursion
|
||||
(if (not comment-use-syntax)
|
||||
(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))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/dumb-indent ()
|
||||
"Inserts a tab character (or spaces x tab-width)."
|
||||
(interactive)
|
||||
(if indent-tabs-mode
|
||||
(insert "\t")
|
||||
(let* ((movement (% (current-column) tab-width))
|
||||
(spaces (if (= 0 movement) tab-width (- tab-width movement))))
|
||||
(insert (make-string spaces ? )))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/dumb-dedent ()
|
||||
"Dedents the current line."
|
||||
(interactive)
|
||||
(if indent-tabs-mode
|
||||
(call-interactively #'backward-delete-char)
|
||||
(unless (bolp)
|
||||
(save-excursion
|
||||
(when (> (current-column) (current-indentation))
|
||||
(back-to-indentation))
|
||||
(let ((movement (% (current-column) tab-width)))
|
||||
(delete-char
|
||||
(- (if (= 0 movement)
|
||||
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
|
||||
buffer start and end, to make indentation consistent. Which it does depends on
|
||||
the value of `indent-tab-mode'.
|
||||
|
||||
If ARG (universal argument) is non-nil, retab the current buffer using the
|
||||
opposite indentation style."
|
||||
(interactive "Pr")
|
||||
(unless (and beg end)
|
||||
(setq beg (point-min)
|
||||
end (point-max)))
|
||||
(let ((indent-tabs-mode (if arg (not indent-tabs-mode) indent-tabs-mode)))
|
||||
(if indent-tabs-mode
|
||||
(tabify beg end)
|
||||
(untabify beg end))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/delete-trailing-newlines ()
|
||||
"Trim trailing newlines.
|
||||
|
||||
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 "")))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/dos2unix ()
|
||||
"Convert the current buffer to a Unix file encoding."
|
||||
(interactive)
|
||||
(set-buffer-file-coding-system 'undecided-unix nil))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/unix2dos ()
|
||||
"Convert the current buffer to a DOS file encoding."
|
||||
(interactive)
|
||||
(set-buffer-file-coding-system 'undecided-dos nil))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/toggle-indent-style ()
|
||||
"Switch between tabs and spaces indentation style in the current buffer."
|
||||
(interactive)
|
||||
(setq indent-tabs-mode (not indent-tabs-mode))
|
||||
(message "Indent style changed to %s" (if indent-tabs-mode "tabs" "spaces")))
|
||||
|
||||
(defvar editorconfig-lisp-use-default-indent)
|
||||
;;;###autoload
|
||||
(defun doom/set-indent-width (width)
|
||||
"Change the indentation size to WIDTH of the current buffer.
|
||||
|
||||
The effectiveness of this command is significantly improved if you have
|
||||
editorconfig or dtrt-indent installed."
|
||||
(interactive
|
||||
(list (if (integerp current-prefix-arg)
|
||||
current-prefix-arg
|
||||
(read-number "New indent size: "))))
|
||||
(setq tab-width width)
|
||||
(setq-local standard-indent width)
|
||||
(when (boundp 'evil-shift-width)
|
||||
(setq evil-shift-width width))
|
||||
(cond ((require 'editorconfig nil t)
|
||||
(let (editorconfig-lisp-use-default-indent)
|
||||
(editorconfig-set-indentation nil width)))
|
||||
((require 'dtrt-indent nil t)
|
||||
(when-let (var (nth 2 (assq major-mode dtrt-indent-hook-mapping-list)))
|
||||
(doom-log "Updated %s = %d" var width)
|
||||
(set var width))))
|
||||
(message "Changed indentation to %d" width))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-enable-delete-trailing-whitespace-h ()
|
||||
"Enables the automatic deletion of trailing whitespaces upon file save.
|
||||
|
||||
i.e. enables `ws-butler-mode' in the current buffer."
|
||||
(ws-butler-mode +1))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-disable-delete-trailing-whitespace-h ()
|
||||
"Disables the automatic deletion of trailing whitespaces upon file save.
|
||||
|
||||
i.e. disables `ws-butler-mode' in the current buffer."
|
||||
(ws-butler-mode -1))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-enable-show-trailing-whitespace-h ()
|
||||
"Enable `show-trailing-whitespace' in the current buffer."
|
||||
(setq-local show-trailing-whitespace t))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-disable-show-trailing-whitespace-h ()
|
||||
"Disable `show-trailing-whitespace' in the current buffer."
|
||||
(setq-local show-trailing-whitespace nil))
|
||||
51
.emacs.d/core/autoload/themes.el
Normal file
51
.emacs.d/core/autoload/themes.el
Normal file
@@ -0,0 +1,51 @@
|
||||
;;; core/autoload/themes.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun doom--custom-theme-set-face (spec)
|
||||
(cond ((listp (car spec))
|
||||
(cl-loop for face in (car spec)
|
||||
collect
|
||||
(car (doom--custom-theme-set-face (cons face (cdr spec))))))
|
||||
((keywordp (cadr spec))
|
||||
`((,(car spec) ((t ,(cdr spec))))))
|
||||
(`((,(car spec) ,(cdr spec))))))
|
||||
|
||||
;;;###autoload
|
||||
(defmacro custom-theme-set-faces! (theme &rest specs)
|
||||
"Apply a list of face SPECS as user customizations for THEME.
|
||||
|
||||
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)))
|
||||
|
||||
;;;###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."
|
||||
(declare (indent defun))
|
||||
`(custom-theme-set-faces! 'user ,@specs))
|
||||
|
||||
(defvar doom--prefer-theme-elc)
|
||||
;;;###autoload
|
||||
(defun doom/reload-theme ()
|
||||
"Reload the current color theme."
|
||||
(interactive)
|
||||
(let ((theme (or (car-safe custom-enabled-themes) doom-theme)))
|
||||
(when theme
|
||||
(mapc #'disable-theme custom-enabled-themes))
|
||||
(load-theme doom-theme 'noconfirm)
|
||||
(doom/reload-font)))
|
||||
232
.emacs.d/core/autoload/ui.el
Normal file
232
.emacs.d/core/autoload/ui.el
Normal file
@@ -0,0 +1,232 @@
|
||||
;;; core/autoload/ui.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;; Public library
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-resize-window (window new-size &optional horizontal force-p)
|
||||
"Resize a window to NEW-SIZE. If HORIZONTAL, do it width-wise.
|
||||
If FORCE-P is omitted when `window-size-fixed' is non-nil, resizing will fail."
|
||||
(with-selected-window (or window (selected-window))
|
||||
(let ((window-size-fixed (unless force-p window-size-fixed)))
|
||||
(enlarge-window (- new-size (if horizontal (window-width) (window-height)))
|
||||
horizontal))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-quit-p (&optional prompt)
|
||||
"Prompt the user for confirmation when killing Emacs.
|
||||
|
||||
Returns t if it is safe to kill this session. Does not prompt if no real buffers
|
||||
are open."
|
||||
(or (not (ignore-errors (doom-real-buffer-list)))
|
||||
(yes-or-no-p (format "››› %s" (or prompt "Quit Emacs?")))
|
||||
(ignore (message "Aborted"))))
|
||||
|
||||
|
||||
;;
|
||||
;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-recenter-a (&rest _)
|
||||
"Generic advisor for recentering window (typically :after other functions)."
|
||||
(recenter))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-shut-up-a (orig-fn &rest args)
|
||||
"Generic advisor for silencing noisy functions.
|
||||
|
||||
In interactive Emacs, this just inhibits messages from appearing in the
|
||||
minibuffer. They are still logged to *Messages*.
|
||||
|
||||
In tty Emacs, messages suppressed completely."
|
||||
(quiet! (apply orig-fn args)))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-apply-ansi-color-to-compilation-buffer-h ()
|
||||
"Applies ansi codes to the compilation buffers. Meant for
|
||||
`compilation-filter-hook'."
|
||||
(with-silent-modifications
|
||||
(ansi-color-apply-on-region compilation-filter-start (point))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-disable-show-paren-mode-h ()
|
||||
"Turn off `show-paren-mode' buffer-locally."
|
||||
(setq-local show-paren-mode nil))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/toggle-line-numbers ()
|
||||
"Toggle line numbers.
|
||||
|
||||
Cycles through regular, relative and no line numbers. The order depends on what
|
||||
`display-line-numbers-type' is set to. If you're using Emacs 26+, and
|
||||
visual-line-mode is on, this skips relative and uses visual instead.
|
||||
|
||||
See `display-line-numbers' for what these values mean."
|
||||
(interactive)
|
||||
(defvar doom--line-number-style display-line-numbers-type)
|
||||
(let* ((styles `(t ,(if visual-line-mode 'visual 'relative) nil))
|
||||
(order (cons display-line-numbers-type (remq display-line-numbers-type styles)))
|
||||
(queue (memq doom--line-number-style order))
|
||||
(next (if (= (length queue) 1)
|
||||
(car order)
|
||||
(car (cdr queue)))))
|
||||
(setq doom--line-number-style next)
|
||||
(setq display-line-numbers next)
|
||||
(message "Switched to %s line numbers"
|
||||
(pcase next
|
||||
(`t "normal")
|
||||
(`nil "disabled")
|
||||
(_ (symbol-name next))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/delete-frame ()
|
||||
"Delete the current frame, but ask for confirmation if it isn't empty."
|
||||
(interactive)
|
||||
(if (cdr (frame-list))
|
||||
(when (doom-quit-p "Close frame?")
|
||||
(delete-frame))
|
||||
(save-buffers-kill-emacs)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/window-maximize-buffer ()
|
||||
"Close other windows to focus on this one. Activate again to undo this. If the
|
||||
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)))
|
||||
|
||||
(defvar doom--window-enlargened 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."
|
||||
(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))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/window-maximize-horizontally ()
|
||||
"Delete all windows to the left and right of the current window."
|
||||
(interactive)
|
||||
(require 'windmove)
|
||||
(save-excursion
|
||||
(while (ignore-errors (windmove-left)) (delete-window))
|
||||
(while (ignore-errors (windmove-right)) (delete-window))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/window-maximize-vertically ()
|
||||
"Delete all windows above and below the current window."
|
||||
(interactive)
|
||||
(require 'windmove)
|
||||
(save-excursion
|
||||
(while (ignore-errors (windmove-up)) (delete-window))
|
||||
(while (ignore-errors (windmove-down)) (delete-window))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/set-frame-opacity (opacity)
|
||||
"Interactively change the current frame's opacity.
|
||||
|
||||
OPACITY is an integer between 0 to 100, inclusive."
|
||||
(interactive
|
||||
(list (read-number "Opacity (0-100): "
|
||||
(or (frame-parameter nil 'alpha)
|
||||
100))))
|
||||
(set-frame-parameter nil 'alpha opacity))
|
||||
|
||||
(defvar doom--narrowed-base-buffer nil)
|
||||
;;;###autoload
|
||||
(defun doom/narrow-buffer-indirectly (beg end)
|
||||
"Restrict editing in this buffer to the current region, indirectly.
|
||||
|
||||
This recursively creates indirect clones of the current buffer so that the
|
||||
narrowing doesn't affect other windows displaying the same buffer. Call
|
||||
`doom/widen-indirectly-narrowed-buffer' to undo it (incrementally).
|
||||
|
||||
Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/"
|
||||
(interactive
|
||||
(list (or (bound-and-true-p evil-visual-beginning) (region-beginning))
|
||||
(or (bound-and-true-p evil-visual-end) (region-end))))
|
||||
(unless (region-active-p)
|
||||
(setq beg (line-beginning-position)
|
||||
end (line-end-position)))
|
||||
(deactivate-mark)
|
||||
(let ((orig-buffer (current-buffer)))
|
||||
(with-current-buffer (switch-to-buffer (clone-indirect-buffer nil nil))
|
||||
(narrow-to-region beg end)
|
||||
(setq-local doom--narrowed-base-buffer orig-buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/widen-indirectly-narrowed-buffer (&optional arg)
|
||||
"Widens narrowed buffers.
|
||||
|
||||
This command will incrementally kill indirect buffers (under the assumption they
|
||||
were created by `doom/narrow-buffer-indirectly') and switch to their base
|
||||
buffer.
|
||||
|
||||
If ARG, then kill all indirect buffers, return the base buffer and widen it.
|
||||
|
||||
If the current buffer is not an indirect buffer, it is `widen'ed."
|
||||
(interactive "P")
|
||||
(unless (buffer-narrowed-p)
|
||||
(user-error "Buffer isn't narrowed"))
|
||||
(let ((orig-buffer (current-buffer))
|
||||
(base-buffer doom--narrowed-base-buffer))
|
||||
(cond ((or (not base-buffer)
|
||||
(not (buffer-live-p base-buffer)))
|
||||
(widen))
|
||||
(arg
|
||||
(let ((buffer orig-buffer)
|
||||
(buffers-to-kill (list orig-buffer)))
|
||||
(while (setq buffer (buffer-local-value 'doom--narrowed-base-buffer buffer))
|
||||
(push buffer buffers-to-kill))
|
||||
(switch-to-buffer (buffer-base-buffer))
|
||||
(mapc #'kill-buffer (remove (current-buffer) buffers-to-kill))))
|
||||
((switch-to-buffer base-buffer)
|
||||
(kill-buffer orig-buffer)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/toggle-narrow-buffer (beg end)
|
||||
"Narrow the buffer to BEG END. If narrowed, widen it."
|
||||
(interactive
|
||||
(list (or (bound-and-true-p evil-visual-beginning) (region-beginning))
|
||||
(or (bound-and-true-p evil-visual-end) (region-end))))
|
||||
(if (buffer-narrowed-p)
|
||||
(widen)
|
||||
(unless (region-active-p)
|
||||
(setq beg (line-beginning-position)
|
||||
end (line-end-position)))
|
||||
(narrow-to-region beg end)))
|
||||
407
.emacs.d/core/cli/autoloads.el
Normal file
407
.emacs.d/core/cli/autoloads.el
Normal file
@@ -0,0 +1,407 @@
|
||||
;;; 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
|
||||
everyone in the universe and their dog, causing errors that make babies cry. No
|
||||
one wants that.")
|
||||
|
||||
;; externs
|
||||
(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))
|
||||
|
||||
;; 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)
|
||||
|
||||
;; 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."
|
||||
(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))
|
||||
204
.emacs.d/core/cli/byte-compile.el
Normal file
204
.emacs.d/core/cli/byte-compile.el
Normal file
@@ -0,0 +1,204 @@
|
||||
;;; core/cli/byte-compile.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defcli! (compile c)
|
||||
((recompile-p ["-r" "--recompile"])
|
||||
&rest targets)
|
||||
"Byte-compiles your config or selected modules.
|
||||
|
||||
compile [TARGETS...]
|
||||
compile :core :private lang/python
|
||||
compile feature lang
|
||||
|
||||
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))
|
||||
|
||||
(defcli! clean ()
|
||||
"Delete all *.elc files."
|
||||
:bare t
|
||||
(doom-clean-byte-compiled-files))
|
||||
|
||||
|
||||
;;
|
||||
;; Helpers
|
||||
|
||||
(defun doom--byte-compile-ignore-file-p (path)
|
||||
(let ((filename (file-name-nondirectory path)))
|
||||
(or (string-prefix-p "." filename)
|
||||
(string-prefix-p "test-" filename)
|
||||
(not (equal (file-name-extension path) "el"))
|
||||
(member filename (list "packages.el" "doctor.el")))))
|
||||
|
||||
(cl-defun doom-cli-byte-compile (&optional modules recompile-p)
|
||||
"Byte compiles your emacs configuration.
|
||||
|
||||
init.el is always byte-compiled by this.
|
||||
|
||||
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.
|
||||
|
||||
WARNING: byte-compilation yields marginal gains and makes debugging new issues
|
||||
difficult. It is recommended you don't use it unless you understand the
|
||||
reprecussions.
|
||||
|
||||
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"))
|
||||
|
||||
;; 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))
|
||||
|
||||
;;
|
||||
(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))))))
|
||||
|
||||
;; 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)))
|
||||
|
||||
(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)))))))
|
||||
|
||||
(defun doom-clean-byte-compiled-files ()
|
||||
"Delete all the compiled elc files in your Emacs configuration and private
|
||||
module. This does not include your byte-compiled, third party packages.'"
|
||||
(require 'core-modules)
|
||||
(print! (start "Cleaning .elc files"))
|
||||
(print-group!
|
||||
(cl-loop with default-directory = doom-emacs-dir
|
||||
with success = nil
|
||||
for path
|
||||
in (append (doom-glob doom-emacs-dir "*.elc")
|
||||
(doom-files-in doom-private-dir :match "\\.elc$" :depth 1)
|
||||
(doom-files-in doom-core-dir :match "\\.elc$")
|
||||
(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)
|
||||
finally do
|
||||
(print! (if success
|
||||
(success "All elc files deleted")
|
||||
(info "No elc files to clean"))))
|
||||
t))
|
||||
29
.emacs.d/core/cli/debug.el
Normal file
29
.emacs.d/core/cli/debug.el
Normal file
@@ -0,0 +1,29 @@
|
||||
;;; core/cli/debug.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
(defcli! info
|
||||
((format ["--json" "--md" "--lisp"] "What format to dump info into"))
|
||||
"Output system info in markdown for bug reports."
|
||||
(pcase format
|
||||
("--json"
|
||||
(require 'json)
|
||||
(with-temp-buffer
|
||||
(insert (json-encode (doom-info)))
|
||||
(json-pretty-print-buffer)
|
||||
(print! (buffer-string))))
|
||||
("--md"
|
||||
(doom/info))
|
||||
((or `nil "--lisp")
|
||||
(doom/info 'raw))
|
||||
(_
|
||||
(user-error "I don't understand %S. Did you mean --json, --md/--markdown or --lisp?"
|
||||
format)))
|
||||
nil)
|
||||
|
||||
(defcli! (version v) ()
|
||||
"Show version information for Doom & Emacs."
|
||||
:bare t
|
||||
(doom/version)
|
||||
nil)
|
||||
203
.emacs.d/core/cli/doctor.el
Normal file
203
.emacs.d/core/cli/doctor.el
Normal file
@@ -0,0 +1,203 @@
|
||||
;;; core/cli/doctor.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-warnings ())
|
||||
(defvar doom-errors ())
|
||||
|
||||
;;; Helpers
|
||||
(defun elc-check-dir (dir)
|
||||
(dolist (file (directory-files-recursively dir "\\.elc$"))
|
||||
(when (file-newer-than-file-p (concat (file-name-sans-extension file) ".el")
|
||||
file)
|
||||
(warn! "%s is out-of-date" (abbreviate-file-name file)))))
|
||||
|
||||
(defmacro assert! (condition message &rest args)
|
||||
`(unless ,condition
|
||||
(error! ,message ,@args)))
|
||||
|
||||
|
||||
;;; Logging
|
||||
(defmacro error! (&rest args)
|
||||
`(progn (unless inhibit-message (print! (error ,@args)))
|
||||
(push (format! (error ,@args)) doom-errors)))
|
||||
(defmacro warn! (&rest args)
|
||||
`(progn (unless inhibit-message (print! (warn ,@args)))
|
||||
(push (format! (warn ,@args)) doom-warnings)))
|
||||
(defmacro success! (&rest args)
|
||||
`(print! (green ,@args)))
|
||||
(defmacro section! (&rest args)
|
||||
`(print! (bold (blue ,@args))))
|
||||
|
||||
(defmacro explain! (&rest args)
|
||||
`(print-group! (print! (autofill ,@args))))
|
||||
|
||||
|
||||
;;
|
||||
;;; CLI commands
|
||||
|
||||
(defcli! (doctor doc) ()
|
||||
"Diagnoses common issues on your system.
|
||||
|
||||
The Doom doctor is essentially one big, self-contained elisp shell script that
|
||||
uses a series of simple heuristics to diagnose common issues on your system.
|
||||
Issues that could intefere with Doom Emacs.
|
||||
|
||||
Doom modules may optionally have a doctor.el file to run their own heuristics
|
||||
in."
|
||||
:bare t
|
||||
(print! "The doctor will see you now...\n")
|
||||
|
||||
;; REVIEW Refactor me
|
||||
(print! (start "Checking your Emacs version..."))
|
||||
(when EMACS27+
|
||||
(warn! "Emacs %s detected. Emacs HEAD is unstable and may cause errors."
|
||||
emacs-version))
|
||||
|
||||
(print! (start "Checking for Emacs config conflicts..."))
|
||||
(when (file-exists-p "~/.emacs")
|
||||
(warn! "Detected an ~/.emacs file, which may prevent Doom from loading")
|
||||
(explain! "If Emacs finds an ~/.emacs file, it will ignore ~/.emacs.d, where Doom is "
|
||||
"typically installed. If you're seeing a vanilla Emacs splash screen, this "
|
||||
"may explain why. If you use Chemacs, you may ignore this warning."))
|
||||
|
||||
(print! (start "Checking for private config conflicts..."))
|
||||
(let ((xdg-dir (concat (or (getenv "XDG_CONFIG_HOME")
|
||||
"~/.config")
|
||||
"/doom/"))
|
||||
(doom-dir (or (getenv "DOOMDIR")
|
||||
"~/.doom.d/")))
|
||||
(when (and (not (file-equal-p xdg-dir doom-dir))
|
||||
(file-directory-p xdg-dir)
|
||||
(file-directory-p doom-dir))
|
||||
(print! (warn "Detected two private configs, in %s and %s")
|
||||
(abbreviate-file-name xdg-dir)
|
||||
doom-dir)
|
||||
(explain! "The second directory will be ignored, as it has lower precedence.")))
|
||||
|
||||
(print! (start "Checking for stale elc files..."))
|
||||
(elc-check-dir user-emacs-directory)
|
||||
|
||||
(print! (start "Checking Doom Emacs..."))
|
||||
(condition-case-unless-debug ex
|
||||
(print-group!
|
||||
(let ((doom-interactive-mode 'doctor))
|
||||
(doom-initialize 'force)
|
||||
(doom-initialize-core)
|
||||
(doom-initialize-modules))
|
||||
|
||||
(print! (success "Initialized Doom Emacs %s") doom-version)
|
||||
(print!
|
||||
(if (hash-table-p doom-modules)
|
||||
(success "Detected %d modules" (hash-table-count doom-modules))
|
||||
(warn "Failed to load any modules. Do you have an private init.el?")))
|
||||
|
||||
(print! (success "Detected %d packages") (length doom-packages))
|
||||
|
||||
(print! (start "Checking Doom core for irregularities..."))
|
||||
(print-group!
|
||||
;; Check for oversized problem files in cache that may cause unusual/tremendous
|
||||
;; delays or freezing. This shouldn't happen often.
|
||||
(dolist (file (list "savehist" "projectile.cache"))
|
||||
(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))
|
||||
(explain! "Consider deleting it from your system (manually)"))))
|
||||
|
||||
(unless (ignore-errors (executable-find doom-projectile-fd-binary))
|
||||
(warn! "Couldn't find the `fd' binary; project file searches will be slightly slower")
|
||||
(unless (executable-find "rg")
|
||||
(warn! "Couldn't find the `rg' binary either; project file searches will be even slower")))
|
||||
|
||||
(require 'projectile)
|
||||
(when (projectile-project-root "~")
|
||||
(warn! "Your $HOME is recognized as a project root")
|
||||
(explain! "Doom will disable bottom-up root search, which may reduce the accuracy of project\n"
|
||||
"detection."))
|
||||
|
||||
;; There should only be one
|
||||
(when (and (file-equal-p doom-private-dir "~/.config/doom")
|
||||
(file-directory-p "~/.doom.d"))
|
||||
(print! (warn "Both %S and '~/.doom.d' exist on your system")
|
||||
(path doom-private-dir))
|
||||
(explain! "Doom will only load one of these (~/.config/doom takes precedence). Possessing\n"
|
||||
"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!"))
|
||||
;; all-the-icons fonts
|
||||
(when (and (pcase system-type
|
||||
(`gnu/linux (concat (or (getenv "XDG_DATA_HOME")
|
||||
"~/.local/share")
|
||||
"/fonts/"))
|
||||
(`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.")))))))
|
||||
|
||||
(print! (start "Checking for stale elc files in your DOOMDIR..."))
|
||||
(when (file-directory-p doom-private-dir)
|
||||
(print-group!
|
||||
(elc-check-dir doom-private-dir)))
|
||||
|
||||
(when doom-modules
|
||||
(print! (start "Checking your enabled modules..."))
|
||||
(advice-add #'require :around #'doom-shut-up-a)
|
||||
(maphash (lambda (key plist)
|
||||
(let (doom-local-errors
|
||||
doom-local-warnings)
|
||||
(let (doom-errors
|
||||
doom-warnings)
|
||||
(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
|
||||
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))
|
||||
(doom-package-built-in-p name)
|
||||
(doom-package-installed-p name))
|
||||
do (print! (error "%s is not installed") name))
|
||||
(let ((inhibit-message t))
|
||||
(load doctor-file 'noerror 'nomessage)))
|
||||
(file-missing (error! "%s" (error-message-string ex)))
|
||||
(error (error! "Syntax error: %s" ex)))
|
||||
(when (or doom-errors doom-warnings)
|
||||
(print-group!
|
||||
(print! (start (bold "%s %s")) (car key) (cdr key))
|
||||
(print! "%s" (string-join (append doom-errors doom-warnings) "\n")))
|
||||
(setq doom-local-errors doom-errors
|
||||
doom-local-warnings doom-warnings)))
|
||||
(appendq! doom-errors doom-local-errors)
|
||||
(appendq! doom-warnings doom-local-warnings)))
|
||||
doom-modules)))
|
||||
(error
|
||||
(warn! "Attempt to load DOOM failed\n %s\n"
|
||||
(or (cdr-safe ex) (car ex)))
|
||||
(setq doom-modules nil)))
|
||||
|
||||
;; Final report
|
||||
(message "")
|
||||
(dolist (msg (list (list doom-errors "error" 'red)
|
||||
(list doom-warnings "warning" 'yellow)))
|
||||
(when (car msg)
|
||||
(print! (color (nth 2 msg)
|
||||
(if (cdr msg)
|
||||
"There are %d %ss!"
|
||||
"There is %d %s!")
|
||||
(length (car msg)) (nth 1 msg)))))
|
||||
(unless (or doom-errors doom-warnings)
|
||||
(success! "Everything seems fine, happy Emacs'ing!"))
|
||||
t)
|
||||
131
.emacs.d/core/cli/env.el
Normal file
131
.emacs.d/core/cli/env.el
Normal file
@@ -0,0 +1,131 @@
|
||||
;;; 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'
|
||||
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
|
||||
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.
|
||||
|
||||
Why this over exec-path-from-shell?
|
||||
|
||||
1. `exec-path-from-shell' spawns (at least) one process at startup to scrape
|
||||
your shell environment. This can be arbitrarily slow depending on the
|
||||
user's shell configuration. A single program (like pyenv or nvm) or config
|
||||
framework (like oh-my-zsh) could undo all of Doom's startup optimizations
|
||||
in one fell swoop.
|
||||
|
||||
2. `exec-path-from-shell' only scrapes some state from your shell. You have to
|
||||
be proactive in order to get it to capture all the envvars relevant to your
|
||||
development environment.
|
||||
|
||||
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."
|
||||
(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)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Helpers
|
||||
|
||||
(defvar doom-env-ignored-vars
|
||||
'("^DBUS_SESSION_BUS_ADDRESS$"
|
||||
"^GPG_AGENT_INFO$"
|
||||
"^GPG_TTY$"
|
||||
"^HOME$"
|
||||
"^PS1$"
|
||||
"^PWD$"
|
||||
"^R?PROMPT$"
|
||||
"^SSH_AGENT_PID$"
|
||||
"^SSH_AUTH_SOCK$"
|
||||
"^TERM$"
|
||||
;; Doom envvars
|
||||
"^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)
|
||||
"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)))
|
||||
(when (or force-p (not (file-exists-p env-file)))
|
||||
(with-temp-file env-file
|
||||
(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))))))
|
||||
102
.emacs.d/core/cli/help.el
Normal file
102
.emacs.d/core/cli/help.el
Normal file
@@ -0,0 +1,102 @@
|
||||
;;; core/cli/help.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun doom--cli-print-signature (cli)
|
||||
(print! (bold "Usage: doom %s%s%s")
|
||||
(if (doom-cli-internal-p cli)
|
||||
""
|
||||
(concat (doom-cli-name cli) " "))
|
||||
(if-let* ((optlist (doom-cli-optlist cli))
|
||||
(flags (cl-loop for opt in optlist
|
||||
append (doom-cli-option-flags opt)))
|
||||
(fn (doom-partial #'string-prefix-p "--")))
|
||||
(concat (when-let (short-flags (cl-remove-if fn flags))
|
||||
;; TODO Show arguments of short flags
|
||||
(format "[-%s]"
|
||||
(string-join (mapcar (doom-rpartial #'substring 1 nil) short-flags)
|
||||
"")))
|
||||
;; TODO Show long flags
|
||||
;; (when-let (long-flags (cl-remove-if-not fn flags))
|
||||
;; (concat " " (string-join long-flags " ")))
|
||||
" ")
|
||||
"")
|
||||
(if-let (arglist (doom-cli-arglist cli))
|
||||
(string-join (append (cl-loop for arg in arglist
|
||||
until (memq arg cl--lambda-list-keywords)
|
||||
collect (upcase (symbol-name arg)))
|
||||
(cl-loop for arg in (cdr (memq '&optional arglist))
|
||||
until (memq arg cl--lambda-list-keywords)
|
||||
collect (format "[%s]" (upcase (symbol-name arg)))))
|
||||
" ")
|
||||
""))
|
||||
(when-let (aliases (doom-cli-aliases cli))
|
||||
(print! "Aliases: %s" (string-join aliases ", "))))
|
||||
|
||||
(defun doom--cli-print-desc (cli &optional short)
|
||||
(print! "%s"
|
||||
(if short
|
||||
(car (split-string (doom-cli-desc cli) "\n"))
|
||||
(doom-cli-desc cli))))
|
||||
|
||||
(defun doom--cli-print-short-desc (cli)
|
||||
(doom--cli-print-desc cli 'short))
|
||||
|
||||
(defun doom--cli-print-options (cli)
|
||||
(when-let (optlist (doom-cli-optlist cli))
|
||||
(print! (bold "Options:"))
|
||||
(print-group!
|
||||
(cl-loop for opt in optlist
|
||||
for desc = (doom-cli-option-desc opt)
|
||||
for args = (doom-cli-option-args opt)
|
||||
for flagstr = (string-join (doom-cli-option-flags opt) ", ")
|
||||
do
|
||||
;; TODO Adjust columns dynamically
|
||||
(print! "%-18s"
|
||||
(concat flagstr
|
||||
(when-let (arg (car args))
|
||||
(concat " " (upcase (symbol-name arg))))))
|
||||
(print-group!
|
||||
(print! (autofill "%s") desc))))))
|
||||
|
||||
|
||||
(defun doom--cli-print (cli)
|
||||
(doom--cli-print-signature cli)
|
||||
(terpri)
|
||||
(doom--cli-print-desc cli)
|
||||
(terpri)
|
||||
(doom--cli-print-options cli))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
(defcli! (help h) (&optional command)
|
||||
"Describe a command or list them all."
|
||||
:bare t
|
||||
(if command
|
||||
(doom--cli-print (doom-cli-get (intern command)))
|
||||
(doom--cli-print (doom-cli-get :main))
|
||||
(terpri)
|
||||
(print! (bold "Commands:"))
|
||||
(print-group!
|
||||
(dolist (group (seq-group-by (lambda (cli)
|
||||
(plist-get (doom-cli-plist cli) :group))
|
||||
(cl-loop for name being the hash-keys of doom--cli-commands
|
||||
for cli = (gethash name doom--cli-commands)
|
||||
if (and (doom-cli-p cli)
|
||||
(not (doom-cli-internal-p cli))
|
||||
(not (plist-get (doom-cli-plist cli) :hidden)))
|
||||
collect cli)))
|
||||
(if (null (car group))
|
||||
(dolist (cli (cdr group))
|
||||
(print! "%-16s %s"
|
||||
(doom-cli-name cli)
|
||||
(car (split-string (doom-cli-desc cli) "\n"))))
|
||||
(print! "%-26s %s"
|
||||
(bold (concat (car group) ":"))
|
||||
(gethash (car group) doom--cli-groups))
|
||||
(print-group!
|
||||
(dolist (cli (cdr group))
|
||||
(print! "%-16s %s"
|
||||
(doom-cli-name cli)
|
||||
(car (split-string (doom-cli-desc cli) "\n"))))))
|
||||
(terpri)))))
|
||||
102
.emacs.d/core/cli/install.el
Normal file
102
.emacs.d/core/cli/install.el
Normal file
@@ -0,0 +1,102 @@
|
||||
;;; core/cli/install.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defcli! (install i)
|
||||
((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)
|
||||
"Installs and sets up Doom Emacs for the first time.
|
||||
|
||||
This command does the following:
|
||||
|
||||
1. Creates DOOMDIR at ~/.doom.d,
|
||||
2. Copies ~/.emacs.d/init.example.el to $DOOMDIR/init.el (if it doesn't exist),
|
||||
3. Creates dummy files for $DOOMDIR/{config,packages}.el,
|
||||
4. Prompts you to generate an envvar file (same as 'doom env'),
|
||||
5. Installs any dependencies of enabled modules (specified by $DOOMDIR/init.el),
|
||||
6. And prompts to install all-the-icons' fonts
|
||||
|
||||
This command is idempotent and safe to reuse.
|
||||
|
||||
The location of DOOMDIR can be changed with the -p option, or by setting the
|
||||
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'
|
||||
(if noconfig-p
|
||||
(print! (warn "Not copying private config template, as requested"))
|
||||
(print! (start "Creating %s") (relpath doom-private-dir))
|
||||
(make-directory doom-private-dir 'parents)
|
||||
(print-group!
|
||||
(print! (success "Created %s") (relpath doom-private-dir)))
|
||||
|
||||
;; Create init.el, config.el & packages.el
|
||||
(mapc (lambda (file)
|
||||
(cl-destructuring-bind (filename . fn) 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))
|
||||
(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))))))))
|
||||
|
||||
;; 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)
|
||||
|
||||
;; Ask if user would like an envvar file generated
|
||||
(if noenv-p
|
||||
(print! (warn "Not generating envvars file, as requested"))
|
||||
(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)"))
|
||||
(doom-cli-reload-env-file 'force-p))))
|
||||
|
||||
;; Install Doom packages
|
||||
(if noinstall-p
|
||||
(print! (warn "Not installing plugins, as requested"))
|
||||
(print! "Installing plugins")
|
||||
(doom-cli-packages-install))
|
||||
|
||||
(print! "Regenerating autoloads files")
|
||||
(doom-cli-reload-autoloads nil 'force-p)
|
||||
|
||||
(if nofonts-p
|
||||
(print! (warn "Not installing fonts, as requested"))
|
||||
(when (or doom-auto-accept
|
||||
(y-or-n-p "Download and install all-the-icon's fonts?"))
|
||||
(require 'all-the-icons)
|
||||
(let ((window-system (cond (IS-MAC 'ns)
|
||||
(IS-LINUX 'x))))
|
||||
(all-the-icons-install-fonts 'yes))))
|
||||
|
||||
(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)))))
|
||||
317
.emacs.d/core/cli/packages.el
Normal file
317
.emacs.d/core/cli/packages.el
Normal file
@@ -0,0 +1,317 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/cli/packages.el
|
||||
|
||||
(defcli! (update u) ()
|
||||
"Updates packages.
|
||||
|
||||
This works by fetching all installed package repos and checking the distance
|
||||
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)
|
||||
|
||||
(defcli! (build b)
|
||||
((rebuild-p ["-r"] "Only rebuild packages that need rebuilding"))
|
||||
"Byte-compiles & symlinks installed packages.
|
||||
|
||||
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))
|
||||
t)
|
||||
|
||||
(defcli! (purge p)
|
||||
((nobuilds-p ["-b" "--no-builds"] "Don't purge unneeded (built) packages")
|
||||
(noelpa-p ["-p" "--no-elpa"] "Don't purge ELPA packages")
|
||||
(norepos-p ["-r" "--no-repos"] "Don't purge unused straight repos")
|
||||
(regraft-p ["-g" "--regraft"] "Regraft git repos (ie. compact them)"))
|
||||
"Deletes orphaned packages & repos, and compacts them.
|
||||
|
||||
Purges all installed ELPA packages (as they are considered temporary). Purges
|
||||
all orphaned package repos and builds. If -g/--regraft is supplied, the git
|
||||
repos among them will be regrafted and compacted to ensure they are as small as
|
||||
possible.
|
||||
|
||||
It is a good idea to occasionally run this doom purge -g to ensure your package
|
||||
list remains lean."
|
||||
(straight-check-all)
|
||||
(when (doom-cli-packages-purge
|
||||
(not noelpa-p)
|
||||
(not norepos-p)
|
||||
(not nobuilds-p)
|
||||
regraft-p)
|
||||
(doom-cli-reload-package-autoloads 'force-p))
|
||||
t)
|
||||
|
||||
;; (defcli! rollback () ; TODO doom rollback
|
||||
;; "<Not implemented yet>"
|
||||
;; (user-error "Not implemented yet, sorry!"))
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(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))))
|
||||
|
||||
|
||||
(defun doom-cli-packages-build (&optional force-p)
|
||||
"(Re)build all 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))))
|
||||
|
||||
|
||||
(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)
|
||||
(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)))
|
||||
(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))
|
||||
(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)))))
|
||||
(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))
|
||||
t))))
|
||||
|
||||
|
||||
;;; PURGE (for the emperor)
|
||||
(defun doom--cli-packages-purge-build (build)
|
||||
(let ((build-dir (straight--build-dir build)))
|
||||
(delete-directory build-dir 'recursive)
|
||||
(if (file-directory-p build-dir)
|
||||
(ignore (print! (error "Failed to purg build/%s" build)))
|
||||
(print! (success "Purged build/%s" build))
|
||||
t)))
|
||||
|
||||
(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)))))
|
||||
|
||||
(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)))))
|
||||
t)))
|
||||
|
||||
(defun doom--cli-packages-regraft-repos (repos)
|
||||
(if (not repos)
|
||||
(progn (print! (info "No repos to regraft"))
|
||||
0)
|
||||
(let ((before-size (doom-directory-size (straight--repos-dir))))
|
||||
(print-group!
|
||||
(prog1 (delq nil (mapcar #'doom--cli-packages-regraft-repo repos))
|
||||
(princ "\033[K")
|
||||
(let ((after-size (doom-directory-size (straight--repos-dir))))
|
||||
(print! (success "Finished regrafting. Size before: %0.1fKB and after: %0.1fKB (%0.1fKB)")
|
||||
before-size after-size
|
||||
(- after-size before-size))))))))
|
||||
|
||||
(defun doom--cli-packages-purge-repo (repo)
|
||||
(let ((repo-dir (straight--repos-dir repo)))
|
||||
(delete-directory repo-dir 'recursive)
|
||||
(ignore-errors
|
||||
(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))
|
||||
t)))
|
||||
|
||||
(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)))))
|
||||
|
||||
(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))))
|
||||
|
||||
(defun doom-cli-packages-purge (&optional elpa-p builds-p repos-p regraft-repos-p)
|
||||
"Auto-removes orphaned packages and repos.
|
||||
|
||||
An orphaned package is a package that isn't a primary package (i.e. doesn't have
|
||||
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)..."))
|
||||
(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)))))
|
||||
104
.emacs.d/core/cli/test.el
Normal file
104
.emacs.d/core/cli/test.el
Normal file
@@ -0,0 +1,104 @@
|
||||
;;; core/cli/test.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun doom--emacs-binary ()
|
||||
(let ((emacs-binary-path (doom-path invocation-directory invocation-name))
|
||||
(runemacs-binary-path (if IS-WINDOWS (doom-path invocation-directory "runemacs.exe"))))
|
||||
(if (and runemacs-binary-path (file-exists-p runemacs-binary-path))
|
||||
runemacs-binary-path
|
||||
emacs-binary-path)))
|
||||
|
||||
|
||||
(defcli! test (&rest targets)
|
||||
"Run Doom unit tests."
|
||||
:bare t
|
||||
(doom-initialize 'force 'noerror)
|
||||
(require 'ansi-color)
|
||||
(let (files read-files)
|
||||
(unless targets
|
||||
(setq targets
|
||||
(cons doom-core-dir
|
||||
(cl-remove-if-not
|
||||
(doom-rpartial #'file-in-directory-p doom-emacs-dir)
|
||||
;; Omit `doom-private-dir', which is always first
|
||||
(let (doom-modules)
|
||||
(load (expand-file-name "test/init" doom-core-dir) nil t)
|
||||
(cdr (doom-module-load-path)))))))
|
||||
(while targets
|
||||
(let ((target (pop targets)))
|
||||
;; FIXME Module targets don't work
|
||||
(cond ((equal target ":core")
|
||||
(appendq! files (nreverse (doom-glob doom-core-dir "test/test-*.el"))))
|
||||
((file-directory-p target)
|
||||
(setq target (expand-file-name target))
|
||||
(appendq! files (nreverse (doom-glob target "test/test-*.el"))))
|
||||
((file-exists-p target)
|
||||
(push target files)))))
|
||||
(setenv "DOOMLOCALDIR" (concat doom-local-dir "test/"))
|
||||
(setenv "DOOMDIR" (concat doom-core-dir "test/"))
|
||||
(with-temp-buffer
|
||||
(print! (start "Bootstrapping test environment, if necessary..."))
|
||||
(cl-destructuring-bind (status . output)
|
||||
(doom-exec-process
|
||||
(doom--emacs-binary)
|
||||
"--batch"
|
||||
"--eval"
|
||||
(prin1-to-string
|
||||
`(progn
|
||||
(setq user-emacs-directory ,doom-emacs-dir
|
||||
doom-auto-accept t)
|
||||
(require 'core ,(locate-library "core"))
|
||||
(require 'core-cli)
|
||||
(doom-initialize 'force 'noerror)
|
||||
(doom-initialize-modules)
|
||||
(doom-cli-reload-core-autoloads 'force)
|
||||
(when (doom-cli-packages-install)
|
||||
(doom-cli-reload-package-autoloads 'force)))))
|
||||
(unless (zerop status)
|
||||
(error "Failed to bootstrap unit tests"))))
|
||||
(with-temp-buffer
|
||||
(dolist (file files)
|
||||
(if (doom-file-cookie-p file "if" t)
|
||||
(cl-destructuring-bind (_status . output)
|
||||
(apply #'doom-exec-process
|
||||
(doom--emacs-binary)
|
||||
"--batch"
|
||||
"-l" (concat doom-core-dir "core.el")
|
||||
"-l" (concat doom-core-dir "test/helpers.el")
|
||||
(append (when (file-in-directory-p file doom-modules-dir)
|
||||
(list "-f" "doom-initialize-core"))
|
||||
(list "-l" file
|
||||
"-f" "buttercup-run")))
|
||||
(insert (replace-regexp-in-string ansi-color-control-seq-regexp "" output))
|
||||
(push file read-files))
|
||||
(print! (info "Ignoring %s" (relpath file)))))
|
||||
(let ((total 0)
|
||||
(total-failed 0)
|
||||
(i 0))
|
||||
(print! "\n----------------------------------------\nTests finished")
|
||||
(print-group!
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^Ran \\([0-9]+\\) specs, \\([0-9]+\\) failed," nil t)
|
||||
(let ((ran (string-to-number (match-string 1)))
|
||||
(failed (string-to-number (match-string 2))))
|
||||
(when (> failed 0)
|
||||
(terpri)
|
||||
(print! (warn "(%s) Failed %d/%d tests")
|
||||
(path (nth i read-files))
|
||||
failed ran)
|
||||
(save-excursion
|
||||
(print-group!
|
||||
(print!
|
||||
"%s" (string-trim
|
||||
(buffer-substring
|
||||
(match-beginning 0)
|
||||
(dotimes (_ failed (point))
|
||||
(search-backward "========================================"))))))))
|
||||
(cl-incf total ran)
|
||||
(cl-incf total-failed failed)
|
||||
(cl-incf i))))
|
||||
(terpri)
|
||||
(if (= total-failed 0)
|
||||
(print! (success "Ran %d tests successfully." total total-failed))
|
||||
(print! (error "Ran %d tests, %d failed") total total-failed)
|
||||
(kill-emacs 1)))
|
||||
t)))
|
||||
113
.emacs.d/core/cli/upgrade.el
Normal file
113
.emacs.d/core/cli/upgrade.el
Normal file
@@ -0,0 +1,113 @@
|
||||
;;; core/cli/upgrade.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defcli! (upgrade up)
|
||||
((force-p ["-f" "--force"]))
|
||||
"Updates Doom and packages.
|
||||
|
||||
This requires that ~/.emacs.d is a git repo, and is the equivalent of the
|
||||
following shell commands:
|
||||
|
||||
cd ~/.emacs.d
|
||||
git pull --rebase
|
||||
bin/doom clean
|
||||
bin/doom refresh
|
||||
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!")))
|
||||
|
||||
|
||||
;;
|
||||
;;; library
|
||||
|
||||
(defvar doom-repo-url "https://github.com/hlissner/doom-emacs"
|
||||
"The git repo url for Doom Emacs.")
|
||||
(defvar doom-repo-remote "_upgrade"
|
||||
"The name to use as our staging remote.")
|
||||
|
||||
(defun doom--working-tree-dirty-p (dir)
|
||||
(cl-destructuring-bind (success . stdout)
|
||||
(doom-call-process "git" "status" "--porcelain" "-uno")
|
||||
(if (= 0 success)
|
||||
(split-string stdout "\n" t)
|
||||
(error "Failed to check working tree in %s" dir))))
|
||||
|
||||
|
||||
(defun doom-cli-upgrade (&optional auto-accept-p force-p)
|
||||
"Upgrade Doom to the latest version non-destructively."
|
||||
(require 'vc-git)
|
||||
(let ((default-directory doom-emacs-dir)
|
||||
process-file-side-effects)
|
||||
(print! (start "Preparing to upgrade Doom Emacs and its packages..."))
|
||||
|
||||
(let* ((branch (vc-git--symbolic-ref doom-emacs-dir))
|
||||
(target-remote (format "%s/%s" doom-repo-remote branch)))
|
||||
(unless branch
|
||||
(error! (if (file-exists-p! ".git" doom-emacs-dir)
|
||||
"Couldn't find Doom's .git directory. Was Doom cloned properly?"
|
||||
"Couldn't detect what branch you're on. Is Doom detached?")))
|
||||
|
||||
;; We assume that a dirty .emacs.d is intentional and abort
|
||||
(when-let (dirty (doom--working-tree-dirty-p default-directory))
|
||||
(if (not force-p)
|
||||
(user-error! "%s\n\n%s\n\n %s"
|
||||
(format "Refusing to upgrade because %S has been modified." (path doom-emacs-dir))
|
||||
"Either stash/undo your changes or run 'doom upgrade -f' to discard local changes."
|
||||
(string-join dirty "\n"))
|
||||
(print! (info "You have local modifications in Doom's source. Discarding them..."))
|
||||
(doom-call-process "git" "reset" "--hard" (format "origin/%s" branch))
|
||||
(doom-call-process "git" "clean" "-ffd")))
|
||||
|
||||
(doom-call-process "git" "remote" "remove" doom-repo-remote)
|
||||
(unwind-protect
|
||||
(let (result)
|
||||
(or (zerop (car (doom-call-process "git" "remote" "add" doom-repo-remote doom-repo-url)))
|
||||
(error "Failed to add %s to remotes" doom-repo-remote))
|
||||
(or (zerop (car (setq result (doom-call-process "git" "fetch" "--tags" doom-repo-remote branch))))
|
||||
(error "Failed to fetch from upstream"))
|
||||
|
||||
(let ((this-rev (vc-git--rev-parse "HEAD"))
|
||||
(new-rev (vc-git--rev-parse target-remote)))
|
||||
(cond
|
||||
((and (null this-rev)
|
||||
(null new-rev))
|
||||
(error "Failed to get revisions for %s" target-remote))
|
||||
|
||||
((equal this-rev new-rev)
|
||||
(print! (success "Doom is already up-to-date!"))
|
||||
t)
|
||||
|
||||
((print! (info "A new version of Doom Emacs is available!\n\n Old revision: %s (%s)\n New revision: %s (%s)\n"
|
||||
(substring this-rev 0 10)
|
||||
(cdr (doom-call-process "git" "log" "-1" "--format=%cr" "HEAD"))
|
||||
(substring new-rev 0 10)
|
||||
(cdr (doom-call-process "git" "log" "-1" "--format=%cr" target-remote))))
|
||||
|
||||
(when (and (not auto-accept-p)
|
||||
(y-or-n-p "View the comparison diff in your browser?"))
|
||||
(print! (info "Opened github in your browser."))
|
||||
(browse-url (format "https://github.com/hlissner/doom-emacs/compare/%s...%s"
|
||||
this-rev
|
||||
new-rev)))
|
||||
|
||||
(if (not (or auto-accept-p
|
||||
(y-or-n-p "Proceed with upgrade?")))
|
||||
(ignore (print! (error "Aborted")))
|
||||
(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))
|
||||
(error "Failed to check out %s" (substring new-rev 0 10)))
|
||||
(print! (success "Finished upgrading Doom Emacs")))
|
||||
t)))))
|
||||
(ignore-errors
|
||||
(doom-call-process "git" "remote" "remove" doom-repo-remote))))))
|
||||
304
.emacs.d/core/core-cli.el
Normal file
304
.emacs.d/core/core-cli.el
Normal file
@@ -0,0 +1,304 @@
|
||||
;;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
|
||||
(require 'seq)
|
||||
|
||||
;; Eagerly load these libraries because we may be in a session that hasn't been
|
||||
;; fully initialized (e.g. where autoloads files haven't been generated or
|
||||
;; `load-path' populated).
|
||||
(load! "autoload/cli")
|
||||
(load! "autoload/debug")
|
||||
(load! "autoload/files")
|
||||
(load! "autoload/format")
|
||||
(load! "autoload/plist")
|
||||
|
||||
|
||||
;;
|
||||
;;; Variables
|
||||
|
||||
(defvar doom-auto-accept (getenv "YES")
|
||||
"If non-nil, Doom will auto-accept any confirmation prompts during batch
|
||||
commands like `doom-cli-packages-install', `doom-cli-packages-update' and
|
||||
`doom-packages-autoremove'.")
|
||||
|
||||
(defvar doom--cli-p nil)
|
||||
(defvar doom--cli-commands (make-hash-table :test 'equal))
|
||||
(defvar doom--cli-groups (make-hash-table :test 'equal))
|
||||
(defvar doom--cli-group nil)
|
||||
|
||||
(cl-defstruct
|
||||
(doom-cli
|
||||
(:constructor nil)
|
||||
(:constructor
|
||||
make-doom-cli
|
||||
(name &key desc aliases optlist arglist plist fn
|
||||
&aux
|
||||
(optlist
|
||||
(cl-loop for (symbol options desc) in optlist
|
||||
for ((_ . options) (_ . params))
|
||||
= (seq-group-by #'stringp options)
|
||||
collect
|
||||
(make-doom-cli-option :symbol symbol
|
||||
:flags options
|
||||
:args params
|
||||
:desc desc))))))
|
||||
(name nil :read-only t)
|
||||
(desc "TODO")
|
||||
aliases
|
||||
optlist
|
||||
arglist
|
||||
plist
|
||||
(fn (lambda (_) (print! "But nobody came!"))))
|
||||
|
||||
(cl-defstruct doom-cli-option
|
||||
(symbol)
|
||||
(flags ())
|
||||
(args ())
|
||||
(desc "TODO"))
|
||||
|
||||
(defun doom--cli-get-option (cli flag)
|
||||
(cl-find-if (doom-partial #'member flag)
|
||||
(doom-cli-optlist cli)
|
||||
:key #'doom-cli-option-flags))
|
||||
|
||||
(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)))
|
||||
(got 0)
|
||||
restvar
|
||||
rest
|
||||
alist)
|
||||
(catch 'done
|
||||
(while args
|
||||
(let ((arg (pop args)))
|
||||
(cond ((eq (car arglist) '&rest)
|
||||
(setq restvar (cadr arglist)
|
||||
rest (cons arg args))
|
||||
(throw 'done t))
|
||||
|
||||
((string-match "^\\(--\\([a-zA-Z0-9][a-zA-Z0-9-_]*\\)\\)\\(?:=\\(.+\\)\\)?$" arg)
|
||||
(let* ((fullflag (match-string 1 arg))
|
||||
(opt (doom--cli-get-option cli fullflag)))
|
||||
(unless opt
|
||||
(user-error "Unrecognized switch %S" (concat "--" (match-string 2 arg))))
|
||||
(setf (alist-get (doom-cli-option-symbol opt) alist)
|
||||
(or (if (doom-cli-option-args opt)
|
||||
(or (match-string 3 arg)
|
||||
(pop args)
|
||||
(user-error "%S expected an argument, but got none"
|
||||
fullflag))
|
||||
(if (match-string 3 arg)
|
||||
(user-error "%S was not expecting an argument, but got %S"
|
||||
fullflag (match-string 3 arg))
|
||||
fullflag))))))
|
||||
|
||||
((string-match "^\\(-\\([a-zA-Z0-9]+\\)\\)$" arg)
|
||||
(let ((fullflag (match-string 1 arg))
|
||||
(flag (match-string 2 arg)))
|
||||
(dolist (switch (split-string flag "" t))
|
||||
(if-let (opt (doom--cli-get-option cli (concat "-" switch)))
|
||||
(setf (alist-get (doom-cli-option-symbol opt) alist)
|
||||
(if (doom-cli-option-args opt)
|
||||
(or (pop args)
|
||||
(user-error "%S expected an argument, but got none"
|
||||
fullflag))
|
||||
fullflag))
|
||||
(user-error "Unrecognized switch %S" (concat "-" switch))))))
|
||||
|
||||
(arglist
|
||||
(cl-incf got)
|
||||
(let ((spec (pop arglist)))
|
||||
(when (eq spec '&optional)
|
||||
(setq spec (pop arglist)))
|
||||
(setf (alist-get spec alist) arg))
|
||||
(when (null arglist)
|
||||
(throw 'done t)))
|
||||
|
||||
(t
|
||||
(push arg args)
|
||||
(throw 'done t))))))
|
||||
(when (< got expected)
|
||||
(error "Expected %d arguments, got %d" expected got))
|
||||
(when rest
|
||||
(setf (alist-get restvar alist) rest))
|
||||
alist))
|
||||
|
||||
(defun doom-cli-get (command)
|
||||
"Return a CLI object associated by COMMAND name (string)."
|
||||
(cond ((null command) nil)
|
||||
((doom-cli-p command) command)
|
||||
((doom-cli-get
|
||||
(gethash (cond ((symbolp command) command)
|
||||
((stringp command) (intern command))
|
||||
(command))
|
||||
doom--cli-commands)))))
|
||||
|
||||
(defun doom-cli-internal-p (cli)
|
||||
"Return non-nil if CLI is an internal (non-public) command."
|
||||
(string-prefix-p ":" (doom-cli-name cli)))
|
||||
|
||||
(defun doom-cli-execute (command &optional args)
|
||||
"Execute COMMAND (string) with ARGS (list of strings).
|
||||
|
||||
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))
|
||||
(user-error "Couldn't find any %S command" command)))
|
||||
|
||||
(defmacro defcli! (name speclist &optional docstring &rest body)
|
||||
"Defines a CLI command.
|
||||
|
||||
COMMAND is a symbol or a list of symbols representing the aliases for this
|
||||
command. DOCSTRING is a string description; its first line should be short
|
||||
(under 60 characters), as it will be used as a summary for 'doom help'.
|
||||
|
||||
SPECLIST is a specification for options and arguments, which can be a list
|
||||
specification for an option/switch in the following format:
|
||||
|
||||
(VAR [FLAGS... ARGS...] DESCRIPTION)
|
||||
|
||||
Otherwise, SPECLIST accepts the same argument specifiers as `defun'.
|
||||
|
||||
BODY will be run when this dispatcher is called."
|
||||
(declare (indent 2) (doc-string 3))
|
||||
(unless (stringp docstring)
|
||||
(push docstring body)
|
||||
(setq docstring "TODO"))
|
||||
(let ((names (doom-enlist name))
|
||||
(optlist (cl-remove-if-not #'listp speclist))
|
||||
(arglist (cl-remove-if #'listp speclist))
|
||||
(plist (cl-loop for (key val) on body by #'cddr
|
||||
if (keywordp key)
|
||||
nconc (list key val) into plist
|
||||
else return plist)))
|
||||
`(let ((name ',(car names))
|
||||
(aliases ',(cdr names))
|
||||
(plist ',plist))
|
||||
(when doom--cli-group
|
||||
(setq plist (plist-put plist :group doom--cli-group)))
|
||||
(puthash
|
||||
name
|
||||
(make-doom-cli (symbol-name name)
|
||||
:desc ,docstring
|
||||
:aliases (mapcar #'symbol-name aliases)
|
||||
:arglist ',arglist
|
||||
:optlist ',optlist
|
||||
:plist plist
|
||||
:fn
|
||||
(lambda (--alist--)
|
||||
(ignore --alist--)
|
||||
(let ,(cl-loop for opt in speclist
|
||||
for optsym = (if (listp opt) (car opt) opt)
|
||||
unless (memq optsym cl--lambda-list-keywords)
|
||||
collect (list optsym `(cdr (assq ',optsym --alist--))))
|
||||
,@(unless (plist-get plist :bare)
|
||||
'((unless doom-init-p
|
||||
(doom-initialize 'force 'noerror)
|
||||
(doom-initialize-modules))))
|
||||
,@body)))
|
||||
doom--cli-commands)
|
||||
(when aliases
|
||||
(mapc (doom-rpartial #'puthash name doom--cli-commands)
|
||||
aliases)))))
|
||||
|
||||
(defmacro defcligroup! (name docstring &rest body)
|
||||
"Declare all enclosed cli commands are part of the NAME group."
|
||||
(declare (indent defun) (doc-string 2))
|
||||
`(let ((doom--cli-group ,name))
|
||||
(puthash doom--cli-group ,docstring doom--cli-groups)
|
||||
,@body))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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.
|
||||
|
||||
This is the equivalent of running autoremove, install, autoloads, then
|
||||
recompile. Run this whenever you:
|
||||
|
||||
1. Modify your `doom!' block,
|
||||
2. Add or remove `package!' blocks to your config,
|
||||
3. Add or remove autoloaded functions in module autoloaded files.
|
||||
4. Update Doom outside of Doom (e.g. with git)
|
||||
|
||||
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))
|
||||
|
||||
;; 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"))
|
||||
|
||||
(defcligroup! "Diagnostics"
|
||||
"For troubleshooting and diagnostics"
|
||||
(load! "cli/doctor")
|
||||
(load! "cli/debug")
|
||||
(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 ()
|
||||
"Run Doom Emacs from bin/doom's parent directory.
|
||||
|
||||
All arguments are passed on to Emacs.
|
||||
|
||||
doom run
|
||||
doom run -nw init.el
|
||||
|
||||
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")
|
||||
)
|
||||
|
||||
(provide 'core-cli)
|
||||
;;; core-cli.el ends here
|
||||
561
.emacs.d/core/core-editor.el
Normal file
561
.emacs.d/core/core-editor.el
Normal file
@@ -0,0 +1,561 @@
|
||||
;;; core-editor.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-detect-indentation-excluded-modes '(fundamental-mode so-long-mode)
|
||||
"A list of major modes in which indentation should be automatically
|
||||
detected.")
|
||||
|
||||
(defvar-local doom-inhibit-indent-detection nil
|
||||
"A buffer-local flag that indicates whether `dtrt-indent' should try to detect
|
||||
indentation settings or not. This should be set by editorconfig if it
|
||||
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.
|
||||
|
||||
If a file is opened and discovered to be larger than the threshold, Doom
|
||||
performs emergency optimizations to prevent Emacs from hanging, crashing or
|
||||
becoming unusably slow.
|
||||
|
||||
These thresholds are in MB, and is used by `doom--optimize-for-large-files-a'.")
|
||||
|
||||
(defvar doom-large-file-excluded-modes
|
||||
'(so-long-mode special-mode archive-mode tar-mode jka-compr
|
||||
git-commit-mode image-mode doc-view-mode doc-view-mode-maybe
|
||||
ebrowse-tree-mode pdf-view-mode tags-table-mode)
|
||||
"Major modes that `doom-check-large-file-h' will ignore.")
|
||||
|
||||
|
||||
;;
|
||||
;;; File handling
|
||||
|
||||
(defadvice! doom--optimize-for-large-files-a (orig-fn &rest args)
|
||||
"Set `doom-large-file-p' if the file is too large.
|
||||
|
||||
Uses `doom-large-file-size-alist' to determine when a file is too large. When
|
||||
`doom-large-file-p' is set, other plugins can detect this and reduce their
|
||||
runtime costs (or disable themselves) to ensure the buffer is as fast as
|
||||
possible."
|
||||
:around #'after-find-file
|
||||
(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)))))))
|
||||
(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)
|
||||
(message "Large file detected! Cutting a few corners to improve performance...")))
|
||||
(apply orig-fn args)))
|
||||
|
||||
;; Resolve symlinks when opening files, so that any operations are conducted
|
||||
;; from the file's true directory (like `find-file').
|
||||
(setq find-file-visit-truename t
|
||||
vc-follow-symlinks t)
|
||||
|
||||
;; Disable the warning "X and Y are the same file". It's fine to ignore this
|
||||
;; warning as it will redirect you to the existing buffer anyway.
|
||||
(setq find-file-suppress-same-file-warnings t)
|
||||
|
||||
;; Create missing directories when we open a file that doesn't exist under a
|
||||
;; directory tree that may not exist.
|
||||
(add-hook! 'find-file-not-found-functions
|
||||
(defun doom-create-missing-directories-h ()
|
||||
"Automatically create missing directories when creating new files."
|
||||
(unless (file-remote-p buffer-file-name)
|
||||
(let ((parent-directory (file-name-directory buffer-file-name)))
|
||||
(and (not (file-directory-p parent-directory))
|
||||
(y-or-n-p (format "Directory `%s' does not exist! Create it?"
|
||||
parent-directory))
|
||||
(progn (make-directory parent-directory 'parents)
|
||||
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!
|
||||
(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/"))))
|
||||
|
||||
(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))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Formatting
|
||||
|
||||
;; Indentation
|
||||
(setq-default tab-width 4
|
||||
tab-always-indent t
|
||||
indent-tabs-mode nil
|
||||
fill-column 80)
|
||||
|
||||
;; Word wrapping
|
||||
(setq-default word-wrap t
|
||||
truncate-lines t
|
||||
truncate-partial-width-windows nil)
|
||||
|
||||
(setq sentence-end-double-space nil
|
||||
delete-trailing-lines nil
|
||||
require-final-newline t
|
||||
tabify-regexp "^\t* [ \t]+") ; for :retab
|
||||
|
||||
;; Favor hard-wrapping in text modes
|
||||
(add-hook 'text-mode-hook #'auto-fill-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.
|
||||
(setq kill-do-not-save-duplicates t)
|
||||
|
||||
;;
|
||||
(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))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Extra file extensions to support
|
||||
|
||||
(push '("/LICENSE\\'" . text-mode) auto-mode-alist)
|
||||
(push '("\\.log\\'" . text-mode) auto-mode-alist)
|
||||
|
||||
|
||||
;;
|
||||
;;; Built-in plugins
|
||||
|
||||
(use-package! autorevert
|
||||
;; revert buffers when their files/state have changed
|
||||
:hook (focus-in . doom-auto-revert-buffers-h)
|
||||
:hook (after-save . doom-auto-revert-buffers-h)
|
||||
:hook (doom-switch-buffer . doom-auto-revert-buffer-h)
|
||||
:hook (doom-switch-window . doom-auto-revert-buffer-h)
|
||||
:config
|
||||
(setq auto-revert-verbose t ; let us know when it happens
|
||||
auto-revert-use-notify nil
|
||||
auto-revert-stop-on-user-input nil
|
||||
;; 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'.
|
||||
;;
|
||||
;; 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.
|
||||
(defun doom-auto-revert-buffer-h ()
|
||||
"Auto revert current buffer, if necessary."
|
||||
(unless (or auto-revert-mode (active-minibuffer-window))
|
||||
(auto-revert-handler)))
|
||||
|
||||
(defun doom-auto-revert-buffers-h ()
|
||||
"Auto revert stale buffers in visible windows, if necessary."
|
||||
(dolist (buf (doom-visible-buffers))
|
||||
(with-current-buffer buf
|
||||
(doom-auto-revert-buffer-h)))))
|
||||
|
||||
|
||||
(use-package! recentf
|
||||
;; Keep track of recently opened files
|
||||
:defer-incrementally easymenu tree-widget timer
|
||||
:after-call after-find-file
|
||||
:commands recentf-open-files
|
||||
:config
|
||||
(defun doom--recent-file-truename (file)
|
||||
(if (or (file-remote-p file nil t)
|
||||
(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")
|
||||
recentf-auto-cleanup 'never
|
||||
recentf-max-menu-items 0
|
||||
recentf-max-saved-items 200)
|
||||
|
||||
(add-hook! '(doom-switch-window-hook write-file-functions)
|
||||
(defun doom--recentf-touch-buffer-h ()
|
||||
"Bump file in recent file list when it is switched or written to."
|
||||
(when buffer-file-name
|
||||
(recentf-add-file buffer-file-name))
|
||||
;; Return nil for `write-file-functions'
|
||||
nil))
|
||||
|
||||
(add-hook! 'dired-mode-hook
|
||||
(defun doom--recentf-add-dired-directory-h ()
|
||||
"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))))
|
||||
|
||||
|
||||
(use-package! savehist
|
||||
;; persist variables across sessions
|
||||
:defer-incrementally custom
|
||||
:after-call post-command-hook
|
||||
:config
|
||||
(setq savehist-file (concat doom-cache-dir "savehist")
|
||||
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."
|
||||
(setq kill-ring (cl-loop for item in kill-ring
|
||||
if (stringp item)
|
||||
collect (substring-no-properties item)
|
||||
else if item collect it)))))
|
||||
|
||||
|
||||
(use-package! saveplace
|
||||
;; persistent point location in buffers
|
||||
:after-call after-find-file dired-initial-position-hook
|
||||
:config
|
||||
(setq save-place-file (concat doom-cache-dir "saveplace")
|
||||
save-place-limit 100)
|
||||
|
||||
(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--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))
|
||||
|
||||
|
||||
(use-package! server
|
||||
:when (display-graphic-p)
|
||||
:after-call pre-command-hook after-find-file focus-out-hook
|
||||
:init
|
||||
(when-let (name (getenv "EMACS_SERVER_NAME"))
|
||||
(setq server-name name))
|
||||
:config
|
||||
(unless (server-running-p)
|
||||
(server-start)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(use-package! better-jumper
|
||||
:after-call pre-command-hook
|
||||
:preface
|
||||
;; REVIEW Suppress byte-compiler warning spawning a *Compile-Log* buffer at
|
||||
;; startup. This can be removed once gilbertw1/better-jumper#2 is merged.
|
||||
(defvar better-jumper-local-mode nil)
|
||||
:init
|
||||
(global-set-key [remap evil-jump-forward] #'better-jumper-jump-forward)
|
||||
(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)
|
||||
"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)
|
||||
"Set a jump point if ORIG-FN returns non-nil."
|
||||
(let ((origin (point-marker))
|
||||
(result
|
||||
(let* ((evil--jumps-jumping t)
|
||||
(better-jumper--jumping t))
|
||||
(apply orig-fn args))))
|
||||
(unless result
|
||||
(with-current-buffer (marker-buffer origin)
|
||||
(better-jumper-set-jump
|
||||
(if (markerp (car args))
|
||||
(car args)
|
||||
origin))))
|
||||
result))
|
||||
|
||||
(defun doom-set-jump-h ()
|
||||
"Run `better-jumper-set-jump' but return nil, for short-circuiting hooks."
|
||||
(better-jumper-set-jump)
|
||||
nil)
|
||||
|
||||
;; Creates a jump point before killing a buffer. This allows you to undo
|
||||
;; killing a buffer easily (only works with file buffers though; it's not
|
||||
;; possible to resurrect special buffers).
|
||||
(advice-add #'kill-current-buffer :around #'doom-set-jump-a)
|
||||
|
||||
;; Create a jump point before jumping with imenu.
|
||||
(advice-add #'imenu :around #'doom-set-jump-a))
|
||||
|
||||
|
||||
(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)))))
|
||||
:config
|
||||
;; 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)
|
||||
;; Reduced from the default of 5000 for slightly faster analysis
|
||||
(setq dtrt-indent-max-lines 2000)
|
||||
|
||||
;; always keep tab-width up-to-date
|
||||
(push '(t tab-width) dtrt-indent-hook-generic-mapping-list)
|
||||
|
||||
(defvar dtrt-indent-run-after-smie)
|
||||
(defadvice! doom--fix-broken-smie-modes-a (orig-fn arg)
|
||||
"Some smie modes throw errors when trying to guess their indentation, like
|
||||
`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
|
||||
(funcall orig-fn arg)))))
|
||||
|
||||
|
||||
(use-package! helpful
|
||||
;; 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)
|
||||
|
||||
(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))
|
||||
(apply orig-fn args)))
|
||||
|
||||
(after! apropos
|
||||
;; patch apropos buttons to call helpful instead of help
|
||||
(dolist (fun-bt '(apropos-function apropos-macro apropos-command))
|
||||
(button-type-put
|
||||
fun-bt 'action
|
||||
(lambda (button)
|
||||
(helpful-callable (button-get button 'apropos-symbol)))))
|
||||
(dolist (var-bt '(apropos-variable apropos-user-option))
|
||||
(button-type-put
|
||||
var-bt 'action
|
||||
(lambda (button)
|
||||
(helpful-variable (button-get button 'apropos-symbol)))))))
|
||||
|
||||
|
||||
;;;###package imenu
|
||||
(add-hook 'imenu-after-jump-hook #'recenter)
|
||||
|
||||
|
||||
(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
|
||||
:commands sp-pair sp-local-pair sp-with-modes sp-point-in-comment sp-point-in-string
|
||||
:config
|
||||
;; 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...
|
||||
(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)
|
||||
;; The default is 100, because smartparen's scans are relatively expensive
|
||||
;; (especially with large pair lists for somoe modes), we halve 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-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.
|
||||
(setq sp-escape-quotes-after-insert nil)
|
||||
|
||||
;; Silence some harmless but annoying echo-area spam
|
||||
(dolist (key '(:unmatched-expression :no-matching-tag))
|
||||
(setf (cdr (assq 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))))
|
||||
|
||||
;; You're likely writing lisp in the minibuffer, therefore, disable these
|
||||
;; quote pairs, which lisps doesn't use for strings:
|
||||
(sp-local-pair 'minibuffer-inactive-mode "'" nil :actions nil)
|
||||
(sp-local-pair 'minibuffer-inactive-mode "`" nil :actions nil)
|
||||
|
||||
;; Smartparens breaks evil-mode's replace state
|
||||
(defvar doom-buffer-smartparens-mode nil)
|
||||
(add-hook! 'evil-replace-state-exit-hook
|
||||
(defun doom-enable-smartparens-mode-maybe-h ()
|
||||
(when doom-buffer-smartparens-mode
|
||||
(turn-on-smartparens-mode)
|
||||
(kill-local-variable 'doom-buffer-smartparens-mode))))
|
||||
(add-hook! 'evil-replace-state-entry-hook
|
||||
(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))
|
||||
|
||||
|
||||
(use-package! so-long
|
||||
:after-call after-find-file
|
||||
:config
|
||||
(when doom-interactive-mode
|
||||
(global-so-long-mode +1))
|
||||
;; 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',
|
||||
;; however, because long files have a far bigger impact on Emacs performance.
|
||||
(delq! 'font-lock-mode so-long-minor-modes)
|
||||
(delq! 'display-line-numbers-mode so-long-minor-modes)
|
||||
(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))
|
||||
;; Text files could possibly be too long too
|
||||
(add-to-list 'so-long-target-modes 'text-mode)
|
||||
;; But disable everything else that may be unnecessary/expensive for large
|
||||
;; or wide buffers.
|
||||
(appendq! so-long-minor-modes
|
||||
'(flycheck-mode
|
||||
flyspell-mode
|
||||
eldoc-mode
|
||||
smartparens-mode
|
||||
highlight-numbers-mode
|
||||
better-jumper-local-mode
|
||||
ws-butler-mode
|
||||
auto-composition-mode
|
||||
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)
|
||||
(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))
|
||||
|
||||
(provide 'core-editor)
|
||||
;;; core-editor.el ends here
|
||||
425
.emacs.d/core/core-keybinds.el
Normal file
425
.emacs.d/core/core-keybinds.el
Normal file
@@ -0,0 +1,425 @@
|
||||
;;; core-keybinds.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; A centralized keybinds system, integrated with `which-key' to preview
|
||||
;; available keybindings. All built into one powerful macro: `map!'. If evil is
|
||||
;; never loaded, then evil bindings set with `map!' are ignored (i.e. omitted
|
||||
;; entirely for performance reasons).
|
||||
|
||||
(defvar doom-leader-key "SPC"
|
||||
"The leader prefix key for Evil users.")
|
||||
|
||||
(defvar doom-leader-alt-key "M-SPC"
|
||||
"An alternative leader prefix key, used for Insert and Emacs states, and for
|
||||
non-evil users.")
|
||||
|
||||
(defvar doom-localleader-key "SPC m"
|
||||
"The localleader prefix key, for major-mode specific commands.")
|
||||
|
||||
(defvar doom-localleader-alt-key "M-SPC m"
|
||||
"The localleader prefix key, for major-mode specific commands. Used for Insert
|
||||
and Emacs states, and for non-evil users.")
|
||||
|
||||
(defvar doom-leader-map (make-sparse-keymap)
|
||||
"An overriding keymap for <leader> keys.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Keybind settings
|
||||
|
||||
(when IS-MAC
|
||||
(setq mac-command-modifier 'super
|
||||
mac-option-modifier 'meta))
|
||||
|
||||
|
||||
;;
|
||||
;;; Universal, non-nuclear escape
|
||||
|
||||
;; `keyboard-quit' is too much of a nuclear option. I wanted an ESC/C-g to
|
||||
;; do-what-I-mean. It serves four purposes (in order):
|
||||
;;
|
||||
;; 1. Quit active states; e.g. highlights, searches, snippets, iedit,
|
||||
;; multiple-cursors, recording macros, etc.
|
||||
;; 2. Close popup windows remotely (if it is allowed to)
|
||||
;; 3. Refresh buffer indicators, like git-gutter and flycheck
|
||||
;; 4. Or fall back to `keyboard-quit'
|
||||
;;
|
||||
;; And it should do these things incrementally, rather than all at once. And it
|
||||
;; shouldn't interfere with recording macros or the minibuffer. This may require
|
||||
;; you press ESC/C-g two or three times on some occasions to reach
|
||||
;; `keyboard-quit', but this is much more intuitive.
|
||||
|
||||
(defvar doom-escape-hook nil
|
||||
"A hook run when C-g is pressed (or ESC in normal mode, for evil users).
|
||||
|
||||
More specifically, when `doom/escape' is pressed. If any hook returns non-nil,
|
||||
all hooks after it are ignored.")
|
||||
|
||||
(defun doom/escape ()
|
||||
"Run `doom-escape-hook'."
|
||||
(interactive)
|
||||
(cond ((minibuffer-window-active-p (minibuffer-window))
|
||||
;; quit the minibuffer if open.
|
||||
(abort-recursive-edit))
|
||||
;; Run all escape hooks. If any returns non-nil, then stop there.
|
||||
((run-hook-with-args-until-success 'doom-escape-hook))
|
||||
;; don't abort macros
|
||||
((or defining-kbd-macro executing-kbd-macro) nil)
|
||||
;; Back to the default
|
||||
((keyboard-quit))))
|
||||
|
||||
(global-set-key [remap keyboard-quit] #'doom/escape)
|
||||
|
||||
|
||||
;;
|
||||
;;; General + leader/localleader keys
|
||||
|
||||
(use-package general
|
||||
:init
|
||||
;; Convenience aliases
|
||||
(defalias 'define-key! #'general-def)
|
||||
(defalias 'unmap! #'general-unbind))
|
||||
|
||||
;; HACK `map!' uses this instead of `define-leader-key!' because it consumes
|
||||
;; 20-30% more startup time, so we reimplement it ourselves.
|
||||
(defmacro doom--define-leader-key (&rest keys)
|
||||
(let (prefix forms wkforms)
|
||||
(while keys
|
||||
(let ((key (pop keys))
|
||||
(def (pop keys)))
|
||||
(if (keywordp key)
|
||||
(when (memq key '(:prefix :infix))
|
||||
(setq prefix def))
|
||||
(when prefix
|
||||
(setq key `(general--concat t ,prefix ,key)))
|
||||
(let* ((udef (cdr-safe (doom-unquote def)))
|
||||
(bdef (if (general--extended-def-p udef)
|
||||
(general--extract-def (general--normalize-extended-def udef))
|
||||
def)))
|
||||
(unless (eq bdef :ignore)
|
||||
(push `(define-key doom-leader-map (general--kbd ,key)
|
||||
,bdef)
|
||||
forms))
|
||||
(when-let (desc (cadr (memq :which-key udef)))
|
||||
(prependq!
|
||||
wkforms `((which-key-add-key-based-replacements
|
||||
(general--concat t doom-leader-alt-key ,key)
|
||||
,desc)
|
||||
(which-key-add-key-based-replacements
|
||||
(general--concat t doom-leader-key ,key)
|
||||
,desc))))))))
|
||||
(macroexp-progn
|
||||
(cons `(after! which-key ,@(nreverse wkforms))
|
||||
(nreverse forms)))))
|
||||
|
||||
(defmacro define-leader-key! (&rest args)
|
||||
"Define <leader> keys.
|
||||
|
||||
Uses `general-define-key' under the hood, but does not support :states,
|
||||
:wk-full-keys or :keymaps. Use `map!' for a more convenient interface.
|
||||
|
||||
See `doom-leader-key' and `doom-leader-alt-key' to change the leader prefix."
|
||||
`(general-define-key
|
||||
:states nil
|
||||
:wk-full-keys nil
|
||||
:keymaps 'doom-leader-map
|
||||
,@args))
|
||||
|
||||
(defmacro define-localleader-key! (&rest args)
|
||||
"Define <localleader> key.
|
||||
|
||||
Uses `general-define-key' under the hood, but does not support :major-modes,
|
||||
:states, :prefix or :non-normal-prefix. Use `map!' for a more convenient
|
||||
interface.
|
||||
|
||||
See `doom-localleader-key' and `doom-localleader-alt-key' to change the
|
||||
localleader prefix."
|
||||
(if (featurep 'evil)
|
||||
;; :non-normal-prefix doesn't apply to non-evil sessions (only evil's
|
||||
;; emacs state)
|
||||
`(general-define-key
|
||||
:states '(normal visual motion emacs insert)
|
||||
:major-modes t
|
||||
:prefix doom-localleader-key
|
||||
:non-normal-prefix doom-localleader-alt-key
|
||||
,@args)
|
||||
`(general-define-key
|
||||
:major-modes t
|
||||
:prefix doom-localleader-alt-key
|
||||
,@args)))
|
||||
|
||||
;; We use a prefix commands instead of general's :prefix/:non-normal-prefix
|
||||
;; properties because general is incredibly slow binding keys en mass with them
|
||||
;; in conjunction with :states -- an effective doubling of Doom's startup time!
|
||||
(define-prefix-command 'doom/leader 'doom-leader-map)
|
||||
(define-key doom-leader-map [override-state] 'all)
|
||||
|
||||
;; Bind `doom-leader-key' and `doom-leader-alt-key' as late as possible to give
|
||||
;; the user a chance to modify them.
|
||||
(add-hook! 'doom-after-init-modules-hook
|
||||
(defun doom-init-leader-keys-h ()
|
||||
"Bind `doom-leader-key' and `doom-leader-alt-key'."
|
||||
(let ((map general-override-mode-map))
|
||||
(if (not (featurep 'evil))
|
||||
(progn
|
||||
(cond ((equal doom-leader-alt-key "C-c")
|
||||
(set-keymap-parent doom-leader-map mode-specific-map))
|
||||
((equal doom-leader-alt-key "C-x")
|
||||
(set-keymap-parent doom-leader-map ctl-x-map)))
|
||||
(define-key map (kbd doom-leader-alt-key) 'doom/leader))
|
||||
(evil-define-key* '(normal visual motion) map (kbd doom-leader-key) 'doom/leader)
|
||||
(evil-define-key* '(emacs insert) map (kbd doom-leader-alt-key) 'doom/leader))
|
||||
(general-override-mode +1))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(use-package! which-key
|
||||
:defer 1
|
||||
:after-call pre-command-hook
|
||||
:init
|
||||
(setq which-key-sort-order #'which-key-prefix-then-key-order
|
||||
which-key-sort-uppercase-first nil
|
||||
which-key-add-column-padding 1
|
||||
which-key-max-display-columns nil
|
||||
which-key-min-display-lines 6
|
||||
which-key-side-window-slot -10)
|
||||
:config
|
||||
;; general improvements to which-key readability
|
||||
(set-face-attribute 'which-key-local-map-description-face nil :weight 'bold)
|
||||
(which-key-setup-side-window-bottom)
|
||||
(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))
|
||||
|
||||
|
||||
;;
|
||||
;;; `map!' macro
|
||||
|
||||
(defvar doom-evil-state-alist
|
||||
'((?n . normal)
|
||||
(?v . visual)
|
||||
(?i . insert)
|
||||
(?e . emacs)
|
||||
(?o . operator)
|
||||
(?m . motion)
|
||||
(?r . replace)
|
||||
(?g . global))
|
||||
"A list of cons cells that map a letter to a evil state symbol.")
|
||||
|
||||
(defun doom--map-keyword-to-states (keyword)
|
||||
"Convert a KEYWORD into a list of evil state symbols.
|
||||
|
||||
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
|
||||
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)
|
||||
(defvar doom--map-batch-forms nil)
|
||||
(defvar doom--map-state '(:dummy t))
|
||||
(defvar doom--map-parent-state nil)
|
||||
(defvar doom--map-evil-p nil)
|
||||
(after! evil (setq doom--map-evil-p t))
|
||||
|
||||
(defun doom--map-process (rest)
|
||||
(let ((doom--map-fn doom--map-fn)
|
||||
doom--map-state
|
||||
doom--map-forms
|
||||
desc)
|
||||
(while rest
|
||||
(let ((key (pop rest)))
|
||||
(cond ((listp key)
|
||||
(doom--map-nested nil key))
|
||||
|
||||
((keywordp key)
|
||||
(pcase key
|
||||
(:leader
|
||||
(doom--map-commit)
|
||||
(setq doom--map-fn 'doom--define-leader-key))
|
||||
(:localleader
|
||||
(doom--map-commit)
|
||||
(setq doom--map-fn 'define-localleader-key!))
|
||||
(:after
|
||||
(doom--map-nested (list 'after! (pop rest)) rest)
|
||||
(setq rest nil))
|
||||
(:desc
|
||||
(setq desc (pop rest)))
|
||||
(:map
|
||||
(doom--map-set :keymaps `(quote ,(doom-enlist (pop rest)))))
|
||||
(:mode
|
||||
(push (cl-loop for m in (doom-enlist (pop rest))
|
||||
collect (intern (concat (symbol-name m) "-map")))
|
||||
rest)
|
||||
(push :map rest))
|
||||
((or :when :unless)
|
||||
(doom--map-nested (list (intern (doom-keyword-name key)) (pop rest)) rest)
|
||||
(setq rest nil))
|
||||
(:prefix-map
|
||||
(cl-destructuring-bind (prefix . desc)
|
||||
(doom-enlist (pop rest))
|
||||
(let ((keymap (intern (format "doom-leader-%s-map" desc))))
|
||||
(setq rest
|
||||
(append (list :desc desc prefix keymap
|
||||
:prefix prefix)
|
||||
rest))
|
||||
(push `(defvar ,keymap (make-sparse-keymap))
|
||||
doom--map-forms))))
|
||||
(:prefix
|
||||
(cl-destructuring-bind (prefix . desc)
|
||||
(doom-enlist (pop rest))
|
||||
(doom--map-set (if doom--map-fn :infix :prefix)
|
||||
prefix)
|
||||
(when (stringp desc)
|
||||
(setq rest (append (list :desc desc "" nil) rest)))))
|
||||
(:textobj
|
||||
(let* ((key (pop rest))
|
||||
(inner (pop rest))
|
||||
(outer (pop rest)))
|
||||
(push `(map! (:map evil-inner-text-objects-map ,key ,inner)
|
||||
(:map evil-outer-text-objects-map ,key ,outer))
|
||||
doom--map-forms)))
|
||||
(_
|
||||
(condition-case _
|
||||
(doom--map-def (pop rest) (pop rest)
|
||||
(doom--map-keyword-to-states key)
|
||||
desc)
|
||||
(error
|
||||
(error "Not a valid `map!' property: %s" key)))
|
||||
(setq desc nil))))
|
||||
|
||||
((doom--map-def key (pop rest) nil desc)
|
||||
(setq desc nil)))))
|
||||
|
||||
(doom--map-commit)
|
||||
(macroexp-progn (nreverse (delq nil doom--map-forms)))))
|
||||
|
||||
(defun doom--map-append-keys (prop)
|
||||
(let ((a (plist-get doom--map-parent-state prop))
|
||||
(b (plist-get doom--map-state prop)))
|
||||
(if (and a b)
|
||||
`(general--concat nil ,a ,b)
|
||||
(or a b))))
|
||||
|
||||
(defun doom--map-nested (wrapper rest)
|
||||
(doom--map-commit)
|
||||
(let ((doom--map-parent-state (doom--map-state)))
|
||||
(push (if wrapper
|
||||
(append wrapper (list (doom--map-process rest)))
|
||||
(doom--map-process rest))
|
||||
doom--map-forms)))
|
||||
|
||||
(defun doom--map-set (prop &optional value)
|
||||
(unless (equal (plist-get doom--map-state prop) value)
|
||||
(doom--map-commit))
|
||||
(setq doom--map-state (plist-put doom--map-state prop value)))
|
||||
|
||||
(defun doom--map-def (key def &optional states desc)
|
||||
(when (or (memq 'global states)
|
||||
(null states))
|
||||
(setq states (cons 'nil (delq 'global states))))
|
||||
(when desc
|
||||
(let (unquoted)
|
||||
(cond ((and (listp def)
|
||||
(keywordp (car-safe (setq unquoted (doom-unquote def)))))
|
||||
(setq def (list 'quote (plist-put unquoted :which-key desc))))
|
||||
((setq def (cons 'list
|
||||
(if (and (equal key "")
|
||||
(null def))
|
||||
`(:ignore t :which-key ,desc)
|
||||
(plist-put (general--normalize-extended-def def)
|
||||
:which-key desc))))))))
|
||||
(dolist (state states)
|
||||
(push (list key def)
|
||||
(alist-get state doom--map-batch-forms)))
|
||||
t)
|
||||
|
||||
(defun doom--map-commit ()
|
||||
(when doom--map-batch-forms
|
||||
(cl-loop with attrs = (doom--map-state)
|
||||
for (state . defs) in doom--map-batch-forms
|
||||
if (or doom--map-evil-p (not state))
|
||||
collect `(,(or doom--map-fn 'general-define-key)
|
||||
,@(if state `(:states ',state)) ,@attrs
|
||||
,@(mapcan #'identity (nreverse defs)))
|
||||
into forms
|
||||
finally do (push (macroexp-progn forms) doom--map-forms))
|
||||
(setq doom--map-batch-forms nil)))
|
||||
|
||||
(defun doom--map-state ()
|
||||
(let ((plist
|
||||
(append (list :prefix (doom--map-append-keys :prefix)
|
||||
:infix (doom--map-append-keys :infix)
|
||||
:keymaps
|
||||
(append (plist-get doom--map-parent-state :keymaps)
|
||||
(plist-get doom--map-state :keymaps)))
|
||||
doom--map-state
|
||||
nil))
|
||||
newplist)
|
||||
(while plist
|
||||
(let ((key (pop plist))
|
||||
(val (pop plist)))
|
||||
(when (and val (not (plist-member newplist key)))
|
||||
(push val newplist)
|
||||
(push key newplist))))
|
||||
newplist))
|
||||
|
||||
;;
|
||||
(defmacro map! (&rest rest)
|
||||
"A convenience macro for defining keybinds, powered by `general'.
|
||||
|
||||
If evil isn't loaded, evil-specific bindings are ignored.
|
||||
|
||||
States
|
||||
:n normal
|
||||
:v visual
|
||||
:i insert
|
||||
:e emacs
|
||||
:o operator
|
||||
:m motion
|
||||
:r replace
|
||||
:g global (binds the key without evil `current-global-map')
|
||||
|
||||
These can be combined in any order, e.g. :nvi will apply to normal, visual and
|
||||
insert mode. The state resets after the following key=>def pair. If states are
|
||||
omitted the keybind will be global (no emacs state; this is different from
|
||||
evil's Emacs state and will work in the absence of `evil-mode').
|
||||
|
||||
Properties
|
||||
:leader [...] an alias for (:prefix doom-leader-key ...)
|
||||
:localleader [...] bind to localleader; requires a keymap
|
||||
:mode [MODE(s)] [...] inner keybinds are applied to major MODE(s)
|
||||
:map [KEYMAP(s)] [...] inner keybinds are applied to KEYMAP(S)
|
||||
:prefix [PREFIX] [...] set keybind prefix for following keys. PREFIX
|
||||
can be a cons cell: (PREFIX . DESCRIPTION)
|
||||
:prefix-map [PREFIX] [...] same as :prefix, but defines a prefix keymap
|
||||
where the following keys will be bound. DO NOT
|
||||
USE THIS IN YOUR PRIVATE CONFIG.
|
||||
:after [FEATURE] [...] apply keybinds when [FEATURE] loads
|
||||
:textobj KEY INNER-FN OUTER-FN define a text object keybind pair
|
||||
:when [CONDITION] [...]
|
||||
:unless [CONDITION] [...]
|
||||
|
||||
Any of the above properties may be nested, so that they only apply to a
|
||||
certain group of keybinds."
|
||||
(doom--map-process rest))
|
||||
|
||||
(provide 'core-keybinds)
|
||||
;;; core-keybinds.el ends here
|
||||
428
.emacs.d/core/core-lib.el
Normal file
428
.emacs.d/core/core-lib.el
Normal file
@@ -0,0 +1,428 @@
|
||||
;;; core-lib.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'cl-lib)
|
||||
(require 'subr-x)
|
||||
|
||||
;;
|
||||
;;; Helpers
|
||||
|
||||
(defun doom--resolve-hook-forms (hooks)
|
||||
"Converts a list of modes into a list of hook symbols.
|
||||
|
||||
If a mode is quoted, it is left as is. If the entire HOOKS list is quoted, the
|
||||
list is returned as-is."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(let ((hook-list (doom-enlist (doom-unquote hooks))))
|
||||
(if (eq (car-safe hooks) 'quote)
|
||||
hook-list
|
||||
(cl-loop for hook in hook-list
|
||||
if (eq (car-safe hook) 'quote)
|
||||
collect (cadr hook)
|
||||
else collect (intern (format "%s-hook" (symbol-name hook)))))))
|
||||
|
||||
(defun doom--setq-hook-fns (hooks rest &optional singles)
|
||||
(unless (or singles (= 0 (% (length rest) 2)))
|
||||
(signal 'wrong-number-of-arguments (list #'evenp (length rest))))
|
||||
(cl-loop with vars = (let ((args rest)
|
||||
vars)
|
||||
(while args
|
||||
(push (if singles
|
||||
(list (pop args))
|
||||
(cons (pop args) (pop args)))
|
||||
vars))
|
||||
(nreverse vars))
|
||||
for hook in (doom--resolve-hook-forms hooks)
|
||||
for mode = (string-remove-suffix "-hook" (symbol-name hook))
|
||||
append
|
||||
(cl-loop for (var . val) in vars
|
||||
collect
|
||||
(list var val hook
|
||||
(intern (format "doom--setq-%s-for-%s-h"
|
||||
var mode))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Public library
|
||||
|
||||
(defun doom-unquote (exp)
|
||||
"Return EXP unquoted."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(while (memq (car-safe exp) '(quote function))
|
||||
(setq exp (cadr exp)))
|
||||
exp)
|
||||
|
||||
(defun doom-enlist (exp)
|
||||
"Return EXP wrapped in a list, or as-is if already a list."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(if (listp exp) exp (list exp)))
|
||||
|
||||
(defun doom-keyword-intern (str)
|
||||
"Converts STR (a string) into a keyword (`keywordp')."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(cl-check-type str string)
|
||||
(intern (concat ":" str)))
|
||||
|
||||
(defun doom-keyword-name (keyword)
|
||||
"Returns the string name of KEYWORD (`keywordp') minus the leading colon."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(cl-check-type keyword keyword)
|
||||
(substring (symbol-name keyword) 1))
|
||||
|
||||
(defmacro doom-log (format-string &rest args)
|
||||
"Log to *Messages* if `doom-debug-mode' is on.
|
||||
Does not interrupt the minibuffer if it is in use, but still logs to *Messages*.
|
||||
Accepts the same arguments as `message'."
|
||||
`(when doom-debug-mode
|
||||
(let ((inhibit-message (active-minibuffer-window)))
|
||||
(message
|
||||
,(concat (propertize "DOOM " 'face 'font-lock-comment-face)
|
||||
(when (bound-and-true-p doom--current-module)
|
||||
(propertize
|
||||
(format "[%s/%s] "
|
||||
(doom-keyword-name (car doom--current-module))
|
||||
(cdr doom--current-module))
|
||||
'face 'warning))
|
||||
format-string)
|
||||
,@args))))
|
||||
|
||||
(defalias 'doom-partial #'apply-partially)
|
||||
|
||||
(defun doom-rpartial (fn &rest args)
|
||||
"Return a function that is a partial application of FUN to right-hand ARGS.
|
||||
|
||||
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."
|
||||
(lambda (&rest pre-args)
|
||||
(apply fn (append pre-args args))))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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 file! ()
|
||||
"Return the emacs lisp file this macro is called from."
|
||||
(cond ((bound-and-true-p byte-compile-current-file))
|
||||
(load-file-name)
|
||||
((stringp (car-safe current-load-list))
|
||||
(car current-load-list))
|
||||
(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 setq! (&rest settings)
|
||||
"A stripped-down `customize-set-variable' with the syntax of `setq'."
|
||||
(macroexp-progn
|
||||
(cl-loop for (var val) on settings by 'cddr
|
||||
collect `(funcall (or (get ',var 'custom-set) #'set)
|
||||
',var ,val))))
|
||||
|
||||
(defmacro pushnew! (place &rest values)
|
||||
"Push VALUES sequentially into PLACE, if they aren't already present.
|
||||
This is a variadic `cl-pushnew'."
|
||||
(let ((var (make-symbol "result")))
|
||||
`(dolist (,var (list ,@values) (with-no-warnings ,place))
|
||||
(cl-pushnew ,var ,place :test #'equal))))
|
||||
|
||||
(defmacro prependq! (sym &rest lists)
|
||||
"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))
|
||||
|
||||
(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."
|
||||
`(let ((default-directory ,(dir!))
|
||||
file-name-handler-alist)
|
||||
(dolist (dir (list ,@dirs))
|
||||
(cl-pushnew (expand-file-name dir) load-path))))
|
||||
|
||||
(defmacro add-transient-hook! (hook-or-function &rest forms)
|
||||
"Attaches a self-removing function to HOOK-OR-FUNCTION.
|
||||
|
||||
FORMS are evaluated once, when that function/hook is first invoked, then never
|
||||
again.
|
||||
|
||||
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)))))
|
||||
`(let ((sym ,hook-or-function))
|
||||
(defun ,fn (&rest _)
|
||||
,@forms
|
||||
(let ((sym ,hook-or-function))
|
||||
(cond ((functionp sym) (advice-remove sym #',fn))
|
||||
((symbolp sym) (remove-hook sym #',fn))))
|
||||
(unintern ',fn nil))
|
||||
(cond ((functionp sym)
|
||||
(advice-add ,hook-or-function ,(if append :after :before) #',fn))
|
||||
((symbolp sym)
|
||||
(put ',fn 'permanent-local-hook t)
|
||||
(add-hook sym #',fn ,append))))))
|
||||
|
||||
(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
|
||||
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).
|
||||
|
||||
\(fn HOOKS [:append :local] FUNCTIONS)"
|
||||
(declare (indent (lambda (indent-point state)
|
||||
(goto-char indent-point)
|
||||
(when (looking-at-p "\\s-*(")
|
||||
(lisp-indent-defform state indent-point))))
|
||||
(debug t))
|
||||
(let* ((hook-forms (doom--resolve-hook-forms hooks))
|
||||
(func-forms ())
|
||||
(defn-forms ())
|
||||
append-p
|
||||
local-p
|
||||
remove-p
|
||||
forms)
|
||||
(while (keywordp (car rest))
|
||||
(pcase (pop rest)
|
||||
(:append (setq append-p t))
|
||||
(:local (setq local-p t))
|
||||
(:remove (setq remove-p t))))
|
||||
(let ((first (car-safe (car rest))))
|
||||
(cond ((null first)
|
||||
(setq func-forms rest))
|
||||
|
||||
((eq first 'defun)
|
||||
(setq func-forms (mapcar #'cadr rest)
|
||||
defn-forms rest))
|
||||
|
||||
((memq first '(quote function))
|
||||
(setq func-forms
|
||||
(if (cdr rest)
|
||||
(mapcar #'doom-unquote rest)
|
||||
(doom-enlist (doom-unquote (car rest))))))
|
||||
|
||||
((setq func-forms (list `(lambda (&rest _) ,@rest)))))
|
||||
(dolist (hook hook-forms)
|
||||
(dolist (func func-forms)
|
||||
(push (if remove-p
|
||||
`(remove-hook ',hook #',func ,local-p)
|
||||
`(add-hook ',hook #',func ,append-p ,local-p))
|
||||
forms)))
|
||||
(macroexp-progn
|
||||
(append defn-forms
|
||||
(if append-p
|
||||
(nreverse forms)
|
||||
forms))))))
|
||||
|
||||
(defmacro remove-hook! (hooks &rest rest)
|
||||
"A convenience macro for removing N functions from M hooks.
|
||||
|
||||
Takes the same arguments as `add-hook!'.
|
||||
|
||||
If N and M = 1, there's no benefit to using this macro over `remove-hook'.
|
||||
|
||||
\(fn HOOKS [:append :local] FUNCTIONS)"
|
||||
(declare (indent defun) (debug t))
|
||||
`(add-hook! ,hooks :remove ,@rest))
|
||||
|
||||
(defmacro setq-hook! (hooks &rest var-vals)
|
||||
"Sets buffer-local variables on HOOKS.
|
||||
|
||||
\(fn HOOKS &rest [SYM VAL]...)"
|
||||
(declare (indent 1))
|
||||
(macroexp-progn
|
||||
(cl-loop for (var val hook fn) in (doom--setq-hook-fns hooks var-vals)
|
||||
collect `(defun ,fn (&rest _)
|
||||
,(format "%s = %s" var (pp-to-string val))
|
||||
(setq-local ,var ,val))
|
||||
collect `(remove-hook ',hook #',fn) ; ensure set order
|
||||
collect `(add-hook ',hook #',fn))))
|
||||
|
||||
(defmacro unsetq-hook! (hooks &rest vars)
|
||||
"Unbind setq hooks on HOOKS for VARS.
|
||||
|
||||
\(fn HOOKS &rest [SYM VAL]...)"
|
||||
(declare (indent 1))
|
||||
(macroexp-progn
|
||||
(cl-loop for (_var _val hook fn)
|
||||
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.
|
||||
|
||||
ARGLIST is as in `defun'. WHERE is a keyword as passed to `advice-add', and
|
||||
PLACE is the function to which to add the advice, like in `advice-add'.
|
||||
DOCSTRING and BODY are as in `defun'.
|
||||
|
||||
\(fn SYMBOL ARGLIST &optional DOCSTRING &rest [WHERE PLACES...] BODY\)"
|
||||
(declare (doc-string 3) (indent defun))
|
||||
(unless (stringp docstring)
|
||||
(push docstring body)
|
||||
(setq docstring nil))
|
||||
(let (where-alist)
|
||||
(while (keywordp (car body))
|
||||
(push `(cons ,(pop body) (doom-enlist ,(pop body)))
|
||||
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)))))))
|
||||
|
||||
(provide 'core-lib)
|
||||
;;; core-lib.el ends here
|
||||
603
.emacs.d/core/core-modules.el
Normal file
603
.emacs.d/core/core-modules.el
Normal file
@@ -0,0 +1,603 @@
|
||||
;;; core-modules.el --- module & package management system -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-init-modules-p nil
|
||||
"Non-nil if `doom-initialize-modules' has run.")
|
||||
|
||||
(defvar doom-modules ()
|
||||
"A hash table of enabled modules. Set by `doom-initialize-modules'.")
|
||||
|
||||
(defvar doom-modules-dirs
|
||||
(list (expand-file-name "modules/" doom-private-dir)
|
||||
doom-modules-dir)
|
||||
"A list of module root directories. Order determines priority.")
|
||||
|
||||
(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))))
|
||||
"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))
|
||||
(syntax-checker (:tools flycheck)))
|
||||
|
||||
This marks :feature version-control, :feature spellcheck and :feature
|
||||
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
|
||||
"If non-nil, don't emit deprecated or missing module warnings at startup.")
|
||||
|
||||
;;; Custom hooks
|
||||
(defvar doom-before-init-modules-hook nil
|
||||
"A list of hooks to run before Doom's modules' config.el files are loaded, but
|
||||
after their init.el files are loaded.")
|
||||
|
||||
(defvar doom-init-modules-hook nil
|
||||
"A list of hooks to run after Doom's modules' config.el files have loaded, but
|
||||
before the user's private module.")
|
||||
|
||||
(defvaralias 'doom-after-init-modules-hook 'after-init-hook)
|
||||
|
||||
(defvar doom--current-module nil)
|
||||
(defvar doom--current-flags nil)
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrap API
|
||||
|
||||
(defun doom-initialize-modules (&optional force-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))
|
||||
(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))
|
||||
(run-hook-wrapped 'doom-init-modules-hook #'doom-try-run-hook)
|
||||
(load! "config" doom-private-dir t)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Module API
|
||||
|
||||
(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)))
|
||||
|
||||
(defun doom-module-get (category module &optional property)
|
||||
"Returns the plist for CATEGORY MODULE. Gets PROPERTY, specifically, if set."
|
||||
(declare (pure t) (side-effect-free t))
|
||||
(when-let (plist (gethash (cons category module) doom-modules))
|
||||
(if property
|
||||
(plist-get plist property)
|
||||
plist)))
|
||||
|
||||
(defun doom-module-put (category module &rest plist)
|
||||
"Set a PROPERTY for CATEGORY MODULE to VALUE. PLIST should be additional pairs
|
||||
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)))
|
||||
|
||||
(defun doom-module-set (category module &rest plist)
|
||||
"Enables a module by adding it to `doom-modules'.
|
||||
|
||||
CATEGORY is a keyword, module is a symbol, PLIST is a plist that accepts the
|
||||
following properties:
|
||||
|
||||
:flags [SYMBOL LIST] list of enabled category flags
|
||||
:path [STRING] path to category root directory
|
||||
|
||||
Example:
|
||||
(doom-module-set :lang 'haskell :flags '(+intero))"
|
||||
(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
|
||||
MODULE (symbol).
|
||||
|
||||
If the category isn't enabled this will always return nil. For finding disabled
|
||||
modules use `doom-module-locate-path'."
|
||||
(let ((path (doom-module-get category module :path)))
|
||||
(if file
|
||||
(let (file-name-handler-alist)
|
||||
(expand-file-name file path))
|
||||
path)))
|
||||
|
||||
(defun doom-module-locate-path (category &optional module file)
|
||||
"Searches `doom-modules-dirs' to find the path to a module.
|
||||
|
||||
CATEGORY is a keyword (e.g. :lang) and MODULE is a symbol (e.g. 'python). FILE
|
||||
is a string that will be appended to the resulting path. If no path exists, this
|
||||
returns nil, otherwise an absolute path.
|
||||
|
||||
This doesn't require modules to be enabled. For enabled modules us
|
||||
`doom-module-path'."
|
||||
(when (keywordp category)
|
||||
(setq category (doom-keyword-name category)))
|
||||
(when (and module (symbolp module))
|
||||
(setq module (symbol-name module)))
|
||||
(cl-loop with file-name-handler-alist = nil
|
||||
for default-directory in doom-modules-dirs
|
||||
for path = (concat category "/" module "/" file)
|
||||
if (file-exists-p path)
|
||||
return (expand-file-name path)))
|
||||
|
||||
(defun doom-module-from-path (&optional path enabled-only)
|
||||
"Returns a cons cell (CATEGORY . MODULE) derived from PATH (a file path).
|
||||
If ENABLED-ONLY, return nil if the containing module isn't enabled."
|
||||
(if (null path)
|
||||
(if doom--current-module
|
||||
(if enabled-only
|
||||
(and (doom-module-p (car doom--current-module)
|
||||
(cdr doom--current-module))
|
||||
doom--current-module)
|
||||
doom--current-module)
|
||||
(ignore-errors
|
||||
(doom-module-from-path (file!))))
|
||||
(let* ((file-name-handler-alist nil)
|
||||
(path (file-truename (or path (file!)))))
|
||||
(save-match-data
|
||||
(cond ((string-match "/modules/\\([^/]+\\)/\\([^/]+\\)\\(?:/.*\\)?$" path)
|
||||
(when-let* ((category (doom-keyword-intern (match-string 1 path)))
|
||||
(module (intern (match-string 2 path))))
|
||||
(and (or (null enabled-only)
|
||||
(doom-module-p category module))
|
||||
(cons category module))))
|
||||
((file-in-directory-p path doom-core-dir)
|
||||
(cons :core (intern (file-name-base path))))
|
||||
((file-in-directory-p path doom-private-dir)
|
||||
(cons :private (intern (file-name-base path)))))))))
|
||||
|
||||
(defun doom-module-load-path (&optional module-dirs)
|
||||
"Return a list of file paths to activated modules.
|
||||
|
||||
The list is in no particular order and its file paths are absolute. If
|
||||
MODULE-DIRS is non-nil, include all modules (even disabled ones) available in
|
||||
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)
|
||||
collect (plist-get plist :path)))
|
||||
nil))
|
||||
|
||||
(defun doom-modules (&optional refresh-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)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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))
|
||||
|
||||
(with-eval-after-load 'use-package-core
|
||||
;; Macros are already fontified, no need for this
|
||||
(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
|
||||
;; so we don't have to load `auto-minor-mode' so early.
|
||||
(dolist (keyword '(:minor :magic-minor))
|
||||
(setq use-package-keywords
|
||||
(use-package-list-insert keyword use-package-keywords :commands)))
|
||||
|
||||
(defalias 'use-package-normalize/:minor #'use-package-normalize-mode)
|
||||
(defun use-package-handler/:minor (name _ arg rest state)
|
||||
(use-package-handle-mode name 'auto-minor-mode-alist arg rest state))
|
||||
|
||||
(defalias 'use-package-normalize/:magic-minor #'use-package-normalize-mode)
|
||||
(defun use-package-handler/:magic-minor (name _ arg rest state)
|
||||
(use-package-handle-mode name 'auto-minor-mode-magic-alist arg rest state))
|
||||
|
||||
;; Adds two keywords to `use-package' to expand its lazy-loading capabilities:
|
||||
;;
|
||||
;; :after-call SYMBOL|LIST
|
||||
;; :defer-incrementally SYMBOL|LIST|t
|
||||
;;
|
||||
;; Check out `use-package!'s documentation for more about these two.
|
||||
(dolist (keyword '(:defer-incrementally :after-call))
|
||||
(push keyword use-package-deferring-keywords)
|
||||
(setq use-package-keywords
|
||||
(use-package-list-insert keyword use-package-keywords :after)))
|
||||
|
||||
(defalias 'use-package-normalize/:defer-incrementally #'use-package-normalize-symlist)
|
||||
(defun use-package-handler/:defer-incrementally (name _keyword targets rest state)
|
||||
(use-package-concat
|
||||
`((doom-load-packages-incrementally
|
||||
',(if (equal targets '(t))
|
||||
(list name)
|
||||
(append targets (list name)))))
|
||||
(use-package-process-keywords name rest state)))
|
||||
|
||||
(defalias 'use-package-normalize/:after-call #'use-package-normalize-symlist)
|
||||
(defun use-package-handler/:after-call (name _keyword hooks rest state)
|
||||
(if (plist-get state :demand)
|
||||
(use-package-process-keywords name rest state)
|
||||
(let ((fn (make-symbol (format "doom--after-call-%s-h" name))))
|
||||
(use-package-concat
|
||||
`((fset ',fn
|
||||
(lambda (&rest _)
|
||||
(doom-log "Loading deferred package %s from %s" ',name ',fn)
|
||||
(condition-case e
|
||||
;; If `default-directory' is a directory that doesn't
|
||||
;; exist or is unreadable, Emacs throws up file-missing
|
||||
;; errors, so we set it to a directory we know exists and
|
||||
;; is readable.
|
||||
(let ((default-directory doom-emacs-dir))
|
||||
(require ',name))
|
||||
((debug error)
|
||||
(message "Failed to load deferred package %s: %s" ',name e)))
|
||||
(when-let (deferral-list (assq ',name doom--deferred-packages-alist))
|
||||
(dolist (hook (cdr deferral-list))
|
||||
(advice-remove hook #',fn)
|
||||
(remove-hook hook #',fn))
|
||||
(delq! deferral-list doom--deferred-packages-alist)
|
||||
(unintern ',fn nil)))))
|
||||
(let (forms)
|
||||
(dolist (hook hooks forms)
|
||||
(push (if (string-match-p "-\\(?:functions\\|hook\\)$" (symbol-name hook))
|
||||
`(add-hook ',hook #',fn)
|
||||
`(advice-add #',hook :before #',fn))
|
||||
forms)))
|
||||
`((unless (assq ',name doom--deferred-packages-alist)
|
||||
(push '(,name) doom--deferred-packages-alist))
|
||||
(nconc (assq ',name doom--deferred-packages-alist)
|
||||
'(,@hooks)))
|
||||
(use-package-process-keywords name rest state))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Module config macros
|
||||
|
||||
(put :if 'lisp-indent-function 2)
|
||||
(put :when 'lisp-indent-function 'defun)
|
||||
(put :unless 'lisp-indent-function 'defun)
|
||||
|
||||
(defmacro doom! (&rest modules)
|
||||
"Bootstraps DOOM Emacs and its modules.
|
||||
|
||||
If the first item in MODULES doesn't satisfy `keywordp', MODULES is evaluated,
|
||||
otherwise, MODULES is a multiple-property list (a plist where each key can have
|
||||
multiple, linear values).
|
||||
|
||||
The bootstrap process involves making sure the essential directories exist, core
|
||||
packages are installed, `doom-autoload-file' is loaded, `doom-packages-file'
|
||||
cache exists (and is loaded) and, finally, loads your private init.el (which
|
||||
should contain your `doom!' block).
|
||||
|
||||
The overall load order of Doom is as follows:
|
||||
|
||||
~/.emacs.d/init.el
|
||||
~/.emacs.d/core/core.el
|
||||
$DOOMDIR/init.el
|
||||
{$DOOMDIR,~/.emacs.d}/modules/*/*/init.el
|
||||
`doom-before-init-modules-hook'
|
||||
{$DOOMDIR,~/.emacs.d}/modules/*/*/config.el
|
||||
`doom-init-modules-hook'
|
||||
$DOOMDIR/config.el
|
||||
`doom-after-init-modules-hook'
|
||||
`after-init-hook'
|
||||
`emacs-startup-hook'
|
||||
`window-setup-hook'
|
||||
|
||||
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
|
||||
for a list of all recognized module trees. Order defines precedence (from most
|
||||
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)))
|
||||
|
||||
(defvar doom-disabled-packages)
|
||||
(defmacro use-package! (name &rest plist)
|
||||
"Declares and configures a package.
|
||||
|
||||
This is a thin wrapper around `use-package', and is ignored if the NAME package
|
||||
is disabled by the user (with `package!').
|
||||
|
||||
See `use-package' to see what properties can be provided. Doom adds support for
|
||||
two extra properties:
|
||||
|
||||
:after-call SYMBOL|LIST
|
||||
Takes a symbol or list of symbols representing functions or hook variables.
|
||||
The first time any of these functions or hooks are executed, the package is
|
||||
loaded.
|
||||
|
||||
:defer-incrementally SYMBOL|LIST|t
|
||||
Takes a symbol or list of symbols representing packages that will be loaded
|
||||
incrementally at startup before this one. This is helpful for large packages
|
||||
like magit or org, which load a lot of dependencies on first load. This lets
|
||||
you load them piece-meal during idle periods, so that when you finally do need
|
||||
the package, it'll load quicker.
|
||||
|
||||
NAME is implicitly added if this property is present and non-nil. No need to
|
||||
specify it. A value of `t' implies NAME."
|
||||
(declare (indent 1))
|
||||
(unless (or (memq name doom-disabled-packages)
|
||||
;; At compile-time, use-package will forcibly load packages to
|
||||
;; prevent compile-time errors. However, if a Doom user has
|
||||
;; disabled packages you get file-missing package errors, so it's
|
||||
;; necessary to check for packages at compile time:
|
||||
(and (bound-and-true-p byte-compile-current-file)
|
||||
(not (locate-library (symbol-name name)))))
|
||||
`(use-package ,name ,@plist)))
|
||||
|
||||
(defmacro use-package-hook! (package when &rest body)
|
||||
"Reconfigures a package's `use-package!' block.
|
||||
|
||||
This macro must be used *before* PACKAGE's `use-package!' block. Often, this
|
||||
means using it from your DOOMDIR/init.el.
|
||||
|
||||
Under the hood, this uses use-package's `use-package-inject-hooks'.
|
||||
|
||||
PACKAGE is a symbol; the package's name.
|
||||
WHEN should be one of the following:
|
||||
:pre-init :post-init :pre-config :post-config
|
||||
|
||||
WARNINGS:
|
||||
- The use of this macro is more often than not a code smell. Use it as last
|
||||
resort. There is almost always a better alternative.
|
||||
- If you are using this solely for :post-config, stop! `after!' is much better.
|
||||
- If :pre-init or :pre-config hooks return nil, the original `use-package!''s
|
||||
:init/:config block (respectively) is overwritten, so remember to have them
|
||||
return non-nil (or exploit that to overwrite Doom's config)."
|
||||
(declare (indent defun))
|
||||
(unless (memq when '(:pre-init :post-init :pre-config :post-config))
|
||||
(error "'%s' isn't a valid hook for use-package-hook!" when))
|
||||
`(progn
|
||||
(setq use-package-inject-hooks t)
|
||||
(add-hook ',(intern (format "use-package--%s--%s-hook"
|
||||
package
|
||||
(substring (symbol-name when) 1)))
|
||||
(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.
|
||||
|
||||
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:
|
||||
|
||||
: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)"
|
||||
(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)))))
|
||||
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
|
||||
329
.emacs.d/core/core-packages.el
Normal file
329
.emacs.d/core/core-packages.el
Normal file
@@ -0,0 +1,329 @@
|
||||
;;; 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
|
||||
;; 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.
|
||||
;;
|
||||
;; 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).
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; + `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
|
||||
;; 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
|
||||
;; versions. There's also 'bin/doom update' for updating only your packages.
|
||||
;;
|
||||
;; How this works is: the system reads packages.el files located in each
|
||||
;; activated module, your private directory (`doom-private-dir'), and one in
|
||||
;; `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.")
|
||||
|
||||
(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!'.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Package managers
|
||||
|
||||
;; 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
|
||||
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
|
||||
;; than pulled, so packages are often out of date with upstream.
|
||||
package-archives
|
||||
(let ((proto (if gnutls-verify-error "https" "http")))
|
||||
`(("gnu" . ,(concat proto "://elpa.gnu.org/packages/"))
|
||||
("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)))
|
||||
|
||||
;; Refresh package.el the first time you call `package-install'
|
||||
(add-transient-hook! 'package-install (package-refresh-contents))
|
||||
|
||||
;;; 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.
|
||||
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.
|
||||
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)
|
||||
|
||||
(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))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrapper
|
||||
|
||||
(defun doom-initialize-packages (&optional force-p)
|
||||
"Ensures that Doom's package system and straight.el are initialized.
|
||||
|
||||
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))
|
||||
(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))))
|
||||
|
||||
(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))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Module package macros
|
||||
|
||||
(cl-defmacro package!
|
||||
(name &rest plist &key built-in recipe ignore _disable _freeze)
|
||||
"Declares a package and how to install it (if applicable).
|
||||
|
||||
This macro is declarative and does not load nor install packages. It is used to
|
||||
populate `doom-packages' with metadata about the packages Doom needs to keep
|
||||
track of.
|
||||
|
||||
Only use this macro in a module's packages.el file.
|
||||
|
||||
Accepts the following properties:
|
||||
|
||||
:recipe RECIPE
|
||||
Takes a MELPA-style recipe (see `quelpa-recipe' in `quelpa' for an example);
|
||||
for packages to be installed from external sources.
|
||||
:disable BOOL
|
||||
Do not install or update this package AND disable all of its `use-package!'
|
||||
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.
|
||||
|
||||
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)))
|
||||
(when built-in
|
||||
(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))))
|
||||
(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)
|
||||
nil))))
|
||||
|
||||
(doplist! ((prop val) (list ,@plist) plist)
|
||||
(unless (null val)
|
||||
(plist-put! plist prop val)))
|
||||
|
||||
;; Some basic key validation; error if you're not using a valid key
|
||||
(condition-case e
|
||||
(cl-destructuring-bind
|
||||
(&key _local-repo _files _flavor _no-build
|
||||
_type _repo _host _branch _remote _nonrecursive _fork _depth)
|
||||
(plist-get plist :recipe))
|
||||
(error
|
||||
(signal 'doom-package-error
|
||||
(cons ,(symbol-name name)
|
||||
(error-message-string e)))))
|
||||
|
||||
(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)))
|
||||
|
||||
(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))))
|
||||
|
||||
(provide 'core-packages)
|
||||
;;; core-packages.el ends here
|
||||
282
.emacs.d/core/core-projects.el
Normal file
282
.emacs.d/core/core-projects.el
Normal file
@@ -0,0 +1,282 @@
|
||||
;;; core-projects.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-projectile-cache-limit 25000
|
||||
"If any project cache surpasses this many files it is purged when quitting
|
||||
Emacs.")
|
||||
|
||||
(defvar doom-projectile-cache-blacklist '("~" "/tmp" "/")
|
||||
"Directories that should never be cached.")
|
||||
|
||||
(defvar doom-projectile-cache-purge-non-projects nil
|
||||
"If non-nil, non-projects are purged from the cache on `kill-emacs-hook'.")
|
||||
|
||||
(defvar doom-projectile-fd-binary
|
||||
(or (cl-find-if #'executable-find '("fdfind" "fd"))
|
||||
"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-globally-ignored-files '(".DS_Store" "Icon
|
||||
" "TAGS")
|
||||
projectile-globally-ignored-file-suffixes '(".elc" ".pyc" ".o")
|
||||
projectile-kill-buffers-filter 'kill-only-files
|
||||
projectile-known-projects-file (concat doom-cache-dir "projectile.projects")
|
||||
projectile-ignored-projects '("~/" "/tmp"))
|
||||
|
||||
(global-set-key [remap evil-jump-to-tag] #'projectile-find-tag)
|
||||
(global-set-key [remap find-tag] #'projectile-find-tag)
|
||||
|
||||
:config
|
||||
(projectile-mode +1)
|
||||
|
||||
;; 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
|
||||
(append '(".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
|
||||
"_FOSSIL_")) ; Fossil VCS root DB on Windows
|
||||
(when (executable-find "bzr")
|
||||
'(".bzr")) ; Bazaar VCS root dir
|
||||
(when (executable-find "darcs")
|
||||
'("_darcs"))) ; Darcs VCS root dir
|
||||
;; This will be filled by other modules. We build this list manually so
|
||||
;; projectile doesn't perform so many file checks every time it resolves
|
||||
;; a project's root -- particularly when a file has no project.
|
||||
projectile-project-root-files '("TAGS")
|
||||
projectile-project-root-files-top-down-recurring '(".svn" "Makefile"))
|
||||
|
||||
(push (abbreviate-file-name doom-local-dir) projectile-globally-ignored-directories)
|
||||
|
||||
;; Disable commands that won't work, as is, and that Doom already provides a
|
||||
;; better alternative for.
|
||||
(put 'projectile-ag 'disabled "Use +{ivy,helm}/project-search instead")
|
||||
(put 'projectile-ripgrep 'disabled "Use +{ivy,helm}/project-search instead")
|
||||
(put 'projectile-grep 'disabled "Use +{ivy,helm}/project-search instead")
|
||||
|
||||
;; Treat current directory in dired as a "file in a project" and track it
|
||||
(add-hook 'dired-before-readin-hook #'projectile-track-known-projects-find-file-hook)
|
||||
|
||||
;; Accidentally indexing big directories like $HOME or / will massively bloat
|
||||
;; projectile's cache (into the hundreds of MBs). This purges those entries
|
||||
;; when exiting Emacs to prevent slowdowns/freezing when cache files are
|
||||
;; loaded or written to.
|
||||
(add-hook! 'kill-emacs-hook
|
||||
(defun doom-cleanup-project-cache-h ()
|
||||
"Purge projectile cache entries that:
|
||||
|
||||
a) have too many files (see `doom-projectile-cache-limit'),
|
||||
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)
|
||||
doom-interactive-mode)
|
||||
(cl-loop with blacklist = (mapcar #'file-truename doom-projectile-cache-blacklist)
|
||||
for proot in (hash-table-keys projectile-projects-cache)
|
||||
if (or (not (stringp proot))
|
||||
(>= (length (gethash proot projectile-projects-cache))
|
||||
doom-projectile-cache-limit)
|
||||
(member (substring proot 0 -1) blacklist)
|
||||
(and doom-projectile-cache-purge-non-projects
|
||||
(not (doom-project-p proot))))
|
||||
do (doom-log "Removed %S from projectile cache" proot)
|
||||
and do (remhash proot projectile-projects-cache)
|
||||
and do (remhash proot projectile-projects-cache-time)
|
||||
and do (remhash proot projectile-project-type-cache))
|
||||
(projectile-serialize-cache))))
|
||||
|
||||
;; It breaks projectile's project root resolution if HOME is a project (e.g.
|
||||
;; it's a git repo). In that case, we disable bottom-up root searching to
|
||||
;; prevent issues. This makes project resolution a little slower and less
|
||||
;; accurate in some cases.
|
||||
(let ((default-directory "~"))
|
||||
(when (cl-find-if #'projectile-file-exists-p
|
||||
projectile-project-root-files-bottom-up)
|
||||
(doom-log "HOME appears to be a project. Disabling bottom-up root search.")
|
||||
(setq projectile-project-root-files
|
||||
(append projectile-project-root-files-bottom-up
|
||||
projectile-project-root-files)
|
||||
projectile-project-root-files-bottom-up nil)))
|
||||
|
||||
(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)
|
||||
projectile-git-command projectile-generic-command
|
||||
projectile-git-submodule-command nil
|
||||
;; ensure Windows users get fd's benefits
|
||||
projectile-indexing-method 'alien))
|
||||
|
||||
;; 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)))
|
||||
projectile-git-command projectile-generic-command
|
||||
projectile-git-submodule-command nil
|
||||
;; ensure Windows users get rg's benefits
|
||||
projectile-indexing-method 'alien))
|
||||
|
||||
;; Fix breakage on windows in git projects with submodules, since Windows
|
||||
;; doesn't have tr
|
||||
((not (executable-find "tr"))
|
||||
(setq projectile-git-submodule-command nil)))
|
||||
|
||||
(defadvice! doom--projectile-cache-timers-a ()
|
||||
"Persist `projectile-projects-cache-time' across sessions, so that
|
||||
`projectile-files-cache-expire' checks won't reset when restarting Emacs."
|
||||
:before #'projectile-serialize-cache
|
||||
(projectile-serialize projectile-projects-cache-time doom-projectile-cache-timer-file))
|
||||
;; Restore it
|
||||
(when (file-readable-p doom-projectile-cache-timer-file)
|
||||
(setq projectile-projects-cache-time
|
||||
(projectile-unserialize doom-projectile-cache-timer-file)))
|
||||
|
||||
(defadvice! doom--projectile-default-generic-command-a (orig-fn &rest args)
|
||||
"If projectile can't tell what kind of project you're in, it issues an error
|
||||
when using many of projectile's command, e.g. `projectile-compile-command',
|
||||
`projectile-run-project', `projectile-test-project', and
|
||||
`projectile-configure-project', for instance.
|
||||
|
||||
This suppresses the error so these commands will still run, but prompt you for
|
||||
the command instead."
|
||||
:around #'projectile-default-generic-command
|
||||
(ignore-errors (apply orig-fn args)))
|
||||
|
||||
;; Projectile root-searching functions can cause an infinite loop on TRAMP
|
||||
;; connections, so disable them.
|
||||
;; TODO Is this still necessary?
|
||||
(defadvice! doom--projectile-locate-dominating-file-a (orig-fn file name)
|
||||
"Don't traverse the file system if on a remote connection."
|
||||
:around #'projectile-locate-dominating-file
|
||||
(when (and (stringp file)
|
||||
(not (file-remote-p file nil t)))
|
||||
(funcall orig-fn file name))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Project-based minor modes
|
||||
|
||||
(defvar doom-project-hook nil
|
||||
"Hook run when a project is enabled. The name of the project's mode and its
|
||||
state are passed in.")
|
||||
|
||||
(cl-defmacro def-project-mode! (name &key
|
||||
modes
|
||||
files
|
||||
when
|
||||
match
|
||||
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
|
||||
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.
|
||||
|
||||
:modes MODES -- if buffers are derived from MODES (one or a list of symbols).
|
||||
|
||||
:files FILES -- if project contains FILES; takes a string or a form comprised
|
||||
of nested (and ...) and/or (or ...) forms. Each path is relative to the
|
||||
project root, however, if prefixed with a '.' or '..', it is relative to the
|
||||
current buffer.
|
||||
|
||||
:match REGEXP -- if file name matches REGEXP
|
||||
|
||||
:when PREDICATE -- if PREDICATE returns true (can be a form or the symbol of a
|
||||
function)
|
||||
|
||||
:add-hooks HOOKS -- HOOKS is a list of hooks to add this mode's hook.
|
||||
|
||||
:on-load FORM -- FORM to run the first time this project mode is enabled.
|
||||
|
||||
:on-enter FORM -- FORM is run each time the mode is activated.
|
||||
|
||||
:on-exit FORM -- FORM is run each time the mode is disabled.
|
||||
|
||||
Relevant: `doom-project-hook'."
|
||||
(declare (indent 1))
|
||||
(let ((init-var (intern (format "%s-init" name))))
|
||||
(macroexp-progn
|
||||
(append
|
||||
(when on-load
|
||||
`((defvar ,init-var nil)))
|
||||
`((define-minor-mode ,name
|
||||
"A project minor mode generated by `def-project-mode!'."
|
||||
:init-value nil
|
||||
:lighter ""
|
||||
:keymap (make-sparse-keymap)
|
||||
(if (not ,name)
|
||||
,on-exit
|
||||
(run-hook-with-args 'doom-project-hook ',name ,name)
|
||||
,(when on-load
|
||||
`(unless ,init-var
|
||||
,on-load
|
||||
(setq ,init-var t)))
|
||||
,on-enter))
|
||||
(dolist (hook ,add-hooks)
|
||||
(add-hook ',(intern (format "%s-hook" name)) hook)))
|
||||
(cond ((or files modes when)
|
||||
(cl-check-type files (or null list string))
|
||||
(let ((fn
|
||||
`(lambda ()
|
||||
(and (not (bound-and-true-p ,name))
|
||||
(and buffer-file-name (not (file-remote-p buffer-file-name nil t)))
|
||||
,(or (null match)
|
||||
`(if buffer-file-name (string-match-p ,match buffer-file-name)))
|
||||
,(or (null files)
|
||||
;; Wrap this in `eval' to prevent eager expansion
|
||||
;; of `project-file-exists-p!' from pulling in
|
||||
;; autoloaded files prematurely.
|
||||
`(eval
|
||||
'(project-file-exists-p!
|
||||
,(if (stringp (car files)) (cons 'and files) files))))
|
||||
,(or when t)
|
||||
(,name 1)))))
|
||||
(if modes
|
||||
`((dolist (mode ,modes)
|
||||
(let ((hook-name
|
||||
(intern (format "doom--enable-%s%s-h" ',name
|
||||
(if (eq mode t) "" (format "-in-%s" mode))))))
|
||||
(fset hook-name #',fn)
|
||||
(if (eq mode t)
|
||||
(add-to-list 'auto-minor-mode-magic-alist (cons hook-name #',name))
|
||||
(add-hook (intern (format "%s-hook" mode)) hook-name)))))
|
||||
`((add-hook 'change-major-mode-after-body-hook #',fn)))))
|
||||
(match
|
||||
`((add-to-list 'auto-minor-mode-alist (cons ,match #',name)))))))))
|
||||
|
||||
(provide 'core-projects)
|
||||
649
.emacs.d/core/core-ui.el
Normal file
649
.emacs.d/core/core-ui.el
Normal file
@@ -0,0 +1,649 @@
|
||||
;;; core-ui.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;;; Variables
|
||||
|
||||
(defvar doom-init-theme-p nil
|
||||
"If non-nil, a theme as been loaded.")
|
||||
|
||||
(defvar doom-theme nil
|
||||
"A symbol representing the Emacs theme to load at startup.
|
||||
|
||||
This is changed by `load-theme'.")
|
||||
|
||||
(defvar doom-font nil
|
||||
"The default font to use.
|
||||
|
||||
Expects either a `font-spec', font object, an XFT font string or an XLFD font
|
||||
string.
|
||||
|
||||
This affects the `default' and `fixed-pitch' faces.
|
||||
|
||||
Examples:
|
||||
(setq doom-font (font-spec :family \"Fira Mono\" :size 12))
|
||||
(setq doom-font \"Terminus (TTF):pixelsize=12:antialias=off\")")
|
||||
|
||||
(defvar doom-variable-pitch-font nil
|
||||
"The font to use for variable-pitch text.
|
||||
|
||||
Expects either a `font-spec', font object, a XFT font string or XLFD string. See
|
||||
`doom-font' for examples.
|
||||
|
||||
It is recommended you don't set specify a font-size, as to inherit `doom-font's
|
||||
size.")
|
||||
|
||||
(defvar doom-serif-font nil
|
||||
"The default font to use for the `fixed-pitch-serif' face.
|
||||
|
||||
Expects either a `font-spec', font object, a XFT font string or XLFD string. See
|
||||
`doom-font' for examples.
|
||||
|
||||
It is recommended you don't set specify a font-size, as to inherit `doom-font's
|
||||
size.")
|
||||
|
||||
(defvar doom-unicode-font
|
||||
(if IS-MAC
|
||||
(font-spec :family "Apple Color Emoji")
|
||||
(font-spec :family "Symbola"))
|
||||
"Fallback font for unicode glyphs.
|
||||
|
||||
It defaults to Apple Color Emoji on MacOS and Symbola on Linux. Expects either a
|
||||
`font-spec', font object, a XFT font string or XLFD string. See `doom-font' for
|
||||
examples.
|
||||
|
||||
It is recommended you don't set specify a font-size, as to inherit `doom-font's
|
||||
size.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Custom hooks
|
||||
|
||||
(defvar doom-init-ui-hook nil
|
||||
"List of hooks to run when the UI has been initialized.")
|
||||
|
||||
(defvar doom-load-theme-hook nil
|
||||
"Hook run after the theme is loaded with `load-theme' or reloaded with
|
||||
`doom/reload-theme'.")
|
||||
|
||||
(defvar doom-switch-buffer-hook nil
|
||||
"A list of hooks run after changing the current buffer.")
|
||||
|
||||
(defvar doom-switch-window-hook nil
|
||||
"A list of hooks run after changing the focused windows.")
|
||||
|
||||
(defvar doom-switch-frame-hook nil
|
||||
"A list of hooks run after changing the focused frame.")
|
||||
|
||||
(defvar doom-inhibit-switch-buffer-hooks nil
|
||||
"Letvar for inhibiting `doom-switch-buffer-hook'. Do not set this directly.")
|
||||
(defvar doom-inhibit-switch-window-hooks nil
|
||||
"Letvar for inhibiting `doom-switch-window-hook'. Do not set this directly.")
|
||||
(defvar doom-inhibit-switch-frame-hooks nil
|
||||
"Letvar for inhibiting `doom-switch-frame-hook'. Do not set this directly.")
|
||||
|
||||
(defvar doom--last-window nil)
|
||||
(defvar doom--last-frame nil)
|
||||
|
||||
(defun doom-run-switch-window-hooks-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))))))
|
||||
|
||||
(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))
|
||||
(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)))))
|
||||
|
||||
(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)))))
|
||||
|
||||
(defun doom-protect-fallback-buffer-h ()
|
||||
"Don't kill the scratch buffer. Meant for `kill-buffer-query-functions'."
|
||||
(not (eq (current-buffer) (doom-fallback-buffer))))
|
||||
|
||||
(defun doom-highlight-non-default-indentation-h ()
|
||||
"Highlight whitespace that doesn't match your `indent-tabs-mode' setting.
|
||||
|
||||
e.g. If you indent with spaces by default, tabs will be highlighted. If you
|
||||
indent with tabs, spaces at BOL are highlighted.
|
||||
|
||||
Does nothing if `whitespace-mode' is already active or the current buffer is
|
||||
read-only or not file-visiting."
|
||||
(unless (or (eq major-mode 'fundamental-mode)
|
||||
buffer-read-only
|
||||
(null buffer-file-name))
|
||||
(require 'whitespace)
|
||||
(set (make-local-variable 'whitespace-style)
|
||||
(let ((style (if indent-tabs-mode '(indentation) '(tabs tab-mark))))
|
||||
(if whitespace-mode
|
||||
(cl-union style whitespace-active-style)
|
||||
style)))
|
||||
(cl-pushnew 'face whitespace-style)
|
||||
(whitespace-mode +1)))
|
||||
|
||||
|
||||
;;
|
||||
;;; General UX
|
||||
|
||||
;; Simpler confirmation prompt when killing Emacs
|
||||
(setq confirm-kill-emacs #'doom-quit-p)
|
||||
|
||||
(setq uniquify-buffer-name-style 'forward
|
||||
;; no beeping or blinking please
|
||||
ring-bell-function #'ignore
|
||||
visible-bell nil)
|
||||
|
||||
;; middle-click paste at point, not at click
|
||||
(setq mouse-yank-at-point t)
|
||||
|
||||
;; Enable mouse in terminal Emacs
|
||||
(add-hook 'tty-setup-hook #'xterm-mouse-mode)
|
||||
|
||||
|
||||
;;
|
||||
;;; Scrolling
|
||||
|
||||
(setq hscroll-margin 2
|
||||
hscroll-step 1
|
||||
scroll-conservatively 10
|
||||
scroll-margin 0
|
||||
scroll-preserve-screen-position t
|
||||
;; Reduce cursor lag by a tiny bit by not auto-adjusting `window-vscroll'
|
||||
;; for tall lines.
|
||||
auto-window-vscroll nil
|
||||
;; mouse
|
||||
mouse-wheel-scroll-amount '(5 ((shift) . 2))
|
||||
mouse-wheel-progressive-speed nil) ; don't accelerate scrolling
|
||||
|
||||
;; Remove hscroll-margin in shells, otherwise it causes jumpiness
|
||||
(setq-hook! '(eshell-mode-hook term-mode-hook) hscroll-margin 0)
|
||||
|
||||
(when IS-MAC
|
||||
;; sane trackpad/mouse scroll settings
|
||||
(setq mac-redisplay-dont-reset-vscroll t
|
||||
mac-mouse-wheel-smooth-scroll nil))
|
||||
|
||||
|
||||
;;
|
||||
;;; Cursor
|
||||
|
||||
;; Don't blink the cursor, it's too distracting.
|
||||
(blink-cursor-mode -1)
|
||||
|
||||
;; Don't blink the paren matching the one at point, it's too distracting.
|
||||
(setq blink-matching-paren nil)
|
||||
|
||||
(setq visible-cursor nil)
|
||||
|
||||
;; Don't stretch the cursor to fit wide characters, it is disorienting,
|
||||
;; especially for tabs.
|
||||
(setq x-stretch-cursor nil)
|
||||
|
||||
|
||||
;;
|
||||
;;; Buffers
|
||||
|
||||
;; 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.
|
||||
|
||||
Advice for `kill-current-buffer'. If in a dedicated window, delete it. If there
|
||||
are no real buffers left OR if all remaining buffers are visible in other
|
||||
windows, switch to `doom-fallback-buffer'. Otherwise, delegate to original
|
||||
`kill-current-buffer'."
|
||||
:before-until #'kill-current-buffer
|
||||
(let ((buf (current-buffer)))
|
||||
(cond ((window-dedicated-p)
|
||||
(delete-window)
|
||||
t)
|
||||
((eq buf (doom-fallback-buffer))
|
||||
(message "Can't kill the fallback buffer.")
|
||||
t)
|
||||
((doom-real-buffer-p buf)
|
||||
(if (and buffer-file-name
|
||||
(buffer-modified-p buf)
|
||||
(not (y-or-n-p
|
||||
(format "Buffer %s is modified; kill anyway?" buf))))
|
||||
(message "Aborted")
|
||||
(set-buffer-modified-p nil)
|
||||
(let (buffer-list-update-hook)
|
||||
(when (or ;; if there aren't more real buffers than visible buffers,
|
||||
;; then there are no real, non-visible buffers left.
|
||||
(not (cl-set-difference (doom-real-buffer-list)
|
||||
(doom-visible-buffers)))
|
||||
;; if we end up back where we start (or previous-buffer
|
||||
;; returns nil), we have nowhere left to go
|
||||
(memq (switch-to-prev-buffer nil t) (list buf 'nil)))
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(unless (delq (selected-window) (get-buffer-window-list buf nil t))
|
||||
(kill-buffer buf)))
|
||||
(run-hooks 'buffer-list-update-hook))
|
||||
t))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Fringes
|
||||
|
||||
;; Reduce the clutter in the fringes; we'd like to reserve that space for more
|
||||
;; useful information, like git-gutter and flycheck.
|
||||
(setq indicate-buffer-boundaries nil
|
||||
indicate-empty-lines nil)
|
||||
|
||||
;; remove continuation arrow on right fringe
|
||||
(delq! 'continuation fringe-indicator-alist 'assq)
|
||||
|
||||
|
||||
;;
|
||||
;;; Windows/frames
|
||||
|
||||
;; A simple frame title
|
||||
(setq frame-title-format '("%b – Doom Emacs")
|
||||
icon-title-format frame-title-format)
|
||||
|
||||
;; Don't resize emacs in steps, it 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))
|
||||
|
||||
;; Sets `ns-appearance' and `ns-transparent-titlebar' on GUI frames (and fixes
|
||||
;; mismatching text color in the frame title)
|
||||
(when IS-MAC
|
||||
;; Curse Lion and its sudden but inevitable fullscreen mode!
|
||||
;; NOTE Meaningless to railwaycat's emacs-mac build
|
||||
(setq ns-use-native-fullscreen nil
|
||||
;; Visit files opened outside of Emacs in existing frame, rather than a
|
||||
;; new one
|
||||
ns-pop-up-frames nil)
|
||||
|
||||
;; Sets ns-transparent-titlebar and ns-appearance frame parameters as is
|
||||
;; appropriate for the loaded 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)))))
|
||||
|
||||
;; The native border "consumes" a pixel of the fringe on righter-most splits,
|
||||
;; `window-divider' does not. Available since Emacs 25.1.
|
||||
(setq window-divider-default-places t
|
||||
window-divider-default-bottom-width 1
|
||||
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)
|
||||
|
||||
;; 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
|
||||
(when IS-LINUX
|
||||
(setq x-gtk-use-system-tooltips nil))
|
||||
|
||||
;; Favor vertical splits over horizontal ones
|
||||
(setq split-width-threshold 160
|
||||
split-height-threshold nil)
|
||||
|
||||
|
||||
;;
|
||||
;;; Minibuffer
|
||||
|
||||
;; Allow for minibuffer-ception. Sometimes we need another minibuffer command
|
||||
;; _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.
|
||||
(setq echo-keystrokes 0.02)
|
||||
|
||||
;; Expand the minibuffer to fit multi-line text displayed in the echo-area. This
|
||||
;; doesn't look too great with direnv, however...
|
||||
(setq resize-mini-windows 'grow-only
|
||||
;; But don't let the minibuffer grow beyond this size
|
||||
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)
|
||||
|
||||
;; Try really hard to keep the cursor from getting stuck in the read-only prompt
|
||||
;; portion of the minibuffer.
|
||||
(setq minibuffer-prompt-properties '(read-only t intangible t cursor-intangible t face minibuffer-prompt))
|
||||
(add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
|
||||
|
||||
|
||||
;;
|
||||
;;; Built-in packages
|
||||
|
||||
;;;###package ansi-color
|
||||
(setq ansi-color-for-comint-mode t)
|
||||
|
||||
|
||||
(after! compile
|
||||
(setq compilation-always-kill t ; kill compilation process before starting another
|
||||
compilation-ask-about-save nil ; save all buffers on `compile'
|
||||
compilation-scroll-output 'first-error)
|
||||
;; Handle ansi codes in compilation buffer
|
||||
(add-hook 'compilation-filter-hook #'doom-apply-ansi-color-to-compilation-buffer-h))
|
||||
|
||||
|
||||
(after! ediff
|
||||
(setq ediff-diff-options "-w" ; turn off whitespace checking
|
||||
ediff-split-window-function #'split-window-horizontally
|
||||
ediff-window-setup-function #'ediff-setup-windows-plain)
|
||||
|
||||
(defvar doom--ediff-saved-wconf nil)
|
||||
;; Restore window config after quitting ediff
|
||||
(add-hook! 'ediff-before-setup-hook
|
||||
(defun doom-ediff-save-wconf-h ()
|
||||
(setq doom--ediff-saved-wconf (current-window-configuration))))
|
||||
(add-hook! '(ediff-quit-hook ediff-suspend-hook) :append
|
||||
(defun doom-ediff-restore-wconf-h ()
|
||||
(when (window-configuration-p doom--ediff-saved-wconf)
|
||||
(set-window-configuration doom--ediff-saved-wconf)))))
|
||||
|
||||
|
||||
(use-package! goto-addr
|
||||
:hook (text-mode . goto-address-mode)
|
||||
:hook (prog-mode . goto-address-prog-mode)
|
||||
:config
|
||||
(define-key goto-address-highlight-keymap (kbd "RET") #'goto-address-at-point))
|
||||
|
||||
|
||||
(use-package! hl-line
|
||||
;; Highlights the current line
|
||||
:hook ((prog-mode text-mode conf-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))))))
|
||||
|
||||
|
||||
(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)
|
||||
(appendq! winner-boring-buffers
|
||||
'("*Compile-Log*" "*inferior-lisp*" "*Fuzzy Completions*"
|
||||
"*Apropos*" "*Help*" "*cvs*" "*Buffer List*" "*Ibuffer*"
|
||||
"*esh command on file*")))
|
||||
|
||||
|
||||
(use-package! paren
|
||||
;; highlight matching delimiters
|
||||
:after-call after-find-file doom-switch-buffer-hook
|
||||
: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))
|
||||
|
||||
|
||||
;;;###package whitespace
|
||||
(setq whitespace-line-column nil
|
||||
whitespace-style
|
||||
'(face indentation tabs tab-mark spaces space-mark newline newline-mark
|
||||
trailing lines-tail)
|
||||
whitespace-display-mappings
|
||||
'((tab-mark ?\t [?› ?\t])
|
||||
(newline-mark ?\n [?¬ ?\n])
|
||||
(space-mark ?\ [?·] [?.])))
|
||||
|
||||
|
||||
;;
|
||||
;;; Third party packages
|
||||
|
||||
(use-package! all-the-icons
|
||||
:commands (all-the-icons-octicon
|
||||
all-the-icons-faicon
|
||||
all-the-icons-fileicon
|
||||
all-the-icons-wicon
|
||||
all-the-icons-material
|
||||
all-the-icons-alltheicon)
|
||||
:init
|
||||
(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)
|
||||
"")))
|
||||
|
||||
;;;###package hide-mode-line-mode
|
||||
(add-hook! '(completion-list-mode-hook Man-mode-hook)
|
||||
#'hide-mode-line-mode)
|
||||
|
||||
;; Many major modes do no highlighting of number literals, so we do it for them
|
||||
(use-package! highlight-numbers
|
||||
:hook ((prog-mode conf-mode) . highlight-numbers-mode)
|
||||
:config (setq highlight-numbers-generic-regexp "\\_<[[:digit:]]+\\(?:\\.[0-9]*\\)?\\_>"))
|
||||
|
||||
;;;###package image
|
||||
(setq image-animate-loop t)
|
||||
|
||||
;;;###package rainbow-delimiters
|
||||
;; Helps us distinguish stacked delimiter pairs, especially in parentheses-drunk
|
||||
;; 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
|
||||
|
||||
;; Explicitly define a width to reduce computation
|
||||
(setq-default display-line-numbers-width 3)
|
||||
|
||||
;; Show absolute line numbers for narrowed regions
|
||||
(setq-default display-line-numbers-widen t)
|
||||
|
||||
;; Enable line numbers in most text-editing modes
|
||||
(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
|
||||
|
||||
;; Underline looks a bit better when drawn lower
|
||||
(setq x-underline-at-descent-line t)
|
||||
|
||||
(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)))))
|
||||
|
||||
(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-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)))
|
||||
((debug error)
|
||||
(if (string-prefix-p "Font not available: " (error-message-string e))
|
||||
(lwarn 'doom-ui :warning
|
||||
"Could not find the '%s' font on your system, falling back to system font"
|
||||
(font-get (caddr e) :family))
|
||||
(signal 'doom-error e)))))
|
||||
|
||||
(defun doom-init-theme-h (&optional frame)
|
||||
"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))
|
||||
(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--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."
|
||||
: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)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrap
|
||||
|
||||
(defun doom-init-ui-h ()
|
||||
"Initialize Doom's user interface by applying all its advice and hooks."
|
||||
(run-hook-wrapped 'doom-init-ui-hook #'doom-try-run-hook)
|
||||
|
||||
(add-hook 'kill-buffer-query-functions #'doom-protect-fallback-buffer-h)
|
||||
(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'
|
||||
(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))
|
||||
(advice-add fn :around #'doom-run-switch-to-next-prev-buffer-hooks-a))
|
||||
(dolist (fn '(switch-to-buffer display-buffer))
|
||||
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
|
||||
|
||||
;; Apply `doom-font' et co
|
||||
(add-hook 'doom-after-init-modules-hook #'doom-init-fonts-h)
|
||||
(add-hook 'doom-load-theme-hook #'doom-init-extra-fonts-h)
|
||||
|
||||
;; Apply `doom-theme'
|
||||
(add-hook (if (daemonp)
|
||||
'after-make-frame-functions
|
||||
'doom-init-ui-hook)
|
||||
#'doom-init-theme-h)
|
||||
|
||||
(add-hook 'window-setup-hook #'doom-init-ui-h)
|
||||
|
||||
|
||||
;;
|
||||
;;; 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")
|
||||
(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
|
||||
(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."
|
||||
(unless (frame-parameter nil 'parent-frame)
|
||||
(funcall orig-fn)))
|
||||
(add-function :around whitespace-enable-predicate #'doom-disable-whitespace-mode-in-childframes-a))
|
||||
|
||||
;; Don't display messages in the minibuffer when using the minibuffer
|
||||
(defmacro doom-silence-motion-key (command key)
|
||||
(let ((key-command (intern (format "doom/silent-%s" command))))
|
||||
`(progn
|
||||
(defun ,key-command ()
|
||||
(interactive)
|
||||
(ignore-errors (call-interactively ',command)))
|
||||
(define-key minibuffer-local-map (kbd ,key) #',key-command))))
|
||||
(doom-silence-motion-key backward-delete-char "<backspace>")
|
||||
(doom-silence-motion-key delete-char "<delete>")
|
||||
|
||||
(provide 'core-ui)
|
||||
;;; core-ui.el ends here
|
||||
553
.emacs.d/core/core.el
Normal file
553
.emacs.d/core/core.el
Normal file
@@ -0,0 +1,553 @@
|
||||
;;; 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))
|
||||
|
||||
(defconst doom-version "2.0.9"
|
||||
"Current version of Doom Emacs.")
|
||||
|
||||
(defconst EMACS27+ (> emacs-major-version 26))
|
||||
(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)))
|
||||
|
||||
;; 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)
|
||||
|
||||
;; Restore `file-name-handler-alist', because it is needed for handling
|
||||
;; encrypted or compressed files, among other things.
|
||||
(defun doom-reset-file-handler-alist-h ()
|
||||
(setq file-name-handler-alist doom--initial-file-name-handler-alist))
|
||||
(add-hook 'emacs-startup-hook #'doom-reset-file-handler-alist-h)
|
||||
|
||||
;; Load the bare necessities
|
||||
(require 'core-lib)
|
||||
|
||||
(autoload 'doom-initialize-packages "core-packages")
|
||||
|
||||
|
||||
;;
|
||||
;;; Global variables
|
||||
|
||||
(defvar doom-init-p nil
|
||||
"Non-nil if Doom has been initialized.")
|
||||
|
||||
(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)
|
||||
"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.")
|
||||
|
||||
(defvar doom-interactive-mode (not noninteractive)
|
||||
"If non-nil, Emacs is in interactive mode.")
|
||||
|
||||
;;; Directories/files
|
||||
(defconst doom-emacs-dir
|
||||
(eval-when-compile (file-truename user-emacs-directory))
|
||||
"The path to the currently loaded .emacs.d directory. Must end with a slash.")
|
||||
|
||||
(defconst doom-core-dir (concat doom-emacs-dir "core/")
|
||||
"The root directory of Doom's core files. Must end with a slash.")
|
||||
|
||||
(defconst doom-modules-dir (concat doom-emacs-dir "modules/")
|
||||
"The root directory for Doom's modules. Must end with a slash.")
|
||||
|
||||
(defconst doom-local-dir
|
||||
(if-let (localdir (getenv "DOOMLOCALDIR"))
|
||||
(expand-file-name (file-name-as-directory localdir))
|
||||
(concat doom-emacs-dir ".local/"))
|
||||
"Root directory for local storage.
|
||||
|
||||
Use this as a storage location for this system's installation of Doom Emacs.
|
||||
These files should not be shared across systems. By default, it is used by
|
||||
`doom-etc-dir' and `doom-cache-dir'. Must end with a slash.")
|
||||
|
||||
(defconst doom-etc-dir (concat doom-local-dir "etc/")
|
||||
"Directory for non-volatile local storage.
|
||||
|
||||
Use this for files that don't change much, like server binaries, external
|
||||
dependencies or long-term shared data. Must end with a slash.")
|
||||
|
||||
(defconst doom-cache-dir (concat doom-local-dir "cache/")
|
||||
"Directory for volatile local storage.
|
||||
|
||||
Use this for files that change often, like cache files. Must end with a slash.")
|
||||
|
||||
(defconst doom-docs-dir (concat doom-emacs-dir "docs/")
|
||||
"Where Doom's documentation files are stored. Must end with a slash.")
|
||||
|
||||
(defconst doom-private-dir
|
||||
(if-let (doomdir (getenv "DOOMDIR"))
|
||||
(expand-file-name (file-name-as-directory doomdir))
|
||||
(or (let ((xdgdir
|
||||
(expand-file-name "doom/"
|
||||
(or (getenv "XDG_CONFIG_HOME")
|
||||
"~/.config"))))
|
||||
(if (file-directory-p xdgdir) xdgdir))
|
||||
"~/.doom.d/"))
|
||||
"Where your private configuration is placed.
|
||||
|
||||
Defaults to ~/.config/doom, ~/.doom.d or the value of the DOOMDIR envvar;
|
||||
whichever is found first. Must end in a slash.")
|
||||
|
||||
(defconst doom-autoload-file (concat doom-local-dir "autoloads.el")
|
||||
"Where `doom-reload-core-autoloads' stores its core autoloads.
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
\(easily) be launched from the correct shell session (particularly for MacOS
|
||||
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-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)
|
||||
|
||||
|
||||
;;
|
||||
;;; 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)
|
||||
|
||||
;; UTF-8 as 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.
|
||||
(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
|
||||
(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!
|
||||
(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.
|
||||
(setq inhibit-startup-message t
|
||||
inhibit-startup-echo-area-message user-login-name
|
||||
inhibit-default-init t
|
||||
initial-major-mode 'fundamental-mode
|
||||
initial-scratch-message nil)
|
||||
(fset #'display-startup-echo-area-message #'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)
|
||||
|
||||
;; 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")
|
||||
tls-checktrust gnutls-verify-error
|
||||
tls-program '("gnutls-cli --x509cafile %t -p %p %h"
|
||||
;; compatibility fallbacks
|
||||
"gnutls-cli -p %p %h"
|
||||
"openssl s_client -connect %h:%p -no_ssl2 -no_ssl3 -ign_eof"))
|
||||
|
||||
;; Emacs stores authinfo in HOME and in plaintext. Let's not do that, mkay? This
|
||||
;; file usually 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)
|
||||
"~/.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'
|
||||
(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-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
|
||||
tramp-persistency-file-name (concat doom-cache-dir "tramp-persistency.el")
|
||||
url-cache-directory (concat doom-cache-dir "url/")
|
||||
url-configuration-directory (concat doom-etc-dir "url/")
|
||||
gamegrid-user-score-file-directory (concat doom-etc-dir "games/"))
|
||||
|
||||
;; HACK Stop sessions from littering the user directory
|
||||
(defadvice! doom--use-cache-dir-a (session-id)
|
||||
:override #'emacs-session-filename
|
||||
(concat doom-cache-dir "emacs-session." session-id))
|
||||
|
||||
|
||||
;;
|
||||
;;; 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.
|
||||
(setq-default bidi-display-reordering 'left-to-right
|
||||
bidi-paragraph-direction 'left-to-right)
|
||||
|
||||
;; Reduce rendering/line scan work for Emacs by not rendering cursors or regions
|
||||
;; in non-focused windows.
|
||||
(setq-default cursor-in-non-selected-windows nil)
|
||||
(setq highlight-nonselected-windows nil)
|
||||
|
||||
;; More performant rapid scrolling over unfontified regions. May cause brief
|
||||
;; spells of inaccurate fontification immediately after scrolling.
|
||||
(setq fast-but-imprecise-scrolling t)
|
||||
|
||||
;; Resizing the Emacs frame can be a terribly expensive part of changing the
|
||||
;; font. By inhibiting this, we halve startup times, particularly when we use
|
||||
;; fonts that are larger than the system default (which would resize the frame).
|
||||
(setq frame-inhibit-implied-resize t)
|
||||
|
||||
;; Don't ping things that look like domain names.
|
||||
(setq ffap-machine-p-known 'reject)
|
||||
|
||||
;; 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))
|
||||
|
||||
;; 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))
|
||||
|
||||
;; 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)))
|
||||
|
||||
|
||||
;;
|
||||
;;; MODE-local-vars-hook
|
||||
|
||||
;; File+dir local variables are initialized after the major mode and its hooks
|
||||
;; have run. If you want hook functions to be aware of these customizations, add
|
||||
;; them to MODE-local-vars-hook instead.
|
||||
(defun doom-run-local-var-hooks-h ()
|
||||
"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 ()
|
||||
"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)
|
||||
|
||||
|
||||
;;
|
||||
;;; Incremental lazy-loading
|
||||
|
||||
(defvar doom-incremental-packages '(t)
|
||||
"A list of packages to load incrementally after startup. Any large packages
|
||||
here may cause noticable pauses, so it's recommended you break them up into
|
||||
sub-packages. For example, `org' is comprised of many packages, and can be
|
||||
broken up into:
|
||||
|
||||
(doom-load-packages-incrementally
|
||||
'(calendar find-func format-spec org-macs org-compat
|
||||
org-faces org-entities org-list org-pcomplete org-src
|
||||
org-footnote org-macro ob org org-clock org-agenda
|
||||
org-capture))
|
||||
|
||||
This is already done by the lang/org module, however.
|
||||
|
||||
If you want to disable incremental loading altogether, either remove
|
||||
`doom-load-packages-incrementally-h' from `emacs-startup-hook' or set
|
||||
`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
|
||||
"How long (in idle seconds) until incremental loading starts.
|
||||
|
||||
Set this to nil to disable incremental loading.")
|
||||
|
||||
(defvar doom-incremental-idle-timer 1.5
|
||||
"How long (in idle seconds) in between incrementally loading packages.")
|
||||
|
||||
(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)
|
||||
(while packages
|
||||
(let ((req (pop packages)))
|
||||
(unless (featurep req)
|
||||
(doom-log "Incrementally loading %s" req)
|
||||
(condition-case e
|
||||
(or (while-no-input
|
||||
;; If `default-directory' is a directory that doesn't exist
|
||||
;; or is unreadable, Emacs throws up file-missing errors, so
|
||||
;; we set it to a directory we know exists and is readable.
|
||||
(let ((default-directory doom-emacs-dir)
|
||||
(gc-cons-threshold most-positive-fixnum)
|
||||
file-name-handler-alist)
|
||||
(require req nil t))
|
||||
t)
|
||||
(push req packages))
|
||||
((error debug)
|
||||
(message "Failed to load '%s' package incrementally, because: %s"
|
||||
req e)))
|
||||
(if (not packages)
|
||||
(doom-log "Finished incremental loading")
|
||||
(run-with-idle-timer doom-incremental-idle-timer
|
||||
nil #'doom-load-packages-incrementally
|
||||
packages t)
|
||||
(setq packages nil)))))))
|
||||
|
||||
(defun doom-load-packages-incrementally-h ()
|
||||
"Begin incrementally loading packages in `doom-incremental-packages'.
|
||||
|
||||
If this is a daemon session, load them all immediately instead."
|
||||
(if (daemonp)
|
||||
(mapc #'require (cdr doom-incremental-packages))
|
||||
(when (integerp 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)
|
||||
|
||||
|
||||
;;
|
||||
;;; 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.
|
||||
|
||||
If RETURN-P, return the message as a string instead of displaying it."
|
||||
(funcall (if return-p #'format #'message)
|
||||
"Doom loaded %d packages across %d modules in %.03fs"
|
||||
(- (length load-path) (length doom--initial-load-path))
|
||||
(if doom-modules (hash-table-count doom-modules) 0)
|
||||
(or doom-init-time
|
||||
(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)
|
||||
"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 overall load order of Doom is as follows:
|
||||
|
||||
~/.emacs.d/init.el
|
||||
~/.emacs.d/core/core.el
|
||||
~/.doom.d/init.el
|
||||
Module init.el files
|
||||
`doom-before-init-modules-hook'
|
||||
Module config.el files
|
||||
~/.doom.d/config.el
|
||||
`doom-init-modules-hook'
|
||||
`after-init-hook'
|
||||
`emacs-startup-hook'
|
||||
`doom-init-ui-hook'
|
||||
`window-setup-hook'
|
||||
|
||||
Module load order is determined by your `doom!' block. See `doom-modules-dirs'
|
||||
for a list of all recognized module trees. Order defines precedence (from most
|
||||
to least)."
|
||||
(when (or force-p (not doom-init-p))
|
||||
(setq doom-init-p t)
|
||||
|
||||
;; Reset as much state as possible, so `doom-initialize' can be treated like
|
||||
;; a reset function. Particularly useful for 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
|
||||
;; your shell environment there.
|
||||
(when (and (or (display-graphic-p)
|
||||
(daemonp))
|
||||
(file-exists-p doom-env-file))
|
||||
(doom-load-envvars-file doom-env-file))
|
||||
|
||||
(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)))
|
||||
|
||||
;; Eagerly load these libraries because we may be in a session that hasn't been
|
||||
;; fully initialized (e.g. where autoloads files haven't been generated or
|
||||
;; `load-path' populated).
|
||||
(mapc (doom-rpartial #'load nil (not doom-debug-mode) 'nosuffix)
|
||||
(file-expand-wildcards (concat doom-core-dir "autoload/*.el")))
|
||||
|
||||
;; Create all our core directories to quell file errors
|
||||
(dolist (dir (list doom-local-dir
|
||||
doom-etc-dir
|
||||
doom-cache-dir))
|
||||
(unless (file-directory-p dir)
|
||||
(make-directory dir 'parents)))
|
||||
|
||||
;; 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))
|
||||
|
||||
(provide 'core)
|
||||
;;; core.el ends here
|
||||
40
.emacs.d/core/packages.el
Normal file
40
.emacs.d/core/packages.el
Normal file
@@ -0,0 +1,40 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/packages.el
|
||||
|
||||
;; core.el
|
||||
(package! dotenv-mode)
|
||||
(package! auto-minor-mode)
|
||||
|
||||
;; core-ui.el
|
||||
(package! all-the-icons)
|
||||
(package! hide-mode-line)
|
||||
(package! highlight-numbers)
|
||||
(package! rainbow-delimiters)
|
||||
(package! restart-emacs)
|
||||
|
||||
;; core-editor.el
|
||||
(package! better-jumper)
|
||||
(package! dtrt-indent)
|
||||
(package! helpful)
|
||||
(package! ns-auto-titlebar :ignore (not IS-MAC))
|
||||
(package! pcre2el)
|
||||
(package! smartparens)
|
||||
(package! so-long
|
||||
:built-in 'prefer
|
||||
;; 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)
|
||||
|
||||
;; core-projects.el
|
||||
(package! projectile)
|
||||
|
||||
;; core-keybinds.el
|
||||
(package! general)
|
||||
(package! which-key)
|
||||
29
.emacs.d/core/templates/BUG_REPORT
Normal file
29
.emacs.d/core/templates/BUG_REPORT
Normal file
@@ -0,0 +1,29 @@
|
||||
Please read through the following before you submit your issue.
|
||||
|
||||
+ [ ] Running `make` (then restarting Emacs) did not fix my issue
|
||||
+ [ ] If I have byte-compiled, I've tried recompiling with `make compile`
|
||||
+ [ ] If I changed the version of Emacs installed, I've recompiled by plugins
|
||||
with `make compile-elpa`
|
||||
+ [ ] I ran `make doctor` and it produced no leads
|
||||
+ [ ] My issue cannot be found [on the wiki](/docs/troubleshoot.org)
|
||||
+ [ ] I filled out the four fields in the template below
|
||||
|
||||
-------------------------------------------------------------------
|
||||
|
||||
### Observed behavior
|
||||
|
||||
<!-- What happened -->
|
||||
|
||||
### Expected behavior
|
||||
|
||||
<!-- What *should* have happened -->
|
||||
|
||||
### Steps to reproduce
|
||||
|
||||
<!-- Tell us how to reproduce the issue in steps -->
|
||||
|
||||
### Extra details
|
||||
|
||||
<!-- Include backtraces & screenshots if possible -->
|
||||
|
||||
-------------------------------------------------------------------
|
||||
25
.emacs.d/core/templates/QUICKSTART_INTRO
Normal file
25
.emacs.d/core/templates/QUICKSTART_INTRO
Normal file
@@ -0,0 +1,25 @@
|
||||
Before you doom yourself, there are a few things you should know:
|
||||
|
||||
1. Whenever you edit your doom! block in ~/.doom.d/init.el or modify your
|
||||
modules, run:
|
||||
|
||||
bin/doom refresh
|
||||
|
||||
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 inexplicably goes wrong, try `bin/doom doctor`
|
||||
|
||||
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`.
|
||||
|
||||
Have fun!
|
||||
12
.emacs.d/core/templates/VANILLA_SANDBOX
Normal file
12
.emacs.d/core/templates/VANILLA_SANDBOX
Normal file
@@ -0,0 +1,12 @@
|
||||
;; Welcome to the sandbox!
|
||||
;;
|
||||
;; This is a test bed for running Emacs Lisp in another instance of Emacs that
|
||||
;; has varying amounts of Doom loaded:
|
||||
;;
|
||||
;; - vanilla Emacs (nothing loaded) \[doom--run-vanilla-emacs]
|
||||
;; - vanilla Doom (only Doom core) \[doom--run-vanilla-doom]
|
||||
;; - Doom + modules - your private config \[doom--run-vanilla-doom+]
|
||||
;; - Doom + modules + your private config \[doom--run-full-doom]
|
||||
;;
|
||||
;; This is done without sacrificing access to installed packages. Use the sandbox
|
||||
;; to reproduce bugs and determine if Doom is to blame.
|
||||
69
.emacs.d/core/test/helpers.el
Normal file
69
.emacs.d/core/test/helpers.el
Normal file
@@ -0,0 +1,69 @@
|
||||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; core/test/helpers.el
|
||||
|
||||
(eval-and-compile
|
||||
(setq doom-interactive-mode 'test)
|
||||
(doom-initialize 'force 'noerror)
|
||||
(require 'buttercup)
|
||||
(setq split-width-threshold 0
|
||||
split-height-threshold 0
|
||||
window-min-width 0
|
||||
window-min-height 0))
|
||||
|
||||
;;
|
||||
;;; Buttercup extensions
|
||||
|
||||
(buttercup-define-matcher-for-binary-function :to-equal-file file-equal-p)
|
||||
|
||||
(buttercup-define-matcher :to-expand-into (form expected)
|
||||
(cl-destructuring-bind (form expected)
|
||||
(mapcar #'funcall (list form expected))
|
||||
(let ((expanded (macroexpand-1 form)))
|
||||
(if (equal expanded expected)
|
||||
(cons t (format "Expected `%S' to not expand to `%S'"
|
||||
form expected))
|
||||
(cons nil (format "Expected `%S' to not expand to `%S', but got `%S' instead"
|
||||
form expected expanded))))))
|
||||
|
||||
(buttercup-define-matcher :to-output (form &optional expected-output)
|
||||
(let ((expected-output (and (functionp expected-output)
|
||||
(funcall expected-output)))
|
||||
output)
|
||||
(with-current-buffer (get-buffer "*Messages*")
|
||||
(let ((standard-output (current-buffer))
|
||||
(start (point)))
|
||||
(let ((inhibit-message t))
|
||||
(funcall form))
|
||||
(setq output (buffer-substring-no-properties start (point-max)))
|
||||
(with-silent-modifications (erase-buffer))))
|
||||
(cond ((null expected-output)
|
||||
(if (string-empty-p output)
|
||||
(cons nil (format "Expected output %S, but got none"
|
||||
expected-output))
|
||||
(cons t (format "Expected no output, but got %S"
|
||||
output))))
|
||||
((not (equal expected-output output))
|
||||
(cons nil (format "Expected output %S, but got %S instead"
|
||||
expected-output output)))
|
||||
((cons t (format "Expected to not get %S as output"
|
||||
expected-output))))))
|
||||
|
||||
(buttercup-define-matcher :to-contain-items (items expected)
|
||||
(cl-destructuring-bind (items expected)
|
||||
(mapcar #'funcall (list items expected))
|
||||
(if-let (missing (cl-set-difference expected items))
|
||||
(cons nil (format "Expected list to contain %S, but it was missing %S"
|
||||
expected missing))
|
||||
(cons t (format "Expected list to not contain %S, but it did: %S"
|
||||
expected items)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Helper macros
|
||||
|
||||
(defmacro insert!! (&rest text)
|
||||
"Insert TEXT in buffer, then move cursor to last {0} marker."
|
||||
`(progn
|
||||
(insert ,@text)
|
||||
(when (search-backward "{0}" nil t)
|
||||
(replace-match "" t t))))
|
||||
17
.emacs.d/core/test/init.el
Normal file
17
.emacs.d/core/test/init.el
Normal file
@@ -0,0 +1,17 @@
|
||||
;;; core/test/init.el -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
|
||||
;; An init.el for our unit test suites. Do not use this!
|
||||
|
||||
(doom! :completion
|
||||
company
|
||||
:ui
|
||||
doom-dashboard
|
||||
popup
|
||||
workspaces
|
||||
:editor
|
||||
evil
|
||||
:tools
|
||||
pass
|
||||
:lang
|
||||
org
|
||||
web)
|
||||
4
.emacs.d/core/test/packages.el
Normal file
4
.emacs.d/core/test/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/packages.el
|
||||
|
||||
(package! buttercup)
|
||||
160
.emacs.d/core/test/test-autoload-buffers.el
Normal file
160
.emacs.d/core/test/test-autoload-buffers.el
Normal file
@@ -0,0 +1,160 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-autoload-buffers.el
|
||||
|
||||
(describe "core/autoload/buffers"
|
||||
:var (a b c d)
|
||||
|
||||
(require 'core-projects)
|
||||
(load! "autoload/buffers" doom-core-dir)
|
||||
|
||||
(before-each
|
||||
(delete-other-windows)
|
||||
(setq a (switch-to-buffer (get-buffer-create "a"))
|
||||
b (get-buffer-create "b")
|
||||
c (get-buffer-create "c")
|
||||
d (get-buffer-create "d")))
|
||||
(after-each
|
||||
(kill-buffer a)
|
||||
(kill-buffer b)
|
||||
(kill-buffer c)
|
||||
(kill-buffer d))
|
||||
|
||||
(describe "buffer lists"
|
||||
(describe "doom-buffer-list"
|
||||
(it "should only see four buffers"
|
||||
(expect (doom-buffer-list) :to-contain-items (list a b c d)))))
|
||||
|
||||
;; TODO predicate tests
|
||||
(xdescribe "predicate functions"
|
||||
(describe "doom-dired-buffer-p")
|
||||
(describe "doom-special-buffer-p")
|
||||
(describe "doom-temp-buffer-p")
|
||||
(describe "doom-visible-buffer-p")
|
||||
(describe "doom-buried-buffer-p")
|
||||
(describe "doom-non-file-visiting-buffer-p")
|
||||
(describe "doom-dired-buffer-p")
|
||||
(describe "doom-buffer-frame-predicate"))
|
||||
|
||||
(describe "doom-project-buffer-list"
|
||||
:var (projectile-projects-cache-time projectile-projects-cache)
|
||||
(before-all (require 'projectile))
|
||||
(after-all (unload-feature 'projectile t))
|
||||
|
||||
(before-each
|
||||
(with-current-buffer a (setq default-directory doom-emacs-dir))
|
||||
(with-current-buffer b (setq default-directory doom-core-dir))
|
||||
(with-current-buffer c (setq default-directory "/tmp/"))
|
||||
(with-current-buffer d (setq default-directory "~"))
|
||||
(projectile-mode +1))
|
||||
(after-each
|
||||
(projectile-mode -1))
|
||||
|
||||
(it "returns buffers in the same project"
|
||||
(with-current-buffer a
|
||||
(expect (doom-project-buffer-list)
|
||||
:to-contain-items (list a b))))
|
||||
|
||||
(it "returns all buffers if not in a project"
|
||||
(with-current-buffer c
|
||||
(expect (doom-project-buffer-list)
|
||||
:to-have-same-items-as (buffer-list)))))
|
||||
|
||||
(describe "doom-fallback-buffer"
|
||||
(it "returns a live buffer"
|
||||
(expect (buffer-live-p (doom-fallback-buffer))))
|
||||
|
||||
(it "returns the scratch buffer"
|
||||
(expect (doom-fallback-buffer) :to-equal (get-buffer "*scratch*"))))
|
||||
|
||||
(describe "real buffers"
|
||||
(before-each
|
||||
(with-current-buffer b (setq buffer-file-name "x"))
|
||||
(with-current-buffer c (rename-buffer "*C*")))
|
||||
|
||||
(describe "doom-mark-buffer-as-real-h"
|
||||
(with-current-buffer a
|
||||
(doom-mark-buffer-as-real-h)
|
||||
(expect (buffer-local-value 'doom-real-buffer-p a))))
|
||||
|
||||
(describe "doom-set-buffer-real"
|
||||
(it "sets `doom-real-buffer-p' buffer-locally"
|
||||
(doom-set-buffer-real a t)
|
||||
(expect (buffer-local-value 'doom-real-buffer-p a))))
|
||||
|
||||
(describe "doom-real-buffer-p"
|
||||
(it "returns t for buffers manually marked real"
|
||||
(doom-set-buffer-real a t)
|
||||
(expect (doom-real-buffer-p a)))
|
||||
(it "returns t for file-visiting buffers"
|
||||
(expect (doom-real-buffer-p b)))
|
||||
(it "returns nil for temporary buffers"
|
||||
(expect (doom-real-buffer-p c) :to-be nil)
|
||||
(expect (doom-real-buffer-p d) :to-be nil)))
|
||||
|
||||
(describe "doom-unreal-buffer-p"
|
||||
(it "returns t for unreal buffers"
|
||||
(expect (doom-unreal-buffer-p c))
|
||||
(expect (doom-unreal-buffer-p d)))
|
||||
(it "returns nil for real buffers"
|
||||
(doom-set-buffer-real a t)
|
||||
(expect (not (doom-unreal-buffer-p a)))
|
||||
(expect (not (doom-unreal-buffer-p b)))))
|
||||
|
||||
(describe "doom-real-buffer-list"
|
||||
(it "returns only real buffers"
|
||||
(expect (doom-real-buffer-list) :to-contain-items (list a b)))))
|
||||
|
||||
(describe "buffer/window management"
|
||||
(describe "buffer search methods"
|
||||
(before-each
|
||||
(with-current-buffer a (lisp-mode))
|
||||
(with-current-buffer b (text-mode))
|
||||
(with-current-buffer c (text-mode))
|
||||
(split-window)
|
||||
(switch-to-buffer b))
|
||||
|
||||
(describe "doom-matching-buffers"
|
||||
(it "can match buffers by regexp"
|
||||
(expect (doom-matching-buffers "^[ac]$") :to-have-same-items-as (list a c))))
|
||||
|
||||
(describe "doom-buffers-in-mode"
|
||||
(it "can match buffers by major-mode"
|
||||
(expect (doom-buffers-in-mode 'text-mode) :to-have-same-items-as (list b c))))
|
||||
|
||||
(describe "doom-buried-buffers"
|
||||
(it "can find all buried buffers"
|
||||
(expect (doom-buried-buffers) :to-contain-items (list c d))))
|
||||
|
||||
(describe "doom-visible-buffers"
|
||||
(it "can find all visible buffers"
|
||||
(expect (doom-visible-buffers)
|
||||
:to-have-same-items-as (list a b))))
|
||||
|
||||
(describe "doom-visible-windows"
|
||||
(it "can find all visible windows"
|
||||
(expect (doom-visible-windows)
|
||||
:to-have-same-items-as
|
||||
(mapcar #'get-buffer-window (list a b))))))
|
||||
|
||||
(describe "killing buffers/windows"
|
||||
(describe "doom-kill-buffer-and-windows"
|
||||
(before-each
|
||||
(split-window) (switch-to-buffer b)
|
||||
(split-window) (switch-to-buffer a))
|
||||
|
||||
(it "kills the selected buffers and all its windows"
|
||||
(doom-kill-buffer-and-windows a)
|
||||
(expect (buffer-live-p a) :to-be nil)
|
||||
(expect (length (doom-visible-windows)) :to-be 1)))
|
||||
|
||||
;; TODO
|
||||
(xdescribe "doom-fixup-windows")
|
||||
(xdescribe "doom-kill-buffer-fixup-windows")
|
||||
(xdescribe "doom-kill-buffers-fixup-windows"))
|
||||
|
||||
(xdescribe "commands"
|
||||
(describe "doom/kill-all-buffers")
|
||||
(describe "doom/kill-other-buffers")
|
||||
(describe "doom/kill-matching-buffers")
|
||||
(describe "doom/kill-buried-buffers")
|
||||
(describe "doom/kill-project-buffers"))))
|
||||
164
.emacs.d/core/test/test-autoload-files.el
Normal file
164
.emacs.d/core/test/test-autoload-files.el
Normal file
@@ -0,0 +1,164 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-autoload-files.el
|
||||
|
||||
(describe "core/autoload/files"
|
||||
|
||||
(load! "autoload/files" doom-core-dir)
|
||||
|
||||
(describe "library"
|
||||
(describe "file-exists-p!"
|
||||
(it "is a (quasi) drop-in replacement for `file-exists-p'"
|
||||
(let ((default-directory doom-emacs-dir)
|
||||
(init-file "init.el"))
|
||||
(expect (file-exists-p "init.el"))
|
||||
(expect (and (file-exists-p! "init.el")
|
||||
(file-exists-p "init.el")))
|
||||
(expect (and (file-exists-p! init-file)
|
||||
(file-exists-p init-file)))
|
||||
(expect (and (file-exists-p! doom-emacs-dir)
|
||||
(file-exists-p doom-emacs-dir)))
|
||||
(expect (and (not (file-exists-p! "/cant/possibly/exist/please/dont/exist"))
|
||||
(not (file-exists-p "/cant/possibly/exist/please/dont/exist"))))))
|
||||
|
||||
(it "returns the file path if it exists"
|
||||
(expect (file-exists-p! "init.example.el"
|
||||
doom-emacs-dir)
|
||||
:to-equal (expand-file-name "init.example.el" doom-emacs-dir)))
|
||||
|
||||
(it "understands compound statements"
|
||||
(let ((default-directory doom-emacs-dir))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el")))
|
||||
(expect (file-exists-p! (or "doesnotexist" "init.example.el")))
|
||||
(expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST")))))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el")
|
||||
doom-emacs-dir))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el")
|
||||
doom-emacs-dir))
|
||||
(expect (file-exists-p! (or "doesnotexist" "init.example.el")
|
||||
doom-emacs-dir))
|
||||
(expect (not (file-exists-p! (or "doesnotexist" "DOESNOTEXIST")
|
||||
doom-emacs-dir))))
|
||||
|
||||
(it "understands nested compound statements"
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el"
|
||||
(or "doesnotexist" "LICENSE"))
|
||||
doom-emacs-dir))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el"
|
||||
(and "LICENSE" "README.md"
|
||||
(or "doesnotexist"
|
||||
"early-init.el")))
|
||||
doom-emacs-dir))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el"
|
||||
(or "edoesnotexist" "DOESNOTEXIST"
|
||||
(and "idontexist"
|
||||
"doanyofusexist?")))
|
||||
doom-emacs-dir)
|
||||
:to-be nil))
|
||||
|
||||
(it "returns the last form if a compound file check succeeds"
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el"
|
||||
(or "doesnotexist" "LICENSE"))
|
||||
doom-emacs-dir)
|
||||
:to-equal (expand-file-name "LICENSE" doom-emacs-dir))
|
||||
(expect (file-exists-p! (and "init.el" "init.example.el"
|
||||
(or (or "doesnotexist" "DOESNOTEXIST")
|
||||
"doanyofusreallyexist"
|
||||
(or "cantexist" "LICENSE")))
|
||||
doom-emacs-dir)
|
||||
:to-equal (expand-file-name "LICENSE" doom-emacs-dir)))
|
||||
|
||||
(it "disregards the directory argument if given absolute path"
|
||||
(expect (file-exists-p! "/tmp" "/directory/that/doesnt/exist"))
|
||||
(expect (file-exists-p! doom-core-dir "/directory/that/doesnt/exist"))
|
||||
(expect (file-exists-p! (and "/tmp" doom-core-dir) "/directory/that/doesnt/exist"))
|
||||
(expect (file-exists-p! (or "/tmp" doom-core-dir) "/directory/that/doesnt/exist")))
|
||||
|
||||
(it "interpolates variables"
|
||||
(let ((file-1 "init.el")
|
||||
(file-2 "init.example.el")
|
||||
(file-3 "LICENSE")
|
||||
(file-404 "doesnotexistlikenoreally"))
|
||||
(expect (file-exists-p! file-1 doom-emacs-dir))
|
||||
(expect (file-exists-p! (and file-1 file-2) doom-emacs-dir))
|
||||
(expect (file-exists-p! (and file-1 (or file-404 file-2)) doom-emacs-dir))
|
||||
(expect (file-exists-p! (or (and file-404 file-2) (and file-3 file-1))
|
||||
doom-emacs-dir))))
|
||||
|
||||
(it "interpolates forms"
|
||||
(cl-letf (((symbol-function 'getfilename)
|
||||
(lambda () "init.example.el")))
|
||||
(expect (file-exists-p! (and (or (if nil "init.el" "doesnotexist")
|
||||
(getfilename))
|
||||
"LICENSE")
|
||||
doom-emacs-dir)
|
||||
:to-equal (expand-file-name "LICENSE" doom-emacs-dir)))))
|
||||
|
||||
;; TODO
|
||||
(xdescribe "doom-glob")
|
||||
(xdescribe "doom-path")
|
||||
(xdescribe "doom-dir")
|
||||
(xdescribe "doom-files-in")
|
||||
(xdescribe "doom-file-size")
|
||||
(xdescribe "doom-directory-size")
|
||||
(xdescribe "doom-file-cookie-p"))
|
||||
|
||||
(describe "interactive file operations"
|
||||
:var (src dest projectile-projects-cache-time projectile-projects-cache)
|
||||
|
||||
(require 'core-projects)
|
||||
(require 'projectile)
|
||||
|
||||
(before-each
|
||||
(setq src (make-temp-file "test-src")
|
||||
existing (make-temp-file "test-existing")
|
||||
dest (expand-file-name "test-dest" temporary-file-directory))
|
||||
(quiet! (find-file-literally src))
|
||||
(spy-on 'y-or-n-p :and-return-value nil)
|
||||
(projectile-mode +1))
|
||||
|
||||
(after-each
|
||||
(projectile-mode -1)
|
||||
(switch-to-buffer (doom-fallback-buffer))
|
||||
(ignore-errors (delete-file src))
|
||||
(ignore-errors (delete-file existing))
|
||||
(ignore-errors (delete-file dest)))
|
||||
|
||||
(describe "move-this-file"
|
||||
(it "won't move to itself"
|
||||
(expect (quiet! (doom/move-this-file src)) :to-throw))
|
||||
(it "will move to another file"
|
||||
(expect (quiet! (doom/move-this-file dest t)))
|
||||
(expect (file-exists-p dest))
|
||||
(expect (file-exists-p src) :to-be nil))
|
||||
(it "will prompt if overwriting a file"
|
||||
(quiet! (doom/move-this-file existing))
|
||||
(expect 'y-or-n-p :to-have-been-called-times 1)
|
||||
(expect (file-exists-p src))))
|
||||
|
||||
(describe "copy-this-file"
|
||||
(it "refuses to copy to itself"
|
||||
(expect (quiet! (doom/copy-this-file src)) :to-throw))
|
||||
(it "copies to another file"
|
||||
(expect (quiet! (doom/copy-this-file dest t)))
|
||||
(expect (file-exists-p! src dest)))
|
||||
(it "prompts if overwriting a file"
|
||||
(quiet! (doom/copy-this-file existing))
|
||||
(expect 'y-or-n-p :to-have-been-called-times 1)))
|
||||
|
||||
(describe "delete-this-file"
|
||||
(it "fails gracefully on non-existent files"
|
||||
(expect (quiet! (doom/delete-this-file dest)) :to-throw))
|
||||
(it "deletes existing files"
|
||||
(quiet! (doom/delete-this-file existing t))
|
||||
(expect (file-exists-p existing) :to-be nil))
|
||||
(it "prompts to delete any existing file"
|
||||
(quiet! (doom/delete-this-file existing))
|
||||
(expect 'y-or-n-p :to-have-been-called-times 1))))
|
||||
|
||||
(xdescribe "sudo {this,find} file"
|
||||
(before-each
|
||||
(spy-on 'find-file :and-return-value nil)
|
||||
(spy-on 'find-alternate-file :and-return-value nil))
|
||||
|
||||
(describe "doom/sudo-find-file")
|
||||
(describe "doom/sudo-this-file")))
|
||||
44
.emacs.d/core/test/test-autoload-format.el
Normal file
44
.emacs.d/core/test/test-autoload-format.el
Normal file
@@ -0,0 +1,44 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-autoload-message.el
|
||||
|
||||
(describe "core/autoload/format"
|
||||
(describe "format!"
|
||||
:var (doom-format-backend)
|
||||
(before-all
|
||||
(setq doom-format-backend 'ansi))
|
||||
|
||||
(it "should be a drop-in replacement for `format'"
|
||||
(expect (format! "Hello %s" "World")
|
||||
:to-equal "Hello World"))
|
||||
|
||||
(it "supports ansi coloring in noninteractive sessions"
|
||||
(expect (format! (red "Hello %s") "World")
|
||||
:to-equal "[31mHello World[0m"))
|
||||
|
||||
(it "supports text properties in interactive sessions"
|
||||
(let ((doom-format-backend 'text-properties))
|
||||
(expect (get-text-property 0 'face (format! (red "Hello %s") "World"))
|
||||
:to-equal (list :foreground (face-foreground 'term-color-red)))))
|
||||
|
||||
(it "supports nested color specs"
|
||||
(expect (format! (bold (red "Hello %s")) "World")
|
||||
:to-equal (format "\e[%dm%s\e[0m" 1
|
||||
(format "\e[%dm%s\e[0m" 31 "Hello World")))
|
||||
(expect (format! (on-red (bold "Hello %s")) "World")
|
||||
:to-equal (format "\e[%dm%s\e[0m" 41
|
||||
(format "\e[%dm%s\e[0m" 1 "Hello World")))
|
||||
(expect (format! (dark (white "Hello %s")) "World")
|
||||
:to-equal (format "\e[%dm%s\e[0m" 2
|
||||
(format "\e[%dm%s\e[0m" 37 "Hello World"))))
|
||||
|
||||
(it "supports dynamic color apply syntax"
|
||||
(expect (format! (color 'red "Hello %s") "World")
|
||||
:to-equal (format! (red "Hello %s") "World"))
|
||||
(expect (format! (color (if nil 'red 'blue) "Hello %s") "World")
|
||||
:to-equal (format! (blue "Hello %s") "World"))))
|
||||
|
||||
(xdescribe "insert!")
|
||||
(xdescribe "print!")
|
||||
(xdescribe "print-group!")
|
||||
(xdescribe "error!")
|
||||
(xdescribe "user-error!"))
|
||||
5
.emacs.d/core/test/test-autoload-package.el
Normal file
5
.emacs.d/core/test/test-autoload-package.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-autoload-package.el
|
||||
;;;###if nil
|
||||
|
||||
(xdescribe "core/autoload/packages")
|
||||
256
.emacs.d/core/test/test-core-keybinds.el
Normal file
256
.emacs.d/core/test/test-core-keybinds.el
Normal file
@@ -0,0 +1,256 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core-keybinds.el
|
||||
|
||||
(describe "core/keybinds"
|
||||
(require 'core-keybinds)
|
||||
|
||||
;; FIXME test against their side effects rather than their implementation
|
||||
(describe "map!"
|
||||
:var (doom--map-evil-p states-alist)
|
||||
(before-each
|
||||
(setq doom--map-evil-p t
|
||||
states-alist '((:n . normal)
|
||||
(:v . visual)
|
||||
(:i . insert)
|
||||
(:e . emacs)
|
||||
(:o . operator)
|
||||
(:m . motion)
|
||||
(:r . replace))))
|
||||
|
||||
(describe "Single keybinds"
|
||||
(it "binds a global key"
|
||||
(expect '(map! "C-." #'a)
|
||||
:to-expand-into '(general-define-key "C-." #'a)))
|
||||
|
||||
(it "binds a key in one evil state"
|
||||
(dolist (state states-alist)
|
||||
(expect `(map! ,(car state) "C-." #'a)
|
||||
:to-expand-into
|
||||
`(general-define-key :states ',(cdr state) "C-." #'a))))
|
||||
|
||||
(it "binds a key in multiple evil states"
|
||||
(expect '(map! :nvi "C-." #'a)
|
||||
:to-expand-into
|
||||
'(progn (general-define-key :states 'insert "C-." #'a)
|
||||
(general-define-key :states 'visual "C-." #'a)
|
||||
(general-define-key :states 'normal "C-." #'a))))
|
||||
|
||||
(it "binds evil keybinds together with global keybinds"
|
||||
(expect '(map! :ng "C-." #'a)
|
||||
:to-expand-into
|
||||
'(progn
|
||||
(general-define-key :states 'normal "C-." #'a)
|
||||
(general-define-key "C-." #'a)))))
|
||||
|
||||
(describe "Multiple keybinds"
|
||||
(it "binds global keys and preserves order"
|
||||
(expect '(map! "C-." #'a "C-," #'b "C-/" #'c)
|
||||
:to-expand-into
|
||||
'(general-define-key "C-." #'a "C-," #'b "C-/" #'c)))
|
||||
|
||||
(it "binds multiple keybinds in an evil state and preserve order"
|
||||
(dolist (state states-alist)
|
||||
(expect `(map! ,(car state) "a" #'a
|
||||
,(car state) "b" #'b
|
||||
,(car state) "c" #'c)
|
||||
:to-expand-into
|
||||
`(general-define-key :states ',(cdr state)
|
||||
"a" #'a
|
||||
"b" #'b
|
||||
"c" #'c))))
|
||||
|
||||
(it "binds multiple keybinds in different evil states"
|
||||
(expect `(map! :n "a" #'a
|
||||
:n "b" #'b
|
||||
:n "e" #'e
|
||||
:v "c" #'c
|
||||
:i "d" #'d)
|
||||
:to-expand-into
|
||||
`(progn (general-define-key :states 'insert "d" #'d)
|
||||
(general-define-key :states 'visual "c" #'c)
|
||||
(general-define-key :states 'normal "a" #'a "b" #'b "e" #'e))))
|
||||
|
||||
(it "groups multi-state keybinds while preserving same-group key order"
|
||||
(expect `(map! :n "a" #'a
|
||||
:v "c" #'c
|
||||
:n "b" #'b
|
||||
:i "d" #'d
|
||||
:n "e" #'e)
|
||||
:to-expand-into
|
||||
`(progn (general-define-key :states 'insert "d" #'d)
|
||||
(general-define-key :states 'visual "c" #'c)
|
||||
(general-define-key :states 'normal "a" #'a "b" #'b "e" #'e))))
|
||||
|
||||
(it "binds multiple keybinds in multiple evil states"
|
||||
(expect `(map! :nvi "a" #'a
|
||||
:nvi "b" #'b
|
||||
:nvi "c" #'c)
|
||||
:to-expand-into
|
||||
'(progn (general-define-key :states 'insert "a" #'a "b" #'b "c" #'c)
|
||||
(general-define-key :states 'visual "a" #'a "b" #'b "c" #'c)
|
||||
(general-define-key :states 'normal "a" #'a "b" #'b "c" #'c)))))
|
||||
|
||||
(describe "Nested keybinds"
|
||||
(it "binds global keys"
|
||||
(expect '(map! "C-." #'a
|
||||
("C-a" #'b)
|
||||
("C-x" #'c))
|
||||
:to-expand-into
|
||||
'(progn (general-define-key "C-." #'a)
|
||||
(general-define-key "C-a" #'b)
|
||||
(general-define-key "C-x" #'c))))
|
||||
|
||||
(it "binds nested evil keybinds"
|
||||
(expect '(map! :n "C-." #'a
|
||||
(:n "C-a" #'b)
|
||||
(:n "C-x" #'c))
|
||||
:to-expand-into
|
||||
'(progn (general-define-key :states 'normal "C-." #'a)
|
||||
(general-define-key :states 'normal "C-a" #'b)
|
||||
(general-define-key :states 'normal "C-x" #'c))))
|
||||
|
||||
(it "binds global keybinds in between evil keybinds"
|
||||
(expect '(map! :n "a" #'a
|
||||
"b" #'b
|
||||
:n "c" #'c)
|
||||
:to-expand-into
|
||||
'(progn (general-define-key "b" #'b)
|
||||
(general-define-key :states 'normal "a" #'a "c" #'c)))))
|
||||
|
||||
;;
|
||||
(describe "Properties"
|
||||
(describe ":after"
|
||||
(it "wraps `general-define-key' in a `after!' block"
|
||||
(dolist (form '((map! :after helm "a" #'a "b" #'b)
|
||||
(map! (:after helm "a" #'a "b" #'b))))
|
||||
(expect form :to-expand-into '(after! helm (general-define-key "a" #'a "b" #'b))))
|
||||
(expect '(map! "a" #'a (:after helm "b" #'b "c" #'c))
|
||||
:to-expand-into
|
||||
'(progn
|
||||
(general-define-key "a" #'a)
|
||||
(after! helm
|
||||
(general-define-key "b" #'b "c" #'c))))
|
||||
(expect '(map! (:after helm "b" #'b "c" #'c) "a" #'a)
|
||||
:to-expand-into
|
||||
'(progn
|
||||
(after! helm
|
||||
(general-define-key "b" #'b "c" #'c))
|
||||
(general-define-key "a" #'a))))
|
||||
|
||||
(it "nests `after!' blocks"
|
||||
(expect '(map! :after x "a" #'a
|
||||
(:after y "b" #'b
|
||||
(:after z "c" #'c)))
|
||||
:to-expand-into
|
||||
'(after! x
|
||||
(progn
|
||||
(general-define-key "a" #'a)
|
||||
(after! y
|
||||
(progn
|
||||
(general-define-key "b" #'b)
|
||||
(after! z
|
||||
(general-define-key "c" #'c))))))))
|
||||
|
||||
(it "nests `after!' blocks in other nested blocks"
|
||||
(expect '(map! :after x "a" #'a
|
||||
(:when t "b" #'b
|
||||
(:after z "c" #'c)))
|
||||
:to-expand-into
|
||||
'(after! x
|
||||
(progn
|
||||
(general-define-key "a" #'a)
|
||||
(when t
|
||||
(progn
|
||||
(general-define-key "b" #'b)
|
||||
(after! z (general-define-key "c" #'c)))))))))
|
||||
|
||||
(describe ":desc"
|
||||
(it "add a :which-key property to a keybind's DEF"
|
||||
(expect '(map! :desc "A" "a" #'a)
|
||||
:to-expand-into
|
||||
`(general-define-key "a" (list :def #'a :which-key "A")))))
|
||||
|
||||
(describe ":when/:unless"
|
||||
(it "wraps keys in a conditional block"
|
||||
(dolist (prop '(:when :unless))
|
||||
(let ((prop-fn (intern (doom-keyword-name prop))))
|
||||
(expect `(map! ,prop t "a" #'a "b" #'b)
|
||||
:to-expand-into
|
||||
`(,prop-fn t (general-define-key "a" #'a "b" #'b)))
|
||||
(expect `(map! (,prop t "a" #'a "b" #'b))
|
||||
:to-expand-into
|
||||
`(,prop-fn t (general-define-key "a" #'a "b" #'b))))))
|
||||
|
||||
(it "nests conditional blocks"
|
||||
(expect '(map! (:when t "a" #'a (:when t "b" #'b)))
|
||||
:to-expand-into
|
||||
'(when t
|
||||
(progn (general-define-key "a" #'a)
|
||||
(when t (general-define-key "b" #'b)))))))
|
||||
|
||||
(describe ":leader"
|
||||
(it "uses leader definer"
|
||||
(expect '(map! :leader "a" #'a "b" #'b)
|
||||
:to-expand-into
|
||||
'(doom--define-leader-key "a" #'a "b" #'b)))
|
||||
|
||||
(it "it persists for nested keys"
|
||||
(expect '(map! :leader "a" #'a ("b" #'b))
|
||||
:to-expand-into
|
||||
'(progn (doom--define-leader-key "a" #'a)
|
||||
(doom--define-leader-key "b" #'b)))))
|
||||
|
||||
(describe ":localleader"
|
||||
(it "uses localleader definer"
|
||||
(expect '(map! :localleader "a" #'a "b" #'b)
|
||||
:to-expand-into
|
||||
'(define-localleader-key! "a" #'a "b" #'b)))
|
||||
|
||||
(it "it persists for nested keys"
|
||||
(expect '(map! :localleader "a" #'a ("b" #'b))
|
||||
:to-expand-into
|
||||
'(progn (define-localleader-key! "a" #'a)
|
||||
(define-localleader-key! "b" #'b)))))
|
||||
|
||||
(describe ":map/:keymap"
|
||||
(it "specifies a single keymap for keys"
|
||||
(expect '(map! :map emacs-lisp-mode-map "a" #'a)
|
||||
:to-expand-into
|
||||
'(general-define-key :keymaps '(emacs-lisp-mode-map) "a" #'a)))
|
||||
|
||||
(it "specifies multiple keymap for keys"
|
||||
(expect '(map! :map (lisp-mode-map emacs-lisp-mode-map) "a" #'a)
|
||||
:to-expand-into
|
||||
'(general-define-key :keymaps '(lisp-mode-map emacs-lisp-mode-map) "a" #'a))))
|
||||
|
||||
(describe ":mode"
|
||||
(it "appends -map to MODE"
|
||||
(expect '(map! :mode emacs-lisp-mode "a" #'a)
|
||||
:to-expand-into
|
||||
'(general-define-key :keymaps '(emacs-lisp-mode-map) "a" #'a))))
|
||||
|
||||
(describe ":prefix"
|
||||
(it "specifies a prefix for all keys"
|
||||
(expect '(map! :prefix "a" "x" #'x "y" #'y "z" #'z)
|
||||
:to-expand-into
|
||||
'(general-define-key :prefix "a" "x" #'x "y" #'y "z" #'z)))
|
||||
|
||||
(it "overwrites previous inline :prefix properties"
|
||||
(expect '(map! :prefix "a" "x" #'x "y" #'y :prefix "b" "z" #'z)
|
||||
:to-expand-into
|
||||
'(progn (general-define-key :prefix "a" "x" #'x "y" #'y)
|
||||
(general-define-key :prefix "b" "z" #'z))))
|
||||
|
||||
(it "accumulates keys when nested"
|
||||
(expect '(map! (:prefix "a" "x" #'x (:prefix "b" "x" #'x)))
|
||||
:to-expand-into
|
||||
`(progn (general-define-key :prefix "a" "x" #'x)
|
||||
(general-define-key :prefix (general--concat nil "a" "b")
|
||||
"x" #'x)))))
|
||||
|
||||
(describe ":textobj"
|
||||
(it "defines keys in evil-{inner,outer}-text-objects-map"
|
||||
(expect '(map! :textobj "a" #'inner #'outer)
|
||||
:to-expand-into
|
||||
'(map! (:map evil-inner-text-objects-map "a" #'inner)
|
||||
(:map evil-outer-text-objects-map "a" #'outer))))))))
|
||||
277
.emacs.d/core/test/test-core-lib.el
Normal file
277
.emacs.d/core/test/test-core-lib.el
Normal file
@@ -0,0 +1,277 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core-lib.el
|
||||
|
||||
(describe "core-lib"
|
||||
(before-all
|
||||
(require 'core-lib))
|
||||
|
||||
;; --- Helpers ----------------------------
|
||||
(describe "doom-unquote"
|
||||
(it "unquotes a quoted form"
|
||||
(expect (doom-unquote '(quote hello)) :to-be 'hello))
|
||||
(it "unquotes nested quoted forms"
|
||||
(expect (doom-unquote '(quote (quote (a b c)))) :to-equal '(a b c)))
|
||||
(it "unquotes function-quoted forms"
|
||||
(expect (doom-unquote '(function a)) :to-be 'a))
|
||||
(it "does nothing to unquoted forms"
|
||||
(expect (doom-unquote 'hello) :to-be 'hello)
|
||||
(expect (doom-unquote 5) :to-be 5)
|
||||
(expect (doom-unquote t) :to-be t)))
|
||||
|
||||
(describe "doom-enlist"
|
||||
(it "returns nil if given nil"
|
||||
(expect (doom-enlist nil) :to-be nil))
|
||||
(it "creates a list out of non-lists"
|
||||
(expect (doom-enlist 'a) :to-equal '(a)))
|
||||
(it "returns lists as-is"
|
||||
(expect (doom-enlist '(a)) :to-equal '(a))))
|
||||
|
||||
(describe "doom-keyword-intern"
|
||||
(it "returns a keyword"
|
||||
(expect (doom-keyword-intern "test") :to-equal :test))
|
||||
(it "errors if given anything but a string"
|
||||
(expect (doom-keyword-intern t) :to-throw 'wrong-type-argument)))
|
||||
|
||||
(describe "doom-keyword-name"
|
||||
(it "returns the string name of a keyword"
|
||||
(expect (doom-keyword-name :test) :to-equal "test"))
|
||||
(it "errors if given anything but a keyword"
|
||||
(expect (doom-keyword-name "test") :to-throw 'wrong-type-argument)))
|
||||
|
||||
(describe "doom-partial"
|
||||
(it "returns a closure"
|
||||
(expect (functionp (doom-partial #'+ 1))))
|
||||
(it "returns a partial closure"
|
||||
(expect (funcall (doom-partial #'+ 1) 2) :to-be 3)))
|
||||
|
||||
(describe "doom-rpartial"
|
||||
(it "returns a closure"
|
||||
(expect (functionp (doom-rpartial #'+ 1))))
|
||||
(it "returns a partial closure with right-aligned arguments"
|
||||
(expect (funcall (doom-rpartial #'/ 2) 10) :to-be 5)))
|
||||
|
||||
|
||||
;; --- Sugars -----------------------------
|
||||
(describe "lambda!"
|
||||
(it "returns an interactive function"
|
||||
(expect (commandp (lambda!)))
|
||||
(expect (funcall (lambda! 5)) :to-equal 5)))
|
||||
|
||||
(describe "lambda!!"
|
||||
(it "returns an interactive function with a prefix argument"
|
||||
(expect (commandp (lambda! #'ignore t)))
|
||||
(expect (funcall (lambda!! (lambda (arg)
|
||||
(interactive "P")
|
||||
arg)
|
||||
5))
|
||||
:to-equal 5)))
|
||||
|
||||
(describe "file!"
|
||||
(it "returns the executing file"
|
||||
(expect (eval-and-compile (file!))
|
||||
:to-equal
|
||||
(eval-and-compile load-file-name))))
|
||||
|
||||
(describe "dir!"
|
||||
(it "returns the executing directory"
|
||||
(expect (eval-and-compile (dir!))
|
||||
:to-equal
|
||||
(eval-and-compile
|
||||
(directory-file-name (file-name-directory load-file-name))))))
|
||||
|
||||
(describe "pushnew!"
|
||||
(it "pushes values onto a list symbol, in order"
|
||||
(let ((a '(1 2 3)))
|
||||
(expect (pushnew! a 9 8 7)
|
||||
:to-equal '(7 8 9 1 2 3))))
|
||||
(it "only adds values that aren't already in the list"
|
||||
(let ((a '(1 symbol 3.14 "test")))
|
||||
(expect (pushnew! a "test" 'symbol 3.14 1)
|
||||
:to-equal '(1 symbol 3.14 "test")))))
|
||||
|
||||
(describe "prependq!"
|
||||
(it "prepends a list to a list symbol"
|
||||
(let ((list '(a b c)))
|
||||
(expect (prependq! list '(d e f))
|
||||
:to-equal '(d e f a b c)))))
|
||||
|
||||
(describe "append!"
|
||||
(it "appends a list to a list symbol"
|
||||
(let ((list '(a b c)))
|
||||
(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)))
|
||||
(delq! 'b list)
|
||||
(expect list :to-equal '(a c))))
|
||||
(it "delete's an element from an alist by key"
|
||||
(let ((alist '((a 1) (b 2) (c 3))))
|
||||
(delq! 'b alist 'assq)
|
||||
(expect alist :to-equal '((a 1) (c 3))))))
|
||||
|
||||
(describe "hooks"
|
||||
(describe "add-hook!"
|
||||
:var (fake-mode-hook other-mode-hook some-mode-hook)
|
||||
(before-each
|
||||
(setq fake-mode-hook '(first-hook)
|
||||
other-mode-hook nil
|
||||
some-mode-hook '(first-hook second-hook)))
|
||||
|
||||
(it "resolves quoted hooks literally"
|
||||
(expect '(add-hook! 'fake-mode-hook #'ignore) :to-expand-into
|
||||
`(add-hook 'fake-mode-hook #'ignore nil nil)))
|
||||
(it "resolves unquoted modes to their hook variables"
|
||||
(expect '(add-hook! fake-mode #'ignore) :to-expand-into
|
||||
`(add-hook 'fake-mode-hook #'ignore nil nil)))
|
||||
|
||||
(it "adds one-to-one hook"
|
||||
(add-hook! fake-mode #'hook-2)
|
||||
(add-hook! 'fake-mode-hook #'hook-1)
|
||||
(expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook)))
|
||||
|
||||
(it "adds one-to-many hook"
|
||||
(add-hook! (fake-mode other-mode some-mode) #'hook-2)
|
||||
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'hook-1)
|
||||
(add-hook! (fake-mode other-mode some-mode) :append #'last-hook)
|
||||
(expect fake-mode-hook :to-equal '(hook-1 hook-2 first-hook last-hook))
|
||||
(expect other-mode-hook :to-equal '(hook-1 hook-2 last-hook))
|
||||
(expect some-mode-hook :to-equal '(hook-1 hook-2 first-hook second-hook last-hook)))
|
||||
|
||||
(it "adds many-to-many hooks and preserve provided order"
|
||||
(add-hook! (fake-mode other-mode some-mode) #'(hook-3 hook-4))
|
||||
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) #'(hook-1 hook-2))
|
||||
(add-hook! '(fake-mode-hook other-mode-hook some-mode-hook) :append #'(last-hook-1 last-hook-2))
|
||||
(expect fake-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook last-hook-1 last-hook-2))
|
||||
(expect other-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 last-hook-1 last-hook-2))
|
||||
(expect some-mode-hook :to-equal '(hook-1 hook-2 hook-3 hook-4 first-hook second-hook last-hook-1 last-hook-2)))
|
||||
|
||||
(it "adds implicit lambda to one hook"
|
||||
(add-hook! fake-mode (progn))
|
||||
(add-hook! 'other-mode-hook (ignore))
|
||||
(add-hook! 'some-mode-hook :append (ignore))
|
||||
(expect (caar fake-mode-hook) :to-be 'lambda)
|
||||
(expect (caar other-mode-hook) :to-be 'lambda)
|
||||
(expect (caar (last other-mode-hook)) :to-be 'lambda))
|
||||
|
||||
(it "handles inline defuns as hook symbols"
|
||||
(add-hook! fake-mode (defun hook-a ()))
|
||||
(add-hook! 'other-mode-hook
|
||||
(defun hook-b ())
|
||||
(defun hook-c ()))
|
||||
(expect (car fake-mode-hook) :to-be 'hook-a)
|
||||
(expect other-mode-hook :to-equal '(hook-b hook-c))))
|
||||
|
||||
(describe "remove-hook!"
|
||||
:var (fake-mode-hook)
|
||||
(before-each
|
||||
(setq fake-mode-hook '(first-hook second-hook third-hook fourth-hook)))
|
||||
(it "removes one hook"
|
||||
(remove-hook! fake-mode #'third-hook)
|
||||
(remove-hook! 'fake-mode-hook #'second-hook)
|
||||
(expect fake-mode-hook :to-equal '(first-hook fourth-hook)))
|
||||
(it "removes multiple hooks"
|
||||
(remove-hook! fake-mode #'(first-hook third-hook))
|
||||
(remove-hook! 'fake-mode-hook #'(second-hook fourth-hook))
|
||||
(expect fake-mode-hook :to-be nil)))
|
||||
|
||||
(describe "add-transient-hook!"
|
||||
(it "adds a transient function to hooks"
|
||||
(let (hooks value)
|
||||
(add-transient-hook! 'hooks (setq value t))
|
||||
(run-hooks 'hooks)
|
||||
(expect value)
|
||||
(expect hooks :to-be nil)))
|
||||
(it "advises a function with a transient advisor"
|
||||
(let (value)
|
||||
(add-transient-hook! #'ignore (setq value (not value)))
|
||||
(ignore t)
|
||||
(expect value)
|
||||
;; repeat to ensure it was only run once
|
||||
(ignore t)
|
||||
(expect value))))
|
||||
|
||||
(describe "(un)setq-hook!"
|
||||
:var (fake-hook x y z)
|
||||
(before-each
|
||||
(setq x 10 y 20 z 30))
|
||||
|
||||
(it "sets variables buffer-locally"
|
||||
(setq-hook! 'fake-hook x 1)
|
||||
(with-temp-buffer
|
||||
(run-hooks 'fake-hook)
|
||||
(expect (local-variable-p 'x))
|
||||
(expect (= x 1)))
|
||||
(expect (= x 10)))
|
||||
|
||||
(it "overwrites earlier hooks"
|
||||
(setq-hook! 'fake-hook x 1 y 0)
|
||||
(setq-hook! 'fake-hook x 5 y -1)
|
||||
(with-temp-buffer
|
||||
(run-hooks 'fake-hook)
|
||||
(expect (= x 5))
|
||||
(expect (= y -1))))
|
||||
|
||||
(it "unset setq hooks"
|
||||
(setq-hook! 'fake-hook x 1 y 0)
|
||||
(unsetq-hook! 'fake-hook y)
|
||||
(with-temp-buffer
|
||||
(run-hooks 'fake-hook)
|
||||
(expect (local-variable-p 'x))
|
||||
(expect (= x 1))
|
||||
(expect (not (local-variable-p 'y)))
|
||||
(expect (= y 20))))))
|
||||
|
||||
(describe "load!"
|
||||
(before-each
|
||||
(spy-on 'load :and-return-value t))
|
||||
|
||||
(it "loads a file relative to the current directory"
|
||||
(load! "path")
|
||||
(expect 'load :to-have-been-called)
|
||||
(expect 'load :to-have-been-called-with
|
||||
(expand-file-name "path" (eval-when-compile (dir!))) nil 'nomessage))
|
||||
|
||||
(it "loads a file relative to a specified directory"
|
||||
(load! "path" doom-etc-dir)
|
||||
(expect 'load :to-have-been-called-with
|
||||
(expand-file-name "path" doom-etc-dir) nil 'nomessage)))
|
||||
|
||||
(describe "quiet!"
|
||||
:var (doom-debug-mode)
|
||||
(before-each
|
||||
(setq doom-debug-mode nil))
|
||||
|
||||
(it "suppresses output from message"
|
||||
(expect (message "hello world") :to-output "hello world\n")
|
||||
(expect (message "hello world") :to-output)
|
||||
(let (doom-interactive-mode)
|
||||
(expect (quiet! (message "hello world")) :not :to-output))
|
||||
(let ((doom-interactive-mode t))
|
||||
(expect (quiet! inhibit-message))
|
||||
(expect (quiet! save-silently))))
|
||||
|
||||
(it "suppresses load messages from `load' & `load-file'"
|
||||
(let ((tmpfile (make-temp-file "test" nil ".el")))
|
||||
(with-temp-file tmpfile)
|
||||
(let (doom-interactive-mode)
|
||||
(expect (load-file tmpfile) :to-output (format "Loading %s (source)...\n" tmpfile))
|
||||
(expect (quiet! (load-file tmpfile)) :not :to-output))
|
||||
(delete-file tmpfile)))
|
||||
|
||||
(it "won't suppress output in debug mode"
|
||||
(let ((doom-debug-mode t)
|
||||
(tmpfile (make-temp-file "test" nil ".el")))
|
||||
(dolist (doom-interactive-mode (list t nil))
|
||||
(expect (quiet! (message "hello world"))
|
||||
:to-output "hello world\n")
|
||||
(with-temp-file tmpfile)
|
||||
(expect (quiet! (load-file tmpfile))
|
||||
:to-output (format "Loading %s (source)...\n" tmpfile)))))))
|
||||
23
.emacs.d/core/test/test-core-modules.el
Normal file
23
.emacs.d/core/test/test-core-modules.el
Normal file
@@ -0,0 +1,23 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core-modules.el
|
||||
|
||||
(xdescribe "core-modules"
|
||||
(require 'core-modules)
|
||||
|
||||
(describe "doom!")
|
||||
(describe "doom-modules")
|
||||
|
||||
(describe "doom-module-p")
|
||||
(describe "doom-module-get")
|
||||
(describe "doom-module-put")
|
||||
(describe "doom-module-set")
|
||||
(describe "doom-module-path")
|
||||
(describe "doom-module-locate-path")
|
||||
(describe "doom-module-from-path")
|
||||
(describe "doom-module-load-path")
|
||||
|
||||
(describe "require!")
|
||||
(describe "featurep!")
|
||||
(describe "after!")
|
||||
(describe "use-package!")
|
||||
(describe "use-package-hook!"))
|
||||
5
.emacs.d/core/test/test-core-packages.el
Normal file
5
.emacs.d/core/test/test-core-packages.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core-packages.el
|
||||
;;;###if nil
|
||||
|
||||
(xdescribe "core-packages")
|
||||
40
.emacs.d/core/test/test-core-projects.el
Normal file
40
.emacs.d/core/test/test-core-projects.el
Normal file
@@ -0,0 +1,40 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core-projects.el
|
||||
|
||||
(describe "core/projects"
|
||||
:var (projectile-enable-caching)
|
||||
|
||||
(require 'core-projects)
|
||||
(require 'projectile)
|
||||
|
||||
(before-each
|
||||
(setq projectile-enable-caching nil)
|
||||
(projectile-mode +1))
|
||||
(after-each
|
||||
(projectile-mode -1))
|
||||
|
||||
(describe "project-p"
|
||||
(it "Should detect when in a valid project"
|
||||
(expect (doom-project-p doom-emacs-dir)))
|
||||
(it "Should detect when not in a valid project"
|
||||
(expect (doom-project-p (expand-file-name "~")) :to-be nil)))
|
||||
|
||||
(describe "project-root"
|
||||
(it "should resolve to the project's root"
|
||||
(expect (doom-project-root doom-core-dir) :to-equal-file doom-emacs-dir))
|
||||
(it "should return nil if not in a project"
|
||||
(expect (doom-project-root (expand-file-name "~")) :to-be nil)))
|
||||
|
||||
(describe "project-expand"
|
||||
(it "expands to a path relative to the project root"
|
||||
(expect (doom-project-expand "init.el" doom-core-dir) :to-equal-file
|
||||
(expand-file-name "init.el" (doom-project-root doom-core-dir)))))
|
||||
|
||||
(describe "project-file-exists-p!"
|
||||
(let ((default-directory doom-core-dir))
|
||||
;; Resolve from project root
|
||||
(expect (project-file-exists-p! "init.el"))
|
||||
;; Chained file checks
|
||||
(expect (project-file-exists-p! (and "init.el" "LICENSE")))
|
||||
(expect (project-file-exists-p! (or "init.el" "does-not-exist")))
|
||||
(expect (project-file-exists-p! (and "init.el" (or "LICENSE" "does-not-exist")))))))
|
||||
106
.emacs.d/core/test/test-core-ui.el
Normal file
106
.emacs.d/core/test/test-core-ui.el
Normal file
@@ -0,0 +1,106 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ../core/test/test-core-ui.el
|
||||
|
||||
(describe "core/ui"
|
||||
(before-all
|
||||
(with-demoted-errors "Import error: %s"
|
||||
(require 'core-ui)))
|
||||
|
||||
(describe "doom-protect-fallback-buffer-h"
|
||||
:var (kill-buffer-query-functions)
|
||||
(before-all
|
||||
(setq kill-buffer-query-functions '(doom-protect-fallback-buffer-h)))
|
||||
|
||||
(it "should kill other buffers"
|
||||
(expect (kill-buffer (get-buffer-create "a"))))
|
||||
|
||||
(it "shouldn't kill the fallback buffer"
|
||||
(expect (not (kill-buffer (doom-fallback-buffer))))))
|
||||
|
||||
(describe "custom hooks"
|
||||
(describe "switch hooks"
|
||||
:var (before-hook after-hook a b)
|
||||
(before-each
|
||||
(setq a (switch-to-buffer (get-buffer-create "a"))
|
||||
b (get-buffer-create "b"))
|
||||
(spy-on 'hook)
|
||||
(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-buffer display-buffer))
|
||||
(advice-add fn :around #'doom-run-switch-buffer-hooks-a)))
|
||||
(after-each
|
||||
(remove-hook 'buffer-list-update-hook #'doom-run-switch-window-hooks-h)
|
||||
(remove-hook 'focus-in-hook #'doom-run-switch-frame-hooks-h)
|
||||
(dolist (fn '(switch-to-buffer display-buffer))
|
||||
(advice-remove fn #'doom-run-switch-buffer-hooks-a))
|
||||
(kill-buffer a)
|
||||
(kill-buffer b))
|
||||
|
||||
(describe "switch-buffer"
|
||||
:var (doom-switch-buffer-hook)
|
||||
(before-each
|
||||
(setq doom-switch-buffer-hook '(hook)))
|
||||
(after-each
|
||||
(setq doom-switch-buffer-hook nil))
|
||||
|
||||
(it "should trigger when switching buffers"
|
||||
(switch-to-buffer b)
|
||||
(switch-to-buffer a)
|
||||
(switch-to-buffer b)
|
||||
(expect 'hook :to-have-been-called-times 3))
|
||||
|
||||
(it "should trigger only once on the same buffer"
|
||||
(switch-to-buffer b)
|
||||
(switch-to-buffer b)
|
||||
(switch-to-buffer a)
|
||||
(expect 'hook :to-have-been-called-times 2)))
|
||||
|
||||
|
||||
(describe "switch-window"
|
||||
:var (doom-switch-window-hook x y)
|
||||
(before-each
|
||||
(delete-other-windows)
|
||||
(setq x (get-buffer-window a)
|
||||
y (save-selected-window (split-window)))
|
||||
(with-selected-window y
|
||||
(switch-to-buffer b))
|
||||
(select-window x)
|
||||
(spy-calls-reset 'hook)
|
||||
(setq doom-switch-window-hook '(hook)))
|
||||
|
||||
(it "should trigger when switching windows"
|
||||
(select-window y)
|
||||
(select-window x)
|
||||
(select-window y)
|
||||
(expect 'hook :to-have-been-called-times 3))
|
||||
|
||||
(it "should trigger only once on the same window"
|
||||
(select-window y)
|
||||
(select-window y)
|
||||
(select-window x)
|
||||
(expect 'hook :to-have-been-called-times 2)))
|
||||
|
||||
|
||||
(xdescribe "switch-frame"
|
||||
:var (doom-switch-frame-hook x y)
|
||||
(before-each
|
||||
(delete-other-windows)
|
||||
(setq x (get-buffer-window a)
|
||||
y (save-selected-window (split-window)))
|
||||
(with-selected-window y
|
||||
(switch-to-buffer b))
|
||||
(select-window x)
|
||||
(spy-calls-reset 'hook)
|
||||
(setq doom-switch-window-hook '(hook)))
|
||||
|
||||
(it "should trigger when switching windows"
|
||||
(select-window y)
|
||||
(select-window x)
|
||||
(select-window y)
|
||||
(expect 'hook :to-have-been-called-times 3))
|
||||
|
||||
(it "should trigger only once on the same window"
|
||||
(select-window y)
|
||||
(select-window y)
|
||||
(select-window x)
|
||||
(expect 'hook :to-have-been-called-times 2))))))
|
||||
127
.emacs.d/core/test/test-core.el
Normal file
127
.emacs.d/core/test/test-core.el
Normal file
@@ -0,0 +1,127 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; core/test/test-core.el
|
||||
|
||||
(describe "core"
|
||||
:var (doom-interactive-mode)
|
||||
(before-each
|
||||
(setq doom-interactive-mode nil))
|
||||
|
||||
(describe "initialization"
|
||||
(describe "doom-initialize"
|
||||
:var (doom-init-p)
|
||||
(before-each
|
||||
(setq doom-init-p nil))
|
||||
|
||||
(it "initializes once"
|
||||
(expect (doom-initialize nil 'noerror))
|
||||
(expect (not (doom-initialize nil 'noerror)))
|
||||
(expect (not (doom-initialize nil 'noerror)))
|
||||
(expect doom-init-p))
|
||||
|
||||
(it "initializes multiple times, if forced"
|
||||
(expect (doom-initialize nil 'noerror))
|
||||
(expect (not (doom-initialize nil 'noerror)))
|
||||
(expect (doom-initialize 'force 'noerror)))
|
||||
|
||||
(describe "package initialization"
|
||||
(before-each
|
||||
(spy-on 'doom-initialize-packages :and-return-value t))
|
||||
|
||||
(it "initializes packages if core autoload file doesn't exist"
|
||||
(let ((doom-autoload-file "doesnotexist"))
|
||||
(expect (doom-initialize nil 'noerror))
|
||||
(expect 'doom-initialize-packages :to-have-been-called))
|
||||
|
||||
(it "doesn't initialize packages if core autoload file was loaded"
|
||||
(let ((doom-interactive-mode t))
|
||||
(spy-on 'doom-load-autoloads-file :and-return-value t)
|
||||
(doom-initialize nil 'noerror)
|
||||
(expect 'doom-load-autoloads-file :to-have-been-called-with doom-package-autoload-file)
|
||||
(expect 'doom-initialize-packages :to-have-been-called)))
|
||||
|
||||
(it "initializes packages when forced"
|
||||
(doom-initialize 'force 'noerror)
|
||||
(expect 'doom-initialize-packages :to-have-been-called)))
|
||||
|
||||
(describe "autoloads files"
|
||||
(before-each
|
||||
(spy-on 'doom-load-autoloads-file)
|
||||
(spy-on 'warn :and-return-value t))
|
||||
|
||||
(it "loads autoloads files"
|
||||
(ignore-errors (doom-initialize nil 'noerror))
|
||||
(expect 'doom-load-autoloads-file
|
||||
:to-have-been-called-with doom-autoload-file)
|
||||
(expect 'doom-load-autoloads-file
|
||||
:to-have-been-called-with doom-package-autoload-file))
|
||||
|
||||
(it "throws doom-autoload-error when autoload files don't exist"
|
||||
(let ((doom-autoload-file "doesnotexist")
|
||||
(doom-package-autoload-file "doesnotexist"))
|
||||
(expect (doom-initialize) :to-throw 'doom-autoload-error)))))
|
||||
|
||||
(describe "doom-initialize-core"
|
||||
(before-each
|
||||
(spy-on 'require))
|
||||
|
||||
(it "loads all doom core libraries"
|
||||
(doom-initialize-core)
|
||||
(expect 'require :to-have-been-called-with 'core-keybinds)
|
||||
(expect 'require :to-have-been-called-with 'core-ui)
|
||||
(expect 'require :to-have-been-called-with 'core-projects)
|
||||
(expect 'require :to-have-been-called-with 'core-editor))))
|
||||
|
||||
(describe "doom-load-autoloads-file"
|
||||
:var (doom-autoload-file doom-alt-autoload-file result)
|
||||
(before-each
|
||||
(setq doom-autoload-file (make-temp-file "doom-autoload" nil ".el"))
|
||||
(with-temp-file doom-autoload-file)
|
||||
(byte-compile-file doom-autoload-file))
|
||||
(after-each
|
||||
(delete-file doom-autoload-file)
|
||||
(delete-file (byte-compile-dest-file doom-autoload-file)))
|
||||
|
||||
(it "loads the byte-compiled autoloads file if available"
|
||||
(doom-load-autoloads-file doom-autoload-file)
|
||||
(expect (caar load-history) :to-equal-file
|
||||
(byte-compile-dest-file doom-autoload-file))
|
||||
|
||||
(delete-file (byte-compile-dest-file doom-autoload-file))
|
||||
(doom-load-autoloads-file doom-autoload-file)
|
||||
(expect (caar load-history) :to-equal-file doom-autoload-file))
|
||||
|
||||
(it "returns non-nil if successful"
|
||||
(expect (doom-load-autoloads-file doom-autoload-file)))
|
||||
|
||||
(it "returns nil on failure or error, non-fatally"
|
||||
(expect (doom-load-autoloads-file "/does/not/exist") :to-be nil)))
|
||||
|
||||
(describe "doom-load-envvars-file"
|
||||
:var (doom-env-file process-environment)
|
||||
(before-each
|
||||
(setq process-environment nil
|
||||
doom-env-file (make-temp-file "doom-env"))
|
||||
(with-temp-file doom-env-file
|
||||
(insert "A=1\nB=2\nC=3\n")))
|
||||
(after-each
|
||||
(delete-file doom-env-file))
|
||||
|
||||
(it "throws a file-error if file doesn't exist"
|
||||
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist")
|
||||
:to-throw 'file-error))
|
||||
|
||||
(it "to fail silently if NOERROR is non-nil"
|
||||
(expect (doom-load-envvars-file "/tmp/envvardoesnotexist" 'noerror)
|
||||
:not :to-throw))
|
||||
|
||||
(it "returns the new value for `process-environment'"
|
||||
(expect (doom-load-envvars-file doom-env-file)
|
||||
:to-have-same-items-as '("A" "B" "C")))
|
||||
|
||||
(it "alters environment variables"
|
||||
(dolist (key '("A" "B" "C"))
|
||||
(expect (getenv key) :not :to-be-truthy))
|
||||
(expect (doom-load-envvars-file doom-env-file))
|
||||
(expect (getenv "A") :to-equal "1")
|
||||
(expect (getenv "B") :to-equal "2")
|
||||
(expect (getenv "C") :to-equal "3"))))
|
||||
569
.emacs.d/docs/api.org
Normal file
569
.emacs.d/docs/api.org
Normal file
@@ -0,0 +1,569 @@
|
||||
#+TITLE: API Demos
|
||||
#+PROPERTY: header-args:elisp :results pp
|
||||
|
||||
This appendix serves as a reference on how to use Doom Emacs' standard library.
|
||||
It is integrated into Helpful, in Doom.
|
||||
|
||||
* Table of Contents :TOC_3:
|
||||
- [[#examples-for-dooms-library][Examples for Doom's library]]
|
||||
- [[#core-lib][core-lib]]
|
||||
- [[#add-hook][add-hook!]]
|
||||
- [[#add-transient-hook][add-transient-hook!]]
|
||||
- [[#after][after!]]
|
||||
- [[#appendq][appendq!]]
|
||||
- [[#custom-set-faces][custom-set-faces!]]
|
||||
- [[#custom-theme-set-faces][custom-theme-set-faces!]]
|
||||
- [[#defer-feature][defer-feature!]]
|
||||
- [[#defer-until][defer-until!]]
|
||||
- [[#disable-packages][disable-packages!]]
|
||||
- [[#doom][doom!]]
|
||||
- [[#file-exists-p][file-exists-p!]]
|
||||
- [[#lambda][lambda!]]
|
||||
- [[#lambda-1][lambda!!]]
|
||||
- [[#letenv][letenv!]]
|
||||
- [[#load][load!]]
|
||||
- [[#map][map!]]
|
||||
- [[#package][package!]]
|
||||
- [[#pushnew][pushnew!]]
|
||||
- [[#prependq][prependq!]]
|
||||
- [[#quiet][quiet!]]
|
||||
- [[#remove-hook][remove-hook!]]
|
||||
- [[#setq-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]]
|
||||
- [[#persist-emacs-initial-frame-position-dimensions-andor-full-screen-state-across-sessions][Persist Emacs' initial frame position, dimensions and/or full-screen state across sessions]]
|
||||
- [[#update-cursor-shape-under-terminal-emacs][Update cursor shape under terminal Emacs]]
|
||||
|
||||
* Examples for Doom's library
|
||||
** core-lib
|
||||
*** add-hook!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; With only one hook and one function, this is identical to `add-hook'. In that
|
||||
;; case, use that instead.
|
||||
(add-hook! 'some-mode-hook #'enable-something)
|
||||
|
||||
;; Adding many-to-many functions to hooks
|
||||
(add-hook! some-mode #'enable-something #'and-another)
|
||||
(add-hook! some-mode #'(enable-something and-another))
|
||||
(add-hook! '(one-mode-hook second-mode-hook) #'enable-something)
|
||||
(add-hook! (one-mode second-mode) #'enable-something)
|
||||
|
||||
;; Appending and local hooks
|
||||
(add-hook! (one-mode second-mode) :append #'enable-something)
|
||||
(add-hook! (one-mode second-mode) :local #'enable-something)
|
||||
|
||||
;; With arbitrary forms
|
||||
(add-hook! (one-mode second-mode) (setq v 5) (setq a 2))
|
||||
(add-hook! (one-mode second-mode) :append :local (setq v 5) (setq a 2))
|
||||
|
||||
;; Inline named hook functions
|
||||
(add-hook! '(one-mode-hook second-mode-hook)
|
||||
(defun do-something ()
|
||||
...)
|
||||
(defun do-another-thing ()
|
||||
...))
|
||||
#+END_SRC
|
||||
|
||||
*** TODO add-transient-hook!
|
||||
*** after!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;;; `after!' will take:
|
||||
|
||||
;; An unquoted package symbol (the name of a package)
|
||||
(after! helm ...)
|
||||
|
||||
;; An unquoted list of package symbols (i.e. BODY is evaluated once both magit
|
||||
;; and git-gutter have loaded)
|
||||
(after! (magit git-gutter) ...)
|
||||
|
||||
;; An unquoted, nested list of compound package lists, using any combination of
|
||||
;; :or/:any and :and/:all
|
||||
(after! (:or package-a package-b ...) ...)
|
||||
(after! (:and package-a package-b ...) ...)
|
||||
(after! (:and package-a (:or package-b package-c) ...) ...)
|
||||
;; (Without :or/:any/:and/:all, :and/:all are implied.)
|
||||
|
||||
;; A common mistake is to pass it the names of major or minor modes, e.g.
|
||||
(after! rustic-mode ...)
|
||||
(after! python-mode ...)
|
||||
;; But the code in them will never run! rustic-mode is in the `rustic' package
|
||||
;; and python-mode is in the `python' package. This is what you want:
|
||||
(after! rustic ...)
|
||||
(after! python ...)
|
||||
#+END_SRC
|
||||
*** appendq!
|
||||
#+BEGIN_SRC elisp
|
||||
(let ((x '(a b c)))
|
||||
(appendq! x '(c d e))
|
||||
x)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: (a b c c d e)
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
(let ((x '(a b c))
|
||||
(y '(c d e))
|
||||
(z '(f g)))
|
||||
(appendq! x y z '(h))
|
||||
x)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: (a b c c d e f g h)
|
||||
|
||||
*** custom-set-faces!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(custom-set-faces!
|
||||
'(outline-1 :weight normal)
|
||||
'(outline-2 :weight normal)
|
||||
'(outline-3 :weight normal)
|
||||
'(outline-4 :weight normal)
|
||||
'(outline-5 :weight normal)
|
||||
'(outline-6 :weight normal)
|
||||
'(default :background "red" :weight bold)
|
||||
'(region :background "red" :weight bold))
|
||||
|
||||
(custom-set-faces!
|
||||
'((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6)
|
||||
:weight normal)
|
||||
'((default region)
|
||||
:background "red" :weight bold))
|
||||
|
||||
(let ((red-bg-faces '(default region)))
|
||||
(custom-set-faces!
|
||||
`(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i)))
|
||||
:weight normal)
|
||||
`(,red-bg-faces
|
||||
:background "red" :weight bold)))
|
||||
|
||||
;; If you want to make use of the `doom-themes' package API (e.g. `doom-color',
|
||||
;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!'
|
||||
;; *after* the theme has been loaded. e.g.
|
||||
(load-theme 'doom-one t)
|
||||
(custom-set-faces!
|
||||
`(outline-1 :foreground ,(doom-color 'red))
|
||||
`(outline-2 :background ,(doom-color 'blue)))
|
||||
#+END_SRC
|
||||
|
||||
*** custom-theme-set-faces!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(custom-theme-set-faces! 'doom-one-theme
|
||||
'(outline-1 :weight normal)
|
||||
'(outline-2 :weight normal)
|
||||
'(outline-3 :weight normal)
|
||||
'(outline-4 :weight normal)
|
||||
'(outline-5 :weight normal)
|
||||
'(outline-6 :weight normal)
|
||||
'(default :background "red" :weight bold)
|
||||
'(region :background "red" :weight bold))
|
||||
|
||||
(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme)
|
||||
'((outline-1 outline-2 outline-3 outline-4 outline-5 outline-6)
|
||||
:weight normal)
|
||||
'((default region)
|
||||
:background "red" :weight bold))
|
||||
|
||||
(let ((red-bg-faces '(default region)))
|
||||
(custom-theme-set-faces! '(doom-one-theme doom-one-light-theme)
|
||||
`(,(cl-loop for i from 0 to 6 collect (intern (format "outline-%d" i)))
|
||||
:weight normal)
|
||||
`(,red-bg-faces
|
||||
:background "red" :weight bold)))
|
||||
|
||||
;; If you want to make use of the `doom-themes' package API (e.g. `doom-color',
|
||||
;; `doom-lighten', `doom-darken', etc.), you must use `custom-set-faces!'
|
||||
;; *after* the theme has been loaded. e.g.
|
||||
(load-theme 'doom-one t)
|
||||
(custom-theme-set-faces! 'doom-one
|
||||
`(outline-1 :foreground ,(doom-color 'red))
|
||||
`(outline-2 :background ,(doom-color 'blue)))
|
||||
#+END_SRC
|
||||
|
||||
*** TODO defer-feature!
|
||||
*** TODO defer-until!
|
||||
*** disable-packages!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; Disable packages enabled by DOOM
|
||||
(disable-packages! some-package second-package)
|
||||
#+END_SRC
|
||||
|
||||
*** doom!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(doom! :completion
|
||||
company
|
||||
ivy
|
||||
;;helm
|
||||
|
||||
:tools
|
||||
(:if IS-MAC macos)
|
||||
docker
|
||||
lsp
|
||||
|
||||
:lang
|
||||
(cc +lsp)
|
||||
(:cond ((string= system-name "work-pc")
|
||||
python
|
||||
rust
|
||||
web)
|
||||
((string= system-name "writing-pc")
|
||||
(org +dragndrop)
|
||||
ruby))
|
||||
(:if IS-LINUX
|
||||
(web +lsp)
|
||||
web)
|
||||
|
||||
:config
|
||||
literate
|
||||
(default +bindings +smartparens))
|
||||
#+END_SRC
|
||||
|
||||
*** file-exists-p!
|
||||
#+BEGIN_SRC elisp
|
||||
(file-exists-p! "init.el" doom-emacs-dir)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: /home/hlissner/.emacs.d/init.el
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
(file-exists-p! (and (or "doesnotexist" "init.el")
|
||||
"LICENSE")
|
||||
doom-emacs-dir)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: /home/hlissner/.emacs.d/LICENSE
|
||||
|
||||
*** lambda!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(map! "C-j" (lambda! (newline) (indent-according-to-mode)))
|
||||
|
||||
;; The `λ!' short-form alias exists. If you have the snippets module enabled and
|
||||
;; Doom's default snippets, the 'lam' snippet will expand into 'λ!'. Otherwise,
|
||||
;; you can use `lambda!'.
|
||||
(map! "C-j" (λ! (newline) (indent-according-to-mode)))
|
||||
#+END_SRC
|
||||
*** lambda!!
|
||||
When ~newline~ is passed a numerical prefix argument (=C-u 5 M-x newline=), it
|
||||
inserts N newlines. We can use ~lambda!!~ to easily create a keybinds that bakes
|
||||
in the prefix arg into the command call:
|
||||
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(map! "C-j" (lambda!! #'newline 5))
|
||||
|
||||
;; The `λ!!' short-form alias exists. If you have the snippets module enabled
|
||||
;; and Doom's default snippets, a 'lam' snippet is available to expand into
|
||||
;; 'λ!'. Otherwise, you can use `lambda!!'.
|
||||
(map! "C-j" (λ!! #'newline 5))
|
||||
#+END_SRC
|
||||
|
||||
Or to create aliases for functions that behave differently:
|
||||
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(fset 'insert-5-newlines (lambda!! #'newline 5))
|
||||
|
||||
;; The equivalent of C-u M-x org-global-cycle, which resets the org document to
|
||||
;; its startup visibility settings.
|
||||
(fset 'org-reset-global-visibility (lambda!! #'org-global-cycle '(4))
|
||||
#+END_SRC
|
||||
*** letenv!
|
||||
#+BEGIN_SRC elisp
|
||||
(letenv! (("SHELL" "/bin/sh"))
|
||||
(shell-command-to-string "echo $SHELL"))
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: "/bin/sh\n"
|
||||
|
||||
*** load!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;;; Lets say we're in ~/.doom.d/config.el
|
||||
(load! "lisp/module") ; loads ~/.doom.d/lisp/module.el
|
||||
(load! "somefile" doom-emacs-dir) ; loads ~/.emacs.d/somefile.el
|
||||
(load! "anotherfile" doom-private-dir) ; loads ~/.doom.d/anotherfile.el
|
||||
|
||||
;; If you don't want a `load!' call to throw an error if the file doesn't exist:
|
||||
(load! "~/.maynotexist" nil t)
|
||||
#+END_SRC
|
||||
|
||||
*** map!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(map! :map magit-mode-map
|
||||
:m "C-r" 'do-something ; C-r in motion state
|
||||
:nv "q" 'magit-mode-quit-window ; q in normal+visual states
|
||||
"C-x C-r" 'a-global-keybind
|
||||
:g "C-x C-r" 'another-global-keybind ; same as above
|
||||
|
||||
(:when IS-MAC
|
||||
:n "M-s" 'some-fn
|
||||
:i "M-o" (lambda (interactive) (message "Hi"))))
|
||||
|
||||
(map! (:when (featurep! :completion company) ; Conditional loading
|
||||
:i "C-@" #'+company/complete
|
||||
(:prefix "C-x" ; Use a prefix key
|
||||
:i "C-l" #'+company/whole-lines)))
|
||||
|
||||
(map! (:when (featurep! :lang latex) ; local conditional
|
||||
(:map LaTeX-mode-map
|
||||
:localleader ; Use local leader
|
||||
:desc "View" "v" #'TeX-view)) ; Add which-key description
|
||||
:leader ; Use leader key from now on
|
||||
:desc "Eval expression" ";" #'eval-expression)
|
||||
#+END_SRC
|
||||
|
||||
These are side-by-side comparisons, showing how to bind keys with and without
|
||||
~map!~:
|
||||
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; bind a global key
|
||||
(global-set-key (kbd "C-x y") #'do-something)
|
||||
(map! "C-x y" #'do-something)
|
||||
|
||||
;; bind a key on a keymap
|
||||
(define-key emacs-lisp-mode (kbd "C-c p") #'do-something)
|
||||
(map! :map emacs-lisp-mode "C-c p" #'do-something)
|
||||
|
||||
;; unbind a key defined elsewhere
|
||||
(define-key lua-mode-map (kbd "SPC m b") nil)
|
||||
(map! :map lua-mode-map "SPC m b" nil)
|
||||
|
||||
;; bind multiple keys
|
||||
(global-set-key (kbd "C-x x") #'do-something)
|
||||
(global-set-key (kbd "C-x y") #'do-something-else)
|
||||
(global-set-key (kbd "C-x z") #'do-another-thing)
|
||||
(map! "C-x x" #'do-something
|
||||
"C-x y" #'do-something-else
|
||||
"C-x z" #'do-another-thing)
|
||||
|
||||
;; bind global keys in normal mode
|
||||
(evil-define-key* 'normal 'global
|
||||
(kbd "C-x x") #'do-something
|
||||
(kbd "C-x y") #'do-something-else
|
||||
(kbd "C-x z") #'do-another-thing)
|
||||
(map! :n "C-x x" #'do-something
|
||||
:n "C-x y" #'do-something-else
|
||||
:n "C-x z" #'do-another-thing)
|
||||
|
||||
;; or on a deferred keymap
|
||||
(evil-define-key 'normal emacs-lisp-mode-map
|
||||
(kbd "C-x x") #'do-something
|
||||
(kbd "C-x y") #'do-something-else
|
||||
(kbd "C-x z") #'do-another-thing)
|
||||
(map! :map emacs-lisp-mode-map
|
||||
:n "C-x x" #'do-something
|
||||
:n "C-x y" #'do-something-else
|
||||
:n "C-x z" #'do-another-thing)
|
||||
|
||||
;; or multiple maps
|
||||
(dolist (map (list emacs-lisp-mode go-mode-map ivy-minibuffer-map))
|
||||
(evil-define-key '(normal insert) map
|
||||
"a" #'a
|
||||
"b" #'b
|
||||
"c" #'c))
|
||||
(map! :map (emacs-lisp-mode go-mode-map ivy-minibuffer-map)
|
||||
:ni "a" #'a
|
||||
:ni "b" #'b
|
||||
:ni "c" #'c)
|
||||
|
||||
;; or in multiple states (order of states doesn't matter)
|
||||
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
|
||||
(evil-define-key* 'insert emacs-lisp-mode-map (kbd "C-x x") #'do-something-else)
|
||||
(evil-define-key* '(visual normal insert emacs) emacs-lisp-mode-map (kbd "C-x z") #'do-another-thing)
|
||||
(map! :map emacs-lisp-mode
|
||||
:nv "C-x x" #'do-something ; normal+visual
|
||||
:i "C-x y" #'do-something-else ; insert
|
||||
:vnie "C-x z" #'do-another-thing) ; visual+normal+insert+emacs
|
||||
|
||||
;; You can nest map! calls:
|
||||
(evil-define-key* '(normal visual) emacs-lisp-mode-map (kbd "C-x x") #'do-something)
|
||||
(evil-define-key* 'normal go-lisp-mode-map (kbd "C-x x") #'do-something-else)
|
||||
(map! (:map emacs-lisp-mode :nv "C-x x" #'do-something)
|
||||
(:map go-lisp-mode :n "C-x x" #'do-something-else))
|
||||
#+END_SRC
|
||||
|
||||
*** 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':
|
||||
(package! evil)
|
||||
(package! js2-mode)
|
||||
(package! rainbow-delimiters)
|
||||
|
||||
;; To disable a package included with Doom (which will no-op all its `after!'
|
||||
;; and `use-package!' blocks):
|
||||
(package! evil :disable t)
|
||||
(package! rainbow-delimiters :disable t)
|
||||
|
||||
;; To install a package from a github repo
|
||||
(package! so-long :recipe (:host github :repo "hlissner/emacs-so-long"))
|
||||
|
||||
;; If a package is particularly big and comes with submodules you don't need,
|
||||
;; 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"))
|
||||
|
||||
(package! evil :recipe (:branch "stable"))
|
||||
|
||||
(package! evil :recipe (:tag "1.2.9"))
|
||||
|
||||
;; If you share your config between two computers, and don't want bin/doom
|
||||
;; refresh to delete packages used only on one system, use :ignore
|
||||
(package! evil :ignore (not (equal system-name "my-desktop")))
|
||||
#+END_SRC
|
||||
|
||||
*** pushnew!
|
||||
#+BEGIN_SRC elisp
|
||||
(let ((list '(a b c)))
|
||||
(pushnew! list 'c 'd 'e)
|
||||
list)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: (e d a b c)
|
||||
|
||||
*** prependq!
|
||||
#+BEGIN_SRC elisp
|
||||
(let ((x '(a b c)))
|
||||
(prependq! x '(c d e))
|
||||
x)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: (c d e a b c)
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
(let ((x '(a b c))
|
||||
(y '(c d e))
|
||||
(z '(f g)))
|
||||
(prependq! x y z '(h))
|
||||
x)
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: (c d e f g h a b c)
|
||||
|
||||
*** quiet!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; Enters recentf-mode without extra output
|
||||
(quiet! (recentf-mode +1))
|
||||
#+END_SRC
|
||||
*** remove-hook!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; With only one hook and one function, this is identical to `remove-hook'. In
|
||||
;; that case, use that instead.
|
||||
(remove-hook! 'some-mode-hook #'enable-something)
|
||||
|
||||
;; Removing N functions from M hooks
|
||||
(remove-hook! some-mode #'enable-something #'and-another)
|
||||
(remove-hook! some-mode #'(enable-something and-another))
|
||||
(remove-hook! '(one-mode-hook second-mode-hook) #'enable-something)
|
||||
(remove-hook! (one-mode second-mode) #'enable-something)
|
||||
|
||||
;; Removing buffer-local hooks
|
||||
(remove-hook! (one-mode second-mode) :local #'enable-something)
|
||||
|
||||
;; 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-hook!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; Set multiple variables after a hook
|
||||
(setq-hook! 'markdown-mode-hook
|
||||
line-spacing 2
|
||||
fill-column 80)
|
||||
|
||||
;; Set variables after multiple hooks
|
||||
(setq-hook! '(eshell-mode-hook term-mode-hook)
|
||||
hscroll-margin 0)
|
||||
#+END_SRC
|
||||
|
||||
*** unsetq-hook!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
(unsetq-hook! 'markdown-mode-hook line-spacing)
|
||||
|
||||
;; Removes the following variable hook
|
||||
(setq-hook! 'markdown-mode-hook line-spacing 2)
|
||||
|
||||
;; Removing N variables from M hooks
|
||||
(unsetq-hook! some-mode enable-something and-another)
|
||||
(unsetq-hook! some-mode (enable-something and-another))
|
||||
(unsetq-hook! '(one-mode-hook second-mode-hook) enable-something)
|
||||
(unsetq-hook! (one-mode second-mode) enable-something)
|
||||
#+END_SRC
|
||||
|
||||
*** use-package!
|
||||
#+BEGIN_SRC elisp :eval no
|
||||
;; Use after-call to load package before hook
|
||||
(use-package! projectile
|
||||
:after-call (pre-command-hook after-find-file dired-before-readin-hook))
|
||||
|
||||
;; defer recentf packages one by one
|
||||
(use-package! recentf
|
||||
:defer-incrementally easymenu tree-widget timer
|
||||
:after-call after-find-file)
|
||||
|
||||
;; This is equivalent to :defer-incrementally (abc)
|
||||
(use-package! abc
|
||||
:defer-incrementally t)
|
||||
#+END_SRC
|
||||
* Interesting snippets
|
||||
** Persist Emacs' initial frame size across sessions
|
||||
#+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)))))
|
||||
#+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))
|
||||
(cl-destructuring-bind ((left . top) width height fullscreen) dims
|
||||
(setq initial-frame-alist
|
||||
(append initial-frame-alist
|
||||
`((left . ,left)
|
||||
(top . ,top)
|
||||
(width . ,width)
|
||||
(height . ,height)
|
||||
(fullscreen . ,fullscreen))))))
|
||||
|
||||
(defun save-frame-dimensions ()
|
||||
(doom-cache-set 'last-frame-size
|
||||
(list (frame-position)
|
||||
(frame-width)
|
||||
(frame-height)
|
||||
(frame-parameter nil 'fullscreen))))
|
||||
|
||||
(add-hook 'kill-emacs-hook #'save-frame-dimensions)
|
||||
#+END_SRC
|
||||
|
||||
** Update cursor shape under terminal Emacs
|
||||
Install [[https://github.com/7696122/evil-terminal-cursor-changer][evil-terminal-cursor-changer]]. The package isn't included in Doom because
|
||||
it is not maintained, unreasonably buggy, and lacks support for a number of
|
||||
terminals. Where it fails, it inserts unexpected characters into the buffer. To
|
||||
uphold the principle of least surprise, an unchanging cursor is less surprising
|
||||
than unwarranted characters.
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
;; ~/.doom.d/packages.el
|
||||
(package! evil-terminal-cursor-changer)
|
||||
|
||||
;; ~/.doom.d/config.el
|
||||
(use-package! evil-terminal-cursor-changer
|
||||
:hook (tty-setup . evil-terminal-cursor-changer-activate))
|
||||
#+END_SRC
|
||||
|
||||
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.
|
||||
170
.emacs.d/docs/contributing.org
Normal file
170
.emacs.d/docs/contributing.org
Normal file
@@ -0,0 +1,170 @@
|
||||
#+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.
|
||||
|
||||
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.
|
||||
|
||||
* Table of Contents :TOC_3:
|
||||
- [[#where-can-i-help][Where can I help?]]
|
||||
- [[#reporting-issues][Reporting issues]]
|
||||
- [[#collect-backtraces-of-any-error-messages][Collect backtraces of any error messages]]
|
||||
- [[#create-a-step-by-step-reproduction-guide][Create a step-by-step reproduction guide]]
|
||||
- [[#include-information-about-your-doom-install][Include information about your Doom install]]
|
||||
- [[#debugging-crashes-with-gdb][Debugging crashes with gdb]]
|
||||
- [[#suggesting-features-keybinds-andor-enhancements][Suggesting features, keybinds and/or enhancements]]
|
||||
- [[#contributing-code][Contributing code]]
|
||||
- [[#conventions][Conventions]]
|
||||
- [[#code-style][Code style]]
|
||||
- [[#naming-conventions][Naming conventions]]
|
||||
- [[#commits--prs][Commits & PRs]]
|
||||
- [[#keybind-conventions][Keybind conventions]]
|
||||
- [[#your-first-code-contribution][Your first code contribution]]
|
||||
- [[#submitting-pull-requests][Submitting pull requests]]
|
||||
- [[#contributing-to-doom-core][Contributing to Doom core]]
|
||||
- [[#contributing-to-an-existing-module][Contributing to an existing module]]
|
||||
- [[#contributing-a-new-module][Contributing a new module]]
|
||||
- [[#contributing-documentation][Contributing documentation]]
|
||||
- [[#contributing-to-dooms-manual][Contributing to Doom's manual]]
|
||||
- [[#contributing-module-documentation][Contributing module documentation]]
|
||||
- [[#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!
|
||||
+ 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
|
||||
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
|
||||
open for you to implement yourself.
|
||||
+ The [[https://github.com/hlissner/doom-emacs/projects/5][upstream bugs board]] lists known issues that have external causes, but
|
||||
affect Doom. If you're feeling adventurous (or are better acquainted with the
|
||||
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:
|
||||
|
||||
+ 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.
|
||||
|
||||
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:
|
||||
|
||||
** TODO Collect backtraces of any error messages
|
||||
|
||||
** TODO Create a step-by-step reproduction guide
|
||||
|
||||
** TODO Include information about your Doom install
|
||||
|
||||
** TODO Debugging crashes with gdb
|
||||
|
||||
* TODO Suggesting features, keybinds and/or enhancements
|
||||
|
||||
* TODO Contributing code
|
||||
There's much to be done around here! We need bugfixes, new features, and
|
||||
documentation. If you'd like to convert some caffeine into Emacs Lisp, here are
|
||||
a few considerations before starting that PR:
|
||||
|
||||
** TODO Conventions
|
||||
*** TODO Code style
|
||||
Doom conforms to [[https://github.com/bbatsov/emacs-lisp-style-guide][@bbatsov's emacs-lisp style guide]] with the following
|
||||
exceptions:
|
||||
|
||||
+ Use ~mapc~ instead of ~seq-do~.
|
||||
+ No hanging parentheses
|
||||
+ We use =DEPRECATED= to indicate code that will eventually be removed.
|
||||
|
||||
*** Naming conventions
|
||||
Doom has a number of naming conventions that it uses in addition to the standard
|
||||
lisp conventions. Third party packages may use their own conventions as well.
|
||||
|
||||
**** Lisp Naming Conventions
|
||||
The lisp conventions are simple. Symbols follow ~NAMESPACE-SYMBOLNAME~ for
|
||||
public variables/functions (e.g. ~bookmark-default-file~ or
|
||||
~electric-indent-mode~) and ~NAMESPACE--SYMBOLNAME~ for private ones (e.g.
|
||||
~byte-compile--lexical-environment~ and ~yas--tables~).
|
||||
|
||||
~NAMESPACE~ is usually the name of the containing file or package. E.g. the
|
||||
~company~ plugin prefixes all its variables/functions with ~company-~.
|
||||
|
||||
**** Doom Naming Conventions
|
||||
+ ~doom/NAME~ or ~+MODULE/NAME~ :: Denotes a public command designed to be used
|
||||
interactively, via =M-x= or a keybinding. e.g. ~doom/info~, ~+popup/other~,
|
||||
~+ivy/rg~.
|
||||
+ ~doom:NAME~ :: A public evil operator, motion or command. e.g. ~+evil:align~,
|
||||
~+ivy:rg~.
|
||||
+ ~doom-[-]NAME-h~ or ~+MODULE-[-]NAME-h~ :: A non-interactive function meant to
|
||||
be used (exclusively) as a hook. e.g. ~+cc-fontify-constants-h~,
|
||||
~+flycheck-buffer-h~.
|
||||
+ ~doom-[-]NAME-a~ or ~+MODULE-[-]NAME-a~ :: Functions designed to be used as
|
||||
advice for other functions. e.g. ~doom-set-jump-a~,
|
||||
~doom--fix-broken-smie-modes-a~, ~+org--babel-lazy-load-library-a~
|
||||
+ ~doom-[-]NAME-fn~ or ~+MODULE-[-]NAME-fn~ :: Indicates an [[https://en.wikipedia.org/wiki/Strategy_pattern][strategy]] function. A
|
||||
good rule of thumb for what makes a strategy function is: is it
|
||||
interchangeable? Can it be replaced with another function with a matching
|
||||
signature? e.g. ~+lookup-dumb-jump-backend-fn~, ~+magit-display-buffer-fn~,
|
||||
~+workspaces-set-project-action-fn~
|
||||
+ ~abc!~ :: A public Doom "autodef" function or macro. An autodef should always
|
||||
be defined, even if its containing module is disabled (i.e. they will not
|
||||
throw a void-function error). The purpose of this is to avoid peppering module
|
||||
configs with conditionals or `after!` blocks before using their APIs. They
|
||||
should noop if their module is disabled, and should be zero-cost in the case
|
||||
their module is disabled.
|
||||
|
||||
Autodefs usually serve to configure Doom or a module. e.g. ~after!~,
|
||||
~set-company-backends!~, ~set-evil-initial-state!~
|
||||
|
||||
*** TODO Commits & PRs
|
||||
+ Target =develop= instead of =master=. The only exception are hotfixes!
|
||||
|
||||
*** TODO Keybind conventions
|
||||
|
||||
** TODO Your first code contribution
|
||||
|
||||
** TODO Submitting pull requests
|
||||
|
||||
** TODO Contributing to Doom core
|
||||
|
||||
** TODO Contributing to an existing module
|
||||
|
||||
** TODO Contributing a new module
|
||||
|
||||
* TODO Contributing documentation
|
||||
Doom Emacs' documentation is an ongoing effort. If you have suggestions,
|
||||
improvements, tutorials and/or articles to submit, don't hesitate to get in
|
||||
contact via our [[https://discord.gg/bcZ6P3y][Discord server]] or [[mailto:henrik@lissner.net][email]]. I appreciate any help I can get!
|
||||
|
||||
** TODO Contributing to Doom's manual
|
||||
|
||||
** TODO Contributing module documentation
|
||||
|
||||
* TODO Other ways to support Doom Emacs
|
||||
|
||||
* TODO Special thanks
|
||||
1131
.emacs.d/docs/faq.org
Normal file
1131
.emacs.d/docs/faq.org
Normal file
File diff suppressed because it is too large
Load Diff
1187
.emacs.d/docs/getting_started.org
Normal file
1187
.emacs.d/docs/getting_started.org
Normal file
File diff suppressed because it is too large
Load Diff
276
.emacs.d/docs/index.org
Normal file
276
.emacs.d/docs/index.org
Normal file
@@ -0,0 +1,276 @@
|
||||
#+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.
|
||||
|
||||
#+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 /=.
|
||||
#+end_quote
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#release-notes][Release Notes]]
|
||||
- [[#documentation][Documentation]]
|
||||
- [[#getting-started][Getting Started]]
|
||||
- [[#frequently-asked-questions][Frequently Asked Questions]]
|
||||
- [[#contributing][Contributing]]
|
||||
- [[#workflow-tips-tricks--tutorials][Workflow Tips, Tricks & Tutorials]]
|
||||
- [[#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]]
|
||||
|
||||
* 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:faq.org][Frequently Asked Questions]]
|
||||
- [[file:faq.org::*General][General]]
|
||||
- [[file:faq.org::*Configuration][Configuration]]
|
||||
- [[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
|
||||
- Other ways to support Doom Emacs
|
||||
- Special thanks
|
||||
|
||||
** TODO [[file:workflow.org][Workflow Tips, Tricks & Tutorials]]
|
||||
|
||||
* Community Resources
|
||||
** Asking for help
|
||||
- [[https://discord.gg/bcZ6P3y][Our Discord server]]
|
||||
- [[https://github.com/hlissner/doom-emacs/issues][Our issue tracker]]
|
||||
|
||||
** Project roadmap
|
||||
- [[https://github.com/hlissner/doom-emacs/projects/3][Development roadmap]] - A timeline outlining what's being worked on and when it
|
||||
is expected to be done.
|
||||
- [[https://github.com/hlissner/doom-emacs/projects/2][Plugins under review]] - A sitrep on third party plugins that we've considered,
|
||||
rejected, or awaiting integration into Doom.
|
||||
- [[https://github.com/hlissner/doom-emacs/projects/5][Upstream bugs]] - Tracks issues originating from plugins and external programs
|
||||
that Doom relies on.
|
||||
|
||||
** Tutorials & guides
|
||||
+ *Doom Emacs*
|
||||
- [[https://noelwelsh.com/posts/2019-01-10-doom-emacs.html][Noel's crash course on Doom Emacs]]
|
||||
- [[https://medium.com/@aria_39488/getting-started-with-doom-emacs-a-great-transition-from-vim-to-emacs-9bab8e0d8458][Getting Started with Doom Emacs -- a great transition from Vim to Emacs]]
|
||||
- [[https://medium.com/@aria_39488/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]]
|
||||
+ *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
|
||||
- Quick crash courses on Emacs Lisp's syntax for programmers:
|
||||
- https://learnxinyminutes.com/docs/elisp/
|
||||
- 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]]
|
||||
+ *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.
|
||||
|
||||
+ [[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
|
||||
73
.emacs.d/docs/workflow.org
Normal file
73
.emacs.d/docs/workflow.org
Normal file
@@ -0,0 +1,73 @@
|
||||
#+TITLE: Workflow tips, tricks & tutorials
|
||||
#+STARTUP: nofold
|
||||
|
||||
This page is a WIP.
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#day-1-in-doom-emacs][Day 1 in Doom Emacs]]
|
||||
- [[#an-introduction-to-modal-editing-with-evil][An introduction to modal editing with Evil]]
|
||||
- [[#an-introduction-to-vim][An introduction to Vim]]
|
||||
- [[#getting-around-the-buffer][Getting around the buffer]]
|
||||
- [[#operating-with-operators][Operating with operators]]
|
||||
- [[#get-what-you-want-with-text-objects][Get what you want with text objects]]
|
||||
- [[#ways-to-edit-multiple-places-at-once][Ways to edit multiple places at once]]
|
||||
- [[#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]]
|
||||
- [[#search--replace][Search & replace]]
|
||||
- [[#project-wide-text-search][Project-wide text search]]
|
||||
- [[#search--replace-1][Search & replace]]
|
||||
- [[#how-to-get-around-your-projects][How to get around your projects]]
|
||||
- [[#looking-up-definitionreferences][Looking up definition/references]]
|
||||
- [[#looking-up-documentation][Looking up documentation]]
|
||||
- [[#projectfile-navigation-getting-around-quickly][Project/file navigation; getting around quickly]]
|
||||
- [[#getting-around-slowly][Getting around slowly]]
|
||||
- [[#alternating-files-eg-scss-css-h-c][Alternating files (e.g. .scss<->.css, .h<->.c)]]
|
||||
- [[#looking-things-up-online-or-in-documentation][Looking things up online or in documentation]]
|
||||
- [[#creating-and-using-snippets][Creating and using snippets]]
|
||||
- [[#how-to-use-workspaces][How to use workspaces]]
|
||||
- [[#version-control-with-magit-forge--git-gutter][Version control with Magit, Forge & git-gutter]]
|
||||
- [[#how-to-write--debug-emacs-lisp][How to write & debug Emacs Lisp]]
|
||||
- [[#on-the-fly-code-evaluation--repls][On-the-fly code evaluation & REPLs]]
|
||||
- [[#using-emacs-for][Using Emacs for...]]
|
||||
- [[#writing-fiction][Writing fiction]]
|
||||
- [[#writing-papers][Writing papers]]
|
||||
- [[#composing-music][Composing music]]
|
||||
- [[#game-development][Game development]]
|
||||
- [[#web-development][Web development]]
|
||||
- [[#machine-learning][Machine learning]]
|
||||
- [[#as-an-x-client-twitter-email-rss-reader-etc][As an X client (twitter, email, RSS reader, etc)]]
|
||||
|
||||
* TODO Day 1 in Doom Emacs
|
||||
* TODO An introduction to modal editing with Evil
|
||||
** TODO An introduction to Vim
|
||||
** TODO Getting around the buffer
|
||||
** TODO Operating with operators
|
||||
** TODO Get what you want with text objects
|
||||
** TODO Ways to edit multiple places at once
|
||||
** TODO Pipe text through ex commands and programs
|
||||
** TODO Transposing/swapping text
|
||||
* TODO Managing your projects
|
||||
* TODO Search & replace
|
||||
** TODO Project-wide text search
|
||||
** TODO Search & replace
|
||||
* TODO How to get around your projects
|
||||
** TODO Looking up definition/references
|
||||
** TODO Looking up documentation
|
||||
** TODO Project/file navigation; getting around quickly
|
||||
** TODO Getting around slowly
|
||||
** TODO Alternating files (e.g. .scss<->.css, .h<->.c)
|
||||
* TODO Looking things up online or in documentation
|
||||
* TODO Creating and using snippets
|
||||
* TODO How to use workspaces
|
||||
* TODO Version control with Magit, Forge & git-gutter
|
||||
* TODO How to write & debug Emacs Lisp
|
||||
* TODO On-the-fly code evaluation & REPLs
|
||||
* TODO Using Emacs for...
|
||||
** TODO Writing fiction
|
||||
** TODO Writing papers
|
||||
** TODO Composing music
|
||||
** TODO Game development
|
||||
** TODO Web development
|
||||
** TODO Machine learning
|
||||
** TODO As an X client (twitter, email, RSS reader, etc)
|
||||
27
.emacs.d/early-init.el
Normal file
27
.emacs.d/early-init.el
Normal file
@@ -0,0 +1,27 @@
|
||||
;;; early-init.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Emacs HEAD (27+) introduces early-init.el, which is run before init.el,
|
||||
;; before package and UI initialization happens.
|
||||
|
||||
;; Defer garbage collection further back in the startup process
|
||||
(setq gc-cons-threshold most-positive-fixnum)
|
||||
|
||||
;; In Emacs 27+, package initialization occurs before `user-init-file' is
|
||||
;; 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)
|
||||
|
||||
;; Prevent the glimpse of un-styled Emacs by disabling these UI elements early.
|
||||
(push '(menu-bar-lines . 0) default-frame-alist)
|
||||
(push '(tool-bar-lines . 0) default-frame-alist)
|
||||
(push '(vertical-scroll-bars) default-frame-alist)
|
||||
|
||||
;; Resizing the Emacs frame can be a terribly expensive part of changing the
|
||||
;; font. By inhibiting this, we easily halve startup times with fonts that are
|
||||
;; larger than the system default.
|
||||
(setq frame-inhibit-implied-resize t)
|
||||
|
||||
;; Ignore X resources; its settings would be redundant with the other settings
|
||||
;; in this file and can conflict with later config (particularly where the
|
||||
;; cursor color is concerned).
|
||||
(advice-add #'x-apply-session-resources :override #'ignore)
|
||||
151
.emacs.d/init.el
Executable file → Normal file
151
.emacs.d/init.el
Executable file → Normal file
@@ -1,105 +1,56 @@
|
||||
;; ____ _____
|
||||
;; | _ \_ _| Derek Taylor (DistroTube)
|
||||
;; | | | || | http://www.youtube.com/c/DistroTube
|
||||
;; | |_| || | http://www.gitlab.com/dwt1/
|
||||
;; |____/ |_|
|
||||
;;
|
||||
;; A customized config for Emacs (https://www.gnu.org/software/emacs/)
|
||||
;; Modified by Derek Taylor (http://www.gitlab.com/dwt1/)
|
||||
;;; init.el -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Author: Henrik Lissner <henrik@lissner.net>
|
||||
;; URL: https://github.com/hlissner/doom-emacs
|
||||
;;
|
||||
;; ================= =============== =============== ======== ========
|
||||
;; \\ . . . . . . .\\ //. . . . . . .\\ //. . . . . . .\\ \\. . .\\// . . //
|
||||
;; ||. . ._____. . .|| ||. . ._____. . .|| ||. . ._____. . .|| || . . .\/ . . .||
|
||||
;; || . .|| ||. . || || . .|| ||. . || || . .|| ||. . || ||. . . . . . . ||
|
||||
;; ||. . || || . .|| ||. . || || . .|| ||. . || || . .|| || . | . . . . .||
|
||||
;; || . .|| ||. _-|| ||-_ .|| ||. . || || . .|| ||. _-|| ||-_.|\ . . . . ||
|
||||
;; ||. . || ||-' || || `-|| || . .|| ||. . || ||-' || || `|\_ . .|. .||
|
||||
;; || . _|| || || || || ||_ . || || . _|| || || || |\ `-_/| . ||
|
||||
;; ||_-' || .|/ || || \|. || `-_|| ||_-' || .|/ || || | \ / |-_.||
|
||||
;; || ||_-' || || `-_|| || || ||_-' || || | \ / | `||
|
||||
;; || `' || || `' || || `' || || | \ / | ||
|
||||
;; || .===' `===. .==='.`===. .===' /==. | \/ | ||
|
||||
;; || .==' \_|-_ `===. .===' _|_ `===. .===' _-|/ `== \/ | ||
|
||||
;; || .==' _-' `-_ `=' _-' `-_ `=' _-' `-_ /| \/ | ||
|
||||
;; || .==' _-' '-__\._-' '-_./__-' `' |. /| | ||
|
||||
;; ||.==' _-' `' | /==.||
|
||||
;; ==' _-' \/ `==
|
||||
;; \ _-' `-_ /
|
||||
;; `'' ``'
|
||||
;;
|
||||
;; These demons are not part of GNU Emacs.
|
||||
;;
|
||||
;;; License: MIT
|
||||
|
||||
(require 'package)
|
||||
(require 'erc)
|
||||
(add-to-list 'load-path "~/.emacs.d/evil")
|
||||
(setq evil-want-integration t) ;; This is optional since it's already set to t by default.
|
||||
(setq evil-want-keybinding nil)
|
||||
(require 'evil)
|
||||
(evil-mode 1)
|
||||
(add-to-list 'load-path "~/emacs-libvterm")
|
||||
(require 'vterm)
|
||||
(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.
|
||||
'(blink-cursor-delay 0.5)
|
||||
'(blink-cursor-interval 0.5)
|
||||
'(blink-cursor-mode t)
|
||||
'(display-line-numbers-type (quote relative))
|
||||
'(global-display-line-numbers-mode t)
|
||||
'(menu-bar-mode nil)
|
||||
'(minimap-window-location (quote right))
|
||||
'(package-archives
|
||||
(quote
|
||||
(("gnu" . "http://elpa.gnu.org/packages/")
|
||||
("melpa-stable" . "http://stable.melpa.org/packages/"))))
|
||||
'(package-selected-packages
|
||||
(quote
|
||||
(ipython-shell-send evil-collection goto-chg multi-term org-bullets undo-tree rainbow-mode pdf-tools emojify minimap lua-mode haskell-mode ##)))
|
||||
'(scroll-bar-mode nil)
|
||||
'(send-mail-function (quote mailclient-send-it))
|
||||
'(tool-bar-mode nil)
|
||||
'(tooltip-mode nil))
|
||||
(package-initialize)
|
||||
(add-to-list 'load-path "/home/dt/.emacs.d/elpa/which-key-3.3.0/which-key.el")
|
||||
(require 'which-key)
|
||||
(which-key-mode)
|
||||
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")
|
||||
(require 'mu4e)
|
||||
;; 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.
|
||||
(setq gc-cons-threshold most-positive-fixnum)
|
||||
|
||||
(setq mu4e-get-mail-command "mbsync -c ~/.emacs.d/mu4e/.mbsyncrc -a"
|
||||
mu4e-update-interval 300)
|
||||
;; In noninteractive sessions, prioritize non-byte-compiled source files to
|
||||
;; prevent the use of stale byte-code. Otherwise, it saves us a little IO time
|
||||
;; to skip the mtime checks on every *.elc file.
|
||||
(setq load-prefer-newer noninteractive)
|
||||
|
||||
;; Fields that are auto-filled in email
|
||||
(setq
|
||||
user-mail-address "derek@distrotube.com"
|
||||
user-full-name "Derek Taylor"
|
||||
mu4e-compose-signature
|
||||
(concat
|
||||
"Derek Taylor\n"
|
||||
"http://www.youtube.com/DistroTube\n"))
|
||||
(let (file-name-handler-alist)
|
||||
;; Ensure Doom is running out of this file's directory
|
||||
(setq user-emacs-directory (file-name-directory load-file-name)))
|
||||
|
||||
;; sending mail -- replace USERNAME with your gmail username
|
||||
;; also, make sure the gnutls command line utils are installed
|
||||
;; package 'gnutls-bin' in Debian/Ubuntu
|
||||
(require 'smtpmail)
|
||||
(setq message-send-mail-function 'smtpmail-send-it
|
||||
starttls-use-gnutls t
|
||||
smtpmail-starttls-credentials '(("smtp.1and1.com" 587 nil nil))
|
||||
smtpmail-auth-credentials
|
||||
'(("smtp.1and1.com" 587 "derek@distrotube.com" nil))
|
||||
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)
|
||||
;; Load the heart of Doom Emacs
|
||||
(load (concat user-emacs-directory "core/core")
|
||||
nil 'nomessage)
|
||||
|
||||
(require 'rainbow-mode)
|
||||
(use-package rainbow-mode
|
||||
:ensure t
|
||||
:config
|
||||
(setq rainbow-x-colors nil)
|
||||
(add-hook 'prog-mode-hook 'rainbow-mode))
|
||||
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes")
|
||||
(load-theme 'dracula t)
|
||||
(add-hook 'after-init-hook #'global-emojify-mode)
|
||||
(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.
|
||||
'(default ((t (:family "Mononoki Nerd Font Mono" :foundry "UKWN" :slant normal :weight normal :height 120 :width normal)))))
|
||||
(when (require 'evil-collection nil t)
|
||||
(evil-collection-init))
|
||||
(require 'org-bullets)
|
||||
;; (setq org-bullets-bullet-list '("☯" "○" "✸" "✿" "~"))
|
||||
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
|
||||
|
||||
;; minimal rendering by default in eww
|
||||
(setq-default shr-inhibit-images t)`
|
||||
(setq-default shr-use-fonts nil)
|
||||
|
||||
;;; Keybindings
|
||||
(global-set-key (kbd "C->") 'indent-rigidly-right-to-tab-stop) ; Indent selection by one tab length
|
||||
(global-set-key (kbd "C-<") 'indent-rigidly-left-to-tab-stop) ; De-indent selection by one tab length
|
||||
(put 'upcase-region 'disabled nil)
|
||||
(put 'downcase-region 'disabled nil)
|
||||
;; 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)))
|
||||
|
||||
171
.emacs.d/init.example.el
Normal file
171
.emacs.d/init.example.el
Normal file
@@ -0,0 +1,171 @@
|
||||
;;; 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.
|
||||
|
||||
(doom! :input
|
||||
;;chinese
|
||||
;;japanese
|
||||
|
||||
:completion
|
||||
company ; the ultimate code completion backend
|
||||
;;helm ; the *other* search engine for love and life
|
||||
;;ido ; the other *other* search engine...
|
||||
ivy ; a search engine for love and life
|
||||
|
||||
:ui
|
||||
;;deft ; notational velocity for Emacs
|
||||
doom ; what makes DOOM look the way it does
|
||||
doom-dashboard ; a nifty splash screen for Emacs
|
||||
doom-quit ; DOOM quit-message prompts when you quit Emacs
|
||||
;;fill-column ; a `fill-column' indicator
|
||||
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
|
||||
;;hydra
|
||||
;;indent-guides ; highlighted indent columns
|
||||
modeline ; snazzy, Atom-inspired modeline, plus API
|
||||
nav-flash ; blink the current line after jumping
|
||||
;;neotree ; a project drawer, like NERDTree for vim
|
||||
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
|
||||
;;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
|
||||
workspaces ; tab emulation, persistence & separate workspaces
|
||||
|
||||
:editor
|
||||
(evil +everywhere); come to the dark side, we have cookies
|
||||
file-templates ; auto-snippets for empty files
|
||||
fold ; (nigh) universal code folding
|
||||
;;(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
|
||||
;;objed ; text object editing for the innocent
|
||||
;;parinfer ; turn lisp into python, sort of
|
||||
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
|
||||
|
||||
:term
|
||||
;;eshell ; a consistent, cross-platform shell (WIP)
|
||||
;;shell ; a terminal REPL for Emacs
|
||||
;;term ; terminals in Emacs
|
||||
;;vterm ; another terminals in Emacs
|
||||
|
||||
:tools
|
||||
;;ansible
|
||||
;;debugger ; FIXME stepping through code, to help you add bugs
|
||||
;;direnv
|
||||
;;docker
|
||||
;;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
|
||||
;;lsp
|
||||
;;macos ; MacOS-specific commands
|
||||
magit ; a git porcelain for Emacs
|
||||
;;make ; run make tasks from Emacs
|
||||
;;pass ; password manager for nerds
|
||||
;;pdf ; pdf enhancements
|
||||
;;prodigy ; FIXME managing external services & code builders
|
||||
;;rgb ; creating color strings
|
||||
;;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
|
||||
;;elixir ; erlang done right
|
||||
;;elm ; care for a cup of TEA?
|
||||
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
|
||||
;;hy ; readability of scheme w/ speed of python
|
||||
;;idris ;
|
||||
;;(java +meghanada) ; the poster child for carpal tunnel syndrome
|
||||
;;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
|
||||
;;lean
|
||||
;;factor
|
||||
;;ledger ; an accounting system in Emacs
|
||||
;;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
|
||||
;;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
|
||||
;;rest ; Emacs as a REST client
|
||||
;;rst ; ReST in peace
|
||||
;;ruby ; 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
|
||||
;;solidity ; do you need a blockchain? No.
|
||||
;;swift ; who asked for emoji variables?
|
||||
;;terra ; Earth and Moon in alignment for performance.
|
||||
;;web ; the tubes
|
||||
|
||||
:email
|
||||
;;(mu4e +gmail)
|
||||
;;notmuch
|
||||
;;(wanderlust +gmail)
|
||||
|
||||
:app
|
||||
;;calendar
|
||||
;;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))
|
||||
66
.emacs.d/modules/app/calendar/README.org
Normal file
66
.emacs.d/modules/app/calendar/README.org
Normal file
@@ -0,0 +1,66 @@
|
||||
#+TITLE: app/calendar
|
||||
#+DATE: January 13, 2018
|
||||
#+SINCE: v2.1
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#packages][Packages]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#changing-calendar-sources][Changing calendar sources]]
|
||||
- [[#synchronizing-org-and-google-calendar][Synchronizing Org and Google Calendar]]
|
||||
|
||||
* Description
|
||||
This module adds a calendar view for Emacs, with org and google calendar sync
|
||||
support.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Packages
|
||||
+ [[https://github.com/kiwanami/emacs-calfw][calfw]]
|
||||
+ [[https://github.com/kiwanami/emacs-calfw][calfw-org]]
|
||||
+ [[https://github.com/myuhe/org-gcal.el][org-gcal]]
|
||||
|
||||
* Configuration
|
||||
** Changing calendar sources
|
||||
By defining your own calendar commands, you can control what sources to pull
|
||||
calendar data from:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(defun my-open-calendar ()
|
||||
(interactive)
|
||||
(cfw:open-calendar-buffer
|
||||
:contents-sources
|
||||
(list
|
||||
(cfw:org-create-source "Green") ; orgmode source
|
||||
(cfw:howm-create-source "Blue") ; howm source
|
||||
(cfw:cal-create-source "Orange") ; diary source
|
||||
(cfw:ical-create-source "Moon" "~/moon.ics" "Gray") ; ICS source1
|
||||
(cfw:ical-create-source "gcal" "https://..../basic.ics" "IndianRed") ; google calendar ICS
|
||||
)))
|
||||
#+END_SRC
|
||||
|
||||
To control what org files ~clfw:org-create-source~ will use, ~let~-bind
|
||||
~org-agenda-files~ around a call to ~+calendar/open-calendar~ like so:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;;;###autoload
|
||||
(defun cfw:open-org-calendar-with-cal1 ()
|
||||
(interactive)
|
||||
(let ((org-agenda-files '("/path/to/org/" "/path/to/cal1.org")))
|
||||
(call-interactively #'+calendar/open-calendar)))
|
||||
|
||||
;;;###autoload
|
||||
(defun cfw:open-org-calendar-with-cal2 ()
|
||||
(interactive)
|
||||
(let ((org-agenda-files '("/path/to/org/" "/path/to/cal2.org")))
|
||||
(call-interactively #'+calendar/open-calendar)))
|
||||
#+END_SRC
|
||||
|
||||
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
|
||||
to link your calendar with Google calendars.
|
||||
61
.emacs.d/modules/app/calendar/autoload.el
Normal file
61
.emacs.d/modules/app/calendar/autoload.el
Normal file
@@ -0,0 +1,61 @@
|
||||
;;; app/calendar/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(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)))
|
||||
(select-window win)
|
||||
(call-interactively +calendar-open-function)))
|
||||
|
||||
;;;###autoload
|
||||
(defun =calendar ()
|
||||
"Activate (or switch to) `calendar' in its workspace."
|
||||
(interactive)
|
||||
(if (featurep! :ui workspaces)
|
||||
(progn
|
||||
(+workspace-switch "Calendar" t)
|
||||
(doom/switch-to-scratch-buffer)
|
||||
(+calendar--init)
|
||||
(+workspace/display))
|
||||
(setq +calendar--wconf (current-window-configuration))
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer (doom-fallback-buffer))
|
||||
(+calendar--init)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar/quit ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(if (featurep! :ui workspaces)
|
||||
(+workspace/delete "Calendar")
|
||||
(doom-kill-matching-buffers "^\\*cfw:")
|
||||
(set-window-configuration +calendar--wconf)
|
||||
(setq +calendar--wconf nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar/open-calendar ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(cfw:open-calendar-buffer
|
||||
;; :custom-map cfw:my-cal-map
|
||||
:contents-sources
|
||||
(list
|
||||
(cfw:org-create-source (face-foreground 'default)) ; orgmode source
|
||||
)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +calendar-cfw:render-button-a (title command &optional state)
|
||||
"render-button
|
||||
TITLE
|
||||
COMMAND
|
||||
STATE"
|
||||
(let ((text (concat " " title " "))
|
||||
(keymap (make-sparse-keymap)))
|
||||
(cfw:rt text (if state 'cfw:face-toolbar-button-on
|
||||
'cfw:face-toolbar-button-off))
|
||||
(define-key keymap [mouse-1] command)
|
||||
(cfw:tp text 'keymap keymap)
|
||||
(cfw:tp text 'mouse-face 'highlight)
|
||||
text))
|
||||
51
.emacs.d/modules/app/calendar/config.el
Normal file
51
.emacs.d/modules/app/calendar/config.el
Normal file
@@ -0,0 +1,51 @@
|
||||
;;; app/calendar/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +calendar-open-function #'+calendar/open-calendar
|
||||
"TODO")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(use-package! calfw
|
||||
:commands cfw:open-calendar-buffer
|
||||
:config
|
||||
;; better frame for calendar
|
||||
(setq cfw:face-item-separator-color nil
|
||||
cfw:render-line-breaker 'cfw:render-line-breaker-none
|
||||
cfw:fchar-junction ?╋
|
||||
cfw:fchar-vertical-line ?┃
|
||||
cfw:fchar-horizontal-line ?━
|
||||
cfw:fchar-left-junction ?┣
|
||||
cfw:fchar-right-junction ?┫
|
||||
cfw:fchar-top-junction ?┯
|
||||
cfw:fchar-top-left-corner ?┏
|
||||
cfw:fchar-top-right-corner ?┓)
|
||||
|
||||
(define-key cfw:calendar-mode-map "q" #'+calendar/quit)
|
||||
|
||||
(add-hook 'cfw:calendar-mode-hook #'doom-mark-buffer-as-real-h)
|
||||
(add-hook 'cfw:calendar-mode-hook 'hide-mode-line-mode)
|
||||
|
||||
(advice-add #'cfw:render-button :override #'+calendar-cfw:render-button-a))
|
||||
|
||||
|
||||
(use-package! calfw-org
|
||||
:commands (cfw:open-org-calendar
|
||||
cfw:org-create-source
|
||||
cfw:open-org-calendar-withkevin
|
||||
my-open-calendar))
|
||||
|
||||
|
||||
(use-package! org-gcal
|
||||
:commands (org-gcal-sync
|
||||
org-gcal-fetch
|
||||
org-gcal-post-at-point
|
||||
org-gcal-delete-at-point)
|
||||
:config
|
||||
;; hack to avoid the deferred.el error
|
||||
(defun org-gcal--notify (title mes)
|
||||
(message "org-gcal::%s - %s" title mes)))
|
||||
|
||||
|
||||
;; (use-package! alert)
|
||||
6
.emacs.d/modules/app/calendar/packages.el
Normal file
6
.emacs.d/modules/app/calendar/packages.el
Normal file
@@ -0,0 +1,6 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/calendar/packages.el
|
||||
|
||||
(package! calfw)
|
||||
(package! calfw-org)
|
||||
(package! org-gcal)
|
||||
141
.emacs.d/modules/app/irc/README.org
Normal file
141
.emacs.d/modules/app/irc/README.org
Normal file
@@ -0,0 +1,141 @@
|
||||
#+TITLE: app/irc
|
||||
#+DATE: June 11, 2017
|
||||
#+SINCE: v2.0.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#dependencies][Dependencies]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#features][Features]]
|
||||
- [[#an-irc-client-in-emacs][An IRC Client in Emacs]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#pass-the-unix-password-manager][Pass: the unix password manager]]
|
||||
- [[#emacs-auth-source-api][Emacs' auth-source API]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
|
||||
* Description
|
||||
This module turns adds an IRC client to Emacs with OS notifications.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[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.
|
||||
|
||||
* Features
|
||||
** An IRC Client in Emacs
|
||||
To connect to IRC you can invoke the ~=irc~ function using =M-x= or your own
|
||||
custom keybinding.
|
||||
|
||||
| command | description |
|
||||
|---------+-------------------------------------------|
|
||||
| ~=irc~ | Connect to IRC and all configured servers |
|
||||
|
||||
When in a circe buffer these keybindings will be available.
|
||||
|
||||
| command | key | description |
|
||||
|-----------------------------+-----------+----------------------------------------------|
|
||||
| ~+irc/tracking-next-buffer~ | =SPC m a= | Switch to the next active buffer |
|
||||
| ~circe-command-JOIN~ | =SPC m j= | Join a channel |
|
||||
| ~+irc/send-message~ | =SPC m m= | Send a private message |
|
||||
| ~circe-command-NAMES~ | =SPC m n= | List the names of the current channel |
|
||||
| ~circe-command-PART~ | =SPC m p= | Part the current channel |
|
||||
| ~+irc/quit~ | =SPC m Q= | Kill the current circe session and workgroup |
|
||||
| ~circe-reconnect~ | =SPC m R= | Reconnect the current server |
|
||||
|
||||
* Configuration
|
||||
Use ~set-irc-server!~ 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")))
|
||||
#+END_SRC
|
||||
|
||||
*It is a obviously a bad idea to store auth-details in plaintext,* so here are
|
||||
some 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
|
||||
[[../../../modules/tools/pass/README.org][:tools pass]] module you get an elisp API through which to access your
|
||||
password store.
|
||||
|
||||
~set-irc-server!~ accepts a plist can use functions instead of strings.
|
||||
~+pass-get-user~ and ~+pass-get-secret~ can help here:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
`(:tls t
|
||||
:nick "doom"
|
||||
:sasl-username ,(+pass-get-user "irc/freenode.net")
|
||||
:sasl-password ,(+pass-get-secret "irc/freenode.net")
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
But wait, there's more! This stores your password in a public variable which
|
||||
could be accessed or appear in backtraces. Not good! So we go a step further:
|
||||
|
||||
#+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 (lambda (&rest _) (+pass-get-secret "irc/freenode.net"))
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
And you're good to go!
|
||||
|
||||
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
|
||||
mysecretpassword
|
||||
username: myusername
|
||||
#+END_SRC
|
||||
|
||||
** 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
|
||||
(and retrieve) encrypted passwords with it.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
(setq auth-sources '("~/.authinfo.gpg"))
|
||||
|
||||
(defun my-fetch-password (&rest params)
|
||||
(require 'auth-source)
|
||||
(let ((match (car (apply #'auth-source-search params))))
|
||||
(if match
|
||||
(let ((secret (plist-get match :secret)))
|
||||
(if (functionp secret)
|
||||
(funcall secret)
|
||||
secret))
|
||||
(error "Password not found for %S" params))))
|
||||
|
||||
(defun my-nickserv-password (server)
|
||||
(my-fetch-password :user "forcer" :host "irc.freenode.net"))
|
||||
|
||||
(set-irc-server! "chat.freenode.net"
|
||||
'(:tls t
|
||||
:port 6697
|
||||
:nick "doom"
|
||||
:sasl-password my-nickserver-password
|
||||
:channels ("#emacs")))
|
||||
#+END_SRC
|
||||
|
||||
* TODO Troubleshooting
|
||||
123
.emacs.d/modules/app/irc/autoload/irc.el
Normal file
123
.emacs.d/modules/app/irc/autoload/irc.el
Normal file
@@ -0,0 +1,123 @@
|
||||
;;; app/irc/autoload/email.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +irc--workspace-name "*IRC*")
|
||||
|
||||
(defun +irc-setup-wconf (&optional inhibit-workspace)
|
||||
(unless inhibit-workspace
|
||||
(+workspace-switch +irc--workspace-name t))
|
||||
(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)))
|
||||
|
||||
;;;###autoload
|
||||
(defun =irc (&optional inhibit-workspace)
|
||||
"Connect to IRC and auto-connect to all registered networks.
|
||||
|
||||
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))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/connect (&optional inhibit-workspace)
|
||||
"Connect to a specific registered server.
|
||||
|
||||
If INHIBIT-WORKSPACE (the universal argument) is non-nil, don't spawn a new
|
||||
workspace for it."
|
||||
(interactive "P")
|
||||
(and (+irc-setup-wconf inhibit-workspace)
|
||||
(call-interactively #'circe)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/send-message (who what)
|
||||
"Send WHO a message containing WHAT."
|
||||
(interactive "sWho: \nsWhat: ")
|
||||
(circe-command-MSG who what))
|
||||
|
||||
;;;###autoload
|
||||
(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")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/ivy-jump-to-channel (&optional this-server)
|
||||
"Jump to an open channel or server buffer with ivy. If THIS-SERVER (universal
|
||||
argument) is non-nil only show channels in current server."
|
||||
(interactive "P")
|
||||
(if (not (circe-server-buffers))
|
||||
(message "No circe buffers available")
|
||||
(when (and this-server (not circe-server-buffer))
|
||||
(setq this-server nil))
|
||||
(ivy-read (format "Jump to%s: " (if this-server (format " (%s)" (buffer-name circe-server-buffer)) ""))
|
||||
(cl-loop with servers = (if this-server (list circe-server-buffer) (circe-server-buffers))
|
||||
with current-buffer = (current-buffer)
|
||||
for server in servers
|
||||
collect (buffer-name server)
|
||||
nconc
|
||||
(with-current-buffer server
|
||||
(cl-loop for buf in (circe-server-chat-buffers)
|
||||
unless (eq buf current-buffer)
|
||||
collect (format " %s" (buffer-name buf)))))
|
||||
:action #'+irc--ivy-switch-to-buffer-action
|
||||
:preselect (buffer-name (current-buffer))
|
||||
:keymap ivy-switch-buffer-map
|
||||
:caller '+irc/ivy-jump-to-channel)))
|
||||
|
||||
(defun +irc--ivy-switch-to-buffer-action (buffer)
|
||||
(when (stringp buffer)
|
||||
(ivy--switch-buffer-action (string-trim-left buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc/tracking-next-buffer ()
|
||||
"Dissables switching to an unread buffer unless in the irc workspace."
|
||||
(interactive)
|
||||
(when (derived-mode-p 'circe-mode)
|
||||
(tracking-next-buffer)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks/fns
|
||||
|
||||
;;;###autoload
|
||||
(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))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +irc--add-circe-buffer-to-persp-h ()
|
||||
(when (bound-and-true-p persp-mode)
|
||||
(let ((persp (get-current-persp))
|
||||
(buf (current-buffer)))
|
||||
;; Add a new circe buffer to irc workspace when we're in another workspace
|
||||
(unless (eq (safe-persp-name persp) +irc--workspace-name)
|
||||
;; Add new circe buffers to the persp containing circe buffers
|
||||
(persp-add-buffer buf (persp-get-by-name +irc--workspace-name))
|
||||
;; Remove new buffer from accidental workspace
|
||||
(persp-remove-buffer buf persp)))))
|
||||
9
.emacs.d/modules/app/irc/autoload/settings.el
Normal file
9
.emacs.d/modules/app/irc/autoload/settings.el
Normal file
@@ -0,0 +1,9 @@
|
||||
;;; app/irc/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-irc-server! (server letvars)
|
||||
"Registers an irc SERVER for circe.
|
||||
|
||||
See `circe-network-options' for details."
|
||||
(after! circe
|
||||
(push (cons server letvars) circe-network-options)))
|
||||
235
.emacs.d/modules/app/irc/config.el
Normal file
235
.emacs.d/modules/app/irc/config.el
Normal file
@@ -0,0 +1,235 @@
|
||||
;;; app/irc/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +irc-left-padding 13
|
||||
"By how much spaces the left hand side of the line should be padded.
|
||||
Below a value of 12 this may result in uneven alignment between the various
|
||||
types of messages.")
|
||||
|
||||
(defvar +irc-truncate-nick-char ?…
|
||||
"Character to displayed when nick > `+irc-left-padding' in length.")
|
||||
|
||||
(defvar +irc-scroll-to-bottom-on-commands
|
||||
'(self-insert-command yank hilit-yank)
|
||||
"If these commands are called pre prompt the buffer will scroll to `point-max'.")
|
||||
|
||||
(defvar +irc-disconnect-hook nil
|
||||
"Runs each hook when circe noticies the connection has been disconnected.
|
||||
Useful for scenarios where an instant reconnect will not be successful.")
|
||||
|
||||
(defvar +irc-bot-list '("fsbot" "rudybot")
|
||||
"Nicks listed have `circe-fool-face' applied and will not be tracked.")
|
||||
|
||||
(defvar +irc-time-stamp-format "%H:%M"
|
||||
"The format of time stamps.
|
||||
|
||||
See `format-time-string' for a full description of available
|
||||
formatting directives. ")
|
||||
|
||||
(defvar +irc-notifications-watch-strings nil
|
||||
"A list of strings which can trigger a notification. You don't need to put
|
||||
your nick here.
|
||||
|
||||
See `circe-notifications-watch-strings'.")
|
||||
|
||||
(defvar +irc-defer-notifications nil
|
||||
"How long to defer enabling notifications, in seconds (e.g. 5min = 300).
|
||||
Useful for ZNC users who want to avoid the deluge of notifications during buffer
|
||||
playback.")
|
||||
|
||||
(defvar +irc--defer-timer nil)
|
||||
|
||||
(defsubst +irc--pad (left right)
|
||||
(format (format "%%%ds | %%s" +irc-left-padding)
|
||||
(concat "*** " left) right))
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(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
|
||||
circe-use-cycle-completion t
|
||||
circe-reduce-lurker-spam t
|
||||
|
||||
circe-format-say (format "{nick:+%ss} │ {body}" +irc-left-padding)
|
||||
circe-format-self-say circe-format-say
|
||||
circe-format-action (format "{nick:+%ss} * {body}" +irc-left-padding)
|
||||
circe-format-self-action circe-format-action
|
||||
circe-format-server-notice
|
||||
(let ((left "-Server-")) (concat (make-string (- +irc-left-padding (length left)) ? )
|
||||
(concat left " _ {body}")))
|
||||
circe-format-notice (format "{nick:%ss} _ {body}" +irc-left-padding)
|
||||
circe-format-server-topic
|
||||
(+irc--pad "Topic" "{userhost}: {topic-diff}")
|
||||
circe-format-server-join-in-channel
|
||||
(+irc--pad "Join" "{nick} ({userinfo}) joined {channel}")
|
||||
circe-format-server-join
|
||||
(+irc--pad "Join" "{nick} ({userinfo})")
|
||||
circe-format-server-part
|
||||
(+irc--pad "Part" "{nick} ({userhost}) left {channel}: {reason}")
|
||||
circe-format-server-quit
|
||||
(+irc--pad "Quit" "{nick} ({userhost}) left IRC: {reason}]")
|
||||
circe-format-server-quit-channel
|
||||
(+irc--pad "Quit" "{nick} ({userhost}) left {channel}: {reason}]")
|
||||
circe-format-server-rejoin
|
||||
(+irc--pad "Re-join" "{nick} ({userhost}), left {departuredelta} ago")
|
||||
circe-format-server-netmerge
|
||||
(+irc--pad "Netmerge" "{split}, split {ago} ago (Use /WL to see who's still missing)")
|
||||
circe-format-server-nick-change
|
||||
(+irc--pad "Nick" "{old-nick} ({userhost}) is now known as {new-nick}")
|
||||
circe-format-server-nick-change-self
|
||||
(+irc--pad "Nick" "You are now known as {new-nick} ({old-nick})")
|
||||
circe-format-server-nick-change-self
|
||||
(+irc--pad "Nick" "{old-nick} ({userhost}) is now known as {new-nick}")
|
||||
circe-format-server-mode-change
|
||||
(+irc--pad "Mode" "{change} on {target} by {setter} ({userhost})")
|
||||
circe-format-server-lurker-activity
|
||||
(+irc--pad "Lurk" "{nick} joined {joindelta} ago"))
|
||||
|
||||
(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)
|
||||
|
||||
(defadvice! +irc--circe-run-disconnect-hook-a (&rest _)
|
||||
"Runs `+irc-disconnect-hook' after circe disconnects."
|
||||
:after #'circe--irc-conn-disconnected
|
||||
(run-hooks '+irc-disconnect-hook))
|
||||
|
||||
(add-hook! 'lui-pre-output-hook
|
||||
(defun +irc-circe-truncate-nicks-h ()
|
||||
"Truncate long nicknames in chat output non-destructively."
|
||||
(when-let (beg (text-property-any (point-min) (point-max) 'lui-format-argument 'nick))
|
||||
(goto-char beg)
|
||||
(let ((end (next-single-property-change beg 'lui-format-argument))
|
||||
(nick (plist-get (plist-get (text-properties-at beg) 'lui-keywords)
|
||||
:nick)))
|
||||
(when (> (length nick) +irc-left-padding)
|
||||
(compose-region (+ beg +irc-left-padding -1) end
|
||||
+irc-truncate-nick-char))))))
|
||||
|
||||
(add-hook! 'circe-message-option-functions
|
||||
(defun +irc-circe-message-option-bot-h (nick &rest ignored)
|
||||
"Fontify known bots and mark them to not be tracked."
|
||||
(when (member nick +irc-bot-list)
|
||||
'((text-properties . (face circe-fool-face lui-do-not-track t))))))
|
||||
|
||||
;; Let `+irc/quit' and `circe' handle buffer cleanup
|
||||
(define-key circe-mode-map [remap kill-buffer] #'bury-buffer)
|
||||
;; Fail gracefully if not in a circe buffer
|
||||
(global-set-key [remap tracking-next-buffer] #'+irc/tracking-next-buffer)
|
||||
|
||||
(map! :localleader
|
||||
(:map circe-mode-map
|
||||
"a" #'tracking-next-buffer
|
||||
"j" #'circe-command-JOIN
|
||||
"m" #'+irc/send-message
|
||||
"p" #'circe-command-PART
|
||||
"Q" #'+irc/quit
|
||||
"R" #'circe-reconnect
|
||||
(:when (featurep! :completion ivy)
|
||||
"c" #'+irc/ivy-jump-to-channel))
|
||||
(:map circe-channel-mode-map
|
||||
"n" #'circe-command-NAMES)))
|
||||
|
||||
|
||||
(use-package! circe-color-nicks
|
||||
:hook (circe-channel-mode . enable-circe-color-nicks)
|
||||
:config
|
||||
(setq circe-color-nicks-min-constrast-ratio 4.5
|
||||
circe-color-nicks-everywhere t))
|
||||
|
||||
|
||||
(use-package! circe-new-day-notifier
|
||||
:after circe
|
||||
:config
|
||||
(enable-circe-new-day-notifier)
|
||||
(setq circe-new-day-notifier-format-message
|
||||
(+irc--pad "Day" "Date changed [{day}]")))
|
||||
|
||||
|
||||
(use-package! circe-notifications
|
||||
:commands enable-circe-notifications
|
||||
:init
|
||||
(if +irc-defer-notifications
|
||||
(add-hook! 'circe-server-connected-hook
|
||||
(setq +irc--defer-timer
|
||||
(run-at-time +irc-defer-notifications nil
|
||||
#'enable-circe-notifications)))
|
||||
(add-hook 'circe-server-connected-hook #'enable-circe-notifications))
|
||||
:config
|
||||
(setq circe-notifications-watch-strings +irc-notifications-watch-strings
|
||||
circe-notifications-emacs-focused nil
|
||||
circe-notifications-alert-style
|
||||
(cond (IS-MAC 'osx-notifier)
|
||||
(IS-LINUX 'libnotify)
|
||||
(circe-notifications-alert-style))))
|
||||
|
||||
|
||||
(use-package! lui
|
||||
:commands lui-mode
|
||||
:config
|
||||
(define-key lui-mode-map "\C-u" #'lui-kill-to-beginning-of-line)
|
||||
(setq lui-fill-type nil)
|
||||
|
||||
(when (featurep! :tools flyspell)
|
||||
(setq lui-flyspell-p t))
|
||||
|
||||
(after! evil
|
||||
(defun +irc-evil-insert-h ()
|
||||
"Ensure entering insert mode will put us at the prompt, unless editing
|
||||
after prompt marker."
|
||||
(when (> (marker-position lui-input-marker) (point))
|
||||
(goto-char (point-max))))
|
||||
|
||||
(add-hook! 'lui-mode-hook
|
||||
(add-hook 'evil-insert-state-entry-hook #'+irc-evil-insert-h
|
||||
nil 'local))
|
||||
|
||||
(mapc (lambda (cmd) (push cmd +irc-scroll-to-bottom-on-commands))
|
||||
'(evil-paste-after evil-paste-before evil-open-above evil-open-below)))
|
||||
|
||||
|
||||
(defun +irc-preinput-scroll-to-bottom-h ()
|
||||
"Go to the end of the buffer in all windows showing it.
|
||||
Courtesy of esh-mode.el"
|
||||
(when (memq this-command +irc-scroll-to-bottom-on-commands)
|
||||
(let* ((selected (selected-window))
|
||||
(current (current-buffer)))
|
||||
(when (> (marker-position lui-input-marker) (point))
|
||||
(walk-windows
|
||||
(function
|
||||
(lambda (window)
|
||||
(when (eq (window-buffer window) current)
|
||||
(select-window window)
|
||||
(goto-char (point-max))
|
||||
(select-window selected))))
|
||||
nil t)))))
|
||||
|
||||
(add-hook! 'lui-mode-hook
|
||||
(add-hook 'pre-command-hook #'+irc-preinput-scroll-to-bottom-h nil t))
|
||||
|
||||
;; enable a horizontal line marking the last read message
|
||||
(add-hook 'lui-mode-hook #'enable-lui-track-bar)
|
||||
|
||||
(add-hook! 'lui-mode-hook
|
||||
(defun +irc-init-lui-margins-h ()
|
||||
(setq lui-time-stamp-position 'right-margin
|
||||
lui-time-stamp-format +irc-time-stamp-format
|
||||
right-margin-width (length (format-time-string lui-time-stamp-format))))
|
||||
(defun +irc-init-lui-wrapping-a ()
|
||||
(setq fringes-outside-margins t
|
||||
word-wrap t
|
||||
wrap-prefix (make-string (+ +irc-left-padding 3) ? )))))
|
||||
|
||||
|
||||
(use-package! lui-logging
|
||||
:after lui
|
||||
:config (enable-lui-logging))
|
||||
|
||||
|
||||
(use-package! lui-autopaste
|
||||
:hook (circe-channel-mode . enable-lui-autopaste))
|
||||
5
.emacs.d/modules/app/irc/packages.el
Normal file
5
.emacs.d/modules/app/irc/packages.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/irc/packages.el
|
||||
|
||||
(package! circe)
|
||||
(package! circe-notifications)
|
||||
14
.emacs.d/modules/app/regex/autoload/export.el
Normal file
14
.emacs.d/modules/app/regex/autoload/export.el
Normal file
@@ -0,0 +1,14 @@
|
||||
;;; 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 ()) ; /.+/
|
||||
272
.emacs.d/modules/app/regex/autoload/regex.el
Normal file
272
.emacs.d/modules/app/regex/autoload/regex.el
Normal file
@@ -0,0 +1,272 @@
|
||||
;;; app/regex/autoload/regex.el
|
||||
|
||||
(defvar +regex--text-buffer nil)
|
||||
(defvar +regex--text-replace-buffer nil)
|
||||
(defvar +regex--expr-buffer nil)
|
||||
(defvar +regex--expr-replace-buffer nil)
|
||||
(defvar +regex--groups-buffer nil)
|
||||
(defvar +regex--replace-buffer nil)
|
||||
|
||||
;;
|
||||
(defface +regex-match-0-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'magenta) :bold t)))
|
||||
"TODO"
|
||||
:group 'faces)
|
||||
|
||||
(defface +regex-match-1-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'blue) :bold t)))
|
||||
"TODO"
|
||||
:group 'faces)
|
||||
|
||||
(defface +regex-match-2-face
|
||||
`((t (:foreground "Black" :background ,(doom-color 'green) :bold t)))
|
||||
"TODO"
|
||||
:group 'faces)
|
||||
|
||||
(defvar +regex-faces
|
||||
'(+regex-match-0-face +regex-match-1-face +regex-match-2-face)
|
||||
"TODO")
|
||||
|
||||
;;
|
||||
(defvar +regex-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map "\C-c\C-c" #'+regex-update-buffers)
|
||||
(define-key map "\C-c\C-r" #'=regex/replace)
|
||||
(define-key map "\C-c\C-k" #'+regex/quit)
|
||||
(define-key map [remap kill-current-buffer] #'+regex/quit)
|
||||
(define-key map [remap kill-buffer] #'+regex/quit)
|
||||
map)
|
||||
"TODO")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode +regex-mode
|
||||
"TODO"
|
||||
:init-value nil
|
||||
:global nil
|
||||
:lighter ""
|
||||
:keymap +regex-mode-map
|
||||
(if +regex-mode
|
||||
(add-hook 'after-change-functions #'+regex-update-buffers nil t)
|
||||
(remove-hook 'after-change-functions #'+regex-update-buffers t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun =regex (&optional dummy-text)
|
||||
"Start the Regex IDE."
|
||||
(interactive "P")
|
||||
(unless (buffer-live-p +regex--expr-buffer)
|
||||
(condition-case ex
|
||||
(progn
|
||||
(setq +regex--expr-buffer (get-buffer-create "*doom-regex*")
|
||||
+regex--text-buffer (if dummy-text (get-buffer-create "*doom-regex-text*") (current-buffer))
|
||||
+regex--groups-buffer (get-buffer-create "*doom-regex-groups*"))
|
||||
(when dummy-text
|
||||
(+workspace-switch +regex-workspace-name t)
|
||||
(switch-to-buffer +regex--text-buffer)
|
||||
(with-current-buffer +regex--text-buffer
|
||||
(insert +regex-dummy-text)))
|
||||
(pop-to-buffer +regex--groups-buffer)
|
||||
(pop-to-buffer +regex--expr-buffer)
|
||||
(with-current-buffer +regex--expr-buffer
|
||||
(conf-mode)
|
||||
(rainbow-delimiters-mode +1)
|
||||
(doom/toggle-line-numbers +1)
|
||||
(setq-local require-final-newline nil)
|
||||
(+regex-mode +1)
|
||||
(text-scale-set 3)))
|
||||
('error
|
||||
(+regex/quit)
|
||||
(error "Failed to open the Regexp IDE: %s" ex)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun =regex/replace ()
|
||||
(interactive)
|
||||
(unless (buffer-live-p +regex--replace-buffer)
|
||||
(let (text)
|
||||
(=regex t)
|
||||
(with-selected-window (get-buffer-window +regex--text-buffer)
|
||||
(setq text (buffer-string))
|
||||
(select-window (split-window-right))
|
||||
(switch-to-buffer (get-buffer-create "*doom-regex-text-repl*"))
|
||||
(erase-buffer)
|
||||
(insert text)
|
||||
(read-only-mode +1)
|
||||
(setq +regex--text-replace-buffer (current-buffer)))
|
||||
(with-current-buffer +regex--expr-buffer
|
||||
(select-window (split-window-right))
|
||||
(switch-to-buffer (get-buffer-create "*doom-regex-repl*"))
|
||||
(conf-mode)
|
||||
(rainbow-delimiters-mode +1)
|
||||
(doom/toggle-line-numbers -1)
|
||||
(setq-local require-final-newline nil)
|
||||
(setq mode-line-format nil
|
||||
+regex--expr-replace-buffer (current-buffer))
|
||||
(+regex-mode +1)
|
||||
(text-scale-set 3)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +regex/quit ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(when (and +regex--text-buffer (buffer-live-p +regex--text-buffer))
|
||||
(with-current-buffer +regex--text-buffer
|
||||
(+regex-mode -1)
|
||||
(remove-overlays nil nil 'category '+regex))
|
||||
(when (equal (buffer-name +regex--text-buffer) "*doom-regex-text*")
|
||||
(kill-buffer +regex--text-buffer)))
|
||||
(when (equal (+workspace-current-name) +regex-workspace-name)
|
||||
(+workspace/delete +regex-workspace-name))
|
||||
(mapc (lambda (bufname)
|
||||
(let ((buf (symbol-value bufname)))
|
||||
(when (and buf (buffer-live-p buf))
|
||||
(kill-buffer buf)
|
||||
(set bufname nil))))
|
||||
(list '+regex--text-replace-buffer
|
||||
'+regex--expr-replace-buffer
|
||||
'+regex--expr-buffer
|
||||
'+regex--groups-buffer
|
||||
'+regex--replace-buffer)))
|
||||
|
||||
(defun +regex--expr ()
|
||||
(when (buffer-live-p +regex--expr-buffer)
|
||||
(with-current-buffer +regex--expr-buffer
|
||||
(string-trim (buffer-string)))))
|
||||
|
||||
(defun +regex--expr-replace ()
|
||||
(when (buffer-live-p +regex--expr-replace-buffer)
|
||||
(with-current-buffer +regex--expr-replace-buffer
|
||||
(string-trim (buffer-string)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +regex-update-buffers (&optional beg end len)
|
||||
(interactive)
|
||||
(let* ((inhibit-read-only t)
|
||||
(regex (or (+regex--expr) ""))
|
||||
(replace (or (+regex--expr-replace) ""))
|
||||
(str (or (with-current-buffer +regex--text-buffer (buffer-string)) "")))
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(erase-buffer))
|
||||
(with-current-buffer +regex--text-buffer
|
||||
(remove-overlays nil nil 'category '+regex)
|
||||
(when (> (length regex) 0)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(pcase +regex-default-backend
|
||||
('emacs (+regex-backend-emacs regex replace str))
|
||||
('perl (+regex-backend-perl regex replace str))))))
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(goto-char (point-min)))))
|
||||
|
||||
|
||||
;; --- backends ---------------------------
|
||||
|
||||
(defun +regex--render-perl (regex text)
|
||||
"From <https://github.com/jwiegley/regex-tool>"
|
||||
(with-temp-buffer
|
||||
(insert (format "@lines = <DATA>;
|
||||
$line = join(\"\", @lines);
|
||||
print \"(\";
|
||||
while ($line =~ m/%s/gm) {
|
||||
print \"(\", length($`), \" \", length($&), \" \";
|
||||
for $i (1 .. 20) {
|
||||
if ($$i) {
|
||||
my $group = $$i;
|
||||
$group =~ s/([\\\\\"])/\\\\\\1/g;
|
||||
print \"(\", $i, \" . \\\"\", $group, \"\\\") \";
|
||||
}
|
||||
}
|
||||
print \")\";
|
||||
}
|
||||
print \")\";
|
||||
__DATA__
|
||||
%s" (replace-regexp-in-string "/" "\\/" regex nil t) text))
|
||||
(call-process-region (point-min) (point-max) "perl" t t)
|
||||
(goto-char (point-min))
|
||||
(read (current-buffer))))
|
||||
|
||||
(defun +regex--replace-perl (regex replace text)
|
||||
(unless (or (string-empty-p regex)
|
||||
(string-empty-p replace)
|
||||
(string-empty-p text))
|
||||
(with-temp-buffer
|
||||
(insert (format "@lines = <DATA>;
|
||||
$line = join(\"\", @lines);
|
||||
$line =~ s/%s/%s/gm;
|
||||
print $line;
|
||||
__DATA__
|
||||
%s" (replace-regexp-in-string "/" "\\/" regex nil t) (replace-regexp-in-string "/" "\\/" replace nil t) text))
|
||||
(call-process-region (point-min) (point-max) "perl" t t)
|
||||
(buffer-string))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +regex-backend-perl (regex replace str)
|
||||
"TODO"
|
||||
(cl-assert (stringp regex))
|
||||
(cl-assert (stringp replace))
|
||||
(cl-assert (stringp str))
|
||||
(let ((i 0)
|
||||
(results (+regex--render-perl regex str)))
|
||||
(when (buffer-live-p +regex--text-replace-buffer)
|
||||
(let ((replace (+regex--replace-perl regex replace str)))
|
||||
(with-current-buffer +regex--text-replace-buffer
|
||||
(erase-buffer)
|
||||
(insert
|
||||
(if (and (listp results)
|
||||
replace
|
||||
(not (string-empty-p replace)))
|
||||
replace
|
||||
str)))))
|
||||
(dolist (result (if (listp results) results))
|
||||
(let* ((offset (nth 0 result))
|
||||
(length (nth 1 result))
|
||||
(matches (nthcdr 2 result))
|
||||
(ov (make-overlay (1+ offset) (+ offset length 1))))
|
||||
(overlay-put ov 'face (nth (mod i 3) +regex-faces))
|
||||
(overlay-put ov 'category '+regex)
|
||||
(cl-incf i)
|
||||
(let* ((match-zero (buffer-substring (1+ offset) (+ offset length 1)))
|
||||
(line (format "Match: %s\n" (propertize match-zero 'face 'font-lock-string-face))))
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(insert line)))
|
||||
(dolist (match matches)
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(goto-char (point-max))
|
||||
(insert (format "Group %d: %s\n"
|
||||
(propertize (car match) 'face 'font-lock-constant-face)
|
||||
(propertize (cdr match) 'face 'font-lock-string-face)))))
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(insert ?\n))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +regex-backend-emacs (regex replace str)
|
||||
"TODO"
|
||||
(cl-assert (stringp regex))
|
||||
(cl-assert (stringp replace))
|
||||
(cl-assert (stringp str))
|
||||
(let ((i 0)
|
||||
pos)
|
||||
(when (buffer-live-p +regex--text-replace-buffer)
|
||||
(with-current-buffer +regex--text-replace-buffer
|
||||
(erase-buffer)
|
||||
(insert str)
|
||||
(when (and (listp results) (string-empty-p replace))
|
||||
(replace-regexp regex replace))))
|
||||
(while (and (setq pos (point))
|
||||
(re-search-forward regex nil t))
|
||||
(if (= (point) pos)
|
||||
(forward-char 1)
|
||||
(let ((ov (make-overlay (match-beginning 0) (match-end 0))))
|
||||
(overlay-put ov 'face (nth (mod i 3) +regex-faces))
|
||||
(overlay-put ov 'category '+regex))
|
||||
(cl-incf i)
|
||||
(dotimes (i 10)
|
||||
(when-let (text (match-string i))
|
||||
(save-match-data
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(goto-char (point-max))
|
||||
(insert
|
||||
(format "Group %d: %s\n"
|
||||
(propertize i 'face 'font-lock-constant-face)
|
||||
(propertize text 'face 'font-lock-string-face)))))))
|
||||
(with-current-buffer +regex--groups-buffer
|
||||
(insert ?\n))))))
|
||||
|
||||
51
.emacs.d/modules/app/regex/config.el
Normal file
51
.emacs.d/modules/app/regex/config.el
Normal file
@@ -0,0 +1,51 @@
|
||||
;;; 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)))
|
||||
117
.emacs.d/modules/app/rss/autoload.el
Normal file
117
.emacs.d/modules/app/rss/autoload.el
Normal file
@@ -0,0 +1,117 @@
|
||||
;;; app/rss/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defalias '=rss #'elfeed
|
||||
"Activate (or switch to) `elfeed' in its workspace.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/delete-pane ()
|
||||
"Delete the *elfeed-entry* split pane."
|
||||
(interactive)
|
||||
(let* ((buf (get-buffer "*elfeed-entry*"))
|
||||
(window (get-buffer-window buf)))
|
||||
(delete-window window)
|
||||
(when (buffer-live-p buf)
|
||||
(kill-buffer buf))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/open (entry)
|
||||
"Display the currently selected item in a buffer."
|
||||
(interactive (list (elfeed-search-selected :ignore-region)))
|
||||
(when (elfeed-entry-p entry)
|
||||
(elfeed-untag entry 'unread)
|
||||
(elfeed-search-update-entry entry)
|
||||
(elfeed-show-entry entry)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/next ()
|
||||
"Show the next item in the elfeed-search buffer."
|
||||
(interactive)
|
||||
(funcall elfeed-show-entry-delete)
|
||||
(with-current-buffer (elfeed-search-buffer)
|
||||
(forward-line)
|
||||
(call-interactively '+rss/open)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss/previous ()
|
||||
"Show the previous item in the elfeed-search buffer."
|
||||
(interactive)
|
||||
(funcall elfeed-show-entry-delete)
|
||||
(with-current-buffer (elfeed-search-buffer)
|
||||
(forward-line -1)
|
||||
(call-interactively '+rss/open)))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-elfeed-wrap-h ()
|
||||
"Enhances an elfeed entry's readability by wrapping it to a width of
|
||||
`fill-column'."
|
||||
(let ((inhibit-read-only t)
|
||||
(inhibit-modification-hooks t))
|
||||
(setq-local truncate-lines nil)
|
||||
(setq-local shr-use-fonts nil)
|
||||
(setq-local shr-width 85)
|
||||
(set-buffer-modified-p nil)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-cleanup-h ()
|
||||
"Clean up after an elfeed session. Kills all elfeed and elfeed-org files."
|
||||
(interactive)
|
||||
;; `delete-file-projectile-remove-from-cache' slows down `elfeed-db-compact'
|
||||
;; tremendously, so we disable the projectile cache:
|
||||
(let (projectile-enable-caching)
|
||||
(elfeed-db-compact))
|
||||
(let ((buf (previous-buffer)))
|
||||
(when (or (null buf) (not (doom-real-buffer-p buf)))
|
||||
(switch-to-buffer (doom-fallback-buffer))))
|
||||
(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)
|
||||
(when-let (buf (get-file-buffer (expand-file-name file org-directory)))
|
||||
(kill-buffer buf)))
|
||||
(dolist (b search-buffers)
|
||||
(with-current-buffer b
|
||||
(remove-hook 'kill-buffer-hook #'+rss-cleanup-h :local)
|
||||
(kill-buffer b)))
|
||||
(mapc #'kill-buffer show-buffers)))
|
||||
|
||||
|
||||
;;
|
||||
;; Functions
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-dead-feeds (&optional years)
|
||||
"Return a list of feeds that haven't posted anything in YEARS."
|
||||
(let* ((years (or years 1.0))
|
||||
(living-feeds (make-hash-table :test 'equal))
|
||||
(seconds (* years 365.0 24 60 60))
|
||||
(threshold (- (float-time) seconds)))
|
||||
(with-elfeed-db-visit (entry feed)
|
||||
(let ((date (elfeed-entry-date entry)))
|
||||
(when (> date threshold)
|
||||
(setf (gethash (elfeed-feed-url feed) living-feeds) t))))
|
||||
(cl-loop for url in (elfeed-feed-list)
|
||||
unless (gethash url living-feeds)
|
||||
collect url)))
|
||||
|
||||
;;;###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)))))
|
||||
(shr-put-image spec alt flags)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +rss-render-image-tag-without-underline-fn (dom &optional url)
|
||||
"TODO"
|
||||
(let ((start (point)))
|
||||
(shr-tag-img dom url)
|
||||
;; And remove underlines in case images are links, otherwise we get an
|
||||
;; underline beneath every slice.
|
||||
(put-text-property start (point) 'face '(:underline nil))))
|
||||
74
.emacs.d/modules/app/rss/config.el
Normal file
74
.emacs.d/modules/app/rss/config.el
Normal file
@@ -0,0 +1,74 @@
|
||||
;;; app/rss/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is an opinionated workflow that turns Emacs into an RSS reader, inspired
|
||||
;; 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.")
|
||||
|
||||
(defvar +rss-enable-sliced-images t
|
||||
"Automatically slice images shown in elfeed-show-mode buffers, making them
|
||||
easier to scroll through.")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(use-package! elfeed
|
||||
:commands elfeed
|
||||
: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)
|
||||
|
||||
(set-popup-rule! "^\\*elfeed-entry"
|
||||
:size 0.75 :actions '(display-buffer-below-selected)
|
||||
:select t :quit nil :ttl t)
|
||||
|
||||
(make-directory elfeed-db-directory t)
|
||||
|
||||
;; Ensure elfeed buffers are treated as real
|
||||
(add-hook! 'doom-real-buffer-functions
|
||||
(defun +rss-buffer-p (buf)
|
||||
(string-match-p "^\\*elfeed" (buffer-name buf))))
|
||||
|
||||
;; Enhance readability of a post
|
||||
(add-hook 'elfeed-show-mode-hook #'+rss-elfeed-wrap-h)
|
||||
(add-hook! 'elfeed-search-mode-hook
|
||||
(add-hook 'kill-buffer-hook #'+rss-cleanup-h nil 'local))
|
||||
|
||||
;; Large images are annoying to scroll through, because scrolling follows the
|
||||
;; cursor, so we force shr to insert images in slices.
|
||||
(when +rss-enable-sliced-images
|
||||
(setq-hook! 'elfeed-show-mode-hook
|
||||
shr-put-image-function #'+rss-put-sliced-image-fn
|
||||
shr-external-rendering-functions '((img . +rss-render-image-tag-without-underline-fn))))
|
||||
|
||||
;; Keybindings
|
||||
(after! elfeed-show
|
||||
(define-key! elfeed-show-mode-map
|
||||
[remap next-buffer] #'+rss/next
|
||||
[remap previous-buffer] #'+rss/previous))
|
||||
(when (featurep! :editor evil +everywhere)
|
||||
(evil-define-key 'normal elfeed-search-mode-map
|
||||
"q" #'elfeed-kill-buffer
|
||||
"r" #'elfeed-search-update--force
|
||||
(kbd "M-RET") #'elfeed-search-browse-url)))
|
||||
|
||||
|
||||
(use-package! elfeed-org
|
||||
:when (featurep! +org)
|
||||
:after elfeed
|
||||
:config
|
||||
(let ((default-directory org-directory))
|
||||
(setq rmh-elfeed-org-files
|
||||
(mapcar #'expand-file-name +rss-elfeed-files)))
|
||||
(elfeed-org))
|
||||
5
.emacs.d/modules/app/rss/packages.el
Normal file
5
.emacs.d/modules/app/rss/packages.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/rss/packages.el
|
||||
|
||||
(package! elfeed)
|
||||
(package! elfeed-org)
|
||||
96
.emacs.d/modules/app/twitter/README.org
Normal file
96
.emacs.d/modules/app/twitter/README.org
Normal file
@@ -0,0 +1,96 @@
|
||||
#+TITLE: app/twitter
|
||||
#+DATE: October 11, 2019
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#hacks][Hacks]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#features][Features]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#commands--keybindings][Commands & Keybindings]]
|
||||
|
||||
* Description
|
||||
Enjoy twitter from emacs.
|
||||
|
||||
+ View various timelines side by side, e.g. user's timeline, home, etc.
|
||||
+ Post new tweets
|
||||
+ Send direct messages
|
||||
+ Retweet
|
||||
+ Follow and un-follow users
|
||||
+ Favorite tweets
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/hayamiz/twittering-mode][twittering-mode]]
|
||||
+ [[https://github.com/abo-abo/avy][avy]]
|
||||
|
||||
** TODO Hacks
|
||||
{A list of internal modifications to included packages}
|
||||
|
||||
* Prerequisites
|
||||
|
||||
+ For SSL connection (required by Twitter's API), one of the followings is required:
|
||||
+ [[http://curl.haxx.se/][cURL]]
|
||||
+ [[http://www.gnu.org/software/wget/][GNU Wget]]
|
||||
+ [[http://www.openssl.org/][OpenSSL]]
|
||||
+ [[http://www.gnu.org/software/gnutls/][GnuTLS]]
|
||||
+ [[http://www.gnupg.org/][GnuPG]] is required for keeping the OAuth token encrypted in local storage.
|
||||
+ ~twittering-icon-mode~ converts retrieved icons into XPM by default. To
|
||||
achieve this and for displaying icons in formats that are not supported by
|
||||
Emacs as well as for resizing icon images, [[http://www.imagemagick.org/][ImageMagick]] is required.
|
||||
|
||||
To build emacs with ImageMagick support the ~--with-imagemagick~ flag needs to
|
||||
be passed to the ~configure~ script, e.g. ~./configure --with-imagemagick~.
|
||||
For detailed instruction on how to build Emacs from source please refer to
|
||||
[[https://git.savannah.gnu.org/cgit/emacs.git/tree/INSTALL][INSTALL]] in Emacs' savannah repository.
|
||||
+ For keeping retrieved icons in local storage, [[http://www.gzip.org/][gzip]] is required.
|
||||
|
||||
* TODO Features
|
||||
An in-depth list of features, how to use them, and their dependencies.
|
||||
|
||||
* TODO Configuration
|
||||
How to configure this module, including common problems and how to address them.
|
||||
|
||||
* Troubleshooting
|
||||
Currently ~twittering-mode~ binds =SPC=, breaking its functionality as a leader
|
||||
key. To work around this issue you may use =M-SPC= instead when in
|
||||
~twittering-mode~.
|
||||
|
||||
* Appendix
|
||||
** Commands & Keybindings
|
||||
Here is a list of available commands and their default keybindings (defined in
|
||||
[[./config.el][config.el]]).
|
||||
|
||||
| command | key / ex command | description |
|
||||
|---------------------+------------------+-------------------------------------------------------------|
|
||||
| ~+twitter/quit~ | =q= | Close current window |
|
||||
| ~+twitter/quit-all~ | =Q= | Close all twitter windows and buffers, and delete workspace |
|
||||
|
||||
And when ~:editor evil +everywhere~ is active:
|
||||
|
||||
| command | key / ex command | description |
|
||||
|--------------------------------------------------+------------------+------------------------------------------------------------------|
|
||||
| ~twittering-favorite~ | =f= | Favorite/Like a tweet |
|
||||
| ~twittering-unfavorite~ | =F= | Un-favorite/Un-like a tweet |
|
||||
| ~twittering-follow~ | =C-f= | Follow user |
|
||||
| ~twittering-unfollow~ | =C-F= | Un-follow user |
|
||||
| ~twittering-delete-status~ | =d= | Delete a tweet |
|
||||
| ~twittering-retweet~ | =r= | Retweet |
|
||||
| ~twittering-toggle-or-retrieve-replied-statuses~ | =R= | Toggle or retrieve replies |
|
||||
| ~twittering-update-status-interactive~ | =o= | Update tweets |
|
||||
| ~+twitter/ace-link~ | =O= | Open some visible link from a ~twittering-mode~ buffer using ace |
|
||||
| ~twittering-search~ | =/= | Search |
|
||||
| ~twittering-goto-next-status~ | =J= | Go to next tweet |
|
||||
| ~twittering-goto-previous-status~ | =K= | Go to previous tweet |
|
||||
| ~twittering-goto-first-status~ | =gg= | Go to first tweet |
|
||||
| ~twittering-goto-last-status~ | =G= | Go to last tweet |
|
||||
| ~twittering-goto-next-status-of-user~ | =gj= | Go to next tweet of user |
|
||||
| ~twittering-goto-previous-status-of-user)))~ | =gk= | Go to previous tweet of user |
|
||||
103
.emacs.d/modules/app/twitter/autoload.el
Normal file
103
.emacs.d/modules/app/twitter/autoload.el
Normal file
@@ -0,0 +1,103 @@
|
||||
;;; app/twitter/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +twitter-workspace-name "*Twitter*"
|
||||
"The name to use for the twitter workspace.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter-display-buffer-fn (buf)
|
||||
"A replacement display-buffer command for `twittering-pop-to-buffer-function'
|
||||
that works with the feature/popup module."
|
||||
(let ((win (selected-window)))
|
||||
(display-buffer buf)
|
||||
;; This is required because the new window generated by `pop-to-buffer'
|
||||
;; may hide the region following the current position.
|
||||
(twittering-ensure-whole-of-status-is-visible win)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter-buffer-p (buf)
|
||||
"Return non-nil if BUF is a `twittering-mode' buffer."
|
||||
(eq 'twittering-mode (buffer-local-value 'major-mode buf)))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
(defvar +twitter--old-wconf nil)
|
||||
;;;###autoload
|
||||
(defun =twitter (arg)
|
||||
"Opens a workspace dedicated to `twittering-mode'."
|
||||
(interactive "P")
|
||||
(condition-case _
|
||||
(progn
|
||||
(if (and (not arg) (featurep! :ui workspaces))
|
||||
(+workspace/new +twitter-workspace-name)
|
||||
(setq +twitter--old-wconf (current-window-configuration))
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(call-interactively #'twit)
|
||||
(unless (get-buffer (car twittering-initial-timeline-spec-string))
|
||||
(error "Failed to open twitter"))
|
||||
(switch-to-buffer (car twittering-initial-timeline-spec-string))
|
||||
(dolist (name (cdr twittering-initial-timeline-spec-string))
|
||||
(split-window-horizontally)
|
||||
(switch-to-buffer name))
|
||||
(balance-windows)
|
||||
(call-interactively #'+twitter/rerender-all))
|
||||
('error (+twitter/quit-all))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/quit ()
|
||||
"Close the current `twitter-mode' buffer."
|
||||
(interactive)
|
||||
(when (eq major-mode 'twittering-mode)
|
||||
(twittering-kill-buffer)
|
||||
(cond ((one-window-p) (+twitter/quit-all))
|
||||
((featurep! :ui workspaces)
|
||||
(+workspace/close-window-or-workspace))
|
||||
((delete-window)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/quit-all ()
|
||||
"Close all open `twitter-mode' buffers and the associated workspace, if any."
|
||||
(interactive)
|
||||
(when (featurep! :ui workspaces)
|
||||
(+workspace/delete +twitter-workspace-name))
|
||||
(when +twitter--old-wconf
|
||||
(set-window-configuration +twitter--old-wconf)
|
||||
(setq +twitter--old-wconf nil))
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode (buffer-list) t))
|
||||
(twittering-kill-buffer buf)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/rerender-all ()
|
||||
"Rerender all `twittering-mode' buffers."
|
||||
(interactive)
|
||||
(dolist (buf (doom-buffers-in-mode 'twittering-mode (buffer-list) t))
|
||||
(with-current-buffer buf
|
||||
(twittering-rerender-timeline-all buf)
|
||||
(setq-local line-spacing 0.2)
|
||||
(goto-char (point-min)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +twitter/ace-link ()
|
||||
"Open a visible link, username or hashtag in a `twittering-mode' buffer."
|
||||
(interactive)
|
||||
(require 'avy)
|
||||
(let ((pt (avy-with +twitter/ace-link
|
||||
(avy--process
|
||||
(+twitter--collect-links)
|
||||
(avy--style-fn avy-style)))))
|
||||
(when (number-or-marker-p pt)
|
||||
(goto-char pt)
|
||||
(let ((uri (get-text-property (point) 'uri)))
|
||||
(if uri (browse-url uri))))))
|
||||
|
||||
(defun +twitter--collect-links ()
|
||||
(let ((end (window-end))
|
||||
points)
|
||||
(save-excursion
|
||||
(goto-char (window-start))
|
||||
(while (and (< (point) end)
|
||||
(ignore-errors (twittering-goto-next-thing) t))
|
||||
(push (point) points))
|
||||
(nreverse points))))
|
||||
79
.emacs.d/modules/app/twitter/config.el
Normal file
79
.emacs.d/modules/app/twitter/config.el
Normal file
@@ -0,0 +1,79 @@
|
||||
;;; app/twitter/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! twittering-mode
|
||||
:commands twit
|
||||
:config
|
||||
(setq twittering-private-info-file
|
||||
(expand-file-name "twittering-mode.gpg" doom-etc-dir)
|
||||
twittering-use-master-password t
|
||||
twittering-request-confirmation-on-posting t
|
||||
;; twittering-icon-mode t
|
||||
;; twittering-use-icon-storage t
|
||||
;; twittering-icon-storage-file (concat doom-cache-dir "twittering-mode-icons.gz")
|
||||
;; twittering-convert-fix-size 12
|
||||
twittering-timeline-header ""
|
||||
twittering-timeline-footer ""
|
||||
twittering-edit-skeleton 'inherit-any
|
||||
twittering-status-format "%FACE[font-lock-function-name-face]{ @%s} %FACE[italic]{%@} %FACE[error]{%FIELD-IF-NONZERO[❤ %d]{favorite_count}} %FACE[warning]{%FIELD-IF-NONZERO[↺ %d]{retweet_count}}
|
||||
%FOLD[ ]{%FILL{%t}%QT{
|
||||
%FOLD[ ]{%FACE[font-lock-function-name-face]{@%s}\t%FACE[shadow]{%@}
|
||||
%FOLD[ ]{%FILL{%t}}
|
||||
}}}
|
||||
|
||||
%FACE[twitter-divider]{ }
|
||||
"
|
||||
;; twittering-timeline-spec-alias '()
|
||||
twittering-initial-timeline-spec-string
|
||||
'(":home" ":mentions" ":direct_messages"))
|
||||
|
||||
(set-popup-rule! "^\\*twittering-edit" :size 15 :ttl nil :quit nil :select t)
|
||||
|
||||
(defface twitter-divider
|
||||
'((((background dark)) (:underline (:color "#141519")))
|
||||
(((background light)) (:underline (:color "#d3d3d3"))))
|
||||
"The vertical divider between tweets."
|
||||
:group 'twittering-mode)
|
||||
|
||||
(add-hook 'doom-real-buffer-functions #'+twitter-buffer-p)
|
||||
(when (featurep! :ui popup)
|
||||
(setq twittering-pop-to-buffer-function #'+twitter-display-buffer-fn))
|
||||
|
||||
(after! solaire-mode
|
||||
(add-hook 'twittering-mode-hook #'solaire-mode))
|
||||
|
||||
;; Custom header-line for twitter buffers
|
||||
(add-hook! 'twittering-mode-hook
|
||||
(defun +twitter-switch-mode-and-header-line-h ()
|
||||
(setq header-line-format mode-line-format
|
||||
mode-line-format nil)))
|
||||
|
||||
;; `epa--decode-coding-string' isn't defined in later versions of Emacs 27
|
||||
(unless (fboundp 'epa--decode-coding-string)
|
||||
(defalias 'epa--decode-coding-string #'decode-coding-string))
|
||||
|
||||
(define-key! twittering-mode-map
|
||||
"q" #'+twitter/quit
|
||||
"Q" #'+twitter/quit-all
|
||||
[remap twittering-kill-buffer] #'+twitter/quit
|
||||
[remap delete-window] #'+twitter/quit
|
||||
[remap +workspace/close-window-or-workspace] #'+twitter/quit)
|
||||
(when (featurep! :editor evil +everywhere)
|
||||
(define-key! twittering-mode-map
|
||||
[remap evil-window-delete] #'+twitter/quit
|
||||
"f" #'twittering-favorite
|
||||
"F" #'twittering-unfavorite
|
||||
"\C-f" #'twittering-follow
|
||||
"\C-F" #'twittering-unfollow
|
||||
"d" #'twittering-delete-status
|
||||
"r" #'twittering-retweet
|
||||
"R" #'twittering-toggle-or-retrieve-replied-statuses
|
||||
"o" #'twittering-update-status-interactive
|
||||
"O" #'+twitter/ace-link
|
||||
"/" #'twittering-search
|
||||
"J" #'twittering-goto-next-status
|
||||
"K" #'twittering-goto-previous-status
|
||||
"g" nil
|
||||
"gg" #'twittering-goto-first-status
|
||||
"G" #'twittering-goto-last-status
|
||||
"gj" #'twittering-goto-next-status-of-user
|
||||
"gk" #'twittering-goto-previous-status-of-user)))
|
||||
5
.emacs.d/modules/app/twitter/packages.el
Normal file
5
.emacs.d/modules/app/twitter/packages.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/twitter/packages.el
|
||||
|
||||
(package! twittering-mode)
|
||||
(package! avy)
|
||||
131
.emacs.d/modules/app/write/README.org
Normal file
131
.emacs.d/modules/app/write/README.org
Normal file
@@ -0,0 +1,131 @@
|
||||
#+TITLE: app/write
|
||||
#+DATE: October 10, 2019
|
||||
#+SINCE: v1.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#language-tool][Language Tool]]
|
||||
- [[#wordnut][Wordnut]]
|
||||
- [[#features][Features]]
|
||||
- [[#m-x-write-mode][~M-x +write-mode~]]
|
||||
- [[#language-tool-langtool][Language Tool ~+langtool~]]
|
||||
- [[#commands][Commands]]
|
||||
- [[#wordnut-wordnut][Wordnut ~+wordnut~]]
|
||||
- [[#commands-1][Commands]]
|
||||
- [[#synosaurus][Synosaurus]]
|
||||
- [[#commands-2][Commands]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#mixed-pitch-mode][mixed-pitch-mode]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#minor-modes][Minor modes]]
|
||||
- [[#commands-3][Commands]]
|
||||
|
||||
* Description
|
||||
Adds word processing tools and the ~+write-mode~ minor mode, which converts
|
||||
Emacs into a more comfortable writing environment.
|
||||
|
||||
** Module Flags
|
||||
This module provides two module flags:
|
||||
|
||||
- ~+langtool~ Enables language tool integration.
|
||||
- ~+wordnut~ Enables wordnet integration.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/hpdeifel/synosaurus][synosaurus]]
|
||||
+ [[https://gitlab.com/jabranham/mixed-pitch][mixed-pitch]]
|
||||
+ [[https://github.com/joostkremers/visual-fill-column][visual-fill-column]]
|
||||
+ [[https://github.com/mhayashi1120/Emacs-langtool][langtool]]* (=+langtool=)
|
||||
+ [[https://github.com/gromnitsky/wordnut][wordnut]]* (=+wordnut=)
|
||||
|
||||
* Prerequisites
|
||||
** Language Tool
|
||||
Either download and deploy it from https://languagetool.org/ or install it
|
||||
through your OS package manager:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
# MacOS/Homebrew users:
|
||||
brew install languagetool
|
||||
|
||||
# Arch Linux users:
|
||||
sudo pacman -S languagetool
|
||||
#+END_SRC
|
||||
|
||||
This module tries to guess the location of languagetool-commandline.jar. If you
|
||||
get a warning that Doom =couldn't find languagetool-commandline.jar=, you will
|
||||
need to find langaugetool-commandline.jar and set ~langtool-language-tool-jar~
|
||||
to its path.
|
||||
|
||||
** Wordnut
|
||||
This requires =wordnet= to be installed, which should be available through your
|
||||
OS package manager:
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
# MacOS/Homebrew users:
|
||||
brew install wordnet
|
||||
|
||||
# Arch Linux users:
|
||||
sudo pacaur -S wordnet # on the AUR
|
||||
#+END_SRC
|
||||
|
||||
* Features
|
||||
** ~M-x +write-mode~
|
||||
Write mode makes Emacs a more comfortable writing environment by:
|
||||
|
||||
- Centering the buffer (with ~visual-fill-column-mode~), ala distraction-free
|
||||
mode from other text editors.
|
||||
- Soft-wrapping long text lines with ~visual-line-mode~.
|
||||
- Enabling ~mixed-pitch-mode~, allowing fixed-width and variable-pitch fonts to
|
||||
co-exist in one buffer. For example, a monospace font for SRC blocks and Arial
|
||||
for everything else.
|
||||
- In org-mode:
|
||||
- Turns on ~org-indent-mode~
|
||||
- Turns on ~+org-pretty-mode~
|
||||
|
||||
** Language Tool ~+langtool~
|
||||
[[https://www.languagetool.org/][Language Tool]] is a polyglot proofreader service that checks for grammar and
|
||||
stylistic issues in your writing. This requires Java 1.8+.
|
||||
|
||||
#+begin_quote
|
||||
This requires Java 1.8+
|
||||
#+end_quote
|
||||
|
||||
*** Commands
|
||||
- ~langtool-check~
|
||||
- ~langtool-correct-buffer~
|
||||
|
||||
** Wordnut ~+wordnut~
|
||||
Wordnut provides a searchable dictionary frontend for Emacs. This requires
|
||||
~wordnet~, which should be available in your OS's package manager.
|
||||
|
||||
*** Commands
|
||||
- ~wordnut-search~
|
||||
- ~wordnut-lookup-curent-word~
|
||||
|
||||
** Synosaurus
|
||||
Synosaurus provides a service for looking up synonyms. It requires an internet
|
||||
connection.
|
||||
|
||||
*** Commands
|
||||
- ~synosaurus-lookup~
|
||||
- ~synosaurus-choose-and-replace~
|
||||
|
||||
* Configuration
|
||||
** mixed-pitch-mode
|
||||
To configure which faces are displayed with fixed-pitch fonts in
|
||||
~mixed-pitch-mode~, look into ~mixed-pitch-fixed-pitch-faces~.
|
||||
|
||||
* Appendix
|
||||
** Minor modes
|
||||
- ~+write-mode~
|
||||
- ~mixed-pitch-mode~
|
||||
** Commands
|
||||
- ~langtool-check~
|
||||
- ~langtool-correct-buffer~
|
||||
- ~synosaurus-choose-and-replace~
|
||||
- ~synosaurus-lookup~
|
||||
- ~wordnut-lookup-curent-word~
|
||||
- ~wordnut-search~
|
||||
43
.emacs.d/modules/app/write/autoload.el
Normal file
43
.emacs.d/modules/app/write/autoload.el
Normal file
@@ -0,0 +1,43 @@
|
||||
;;; app/write/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar +write-mode-map (make-sparse-keymap)
|
||||
"TODO")
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode +write-mode
|
||||
"Turns Emacs into a more comfortable writing environment and word processor."
|
||||
:init-value nil
|
||||
:keymap +write-mode-map
|
||||
(setq-local visual-fill-column-center-text t)
|
||||
(when +write-text-scale
|
||||
(text-scale-set (if +write-mode 2 0)))
|
||||
(when +write-line-spacing
|
||||
(setq-local line-spacing +write-line-spacing)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write-init-org-mode-h ()
|
||||
"Initializes `org-mode' specific settings for `+write-mode'."
|
||||
(when (eq major-mode 'org-mode)
|
||||
(+org-pretty-mode (if +write-mode +1 -1))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write-init-line-numbers-h ()
|
||||
(display-line-numbers-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write-init-mixed-pitch-h ()
|
||||
(mixed-pitch-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +write-init-visual-fill-column-h ()
|
||||
(visual-fill-column-mode (if +write-mode +1 -1)))
|
||||
|
||||
;;;###autoload
|
||||
(add-hook! '+write-mode-hook
|
||||
#'(flyspell-mode
|
||||
visual-line-mode
|
||||
+write-init-mixed-pitch-h
|
||||
+write-init-visual-fill-column-h
|
||||
+write-init-line-numbers-h
|
||||
+write-init-org-mode-h))
|
||||
56
.emacs.d/modules/app/write/config.el
Normal file
56
.emacs.d/modules/app/write/config.el
Normal file
@@ -0,0 +1,56 @@
|
||||
;;; app/write/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +write-text-scale nil
|
||||
"What to scale the text up to in `+write-mode'. Uses `text-scale-set'.")
|
||||
|
||||
(defvar +write-line-spacing nil
|
||||
"What to set `line-spacing' in `+write-mode'.")
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(use-package! langtool
|
||||
:when (featurep! +langtool)
|
||||
:commands (langtool-check
|
||||
langtool-check-done
|
||||
langtool-show-message-at-point
|
||||
langtool-correct-buffer)
|
||||
:init (setq langtool-default-language "en-US")
|
||||
:config
|
||||
(unless langtool-language-tool-jar
|
||||
(setq langtool-language-tool-jar
|
||||
(cond (IS-MAC
|
||||
(locate-file "libexec/languagetool-commandline.jar"
|
||||
(doom-files-in "/usr/local/Cellar/languagetool"
|
||||
:type 'dirs
|
||||
:depth 2)))
|
||||
(IS-LINUX
|
||||
"/usr/share/java/languagetool/languagetool-commandline.jar")))))
|
||||
|
||||
|
||||
;; `synosaurus'
|
||||
(setq synosaurus-choose-method 'default)
|
||||
|
||||
|
||||
;; `mixed-pitch'
|
||||
(after! mixed-pitch
|
||||
(setq mixed-pitch-fixed-pitch-faces
|
||||
(append mixed-pitch-fixed-pitch-faces
|
||||
'(org-todo-keyword-todo
|
||||
org-todo-keyword-habt
|
||||
org-todo-keyword-done
|
||||
org-todo-keyword-wait
|
||||
org-todo-keyword-kill
|
||||
org-todo-keyword-outd
|
||||
org-todo
|
||||
org-indent
|
||||
line-number
|
||||
line-number-current-line
|
||||
org-special-keyword
|
||||
org-date
|
||||
org-property-value
|
||||
org-special-keyword
|
||||
org-property-value
|
||||
org-ref-cite-face
|
||||
org-tag
|
||||
font-lock-comment-face))))
|
||||
7
.emacs.d/modules/app/write/doctor.el
Normal file
7
.emacs.d/modules/app/write/doctor.el
Normal file
@@ -0,0 +1,7 @@
|
||||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; app/write/doctor.el
|
||||
|
||||
(when (featurep! +langtool)
|
||||
(require 'langtool)
|
||||
(unless (file-exists-p langtool-language-tool-jar)
|
||||
(warn! "Couldn't find languagetool-commandline.jar")))
|
||||
12
.emacs.d/modules/app/write/packages.el
Normal file
12
.emacs.d/modules/app/write/packages.el
Normal file
@@ -0,0 +1,12 @@
|
||||
;; -*- 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)
|
||||
131
.emacs.d/modules/completion/company/README.org
Normal file
131
.emacs.d/modules/completion/company/README.org
Normal file
@@ -0,0 +1,131 @@
|
||||
#+TITLE: completion/company
|
||||
#+DATE: February 19, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#features][Features]]
|
||||
- [[#code-completion][Code completion]]
|
||||
- [[#vim-esque-omni-completion-prefix-c-x][Vim-esque omni-completion prefix (C-x)]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#enable-company-backends-in-certain-modes][Enable company backend(s) in certain modes]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
- [[#x-mode-doesnt-have-code-completion-support-or-requires-extra-setup][X-mode doesn't have code completion support or requires extra setup.]]
|
||||
- [[#no-backends-or-the-incorrect-ones-have-been-registered-for-x-mode][No backends (or the incorrect ones) have been registered for X-mode.]]
|
||||
|
||||
* Description
|
||||
This module provides code completion, powered by [[https://github.com/company-mode/company-mode][company-mode]]. It is required
|
||||
for code completion in many of Doom's :lang modules.
|
||||
|
||||
https://assets.doomemacs.org/completion/company/overlay.png
|
||||
|
||||
** Module Flags
|
||||
+ =+childframe= Enables displaying completion candidates in a child frame,
|
||||
rather than an overlay or tooltip (among with other UI enhancements). *This
|
||||
requires GUI Emacs 26.1+ and is incompatible with the =+tng= flag*
|
||||
+ =+tng= Enables completion using only ~TAB~. Pressing ~TAB~ will select the
|
||||
next completion suggestion, while ~S-TAB~ will select the previous one. *This
|
||||
is incompatible with the =+childframe= flag*
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/company-mode/company-mode][company-mode]]
|
||||
+ [[https://github.com/hlissner/emacs-company-dict][company-dict]]
|
||||
+ [[https://github.com/raxod502/prescient.el][company-prescient]]
|
||||
+ [[https://github.com/sebastiencs/company-box][company-box]]* (=+childframe=)
|
||||
|
||||
* Prerequisites
|
||||
This module has no direct prerequisites.
|
||||
|
||||
However, some major modes may require additional setup for code completion to
|
||||
work in them. Some major modes may have no completion support at all. Check that
|
||||
major mode's module's documentation for details.
|
||||
|
||||
* Features
|
||||
** Code completion
|
||||
By default, completion is triggered after a short idle period or with the
|
||||
=C-SPC= key. While the popup is visible, the following keys are available:
|
||||
|
||||
| Keybind | Description |
|
||||
|---------+------------------------------------------|
|
||||
| =C-n= | Go to next candidate |
|
||||
| =C-p= | Go to previous candidate |
|
||||
| =C-j= | (evil) Go to next candidate |
|
||||
| =C-k= | (evil) Go to previous candidate |
|
||||
| =C-h= | Display documentation (if available) |
|
||||
| =C-u= | Move to previous page of candidates |
|
||||
| =C-d= | Move to next page of candidates |
|
||||
| =C-s= | Filter candidates |
|
||||
| =C-S-s= | Search candidates with helm/ivy |
|
||||
| =C-SPC= | Complete common |
|
||||
| =TAB= | Complete common or select next candidate |
|
||||
| =S-TAB= | Select previous candidate |
|
||||
|
||||
** Vim-esque omni-completion prefix (C-x)
|
||||
In the spirit of Vim's omni-completion, the following insert mode keybinds are
|
||||
available to evil users to access specific company backends:
|
||||
|
||||
| Keybind | Description |
|
||||
|-----------+-----------------------------------|
|
||||
| =C-x C-]= | Complete etags |
|
||||
| =C-x C-f= | Complete file path |
|
||||
| =C-x C-k= | Complete from dictionary/keyword |
|
||||
| =C-x C-l= | Complete full line |
|
||||
| =C-x C-o= | Invoke complete-at-point function |
|
||||
| =C-x C-n= | Complete next symbol at point |
|
||||
| =C-x C-p= | Complete previous symbol at point |
|
||||
| =C-x C-s= | Complete snippet |
|
||||
| =C-x s= | Complete spelling suggestions |
|
||||
|
||||
* Configuration
|
||||
** Enable company backend(s) in certain modes
|
||||
The ~set-company-backend!~ function exists for setting ~company-backends~
|
||||
buffer-locally in MODES, which is either a major-mode symbol, a minor-mode
|
||||
symbol, or a list of either. BACKENDS are prepended to ~company-backends~ for
|
||||
those modes.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(after! js2-mode
|
||||
(set-company-backend! 'js2-mode 'company-tide 'company-yasnippet))
|
||||
|
||||
(after! sh-script
|
||||
(set-company-backend! 'sh-mode
|
||||
'(company-shell :with company-yasnippet)))
|
||||
|
||||
(after! cc-mode
|
||||
(set-company-backend! 'c-mode
|
||||
'(:separate company-irony-c-headers company-irony)))
|
||||
#+END_SRC
|
||||
|
||||
To unset the backends for a particular mode, pass ~nil~ to it:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(after! sh-script
|
||||
(set-company-backend! 'sh-mode nil))
|
||||
#+END_SRC
|
||||
|
||||
* Troubleshooting
|
||||
If code completion isn't working for you, consider the following common causes
|
||||
before you file a bug report:
|
||||
|
||||
** X-mode doesn't have code completion support or requires extra setup.
|
||||
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~
|
||||
requires that you have a Robe server running (~M-x robe-start~).
|
||||
|
||||
Check the relevant module's documentation for this kind of information.
|
||||
|
||||
** No backends (or the incorrect ones) have been registered for X-mode.
|
||||
Doom expects every mode to have an explicit list of company-backends (and as
|
||||
short a list as possible). This may mean you aren't getting all the completion
|
||||
you want or any at all.
|
||||
|
||||
Check the value of ~company-backends~ (=SPC h v company-backends=) from that
|
||||
mode to see what backends are available. Check the [[*Assigning company backend(s) to modes][Configuration section]] for
|
||||
details on changing what backends are available for that mode.
|
||||
156
.emacs.d/modules/completion/company/autoload.el
Normal file
156
.emacs.d/modules/completion/company/autoload.el
Normal file
@@ -0,0 +1,156 @@
|
||||
;;; completion/company/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar +company-backend-alist
|
||||
'((text-mode company-dabbrev company-yasnippet company-ispell)
|
||||
(prog-mode company-capf company-yasnippet)
|
||||
(conf-mode company-capf company-dabbrev-code company-yasnippet))
|
||||
"An alist matching modes to company backends. The backends for any mode is
|
||||
built from this.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-company-backend! (modes &rest backends)
|
||||
"Prepends BACKENDS (in order) to `company-backends' in MODES.
|
||||
|
||||
MODES should be one symbol or a list of them, representing major or minor modes.
|
||||
This will overwrite backends for MODES on consecutive uses.
|
||||
|
||||
If the car of BACKENDS is nil, unset the backends for MODES.
|
||||
|
||||
Examples:
|
||||
|
||||
(set-company-backend! 'js2-mode
|
||||
'company-tide 'company-yasnippet)
|
||||
|
||||
(set-company-backend! 'sh-mode
|
||||
'(company-shell :with company-yasnippet))
|
||||
|
||||
(set-company-backend! '(c-mode c++-mode)
|
||||
'(:separate company-irony-c-headers company-irony))
|
||||
|
||||
(set-company-backend! 'sh-mode nil) ; unsets backends for sh-mode"
|
||||
(declare (indent defun))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(if (null (car backends))
|
||||
(setq +company-backend-alist
|
||||
(delq (assq mode +company-backend-alist)
|
||||
+company-backend-alist))
|
||||
(setf (alist-get mode +company-backend-alist)
|
||||
backends))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(defun +company--backends ()
|
||||
(let (backends)
|
||||
(let ((mode major-mode)
|
||||
(modes (list major-mode)))
|
||||
(while (setq mode (get mode 'derived-mode-parent))
|
||||
(push mode modes))
|
||||
(dolist (mode modes)
|
||||
(dolist (backend (append (cdr (assq mode +company-backend-alist))
|
||||
(default-value 'company-backends)))
|
||||
(push backend backends)))
|
||||
(delete-dups
|
||||
(append (cl-loop for (mode . backends) in +company-backend-alist
|
||||
if (or (eq major-mode mode) ; major modes
|
||||
(and (boundp mode)
|
||||
(symbol-value mode))) ; minor modes
|
||||
append backends)
|
||||
(nreverse backends))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
;;;###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)))
|
||||
|
||||
(put '+company-init-backends-h 'permanent-local-hook t)
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +company-has-completion-p ()
|
||||
"Return non-nil if a completion candidate exists at point."
|
||||
(and (company-manual-begin)
|
||||
(= company-candidates-length 1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/toggle-auto-completion ()
|
||||
"Toggle as-you-type code completion."
|
||||
(interactive)
|
||||
(require 'company)
|
||||
(setq company-idle-delay (unless company-idle-delay 0.2))
|
||||
(message "Auto completion %s"
|
||||
(if company-idle-delay "enabled" "disabled")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/complete ()
|
||||
"Bring up the completion popup. If only one result, complete it."
|
||||
(interactive)
|
||||
(require 'company)
|
||||
(when (ignore-errors
|
||||
(/= (point)
|
||||
(cdr (bounds-of-thing-at-point 'symbol))))
|
||||
(save-excursion (insert " ")))
|
||||
(when (and (company-manual-begin)
|
||||
(= company-candidates-length 1))
|
||||
(company-complete-common)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dabbrev ()
|
||||
"Invokes `company-dabbrev-code' in prog-mode buffers and `company-dabbrev'
|
||||
everywhere else."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (derived-mode-p 'prog-mode)
|
||||
#'company-dabbrev-code
|
||||
#'company-dabbrev)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/whole-lines (command &optional arg &rest ignored)
|
||||
"`company-mode' completion backend that completes whole-lines, akin to vim's
|
||||
C-x C-l."
|
||||
(interactive (list 'interactive))
|
||||
(require 'company)
|
||||
(pcase command
|
||||
(`interactive (company-begin-backend '+company/whole-lines))
|
||||
(`prefix (company-grab-line "^[\t\s]*\\(.+\\)" 1))
|
||||
(`candidates
|
||||
(all-completions
|
||||
arg
|
||||
(delete-dups
|
||||
(split-string
|
||||
(replace-regexp-in-string
|
||||
"^[\t\s]+" ""
|
||||
(concat (buffer-substring-no-properties (point-min) (line-beginning-position))
|
||||
(buffer-substring-no-properties (line-end-position) (point-max))))
|
||||
"\\(\r\n\\|[\n\r]\\)" t))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dict-or-keywords ()
|
||||
"`company-mode' completion combining `company-dict' and `company-keywords'."
|
||||
(interactive)
|
||||
(require 'company-dict)
|
||||
(require 'company-keywords)
|
||||
(let ((company-backends '((company-keywords company-dict))))
|
||||
(call-interactively #'company-complete)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dabbrev-code-previous ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(require 'company-dabbrev)
|
||||
(let ((company-selection-wrap-around t))
|
||||
(call-interactively #'+company/dabbrev)
|
||||
(company-select-previous-or-abort)))
|
||||
133
.emacs.d/modules/completion/company/config.el
Normal file
133
.emacs.d/modules/completion/company/config.el
Normal file
@@ -0,0 +1,133 @@
|
||||
;;; completion/company/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! company
|
||||
:commands company-complete-common company-manual-begin company-grab-line
|
||||
:after-call pre-command-hook after-find-file
|
||||
:init
|
||||
(setq 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))
|
||||
:config
|
||||
(when (featurep! :editor evil)
|
||||
(add-hook 'company-mode-hook #'evil-normalize-keymaps)
|
||||
|
||||
;; 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'.
|
||||
(defadvice! +company--abort-previous-a (&rest _)
|
||||
:before #'company-begin-backend
|
||||
(company-abort)))
|
||||
|
||||
(add-hook 'company-mode-hook #'+company-init-backends-h)
|
||||
(global-company-mode +1))
|
||||
|
||||
|
||||
(use-package! company-tng
|
||||
:when (featurep! +tng)
|
||||
:after-call post-self-insert-hook
|
||||
:config
|
||||
(add-to-list 'company-frontends 'company-tng-frontend)
|
||||
(define-key! company-active-map
|
||||
"RET" nil
|
||||
[return] nil
|
||||
"TAB" #'company-select-next
|
||||
[tab] #'company-select-next
|
||||
[backtab] #'company-select-previous))
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(after! company-files
|
||||
(pushnew! company-files--regexps
|
||||
"file:\\(\\(?:\\.\\{1,2\\}/\\|~/\\|/\\)[^\]\n]*\\)"))
|
||||
|
||||
|
||||
(use-package! company-prescient
|
||||
:hook (company-mode . company-prescient-mode)
|
||||
:config
|
||||
;; NOTE prescient config duplicated with `ivy'
|
||||
(setq prescient-save-file (concat doom-cache-dir "prescient-save.el"))
|
||||
(prescient-persist-mode +1))
|
||||
|
||||
|
||||
(use-package! company-box
|
||||
:when (featurep! +childframe)
|
||||
:hook (company-mode . company-box-mode)
|
||||
:config
|
||||
(setq company-box-show-single-candidate t
|
||||
company-box-backends-colors nil
|
||||
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)
|
||||
company-box-icons-all-the-icons
|
||||
`((Unknown . ,(all-the-icons-material "find_in_page" :height 0.8 :face 'all-the-icons-purple))
|
||||
(Text . ,(all-the-icons-material "text_fields" :height 0.8 :face 'all-the-icons-green))
|
||||
(Method . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Function . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Constructor . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Field . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(Variable . ,(all-the-icons-material "adjust" :height 0.8 :face 'all-the-icons-blue))
|
||||
(Class . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
|
||||
(Interface . ,(all-the-icons-material "settings_input_component" :height 0.8 :face 'all-the-icons-red))
|
||||
(Module . ,(all-the-icons-material "view_module" :height 0.8 :face 'all-the-icons-red))
|
||||
(Property . ,(all-the-icons-material "settings" :height 0.8 :face 'all-the-icons-red))
|
||||
(Unit . ,(all-the-icons-material "straighten" :height 0.8 :face 'all-the-icons-red))
|
||||
(Value . ,(all-the-icons-material "filter_1" :height 0.8 :face 'all-the-icons-red))
|
||||
(Enum . ,(all-the-icons-material "plus_one" :height 0.8 :face 'all-the-icons-red))
|
||||
(Keyword . ,(all-the-icons-material "filter_center_focus" :height 0.8 :face 'all-the-icons-red))
|
||||
(Snippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-red))
|
||||
(Color . ,(all-the-icons-material "color_lens" :height 0.8 :face 'all-the-icons-red))
|
||||
(File . ,(all-the-icons-material "insert_drive_file" :height 0.8 :face 'all-the-icons-red))
|
||||
(Reference . ,(all-the-icons-material "collections_bookmark" :height 0.8 :face 'all-the-icons-red))
|
||||
(Folder . ,(all-the-icons-material "folder" :height 0.8 :face 'all-the-icons-red))
|
||||
(EnumMember . ,(all-the-icons-material "people" :height 0.8 :face 'all-the-icons-red))
|
||||
(Constant . ,(all-the-icons-material "pause_circle_filled" :height 0.8 :face 'all-the-icons-red))
|
||||
(Struct . ,(all-the-icons-material "streetview" :height 0.8 :face 'all-the-icons-red))
|
||||
(Event . ,(all-the-icons-material "event" :height 0.8 :face 'all-the-icons-red))
|
||||
(Operator . ,(all-the-icons-material "control_point" :height 0.8 :face 'all-the-icons-red))
|
||||
(TypeParameter . ,(all-the-icons-material "class" :height 0.8 :face 'all-the-icons-red))
|
||||
;; (Template . ,(company-box-icons-image "Template.png"))))
|
||||
(Yasnippet . ,(all-the-icons-material "short_text" :height 0.8 :face 'all-the-icons-green))
|
||||
(ElispFunction . ,(all-the-icons-material "functions" :height 0.8 :face 'all-the-icons-red))
|
||||
(ElispVariable . ,(all-the-icons-material "check_circle" :height 0.8 :face 'all-the-icons-blue))
|
||||
(ElispFeature . ,(all-the-icons-material "stars" :height 0.8 :face 'all-the-icons-orange))
|
||||
(ElispFace . ,(all-the-icons-material "format_paint" :height 0.8 :face 'all-the-icons-pink))))
|
||||
|
||||
(defun +company-box-icons--yasnippet-fn (candidate)
|
||||
(when (get-text-property 0 'yas-annotation candidate)
|
||||
'Yasnippet))
|
||||
|
||||
(defun +company-box-icons--elisp-fn (candidate)
|
||||
(when (derived-mode-p 'emacs-lisp-mode)
|
||||
(let ((sym (intern candidate)))
|
||||
(cond ((fboundp sym) 'ElispFunction)
|
||||
((boundp sym) 'ElispVariable)
|
||||
((featurep sym) 'ElispFeature)
|
||||
((facep sym) 'ElispFace))))))
|
||||
|
||||
|
||||
(use-package! company-dict
|
||||
:defer t
|
||||
:config
|
||||
(setq company-dict-dir (expand-file-name "dicts" doom-private-dir))
|
||||
(add-hook! 'doom-project-hook
|
||||
(defun +company-enable-project-dicts-h (mode &rest _)
|
||||
"Enable per-project dictionaries."
|
||||
(if (symbol-value mode)
|
||||
(add-to-list 'company-dict-minor-mode-list mode nil #'eq)
|
||||
(setq company-dict-minor-mode-list (delq mode company-dict-minor-mode-list))))))
|
||||
8
.emacs.d/modules/completion/company/packages.el
Normal file
8
.emacs.d/modules/completion/company/packages.el
Normal file
@@ -0,0 +1,8 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; completion/company/packages.el
|
||||
|
||||
(package! company)
|
||||
(package! company-dict)
|
||||
(package! company-prescient)
|
||||
(when (featurep! +childframe)
|
||||
(package! company-box))
|
||||
75
.emacs.d/modules/completion/company/test/test-company.el
Normal file
75
.emacs.d/modules/completion/company/test/test-company.el
Normal file
@@ -0,0 +1,75 @@
|
||||
;; -*- lexical-binding: t; no-byte-compile: t; -*-
|
||||
;;; completion/company/test/test-company.el
|
||||
|
||||
(describe "completion/company"
|
||||
(before-all
|
||||
(load! "../autoload"))
|
||||
|
||||
(describe ":company-backend"
|
||||
:var (a +company-backend-alist backends)
|
||||
(before-each
|
||||
(setq-default company-backends '(t))
|
||||
(setq +company-backend-alist nil
|
||||
a (get-buffer-create "x"))
|
||||
(fset 'backends
|
||||
(lambda (mode)
|
||||
(let ((major-mode mode))
|
||||
(+company--backends))))
|
||||
(set-buffer a)
|
||||
(spy-on 'require))
|
||||
(after-each
|
||||
(kill-buffer a))
|
||||
|
||||
;;
|
||||
(it "sets backends for a major mode"
|
||||
(set-company-backend! 'text-mode 'a)
|
||||
(expect (backends 'text-mode) :to-equal '(a t)))
|
||||
|
||||
(it "sets backends for a derived-mode"
|
||||
(set-company-backend! 'prog-mode 'a)
|
||||
(expect (backends 'prog-mode) :to-equal '(a t))
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(a t)))
|
||||
|
||||
(it "sets multiple backends for exact major modes"
|
||||
(set-company-backend! '(text-mode emacs-lisp-mode) 'a 'b)
|
||||
(expect (backends 'text-mode) :to-equal (backends 'emacs-lisp-mode)))
|
||||
|
||||
(it "sets cumulative backends"
|
||||
(set-company-backend! 'prog-mode '(a b c))
|
||||
(set-company-backend! 'emacs-lisp-mode 'd 'e)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(d e (a b c) t)))
|
||||
|
||||
(it "sets cumulative backends with a minor mode"
|
||||
(set-company-backend! 'prog-mode '(a b c))
|
||||
(set-company-backend! 'emacs-lisp-mode 'd 'e)
|
||||
(set-company-backend! 'some-minor-mode 'x 'y)
|
||||
(setq-local some-minor-mode t)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(x y d e (a b c) t)))
|
||||
|
||||
(it "overwrites past backends"
|
||||
(set-company-backend! 'text-mode 'old 'backends)
|
||||
(set-company-backend! 'text-mode 'new 'backends)
|
||||
(expect (backends 'text-mode) :to-equal '(new backends t)))
|
||||
|
||||
(it "unsets past backends"
|
||||
(set-company-backend! 'text-mode 'old)
|
||||
(set-company-backend! 'text-mode nil)
|
||||
(expect (backends 'text-mode) :to-equal (default-value 'company-backends)))
|
||||
|
||||
(it "unsets past parent backends"
|
||||
(set-company-backend! 'prog-mode 'old)
|
||||
(set-company-backend! 'emacs-lisp-mode 'child)
|
||||
(set-company-backend! 'prog-mode nil)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(child t)))
|
||||
|
||||
(it "overwrites past cumulative backends"
|
||||
(set-company-backend! 'prog-mode 'base)
|
||||
(set-company-backend! 'emacs-lisp-mode 'old)
|
||||
(set-company-backend! 'emacs-lisp-mode 'new)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(new base t)))
|
||||
|
||||
(it "overwrites past parent backends"
|
||||
(set-company-backend! 'prog-mode 'base)
|
||||
(set-company-backend! 'emacs-lisp-mode 'child)
|
||||
(set-company-backend! 'prog-mode 'new)
|
||||
(expect (backends 'emacs-lisp-mode) :to-equal '(child new t)))))
|
||||
42
.emacs.d/modules/completion/helm/autoload/evil.el
Normal file
42
.emacs.d/modules/completion/helm/autoload/evil.el
Normal file
@@ -0,0 +1,42 @@
|
||||
;;; completion/helm/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+helm:project-search "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:project-search (all-files-p query)
|
||||
"Ex interface for `+helm/grep'"
|
||||
(interactive "<!><a>")
|
||||
(+helm/project-search all-files-p query))
|
||||
|
||||
;;;###autoload (autoload '+helm:project-search-from-cwd "completion/helm/autoload/evil" nil t)
|
||||
(evil-define-command +helm:project-search-from-cwd (query &optional recurse-p)
|
||||
"Ex interface for `+helm/grep-from-cwd'."
|
||||
(interactive "<a><!>")
|
||||
(+helm/project-search-from-cwd (not recurse-p) query))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm--set-prompt-display (pos)
|
||||
"TODO"
|
||||
(let (beg state region-active m)
|
||||
(with-selected-window (minibuffer-window)
|
||||
(setq beg (save-excursion (vertical-motion 0 (helm-window)) (point))
|
||||
state evil-state
|
||||
region-active (region-active-p)
|
||||
m (mark t)))
|
||||
(when region-active
|
||||
(setq m (- m beg))
|
||||
;; Increment pos to handle the space before prompt (i.e `pref').
|
||||
(put-text-property (1+ (min m pos)) (+ 2 (max m pos))
|
||||
'face
|
||||
(list :background (face-background 'region))
|
||||
header-line-format))
|
||||
(put-text-property
|
||||
;; Increment pos to handle the space before prompt (i.e `pref').
|
||||
(+ 1 pos) (+ 2 pos)
|
||||
'face
|
||||
(if (eq state 'insert)
|
||||
'underline
|
||||
;; Don't just use 'cursor, this can hide the current character.
|
||||
(list :inverse-video t
|
||||
:foreground (face-background 'cursor)
|
||||
:background (face-background 'default)))
|
||||
header-line-format)))
|
||||
96
.emacs.d/modules/completion/helm/autoload/helm.el
Normal file
96
.emacs.d/modules/completion/helm/autoload/helm.el
Normal file
@@ -0,0 +1,96 @@
|
||||
;;; completion/helm/autoload/helm.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/projectile-find-file ()
|
||||
"Call `helm-find-files' if called from HOME, otherwise
|
||||
`helm-projectile-find-file'."
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(if (or (file-equal-p default-directory "~")
|
||||
(if-let* ((proot (doom-project-root)))
|
||||
(file-equal-p proot "~")
|
||||
t))
|
||||
#'helm-find-files
|
||||
#'helm-projectile-find-file)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/workspace-buffer-list ()
|
||||
"A version of `helm-buffers-list' with its buffer list restricted to the
|
||||
current workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :ui workspaces)
|
||||
(user-error "This command requires the :ui workspaces module"))
|
||||
(with-no-warnings
|
||||
(with-persp-buffer-list nil (helm-buffers-list))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/workspace-mini ()
|
||||
"A version of `helm-mini' with its buffer list restricted to the current
|
||||
workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :ui workspaces)
|
||||
(user-error "This command requires the :ui workspaces module"))
|
||||
(with-no-warnings
|
||||
(with-persp-buffer-list nil (helm-mini))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Project search
|
||||
|
||||
;;;###autoload
|
||||
(cl-defun +helm-file-search (&key query in all-files (recursive t))
|
||||
"Conduct a file search using ripgrep.
|
||||
|
||||
:query STRING
|
||||
Determines the initial input to search for.
|
||||
:in PATH
|
||||
Sets what directory to base the search out of. Defaults to the current
|
||||
project's root.
|
||||
:recursive BOOL
|
||||
Whether or not to search files recursively from the base directory."
|
||||
(declare (indent defun))
|
||||
(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))
|
||||
(helm-rg-default-extra-args
|
||||
(delq nil (list (when all-files "-z -uu")
|
||||
(unless recursive "--maxdepth 1")))))
|
||||
(helm-rg (or query
|
||||
(when (use-region-p)
|
||||
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
|
||||
(end (or (bound-and-true-p evil-visual-end) (region-end))))
|
||||
(when (> (abs (- end beg)) 1)
|
||||
(buffer-substring-no-properties beg end))))
|
||||
""))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/project-search (&optional arg initial-query directory)
|
||||
"Performs a project search from the project root with ripgrep.
|
||||
|
||||
ARG (universal argument), include all files, even hidden or compressed ones, in
|
||||
the search."
|
||||
(interactive "P")
|
||||
(+helm-file-search
|
||||
:query initial-query
|
||||
:in directory
|
||||
:all-files (and (not (null arg))
|
||||
(listp arg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/project-search-from-cwd (&optional arg initial-query)
|
||||
"Performs a project search recursively from the current directory.
|
||||
|
||||
If ARG (universal argument), include all files, even hidden or compressed ones."
|
||||
(interactive "P")
|
||||
(+helm-file-search
|
||||
:query initial-query
|
||||
:in default-directory
|
||||
:all-files (and (not (null arg))
|
||||
(listp arg))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +helm/jump-list ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(error "not implemented yet"))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user