NixOS/dotfiles/doom/config.org
2025-10-01 13:40:05 -06:00

1077 lines
45 KiB
Org Mode
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+AUTHOR: Danilo Reyes (JawZ)
#+STARTUP: content
#+TITLE: JawZ's Doom Emacs config
* ABOUT THIS CONFIG
This is my personal Doom Emacs config file, forked from DT's config file on
GitLab and modified to fit my personal preferences, plugins, extensions and
other customization settings.
The purpose of this file, is to have an easy-to-read documentation which
explains why and how each customization works, for future editing and other
friends to use.
** Resources
+ The following [[https://systemcrafters.net/build-a-second-brain-in-emacs/5-org-roam-hacks/][tutorial]] and [[https://shiori.danilo-reyes.com/bookmark/39/content][archived tutorial]].
+ [[https://github.com/psamim/dotfiles][Agenda/org prettify stuff]]
+ [[https://gitlab.com/dwt1/dotfiles/-/tree/master/.config/doom][DT's original repo]]
** Variables
Below are some variables, which effect a lot of paths on the configuration,
putting these variables on top, will make it easier to make this file modular.
#+begin_src emacs-lisp :tangle ./config.el
(defvar my/org-device (or (getenv "ORG_DEVICE") "default"))
(defvar my/org-base-dir "~/Documents/Notes/")
(defvar my/doom-config-dir
(cond
((string= my/org-device "galaxy") "~/.config/doom/")
((string= my/org-device "workstation") "~/Development/NixOS/dotfiles/doom/")
(t "~/Development/NixOS/dotfiles/doom/")))
(defvar my/doom-data-dir
(cond
((string= my/org-device "galaxy") "~/.config/doom")
((string= my/org-device "workstation") "~/.local/share/doom/")
(t "~/.local/share/doom/")))
(defvar my/hugo-posts-dir
(cond
((string= my/org-device "galaxy") "~/portfolio/content-org/")
((string= my/org-device "workstation") "~/Development/Websites/portfolio/content-org/")
(t "~/Development/Websites/portfolio/content-org/")))
(defvar my/org-agenda-cache-file (expand-file-name ".cache" my/org-base-dir)
"Path to the org-agenda cache file.")
(defvar my/org-always-included-agenda-files
(list
(expand-file-name "20220823172331-chores.org" my/org-base-dir))
"Org files to always include in the agenda, even if no TODOs are found.")
(defvar my/doom-template-dir (expand-file-name "templates/" my/doom-data-dir)
"Directory for org-roam capture templates.")
(setq! org-roam-directory my/org-base-dir
org-roam-dailies-directory (expand-file-name "daily/" my/org-base-dir))
#+end_src
* BIBLE
Allows me to insert bible quotes into my org notes.
=NOTE= I'm not religious just for worldbuilding purposes lol.
#+begin_src emacs-lisp :tangle ./config.el
(use-package
insert-esv
:init
(setq insert-esv-crossway-api-key "bb1872462ecc59624c7bb8ab36c9701ce2027cd1")
(setq insert-esv-include-short-copyright 'false)
(setq insert-esv-include-headings 'true)
(setq insert-esv-include-passage-horizontal-lines 'false)
(setq insert-esv-line-length '500)
:bind ("C-x C-e" . insert-esv-passage))
#+end_src
* BOOKMARKS AND BUFFERS
Doom Emacs uses 'SPC b' for keybindings related to bookmarks and buffers.
** Bookmarks
Emacs bookmarking works somewhat like registers in that they record positions
you can jump to. Unlike registers, they have long names, and they persist
automatically from one Emacs session to the next. The prototypical use of
bookmarks is to record where you were reading in bookmarked files.
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------+----------------------------------------+------------|
| list-bookmarks | /List bookmarks/ | SPC b L |
| bookmark-set | /Set bookmark/ | SPC b m |
| bookmark-delete | /Delete bookmark/ | SPC b M |
| bookmark-save | /Save current bookmark to bookmark file/ | SPC b w |
#+BEGIN_src emacs-lisp :tangle ./config.el
(setq bookmark-default-file (expand-file-name "bookmarks" my/doom-data-dir))
(map! :leader
(:prefix ("b". "buffer")
:desc "List bookmarks" "L" #'list-bookmarks
:desc "Set bookmark" "m" #'bookmark-set
:desc "Delete bookmark" "M" #'bookmark-set
:desc "Save current bookmarks to bookmark file" "w" #'bookmark-save))
#+END_SRC
** Buffers
Regarding /buffers/, the text you are editing in Emacs resides in an object
called a /buffer/. Each time you visit a file, a buffer holds the files text.
Each time you invoke Dired, a buffer holds the directory listing. /Ibuffer/ is
a program that lists all of your Emacs /buffers/, allowing you to navigate
between them and filter them.
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------+----------------------+------------|
| ibuffer | Launch ibuffer | SPC b i |
| kill-buffer | Kill current buffer | SPC b k |
| next-buffer | Goto next buffer | SPC b n |
| previous-buffer | Goto previous buffer | SPC b p |
| save-buffer | Save current buffer | SPC b s |
** Global Auto Revert
A buffer can get out of sync with respect to its visited file on disk if that
file changes by another program. To keep it up to date, you can enable Auto
Revert mode by typing M-x auto-revert-mode, or you can set to turn on globally
with global-auto-revert-mode. I have also turned on Global Auto Revert on
non-file buffers, which is especially useful for dired buffers.
#+begin_src emacs-lisp :tangle ./config.el
(global-auto-revert-mode 1)
(setq global-auto-revert-non-file-buffers t)
#+end_src
** Keybindings within ibuffer mode
ibuffer mode, it's a more elegant way to display your opened buffers, and plus
you can manipulate them using vim keybindings.
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------------------------+----------------------------------------+------------|
| ibuffer-mark-forward | Mark the buffer | m |
| ibuffer-unmark-forward | Unmark the buffer | u |
| ibuffer-do-kill-on-deletion-marks | Kill the marked buffers | x |
| ibuffer-filter-by-content | ibuffer filter by content | f c |
| ibuffer-filter-by-directory | ibuffer filter by directory | f d |
| ibuffer-filter-by-filename | ibuffer filter by filename (full path) | f f |
| ibuffer-filter-by-mode | ibuffer filter by mode | f m |
| ibuffer-filter-by-name | ibuffer filter by name | f n |
| ibuffer-filter-disable | Disable ibuffer filter | f x |
| ibuffer-do-kill-lines | Hide marked buffers | g h |
| ibuffer-update | Restore hidden buffers | g H |
#+begin_src emacs-lisp :tangle ./config.el
(evil-define-key 'normal ibuffer-mode-map
(kbd "f c") 'ibuffer-filter-by-content
(kbd "f d") 'ibuffer-filter-by-directory
(kbd "f f") 'ibuffer-filter-by-filename
(kbd "f m") 'ibuffer-filter-by-mode
(kbd "f n") 'ibuffer-filter-by-name
(kbd "f x") 'ibuffer-filter-disable
(kbd "g h") 'ibuffer-do-kill-lines
(kbd "g H") 'ibuffer-update)
#+end_src
* DASHBOARD
Emacs Dashboard is a startup screen showing you recent files, bookmarks, agenda
items and an Emacs banner.
** Dashboard in Emacsclient
This setting ensures that emacsclient always opens on *dashboard* rather than
*scratch*.
#+begin_src emacs-lisp :tangle ./config.el
(setq doom-fallback-buffer "*dashboard*")
#+end_src
* DIRED
The file manager within Emacs. Below, I setup keybindings for image previews
(peep-dired). Doom Emacs does not use 'SPC d' for any of its keybindings, so
I've chosen the format of 'SPC d' plus 'key'.
** Keybindings To Open Dired
| COMMAND | DESCRIPTION | KEYBINDING |
|------------+------------------------------------+------------|
| dired | /Open dired file manager/ | SPC d d |
| dired-jump | /Jump to current directory in dired/ | SPC d j |
** Basic dired commands
| COMMAND | DESCRIPTION | KEYBINDING |
|------------------------+---------------------------------------------+------------|
| dired-view-file | /View file in dired/ | SPC d v |
| dired-up-directory | /Go up in directory tree/ | h |
| dired-find-file | /Go down in directory tree (or open if file)/ | l |
| dired-next-line | Move down to next line | j |
| dired-previous-line | Move up to previous line | k |
| dired-mark | Mark file at point | m |
| dired-unmark | Unmark file at point | u |
| dired-do-copy | Copy current file or marked files | C |
| dired-do-rename | Rename current file or marked files | R |
| dired-hide-details | Toggle detailed listings on/off | ( |
| dired-git-info-mode | Toggle git information on/off | ) |
| dired-create-directory | Create new empty directory | + |
| dired-diff | Compare file at point with another | = |
| dired-subtree-toggle | Toggle viewing subtree at point | TAB |
** Dired commands using regex
| COMMAND | DESCRIPTION | KEYBINDING |
|-------------------------+----------------------------+------------|
| dired-mark-files-regexp | Mark files using regex | % m |
| dired-do-copy-regexp | Copy files using regex | % C |
| dired-do-rename-regexp | Rename files using regex | % R |
| dired-mark-files-regexp | Mark all files using regex | * % |
** File permissions and ownership
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------+----------------------------------+------------|
| dired-do-chgrp | Change the group of marked files | g G |
| dired-do-chmod | Change the mode of marked files | M |
| dired-do-chown | Change the owner of marked files | O |
| dired-do-rename | Rename file or all marked files | R |
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
(:prefix ("d" . "dired")
:desc "Open dired" "d" #'dired
:desc "Dired jump to current" "j" #'dired-jump)
(:after dired
(:map dired-mode-map
:desc "Peep-dired image previews" "d p" #'peep-dired
:desc "Dired view file" "d v" #'dired-view-file)))
(evil-define-key 'normal dired-mode-map
(kbd "M-RET") 'dired-display-file
(kbd "h") 'dired-up-directory
(kbd "l") 'dired-open-file ; use dired-find-file instead of dired-open.
(kbd "m") 'dired-mark
(kbd "t") 'dired-toggle-marks
(kbd "u") 'dired-unmark
(kbd "C") 'dired-do-copy
(kbd "D") 'dired-do-delete
(kbd "J") 'dired-goto-file
(kbd "M") 'dired-do-chmod
(kbd "O") 'dired-do-chown
(kbd "P") 'dired-do-print
(kbd "R") 'dired-do-rename
(kbd "T") 'dired-do-touch
(kbd "Y") 'dired-copy-filenamecopy-filename-as-kill ; copies filename to kill ring.
(kbd "Z") 'dired-do-compress
(kbd "+") 'dired-create-directory
(kbd "-") 'dired-do-kill-lines
(kbd "% l") 'dired-downcase
(kbd "% m") 'dired-mark-files-regexp
(kbd "% u") 'dired-upcase
(kbd "* %") 'dired-mark-files-regexp
(kbd "* .") 'dired-mark-extension
(kbd "* /") 'dired-mark-directories
(kbd "; d") 'epa-dired-do-decrypt
(kbd "; e") 'epa-dired-do-encrypt)
;; (kbd "Q") 'quick-preview-at-point) ;; previews with sushi
;; Get file icons in dired
;; (add-hook 'dired-mode-hook 'all-the-icons-dired-mode)
;; With dired-open plugin, you can launch external programs for certain extensions
;; For example, I set all .png files to open in 'sxiv' and all .mp4 files to open in 'mpv'
(setq dired-open-extensions '(("gif" . "eog")
("jpg" . "eog")
("png" . "eog")
("mkv" . "celluloid")
("mp4" . "celluloid")))
#+end_src
** Keybindings Within Dired With Peep-Dired-Mode Enabled
With peep-dired, you will get image previews as you go up/down with 'j'
and 'k'
| COMMAND | DESCRIPTION | KEYBINDING |
|----------------------+------------------------------------------+------------|
| peep-dired | /Toggle previews within dired/ | SPC d p |
| peep-dired-next-file | /Move to next file in peep-dired-mode/ | j |
| peep-dired-prev-file | /Move to previous file in peep-dired-mode/ | k |
#+begin_src emacs-lisp :tangle ./config.el
(evil-define-key 'normal peep-dired-mode-map
(kbd "j") 'peep-dired-next-file
(kbd "k") 'peep-dired-prev-file)
(add-hook 'peep-dired-hook 'evil-normalize-keymaps)
#+end_src
** Making deleted files go to trash can
#+begin_src emacs-lisp :tangle ./config.el
(setq delete-by-moving-to-trash t
trash-directory "~/.local/share/Trash/files/")
#+end_src
** Clean up dired buffers while navigating away
#+begin_src emacs-lisp :tangle ./config.el
;; (diredp-toggle-find-file-reuse-dir 1)
(setq dired-kill-when-opening-new-dired-buffer 1)
#+end_src
* DOOM THEME
Setting the theme to doom-one. To try out new themes, I set a keybinding for
counsel-load-theme with 'SPC h t'.
#+begin_src emacs-lisp :tangle ./config.el
(unless (string= my/org-device "galaxy")
(ignore-errors
(require 'base16-stylix-theme)))
(setq doom-theme
(cond
((string= my/org-device "galaxy")
'doom-opera-light)
((featurep 'base16-stylix-theme)
'base16-stylix)
(t 'doom-opera-light)))
#+end_src
* EVALUATE ELISP EXPRESSIONS
Changing some keybindings from their defaults to better fit with Doom Emacs, and
to avoid conflicts with my window managers which sometimes use the control key
in their keybindings. By default, Doom Emacs does not use 'SPC e' for anything,
so I choose to use the format 'SPC e' plus 'key' for these (I also use 'SPC e'
for 'eww' keybindings).
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------+----------------------------------------------+------------|
| eval-buffer | /Evaluate elisp in buffer/ | SPC e b |
| eval-defun | /Evaluate the defun containing or after point/ | SPC e d |
| eval-expression | /Evaluate an elisp expression/ | SPC e e |
| eval-last-sexp | /Evaluate elisp expression before point/ | SPC e l |
| eval-region | /Evaluate elisp in region/ | SPC e r |
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
(:prefix ("e". "evaluate/EWW")
:desc "Evaluate elisp in buffer" "b" #'eval-buffer
:desc "Evaluate defun" "d" #'eval-defun
:desc "Evaluate elisp expression" "e" #'eval-expression
:desc "Evaluate last sexpression" "l" #'eval-last-sexp
:desc "Evaluate elisp in region" "r" #'eval-region))
#+end_src
* EWW
This is the Emacs Web Wowser, the builtin browser in Emacs. Below I set urls to
open in a specific browser (eww) with browse-url-browser-function. By default,
Doom Emacs does not use 'SPC e' for anything, so I choose to use the format 'SPC
e' plus 'key' for these (I also use 'SPC e' for 'eval' keybindings). I chose to
use 'SPC s w' for eww-search-words because Doom Emacs uses 'SPC s' for 'search'
commands.
#+begin_src emacs-lisp :tangle ./config.el
;; (setq browse-url-browser-function 'eww-browse-url)
(map! :leader
:desc "Search web for text between BEG/END"
"s w" #'eww-search-words
(:prefix ("e" . "evaluate/EWW")
:desc "Eww web browser" "w" #'eww
:desc "Eww reload page" "R" #'eww-reload))
#+end_src
* FONTS
Settings related to fonts within Doom Emacs:
+ 'doom-font' -- standard monospace font that defaults for most things in Emacs.
+ 'doom-variable-pitch-font' -- variable font which is useful in some Emacs
plugins.
+ 'doom-big-font' -- used in doom-big-font-mode; useful for presentations.
+ 'font-lock-comment-face' -- for comments.
+ 'font-lock-keyword-face' -- for keywords with special significance like 'setq'
in elisp.
#+begin_src emacs-lisp :tangle ./config.el
(setq doom-unicode-font "Symbola")
(setq doom-font (font-spec :family "ComicShannsMono Nerd Font Mono" :size 18)
doom-variable-pitch-font (font-spec :family "ComicShannsMono Nerd Font Mono" :size 18)
doom-big-font (font-spec :family "ComicShannsMono Nerd Font Mono" :size 22))
(after! doom-themes
(setq doom-themes-enable-bold t
doom-themes-enable-italic t))
(custom-set-faces!
'(bold :weight ultra-bold)
'(font-lock-comment-face :slant italic)
'(font-lock-keyword-face :slant italic))
#+end_src
* FUNCTIONS
These are a collection of functions which I've snipped from multiple sources on
the internet. Some are slightly modified for my preferences, but overall I'm
not good enough with LISP to write my own functions yet.
** Insert date
Some custom functions to insert the date. The function 'insert-todays-date' has
three different formats:
1) Just the keybinding without the universal argument prefix.
2) With one universal argument prefix.
3) With two universal argument prefixes.
The universal argument prefix is 'SPC-u' in Doom Emacs (C-u in standard GNU
Emacs). The function 'insert-any-date' only outputs to one format, which is the
same format as 'insert-todays-date' without a prefix.
| COMMAND | EXAMPLE OUTPUT | KEYBINDING |
|-----------------------+---------------------------+-----------------------|
| dt/insert-todays-date | 2021-11-19 | SPC i d t |
| dt/insert-todays-date | Friday, November 19, 2021 | SPC u SPC i d t |
| dt/insert-any-date | Friday, November 19, 2021 | SPC i d a |
=NOTE= Made by DT
#+begin_src emacs-lisp :tangle ./config.el
(defun my/insert-todays-date (prefix)
"Insert any DATE using the current locale."
(interactive "P")
(let ((format (cond
((not prefix) "%Y-%m-%d")
((equal prefix '(4)) "%A, %B %d, %Y"))))
(insert (format-time-string format))))
(require 'calendar)
(defun my/insert-any-date (date)
"Insert DATE using the current locale."
(interactive (list (calendar-read-date)))
(insert (calendar-date-string date)))
(map! :leader
(:prefix ("i d" . "Insert date")
:desc "Insert any date" "a" #'func/insert-any-date
:desc "Insert todays date" "t" #'func/insert-todays-date))
#+end_src
** Capture a task directly into a specific project
One important thing to point out here is that we're using 'file + head + olp' in
the capture template so that we can drop the new task entry under the "Tasks"
heading.
Works, but I'm unsure how to use it on my workflow, may be safe to delete it
later.
=NOTE= made by SystemCrafters
#+begin_src emacs-lisp :tangle ./config.el
(defun my/org-roam-capture-task ()
"Captures an org-roam task."
(interactive)
;; Capture the new task, creating the project file if necessary
(org-roam-capture-
:node (org-roam-node-read nil)
:templates '(("p" "project" plain "** TODO %?"
:if-new (file+head+olp "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n#+category: ${title}\n#+filetags: Project"
("Tasks"))))))
(global-set-key (kbd "C-c n t") #'my/org-roam-capture-task)
#+end_src
* FLYCHECK
Configurations to add linting support to specific languages by integrating linters with flycheck.
#+begin_src emacs-lisp :tangle ./config.el
(use-package! flycheck
:config
(flycheck-define-checker nix-statix
"A syntax checker for Nix using Statix."
:command ("statix" "check" source)
:error-patterns
((warning line-start (file-name) ":" line ":" column
": " (message) line-end))
:modes (nix-mode))
(add-to-list 'flycheck-checkers 'nix-statix))
#+end_src
* GPT
Integrate most AI agents with emacs on a way that through api keys it can do
more than cursor!
#+begin_src emacs-lisp :tangle ./config.el
(use-package! gptel
:config
(setq! gptel-api-key "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")
(gptel-make-ollama "Ollama"
:host "localhost:11434"
:stream t
:models '(mistral:latest))
)
#+end_src
* HUGO
Capture template for new hugo posts.
#+begin_src emacs-lisp :tangle ./config.el
;; Populates only the EXPORT_FILE_NAME property in the inserted headline.
(with-eval-after-load 'org-capture
(defun org-hugo-new-subtree-post-capture-template ()
"Returns `org-capture' template string for new Hugo post.
See `org-capture-templates' for more information."
(let* ((title (read-from-minibuffer "Post Title: ")) ;Prompt to enter the post title
(fname (org-hugo-slug title)))
(mapconcat #'identity
`(
,(concat "* TODO " title)
":PROPERTIES:"
,(concat ":EXPORT_FILE_NAME: " (format-time-string "%Y-%m-%d-") fname)
":END:"
"%?\n") ;Place the cursor here finally
"\n"))))
;; org capture templates
(setq org-capture-templates
'(
("h" ;`org-capture' binding + h
"Hugo post"
entry
;; It is assumed that below file is present in `org-directory'
;; and that it has a "Blog Ideas" heading. It can even be a
;; symlink pointing to the actual location of all-posts.org!
(file+olp (expand-file-name "posts.org" my/hugo-posts-dir) "blog")
(function org-hugo-new-subtree-post-capture-template))
))
#+end_src
* LINE SETTINGS
I set comment-line to 'SPC TAB TAB' which is a rather comfortable keybinding for
me. The standard Emacs keybinding for comment-line is 'C-x C-;'. The other
keybindings are for commands that toggle on/off line-related settings.
Doom Emacs uses 'SPC t' for "toggle" commands, so I choose 'SPC t' plus 'key'
for those bindings.
| COMMAND | DESCRIPTION | KEYBINDING |
|--------------------------+-------------------------------------------+-------------|
| comment-line | /Toggle commenting lines/ | SPC TAB TAB |
| hl-line-mode | /Toggle line highlighting in current frame/ | SPC t h |
| global-hl-line-mode | /Toggle line highlighting globally/ | SPC t H |
| doom/toggle-line-numbers | /Toggle line numbers/ | SPC t l |
| toggle-truncate-lines | /Toggle truncate lines/ | SPC t t |
#+begin_src emacs-lisp :tangle ./config.el
(setq display-line-numbers-type t)
(map! :leader
:desc "Comment or uncomment lines" "TAB TAB" #'comment-line
(:prefix ("t" . "toggle")
:desc "Toggle line numbers" "l" #'doom/toggle-line-numbers
:desc "Toggle line highlight in frame" "h" #'hl-line-mode
:desc "Toggle line highlight globally" "H" #'global-hl-line-mode
:desc "Toggle truncate lines" "t" #'toggle-truncate-lines))
#+end_src
These are my default line display preferences.
#+begin_src emacs-lisp :tangle ./config.el
(setq display-line-numbers-type `relative)
(global-visual-line-mode t)
#+end_src
* LOAD-MODES
These settings make it so company modes load by default on specific file formats.
#+begin_src emacs-lisp :tangle ./config.el
;; CONFIG
(require 'config-general-mode)
(add-to-list 'auto-mode-alist '("\\.conf$" . config-general-mode))
#+end_src
* MODELINE
The modeline is the bottom status bar that appears in Emacs windows. For more
information on what is available to configure in the Doom modeline, check out:
https://github.com/seagle0128/doom-modeline
#+begin_src emacs-lisp :tangle ./config.el
(setq all-the-icons-scale-factor .8) ;; fixes the issue of rightmost characters not fitting.
(set-face-attribute 'mode-line nil :font "Iosevka Nerd Font-15")
(setq doom-modeline-height 30 ;; sets modeline height
doom-modeline-bar-width 5 ;; sets right bar width
doom-modeline-persp-name t ;; adds perspective name to modeline
doom-modeline-persp-icon t) ;; adds folder icon next to persp name
#+end_src
* MOUSE SUPPORT
Adding mouse support in the terminal version of Emacs.
#+begin_src emacs-lisp :tangle ./config.el
(xterm-mouse-mode 1)
#+end_src
* OPEN SPECIFIC FILES
Keybindings to open files that I work with all the time using the find-file
command, which is the interactive file search that opens with 'C-x C-f' in GNU
Emacs or 'SPC f f' in Doom Emacs. These keybindings use find-file
non-interactively since we specify exactly what file to open. The format I use
for these bindings is 'SPC =' plus 'key' since Doom Emacs does not use 'SPC ='.
| PATH TO FILE | DESCRIPTION | KEYBINDING |
|---------------------+-----------------------+------------|
| ../Agenda.org | /Edit agenda file/ | SPC = a |
| ../doom/config.org | /Edit doom config.org/ | SPC = c |
| ../doom/init.el | /Edit doom init.el/ | SPC = i |
| ../doom/packages.el | /Edit doom packages.el/ | SPC = p |
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
(:prefix ("=" . "open file")
:desc "Edit agenda file" "a"
#'(lambda () (interactive)
(find-file (expand-file-name "20220819130052-agenda.org" my/org-base-dir)))
:desc "Edit doom config.org" "c"
#'(lambda () (interactive)
(find-file (expand-file-name "config.org" my/doom-config-dir)))
:desc "Edit doom init.el" "i"
#'(lambda () (interactive)
(find-file (expand-file-name "init.el" my/doom-config-dir)))
:desc "Edit doom packages.el" "p"
#'(lambda () (interactive)
(find-file (expand-file-name "packages.el" my/doom-config-dir)))))
#+end_src
* LSP
When configured properly lsp-mode's performance is on par with mainstream LSP
clients (e. g. VScode, Theia, etc). Here are steps to achieve optimal results.
#+begin_src emacs-lisp :tangle ./config.el
(setq read-process-output-max (* 1024 1024)) ;; 1mb
(setq lsp-idle-delay 0.500)
(setq lsp-log-io nil) ; if set to true can cause a performance hit
;; c# LSP
(after! lsp-mode
(setq lsp-csharp-server-path "/usr/bin/omnisharp"))
#+end_src
* ORG MODE
I wrapped most of this block in (after! org). Without this, my settings might
evaluate too early, which will result in my settings being overwritten by Doom's
defaults. I have also enabled org-journal, org-superstar and org-roam by adding
(+journal +pretty +roam2) to the org section of my Doom Emacs init.el.
These are my personal settings, heavily modified from DT's original config. For
starters, I made it so org-agenda-files recursively feeds the agenda from .org
files found on my Notes directory, also made it so logs go into drawers, and
lastly customized the TODO keywords based on my personal preferences, these are
settings overwrite the original org settings, and this require to run after org,
so they can build upon the variables defined by the default configuration of
Doom Emacs.
#+begin_src emacs-lisp :tangle ./config.el
(after! org
(setq org-directory my/org-base-dir
org-id-locations-file (expand-file-name ".orgids" my/org-base-dir)
org-attach-id-dir (expand-file-name "images" my/org-base-dir)
org-ellipsis ""
org-superstar-headline-bullets-list '("" "" "" "" "" "" "")
org-superstar-item-bullet-alist '((?+ . ?+) (?- . ?-))
org-log-done 'time
org-log-into-drawer t
org-hide-emphasis-markers t
org-todo-keywords
'((sequence
"TODO(t)" ; A task that needs doing & is ready to do
"PROJ(p)" ; A project, which usually contains other tasks
"ART(a)" ; Similar to PROJ but focused on drawing
"IDEA(i)" ; Misc tasks, usually to elaborate on writing later
"HOLD(h)" ; This task on hold because I'm a lazy fuck
"|"
"DONE(d)" ; Task succesfully completed
"CANCELED(c)") ; Cancelled task
(sequence
"[ ](T)" ; A task that needs doing
"[-](S)" ; A task in progress
"[?](H)" ; A task on hold
"|"
"[X](D)")) ; A task completed
org-todo-keyword-faces
'(("[-]" . +org-todo-active)
("[?]" . +org-todo-onhold)
("HOLD" . +org-todo-onhold)
("ART" . +org-todo-project)
("IDEA" . +org-todo-project)
("PROJ" . +org-todo-project)
("CANCELED" . +org-todo-cancel)))
(require 'org-habit))
#+end_src
** Org Agenda Cache
Reads the `.cache` file in `my/org-base-dir`, which contains only Org files
with active TODOs, and sets `org-agenda-files` accordingly. Falls back to an
empty list if the cache doesnt exist.
#+begin_src emacs-lisp :tangle ./config.el
(defun my/update-org-agenda-cache ()
"Scan Org files and cache those with TODOs, checkboxes, or timestamped entries."
(interactive)
(let* ((org-dir my/org-base-dir)
(files (directory-files-recursively org-dir "\\.org$"))
(todo-files '())
(regex (concat
;; Standard TODO entries and checkboxes
"\\*+[ \t]+\\(TODO\\|PROJ\\|ART\\|IDEA\\|HOLD\\|\\[ \\]\\|\\[-\\]\\|\\[\\?\\]\\)"
;; Org scheduling keywords
"\\|SCHEDULED:"
"\\|DEADLINE:"
;; Standalone timestamps like <2025-04-14 Mon>
"\\|<[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}")))
(dolist (file files)
(message "Scanning: %s" file)
(condition-case err
(with-temp-buffer
(insert-file-contents file)
(goto-char (point-min))
(when (re-search-forward regex nil t)
(message " -> Relevant entry found in: %s" file)
(push file todo-files)))
(error (message "Error reading %s: %s" file err))))
(setq todo-files (delete-dups (append todo-files my/org-always-included-agenda-files)))
(if todo-files
(progn
(message "Writing %d agenda-relevant files to cache" (length todo-files))
(with-temp-file my/org-agenda-cache-file
(prin1 todo-files (current-buffer))))
(message "No relevant entries found — not writing cache."))))
(defun my/load-org-agenda-from-cache ()
"Load org-agenda-files from the cache, or fallback to an empty list."
(interactive)
(let ((file my/org-agenda-cache-file))
(if (and (file-readable-p file)
(> (nth 7 (file-attributes file)) 0)) ; file size > 0
(with-temp-buffer
(insert-file-contents file)
(read (current-buffer)))
(message "Agenda cache not found or empty")
nil)))
(after! org
(setq org-agenda-files (my/load-org-agenda-from-cache)))
#+end_src
** Set font sizes for each header level in Org
You can set the Org heading levels to be different font sizes. So I choose to
have level 1 headings to be 140% in height, level 2 to be 130%, etc. Other
interesting things you could play with include adding :foreground color and/or
:background color if you want to override the theme colors.
#+begin_src emacs-lisp :tangle ./config.el
(custom-set-faces
'(org-level-1 ((t (:inherit outline-1 :height 1.4))))
'(org-level-2 ((t (:inherit outline-2 :height 1.3))))
'(org-level-3 ((t (:inherit outline-3 :height 1.2))))
'(org-level-4 ((t (:inherit outline-4 :height 1.1))))
'(org-level-5 ((t (:inherit outline-5 :height 1.0))))
'(org-document-title ((t (:inherit outline-1 :height 2.0))))
)
#+end_src
** Org-Alert
This change makes it so org-alert uses libnotify for system notifications as
well as update intervals.
#+begin_src emacs-lisp :tangle ./config.el
(use-package org-alert
:ensure t)
(setq alert-default-style 'libnotify
org-alert-interval 3600)
;; Auto start org-alert when emacs/daemon load
(org-alert-enable)
#+end_src
** Org-Auto-Tangle
Allows you to add the option #+auto_tangle: t in your Org file so that it
automatically tangles when you save the document.
#+begin_src emacs-lisp :tangle ./config.el
(use-package org-auto-tangle
:defer t
:hook (org-mode . org-auto-tangle-mode))
#+end_src
** Org-Babel
Load language support on org-babel, also map out org-babel-tangle.
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
:desc "Org babel tangle" "m B" #'org-babel-tangle)
;; (org-babel-do-load-languages
;; 'org-babel-load-languages
;; '((R . t)
;; (emacs-lisp . t)
;; (nix . t)))
#+end_src
** Org-Inlinetask
Enables a feature native to org-mode, which after a set amount of indentations,
turns the header into a task, allowing for the insertion of TODO or PROJ without
disrupting the indentation of the lines below it.
https://github.com/amluto/org-mode/blob/master/lisp/org-inlinetask.el
#+begin_src emacs-lisp :tangle ./config.el
(require 'org-inlinetask)
(setq org-inlinetask-min-level 9)
#+end_src
** Org-Roam
First lines set up some preferences through variables, then it sets up some
templates for dailies, and nodes.
| COMMAND | DESCRIPTION | KEYBINDING |
|---------------------------------+---------------------------------+-------------|
| org-roam-find-file | org roam find file | SPC n r f |
| org-roam-insert | org roam insert | SPC n r i |
| org-roam-dailies-find-date | org roam dailies find date | SPC n r d d |
| org-roam-dailies-find-today | org roam dailies find today | SPC n r d t |
| org-roam-dailies-find-tomorrow | org roam dailies find tomorrow | SPC n r d m |
| org-roam-dailies-find-yesterday | org roam dailies find yesterday | SPC n r d y |
#+begin_src emacs-lisp :tangle ./config.el
(setq! deft-directory my/org-base-dir)
(use-package org-roam
:ensure t
:custom
(org-roam-completion-everywhere t)
(org-roam-dailies-capture-templates
'(("d" "default" entry "* %<%I:%M %p>: %?"
:if-new (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n"))))
(org-roam-capture-templates
`(("d" "default" plain
"%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
:unnarrowed t)
("l" "programming language" plain
(file ,(expand-file-name "programming.org" my/doom-template-dir))
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+filetags: :programming:language:${title}:\n#+title: ${title}")
:unnarrowed t)
("e" "political events" plain
(file ,(expand-file-name "events.org" my/doom-template-dir))
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+filetags: :politics:conflicts:\n#+title: ${title}")
:unnarrowed t)
("p" "project" plain
"* PROJ ${title}\n%?\n* Tasks"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+category: ${title}\n#+filetags: :project:\n#+title: ${title}")
:unnarrowed t)))
:bind()
:bind-keymap()
:config
(org-roam-db-autosync-mode))
(setq completion-ignore-case t)
(set-file-template!
(concat (regexp-quote my/org-base-dir) ".+\\.org$")
'org-mode
:ignore t)
#+end_src
** Org-Roam-UI
This is a superior (and interactive) visualizer for org-roam graph.
https://github.com/org-roam/org-roam-ui
#+begin_src emacs-lisp :tangle ./config.el
(use-package! websocket
:after org-roam)
(use-package! org-roam-ui
:after org-roam ;; or :after org
;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; a hookable mode anymore, you're advised to pick something yourself
;; if you don't care about startup time, use
;; :hook (after-init . org-roam-ui-mode)
:config
(setq org-roam-ui-sync-theme t
org-roam-ui-follow t
org-roam-ui-update-on-save t
org-roam-ui-open-on-start nil))
#+end_src
** Org-Transclusion
Org-transclusion lets you insert a copy of text content via a file link or ID
link within an Org file. It lets you have the same content present in different
buffers at the same time without copy-and-pasting it. Edit the source of the
content, and you can refresh the transcluded copies to the up-to-date state.
Org-transclusion keeps your files clear of the transcluded copies, leaving only
the links to the original content.
https://github.com/nobiot/org-transclusion
| COMMAND | DESCRIPTION | KEYBINDING |
|-----------------------+--------------------------+------------|
| org-transclusion-add | Add transclusion block | SPC n r a |
| org-transclusion-mode | Toggle transclusion mode | SPC n r t |
#+begin_src emacs-lisp :tangle ./config.el
(use-package! org-transclusion
:after org
:init
(map!
:map global-map "<f12>" #'org-transclusion-add
:leader
(:prefix ("n r" . "toggle")
:desc "Org Transclussion Add" "a" #'org-transclusion-add
:desc "Org Transclusion Mode" "t" #'org-transclusion-mode)))
#+end_src
* OTHER SETTINGS
These are some personal settings, and other fixes.
** Disable persistent undo
Uses [[doom-package:][undo-tree]] instead of [[doom-package:][undo-fu]], which is a little less stable, but offers
branching undo history and a visualizer for navigating it.
+ If using +tree
#+begin_src emacs-lisp :tangle ./config.el
(after! undo-tree
(setq undo-tree-auto-save-history nil))
#+end_src
+ Else
#+begin_src emacs-lisp :tangle ./config.el
;; (remove-hook 'undo-fu-mode-hook #'global-undo-fu-session-mode)
#+end_src
** User information
#+begin_src emacs-lisp :tangle ./config.el
(setq user-full-name "Danilo Reyes"
user-mail-address "CaptainJawZ@outlook.com")
#+end_src
** Variables
Custom set variables, I should note that I don't know what git commit major mode
does, but flycheck is for minimizing errors on python scripts for long line
length.
#+begin_src emacs-lisp :tangle ./config.el
(custom-set-variables
'(flycheck-flake8-maximum-line-length 88)
'(safe-local-variable-values '((git-commit-major-mode . git-commit-elisp-text-mode))))
#+end_src
* REGISTERS
Emacs registers are compartments where you can save text, rectangles and
positions for later use. Once you save text or a rectangle in a register, you
can copy it into the buffer once or multiple times; once you save a position in
a register, you can jump back to that position once or multiple times. The
default GNU Emacs keybindings for these commands (with the exception of
counsel-register) involves 'C-x r' followed by one or more other keys. I wanted
to make this a little more user friendly, and since I am using Doom Emacs, I
choose to replace the 'C-x r' part of the key chords with 'SPC r'.
| COMMAND | DESCRIPTION | KEYBINDING |
|----------------------------------+----------------------------------+------------|
| copy-to-register | /Copy to register/ | SPC r c |
| frameset-to-register | /Frameset to register/ | SPC r f |
| insert-register | /Insert contents of register/ | SPC r i |
| jump-to-register | /Jump to register/ | SPC r j |
| list-registers | /List registers/ | SPC r l |
| number-to-register | /Number to register/ | SPC r n |
| counsel-register | /Interactively choose a register/ | SPC r r |
| view-register | /View a register/ | SPC r v |
| window-configuration-to-register | /Window configuration to register/ | SPC r w |
| increment-register | /Increment register/ | SPC r + |
| point-to-register | /Point to register/ | SPC r SPC |
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
(:prefix ("r" . "registers")
:desc "Copy to register" "c" #'copy-to-register
:desc "Frameset to register" "f" #'frameset-to-register
:desc "Insert contents of register" "i" #'insert-register
:desc "Jump to register" "j" #'jump-to-register
:desc "List registers" "l" #'list-registers
:desc "Number to register" "n" #'number-to-register
:desc "Interactively choose a register" "r" #'counsel-register
:desc "View a register" "v" #'view-register
:desc "Window configuration to register" "w" #'window-configuration-to-register
:desc "Increment register" "+" #'increment-register
:desc "Point to register" "SPC" #'point-to-register))
#+end_src
* SPELL
This will disable Proselint from running inside code blocks.
#+begin_src emacs-lisp :tangle ./config.el
(defadvice! fixed-flycheck-proselint-parse-errors-a (output checker buffer)
:override #'flycheck-proselint-parse-errors
(delq
nil (mapcar (lambda (err)
(let-alist err
(and (or (not (derived-mode-p 'org-mode))
(save-excursion (goto-char .start)
(not (org-in-src-block-p))))
(flycheck-error-new-at-pos
.start
(pcase .severity
(`"suggestion" 'info)
(`"warning" 'warning)
(`"error" 'error)
(_ 'error))
.message
:id .check
:buffer buffer
:checker checker
:end-pos .end))))
(let-alist (car (flycheck-parse-json output))
.data.errors))))
#+end_src
* SPLITS
I set splits to default to opening on the right using 'prefer-horizontal-split'.
I set a keybinding for 'clone-indirect-buffer-other-window' for when I want to
have the same document in two splits. The text of the indirect buffer is always
identical to the text of its base buffer; changes made by editing either one are
visible immediately in the other. But in all other respects, the indirect
buffer and its base buffer are separate. For example, I can fold one split but
the other remains unfolded.
#+begin_src emacs-lisp :tangle ./config.el
(defun prefer-horizontal-split ()
(set-variable 'split-height-threshold nil t)
(set-variable 'split-width-threshold 40 t)) ; make this as low as needed
(add-hook 'markdown-mode-hook 'prefer-horizontal-split)
(map! :leader
:desc "Clone indirect buffer other window" "b c" #'clone-indirect-buffer-other-window)
#+end_src
* TWITTER
Needed to login on twitter.
#+begin_src emacs-lisp :tangle ./config.el
(setq twittering-allow-insecure-server-cert t)
#+end_src
* WINNER MODE
Winner mode comes with GNU Emacs since version 20. This is a global minor mode
and, when activated, it allows you to “undo” (and “redo”) changes in the window
configuration with the key commands 'SCP w <left>' and 'SPC w <right>'.
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
(:prefix ("w" . "window")
:desc "Winner redo" "<right>" #'winner-redo
:desc "Winner undo" "<left>" #'winner-undo))
#+end_src
* ZAP TO CHAR
Emacs provides a 'zap-to-char' command that kills from the current point to a
character. It bounds to 'M-z' in standard GNU Emacs but since Doom Emacs uses
'SPC' as its leader key and does not have 'SPC z' binding to anything, it just
makes since to use it for 'zap-to-char'. Note that 'zap-to-char' can combine
with the universal argument 'SPC u' to modify its behaviour. Examples of
'zap-to-char' usage listed in the table below:
| KEYBINDING | WHAT IS DOES |
|---------------------------+------------------------------------------------------------|
| SPC z e | deletes all chars to the next occurrence of 'e' |
| SPC u 2 SPC z e | deletes all chars to the second occurrence of 'e' |
| SPC u - SPC z e | deletes all chars to the previous occurrence of 'e' |
| SPC u -2 SPC z e | deletes all chars to the fourth previous occurrence of 'e' |
| SPC u 1 0 0 SPC u SPC z e | deletes all chars to the 100th occurrence of 'e' |
=TIP= The universal argument (SPC u) can only take a single integer by default.
If you need to use a multi-digit number (like 100 in the last example in the
table above), then you must terminate the universal argument with another 'SPC
u' after typing the number.
'zap-up-to-char' is an alternative command that does not zap the char specified.
It bounds to 'SPC Z'. It can also combine in conjunction with the universal
argument 'SPC u' in similar fashion to the 'zap-to-char' examples above.
=NOTE= Vim (evil mode) has similar functionality builtin. You can delete to
the next occurrence of 'e' by using 'dte' in normal. To delete to the next
occurrence of 'e' including the 'e', then you would use 'dfe'. And you can
modify 'dt' and 'df' by prefixing them with numbers, so '2dte' would delete to
the second occurrence of 'e'.
#+begin_src emacs-lisp :tangle ./config.el
(map! :leader
:desc "Zap to char" "z" #'zap-to-char
:desc "Zap up to char" "Z" #'zap-up-to-char)
#+end_src
# LocalWords: ibuffer dired elisp setq modeline libnotify Proselint DT's SPC
# LocalWords: GitLab unmark emacsclient modeline Goto emms eww Transclusion
# LocalWords: Inlinetask LSP