- Todos
- Packages
- Header
- Imports
- Functions
- Initial Setup
- Basic Functionality
- Basic UI Configuration
- Setup Indent
- Org Mode
- Plugin Config
- Tramp
- Key Bindings
- Additional Information
Figure out how to make LSP ignore certain files so that it can watch a reasonable amount of files without asking or slowing things down
Add package!
directives here, and packages.el
will be generated from this section.
;; -*- no-byte-compile: t; -*-
;;; $DOOMDIR/packages.el
;;; NOTE: `packages.el' is now generated from `config.org'. Please edit that file
;;; in Emacs and `packages.el' will be generated automatically!
;; To install a package with Doom you must declare them here and run 'doom sync'
;; on the command line, then restart Emacs for the changes to take effect -- or
;; use 'M-x doom/reload'.
;; To install SOME-PACKAGE from MELPA, ELPA or emacsmirror:
;(package! some-package)
(package! polymode)
;; (package! terraform-mode)
;; (package! zig-mode)
Write the header and warning to edit this file instead of config.el directly.
;;; $DOOMDIR/config.el -*- lexical-binding: t; -*-
;;; NOTE: `config.el' is now generated from `config.org'. Please edit that file
;;; in Emacs and `config.el' will be generated automatically!
You will need to fill out a secret.el
file for this config to work.
If you’d like an example secret.el
file to fill out, put your cursor on this code block and press C-c C-c
to run it.
if [ ! -f secret.el ]; then
cp secret.example.el secret.el
echo "copied to secret.el, now go fill out the file"
else
echo "secret.el already exists, make sure it has the correct information"
fi
(load! "secret.el")
Helpful stuff
(defun me/random-choice (items)
"Returns a random item from a given list"
(let* ((size (length items))
(index (random size)))
(nth index items)))
(defun me/read-lines (file)
"Reads a file, filters out lines starting with #, and returns the lines as a list"
(let* ((file-contents (with-temp-buffer
(insert-file-contents file)
(buffer-substring-no-properties
(point-min)
(point-max))))
(lines (split-string file-contents "\n" t)))
(seq-remove (lambda (line) (string-match-p "^#" line)) lines)))
(defun me/random-line-from-file (file)
"Reads a file and returns a random line"
(me/random-choice (me/read-lines file)))
Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates and snippets.
(setq user-full-name me/full-name
user-mail-address me/mail-address)
Sets a random banner on startup - this will select a random banner from banners/*.png
(let* ((banner-directory (substitute-in-file-name "$HOME/.config/doom/resources/banners"))
(command (concat "\\ls -A1d " banner-directory "/*.png"))
(output (shell-command-to-string command))
(banners (split-string output "\n" t))
(banner (me/random-choice banners)))
(setq fancy-splash-image banner))
(setq +doom-quit-messages
(me/read-lines
(substitute-in-file-name
"$HOME/.config/doom/resources/messages.txt")))
Load custom shell env
(doom-load-envvars-file "~/.emacs.d/.local/doom_only_env")
Set some sensible defaults
(setq-default delete-by-moving-to-trash t
window-combination-resize t
x-stretch-cursor t)
(setq undo-limit (* 80 1024 1024)
evil-want-fine-undo t
auto-save-default t
truncate-string-ellipsis "…")
Don’t move cursor back when exiting insert mode
(setq evil-move-cursor-back nil)
Turn on Auto Revert Mode globally. This will automatically refresh the buffer when the file changes on disk (either through externaledits or something like a git branch change). The buffer will NOT revert if you have unsaved changes.
(global-auto-revert-mode t)
Setup some basic UI defaults
- Start with a set size and position
- No minimal window chrome
- Relative line numbers
- No frame title text
(add-to-list 'initial-frame-alist '(width . (text-pixels . 1180)))
(add-to-list 'initial-frame-alist '(height . (text-pixels . 780)))
(add-to-list 'initial-frame-alist '(top . 50))
(add-to-list 'initial-frame-alist '(left . 45))
(after! doom-ui
(scroll-bar-mode -1)
(tool-bar-mode -1)
(tooltip-mode -1))
(setq display-line-numbers-type 'relative)
(setq-default frame-title-format '(""))
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.
Download fonts
- FiraCode Nerd Font (This is my own version, but you can also get it from nerdfonts.com)
- Overpass (It’s pretty ¯\_(ツ)_/¯)
- Myriad Pro (A proprietary Adobe font from a shady Turkish website, what could go wrong?)
- SF Pro (Default system font in macOS that for some reason you have to download to use yourself)
- Bookerly (Amazon’s latest and best eBook/Kindle font)
(setq me/fixed-width-font '(:family "ComicCode Nerd Font" :style "Medium")
me/variable-pitch-font '(:family "Overpass" :style "Regular")
me/variable-pitch-serif-font '(:family "Bookerly" :style "Regular"))
(setq me/org-font-family (plist-get me/variable-pitch-font :family)
me/ebook-font-family (plist-get me/variable-pitch-serif-font :family))
(setq doom-emoji-fallback-font-families nil)
(setq doom-symbol-fallback-font-families nil)
(setq doom-font
(font-spec :family (plist-get me/fixed-width-font :family)
:style (plist-get me/fixed-width-font :style)
:size 14)
doom-big-font
(font-spec :family (plist-get me/fixed-width-font :family)
:style (plist-get me/fixed-width-font :style)
:size 20)
doom-variable-pitch-font
(font-spec :family (plist-get me/variable-pitch-font :family)
:style (plist-get me/variable-pitch-font :style)
:size 16))
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.
Some good themes:
- doom-one (default)
- doom-nord
- doom-palenight
(setq doom-theme 'doom-palenight)
Based on this.
(defun me/setup-indent (n)
;; java/c/c++
(setq-local c-basic-offset n)
;; shell
(setq-local sh-set-indent n)
;; web development
(setq-local coffee-tab-width n) ; coffeescript
(setq-local javascript-indent-level n) ; javascript-mode
(setq-local js-indent-level n) ; js-mode
(setq-local js2-basic-offset n) ; js2-mode, in latest js2-mode, it's alias of js-indent-level
(setq-local web-mode-markup-indent-offset n) ; web-mode, html tag in html file
(setq-local web-mode-css-indent-offset n) ; web-mode, css in html file
(setq-local web-mode-code-indent-offset n) ; web-mode, js code in html file
(setq-local css-indent-offset n) ; css-mode
)
(defun me/office-code-style ()
(interactive)
(message "Office code style!")
;; use tab instead of space
(setq-local indent-tabs-mode t)
;; indent 4 spaces width
(me/setup-indent 4))
(defun me/personal-code-style ()
(interactive)
(message "My personal code style!")
;; use space instead of tab
(setq indent-tabs-mode nil)
;; indent 2 spaces width
(me/setup-indent 2))
(defun me/setup-develop-environment ()
(interactive)
(me/personal-code-style))
;; How to do this dynamically based on project name:
;; (defun me/setup-develop-environment ()
;; (interactive)
;; (let ((proj-dir (file-name-directory (buffer-file-name))))
;; ;; if hobby project path contains string "hobby-proj1"
;; (if (string-match-p "hobby-proj1" proj-dir)
;; (me/personal-code-style))
;; ;; if commericial project path contains string "commerical-proj"
;; (if (string-match-p "commerical-proj" proj-dir)
;; (me/office-code-style))))
;; prog-mode-hook requires emacs24+
(add-hook 'prog-mode-hook 'me/setup-develop-environment)
;; a few major-modes does NOT inherited from prog-mode
(add-hook 'web-mode-hook 'me/setup-develop-environment)
- Set faces for heading levels
- Ensure that anything that should be fixed-pitch in Org files appears that way
(defun me/org-font-setup ()
(dolist (face '((:name org-level-1 :weight bold :height 1.3)
(:name org-level-2 :weight bold :height 1.2)
(:name org-level-3 :weight bold :height 1.1)
(:name org-level-4 :weight normal :height 1.1)
(:name org-level-5 :weight normal :height 1.1)
(:name org-level-6 :weight normal :height 1.1)
(:name org-level-7 :weight normal :height 1.1)
(:name org-level-8 :weight normal :height 1.1)))
(set-face-attribute (plist-get face :name) nil
:family me/org-font-family
:weight (plist-get face :weight)
:height (plist-get face :height))))
- set org directory and agenda files
- add timestamp when finished
- add some org templates (try
<el
TAB
in insert mode) - indent text according to outline structure
- use variable pitch fonts in org mode
- better text wrapping
- setup fonts
- no line numbers
- habit support (syncs with FlatHabit)
(require 'org-tempo)
(require 'org-habit)
(after! org
(setq
org-ellipsis " ▾"
org-directory "~/projects/org/"
org-agenda-files '("~/projects/org/agenda.org"
"~/projects/org/todo.org"
"~/Documents/FlatHabits/MyHabits.org")
org-log-done 'time)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("sh" . "src sh"))
(add-to-list 'org-structure-template-alist '("iex" . "src elixir"))
(variable-pitch-mode 1)
(me/org-font-setup))
(add-hook 'org-mode-hook (lambda ()
(visual-fill-column-mode 1)
(setq-local visual-fill-column-center-text t
visual-fill-column-width 100)
(org-indent-mode 1)
(visual-line-mode 1)
(display-line-numbers-mode 0)))
List the files here that you want to auto-tangle on save
(defun me/org-babel-tangle-config ()
(when (member (buffer-file-name)
(list (expand-file-name "~/.config/doom/config.org")
(expand-file-name "~/.config/doom/install.org")))
(let ((org-confirm-babel-evaluate nil))
(org-babel-tangle))))
(add-hook 'org-mode-hook (lambda () (add-hook 'after-save-hook #'me/org-babel-tangle-config)))
- show mode icons
- make the modeline slightly taller
- show the project name in the modeline
(after! doom-modeline
(setq
doom-modeline-major-mode-icon t
doom-modeline-height 35
doom-modeline-persp-name t))
Display the current time in the modeline (without date or load average)
(setq display-time-day-and-date nil
display-time-default-load-average nil)
(display-time-mode 1)
If there is a battery, as in, on a laptop, then display it in the modeline
(if (equal "Battery status not available"
(battery))
(display-battery-mode 0)
(display-battery-mode 1))
LF UTF-8 is the default file encoding, and thus not worth noting in the modeline. So, let’s conditionally hide it and only show the encoding when it’s different
(defun me/doom-modeline-conditional-buffer-encoding ()
"We expect the encoding to be LF UTF-8, so only show the modeline when this is not the case"
(setq-local doom-modeline-buffer-encoding
(unless (and (memq (plist-get (coding-system-plist buffer-file-coding-system) :category)
'(coding-category-undecided coding-category-utf-8))
(not (memq (coding-system-eol-type buffer-file-coding-system) '(1 2))))
t)))
(add-hook 'after-change-major-mode-hook #'me/doom-modeline-conditional-buffer-encoding)
I don’t use evil-escape-mode, so I may as well turn it off, I’ve heard it contributes a typing delay. I’m not sure it’s much, but it is an extra pre-command-hook that I don’t benefit from, so…
(after! evil-escape (evil-escape-mode -1))
(setq magit-revision-show-gravatars '("^Author: " . "^Commit: "))
;; (use-package! org-roam
;; :defer t
;; :init
;; (setq org-roam-directory "~/Documents/OrgRoam")
;; (setq +org-roam-open-buffer-on-find-file nil))
Turns off proselint because it complains when I cuss and we can’t have that
(setq-default flycheck-disabled-checkers '(proselint))
Set Treemacs visual config and theme
(setq
treemacs-width 30
treemacs-follow-mode t
treemacs-position 'left
doom-themes-treemacs-theme "doom-colors")
Set Centaur tabs visuals and font
Also, make most tabs group by project not by org or elisp modes
Sets up tab grouping by:
- *star buffers and magit buffers
- EShell buffers
- Dired buffers
- Everything else is grouped by project
(after! centaur-tabs
(centaur-tabs-group-by-projectile-project)
(setq
centaur-tabs-style "bar"
centaur-tabs-set-bar 'none
centaur-tabs-bar-height 30
centaur-tabs-height 28)
(centaur-tabs-change-fonts (plist-get me/fixed-width-font :family) 130)
(defun centaur-tabs-hide-tab (x)
"Do no to show buffer X in tabs."
(let ((name (format "%s" x)))
(or
;; Current window is not dedicated window.
(window-dedicated-p (selected-window))
;; Buffer name not match below blacklist.
(string-suffix-p "ex[web]" name)
(string-prefix-p "*epc" name)
(string-prefix-p "*helm" name)
(string-prefix-p "*Helm" name)
(string-prefix-p "*Compile-Log*" name)
(string-prefix-p "*lsp" name)
(string-prefix-p "*company" name)
(string-prefix-p "*Flycheck" name)
(string-prefix-p "*tramp" name)
(string-prefix-p " *Mini" name)
(string-prefix-p "*help" name)
(string-prefix-p "*straight" name)
(string-prefix-p " *temp" name)
(string-prefix-p "*Help" name)
(string-prefix-p "*mybuf" name)
;; Is not magit buffer.
(and (string-prefix-p "magit" name)
(not (file-name-extension name)))
)))
)
Set projectile ignored projects Set Projectile project search path
Refresh projects with M-x projectile-discover-projects-in-search-path
.
(after! projectile
(add-hook 'projectile-after-switch-project-hook (lambda ()
(if (s-suffix? "printserver/" (projectile-project-root))
(setq-local lsp-elixir-project-dir "printserver/packages/ex_printserver/"))))
(setq projectile-ignored-projects '("~/" "/tmp/" "~/.emacs.d/" "/opt/homebrew/"))
(setq projectile-project-search-path '("~/projects/" "~/campaigns/")))
Disable evil-snipe mode so that S
and s
work as they do in vim
(remove-hook 'doom-first-input-hook #'evil-snipe-mode)
This takes emacs from freezing up when opening a PDF to rendering it smoothly on a HiDPI screen
(use-package! pdf-tools
:defer t
:config
(pdf-loader-install)
(setq pdf-view-use-scaling t
pdf-view-use-imagemagick nil))
Do not watch files because it’s annoying when it asks every time
(setq lsp-enable-file-watchers nil)
(setq company-idle-delay 0.5)
;; experimental treesitter setup
;; (use-package
;; emacs
;; :ensure nil
;; :custom
;; ;; Should use:
;; ;; (mapc #'treesit-install-language-grammar (mapcar #'car treesit-language-source-alist)) ;
;; ;; at least once per installation or while changing this list
;; (treesit-language-source-alist
;; '((heex "https://github.com/phoenixframework/tree-sitter-heex")
;; (elixir "https://github.com/elixir-lang/tree-sitter-elixir")))
;; (major-mode-remap-alist
;; '((elixir-mode . elixir-ts-mode)))
;; )
;; (use-package
;; eglot
;; :ensure nil
;; :config (add-to-list 'eglot-server-programs '(elixir-ts-mode "language_server.sh")))
;; (use-package
;; eglot
;; :ensure nil
;; :config
;; (add-to-list
;; 'eglot-server-programs `((elixir-ts-mode heex-ts-mode elixir-mode) . ("language_server.sh"))))
;; (use-package
;; elixir-ts-mode
;; :hook (elixir-ts-mode . eglot-ensure)
;; (elixir-ts-mode
;; .
;; (lambda ()
;; (push '(">=" . ?\u2265) prettify-symbols-alist)
;; (push '("<=" . ?\u2264) prettify-symbols-alist)
;; (push '("!=" . ?\u2260) prettify-symbols-alist)
;; (push '("==" . ?\u2A75) prettify-symbols-alist)
;; (push '("=~" . ?\u2245) prettify-symbols-alist)
;; (push '("<-" . ?\u2190) prettify-symbols-alist)
;; (push '("->" . ?\u2192) prettify-symbols-alist)
;; (push '("<-" . ?\u2190) prettify-symbols-alist)
;; (push '("|>" . ?\u25B7) prettify-symbols-alist)))
;; (before-save . eglot-format))
Create a buffer-local hook to run elixir-format on save, only when we enable elixir-mode.
;; bind alchemist keys
;; (setq alchemist-key-command-prefix (kbd doom-localleader-key))
(map! :after elixir-mode
:map elixir-mode-map
:localleader
:n "f" #'elixir-format)
;; Enable format and iex reload on save
(add-hook 'elixir-mode-hook
(lambda ()
(add-hook 'before-save-hook 'elixir-format nil t)
;;(add-hook 'after-save-hook 'alchemist-iex-reload-module)
))
This sets up support for webmode inside of ~H
Liveview eex sigils in Elixir files as well as support for .heex
template files
(use-package! polymode
:defer t
:mode ("\.ex$" . poly-elixir-web-mode)
:config
(define-hostmode poly-elixir-hostmode :mode 'elixir-mode)
(define-innermode poly-liveview-expr-elixir-innermode
:mode 'web-mode
:head-matcher (rx line-start (* space) "~H" (= 3 (char "\"'")) line-end)
:tail-matcher (rx line-start (* space) (= 3 (char "\"'")) line-end)
:head-mode 'host
:tail-mode 'host
:allow-nested nil
:keep-in-mode 'host
:fallback-mode 'host)
(define-polymode poly-elixir-web-mode
:hostmode 'poly-elixir-hostmode
:innermodes '(poly-liveview-expr-elixir-innermode)))
(after! web-mode
(dolist (tuple '(("elixir" . "\\.ex\\'")
("elixir" . "\\.eex\\'")
("elixir" . "\\.heex\\'")))
(add-to-list 'web-mode-engines-alist tuple)))
To use Tramp to edit files on remote servers, just use find-file
(SPC .
) and type something like /ssh:user@server:file/or/directory
or /ssh:server:
.
Tramp needs to recognize the prompt on the remote server to work correctly.
Below I set tramp’s terminal type to tramp
so that I can use that in my remote configs.
If you have customized your prompt on the remote server, make sure that you add something like the following early on in the shell startup process. (I put it at the top of my .bashrc
)
# remote: ~/.bashrc
# bail out before setting custom prompt (or anything else that tramp doesn't need)
[ "$TERM" = "tramp" ] && return
# or at the very least
if [ "$TERM" = "tramp" ]; then
export PS1='$ '
else
# load custom prompt here
fi
(setq tramp-default-method "ssh")
(setq tramp-terminal-type "tramp")
(map! :desc "Open Dired here" :n "-" #'dired-jump)
(map! :desc "Next Tab" :g "s-}" #'centaur-tabs-forward)
(map! :desc "Previous Tab" :g "s-{" #'centaur-tabs-backward)
(map! :desc "Decrease current window width" :g "s-[" #'evil-window-decrease-width)
(map! :desc "Increase current window width" :g "s-]" #'evil-window-increase-width)
Here are some additional functions/macros that could help you configure Doom:
load!
for loading external *.el files relative to this oneuse-package!
for configuring packagesafter!
for running code after a package has loadedadd-load-path!
for adding directories to theload-path
, relative to this file. Emacs searches theload-path
when you load packages withrequire
oruse-package
.map!
for binding new keys
To get information about any of these functions/macros, move the cursor over
the highlighted symbol at press k
(non-evil users must press C-c c k
).
This will open documentation for it, including demos of how they are used.
You can also try gd
(or C-c c d
) to jump to their definition and see how
they are implemented.