82 KiB
Emacs Config
- Early Init
- COMMENT Elpaca
- Package Management
- COMMENT Benchmarking
- Misc Stuff
- Visuals
- Text
- Text Editor
- Buffers
- Windows
- Tabs/Workspaces
- Projects
- VEMCO
- App Launcher
- Dirvish/Dired
- Avy
- Helpful
- Terminals/Shells
- Undo Fu
- Which Key
- IDE Features
- Programming Languages
- Debugging
- Org-Mode
- Org-capture
- Magit
- Restclient
- Initial Buffer
- COMMENT Local variables
Early Init
Filename handler alist
Skipping a bunch of regular expression searching in the file-name-handler-alist should improve start time.
(defvar default-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
Garbage Collection
The default Garbage Collector is triggered at 800 KB, way too conservative, let's bump to 512 MB. Garbage collection is a big contributor to startup times. This fends it off, then is reset later by enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
;; -*- lexical-binding: t -*-
(setq gc-cons-threshold (expt 2 32))
(add-hook 'emacs-startup-hook
(lambda ()
"Restore defalut values after init."
(setq file-name-handler-alist default-file-name-handler-alist)
(if (boundp 'after-focus-change-function)
(add-function :after after-focus-change-function
(lambda ()
(unless (frame-focus-state)
(garbage-collect))))
(add-hook 'focus-out-hook 'garbage-collect))))
(setq native-comp-async-report-warnings-errors nil)
Disabling these classic visual options during early-init seems to net a 0.04 ms boost in init time
(setq max-specpdl-size 1200)
(setq max-lisp-eval-depth 800)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(menu-bar-mode -1)
(tooltip-mode -1)
Enhancements
Disable package.el, since we will be using elpaca.el. According to the elpaca.el documentation;
(setq package-enable-at-startup nil)
Prioritize old byte-compiled source files over newer sources. It saves us a little IO time to skip all the mtime checks on each lookup.
(setq load-prefer-newer nil)
(setq safe-local-variable-values
'((org-src-preserve-indentation . t)
(eval add-hook 'after-save-hook
'(lambda nil
(org-babel-tangle))
nil t)))
;; (setq server-name (format "Emacs-%d" (emacs-pid)))
;; (add-hook 'after-init-hook #'server-start)
Prevent instructions on how to close an emacsclient frame.
(setq server-client-instructions nil)
Implicitly resizing the Emacs frame adds to init time. Fonts larger than the system default can cause frame resizing, which adds to startup time.
(setq frame-inhibit-implied-resize t)
Ignore X resources.
(advice-add #'x-apply-session-resources :override #'ignore)
UTF-8 Support
(setq default-input-method nil)
(setq utf-translate-cjk-mode nil) ; disable CJK coding/encoding (Chinese/Japanese/Korean characters)
(set-language-environment 'utf-8)
(set-keyboard-coding-system 'utf-8-mac) ; For old Carbon emacs on OS X only
(setq locale-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
Finish up
(provide 'early-init)
;;; early-init.el ends here
COMMENT Elpaca
;; -*- lexical-binding: t -*-
(defvar elpaca-installer-version 0.3)
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
:ref nil
:files (:defaults (:exclude "extensions"))
:build (:not elpaca--activate-package)))
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
(build (expand-file-name "elpaca/" elpaca-builds-directory))
(order (cdr elpaca-order))
(default-directory repo))
(add-to-list 'load-path (if (file-exists-p build) build repo))
(unless (file-exists-p repo)
(make-directory repo t)
(condition-case-unless-debug err
(if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
((zerop (call-process "git" nil buffer t "clone"
(plist-get order :repo) repo)))
((zerop (call-process "git" nil buffer t "checkout"
(or (plist-get order :ref) "--"))))
(emacs (concat invocation-directory invocation-name))
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
((require 'elpaca))
((elpaca-generate-autoloads "elpaca" repo)))
(kill-buffer buffer)
(error "%s" (with-current-buffer buffer (buffer-string))))
((error) (warn "%s" err) (delete-directory repo 'recursive))))
(unless (require 'elpaca-autoloads nil t)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" repo)
(load "./elpaca-autoloads")))
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca `(,@elpaca-order))
(setq elpaca-queue-limit 30)
Package Management
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
(setq package-archives
'(("elpa" . "https://elpa.gnu.org/packages/")
("elpa-devel" . "https://elpa.gnu.org/devel/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
;; Add MELPA to `package-archives'
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
;; Proof-of-concept to install a list of packages
(mapc
(lambda (package)
(unless (package-installed-p package)
(package-install package)))
'(recentf
benchmark-init
;; dashboard
;; ligature
;; hydra
;; multiple-cursors
;; Text Editing
evil
evil-collection
evil-surround
evil-snipe
evil-commentary
evil-goggles
avy
all-the-icons-ibuffer
;; ace-window
;; Mail
smtpmail
sendmail
age
popper
centaur-tabs
;; VEMCO
vertico
vertico-posframe
savehist
embark
embark-consult
marginalia
orderless
consult
consult-eglot
all-the-icons-completion
helpful
vterm
;; Enhancements
olivetti
doom-modeline
undo-fu
undo-fu-session
vundo
which-key
mono-complete
beframe
;; company
;; lsp-mode
;; lsp-ui
;; flycheck
;; dap-mode
;; Org
org-bullets
org-kanban
org-fancy-priorities
org-roam
org-download
;; Programming Languages
highlight-quoted
rustic
ob-rust
haskell-mode
clojure-mode
cider
fsharp-mode
go-mode
json-mode
markdown-mode
typescript-mode
elm-mode
gdscript-mode
;; Tools
dirvish
restclient
disaster
magit))
(setopt package-vc-selected-packages
'((dotenv :url "https://github.com/pkulev/dotenv.el")
(doom-themes :url "https://github.com/JosephFerano/doom-themes")
(pico8-mode :url "https://github.com/Kaali/pico8-mode")
(app-launcher :url "https://github.com/SebastienWae/app-launcher")))
(package-initialize)
COMMENT Benchmarking
This is commented out since it adds ever so slightly to init time, but keep it around in case we need to benchmark slow init times later.
(require 'benchmark-init)
(add-hook 'after-init-hook 'benchmark-init/deactivate)
Misc Stuff
(setq default-directory "/home/joe/")
(setq vc-follow-symlinks t) ; Visit real file when editing a symlink without prompting.
(global-auto-revert-mode t) ; Revert buffer's file when the file changes on disk
(setq confirm-kill-emacs 'y-or-n-p)
(require 'recentf)
(recentf-mode t)
(setq recentf-auto-cleanup 10)
(setq recentf-keep '(file-remote-p file-readable-p))
(setq recentf-max-saved-items 1000)
(setq package-native-compile t)
This avoids those annoying #backup# files that get added and eventually slow down loading the file again.
(setq auto-save-default nil)
(setq create-lockfiles nil)
(setq use-dialog-box nil)
(fset 'yes-or-no-p 'y-or-n-p)
(setq large-file-warning-threshold 100000000)
(setq backup-directory-alist `((".*" . ,(expand-file-name "backups" user-emacs-directory))))
(setq backup-by-copying t
delete-old-versions t
kept-new-versions 6
kept-old-versions 2
version-control t)
I don't even know how you resume from GUI mode, we'll find a use for this keybinding later on
(when (display-graphic-p)
(global-unset-key (kbd "C-z")))
Visuals
COMMENT Dashboard
Use Dashboard.el. First load `all-the-icons` for nicer rendering
;; (elpaca 'all-the-icons)
;; (package-vc-install (dashboard :ref)
(require 'dashboard)
(dashboard-setup-startup-hook)
(setq dashboard-items '((recents . 6)
(projects . 5)
(bookmarks . 5)))
(setq dashboard-startup-banner 'logo)
(setq dashboard-center-content t)
(setq dashboard-set-file-icons t)
(setq dashboard-set-navigator t)
(setq dashboard-set-heading-icons t)
(setq dashboard-projects-backend 'project-el)
(add-hook 'dashboard-mode-hook
(lambda ()
(setq-local line-spacing 12)
;; TODO: It's not jumping
(dashboard-jump-to-recents)))
;; (defun joe/launch-dashboard ()
;; "Jump to the dashboard buffer, if doesn't exists create one."
;; (interactive)
;; (switch-to-buffer dashboard-buffer-name)
;; (dashboard-mode)
;; (dashboard-insert-startupify-lists))
Olivetti
(require 'olivetti)
(setq olivetti-minimum-body-width 100)
(global-set-key (kbd "C-x x o") 'olivetti-mode)
Themes
;; Small changes to my favorite themes
;; (load-theme 'doom-nord-light))
(setq custom-safe-themes t)
(custom-set-faces
'(dashboard-items-face ((t (:inherit widget-button :weight normal)))))
We want to add whatever custom theme we selected to the custom variables since
emacs will just automatically read it on startup but we don't want emacs to add
these settings to init.el, which in our case is worse since we have a literate
file. Send all custom variables to custom.el
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)
I copied this from consult so that I could add the customize-save-variable at the end, so if I load another emacs process, it can have the same thing.
(defun joe/consult-theme (theme)
"Disable current themes and enable THEME from `consult-themes'.
The command supports previewing the currently selected theme."
(interactive
(list
(let* ((regexp (consult--regexp-filter
(mapcar (lambda (x) (if (stringp x) x (format "\\`%s\\'" x)))
consult-themes)))
(avail-themes (seq-filter
(lambda (x) (string-match-p regexp (symbol-name x)))
(cons 'default (custom-available-themes))))
(saved-theme (car custom-enabled-themes)))
(consult--read
(mapcar #'symbol-name avail-themes)
:prompt "Theme: "
:require-match t
:category 'theme
:history 'consult--theme-history
:lookup (lambda (selected &rest _)
(setq selected (and selected (intern-soft selected)))
(or (and selected (car (memq selected avail-themes)))
saved-theme))
:state (lambda (action theme)
(pcase action
('return (consult-theme (or theme saved-theme)))
((and 'preview (guard theme)) (consult-theme theme))))
:default (symbol-name (or saved-theme 'default))))))
(when (eq theme 'default) (setq theme nil))
(unless (eq theme (car custom-enabled-themes))
(mapc #'disable-theme custom-enabled-themes)
(when theme
(if (custom-theme-p theme)
(enable-theme theme)
(load-theme theme :no-confirm))
))
(customize-save-variable 'custom-enabled-themes custom-enabled-themes))
Other
Setup other stuff
(setq ring-bell-function 'ignore)
(when (equal "flowjoe-f37" (system-name))
(add-to-list 'default-frame-alist '(undecorated . t))
(add-to-list 'default-frame-alist '(fullscreen . maximized)))
(add-to-list 'default-frame-alist '(vertical-scroll-bars . nil))
;; (setq default-frame-alist '(
;; (vertical-scroll-bars)
;; (right-divider-width . 5)
;; (tab-bar-lines . 0)
;; (internal-border-width . 12)))
(add-hook 'text-mode-hook (lambda () (setq fill-column 80) (turn-on-auto-fill)))
(setq-default display-line-numbers 'relative)
(make-variable-buffer-local 'global-hl-line-mode)
(defun joe/disable-line-numbers () (display-line-numbers-mode 0))
(dolist (mode '( dashboard-mode-hook org-mode-hook term-mode-hook eww-mode-hook
vterm-mode-hook dirvish-directory-view-mode-hook eshell-mode-hook
dired-mode-hook shell-mode-hook magit-mode-hook compilation-mode-hook
mu4e-headers-mode-hook mu4e-main-mode-hook))
(add-hook mode #'joe/disable-line-numbers))
(set-window-margins nil 0)
(setq-default right-fringe-width 10)
(setq scroll-margin 0
scroll-conservatively 100000
scroll-preserve-screen-position 1)
(global-hl-line-mode +1)
(column-number-mode +1)
(modify-all-frames-parameters
'((right-divider-width . 5)
(tab-bar-lines . 0)
(internal-border-width . 12)))
(when (>= emacs-major-version 29)
(pixel-scroll-precision-mode t))
(setq inhibit-startup-screen t)
;; Don’t compact font caches during GC, in case doom modeline gets laggy issue
(setq inhibit-compacting-font-caches t)
(require 'highlight-quoted)
(add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)
(require 'doom-modeline)
(doom-modeline-mode)
(doom-modeline-def-modeline 'main
'(bar modals bar window-number matches buffer-info remote-host buffer-position word-count selection-info)
'(parrot objed-state misc-info battery grip irc mu4e gnus github debug repl lsp bar input-method indent-info buffer-encoding bar major-mode process))
;; Show the tab names, just put this at the car of the previous list
;; workspace-name
;; Set default mode-line
(add-hook 'doom-modeline-mode-hook
(lambda ()
(doom-modeline-set-modeline 'main 'default)))
;; TODO Likely not needed anymore
(dolist (mode '(dired-mode-hook lsp-help-mode-hook fundamental-mode-hook))
(add-hook mode (lambda () (setq truncate-lines t))))
Ligatures… are they that useful?
(require 'ligature)
(global-ligature-mode)
Text
;; (set-face-attribute 'default nil :family "Fira Code Nerd Font" :height 150)
(let ((height (if (equal "flowjoe-f37" (system-name))
115
130)))
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font Mono" :height height))
;; (set-face-attribute 'default nil :family "JetBrainsMono Nerd Font Mono" :height 115)
;; (set-face-attribute 'variable-pitch nil :family "Source Code Pro" :height 120)
(setq-default c-basic-offset 4) ;; This is annoying
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)
(setq-default line-spacing 5)
(setq indent-line-function #'indent-relative)
(set-default 'truncate-lines t)
;; (add-hook 'before-save-hook 'whitespace-cleanup)
Text Editor
Emacs is an great operating system, if only it had a good text editor…
Text editing
;; TODO Find out what to do with this
(defun joe/bookmark-set-and-save ()
"Save the current buffer as a bookmark"
(interactive)
(bookmark-set)
(bookmark-save))
(global-set-key (kbd "M-z") #'zap-up-to-char)
(global-set-key (kbd "M-Z") #'zap-to-char)
(defun joe/duplicate-line-comment ()
(interactive)
(let ((col (current-column)))
(duplicate-line)
(comment-line 1)
(move-to-column col)))
(global-set-key (kbd "C-c d") 'duplicate-line)
(global-set-key (kbd "C-c C-;") 'joe/duplicate-line-comment)
(global-set-key (kbd "M-n") (kbd "C-u 1 C-v"))
(global-set-key (kbd "M-p") (kbd "C-u 1 M-v"))
(defun joe/insert-line-below ()
"Insert an empty line below the current line."
(interactive)
(save-excursion
(end-of-line)
(open-line 1)))
(defun joe/insert-line-above ()
"Insert an empty line above the current line."
(interactive)
(save-excursion
(end-of-line 0)
(open-line 1)))
(global-set-key (kbd "M-o") #'joe/insert-line-below)
(global-set-key (kbd "M-O") #'joe/insert-line-above)
Stole this from Purcell but didn't feel like making it a package depencendy
(defun unfill-toggle ()
"Toggle filling/unfilling of the current region.
Operates on the current paragraph if no region is active."
(interactive)
(let (deactivate-mark
(fill-column
(if (eq last-command this-command)
(progn (setq this-command nil)
most-positive-fixnum)
fill-column)))
(call-interactively 'fill-paragraph)))
For the longest time I had no idea why the (
and )
vim motions for sentences
weren't working, until I randomly saw this in someone's init.el
(setq sentence-end-double-space nil)
#+end_src
COMMENT Hydra
(require 'hydra)
(defhydra hydra-navigate (global-map "<f2>")
"Window Navigation"
("q" nil "quit")
("d" joe/smooth-scroll-half-page-down "half page down")
("u" joe/smooth-scroll-half-page-up "half page up")
("e" joe/smooth-scroll-short-down "line down")
("y" joe/smooth-scroll-short-up "line up")
("n" next-line "line down")
("p" previous-line "line up")
("M-r" move-to-window-line-top-bottom "Reposition cursor"))
COMMENT Multiple Cursors
(require 'multiple-cursors)
COMMENT Meow
(elpaca 'meow)
(defun meow-setup ()
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
(meow-motion-overwrite-define-key
'("j" . meow-next)
'("k" . meow-prev)
'("<escape>" . ignore))
(meow-leader-define-key
;; SPC j/k will run the original command in MOTION state.
'("j" . "H-j")
'("k" . "H-k")
;; Use SPC (0-9) for digit arguments.
'("1" . meow-digit-argument)
'("2" . meow-digit-argument)
'("3" . meow-digit-argument)
'("4" . meow-digit-argument)
'("5" . meow-digit-argument)
'("6" . meow-digit-argument)
'("7" . meow-digit-argument)
'("8" . meow-digit-argument)
'("9" . meow-digit-argument)
'("0" . meow-digit-argument)
'("/" . meow-keypad-describe-key)
'("?" . meow-cheatsheet))
(meow-normal-define-key
'("0" . meow-expand-0)
'("9" . meow-expand-9)
'("8" . meow-expand-8)
'("7" . meow-expand-7)
'("6" . meow-expand-6)
'("5" . meow-expand-5)
'("4" . meow-expand-4)
'("3" . meow-expand-3)
'("2" . meow-expand-2)
'("1" . meow-expand-1)
'("-" . negative-argument)
'(";" . meow-reverse)
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("[" . meow-beginning-of-thing)
'("]" . meow-end-of-thing)
'("a" . meow-append)
'("A" . meow-open-below)
'("b" . meow-back-word)
'("B" . meow-back-symbol)
'("c" . meow-change)
'("d" . meow-delete)
'("D" . meow-backward-delete)
'("e" . meow-next-word)
'("E" . meow-next-symbol)
'("f" . meow-find)
'("g" . meow-cancel-selection)
'("G" . meow-grab)
'("h" . meow-left)
'("H" . meow-left-expand)
'("i" . meow-insert)
'("I" . meow-open-above)
'("j" . meow-next)
'("J" . meow-next-expand)
'("k" . meow-prev)
'("K" . meow-prev-expand)
'("l" . meow-right)
'("L" . meow-right-expand)
'("m" . meow-join)
'("n" . meow-search)
'("o" . meow-block)
'("O" . meow-to-block)
'("p" . meow-yank)
'("q" . meow-quit)
'("Q" . meow-goto-line)
'("r" . meow-replace)
'("R" . meow-swap-grab)
'("s" . meow-kill)
'("t" . meow-till)
'("u" . meow-undo)
'("U" . meow-undo-in-selection)
'("v" . meow-visit)
'("w" . meow-mark-word)
'("W" . meow-mark-symbol)
'("x" . meow-line)
'("X" . meow-goto-line)
'("y" . meow-save)
'("Y" . meow-sync-grab)
'("z" . meow-pop-selection)
'("'" . repeat)
'("<escape>" . ignore)))
(require 'meow)
(meow-setup)
(meow-global-mode t)
(setq scroll-preserve-screen-position nil)
Boon
(defun joe/psp-scroll-down-half-page ()
(interactive)
(pixel-scroll-precision-scroll-down-page (/ (window-pixel-height) 2)))
(defun joe/psp-scroll-up-half-page ()
(interactive)
(pixel-scroll-precision-scroll-up-page (/ (window-pixel-height) 2)))
(elpaca 'boon
(require 'boon-qwerty)
(boon-mode)
(define-key boon-moves-map "h" 'backward-char)
(define-key boon-moves-map "j" 'next-line)
(define-key boon-moves-map "k" 'previous-line)
(define-key boon-moves-map "l" 'forward-char)
(define-key boon-moves-map "b" 'boon-smarter-backward)
(define-key boon-moves-map "w" 'boon-smarter-forward)
(define-key boon-moves-map "q" '("hop" . avy-goto-char-2))
(define-key boon-command-map (kbd "C-k") #'joe/scroll-down-line)
(define-key boon-command-map (kbd "C-j") #'joe/scroll-up-line)
(define-key boon-command-map (kbd "C-d") #'joe/psp-scroll-down-half-page)
(define-key boon-command-map (kbd "C-u") #'joe/psp-scroll-up-half-page)
(defun joe/scroll-up-line () (interactive) (scroll-up-line 2))
(defun joe/scroll-down-line () (interactive) (scroll-down-line 2))
(define-key boon-moves-map "H" 'backward-paragraph)
(define-key boon-moves-map "L" 'forward-paragraph)
(define-key boon-moves-map "K" 'boon-smarter-upward)
(define-key boon-moves-map "J" 'boon-smarter-downward)
(define-key boon-moves-map "o" 'boon-open-next-line-and-insert)
(define-key boon-moves-map "O" 'boon-open-line-and-insert)
(define-key boon-moves-map "i" 'boon-set-insert-like-state)
(define-key boon-moves-map "r" 'boon-replace-by-character)
(define-key boon-moves-map "y" 'boon-replace-by-character)
(define-key boon-moves-map "p" 'boon-splice)
(define-key boon-moves-map "y" 'boon-treasure-region)
(define-key ctl-x-map "s" 'save-buffer))
Evil
(setq evil-want-keybinding nil)
(setq evil-undo-system 'undo-fu)
(setq evil-want-C-u-scroll t)
(setq evil-want-Y-yank-to-eol t)
(setq evil-disable-insert-state-bindings t)
(setq evil-echo-state nil)
(require 'evil)
(evil-mode t)
(evil-global-set-key 'insert (kbd "C-w") #'evil-delete-backward-word)
(evil-define-key 'normal 'global (kbd "q") 'avy-goto-word-0)
;; vv to expand selection to line
(evil-global-set-key 'visual
(kbd "v")
(lambda ()
(interactive)
(evil-first-non-blank)
(exchange-point-and-mark)
(evil-end-of-line)))
(defvar joe/evil-space-mode-map (make-sparse-keymap)
"High precedence keymap.")
(define-minor-mode joe/evil-space-mode
"Global minor mode for higher precedence evil keybindings."
:global t)
(joe/evil-space-mode)
(dolist (state '(normal visual insert))
(evil-make-intercept-map
;; NOTE: This requires an evil version from 2018-03-20 or later
(evil-get-auxiliary-keymap joe/evil-space-mode-map state t t)
state))
(evil-define-key 'normal joe/evil-space-mode-map
(kbd "K") #'eldoc
(kbd "SPC t") tab-prefix-map
(kbd "SPC p") project-prefix-map
(kbd "SPC q") 'kill-buffer-and-window
(kbd "SPC k") 'kill-this-buffer
(kbd "SPC h") 'help-command
(kbd "SPC hf") 'helpful-callable
(kbd "SPC hv") 'helpful-variable
(kbd "SPC hk") 'helpful-key
(kbd "SPC ho") 'helpful-symbol
(kbd "SPC hg") 'helpful-at-point
(kbd "SPC fb") 'bookmark-jump
(kbd "SPC fr") 'consult-recent-file
(kbd "SPC ff") 'project-find-file
(kbd "SPC fa") '(lambda () (interactive) (project-find-file t))
(kbd "SPC fi") 'joe/edit-init
(kbd "SPC bl") 'mode-line-other-buffer
(kbd "SPC ba") 'switch-to-buffer
(kbd "SPC bb") 'consult-buffer
(kbd "SPC bi") 'ibuffer
(kbd "SPC bm") 'joe/toggle-buffer-mode
(kbd "SPC br") 'joe/revert-buffer-no-confirm
(kbd "SPC gg") 'magit-status
(kbd "SPC gc") 'magit-clone
(kbd "SPC ss") 'joe/vterm-here
(kbd "SPC sv") 'vterm
(kbd "SPC Ba") 'joe/bookmark-set-and-save
(kbd "SPC Bd") 'bookmark-delete
(kbd "SPC mr") 'joe/compile-run
(kbd "SPC mc") 'joe/compile-comp
(kbd "SPC ct") 'joe/consult-theme
(kbd "SPC cl") 'consult-line
(kbd "SPC ci") 'consult-imenu
(kbd "SPC cy") 'consult-yank-from-kill-ring
(kbd "SPC cg") 'consult-ripgrep
(kbd "SPC cF") 'consult-find
(kbd "SPC co") 'consult-outline)
(define-key evil-window-map "u" #'winner-undo)
(define-key evil-window-map "U" #'winner-redo)
(defvar joe-mode-map
(let ((map (make-sparse-keymap)))
;; (define-key map (kbd "C-'") #'embark-act)
map)
"my-keys-minor-mode keymap.")
(define-key joe/evil-space-mode-map (kbd "M-'") #'embark-dwim)
(define-key joe/evil-space-mode-map (kbd "C-'") #'embark-act)
(define-key joe/evil-space-mode-map (kbd "C-/") #'comment-line)
(defun joe/scroll-up-line () (interactive) (scroll-up-line 2))
(defun joe/scroll-down-line () (interactive) (scroll-down-line 2))
(evil-global-set-key 'normal (kbd "C-e") #'joe/scroll-up-line)
(evil-global-set-key 'normal (kbd "C-y") #'joe/scroll-down-line)
(require 'evil-collection)
(evil-collection-init)
(require 'evil-surround)
(global-evil-surround-mode t)
(require 'evil-snipe)
(evil-snipe-override-mode +1)
(require 'evil-commentary)
(evil-commentary-mode t)
(require 'evil-goggles)
(evil-goggles-mode t)
(setq evil-goggles-duration 0.075)
(setq evil-goggles-pulse t)
(setq evil-goggles-async-duration 0.55)
Buffers
(defun joe/kill-this-buffer-or-popup ()
(interactive)
"Kill the buffer normally, but if it's a popper popup, call the popper version"
(with-current-buffer (current-buffer)
(if (or (eq popper-popup-status nil)
(eq popper-popup-status 'raised))
(kill-this-buffer)
(popper-kill-latest-popup))))
(global-set-key (kbd "C-x k") #'joe/kill-this-buffer-or-popup)
(global-set-key (kbd "C-x M-k") #'kill-buffer)
(require 'all-the-icons-ibuffer)
(add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode)
(global-set-key [remap list-buffers] 'ibuffer)
(global-set-key (kbd "C-x B") 'ibuffer)
(global-set-key (kbd "C-x b") 'consult-project-buffer)
(global-set-key (kbd "C-x C-b") 'consult-buffer)
(defun joe/switch-other-buffer ()
"Switch to other buffer"
(interactive)
(switch-to-buffer (other-buffer)))
(global-set-key (kbd "C-x l") 'joe/switch-other-buffer)
(save-place-mode t)
(setq save-place-file (expand-file-name "places" user-emacs-directory))
The theme of `C-x 4` bindings is that they operate on other windows, so this function matches that behavior.
(defun joe/kill-other-buffer-and-window ()
"Kill other buffer and window"
(interactive)
(other-window 1)
(kill-buffer-and-window))
(global-set-key (kbd "C-x 4 0") 'joe/kill-other-buffer-and-window)
(global-set-key (kbd "C-x C-0") 'kill-buffer-and-window)
Windows
Window Management
(add-hook 'after-init-hook (lambda () (winner-mode t)))
(setq joe/popper-side-toggle 'right)
(defun joe/window-split-vertical () (interactive) (set 'joe/popper-side-toggle 'right))
(defun joe/window-split-horizontal () (interactive) (set 'joe/popper-side-toggle 'below))
(define-key ctl-x-4-map (kbd "|") #'joe/window-split-vertical)
(define-key ctl-x-4-map (kbd "-") #'joe/window-split-horizontal)
(define-key ctl-x-4-map (kbd "t") #'rotate-window)
(global-set-key (kbd "s-h") #'windmove-left)
(global-set-key (kbd "s-l") #'windmove-right)
(global-set-key (kbd "s-k") #'windmove-up)
(global-set-key (kbd "s-j") #'windmove-down)
;; There's a bug in Gnome where frames are resized to the wrong dimensions
(defun joe/resize-frames ()
(interactive)
(dolist (frame (frame-list))
(toggle-frame-maximized frame)
(toggle-frame-maximized frame)))
(global-set-key (kbd "s-<f10>") #'joe/resize-frames)
Ace Window will show a hint if there are more than 2 windows, but I don't really use it
(require 'ace-window)
(global-set-key (kbd "C-x o") #'ace-window)
(global-set-key (kbd "C-x C-o") #'ace-swap-window)
;; TODO Prot help improving workflow
(global-set-key (kbd "C-`") #'window-toggle-side-windows)
(defvar joe/side-window-buffers '("^\\*Flycheck errors\\*$"
"^\\*Completions\\*$"
"^\\*Occur\\*$"
"^\\*Help\\*$"
"^\\*Embark.*"
"^\\*helpful .*"
"^\\*Compilation\\*$"
"^\\*SQL.*"
"^\\*HTTP Response\\*$"
"^\\*grep\\*$"
"^\\*Colors\\*$"
"^\\*Async Shell Command\\*$"))
(dolist (bufname joe/side-window-buffers)
(add-to-list 'display-buffer-alist
`(,bufname
display-buffer-in-side-window
(side . right)
(window-width . 0.43)
(slot . 0)
(window-parameters
(no-delete-other-windows . t)))))
Beframe
(defvar consult-buffer-sources)
(declare-function consult--buffer-state "consult")
(with-eval-after-load 'consult
(defface beframe-buffer
'((t :inherit font-lock-string-face))
"Face for `consult' framed buffers.")
;; If you want to filter the current buffer you can use this and replace :items in the
;; beframe-consult-source var
;; (defun joe/consult-beframe-names-minus-current ()
;; (delete (buffer-name) (beframe-buffer-names)))
;; :items ,#'joe/consult-beframe-names-minus-current
(defvar beframe-consult-source
`( :name "Frame-specific buffers (current frame)"
:narrow ?F
:category buffer
:face beframe-buffer
:history beframe-history
:items ,#'beframe-buffer-names
:action ,#'switch-to-buffer
:state ,#'consult--buffer-state))
(add-to-list 'consult-buffer-sources 'beframe-consult-source))
(setq beframe-create-frame-scratch-buffer nil)
(beframe-mode +1)
Popper
(require 'popper)
(setq popper-reference-buffers
'("\\*compilation\\*" compilation-mode
"^\\*vterm\\*" vterm-mode
"^\\*Flymake.*" flymake-mode
"^\\*Flycheck.*" flycheck-error-list-mode
"^\\*Occur\\*$" occur-mode
"^\\*lsp-help\\*" lsp-help-mode
"^\\*eldoc\\*" special-mode
"^\\*godot.*" godot-mode
"^\\*ert\\*" ert-results-mode
"^\\*HTTP Response\\*" javascript-mode
"^\\*SQL.*" sql-interactive-mode
"^\\*cargo-test\\*" cargo-test-mode
"^\\*cargo-run\\*" cargo-run-mode
"^\\*rustic-compilation\\*" rustic-compilation-mode
"^\\*ansi-term\\*$" term-mode
"^\\*Async Shell Command\\*$" shell-mode
("^\\*Warnings\\*$" . hide)
help-mode
helpful-mode))
(global-set-key (kbd "C-`") 'popper-toggle-latest)
(global-set-key (kbd "C-~") 'popper-cycle)
(global-set-key (kbd "C-M-`") 'popper-toggle-type)
(require 'popper-echo)
(popper-echo-mode t)
(defun joe/get-popper-dir ()
(with-current-buffer (current-buffer)
(if (or (> (window-width) 170) (eq olivetti-mode t))
joe/popper-side-toggle
'below)))
;; TODO Prot We need to revisit this function and change the rules; I
;; just saw that Popper uses side-window for it's internal popup at
;; the bottom function. I prefer the way Popper does it for anything
;; that is going to display horizontally at the bottom. However for
;; side splits I much prefer the functionality we have here where if
;; there's a window on the right, then overtake it and then when I
;; close it, keep whatever window was there open. The issue with
;; side-windows is that they keep the two window splits in tact and
;; that's not what I want. Sort of like how Magit does it, that's what
;; I want to copy. But like I have it here, it should respect my
;; "popper side" variable so I can toggle whether it appears at the
;; bottom or above.
(defun joe/popper-display-func (buffer &optional _alist)
(cond
((eq joe/popper-side-toggle 'below)
(popper-select-popup-at-bottom buffer _alist))
((when-let ((popup-buffer
(cl-find-if
#'popper-popup-p
(mapcar #'window-buffer (window-list)))))
(window--display-buffer
buffer (get-buffer-window popup-buffer) 'reuse
`((body-function . ,#'select-window)))))
((and (eq joe/popper-side-toggle 'right)
(window-in-direction 'left))
(window--display-buffer
buffer (get-buffer-window (current-buffer)) 'reuse))
((when-let ((right-window (window-in-direction 'right))
((eq joe/popper-side-toggle 'right)))
(window--display-buffer
buffer right-window 'reuse
`((body-function . ,#'select-window)))))
((and (not (window-in-direction 'right))
(not (window-in-direction 'left)))
(display-buffer-in-direction
buffer
`((window-height . 0.45)
(window-width . 0.45)
(direction . right)
(body-function . ,#'select-window))))
(t
(display-buffer-in-direction
buffer
`((window-height . 0.45)
(window-width . 0.45)
(direction . below)
(body-function . ,#'select-window))))))
(setq popper-display-function #'joe/popper-display-func)
(popper-mode t)
COMMENT Tab-bar & Tab-line
(global-set-key (kbd "s-n") #'tab-line-switch-to-next-tab)
(global-set-key (kbd "s-p") #'tab-line-switch-to-prev-tab)
COMMENT Scrolling
(require 'pixel-scroll)
(setq pixel-scroll-precision-large-scroll-height 10.0)
(setq pixel-scroll-precision-interpolation-factor 30)
(defun joe/pixel-scroll-lerp (amount direction)
(let ((half-height (* direction (/ (window-height) amount)))
(point-min-or-max (if (> direction 0) (point-min) (point-max))))
(when (or (and (pos-visible-in-window-p (point-min))
(< direction 0)))
(pixel-scroll-precision-interpolate (* 5 half-height)))
(pixel-scroll-precision-interpolate (* 5 half-height))))
(defun joe/smooth-scroll-half-page-down ()
"Smooth scroll down"
(interactive)
(joe/pixel-scroll-lerp 1.5 -1))
;; (pixel-scroll-kbd-up))
(defun joe/smooth-scroll-half-page-up ()
"Smooth scroll up"
(interactive)
(joe/pixel-scroll-lerp 1.5 1))
(defun joe/smooth-scroll-short-down ()
"Smooth scroll down"
(interactive)
(joe/pixel-scroll-lerp 8 -1))
(defun joe/smooth-scroll-short-up ()
"Smooth scroll down"
(interactive)
(joe/pixel-scroll-lerp 8 1))
;; scroll-up-command
(global-set-key (kbd "C-v") #'joe/smooth-scroll-half-page-down)
(global-set-key (kbd "M-v") #'joe/smooth-scroll-half-page-up)
(global-set-key (kbd "C-S-v") #'joe/smooth-scroll-short-down)
(global-set-key (kbd "M-S-v") #'joe/smooth-scroll-short-up)
(defun joe/scroll-other-half-down ()
(interactive)
(scroll-other-window 8))
(defun joe/scroll-other-half-up ()
(interactive)
(scroll-other-window -8))
(global-set-key (kbd "C-M-v") #'joe/scroll-other-half-down)
(global-set-key (kbd "C-M-S-V") #'joe/scroll-other-half-up)
(require 'topspace)
Tabs/Workspaces
COMMENT Centaur Tabs
;; (require 'centaur-tabs)
(setq centaur-tabs-set-bar 'under)
(setq x-underline-at-descent-line t)
(setq centaur-tabs-set-close-button nil)
(setq centaur-tabs-set-icons t)
(setq centaur-tabs-show-navigation-buttons nil)
(setq centaur-tabs-set-close-button nil)
(setq centaur-tabs-set-modified-marker nil)
(setq centaur-tabs-show-new-tab-button nil)
(setq centaur-tabs-label-fixed-length 16)
(defun joe/forward-tab-or-popup ()
(interactive)
(if (popper-popup-p (current-buffer))
(popper-cycle)
(centaur-tabs-forward-tab)))
(defun joe/backward-tab-or-popup ()
(interactive)
(if (popper-popup-p (current-buffer))
(popper-cycle)
(centaur-tabs-backward-tab)))
;; (global-set-key (kbd "s-n") #'centaur-tabs-forward-tab)
;; (global-set-key (kbd "s-p") #'centaur-tabs-backward-tab)
(global-set-key (kbd "s-n") #'joe/forward-tab-or-popup)
(global-set-key (kbd "s-p") #'joe/backward-tab-or-popup)
(global-set-key (kbd "s-N") #'centaur-tabs-forward-group)
(global-set-key (kbd "s-P") #'centaur-tabs-backward-group)
(global-set-key (kbd "C-s-p") #'centaur-tabs-move-current-tab-to-left)
(global-set-key (kbd "C-s-n") #'centaur-tabs-move-current-tab-to-right)
(dolist (mode '(dashboard-mode-hook))
(add-hook mode 'centaur-tabs-local-mode))
(with-eval-after-load 'centaur-tabs
(defun joe/fix-centaur-tabs (THEME)
(centaur-tabs-mode -1)
(centaur-tabs-mode))
(advice-add 'consult-theme :after #'joe/fix-centaur-tabs)
(defun centaur-tabs-buffer-groups ()
"`centaur-tabs-buffer-groups' control buffers' group rules.
Group centaur-tabs with mode if buffer is derived from `eshell-mode' `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'.
All buffer name start with * will group to \"Emacs\".
Other buffer group by `centaur-tabs-get-group-name' with project name."
(list
(cond
((or (derived-mode-p 'comint-mode)
(derived-mode-p 'sql-interactive-mode)
(string-match "*HTTP" (buffer-name))
(derived-mode-p 'compilation-mode))
"REPLs")
((or (and (string-equal "*" (substring (buffer-name) 0 1))
(not (string-match "*Org Src" (buffer-name))))
(memq major-mode '(magit-process-mode
magit-status-mode
magit-diff-mode
magit-log-mode
magit-file-mode
magit-blob-mode
magit-blame-mode)))
"*Buffers*")
(t
"Emacs")))))
(centaur-tabs-mode +1)
COMMENT iflipb
(global-set-key (kbd "s-n") #'iflipb-next-buffer)
(global-set-key (kbd "s-p") #'iflipb-previous-buffer)
(setq iflipb-permissive-flip-back t)
(setq iflipb-other-buffer-template " %s ")
(setq iflipb-current-buffer-template "<[%s]>")
(setq iflipb-buffer-list-function #'tabspaces--buffer-list)
COMMENT Tabspaces
(tabspaces-mode +1)
;; Filter Buffers for Consult-Buffer
(with-eval-after-load 'consult
;; hide full buffer list (still available with "b" prefix)
(consult-customize consult--source-buffer :hidden t :default nil)
;; set consult-workspace buffer list
(defvar consult--source-workspace
(list :name "Workspace Buffers"
:narrow ?w
:history 'buffer-name-history
:category 'buffer
:state #'consult--buffer-state
:default t
:items (lambda () (consult--buffer-query
:predicate #'tabspaces--local-buffer-p
:sort 'visibility
:as #'buffer-name)))
"Set workspace buffer list for consult-buffer.")
(add-to-list 'consult-buffer-sources 'consult--source-workspace))
COMMENT Tabs
;; (setq tab-bar-mode t)
;; (setq tab-bar-show nil)
;; (global-set-key (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
;; (global-set-key (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
;; (global-set-key (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
;; (global-set-key (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
;; (global-set-key (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
;; (global-set-key (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
;; (global-set-key (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
;; (global-set-key (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
;; (global-set-key (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
;; (evil-global-set-key 'insert (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
;; (evil-global-set-key 'insert (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
;; (evil-global-set-key 'insert (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
;; (evil-global-set-key 'insert (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
;; (evil-global-set-key 'insert (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
;; (evil-global-set-key 'insert (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
;; (evil-global-set-key 'insert (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
;; (evil-global-set-key 'insert (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
;; (evil-global-set-key 'insert (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
;; (evil-global-set-key 'normal (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
;; (evil-global-set-key 'normal (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
;; (evil-global-set-key 'normal (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
;; (evil-global-set-key 'normal (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
;; (evil-global-set-key 'normal (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
;; (evil-global-set-key 'normal (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
;; (evil-global-set-key 'normal (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
;; (evil-global-set-key 'normal (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
;; (evil-global-set-key 'normal (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
Projects
(with-eval-after-load 'project
(setq project-vc-ignores '("target/" "bin/" "obj/"))
(add-to-list 'project-switch-commands '(magit-project-status "Magit" ?m))
;; TODO: This doesn't start in the correct working directory
(add-to-list 'project-switch-commands '(joe/vterm-here "VTerm" ?s))
(add-to-list 'project-vc-extra-root-markers ".dir-locals.el")
(defun joe/project-root-override (dir)
(let ((proj-name (project-try-vc dir)))
(when (and proj-name
(not (frame-parameter (selected-frame) 'explicit-name)))
(progn
(set-frame-name (file-name-nondirectory (directory-file-name (nth 2 proj-name)))))
proj-name)))
;; TODO: There's an issue with this and it's causing some weird nesting/recursion
;; (add-hook 'project-find-functions #'joe/project-root-override)
(define-key 'ctl-x-5-prefix "n" #'set-frame-name))
VEMCO
Vertico
(require 'all-the-icons-completion)
;; (require '(vertico :files (:defaults "extensions/*")
;; :includes (vertico-indexed
;; vertico-repeat
;; vertico-directory))
(require 'vertico)
(vertico-mode)
;; (elpaca 'vertico-posframe)
;; (define-key vertico-map (kbd "C-w") #'vertico-directory-delete-word)
;; (define-key vertico-map (kbd "C-r") #'vertico-repeat-)
;; (vertico-posframe-mode t)
;; (vertico-indexed-mode)
;; (setq vertico-posframe-parameters
;; '((left-fringe . 100)
;; (right-fringe . 100)))
;; (setq vertico-posframe-border-width 5)
;; (setq vertico-posframe-min-height 20)
(defun posframe-poshandler-slightly-below-top (info)
(cons (/ (- (plist-get info :parent-frame-width)
(plist-get info :posframe-width))
2)
180))
;; (setq vertico-posframe-poshandler #'posframe-poshandler-frame-center)
(setq vertico-posframe-poshandler #'posframe-poshandler-slightly-below-top)
(defun joe/consult-buffer-vertico-indexed (start-index)
(interactive)
(let ((vertico-count 12)
(vertico-posframe-width 110)
(vertico-posframe-height 20)
(vertico-group-format nil)
(vertico-indexed-start 1)
(vertico--index start-index)
(consult-buffer-sources '(beframe-consult-source)))
(consult-buffer)))
(defun joe/consult-buffer-vertico-next ()
(interactive)
(joe/consult-buffer-vertico-indexed 1))
(defun joe/consult-buffer-vertico-last ()
(interactive)
(joe/consult-buffer-vertico-indexed (1- (length (beframe-buffer-names)))))
(global-set-key (kbd "s-n") #'joe/consult-buffer-vertico-next)
(global-set-key (kbd "s-p") #'joe/consult-buffer-vertico-last)
(setq vertico-count 20
vertico-resize nil
vertico-cycle t)
(setq vertico-posframe-width 130)
(setq vertico-posframe-height 30)
(setq vertico-posframe-parameters
'((child-frame-border-width . 5)
(left-fringe . 30)
(right-fringe . 30)))
(require 'savehist)
(savehist-mode)
(add-hook 'minibuffer-setup-hook #'vertico-repeat-save)
(add-to-list 'savehist-additional-variables 'vertico-repeat-history)
(define-key vertico-map (kbd "C-M-n") #'vertico-next-group)
(define-key vertico-map (kbd "s-p") #'vertico-previous)
(define-key vertico-map (kbd "s-n") #'vertico-next)
;; #' "C-M-p" #'vertico-previous-group)
;; (elpaca 'vertico-directory)
;; :bind (:map vertico-map
;; ("RET" . vertico-directory-enter)
;; ("DEL" . vertico-directory-delete-char)
;; ("M-DEL" . vertico-directory-delete-word))
;; :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
Embark
(require 'embark)
;; TODO Try using embark to kill this buffer
(defun prot-simple-kill-buffer-current (&optional arg)
"Kill current buffer.
With optional prefix ARG (\\[universal-argument]) delete the
buffer's window as well."
(interactive "P")
(let ((kill-buffer-query-functions nil))
(if (and arg (not (one-window-p)))
(kill-buffer-and-window)
(kill-buffer))))
(defun prot-simple-kill-buffer (buffer)
"Kill current BUFFER.
When called interactively, prompt for BUFFER."
(interactive (list (read-buffer "Select buffer: ")))
(let ((kill-buffer-query-functions nil))
(kill-buffer (or buffer (current-buffer)))))
(setq embark-quit-after-action '((kill-buffer . nil)))
;; TODO Add this to display-buffer-alist
;; ("\\*Embark Actions\\*"
;; (display-buffer-reuse-mode-window display-buffer-at-bottom)
;; (window-height . fit-window-to-buffer)
;; (window-parameters . ((no-other-window . t)
;; (mode-line-format . none))))
;; TODO Remove the which-key stuff because it seems to be breaking things
(defun embark-which-key-indicator ()
"An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
(lambda (&optional keymap targets prefix)
(if (null keymap)
(which-key--hide-popup-ignore-command)
(which-key--show-keymap
(if (eq (plist-get (car targets) :type) 'embark-become)
"Become"
(format "Act on %s '%s'%s"
(plist-get (car targets) :type)
(embark--truncate-target (plist-get (car targets) :target))
(if (cdr targets) "…" "")))
(if prefix
(pcase (lookup-key keymap prefix 'accept-default)
((and (pred keymapp) km) km)
(_ (key-binding prefix 'accept-default)))
keymap)
nil nil t (lambda (binding)
(not (string-suffix-p "-argument" (cdr binding))))))))
(setq embark-indicators
'(embark-which-key-indicator
embark-highlight-indicator
embark-isearch-highlight-indicator))
(defun embark-hide-which-key-indicator (fn &rest args)
"Hide the which-key indicator immediately when using the completing-read prompter."
(which-key--hide-popup-ignore-command)
(let ((embark-indicators
(remq #'embark-which-key-indicator embark-indicators)))
(apply fn args)))
(advice-add #'embark-completing-read-prompter
:around #'embark-hide-which-key-indicator)
(advice-remove #'embark-completing-read-prompter #'embark-hide-which-key-indicator)
Marginalia
(require 'marginalia)
(setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
(setq marginalia-align 'right)
(setq marginalia-max-relative-age most-positive-fixnum)
(marginalia-mode)
(define-key minibuffer-local-map (kbd "M-A") #'marginalia-cycle)
(require 'all-the-icons-completion)
(all-the-icons-completion-mode)
(all-the-icons-completion-marginalia-setup)
(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)
Consult
(require 'embark-consult)
(require 'consult)
;; (require 'consult-lsp)
(global-set-key (kbd "C-. C-l") 'consult-line)
(global-set-key (kbd "C-. C-i") 'consult-imenu)
(global-set-key (kbd "C-. C-t") 'joe/consult-theme)
(global-set-key (kbd "C-. C-r") 'consult-recent-file)
(global-set-key (kbd "C-. C-y") 'consult-yank-from-kill-ring)
Orderless
(require 'orderless)
(setq completion-styles '(orderless basic)
completion-category-overrides '((file (styles basic partial-completion))))
App Launcher
(defun emacs-run-launcher ()
(interactive)
(unwind-protect
(app-launcher-run-app)
(delete-frame)))
Dirvish/Dired
(require 'dirvish)
(with-eval-after-load 'dirvish
(dirvish-override-dired-mode)
(setq delete-by-moving-to-trash t)
(setq dired-dwim-target t)
(setq dirvish-reuse-session nil)
;; (add-hook 'dired-mode-hook 'centaur-tabs-local-mode)
(dirvish-define-preview exa (file)
"Use `exa' to generate directory preview."
:require ("exa") ; tell Dirvish to check if we have the executable
(when (file-directory-p file) ; we only interest in directories here
`(shell . ("exa" "--icons" "--color=always" "--no-user" "-al" "--group-directories-first" ,file))))
(add-to-list 'dirvish-preview-dispatchers 'exa)
(setq dired-listing-switches "-l --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group")
(setq dirvish-preview-dispatchers (cl-substitute 'pdf-preface 'pdf dirvish-preview-dispatchers))
(setq dirvish-attributes '(all-the-icons file-size collapse subtree-state))
(defun joe/dirvish-find-directory (dir)
(interactive "FDirvish Directory:")
(dirvish-dwim dir))
(setq dirvish-quick-access-entries
'(("h" "~/" "Home")
("d" "~/Downloads/" "Downloads")
("D" "~/Documents/" "Documents")
("b" "~/Documents/Books/" "Books")
("p" "~/Development/" "Dev")
("t" "~/TYCS/" "Teachyourselfcs")
("n" "~/Notes/" "OrgNotes")
("r" "~/Repositories" "Repos")
("B" "~/pCloudDrive/" "pCloud")))
(when (boundp 'evil-mode)
(evil-define-key 'normal dirvish-mode-map
(kbd "C-c f") #'dirvish-fd
(kbd "a") #'dirvish-quick-access
(kbd ".") #'dired-create-empty-file
(kbd "f") #'dirvish-file-info-menu
(kbd "y") #'dirvish-yank-menu
(kbd "h") #'dired-up-directory
(kbd "l") #'dired-find-file
(kbd "s") #'dirvish-quicksort
(kbd "v") #'dirvish-vc-menu
(kbd "q") #'dirvish-quit
(kbd "L") #'dirvish-history-go-forward
(kbd "H") #'dirvish-history-go-backward
(kbd "o") #'dired-open-file
(kbd "TAB") #'dirvish-subtree-toggle
(kbd "M-n") #'dirvish-narrow
(kbd "M-l") #'dirvish-ls-switches-menu
(kbd "M-m") #'dirvish-mark-menu
(kbd "M-t") #'dirvish-layout-toggle
(kbd "M-s") #'dirvish-setup-menu
(kbd "M-e") #'dirvish-emerge-menu
(kbd "M-j") #'dirvish-fd-jump)))
;; There's an issue with the keybinding precedence so we need to run this since
;; joe/evil-space-mode-map takes precedence above all else and '-' doesn't do
;; what it should
(defun joe/dirvish-up-dwim ()
(interactive)
(if (eq major-mode 'dired-mode)
(dired-up-directory)
(dirvish-dwim)))
(when (boundp 'evil-mode)
(evil-define-key 'normal joe/evil-space-mode-map (kbd "_") #'project-dired)
(evil-define-key 'normal joe/evil-space-mode-map (kbd "-") #'joe/dirvish-up-dwim))
(global-set-key (kbd "C-x d") #'dirvish-dwim)
(global-set-key (kbd "C-x C-d") #'joe/dirvish-find-directory)
(defun dired-open-file ()
"In dired, open the file named on this line."
(interactive)
(let* ((file (dired-get-filename nil t)))
(message "Opening %s..." file)
(call-process "xdg-open" nil 0 nil file)
(message "Opening %s done" file)))
(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e/")
(require 'mu4e)
(evil-global-set-key 'normal (kbd "SPC mm") #'mu4e)
;; Attach files to a message composition buffer by going into `dired'
;; and doing C-c C-m C-a (M-x `gnus-dired-attach').
(require 'gnus-dired) ; does not require `gnus'
(add-hook 'dired-mode-hook #'gnus-dired-mode)
(add-hook 'mu4e-main-mode-hook 'olivetti-mode)
(add-to-list 'auto-mode-alist '("authinfo" . authinfo-mode))
(setq mu4e-get-mail-command "parallel mbsync -V \"-c ~/.config/mbsync/config\" ::: ferano.io.inbox gmail.allmail")
;;; Sending email (SMTP)
(require 'smtpmail)
(setq smtpmail-default-smtp-server "mail.gandi.net"
smtpmail-smtp-server "mail.gandi.net"
smtpmail-stream-type 'ssl
smtpmail-smtp-service 465
smtpmail-queue-mail nil)
(require 'sendmail)
(setq send-mail-function 'smtpmail-send-it)
(setq message-kill-buffer-on-exit nil)
(setq mu4e-completing-read-function 'completing-read)
(setq mu4e-sent-messages-behavior 'sent)
(setq mu4e-context-policy 'pick-first)
(setq mu4e-compose-context-policy 'ask)
(setq mu4e-view-auto-mark-as-read nil)
(setq message-kill-buffer-on-exit t)
(require 'age)
(setq age-default-identity '("~/.local/credentials/personal"))
(setq age-default-recipient '("~/.local/credentials/personal.pub"))
(setq auth-source-do-cache nil)
(defun joe/mu4e-auth-get-field (host prop)
"Find PROP in `auth-sources' for HOST entry."
(when-let ((source (auth-source-search :host host)))
(if (eq prop :secret)
(funcall (plist-get (car source) prop))
(plist-get (flatten-list source) prop))))
(setq auth-sources '("~/.local/credentials/authinfo.age"))
(age-file-enable)
(age-encryption-mode +1)
(setq mu4e-bookmarks nil)
(setq mu4e-contexts
`(,(make-mu4e-context
:name "Ferano.io"
:enter-func (lambda () (mu4e-message "Entering ferano.io"))
:leave-func (lambda () (mu4e-message "Leaving ferano.io"))
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches
msg :to (joe/mu4e-auth-get-field "mail.gandi.net" :user))))
:vars `((user-mail-address . ,(joe/mu4e-auth-get-field "mail.gandi.net" :user))
(user-full-name . "Joseph Ferano")
(mu4e-drafts-folder . "/ferano.io/Drafts/")
(mu4e-trash-folder . "/ferano.io/Trash/")
(mu4e-sent-folder . "/ferano.io/Sent/")))
,(make-mu4e-context
:name "Gmail"
:enter-func (lambda () (mu4e-message "Entering gmail"))
:leave-func (lambda () (mu4e-message "Leaving gmail"))
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches
msg :to (joe/mu4e-auth-get-field "mail.gmail.com" :user))))
:vars `((user-mail-address . ,(joe/mu4e-auth-get-field "mail.gmail.com" :user))
(user-full-name . "Joseph Ferano")
(mu4e-drafts-folder . "/gmail/[Gmail]/Drafts/")
(mu4e-trash-folder . "/gmail/[Gmail]/Trash/")
(mu4e-sent-folder . "/gmail/[Gmail]/Sent Mail/")))))
(setq mu4e-maildir-shortcuts
'((:maildir "/ferano.io/Inbox" :key ?f)
(:maildir "/gmail/[Gmail]/All Mail" :key ?g)))
;; (:name "Ferano.io Unread" :query "m:/ferano.io/Inbox AND g:unread" :key ?u)))
Fold threads. This is a gist provided by Rougier here. His other package is mu4e-thread-folding but they work slightly differently. Code for the latter will be kept here commented out in case we want to try it again.
(load-file "/home/joe/Dotfiles/.config/emacs/elisp/mu4e-fast-folding.el")
(evil-define-key 'normal mu4e-headers-mode-map (kbd "TAB")
#'mu4e-fast-folding-thread-toggle)
(evil-define-key 'normal mu4e-headers-mode-map (kbd "<backtab>")
#'mu4e-fast-folding-thread-toggle-all)
;; (load-file "/home/joe/Dotfiles/.config/emacs/elisp/mu4e-thread-folding.el")
;; (require 'mu4e-fast-folding)
;; (add-to-list 'mu4e-header-info-custom
;; '(:empty . (:name "Empty"
;; :shortname ""
;; :function (lambda (msg) " "))))
;; (setq mu4e-headers-fields '((:empty . 2)
;; (:human-date . 12)
;; (:flags . 6)
;; (:mailing-list . 10)
;; (:from . 22)
;; (:subject . nil)))
;; (evil-define-key 'normal mu4e-headers-mode-map (kbd "TAB")
;; #'mu4e-headers-fold-at-point)
;; (evil-define-key 'normal mu4e-headers-mode-map (kbd "<backtab>")
;; #'mu4e-headers-unfold-at-point)
;; (mu4e-thread-folding-mode +1)
Avy
(require 'avy)
(setq avy-case-fold-search nil)
(setq avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l ?q ?w ?e ?r ?u ?i ?o ?p ?z ?x ?c ?v ?n ?m))
(define-key global-map (kbd "C-;") 'avy-goto-char) ;; I use this most frequently
;; (define-key global-map (kbd "C-'") 'avy-goto-line) ;; Consistent with ivy-avy
(define-key global-map (kbd "M-g c") 'avy-goto-char)
(define-key global-map (kbd "M-g e") 'avy-goto-word-0) ;; lots of candidates
(define-key global-map (kbd "M-g g") 'avy-goto-line) ;; digits behave like goto-line
(define-key global-map (kbd "M-g w") 'avy-goto-word-1) ;; first character of the word
(define-key global-map (kbd "M-g P") 'avy-pop-mark)
Helpful
(require 'helpful)
(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)
(global-set-key (kbd "C-h C-d") #'helpful-at-point)
(global-set-key (kbd "C-h F") #'helpful-function)
(global-set-key (kbd "C-h C") #'helpful-command)
Terminals/Shells
(require 'vterm)
(setq vterm-shell "/bin/fish")
(setq vterm-timer-delay 0.01)
;; (setq vterm-buffer-name-string "VTerm - %s")
(setq vterm-buffer-name-string nil)
(setq vterm-max-scrollback 100000)
(setq vterm-kill-buffer-on-exit t)
(defun joe/close-popup-buffer (vterm-buf event-msg)
(unless (or (eq popper-popup-status nil)
(eq popper-popup-status 'raised))
(popper-close-latest)))
(setq vterm-exit-functions '(joe/close-popup-buffer))
(defun joe/vterm-here ()
(interactive)
(let ((vterm-buf (vterm--internal #'switch-to-buffer)))
(with-current-buffer vterm-buf
(setq popper-popup-status 'raised))))
(global-set-key (kbd "C-c t") #'vterm)
(global-set-key (kbd "C-c T") #'joe/vterm-here)
;; (setq explicit-shell-file-name "~/Development/fell/fell")
(add-hook 'shell-mode (lambda () (setq-local global-hl-line-mode nil)))
(setq shell-kill-buffer-on-exit t)
(defun joe/vterm-mode-hook ()
(define-key vterm-mode-map (kbd "C-c C-x") #'vterm-send-C-x)
(when (boundp 'evil-mode)
(evil-define-key 'insert vterm-mode-map (kbd "C-f") #'vterm-send-C-f)
(evil-define-key 'insert vterm-mode-map (kbd "C-w") #'vterm-send-C-w)
(evil-define-key 'insert vterm-mode-map (kbd "<delete>") #'vterm-send-delete))
(setq-local global-hl-line-mode nil)
(setq buffer-face-mode-face '(:family "Fira Code Nerd Font"))
(buffer-face-mode))
(add-hook 'vterm-mode-hook #'joe/vterm-mode-hook)
VTerm is loading TRAMP along with it which slows down init time noticeably so call this after startup. Reason we have to call this is so the vterm fucntion can call `vterm–internal`.
;; (add-hook 'emacs-startup-hook (lambda () (require 'vterm)))
This bit of code is to achieve a vterm scratchpad. In order to know I'm closing the right frame, I'm going to use the frame's name to close and remove the hook
(defun vterm--set-title (title)
"Use TITLE to set the buffer name according to `vterm-buffer-name-string'."
(when (and vterm-buffer-name-string (not (equal (buffer-name) "Scratch VTerm")))
(rename-buffer (format vterm-buffer-name-string title) t)))
(defun joe/kill-frame ()
(when (equal (buffer-name) "Scratch VTerm")
(remove-hook 'delete-frame-functions #'joe/kill-vterm-scratch)
(remove-hook 'kill-buffer-hook #'joe/kill-frame)
(delete-frame)))
(defun joe/kill-vterm-scratch (FRAME)
(let* ((kill-buffer-query-functions nil)
(scratch-framep (cdr (assoc 'scratch-frame (frame-parameters))))
(vterm-buf (get-buffer "Scratch VTerm")))
(when (and scratch-framep
vterm-buf)
(kill-buffer vterm-buf)
(remove-hook 'delete-frame-functions #'joe/kill-vterm-scratch)
(remove-hook 'kill-buffer-hook #'joe/kill-frame))))
(defun joe/vterm-scratch ()
(interactive)
(let* ((vterm-buf (vterm--internal #'switch-to-buffer "Scratch VTerm")))
(set-frame-parameter nil 'scratch-frame t)
(with-current-buffer vterm-buf
(setq mode-line-format nil)
(setq popper-popup-status 'raised)
(olivetti-mode)
(add-hook 'delete-frame-functions #'joe/kill-vterm-scratch)
(add-hook 'kill-buffer-hook #'joe/kill-frame))))
Undo Fu
(require 'undo-fu)
(undo-fu-session-global-mode +1)
(setq undo-limit 6710886400) ;; 64mb.
(setq undo-strong-limit 100663296) ;; 96mb.
(setq undo-outer-limit 1006632960) ;; 960mb.
(require 'undo-fu-session)
(setq undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'"))
(require 'vundo)
Which Key
(require 'which-key)
(setq which-key-idle-delay 0.3)
(which-key-mode)
(when (boundp 'evil-mode)
(which-key-add-keymap-based-replacements evil-normal-state-map
"SPC f" '("Files")
"SPC b" '("Buffers")
"SPC B" '("Bookmarks")
"SPC c" '("Consult")
"SPC d" '("Dired")
"SPC g" '("Git")
"SPC m" '("Make")
"SPC t" '("Tabs")
"SPC p" '("Packages")
"SPC s" '("Shell (vterm)")
"SPC h" '("Help")))
IDE Features
REPLs
(when (boundp 'evil-mode)
(evil-define-key 'insert comint-mode-map (kbd "C-n") 'comint-next-input)
(evil-define-key 'insert comint-mode-map (kbd "C-p") 'comint-previous-input))
Completion
;; (evil-global-set-key 'insert (kbd "C-f") #'mono-complete-expand)
(require 'mono-complete)
(evil-global-set-key 'insert (kbd "C-f") #'mono-complete-expand-or-fallback)
(setq mono-complete-backends (list 'dabbrev 'filesystem))
(setq mono-complete-backends (list 'capf 'dabbrev 'filesystem))
(setq mono-complete-backend-capf-complete-fn #'eglot-completion-at-point)
(setq mono-complete-preview-delay 0.1)
;; (add-to-list 'face-remapping-alist '(mono-complete-preview-face . shadow))
(set-face-attribute 'mono-complete-preview-face nil
:foreground 'unspecified
:background 'unspecified
:inherit 'shadow)
(mono-complete-mode +1)
(setq completion-in-region-function
(lambda (&rest args)
(apply (if vertico-mode
#'consult-completion-in-region
#'completion--in-region)
args)))
Eldoc
(evil-global-set-key 'normal (kbd "K") #'eldoc)
(global-eldoc-mode -1)
Eglot
(with-eval-after-load 'eglot
;; (flymake-mode -1)
;; Disable it completely until we find out how the hell we can toggle it
(setq eglot-stay-out-of '(flymake))
(setq eglot-stay-out-of '())
(setq eldoc-idle-delay 0.15)
;; (setq eglot-stay-out-of '())
;; (add-hook 'eglot-managed-mode-hook (lambda () (flymake-mode -1)))
(evil-global-set-key 'normal (kbd "SPC li") 'eglot-inlay-hints-mode)
(evil-global-set-key 'normal (kbd "SPC cs") 'consult-eglot-symbols)
(evil-global-set-key 'normal (kbd "SPC cr") 'eglot-rename)
(evil-global-set-key 'normal (kbd "SPC ca") 'eglot-code-actions))
;; These don't work
;; (setq flymake-start-on-save-buffer nil)
;; (setq flymake-start-on-flymake-mode nil)
COMMENT LSP
(require 'lsp-mode)
(setq lsp-enable-which-key-integration t)
(setq lsp-keymap-prefix "C-c c")
;; (setq lsp-ui-peek-always-show t)
;; (setq lsp-ui-doc-enable t)
;; TODO: Disable lsp sideline
(setq lsp-headerline-breadcrumb-enable nil)
(setq lsp-eldoc-enable-hover t)
(setq max-mini-window-height 0.25)
(setq lsp-eldoc-enable-hover t)
(setq lsp-ui-doc-enable nil)
(setq eldoc-echo-area-use-multiline-p 'truncate-sym-name-if-fit)
(setq eldoc-idle-delay 0)
(setq lsp-idle-delay 0.2)
;; Signatures
(setq lsp-signature-doc-lines t)
(setq lsp-signature-render-documentation nil)
(setq lsp-eldoc-render-all nil)
;; All this changes because we are using eglot now
(when (boundp 'evil-mode)
(evil-global-set-key 'normal (kbd "M-d") #'lsp-describe-thing-at-point)
(evil-global-set-key 'normal (kbd "<leader>lh") 'lsp-headerline-breadcrumb-mode)
(evil-global-set-key 'normal (kbd "SPC li") 'lsp-rust-analyzer-inlay-hints-mode)
(evil-global-set-key 'normal (kbd "<leader>cs") 'consult-lsp-symbols)
(evil-global-set-key 'normal (kbd "<leader>cf") 'consult-lsp-file-symbols))
;; This function allows us to extract Rust's true function signature
(cl-defmethod lsp-clients-extract-signature-on-hover (contents (_server-id (eql rust-analyzer)))
(-let* (((&hash "value") contents)
(groups (--partition-by (s-blank? it) (s-lines (s-trim value))))
(sig_group (if (s-equals? "```rust" (car (-third-item groups)))
(-third-item groups)
(car groups)))
(sig (--> sig_group
(--drop-while (s-equals? "```rust" it) it)
(--take-while (not (s-equals? "```" it)) it)
(--map (s-trim it) it)
(s-join " " it))))
(lsp--render-element (concat "```rust\n" sig "\n```"))))
(require 'lsp-ui)
COMMENT Flycheck
(require 'flycheck)
;; TODO Add wrapping to these functions
(when (boundp 'evil-mode)
(evil-global-set-key 'normal (kbd "M-e") #'flycheck-next-error)
(evil-global-set-key 'normal (kbd "M-E") #'flycheck-previous-error)
(evil-global-set-key 'normal (kbd "<leader>ee") 'flycheck-mode)
(evil-global-set-key 'normal (kbd "<leader>el") #'flycheck-list-errors)
(evil-global-set-key 'normal (kbd "<leader>ce") #'consult-lsp-diagnostics))
Compilation
(setq compilation-auto-jump-to-first-error t)
(defun joe/save-then-recompile ()
"Save the buffer before recompiling"
(interactive)
(when (buffer-file-name)
(save-buffer))
(recompile))
(defun joe/save-if-file-ignore-args (&rest _)
"Save the buffer before recompiling"
(interactive)
(joe/save-if-file))
(defun joe/save-if-file ()
"Save buffer only if it has a backing file"
(interactive)
(when (buffer-file-name)
(save-buffer)))
(add-hook 'evil-insert-state-exit-hook #'joe/save-if-file)
;; (add-hook 'after-change-functions #'joe/save-if-file-ignore-args)
;; (define-key global-map (kbd "C-x C-s) #'save-buffer)
(define-key global-map (kbd "<f9>") #'joe/save-then-recompile)
(define-key global-map (kbd "<f10>") #'compile)
(defun joe/colorize-compilation-buffer ()
(ansi-color-apply-on-region compilation-filter-start (point)))
(add-hook 'compilation-filter-hook 'joe/colorize-compilation-buffer)
(defun joe/close-compilation-if-no-warn-err (buffer string)
"Bury a compilation buffer if succeeded without warnings "
(if (and
(string-match "compilation" (buffer-name buffer))
(string-match "finished" string)
(not
(with-current-buffer (current-buffer)
(search-forward "warning" nil t))))
(run-with-timer 1.5 nil
(lambda ()
(and (eq (point) 1)
(string-match "compilation" (buffer-name (current-buffer)))
(popper-close-latest))))))
(add-hook 'compilation-finish-functions 'joe/close-compilation-if-no-warn-err)
Godot
(require 'gdscript-mode)
Programming Languages
Python
(require 'elpy)
(define-key inferior-python-mode-map (kbd "C-n") #'comint-next-input)
(define-key inferior-python-mode-map (kbd "C-p") #'comint-previous-input)
Rust
(setq rustic-lsp-setup-p nil)
(require 'rustic)
(require 'ob-rust)
;; Org-Babel
;; Disabling until we figure out how to get it working
;; (elpaca 'parsec) ;; Required by evcxr-mode
;; (elpaca
;; '(evcxr
;; :type git
;; :host github
;; :repo "serialdev/evcxr-mode"))
(add-hook 'rust-mode-hook
(lambda ()
;; (evcxr-minor-mode)
(electric-pair-local-mode)))
;; (with-eval-after-load 'rustic
;; ;; Don't autostart
;; ;; (define-key rustic-mode-map (kbd "<f9>") #'joe/save-then-recompile)
;; (setq lsp-rust-analyzer-server-display-inlay-hints t)
;; (setq lsp-rust-analyzer-display-lifetime-elision-hints-enable "always")
;; (setq lsp-rust-analyzer-display-chaining-hints t)
;; (setq lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names t)
;; (setq lsp-rust-analyzer-display-closure-return-type-hints t)
;; (setq lsp-rust-analyzer-display-parameter-hints t)
;; (setq lsp-rust-analyzer-display-reborrow-hints t)
;; (setq lsp-rust-analyzer-cargo-watch-command "clippy"))
Elisp
(global-set-key (kbd "C-x C-r") 'eval-region)
(evil-define-key 'insert emacs-lisp-mode-map (kbd "C-j") 'eval-print-last-sexp)
Web
(require 'typescript-mode)
(setq typescript-indent-level 2)
SQL
(defun joe/mark-sql-defun ()
"Mark the current SQL function definition."
(interactive)
(let ((beg (save-excursion
(re-search-backward "^create or replace function [^(]+" nil t)
(match-end 0)))
(end (save-excursion
(re-search-forward "\\(language\\)" nil t)
(line-end-position))))
(when (and beg end)
(goto-char beg)
(set-mark end)))
(evil-visual-line)
(evil-visual-line))
(defun sql-send-pg-function ()
"Send the current PGSQL function to sql interactive."
(interactive)
(let ((beg (save-excursion
(re-search-backward "^create or replace function [^(]+" nil t)
(line-beginning-position)))
(end (save-excursion
(re-search-forward "\\(language\\)" nil t)
(line-end-position))))
(save-excursion
(when (and beg end)
(goto-char beg)
(set-mark end))
(sql-send-region beg end)
(deactivate-mark))))
;; This is required so that .dir-locals that read env files for credentials works
(require 'dotenv)
(defun joe/sql-mode-hook ()
(define-key sql-mode-map (kbd "C-M-h") #'joe/mark-sql-defun)
(define-key sql-mode-map (kbd "C-M-x") #'sql-send-pg-function)
(global-set-key (kbd "<f8>") #'sql-connect))
(add-hook 'sql-mode-hook #'joe/sql-mode-hook)
(defun joe/sql-save-history-hook ()
(let ((lval 'sql-input-ring-file-name)
(rval 'sql-product))
(if (symbol-value rval)
(let ((filename
(concat user-emacs-directory "sql/"
(symbol-name (symbol-value rval))
"-history.sql")))
(set (make-local-variable lval) filename))
(error
(format "SQL history will not be saved because %s is nil"
(symbol-name rval))))))
(add-hook 'sql-interactive-mode-hook 'joe/sql-save-history-hook)
(defun joe/sql-login-hook ()
"Custom SQL log-in behaviours. See `sql-login-hook'."
;; n.b. If you are looking for a response and need to parse the
;; response, use `sql-redirect-value' instead of `comint-send-string'.
(when (eq sql-product 'postgres)
(let ((proc (get-buffer-process (current-buffer))))
;; Output each query before executing it. (n.b. this also avoids
;; the psql prompt breaking the alignment of query results.)
(comint-send-string proc "\\set ECHO queries\n"))))
(add-hook 'sql-login-hook 'joe/sql-login-hook)
C
Design some basic functions for compiling. There's also a hook to close the popper window if there are no warnings or errors. It will check if we are still in the compilation buffer as well as whether the point hasn't been moved. This way, if I switched to another popper buffer, like vterm, it doesn't close it.
(require 'disaster)
(defun joe/c-mode-hook ()
(local-set-key (kbd "C-x c r") (defun joe/make-run () (interactive) (compile "make run")))
(local-set-key (kbd "C-x c c") (defun joe/make () (interactive) (compile "make")))
(electric-pair-local-mode t)
(c-toggle-comment-style -1))
(add-hook 'c-mode-hook #'joe/c-mode-hook)
COMMENT Haskell
(require 'haskell-mode)
(setq haskell-interactive-popup-errors nil)
(when (boundp 'evil-mode)
(evil-define-key 'insert haskell-interactive-mode-map (kbd "C-n") #'haskell-interactive-mode-history-next)
(evil-define-key 'insert haskell-interactive-mode-map (kbd "C-p") #'haskell-interactive-mode-history-previous))
COMMENT Clojure
(require 'clojure-mode)
(require 'cider)
(setq cider-show-error-buffer 'only-in-repl)
COMMENT OCaml
(require 'tuareg)
(require 'dune)
(require 'utop)
(require 'merlin)
(require 'merlin-eldoc)
;; Might be worth checking out, depeding on whether we stick with flycheck or not
;; (elpaca 'flycheck-ocaml)
;; Also check this out, see if it adds anything
;; (require 'ocp-indent)
(defun opam-env ()
"Load the opam env to get the PATH variables so everything works"
(interactive nil)
(dolist (var
(car (read-from-string
(shell-command-to-string "opam config env --sexp"))))
(setenv (car var) (cadr var))))
(setq opam-share
(substring (shell-command-to-string
"opam config var share 2> /dev/null") 0 -1))
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))
We won't use the LSP server but rather directly talk to Merlin, since I guess LSP just wraps Merlin and there's no need for a middle-man when it's already been implemented.
;; (require 'utop)
;; Use the opam installed utop
(setq utop-command "opam exec -- utop -emacs")
(let ((opam-share (ignore-errors (car (process-lines "opam" "var" "share")))))
(when (and opam-share (file-directory-p opam-share))
;; Register Merlin
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))
(autoload 'merlin-mode "merlin" nil t nil)
;; Automatically start it in OCaml buffers
(add-hook 'tuareg-mode-hook 'merlin-mode t)
(add-hook 'caml-mode-hook 'merlin-mode t)
;; Use opam switch to lookup ocamlmerlin binary
(setq merlin-command 'opam)))
COMMENT FSharp
(require 'fsharp-mode)
;; (elpaca 'eglot-fsharp)
COMMENT Go
(require 'go-mode)
;; (elpaca 'go-imports)
Other
(require 'json-mode)
(require 'markdown-mode)
Debugging
COMMENT DAP
(require 'dap-mode)
;; (setq dap-auto-configure-features '(locals breakpoints expressions tooltip))
(require 'dap-cpptools)
(dap-cpptools-setup)
(add-hook 'dap-stopped-hook
(lambda (arg) (call-interactively #'dap-hydra)))
(setq dap-cpptools-extension-version "1.12.1")
(setq dap-default-terminal-kind "integrated")
(dap-auto-configure-mode +1)
(dap-register-debug-template
"Rust::CppTools Run Configuration"
(list :type "cppdbg"
:request "launch"
:name "Rust::Run"
:MIMode "gdb"
:miDebuggerPath "rust-gdb"
:environment []
:program "${workspaceFolder}/target/debug/kanban-tui"
:cwd "${workspaceFolder}"
:console "external"
:dap-compilation "cargo build"
:dap-compilation-dir "${workspaceFolder}"))
Org-Mode
(require 'org-bullets)
(defun joe/org-init ()
(setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "BACKLOG")))
(setq org-agenda-files '("~/Notes/Schedule.org"))
(setq org-directory "~/Notes/")
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(makefile . t)
(ocaml . t)
(python . t)
(C . t)
(haskell . t)
;; (rust . t)
(shell . t)))
(require 'org-tempo)
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("ml" . "src ocaml"))
(add-to-list 'org-structure-template-alist '("rs" . "src rust"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("hs" . "src haskell"))
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("cc" . "src C :includes stdio.h stdlib.h"))
(setq org-edit-src-content-indentation 0))
(with-eval-after-load 'org (joe/org-init))
(setq org-blank-before-new-entry
'((heading . nil)
(plain-list-item . nil)))
(setq org-cycle-separator-lines 1)
(setq org-hide-emphasis-markers t)
(setq org-src-window-setup 'current-window)
(defun joe/org-hook ()
(local-set-key (kbd "C-. C-i") 'consult-org-heading)
(setq org-download-image-dir "./Images/")
(setq org-download-heading-lvl nil)
(org-fancy-priorities-mode)
(olivetti-mode)
(org-bullets-mode)
(org-indent-mode))
(add-hook 'org-mode-hook 'joe/org-hook)
(require 'org-download)
(require 'org-kanban)
(require 'org-fancy-priorities)
;; (setq org-fancy-priorities-list '("🅰" "🅱" "🅲" "🅳" "🅴"))
;; (setq org-fancy-priorities-list '("⚡" "⬆" "⬇"))
;; (setq org-fancy-priorities-list '("⚡" "⬆" "⬇"))
(setq org-fancy-priorities-list '((?D . "🌃") (?C . "🌇") (?B . "☀️") (?A . "🌄")))
(require 'org-roam)
(setq org-roam-directory "/home/joe/Notes/Roam/")
(setq org-roam-node-display-template (concat "${title:*} "
(propertize "${tags:10}" 'face 'org-tag)))
(org-roam-db-autosync-mode)
(define-key global-map (kbd "C-c n l") #'org-roam-buffer-toggle)
(define-key global-map (kbd "C-c n f") #'org-roam-node-find)
(define-key global-map (kbd "C-c n g") #'org-roam-graph)
(define-key global-map (kbd "C-c n i") #'org-roam-node-insert)
(define-key global-map (kbd "C-c n c") #'org-roam-capture)
(org-roam-setup)
(evil-define-key 'normal calendar-mode-map (kbd "RET") #'org-calendar-select)
Org-capture
;; (defun my-org-capture-today ()
;; (concat "<" (format-time-string "%Y-%m-%d %a") ">"))
(defun joe/capture-leetcode-template ()
(mapconcat
'identity
'("#+TITLE: Leetcode %?: "
"#+AUTHOR: Joseph Ferano"
"#+STARTUP: show2levels"
"#+TAGS: "
""
"* Python Solution :python:"
":PROPERTIES:"
":CompletionTime: 7m"
":TimeComplexity: O(logn)"
":MemoryComplexity: O(h)"
":END:"
":LOGBOOK:"
":END:"
""
"#+begin_src python"
"#+end_src"
"** Review"
"** Techniques & Patterns")
"\n"))
(defun joe/capture-leetcode-newfile ()
(let* ((title (read-string "Title: "))
(title (string-replace " " "-" title))
(title (string-replace "." "" title)))
(expand-file-name (format "%s.org" title) "~/Development/coding-challenges/leetcode/")))
(setq org-capture-templates
`(
("l" "Leetcode Solution" plain
(function ,(lambda () (find-file (joe/capture-leetcode-newfile))))
#'joe/capture-leetcode-template)
("d" "Demo of adding to a daily.org" entry
;; A relative file name is interpreted based on
;; `org-directory'.
(file+headline "/tmp/daily.org" "Daily notes")
#'my-org-capture-daily-note)
("D" "Demo of adding to a daily.org with a prompt" entry
;; A relative file name is interpreted based on
;; `org-directory'.
(file+headline "/tmp/daily.org" "Daily notes")
,(concat
"** " "%^t" "\n"
"*** Schedule" "\n"
"*** Notes" "\n"
"*** Resources" "\n"))))
Magit
The best git porcelain/client I've ever used. Also kill stray magit buffers left over as explained here
(require 'magit)
(defun joe/magit-kill-buffers (param)
"Restore window configuration and kill all Magit buffers."
(let ((buffers (magit-mode-get-buffers)))
(magit-restore-window-configuration)
(mapc #'kill-buffer buffers)))
(when (boundp 'evil-mode)
(add-hook 'with-editor-mode-hook 'evil-insert-state))
(setq magit-bury-buffer-function #'joe/magit-kill-buffers)
(setq magit-clone-set-remote.pushDefault t)
(setq magit-clone-default-directory "~/Development/")
;; (define-key magit-mode-map "h" 'backward-char)
;; (define-key magit-mode-map "l" 'backward-char)
Restclient
(require 'restclient)
(add-to-list 'auto-mode-alist '("\\.restclient\\'" . restclient-mode))
(with-eval-after-load 'restclient
(define-key restclient-mode-map (kbd "C-c C-c") #'restclient-http-send-current-stay-in-window)
(define-key restclient-mode-map (kbd "C-c C-v") #'restclient-http-send-current))
Initial Buffer
(load-file "/home/joe/Dotfiles/.config/emacs/elisp/welcome.el")
COMMENT Local variables
;; Local Variables: ;; eval: (olivetti-mode t) ;; eval: (add-hook 'after-save-hook '(lambda () (org-babel-tangle)) nil t) ;; End: