94 KiB
Raw Blame History

Emacs Config

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/")))


;; 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
   drag-stuff
   ;; 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
   valign
   ;; 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
   mu4e-alert
   dirvish
   restclient
   disaster
   magit))

(setopt package-vc-selected-packages
        '((dotenv :url "https://github.com/pkulev/dotenv.el")
          (indent-bars :url "https://github.com/jdtsmith/indent-bars")
          (doom-themes :url "https://github.com/JosephFerano/doom-themes")
          (pico8-mode :url "https://github.com/Kaali/pico8-mode")
          (org-timeblock :url "https://github.com/ichernyshovvv/org-timeblock")
          (dape :url "https://github.com/svaante/dape")
          (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)
(add-hook 'prog-mode-hook '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)

;; 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
  '(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)

Random Functions

(defun joe/print-digit-as-ascii (digit)
  (string-match-p "^\\(?:0\\|[1-9][0-9]*\\)$" digit))

(defun joe/get-ascii-in-region (beg end)
  (interactive "r")
  (if (use-region-p)
      (message "%s"
       (concat
        (mapcar 'string-to-number
                (split-string (buffer-substring-no-properties beg end) " "))))
    (message "No region selected")))

(define-key ctl-x-x-map (kbd "a") #'joe/get-ascii-in-region)

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)

drag-stuff package to move lines around, here's a snippet that re-indents

(defun indent-region-advice (&rest ignored)
  (let ((deactivate deactivate-mark))
    (if (region-active-p)
        (indent-region (region-beginning) (region-end))
      (indent-region (line-beginning-position) (line-end-position)))
    (setq deactivate-mark deactivate)))

(advice-add 'drag-stuff-up :after 'indent-region-advice)
(advice-add 'drag-stuff-down :after 'indent-region-advice)
(define-key prog-mode-map (kbd "M-<up>") #'drag-stuff-up)
(define-key prog-mode-map (kbd "M-<down>") #'drag-stuff-down)
(define-key prog-mode-map (kbd "M-<left>") #'drag-stuff-left)
(define-key prog-mode-map (kbd "M-<right>") #'drag-stuff-right)

Simple function more quickly align text

(defun joe/align-whitespace (beg end)
  "Align column text in region by whitespace."
  (interactive "r")
  (align-regexp beg end "\\(\\s-*\\)\\s-" 1 0 t)
  (indent-region beg end))
(add-hook 'csv-mode-hook #'csv-header-line)
(add-hook 'csv-mode-hook #'csv-align-mode)

(defun csv-align-visible ()
  "Align visible fields."
  (interactive)
  (csv-align-fields nil (window-start) (window-end)))

(add-hook 'csv-mode-hook
          (lambda ()
            (define-key csv-mode-map (kbd "C-c C-a") 'csv-align-mode)))

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)

  ;; 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 bu") 'recentf-open-most-recent-file
    (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 mm") 'mu4e
    (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)

(defun joe/qtile-move-dir (dir)
  (start-process "qtile-dir" nil "qtile" "cmd-obj" "-o" "layout" "-f" dir))
  ;; (start-process "qtile-dir" nil "qtile" "cmd-obj" "-o" "layout" "-f" "right"))

(defun joe/windmove-left ()
  (interactive)
  (if (window-in-direction 'left)
      (windmove-left)
    (joe/qtile-move-dir "left")))

(defun joe/windmove-right ()
  (interactive)
  (if (window-in-direction 'right)
      (windmove-right)
    (joe/qtile-move-dir "right")))

(defun joe/windmove-up ()
  (interactive)
  (if (window-in-direction 'up)
      (windmove-up)
    (joe/qtile-move-dir "up")))

(defun joe/windmove-down ()
  (interactive)
  (if (window-in-direction 'down)
      (windmove-down)
    (joe/qtile-move-dir "down")))

;; (global-set-key (kbd "s-h") #'joe/windmove-left)
;; (global-set-key (kbd "s-l") #'joe/windmove-right)
;; (global-set-key (kbd "s-k") #'joe/windmove-up)
;; (global-set-key (kbd "s-j") #'joe/windmove-down)

(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)
COMMENT Unused for now

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
        "^\\*Async Shell Command\\*$"
        ("^\\*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)))

Frames

(undelete-frame-mode)
(defun joe/select-frame ()
  (interactive)
  (let* ((frames (mapcar
                  (lambda (f) (cons (substring-no-properties
                                     (cdr (assoc 'name (frame-parameters f))))
                                    f))
                  (frame-list)))
         (selected-frame-name (completing-read "Select Frame: " (mapcar #'car frames)))
         (selected-frame (alist-get selected-frame-name frames "" nil 'string-equal)))
    (select-frame-set-input-focus selected-frame)))

(define-key 'ctl-x-5-prefix (kbd "RET") #'joe/select-frame)

Projects

Basic enhancements to project.el

(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"))

These are functions to load a project specific file given the conventions I use.

(defun joe/project-open-project-file (FILENAME)
  (when (project-current)
    (let ((proj-dir (project-root (project-current t))))
      (find-file-other-window (expand-file-name FILENAME proj-dir)))))

(defun joe/project-open-project-todo ()
  (interactive)
  (joe/project-open-project-file "TODO.org"))

(defun joe/project-open-project-hours ()
  (interactive)
  (joe/project-open-project-file "HOURS.org"))

(defun joe/project-open-project-notes ()
  (interactive)
  (joe/project-open-project-file "NOTES.org"))

(defun joe/project-open-project-readme ()
  (interactive)
  (let* ((project (project-current))
         (root (project-root project))
         (README (cond ((file-exists-p (expand-file-name "README.org" root)) "README.org")
                      ((file-exists-p (expand-file-name "README.md" root)) "README.md")
                      ((file-exists-p (expand-file-name "README" root)) "README")
                      (t nil))))
    (when README
      (joe/project-open-project-file README))))

(defun joe/project-open-project-license ()
  (interactive)
  (joe/project-open-project-file "LICENSE"))

(defun joe/project-dirvish-dwim ()
  (interactive)
  (when (project-current)
    (dirvish-dwim (project-root (project-current)))))


(evil-define-key 'normal joe/evil-space-mode-map (kbd "_") #'joe/project-dirvish-dwim)
(define-key project-prefix-map "t" #'joe/project-open-project-todo)
(define-key project-prefix-map "h" #'joe/project-open-project-hours)
(define-key project-prefix-map "n" #'joe/project-open-project-notes)
(define-key project-prefix-map "r" #'joe/project-open-project-readme)
(define-key project-prefix-map "l" #'joe/project-open-project-license)
;; Remape this
(define-key project-prefix-map "C-r" #'project-query-replace-regexp)

If you want to try and incorporate something a bit more robust, try this

(defun +project-todo ()
    "Edit the to-do file at the root of the current project."
    (interactive)
    (let* ((project (project-root (project-current t)))
           (todo (car (directory-files project t "^TODO\\(\\..*\\)?$"))))
      (cond ((and todo (file-exists-p todo)) (find-file todo))
            (t (message "Project does not contain a TODO file.")))))

Stuff to immediately switch to Jetbrains for debugging

(global-set-key (kbd "C-M-r") #'joe/open-in-rider)
(defun joe/raise-frame-hook ()
(select-frame-set-input-focus (selected-frame)))
(add-hook 'server-switch-hook #'joe/raise-frame-hook)
(add-hook 'server-switch-hook #'raise-frame)

(defun joe/open-in-rider ()
  (interactive)
  (shell-command
   (mapconcat #'shell-quote-argument
              (list "pycharm"
                    "--line"
                    (int-to-string (line-number-at-pos))
                    "--column"
                    (int-to-string (current-column))
                    buffer-file-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)

These two lines allow you to kill buffers without a confirmation and without closing the mini-buffer so you can quickly kill multiple buffers if needed. The odd looking '(t .t) is for specifying a default for all other actions.

(setq embark-quit-after-action '((t . t)
                                 (kill-buffer . nil)))

(setf (alist-get 'kill-buffer embark-pre-action-hooks) nil)
(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" "~/Tutorials/" "Tutorials")
          ("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 "-") #'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)))

Email

(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e/")
(require '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-update-interval 10)
(setq mu4e-hide-index-messages 't)
(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)))

(mu4e 't)
(add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display)
;; (: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 "M-s")    '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 `vterminternal`.

;; (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-echo-area-use-multiline-p nil)
  (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)

Indentation

(setq indent-bars-color '(highlight :face-bg t :blend 0.1))
(setq indent-bars-color-by-depth nil)
(global-set-key (kbd "C-x x i") #'indent-bars-mode)

Game Dev

(add-to-list 'auto-mode-alist '("\\.vert\\'" . shader-mode))
(add-to-list 'auto-mode-alist '("\\.frag\\'" . shader-mode))

Programming Languages

Python

(require 'python)
(setq python-shell-interpreter "python")
(setq python-shell-interpreter-args "--pylab") ;; What is this for?
(setq python-shell-interpreter-args "") ;; What is this for?

(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)
(setq c-default-style "bsd")
(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)

Haskell

  (require 'haskell-mode)
  (setq haskell-interactive-popup-errors nil)

  (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}"))

COMMENT GDB/GUD

(setq gdb-many-windows t)
(setq gud-tooltip-dereference t)

(defun joe/gdb ()
   (interactive)
   (gdb (format "%s -i=mi" (file-name-sans-extension buffer-file-name))))

(defun hook-gud-mode ()
  (define-key gud-global-map (kbd "C-c") #'gud-cont)
  (define-key gud-global-map (kbd "C-r") #'gud-run))
(add-hook 'gud-mode-hook #'hook-gud-mode)

dape

(setq dape-buffer-window-arrangement 'right)

(defun joe/dape-toggle-breakpoint-and-rerun ()
  (interactive)
  (unless (dape--breakpoints-at-point)
    (dape-breakpoint-toggle))
  (dape-restart))

(define-key global-map (kbd "<f5>") #'joe/dape-toggle-breakpoint-and-rerun)

org-mode

General

(setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "BACKLOG")))
(setq org-directory "~/Notes/")

(evil-define-key 'normal calendar-mode-map (kbd "RET") #'org-calendar-select)
;; This is for org-clock-report
(setq org-duration-format 'h:mm)

There's an issue when I invoke org-clock-in where it seems to search all my org-agenda-files for any open clocks. This is a bit annoying as it opens all these files whenever I clock in, making clocking in slow. This fixes it but then existing clocks don't get resolved.

Here's a github issue describing a little bit about the problem;

(with-eval-after-load 'org-clock
  (defun joe/org-files-list ()
    "A version of `org-files-list' that doesn't open all `org-agenda-files'"
    (let ((files '()))
      (dolist (buf (buffer-list))
        (with-current-buffer buf
          (when (and (derived-mode-p 'org-mode) (buffer-file-name))
            (cl-pushnew (expand-file-name (buffer-file-name)) files
                        :test #'equal))))
      files))

  (defun org-resolve-clocks (&optional only-dangling-p prompt-fn last-valid)
    "Resolve all currently open Org clocks.
If `only-dangling-p' is non-nil, only ask to resolve dangling
\(i.e., not currently open and valid) clocks."
    (interactive "P")
    (unless org-clock-resolving-clocks
      (let ((org-clock-resolving-clocks t))
        (dolist (file (joe/org-files-list))
          (let ((clocks (org-find-open-clocks file)))
            (dolist (clock clocks)
              (let ((dangling (or (not (org-clock-is-active))
                                  (/= (car clock) org-clock-marker))))
                (if (or (not only-dangling-p) dangling)
                    (org-clock-resolve
                     clock
                     (or prompt-fn
                         (lambda (clock)
                           (format
                            "Dangling clock started %d mins ago"
                            (floor (org-time-convert-to-integer
                                    (time-since (cdr clock)))
                                   60))))
                     (or last-valid
                         (cdr clock))))))))))))

In this modified function, dolist processes the list returned by buffer-list instead of org-files-list. For each buffer in the list, we switch to it with with-current-buffer and then test if its major mode is org-mode. If it is, we process the clocks in that buffer as before. We find open clocks in the current buffer's associated file using (buffer-file-name buffer). It's assumed that all Org mode buffers have associated files.

#+end_src

Visuals

(setq org-blank-before-new-entry
      '((heading . nil)
        (plain-list-item . nil)))
(setq org-cycle-separator-lines 1)
(setq org-hide-emphasis-markers t)

(require 'org-bullets)

(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 . "🌄")))

(defun joe/org-hook ()
  (org-fancy-priorities-mode)
  (org-bullets-mode)
  (org-indent-mode)
  (olivetti-mode))
(add-hook 'org-mode-hook 'joe/org-hook)

org-agenda

(setq org-agenda-files '("~/Notes/"))
(setq org-agenda-time-grid '((daily today require-timed)
                             (700 900 1200 1700 1900 20:30 23:00)
                             " ═════ "
                             " ════════════════════ "))
(define-key global-map (kbd "C-c a") #'org-agenda)

(setq org-agenda-custom-commands
      '(("n" "Agenda and all TODOs"
         ((agenda "")
          (alltodo "")))
        ("d" "Daily view"
         ((agenda ""
                  ((org-agenda-overriding-header "Daily Agenda")
                   (org-agenda-span 1)
                   (org-agenda-time-grid nil)
                   (org-agenda-overriding-columns-format "%20ITEM %DEADLINE")
                   (org-agenda-view-columns-initially nil)))
          (tags-todo "dampsig"
                     ((org-agenda-overriding-header "🖥️📚🔢⚛️📊🕹️ DAMPSIG\n")
                      (org-agenda-block-separator ?*)))
          (tags-todo "+PRIORITY=\"B\""
                     ())
          (tags-todo "+PRIORITY=\"C\"")
          (tags-todo "+PRIORITY=\"D\"")))
        ))

org-capture

(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/")))

(defun joe/capture-leetcode-template ()
  (mapconcat
   'identity
   '(":PROPERTIES:"
     ":DIFFICULTY: medium"
     ":END:"
     ""
     "#+TITLE: Leetcode %?"
     "#+AUTHOR: Joseph Ferano"
     "#+STARTUP: show2levels"
     "#+TAGS: "
     ""
     "* Python Solution"
     ":PROPERTIES:"
     ":CompletionTime: "
     ":TimeComplexity: O()"
     ":MemoryComplexity: O()"
     ":END:"
     ":LOGBOOK:"
     ":END:"
     ""
     "#+begin_src python"
     "#+end_src"
     "** Review"
     "** Techniques & Patterns")
   "\n"))

(defun my-org-capture-today ()
  (concat "<" (format-time-string "%Y-%m-%d %a") ">"))

(defun joe/capture-daily ()
  (concat
   "* %^t\n"
   "** Schedule\n"
   "** Resources\n"
   "** Notes"))
(setq org-capture-templates
      `(
        ("l" "Leetcode Solution" plain
         (function ,(lambda () (find-file (joe/capture-leetcode-newfile))))
         #'joe/capture-leetcode-template)

        ("d" "Daily Entry" entry
         (file "Daily.org")
         #'joe/capture-daily)

        ("b" "New Book To Read" entry
         (file+headline "Books.org" "To Read")
         "** %^{Book Title}%^{AUTHOR}p" :prepend t)))

(define-key global-map (kbd "C-c c") #'org-capture)

org-babel

(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)))

(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)
(setq org-src-window-setup 'current-window)

org-tempo provides the ability to insert block snippets in the form of <el and with the cursor you hit TAB and you complete the following;

#+begin_src emacs-lisp #+end_src

(require 'org-tempo)

org-download

(require 'org-download)
(setq org-download-image-dir "./Images/")
(setq org-download-heading-lvl nil)

COMMENT org-roam

(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)

COMMENT org-kanban

(require 'org-kanban)

Calendar

(require 'cl)
(require 'calendar)

(defun twelve-month-calendar ()
  (interactive)
  (let ((calendar-buffer (get-buffer-create "12-month calendar"))
        (month 12)
        (year 2012))
    (set-buffer calendar-buffer)
    (setq calendar-frame (make-frame))
    (make-variable-buffer-local 'font-lock-face)
    (set-face-attribute 'default calendar-frame :height 70)
    (set-frame-width calendar-frame 300)
    (erase-buffer)
    (dotimes (i 12)
      (calendar-generate-month month year 0)
      (calendar-increment-month month year -1))
    (calendar-mode)))

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/")

(defun joe/magit-status-here ()
  (interactive)
  (let ((magit-display-buffer-function
        (lambda (buffer)
          (display-buffer buffer '(display-buffer-same-window)))))
    (magit-status)))

(evil-define-key 'normal joe/evil-space-mode-map (kbd "SPC g h") #'joe/magit-status-here)
;; (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: (add-hook 'after-save-hook '(lambda () (org-babel-tangle)) nil t) ;; End: