During my undergraduate years, I remember having trouble setting up a postfix
server for a practical exercise in one of my classes. I had to edit the default
configuration file to harden the security of the system. My server was wrongly
configured, and I couldn’t figure out what part of my edits I had messed up. We
had to do all the changes to configuration file through a tty in an FreeBDS
virtual machine, so I had to use a terminal editor. nano
was my choice
since it was similar to the other GUI editors I had used at the time. After much
frustration, I asked a professor of mine to please help me out. He took over my
seat, closed my nano
session and reopened the configuration file with a different editor.
He was pressing all sorts of keys on the keyboard at an amazing speed, the
cursor was flying all over the screen. I was simply at awe.
The professor found what was wrong with my configuration and fixed it, telling me what
he had changed. When he left, I tried to look up what command he had run to edit
the file. This was my first experience with… vim
. I got introduced to emacs
some
months later, when I saw a friend of mine taking some notes with it.
In any case, this is the earliest point that I can remember thinking that the efficiency of text-editing, just like with any other task, is fully dependent on the tool used to achieve it. Considering that I was on a professional path to become a software engineer, a job that consists of reading documentation, source code and editing, I figured that it would pay off in the long run to take some time to explore what editors were out there and make an educated choice for a text editor, instead of just using whatever was popular.
Having taken that decision, I played around with vim
and later on with emacs
,
finally settling with the former to write the code for my thesis. I started out
with a vanilla configuration, a single .emacs
file where I would copy and paste
Emacs Lisp from other people’s configuration without much thinking. It was
ugly, messy and buggy, but It was also fun. I loved the idea of using code to
configure the tool that I would use to edit code, so I stuck with it.
My knowledge of Emacs would come from Google searches, blog posts and the online GNU Emacs documentation. I still hadn’t learned how to ask Emacs the things that I didn’t know (The invaluable C-h _). Embarrassingly, I didn’t learn that until late into my Emacs usage history. My editing needs were minimal so even if my configuration didn’t have all the bells and whistles of other editors, It was good enough for me. I also knew, at least on a theoretical level, that Emacs had almost no limits when it came to extensibility. Emacs can interact with the OS to do just about anything, which explains part of the popular saying that refers to Emacs as:
A great OS, if only missing a good text editor.
When I got frustrated with Emacs because I broke it somehow, or I didn’t know how to do something with it, the potential of Emacs as an almost infinitely extensible editor always kept me from switching editors and moving on. Even now, is this fact that keeps me going back to it.
Life changes and so do our needs. I got my first full-time job as Web Developer and Emacs had to level up to a robust daily driver. I had to use different technologies in a project with continuously changing requirements. My cute emacs experiment could not keep up. I had to use other IDEs to be able to push the work-out. However, I was always missing aspects of Emacs when using other editors, like the frame, buffer and window model, the automatic backup files or just the simple fact that I could change almost any aspect of my emacs if I so desired.
I would still open up Emacs for magit when interacting with git or for org-mode as my todo app. However, I wanted to use Emacs for more than that. Emacs shines when you know what you want from it. However, It can be daunting and take a lot of work to set up a configuration for general programming purposes. Which packages are good? Which are compatible with what you have? When two or more packages are similar, how do you decide which one to use? If Emacs was to be my daily driver, I would have to answer this questions often, which implies research, time and effort.
Why work on problems that others have already solved? With this in mind, I
decided to try out spacemacs
, a community developed Emacs distribution full of
pre-configured packages grouped by layers
. And for a while, It worked out great.
The defaults were good, it was functional enough, and could keep up with the
technologies I was using at work. What was not to love? Well, with continuous
use the wrinkles started to appear. For one, spacemacs
was slow. It would take a
long time to startup and commands were noticeably sluggish. It was also pretty
buggy. Sure, the layers for different languages worked reasonably well but from
time to time Emacs would behave in unexpected ways and I would have no idea why.
Looking into ways of making spacemacs
faster, I stumbled upon doom
and decided
to try it out. It is fast, at startup and during use. It feels quick and snappy.
Installing doom
modules was similar enough to configuring spacemacs
layers.
While doom
was not by any means buggy, I would get frustrated with its
opinionated defaults. doom
would have some keybinding I didn’t like, or some
package had some extra behavior different to what I expected, and I wouldn’t
know how to change it to what I wanted.
At this point it was clear to me what was the real problem. My ignorance of the
inner workings of Emacs was my only limiting factor. So I took an extreme
position, I went back to pure vanilla again, discarding all my previous
configurations and starting from scratch, while using other editors for my job.
My objective was to recreate the aspects that I liked from doom
from scratch in
my own configuration.
I took it slow, researching Emacs thoroughly and little by little building my configuration, but trying to understand everything that I was changing. I read Mickey Peterson’s Mastering Emacs, where I finally learned how to ask Emacs about the things I don’t understand. Xah’s ergoemacs blog was an awesome reference I keep consulting even now. It has all sorts of information on Emacs lisp syntax, Emacs concepts and configuration tips and tricks.
Other members of the awesome Emacs community I started follow where Protesilaos
Stavrou, a long term vim
user transformed into a hardcore Emacs user, whose
videos explaining his configuration where a great inspiration for me and taught
me to favor built-in packages over third party packages and most importantly,
how to build my own criteria for what packages I should use.
It took at least two months until I made a configuration that I could use at work again, but it felt great when I finally managed to have something that was reasonably fast, reproducible in any computer running Linux and was functional enough for my use cases that I had made, and I intimately understood! Although I am nowhere near an Emacs expert, if such a thing exists, and a lot of details still escape me. I learned a LOT about not only Emacs, but about lisp, functional programming and free (as in freedom) extensible software! Going back to basics paid of in spades.
I’ll ask again, Why work on problems that others have already solved?. Well, in
my case, It was to learn more about the problem-context. The thing is, Emacs is
truly immense, even if we don’t take into account all the third party packages
written for it. It has its own lisp dialect for extensibility, a mode
system for
defining unique behavior in each buffer, with major modes
(one per buffer) and
minor modes
(can be multiple or none in a buffer) that change the visual
elements, available commands and keybindings, and it has different systems to
detect when and which of these modes it should activate at any given time. It
also has different ways of running system commands synchronously or
asynchronously and processing their output, including a process manager for the
programs that are running under Emacs!
I’m not even being exhaustive. Add to that 40 years of packages and multiple
Emacs releases! This wouldn’t be so troublesome if it weren’t for the terrible
defaults with which Emacs installs. Ugly default theme and questionable default
bindings aside, it is terribly unoptimized for modern systems slowing down
performance. During my vanilla adventure, a lot of my time was just spent trying
to make Emacs feel more modern and fast, which is time-consuming. It’s surprising
that packages such as gchm-mode
and use-package don’t come
preinstalled with Emacs as they are incredible time savers, not just with
performance but also in configuration time…
At the beginning of re-configuring my vanilla Emacs, after addressing the
terrible defaults, the problems I was trying to solve were interesting, perhaps
because it was my first time trying to solve them. Things like: What’s the best
moment to lazy load this package?, How do I write Spanish accents in Emacs?,
How should I structure my *.el
files directory?. As the configuration grew,
more and more issues started appearing. Nothing major that broke my workflow
but annoyances nonetheless. I would write FIXME
comments in my .el
files to
keep track of these issues, so I could fix them later.
When I wanted to set up Emacs for a new language environments, I would spend a
lot of time checking out what community packages there were for that specific
environment, putting TODO
comments with the projects’ repository URL, so I could
try out and configure it out later on. Quickly It became the case that for every
FIXME
or TODO
comment I would solve, two or three more would appear.
The FIXME
were not such a big deal, I like hunting bugs and fixing them, since I
always feel like I at least learn something in the process. The big problem
were the TODO
, which were not remotely as interesting to solve. Looking for
packages is time-consuming and I would end up not using what I tried out. Other
times, the packages were so massive I never wanted to because I knew It would
take a long time to really configure it like I wanted to. Honorable mentions in
these categories are lsp
and treemacs
.
Unresolved issues in my vanilla configuration.
So, Why work on problems that others have already solved?. Not all problems are
equal, and some problems are just tedious to solve, this is the principal
reason why I choose to go back to doom
. Another reason is that I strongly agree
with the project guiding principles. doom
is not and IDE replacement or a
what-you-see-is-what-you-get type of editor. It’s fully expected of its users to
customize it and all its functionality is open to the user, so it can be
tinkered with. No magic, just well-thought-out Emacs lisp macros and hooks!
This is perhaps what I like the most about doom
, its true power resides in it’s
core
module, where all the macros, functions and hooks used to help the user
extend Emacs resides. The modules
in doom
just use those set of tools to offer
configuration options for specific use cases. This offers a mix of the best of
both the worlds of vanilla Emacs and spacemacs. With doom
I can try out a
module, see what I like, bring it over to my configuration, disable packages
that I don’t like and mix them with my own packages in a quick and reliable
manner, much more so that If I were back in vanilla Emacs.
- Git 2.23+
- Emacs 26.1+ (27.x is recommended)
- ripgrep 11.0+
- GNU Find
- (Optional) fd 7.3.0+ (known as
fd-find
on Debian, Ubuntu & derivatives) – improves performance for many file indexing commands
Additionally, the doom
executable (located at user-emacs-directory/bin/doom
)
can be called with the doctor
argument to obtain information of possible
missing dependencies used by the modules.
First, clone this repository in your DOOMDIR
. DOOMDIR
is an environment variable
that points to the location of your private configuration. If DOOMDIR
does not
exist, doom
will look for your configuration in doom.d
.
export DOOMDIR=/path/to/doom/dir
With the following command you can clone the repository in either case:
git clone https://github.com/danilevy1212/doom.git ${DOOMDIR:-~/.doom.d}
Then, just follow the instructions for installing doom emacs. Run doom env
, then doom tangle
and finally doom install
.
Blocks preceded with IE
are just examples that are not evaluated, the rest of
the blocks are put in the filename of the corresponding heading.
This file controls what Doom modules are enabled and what order they load
in. Remember to run doom sync
after modifying it!
Emacs lisp by default has dynamic-scope, which is fine if a little weird. However, dynamic scope comes with a performance penalty. Optional lexical scope has to be activated with a file parameter, as such:
;;; $DOOMDIR/init.el -*- lexical-binding: t; -*-
This option must be set in each individual file, so it’s hardly the last time we will use these block of code.
The doom!
macro controls which modules are loaded into Emacs. Modules are
package configurations made by the community. In the spirit of Emacs, all the
configuration that comes with a particular module can be extended or even
overwritten by your private configuration.
Modules are open for discovery. Press SPC h d h
(or C-h d h
for
non-vim users) to access Doom’s documentation. There you’ll find a Module Index
link where you’ll find a comprehensive list of Doom’s modules and what
flags they support.
Alternatively, press gd
(or C-c c d
) on a module to browse its directory
(for easy access to its source code).
The doom!
macro is capable of some conditional logic, thanks to the :if
and
:cond
keywords. Unfortunately, these keywords are not well documented beyond
and example in the docs. The rest of the keywords match with a directory location.
The symbols following a keyword are a module that reside in said directory.
A module is structurally similar to the $DOODIR
folder. Defines a packages.el
and
config.el
, plus some extra files that integrates with autoloads
or doctor
. Some
modules come with a README.org
for documentation purposes, others are not, so
it’s important to take a look at the source code, see what they define and
configure, before deciding to use a module.
Some modules can be wrapped in a list and given ‘flags’, that activate extra optional configuration. The list must have the module name as the head, the flags as the tail.
In the japanese
module only pangu spacing
seems like a package I could use, so I
rather install it standalone.
(doom! :input
;;bidi ; (tfel ot) thgir etirw uoy gnipleh
;;chinese
;;japanese
;;layout ; auie,ctsrnm is the superior home row
In my opinion, this package offers such a boost in productivity it’s almost essential. Sure, the overlay can be distracting for some, but it’s unobtrusive and optional while being a good tool for discoverability.
:completion
(company +childframe) ; the ultimate code completion backend
Doom offers a bunch of neat little extras. For starters, +childframe
flag
configures the company overlay to live in its own frame, which looks nicer
in the GUI.
By default, completion starts 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 |
In the spirit of Vim’s omni completion, the following insert mode key binds are available to evil users to access specific company backend:
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 |
Completion candidates are supplied by the functions defined in
company-backends
. Doom offers a helper macro, set-company-backend!
to change
the value of a company-backends
for a specific major/minor mode locally in the
buffer. Some examples of how to use it can be found in the
set-company-backend!
documentation.
This module is a combination of several modular packages that enhanced emacs
built-in completion capabilities. This approach is different to those of ivy
or helm
, which offer their own separate ecosystem.
- Vertico, which provides the vertical completion user interface
- Consult, which provides a suite of useful commands using
completing-read
- Embark, which provides a set of minibuffer actions
- Marginalia, which provides annotations to completion candidates
- Orderless, which provides better filtering methods
Some important keybindings.
When in an active Vertico completion session, the following doom added keybindings are available:
Keybind Description C-p
Go to previous candidate C-n
Go to next candidate C-k
(evil) Go to previous candidate C-j
(evil) Go to next candidate C-;
or<leader> a
Open an embark-act
menu to choose a useful actionC-c C-;
export the current candidate list to a buffer C-SPC
Preview the current candidate C-M-k
(evil) Go to previous candidate and preview. C-M-j
(evil) Go to next candidate and preview.
embark-act
will prompt you with awhich-key
menu with useful commands on the selected candidate or candidate list, depending on the completion category. Note that you can pressC-h
instead of choosing a command to filter through the options with a Vertico buffer, that also has slightly more detailed descriptions due to Marginalia annotations.
This module offers a lot unique search commands through the SPC s
and SPC f
prefixes. If the commands come prefixed with the universal command (SPC u
),
their result with include hidden files.
Marginalia toggle:
Keybind Description M-A
Cycle between annotation levels
If you want to further configure this module, here are some good places to start:
- Vertico provides several extentions that can be used to extend it’s interface
- You can add more Marginalia annotation levels and change the existing ones by editing
marginalia-annotator-registry
- You can change the available commands in Embark for category
$cat
by editingembark-$cat-map
, and even add new categories. Note that you add categories by defining them through marginalia, and embark picks up on them.
+icons
Adds icons tofile
andbuffer
category completion selections.
(vertico +icons) ; the search engine of the future
Most of what makes doom feel like doom is in the doom
, doom-dashboard
and doom-quit
.
: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
Not really necessary, but they are fun. Use the emojify-insert-emoji
function
(SPC i e
) to insert and emoji and emojify-apropos-emoji
to search for them.
(emoji +unicode) ; 🙂
hl-todo
not highlights `TODO` comments in buffers, but also comes some handy
keybindings:
keybind | description |
---|---|
]t | go to next TODO item |
[t | go to previous TODO item |
SPC p t | show all TODO items in a project |
SPC s p | search project for a string |
SPC s b | search buffer for string |
;;fill-column ; a `fill-column' indicator
hl-todo ; highlight TODO/FIXME/NOTE/DEPRECATED/HACK/REVIEW
The hydra
module activates a convenient hydras for window controls and text
zoom level.
hydra
Some visual help to quickly understand the indent levels in your code.
indent-guides ; highlighted indent columns
When using emacs-major-version >= 28
, enable ligatures, since they can be
composed by hardfuzz
.
(:if (>= emacs-major-version 28) ligatures) ; ligatures and symbols to make your code pretty again
Doom ain’t doom without its mode line.
;; minimap ; show a map of the code on the side
modeline ; snazzy, Atom-inspired modeline, plus API
To help with getting lost in big buffers, use the nav-flash
module:
nav-flash ; blink cursor line after big motions
This module give a visual hint when selecting or doing operations over text-objects.
;;neotree ; a project drawer, like NERDTree for vim
ophints ; highlight the region an operation acts on
Using display-buffer-alist
under the hood, doom
has an emergent window (or
pop-up) management system. It is well documented, and offers an API to
arbitrarily extend it.
(popup +defaults) ; tame sudden yet inevitable temporary windows
;;tabs ; a tab bar for Emacs
;; unicode ; extended unicode support for various languages
This module integrates with git to show hinges on the side of the buffer that indicate the difference between its contents and the version control version.
(vc-gutter +pretty) ; vcs diff in the fringe
Add a small ~
indicating an empty line, like vi.
vi-tilde-fringe ; fringe tildes to mark beyond EOB
Configuration for ace-window
and winum
. These packages associate windows in the
frame with number, offering a quick and convenient way of selecting a
specific window in the frame.
To use ace-window
use C-w C-w
. You can short-cut to the associated window number
using winum
, with SPC w {window number}
.
(window-select +numbers) ; visually switch windows
Workspaces is a wrapper over persp-mode
. It offers isolated buffers and windows
setups that can be saved into a file a loaded for persistent configurations.
Commands associated with workspaces are under the SPC TAB
prefix.
It also has a API for programmatic access.
workspaces ; tab emulation, persistence & separate workspaces
Using writeroom-mode
, changes the UI elements of a buffer so its contents
become the main focus. It can be toggled on and off with SPC t z
.
zen ; distraction-free coding or writing
I prefer vim’s keybindings to Emacs and thankfully, doom
offers first class
support for evil-mode
, a vim emulator inside Emacs, making it easy to get the
benefits of both Emacs and vim.
:editor
(evil +everywhere); come to the dark side, we have cookies
Evil is quite complex, and customizing it beyond the default settings can be tricky, as it’s finer details are not well documented. Luckily, the community has covered some of these points, which make the source code of evil much more bearable.
doom
comes with emulation for some popular vim plugins:
Vim Plugin | Emacs Plugin | Keybind(s) |
---|---|---|
vim-commentary | evil-nerd-commenter | omap gc |
vim-easymotion | evil-easymotion | omap gs |
vim-lion | evil-lion | omap gl / gL |
vim-seek or vim-sneak | evil-snipe | mmap s / S , omap z / Z & x / X |
vim-surround | evil-embrace and evil-surround | vmap S , omap ys |
Along with some extra text objects:
a
C-style function arguments (provided byevil-args
)B
any block delimited by braces, parentheses or brackets (provided byevil-textobj-anyblock
)c
Commentsf
For functions (but relies on the major mode to have sane definitions forbeginning-of-defun-function
andend-of-defun-function
)g
The entire bufferi j k
by indentation (k
includes one line above;j
includes one line above and below) (provided byevil-indent-plus
)q
For quotes (any kind)u
For URLsx
XML attributes (provided byexato
)
And custom Ex commands.
Ex Command | Description |
---|---|
:@ | Apply macro on selected lines |
:al[ign][!] REGEXP | Align text to the first match of REGEXP. If BANG, align all matches on each line |
:cp[!] NEWPATH | Copy the current file to NEWPATH |
:dash QUERY | Look up QUERY (or the symbol at point) in dash docsets |
:dehtml [INPUT] | HTML decode selected text / inserts result if INPUT is given |
:enhtml [INPUT] | HTML encode selected text / inserts result if INPUT is given |
:iedit REGEXP | Invoke iedit on all matches for REGEXP |
:k[ill]all[!] | Kill all buffers (if BANG, affect buffer across workspaces) |
:k[ill]b | Kill all buried buffers |
:k[ill]m[!] REGEXP | Kill buffers whose name matches REGEXP (if BANG, affect buffers across workspaces) |
:k[ill]o | Kill all other buffers besides the selected one |
:k[ill] | Kill the current buffer |
:lo[okup] QUERY | Look up QUERY on an online search engine |
:mc REGEXP | Invoke multiple cursors on all matches for REGEXP |
:mv[!] NEWPATH | Move the current file to NEWPATH |
:na[rrow] | Narrow the buffer to the selection |
:pad | Open a scratch pad for running code quickly |
:ral[ign][!] REGEXP | Right-Align text that matches REGEXP. If BANG, align all matches on each line |
:repl | Open a REPL and/or copy the current selection to it |
:retab | Convert indentation to the default within the selection |
:rev[erse] | Reverse the selected lines |
:rm[!] [PATH] | Delete the current buffer’s file and buffer |
:tcd[!] | Send cd X to tmux. X = the project root if BANG, X = default-directory otherwise |
Like yas-snippets
, but for empty files. Includes a mechanism to insert software
licenses as well, through M-x +file-templates/insert-license
. The module
documentation gives extra information on customization of the snippets.
file-templates ; auto-snippets for empty files
fold ; (nigh) universal code folding
This module marries hideshow, vimish-fold and outline-minor-mode to bring you marker, indent and syntax-based code folding for as many languages as possible.
Some keybindings include:
Keybind | Description |
---|---|
z f | Fold region |
z o | Unfold region |
z a | Toogle fold |
z d | Delete folded region |
z m | Refold all regions |
z r | Unfold all regions |
z E | Delete all folded regions |
z j | Jump to next fold |
z k | Jump to previous fold |
This module integrates code formatters into Emacs.
For setting my own formatter, there are two options:
- Use the
set-formatter!
macro. - Set the buffer-local variable
+format-with
to the name of the formatter to
use. e.g.
(setq-hook! 'python-mode-hook +format-with 'html-tidy)
;; Or set it to `:none' to disable formatting
(setq-hook! 'python-mode-hook +format-with :none)
Formatters are referred to by the name they were defined with. They can be looked up in the
format-all-mode-table
hash table or in format-all’s source code.
format ; automated prettiness
Lisp aware vim, brought to you by lispyville. It brings changes to evil’s
movement and text objects in lisps. Only bad thing is that evil-goggles
doesn’t
work with lispyville
’s commands. Bummer.
lispyville
is automatically activated for:
;;god ; run Emacs commands without modifier keys
lispy ; vim for lisp, for people who don't like vim
This module adds multiple cursors through two plugins, evil-mc
and evil-multiedit
.
multiple-cursors ; editing in many places at once
Keybindings:
Keybinding | command |
---|---|
M-d | evil-multiedit-match-symbol-and-next |
M-D | evil-multiedit-match-symbol-and-prev |
R | evil-multiedit-match-all (visual) |
C-M-d | evil-multiedit-restore |
Region active bidings:
Keybinding | Effect |
---|---|
D | Clear region |
M-D | Clear to end-of-region and go to insert mode |
A | Go into insert mode at end-of-region |
I | Go into insert mode at start-of-region |
V | Select the region |
P | Replace the iedit region with the contents of the clipboard |
$ | Go to end-of-region |
0 / ^ | Go to start-of-region |
gg / G | Go to the first/last region |
Keybindings
Keybinding | command |
---|---|
gzd | evil-mc-make-and-goto-next-match |
gzD | evil-mc-make-and-goto-prev-match |
gzj | evil-mc-make-cursor-move-next-line |
gzk | evil-mc-make-cursor-move-prev-line |
gzm | evil-mc-make-all-cursors |
gzn | evil-mc-make-and-goto-next-cursor |
gzN | evil-mc-make-and-goto-last-cursor |
gzp | evil-mc-make-and-goto-prev-cursor |
gzP | evil-mc-make-and-goto-first-cursor |
gzq | evil-mc-undo-all-cursors |
gzs | evil-mc-skip-and-goto-next-match |
gzS | evil-mc-skip-and-goto-prev-match |
gzc | evil-mc-skip-and-goto-next-cursor |
gzC | evil-mc-skip-and-goto-prev-cursor |
gzt | +multiple-cursors/evil-mc-toggle-cursors |
gzu | +multiple-cursors/evil-mc-undo-cursor |
gzz | +multiple-cursors/evil-mc-toggle-cursor-here |
gzI | evil-mc-make-cursor-in-visual-selection-beg (visual) |
gzA | evil-mc-make-cursor-in-visual-selection-end (visual) |
Parinfer is a proof-of-concept editor mode for Lisp programming languages. It will infer some changes to keep Parens and Indentation inline with one another.
Basically, it’s a another editing helper package for lisp, in particular:
- Emacs Lisp
- Clojure
- Scheme
- Lisp
- Racket
- Hy
;;objed ; text object editing for the innocent
(:if IS-LINUX parinfer) ; turn lisp into python, sort of
This module adds snippets to Emacs, powered by yasnippet.
;;rotate-text ; cycle region at point between text candidates
snippets ; my elves. They type so I don't have to
This module adds a minor-mode
+word-wrap-mode
, which intelligently wraps long lines in the buffer without modifying the buffer content.
word-wrap ; soft wrapping with language-aware indent
:emacs
(dired +icons +dirvish) ; making dired pretty [functional]
dired-mode
, as configured in the dired
module, has only a few extra bells and
whistles added. Apart from aesthetic stuff, there are some extra keybindings:
Keybind | Description |
---|---|
SPC f d | Find directory with dired |
q | Exit dired buffer |
C-c C-r | Run dired-rsync |
C-c C-e | Rename entries with wdired |
This complements the default keybindings. Additionally, the dirvish
package makes dired
feel more like ranger
by building on top of dired
and emacs
built-ins. It is also fully extensible.
Built-in automated indentation.
electric ; smarter, keyword-based electric-indent
Project-aware buffer management. Similar to dired
, but for buffers.
Toggled on by the SPC b i
keybinding.
(ibuffer +icons) ; interactive buffer management
;; undo ; persistent, smarter undo for your inevitable mistakes
This module augments Emacs’ built-in undo system to be more intuitive and to persist across Emacs sessions.
undo ; persistent, smarter undo for your inevitable mistakes
This module augments Emacs built-in version control support and provides better integration with git
It offers different modes for .git{ignore,info,attributes,config} files, a way
to easily visit the remote file of a repo, ~M-x browse-at-remote
, bind to SPC g o o
.
vc ; version-control and Emacs, sitting in a tree
An emacs
alternative to the traditional shell. From this shell, you have access to all of Emacs internal functions and variables. The features of the eshell
are too many to explain here.
:term
eshell ; the elisp shell that works everywhere
A traditional terminal emulator, powered by libvterm and Emacs c modules.
;;shell ; simple shell REPL for Emacs
;;term ; basic terminal emulator for Emacs
vterm ; the best terminal emulation in Emacs
Setup flycheck, the unofficial general programming language checker of Emacs.
:checkers
(syntax +childframe) ; tasing you for every semicolon you forget
Don’t misspell, ever again!
(spell +aspell +everywhere) ; tasing you for misspelling mispelling
This module adds grammar checking to Emacs to aid your writing by combining
lang-tool
andwritegood-mode
.
My english is not the best, neither is my spanish or my 日本語 for that matter. Maybe this module can help!
grammar ; tasing grammar mistake every you make
This module integrates direnv into Emacs.
:tools
;;ansible
;;biblio ; Writes a PhD for you (citation needed)
;;collab ; buffers with friends
;;(debugger +lsp) ; FIXME stepping through code, to help you add bugs
direnv
This module allows you to manipulate Docker images, containers & more from Emacs.
Provides a major
dockerfile-mode
to editDockerfiles
. Additional convenience functions allow images to be built easily.
docker-tramp.el
offers a TRAMP method for Docker containers.
(docker +lsp)
This module integrates EditorConfig into Emacs, allowing users to dictate code style on a per-project basis with an
.editorconfig
file (formal specification).
editorconfig ; let someone else argue about tabs vs spaces
;;ein ; tame Jupyter notebooks with emacs
This modules adds inline code evaluation support to Emacs and a universal interface for opening and interacting with REPLs.
Most important features.
- Inline code evaluation.
Quickrun can be invoked via:
M-x +eval/buffer
(orgR
, orM-r
)M-x +eval/region
M-x +eval/region-and-replace
- Evil users can use the
gr
operator to select and run a region.
Evaluation handlers can be set with
set-eval-handler!
function. - REPLs
Invoked via: + =SPC o r= or ~:repl~ will open a REPL in a popup window. =SPC o R= or ~:repl!~ will open a REPL in the current window. If a REPL is already open and a selection is active, it will be sent to the REPL. + ~M-x +eval/open-repl-other-window~ (=SPC o r=) + ~M-x +eval/open-repl-same-window~ (=SPC o R=) + ~M-x +eval/send-region-to-repl~ (=SPC c s=) while a selection (and REPL) is active
REPLs
can be registered withset-repl-handler!
function.
More about it’s features can be learned in here.
(eval +overlay) ; run code, run (also, repls)
;;gist ; interacting with github gists
This module adds code navigation and documentation lookup tools to help you quickly look up definitions, references, documentation, dictionary definitions or synonyms.
- Jump-to-definition and find-references implementations that just work.
- Powerful xref integration for languages that support it.
- Search online providers like devdocs.io, stackoverflow, google, duckduckgo or youtube (duckduckgo and google have live suggestions).
- Integration with Dash.app docsets.
- Support for online (and offline) dictionaries and thesauruses.
(lookup +docsets +dictionary +offline) ; navigate your code and its documentation
The Language Server protocol is used between a tool (the client) and a language smartness provider (the server) to integrate features like auto complete, go to definition, find all references and alike into the tool.
(lsp +peek)
The best porcelain for git
, in emacs
!
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
;;taskrunner ; taskrunner for all your projects
;;terraform ; infrastructure as code
;;tmux ; an API for interacting with tmux
Tree-sitter is a general programming language parser that efficiently builds and updates Abstract Syntax Trees (AST) for your code. Basically, it can read programming languages and understand the structure and meaning of code without having to execute it. Among many amazing things, one of its best and simplest features to take advantage of is richer syntax highlighting.
— https://hungyi.net/posts/use-emacs-tree-sitter-doom-emacs/
Additionally, this module adds new text objects for evil-mode
:
key | text object |
---|---|
A | parameter list |
f | function definition |
F | function call |
C | class |
c | comment |
v | conditional |
l | loop |
You can jump directly to any of this nodes with the [g
and ]g
motion commands.
tree-sitter ; syntax and parsing, sitting in a tree.. .
;;upload ; map local to remote projects via ssh/ftp
I use a macbook for work, so this module adds some niceties.
:os
(:if IS-MAC macos) ; improve compatibility with macOS
Better integration of Emacs with the terminal emulator, particularly:
- System clipboard integration (through an external clipboard program or OSC-52 escape codes in supported terminals).
- Fixes cursor-shape changing across evil states in terminal that support it.
- Mouse support in the terminal.
tty ; improve the terminal Emacs experience
Occasionally I will have to edit .csv
files manually. The data
module comes in handy for this task.
:lang
;;agda ; types of types of types of types...
;;beancount
;;cc ; C/C++/Obj-C madness
;;(clojure +lsp) ; 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
This module extends support for Emacs Lisp in Doom Emacs.
- Macro expansion
- Go-to-definitions or references functionality
;;(dart +flutter) ; paint ui and not much else
;;dhall
;;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
;;fortran ; in FORTRAN, GOD is REAL (unless declared INTEGER)
;;fsharp ; ML stands for Microsoft's Language
;;fstar ; (dependent) types and (monadic) effects and Z3
;;gdscript ; the language you waited for
;;(go +lsp +tree-sitter) ; the hipster dialect
;;(graphql +lsp) ; Give queries a REST
;;(haskell +lsp) ; a language that's lazier than I am
;;hy ; readability of scheme w/ speed of python
;;idris ;
(json +lsp +tree-sitter) ; At least it ain't XML
;;(java +lsp) ; the poster child for carpal tunnel syndrome
(javascript +lsp +tree-sitter) ; all(hope(abandon(ye(who(enter(here)))))
;;julia ; a better, faster MATLAB
;;kotlin ; a better, slicker Java(Script)
(latex +lsp) ; writing papers in Emacs has never been so fun
;;lean
;;factor
;;ledger ; an accounting system in Emacs
;;(lua +lsp +fennel) ; one-based indices? one-based indices
This module provides Markdown support for Emacs. The +grip
flag enables grip support (on <localleader> p), to provide live github-style previews of your markdown (or org) files.
(markdown +grip) ; writing docs for people to ignore
This module adds support for the Nix language to Doom Emacs, along with tools for managing Nix(OS).
Includes:
- Syntax highlighting
- Completion through company and/or helm
- Nix option lookup
- Formatting (
nixfmt
)
;;nim ; python + lisp at the speed of c
(nix +tree-sitter +lsp) ; I hereby declare "nix geht mehr!"
;;ocaml ; an objective camel
(org +dragndrop +pretty) ; organize your plain life in plain text
;;php ; perl's insecure younger brother
;;plantuml ; diagrams for confusing people more
;;purescript ; javascript, but functional
;;(python +lsp +pyright +poetry +tree-sitter) ; beautiful is better than ugly
;;qt ; the 'cutest' gui framework ever
;;racket ; a DSL for DSLs
;;raku ; the artist formerly known as perl6
💡
restclient-mode
is tremendously useful for automated or quick testing REST APIs. My workflow is to open anorg-mode
buffer, create a restclient source block and hack away.restclient-mode
andcompany-restclient
power this arcane wizardry.
(rest +jq) ; Emacs as a REST client
;;rst ; ReST in peace
;;(ruby +rails) ; 1.step {|i| p "Ruby is #{i.even? ? 'love' : 'life'}"}
Rustic mode is great and the integrates really well with cargo. The defaults are
reasonable and with the +lsp
it integrates nicely with lsp-mode
, what’s not to
love?
(rust +lsp +tree-sitter) ; Fe2O3.unwrap().unwrap().unwrap().unwrap()
This module adds support for shell scripting languages.
;;scala ; java, but good
;;scheme ; a fully conniving family of lisps
(sh +tree-sitter +lsp) ; she sells {ba,z,fi}sh shells on the C xor
;;sml
;;solidity ; do you need a blockchain? No.
;;swift ; who asked for emoji variables?
;;terra ; Earth and Moon in alignment for performance.
;;(web +lsp) ; the tubes
(yaml +lsp) ; JSON, but readable
:email
;;(mu4e +org +gmail)
;;notmuch
;;(wanderlust +gmail)
This module adds a calendar view for Emacs, with org and google calendar sync support.
:app
calendar
;;emms
everywhere ; *leave* Emacs!? You must be joking
;;irc ; how neckbeards socialize
;;(rss +org) ; Emacs as an RSS reader
;;twitter ; twitter client https://twitter.com/vnought
The meat and potatoes of this configuration. This module tangles the source code blocks in $DOOMDIR/config.org
.
:config
literate
This module provides a set of reasonable defaults, including:
- A Spacemacs-esque keybinding scheme
- Extra Ex commands for evil-mode users
- A yasnippet snippets library tailored to Doom emacs
- A configuration for (almost) universally repeating searches with
;
and,
Alongside the reasonable defaults, this module offers tons of convenience commands, under the +default/
prefix.
(default +bindings +smartparens))
To install a package with Doom you must declare them here and run doom sync
on the command line, then restart Emacs for the changes to take effect – or
use M-x doom/reload
.
To install SOME-PACKAGE from MELPA, ELPA or emacsmirror:
ie:
(package! some-package)
To install a package directly from a remote git repo, you must specify a
:recipe
. You’ll find documentation on what :recipe
accepts here:
https://github.com/raxod502/straight.el#the-recipe-format
ie:
(package! another-package
:recipe (:host github :repo "username/repo"))
If the package you are trying to install does not contain a PACKAGENAME.el
file, or is located in a sub-directory of the repository, you’ll need to specify
:files
in the :recipe
:
ie:
(package! this-package
:recipe (:host github :repo "username/repo"
:files ("some-file.el" "src/lisp/*.el")))
If you’d like to disable a package included with Doom, you can do so here
with the :disable
property:
ie:
(package! builtin-package :disable t)
You can override the recipe of a built-in package without having to specify
all the properties for :recipe
. These will inherit the rest of its recipe
from Doom or MELPA/ELPA/Emacsmirror:
ie:
(package! builtin-package :disable t)
You can override the recipe of a built in package without having to specify
all the properties for :recipe
. These will inherit the rest of its recipe
from Doom or MELPA/ELPA/Emacsmirror:
ie:
(package! builtin-package :recipe (:nonrecursive t))
(package! builtin-package-2 :recipe (:repo "myfork/package"))
Specify a :branch
to install a package from a particular branch or tag.
This is required for some packages whose default branch isn’t master
(which
our package manager can’t deal with; see radian-software/straight.el#279)
ie:
(package! builtin-package :recipe (:branch "develop"))
Use :pin
to specify a particular commit to install.
ie:
(package! builtin-package :pin "1a2b3c4d5e")
Doom’s packages are pinned to a specific commit and updated from release to
release. The unpin!
macro allows you to unpin single packages…
ie:
(unpin! pinned-package)
; ...or multiple packages
(unpin! pinned-package another-pinned-package)
; ...Or *all* packages (NOT RECOMMENDED; will likely break things)
(unpin! t)
For convenience, packages are declared in code blocks close to their
configuration code blocks. Package declaration blocks actually go to into
packages.el
. Package declarations blocks can be distinguished for only
containing the package!
macro.
We don’t permit the package.el
file to be byte compiled and declare its
lexical binding.
;; -*- no-byte-compile: t; lexical-binding:t; -*-
;;; $DOOMDIR/packages.el
Auto-loads blocks go into different files in the autoload
folder. In this folder
there are files which define functions that and values that whose evaluation is
the entry point into loading other packages. This permits packages to be loaded
exactly when they are needed.
This is all made possible thanks to the auto-load cookie: ;;;###autoload
.
Placing this on top of a lisp form will do one of two things:
- Add a
autoload
call to Doom’s auto-load file (found in~/.emacs.d/.local/autoloads.el
, which is read very early in the startup process). - Or copy that lisp form to Doom’s auto-load file verbatim (usually the case for
anything other than
def*
forms, likedefun
ordefmacro
).
Doom’s auto-load file is generated by scanning these files when you execute doom
sync
.
As with package declarations blocks, auto-load code blocks will be placed close to their related configuration blocks. These will be placed in an auto-load subheading within the corresponding package heading.
Most of the configuration actually takes place here. In config.el
we further
customize the packages from the different modules and in packages.el
. In other
words, the real fun starts here. As always, we start by declaring the lexical
binding:
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
Here’s a simple trick to make she-banged
scripts executable auto-magically by default.
(add-hook! 'after-save-hook
#'executable-make-buffer-file-executable-if-script-p)
Alert is a Growl-workalike for Emacs which uses a common notification interface and multiple, selectable “styles”, whose use is fully customizable by the user.
(package! alert)
Depending if I am on linux
or macos
, choose the notification style.
(use-package! alert
:defer t
:custom
(alert-default-style (if IS-LINUX 'libnotify 'osx-notifier)))
This package provides a minor mode annotate-mode, which can add annotations to arbitrary files without changing the files themselves. This is very useful for code reviews.
(package! annotate)
Activate annotate-mode
in file buffers that have annotations.
(use-package! annotate
:commands (annotate-load-annotation-data))
(add-hook! find-file
(let ((file-name (buffer-file-name))
(annotation-files (mapcar #'car (annotate-load-annotation-data t))))
(when (and file-name
(member file-name annotation-files))
(annotate-mode +1))))
The current database for annotations is contained in the file indicated by the variable annotate-file.
(after! annotate
(setq annotate-file (expand-file-name "annotate" doom-cache-dir)))
Blacklist org-mode
(setq annotate-blacklist-major-mode '(org-mode))
Add keybindings.
(after! annotate
(setq annotate-mode-map (make-sparse-keymap))
(map! :map annotate-mode-map
:leader
:prefix ("b a" . "annotate")
"a" #'annotate-annotate
"d" #'annotate-delete-annotation
"s" #'annotate-show-annotation-summary
"]" #'annotate-goto-next-annotation
"[" #'annotate-goto-previous-annotation))
A hidden feature of the eval
module is that any function whose name matches
with the regex ^\\(?:\\+\\)?\\([^/]+\\)/open-\\(?:\\(.+\\)-\\)?repl$
, will appear as an option in the +eval-open-repl
.
With this, we can create a repl
for the awesome-client
.
;;;###autoload
(defun +lua/open-awesome-client-repl ()
(interactive)
(pop-to-buffer (make-comint "repl:awesome-client" "awesome-client" nil)))
Share bookmarks between hosts.
(after! bookmark
(setq bookmark-default-file (expand-file-name "bookmark" "~/Cloud")))
Save bookmarks to a file as soon as they are modified.
(after! bookmark
(setq bookmark-save-flag 1))
Some of my RSS feeds offer links to lbry
, which cannot be open directly with a
browser. The following advice takes care of this edge case.
(after! browse-url
(defadvice! dan/browse-url-encode-url--parse-lbry-url (args)
"Process a `lbry://' link so it can be opened with `browse-url'."
:filter-args #'browse-url-xdg-open
:filter-args #'browse-url-default-macosx-browser
(list
(replace-regexp-in-string "^lbry:\/\/" "https://odysee.com/" (car args)))))
Until 134 gets merged, this fixes repeating entries in range periods.
(after! calfw
(defadvice! dan/cfw:org-get-timerange (text)
"Return a range object (begin end text).
If TEXT does not have a range, return nil."
:override #'cfw:org-get-timerange
(let* ((dotime (cfw:org-tp text 'dotime)))
(and (stringp dotime) (string-match org-ts-regexp dotime)
(let ((date-string (match-string 1 dotime))
(extra (cfw:org-tp text 'extra)))
(if (string-match "(\\([0-9]+\\)/\\([0-9]+\\)): " extra)
(let* ((cur-day (string-to-number
(match-string 1 extra)))
(total-days (string-to-number
(match-string 2 extra)))
(start-date (org-read-date nil t date-string))
(end-date (time-add
start-date
(seconds-to-time (* 3600 24 (- total-days 1))))))
(unless (= cur-day total-days)
(list (calendar-gregorian-from-absolute (time-to-days start-date))
(calendar-gregorian-from-absolute (time-to-days end-date)) text)))))))))
Finally, we create an entrance point to the calendar view.
(map! :leader
:desc "Calfw" "o c" #'=calendar)
Disable evil-snipe
in this mode, since It overrides some of the keybindings.
(after! evil-snipe
(add-to-list 'evil-snipe-disabled-modes 'cfw:calendar-mode))
Cape provides Completion At Point Extensions which can be used in combination with the Corfu completion UI or the default completion UI
(package! cape)
Until completion stops working in eshell when typing quote characters is fixed, we apply the fix ourselves. See here.
(use-package! cape
:init
(after! eshell
(advice-add 'pcomplete-completions-at-point :around #'cape-wrap-silent)
(advice-add 'pcomplete-completions-at-point :around #'cape-wrap-purify))
:defer t)
Begin giving candidates as soon as something is typed. This can be slow sometimes, so it might be a good idea to change it back to it’s default value of 3
. Also reduce the idle delay so it feels more responsive.
(after! company
(setq company-minimum-prefix-length 2
company-idle-delay 0.05))
Compilation URLs should be followable.
(add-hook! compilation-mode #'goto-address-mode)
Enable true color support in compilation buffers.
(after! compile
(setq compilation-environment '("TERM=xterm-256color"))
(defadvice! dan/compilation-filter--xterm-color (f proc string)
:around #'compilation-filter
(funcall f proc (xterm-color-filter string))))
A _consult_ing-read interface for company-mode
.
(package! consult-company)
First, let’s ensure the package is only loaded on demand, by creating a consult-company
auto-command.
(use-package! consult-company
:commands consult-company)
This package remaps completion-at-point
to use consult
’s interface. Keybind to C-S-s
.
(after! (company consult)
(map! :map company-active-map
"C-S-s" #'consult-company))
Here is a source that adds directory paths provided by the shell tool Fasd:
(after! consult-dir
(defun consult-dir--fasd-dirs ()
"Return list of fasd dirs."
(split-string (shell-command-to-string "fasd -ld") "\n" t))
(defvar consult-dir--source-fasd
`(:name "Fasd dirs"
:narrow ?f
:category file
:face consult-file
:history file-name-history
:enabled ,(lambda () (executable-find "fasd"))
:items ,#'consult-dir--fasd-dirs)
"Fasd directory source for `consult-dir'.")
(add-to-list 'consult-dir-sources 'consult-dir--source-fasd t))
A package to incorporate projectile into consult. This allows to choose a project, when none is selected or choose a project buffer/file.
(package! consult-projectile)
consult-projectile
also allows for narrowing in emacs >= 28
, making it more useful than the default projectile command in doom
. Narrow selection with B for buffer, F for file or P for project.
(use-package! consult-projectile
:defer t
:init
(map! :leader
:desc "Project find" "SPC" #'consult-projectile))
Incorporate the same behavior as projectile-switch-project
when changing project:
(after! consult-projectile
(setq consult-projectile-use-projectile-switch-project t))
An essential interface to know what to customize!
(use-package! cus-edit
:defer t
I use it to know the customizable options in a package, changing the values within this configuration. So, let’s make it show the actual real values.
:custom
(custom-unlispify-menu-entries nil)
(custom-unlispify-tag-names nil)
(custom-unlispify-remove-prefixes nil))
Doom exposes five (optional) variables for controlling fonts in Doom. Here are the three important ones:
doom-font
doom-variable-pitch-font
doom-big-font
– used fordoom-big-font-mode
; use this for presentations or streaming.
They all accept either a font-spec, font string (“Input Mono-12”), or xlfd font string. You generally only need these two:
ie:
(setq doom-font (font-spec :family "monospace" :size 12 :weight 'semi-light)
doom-variable-pitch-font (font-spec :family "sans" :size 13))
Let’s choose our monospaced font, Sarasa Mono J goodness:
(setq doom-font (font-spec :family "Sarasa Mono J" :size 18 :weight 'semi-light))
And our variable pitch
font, Sarasa UI J
:
(setq doom-variable-pitch-font (font-spec :family "Sarasa UI J" :size 18 :weight 'extra-light))
Comments and keywords should pop more…
(custom-set-faces!
'(font-lock-comment-face :slant italic)
'(font-lock-keyword-face :slant italic))
When in zen mode, scale text just a bit.
(after! writeroom-mode
(setq +zen-text-scale 1.25))
Not everything fits in the mode-line
, so let’s make fonts and icons smaller:
(custom-set-faces!
'(mode-line :height 90 :inherit 'variable-pitch)
'(mode-line-inactive :height 80 :inherit 'variable-pitch))
(after! all-the-icons
(setq all-the-icons-scale-factor 1.1))
The filename in the mode line
occupies way too much space.
(after! doom-modeline
(setq doom-modeline-buffer-file-name-style 'truncate-with-project))
There are two ways to load a theme. Both assume the theme is installed and
available. You can either set doom-theme
or manually load a theme with the
load-theme
function. This is the default:
(setq doom-theme 'doom-nord)
Let’s add some small customization to make everything a bit brighter and bigger:
(use-package! doom-nord-theme
:defer t
:custom
(doom-nord-brighter-modeline t)
(doom-nord-padded-modeline t)
(doom-nord-region-highlight 'frost))
Detached, or Detach Emacs, is a package to run shell commands completely detached from Emacs.
(package! detached)
The detachable nature of the package means that commands started with it can outlive Emacs, which also works on remote hosts, essentially offering a lightweight alternative to Tmux or GNU Screen.
(use-package! detached
:after-call (compile dired dired-rsync embark-act eshell org-mode projectile-mode shell vterm)
:config
(setq detached-notification-function (if IS-LINUX
#'detached-state-transition-notifications-message
#'detached-extra-alert-notification)
detached-db-directory doom-cache-dir
detached-init-block-list '(dired-rsync dired)
detached-session-directory (temporary-file-directory)))
detached-shell-command
acts as a replacement to async-shell-command
.
(map! :g "M-&" #'detached-shell-command)
detached-consult
allows us to manage the active sessions behind detached
.
(use-package! detached-consult
:defer t
:init
(map! :leader
:desc "Detached Sessions" :g "o s" #'detached-consult-session))
The commands detached-compile
and detached-compile-recompile
can act as drop in replacements for compile
and compile-recompile
, respectively.
(use-package! detached-compile
:defer t
:init
(map! :leader
:desc "Compile" :g "c c" #'detached-compile
:desc "Recompile" :g "c C" #'detached-compile-recompile))
Use detached-list-sessions
to manage multiple sessions.
(use-package! detached-list
:defer t
:init
(map! :leader
:desc "Detached Manage Sessions" :g "o S" #'detached-list-sessions)
:config
(evil-set-initial-state 'detached-list-mode 'emacs))
We start detached
integration.
(after! detached
(detached-init))
Most of the information dired
throws at you is not really necessary, so let’s
hide it by default. One can toggle this information on/off with (
keybinding.
(add-hook! dired-mode #'dired-hide-details-mode)
Opening files from dired
with an external program is a bit of drag by default, so
we add the dired-open
package to take care of that.
(package! dired-open)
The variable dired-open-guess-shell-alist
determines if the file is opened with
an external program.
(use-package! dired-open
:after dired
:custom
(dired-open-functions (list #'dired-open-guess-shell-alist
#'dired-open-by-extension
#'dired-open-subdir))
(dired-guess-shell-alist-user '(("\\.\\(?:docx\\|djvu\\|eps\\)\\'" "xdg-open")
("\\.\\(?:\\|gif\\|xpm\\)\\'" "xdg-open")
("\\.\\(?:xcf\\)\\'" "xdg-open")
("\\.csv\\'" "xdg-open")
("\\.tex\\'" "xdg-open")
("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\|mov\\)\\(?:\\.part\\)?\\'" "xdg-open")
("\\.\\(?:mp3\\|flac\\)\\'" "xdg-open")
("\\.html?\\'" "xdg-open")
("\\.md\\'" "xdg-open"))))
The following packages reduce the dired buffer
clutter by condensing the information into a single buffer.
(package! dired-subtree)
dired-subtree
enables a tree view of te directory, which puts the information of sub-directories contents in the same buffer.
(use-package! dired-subtree
:after dired)
dired
can get hung up in some operations. Luckily, dired-async
can do the same procedures without blocking.
(add-hook! dired-mode #'dired-async-mode)
Add keybindings overriding blocking operations.
(after! dired
(map! :map dired-mode-map
:n "R" #'dired-async-do-rename
:n "C" #'dired-async-do-copy
:n "S" #'dired-async-do-symlink
:n "H" #'dired-async-do-hardlink))
Use most recently used dired
window as default directory for file operations.
(after! dired
(setq dired-dwim-target #'dired-dwim-target-recent))
Dirvish is an improved version of the Emacs inbuilt package Dired. It not only gives Dired an appealing and highly customizable user interface, but also comes together with almost all possible parts required for full usability as a modern file manager.
Override default layouts.
(after! dirvish
(setq dirvish-default-layout '(0 0 0.4)
dirvish-layout-recipes '((1 0.11 0.55)
(0 0 0.40))))
Add file size to display.
(after! dirvish
(pushnew! dirvish-attributes 'file-size))
Override dired-jump
with dirvish
!
(map! :leader
:desc "Dired" "o -" #'dired-jump)
Emacs is missing a mode to edit .env
files. So let’s add it one:
(package! dotenv-mode)
Now let’s activate it when opening a .env
:
(use-package! dotenv-mode
:mode ("\\.env\\.?.*\\'" . dotenv-mode))
(after! dumb-jump
(setq dumb-jump-selector 'completing-read))
home-manager
overwrites the PATH
variable in zshrc
, so we cannot use zsh
as our default shell for emacs
processes if we want conserve the PATH
set in each project.
(setq-default shell-file-name "/bin/sh")
The keybinding M-SPC m b
inserts the name of a buffer in the eshell
syntax.
However, sometimes it’s useful to refer to the buffer by its string name
representation. So let’s make an extra keybinding for said case:
(after! eshell
(map! :map eshell-mode-map
(:localleader
:desc "Insert Symbolic Buffer Name" "B" #'eshell-insert-buffer-name
:desc "Insert String Buffer Name" "b" #'dan/eshell-insert-buffer-name))
Do not use bash for auto-completion backend.
(setq fish-completion-fallback-on-bash-p nil))
Hack into eshell to get proper 256 color support. Make commands run through eshell
show color codes that emacs
can understand but only in eshell
buffers, so other shell-facing commands have the default TERM
value.
(after! eshell
(add-hook! 'eshell-banner-load-hook
(setq-local xterm-color-preserve-properties t))
(add-hook! 'eshell-before-prompt-hook
(setq-local process-environment `(,@(seq-remove (lambda (e)
(string-match-p "\\`TERM=" e))
process-environment)
"TERM=xterm-256color")))
(add-to-list 'eshell-preoutput-filter-functions #'xterm-color-filter)
(delq! #'eshell-handle-ansi-color eshell-output-filter-functions))
Remote servers are a bit slow for auto-completion.
(after! eshell
(add-hook! 'eshell-before-prompt-hook
(company-mode (if (file-remote-p default-directory) -1 +1))))
Here I define the slightly modified version of eshell-insert-buffer-name
.
;;; $DOOMDIR/autoload/eshell.el -*- lexical-binding: t; -*-
;;;###autoload
(defun dan/eshell-insert-buffer-name (buffer-name)
"Insert BUFFER-NAME into the current buffer at point.
The BUFFER-NAME is given as string surrounded by \"\"."
(interactive "BName of buffer: ")
(insert-and-inherit "\"" buffer-name "\""))
An emacs
global minor mode allowing eshell
to use vterm
for visual commands.
(package! eshell-vterm)
Add an alias to eshell
to be able to run any command in visual mode, vt
.
(use-package! eshell-vterm
:after eshell
:config
(eshell-vterm-mode)
(defalias 'eshell/vt 'eshell-exec-visual))
Hack while visual commands not inheriting environment bug gets resolved.
(after! eshell-vterm
(inheritenv-add-advice #'eshell-vterm-exec-visual))
Vim keybindings are hard to let go once you are used to them. Luckily, doom comes with much of the heavy lifting already done when it comes to evil mode. We just gotta customize some minor details.
(use-package! evil
:defer t
:custom
Make horizontal motions move to other lines.
(evil-cross-lines t)
Remove highlighted items after search is finished.
(evil-ex-search-persistent-highlight nil)
Don’t continue commented lines with o/O.
(+evil-want-o/O-to-continue-comments nil)
Don’t override the contents of the ” register after pasting on a block.
(evil-kill-on-visual-paste nil)
Implicit /g flag on evil ex substitution. Put g
flag to reverse it.
(evil-ex-substitute-global t)
Yank by actual lines of text and not by screen lines, less confusing.
:init
(setq evil-respect-visual-line-mode nil)
Universal argument mapped to M-u instead.
:config
(map! :g "M-u" #'universal-argument
Remove highlighted items after a search.
:m "C-l" #'evil-ex-nohighlight))
Center the cursor after jumping to a new search entry.
(after! evil
(defun dan/center-after-move (&rest _)
"Center screen after search."
(evil-scroll-line-to-center nil))
(dolist (command '(evil-ex-search-next
evil-ex-search-previous))
(advice-add command :after #'dan/center-after-move))
(after! evil-snipe
(dolist (command '(evil-snipe-repeat
evil-snipe-seek))
(advice-add command :after #'dan/center-after-move))))
Create undo
boundaries after certain key presses in insert-mode
.
(after! evil
(add-hook! 'post-self-insert-hook
(when (and (evil-insert-state-p)
(memq (char-before)
(seq-concatenate 'list
;; English and Spanish
'(?. ?, ?! ?\( ?\{ ?\[ ??)
;; 日本語
'(?。 ?、 ?\「 ?\( ?\{ ?・))))
(evil-end-undo-step)
(evil-start-undo-step))))
Quickly diff to regions of text.
(map! :leader
:prefix "b"
"q" #'evil-quick-diff
"Q" #'evil-quick-diff-cancel)
evil-snipe
is a simple but powerful plugin, that adds a snipe
two character motion, plus the possibility of make builtin motions work further than a single line.
(after! evil-snipe
(setq evil-snipe-scope 'whole-buffer))
Hack to add buffer local flycheck
checkers after lsp
for different major modes, taken from this issue.
(defvar-local dan/flycheck-local-cache nil)
(after! flycheck
(defadvice! dan/flycheck-local-checker-get (fn checker property)
"Check the buffer local cache for the LSP checker."
:around #'flycheck-checker-get
(if (eq checker 'lsp)
(or (alist-get property dan/flycheck-local-cache)
(funcall fn checker property))
(funcall fn checker property))))
Add the ability to use a checker in a per-directory basis, thanks to .dir-locals.el
(put 'flycheck-checker 'safe-local-variable #'symbolp)
Harpoon plugin for emacs, based on the plugin from ThePrimeagen.
This plugin offers quick bookmarks separated by project and branch. You can quick navigate between your working files and forget about that files opened that you will not use anymore.
Harpoon persists between emacs sessions.
(package! harpoon
:recipe (:host github :repo "otavioschwanck/harpoon.el"))
Define quick access keybindings.
(use-package harpoon
:defer t
:custom
(harpoon-separate-by-branch t)
:init
(map! :leader
:prefix ("j" . "harpoon")
"c" 'harpoon-clear
"f" 'harpoon-toggle-file
"a" 'harpoon-add-file
"m" 'harpoon-toggle-quick-menu
"1" 'harpoon-go-to-1
"2" 'harpoon-go-to-2
"3" 'harpoon-go-to-3
"4" 'harpoon-go-to-4
"5" 'harpoon-go-to-5
"6" 'harpoon-go-to-6
"7" 'harpoon-go-to-7
"8" 'harpoon-go-to-8
"9" 'harpoon-go-to-9))
An manual completion system, bind to M-/
. Useful for expanding in modes that don’t have full context, like a fundamental-mode
buffer.
(map! :g [remap dabbrev-expand] #'hippie-expand)
Let’s make the hydra
module’s functions easily accessible:
(map! :leader
:desc "Navigate/Hydra" :m "w N" #'+hydra/window-nav/body
:desc "Text-Zoom/Hydra" :m "w f" #'+hydra/text-zoom/body)
Help emacs
interpret exotic image formats using external programs, like ffmpeg
.
(setq image-use-external-converter t)
This Emacs minor-mode creates an automatically updated buffer called Ilist that is populated with the current buffer’s imenu entries. The Ilist buffer is typically shown as a sidebar (Emacs vertically splits the window).
(package! imenu-list)
Keybindings:
Command | Keybinding | Description |
---|---|---|
imenu-list | SPC o i | Open/Toggle Ilist buffer. |
imenu-list-go-entry | RET | Go to imenu entry. |
imenu-list-display-entry | d | Display the imenu entry in other buffer. |
imenu-list-quit-window | q | Quit Ilist buffer. |
hs-toggle-hiding | TAB | Toggle fold/unfold imenu entry. |
(use-package! imenu-list
:defer t
:init
(map! :leader
:desc "Index" :g "o i" #'imenu-list-smart-toggle))
Reduce the delay to update the buffer when idle.
(setq imenu-list-idle-update-delay 0.2)
Take less screen real-state.
(setq imenu-list-size 0.2)
Force size we just defined.
(setq imenu-list-auto-resize t)
Recenter window after jumping
to an entry.
(after! imenu-list
(setq imenu-list-after-jump-hook '(recenter-top-bottom)))
Use the bitmap to display the indent level if we are in a graphic interface.
(after! highlight-indent-guides
(setq highlight-indent-guides-method (if (display-graphic-p)
'bitmap
'character)
highlight-indent-guides-responsive 'top))
inheritenv provides a couple of tools for dealing with this issue:
- Library authors can wrap code that plans to execute processes in temporary buffers with the inheritenv macro.
- Users can modify commands like shell-command-to-string using the inheritenv-add-advice macro.
(package! inheritenv)
Make an autoload
out of inheritenv-add-advice
.
(use-package! inheritenv
:commands (inheritenv-add-advice))
I have to use Intellij Idea
for some tasks work tasks. This function makes it possible to open the current file in Intellij Idea
, provided the scripts from Jetbrains Toolbox are installed and in path.
;;; $DOOMDIR/autoload/idea.el -*- lexical-binding: t; -*-
;;;###autoload
(defun dan/open-in-intellij-idea (file line column)
(interactive (list
(buffer-file-name)
(format "%d" (line-number-at-pos))
(format "%d" (current-column))))
(unless (and file line column)
(throw 'user-error "Needs to be called on a real file"))
(start-process "idea" nil "idea" "--line" line "--column" column file))
Bind it to SPC o j
.
(when IS-MAC
(map! :leader
:desc "Open in Intellij" :g "o j" #'dan/open-in-intellij-idea))
Do not run the jar file! Run the binary.
(setq langtool-bin "languagetool-commandline")
Most of the time, assume I speak English.
(setq langtool-mother-tongue "en")
This determines the style of line numbers in effect. If set to nil
, line
numbers are disabled. For relative line numbers, set this to relative
.
(setq display-line-numbers-type 'relative)
Allow per-directory disabling of certain lsp-clients
.
(put 'lsp-disabled-clients 'safe-local-variable #'sequencep)
Search for the lsp-server
in path:
(after! lsp-lua
(setq lsp-clients-lua-language-server-command "lua-language-server"))
Add lsp-lua-diagnostics-globals
as a safe-local-variable
.
(put 'lsp-lua-diagnostics-globals
'safe-local-variable
(lambda (e)
(and (vectorp e)
(--all?
(and (stringp it)
(not (string-empty-p it)))
(seq-concatenate 'list e)))))
Add lsp-lua-workspace-library
as a safe-local-variable
.
(put 'lsp-lua-workspace-library 'safe-local-variable
(lambda (e)
(let ((is-dir-p (lambda (dir) (and (stringp dir))
(file-directory-p dir))))
(and (listp e)
(--all? (pcase it
(`(,first . ,last) (and (funcall is-dir-p first)
(or (eq last t)
(and (listp last)
(-all? is-dir-p
last))))))
e)))))
Due to the limited number of file descriptors available in the MacOS version of emacs
, we need to be conservative with their use. This means turning off automatic file-watching in lsp
.
(setq lsp-enable-file-watchers (not IS-MAC))
This emacs
package provides a minor mode which configures magit
to use delta when displaying diffs.
(package! magit-delta)
(use-package! magit-delta
:custom (magit-delta-default-dark-theme "Nord")
:hook (magit-mode . magit-delta-mode))
magit-delta-mode
can be slow on big diffs. Luckily, we can advice the appropiate functions to deactivate the mode on big buffers.
(after! magit-delta
(defcustom dan/magit-delta-point-max 50000
"Maximum length of diff buffer which `magit-delta' will tolerate."
:group 'magit-delta
:type 'natnum)
(defadvice! dan/magit-delta-colorize-maybe (fn &rest args)
"Disable mode if there are too many characters."
:around #'magit-delta-call-delta-and-convert-ansi-escape-sequences
(if (<= (point-max) dan/magit-delta-point-max)
(apply fn args)
(magit-delta-mode -1))))
Re-enable mode after magit-refresh
if there aren’t too many characters.
(after! magit
(add-hook! 'magit-post-refresh-hook
(when (and (not magit-delta-mode)
(<= (point-max) dan/magit-delta-point-max))
(magit-delta-mode +1))))
The default popup-rule
makes the buffer appear in the lower half of the screen. man
pages use more vertical space, so we push it to the right side of the frame.
(add-transient-hook! 'doom-first-input-hook
(let ((cell (assoc 'side
(assoc "^\\*\\(?:Wo\\)?Man " display-buffer-alist))))
(setcdr cell 'right)))
The default doom-modeline
is great, the only thing is that I want it to show me
the evil state I am in with a letter instead of an icon:
(use-package! doom-modeline
:defer t
:custom
(doom-modeline-modal-icon nil))
Emacs aggressively adds a \n
to files, which is technically a good practice. However, lots of other IDEs don’t do this. In collaborative version controlled projects, this can result in emacs stubbornly adding a hunk at the end of the file, which can lead to problems, from strange commits diffs to tests failing because files where not expected to have a trailing new line.
We change it so emacs simply asks us first if we want to add the \n
before saving.
(setq require-final-newline 'ask)
This little hack prevents persp-mode
to bother me when saving its autosave
file. We advice basic-save-buffer
so files in persp-save-dir
insert a breakline if missing one.
(after! persp-mode
(defadvice! dan/persp-autosave--add-breakline (&rest _)
"Automatically add breakline for certain buffers before saving to file."
:before #'basic-save-buffer
(when (and
(/= (point-max) (point-min))
(/= (char-after (1- (point-max))) ?\n)
(string-equal (file-name-directory
(or (buffer-file-name (current-buffer)) ""))
persp-save-dir))
(goto-char (point-max))
(insert ?\n))))
Use alejandra
as the default formatter.
(after! nix-mode
(set-formatter! 'alejandra "alejandra --quiet" :modes '(nix-mode))
(map! :localleader
:map nix-mode-map
:desc "nix-format-buffer" "p" #'+format/buffer))
nodejs-repl
is a super useful package. However, it’s missing a comfortable way to interact with promises. We can change that with an experimental nodejs
.
(after! nodejs-repl
(setq nodejs-repl-arguments '("--experimental-repl-await")))
One of the killer features of Emacs.
(use-package! org
:defer t
If you use org
and don’t want your org files in the default location below,
change org-directory
. It must be set before org loads!
:custom
(org-directory "~/Cloud/org/")
Set org-attach-id-dir
back to default value.
(org-attach-id-dir "data")
Any file in the agenda
directory is part of the agenda view.
(org-agenda-files (list (expand-file-name "agenda/" org-directory)))
Modules for completing checklists and making links to bookmarks
, elisp
symbols, pdf
s, info
pages, man
pages and others.
(org-modules '(org-checklist
org-mouse
ol-elisp-symbol
ol-bookmark
ol-info
ol-man
ol-telega))
A project with no NEXT subheads is stuck.
(org-stuck-projects '("TODO=\"PROJ\"" ("NEXT") nil ""))
Default priority is lowest priority
(org-priority-default org-priority-lowest))
In org buffers, remove the line number fringe.
(after! org
(setq-hook! org-mode
display-line-numbers nil))
Use the TODO keywords that suit my workflow.
- TODO
- Task that needs to be done. Needs a SCHEDULED or DEADLINE date, else it’s just a pipe dream.
- NEXT
- Task that has already started.
- PROJ
- Task that holds a collection of related tasks, events and ideas.
- WAIT
- Tasks that cannot be completed until an external event happens, that is, something outside my control. Tasks that depend on other tasks, whose completion depend exclusively on me, use the
BLOCKER
property. - EVENT
- Indicates a happening.
- IDEA
- A note that could turn into a task or an entire project, needs refinement.
- DONE
- Completed task.
- CANCELLED
- Task that will not be done.
Also, add them faces so they stick out more:
(after! org
(custom-declare-face '+org-todo-wait '((t (:inherit (bold mode-line-emphasis org-todo)))) "")
(setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "PROJ(p)" "WAIT(w)" "IDEA(i)" "EVENT(e)" "|"
"DONE(d)" "CANCELLED(c)"))
org-todo-keyword-faces '(("NEXT" . +org-todo-active)
("WAIT" . +org-todo-wait)
("EVENT" . +org-todo-onhold)
("PROJ" . +org-todo-project)
("CANCELLED" . +org-todo-cancel))))
org-capture-templates
are task templates that help automate the process of automating tasks. The defaults in doom
are a bit over-kill for me and make assumptions that are not fit for my workflow flow.
The key files of the capture template are:
+org-capture-projects-file
- Project related todos.
+org-capture-todo-file
- Unplanned or emergent todos, tend to be resolved quickly.
+org-capture-journal-file
- Scheduled todos or events.
+org-capture-notes-file
- Ideas file.
(after! org
(setq +org-capture-projects-file (expand-file-name "projects.org" (car org-agenda-files))
+org-capture-todo-file (expand-file-name "diary.org" (car org-agenda-files))
+org-capture-journal-file (expand-file-name "schedule.org" (car org-agenda-files))
+org-capture-notes-file (expand-file-name "ideas.org" (car org-agenda-files))))
And the templates that use those files:
(after! org
(setq org-capture-templates '(("t" "Quick todo" entry
(file+headline +org-capture-todo-file "Quick errands")
"* TODO %?\n%i\n" :prepend t)
("e" "Event" entry
(file+olp +org-capture-journal-file "Events")
"* EVENT %?\n%i\n" :prepend nil)
("i" "Random idea" entry
(file+olp +org-capture-notes-file "Inbox")
"* IDEA %?\n%i\n" :prepend t)
("p" "Centralized templats for projects")
("pt" "Project todo" entry
#'+org-capture-central-project-todo-file
"* TODO %?\n %i\n %a" :heading "Tasks" :prepend t)
("pe""Project event" entry
#'+org-capture-central-project-todo-file
"* EVENT %?\n %i\n" :heading "Events" :prepend nil)
("pi" "Project idea" entry
#'+org-capture-central-project-todo-file
"* IDEA %?\n %i\n %a" :heading "Ideas" :prepend t))))
Easier to find org-attach
links.
(after! org
(setq org-id-method 'ts
org-attach-id-to-path-function-list '(org-attach-id-ts-folder-format
org-attach-id-uuid-folder-format)))
Now we specify the org-refile-targets
. Any open org file and the files that are part of the agenda.
(after! org
(setq org-refile-targets '((nil :maxlevel . 9)
(org-agenda-files :maxlevel . 9))))
To have timestamps consistently in english, no matter the system language.
(setq system-time-locale "C")
Add integration with evil-mode
, to auto hide/show entities. With the builtin +org-pretty-mode
we hide the emphasis markers.
(after! org
(add-hook! org-mode #'+org-pretty-mode #'org-appear-mode))
org-edna
offers more control over how and when tasks change state and manages
dependencies between tasks through extra heading properties.
(package! org-edna)
Load and activate org-edna
together with org
.
(use-package! org-edna
:hook (org-load . org-edna-mode)
Make org-edna
to trigger in any state change except done.
:custom
(org-edna-from-todo-states 'not-done))
Bind org-edna-edit
in org-mode
buffers.
(map! (:after org
:localleader
:map org-mode-map
:desc "Org Edna Edit" :g "E" #'org-edna-edit))
Like topsy
, for org mode.
(package! org-sticky-header)
Lazy load only in org-mode
buffers. Show then full the path of the outline in the header.
(use-package! org-sticky-header
:custom
(org-sticky-header-full-path 'full)
:hook
(org-mode . org-sticky-header-mode))
This package lets you “supercharge” your Org daily/weekly agenda. The idea is to group items into sections, rather than having them all in one big list.
(package! org-super-agenda)
So this package filters the results from org-agenda-finalize-entries, which runs just before items are inserted into agenda views. It runs them through a set of filters that separate them into groups. Then the groups are inserted into the agenda buffer, and any remaining items are inserted at the end. Empty groups are not displayed.
The end result is your standard daily/weekly agenda, but arranged into groups defined by you. You might put items with certain tags in one group, habits in another group, items with certain todo keywords in another, and items with certain priorities in another. The possibilities are only limited by the grouping functions. u The primary use of this package is for the daily/weekly agenda, made by the org-agenda-list command, but it also works for other agenda views, like org-tags-view, org-todo-list, org-search-view, etc.
(use-package! org-super-agenda
:after org
:config
(org-super-agenda-mode))
The org-agenda
shows today’s tasks.
(after! org-agenda
(setq org-agenda-span 'day
org-agenda-start-day "<today>"))
Keybinding for the org-agenda
.
(map! (:prefix "o"
:leader
:desc "Agenda Menu" "a" #'org-agenda))
Create a workspace like feel to each org-agenda
calling. We make it fill the whole frame and restore the previous window configuration after we are done.
(after! org-agenda
(setq org-agenda-window-setup 'only-window
org-agenda-restore-windows-after-quit t))
Sometimes I schedule a task for a particular day and set a deadline for a time in that same day. In those cases, we want to emphasize the deadline and not the schedule date.
(after! org-agenda
(setq org-agenda-skip-scheduled-if-deadline-is-shown t))
Don’t show deadline warning if task is scheduled prior to the deadline time.
(after! org-agenda
(setq org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled))
Prevent done tasks to show in the agenda.
(after! org-agenda
(setq org-agenda-skip-scheduled-if-done t
org-agenda-skip-deadline-if-done t
org-agenda-skip-timestamp-if-done t))
Show scheduled items without a time first in the Day Agenda
.
(after! org-agenda
(setq org-agenda-sort-notime-is-late nil))
Don’t show blocked tasks.
(after! org-agenda
(setq org-enforce-todo-dependencies t
org-agenda-dim-blocked-tasks 'invisible))
For extra visibility, we tweak the agenda sorting strategy to make scheduled
tasks appear first, ordered by priority
.
(after! org-agenda
(setq org-agenda-sorting-strategy '((agenda time-up priority-down)
(todo scheduled-up priority-down)
(tags scheduled-up priority-down)
(search scheduled-up priority-down))))
Display any scheduled
or timestamp
info in agenda.
(after! org-agenda
(setq org-agenda-prefix-format '((agenda . " %?-12t%-12s")
(todo . " %?-12s")
(tags . " %?-12s")
(search . " %?-12s"))))
Provide a macro to dynamically build TIMESTAMP
selectors. We want the date providers to be functions so their return values are refreshed on each calling.
(after! org-super-agenda
(defsubst dan/org-super-agenda--soon-limit ()
"Return the last ISO Date that is within the soon limit."
(org-read-date nil nil "+3d"))
(defmacro dan/org-super-agenda--build-timestamp-selector (&optional date-provider)
"Build a timestamp selector that includes the date returned by
DATE-PROVIDER.
DATE-PROVIDER is a function with no arguments that returns a date in the style
of `calendar-gregorian-from-absolute'. It defaults to `org-today' if empty."
(let ((date-provider (or (and (functionp date-provider)
(bound-and-true-p date-provider))
#'org-today)))
`(lambda (date)
(let ((other-date ,(funcall date-provider)))
(if (string-match-p "--" date)
(cl-destructuring-bind (date-start date-end) (split-string date "--" t)
(and (>= other-date
(org-time-string-to-absolute date-start))
(<= other-date
(org-time-string-to-absolute date-end))))
(>= other-date
(org-time-string-to-absolute date))))))))
The dan/org-super-agenda--transformer-show-scheduled-or-deadline
shows an item’s DEADLINE and/or SCHEDULED in the entry.
(after! org-super-agenda
(defun dan/org-super-agenda--transformer-show-scheduled-or-deadline (item)
"Transform ITEM to show SCHEDULED and DEADLINE properties."
(let* ((this-marker (org-find-text-property-in-string 'org-marker item))
(scheduled-date (org-entry-get this-marker "SCHEDULED"))
(deadline-date (org-entry-get this-marker "DEADLINE"))
(tags (or (and (string-match "\s*:[[:word:]:@_]*: *$" item)
(match-string 0 item))
"")))
(if (or scheduled-date deadline-date)
(concat (string-remove-suffix tags item)
(when scheduled-date
(concat "\t"
(propertize "SCHEDULED: " 'face 'org-special-keyword)
(propertize (or scheduled-date "") 'face 'org-date)))
(when deadline-date
(concat "\t"
(propertize "DEADLINE: " 'face 'org-special-keyword)
(propertize (or deadline-date "") 'face 'org-date)))
tags)
item))))
Refresh the soon
limit date on each org-super-agenda
calling.
(after! org-super-agenda
(defadvice! dan/org-super-agenda--refresh-soon-limit (_)
:before #'org-super-agenda--group-items
(let ((group (cl-find-if (lambda (group)
(string= (plist-get group :name) "Soon"))
org-super-agenda-groups)))
(setf (plist-get group :scheduled)
`(before ,(dan/org-super-agenda--soon-limit))))))
With the package installed, we are ready to define org-agenda-custom-commands
as well as the org-super-agenda-groups
. First we start with my personal custom commands, which focus on anything that is not work related.
(after! org-agenda
(setq org-agenda-custom-commands '(("p" . "Personal Tasks"))))
How does my day look like? Day agenda
focus on answering that question with as little noise as posible. No info about anything else but my current day.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("pd"
"Personal Day Agenda"
agenda
""
((org-agenda-tag-filter-preset '("-work"))
(org-super-agenda-groups nil)
(org-super-agenda-unmatched-name "")
(org-super-agenda-header-prefix "")))))
A more general view of my tasks, with extra contextual information, such as task that are on hold, task that are scheduled soon, etc. See org-super-agenda-groups
.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("pt"
"Personal TODOs"
tags-todo
"-work"
((org-tags-history (pushnew! org-tags-history "-work"))))))
Work custom commands.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("w" . "Work Tasks")))
Same as my personal Day Agenda
but only for work related tasks.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("wd"
"Work Day Agenda"
agenda
""
((org-super-agenda-groups nil)
(org-agenda-tag-filter-preset '("+work"))
(org-super-agenda-unmatched-name "")
(org-super-agenda-header-prefix "")))))
Same as my personal Global TODOs
, but only for work related tasks.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("wt"
"Work TODOs"
tags-todo
"+work"
((org-tags-history (pushnew! org-tags-history "+work"))))))
A Day Agenda
with any and all tasks of today.
(after! org-agenda
(add-to-list 'org-agenda-custom-commands '("d"
"Global Day Agenda"
agenda
""
((org-super-agenda-groups nil)
(org-super-agenda-unmatched-name "")
(org-super-agenda-header-prefix "")))))
The default org-super-agenda-groups
.
- Started
- Tasks that are being actively worked on. Omit the tasks that are rescheduled.
- Today
- Anything that is due, scheduled or is going to happen today or in the past.
- Soon
- Anything that is due, scheduled or is going to happen within the next three days.
- Incoming Events
- Future happenings.
- Important
- Important tasks that haven’t been scheduled. Ideally should always be empty.
- On Hold
- Tasks that are wating on other task or some external happening.
- Are Scheduled
- Scheduled future tasks.
- Have Deadline
- Task with a dealine, but not scheduled>
- Backlog
- Everything else, except projects.
- Projects
- All projects.
Everything
(after! org-super-agenda
(setq org-super-agenda-groups `((:name "Started"
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline
:and (:todo "NEXT" :not (:scheduled future)))
(:name "Today"
:scheduled today
:scheduled past
:property ("TIMESTAMP"
,(dan/org-super-agenda--build-timestamp-selector))
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline)
(:name "Soon"
:scheduled (before ,(dan/org-super-agenda--soon-limit))
:deadline (before ,(dan/org-super-agenda--soon-limit))
:property ("TIMESTAMP"
,(dan/org-super-agenda--build-timestamp-selector
(lambda ()
(org-time-string-to-absolute (dan/org-super-agenda--soon-limit)))))
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline)
(:name "Incoming Events"
:todo "EVENT")
(:name "Important"
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline
:and (:priority "A"
:not (:todo "WAIT"
:scheduled future)))
(:name "On Hold"
:todo "WAIT")
(:name "Are Scheduled"
:scheduled future
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline)
(:name "Have Deadline"
:deadline future
:transformer dan/org-super-agenda--transformer-show-scheduled-or-deadline)
(:name "Backlog"
:not (:todo "PROJ"))
(:name "Projects"
:todo "PROJ"))))
There are a few clashes between evil-org-agenda-mode-map
and org-super-agenda-header-map
. To remove them, we simply override the latter keymap, after loading both evil-org-agenda
and org-super-agenda
.
(after! (evil-org-agenda org-super-agenda)
(setq org-super-agenda-header-map (copy-keymap evil-org-agenda-mode-map))
(map! :map org-super-agenda-header-map
:nm "q" #'org-agenda-quit
:nm "TAB" #'ignore))
To prevent unnecessary scrolling when calling the agenda buffer, we automate the cursor position on agenda todo
views.
(after! (evil-org-agenda org-super-agenda)
(add-hook! 'org-agenda-finalize-hook
:append (when (and (not (buffer-narrowed-p))
(goto-char (point-min)))
(when (not (org-agenda-check-type nil 'agenda))
(re-search-forward "^$")
(forward-line)
(evil-scroll-line-to-center (count-lines (point-min) (point))))
(org-agenda-next-item 1))))
Prevent org-capture
buffers from loosing their target when restarting.
(after! org
(defadvice! dan/+org--restart-mode-h-careful-restart (fn &rest args)
:around #'+org--restart-mode-h
(let ((old-org-capture-current-plist (and (bound-and-true-p org-capture-mode)
(bound-and-true-p org-capture-current-plist))))
(apply fn args)
(when old-org-capture-current-plist
(setq-local org-capture-current-plist old-org-capture-current-plist)
(org-capture-mode +1)))))
Prevent false error messages with org-element--cache-sync
.
(after! org
(add-hook! 'org-capture-after-finalize-hook (org-element-cache-reset t)))
Prevent attempting to move files to trash when in a remote directory.
(when IS-MAC
(setq-hook! (dired-mode magit-mode 'eshell-before-prompt-hook)
delete-by-moving-to-trash (not (file-remote-p default-directory))))
Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates and snippets.
(setq user-full-name "Daniel Levy"
user-mail-address "[email protected]")
A builtin hidden gem, which has substituted htop
for me.
Mode for displaying system processes and sending signals to them.
(use-package! proced
:defer t
By default, proced
doesn’t auto update the process list, which is a bummer. Luckily, we can change that.
:custom
(proced-auto-update-flag t)
(proced-auto-update-interval 1))
Let’s make projectile’s life easier by giving it some paths where I normally store projects.
(setq projectile-project-search-path '("~/Projects/" "~/.config/"))
There is a bug in projectile-skel-dir-locals
that prevents completion of the template, as a workaround:
(after! projectile
(defadvice! dan/projectile-skel-dir-locals--fixed (&optional str arg)
:override #'projectile-skel-dir-locals
(interactive "*P\nP")
(skeleton-proxy-new
'(nil "((nil . (" ("" '(projectile-skel-variable-cons) n)
resume: ")))")
str arg)))
I work on a large monorepo at work, which projectile-find-file
can struggle with. Setting the indexing method to alien
can speed things up. We make this variable a safe-local-variable
.
(put 'projectile-indexing-method
'safe-local-variable
(lambda (val)
(memq val '(alien hybrid native))))
QR Code encoder written in pure Emacs Lisp.
(package! qrencode)
This package provides two user facing interactive functions, that will encode text into a QR Code and show it in a separate buffer.
qrencode-region
Shows the current selection as a QR Code.
qrencode-url-at-point
Encode URL at point as QR Code.
(use-package! qrencode
:defer t)
Matching pairs draw with the same face color, making them easily identifiable.
(add-hook! prog-mode #'rainbow-delimiters-mode-enable)
run-command you write a short recipe and obtain a command that is easy to bring up, invoke, and keep track of, without leaving Emacs.
(package! run-command)
We advice run-command--run-compile
to use detached
.
(use-package! run-command
:defer t
:config
;; NOTE: Forgotten builtin dependency
(unless (featurep 'map)
(require 'map))
(defadvice! dan/run-command--run-detached-shell-command (command-line buffer-base-name)
"Override `run-command--run-compile' to use `detached-compile'."
:override #'run-command--run-compile
(let ((compilation-buffer-name-function
(lambda (_name-of-mode) buffer-base-name)))
(detached-compile command-line t))))
Make sure run-command
uses vertico
.
(after! run-command
(setq run-command-completion-method 'completing-read))
An easy mnemonic
keybinding.
(map! :leader
:desc "Run command recipe" "r" #'run-command)
NixOS
recipes.
(after! run-command
(defun dan/run-command--nix ()
(if IS-LINUX
(list
;; FIXME Detect the host first
;; (list :command-line "rm -f ~/.config/mimeapps.list && sudo nixos-rebuild switch --flake '/etc/nixos#nyx15v2'"
;; :command-name "Rebuild NixOS"
;; :display "Create a new NixOS generation and switch to it")
(list :command-line "nix flake update /etc/nixos"
:command-name "Update NixOS"
:display "Update the NixOS flake inputs")
(list :command-line "sudo nix-collect-garbage -d"
:command-name "GC NixOS"
:display "Delete unused derivations and previous generations"))
(list
(list :command-line "darwin-rebuild switch --flake '/Users/dlevy/.config/nixos#autoMac'"
:command-name "Rebuild darwin"
:display "Create a new darwin generation and switch to it")
(list :command-line "darwin-rebuild check --flake '/Users/dlevy/.config/nixos#autoMac'"
:command-name "Check darwin"
:display "Check if new darwin generation is buildable")
(list :command-line "nix flake update /Users/dlevy/.config/nixos"
:command-name "Update darwin"
:display "Update the darwin flake inputs")
(list :command-line "nix-collect-garbage -d"
:command-name "GC darwin"
:display "Delete unused derivations and previous generations"))))
(add-to-list 'run-command-recipes #'dan/run-command--nix t))
MacOS
recipes.
(after! run-command
(when IS-MAC
(defun dan/run-command--macos ()
(list
(list :command-line "arch -arm64 brew upgrade"
:command-name "Upgrade Brew"
:display "Upgrade Brew packages")))
(add-to-list 'run-command-recipes #'dan/run-command--macos)))
Use rust-analyzer
by default, since it has more features.
(use-package! rustic
:defer t
:custom
(rustic-lsp-server 'rust-analyzer)
When using the rustic popup, be in Emacs state.
:config
(when (featurep 'evil)
(add-hook! 'rustic-popup-mode-hook #'evil-emacs-state)))
A nice, comfy, useless package.
(package! snow)
We define an auto-load function as an entry point to the winter season.
(use-package! snow
:commands snow)
Default doom dashboard is pretty and welcoming, let’s just give it a small personal touch.
(setq fancy-splash-image (expand-file-name "img/qs.png" doom-private-dir))
But what if I am in the terminal? No worries:
(defun dan/my-weebery-is-always-greater ()
(mapc (lambda (line)
(insert (propertize (+doom-dashboard--center +doom-dashboard--width line)
'face 'doom-dashboard-banner) " ")
(insert "\n"))
'("⢸⣿⣿⣿⣿⠃⠄⢀⣴⡾⠃⠄⠄⠄⠄⠄⠈⠺⠟⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣶⣤⡀⠄"
"⢸⣿⣿⣿⡟⢀⣴⣿⡿⠁⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣸⣿⣿⣿⣿⣿⣿⣿⣷"
"⢸⣿⣿⠟⣴⣿⡿⡟⡼⢹⣷⢲⡶⣖⣾⣶⢄⠄⠄⠄⠄⠄⢀⣼⣿⢿⣿⣿⣿⣿⣿⣿⣿"
"⢸⣿⢫⣾⣿⡟⣾⡸⢠⡿⢳⡿⠍⣼⣿⢏⣿⣷⢄⡀⠄⢠⣾⢻⣿⣸⣿⣿⣿⣿⣿⣿⣿"
"⡿⣡⣿⣿⡟⡼⡁⠁⣰⠂⡾⠉⢨⣿⠃⣿⡿⠍⣾⣟⢤⣿⢇⣿⢇⣿⣿⢿⣿⣿⣿⣿⣿"
"⣱⣿⣿⡟⡐⣰⣧⡷⣿⣴⣧⣤⣼⣯⢸⡿⠁⣰⠟⢀⣼⠏⣲⠏⢸⣿⡟⣿⣿⣿⣿⣿⣿"
"⣿⣿⡟⠁⠄⠟⣁⠄⢡⣿⣿⣿⣿⣿⣿⣦⣼⢟⢀⡼⠃⡹⠃⡀⢸⡿⢸⣿⣿⣿⣿⣿⡟"
"⣿⣿⠃⠄⢀⣾⠋⠓⢰⣿⣿⣿⣿⣿⣿⠿⣿⣿⣾⣅⢔⣕⡇⡇⡼⢁⣿⣿⣿⣿⣿⣿⢣"
"⣿⡟⠄⠄⣾⣇⠷⣢⣿⣿⣿⣿⣿⣿⣿⣭⣀⡈⠙⢿⣿⣿⡇⡧⢁⣾⣿⣿⣿⣿⣿⢏⣾"
"⣿⡇⠄⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⢻⠇⠄⠄⢿⣿⡇⢡⣾⣿⣿⣿⣿⣿⣏⣼⣿"
"⣿⣷⢰⣿⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⢰⣧⣀⡄⢀⠘⡿⣰⣿⣿⣿⣿⣿⣿⠟⣼⣿⣿"
"⢹⣿⢸⣿⣿⠟⠻⢿⣿⣿⣿⣿⣿⣿⣿⣶⣭⣉⣤⣿⢈⣼⣿⣿⣿⣿⣿⣿⠏⣾⣹⣿⣿"
"⢸⠇⡜⣿⡟⠄⠄⠄⠈⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟⣱⣻⣿⣿⣿⣿⣿⠟⠁⢳⠃⣿⣿⣿"
"⠄⣰⡗⠹⣿⣄⠄⠄⠄⢀⣿⣿⣿⣿⣿⣿⠟⣅⣥⣿⣿⣿⣿⠿⠋⠄⠄⣾⡌⢠⣿⡿⠃"
"⠜⠋⢠⣷⢻⣿⣿⣶⣾⣿⣿⣿⣿⠿⣛⣥⣾⣿⠿⠟⠛⠉⠄⠄ ")))
(when (not (display-graphic-p))
(setq +doom-dashboard-ascii-banner-fn #'dan/my-weebery-is-always-greater))
telega
is full featured unofficial client for Telegram platform for GNU Emacs.
(package! telega
:recipe (:files (:defaults
"contrib/*.el"
"etc"
"server"
"Makefile")))
Minimal configuration to be able to build the server and load the cache.
(use-package! telega
:defer t
:custom
(telega-directory (expand-file-name "telega" doom-cache-dir))
(telega-video-player-command '(concat "mpv"
(when telega-ffplay-media-timestamp
(format " --start=%f" telega-ffplay-media-timestamp))))
(telega-completing-read-function completing-read-function)
(telega-use-docker t))
Quick keybinding to open and close telega
.
(map! (:leader
:desc "Telegram" :mv "o m" #'dan/=telega)
(:after telega
:map telega-root-mode-map
:n "q" #'dan/=telega-kill))
evil-snipe
shadows the search
default bindings of telega
in evil-collection
, so we disable it for telega-root-mode
.
(after! evil-snipe
(add-to-list 'evil-snipe-disabled-modes 'telega-root-mode))
We set up auto-completion for chats.
(after! telega
(set-company-backend! 'telega-chat-mode
'(company-ispell
company-dabbrev
telega-company-telegram-emoji
telega-company-username
telega-company-botcmd
telega-company-hashtag)))
Finally, we add some extra goodies, some in core
and others in contrib
. These include visual customization like shortening url's
or code blocks syntax highlighting
to more complicated functionality like system notifications
brought to you via alert.el
.
(add-hook! telega-load
;; core
#'telega-mode-line-mode
#'global-telega-squash-message-mode
#'telega-notifications-mode
#'telega-autoplay-mode
;; contrib
#'global-telega-url-shorten-mode
#'global-telega-mnz-mode
#'telega-alert-mode
#'telega-transient-mode
#'telega-status-history-mode)
Highlight the current line in telega
buffer.
(add-hook! '(telega-root-mode-hook telega-chat-mode-hook)
#'hl-line-mode)
Sometimes telega
emacs when trying to quit it. Forcing to quit telega
when quitting emacs
prevents this situation.
(add-hook! kill-emacs
(when (and (boundp 'telega-root-buffer-name)
(get-buffer telega-root-buffer-name))
(telega-kill t)))
telega
root buffer is managed by the workspace configuration.
(after! telega
(set-popup-rule! "\\`\\*Telega.+"
:ignore t))
Some functions to integrate with the workspaces
module.
;;; $DOOMDIR/autoload/telega.el -*- lexical-binding: t; -*-
A function to open the telega
workspace.
(defvar +telega-workspace-name "Telega")
;;;###autoload
(defun dan/=telega (&optional arg)
"Like `telega', but opens the buffer in it's own workspace."
(interactive "P")
(+workspace-switch +telega-workspace-name t)
(unless (memq (buffer-local-value 'major-mode (window-buffer (selected-window)))
'(telega-root-mode telega-chat-mode telega-image-mode))
(doom/switch-to-scratch-buffer)
(telega arg)
(+workspace/display)))
A function to close the telega
workspace.
;;;###autoload
(defun dan/=telega-kill (&optional args)
"Like `telega-kill', but deletes the workspace associated with `+telega-workspace-name' if it exists."
(interactive "P")
(telega-kill args)
(+workspace-delete +telega-workspace-name))
Scroll down and recenter top lines in Emacs.
- Easier on the eyes: Recenter or scroll down top text to a more comfortable eye level for reading, especially when in full-screen or on a large monitor.
- Easy to use: No new keybindings are required, keep using all your previous scrolling & recentering commands, except now you can also scroll above the top lines. It also integrates seamlessly with centered-cursor-mode to keep the cursor centered all the way to the top line.
(package! topspace)
A package I didn’t know I needed.
(use-package! topspace
:custom
(topspace-active #'dan/topspace--active-p)
(topspace-autocenter-buffers #'dan/topspace--active-p)
(indicate-empty-lines t)
:hook
(doom-first-buffer . global-topspace-mode))
We create a blacklist
of sorts to deactivate topspace-mode
in special modes.
(after! topspace
(defun dan/topspace--active-p ()
(not (or (minibufferp)
(derived-mode-p 'vterm-mode
'comint-mode
'org-agenda-mode
'+doom-dashboard-mode
'eshell-mode
'magit-mode
'telega-root-mode
'telega-chat-mode
'image-mode
'Custom-mode
'compilation-mode
'cfw:calendar-mode
'special-mode)))))
This library shows a sticky header at the top of the window. The header shows which definition the top line of the window is within.
(package! topsy)
We activate topsy-mode
only in programming contexts.
(use-package! topsy
:defer t
:init
(add-hook! prog-mode
(unless (memq major-mode '(+doom-dashboard-mode org-mode dirvish-mode))
(topsy-mode +1)))
To make the header-line
a little more noticeable while consistent with the buffer style, we change it’s font and inherit from the default
style.
:config
(custom-set-faces!
'(header-line :family "Victor Mono"
:inherit default)))
Already have ssh-controlmaster
configured in my config
for ssh. Additionally, make the chunksize
a reasonable default.
(after! tramp-sh
(setq tramp-use-ssh-controlmaster-options nil
tramp-chunksize 2000))
Uniquify makes buffers with the same name easier to distinguish. The 'forward
configuration shows the file path of the buffer.
(setq uniquify-buffer-name-style 'forward)
We want packages to be lazy by default. In other words, load only on demand.
(setq use-package-always-defer t)
Make it easy to find use-package
definitions in emacs-lisp
buffers.
(setq use-package-enable-imenu-support t)
Let’s add minor customization to the vc-gutter
module:
First, diff the file while the buffer is changing, not just when it’s saved.
(setq +vc-gutter-diff-unsaved-buffer t)
Lastly, let’s activate git-gutter
for remote files as well.
(setq +vc-gutter-in-remote-files t)
Emacs minor mode that allows viewing, editing, searching and comparing large files in batches, trading memory for processor time.
(package! vlf)
vlf-setup
adds a menu option to use vlf
when accessing a file larger than large-file-warning-threshold
. Other optimizations included expanding the number of bytes read on each batch and activating so-long-mode
eagerly.
(use-package! vlf
:defer-incrementally t
:custom
(vlf-batch-size-remote read-process-output-max)
:config
(require 'vlf-setup)
(add-hook! 'vlf-mode-hook #'so-long-mode))
(after! so-long
(add-to-list 'so-long-mode-preserved-variables 'vlf-mode))
For opening big files in remote servers, this package is a God send. When vlf-mode
is active, other useful keybindings can be found under the prefix C-c v
.
Use default system shell for vterm
.
(after! vterm
(setq-default vterm-shell (getenv "SHELL")))
Make urls
clickable in vterm
modes.
(add-hook! vterm-mode #'goto-address-mode)
Maximum scrollback to the max!
(after! vterm
(setq vterm-max-scrollback 100000))
Make vterm
as snappy as it can be.
(after! vterm
(setq vterm-timer-delay nil))
Use bash
as default shell in remote servers.
(after! vterm
(pushnew! vterm-tramp-shells
'("ssh" "/bin/bash")
'("scp" "/bin/bash")))
Unfortunately, MacOS M1
compilation is hardly straightforward. For a HACK/FIX
, I force compilation through the latest gcc
installed through homebrew
.
(after! vterm
(defadvice! dan/vterm-module-compile (fn &rest _)
:around #'vterm-module-compile
(if IS-MAC
(let* ((vterm-directory
(shell-quote-argument (file-name-directory (locate-library "vterm.el" t))))
(vterm-module-cmake-args "-DCMAKE_C_COMPILER=`which gcc-12`")
(make-commands
(concat
"cd " vterm-directory "; \
rm -rf build; \
mkdir -p build; \
cd build; \
cmake -B build "
vterm-module-cmake-args
" ..; \
arch -arm64 cmake --build build; \
cd -"))
(buffer (get-buffer-create vterm-install-buffer-name)))
(pop-to-buffer buffer)
(compilation-mode)
(if (zerop (let ((inhibit-read-only t))
(call-process "sh" nil buffer t "-c" make-commands)))
(message "Compilation of `emacs-libvterm' module succeeded")
(error "Compilation of `emacs-libvterm' module failed!")))
(funcall fn))))
A visual representation of emacs
built-in undo tree
.
(package! vundo)
For node navigation:
Key | Description |
---|---|
l | to go forward |
h | to go backward |
j | to go to the node below when you at a branching point |
k | to go to the node above |
H | to go back to the last branching point |
L | to go forward to the end/tip of the branch |
q | to quit, you can also type C-g |
(use-package! vundo
:custom
(vundo-glyph-alist vundo-unicode-symbols)
(vundo-compact-display t)
:config
(map! :map vundo-mode-map
[remap doom/escape] #'vundo-quit)
:defer t)
Set an entry point.
(map! :leader
:desc "Visual Undo Tree" "b U" #'vundo)
Doom Emacs default configuration is too slow, let’s speed it up.
(after! which-key
(setq which-key-idle-delay 0.1
which-key-idle-secondary-delay 0.2))
Make white spaces visible in programming modes.
(after! prog-mode
(add-hook! prog-mode #'whitespace-mode))
Minimal configuration to show most of everything except newlines. Missing newlines and end-of-files are also visually indicated.
(add-transient-hook! 'prog-mode-hook
(setq whitespace-style '(face
indentation
tabs
tab-mark
spaces
space-mark
missing-newline-at-eof
trailing)))
I work with lots of files that have long lines, which can be hard to read
sometimes. +word-wrap-mode
helps to make these files more readable without
changing the buffer contents.
(add-hook! 'doom-first-buffer-hook #'+global-word-wrap-mode)
I have family all over the place, it’s hard to keep up with the time difference. Luckily, emacs
can help with world-clock
mode.
(after! time
(setq world-clock-list '(("America/Denver" "Colorado")
("America/New_York" "North Wales")
("America/Sao_Paulo" "Brasilia")
("Europe/Madrid" "Madrid")
("Europe/Kiev" "Ukraine")
("Asia/Tokyo" "Tokyo"))))
Make the wclock
buffer into a popup.
(set-popup-rule! "^\\*wclock" :quit 'current :side 'top)
Make it quit-able with q
.
(after! time
(map! :map 'world-clock-mode-map
:nm "q" #'quit-window))
Make it play nice with evil-mode
.
(add-hook! world-clock-mode #'evil-normalize-keymaps)
Give it an easy entry keybinding SPC o w
.
(map! :leader
:desc "World Clock" :nm "o w" #'world-clock)
Keep header lines for +zen-mode
, so org-sticky-header-mode
works as intended.
(add-hook! org-mode
(setq-local writeroom-header-line t))
Make zen-mode
togglable in a per file basis, just like in this config.org
!
(add-to-list 'safe-local-eval-forms
'(when (and (fboundp #'+zen/toggle)
(not noninteractive))
(+zen/toggle)))
xterm-color.el is an ANSI control sequence to text-property translator.
(package! xterm-color)
Lazy load.
(use-package! xterm-color
:defer t)
YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.
For 日本語 grammar snippets.
# -*- mode: snippet -*-
# name: 文法の事項
# key: bunpou
# --
`(make-string (+ 1 (org-outline-level)) ?*)` ${1:文法の事項}
${2:説明}
`(make-string (+ 2 (org-outline-level)) ?*)` 文型
${3:文型}
`(make-string (+ 2 (org-outline-level)) ?*)` 翻訳
${4:翻訳}
`(make-string (+ 2 (org-outline-level)) ?*)` 例
${5:例}
${6:例の翻訳}
For starting a new project!
# -*- mode: snippet -*-
# name: New project
# key: project
# --
`(make-string (+ 1 (org-outline-level)) ?*)` PROJ ${1:name} :${2:tag_name}:
${3:description}
`(make-string (+ 2 (org-outline-level)) ?*)` Ideas
`(make-string (+ 3 (org-outline-level)) ?*)` IDEA ${4:My first task!}
`(make-string (+ 2 (org-outline-level)) ?*)` Tasks
`(make-string (+ 2 (org-outline-level)) ?*)` Events
For adding a new package in package.el
.
# -*- mode: snippet -*-
# name: New package
# key: package
# --
#+begin_src emacs-lisp :tangle packages.el
(package! ${1:package-name})
#+end_src
For adding new autoload functions in autoload/*.el
.
# -*- mode: snippet -*-
# name: New Autoload function
# key: autoload
# --
#+begin_src emacs-lisp :tangle autoload/${1:`(downcase (car (last (org-get-outline-path))))`}.el
;;;###autoload
(defun dan/${2:function} (${3:args})
"${4:docs}"
$0)
#+end_src
doom
allows the user to to write their own modules in their $DOOMDIR
directory, which will be auto-loaded at startup. It offers an extra features
like interaction and extension of bin/doom
, a fixed file structure where each
file is loaded at different points of the runtime and other niceties (more
macros!
).
For now, I haven’t had the need to use this feature but is good to be aware of it.