Emacs
Preparation
Packages
First, let's set the package archives and do some basic tweaks.
(require 'package) (setq package-enable-at-startup nil) ;; Speed tip taken from Doom Emacs (setq package-archives '(("ELPA" . "https://tromey.com/elpa/") ("gnu" . "https://elpa.gnu.org/packages/") ("melpa" . "https://melpa.org/packages/"))) ;; General convieniences, somewhat questionable (setq url-http-attempt-keepalives nil) (setq package-check-signature nil)
We're going to use straight.el
for package management.
(defvar bootstrap-version) (let ((bootstrap-file (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) (bootstrap-version 5)) (unless (file-exists-p bootstrap-file) (with-current-buffer (url-retrieve-synchronously "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" 'silent 'inhibit-cookies) (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage))
This configuration also uses use-package
for package management: it's a macro that'll make things much easier. All that's generally important to understand right now is that code after the :init
keyword argument will be evaluated as the package is initialized, code after the :config
keyword arg is lazy-loaded, the :bind
and :hook
keyword arguments are for specifying package-related keybinds and hooks respectively.
(straight-use-package 'use-package) (eval-when-compile (require 'use-package)) ;; Automatically install all packages with straight.el if not present. (setq straight-use-package-by-default t) ;; Always lazy-load if doable. TODO Properly look into good defer setup (setq use-package-always-defer t) (use-package general) (use-package projectile) (use-package s)
Are we using native-comp? If so, let's actually native compile things. Also, the popup compilation warnings are really bothersome, so turn those off, too.
(setq package-native-compile t) (setq comp-deferred-compilation t) (setq native-comp-deferred-compilation-deny-list nil) (setq warning-suppress-log-types '((comp)))
Speed
Let's then use a variety of the tips for speeding up initialization time given by the creator of Doom Emacs.
;; Go back to normal GC behavior after init (add-hook 'emacs-startup-hook (lambda () (setq gc-cons-threshold 16777216 ; 16mb gc-cons-percentage 0.1))) ;; Don't do GC when the minibuffer is being used (lag during minibuffer usage is frustrating) (defun doom-defer-garbage-collection-h () "Disable garbage collection." (setq gc-cons-threshold most-positive-fixnum)) (defun doom-restore-garbage-collection-h () "Restore garbage collection." (run-at-time 1 nil (lambda () (setq gc-cons-threshold 16777216)))) (add-hook 'minibuffer-setup-hook #'doom-defer-garbage-collection-h) (add-hook 'minibuffer-exit-hook #'doom-restore-garbage-collection-h) ;; GCMH (literally Garbage Collector Magic Hack) optimizes GC calls? (use-package gcmh :init (setq gcmh-idle-delay 5) (setq gcmh-high-cons-threshold (* 16 1024 1024)) (gcmh-mode))
Undoing Defaults
Emacs has some default behaviors that are generally annoying. Let's disable them!
;; Turn off all unnecessary GUI elements. (tool-bar-mode -1) (menu-bar-mode -1) (scroll-bar-mode -1) ;; Unless something is actively exploding, I do not care. (setq warning-minimum-level :emergency) ;; customize is the worst. (setq custom-file "/dev/null") (setq package-selected-packages "/dev/null/") ;; These keybinds suspend Emacs (in order to mimic terminal behavior). ;; This has *only* caused me trouble in GUI Emacs. (when (display-graphic-p) (global-unset-key (kbd "C-z")) (global-unset-key (kbd "C-x C-z"))) ;; Stop making backup files everywhere, put them all in one place! (setq backup-directory-alist `(("." . "~/.saves"))) (setq backup-by-copying t) ;; Stop Emacs from bothering you about disabled commands. (setq disabled-command-function nil) ;; Prevent any attempts to resize the frame. (setq frame-inhibit-implied-resize t) ;; Stop Emacs from trying to use dialog boxes. (setq use-dialog-box nil) ;; Prefer y/n over yes/no. (fset 'yes-or-no-p 'y-or-n-p) ;; Mouse behavior tweaks? TODO look into me (setq mouse-wheel-scroll-amount '(1 ((shift) . 1) ((control) . nil))) (setq mouse-wheel-progressive-speed nil) ;; Visual line mode is just better. (global-visual-line-mode)
System
System Packages
Let's now move on to system-level configuration. First, some utility functions for running commands and deducing distro/OS.
(defun process-exit-code-and-output (program &rest args) "Run PROGRAM with ARGS and return the exit code and output in a list." (with-temp-buffer (list (apply 'call-process program nil (current-buffer) nil args) (buffer-string)))) (defun get-distro-or-os () "Return the Linux distribution or OS Emacs is running on." (if (eq system-type 'darwin) "Darwin" (when (eq system-type 'gnu/linux) (if (file-exists-p "/etc/os-release") (substring (shell-command-to-string "source /etc/os-release && echo $NAME") 0 -1) (substring (car (cdr (process-exit-code-and-output "uname" "-o"))) 0 -1)))))
Then, let's set up system-packages, an awesome package that lets you programmatically install packages from Emacs across operating systems.
(use-package system-packages :init (let (os-name (get-distro-or-os)) ;; system-packages doesn't support yay by default, so add it. (when (string= os-name "Arch Linux") (add-to-list 'system-packages-supported-package-managers '(yay . ((default-sudo . nil) (install . "yay -S") (uninstall . "yay -Rs") (update . "yay -Syu") (log . "cat /var/log/pacman.log") (change-log . "yay -Qc") (clean-cache . "yay -Sc") (get-info . "yay -Qi") (get-info-remote . "yay -Si") (list-files-provided-by . "yay -Ql") (owning-file . "yay -Qo") (verify-all-dependencies . "yay -Dk") (remove-orphaned . "yay -Rsn $(pacman -Qtdq)") (list-installed-packages . "yay -Qe") (list-installed-packages-all . "yay -Q") (noconfirm . "--noconfirm")))) (setq system-packages-package-manager 'yay)) (when (string= os-name "Debian GNU/Linux") (setq system-packages-use-sudo t) (setq system-packages-package-manager 'apt)) (if (string= os-name "Darwin") (setq system-packages-package-manager 'brew))) (setq system-packages-noconfirm t))
This package also has some nice extensions like use-package-ensure-system-package
which lets you express system-level dependencies for Emacs packages, and helm-system-packages
which is the ultimate package manager interface (although it unfortunately means we'll need to install all of Helm for just this).
(use-package use-package-ensure-system-package) (use-package helm-system-packages :commands (helm-system-packages))
External Programs
pywal
will be our savior for theming by allowing for thematic consistency.
;; (use-package exwm ;; :ensure-system-package python-pywal)
kitty
is a terminal emulator that's featureful and usable.
include ~/.cache/wal/colors-kitty.conf font_family IBM Plex Mono window_padding_width 10 15 map page_up scroll_page_up map page_down scroll_page_down map ctrl+shift+equal change_font_size all +2.0 map ctrl+shift+plus change_font_size all +2.0 map ctrl+shift+kp_add change_font_size all +2.0 initial_window_width 1000 initial_window_height 400
zsh
is good.
# p10k instant prompt if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" fi export PATH=$PATH:$HOME/.local/bin/:$HOME/.cargo/bin/ export ZSH="$HOME/.oh-my-zsh" ZSH_THEME="powerlevel10k/powerlevel10k" plugins=(git) source $ZSH/oh-my-zsh.sh export EDITOR='emacs' # Aliases alias ydl="youtube-dl --extract-audio --audio-format mp3 -o '%(title)s.%(ext)s'" alias neofetch="neofetch --ascii ~/.config/neofetch/arch.ascii" alias gs="git status" alias nano=mg alias ls="exa --icons" alias hexdump=hexyl alias cat=bat alias rm=rip alias gcc="gcc -Wall -Werror -pedantic-errors" alias g++="g++ -Wall -Weffc++ -Werror -pedantic-errors" function recompile() { cd ~/.config/$1 sudo make clean install &> /dev/null cd - } function fix_titles() { for a in * id3v2 -t ${a%.mp3} $a } function themeage() { wal -i $1 &> /dev/null xdotool key alt+r &> /dev/null emacsclient --eval "(load-theme 'ewal-doom-one)" &> /dev/null /home/quantumish/.local/bin/pywalfox update python ~/test.py colors-wal-dwm2.h python ~/test.py colors-wal-dmenu2.h python ~/test.py zathurarc python ~/test.py colors-vis recompile dmenu } # To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. [[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh source /usr/share/zsh/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh
export PATH=$PATH:$HOME/.local/bin/:$HOME/.cargo/bin/ export ZSH="$HOME/.oh-my-zsh" ZSH_THEME="lambdamod" plugins=(git zsh-autosuggestions zsh-syntax-highlighting) source $ZSH/oh-my-zsh.sh export EDITOR='emacs' alias gs="git status" alias nano=mg alias gcc="gcc -Wall -Werror -pedantic-errors" alias g++="g++ -Wall -Weffc++ -Werror -pedantic-errors"
It is clearly of top priority to ensure the Arch logo in neofetch
looks good.
${c1} ▄ ▟█▙ ▟███▙ ▟█████▙ ▟███████▙ ▂▔▀▜██████▙ ▟██▅▂▝▜█████▙ ▟█████████████▙ ▟███████████████▙ ▟█████████████████▙ ▟███████████████████▙ ▟█████████▛▀▀▜████████▙ ▟████████▛ ▜███████▙ ▟█████████ ████████▙ ▟██████████ █████▆▅▄▃▂ ▟██████████▛ ▜█████████▙ ▟██████▀▀▀ ▀▀██████▙ ▟███▀▘ ▝▀███▙ ▟▛▀ ▀▜▙
Firefox could be prettier.
;; (use-package exwm ;; :ensure-system-package (firefox python-pywalfox))
#TabsToolbar {visibility: collapse;} #statuspanel[type="overLink"] #statuspanel-label { display:none!important; }
Desktop
It's time to load EXWM, the Emacs X Window Manager.
(use-package exwm :init (setq exwm-workspace-number 3) (setq exwm-input-global-keys `(([?\s-r] . exwm-reset) ([?\s-w] . exwm-workspace-switch) ([?\s-&] . (lambda (command) (interactive (list (read-shell-command "$ "))) (start-process-shell-command command nil command))))) ;; Set default simulation keys (setq exwm-input-simulation-keys '(([?\C-b] . [left]) ([?\C-f] . [right]) ([?\C-p] . [up]) ([?\C-n] . [down]) ([?\C-a] . [home]) ([?\C-e] . [end]) ([?\M-v] . [prior]) ([?\C-v] . [next]) ([?\C-d] . [delete]) ([?\C-k] . [S-end delete]))) ;; Allow windows to be moved across screens and interacted with normally. (setq exwm-layout-show-all-buffers t) (setq exwm-workspace-show-all-buffers t) (exwm-enable))
Setting up multi-monitor support is a bit of a hack in my configuration since my input devices tend to mysteriously swap around. You'll notice I'm using use-package
for the same package twice in a row here, but fear not, it merely executes them sequentially and it means I can intersperse long-winded package configuration with text without fear of accidentally breaking something one day.
(use-package exwm :init (defvar left-screen "DP-3") (defvar middle-screen "HDMI-0") (defvar right-screen "DP-1") (require 'exwm-randr) (setq exwm-randr-workspace-output-plist `(0 ,middle-screen 1 ,left-screen 2 ,right-screen)) (add-hook 'exwm-randr-screen-change-hook (lambda () (start-process-shell-command "xrandr" nil (concat "xrandr --output " left-screen " --output " middle-screen " --output " right-screen " --auto")))) (exwm-randr-enable) (add-hook 'exwm-init-hook (lambda () (start-process-shell-command "xrandr" nil (concat "xrandr --output " left-screen " --rotate left")))))
Next, if we're on Linux, let's do everything we need to do at startup.
xmodmap
lets you modify the keys, so let's make things a lot nicer for Emacs.
clear lock clear control clear mod1 clear mod2 clear mod3 clear mod4 clear mod5 keycode 37 = Hyper_L keycode 66 = Control_L keycode 9 = Escape keycode 0xffca = Escape add control = Control_L Control_R add mod1 = Alt_L Alt_R Meta_L add mod2 = Num_Lock add mod3 = Hyper_L add mod4 = Super_L Super_R add mod5 = Mode_switch ISO_Level3_Shift
xbindkeys
allows for customizing system-wide keybinds which can be useful when you're in a pickle. Most of this is legacy config from back before I started using EXWM.
# -*- shell-script -*- # TODO Phase me out! # Increase volume "amixer set Master 5%+" XF86AudioRaiseVolume # Decrease volume "amixer set Master 5%-" XF86AudioLowerVolume "amixer set Master toggle" XF86AudioMute "bash ~/.config/rofi/applets/menu/screenshot.sh" Print "bash ~/.config/rofi/applets/menu/powermenu.sh" Pause "bash ~/.config/rofi/applets/menu/apps.sh" Scroll_Lock "bash ~/.config/rofi/launchers/text/launcher.sh" alt + p "bash ~/.config/rofi/launchers/ribbon/launcher.sh" alt + shift + p "sh ~/.config/focus.sh" alt + shift + f "python ~/.config/modeset.py 'normal'" m:0x20 + c:37 + F1 "rofi -show calc -modi calc -no-show-match -no-sort" XF86Calculator
xcape
allows for "dual-function" keys that can act as one key when held down, and another when tapped. It's niche but useful. We'll remap tapping left-shift and right-shift to left and right parentheses respectively, as well as remap tapping caps-lock to escape.
xcape -e "Control_L=Escape" xcape -e "Shift_R=parenright" xcape -e "Shift_L=parenleft"
dunst
is a great notification server.
[global] monitor = 0 follow = keyboard geometry = "320x20-36+36" indicate_hidden = yes shrink = yes transparency = 0 notification_height = 0 separator_height = 0 padding = 8 horizontal_padding = 8 frame_width = 2 frame_color = "#000000" separator_color = frame sort = yes idle_threshold = 120 font = IBM Plex Mono 10 line_height = 0 markup = full format = "<b>%s</b>\n<i>%b</i>" alignment = left show_age_threshold = 60 word_wrap = yes ellipsize = middle ignore_newline = no stack_duplicates = true hide_duplicate_count = false show_indicators = false icon_position = left max_icon_size = 32 icon_path = /usr/share/icons/candy-icons/apps/scalable:/usr/share/icons/candy-icons/devices/scalable/ sticky_history = yes history_length = 20 dmenu = /usr/bin/dmenu -p dunst: browser = /usr/bin/firefox -new-tab always_run_script = true title = Dunst class = Dunst startup_notification = false verbosity = mesg corner_radius = 0 force_xinerama = false mouse_left_click = close_current mouse_middle_click = do_action mouse_right_click = close_all [experimental] per_monitor_dpi = false [shortcuts] close = ctrl+space close_all = ctrl+shift+space history = ctrl+grave context = ctrl+shift+grave [urgency_low] foreground = "#ffd5cd" background = "#121212" frame_color = "#a2c5de" timeout = 10 icon = ~/.config/dunst/images/notification.png [urgency_normal] background = "#121212" foreground = "#ffd5cd" frame_color = "#a2c5de" timeout = 10 icon = ~/.config/dunst/images/notification.png [urgency_critical] background = "#121212" foreground = "#ffd5cd" frame_color = "#a2c5de" timeout = 0 icon = ~/.config/dunst/images/alert.png
Let's define a quick script to reload it based on pywal, too.
. "${HOME}/.cache/wal/colors.sh" pkill dunst dunst \ -frame_width 2 \ -lb "${color0}" \ -nb "${color0}" \ -cb "${color0}" \ -lf "${color7}" \ -bf "${color7}" \ -cf "${color7}" \ -nf "${color7}" \ -frame_color "${color2}" &
picom
is a nice compositor, and will allow us to have effects like rounded corners and transparency if we want them. Dual kawase blur looks very nice, so let's use it.
backend = "glx"; blur: { method = "dual_kawase"; strength = 10; background = false; background-frame = false; background-fixed = false; }
Finally, we actually run the startup.
(use-package exwm ; :ensure-system-package (xbindkeys xcape dunst flameshot unclutter polybar feh picom) :init ;; Rebind keys (call-process-shell-command "xmodmap ~/.config/X/Xmodmap" nil 0) (call-process-shell-command "xbindkeys" nil 0) (call-process-shell-command "sh ~/.config/X/xcape.sh" nil 0) ;; Notifications w/ dunst (call-process-shell-command "dunst &" nil 0) (call-process-shell-command "sh ~/.config/dunst/reload_dunst.sh" nil 0) ;; Make mouse vanish when not used (call-process-shell-command "unclutter &" nil 0) ;; The best screenshot utility! (call-process-shell-command "flameshot &" nil 0) ;; Compositor (call-process-shell-command "picom &" nil 0) (call-process-shell-command "feh --bg-fill ~/.config/wallpapers/firewatch-galaxy.jpg" nil 0))
Let's make moving across monitors and workspaces a little easier.
(defun exwm-workspace-next () (interactive) (if (< exwm-workspace-current-index (- exwm-workspace-number 1)) (exwm-workspace-switch (+ exwm-workspace-current-index 1)))) (defun exwm-workspace-prev () (interactive) (if (> exwm-workspace-current-index 0) (exwm-workspace-switch (- exwm-workspace-current-index 1)))) (general-define-key "M-h" 'exwm-workspace-next "M-l" 'exwm-workspace-prev) ;; Make mouse follow focus (use-package exwm-mff :init (exwm-mff-mode)) (use-package exwmsw :straight (exwmsw :type git :host github :repo "Lemonbreezes/exwmsw" :fork (:host github :repo "richardfeynmanrocks/exwmsw")) :init (setq exwmsw-active-workspace-plist `(,middle-screen 0 ,right-screen 0 ,left-screen 0)) (setq exwmsw-the-right-screen right-screen) (setq exwmsw-the-center-screen middle-screen) (setq exwmsw-the-left-screen left-screen) :general (override-global-map "C-M-j" #'exwmsw-cycle-screens "C-M-k" #'exwmsw-cycle-screens-backward) (exwm-mode-map ;; HACK "C-M-j" #'exwmsw-cycle-screens "C-M-k" #'exwmsw-cycle-screens-backward))
Then, make it so EXWM buffer names contain part of the the window title based off this great tip from r/emacs.
(use-package exwm :init (defun b3n-exwm-set-buffer-name () (if (and exwm-title (string-match "\\`http[^ ]+" exwm-title)) (let ((url (match-string 0 exwm-title))) (setq-local buffer-file-name url) (setq-local exwm-title (replace-regexp-in-string (concat (regexp-quote url) " - ") "" exwm-title)))) (setq-local exwm-title (concat exwm-class-name "<" (if (<= (length exwm-title) 50) exwm-title (concat (substring exwm-title 0 50) "…")) ">")) (exwm-workspace-rename-buffer exwm-title)) (add-hook 'exwm-update-class-hook 'b3n-exwm-set-buffer-name) (add-hook 'exwm-update-title-hook 'b3n-exwm-set-buffer-name))
Finally, update polybar config file to match monitor and make it so we have decorative gaps around all of EXWM (not individual buffers/windows unfortunately).
;; TODO Use Org Babel and tangle polybar config? (start-process-shell-command "polybar-update" nil (concat "sed s/<MONITOR>/" middle-screen "/g -i ~/.config/polybar/config.ini.bak > ~/.config/polybar/config.ini")) (use-package exwm-outer-gaps :straight (exmw-outer-gaps :type git :host github :repo "lucasgruss/exwm-outer-gaps") :hook (exwm-init . (lambda () (exwm-outer-gaps-mode)))) (use-package exwm :hook (exwm-init . (lambda () (call-process-shell-command "bash ~/.config/polybar/launch.sh --docky" nil 0))))
Interface
Theming
;; TODO: Set up treemacs. (use-package hide-mode-line) (use-package doom-themes :init ;; Global settings (defaults) (setq doom-themes-enable-bold t ; if nil, bold is universally disabled doom-themes-enable-italic t) ; if nil, italics is universally disabled (doom-themes-visual-bell-config) ;(setq doom-themes-treemacs-theme "doom-colors") ; use the colorful treemacs theme ;(doom-themes-treemacs-config) (doom-themes-org-config)) (use-package ewal) (use-package ewal-doom-themes :init (load-theme 'ewal-doom-one t)) (use-package doom-modeline :init (setq doom-modeline-height 40) (setq doom-modeline-buffer-encoding nil) (doom-modeline-mode)) ;; TODO: Contextual solaire (use-package solaire-mode :hook (prog-mode . solaire-mode)) (fringe-mode 0) (use-package centaur-tabs :init (setq centaur-tabs-height 16) (setq centaur-tabs-style "bar") (setq centaur-tabs-set-icons t) (setq centaur-tabs-icon-scale-factor 0.7) (setq centaur-tabs-set-bar 'left) (setq x-underline-at-descent-line t) (defun contextual-tabs () (interactive) (if (and (centaur-tabs-mode-on-p) (eq (derived-mode-p 'prog-mode) nil)) (centaur-tabs-local-mode))) (defun centaur-tabs-hide-tab (x) (let ((name (format "%s" x))) (or (window-dedicated-p (selected-window)) (string-match-p (regexp-quote "<") name) (string-prefix-p "*lsp" name) (string-prefix-p "*Compile-Log*" name) (string-prefix-p "*company" name) (string-prefix-p "*compilation" name) (string-prefix-p "*Help" name) (string-prefix-p "*straight" name) (string-prefix-p "*Flycheck" name) (string-prefix-p "*tramp" name) (string-prefix-p "*help" name) (and (string-prefix-p "magit" name) (not (file-name-extension name))) ))) (defun centaur-tabs-hide-tab-cached (x) (centaur-tabs-hide-tab x)) (centaur-tabs-mode) :hook (after-change-major-mode . contextual-tabs) :bind ("H-l" . 'centaur-tabs-forward-tab) ("H-h" . 'centaur-tabs-backward-tab)) (use-package treemacs :after doom-themes :init (doom-themes-treemacs-config) (setq doom-themes-treemacs-theme "doom-colors") (setq treemacs-width 30) :bind ("C-c t" . treemacs)) (use-package treemacs-all-the-icons :after treemacs :init (treemacs-load-theme "all-the-icons")) (use-package olivetti :config :hook (prog-mode . (lambda () (olivetti-mode))))
Translucent
Transparency can look nice - sometimes. Polybar clashes with transparency, so disable it while we're using it.
;; FIXME hacky and broken (define-minor-mode translucent-mode "Make the current frame slightly transparent and don't use polybar." nil :global t (if translucent-mode (set-frame-parameter (selected-frame) 'alpha '(100)) (set-frame-parameter (selected-frame) 'alpha '(90))))
Dashboard
(use-package dashboard :straight (emacs-dashboard :type git :host github :repo "emacs-dashboard/emacs-dashboard" :fork (:host github :repo "richardfeynmanrocks/emacs-dashboard")) :init (setq dashboard-center-content t) (setq dashboard-set-heading-icons t) (setq dashboard-projects-backend 'projectile) (setq dashboard-footer-messages '("The One True Editor!" "Protocol 3: Protect the Pilot" "All systems nominal." "Democracy... is non negotiable." "It's my way or... hell, it's my way!" "Make life rue the day it though it could give Richard Stallman lemons!" "Vi-Vi-Vi, the editor of the beast." "Happy hacking!" "While any text editor can save your files, only Emacs can save your soul." "There's an Emacs package for that." "Rip and tear, until it is done!" "It's time to kick ass and chew bubblegum... and I'm all outta gum." "M-x butterfly" "")) (setq dashboard-items '((recents . 3) (projects . 3) (agenda . 5))) (setq dashboard-startup-banner "~/.config/wallpapers/firewatch-galaxy.png") (setq dashboard-image-banner-max-height 250) (setq dashboard-image-banner-max-width 500) (setq dashboard-set-init-info nil) (setq dashboard-set-navigator t) ;; Format: "(icon title help action face prefix suffix)" (setq dashboard-navigator-buttons `(;; line1 ((,(all-the-icons-faicon "github" :height 1 :v-adjust 0.0) "Github" "Browse repo" (lambda (&rest _) (browse-url "https://github.com/quantumish/.emacs.d"))) (,(all-the-icons-faicon "reddit-alien" :height 1 :v-adjust 0.0) "r/emacs" "Waste time" (lambda (&rest _) (browse-url "https://reddit.com/r/emacs")))))) (setq dashboard-page-separator "\n\n") (dashboard-setup-startup-hook) :hook (dashboard-mode . hide-mode-line-mode) (dashboard-mode . turn-off-solaire-mode))
Minibuffer Completion
Next, let's improve interactions with Emacs: things like finding files, running commands, switching buffers, etc… by using ivy
, a light(ish) minibuffer completion system. Ivy is one of the more popular packages for this, meaning that there's quite a bit of integration with other packages. Notably, counsel
extends its functionality and swiper
provides a nicer interface to interactive search.
On top of this, prescient
allows for completions to be even more useful by basing them off of history and sorting them better. Finally, we can add some icons and extra text to make it all prettier.
(use-package prescient :init (setq prescient-persist-mode t)) (use-package ivy :init (use-package counsel :config (counsel-mode 1)) (use-package swiper :defer t) (ivy-mode 1) (setq counsel-search-engine 'google) :bind (("C-s" . swiper-isearch) ("M-x" . counsel-M-x) ("C-x C-f" . counsel-find-file) ("C-x C-l" . counsel-load-theme) ("C-x b" . counsel-switch-buffer) ("C-h C-f" . counsel-faces) ("C-x C-g" . counsel-search))) (use-package ivy-rich :after ivy :init (ivy-rich-mode)) (use-package all-the-icons-ivy-rich :after ivy-rich counsel :init (all-the-icons-ivy-rich-mode)) (use-package ivy-prescient :after ivy prescient :init (ivy-prescient-mode)) (use-package marginalia :config (marginalia-mode))
Help
In order to make some parts of exploring Emacs slightly nicer, let's install helpful
which overhauls the Help interface, and which-key
which helps you discover keybinds.
(use-package helpful :init ;; Advise describe-style functions so that Helpful appears no matter what (advice-add 'describe-function :override #'helpful-function) (advice-add 'describe-variable :override #'helpful-variable) (advice-add 'describe-command :override #'helpful-callable) (advice-add 'describe-key :override #'helpful-key) (advice-add 'describe-symbol :override #'helpful-symbol) :config ;; Baseline keybindings, not very opinionated (global-set-key (kbd "C-h f") #'counsel-describe-function) (global-set-key (kbd "C-h v") #'counsel-describe-variable) (global-set-key (kbd "C-h k") #'helpful-key) (global-set-key (kbd "C-c C-d") #'helpful-at-point) (global-set-key (kbd "C-h F") #'helpful-function) (global-set-key (kbd "C-h C") #'helpful-command) ;; Counsel integration (setq counsel-describe-function-function #'helpful-callable) (setq counsel-describe-variable-function #'helpful-variable)) (use-package which-key :init (which-key-mode))
Movement
(use-package zygospore) (defun opposite-other-window () "Cycle buffers in the opposite direction." (interactive) (other-window -1)) (defun opposite-other-frame () "Cycle frames in the opposite direction." (interactive) (other-frame -1)) (general-define-key :keymaps '(exwm-mode-map override-global-map) "M-k" 'other-window "M-j" 'opposite-other-window "C-M-j" 'opposite-other-frame "C-M-k" 'other-frame "M-m" 'zygospore-toggle-delete-other-windows)
Hydra
(use-package hydra :init (global-unset-key (kbd "C-x h")) (general-def "C-x h l" 'hydra-launcher/body "C-x h a" 'hydra-org-agenda/body "C-x h f" 'hydra-go-to-file/body)) (use-package pretty-hydra) (use-package s) (use-package major-mode-hydra :after hydra :preface (defun with-alltheicon (icon str &optional height v-adjust face) "Display an icon from all-the-icon." (s-concat (all-the-icons-alltheicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str)) (defun with-faicon (icon str &optional height v-adjust face) "Display an icon from Font Awesome icon." (s-concat (all-the-icons-faicon icon ':v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str)) (defun with-fileicon (icon str &optional height v-adjust face) "Display an icon from the Atom File Icons package." (s-concat (all-the-icons-fileicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str)) (defun with-octicon (icon str &optional height v-adjust face) "Display an icon from the GitHub Octicons." (s-concat (all-the-icons-octicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str))) (pretty-hydra-define hydra-flycheck (:hint nil :color teal :quit-key "q" :title (with-faicon "plane" "Flycheck" 1 -0.05)) ("Checker" (("?" flycheck-describe-checker "describe") ("d" flycheck-disable-checker "disable") ("m" flycheck-mode "mode") ("s" flycheck-select-checker "select")) "Errors" (("<" flycheck-previous-error "previous" :color pink) (">" flycheck-next-error "next" :color pink) ("f" flycheck-buffer "check") ("l" flycheck-list-errors "list")) "Other" (("M" flycheck-manual "manual") ("v" flycheck-verify-setup "verify setup")))) (pretty-hydra-define hydra-go-to-file (:hint nil :color teal :quit-key "q" :title (with-faicon "arrow-right" "Go To" 1 -0.05)) ("Org" (("oi" (find-file "~/sync/org/inbox.org") "inbox") ("oc" (find-file "~/sync/org/completed.org") "completed") ("op" (find-file "~/sync/ideas.org") "ideas")) "Config" (("cc" (find-file "~/.emacs.d/config.org") "config.org") ("ci" (find-file "~/.emacs.d/init.el") "init.el" )) "Notes" (("ni" (find-file "~/sync/notes/index.org") "Main Index")) )) (pretty-hydra-define hydra-org-agenda (:hint nil :color teal :quit-key "q" :title (with-faicon "list-ol" "Agenda" 1 -0.05)) ("Standard" (("w" (org-agenda))))) (pretty-hydra-define hydra-launcher (:hint nil :color teal :quit-key "q" :title (with-faicon "rocket" "Launch" 1 -0.05)) ("Shell-likes" (("v" vterm "Vterm") ("e" eshell "Eshell") ("l" ielm "IELM") ("k" (call-process-shell-command "open -a Kitty" nil 0) "Kitty")) "Messaging" (("i" erc "ERC") ("d" (call-process-shell-command "open -a Discord" nil 0) "Discord") ("t" (call-process-shell-command "open -a Telegram" nil 0) "Telegram")) "Misc" (("f" (call-process-shell-command "open -a Firefox" nil 0) "Firefox") ("s" (call-process-shell-command "open -a Spotify" nil 0) "Spotify") ("m" (call-process-shell-command "open -a Spark" nil 0) "Spark")) ))
Perspectives
TODO Org
First, let's set up the basics.
(use-package org :init (setq org-todo-keywords '((sequence "TODO(t)" "WAIT(w)" "|" "DONE(d)" "NOPE(n)"))) (setq org-modules (append org-modules '(org-habit org-id))) )
Aesthetics
Let's add aesthetics for normal prose-style Org usage.
(use-package org :config (setq org-fontify-quote-and-verse-blocks t) (setq org-fontify-emphasized-text t) (setq org-hide-emphasis-markers t) (setq org-ellipsis " ") (setq org-hide-leading-stars t) (set-face-attribute 'org-document-title nil :height 2.0 :weight 'bold) :hook (org-mode . org-indent-mode))
There are a variety of useful packages that make Org look nicer:
(setq org-latex-create-formula-image-program 'dvisvgm) ;; Smart mixing of variable pitch and monospace ;; This is preferred over `mixed-pitch` because of small details (use-package org-variable-pitch :init (org-variable-pitch-setup)) ;; Better headline icons (use-package org-superstar :config (setq org-superstar-headline-bullets-list '("◉" "○" "◈" "◎")) :hook (org-mode . org-superstar-mode)) ;; Auto-toggle emphasis (use-package org-appear :straight (:host github :repo "awth13/org-appear") :hook (org-mode . org-appear-mode)) ;; Auto-toggle LaTeX rendering (use-package org-fragtog :hook (org-mode . org-fragtog-mode)) ;; Natural bulleted lists (use-package org-autolist :hook (org-mode . org-autolist-mode)) ;; Centering w/ Olivetti (use-package olivetti :hook (org-mode . (lambda () (interactive) (olivetti-mode) (olivetti-set-width 100))))
Icons
(use-package org :config (defun magic-icon-fix () (let ((fontset (face-attribute 'default :fontset))) (set-fontset-font fontset '(?\xf000 . ?\xf2ff) "FontAwesome" nil 'append))) :hook (org-mode . (lambda () (interactive) (setq prettify-symbols-alist '(("[#A]" . "") ("[#B]" . "") ("[#C]" . "") ("[ ]" . "") ("[X]" . "") ("[-]" . "") ("#+begin_src" . "") ("#+end_src" . "―") ("#+begin_collapsible" . "") ("#+end_collapsible" . "―") ("#+begin_aside" . "") ("#+end_aside" . "―") ("#+begin_defn" . "") ("#+end_defn" . "―") ("#+begin_questionable" . "") ("#+end_questionable" . "―") ("#+begin_problem" . "") ("#+end_problem" . "―") ("#+EXCLUDE_TAGS:" . "") (":PROPERTIES:" . "\n") (":END:" . "―") ("#+STARTUP:" . "") ("#+TITLE: " . "") ("#+title: " . "") ("#+RESULTS:" . "") ("#+NAME:" . "") ("#+ROAM_TAGS:" . "") ("#+FILETAGS:" . "") ("#+HTML_HEAD:" . "") ("#+SUBTITLE:" . "") ("#+AUTHOR:" . "") (":Effort:" . "") ("SCHEDULED:" . "") ("DEADLINE:" . "") ("#+begin_defn" . "") ("#+end_defn" . "―"))) (prettify-symbols-mode) (let ((fontset (face-attribute 'default :fontset))) (set-fontset-font fontset '(?\xf000 . ?\xf2ff) "FontAwesome" nil 'append)))))
Property Drawers
(defun org-cycle-hide-drawers (state) "Re-hide all drawers after a visibility state change." (when (and (derived-mode-p 'org-mode) (not (memq state '(overview folded contents)))) (save-excursion (let* ((globalp (memq state '(contents all))) (beg (if globalp (point-min) (point))) (end (if globalp (point-max) (if (eq state 'children) (save-excursion (outline-next-heading) (point)) (org-end-of-subtree t))))) (goto-char beg) (while (re-search-forward org-drawer-regexp end t) (save-excursion (beginning-of-line 1) (when (looking-at org-drawer-regexp) (let* ((start (1- (match-beginning 0))) (limit (save-excursion (outline-next-heading) (point))) (msg (format (concat "org-cycle-hide-drawers: " "`:END:`" " line missing at position %s") (1+ start)))) (if (re-search-forward "^[ \t]*:END:" limit t) (outline-flag-region start (point-at-eol) t) (user-error msg)))))))))) (add-hook 'org-mode-hook (lambda () (org-cycle-hide-drawers 'all)))
Export
(use-package org-special-block-extras :init (org-special-block-extras-mode) (org-special-block-extras-defblock collapsible (title "Details") (contents "") (format (pcase backend (_ "<details> <summary> <i> %s </i> </summary> %s </details>")) title contents))) (use-package org :init (setq org-html-text-markup-alist '((bold . "<b>%s</b>") (code . "<code>%s</code>") (italic . "<i>%s</i>") (strike-through . "<del>%s</del>") (underline . "<span class=\"underline\">%s</span>") (verbatim . "<kbd>%s</kbd>"))) (setq org-html-head "<link rel=\"stylesheet\" href=\"file:///Users/davfrei/org.css\"><link rel=\"stylesheet\" href=\"https://quantumish.github.io/admonition.css\"><script src=\"https://kit.fontawesome.com/76c5ce8bda.js\" crossorigin=\"anonymous\"") (setq org-html-postamble nil) (setq org-export-with-section-numbers nil) (setq org-export-with-toc nil) (setq org-publish-project-alist '(("github.io" :base-directory "~/Dropbox/publicnotes/" :base-extension "org" :publishing-directory "~/richardfeynmanrocks.github.io/notes/" :recursive t :publishing-function org-html-publish-to-html :headline-levels 4 :html-extension "html" :with-toc nil :section-numbers nil :html-head "<link rel=\"stylesheet\" href=\"https://richardfeynmanrocks.github.io/org.css\">" :preserve-breaks t ))))
Notes
(use-package org-roam :init (setq org-roam-directory (concat (getenv "HOME") "/sync/notes")) (setq org-roam-v2-ack t) :bind ("C-c n i" . org-roam-node-insert) ("C-c n f" . org-roam-node-find) ("C-c n s" . org-roam-db-sync)) (use-package org-roam-ui :straight (:host github :repo "org-roam/org-roam-ui" :branch "main" :files ("*.el" "out")) :after org-roam ;; :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 t)) (use-package deft :init (setq deft-directory org-roam-directory) (defun my/deft-parse-title (file contents) "Parse the given FILE and CONTENTS and determine the title. If `deft-use-filename-as-title' is nil, the title is taken to be the first non-empty line of the FILE. Else the base name of the FILE is used as title." (let ((begin (string-match "^#\\+[tT][iI][tT][lL][eE]: .*$" contents))) (if begin (string-trim (substring contents begin (match-end 0)) "#\\+[tT][iI][tT][lL][eE]: *" "[\n\t ]+") (deft-base-filename file)))) (advice-add 'deft-parse-title :override #'my/deft-parse-title) (setq deft-strip-summary-regexp (concat "\\(" "[\n\t]" ;; blank "\\|^#\\+[[:alpha:]_]+:.*$" ;; org-mode metadata "\\|^:PROPERTIES:\n\\(.+\n\\)+:END:\n" "\\)")))
Taproot-specific
(defvar taproot-dir (concat (getenv "HOME") "/taproot3"))
Let's define a function to export to Taproot:
(defun org-roam-export-to-taproot () (interactive) (call-process-shell-command (concat "cp -R " org-roam-directory "*.org " taproot-dir "/corners/david")))
Also, a function to convert to file-based links would be nice.
(defun org-roam-emergency-exit (in-path out-path) "Emergency exit from Org Roam v2. Returns list of commands to convert notes in IN-PATH to traditional format in OUT-PATH." (let ((sed (if (eq system-type 'darwin) "gsed" "sed"))) (progn (call-process-shell-command (concat "cp -R " in-path "*.org " out-path)) (dolist (pair (org-roam-db-query [:select [ID FILE] :from nodes])) (call-process-shell-command (concat sed " -i \"s/id:" (car pair) "/file:" (substring (car (cdr pair)) (length in-path)) "/g\" " out-path "*.org"))) (message (concat sed " -i \"/:PROPERTIES:/d\" " out-path "*.org")) (call-process-shell-command (concat sed " -i \"/:PROPERTIES:/d\" " out-path "*.org")) (call-process-shell-command (concat sed " -i \"/:ID:/d\" " out-path "*.org")) (call-process-shell-command (concat sed " -i \"/:END:/d\" " out-path "*.org"))))) ;; (org-roam-emergency-exit (concat org-roam-directory "/") (concat taproot-dir "/corners/david/"))
And a function to open in Taproot:
(defun org-roam-open-in-taproot () (interactive) (if (not (eq (buffer-file-name) nil)) (if (string= (substring (buffer-file-name) 0 (length taproot-dir)) taproot-dir) (call-process-shell-command (concat "open https://taproot3.sanity.gq" (substring (file-name-sans-extension (buffer-file-name)) (length taproot-dir)))) (message "Not a Taproot buffer!")) (message "Not a file buffer!"))) (defun extended-org-roam-open-in-taproot () (interactive) (message (substring (buffer-file-name) 0 (length org-roam-directory))) (if (not (eq (buffer-file-name) nil)) (if (string= (substring (buffer-file-name) 0 (length org-roam-directory)) org-roam-directory) (call-process-shell-command (concat "open https://taproot3.sanity.gq/corners/david" (substring (file-name-sans-extension (buffer-file-name)) (length org-roam-directory)))) (message "Not a Org Roam buffer!")) (message "Not a file buffer!")))
Productivity
Agenda
(use-package org :init (defvar org-inbox-file (concat (getenv "HOME") "/sync/org/inbox.org")) (defvar org-completed-file (concat (getenv "HOME") "/sync/org/completed.org")) (setq org-archive-location (concat org-completed-file "::")) (setq org-agenda-files `(,org-inbox-file ,org-completed-file)) :general ("C-c o i" #'(lambda () (interactive) (find-file org-inbox-file))) ("C-c o a" #'(lambda () (interactive) (org-agenda 'a))))
Projects
(use-package org :init (setq org-enforce-todo-dependencies t) (setq org-enforce-todo-checkbox-dependencies t) (setq org-agenda-dim-blocked-tasks t))
Development
Terminal
(use-package vterm)
(defun dw/get-prompt-path () (let* ((current-path (eshell/pwd)) (git-output (shell-command-to-string "git rev-parse --show-toplevel")) (has-path (not (string-match "^fatal" git-output)))) (if (not has-path) (abbreviate-file-name current-path) (string-remove-prefix (file-name-directory git-output) current-path)))) ;; This prompt function mostly replicates my custom zsh prompt setup ;; that is powered by github.com/denysdovhan/spaceship-prompt. (defun dw/eshell-prompt () (concat "\n" (propertize "davfrei" 'face `(:foreground ,(doom-color 'orange)) 'read-only t) (propertize " " 'face `(:foreground "white") 'read-only t) (propertize (dw/get-prompt-path) 'face `(:foreground ,(doom-color 'orange)) 'read-only t) (propertize " · " 'face `(:foreground "white") 'read-only t) (propertize (format-time-string "%I:%M:%S %p") 'face `(:foreground ,(doom-color 'cyan)) 'read-only t) (if (= (user-uid) 0) (propertize "\n#" 'face `(:foreground "red2") 'read-only t) (propertize "\nλ" 'face `(:foreground ,(doom-color 'blue)) 'read-only t)) (propertize " " 'face `(:foreground ,(doom-color 'fg))) )) (defun dw/eshell-configure () (use-package xterm-color) (push 'eshell-tramp eshell-modules-list) (push 'xterm-color-filter eshell-preoutput-filter-functions) (delq 'eshell-handle-ansi-color eshell-output-filter-functions) ;; Save command history when commands are entered (add-hook 'eshell-pre-command-hook 'eshell-save-some-history) (add-hook 'eshell-before-prompt-hook (lambda () (setq xterm-color-preserve-properties t))) ;; Truncate buffer for performance (add-to-list 'eshell-output-filter-functions 'eshell-truncate-buffer) ;; We want to use xterm-256color when running interactive commands ;; in eshell but not during other times when we might be launching ;; a shell command to gather its output. (add-hook 'eshell-pre-command-hook (lambda () (setenv "TERM" "xterm-256color"))) (add-hook 'eshell-post-command-hook (lambda () (setenv "TERM" "dumb"))) ;; Use completion-at-point to provide completions in eshell (define-key eshell-mode-map (kbd "<tab>") 'completion-at-point) ;; Initialize the shell history (eshell-hist-initialize) (setenv "PAGER" "cat") (setq eshell-prompt-function 'dw/eshell-prompt eshell-prompt-regexp "^λ " eshell-history-size 10000 eshell-buffer-maximum-lines 10000 eshell-hist-ignoredups t eshell-highlight-prompt t eshell-scroll-to-bottom-on-input t eshell-prefer-lisp-functions nil)) (use-package eshell :hook (eshell-first-time-mode . dw/eshell-configure) :init (setq eshell-directory-name "~/.dotfiles/.emacs.d/eshell/" eshell-aliases-file (expand-file-name "~/.dotfiles/.emacs.d/eshell/alias"))) (use-package eshell-z :hook ((eshell-mode . (lambda () (require 'eshell-z))) (eshell-z-change-dir . (lambda () (eshell/pushd (eshell/pwd)))))) (use-package exec-path-from-shell :init (setq exec-path-from-shell-check-startup-files nil) :config (when (memq window-system '(mac ns x)) (exec-path-from-shell-initialize))) (setq eshell-prompt-function 'dw/eshell-prompt) (use-package esh-autosuggest :hook (eshell-mode . esh-autosuggest-mode)) (use-package eshell-toggle :straight (eshell-toggle :type git :host github :repo "4DA/eshell-toggle") :init (setq eshell-toggle-size-fraction 4) (setq eshell-toggle-use-projectile-root t) (setq eshell-toggle-run-command nil)) (use-package eshell-up) ;; TODO eshell-up ;; (use-package eshell-info-banner ;; :straight (eshell-info-banner :type git :host github ;; :repo "phundrak/eshell-info-banner.el") ;; :hook (eshell-banner-load . eshell-info-banner-update-banner)) (use-package eshell-manual :straight (eshell-manual :type git :host github :repo "nicferrier/eshell-manual")) ;; (use-package eshell-fringe-status ;; :init ;; (setq eshell-fringe-status-success-bitmap 'my-flycheck-fringe-indicator) ;; (setq eshell-fringe-status-failure-bitmap 'my-flycheck-fringe-indicator) ;; :hook (eshell-mode . eshell-fringe-status-mode)) ;; (use-package esh-help ;; :init (setup-esh-help-eldoc))
LSP
lsp-mode
enables us to get Intellisense-esque features in Emacs: setting it up requires both config on Emacs' side and installing actual language servers on your side. We'll auto-install them with the magic of use-package-ensure-system-package
, although brace yourself for the potential for lots of debugging if the server doesn't work as expected on your system.
lsp-mode
can do more than just provide good completions: you can jump to definitions and references with lsp-find-definition
and lsp-find-references
respectively, as well as most other things you'd expect from an IDE.
(use-package lsp-mode ; :ensure-system-package ccls ; :ensure-system-package (pyls . "python -m pip install pyls") ; :ensure-system-package rust-analyzer :init ;; Disable annoying headerline (setq lsp-headerline-breadcrumb-enable nil) ;; Don't show unneeded function info in completions (setq lsp-completion-show-detail nil) ;; Disable annoying autoformatting! (setq-default lsp-enable-indentation nil) (setq-default lsp-enable-on-type-formatting nil) :commands lsp ;; Add languages of your choice! :hook ((c-mode . lsp) (c++-mode . lsp) (python-mode . lsp) (typescript-mode . lsp) (rust-mode . lsp))) (use-package lsp-ui :after lsp :init (setq lsp-ui-doc-delay 5) (add-hook 'flycheck-mode-hook 'lsp-ui-mode) ;; HACK :config (eval `(set-face-attribute 'lsp-ui-doc-background nil :background ,(doom-darken 'bg .2))))
Company
company-mode
provides code completions in Emacs, and will work together with lsp-mode
to provide a nice experience. On top of that, let's use add-ons that allow documentation for completions to pop up and also let prescient
make things better like it did with Ivy.
(use-package company :init (setq company-idle-delay 0) (setq company-tooltip-maximum-width 40) :hook (prog-mode . company-mode)) (use-package company-quickhelp :after company :init (company-quickhelp-mode)) (use-package company-quickhelp-terminal :after company-quickhelp) (use-package company-prescient :after company prescient :init (setq-default history-length 1000) (setq-default prescient-history-length 1000) :init (company-prescient-mode))
Compilation
(use-package kv) (require 'kv) (defvar custom-compile-cmds '((rustic-mode . ((debug . "cargo build") (release . "cargo build --release") (test . "cargo test"))) (c++-mode . ((cmake . "cmake .") (test . "ctest") (make . "make") (this . "g++ $this.cpp -std=c++17 -o $this") (this-speedy . "g++ $this.cpp -O3 -std=c++17 -o $this") (this-python . "g++ -shared -std=c++17 -undefined_dynamic_lookup `python3 -m pybind11 --includes` $this.cpp -o $this`python3-config --extension-suffix` -D PYTHON -fPIC"))) (c-mode . ((make . "make") (this . "gcc $this.c -o $this") (this-speedy . "gcc $this.c -O3 -o $this") (this-archive . "gcc $this.c -O -c -g && ar rcs $this.a $this.o") (this-mpi . "mpicc $this.c -o $this"))) (cuda-mode . ((this . "nvcc $this.cu -o $this"))) (python-mode . ((this-types . "mypy $this.py --ignore-missing-imports --strict") (this-cython . "cython --embed -o $this.c $this.py -3 && sudo gcc $this.c -o $this -I/usr/include/python3.9 -lpython3.9"))) )) (defun compile-dwim () (interactive) (let ((list (cdr (assoc major-mode custom-compile-cmds)))) ;; Debugging is for suckers (ivy-read "Compilation preset: " (kvalist->keys list) :preselect (car (kvalist->keys list)) :action (lambda (name) (compile (replace-regexp-in-string (regexp-quote "$this") (file-name-sans-extension (buffer-file-name)) (cdr (assoc (intern-soft name) list)))))))) (use-package compile :config (setq compilation-scroll-output t) (setq compilation-ask-about-save nil) (defun compile-project () (interactive) (let ((default-directory (projectile-project-root))) (call-interactively 'compile-dwim))) (require 'ansi-color) (defun colorize-compilation-buffer () (toggle-read-only) (ansi-color-apply-on-region compilation-filter-start (point)) (toggle-read-only)) (add-hook 'compilation-filter-hook 'colorize-compilation-buffer) :bind (:map prog-mode-map ("C-;" . compile-project)) :hook (compilation-mode . hide-mode-line-mode) ; (compilation-mode . (lambda () (set-header-line 200))) (compilation-start . olivetti-mode) (compilation-start . determine-olivetti))
Documentation
(defun minimal-browse-url (url) "Browse an arbitrary url (as URL) in a new frameless Firefox window." (split-window-right) (other-window 1) (call-process-shell-command (concat "firefox -P default-release --new-window " url) nil 0)) (use-package dash-docs) (use-package counsel-dash :config (setq dash-docs-browser-func 'minimal-browse-url) (setq dash-docs-enable-debugging nil) (defun emacs-lisp-doc () "Restrict dash docsets to Emacs Lisp." (interactive) (setq-local dash-docs-docsets '("Emacs Lisp"))) (defun c-doc () "Restrict dash docsets to C." (interactive) (setq-local dash-docs-docsets '("C"))) (defun c++-doc () "Restrict dash docsets to C/C++." (interactive) (setq-local dash-docs-docsets '("C" "C++"))) (defun rust-doc () "Restrict dash docsets to Rust." (interactive) (setq-local dash-docs-docsets '("Rust"))) (defun python-doc () "Restrict dash docsets to Python." (interactive) (setq-local dash-docs-docsets '("Python 3"))) :bind (:map prog-mode-map ("C-c d" . counsel-dash) ("C-c C-d" . counsel-dash-at-point)) :hook (emacs-lisp-mode . emacs-lisp-doc) (c-mode . c-doc) (c++-mode . c++-doc) (python-mode . python-doc) (rustic-mode . rust-doc) (rust-mode . rust-doc))
TODO Projectile?
Linting
Next, we can add linting to the editor with flycheck!
(use-package flycheck :hook (prog-mode . flycheck-mode) (flycheck-mode . (lambda () (set-window-fringes nil 15 0))))
With a tweak courtesy of @jemoka, we can smooth over bits of the interface. Goodbye squiggly lines and strange fringe indicators. Goodbye linter errors while typing.
(use-package flycheck :config (setq flycheck-check-syntax-automatically '(mode-enabled save)) (set-face-attribute 'flycheck-error nil :underline `(:color ,(doom-color 'orange))) (set-face-attribute 'flycheck-warning nil :underline `(:color ,(doom-color 'blue))) (set-face-attribute 'flycheck-info nil :underline t) (define-fringe-bitmap 'my-flycheck-fringe-indicator (vector #b00000000 #b00000000 #b00000000 #b00000000 #b00000000 #b00000000 #b00000000 #b00011100 #b00111110 #b00111110 #b00111110 #b00011100 #b00000000 #b00000000 #b00000000 #b00000000 #b00000000)) (let ((bitmap 'my-flycheck-fringe-indicator)) (flycheck-define-error-level 'error :severity 2 :overlay-category 'flycheck-error-overlay :fringe-bitmap bitmap :error-list-face 'flycheck-error-list-error :fringe-face 'flycheck-fringe-error) (flycheck-define-error-level 'warning :severity 1 :overlay-category 'flycheck-warning-overlay :fringe-bitmap bitmap :error-list-face 'flycheck-error-list-warning :fringe-face 'flycheck-fringe-warning) (flycheck-define-error-level 'info :severity 0 :overlay-category 'flycheck-info-overlay :fringe-bitmap bitmap :error-list-face 'flycheck-error-list-info :fringe-face 'flycheck-fringe-info)))
#+endcollapsible
Snippets
YASnippet is the premiere package for snippets, so let's install it.
(use-package yasnippet :init (yas-global-mode))
auto-activating-snippets
provides the very useful ability to automatically expand snippets while typing.
(use-package aas :hook (LaTeX-mode . ass-activate-for-major-mode) :hook (org-mode . ass-activate-for-major-mode) :hook (c-mode . ass-activate-for-major-mode) :hook (c++-mode . ass-activate-for-major-mode) :config (aas-set-snippets 'c-mode "u64" "uint64_t"~ "u32" "uint32_t" "u16" "uint16_t" "u8" "uint8_t" "i64" "int64_t" "i32" "int32_t" "i16" "int16_t" "i8" "int8_t" "sz" "size_t") (aas-set-snippets 'c++-mode "mxf" "Eigen::MatrixXf" "mxd" "Eigen::MatrixXd" "v2f" "Eigen::Vector2f" "v2d" "Eigen::Vector2d" "v2i" "Eigen::Vector2i" "v3f" "Eigen::Vector3f" "v3d" "Eigen::Vector3d" "v3i" "Eigen::Vector3i")) (use-package laas :config ; do whatever here (aas-set-snippets 'laas-mode ;; set condition! "mk" (lambda () (interactive) (yas-expand-snippet "$$1$$0")) "--" "—" :cond #'texmathp ; expand only while in math "tt" (lambda () (interactive) (yas-expand-snippet "\\text{$1}$0")) "bff" (lambda () (interactive) (yas-expand-snippet "\\mathbf{$1}$0")) "ll" "\\left" "rr" "\\right" "pm" (lambda () (interactive) (yas-expand-snippet "\\begin{pmatrix} $1 \\end{pmatrix} $0")) "sm" (lambda () (interactive) (yas-expand-snippet "\\left(\\begin{smallmatrix} $1 \\end{smallmatrix}\\right) $0")) ;; add accent snippets :cond #'laas-object-on-left-condition "qq" (lambda () (interactive) (laas-wrap-previous-object "sqrt")) ))
Git
Let's install the wonderful git porcelain Magit and some extra usefulness.
;; The ultimate Git porcelain. (use-package magit) ;; Show all TODOs in a git repo (use-package magit-todos) ;; Edit gitignores w/ highlighting (use-package gitignore-mode)
Language-Specific
(use-package rustic) (use-package cuda-mode) (use-package clojure-mode) (use-package cmake-mode) (use-package json-mode) (use-package rust-mode) ;; for when rustic breaks (use-package nim-mode) (use-package zig-mode) (use-package julia-mode) (use-package typescript-mode) (use-package css-mode)
TODO C++
(setq c-default-style "k&r") (setq-default c-basic-offset 4) (use-package modern-cpp-font-lock :init (modern-c++-font-lock-global-mode t)) (use-package ccls ; :ensure-system-package ccls :hook ((c-mode c++-mode cuda-mode) . (lambda () (require 'ccls) (lsp))) :custom (ccls-executable (executable-find "ccls")) ; Add ccls to path if you haven't done so (ccls-sem-highlight-method 'font-lock) (ccls-enable-skipped-ranges nil) :config (lsp-register-client (make-lsp-client :new-connection (lsp-tramp-connection (cons ccls-executable ccls-args)) :major-modes '(c-mode c++-mode cuda-mode) :server-id 'ccls-remote :multi-root nil :remote? t :notification-handlers (lsp-ht ("$ccls/publishSkippedRanges" #'ccls--publish-skipped-ranges) ("$ccls/publishSemanticHighlight" #'ccls--publish-semantic-highlight)) :initialization-options (lambda () ccls-initialization-options) :library-folders-fn nil))) ;; TODO bind/investigate ccls functions (use-package cpp-auto-include)
TODO Python
(use-package ein) (use-package lsp-mode :config (lsp-register-custom-settings '(("pyls.plugins.pyls_mypy.enabled" t t) ("pyls.plugins.pyls_mypy.live_mode" nil t) ("pyls.plugins.pyls_black.enabled" t t) ("pyls.plugins.pyls_isort.enabled" t t) ("pyls.plugins.flake8.enabled" t t))) (setq lsp-eldoc-enable-hover nil) :hook ((python-mode . lsp))) (use-package buftra :straight (:host github :repo "humitos/buftra.el")) (use-package py-pyment :straight (:host github :repo "humitos/py-cmd-buffer.el") :config (setq py-pyment-options '("--output=google"))) (use-package py-isort :straight (:host github :repo "humitos/py-cmd-buffer.el") :hook (python-mode . py-isort-enable-on-save) :config (setq py-isort-options '("-m=3" "-tc" "-fgw=0" "-ca"))) (use-package py-autoflake :straight (:host github :repo "humitos/py-cmd-buffer.el") :hook (python-mode . py-autoflake-enable-on-save) :config (setq py-autoflake-options '("--expand-star-imports"))) (use-package py-docformatter :straight (:host github :repo "humitos/py-cmd-buffer.el") :hook (python-mode . py-docformatter-enable-on-save) :config (setq py-docformatter-options '("--wrap-summaries=88" "--pre-summary-newline"))) (use-package blacken :straight t :hook (python-mode . blacken-mode) :config (setq blacken-line-length '100)) (use-package python-docstring :straight t :hook (python-mode . python-docstring-mode))
TODO Code Aesthetics
(use-package hl-todo :init (global-hl-todo-mode) (doom-color 'red) (setq hl-todo-keyword-faces `(("TODO" . ,(doom-color 'green)) ("FIXME" . ,(doom-color 'red)) ("DEBUG" . ,(doom-color 'magenta)) ("HACK" . ,(doom-color 'violet)) ("NOTE" . ,(doom-color 'cyan)))) ;; We already have todos in Org Mode! (add-hook 'org-mode-hook (lambda () (hl-todo-mode -1))) (set-face-attribute 'hl-todo nil :italic t) :bind (:map hl-todo-mode-map ("C-c t p" . hl-todo-previous) ("C-c t n" . hl-todo-next) ("C-c t i" . hl-todo-insert)))
(use-package rainbow-mode)
TODO Writing
(use-package flyspell) (use-package lexic :bind ("C-c w l" . lexic-search) ("C-c w w" . lexic-search-word-at-point)) (use-package gdoc :straight (gdoc :type git :host github :repo "jemoka/gdoc.el"))
Also, Google-Docs esque comments:
;; Google Docs style comments (use-package org-marginalia :straight (:host github :repo "nobiot/org-marginalia") :init (add-hook 'org-mode-hook 'org-marginalia-mode) (defun org-marginalia-save-and-open (point) (interactive "d") (org-marginalia-save) (org-marginalia-open point)) :bind (:map org-marginalia-mode-map ("C-c n o" . org-marginalia-save-and-open) ("C-c m" . org-marginalia-mark) ("C-c n ]" . org-marginalia-next) ("C-c n [" . org-marginalia-prev)))
TODO Vanilla++
(use-package crux :bind (("C-a" . crux-move-beginning-of-line) ;; Move to beginning of text, not line. ("C-x 4 t" . crux-transpose-windows) ("C-x K" . crux-kill-other-buffers) ("C-k" . crux-smart-kill-line)) :config (crux-with-region-or-buffer indent-region) (crux-with-region-or-buffer untabify) (crux-with-region-or-point-to-eol kill-ring-save) (defalias 'rename-file-and-buffer #'crux-rename-file-and-buffer)) (use-package goto-line-preview :init (general-define-key "M-g M-g" 'goto-line-preview "C-x n g" 'goto-line-relative-preview)) (use-package all-the-icons-dired :hook (dired-mode . all-the-icons-dired-mode)) (use-package diredfl :init (diredfl-global-mode)) (use-package anzu :init (global-anzu-mode) :bind (("M-r" . anzu-query-replace)))
Fun
FIXME
(use-package pdf-tools)
Exit Message
(setq exit-messages '( "Please don't leave, there's more demons to toast!" "Let's beat it -- This is turning into a bloodbath!" "I wouldn't leave if I were you. Vim is much worse." "Don't leave yet -- There's a demon around that corner!" "Ya know, next time you come in here I'm gonna toast ya." "Go ahead and leave. See if I care." "Are you sure you want to quit this great editor?" "Emacs will remember that." "Emacs, Emacs never changes." "Okay, look. We've both said a lot of things you're going to regret..." "Look, bud. You leave now and you forfeit your body count!" "Get outta here and go back to your boring editors." "You're lucky I don't smack you for thinking about leaving." "Don't go now, there's a dimensional shambler waiting at the prompt!" "Just leave. When you come back I'll be waiting with a bat." "Are you a bad enough dude to stay?" "It was worth the risk... I assure you." "I'm willing to take full responsibility for the horrible events of the last 24 hours." )) (defun random-choice (items) (let* ((size (length items)) (index (random size))) (nth index items))) (defun save-buffers-kill-emacs-with-confirm () (interactive) (if (null current-prefix-arg) (if (y-or-n-p (format "%s Quit? " (random-choice exit-messages))) (save-buffers-kill-emacs)) (save-buffers-kill-emacs))) (global-set-key "\C-x\C-c" 'save-buffers-kill-emacs-with-confirm)
Spotify
Smudge is nice.
(use-package smudge :straight (smudge :type git :host github :repo "danielfm/smudge" :fork (:host github :repo "richardfeynmanrocks/smudge")) :init (load "~/.emacs.d/straight/repos/smudge/smudge-connect.el") (setq smudge-status-location nil) ;; FIXME actively destructive to potential mode-line config! (setq global-mode-string '((" "))) (general-define-key :keymaps '(exwm-mode-map override-global-map) "C-S-s-l" 'smudge-controller-next-track "C-S-s-h" 'smudge-controller-previous-track "C-S-s-j" 'smudge-controller-volume-down "C-S-s-k" 'smudge-controller-volume-up "C-S-s-p" 'smudge-controller-toggle-play "C-S-s-s" 'smudge-controller-toggle-shuffle "C-S-s-r" 'smudge-controller-toggle-repeat) (global-smudge-remote-mode)) (use-package counsel-spotify) (load "~/.emacs.d/secrets.el")
Scratch
(setq org-agenda-scheduled-leaders '("" "")) (setq org-agenda-show-future-repeats nil) (set-face-attribute 'telega-msg-heading nil :background nil) (use-package emojify) (add-hook 'telega-chat-mode-hook 'emojify-mode) (add-hook 'telega-root-mode-hook 'emojify-mode) (defun make-thought-file () (let ((name (concat "~/sync/dump/" (format-time-string "%Y%m%d%H%M%S") ".org"))) (with-temp-file name) name)) (setq org-capture-templates '(("r" "Thought" entry (file make-thought-file) "* %T\n#+FILETAGS: %?") ("p" "Protocol" entry (file make-thought-file) "* %T\n#+FILETAGS: %?\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\nSource: [[%:link][%:description]]\n\n\n") ("L" "Protocol Link" entry (file make-thought-file) "* %T\n#+FILETAGS: %?\n [[%:link][%:description]]") )) (general-define-key "C-c r" (lambda () (interactive) (org-capture nil "r"))) (defun ndk/get-keyword-key-value (kwd) (let ((data (cadr kwd))) (list (plist-get data :key) (plist-get data :value)))) (defun org-current-buffer-get-tags () (nth 1 (assoc "FILETAGS" (org-element-map (org-element-parse-buffer 'greater-element) '(keyword) #'ndk/get-keyword-key-value)))) (defun org-file-get-tags (file) (with-current-buffer (find-file-noselect file) (org-current-buffer-get-tags))) (org-file-get-tags "~/sync/dump/20211112092430.org") (defun my-javadoc-return () "Advanced C-m for Javadoc multiline comments. Inserts `*' at the beggining of the new line if unless return was pressed outside the comment" (interactive) (setq last (point)) (setq is-inside (if (search-backward "*/" nil t) ;; there are some comment endings - search forward (search-forward "/*" last t) ;; it's the only comment - search backward (goto-char last) (search-backward "/*" nil t) ) ) ;; go to last char position (goto-char last) ;; the point is inside some comment, insert `* ' (if is-inside (progn (insert "\n* ") (indent-for-tab-command)) ;; else insert only new-line (insert "\n"))) (add-hook 'c-mode-common-hook (lambda () (local-set-key "\r" 'my-javadoc-return))) (setq org-html-htmlize-output-type 'css)
The End.
Well, that's it. We're done. Time to get going!
(require 'notifications) (notifications-notify :title "Up and at 'em!" :body (format "Loaded %d packages in %s with %d GCs." (length package-activated-list) (format "%.2f seconds" (float-time (time-subtract after-init-time before-init-time))) gcs-done))