1943 lines
63 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

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

#+TITLE: Emacs Config
#+AUTHOR: Joseph Ferano
#+PROPERTY: header-args:emacs-lisp :tangle ./init.el
#+STARTUP: overview
#+TOC: true
** Early Init
*** Filename handler alist
Skipping a bunch of regular expression searching in the file-name-handler-alist should improve start time.
#+begin_src emacs-lisp
(defvar default-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)
#+end_src
*** 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.
#+begin_src emacs-lisp :tangle ./early-init.el
;; -*- 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)
(setq native-comp-deferred-compilation t)
#+end_src
Disabling these classic visual options during early-init seems to net a 0.04 ms boost in init time
#+begin_src emacs-lisp :tangle ./early-init.el
(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)
#+end_src
*** Enhancements
Disable package.el, since we will be using elpaca.el. According to the elpaca.el
documentation;
#+BEGIN_SRC emacs-lisp :tangle ./early-init.el
(setq package-enable-at-startup nil)
#+END_SRC
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.
#+begin_src emacs-lisp :tangle ./early-init.el
(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)
#+end_src
Prevent instructions on how to close an emacsclient frame.
#+begin_src emacs-lisp
(setq server-client-instructions nil)
#+end_src
Implicitly resizing the Emacs frame adds to init time. Fonts larger than the system default can
cause frame resizing, which adds to startup time.
#+begin_src emacs-lisp
(setq frame-inhibit-implied-resize t)
#+end_src
Ignore X resources.
#+begin_src emacs-lisp
(advice-add #'x-apply-session-resources :override #'ignore)
#+end_src
*** UTF-8 Support
#+begin_src emacs-lisp :tangle ./early-init.el
(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)
#+end_src
Finish up
#+begin_src emacs-lisp
(provide 'early-init)
;;; early-init.el ends here
#+end_src
** Elpaca
#+BEGIN_SRC emacs-lisp :tangle no
;; -*- 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)
#+END_SRC
** Package Management
#+begin_src emacs-lisp
;; 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
olivetti
;; ligature
highlight-quoted
doom-modeline
;; hydra
;; multiple-cursors
evil
evil-collection
evil-surround
evil-snipe
evil-commentary
evil-goggles
all-the-icons-ibuffer
;; ace-window
popper
centaur-tabs
all-the-icons-completion
vertico
savehist
embark
embark-consult
marginalia
all-the-icons-completion
orderless
consult
consult-eglot
dirvish
smtpmail
sendmail
age
avy
helpful
vterm
undo-fu
undo-fu-session
vundo
which-key
;; company
mono-complete
;; lsp-mode
;; lsp-ui
;; flycheck
;; dap-mode
rustic
ob-rust
typescript-mode
disaster
haskell-mode
clojure-mode
cider
fsharp-mode
go-mode
json-mode
markdown-mode
org-bullets
org-kanban
restclient
magit))
(setopt package-vc-selected-packages
'((dotenv :url "https://github.com/pkulev/dotenv.el")
(doom-themes :url "https://github.com/JosephFerano/doom-themes")))
(package-initialize)
#+end_src
** 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.
#+begin_src emacs-lisp
(require 'benchmark-init)
(add-hook 'after-init-hook 'benchmark-init/deactivate)
#+end_src
** Misc Stuff
#+begin_SRC emacs-lisp
(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)
#+end_src
This avoids those annoying *#backup#* files that get added and eventually slow down loading the file again.
#+begin_src emacs-lisp
(setq auto-save-default nil)
(setq create-lockfiles nil)
#+end_src
#+begin_src emacs-lisp
(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)
#+END_SRC
I don't even know how you resume from GUI mode, we'll find a use for this keybinding later on
#+begin_src emacs-lisp
(when (display-graphic-p)
(global-unset-key (kbd "C-z")))
#+end_src
** Visuals
*** Initial Buffer
#+begin_src emacs-lisp
(defun load-projects ()
(with-temp-buffer
(insert-file-contents project-list-file)
(goto-char (point-min))
(apply #'append (read (current-buffer)))))
(defun my-dashboard ()
"Show Welcome buffer"
(with-current-buffer (get-buffer-create "*Welcome*")
(erase-buffer)
(goto-char (point-min))
(insert-image (create-image "~/Documents/emacs-alt.png"))
(insert "\n\n\n")
(insert "Welcome to Emacs!\n\n")
(let* ((time-str (emacs-init-time))
(time (string-to-number (car (split-string time-str)))))
(insert (format "It took %.3f seconds to start up\n\n" time)))
(insert "Happy hacking!\n\n")
(insert "Projects:\n")
(dolist (proj (load-projects))
(insert (format "\t%s\n" proj)))
(setq cursor-type nil)
(switch-to-buffer (current-buffer))
(goto-char (point-min))
(display-line-numbers-mode 0)
(read-only-mode +1)))
(setq initial-buffer-choice #'my-dashboard)
#+end_src
*** COMMENT Dashboard
Use Dashboard.el. First load `all-the-icons` for nicer rendering
#+begin_src emacs-lisp
;; (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))
#+end_src
*** Olivetti
#+begin_src emacs-lisp
(require 'olivetti)
(setq olivetti-minimum-body-width 100)
(global-set-key (kbd "C-x x o") 'olivetti-mode)
#+end_src
*** Themes
#+begin_src emacs-lisp
;; 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)))))
#+end_src
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~
#+begin_src emacs-lisp
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(load custom-file)
#+end_src
Save the chosen theme after picking a new one
#+begin_src emacs-lisp
(defun joe/save-current-theme (THEME)
(let ((inhibit-message t)
(message-log-max nil))
(customize-save-variable 'custom-enabled-themes custom-enabled-themes)))
(advice-add 'consult-theme :after #'joe/save-current-theme)
#+end_src
*** Other
Setup other stuff
#+begin_src emacs-lisp
(setq ring-bell-function 'ignore)
(setq default-frame-alist '((undecorated . t) (fullscreen . maximized)))
(add-hook 'text-mode-hook (lambda () (setq fill-column 100) (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-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)
;; Dont 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
'(workspace-name 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))
;; 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))))
#+end_src
Ligatures... are they that useful?
#+begin_src emacs-lisp :tangle no
(require 'ligature)
(global-ligature-mode)
#+end_src
** Text
#+begin_src emacs-lisp
;; (set-face-attribute 'default nil :family "Fira Code Nerd Font Mono" :height 110)
(set-face-attribute 'default nil :family "FiraCode Nerd Font Mono" :height 110)
;; (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)
#+end_src
** Text Editor
Emacs is an great operating system, if only it had a good text editor...
*** Text editing
#+begin_src emacs-lisp
;; 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)
#+end_src
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
#+begin_src emacs-lisp
(setq sentence-end-double-space nil)
#+end_src
#+end_src
*** Hydra
#+begin_src emacs-lisp :tangle no
(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"))
#+end_src
*** Multiple Cursors
#+begin_src emacs-lisp :tangle no
(require 'multiple-cursors)
#+end_src
*** Meow
#+begin_src emacs-lisp :tangle no
(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)
#+end_src
*** Boon
#+begin_src emacs-lisp :tangle no
(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))
#+end_src
*** Evil
#+begin_src emacs-lisp
(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") 'consult-buffer
(kbd "SPC bb") 'consult-project-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") '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)
(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)
#+end_src
** Buffers
#+begin_src emacs-lisp
(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))
#+end_src
The theme of `C-x 4` bindings is that they operate on other windows, so this function matches that behavior.
#+begin_src emacs-lisp
(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)
#+end_src
** Windows
*** Window Management
#+begin_src emacs-lisp
(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)
#+end_src
Ace Window will show a hint if there are more than 2 windows, but I don't really use it
#+begin_src emacs-lisp :tangle no
(require 'ace-window)
(global-set-key (kbd "C-x o") #'ace-window)
(global-set-key (kbd "C-x C-o") #'ace-swap-window)
#+end_src
*** COMMENT Beframe
#+begin_src emacs-lisp
(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.")
(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))
(require 'beframe)
#+end_src
*** Popper
#+begin_src emacs-lisp
(require 'popper)
(setq popper-reference-buffers
'("\\*compilation\\*" compilation-mode
"^\\*vterm\\*" vterm-mode
"^\\*Flymake.*" flymake-mode
"^\\*Flycheck.*" flycheck-error-list-mode
"^\\*lsp-help\\*" lsp-help-mode
"^\\*eldoc\\*" special-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
("^\\*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 Consider adding checks for vertical splits and only popup on right if so
(defun joe/popper-display-func (buffer &optional _alist)
(cond
((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))
(message "Here")
(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)
;; (setq popper-display-function nil)
;; (setq popper-display-function #'popper-select-popup-at-bottom)
(popper-mode t)
#+end_src
*** COMMENT Tab-bar & Tab-line
#+begin_src emacs-lisp
(global-set-key (kbd "s-n") #'tab-line-switch-to-next-tab)
(global-set-key (kbd "s-p") #'tab-line-switch-to-prev-tab)
#+end_src
*** Scrolling
#+begin_src emacs-lisp :tangle no
(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)
#+end_src
** Tabs/Workspaces
*** Centaur Tabs
#+begin_src emacs-lisp
(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)
(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") #'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)
#+end_src
*** Tabs
#+begin_src emacs-lisp :tangle no
;; (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)))
#+end_src
** Projects
#+begin_src emacs-lisp
(with-eval-after-load 'project
(setq project-vc-ignores '("target/" "bin/" "obj/"))
(add-to-list 'project-switch-commands '(magit-project-status "Magit" ?m))
(add-to-list 'project-vc-extra-root-markers ".dir-locals.el")
(setq frame-title-format "%b")
(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)))
(add-hook 'project-find-functions #'joe/project-root-override)
(define-key 'ctl-x-5-prefix "n" #'set-frame-name))
#+end_src
** VEMCO
Vertico Embark Marginalia Consult Orderless
#+begin_src emacs-lisp
(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)
;; 150))
;; (setq vertico-posframe-poshandler #'posframe-poshandler-slightly-below-top)
(setq vertico-count 17
vertico-resize nil
vertico-cycle t)
(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)
;; #' "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))
(require 'embark)
(setq embark-quit-after-action '((kill-buffer . nil)))
(defvar joe-mode-map
(let ((map (make-sparse-keymap)))
;; (define-key map (kbd "C-'") #'embark-act)
(define-key map (kbd "M-'") #'embark-dwim)
(define-key map (kbd "C-/") #'comment-line)
map)
"my-keys-minor-mode keymap.")
(define-minor-mode joe-mode
"A minor mode so that my key settings override annoying major modes."
:init-value t
:lighter " joe-keys")
(joe-mode t)
(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)
(global-set-key (kbd "C-'") #'embark-act)
(require 'embark-consult)
(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)
(require 'orderless)
(setq completion-styles '(orderless basic)
completion-category-overrides '((file (styles basic partial-completion))))
#+end_src
*** Consult
#+begin_src emacs-lisp
(require 'consult)
;; (require 'consult-lsp)
;; (defun joe/consult-line ()
;; (interactive)
;; (let ((vertico-posframe-mode -1))
;; (consult-line)))
(global-set-key (kbd "C-. C-l") 'consult-line)
;; (global-set-key (kbd "C-. C-l") 'joe/consult-line)
(global-set-key (kbd "C-. C-i") 'consult-imenu)
(global-set-key (kbd "C-. C-t") 'consult-theme)
(global-set-key (kbd "C-. C-r") 'consult-recent-file)
(global-set-key (kbd "C-. C-y") 'consult-yank-from-kill-ring)
;; (global-set-key (kbd "C-s") 'swiper-thing-at-point)
#+end_src
** Dirvish/Dired
#+begin_src emacs-lisp
(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")
("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)))
#+end_src
#+begin_src emacs-lisp
#+end_src
** Avy
#+begin_src emacs-lisp
(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)
#+end_src
** Helpful
#+begin_src emacs-lisp
(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)
#+end_src
** Terminals/Shells
#+begin_src emacs-lisp
(require 'vterm)
(setq vterm-shell "/bin/fish")
(setq vterm-timer-delay 0.01)
(setq vterm-buffer-name-string "VTerm - %s")
(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)
(add-hook 'vterm-mode-hook
(lambda ()
(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-w") #'vterm-send-C-w)
(evil-define-key 'insert vterm-mode-map (kbd "<delete>") #'vterm-send-delete))
(setq-local global-hl-line-mode nil)))
#+end_src
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`.
#+begin_src emacs-lisp :tangle no
;; (add-hook 'emacs-startup-hook (lambda () (require 'vterm)))
#+end_src
** Undo Fu
#+begin_src emacs-lisp
(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)
#+end_src
** Which Key
#+begin_src emacs-lisp
(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")))
#+end_src
** IDE Features
*** REPLs
#+begin_src emacs-lisp
(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))
#+end_src
*** Mono-complete
#+begin_src emacs-lisp
;; (evil-global-set-key 'insert (kbd "C-f") #'mono-complete-expand)
(require 'mono-complete)
(evil-global-set-key 'insert (kbd "M-/") #'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))
(mono-complete-mode +1)
#+end_src
*** Eldoc
#+begin_src emacs-lisp
(evil-global-set-key 'normal (kbd "K") #'eldoc)
(global-eldoc-mode -1)
#+end_src
*** Eglot
#+begin_src emacs-lisp
(with-eval-after-load 'eglot
(global-eldoc-mode -1)
(flymake-mode -1)
(when (boundp 'evil-mode)
;; (evil-global-set-key 'normal (kbd "M-d") #'lsp-describe-thing-at-point)
(evil-global-set-key 'normal (kbd "SPC li") 'eglot-inlay-hints-mode)
(evil-global-set-key 'normal (kbd "<leader>cs") 'consult-eglot-symbols)))
#+end_src
*** COMMENT LSP
#+begin_src emacs-lisp
(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)
#+end_src
*** COMMENT Flycheck
#+begin_src emacs-lisp
(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))
#+end_src
*** Compilation
#+begin_src emacs-lisp
(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)
#+end_src
** Debugging
*** COMMENT DAP
#+begin_src emacs-lisp
(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}"))
#+end_src
** Languages
*** Python
#+begin_src emacs-lisp
(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)
#+end_src>
*** Rust
#+begin_src emacs-lisp
(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 () (electric-pair-local-mode)))
(add-hook 'rust-mode-hook
(lambda ()
(evcxr-minor-mode)
(electric-pair-local-mode)))
(with-eval-after-load 'rustic
;; Don't autostart
(setq rustic-lsp-setup-p nil)
(define-key rustic-mode-map (kbd "<f9>") #'rustic-cargo-run)
(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"))
#+end_src
*** Elisp
#+begin_src emacs-lisp
(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)
#+end_src
*** Web
#+begin_src emacs-lisp
(require 'typescript-mode)
(setq typescript-indent-level 2)
#+end_src
*** SQL
#+begin_src emacs-lisp
(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))
;; This is required so that .dir-locals that read env files for credentials works
(require 'dotenv)
(with-eval-after-load 'sql-mode
(define-key sql-mode-map (kbd "C-M-h") #'joe/mark-sql-defun)
(define-key sql-mode-map (kbd "<f8>") #'sql-connect))
(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)
#+end_src
*** 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.
#+begin_src emacs-lisp
(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)
#+end_src
*** Haskell
#+begin_src emacs-lisp
(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))
#+end_src
*** Clojure
#+begin_src emacs-lisp
(require 'clojure-mode)
(require 'cider)
(setq cider-show-error-buffer 'only-in-repl)
#+end_src
*** OCaml
#+begin_src emacs-lisp :tangle no
(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))
#+end_src
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.
#+begin_src emacs-lisp :tangle no
;; (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)))
#+end_src
*** FSharp
#+begin_src emacs-lisp
(require 'fsharp-mode)
;; (elpaca 'eglot-fsharp)
#+end_src
*** Go
#+begin_src emacs-lisp
(require 'go-mode)
;; (elpaca 'go-imports)
#+end_src
*** Other
#+begin_src emacs-lisp
(require 'json-mode)
(require 'markdown-mode)
#+end_src
** Org Mode
#+begin_src emacs-lisp
(require 'org-bullets)
(defun joe/org-init ()
(setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "BACKLOG")))
(setq org-agenda-files '("~/todo.org"))
(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)
(org-bullets-mode)
(org-indent-mode))
(add-hook 'org-mode-hook 'joe/org-hook)
(require 'org-kanban)
#+end_src
** Magit
The best git porcelain/client I've ever used. Also kill stray magit buffers left over as explained [[https://manueluberti.eu/emacs/2018/02/17/magit-bury-buffer/][here]]
#+begin_src emacs-lisp
(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)
#+end_src
** Restclient
#+begin_src emacs-lisp
(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))
#+end_src
* COMMENT Local variables
;; Local Variables:
;; eval: (add-hook 'after-save-hook '(lambda () (org-babel-tangle)) nil t)
;; End: